feat(content): comprehensive extraction hardening, code mode UI, overnight plan

- Relax isBookLikeResponse threshold (>=2 to >=1)
- Widen book patterns: inline prose, verb-preceded, numbered bold, em-dash
- Remove overly aggressive film/song gating on book extraction
- Allow songs to coexist with film/book tags (explicit tags always returned)
- Strengthen TV patterns: seasons, created by, standalone "tv" query match
- Fix TV_EXT_RE to handle both Title|Year|Creator and Title|Creator|Year
- Widen place patterns: type word search in descriptions, bold fallback
- Add "pizza" to isPlaceQuery and preferredFirstTab
- Add Code tab: isCodeQuery, isCodeLikeResponse, extractCodeBlocks, wiring
- Fix image threshold: single image with meaningful alt text shown
- Refactor filterTabsByContext: specialized paths now append remaining content
- Add code mode UI: orange input styling, design system selection, file selection
- DesignSystemGrid: selection toggle only on checkmark, card click opens detail
- Add 64-test extraction quality test suite
- Update overnight plan.md and prompt.md for hardening run

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-04 10:54:03 +00:00
co-authored by Claude Opus 4.6
parent 75e9274582
commit f0eb4383bd
13 changed files with 1049 additions and 260 deletions
+19 -165
View File
@@ -1,184 +1,38 @@
# AIUI Plan 2 — Automated Loop Task List
# Overnight Plan — Content Surfacing Hardening & Feature Completion
> **Format**: `- [ ]` = pending, `- [x]` = done.
> Make at least 30 attempts on any difficult task before moving on. Loop reads this file. Specs are in `PLAN2.md`. Design rules are in `loop/prompt.md`.
> Make at least 30 attempts on any difficult task before moving on. Loop reads this file.
## M8: Chat UX Polish
## Phase 1: Critical ContentPanel Wiring Gaps
- [x] **M8.1**Message editing & regeneration (pencil icon on hover, re-send clears subsequent messages)
- [x] **M8.2** — Conversation branching (fork from any message, branch switcher pill, each branch stored in IDB)
- [x] **M8.3** — Reply-to threading (quoted excerpt in input, thread line visual, prepend `> quote` to message)
- [x] **M8.4** — Conversation search (`Cmd+F` glass panel, real-time highlight, up/down to jump between matches)
- [x] **M8.5** — Auto-title generation (background AI call after first exchange, silently replaces "New Chat")
- [x] **M8.6** — Context window visualiser (slim progress bar, token estimate, orange → red > 80%)
- [x] **M8.7** — Conversation export (Markdown / JSON / plain text, File System Access API + `<a download>` fallback)
- [x] **M8.8** — Import conversations (drag-and-drop JSON, AIUI format + Claude.ai export format, merge into IDB)
- [x] **M8.9** — Long-press / right-click context menus (messages + content cards, `ContextMenu.vue` glass-card)
- [x] **M8.10** — Scroll position memory (restore per conversation ID, works with virtual scroller)
- [x] **TEST:M8** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T1**Wire ImageGrid + ImageDetail into ContentPanel.vue. Import components, destructure `panelImages`, `selectedImage`, `openImageDetail`, `closeImageDetail` from `useContentPanel()`. Add ImageGrid render block following BookGrid/TVSeriesGrid pattern. Add ImageDetail overlay following BookDetail pattern. Add `selectedImage` to `hasDetailOpen` computed. Reference ContentGridView.vue for how ImageGrid is already used there.
## M9: AI Experience
- [ ] **T2** — Wire PlaceGrid + PlaceDetail into ContentPanel.vue. Import components, destructure `panelPlaces`, `selectedPlace`, `openPlaceDetail`, `closePlaceDetail` from `useContentPanel()`. Add PlaceGrid render block + PlaceDetail overlay. Add `selectedPlace` to `hasDetailOpen` computed. Follow same patterns as T1.
- [x] **M9.1**Multi-model comparison mode (split-screen same prompt → 2 models, swipeable on mobile)
- [x] **M9.2** — System prompt editor / personas (named personas, model preference, accent, per-conversation)
- [x] **M9.3** — Prompt template library (`/` command palette, `{{variable}}` substitution, IDB-persisted)
- [x] **M9.4** — Vision input (drag-and-drop image, base64 in message content, only when model supports vision)
- [x] **M9.5** — Response feedback (thumbs up/down per message, stored in IDB, exported with conversations)
- [x] **M9.6** — Token & cost estimator (badge per message, running total in context bar, model pricing table)
- [x] **M9.7** — AI memory panel (pinned facts injected into every system prompt, encrypted IDB, max 20 items)
- [x] **M9.8** — Model capabilities badge (Vision / Tools / Long context badges in model selector)
- [x] **M9.9** — Temperature & params sliders (temperature, max tokens, top-P, persisted per conversation)
- [x] **M9.10** — Stop sequence configuration (comma-separated stop sequences in advanced settings)
- [x] **TEST:M9** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T3**Verify code tab rendering in ContentPanel.vue. Ensure `panelCodeBlocks` is destructured and code content renders when the code tab is active. If missing, add a code block display component or inline rendering with syntax highlighting. The code tab should show extracted code blocks from AI responses.
## M10: Advanced Content Renderers
- [ ] **TEST:P1** — Run `cd packages/app && npx vitest run` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [x] **M10.1** — Full article renderer (TOC sidebar, reading time, font-size control, print mode, markdown-it)
- [x] **M10.2** — PDF viewer (`pdfjs-dist` lazy loaded, page nav, zoom, text selection, page 1 thumbnail preview)
- [x] **M10.3** — Map renderer (Leaflet + OpenStreetMap lazy loaded, pins for all places, cluster > 10, fullscreen)
- [x] **M10.4** — Recipe renderer (new `recipe` content type, ingredients checklist, numbered steps, scale slider)
- [x] **M10.5** — Event renderer (new `event` content type, countdown, ICS download, Google Calendar URL)
- [x] **M10.6** — Math renderer (KaTeX lazy loaded, `$...$` inline + `$$...$$` block, batch render on stream end)
- [x] **M10.7** — Mermaid diagram renderer (lazy loaded, dark theme, flowchart/sequence/gantt, pan/zoom mobile)
- [x] **M10.8** — Audio waveform player (WaveSurfer.js lazy loaded, orange waveform, click-to-seek)
- [x] **M10.9** — Table renderer (sortable columns, row filter, CSV export, virtualise > 500 rows)
- [x] **M10.10** — Timeline renderer (new `timeline` content type, vertical layout, animate in during streaming)
- [x] **M10.11** — Code runner (HTML/CSS/JS sandboxed iframe, `sandbox="allow-scripts"`, output console)
- [x] **M10.12** — Video renderer (native `<video>`, HLS.js lazy loaded, YouTube nocookie embed fallback)
- [x] **TEST:M10** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
## Phase 2: Content Extraction Hardening
## M11: Nostr Ecosystem
- [ ] **T4** — Expand extraction quality test suite to 100+ tests. Add tests for: podcast extraction (library `[[podcast:p1]]` tags + `[[podcast_ext:]]` tags), app extraction (`extractApps`), film extraction (library `[[film:f1]]` tags + `[[film_ext:]]` tags), magazine section extraction, websites/news detection from markdown links and bare domain extraction, edge cases (empty strings, very long text, unicode characters, special chars in titles), mixed content with 3+ types in one response, `filterTabsByContext` with every specialized path (news+TV, nostr+apps, app+code, etc). Each test should use realistic AI response text patterns. File: `packages/app/src/__tests__/extractionQuality.test.ts`
- [x] **M11.1**Publish Nostr notes (compose panel, sign via NIP-07, broadcast to relays, per-relay status)
- [x] **M11.2** — Nostr DMs NIP-17 (encrypted DMs, inbox tab, contact list, store in IDB)
- [x] **M11.3** — Relay management UI (add/remove relays, latency column, read/write toggle, import NIP-65)
- [x] **M11.4** — Nostr profile editor (display name, bio, avatar, NIP-05, Lightning address, publish kind:0)
- [x] **M11.5** — Zaps NIP-57 (⚡ button on notes/profiles, sat amount input, LNURL-pay flow, QR + deep-link)
- [x] **M11.6** — NIP-05 verification badge (✓ badge, fetch from NIP-05 domain, cache 24h in IDB)
- [x] **M11.7** — Nostr search NIP-50 (search input, send REQ with `search` to NIP-50 relays, filter by type)
- [x] **M11.8** — Thread view (fetch root + replies via `#e` tag, render threaded tree, max 5 levels deep)
- [x] **M11.9** — Nostr lists NIP-51 (follow/mute/pin/bookmark lists, kind:3/10000/10001/10003, add/remove)
- [x] **M11.10** — Long-form content NIP-23 (kind:30023 articles, article renderer, discovery tab, share button)
- [x] **TEST:M11** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T5**Fix any extraction bugs found during T4 testing. Run the expanded test suite, fix all failures in `contentExtraction.ts` and/or `contentFiltering.ts`. Iterate until all tests pass. Document any patterns that needed fixing.
## M12: Bitcoin Ecosystem
- [ ] **T6** — Verify filterTabsByContext prioritization is correct for all paths. The function was recently refactored to use a `prioritize()` helper that appends remaining content after priority tabs. Write specific tests ensuring: news queries show news/magazine first but include TV/books/etc if present, nostr queries show nostr first but include other content, app queries show apps first, preferred tab from `preferredFirstTab()` is always first in general case, no content type is ever silently dropped. File: `packages/app/src/__tests__/extractionQuality.test.ts`
- [x] **M12.1**On-chain address display (bech32 + legacy detect, QR, mempool.space link, copy button)
- [x] **M12.2** — Fedimint ecash display (detect token format, amount in sats, "Receive in Fedi" deep-link)
- [x] **M12.3** — BOLT12 offers (detect `lno1...`, decoded amount, description, QR, "Pay with wallet" deep-link)
- [x] **M12.4** — Nostr Wallet Connect NWC (NIP-47, paste connection string, check balance, pay with confirmation)
- [x] **M12.5** — LNURL-auth login (generate LNURL-auth QR, scan with wallet, set persistent Lightning identity)
- [x] **M12.6** — Live sat/fiat price (mempool.space `/api/v1/prices`, 60s refresh, `useBitcoinPrice` composable)
- [x] **M12.7** — Mempool.space tx viewer (detect txid/block height, confirmations, fee rate, live WebSocket)
- [x] **M12.8** — BOLT11 decoder card (amount, description, expiry countdown, red when < 5min, NWC pay button)
- [x] **TEST:M12** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **TEST:P2**Run `cd packages/app && npx vitest run src/__tests__/extractionQuality.test.ts` — must have 100+ tests all passing. Then run `pnpm typecheck && pnpm lint`.
## M13: Content Discovery
## Phase 3: Code Mode UX Completion
- [x] **M13.1**"For You" feed (frequency map from favorites + history, local only, refreshes on app open)
- [x] **M13.2** — Content tagging (user tags on any item, stored in IDB, filter grids by tag, tag cloud in favorites)
- [x] **M13.3** — Smart playlists (recently played, most played, by genre, by decade — computed IDB views)
- [x] **M13.4** — Similar content (background AI call per item, 3 suggestions, cached 7 days in IDB)
- [x] **M13.5** — Recently viewed history (last 50 items any type, "Recent" tab in content panel, stored IDB)
- [x] **M13.6** — Content collections (user-curated mixed-type lists, mosaic thumbnail grid, shareable NIP-51)
- [x] **M13.7** — Trending in conversations (most-referenced items across 30 days, "referenced N times" badge)
- [x] **M13.8** — Share content to Nostr (generate note with title + AI description + tag URL, sign via NIP-07)
- [x] **TEST:M13** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T7**Add Esc key to exit code mode on desktop. In ChatWindow.vue or ChatPage.vue, add a `keydown` event listener for Escape that calls `exitCodeMode()` from `useCodeContext` when code mode is active (`codeMode.value === true`). Only handle on desktop (not mobile). The listener should be added in `onMounted` and cleaned up in `onUnmounted`.
## M14: Plugin Marketplace
- [ ] **T8** — Show selected project indicator near chat input when in code mode. When `codeMode` is true and a project is selected (`currentProject` from `useCodeContext`), display the project name as a small pill/badge near the chat input area. Use accent/orange styling consistent with the code mode input styling (bg-accent/15, text-accent). Show in ChatInput.vue or as a banner above it in ChatWindow.vue.
- [x] **M14.1**Plugin discovery UI (fetch static registry JSON, list with name/type/author/version, install button)
- [x] **M14.2** — Plugin settings panel (`PluginSettingsForm.vue` renders JSON Schema, stored encrypted IDB)
- [x] **M14.3** — Plugin permissions UI (permissions dialog on install, grant/deny per capability, stored per plugin)
- [x] **M14.4** — Plugin dev mode (`VITE_PLUGIN_DEV=true`, hot-reload from `src/plugins/dev/`, error inspector)
- [x] **M14.5** — Built-in plugin: Wikipedia (`/wiki {query}`, Wikipedia REST API, returns `article` card)
- [x] **M14.6** — Built-in plugin: OpenLibrary (`/book {query}`, openlibrary.org, returns `book_ext` cards)
- [x] **M14.7** — Plugin import by URL (paste GitHub raw URL or IPFS CID, validate manifest, permissions dialog)
- [x] **M14.8** — Plugin versioning & auto-update (check registry on start, badge when updates available, changelog)
- [x] **TEST:M14** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [ ] **T9**Wire selected design tokens + files into AI context. When sending a message in code mode, include any selected design tokens and selected file contents as context. In `useAI.ts` `sendMessage()`, check if code mode is active, read `selectedDesignTokens` and `selectedFiles` from `useCodeContext()`, read file contents for selected files, and prepend this context to the system prompt or as a prefixed user message section. Keep context concise — include file paths and key content, not full files.
## M15: Settings & Personalisation
- [ ] **TEST:P3** — Run `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
- [x] **M15.1** — Accent colour picker (colour wheel + presets, updates `--color-accent` CSS var, IDB-persisted)
- [x] **M15.2** — Glass intensity slider (Subtle/Default/Strong presets, blur + opacity CSS vars, live preview)
- [x] **M15.3** — Font size settings (Compact 13px / Default 15px / Large 17px, sets `--font-size-base`)
- [x] **M15.4** — Content type visibility (toggle each of 11 renderers, hidden types still extract, just not shown)
- [x] **M15.5** — Keyboard shortcut map (list all shortcuts, click to rebind, conflict detection, IDB-persisted)
- [x] **M15.6** — Browser push notifications (opt-in, "Generation complete" while backgrounded, SW notification)
- [x] **M15.7** — Auto-archive old conversations (7/30/90/never slider, "Archive" IDB store, unarchive per item)
- [x] **M15.8** — Full data export (JSON archive of all IDB stores, optional encryption, GDPR compliant)
- [x] **M15.9** — Data wipe (two-step confirm, clears IDB + SW cache + localStorage, checkbox for API key vault)
- [x] **M15.10** — Default conversation settings (model, persona, web search, token counts — applies to new convos)
- [x] **TEST:M15** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
## Phase 4: Final Verification
## M16: Mobile UX Polish
- [x] **M16.1** — Bottom sheet component (`BottomSheet.vue`, gesture drag, 40%/80%/100% snap points, backdrop)
- [x] **M16.2** — Swipe to navigate conversations (swipe left/right in chat, slide transition, 80px + 0.3 velocity)
- [x] **M16.3** — Pull-to-refresh on content panels (glass spinner, re-fetch context, haptic on release)
- [x] **M16.4** — Haptic feedback (`useHaptics()` composable, `navigator.vibrate()`, settings toggle to disable)
- [x] **M16.5** — Web Share API (native `navigator.share()`, fallback glass share sheet, copy/Nostr options)
- [x] **M16.6** — Pinch-to-zoom on images & maps (`usePinchZoom()` composable, 1×–4×, double-tap reset)
- [x] **M16.7** — iOS PWA polish (apple-mobile-web-app meta tags, safe area insets, splash screens)
- [x] **M16.8** — Long-press context menus mobile (500ms trigger, opens as BottomSheet, haptic on trigger)
- [x] **M16.9** — Scroll position memory per route (session Map, works across tab switches and back navigation)
- [x] **M16.10** — Landscape mode optimisation (50/50 split layout, minimal player bar, smooth rotate transition)
- [x] **TEST:M16** — Run `pnpm test` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding.
## M17: Accessibility & Internationalisation
- [x] **M17.1** — Keyboard navigation audit (Tab order, focus trap in modals, Escape closes, arrow key grids)
- [x] **M17.2** — ARIA audit (`aria-label` all icon buttons, `aria-live` dynamic content, dialog roles)
- [x] **M17.3** — High contrast mode (`prefers-contrast: more` + Settings toggle, higher border/text opacity)
- [x] **M17.4** — Automated accessibility tests (axe-core in Playwright, `pnpm test:a11y`, fail on critical)
- [x] **M17.5** — i18n foundation (`vue-i18n`, extract strings to `en.json`, add `es.json` + `fr.json`)
- [x] **M17.6** — RTL layout support (`dir="rtl"` for Arabic/Hebrew, logical CSS properties throughout)
- [x] **M17.7** — Dyslexia-friendly font (OpenDyslexic WOFF2 self-hosted, Settings → Appearance → Font)
- [x] **M17.8** — Skip navigation link (hidden "Skip to main content" visible on Tab focus, jumps to `<main>`)
- [x] **TEST:M17** — Run `pnpm test`, `pnpm test:a11y`, and `pnpm typecheck && pnpm lint`. Fix all failures.
## M18: Performance
- [x] **M18.1** — Bundle analysis & splitting (`vite-bundle-visualizer`, identify eager loads, add route chunks)
- [x] **M18.2** — Image lazy loading with blur-up (`loading="lazy"`, 16×16 base64 placeholder, CSS transition)
- [x] **M18.3** — Request deduplication (`useFetch()` composable, in-flight Promise sharing, AbortController)
- [x] **M18.4** — Web Worker for heavy tasks (`heavy.worker.ts` via Comlink, contentExtraction + crypto)
- [x] **M18.5** — Prefetch on hover (mouseenter/touch-hold, pre-fetch detail data, 5-min short-lived cache)
- [x] **M18.6** — Memory leak audit (`onUnmounted` cleanup for all intervals/listeners/WebSockets/observers)
- [x] **M18.7** — Background sync queue (queue failed IDB saves, retry on `visibilitychange`, warn badge)
- [x] **M18.8** — OPFS storage backend (SQLite WASM via `@sqlite.org/sqlite-wasm`, `VITE_STORAGE=opfs` flag)
- [x] **TEST:M18** — Run `pnpm test`, `pnpm build`, verify gzipped bundle < 250 KB. Fix if over budget.
## M19: Developer Experience & Quality
- [x] **M19.1** — Storybook 8 (stories for all `ui/` components, dark glass canvas, `pnpm storybook`)
- [x] **M19.2** — Visual regression tests (Playwright screenshots for key views, fail on > 0.5% pixel diff)
- [x] **M19.3** — Bundle size CI gate (`bundlesize`, fail if > 250 KB gzipped, PR comment with diff)
- [x] **M19.4** — Comprehensive mock data (20+ items per content type, mock Nostr relay, mock TMDB)
- [x] **M19.5** — E2E cross-browser matrix (Chromium + Firefox + WebKit, mobile viewports iPhone 14 + Galaxy S21)
- [x] **M19.6** — Proxy integration tests (mock Anthropic API, assert SSE format, tool_use, error codes, disconnect)
- [x] **M19.7** — Lighthouse CI (LCP < 3s, CLS < 0.15, scores posted as PR comment, stored in `reports/`)
- [x] **M19.8** — Dependency audit (`pnpm audit` + `license-checker`, weekly job, block on critical vulns)
- [x] **TEST:M19** — Run full suite: `pnpm test && pnpm test:e2e && pnpm typecheck && pnpm lint`. All must pass.
## M20: Collaboration & Sharing
- [x] **M20.1** — Share conversation via Nostr (export as kind:30023 long-form article, sign NIP-07, optional encrypt)
- [x] **M20.2** — Read-only conversation viewer (`/view/:nostrAddr` route, renders shared Nostr conversation)
- [x] **M20.3** — Collaborative playlist NIP-51 (shared kind:30004 list, invite by npub, merge contributions)
- [x] **M20.4** — Conversation templates (pre-built starters as glass cards, system prompt + first message, NIP share)
- [x] **M20.5** — Export as audio podcast (Web Speech API TTS, WAV export via Web Audio, optional music mix)
- [x] **M20.6** — Community content packs (import curated JSON from GitHub/IPFS, listed in plugin marketplace)
- [x] **TEST:M20** — Run `pnpm test && pnpm typecheck && pnpm lint`. All green before proceeding.
---
## Final Sign-off
- [x] **FINAL** — Run `pnpm test && pnpm test:e2e && pnpm typecheck && pnpm lint && pnpm build`. All pass. Update PROGRESS.md with completion summary. Tag release `v2.0.0`.
---
## Notes
(Claude adds brief completion notes here as tasks finish.)
- [ ] **T10** — Final verification pass. Run `pnpm test` (all tests pass), `pnpm typecheck` (no new type errors — archyBridge.ts error is pre-existing and acceptable), `pnpm lint` (0 errors). Fix any issues found. Commit all remaining changes with a summary commit message.
+26 -14
View File
@@ -1,9 +1,19 @@
You are executing the AIUI product roadmap autonomously. Read these files first:
You are hardening the AIUI content extraction pipeline and completing incomplete features. Read these files first:
1. `loop/plan.md` — Your task checklist (mark items `- [x]` as you complete them)
2. `PLAN2.md`Detailed specs for each task (M8.1, M8.2, etc.)
3. `CLAUDE.md` — Project conventions, design system rules, and coding standards
4. `PROGRESS.md` — Update the session log after each task
2. `CLAUDE.md`Project conventions, design system rules, and coding standards
## Key Context
The content extraction pipeline lives in:
- `packages/app/src/composables/contentExtraction.ts` — Extracts books, films, TV, songs, podcasts, places, images, code blocks, apps from AI response text
- `packages/app/src/composables/contentFiltering.ts` — Query/response classifiers (isBookQuery, isTVQuery, etc.) and tab routing (filterTabsByContext)
- `packages/app/src/composables/useContentPanel.ts` — Wires extraction functions to Vue reactive state and tab system
- `packages/app/src/components/content/ContentPanel.vue` — Main content panel rendering grids and detail views
- `packages/app/src/components/content/ContentGridView.vue` — Grid view (already has ImageGrid/PlaceGrid wired)
- `packages/app/src/__tests__/extractionQuality.test.ts` — Extraction quality test suite (currently 64 tests)
Content flows: AI response text → extraction functions → useContentPanel refs → filterTabsByContext → ContentPanel/ContentGridView rendering
## Design System Rules (must follow for every file you touch)
@@ -19,22 +29,24 @@ You are executing the AIUI product roadmap autonomously. Read these files first:
## For each task in loop/plan.md:
1. Find the first unchecked `- [ ]` item
2. Read the detailed spec from `PLAN2.md` for that task code (e.g. M8.1)
3. Implement the feature following the spec and design system rules above
4. Run `pnpm typecheck && pnpm lint` — fix all errors before committing
5. Run `pnpm test` — fix any failing tests
6. If the task has a **Testing Gate** (`pnpm test:e2e` or specific test file), run it and fix failures
7. Commit: `type(scope): description` (conventional commits)
8. Mark it done `- [x]` in `loop/plan.md`
9. Update `PROGRESS.md` session log
2. Read the task description carefully
3. Read the relevant source files before making changes
4. Implement the feature/fix following conventions and design system rules
5. Run `pnpm typecheck && pnpm lint` — fix all errors before committing
6. Run `pnpm test` — fix any failing tests
7. If the task involves tests, run `cd packages/app && npx vitest run src/__tests__/extractionQuality.test.ts` specifically
8. Commit: `type(scope): description` (conventional commits)
9. Mark it done `- [x]` in `loop/plan.md`
10. Move to the next unchecked task immediately
## Rules
- Never skip a testing gate — if tests fail, fix them before moving on
- If a task is proving difficult, keep trying different approaches. Make at least 30 genuine attempts before giving up and moving on. If you do move on, note the attempt count in the Notes section and leave the task unchecked.
- If a task is proving difficult, keep trying different approaches. Make at least 30 genuine attempts before giving up and moving on.
- Always run typecheck + lint after every code change
- Keep initial bundle < 250 KB gzipped — lazy-load anything heavy (PDF viewer, Mermaid, KaTeX, etc.)
- Keep initial bundle < 250 KB gzipped — lazy-load anything heavy
- Bitcoin only — never altcoins, never fiat payment flows
- Privacy first — no external analytics calls, no telemetry
- Do not stop until all tasks are checked or you are rate limited
- When adding tests, aim for realistic AI response patterns, not trivial examples
- When wiring components into ContentPanel.vue, follow the exact same pattern used for existing content types (BookGrid/BookDetail, TVSeriesGrid/TVSeriesDetail, etc.)