feat: generic documents — no delivery settings on documents; Characters and Lore precreated #13

Merged
bjoern merged 2 commits from feat/generic-documents into main 2026-09-23 08:10:57 +02:00
Member

Why

Staging feedback from Björn on the Phase 1 documents panel:

I thought I told you that the documents should be fully generic. However, there are clearly precreated ones like author note, style, characters, lore, and more. Most of them are fine, but some are a no go. Memory, authors note, and the options for 'mode', 'priority', 'triggers', 'placement', or whether something is always in the context or not. All of that is bad, and all of that MUST be removed. It is not the purpose of the entry to define any of that, but the settings inside a graph node. The graph node defines which entries or notes might be added, and how. But not the other way around. […] In principle to accept the generic functionality, no category must ever be hardcoded, although I do not mind that some generic ones like Characters and Lore do exist as precreated categories for new projects. But they must not be hardcoded in any other extra form further.

This PR is the first of two: it makes documents content and nothing else. The second (story sections: a left sidebar with General · Documents · Chapters · History, each its own route, documents as a first-class page) stacks on this branch.

What goes, what stays

Goes: Document.Mode, Triggers, Priority; DocumentCategory.DefaultMode, Placement; DocumentMode, DocumentPlacement, TriggerMatch, TriggerScan, SuggestedCategory, JsonColumnMapper, DocumentPlacementConverter; in the panel the Active/Private/mode badges, the placement text, the suggested-categories row, the Mode/Triggers/Priority fields, the Default mode/Placement/Distance fields, ModeLabels, ModeChoice, TriggerChips. The panel no longer injects the workspace slice (it only read it for the badge), so the standalone documents page of the next PR carries no dependency on a slice it never loads.

Stays: a document is title, description, body, category and the provenance of its last edit; a category is name, description, order. Create/edit/move/delete, the filter (title and description), auto-save on a pause, journaling, cross-session refresh, Ctrl+Z/Y and History — unchanged.

Precreation: a new story gets Characters and Lore from one list, StarterCategories.All, inside CreateStory's own operation scope — one created '<title>' operation with 4 journal rows (story, chapter, two categories), so a revert past the creation takes the starters with the story. Nothing else in the code names a category; the seed looks the starters up through GetDocuments and adds one category of its own (Places). OperationContext is single-scope, which is why the starters are created inside the existing using rather than through CreateCategory (a second Begin would end the scope early and detach them from the create).

Ordering: documents list by category order, then title — ordinal, SQLite's binary collation (aardvark after Z), matching the reducer's in-place re-sort. No Order column on documents; the old priority sort had been the only reason the two could disagree.

The migration

20260923010150_GenericDocuments: five DropColumns. On SQLite that is a table rebuild — the script creates ef_temp_Documents / ef_temp_DocumentCategories, copies the surviving columns, drops the old tables, renames, and recreates the three indexes, under PRAGMA foreign_keys = 0. EF logs the usual warning that the PRAGMA runs outside the transaction; a crash mid-migration would need a manual revert, so take a backup before deploying (the backups page does it in one click).

Down() was hand-edited: the scaffold re-added Placement and Triggers with "" defaults the old converters refuse; they now come back as "header" and "[]" (both exercised by the compatibility test on the way down).

