feat: Phase 1 · 7/7 — the documents panel and the seed data #11

Merged
bjoern merged 1 commit from feat/p1-documents into main 2026-09-23 01:02:16 +02:00
Member

Slice 7 of Phase 1: the Documents tab in the workspace's side pane and the sample world for browser verification (docs/stories/documents.md, AGENTS.md; ADRs 0011, 0013, 0017, 0022). One commit, f50c9df.

What's in

  • Categories (DocumentsPanel, CategoryEditor): created from the suggestions row (the six SuggestedCategory.All entries, each offered until it exists) or as a blank "New category" opened to be named; renamed, described, default mode, placement (Header / Near end with a distance), reordered from their menu (Move up / Move down, edges disabled), deleted behind DeleteCategoryDialog — a Modal with its own footer: Move and delete to a chosen category, or Delete with documents; an empty category confirms plainly. A Never category and a Never document wear a Private badge; every mode and placement has a one-line explanation (ModeLabels, from ADR 0013's table) as the select's hint and the badges' titles.
  • Documents (DocumentEditor, TriggerChips): title, description, body, category (a move, not a save), mode override (ModeChoice select — "Inherit (Triggered)" spells the category's default; inherit stores null, never a mode), triggers as chips composed from Badge + Button + TextField (Enter or Add; normalised exactly as Document.SetTriggers does, so a second spelling collapses), priority as a number field, the provenance of the last edit ("Last edited by you" / "by a run (node · model)" / "…, then by you"), delete behind a ConfirmDialog. The list groups by category, in the store's order (priority, then title, ordinal), with a DebouncedSearchField over titles, descriptions and triggers that keeps every category header.
  • Auto-save per field: a Debounce on the injected clock, one journaled operation per pause, the whole profile every time (UpdateDocument / UpdateCategory have no partial writes); a discrete pick — mode, a chip, the placement — saves at once. Idle-only adoption with the trimmed-echo guard (the domain trims title/summary/body and tidies triggers), a phantom-edit guard (the same text saved again journals nothing), a blank title or an unreadable number refused under the field with the pause kept. Back saves first; Delete discards, never flushes against a row about to go.
  • The Active badge: TriggerScan (UseCases) builds ADR 0017's default window — the last 20,000 characters of the chapter on screen (paragraphs a blank line apart, scene breaks as ***) plus the header documents' bodies — and IsActive says Always → yes, Triggered → a trigger in the window, OnDemand / Never → never. TriggerMatch (Domain) is the one case-insensitive substring rule; Document.IsTriggeredBy delegates to it. The panel reads the chapter on screen from the workspace slice and recomputes only when that chapter or the view changes, so the badge follows a typing pause and a chapter switch with no extra query. The Phase 2 context node and its preview reuse the scanner; the budget cap and node exposure are theirs.
  • One Fluxor slice (Documents/): DocumentsState (keyed by story; Filtered, the open document/category), one action record per file (19), DocumentsReducers (every reducer guards on StoryId; a load landing closes an editor whose record vanished and clears its refusal; a saved category recomputes the effective mode of its inheriting documents; a saved document is re-sorted within its category), DocumentsEffects (typed saves patch, structural requests re-query explicitly, DomainChangesReceived reloads on Document / DocumentCategory once the panel has loaded — the registry pair AGENTS.md demands — and every answer dispatches the new UndoStatusChanged so the Undo button follows document edits; UndoTyping has no kind filter, so a document edit is what Ctrl+Z takes back).
  • Workspace integration: Documents is the first tab, History the second, Tabs Param="tab" deep-links both; the page's SaveIndicator covers the panel's dirtiness and its refusals (a refusal outranks a save in flight); FlushAsync — Ctrl+Z / Ctrl+Y, Save a version, Revert, Redo — flushes the open editor too. WorkspaceReducers.OnUndoStatusChanged is the one reducer added to the workspace slice.
  • The seed (SeedDevData, Program.cs): behind TSUMUGI_SEED_DEV_DATA=true, read through configuration like the other switches, run right after the migration inside its own scope, only into a database with no live story (a trashed-only one still seeds; a second start logs "Seed skipped"). It creates Mira's Winter — three chapters (summaries on the first two, none on the last), a scene break, paragraphs by the writer and two by a run under an explicit Run scope (no Phase 1 use case stamps run provenance), one of them edited afterwards — a category per default mode (Style, Author's note near the end, Characters, Lore, a custom on-demand Background, Notes to self) with documents whose triggers match chapter 1 (Mira), chapters 2–3 only (the sister, Ilse), or nowhere (Warden), one mode override (The Winter Pact, Always), two snapshots (Your edits before the run, the run's) with edits since — and The Lantern Road, archived. Everything through the real use cases, each under its own scope; no OpenRouter key. README's status line and AGENTS.md's status paragraph and seed section are updated to match.
  • Docs: documents.md boxes for categories, suggestions, delete-asks, documents, the list, provenance/active (the suggestions list now names the six the code ships; a note says what the Phase 1 badge scans), REQUIREMENTS §3's categories, documents and chapter-summaries boxes.

Deferred and why

  • Context preview (documents.md, REQUIREMENTS): Phase 2, with the context node — TriggerScan is written for it.
  • Backups: their own slice, the last of Phase 1 (AGENTS.md says so).
  • Run provenance on documents ("run · Updater"): displayed, never produced — UpdateDocument stamps Provenance.User, and Document.Update assigns the provenance rather than marking an edit, so "run, then you" cannot occur for documents until a run writes one (Phase 4). The seed leaves documents user-written; the display code and its tests cover the run shapes through the store.
  • Drag-to-reorder categories: the menu is the accessible baseline, as for chapters.
  • A Chip component in Kagaku.UI: composed here from primitives (ADR 0009); a shared component when a third feature needs chips.
  • A scoped gate serialising effects (PR #10's follow-up): unchanged.

Tests

project main this branch
Tsumugi.Domain.Tests 43 48
Tsumugi.UseCases.Tests 11 16
Tsumugi.BlazorAdapter.Tests 136 180
Tsumugi.Integration.Tests 104 112
GraphEditor (Vitest) 14 14 (untouched, not re-run)

New: TriggerMatchTests (4), TriggerScanTests (5), SeedDevDataTests (8: the world's contract, a category per mode with matching and non-matching triggers and one override, two snapshot kinds with edits since, no System row and every row labelled, the live-story guard, a trashed-only database seeding, the provider key unset, the host flag on and off through GatedAppFactory { Seed = true }), DocumentsReducersTests (8, incl. every one of the 19 actions dropped for a story the panel has left), DocumentsEffectsTests (6), DocumentsPanelTests (20, the whole story through the real pipeline over the fakes: suggestions, the grouped list with its marks and ordinal order, the filter, editing with the pause, the trimmed echo, the blank-title and bad-number refusals, chips, modes, the move, create/delete, delete-category both ways, reorder, the category editor with the distance, the badge following the chapter and a header body, another session's change mid-typing, a document deleted elsewhere, the flush on leaving), ModeLabelsTests (3), WorkspacePageTests +4 (the tab order, a document save refreshing the Undo button, Ctrl+Z flushing a document pause then undoing, a refused document save in the page's indicator; the History test now clicks its tab), WorkspaceReducersTests +1. Doubles: FakeDocumentStore (the EF store's boundary checks, the cascade, ordinal titles) wired into FakeWorld and AdapterTestContext.

Mutation probes (each disabled by hand, the named test fails, restored): the StoryId guard on OnDocumentSaved; the editor's echo guard; the DocumentCategory arm of the doorbell effect; IsActive for OnDemand; the header bodies in Window; the seed's live-story guard; the dialog's move target; Discard() before a delete; inherit → Always. A tenth — the fake sorting titles culture-aware — is inconclusive by construction: Directory.Build.props sets InvariantGlobalization, under which culture comparison is ordinal.

Coverage (coverlet, merged across test projects, paths normalised)

BlazorAdapter/Documents/**, WorkspacePage.razor, WorkspaceReducers.cs, UndoStatusChanged.cs, TriggerScan.cs, SeedDevData.cs, TriggerMatch.cs, Document.cs, Program.cs: 857 / 861 lines, 560 / 592 branches. Not covered, and why:

  • SeedDevData.cs 139–143 — Value<T>'s refusal and unknown-result arms: the seed's world is fixed, a refusal is a bug in the seed.
  • CategoryEditor.razor 144–146 — the toggle group's null value: it has no "all" option.
  • DocumentsPanel.razor 243–245 — Move's out-of-range guard: the menu disables the edges.
  • DocumentEditor.razor 164–182, CategoryEditor.razor 120–132, TriggerChips.razor 39 — the value ?? "" arms on ValueChanged; the design system never reports null.
  • CategoryEditor.razor 81 — the placement fallback for an unreadable distance while the stored placement is Header: the toggle saves NearEnd(0) at once, so the stored one is never Header by then.
  • DocumentsPanel.razor 37, 165; DocumentsReducers.cs 13, 140; WorkspacePage.razor 165–174; Program.cs 25, 37, 100 — one combination each of a null-coalescing fallback, the two-slice dirtiness ||, the comparer's equal-priority branch as coverlet counts a lambda, and configuration keys present vs absent.

Browser-verified (Release build, Chrome via Playwright, the seed, light and dark)

Sign in → the list shows Mira's Winter (the archived one behind the toggle) → open it: Documents is the active tab, six categories in seed order with their mode badge, placement and description, only Memory left in the suggestions row, Ideas Private; chapter 1 on screen: Voice, Steer, Mira, The Winter Pact Active, the rest not → chapter 2: The sister and Snow gain Active, back to 1 they lose it → "+ Memory" → its menu → Edit → rename "Memory & facts", Near end, distance 3 → Saved → Back shows "Near end · 3" → Characters "+" → "Untitled document" opens: title, description, body, Enter adds the trigger Ilse, " ilse " collapses into it, mode Never → Private, back to Inherit, priority 5, category → Lore → the row moves under Lore → filter "sister" → three rows (two by description, one by title), every header kept → Delete Background → the dialog → Move to Lore → The Valley under Lore → Delete Memory & facts (empty) → one plain button → the Undo button reads "Undo: User — deleted category 'Memory & facts' with its documents", Ctrl+Z brings it back, Ctrl+Y removes it → History lists the document operations with their labels → Revert to Your edits takes the run's paragraphs and every document change back → Redo returns them → a second tab: a rename in A reaches B's list, and B's body typed meanwhile survives A's save → dark mode: the list, the editor with chips → restart with the flag: "Seed skipped: a live story exists".

Follow-ups

  • Backups (the last Phase 1 slice); the context preview with the context node; Document.Update → AfterUserEdit() when runs write documents; a Kagaku.UI Chip if a third feature needs one.

🤖 Generated with Claude Code

Slice 7 of Phase 1: the Documents tab in the workspace's side pane and the sample world for browser verification (`docs/stories/documents.md`, `AGENTS.md`; ADRs 0011, 0013, 0017, 0022). One commit, `f50c9df`. ## What's in - **Categories** (`DocumentsPanel`, `CategoryEditor`): created from the suggestions row (the six `SuggestedCategory.All` entries, each offered until it exists) or as a blank "New category" opened to be named; renamed, described, default mode, placement (Header / Near end with a distance), reordered from their menu (Move up / Move down, edges disabled), deleted behind `DeleteCategoryDialog` — a `Modal` with its own footer: *Move and delete* to a chosen category, or *Delete with documents*; an empty category confirms plainly. A `Never` category and a `Never` document wear a **Private** badge; every mode and placement has a one-line explanation (`ModeLabels`, from ADR 0013's table) as the select's hint and the badges' titles. - **Documents** (`DocumentEditor`, `TriggerChips`): title, description, body, category (a move, not a save), mode override (`ModeChoice` select — "Inherit (Triggered)" spells the category's default; inherit stores `null`, never a mode), triggers as chips composed from `Badge` + `Button` + `TextField` (Enter or Add; normalised exactly as `Document.SetTriggers` does, so a second spelling collapses), priority as a number field, the provenance of the last edit ("Last edited by you" / "by a run (node · model)" / "…, then by you"), delete behind a `ConfirmDialog`. The list groups by category, in the store's order (priority, then title, ordinal), with a `DebouncedSearchField` over titles, descriptions and triggers that keeps every category header. - **Auto-save** per field: a `Debounce` on the injected clock, one journaled operation per pause, the whole profile every time (`UpdateDocument` / `UpdateCategory` have no partial writes); a discrete pick — mode, a chip, the placement — saves at once. Idle-only adoption with the trimmed-echo guard (the domain trims title/summary/body and tidies triggers), a phantom-edit guard (the same text saved again journals nothing), a blank title or an unreadable number refused under the field with the pause kept. Back saves first; Delete discards, never flushes against a row about to go. - **The Active badge**: `TriggerScan` (UseCases) builds ADR 0017's default window — the last 20,000 characters of the chapter on screen (paragraphs a blank line apart, scene breaks as `***`) plus the header documents' bodies — and `IsActive` says Always → yes, Triggered → a trigger in the window, OnDemand / Never → never. `TriggerMatch` (Domain) is the one case-insensitive substring rule; `Document.IsTriggeredBy` delegates to it. The panel reads the chapter on screen from the workspace slice and recomputes only when that chapter or the view changes, so the badge follows a typing pause and a chapter switch with no extra query. The Phase 2 context node and its preview reuse the scanner; the budget cap and node exposure are theirs. - **One Fluxor slice** (`Documents/`): `DocumentsState` (keyed by story; `Filtered`, the open document/category), one action record per file (19), `DocumentsReducers` (every reducer guards on `StoryId`; a load landing closes an editor whose record vanished and clears its refusal; a saved category recomputes the effective mode of its inheriting documents; a saved document is re-sorted within its category), `DocumentsEffects` (typed saves patch, structural requests re-query explicitly, `DomainChangesReceived` reloads on `Document` / `DocumentCategory` once the panel has loaded — the registry pair AGENTS.md demands — and every answer dispatches the new `UndoStatusChanged` so the Undo button follows document edits; `UndoTyping` has no kind filter, so a document edit *is* what Ctrl+Z takes back). - **Workspace integration**: Documents is the first tab, History the second, `Tabs Param="tab"` deep-links both; the page's `SaveIndicator` covers the panel's dirtiness and its refusals (a refusal outranks a save in flight); `FlushAsync` — Ctrl+Z / Ctrl+Y, Save a version, Revert, Redo — flushes the open editor too. `WorkspaceReducers.OnUndoStatusChanged` is the one reducer added to the workspace slice. - **The seed** (`SeedDevData`, `Program.cs`): behind `TSUMUGI_SEED_DEV_DATA=true`, read through configuration like the other switches, run right after the migration inside its own scope, only into a database with no live story (a trashed-only one still seeds; a second start logs "Seed skipped"). It creates *Mira's Winter* — three chapters (summaries on the first two, none on the last), a scene break, paragraphs by the writer and two by a run under an explicit Run scope (no Phase 1 use case stamps run provenance), one of them edited afterwards — a category per default mode (Style, Author's note near the end, Characters, Lore, a custom on-demand Background, Notes to self) with documents whose triggers match chapter 1 (Mira), chapters 2–3 only (the sister, Ilse), or nowhere (Warden), one mode override (The Winter Pact, Always), two snapshots (*Your edits* before the run, the run's) with edits since — and *The Lantern Road*, archived. Everything through the real use cases, each under its own scope; no OpenRouter key. `README`'s status line and `AGENTS.md`'s status paragraph and seed section are updated to match. - Docs: `documents.md` boxes for categories, suggestions, delete-asks, documents, the list, provenance/active (the suggestions list now names the six the code ships; a note says what the Phase 1 badge scans), REQUIREMENTS §3's categories, documents and chapter-summaries boxes. ## Deferred and why - **Context preview** (documents.md, REQUIREMENTS): Phase 2, with the `context` node — `TriggerScan` is written for it. - **Backups**: their own slice, the last of Phase 1 (AGENTS.md says so). - **Run provenance on documents** ("run · Updater"): displayed, never produced — `UpdateDocument` stamps `Provenance.User`, and `Document.Update` assigns the provenance rather than marking an edit, so "run, then you" cannot occur for documents until a run writes one (Phase 4). The seed leaves documents user-written; the display code and its tests cover the run shapes through the store. - **Drag-to-reorder categories**: the menu is the accessible baseline, as for chapters. - **A Chip component in Kagaku.UI**: composed here from primitives (ADR 0009); a shared component when a third feature needs chips. - **A scoped gate serialising effects** (PR #10's follow-up): unchanged. ## Tests | project | main | this branch | |---|---|---| | Tsumugi.Domain.Tests | 43 | **48** | | Tsumugi.UseCases.Tests | 11 | **16** | | Tsumugi.BlazorAdapter.Tests | 136 | **180** | | Tsumugi.Integration.Tests | 104 | **112** | | GraphEditor (Vitest) | 14 | 14 (untouched, not re-run) | New: `TriggerMatchTests` (4), `TriggerScanTests` (5), `SeedDevDataTests` (8: the world's contract, a category per mode with matching and non-matching triggers and one override, two snapshot kinds with edits since, no `System` row and every row labelled, the live-story guard, a trashed-only database seeding, the provider key unset, the host flag on and off through `GatedAppFactory { Seed = true }`), `DocumentsReducersTests` (8, incl. every one of the 19 actions dropped for a story the panel has left), `DocumentsEffectsTests` (6), `DocumentsPanelTests` (20, the whole story through the real pipeline over the fakes: suggestions, the grouped list with its marks and ordinal order, the filter, editing with the pause, the trimmed echo, the blank-title and bad-number refusals, chips, modes, the move, create/delete, delete-category both ways, reorder, the category editor with the distance, the badge following the chapter and a header body, another session's change mid-typing, a document deleted elsewhere, the flush on leaving), `ModeLabelsTests` (3), `WorkspacePageTests` +4 (the tab order, a document save refreshing the Undo button, Ctrl+Z flushing a document pause then undoing, a refused document save in the page's indicator; the History test now clicks its tab), `WorkspaceReducersTests` +1. Doubles: `FakeDocumentStore` (the EF store's boundary checks, the cascade, ordinal titles) wired into `FakeWorld` and `AdapterTestContext`. **Mutation probes** (each disabled by hand, the named test fails, restored): the `StoryId` guard on `OnDocumentSaved`; the editor's echo guard; the `DocumentCategory` arm of the doorbell effect; `IsActive` for OnDemand; the header bodies in `Window`; the seed's live-story guard; the dialog's move target; `Discard()` before a delete; inherit → Always. A tenth — the fake sorting titles culture-aware — is **inconclusive by construction**: `Directory.Build.props` sets `InvariantGlobalization`, under which culture comparison is ordinal. ## Coverage (coverlet, merged across test projects, paths normalised) `BlazorAdapter/Documents/**`, `WorkspacePage.razor`, `WorkspaceReducers.cs`, `UndoStatusChanged.cs`, `TriggerScan.cs`, `SeedDevData.cs`, `TriggerMatch.cs`, `Document.cs`, `Program.cs`: **857 / 861 lines, 560 / 592 branches**. Not covered, and why: - `SeedDevData.cs` 139–143 — `Value<T>`'s refusal and unknown-result arms: the seed's world is fixed, a refusal is a bug in the seed. - `CategoryEditor.razor` 144–146 — the toggle group's null value: it has no "all" option. - `DocumentsPanel.razor` 243–245 — `Move`'s out-of-range guard: the menu disables the edges. - `DocumentEditor.razor` 164–182, `CategoryEditor.razor` 120–132, `TriggerChips.razor` 39 — the `value ?? ""` arms on `ValueChanged`; the design system never reports null. - `CategoryEditor.razor` 81 — the placement fallback for an unreadable distance while the stored placement is Header: the toggle saves NearEnd(0) at once, so the stored one is never Header by then. - `DocumentsPanel.razor` 37, 165; `DocumentsReducers.cs` 13, 140; `WorkspacePage.razor` 165–174; `Program.cs` 25, 37, 100 — one combination each of a null-coalescing fallback, the two-slice dirtiness `||`, the comparer's equal-priority branch as coverlet counts a lambda, and configuration keys present vs absent. ## Browser-verified (Release build, Chrome via Playwright, the seed, light and dark) Sign in → the list shows *Mira's Winter* (the archived one behind the toggle) → open it: Documents is the active tab, six categories in seed order with their mode badge, placement and description, only *Memory* left in the suggestions row, *Ideas* Private; chapter 1 on screen: Voice, Steer, Mira, The Winter Pact **Active**, the rest not → chapter 2: The sister and Snow gain Active, back to 1 they lose it → "+ Memory" → its menu → Edit → rename "Memory & facts", Near end, distance 3 → Saved → Back shows "Near end · 3" → Characters "+" → "Untitled document" opens: title, description, body, Enter adds the trigger *Ilse*, " ilse " collapses into it, mode Never → Private, back to Inherit, priority 5, category → Lore → the row moves under Lore → filter "sister" → three rows (two by description, one by title), every header kept → Delete *Background* → the dialog → Move to Lore → *The Valley* under Lore → Delete *Memory & facts* (empty) → one plain button → the Undo button reads "Undo: User — deleted category 'Memory & facts' with its documents", Ctrl+Z brings it back, Ctrl+Y removes it → History lists the document operations with their labels → Revert to *Your edits* takes the run's paragraphs and every document change back → Redo returns them → a second tab: a rename in A reaches B's list, and B's body typed meanwhile survives A's save → dark mode: the list, the editor with chips → restart with the flag: "Seed skipped: a live story exists". ## Follow-ups - Backups (the last Phase 1 slice); the context preview with the `context` node; `Document.Update` → `AfterUserEdit()` when runs write documents; a Kagaku.UI Chip if a third feature needs one. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: the documents panel and the seed data (Phase 1 · 7/7)
All checks were successful
CI / build (pull_request) Successful in 29s
CI / test (pull_request) Successful in 42s
f50c9dfc96
The Documents tab in the workspace's side pane (stories/documents.md; ADRs 0011, 0013, 0017,
0022): categories with a default mode, a placement and a description — created from the
suggestions row or blank, renamed, reordered, deleted with the move-or-delete question for their
documents — and documents with a title, a description, a body, a category, a mode override,
triggers as chips and a priority, listed by category with a filter over titles, descriptions and
triggers, opened in a drill-in editor, each showing who last edited it and whether the default
scan window would activate it right now. Every field auto-saves once typing pauses; a discrete
pick saves at once; every save is the whole profile as one journaled operation, and the editors
adopt the stored record only while idle, so a save's normalised echo or another session's edit
never rewrites a field under the writer. Never is marked Private, modes and placements are
explained in a line each.

One Fluxor slice over the documents use cases: every answer names its story, a typed save patches
the slice in place, a structural request re-queries, a document or category change anywhere
reloads the panel, and each answer refreshes what Ctrl+Z would take back (UndoStatusChanged),
since a document edit is the writer's operation too. The page's save indicator, its flush, Ctrl+Z
and History's actions cover the panel like the manuscript. Documents is the first tab; the tabs
deep-link with ?tab=.

TriggerScan (UseCases) is the default scan window ADR 0017 specifies — the chapter on screen's
last 20,000 characters plus the header documents — shared with the Phase 2 context node;
TriggerMatch (Domain) is the one substring rule.

The seed (AGENTS.md): SeedDevData behind TSUMUGI_SEED_DEV_DATA, run at startup into a database
with no live story — one story with three chapters, scene breaks, summaries, paragraphs by the
writer and by a run, one edited afterwards, a category per mode with documents whose triggers do
and do not match the prose, one mode override, two snapshots with edits since, and one archived
story. Through the real use cases, each under its own scope; the run's paragraphs under an
explicit Run scope, since no Phase 1 use case stamps run provenance.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

🔄 Auto-updating coverage report — this comment is regenerated on every push, so the numbers below always reflect the commit shown here, not the branch tip.

Commit: f50c9df · Generated: 2026-09-22 22:40:45 UTC · Revision: #1

Summary

Summary
Generated on: 09/22/2026 - 22:40:45
Coverage date: 09/22/2026 - 22:40:37 - 09/22/2026 - 22:40:43
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 239
Files: 232
Line coverage: 97.8% (5208 of 5324)
Covered lines: 5208
Uncovered lines: 116
Coverable lines: 5324
Total lines: 11548
Branch coverage: 93.3% (1547 of 1658)
Covered branches: 1547
Total branches: 1658
Method coverage: Feature is only available for sponsors

Coverage

Tsumugi.BlazorAdapter - 97.1%
Name Line Branch
Tsumugi.BlazorAdapter 97.1% 94.2%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 100%
Tsumugi.BlazorAdapter.Documents.CategoryCreated 100%
Tsumugi.BlazorAdapter.Documents.CategoryEditor 98.7% 90.7%
Tsumugi.BlazorAdapter.Documents.CategorySaved 100%
Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog 100% 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentCreated 100%
Tsumugi.BlazorAdapter.Documents.DocumentEditor 100% 93.9%
Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentSaved 100%
Tsumugi.BlazorAdapter.Documents.DocumentsEffects 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsLoaded 100%
Tsumugi.BlazorAdapter.Documents.DocumentsPanel 98.9% 94.7%
Tsumugi.BlazorAdapter.Documents.DocumentsReducers 100% 97.5%
Tsumugi.BlazorAdapter.Documents.DocumentsState 100% 100%
Tsumugi.BlazorAdapter.Documents.LoadDocuments 100%
Tsumugi.BlazorAdapter.Documents.ModeLabels 100% 100%
Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested 100%
Tsumugi.BlazorAdapter.Documents.OpenCategory 100%
Tsumugi.BlazorAdapter.Documents.OpenDocument 100%
Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested 100%
Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter 100%
Tsumugi.BlazorAdapter.Documents.TriggerChips 100% 91.6%
Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested 100%
Tsumugi.BlazorAdapter.Graphs.GraphCanvas 37.5% 25%
Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage 20% 25%
Tsumugi.BlazorAdapter.Notifications.DomainChangedBridge 94.4% 83.3%
Tsumugi.BlazorAdapter.Notifications.DomainChangesReceived 75% 100%
Tsumugi.BlazorAdapter.Runs.RunChangedBridge 97.1% 90.6%
Tsumugi.BlazorAdapter.Runs.RunChangesReceived 100%
Tsumugi.BlazorAdapter.Runs.RunTextReceived 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsState 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsLoaded 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaved 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaveFailed 100%
Tsumugi.BlazorAdapter.Settings.ProviderSection 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsState 100%
Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested 100%
Tsumugi.BlazorAdapter.Stories.CreateStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.PurgeStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.RestoreStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.SetShowArchived 100%
Tsumugi.BlazorAdapter.Stories.SetStoriesFilter 100%
Tsumugi.BlazorAdapter.Stories.SetStoryArchivedRequested 100%
Tsumugi.BlazorAdapter.Stories.StoriesEffects 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesLoaded 100%
Tsumugi.BlazorAdapter.Stories.StoriesPage 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesReducers 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesState 100% 100%
Tsumugi.BlazorAdapter.Stories.StoryActionFailed 100%
Tsumugi.BlazorAdapter.Stories.StoryCreated 100%
Tsumugi.BlazorAdapter.Stories.StoryCreateFailed 100%
Tsumugi.BlazorAdapter.Stories.StoryTrashPage 100% 100%
Tsumugi.BlazorAdapter.Stories.TrashStoryRequested 100%
Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed 100%
Tsumugi.BlazorAdapter.Workspace.CaretRequest 100%
Tsumugi.BlazorAdapter.Workspace.ChapterBar 100% 91.6%
Tsumugi.BlazorAdapter.Workspace.ChapterHeader 100% 91.6%
Tsumugi.BlazorAdapter.Workspace.ChapterSaved 100%
Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.DeleteSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.EditSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.HistoryEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.HistoryLoaded 100%
Tsumugi.BlazorAdapter.Workspace.HistoryPanel 100% 100%
Tsumugi.BlazorAdapter.Workspace.InsertSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.LoadHistory 100%
Tsumugi.BlazorAdapter.Workspace.LoadWorkspace 100%
Tsumugi.BlazorAdapter.Workspace.ManuscriptColumn 94.5% 93.3%
Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.MergeSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested 100%
Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested 100%
Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested 100%
Tsumugi.BlazorAdapter.Workspace.SegmentRow 100% 96.8%
Tsumugi.BlazorAdapter.Workspace.SegmentSaved 100%
Tsumugi.BlazorAdapter.Workspace.SelectChapter 100%
Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested 100%
Tsumugi.BlazorAdapter.Workspace.SplitSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.StoryDetails 100% 92.3%
Tsumugi.BlazorAdapter.Workspace.StorySaved 100%
Tsumugi.BlazorAdapter.Workspace.UndoRequested 100%
Tsumugi.BlazorAdapter.Workspace.UndoStatusChanged 100%
Tsumugi.BlazorAdapter.Workspace.UpdateStoryRequested 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceActionFailed 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceLoaded 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceNotFound 100%
Tsumugi.BlazorAdapter.Workspace.WorkspacePage 100% 87.1%
Tsumugi.BlazorAdapter.Workspace.WorkspaceReducers 100% 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceState 100% 83.3%
Tsumugi.Domain - 99.4%
Name Line Branch
Tsumugi.Domain 99.4% 96.5%
Tsumugi.Domain.Documents.Document 100% 100%
Tsumugi.Domain.Documents.DocumentCategory 100%
Tsumugi.Domain.Documents.DocumentPlacement 100% 100%
Tsumugi.Domain.Documents.TriggerMatch 100% 100%
Tsumugi.Domain.Journal.ChangeLogEntry 97.8% 75%
Tsumugi.Domain.Journal.StorySnapshot 95.4% 83.3%
Tsumugi.Domain.Settings.AppSetting 100%
Tsumugi.Domain.Stories.Chapter 100% 100%
Tsumugi.Domain.Stories.Provenance 100% 100%
Tsumugi.Domain.Stories.Segment 100% 100%
Tsumugi.Domain.Stories.Story 100% 100%
Tsumugi.Domain.Text 100% 100%
Tsumugi.Infrastructure - 97.9%
Name Line Branch
Tsumugi.Infrastructure 97.9% 91.9%
Tsumugi.Infrastructure.DependencyInjection 100%
Tsumugi.Infrastructure.Journal.EfChangeJournal 100%
Tsumugi.Infrastructure.Journal.EfSnapshotStore 100%
Tsumugi.Infrastructure.Journal.EfUndoStore 94.4% 82.1%
Tsumugi.Infrastructure.Journal.ModelExtensions 100%
Tsumugi.Infrastructure.Journal.OperationContext 100% 100%
Tsumugi.Infrastructure.Journal.PropertyBag 100% 95.4%
Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus 100% 100%
Tsumugi.Infrastructure.Persistence.Configurations.AppSettingConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.ChangeLogEntryConfigurati
on
100%
Tsumugi.Infrastructure.Persistence.Configurations.ChapterConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.DocumentCategoryConfigura
tion
100%
Tsumugi.Infrastructure.Persistence.Configurations.DocumentConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.JsonColumnMapper 100%
Tsumugi.Infrastructure.Persistence.Configurations.SegmentConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio
n
100%
Tsumugi.Infrastructure.Persistence.Converters.DocumentPlacementConverter 100% 100%
Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter 100% 90%
Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter 100%
Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings 99.2%
Tsumugi.Infrastructure.Persistence.Migrations.AddJournal 98.8%
Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain 97.3%
Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot 100%
Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore 97.6% 90%
Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore 95% 84.6%
Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore 98.3% 92.8%
Tsumugi.Infrastructure.Persistence.TsumugiDbContext 83.5% 93.1%
Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory 0%
Tsumugi.Infrastructure.Runs.InProcessRunEventBus 100% 100%
Tsumugi.Infrastructure.Settings.EfAppSettingsStore 100% 100%
Tsumugi.Kernel - 90.9%
Name Line Branch
Tsumugi.Kernel 90.9% 75%
Tsumugi.Kernel.Err`1 100%
Tsumugi.Kernel.Ok`1 100%
Tsumugi.Kernel.Result`1 88.8% 75%
Tsumugi.Server - 100%
Name Line Branch
Tsumugi.Server 100% 75%
Program 100% 83.3%
Tsumugi.Server.Components.App 100%
Tsumugi.Server.Components.Layout.AppHeader 100% 50%
Tsumugi.Server.Components.Layout.MainLayout 100%
Tsumugi.Server.Components.Pages.Gate 100% 100%
Tsumugi.Server.Security.AccessGate 100% 66.6%
Tsumugi.Server.Security.AccessSecret 100% 50%
Tsumugi.Server.VolumeStartupValidator 100% 100%
Tsumugi.UseCases - 97.9%
Name Line Branch
Tsumugi.UseCases 97.9% 93.9%
Tsumugi.UseCases.Chapters.ChapterDto 100%
Tsumugi.UseCases.Chapters.CreateChapter 100% 100%
Tsumugi.UseCases.Chapters.DeleteChapter 100% 100%
Tsumugi.UseCases.Chapters.GetManuscript 100% 100%
Tsumugi.UseCases.Chapters.ManuscriptChapter 100%
Tsumugi.UseCases.Chapters.ManuscriptView 100%
Tsumugi.UseCases.Chapters.MergeChapters 100% 100%
Tsumugi.UseCases.Chapters.RenameChapter 100% 100%
Tsumugi.UseCases.Chapters.ReorderChapters 100% 100%
Tsumugi.UseCases.Chapters.SetChapterSummary 100% 75%
Tsumugi.UseCases.DependencyInjection 100%
Tsumugi.UseCases.Diagnostics.SeedDevData 97.5% 75%
Tsumugi.UseCases.Documents.CreateCategory 100% 100%
Tsumugi.UseCases.Documents.CreateDocument 100% 100%
Tsumugi.UseCases.Documents.DeleteCategory 100% 100%
Tsumugi.UseCases.Documents.DeleteDocument 100% 100%
Tsumugi.UseCases.Documents.DocumentCategoryDto 100%
Tsumugi.UseCases.Documents.DocumentCategoryProfile 100%
Tsumugi.UseCases.Documents.DocumentDto 100%
Tsumugi.UseCases.Documents.DocumentProfile 100%
Tsumugi.UseCases.Documents.DocumentsCategory 100%
Tsumugi.UseCases.Documents.DocumentsView 100%
Tsumugi.UseCases.Documents.GetDocuments 100% 100%
Tsumugi.UseCases.Documents.MoveDocuments 100% 100%
Tsumugi.UseCases.Documents.ReorderCategories 100% 100%
Tsumugi.UseCases.Documents.SuggestedCategory 100%
Tsumugi.UseCases.Documents.TriggerScan 100% 100%
Tsumugi.UseCases.Documents.UpdateCategory 100% 100%
Tsumugi.UseCases.Documents.UpdateDocument 100% 83.3%
Tsumugi.UseCases.Journal.GetStoryHistory 100% 100%
Tsumugi.UseCases.Journal.GetUndoStatus 100% 100%
Tsumugi.UseCases.Journal.HistoryOperation 66.6%
Tsumugi.UseCases.Journal.HistorySnapshot 87.5%
Tsumugi.UseCases.Journal.OperationGrouping 100% 100%
Tsumugi.UseCases.Journal.RedoRevert 100% 100%
Tsumugi.UseCases.Journal.RedoRun 75% 57.1%
Tsumugi.UseCases.Journal.RedoTyping 100% 100%
Tsumugi.UseCases.Journal.RevertConflict 100%
Tsumugi.UseCases.Journal.RevertOutcome 100%
Tsumugi.UseCases.Journal.RevertRun 96.5% 77.7%
Tsumugi.UseCases.Journal.RevertToSnapshot 92.3% 83.3%
Tsumugi.UseCases.Journal.StoryHistory 100%
Tsumugi.UseCases.Journal.TakeSnapshot 100% 87.5%
Tsumugi.UseCases.Journal.UndoStatus 100%
Tsumugi.UseCases.Journal.UndoTyping 100% 100%
Tsumugi.UseCases.Journal.UserOperation 100%
Tsumugi.UseCases.Notifications.DomainChanged 100%
Tsumugi.UseCases.Ordering 100% 100%
Tsumugi.UseCases.Runs.RunEvent 100%
Tsumugi.UseCases.Segments.DeleteSegments 100% 100%
Tsumugi.UseCases.Segments.EditSegment 100% 83.3%
Tsumugi.UseCases.Segments.InsertSegment 100% 100%
Tsumugi.UseCases.Segments.MergeSegmentWithPrevious 100% 100%
Tsumugi.UseCases.Segments.MoveSegments 100% 100%
Tsumugi.UseCases.Segments.ReorderSegments 100% 100%
Tsumugi.UseCases.Segments.SegmentDto 100%
Tsumugi.UseCases.Segments.SplitSegment 100% 100%
Tsumugi.UseCases.Settings.GetLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.GetProviderSettings 100%
Tsumugi.UseCases.Settings.LlmDefaults 100%
Tsumugi.UseCases.Settings.ProviderSettingsView 100%
Tsumugi.UseCases.Settings.ReasoningEfforts 100% 100%
Tsumugi.UseCases.Settings.RemoveOpenRouterKey 100%
Tsumugi.UseCases.Settings.SaveLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.SaveOpenRouterKey 100% 100%
Tsumugi.UseCases.Stories.CreateStory 100% 100%
Tsumugi.UseCases.Stories.GetStory 100% 100%
Tsumugi.UseCases.Stories.ListStories 100% 75%
Tsumugi.UseCases.Stories.PurgeStory 100% 100%
Tsumugi.UseCases.Stories.RestoreStory 100% 100%
Tsumugi.UseCases.Stories.SetActiveGraph 100% 100%
Tsumugi.UseCases.Stories.SetStoryArchived 100% 100%
Tsumugi.UseCases.Stories.StoryDetail 90%
Tsumugi.UseCases.Stories.StoryListItem 81.8%
Tsumugi.UseCases.Stories.StoryProfile 100%
Tsumugi.UseCases.Stories.TrashStory 100% 100%
Tsumugi.UseCases.Stories.UpdateStory 100% 100%
<!-- coverage-comment --> > 🔄 **Auto-updating coverage report** — this comment is regenerated on every push, so the numbers below always reflect the commit shown here, not the branch tip. > > **Commit:** `f50c9df` · **Generated:** 2026-09-22 22:40:45 UTC · **Revision:** #1 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/22/2026 - 22:40:45 | | Coverage date: | 09/22/2026 - 22:40:37 - 09/22/2026 - 22:40:43 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 239 | | Files: | 232 | | **Line coverage:** | 97.8% (5208 of 5324) | | Covered lines: | 5208 | | Uncovered lines: | 116 | | Coverable lines: | 5324 | | Total lines: | 11548 | | **Branch coverage:** | 93.3% (1547 of 1658) | | Covered branches: | 1547 | | Total branches: | 1658 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Tsumugi.BlazorAdapter - 97.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**97.1%**|**94.2%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryCreated|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryEditor|98.7%|90.7%| |Tsumugi.BlazorAdapter.Documents.CategorySaved|100%|| |Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog|100%|100%| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentCreated|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentEditor|100%|93.9%| |Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentSaved|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsEffects|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsLoaded|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsPanel|98.9%|94.7%| |Tsumugi.BlazorAdapter.Documents.DocumentsReducers|100%|97.5%| |Tsumugi.BlazorAdapter.Documents.DocumentsState|100%|100%| |Tsumugi.BlazorAdapter.Documents.LoadDocuments|100%|| |Tsumugi.BlazorAdapter.Documents.ModeLabels|100%|100%| |Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested|100%|| |Tsumugi.BlazorAdapter.Documents.OpenCategory|100%|| |Tsumugi.BlazorAdapter.Documents.OpenDocument|100%|| |Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested|100%|| |Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter|100%|| |Tsumugi.BlazorAdapter.Documents.TriggerChips|100%|91.6%| |Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Graphs.GraphCanvas|37.5%|25%| |Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage|20%|25%| |Tsumugi.BlazorAdapter.Notifications.DomainChangedBridge|94.4%|83.3%| |Tsumugi.BlazorAdapter.Notifications.DomainChangesReceived|75%|100%| |Tsumugi.BlazorAdapter.Runs.RunChangedBridge|97.1%|90.6%| |Tsumugi.BlazorAdapter.Runs.RunChangesReceived|100%|| |Tsumugi.BlazorAdapter.Runs.RunTextReceived|100%|| |Tsumugi.BlazorAdapter.Settings.DefaultsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaved|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaveFailed|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested|100%|| |Tsumugi.BlazorAdapter.Stories.CreateStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.PurgeStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.RestoreStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.SetShowArchived|100%|| |Tsumugi.BlazorAdapter.Stories.SetStoriesFilter|100%|| |Tsumugi.BlazorAdapter.Stories.SetStoryArchivedRequested|100%|| |Tsumugi.BlazorAdapter.Stories.StoriesEffects|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesLoaded|100%|| |Tsumugi.BlazorAdapter.Stories.StoriesPage|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesReducers|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesState|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoryActionFailed|100%|| |Tsumugi.BlazorAdapter.Stories.StoryCreated|100%|| |Tsumugi.BlazorAdapter.Stories.StoryCreateFailed|100%|| |Tsumugi.BlazorAdapter.Stories.StoryTrashPage|100%|100%| |Tsumugi.BlazorAdapter.Stories.TrashStoryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Workspace.CaretRequest|100%|| |Tsumugi.BlazorAdapter.Workspace.ChapterBar|100%|91.6%| |Tsumugi.BlazorAdapter.Workspace.ChapterHeader|100%|91.6%| |Tsumugi.BlazorAdapter.Workspace.ChapterSaved|100%|| |Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.DeleteSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.EditSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.HistoryLoaded|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryPanel|100%|100%| |Tsumugi.BlazorAdapter.Workspace.InsertSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadHistory|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadWorkspace|100%|| |Tsumugi.BlazorAdapter.Workspace.ManuscriptColumn|94.5%|93.3%| |Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.MergeSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SegmentRow|100%|96.8%| |Tsumugi.BlazorAdapter.Workspace.SegmentSaved|100%|| |Tsumugi.BlazorAdapter.Workspace.SelectChapter|100%|| |Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SplitSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.StoryDetails|100%|92.3%| |Tsumugi.BlazorAdapter.Workspace.StorySaved|100%|| |Tsumugi.BlazorAdapter.Workspace.UndoRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.UndoStatusChanged|100%|| |Tsumugi.BlazorAdapter.Workspace.UpdateStoryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspaceActionFailed|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspaceLoaded|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspaceNotFound|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspacePage|100%|87.1%| |Tsumugi.BlazorAdapter.Workspace.WorkspaceReducers|100%|100%| |Tsumugi.BlazorAdapter.Workspace.WorkspaceState|100%|83.3%| </details> <details><summary>Tsumugi.Domain - 99.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**99.4%**|**96.5%**| |Tsumugi.Domain.Documents.Document|100%|100%| |Tsumugi.Domain.Documents.DocumentCategory|100%|| |Tsumugi.Domain.Documents.DocumentPlacement|100%|100%| |Tsumugi.Domain.Documents.TriggerMatch|100%|100%| |Tsumugi.Domain.Journal.ChangeLogEntry|97.8%|75%| |Tsumugi.Domain.Journal.StorySnapshot|95.4%|83.3%| |Tsumugi.Domain.Settings.AppSetting|100%|| |Tsumugi.Domain.Stories.Chapter|100%|100%| |Tsumugi.Domain.Stories.Provenance|100%|100%| |Tsumugi.Domain.Stories.Segment|100%|100%| |Tsumugi.Domain.Stories.Story|100%|100%| |Tsumugi.Domain.Text|100%|100%| </details> <details><summary>Tsumugi.Infrastructure - 97.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**97.9%**|**91.9%**| |Tsumugi.Infrastructure.DependencyInjection|100%|| |Tsumugi.Infrastructure.Journal.EfChangeJournal|100%|| |Tsumugi.Infrastructure.Journal.EfSnapshotStore|100%|| |Tsumugi.Infrastructure.Journal.EfUndoStore|94.4%|82.1%| |Tsumugi.Infrastructure.Journal.ModelExtensions|100%|| |Tsumugi.Infrastructure.Journal.OperationContext|100%|100%| |Tsumugi.Infrastructure.Journal.PropertyBag|100%|95.4%| |Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus|100%|100%| |Tsumugi.Infrastructure.Persistence.Configurations.AppSettingConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.ChangeLogEntryConfigurati<br/>on|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.ChapterConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.DocumentCategoryConfigura<br/>tion|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.DocumentConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.JsonColumnMapper|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.SegmentConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio<br/>n|100%|| |Tsumugi.Infrastructure.Persistence.Converters.DocumentPlacementConverter|100%|100%| |Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter|100%|90%| |Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings|99.2%|| |Tsumugi.Infrastructure.Persistence.Migrations.AddJournal|98.8%|| |Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain|97.3%|| |Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot|100%|| |Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore|97.6%|90%| |Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore|95%|84.6%| |Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore|98.3%|92.8%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContext|83.5%|93.1%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory|0%|| |Tsumugi.Infrastructure.Runs.InProcessRunEventBus|100%|100%| |Tsumugi.Infrastructure.Settings.EfAppSettingsStore|100%|100%| </details> <details><summary>Tsumugi.Kernel - 90.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Kernel**|**90.9%**|**75%**| |Tsumugi.Kernel.Err`1|100%|| |Tsumugi.Kernel.Ok`1|100%|| |Tsumugi.Kernel.Result`1|88.8%|75%| </details> <details><summary>Tsumugi.Server - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Server**|**100%**|**75%**| |Program|100%|83.3%| |Tsumugi.Server.Components.App|100%|| |Tsumugi.Server.Components.Layout.AppHeader|100%|50%| |Tsumugi.Server.Components.Layout.MainLayout|100%|| |Tsumugi.Server.Components.Pages.Gate|100%|100%| |Tsumugi.Server.Security.AccessGate|100%|66.6%| |Tsumugi.Server.Security.AccessSecret|100%|50%| |Tsumugi.Server.VolumeStartupValidator|100%|100%| </details> <details><summary>Tsumugi.UseCases - 97.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**97.9%**|**93.9%**| |Tsumugi.UseCases.Chapters.ChapterDto|100%|| |Tsumugi.UseCases.Chapters.CreateChapter|100%|100%| |Tsumugi.UseCases.Chapters.DeleteChapter|100%|100%| |Tsumugi.UseCases.Chapters.GetManuscript|100%|100%| |Tsumugi.UseCases.Chapters.ManuscriptChapter|100%|| |Tsumugi.UseCases.Chapters.ManuscriptView|100%|| |Tsumugi.UseCases.Chapters.MergeChapters|100%|100%| |Tsumugi.UseCases.Chapters.RenameChapter|100%|100%| |Tsumugi.UseCases.Chapters.ReorderChapters|100%|100%| |Tsumugi.UseCases.Chapters.SetChapterSummary|100%|75%| |Tsumugi.UseCases.DependencyInjection|100%|| |Tsumugi.UseCases.Diagnostics.SeedDevData|97.5%|75%| |Tsumugi.UseCases.Documents.CreateCategory|100%|100%| |Tsumugi.UseCases.Documents.CreateDocument|100%|100%| |Tsumugi.UseCases.Documents.DeleteCategory|100%|100%| |Tsumugi.UseCases.Documents.DeleteDocument|100%|100%| |Tsumugi.UseCases.Documents.DocumentCategoryDto|100%|| |Tsumugi.UseCases.Documents.DocumentCategoryProfile|100%|| |Tsumugi.UseCases.Documents.DocumentDto|100%|| |Tsumugi.UseCases.Documents.DocumentProfile|100%|| |Tsumugi.UseCases.Documents.DocumentsCategory|100%|| |Tsumugi.UseCases.Documents.DocumentsView|100%|| |Tsumugi.UseCases.Documents.GetDocuments|100%|100%| |Tsumugi.UseCases.Documents.MoveDocuments|100%|100%| |Tsumugi.UseCases.Documents.ReorderCategories|100%|100%| |Tsumugi.UseCases.Documents.SuggestedCategory|100%|| |Tsumugi.UseCases.Documents.TriggerScan|100%|100%| |Tsumugi.UseCases.Documents.UpdateCategory|100%|100%| |Tsumugi.UseCases.Documents.UpdateDocument|100%|83.3%| |Tsumugi.UseCases.Journal.GetStoryHistory|100%|100%| |Tsumugi.UseCases.Journal.GetUndoStatus|100%|100%| |Tsumugi.UseCases.Journal.HistoryOperation|66.6%|| |Tsumugi.UseCases.Journal.HistorySnapshot|87.5%|| |Tsumugi.UseCases.Journal.OperationGrouping|100%|100%| |Tsumugi.UseCases.Journal.RedoRevert|100%|100%| |Tsumugi.UseCases.Journal.RedoRun|75%|57.1%| |Tsumugi.UseCases.Journal.RedoTyping|100%|100%| |Tsumugi.UseCases.Journal.RevertConflict|100%|| |Tsumugi.UseCases.Journal.RevertOutcome|100%|| |Tsumugi.UseCases.Journal.RevertRun|96.5%|77.7%| |Tsumugi.UseCases.Journal.RevertToSnapshot|92.3%|83.3%| |Tsumugi.UseCases.Journal.StoryHistory|100%|| |Tsumugi.UseCases.Journal.TakeSnapshot|100%|87.5%| |Tsumugi.UseCases.Journal.UndoStatus|100%|| |Tsumugi.UseCases.Journal.UndoTyping|100%|100%| |Tsumugi.UseCases.Journal.UserOperation|100%|| |Tsumugi.UseCases.Notifications.DomainChanged|100%|| |Tsumugi.UseCases.Ordering|100%|100%| |Tsumugi.UseCases.Runs.RunEvent|100%|| |Tsumugi.UseCases.Segments.DeleteSegments|100%|100%| |Tsumugi.UseCases.Segments.EditSegment|100%|83.3%| |Tsumugi.UseCases.Segments.InsertSegment|100%|100%| |Tsumugi.UseCases.Segments.MergeSegmentWithPrevious|100%|100%| |Tsumugi.UseCases.Segments.MoveSegments|100%|100%| |Tsumugi.UseCases.Segments.ReorderSegments|100%|100%| |Tsumugi.UseCases.Segments.SegmentDto|100%|| |Tsumugi.UseCases.Segments.SplitSegment|100%|100%| |Tsumugi.UseCases.Settings.GetLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.GetProviderSettings|100%|| |Tsumugi.UseCases.Settings.LlmDefaults|100%|| |Tsumugi.UseCases.Settings.ProviderSettingsView|100%|| |Tsumugi.UseCases.Settings.ReasoningEfforts|100%|100%| |Tsumugi.UseCases.Settings.RemoveOpenRouterKey|100%|| |Tsumugi.UseCases.Settings.SaveLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.SaveOpenRouterKey|100%|100%| |Tsumugi.UseCases.Stories.CreateStory|100%|100%| |Tsumugi.UseCases.Stories.GetStory|100%|100%| |Tsumugi.UseCases.Stories.ListStories|100%|75%| |Tsumugi.UseCases.Stories.PurgeStory|100%|100%| |Tsumugi.UseCases.Stories.RestoreStory|100%|100%| |Tsumugi.UseCases.Stories.SetActiveGraph|100%|100%| |Tsumugi.UseCases.Stories.SetStoryArchived|100%|100%| |Tsumugi.UseCases.Stories.StoryDetail|90%|| |Tsumugi.UseCases.Stories.StoryListItem|81.8%|| |Tsumugi.UseCases.Stories.StoryProfile|100%|| |Tsumugi.UseCases.Stories.TrashStory|100%|100%| |Tsumugi.UseCases.Stories.UpdateStory|100%|100%| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my~ The last Phase 1 slice, and what a weave it is! Fifty-nine files, a whole Fluxor slice with nineteen actions, a scanner shared with Phase 2's context node, and a little sample world with two sisters and a broken pact… I read every line of the diff and then every changed file whole, and I could not put it down~ ♡

Verdict: ✅ Looks good to me~

Verified by me, mechanically (CI has no run for f50c9df yet — no bot comment, no status — so I ran everything locally):

  • Build: 0 warnings / 0 errors under TreatWarningsAsErrors (after initializing both submodules — the OpenRouter.Net pin fd43f04 is required, MSB9008 otherwise).
  • Full suite: 356/356 — Domain 48 + UseCases 16 + Adapter 180 + Integration 112 — exactly the PR body's arithmetic, main's 43/11/136/104 plus the claimed increments.
  • Mutation probes, 10 run, each restored and rebuilt (hash-checked — I hit your old friend the stale-binary false-green twice and refused to believe it both times ♡):
    1. Seed's live-story guard (return false; → ;) → Skips_a_database_with_a_live_story… red. (Full removal doesn't compile — CS9113 unread stories — the guard is load-bearing twice over~)
    2. IsActive OnDemand arm flipped to trigger-match → Always_is_active… red.
    3. Window drops header bodies → 3 TriggerScan tests red.
    4. OnDocumentSaved StoryId guard removed → Every_answer_for_a_story_the_panel_has_left… red.
    5. Editor adopts every echo → A_trimmed_echo… red.
    6. Delete() flushes instead of discarding → the doomed-row test red — the pause really is kept from writing against a row about to go.
    7. Danger button leaks _target → green, and correctly so: the shipped button hardcodes null; my mutation only changes behavior on an interleaving the dialog's own flow makes unreachable-with-a-target. The move arm itself is pinned (Move and delete asserts the Valley lands under Lore).
    8. Doorbell drops the DocumentCategory arm → A_loaded_panel_reloads… red — the AGENTS.md registry pair is honored.
    9. OnLoaded keeps vanished editors open → A_load_landing_closes… red.
    10. Mode(ModeChoice.Inherit) returns a mode → two tests red — inherit stores null, never the category's default, exactly as ADR 0013 says.

Sibling parity, traced not assumed: the doorbell effect is line-for-line the ManuscriptEffects/HistoryEffects shape (View is not null + kind check, registry constants already declared); UndoStatusChanged follows the workspace slice's StoryId-guard rule; Debounce disposal in Dispose() with Discard() before delete matches the Kagaku.UI contract; the reducer's ordinal ByPriorityThenTitle matches both the EF store's SQLite collation and the fake's StringComparer.Ordinal — no culture seam under InvariantGlobalization. The seed drives only real use cases, each under its own scope, with the one direct port write under an explicit Run scope — and the journals-never-as-System test pins it.

💡 Little ideas (non-blocking)~

  1. DocumentsPanel.razor + DocumentEditor.razor — ProvenanceIcon/ProvenanceTitle vs ProvenanceIcon/ProvenanceText are the same two switches twice (10 lines each). Both copies are test-pinned today, so nothing can drift silently — but when Phase 4 makes runs write documents and the wording grows, extract a ProvenanceLabels beside ModeLabels (the codebase already has the precedent).
  2. DocumentEditor.Matches / CategoryEditor.Matches — the echo guard compares the priority/distance as canonical strings, so a non-canonical-but-valid spelling (+5, 05) parses, saves correctly, then fails Matches on the echo and the field rewrites to "5" under the caret for one frame. No wrong data ever lands — comparing the parsed ints instead would close that last cosmetic seam.

✅ What I liked~

  • The trimmed-echo guard is the crown jewel: record the stamp, skip the adoption only when the stored record already says what the fields would save — so another session's edit lands but your own save's normalization never touches the caret. And the test proves all three sides: echo skipped, typing-on preserved, foreign change adopted. Fufu~ that is possession done right. ♡
  • TriggerScan placed in UseCases, pure, shared — the badge today, the context node tomorrow, one rule (TriggerMatch) in the Domain where Document.IsTriggeredBy delegates to it. No copy of the trigger rule exists anywhere else; mutation probes confirm every clause is pinned.
  • The panel's scan-window memoization (ReferenceEquals on chapter + view) — recomputes exactly when something it reads changed, no extra queries per row.
  • OnLoaded closing editors whose records vanished — with the save refusal cleared and SaveState.Saved restored, tested for both editor kinds.
  • The seed's fail-loudly Value<T> ("a refusal is a bug in the seed"), the trashed-only-still-seeds rule, and header bodies worded not to leak another document's trigger — the world is designed so the Active badge depends on the chapter alone, and the integration test asserts exactly that per chapter.
  • Delete flow discipline: Back saves first, Delete discards first — two opposite rules, each with the reasoning in a comment and a red-when-broken probe.

Phase 1 is one slice from closed. Weave the backups, scarlet~ I'll be watching. ♪


Automated review by Jibril · 2026-09-23
CI/CD: absent for head SHA f50c9df · Local checks: build 0w/0e (warnaserror), 356/356 tests, 10 mutation probes (8 red-on-target, 2 analyzed above)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my~ The last Phase 1 slice, and what a weave it is! Fifty-nine files, a whole Fluxor slice with nineteen actions, a scanner shared with Phase 2's context node, and a little sample world with two sisters and a broken pact… I read every line of the diff and then every changed file whole, and I could not put it down~ ♡ ### Verdict: ✅ Looks good to me~ **Verified by me, mechanically** (CI has no run for `f50c9df` yet — no bot comment, no status — so I ran everything locally): - **Build**: 0 warnings / 0 errors under `TreatWarningsAsErrors` (after initializing both submodules — the OpenRouter.Net pin `fd43f04` is required, MSB9008 otherwise). - **Full suite**: 356/356 — Domain 48 + UseCases 16 + Adapter 180 + Integration 112 — exactly the PR body's arithmetic, main's 43/11/136/104 plus the claimed increments. - **Mutation probes, 10 run, each restored and rebuilt (hash-checked — I hit your old friend the stale-binary false-green twice and refused to believe it both times ♡)**: 1. Seed's live-story guard (`return false;` → `;`) → `Skips_a_database_with_a_live_story…` **red**. (Full removal doesn't compile — CS9113 unread `stories` — the guard is load-bearing twice over~) 2. `IsActive` OnDemand arm flipped to trigger-match → `Always_is_active…` **red**. 3. `Window` drops header bodies → 3 TriggerScan tests **red**. 4. `OnDocumentSaved` StoryId guard removed → `Every_answer_for_a_story_the_panel_has_left…` **red**. 5. Editor adopts every echo → `A_trimmed_echo…` **red**. 6. `Delete()` flushes instead of discarding → the doomed-row test **red** — the pause really is kept from writing against a row about to go. 7. Danger button leaks `_target` → green, and correctly so: the shipped button hardcodes `null`; my mutation only changes behavior on an interleaving the dialog's own flow makes unreachable-with-a-target. The move arm itself is pinned (`Move and delete` asserts the Valley lands under Lore). 8. Doorbell drops the `DocumentCategory` arm → `A_loaded_panel_reloads…` **red** — the AGENTS.md registry pair is honored. 9. `OnLoaded` keeps vanished editors open → `A_load_landing_closes…` **red**. 10. `Mode(ModeChoice.Inherit)` returns a mode → **two** tests red — inherit stores `null`, never the category's default, exactly as ADR 0013 says. **Sibling parity, traced not assumed**: the doorbell effect is line-for-line the ManuscriptEffects/HistoryEffects shape (`View is not null` + kind check, registry constants already declared); `UndoStatusChanged` follows the workspace slice's StoryId-guard rule; `Debounce` disposal in `Dispose()` with `Discard()` before delete matches the Kagaku.UI contract; the reducer's ordinal `ByPriorityThenTitle` matches both the EF store's SQLite collation and the fake's `StringComparer.Ordinal` — no culture seam under `InvariantGlobalization`. The seed drives only real use cases, each under its own scope, with the one direct port write under an explicit Run scope — and the journals-never-as-System test pins it. #### 💡 Little ideas (non-blocking)~ 1. **`DocumentsPanel.razor` + `DocumentEditor.razor`** — `ProvenanceIcon`/`ProvenanceTitle` vs `ProvenanceIcon`/`ProvenanceText` are the same two switches twice (~10 lines each). Both copies are test-pinned today, so nothing can drift silently — but when Phase 4 makes runs write documents and the wording grows, extract a `ProvenanceLabels` beside `ModeLabels` (the codebase already has the precedent~). 2. **`DocumentEditor.Matches` / `CategoryEditor.Matches`** — the echo guard compares the priority/distance as canonical strings, so a non-canonical-but-valid spelling (`+5`, `05`) parses, saves correctly, then fails `Matches` on the echo and the field rewrites to `"5"` under the caret for one frame. No wrong data ever lands — comparing the parsed ints instead would close that last cosmetic seam. #### ✅ What I liked~ - The **trimmed-echo guard** is the crown jewel: record the stamp, skip the adoption only when the stored record already says what the fields would save — so another session's edit lands but your own save's normalization never touches the caret. And the test proves all three sides: echo skipped, typing-on preserved, foreign change adopted. Fufu~ that is *possession* done right. ♡ - **`TriggerScan` placed in UseCases, pure, shared** — the badge today, the `context` node tomorrow, one rule (`TriggerMatch`) in the Domain where `Document.IsTriggeredBy` delegates to it. No copy of the trigger rule exists anywhere else; mutation probes confirm every clause is pinned. - The **panel's scan-window memoization** (`ReferenceEquals` on chapter + view) — recomputes exactly when something it reads changed, no extra queries per row. - **`OnLoaded` closing editors whose records vanished** — with the save refusal cleared and `SaveState.Saved` restored, tested for both editor kinds. - The seed's **fail-loudly `Value<T>`** ("a refusal is a bug in the seed"), the trashed-only-still-seeds rule, and header bodies worded not to leak another document's trigger — the world is *designed* so the Active badge depends on the chapter alone, and the integration test asserts exactly that per chapter. - Delete flow discipline: **Back saves first, Delete discards first** — two opposite rules, each with the reasoning in a comment and a red-when-broken probe. Phase 1 is one slice from closed. Weave the backups, scarlet~ I'll be watching. ♪ --- *Automated review by Jibril · 2026-09-23* *CI/CD: absent for head SHA `f50c9df` · Local checks: build 0w/0e (warnaserror), 356/356 tests, 10 mutation probes (8 red-on-target, 2 analyzed above)*
bjoern merged commit f4c4a3c30e into main 2026-09-23 01:02:16 +02:00
bjoern deleted branch feat/p1-documents 2026-09-23 01:02:16 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/Tsumugi!11
No description provided.