You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Pre-release sweep per release-workflow.md Step 11 + ai-workflow.md
"Order for new features" step 7 (smoke spec required for every
new UI feature).
- e2e/smoke/ai-review.spec.ts: 4 Playwright smoke tests for the
AI Review UI surface (three radio focus buttons render, non-prose
warning toggles per chapter type, mocked happy-path download-report
link). Uses route mocks to avoid requiring a live LLM backend.
- docs/help/{de,en}/ai.md: rewrote Chapter Review section with the
three focus modes (Style / Consistency / Beta Reader), cost
estimate, non-prose warning, persistence + download, async
progress. Mirrors the actual v0.20 UI.
- docs/API.md: documents the 8 new /api/ai/ endpoints including the
async flow, SSE stream, FileResponse download, cost estimate, and
UI metadata endpoint.
- .claude/rules/lessons-learned.md: new AI Review extension section
with 7 pitfalls discovered during the release window (backup-import
soft-delete dedup, manuscripta output/ cleanup between format
runs, Pandoc multi-doc YAML, CSS specificity trap h2+p vs
p:not(:first-child), TipTap useEditor not flushing editor.storage,
prefix testid overmatch, recovery draft contentHash contract).
- docs/audits/current-coverage.md: v0.20.0 addendum with the +181
test delta (1,271 -> 1,452 total automated tests).
Copy file name to clipboardExpand all lines: .claude/rules/lessons-learned.md
+46Lines changed: 46 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -395,3 +395,49 @@ Stability filter:
395
395
396
396
-`vite-plugin-pwa@1.2.0` (latest as of 2026-04-18) lists Vite `^3 || ^4 || ^5 || ^6 || ^7` in its peer deps. No Vite 8 support yet. Attempting Vite 6 -> 8 with PWA installed will either refuse to install or runtime-break.
397
397
- This is why DEP-04 landed as Vite 6 -> 7, not 6 -> 8. The Vite 8 bump is deferred until vite-plugin-pwa publishes compat. Check `npm view vite-plugin-pwa peerDependencies` before attempting the next bump.
398
+
399
+
## AI Review extension (v0.20.0)
400
+
401
+
### Backup import must check soft-delete state before dedup
402
+
403
+
-`backup_import._restore_book_from_dir` previously treated any pre-existing `Book.id` in the DB as "already imported" and returned False. That check predates the soft-delete / trash feature: a backup made before trashing silently could not be restored once the books had been moved to trash - the importer saw them in the DB (with `deleted_at` set) and refused to rebuild.
404
+
- Fix: when the pre-existing row is soft-deleted, HARD-delete it along with its chapters + assets, then fall through to the fresh-insert path. Do NOT try to revive via per-attribute setattr: the backup JSON does not carry every NOT NULL column (`ai_tokens_used`, `created_at`, `updated_at`), so SQLAlchemy emits an UPDATE that sets those to NULL and the integrity constraint trips. Hard-delete + fresh-insert sidesteps the whole partial-update dance and matches the backup's snapshot semantics.
405
+
- Generalizes: any "idempotent by id" import path added before a soft-delete feature becomes silently buggy. Always branch on `deleted_at IS NULL` when deduping.
406
+
407
+
### manuscripta `run_export` moves `output/` to `backup/` on every call
408
+
409
+
-`manuscripta.export.book.run_export` copies the existing `project_dir/output/` to `project_dir/backup/` at the start of every invocation and creates a fresh `output/`. A list of per-format output paths collected across a batch-export loop contains stale paths by the time the loop finishes.
410
+
- Symptom in v0.19.x: `FileNotFoundError` at `zipfile.ZipFile.write(f, f.name)` inside `/api/books/{id}/export/batch`, referencing a file that existed moments earlier.
411
+
- Fix: after each `run_pandoc` call, IMMEDIATELY copy the produced file into a stable staging directory (`tmp_dir/batch/`) and zip from there. Do NOT keep references to files under `project_dir/output/` across subsequent `run_export` calls.
412
+
413
+
### Pandoc-wrapped metadata.yaml is a multi-doc YAML stream
414
+
415
+
- The project exporter wraps `metadata.yaml` in Pandoc-style `---` / `---` document markers. PyYAML's `safe_load` expects exactly one document and raises `yaml.composer.ComposerError` on any trailing `---` (even if the second document is empty).
416
+
- Fix: use `yaml.safe_load_all(f)` and return the first non-empty document. Handles both the bare and the Pandoc-wrapped shapes in one code path.
417
+
- Regression: `smart_import` crashing with 500 on a ZIP that `/api/backup/export` had just produced.
418
+
419
+
### CSS specificity trap: `h2 + p` loses to `p:not(:first-child)`
- For `[data-app-theme="classic"] .ProseMirror p:not(:first-child)`: (0, 1, 2, 1) - 1 attr, 1 class + 1 pseudo-class = 2 "classes", 1 element. The pseudo-class pushes the base rule ahead of the adjacent-sibling override.
423
+
- When both rules match (a paragraph that directly follows a heading AND is not the first child), the higher-specificity `:not(:first-child)` wins and the heading override never applies.
424
+
- Fix: append `:not(:first-child)` to each `h* + p` override. Combined (0, 1, 2, 2) beats the base (0, 1, 2, 1).
425
+
- Generalizes: any CSS override against a `:not(:first-child)` base rule needs at least the same pseudo-class weight.
426
+
427
+
### TipTap `useEditor` does NOT flush `editor.storage` reads to React
428
+
429
+
- A status-bar word count written as `{editor?.storage.characterCount?.words()}` inline in JSX looks fine but does not update reliably after keyboard input. TipTap's built-in re-render on transaction fires for selection changes but not for every content transaction we rely on.
430
+
- Reliable fix: subscribe explicitly via `editor.on('update', () => forceUpdate())`, or mirror the count into React state via `useState` + `editor.on('update')`.
431
+
- Surfaced as a smoke-test failure in `smoke/editor-formatting.spec.ts` "word count updates after typing". Currently skipped with a reference to issue #9 until the subscribe pattern is wired up.
432
+
433
+
### Prefix testid selectors match every nested testid that shares the prefix
434
+
435
+
- A selector like `[data-testid^='book-card-']` cleanly matches each card root AND every nested child testid that shares the prefix (`book-card-menu-{id}`, `book-card-menu-delete-{id}`). `toHaveCount(N)` returns `2N` or more per visible card.
436
+
- Fix: `[data-testid^='book-card-']:not([data-testid*='-menu-'])`, or give the root a distinct testid like `book-card-root-{id}`.
437
+
- Same shape as the `[class^=""]` overmatch antipattern. Always test a prefix selector against the full rendered surface before shipping.
438
+
439
+
### IndexedDB recovery draft `contentHash` is a MATCH check, not a MISMATCH
440
+
441
+
-`frontend/src/db/drafts.ts#checkForRecovery` returns a draft iff `draft.contentHash === hashContent(serverContent)` AND `draft.content !== serverContent`. The contract is "this draft was written against THIS server state, local content is newer". Seeding a test draft with `contentHash: '_mismatch_'` will NOT trigger the recovery banner.
442
+
- A misleading test comment saying "must differ from server hash" burned multiple sessions before the `checkForRecovery` source was re-read.
443
+
- When writing tests that seed IndexedDB, compute the hash of the real server content inside the seed script rather than using a sentinel value.
The `/api/ai/` router hosts the multi-provider AI features. The review path supports both synchronous (legacy) and async flows:
53
+
54
+
-`POST /api/ai/review` - synchronous chapter review; accepts `chapter_type` so the system prompt can tailor feedback per section (e.g. dedication vs chapter).
55
+
-`POST /api/ai/review/async` - submit a review as a background job. Returns `{job_id, review_id}`. The worker persists a Markdown report to `uploads/{book_id}/reviews/{review_id}-{chapter-slug}-{YYYY-MM-DD}.md`.
56
+
-`GET /api/ai/jobs/{job_id}` - poll current status, progress and (when terminal) the inline review.
-`DELETE /api/ai/jobs/{job_id}` - cancel a running review.
59
+
-`GET /api/ai/review/{review_id}/report.md?book_id=...` - download the persisted Markdown report.
60
+
-`POST /api/ai/review/estimate` - rough input-token + USD cost estimate (uses a chars/4 heuristic and a small per-model pricing dict).
61
+
-`GET /api/ai/review/meta` - UI metadata: all focus values, the three primary UI focus values, non-prose chapter types, supported languages, chapter types. The frontend reads this to drive the radio buttons + non-prose warning without hardcoding.
62
+
63
+
Review focus values: `style` (existing) plus `consistency` (new: within-chapter contradictions, distinct from `coherence` which checks logical flow) and `beta_reader` (new: simulated first-read feedback). Legacy values (`coherence`, `pacing`, `dialogue`, `tension`) stay on the API for power users but are no longer exposed in the UI.
64
+
65
+
Cascade on chapter delete: when a chapter is removed, all review Markdown files whose filename contains the chapter's slug are deleted alongside the chapter row.
-**AI Review Extension**: 31 new backend tests across `test_ai_review.py` (47 total; includes prompt-builder for 8 languages, chapter-type injection, cost estimate, async review flow via JobStore + SSE, FileResponse download, cascade on chapter delete) and `test_ai_review_store.py` (15 direct unit tests: slugify, filename shape, report roundtrip, cascade boundary safety, DELETE chapter cascade integration).
23
+
-**Backup regressions**: `test_backup_import_revive.py` pins 9 data-critical paths (soft-deleted book revival, idempotent live re-import, merge with non-empty DB, multi-doc YAML parse, smart-import project ZIP roundtrip, batch-export pandoc-skip gate).
24
+
-**Playwright smoke for AI Review**: new `smoke/ai-review.spec.ts` (4 tests) covering the three radio focus buttons, non-prose warning visibility per chapter type, and a mocked happy-path download-report flow. Closes the step-7 protocol gap for the new UI feature.
25
+
-**Frontend Vitest for AI Review**: `src/data/ai-review-strings.test.ts` (8 tests) pins 8-language coverage of the book-language status + warning strings and the non-prose chapter-type set's parity with the backend `NON_PROSE_TYPES` constant.
-**Audiobook generation E2E** - still no dedicated smoke spec; same as 2026-04-18.
31
+
-**Image/asset upload in editor E2E** - still no spec.
32
+
-**TipTap useEditor -> React re-render for status bar** - currently ships with a stale word count on keyboard input. Lessons-learned has the subscribe pattern documented; dedicated fix + test un-skip is deferred to post-v0.20.0.
33
+
-**Chapter-sidebar dropdown layout at 125% / 150% CSS zoom** - 3 skipped smoke tests; needs Radix Popper collision rework or a viewport-downscale test proxy.
Copy file name to clipboardExpand all lines: docs/help/de/ai.md
+32-2Lines changed: 32 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,14 +41,44 @@ Die KI passt ihre Vorschläge an das Genre und die Sprache deines Buches an.
41
41
42
42
## Kapitel-Review
43
43
44
-
Klicke den **Review**-Tab im KI-Panel. Die KI analysiert das gesamte Kapitel und gibt strukturiertes Feedback:
44
+
Klicke den **Review**-Tab im KI-Panel. Die KI analysiert das gesamte Kapitel und liefert einen strukturierten Markdown-Bericht, den du speichern und wieder öffnen kannst.
45
+
46
+
### Fokusmodi
47
+
48
+
Wähle genau einen Fokus, bevor du **Kapitel reviewen** anklickst:
49
+
50
+
-**Stil** - Schreibstil: Wortwahl, Satzvariation, Lesbarkeit, Stimmkonsistenz. Nutze diesen Modus, wenn die Story funktioniert, die Prosa aber geschliffen werden soll.
51
+
-**Konsistenz** - Widersprüche innerhalb des Kapitels: Fakten, Zeitlinie, Figurenmerkmale, Orte, Objektbeschreibungen. Fängt die kleinen "ihr Mantel war vor zwei Seiten blau, jetzt grün"-Fehler ab, bevor sie ein Leser entdeckt.
52
+
-**Testleser** - offenes Erstleser-Feedback: Was zieht rein, was zieht sich, was verwirrt, welche Fragen bleiben offen. Nutze diesen Modus, wenn das Kapitel fertig ist und du einen "frische Augen"-Durchgang willst.
53
+
54
+
Die vier Legacy-Fokuswerte (Kohärenz, Pacing, Dialog, Spannung) sind auf API-Ebene weiterhin verfügbar, tauchen aber nicht mehr im UI auf.
55
+
56
+
### Kostenschätzung
57
+
58
+
Der Start-Button zeigt eine grobe Schätzung der Input-Tokens und der USD-Kosten basierend auf Kapitellänge und konfiguriertem Modell (z.B. `~5k Tokens, ~$0.075`). Die Schätzung ist konservativ; die tatsächliche Nutzung liegt meist darunter. Ohne bekanntes Modell erscheint keine Kostenangabe.
59
+
60
+
### Nicht-Prosa-Kapitel
61
+
62
+
Für Kapiteltypen, die keine erzählende Prosa sind (Titelseite, Copyright, Inhaltsverzeichnis, Impressum, Index, Schmutztitel, Auch-vom-Autor, Nächster-Band, Call-to-Action, Endnoten, Literatur, Glossar), zeigt Bibliogon eine kleine Warnung über dem Start-Button. Du kannst das Review trotzdem starten; das Feedback ist dann meist eingeschränkter als bei Prosa.
63
+
64
+
### Strukturiertes Ergebnis
65
+
66
+
Jedes Review nutzt die gleiche Struktur:
45
67
46
68
-**Zusammenfassung** - ein Satz zum Kapitelinhalt
47
69
-**Stärken** - was gut funktioniert, mit konkreten Verweisen
48
70
-**Vorschläge** - konkrete Verbesserungen mit Erklärungen
49
71
-**Gesamtbewertung** - eine kurze Einschätzung
50
72
51
-
Das Review berücksichtigt das Genre deines Buches und gibt genregerechtes Feedback (z.B. Pacing-Feedback für Thriller, Klarheits-Feedback für Sachbücher).
73
+
Das Review berücksichtigt Genre, Sprache (alle 8 unterstützten UI-Sprachen) und den ausgewählten Kapiteltyp, so dass das Feedback zum jeweiligen Abschnitt passt (z.B. Pacing-Feedback für Thriller, minimale Ton-Hinweise bei einer Widmung, Compliance-Hinweise auf Copyright-Seiten).
74
+
75
+
### Persistenz + Download
76
+
77
+
Jedes Review wird als Markdown-Datei unter `uploads/{book_id}/reviews/` gespeichert, Dateiname nach dem Schema `{review-id}-{kapitel-slug}-{YYYY-MM-DD}.md`. Ein **Bericht herunterladen**-Button erscheint neben dem Ergebnis, so kannst du die Datei lokal sichern, in ein Schreib-Notizbuch legen oder an einen Commit hängen. Beim Löschen eines Kapitels werden die zugehörigen Review-Dateien automatisch mit aufgeräumt.
78
+
79
+
### Asynchroner Fortschritt
80
+
81
+
Grosse Kapitel brauchen 5-60 Sekunden. Das Review läuft als Hintergrund-Job; der Editor bleibt bedienbar, und eine rotierende Statusmeldung (in der Sprache deines Buches) zeigt den Fortschritt. Du kannst das KI-Panel mitten im Review schliessen; sobald das Review fertig ist, taucht das Ergebnis beim erneuten Öffnen wieder auf.
Copy file name to clipboardExpand all lines: docs/help/en/ai.md
+32-2Lines changed: 32 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -41,14 +41,44 @@ The AI adapts its suggestions to your book's genre and language.
41
41
42
42
## Chapter review
43
43
44
-
Click the **Review** tab in the AI panel. The AI analyzes your entire chapter and provides structured feedback:
44
+
Click the **Review** tab in the AI panel. The AI analyzes your entire chapter and returns a structured Markdown report you can save and re-open.
45
+
46
+
### Focus modes
47
+
48
+
Pick exactly one focus before clicking **Review chapter**:
49
+
50
+
-**Style** - writing style: word choice, sentence variety, readability, voice consistency. Use this when the story works but the prose needs polish.
51
+
-**Consistency** - internal contradictions within the chapter: facts, timing, character traits, locations, object descriptions. Use this to catch the small "her coat was blue two pages ago, now green" kind of mistakes before a reader spots them.
52
+
-**Beta Reader** - open-ended first-read feedback: what engages, what drags, what confuses, questions left in the reader's mind. Use this when the chapter is done and you want a "fresh eyes" pass.
53
+
54
+
The three legacy focus values (Coherence, Pacing, Dialogue, Tension) are still supported on the API level for power users but are no longer exposed in the UI.
55
+
56
+
### Cost estimate
57
+
58
+
The Start button shows a rough input-token count and USD cost estimate based on your chapter length and the configured model (e.g. `~5k tokens, ~$0.075`). The estimate is conservative; actual usage is usually lower. No estimate is shown when the model is unknown to Bibliogon's price table.
59
+
60
+
### Non-prose chapters
61
+
62
+
For chapter types that are not narrative prose (title page, copyright, table of contents, imprint, index, half title, also-by-author, next-in-series, call-to-action, endnotes, bibliography, glossary), Bibliogon shows a small warning above the Start button. You can still run a review; the feedback may be more limited than for prose.
63
+
64
+
### Structured output
65
+
66
+
Every review uses the same structure:
45
67
46
68
-**Summary** - one sentence about the chapter's content
47
69
-**Strengths** - what works well, with specific references
48
70
-**Suggestions** - concrete improvements with explanations
49
71
-**Overall** - a brief assessment
50
72
51
-
The review considers your book's genre and provides genre-appropriate feedback (e.g. pacing feedback for thrillers, clarity feedback for non-fiction).
73
+
The review considers your book's genre, language (all 8 supported UI languages), and the selected chapter type, so feedback is appropriate to the section (e.g. pacing feedback for thrillers, minimal tone notes on a dedication, compliance hints on copyright pages).
74
+
75
+
### Persistence + download
76
+
77
+
Every review is saved as a Markdown file under `uploads/{book_id}/reviews/` with a filename like `{review-id}-{chapter-slug}-{YYYY-MM-DD}.md`. A **Download report** button appears next to the result so you can save the file locally for a writing notebook, attach it to a commit, or keep a history per chapter. When a chapter is deleted, its review files are automatically cleaned up alongside the chapter content.
78
+
79
+
### Async progress
80
+
81
+
Large chapters can take 5-60 seconds. The review runs as a background job; the editor stays usable, and a rotating status message (in your book's language) is shown while the analysis runs. You can close the AI panel mid-review; when the review finishes, the result re-appears on reopen.
0 commit comments