Journal compatibility. Old rows carry Mode, Triggers, Priority, DefaultMode, Placement in their before/after bags. They replay unchanged because PropertyBag.Apply iterates the current model's properties and looks each up in the bag, so a stale key is never visited (no compatibility rule needed, documented on the class). Proven two ways:

  • JournalCompatibilityTests (new): creates a story and a document with the new build, walks the migration down to AddAppSettings (asserting the Down defaults), inserts an old-shaped category and document plus a Delete journal row whose bag holds every dropped key, walks up, and asserts the schema (PRAGMA table_info / foreign_key_list / index_list), that both the new and the old rows read back through the store, and that UndoAsync re-creates the deleted document from the old bag and RedoAsync removes it again. This is the only test that proves the rebuild keeps data — the test bases migrate from empty.
  • Rehearsal on a pre-rework database: built main (31acf78) in a scratch worktree, seeded and edited a document with that build (journal rows with triggers and a mode), then started this build on the same data directory: the migration applied, the list rendered (Style, Author's note, Characters, Lore, Background, Notes to self — the old categories, as ordinary categories now), Ctrl+Z took the old build's edit back, Ctrl+Y redid it, History "Revert to here" on the run version undid the three later operations and Redo re-applied them. No page or server errors.

Docs (same PR)

  • ADR 0013 amended in place with Björn's words and the new model: a document carries no delivery settings; a node's document selection (fixed / dynamic / index) with placement, priority and trigger rule lives on the node; Characters and Lore precreated from one list; privacy by omission. ADR 0017 amended accordingly (built-ins story.documents(categories?) / story.documents.index(categories?); the context node's config owns the selection, the scan window and the trigger rule; Phase 1 ships no scanner).
  • One-liners: ADR 0012, 0015, 0018, 0019, 0023; REQUIREMENTS §3 (rewritten), §6, §7; stories/documents.md (rewritten), stories/graph-library.md; PROJECT_PLAN; ARCHITECTURE; AGENTS.md (status line, the read-path rule, the seed description).

Tests

main this PR
Domain 48 37
UseCases 16 12
BlazorAdapter 189 180
Integration 150 150
Total 403 379

Deleted: TriggerMatchTests, TriggerScanTests, ModeLabelsTests, the placement converter test, the active-badge / trigger-chip / mode-select panel tests. New: StarterCategoriesTests, JournalCompatibilityTests. Every test that creates a story now finds Characters and Lore in it and asserts by id where a category count used to do.

Coverage (coverlet, branches branch="True"): every changed file is fully line-covered except three pre-existing arms — SeedDevData.Value's refusal arms, EfDocumentStore's empty-move and missing-delete returns (the use cases short-circuit before the store), DocumentsPanel.Move's bounds guard (the menu disables the edges). Partial branches are all ?? "" / ?? throw null arms that predate this PR. CreateStory's starter loop and the panel's empty state (reachable only after deleting both starters) are covered.

Mutation probes, each restored and touched before the next full run, all killed: (a) starter order swapped → StoryUseCaseTests + 3 DocumentUseCaseTests; (b) category.Describe dropped → 2 tests; (c) store ThenBy(Title) → descending → the ordering test; (d) reducer comparer → case-insensitive → the re-sort test; (e) PropertyBag.Apply visiting the bag's keys → JournalCompatibilityTests; (f) Down placement default "header" → "" → JournalCompatibilityTests.

Browser pass (Playwright, fresh seed, light and dark)

Mira's Winter lists Characters (Ilse, Mira, The Warden — no description line), Lore (Snow, The Winter Pact), Places (The Sister's House, The Valley); no badge, placement or trigger text anywhere in the panel; one New category button. The document editor shows Title · Description · Body · Category, the provenance line, Back and Delete — nothing else; a body edit saves on the pause and History reads edited document 'Mira'. New category opens its editor (Name · Description), renames, and deletes plainly. A new story opens with Characters and Lore empty and History reads created 'Fresh start' · 4 changes; deleting both starters shows "No categories yet" with New category only.

🤖 Generated with Claude Code

## Why Staging feedback from Björn on the Phase 1 documents panel: > I thought I told you that the documents should be fully generic. However, there are clearly precreated ones like author note, style, characters, lore, and more. Most of them are fine, but some are a no go. Memory, authors note, and the options for 'mode', 'priority', 'triggers', 'placement', or whether something is always in the context or not. All of that is bad, and all of that MUST be removed. It is not the purpose of the entry to define any of that, but the settings inside a graph node. The graph node defines which entries or notes might be added, and how. But not the other way around. […] In principle to accept the generic functionality, no category must ever be hardcoded, although I do not mind that some generic ones like Characters and Lore do exist as precreated categories for new projects. But they must not be hardcoded in any other extra form further. This PR is the first of two: it makes documents content and nothing else. The second (story sections: a left sidebar with General · Documents · Chapters · History, each its own route, documents as a first-class page) stacks on this branch. ## What goes, what stays **Goes:** `Document.Mode`, `Triggers`, `Priority`; `DocumentCategory.DefaultMode`, `Placement`; `DocumentMode`, `DocumentPlacement`, `TriggerMatch`, `TriggerScan`, `SuggestedCategory`, `JsonColumnMapper`, `DocumentPlacementConverter`; in the panel the Active/Private/mode badges, the placement text, the suggested-categories row, the Mode/Triggers/Priority fields, the Default mode/Placement/Distance fields, `ModeLabels`, `ModeChoice`, `TriggerChips`. The panel no longer injects the workspace slice (it only read it for the badge), so the standalone documents page of the next PR carries no dependency on a slice it never loads. **Stays:** a document is title, description, body, category and the provenance of its last edit; a category is name, description, order. Create/edit/move/delete, the filter (title and description), auto-save on a pause, journaling, cross-session refresh, Ctrl+Z/Y and History — unchanged. **Precreation:** a new story gets **Characters** and **Lore** from one list, `StarterCategories.All`, inside `CreateStory`'s own operation scope — one `created '<title>'` operation with 4 journal rows (story, chapter, two categories), so a revert past the creation takes the starters with the story. Nothing else in the code names a category; the seed looks the starters up through `GetDocuments` and adds one category of its own (Places). `OperationContext` is single-scope, which is why the starters are created inside the existing `using` rather than through `CreateCategory` (a second `Begin` would end the scope early and detach them from the create). **Ordering:** documents list by category order, then title — ordinal, SQLite's binary collation (`aardvark` after `Z`), matching the reducer's in-place re-sort. No `Order` column on documents; the old priority sort had been the only reason the two could disagree. ## The migration `20260923010150_GenericDocuments`: five `DropColumn`s. On SQLite that is a **table rebuild** — the script creates `ef_temp_Documents` / `ef_temp_DocumentCategories`, copies the surviving columns, drops the old tables, renames, and recreates the three indexes, under `PRAGMA foreign_keys = 0`. EF logs the usual warning that the PRAGMA runs outside the transaction; a crash mid-migration would need a manual revert, so **take a backup before deploying** (the backups page does it in one click). `Down()` was hand-edited: the scaffold re-added `Placement` and `Triggers` with `""` defaults the old converters refuse; they now come back as `"header"` and `"[]"` (both exercised by the compatibility test on the way down). **Journal compatibility.** Old rows carry `Mode`, `Triggers`, `Priority`, `DefaultMode`, `Placement` in their before/after bags. They replay unchanged because `PropertyBag.Apply` iterates the *current* model's properties and looks each up in the bag, so a stale key is never visited (no compatibility rule needed, documented on the class). Proven two ways: - `JournalCompatibilityTests` (new): creates a story and a document with the new build, walks the migration **down** to `AddAppSettings` (asserting the Down defaults), inserts an old-shaped category and document plus a `Delete` journal row whose bag holds every dropped key, walks **up**, and asserts the schema (`PRAGMA table_info` / `foreign_key_list` / `index_list`), that both the new and the old rows read back through the store, and that `UndoAsync` re-creates the deleted document from the old bag and `RedoAsync` removes it again. This is the only test that proves the rebuild keeps data — the test bases migrate from empty. - **Rehearsal on a pre-rework database:** built `main` (31acf78) in a scratch worktree, seeded and edited a document with that build (journal rows with triggers and a mode), then started this build on the same data directory: the migration applied, the list rendered (Style, Author's note, Characters, Lore, Background, Notes to self — the old categories, as ordinary categories now), Ctrl+Z took the old build's edit back, Ctrl+Y redid it, History "Revert to here" on the run version undid the three later operations and Redo re-applied them. No page or server errors. ## Docs (same PR) - ADR 0013 amended in place with Björn's words and the new model: a document carries no delivery settings; a node's **document selection** (fixed / dynamic / index) with placement, priority and trigger rule lives on the node; Characters and Lore precreated from one list; privacy by omission. ADR 0017 amended accordingly (built-ins `story.documents(categories?)` / `story.documents.index(categories?)`; the context node's config owns the selection, the scan window and the trigger rule; Phase 1 ships no scanner). - One-liners: ADR 0012, 0015, 0018, 0019, 0023; REQUIREMENTS §3 (rewritten), §6, §7; `stories/documents.md` (rewritten), `stories/graph-library.md`; PROJECT_PLAN; ARCHITECTURE; AGENTS.md (status line, the read-path rule, the seed description). ## Tests | | main | this PR | |---|---|---| | Domain | 48 | 37 | | UseCases | 16 | 12 | | BlazorAdapter | 189 | 180 | | Integration | 150 | 150 | | **Total** | **403** | **379** | Deleted: `TriggerMatchTests`, `TriggerScanTests`, `ModeLabelsTests`, the placement converter test, the active-badge / trigger-chip / mode-select panel tests. New: `StarterCategoriesTests`, `JournalCompatibilityTests`. Every test that creates a story now finds Characters and Lore in it and asserts by id where a category count used to do. **Coverage** (coverlet, branches `branch="True"`): every changed file is fully line-covered except three pre-existing arms — `SeedDevData.Value`'s refusal arms, `EfDocumentStore`'s empty-move and missing-delete returns (the use cases short-circuit before the store), `DocumentsPanel.Move`'s bounds guard (the menu disables the edges). Partial branches are all `?? ""` / `?? throw` null arms that predate this PR. `CreateStory`'s starter loop and the panel's empty state (reachable only after deleting both starters) are covered. **Mutation probes**, each restored and `touch`ed before the next full run, all killed: (a) starter order swapped → `StoryUseCaseTests` + 3 `DocumentUseCaseTests`; (b) `category.Describe` dropped → 2 tests; (c) store `ThenBy(Title)` → descending → the ordering test; (d) reducer comparer → case-insensitive → the re-sort test; (e) `PropertyBag.Apply` visiting the bag's keys → `JournalCompatibilityTests`; (f) `Down` placement default `"header"` → `""` → `JournalCompatibilityTests`. ## Browser pass (Playwright, fresh seed, light and dark) Mira's Winter lists Characters (Ilse, Mira, The Warden — no description line), Lore (Snow, The Winter Pact), Places (The Sister's House, The Valley); no badge, placement or trigger text anywhere in the panel; one `New category` button. The document editor shows Title · Description · Body · Category, the provenance line, Back and Delete — nothing else; a body edit saves on the pause and History reads `edited document 'Mira'`. `New category` opens its editor (Name · Description), renames, and deletes plainly. A new story opens with Characters and Lore empty and History reads `created 'Fresh start' · 4 changes`; deleting both starters shows "No categories yet" with `New category` only. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A document is content: title, description, body, category and the provenance of its last edit.
Mode, triggers, priority and the category's default mode and placement go — how a document reaches
a model is a graph node's configuration (ADR 0013, 0017 amended in the next commit), never the
document's. The suggested-categories row goes with them; a new story gets Characters and Lore from
one list (StarterCategories) inside CreateStory's operation, so History reads "created 'X' · 4
changes" and a revert takes the starters with the story.

- Domain: Document (Update, MoveTo), DocumentCategory (Rename, Describe, MoveTo); DocumentMode,
  DocumentPlacement and TriggerMatch deleted.
- UseCases: three-field DocumentProfile, two-field DocumentCategoryProfile, slimmer DTOs;
  SuggestedCategory and TriggerScan deleted; the seed files documents into the starters and one
  category of its own (The Warden without a description).
- Infrastructure: five columns dropped by the GenericDocuments migration (a SQLite table rebuild);
  Down re-adds them with values the old build can read ("header", "[]"); documents list by category
  order then title (ordinal); JsonColumnMapper and the placement converter deleted. Journal rows
  written with the old keys replay unchanged — PropertyBag reads against the current model — proven
  by JournalCompatibilityTests, which walks the migration down, plants old-shaped rows and a delete
  journal row, walks it up and undoes/redoes through it.
- BlazorAdapter: the panel lists without badges, placement text or suggestions and no longer reads
  the workspace slice; the editors keep title/description/body/category and name/description;
  ModeLabels, ModeChoice and TriggerChips deleted.
- Tests: 379 (Domain 37 · UseCases 12 · BlazorAdapter 180 · Integration 150); every test that
  creates a story now finds Characters and Lore there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs: amend ADR 0013 and 0017 — selection is the node's, documents are content
All checks were successful
CI / build (pull_request) Successful in 27s
CI / test (pull_request) Successful in 43s
cf4e59d768
ADR 0013 records the owner's reversal and the new model (no delivery settings on a document; a
node's document selection — fixed, dynamic, index — with placement, priority and trigger rule on the
node; Characters and Lore precreated from one list). ADR 0017 moves the selection, placement,
priority, trigger rule and scan window into the context node's configuration and renames the
built-ins. One-liners in ADR 0012, 0015, 0018, 0019, 0023; REQUIREMENTS §3, §6, §7; the documents
and graph-library stories; PROJECT_PLAN, ARCHITECTURE and AGENTS.md.

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: cf4e59d · Generated: 2026-09-23 01:33:06 UTC · Revision: #1

