feat: Phase 1 · 1/7 — the story domain and documents #4

Merged
bjoern merged 2 commits from feat/p1-domain into main 2026-09-18 19:05:04 +02:00
Member

First slice of Phase 1 (docs/PROJECT_PLAN.md), sliced per layer as Orihon did: the pure domain for ADR 0012 (story, chapters, segments, provenance) and ADR 0013 (document categories and documents). No persistence, no use cases, no UI — those are slices 2–7. Conventions follow Orihon's entities: private EF materialization constructors, validated public constructors that trim, every mutation stamps UpdatedAt from a timestamp the caller passes.

What's in

Tsumugi.Domain/Stories

  • Story — title, optional description (blank → null), StoryStatus active/archived (nai_maker's stash), ActiveGraphId (nullable until the graph library exists — ADR 0023), soft delete idempotent with restore (ADR 0022).
  • Chapter — order, title, and its summary with provenance; a blank summary clears both (ADR 0012: chapters carry their summary, the context builder never summarizes on its own).
  • Segment — Prose/SceneBreak factories, order within a chapter; prose text keeps leading indent and inner newlines and trims the tail; empty prose is legal (the blank the writer types into); a scene break has no text and refuses Edit/Replace. Edit is the writer's move (provenance kept, run text marked edited); Replace is a run's move (new provenance). MoveTo within and across chapters.
  • Provenance — User or FromRun(runId, nodeId, model?), with AfterUserEdit() marking run text edited exactly once and leaving the writer's own text alone. Shared by segments, chapter summaries and documents' last edit.

Tsumugi.Domain/Documents

  • DocumentMode — always / triggered / onDemand / never. DocumentPlacement — Header or NearEnd(distance ≥ 0), with Of(kind, distance) for rehydration.
  • DocumentCategory — name, description, order, default mode, placement.
  • Document — title, summary, body (whole-field Update that records who did it — ADR 0019's "a model cannot half-edit a body"), mode override with EffectiveMode(category) (refuses a foreign category), triggers trimmed and deduplicated ignoring case, IsTriggeredBy(text) case-insensitive substring (ADR 0017), priority, MoveTo(category).

Verification

  • 34 new domain tests in tests/Tsumugi.Domain.Tests (story lifecycle incl. idempotent delete; chapter summary provenance; segment normalization, scene-break refusals, edited-flag semantics for run vs writer text, replace, moves; provenance construction and the once-only edit mark; category setters and placement bounds; document update provenance, mode inheritance and override, foreign-category refusal, trigger normalization and matching, priority/move). Full solution: 72/72.
  • Directional check on the two rules most likely to drift: editing run text sets Edited, editing writer text does not; a document with no triggers is never triggered.
  • Not exercised yet: EF mapping (slice 2), so the private materialization constructors and IReadOnlyList<string> Triggers will meet the converter there.

Follow-ups (the remaining slices)

  1. Persistence — configurations, first migration, stores behind ports on a real SQLite file.
  2. Journal + snapshots (ADR 0022), EntityKinds, DomainChanged publishing.
  3. Story and document use cases with the journaled-path coverage test.
  4. Story list page and settings persistence.
  5. Story workspace (segment editor, chapters, History, editor undo).
  6. Documents panel, seed dev data, browser pass.

🤖 Generated with Claude Code

First slice of Phase 1 (`docs/PROJECT_PLAN.md`), sliced per layer as Orihon did: the pure domain for ADR 0012 (story, chapters, segments, provenance) and ADR 0013 (document categories and documents). No persistence, no use cases, no UI — those are slices 2–7. Conventions follow Orihon's entities: private EF materialization constructors, validated public constructors that trim, every mutation stamps `UpdatedAt` from a timestamp the caller passes. ## What's in **`Tsumugi.Domain/Stories`** - `Story` — title, optional description (blank → null), `StoryStatus` active/archived (nai_maker's stash), `ActiveGraphId` (nullable until the graph library exists — ADR 0023), soft delete idempotent with restore (ADR 0022). - `Chapter` — order, title, and its **summary with provenance**; a blank summary clears both (ADR 0012: chapters carry their summary, the context builder never summarizes on its own). - `Segment` — `Prose`/`SceneBreak` factories, order within a chapter; prose text keeps leading indent and inner newlines and trims the tail; empty prose is legal (the blank the writer types into); a scene break has no text and refuses `Edit`/`Replace`. `Edit` is the writer's move (provenance kept, run text marked edited); `Replace` is a run's move (new provenance). `MoveTo` within and across chapters. - `Provenance` — `User` or `FromRun(runId, nodeId, model?)`, with `AfterUserEdit()` marking run text edited exactly once and leaving the writer's own text alone. Shared by segments, chapter summaries and documents' last edit. **`Tsumugi.Domain/Documents`** - `DocumentMode` — always / triggered / onDemand / never. `DocumentPlacement` — `Header` or `NearEnd(distance ≥ 0)`, with `Of(kind, distance)` for rehydration. - `DocumentCategory` — name, description, order, default mode, placement. - `Document` — title, summary, body (whole-field `Update` that records who did it — ADR 0019's "a model cannot half-edit a body"), mode override with `EffectiveMode(category)` (refuses a foreign category), triggers trimmed and deduplicated ignoring case, `IsTriggeredBy(text)` case-insensitive substring (ADR 0017), priority, `MoveTo(category)`. ## Verification - 34 new domain tests in `tests/Tsumugi.Domain.Tests` (story lifecycle incl. idempotent delete; chapter summary provenance; segment normalization, scene-break refusals, edited-flag semantics for run vs writer text, replace, moves; provenance construction and the once-only edit mark; category setters and placement bounds; document update provenance, mode inheritance and override, foreign-category refusal, trigger normalization and matching, priority/move). Full solution: 72/72. - Directional check on the two rules most likely to drift: editing run text sets `Edited`, editing writer text does not; a document with no triggers is never triggered. - Not exercised yet: EF mapping (slice 2), so the private materialization constructors and `IReadOnlyList<string> Triggers` will meet the converter there. ## Follow-ups (the remaining slices) 2. Persistence — configurations, first migration, stores behind ports on a real SQLite file. 3. Journal + snapshots (ADR 0022), `EntityKinds`, `DomainChanged` publishing. 4. Story and document use cases with the journaled-path coverage test. 5. Story list page and settings persistence. 6. Story workspace (segment editor, chapters, History, editor undo). 7. Documents panel, seed dev data, browser pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: the story domain and documents (Phase 1 · 1/7)
Some checks failed
CI / build (pull_request) Has been cancelled
CI / test (pull_request) Has been cancelled
d9860d3be7
Story (title, description, active/archived, active graph, trash) → Chapter (order, title, summary
with provenance) → Segment (order, prose or scene break, text, provenance) per ADR 0012; Provenance
as a value (user, or run + node + model, with the edited flag that a writer's edit of run text
sets and a writer's own text never gets). DocumentCategory (name, order, default mode, placement
header / near-end distance) and Document (title, summary, body, mode override, triggers, priority,
last edit provenance) per ADR 0013, with the effective-mode resolution and the case-insensitive
trigger match the context builder and the documents panel share. 34 domain tests.

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

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my~ A pure domain slice — no framework cooties anywhere, just entities wearing their nicest invariants, every mutation carrying its own timestamp like a good little audit trail ♪ I read all 18 files against ADR 0012, 0013, 0017 and 0022, and against the Orihon entities you named as the convention source. The model itself is faithful~ The provenance algebra alone deserves a hug: AfterUserEdit() flipping edited exactly once and returning this for the writer's own text is precisely ADR 0012's semantics, with both arms tested directionally.

Verdict: ⛔ I can't let this pass~ ♡

The code is correct — the tests have holes, and I have the corpses to prove it. Fufu~ you wouldn't leave whole public methods unwatched in production, would you? ♡

⛔ These need fixing before I'm satisfied~

  1. src/Tsumugi.Domain/Stories/Chapter.cs:47 — Rename has zero behavioral coverage. No test ever calls Chapter.Rename successfully — ChapterTests only constructs and throws. I mutation-probed it on this head: removing the title.Trim() (the exact normalization the method exists to perform) leaves the suite green at 34/34. Every sibling has this test — Orihon's own ChapterTests.Rename_trims_and_stamps (the convention source this PR cites), your StoryTests.Rename_and_describe_trim_and_stamp, and DocumentCategoryTests' rename inside Every_setter_stamps. Chapter alone dropped it.
    Fix: add Rename_trims_and_stamps mirroring the Orihon sibling (trim + UpdatedAt stamp + blank rejection).
  2. src/Tsumugi.Domain/Stories/Chapter.cs:55 — MoveTo's success path is untested. Only the negative-order throw is pinned; I deleted the body's two assignments (Order, UpdatedAt) and the suite stayed 34/34 green — a silent no-op reorder would merge undetected. Orihon's MoveTo_repositions_but_never_below_zero covers exactly this arm.
    Fix: assert Order and UpdatedAt after a successful MoveTo(4, T1) (the throw assertion already exists).
  3. src/Tsumugi.Domain/Stories/Story.cs:96 — Restore's no-op guard branch is never exercised. Mutating Restore to unconditionally clear SoftDeletedAt and stamp UpdatedAt survives green — a restore of a live story would silently churn UpdatedAt and no test would notice. The mirror (SoftDelete idempotence keeping the first date) is tested; this arm deserves the same one line: restore an undeleted story → UpdatedAt unchanged.

The coverage numbers behind this (local, coverlet): Chapter.cs 72.7% line — the clear outlier (Story 93%, Document 96%, DocumentCategory 95%, Segment 96%, Provenance 100%); items 1–2 are why. I do not demand 100% on the EF materialization constructors — slice 2's job, declared and agreed~ — but public mutation methods on brand-new entities are not optional coverage.

💡 Little ideas (non-blocking)~

  1. Document.cs:83 + Segment.cs:102 — the null-tolerance arms (t?.Trim() ?? "", text ?? "") are unexercised; one-line pins (SetTriggers([null!, "x"], …), Prose(…, null!, …)) if you want the defensive story told. Genuinely defensive, so just a nicety.
  2. Story.cs:87 (SetActiveGraph) — accepts Guid.Empty as a graph id, while Provenance.FromRun rejects empty Guids by this codebase's own convention. Worth a guard when Phase 2 makes the id real.
  3. PR body — "34 new domain tests" is 31 new (3 are ResultTests from Phase 0); the full-suite 72/72 is exact.

✅ What I liked~

  • The provenance algebra — FromRun validation, once-only AfterUserEdit(), and the directional pair (run text marks, writer text doesn't) your own PR body calls out as drift-prone. That is how you pin semantics~
  • DocumentPlacement as a smart record with Of(kind, distance) rehydration that refuses to smuggle a distance into the Header — placement invariants live in the type. ♪
  • EffectiveMode refusing a foreign category, IsTriggeredBy empty-triggers→false with its directional test, trigger normalization deduping case-insensitively — ADR 0013/0017 turned into code with teeth.
  • The csproj comment rewritten to tell the truth about what lives in Domain now, instead of accumulating a stale map. Documentation that breathes~
  • Text.BlankToNull — "absent is a real state, never an empty string" — one helper, one doctrine, used everywhere. DRY done right.

Automated review by Jibril · 2026-09-18
CI/CD: absent for head d9860d3 (PR minutes old, no coverage comment yet) · Local checks: full solution 72/72, TreatWarningsAsErrors clean, per-file coverage collected, 3 mutation probes surviving (evidence above)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my~ A *pure* domain slice — no framework cooties anywhere, just entities wearing their nicest invariants, every mutation carrying its own timestamp like a good little audit trail ♪ I read all 18 files against ADR 0012, 0013, 0017 and 0022, and against the Orihon entities you named as the convention source. The model itself is faithful~ The provenance algebra alone deserves a hug: `AfterUserEdit()` flipping `edited` exactly once and returning `this` for the writer's own text is precisely ADR 0012's semantics, with both arms tested directionally. ### Verdict: ⛔ I can't let this pass~ ♡ The code is *correct* — the tests have holes, and I have the corpses to prove it. Fufu~ you wouldn't leave whole public methods unwatched in production, would you? ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **`src/Tsumugi.Domain/Stories/Chapter.cs:47` — `Rename` has zero behavioral coverage.** No test ever calls `Chapter.Rename` successfully — `ChapterTests` only constructs and throws. I mutation-probed it on this head: removing the `title.Trim()` (the exact normalization the method exists to perform) leaves the suite **green at 34/34**. Every sibling has this test — Orihon's own `ChapterTests.Rename_trims_and_stamps` (the convention source this PR cites), your `StoryTests.Rename_and_describe_trim_and_stamp`, and `DocumentCategoryTests`' rename inside `Every_setter_stamps`. Chapter alone dropped it. Fix: add `Rename_trims_and_stamps` mirroring the Orihon sibling (trim + `UpdatedAt` stamp + blank rejection). 2. **`src/Tsumugi.Domain/Stories/Chapter.cs:55` — `MoveTo`'s success path is untested.** Only the negative-order throw is pinned; I deleted the body's two assignments (`Order`, `UpdatedAt`) and the suite stayed **34/34 green** — a silent no-op reorder would merge undetected. Orihon's `MoveTo_repositions_but_never_below_zero` covers exactly this arm. Fix: assert `Order` and `UpdatedAt` after a successful `MoveTo(4, T1)` (the throw assertion already exists). 3. **`src/Tsumugi.Domain/Stories/Story.cs:96` — `Restore`'s no-op guard branch is never exercised.** Mutating `Restore` to unconditionally clear `SoftDeletedAt` and stamp `UpdatedAt` survives green — a restore of a live story would silently churn `UpdatedAt` and no test would notice. The mirror (`SoftDelete` idempotence keeping the first date) *is* tested; this arm deserves the same one line: restore an undeleted story → `UpdatedAt` unchanged. The coverage numbers behind this (local, coverlet): `Chapter.cs` **72.7% line** — the clear outlier (Story 93%, Document 96%, DocumentCategory 95%, Segment 96%, Provenance 100%); items 1–2 are why. I do *not* demand 100% on the EF materialization constructors — slice 2's job, declared and agreed~ — but public mutation methods on brand-new entities are not optional coverage. #### 💡 Little ideas (non-blocking)~ 1. **`Document.cs:83` + `Segment.cs:102`** — the null-tolerance arms (`t?.Trim() ?? ""`, `text ?? ""`) are unexercised; one-line pins (`SetTriggers([null!, "x"], …)`, `Prose(…, null!, …)`) if you want the defensive story told. Genuinely defensive, so just a nicety. 2. **`Story.cs:87` (`SetActiveGraph`)** — accepts `Guid.Empty` as a graph id, while `Provenance.FromRun` rejects empty Guids by this codebase's own convention. Worth a guard when Phase 2 makes the id real. 3. **PR body** — "34 new domain tests" is 31 new (3 are `ResultTests` from Phase 0); the full-suite 72/72 is exact. #### ✅ What I liked~ - The **provenance algebra** — `FromRun` validation, once-only `AfterUserEdit()`, and the directional pair (run text marks, writer text doesn't) your own PR body calls out as drift-prone. That is how you pin semantics~ - `DocumentPlacement` as a smart record with `Of(kind, distance)` rehydration that refuses to smuggle a distance into the Header — placement invariants live *in the type*. ♪ - `EffectiveMode` refusing a foreign category, `IsTriggeredBy` empty-triggers→false with its directional test, trigger normalization deduping case-insensitively — ADR 0013/0017 turned into code with teeth. - The `csproj` comment rewritten to tell the truth about what lives in Domain now, instead of accumulating a stale map. Documentation that breathes~ - `Text.BlankToNull` — "absent is a real state, never an empty string" — one helper, one doctrine, used everywhere. DRY done right. --- *Automated review by Jibril · 2026-09-18* *CI/CD: absent for head d9860d3 (PR minutes old, no coverage comment yet) · Local checks: full solution 72/72, TreatWarningsAsErrors clean, per-file coverage collected, 3 mutation probes surviving (evidence above)*
test: chapter rename and move success paths, the restore no-op, the empty-guid graph guard, and the null-tolerance pins
All checks were successful
CI / build (pull_request) Successful in 25s
CI / test (pull_request) Successful in 33s
272b03ff9e
Chapter.Rename and MoveTo had only their throwing arms covered (removing the trim or the body
survived green); Story.Restore's no-op guard was unexercised. Story.SetActiveGraph now rejects
Guid.Empty like Provenance.FromRun does. Two pins for the defensive null arms in Document.SetTriggers
and Segment text.

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

Addressed in 272b03f. Domain suite 34 → 40; solution 78/78.

⛔ Blockers

  1. Chapter.Rename uncovered — Rename_trims_and_stamps: trims, stamps UpdatedAt, rejects blank. Removing the Trim() now fails it.
  2. Chapter.MoveTo success path — MoveTo_repositions_and_stamps asserts Order and UpdatedAt after MoveTo(4, T1); the throw arm stays where it was.
  3. Story.Restore no-op arm — Restoring_a_story_that_is_not_in_the_trash_changes_nothing: IsDeleted stays false and UpdatedAt stays T0, so the unconditional-clear mutation now fails.

💡 Ideas

  1. Null-tolerance pins — taken: A_null_trigger_is_dropped_like_a_blank_one (SetTriggers([null!, "x"]) → ["x"]) and Null_text_is_the_empty_segment_too (Prose(…, null!, …) and Edit(null!) → "").
  2. SetActiveGraph(Guid.Empty) — taken now rather than in Phase 2: it throws ArgumentException like Provenance.FromRun, pinned by An_empty_guid_is_not_a_graph_id.
  3. Count — you are right: 31 new in the first head (3 were Phase 0's ResultTests); with this commit it is 37 new, 40 in the project.
Addressed in **272b03f**. Domain suite 34 → **40**; solution 78/78. ## ⛔ Blockers 1. **`Chapter.Rename` uncovered** — `Rename_trims_and_stamps`: trims, stamps `UpdatedAt`, rejects blank. Removing the `Trim()` now fails it. 2. **`Chapter.MoveTo` success path** — `MoveTo_repositions_and_stamps` asserts `Order` and `UpdatedAt` after `MoveTo(4, T1)`; the throw arm stays where it was. 3. **`Story.Restore` no-op arm** — `Restoring_a_story_that_is_not_in_the_trash_changes_nothing`: `IsDeleted` stays false and `UpdatedAt` stays `T0`, so the unconditional-clear mutation now fails. ## 💡 Ideas 1. Null-tolerance pins — taken: `A_null_trigger_is_dropped_like_a_blank_one` (`SetTriggers([null!, "x"])` → `["x"]`) and `Null_text_is_the_empty_segment_too` (`Prose(…, null!, …)` and `Edit(null!)` → `""`). 2. `SetActiveGraph(Guid.Empty)` — taken now rather than in Phase 2: it throws `ArgumentException` like `Provenance.FromRun`, pinned by `An_empty_guid_is_not_a_graph_id`. 3. Count — you are right: 31 new in the first head (3 were Phase 0's `ResultTests`); with this commit it is 37 new, 40 in the project.
Member

🔮 fufu~ Jibril reviewed your code!

Back so soon~? One little "Addressed in 272b03f" and here I am, magnifier in hand ♪ The increment is surgical — +70/−0 across exactly 5 files (1 src, 4 test), zero scope creep, and the only production change is the new SetActiveGraph guard. Everything else is teeth. I like teeth~

Verdict: ✅ Looks good to me~

All three blockers closed, and I didn't take your word for it — I re-ran my probes on this head:

⛔ → ✅ The three blockers, verified dead~

  1. Chapter.Rename — Rename_trims_and_stamps trims, stamps, and rejects blank. Mutation probe: deleting the Trim() → exactly that test red (39/40 green). Dead~
  2. Chapter.MoveTo success path — MoveTo_repositions_and_stamps asserts Order and UpdatedAt. Mutation probe: both assignments deleted → exactly that test red. Dead~
  3. Story.Restore no-op arm — Restoring_a_story_that_is_not_in_the_trash_changes_nothing pins IsDeleted false + UpdatedAt == T0. Mutation probe: unconditional clear → exactly that test red. Dead~

And because you went beyond the ask, I probed the bonus too: removing the SetActiveGraph(Guid.Empty) guard → An_empty_guid_is_not_a_graph_id red. 4/4 probes, each felled by its own killer and nothing else — these are directional tests, not tautologies. That's the standard~ ♡

💡 Ideas — all three taken, thank you~

  • Null-tolerance arms pinned (SetTriggers([null!, "x"]) → ["x"]; Prose(…, null!)/Edit(null!) → ""), and the empty-guid guard moved up to now rather than Phase 2 — matching Provenance.FromRun's convention. Correct call; it's cheaper to build the wall before the house.
  • The count correction (31 → 37 new) — honest arithmetic is my favorite kind.

✅ What I liked~

  • Fix-reply discipline: every blocker answered by name, every claim checkable, and every claim checked out. 40/40 domain, 78/78 full solution — matches your arithmetic exactly.
  • Tests that assert the absence of churn (UpdatedAt stays T0 on a no-op restore) — most suites only ever assert that things happen; yours also asserts that nothing does. ♪

On to slice 2 — the EF mapping and those private materialization constructors are waiting for their converter rendezvous~ fufu~


Automated review by Jibril · 2026-09-18
CI/CD: absent for head 272b03f (no coverage bot comment yet) · Local checks: full solution 78/78 (15 adapter + 40 domain + 22 integration + 1 use-cases), 4/4 mutation probes red-on-own-test at head, clone restored pristine

## 🔮 fufu~ Jibril reviewed your code! Back so soon~? One little "Addressed in 272b03f" and here I am, magnifier in hand ♪ The increment is surgical — **+70/−0 across exactly 5 files** (1 src, 4 test), zero scope creep, and the only production change is the new `SetActiveGraph` guard. Everything else is teeth. I like teeth~ ### Verdict: ✅ Looks good to me~ All three blockers closed, and I didn't take your word for it — I re-ran my probes on this head: #### ⛔ → ✅ The three blockers, verified dead~ 1. **`Chapter.Rename`** — `Rename_trims_and_stamps` trims, stamps, and rejects blank. Mutation probe: deleting the `Trim()` → **exactly that test red** (39/40 green). Dead~ 2. **`Chapter.MoveTo` success path** — `MoveTo_repositions_and_stamps` asserts `Order` and `UpdatedAt`. Mutation probe: both assignments deleted → **exactly that test red**. Dead~ 3. **`Story.Restore` no-op arm** — `Restoring_a_story_that_is_not_in_the_trash_changes_nothing` pins `IsDeleted` false + `UpdatedAt` == T0. Mutation probe: unconditional clear → **exactly that test red**. Dead~ And because you went beyond the ask, I probed the bonus too: removing the `SetActiveGraph(Guid.Empty)` guard → `An_empty_guid_is_not_a_graph_id` red. 4/4 probes, each felled by its own killer and nothing else — these are directional tests, not tautologies. That's the standard~ ♡ #### 💡 Ideas — all three taken, thank you~ - Null-tolerance arms pinned (`SetTriggers([null!, "x"])` → `["x"]`; `Prose(…, null!)`/`Edit(null!)` → `""`), and the empty-guid guard moved up to now rather than Phase 2 — matching `Provenance.FromRun`'s convention. Correct call; it's cheaper to build the wall before the house. - The count correction (31 → 37 new) — honest arithmetic is my favorite kind. #### ✅ What I liked~ - Fix-reply discipline: every blocker answered by name, every claim checkable, and every claim **checked out**. 40/40 domain, **78/78 full solution** — matches your arithmetic exactly. - Tests that assert the *absence* of churn (`UpdatedAt` stays T0 on a no-op restore) — most suites only ever assert that things happen; yours also asserts that nothing does. ♪ On to slice 2 — the EF mapping and those private materialization constructors are waiting for their converter rendezvous~ fufu~ --- *Automated review by Jibril · 2026-09-18* *CI/CD: absent for head 272b03f (no coverage bot comment yet) · Local checks: full solution 78/78 (15 adapter + 40 domain + 22 integration + 1 use-cases), 4/4 mutation probes red-on-own-test at head, clone restored pristine*
bjoern merged commit bc4da66a31 into main 2026-09-18 19:05:04 +02:00
bjoern deleted branch feat/p1-domain 2026-09-18 19:05:04 +02:00

🔄 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: 272b03f · Generated: 2026-09-18 17:05:29 UTC · Revision: #1

Summary

Summary
Generated on: 09/18/2026 - 17:05:29
Coverage date: 09/18/2026 - 17:05:24 - 09/18/2026 - 17:05:27
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 34
Files: 31
Line coverage: 90.9% (595 of 654)
Covered lines: 595
Uncovered lines: 59
Coverable lines: 654
Total lines: 1876
Branch coverage: 79.1% (133 of 168)
Covered branches: 133
Total branches: 168
Method coverage: Feature is only available for sponsors

Coverage

Tsumugi.BlazorAdapter - 74.4%
Name Line Branch
Tsumugi.BlazorAdapter 74.4% 64.2%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 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 50% 0%
Tsumugi.BlazorAdapter.Runs.RunChangedBridge 97.1% 90.6%
Tsumugi.BlazorAdapter.Runs.RunChangesReceived 100%
Tsumugi.BlazorAdapter.Runs.RunTextReceived 100%
Tsumugi.Domain - 96%
Name Line Branch
Tsumugi.Domain 96% 100%
Tsumugi.Domain.Documents.Document 96.2% 100%
Tsumugi.Domain.Documents.DocumentCategory 95.2%
Tsumugi.Domain.Documents.DocumentPlacement 100% 100%
Tsumugi.Domain.Stories.Chapter 93.9% 100%
Tsumugi.Domain.Stories.Provenance 100% 100%
Tsumugi.Domain.Stories.Segment 95.7% 100%
Tsumugi.Domain.Stories.Story 95.7% 100%
Tsumugi.Domain.Text 100% 100%
Tsumugi.Infrastructure - 93.2%
Name Line Branch
Tsumugi.Infrastructure 93.2% 75%
Tsumugi.Infrastructure.DependencyInjection 100%
Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus 100% 100%
Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor 55.5% 0%
Tsumugi.Infrastructure.Persistence.TsumugiDbContext 100%
Tsumugi.Infrastructure.Runs.InProcessRunEventBus 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% 81.5%
Program 100% 85.7%
Tsumugi.Server.Components.App 100%
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 - 100%
Name Line Branch
Tsumugi.UseCases 100% ****
Tsumugi.UseCases.DependencyInjection 100%
Tsumugi.UseCases.Notifications.DomainChanged 100%
Tsumugi.UseCases.Runs.RunEvent 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:** `272b03f` · **Generated:** 2026-09-18 17:05:29 UTC · **Revision:** #1 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/18/2026 - 17:05:29 | | Coverage date: | 09/18/2026 - 17:05:24 - 09/18/2026 - 17:05:27 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 34 | | Files: | 31 | | **Line coverage:** | 90.9% (595 of 654) | | Covered lines: | 595 | | Uncovered lines: | 59 | | Coverable lines: | 654 | | Total lines: | 1876 | | **Branch coverage:** | 79.1% (133 of 168) | | Covered branches: | 133 | | Total branches: | 168 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Tsumugi.BlazorAdapter - 74.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**74.4%**|**64.2%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|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|50%|0%| |Tsumugi.BlazorAdapter.Runs.RunChangedBridge|97.1%|90.6%| |Tsumugi.BlazorAdapter.Runs.RunChangesReceived|100%|| |Tsumugi.BlazorAdapter.Runs.RunTextReceived|100%|| </details> <details><summary>Tsumugi.Domain - 96%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**96%**|**100%**| |Tsumugi.Domain.Documents.Document|96.2%|100%| |Tsumugi.Domain.Documents.DocumentCategory|95.2%|| |Tsumugi.Domain.Documents.DocumentPlacement|100%|100%| |Tsumugi.Domain.Stories.Chapter|93.9%|100%| |Tsumugi.Domain.Stories.Provenance|100%|100%| |Tsumugi.Domain.Stories.Segment|95.7%|100%| |Tsumugi.Domain.Stories.Story|95.7%|100%| |Tsumugi.Domain.Text|100%|100%| </details> <details><summary>Tsumugi.Infrastructure - 93.2%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**93.2%**|**75%**| |Tsumugi.Infrastructure.DependencyInjection|100%|| |Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus|100%|100%| |Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor|55.5%|0%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContext|100%|| |Tsumugi.Infrastructure.Runs.InProcessRunEventBus|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%**|**81.5%**| |Program|100%|85.7%| |Tsumugi.Server.Components.App|100%|| |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 - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**100%**|****| |Tsumugi.UseCases.DependencyInjection|100%|| |Tsumugi.UseCases.Notifications.DomainChanged|100%|| |Tsumugi.UseCases.Runs.RunEvent|100%|| </details>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 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!4
No description provided.