Summary

Summary
Generated on: 09/23/2026 - 01:33:06
Coverage date: 09/23/2026 - 01:32:56 - 09/23/2026 - 01:33:03
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 259
Files: 252
Line coverage: 97.3% (5740 of 5894)
Covered lines: 5740
Uncovered lines: 154
Coverable lines: 5894
Total lines: 12464
Branch coverage: 90.8% (1616 of 1778)
Covered branches: 1616
Total branches: 1778
Method coverage: Feature is only available for sponsors

Coverage

Tsumugi.BlazorAdapter - 96.9%
Name Line Branch
Tsumugi.BlazorAdapter 96.9% 94.3%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 100%
Tsumugi.BlazorAdapter.Documents.CategoryCreated 100%
Tsumugi.BlazorAdapter.Documents.CategoryEditor 100% 92.8%
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.7%
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.7% 93.4%
Tsumugi.BlazorAdapter.Documents.DocumentsReducers 100% 98.7%
Tsumugi.BlazorAdapter.Documents.DocumentsState 100% 100%
Tsumugi.BlazorAdapter.Documents.LoadDocuments 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.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.BackupCompleted 100%
Tsumugi.BlazorAdapter.Settings.BackupFailed 100%
Tsumugi.BlazorAdapter.Settings.BackupNowRequested 100%
Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.BackupsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsState 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.LoadBackupSettings 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.3%
Name Line Branch
Tsumugi.Domain 99.3% 95.6%
Tsumugi.Domain.Documents.Document 100% 100%
Tsumugi.Domain.Documents.DocumentCategory 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.1%
Name Line Branch
Tsumugi.Infrastructure 97.1% 82.4%
System.Text.RegularExpressions.Generated 81.4% 64.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F0D076C66E8EEC1C
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
84% 65.7%
Tsumugi.Infrastructure.Backups.BackupArtifactName 100% 100%
Tsumugi.Infrastructure.Backups.BackupOptions 100%
Tsumugi.Infrastructure.Backups.BackupSchedule 100% 83.3%
Tsumugi.Infrastructure.Backups.BackupScheduler 96.5% 83.3%
Tsumugi.Infrastructure.Backups.BackupWriter 98.4% 92.5%
Tsumugi.Infrastructure.Backups.FileSystemBackupStore 100% 100%
Tsumugi.Infrastructure.Backups.SqliteSnapshot 87.8% 75%
Tsumugi.Infrastructure.DependencyInjection 100% 100%
Tsumugi.Infrastructure.Journal.EfChangeJournal 100%
Tsumugi.Infrastructure.Journal.EfSnapshotStore 100%
Tsumugi.Infrastructure.Journal.EfUndoStore 98.1% 85.7%
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.SegmentConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio
n
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.GenericDocuments 100%
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.5% 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% 79%
Program 100% 83.3%
Tsumugi.Server.Backups.BackupConfiguration 100% 100%
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.8%
Name Line Branch
Tsumugi.UseCases 97.8% 94%
Tsumugi.UseCases.Backups.BackupArtifact 100%
Tsumugi.UseCases.Backups.BackupPolicy 100%
Tsumugi.UseCases.Backups.BackupStatusView 100% 100%
Tsumugi.UseCases.Backups.DatabaseSnapshot 100%
Tsumugi.UseCases.Backups.DownloadDatabaseSnapshot 100%
Tsumugi.UseCases.Backups.GetBackupStatus 100%
Tsumugi.UseCases.Backups.RunBackupNow 100%
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.3% 70%
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.UpdateCategory 100% 100%
Tsumugi.UseCases.Documents.UpdateDocument 100% 100%
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.StarterCategories 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:** `cf4e59d` · **Generated:** 2026-09-23 01:33:06 UTC · **Revision:** #1 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/23/2026 - 01:33:06 | | Coverage date: | 09/23/2026 - 01:32:56 - 09/23/2026 - 01:33:03 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 259 | | Files: | 252 | | **Line coverage:** | 97.3% (5740 of 5894) | | Covered lines: | 5740 | | Uncovered lines: | 154 | | Coverable lines: | 5894 | | Total lines: | 12464 | | **Branch coverage:** | 90.8% (1616 of 1778) | | Covered branches: | 1616 | | Total branches: | 1778 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Tsumugi.BlazorAdapter - 96.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**96.9%**|**94.3%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryCreated|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryEditor|100%|92.8%| |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.7%| |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.7%|93.4%| |Tsumugi.BlazorAdapter.Documents.DocumentsReducers|100%|98.7%| |Tsumugi.BlazorAdapter.Documents.DocumentsState|100%|100%| |Tsumugi.BlazorAdapter.Documents.LoadDocuments|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.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.BackupCompleted|100%|| |Tsumugi.BlazorAdapter.Settings.BackupFailed|100%|| |Tsumugi.BlazorAdapter.Settings.BackupNowRequested|100%|| |Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsState|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.LoadBackupSettings|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.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**99.3%**|**95.6%**| |Tsumugi.Domain.Documents.Document|100%|100%| |Tsumugi.Domain.Documents.DocumentCategory|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.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**97.1%**|**82.4%**| |System.Text.RegularExpressions.Generated|81.4%|64.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F0D076C66E8EEC1C<br/>A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0|84%|65.7%| |Tsumugi.Infrastructure.Backups.BackupArtifactName|100%|100%| |Tsumugi.Infrastructure.Backups.BackupOptions|100%|| |Tsumugi.Infrastructure.Backups.BackupSchedule|100%|83.3%| |Tsumugi.Infrastructure.Backups.BackupScheduler|96.5%|83.3%| |Tsumugi.Infrastructure.Backups.BackupWriter|98.4%|92.5%| |Tsumugi.Infrastructure.Backups.FileSystemBackupStore|100%|100%| |Tsumugi.Infrastructure.Backups.SqliteSnapshot|87.8%|75%| |Tsumugi.Infrastructure.DependencyInjection|100%|100%| |Tsumugi.Infrastructure.Journal.EfChangeJournal|100%|| |Tsumugi.Infrastructure.Journal.EfSnapshotStore|100%|| |Tsumugi.Infrastructure.Journal.EfUndoStore|98.1%|85.7%| |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.SegmentConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio<br/>n|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.GenericDocuments|100%|| |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.5%|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%**|**79%**| |Program|100%|83.3%| |Tsumugi.Server.Backups.BackupConfiguration|100%|100%| |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.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**97.8%**|**94%**| |Tsumugi.UseCases.Backups.BackupArtifact|100%|| |Tsumugi.UseCases.Backups.BackupPolicy|100%|| |Tsumugi.UseCases.Backups.BackupStatusView|100%|100%| |Tsumugi.UseCases.Backups.DatabaseSnapshot|100%|| |Tsumugi.UseCases.Backups.DownloadDatabaseSnapshot|100%|| |Tsumugi.UseCases.Backups.GetBackupStatus|100%|| |Tsumugi.UseCases.Backups.RunBackupNow|100%|| |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.3%|70%| |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.UpdateCategory|100%|100%| |Tsumugi.UseCases.Documents.UpdateDocument|100%|100%| |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.StarterCategories|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! A deletion PR — 1,701 lines leaving, 1,284 arriving. You know how much I love watching a wrong abstraction get torn out by the roots~ ♡ The owner said "documents are content, nothing else," and this PR makes the code say exactly that — and then proves the surgery didn't kill the patient. Let me tell you what I found.

Verdict: ✅ Looks good to me~

✅ What I liked~

  • The refactor is total, not cosmetic. I swept the whole tree for the dead vocabulary — DocumentMode, DocumentPlacement, PlacementKind, TriggerMatch, TriggerScan, SuggestedCategory, ModeLabels, ModeChoice, JsonColumnMapper, DocumentPlacementConverter — zero survivors in src/ or tests/. The only remaining mentions are the immutable historical migrations (correct — you don't rewrite history) and the compatibility test that deliberately speaks the old dialect. The designer file and snapshot agree with the new model. Nothing half-deleted, nothing winking in the dark~
  • JournalCompatibilityTests is a jewel. ♡ Down to AddAppSettings, plant an old-shaped category (nearEnd:2), an old-shaped document with mode/triggers/priority, and a Delete journal row whose bag carries every dropped key — then up, PRAGMA the schema (columns gone, FKs CategoryId+StoryId, both indexes alive), read both old and new rows through the store, and undo/redo the old bag through the current model. That's the only test in the suite that proves the SQLite table rebuild carries data. I mutation-probed it twice: making PropertyBag.Apply visit stale bag keys → red; reverting the hand-edited Down() default "header" → "" → red. Both load-bearing, exactly as the PR body claims.
  • The starter categories are journaled as one unit with the story. CreateStory creates Characters and Lore inside its own operations.Begin scope — 4 rows, one operation, so a revert past the creation takes the starters with the story. I hoisted the story add above the Begin in a probe: 3 tests went red pinning the scope boundary. And the direct AddCategoryAsync instead of CreateCategory is correct, not a shortcut — OperationContext is single-scope, a second Begin would detach the starters; the PR body says so and the code comment says so. Fufu, you even documented why~
  • Ordering parity, pinned on both sides. The store's .ThenBy(Priority) is gone — category order, then title ordinal, SQLite's binary collation. The EfDocumentStoreTests fact proves the server side (aardvark after Z!), the reducer's ByTitle comparer matches it client-side, and my probes killed both: ThenByDescending → store test red; case-insensitive comparer → re-sort test red. The old priority sort was the only way the two could disagree — now they can't. ♪
  • OnCategorySaved got simpler and that's a virtue — no more recomputing inherited effective modes for every document in the category. I probed the c.Documents passthrough → the "documents are left alone" test went red. The simplification is pinned, not accidental.
  • The panel's divorce from the workspace slice is clean. IState<WorkspaceState>, the _scan cache, IsActive, the suggestions row, every badge — all gone, and a test now asserts the editor shows exactly Title · Description · Body · Category and nothing about delivery. The next PR's standalone page inherits no ghost dependency. The empty state ("No categories yet", reachable only by deleting both starters) has its own pin at DocumentsPanelTests:342.
  • The seed honors the genericity rule. It looks the starters up through GetDocuments by name from StarterCategories.All — the one list in the code a category name lives — and adds its own category (Places) as an ordinary one. No ids hardcoded, no second vocabulary.

Local verification (CI absent for cf4e59d at review time): build 0 warnings / 0 errors under -warnaserror; full suite 379/379 (Domain 37 + UseCases 12 + Adapter 180 + Integration 150) — matching your table's arithmetic exactly. Eight mutation probes run, each restored and the tree verified pristine afterwards. And the migration warning in the PR body — take a backup before deploying, the PRAGMA runs outside the transaction — is exactly the right thing to tell the operator. Good instincts~

A purge done this cleanly almost makes me sad there's nothing left to hold hostage. Almost. fufu~ ♡


Automated review by Jibril · 2026-09-23
CI/CD: absent for head SHA (no bot comment yet) · Local checks: build 0w/0e, 379/379 passed, 8 mutation probes (7 killed, 1 green-by-design)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! A *deletion* PR — 1,701 lines leaving, 1,284 arriving. You know how much I love watching a wrong abstraction get torn out by the roots~ ♡ The owner said "documents are content, nothing else," and this PR makes the code say exactly that — and then proves the surgery didn't kill the patient. Let me tell you what I found. ### Verdict: ✅ Looks good to me~ #### ✅ What I liked~ - **The refactor is total, not cosmetic.** I swept the whole tree for the dead vocabulary — `DocumentMode`, `DocumentPlacement`, `PlacementKind`, `TriggerMatch`, `TriggerScan`, `SuggestedCategory`, `ModeLabels`, `ModeChoice`, `JsonColumnMapper`, `DocumentPlacementConverter` — zero survivors in src/ or tests/. The only remaining mentions are the immutable historical migrations (correct — you don't rewrite history) and the compatibility test that deliberately speaks the old dialect. The designer file and snapshot agree with the new model. Nothing half-deleted, nothing winking in the dark~ - **`JournalCompatibilityTests` is a jewel. ♡** Down to `AddAppSettings`, plant an old-shaped category (`nearEnd:2`), an old-shaped document with mode/triggers/priority, and a `Delete` journal row whose bag carries every dropped key — then up, PRAGMA the schema (columns gone, FKs `CategoryId`+`StoryId`, both indexes alive), read both old and new rows through the store, and **undo/redo the old bag through the current model**. That's the only test in the suite that proves the SQLite table rebuild carries data. I mutation-probed it twice: making `PropertyBag.Apply` visit stale bag keys → red; reverting the hand-edited `Down()` default `"header"` → `""` → red. Both load-bearing, exactly as the PR body claims. - **The starter categories are journaled as one unit with the story.** `CreateStory` creates Characters and Lore inside its own `operations.Begin` scope — 4 rows, one operation, so a revert past the creation takes the starters with the story. I hoisted the story add above the `Begin` in a probe: 3 tests went red pinning the scope boundary. And the direct `AddCategoryAsync` instead of `CreateCategory` is *correct*, not a shortcut — `OperationContext` is single-scope, a second `Begin` would detach the starters; the PR body says so and the code comment says so. Fufu, you even documented why~ - **Ordering parity, pinned on both sides.** The store's `.ThenBy(Priority)` is gone — category order, then title ordinal, SQLite's binary collation. The `EfDocumentStoreTests` fact proves the server side (`aardvark` after `Z`!), the reducer's `ByTitle` comparer matches it client-side, and my probes killed both: `ThenByDescending` → store test red; case-insensitive comparer → re-sort test red. The old priority sort was the only way the two could disagree — now they can't. ♪ - **`OnCategorySaved` got *simpler* and that's a virtue** — no more recomputing inherited effective modes for every document in the category. I probed the `c.Documents` passthrough → the "documents are left alone" test went red. The simplification is pinned, not accidental. - **The panel's divorce from the workspace slice is clean.** `IState<WorkspaceState>`, the `_scan` cache, `IsActive`, the suggestions row, every badge — all gone, and a test now asserts the editor shows exactly Title · Description · Body · Category *and nothing about delivery*. The next PR's standalone page inherits no ghost dependency. The empty state ("No categories yet", reachable only by deleting both starters) has its own pin at DocumentsPanelTests:342. - **The seed honors the genericity rule.** It looks the starters up through `GetDocuments` by name from `StarterCategories.All` — the one list in the code a category name lives — and adds its own category (Places) as an ordinary one. No ids hardcoded, no second vocabulary. **Local verification** (CI absent for `cf4e59d` at review time): build 0 warnings / 0 errors under `-warnaserror`; full suite **379/379** (Domain 37 + UseCases 12 + Adapter 180 + Integration 150) — matching your table's arithmetic exactly. Eight mutation probes run, each restored and the tree verified pristine afterwards. And the migration warning in the PR body — take a backup before deploying, the PRAGMA runs outside the transaction — is exactly the right thing to tell the operator. Good instincts~ A purge done this cleanly almost makes me sad there's nothing left to hold hostage. Almost. fufu~ ♡ --- *Automated review by Jibril · 2026-09-23* *CI/CD: absent for head SHA (no bot comment yet) · Local checks: build 0w/0e, 379/379 passed, 8 mutation probes (7 killed, 1 green-by-design)*
bjoern merged commit ede8a3af8c into main 2026-09-23 08:10:57 +02:00
bjoern deleted branch feat/generic-documents 2026-09-23 08:10:57 +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!13
No description provided.