feat(commit): the commit node lands a run's prose, and the playground reads the registry (slice 9c) #64

Merged
bjoern merged 5 commits from feat/commit-node into main 2026-09-27 00:42:04 +02:00
Member

This is slice 9c of Phase 2, the last of three PRs for "the story half":

  • 9a (#50): story.* and the document rendering.
  • 9b (#60): the context node.
  • 9c (this PR): the commit node, and the registry in the playground.

The commit node lands a run's prose in the manuscript. It reads the model's text with the lenient reader and appends it to the run's target chapter as one journaled operation of the run. Each paragraph is credited to the node and model that wrote it. The playground's canvas now reads its node types from the registry, through a use case, and its descriptor literal is gone.

Commits (rebased on main 9324103, #63):

  • 4817dbd: the node, its write path, the registry in the playground, tests and docs.
  • bb883bf: the review's round 0. It puts the chapter delete, merge and move under the write lock, decides the fallback chapter there, and makes the commit land once per run.
  • 4f227c0: the second pass. A commit lands once per run whatever became of that landing, and a Retry answers in reading order.
  • 8054fa1: this PR's number in ADR 0012, ADR 0015 and the plan.
  • 376596e: Jibril's 💡, the store refuses a merge of a chapter into itself.

What

Tsumugi.UseCases/Commit:

  • CommitConfig has two settings, both optional, so {} is a valid config:

    • mode: append. It is the one mode until the target sink and the target chapter setting arrive in Phase 3 with replace and the inserts.
    • split: true by default, so each blank line starts a new paragraph. Set to false, the whole text lands as one segment, its paragraphs a blank line apart.

    Every problem is reported at its JSON pointer, and an unknown key is refused.

  • CommitDescriptor is the playground's canvas entry: group Output, colour --warning, a required text sink and a segments json source. It has one attempt, as ADR 0016 gives every type but llm and context, and it takes no slot of the LLM limit. Its config schema agrees with ValidateConfig.

  • CommitExecutor:

    • reads the text with ModelOutput (ADR 0030): emphasis is read, a model's 5 * 3 * 2 stays literal, and a paragraph that is only *** or * * * becomes a scene break;
    • credits each segment Provenance.FromRun(run, upstream node, model) from the upstream row of text, where the model is the one the provider served, else the one the node asked for;
    • outputs segments as {chapterId, segmentIds, text}: the chapter, the segments in reading order, and their text as a template reads story text;
    • reports the chapter it chose, whom it credited, and whether an earlier attempt had landed the text (earlier).
  • AppendRunText is the commit's write path, and the seed uses it too:

    • It writes one operation of the run: origin run, the commit's node id, and a label like "Commit appended 2 paragraphs and a scene break".
    • The node is named by its title, or by its id when the title is blank.
    • It lands once per run and node. A Retry of an attempt whose write landed but whose result was lost finds that landing in the run's journal and writes nothing, whatever became of it since: kept, edited, moved, deleted, or taken back by Revert run (which Redo run brings back). It answers with what of that landing the manuscript still holds.

The stores:

  • ISegmentStore.AppendAsync decides everything under the database's write lock (ADR 0028):

    • the chapter: the segments' chapter while the story still has it, else the story's last chapter (ADR 0012);
    • the places: after that chapter's last segment.

    It returns the chapter it landed in.

  • IChapterStore.DeleteAsync reads the chapter's segments under the lock, so a paragraph a commit lands just as the delete begins is deleted with a journal row, and Undo brings it back.

  • The new IChapterStore.MergeAsync moves the source's segments and deletes the source in one locked save; MergeChapters calls it. It refuses a merge of a chapter into itself.

  • MoveToChapterAsync reads the target's last place under the lock.

The playground gets the canvas JSON from a new GetNodeTypes use case, so the adapter never names an engine type (ADR 0003). AddInfrastructure registers the engine core (AddKagakuFlow), whose registry collects context, commit and llm. The engine itself resolves once 11a registers its ledger and event sink. SampleGraph.DescriptorsJson is deleted. The engine tests' copy of it (sample-descriptors.json) stays, and PlaygroundRegistryTests now checks it against the real registry.

The seed lands its run paragraphs through AppendRunText. History shows the commit's operation ("Commit appended 2 paragraphs", node commit), and the paragraphs stay credited to writer on seed/model.

Docs:

  • ADR 0015 and 0012 are amended (below).
  • AGENTS.md now names AppendRunText as the commit's run-write path.
  • AGENTS.md's WriteAsync rule now covers chapter and segment store mutators.

Why

  • ADR 0015: the commit node, its sockets and config, provenance from the upstream row, one journaled operation, and segments for a follow-up node.
  • ADR 0012: a run's target chapter, and a segment credited to the node and model that wrote it.
  • ADR 0022: one operation per commit, so Revert run takes it back in one step (tested).
  • ADR 0030: model output goes through the lenient reader and is stored as canonical markup.
  • ADR 0028: WriteAsync. A commit writes while you edit, so a chapter write that reads outside the lock can lose run prose.
  • The plan: "the registry serves context, llm and commit, the playground reads it (SampleGraph.DescriptorsJson deleted)".

Decisions to confirm

Decision ADR
segments is {chapterId, segmentIds, text}. The ids are the refs a Phase 3 target will take. The text lets a follow-up node read what landed in a template ({{ inputs.segments.text }}) without a tool. 0015
The model credited is the one the provider served, else the one the node asked for. Otherwise an alias like openrouter/auto would be credited instead of the model that wrote the prose. 0012, 0015
A target chapter deleted while the run lives falls back to the story's last chapter, as story.* and the context node already read it. A commit decides this under the write lock, so a delete that lands at that moment is seen. The target is current again once Undo restores it. The prose continues where the story now ends rather than failing the run. 0012
One attempt, and one landing per run. ADR 0016 gives every type but llm and context one attempt. A crash or a lost settle can orphan an attempt whose write landed; its Retry then writes nothing, whatever became of that landing. Revert run followed by Redo run is how reverted text comes back, so a Retry never duplicates prose. Phase 3's map and loop will key this on the iteration too. 0015, 0016, 0022
An empty text fails the row, writing nothing, rather than succeeding with nothing. A run that "succeeded" but wrote nothing would hide a model's failure. 0015
The label names the node by its title, or by its id when the title is blank: "Commit appended 3 paragraphs", "… a paragraph and 2 scene breaks". 0015, 0022
split: false joins the paragraphs a blank line apart inside one segment, a scene break kept as its *** text. A lone paragraph, including a lone scene break, stays as it reads. 0015, 0030
The chapter delete, merge and move now take the write lock. This was found in review: without it, a paragraph a commit lands just as you delete a chapter vanished with no journal row. The merge becomes one store step. The race also existed with a second tab's typing, before this slice. 0022, 0028
The seed's run paragraphs go through the commit's write path. Their operation now reads "Commit appended 2 paragraphs" under node commit, where it read "Writer committed 2 paragraphs" under writer. 11b adds the seeded ledger runs. AGENTS.md

Out of scope

  • replace, insertAfter, insertBefore, the target sink and the target chapter setting (Phase 3).
  • Runs starting from the UI, run snapshots and the seeded ledger runs (11a, 11b).
  • Commit this text and Retry in the inspector (14).

Tests

Every project was run one at a time, after a full build including the script modules, on main 9324103:

Project main branch
Tsumugi.Domain.Tests 486 486
Kagaku.Flow.Tests 1103 1103
Kagaku.Flow.OpenRouter.Tests 596 (4 live, skipped) 596
Tsumugi.BlazorAdapter.Tests 528 529 (+1: the playground tests, +2; the descriptor drift row, −1)
Tsumugi.UseCases.Tests 321 365 (+44: config 12, descriptor and schema 15, text, label and guards 17)
Tsumugi.Integration.Tests 580 621 (+41: CommitNodeTests 29, the store 11, the registry against the engine fixture 1)

All pass.

CommitNodeTests runs the engine over the real stores. Beyond landing, provenance, output, report, label and failures, it covers:

  • a Retry after a lost settle, with nothing in between, after a reorder, after a merge, and after Revert run;
  • Revert run, then Retry, then Redo run, which lands once;
  • two commits in one run;
  • a target chapter deleted as the commit lands;
  • Revert run.

A new test helper, WriteHook, runs a write just as another write's database transaction begins. The store tests use it to race each locked path against a write landing in between: the append's place, the fallback, the delete, the merge (from the source side and the target side), and the move.

Coverage

Coverlet with branch coverage, on the use-case, integration and adapter tests: 100% of lines and branches on every line this PR adds or changes.

File Lines Branches
Commit/AppendRunText.cs 44/44 14/14 ¹
Commit/CommitConfig.cs 30/30 18/18
Commit/CommitDescriptor.cs 21/21 —
Commit/CommitExecutor.cs 51/51 16/16
Graphs/GetNodeTypes.cs 2/2 —
Chapters/MergeChapters.cs 17/17 8/8
Stores/EfSegmentStore.cs 184/184 32/32
Stores/EfChapterStore.cs 91/91 4/4 ²
DependencyInjection.cs (UseCases, Infrastructure) 71/71, 36/36 —, 6/6
Graphs/GraphPlaygroundPage.razor 17/17 4/4
Diagnostics/SeedDevData.cs 118/120 11/16 ³

¹ The run-id guard's two arms are covered by two projects: the unit test takes the refusal and the integration tests the pass. The merge tool reports the higher of the two per line, so it shows 13/14.
² Coverlet reports no branch data inside WriteAsync lambdas in this file. The two operands of the merge's "chapter gone" check each have a test.
³ These gaps predate this PR: the seed's refusal arms, which throw only if the seed itself is wrong.

Mutation probes

Each probe is a single edit (or a lock swapped for a no-lock helper), built and tested on its own, one at a time.

  • The node: 31 probes. 30 were killed at first; the survivor (the report's relaxed escaping) got a test and is now killed too.
  • Round 0: 20 probes. 17 were killed by tests; the other 3 were refused by the compiler, and their compiling variants are among the 17. Three that first survived got deterministic tests:
    • the journal's node filter, now caught by two commits in sequence;
    • the Retry's answer order, now caught by a reorder case;
    • the merge's reading order for tied places.
  • The second pass: 4 probes on the reworked land-once rule, all killed.
  • Jibril's 💡: 1 probe on the merge-into-itself guard, killed.
  • The reviewers' own probes, run on a copy of the branch, cover each lock, the fallback, and the WriteAsync-less variants of the append, delete, merge and move.

Reviews

Each round was a review workflow followed by a skeptic for each dimension, one agent at a time on one shared copy of the branch.

  • Round 0: 23 findings; the skeptics upheld 21 (16 confirmed, 5 partly) and refuted 2 (D6, D9). All 21 were taken in bb883bf, plus D9's optional note. bb883bf's message says 14 confirmed and 7 partly, which is wrong; the right split is 16 and 5. The main findings:
    • the unlocked chapter delete and merge could lose run prose (A1);
    • a target chapter deleted as the commit landed failed the run (A2);
    • a Retry after a lost settle landed the text twice (A3);
    • the append's lock had no test (A4, C1);
    • the page read the engine directly (B1);
    • test gaps (C2–C8) and doc sentences (D1–D8).
  • Second pass: it checked the 22 fixes, 20 resolved and 2 partly (A3, C8). It raised 14 new findings, 13 confirmed and 1 refuted (F6). All 13 were taken in 4f227c0:
    • Revert run, then Retry, then Redo run landed the text twice (K1, F1);
    • a Retry answered in creation order, not reading order (K2, M1, F4);
    • the docs on the answer's chapter (M2, F3);
    • the merge target's lock had no test (M3);
    • a null-reference race in a Retry (M4);
    • a test that caught its target only by chance (F2);
    • an untested operand of the merge check (F5);
    • the miscount above (M5, F7).

Browser pass

  • Release build with the seed, on the first commit:
    • The playground draws Context, Writer and Commit with the registry's sockets and colours, the same as the old literal.
    • History expands the seeded run to "Commit appended 2 paragraphs".
    • The chapter gutter credits those paragraphs to Run · writer · seed/model.
  • Again after the review rounds, on the rebased head:
    • The playground renders through GetNodeTypes.
    • Merge into previous moved The Sister's House's 3 paragraphs into The Road North (6).
    • Undo restored both chapters.

🤖 Generated with Claude Code

This is slice 9c of Phase 2, the last of three PRs for "the story half": - 9a (#50): `story.*` and the document rendering. - 9b (#60): the `context` node. - 9c (this PR): the `commit` node, and the registry in the playground. The `commit` node lands a run's prose in the manuscript. It reads the model's text with the lenient reader and appends it to the run's target chapter as one journaled operation of the run. Each paragraph is credited to the node and model that wrote it. The playground's canvas now reads its node types from the registry, through a use case, and its descriptor literal is gone. **Commits** (rebased on main 9324103, #63): - 4817dbd: the node, its write path, the registry in the playground, tests and docs. - bb883bf: the review's round 0. It puts the chapter delete, merge and move under the write lock, decides the fallback chapter there, and makes the commit land once per run. - 4f227c0: the second pass. A commit lands once per run whatever became of that landing, and a Retry answers in reading order. - 8054fa1: this PR's number in ADR 0012, ADR 0015 and the plan. - 376596e: Jibril's 💡, the store refuses a merge of a chapter into itself. ## What **`Tsumugi.UseCases/Commit`:** - **`CommitConfig`** has two settings, both optional, so `{}` is a valid config: - `mode`: `append`. It is the one mode until the `target` sink and the target chapter setting arrive in Phase 3 with replace and the inserts. - `split`: true by default, so each blank line starts a new paragraph. Set to false, the whole text lands as one segment, its paragraphs a blank line apart. Every problem is reported at its JSON pointer, and an unknown key is refused. - **`CommitDescriptor`** is the playground's canvas entry: group Output, colour `--warning`, a required `text` sink and a `segments` json source. It has one attempt, as ADR 0016 gives every type but `llm` and `context`, and it takes no slot of the LLM limit. Its config schema agrees with `ValidateConfig`. - **`CommitExecutor`**: - reads the text with `ModelOutput` (ADR 0030): emphasis is read, a model's `5 * 3 * 2` stays literal, and a paragraph that is only `***` or `* * *` becomes a scene break; - credits each segment `Provenance.FromRun(run, upstream node, model)` from the upstream row of `text`, where the model is the one the provider served, else the one the node asked for; - outputs `segments` as `{chapterId, segmentIds, text}`: the chapter, the segments in reading order, and their text as a template reads story text; - reports the chapter it chose, whom it credited, and whether an earlier attempt had landed the text (`earlier`). - **`AppendRunText`** is the commit's write path, and the seed uses it too: - It writes one operation of the run: origin run, the commit's node id, and a label like *"Commit appended 2 paragraphs and a scene break"*. - The node is named by its title, or by its id when the title is blank. - **It lands once per run and node.** A Retry of an attempt whose write landed but whose result was lost finds that landing in the run's journal and writes nothing, whatever became of it since: kept, edited, moved, deleted, or taken back by Revert run (which Redo run brings back). It answers with what of that landing the manuscript still holds. **The stores:** - `ISegmentStore.AppendAsync` decides everything under the database's write lock (ADR 0028): - the chapter: the segments' chapter while the story still has it, else the story's last chapter (ADR 0012); - the places: after that chapter's last segment. It returns the chapter it landed in. - `IChapterStore.DeleteAsync` reads the chapter's segments under the lock, so a paragraph a commit lands just as the delete begins is deleted with a journal row, and Undo brings it back. - The new `IChapterStore.MergeAsync` moves the source's segments and deletes the source in one locked save; `MergeChapters` calls it. It refuses a merge of a chapter into itself. - `MoveToChapterAsync` reads the target's last place under the lock. **The playground** gets the canvas JSON from a new `GetNodeTypes` use case, so the adapter never names an engine type (ADR 0003). `AddInfrastructure` registers the engine core (`AddKagakuFlow`), whose registry collects `context`, `commit` and `llm`. The engine itself resolves once 11a registers its ledger and event sink. `SampleGraph.DescriptorsJson` is deleted. The engine tests' copy of it (`sample-descriptors.json`) stays, and `PlaygroundRegistryTests` now checks it against the real registry. **The seed** lands its run paragraphs through `AppendRunText`. History shows the commit's operation (*"Commit appended 2 paragraphs"*, node `commit`), and the paragraphs stay credited to `writer` on `seed/model`. **Docs:** - ADR 0015 and 0012 are amended (below). - AGENTS.md now names `AppendRunText` as the commit's run-write path. - AGENTS.md's WriteAsync rule now covers chapter and segment store mutators. ## Why - **ADR 0015:** the `commit` node, its sockets and config, provenance from the upstream row, one journaled operation, and `segments` for a follow-up node. - **ADR 0012:** a run's target chapter, and a segment credited to the node and model that wrote it. - **ADR 0022:** one operation per commit, so Revert run takes it back in one step (tested). - **ADR 0030:** model output goes through the lenient reader and is stored as canonical markup. - **ADR 0028:** WriteAsync. A commit writes while you edit, so a chapter write that reads outside the lock can lose run prose. - **The plan:** "the registry serves context, llm and commit, the playground reads it (SampleGraph.DescriptorsJson deleted)". ## Decisions to confirm | Decision | ADR | |---|---| | **`segments` is `{chapterId, segmentIds, text}`.** The ids are the refs a Phase 3 `target` will take. The text lets a follow-up node read what landed in a template (`{{ inputs.segments.text }}`) without a tool. | 0015 | | **The model credited is the one the provider served**, else the one the node asked for. Otherwise an alias like `openrouter/auto` would be credited instead of the model that wrote the prose. | 0012, 0015 | | **A target chapter deleted while the run lives falls back to the story's last chapter**, as `story.*` and the context node already read it. A commit decides this under the write lock, so a delete that lands at that moment is seen. The target is current again once Undo restores it. The prose continues where the story now ends rather than failing the run. | 0012 | | **One attempt, and one landing per run.** ADR 0016 gives every type but `llm` and `context` one attempt. A crash or a lost settle can orphan an attempt whose write landed; its Retry then writes nothing, whatever became of that landing. Revert run followed by Redo run is how reverted text comes back, so a Retry never duplicates prose. Phase 3's `map` and `loop` will key this on the iteration too. | 0015, 0016, 0022 | | **An empty text fails the row**, writing nothing, rather than succeeding with nothing. A run that "succeeded" but wrote nothing would hide a model's failure. | 0015 | | **The label names the node by its title**, or by its id when the title is blank: *"Commit appended 3 paragraphs"*, *"… a paragraph and 2 scene breaks"*. | 0015, 0022 | | **`split: false` joins the paragraphs a blank line apart** inside one segment, a scene break kept as its `***` text. A lone paragraph, including a lone scene break, stays as it reads. | 0015, 0030 | | **The chapter delete, merge and move now take the write lock.** This was found in review: without it, a paragraph a commit lands just as you delete a chapter vanished with no journal row. The merge becomes one store step. The race also existed with a second tab's typing, before this slice. | 0022, 0028 | | **The seed's run paragraphs go through the commit's write path.** Their operation now reads *"Commit appended 2 paragraphs"* under node `commit`, where it read *"Writer committed 2 paragraphs"* under `writer`. 11b adds the seeded ledger runs. | AGENTS.md | ## Out of scope - `replace`, `insertAfter`, `insertBefore`, the `target` sink and the target chapter setting (Phase 3). - Runs starting from the UI, run snapshots and the seeded ledger runs (11a, 11b). - Commit this text and Retry in the inspector (14). ## Tests Every project was run one at a time, after a full build including the script modules, on main 9324103: | Project | main | branch | |---|---|---| | Tsumugi.Domain.Tests | 486 | 486 | | Kagaku.Flow.Tests | 1103 | 1103 | | Kagaku.Flow.OpenRouter.Tests | 596 (4 live, skipped) | 596 | | Tsumugi.BlazorAdapter.Tests | 528 | 529 (+1: the playground tests, +2; the descriptor drift row, −1) | | Tsumugi.UseCases.Tests | 321 | 365 (+44: config 12, descriptor and schema 15, text, label and guards 17) | | Tsumugi.Integration.Tests | 580 | 621 (+41: `CommitNodeTests` 29, the store 11, the registry against the engine fixture 1) | All pass. `CommitNodeTests` runs the engine over the real stores. Beyond landing, provenance, output, report, label and failures, it covers: - a Retry after a lost settle, with nothing in between, after a reorder, after a merge, and after Revert run; - Revert run, then Retry, then Redo run, which lands once; - two commits in one run; - a target chapter deleted as the commit lands; - Revert run. A new test helper, `WriteHook`, runs a write just as another write's database transaction begins. The store tests use it to race each locked path against a write landing in between: the append's place, the fallback, the delete, the merge (from the source side and the target side), and the move. ## Coverage Coverlet with branch coverage, on the use-case, integration and adapter tests: **100% of lines and branches on every line this PR adds or changes.** | File | Lines | Branches | |---|---|---| | `Commit/AppendRunText.cs` | 44/44 | 14/14 ¹ | | `Commit/CommitConfig.cs` | 30/30 | 18/18 | | `Commit/CommitDescriptor.cs` | 21/21 | — | | `Commit/CommitExecutor.cs` | 51/51 | 16/16 | | `Graphs/GetNodeTypes.cs` | 2/2 | — | | `Chapters/MergeChapters.cs` | 17/17 | 8/8 | | `Stores/EfSegmentStore.cs` | 184/184 | 32/32 | | `Stores/EfChapterStore.cs` | 91/91 | 4/4 ² | | `DependencyInjection.cs` (UseCases, Infrastructure) | 71/71, 36/36 | —, 6/6 | | `Graphs/GraphPlaygroundPage.razor` | 17/17 | 4/4 | | `Diagnostics/SeedDevData.cs` | 118/120 | 11/16 ³ | ¹ The run-id guard's two arms are covered by two projects: the unit test takes the refusal and the integration tests the pass. The merge tool reports the higher of the two per line, so it shows 13/14. ² Coverlet reports no branch data inside `WriteAsync` lambdas in this file. The two operands of the merge's "chapter gone" check each have a test. ³ These gaps predate this PR: the seed's refusal arms, which throw only if the seed itself is wrong. ## Mutation probes Each probe is a single edit (or a lock swapped for a no-lock helper), built and tested on its own, one at a time. - **The node:** 31 probes. 30 were killed at first; the survivor (the report's relaxed escaping) got a test and is now killed too. - **Round 0:** 20 probes. 17 were killed by tests; the other 3 were refused by the compiler, and their compiling variants are among the 17. Three that first survived got deterministic tests: - the journal's node filter, now caught by two commits in sequence; - the Retry's answer order, now caught by a reorder case; - the merge's reading order for tied places. - **The second pass:** 4 probes on the reworked land-once rule, all killed. - **Jibril's 💡:** 1 probe on the merge-into-itself guard, killed. - **The reviewers' own probes**, run on a copy of the branch, cover each lock, the fallback, and the WriteAsync-less variants of the append, delete, merge and move. ## Reviews Each round was a review workflow followed by a skeptic for each dimension, one agent at a time on one shared copy of the branch. - **Round 0:** 23 findings; the skeptics upheld 21 (16 confirmed, 5 partly) and refuted 2 (D6, D9). All 21 were taken in bb883bf, plus D9's optional note. bb883bf's message says 14 confirmed and 7 partly, which is wrong; the right split is 16 and 5. The main findings: - the unlocked chapter delete and merge could lose run prose (A1); - a target chapter deleted as the commit landed failed the run (A2); - a Retry after a lost settle landed the text twice (A3); - the append's lock had no test (A4, C1); - the page read the engine directly (B1); - test gaps (C2–C8) and doc sentences (D1–D8). - **Second pass:** it checked the 22 fixes, 20 resolved and 2 partly (A3, C8). It raised 14 new findings, 13 confirmed and 1 refuted (F6). All 13 were taken in 4f227c0: - Revert run, then Retry, then Redo run landed the text twice (K1, F1); - a Retry answered in creation order, not reading order (K2, M1, F4); - the docs on the answer's chapter (M2, F3); - the merge target's lock had no test (M3); - a null-reference race in a Retry (M4); - a test that caught its target only by chance (F2); - an untested operand of the merge check (F5); - the miscount above (M5, F7). ## Browser pass - Release build with the seed, on the first commit: - The playground draws Context, Writer and Commit with the registry's sockets and colours, the same as the old literal. - History expands the seeded run to *"Commit appended 2 paragraphs"*. - The chapter gutter credits those paragraphs to *Run · writer · seed/model*. - Again after the review rounds, on the rebased head: - The playground renders through `GetNodeTypes`. - Merge into previous moved The Sister's House's 3 paragraphs into The Road North (6). - Undo restored both chapters. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The commit node (ADR 0015) reads its text with the lenient reader (ADR 0030)
and appends it to the run's target chapter as one journaled operation of the
run (ADR 0022), named by the node: "Commit appended 2 paragraphs and a scene
break". Each segment is credited to the upstream node of its text sink and the
model that row reports served, else the one it asked for (ADR 0012). Its
segments output is {chapterId, segmentIds, text}; its report names the chapter
and whom it credited. Config: mode (append, the one mode until the target sink
arrives in Phase 3) and split (default on). One attempt, no slot. An empty text
fails the row and writes nothing.

- AppendRunText is the commit's write path, shared with the seed, whose run
  paragraphs now land as the commit's operation.
- ISegmentStore.AppendAsync places the new segments after the chapter's last
  one under the write lock, so a paragraph typed meanwhile never shares a place.
- AddInfrastructure registers the engine core; the playground hands the canvas
  NodeRegistry.ToCanvasJson() — context, commit and llm — and
  SampleGraph.DescriptorsJson is deleted. The engine tests' copy of those
  entries is now held against the real registry (PlaygroundRegistryTests).
- ADR 0015 and 0012 amended: the Phase 2 config, the segments shape, the model
  credited, the label, one attempt, and a deleted target chapter falling back
  to the last for every read and commit alike. AGENTS.md names AppendRunText.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The review workflow's round 0 on 8c13daa: 23 findings, 21 upheld by their skeptics
(14 confirmed, 7 partly), 2 refuted (D6, D9); all 21 taken, and D9's optional note too.

Races with a commit landing (A1, A2, D3, D4):
- EfChapterStore.DeleteAsync reads the chapter's segments under the write lock, so a
  paragraph a commit lands as the delete begins goes with it, journaled, instead of
  vanishing through the cascade with no row to bring it back.
- IChapterStore.MergeAsync moves the source's segments and deletes the source in one
  locked save; MergeChapters calls it instead of a move and a delete.
- MoveToChapterAsync reads the target's last place under the lock.
- ISegmentStore.AppendAsync decides the chapter under the lock: the segments' chapter
  while the story has it, else the story's last (ADR 0012), and returns the chapter it
  landed in; the commit's output and report name that chapter.

One landing per run (A3, D5): AppendRunText looks in the run's journal for segments
this node created that the manuscript still holds; a Retry of an attempt whose write
landed but whose settle was lost answers with them and writes nothing, and the report
says so (earlier: true). Two commit nodes of one run still land each.

The playground reads the node types through a use case, GetNodeTypes, not the engine's
registry (B1, ADR 0003).

Tests (A4, C1-C8): the WriteHook interceptor runs a write as another transaction
begins; with it, the append's lock, the fallback, the delete, the merge and the move
are each tested against a write landing in between. The output test commits text whose
stored form differs from the model's; the commit node's id is no longer its type; a
two-part text not split; a mixed-story batch; the seed's run paragraphs pinned; the
landed segments' stamps. A second commit of a run lands after the first; a Retry after
the writer merged the chapter away answers in the chapter the segments moved to; a merge
keeps the reading order of tied places. 20 probes on this round's code: 17 killed by the
tests, 3 refused by the compiler (their compiling variants are among the 17); R3 (the
answer in creation order) is caught when the ids of one millisecond sort unlike their
creation, five runs in six.

Docs (B2, D1, D2, D4, D5, D7, D8, D9): ADR 0012's fallback sentence says what the code
does (each later read and commit, the target again once restored); ADR 0015 names the
locked writers, both scene-break forms, one landing per run and the target chapter
setting's phase; AGENTS.md's WriteAsync rule covers chapter and segment mutators; the
seed's remark and the engine tests' fixture comments are corrected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(commit): second pass — a commit lands once per run whatever became of it, answering in reading order
Some checks failed
CI / test (pull_request) Has been cancelled
CI / editor-browsers (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
4f227c0fb3
The second pass on 3a73ada (wf_f3fcdd6b-d25) checked round 0's 22 fixes (the 21 upheld
findings and D9's note): 20 resolved, A3 and C8 partly. It met 14 new findings, 13
confirmed by their skeptics, F6 refuted; all 13 taken.

A Retry of a commit whose write landed now finds that landing by this node's rows in
the run's journal, undone ones included, and writes nothing, whatever became of it:
kept, edited, moved, deleted, or taken back by Revert run, which Redo run brings back.
So Revert run, Retry, Redo run leaves one copy, not two interleaved (K1, F1). The
answer is what of that landing the manuscript still holds, in reading order (read
through ListByStoryAsync), and the chapter holding the first of it (K2, M1, F4); no
null when that chapter goes between two reads (M4). ADR 0015 and the docs of RunAppend,
CommittedSegments and CommitReport say so (M2, F3).

Tests: the Retry theory adds a reorder case, whose reading order pins the answer on
every run (F2), and a revert case; Revert run, Retry, Redo run lands once; a merge
places the source after a paragraph landing in its target as it begins (M3); a merge
into a missing target is refused (F5); the landed stamps are the clock the append is
given (C8). Four probes on the new code, all killed.

Round 0's message miscounted its verdicts (M5, F7): of its 21 upheld findings, 16 were
confirmed and 5 partly, not 14 and 7.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs: this PR's number (#64) in ADR 0012, ADR 0015 and the plan's slice 9 line
All checks were successful
CI / build (pull_request) Successful in 43s
CI / editor-browsers (pull_request) Successful in 1m2s
CI / test (pull_request) Successful in 1m40s
8054fa1486
Co-Authored-By: Claude Opus 5.5 (1M context) <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: 376596e · Generated: 2026-09-26 22:36:25 UTC · Revision: #2

Summary

Summary
Generated on: 09/26/2026 - 22:36:24
Coverage date: 09/26/2026 - 22:35:48 - 09/26/2026 - 22:36:21
Parser: MultiReport (6x Cobertura)
Assemblies: 9
Classes: 534
Files: 469
Line coverage: 98.2% (16463 of 16753)
Covered lines: 16463
Uncovered lines: 290
Coverable lines: 16753
Total lines: 36003
Branch coverage: 94.4% (6279 of 6648)
Covered branches: 6279
Total branches: 6648
Method coverage: Feature is only available for sponsors

Coverage

Kagaku.Flow - 99.7%
Name Line Branch
Kagaku.Flow 99.7% 99.2%
Kagaku.Flow.Budgeting.BudgetRequest 100%
Kagaku.Flow.Budgeting.ConsumerBudget 100%
Kagaku.Flow.Execution.NodeExecution 100% 100%
Kagaku.Flow.Execution.NodeOutcome 100% 100%
Kagaku.Flow.Execution.TransientFailureException 100%
Kagaku.Flow.Execution.Upstream 100%
Kagaku.Flow.Extensions.ServiceCollectionExtensions 100%
Kagaku.Flow.FlowJson 100%
Kagaku.Flow.Graphs.GraphDefinition 100%
Kagaku.Flow.Graphs.GraphDocument 100% 100%
Kagaku.Flow.Graphs.GraphEdge 100%
Kagaku.Flow.Graphs.GraphIndex 100% 100%
Kagaku.Flow.Graphs.GraphLoadError 100%
Kagaku.Flow.Graphs.GraphLoadResult 100%
Kagaku.Flow.Graphs.GraphNode 100%
Kagaku.Flow.Graphs.GraphPosition 100%
Kagaku.Flow.Graphs.GraphReader 100% 100%
Kagaku.Flow.Graphs.GraphSchema 100% 100%
Kagaku.Flow.Graphs.GraphShapeException 100%
Kagaku.Flow.Graphs.GraphWriter 100% 100%
Kagaku.Flow.Graphs.SocketRef 100%
Kagaku.Flow.Graphs.WireText 100% 100%
Kagaku.Flow.Nodes.CanvasDescriptorWriter 100% 100%
Kagaku.Flow.Nodes.ConfigProblem 100%
Kagaku.Flow.Nodes.Names 100%
Kagaku.Flow.Nodes.NodeDescriptor 100% 100%
Kagaku.Flow.Nodes.NodeRegistry 100% 100%
Kagaku.Flow.Nodes.NodeSockets 100% 100%
Kagaku.Flow.Nodes.SinkDescriptor 100%
Kagaku.Flow.Nodes.SocketTypes 100% 100%
Kagaku.Flow.Nodes.SourceDescriptor 100%
Kagaku.Flow.Nodes.TemplateField 100%
Kagaku.Flow.Runs.LedgerChange 100% 100%
Kagaku.Flow.Runs.LedgerTransitions 100% 100%
Kagaku.Flow.Runs.NodeAttempt 100%
Kagaku.Flow.Runs.NodeFacts 100%
Kagaku.Flow.Runs.NodeRun 100%
Kagaku.Flow.Runs.Run 100% 100%
Kagaku.Flow.Runs.RunEvent 100%
Kagaku.Flow.Runs.RunStatuses 100% 100%
Kagaku.Flow.Runs.RunTrigger 100% 100%
Kagaku.Flow.Scheduling.Exclusion 100%
Kagaku.Flow.Scheduling.FlowEngine 100% 100%
Kagaku.Flow.Scheduling.FlowEngineOptions 100% 100%
Kagaku.Flow.Scheduling.ReadyRow 100%
Kagaku.Flow.Scheduling.ReadySet 100% 100%
Kagaku.Flow.Scheduling.ReadySetResult 100%
Kagaku.Flow.Scheduling.RunRequest 100% 100%
Kagaku.Flow.Scheduling.RunStartResult 100%
Kagaku.Flow.Scheduling.SocketValues 100% 100%
Kagaku.Flow.Templating.InputsWalker 100% 100%
Kagaku.Flow.Templating.ParsedTemplate 100% 100%
Kagaku.Flow.Templating.SandboxedTemplateContext 100% 100%
Kagaku.Flow.Templating.TemplateError 100%
Kagaku.Flow.Templating.TemplateHost 100% 100%
Kagaku.Flow.Templating.TemplateLimits 100%
Kagaku.Flow.Templating.TemplateModel 100% 100%
Kagaku.Flow.Templating.TemplateProblem 100%
Kagaku.Flow.Templating.TemplateRenderRequest 100% 100%
Kagaku.Flow.Templating.TemplateRenderResult 100%
Kagaku.Flow.Templating.TemplateRun 100%
Kagaku.Flow.Templating.TemplateSinks 100%
Kagaku.Flow.Templating.TemplateValues 100% 100%
Kagaku.Flow.Validation.GraphValidation 100% 100%
Kagaku.Flow.Validation.GraphValidationContext 100%
Kagaku.Flow.Validation.HookFailure 100%
Kagaku.Flow.Validation.ResolvedGraph 100% 100%
Kagaku.Flow.Validation.ValidationError 100%
Kagaku.Flow.Validation.ValidationReport 100%
System.Text.RegularExpressions.Generated 97.4% 92.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2
97.4% 94.4%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1
97.2% 94.4%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0
97.2% 94.4%
Kagaku.Flow.OpenRouter - 100%
Name Line Branch
Kagaku.Flow.OpenRouter 100% 100%
Kagaku.Flow.OpenRouter.Catalog.ModelCapabilities 100% 100%
Kagaku.Flow.OpenRouter.Gateway.LlmCompletion 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterGateway 100% 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup 100% 100%
Kagaku.Flow.OpenRouter.Gateway.SentRequestCapture 100% 100%
Kagaku.Flow.OpenRouter.Gateway.SentRequestCaptureHandler 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmAnswer 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmConfig 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmConfigRead 100%
Kagaku.Flow.OpenRouter.Llm.LlmDescriptor 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmExecutor 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmFacts 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmFailureText 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmMessage 100%
Kagaku.Flow.OpenRouter.Llm.LlmRequestBuilder 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmRequestPlan 100%
Kagaku.Flow.OpenRouter.Llm.LlmRoutingDefaults 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmSampling 100%
Kagaku.Flow.OpenRouter.Llm.LlmSchemaCheck 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmStrictSchemaRule 100% 100%
Kagaku.Flow.OpenRouter.Llm.LlmVocabulary 100% 100%
Kagaku.Flow.OpenRouter.LlmNodeServiceCollectionExtensions 100%
Kagaku.Flow.OpenRouter.OpenRouterServiceCollectionExtensions 100% 100%
Kagaku.Flow.Testing - 100%
Name Line Branch
Kagaku.Flow.Testing 100% 100%
Kagaku.Flow.Testing.FlowEngineDriver 100% 100%
Kagaku.Flow.Testing.InMemoryRunLedger 100% 100%
Kagaku.Flow.Testing.RecordingRunEventSink 100%
Kagaku.Flow.Testing.RunLedgerContract 100% 100%
Kagaku.Flow.Testing.RunLedgerContractException 100%
Tsumugi.BlazorAdapter - 96.5%
Name Line Branch
Tsumugi.BlazorAdapter 96.5% 91.3%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 100%
Tsumugi.BlazorAdapter.Documents.CategoryCreated 100%
Tsumugi.BlazorAdapter.Documents.CategoryEditor 93.2% 88.4%
Tsumugi.BlazorAdapter.Documents.CategorySaved 100%
Tsumugi.BlazorAdapter.Documents.ChangeCategoryFieldsRequested 100%
Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryChoice 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog 100% 94.1%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentCreated 100%
Tsumugi.BlazorAdapter.Documents.DocumentEditor 95.7% 89.4%
Tsumugi.BlazorAdapter.Documents.DocumentImageRefused 100%
Tsumugi.BlazorAdapter.Documents.DocumentImageSet 100%
Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentSaved 100%
Tsumugi.BlazorAdapter.Documents.DocumentsEffects 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsList 98.5% 95%
Tsumugi.BlazorAdapter.Documents.DocumentsLoaded 100%
Tsumugi.BlazorAdapter.Documents.DocumentsPage 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsReadFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentsReducers 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsRefusal 100%
Tsumugi.BlazorAdapter.Documents.DocumentsState 100% 100%
Tsumugi.BlazorAdapter.Documents.FieldInput 100% 96.6%
Tsumugi.BlazorAdapter.Documents.FieldsChanges 100% 100%
Tsumugi.BlazorAdapter.Documents.FieldShapes 90% 87.5%
Tsumugi.BlazorAdapter.Documents.ImageUpload 100%
Tsumugi.BlazorAdapter.Documents.ImageUploadRefusal 100%
Tsumugi.BlazorAdapter.Documents.ImpactText 100% 94.2%
Tsumugi.BlazorAdapter.Documents.LoadDocuments 100%
Tsumugi.BlazorAdapter.Documents.MoveDocumentChoice 100%
Tsumugi.BlazorAdapter.Documents.MoveDocumentDialog 95.6% 71.4%
Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested 100%
Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested 100%
Tsumugi.BlazorAdapter.Documents.SetCategoryCollapsed 100%
Tsumugi.BlazorAdapter.Documents.SetDocumentImageRequested 100%
Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter 100%
Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested 100%
Tsumugi.BlazorAdapter.EffectCalls 100%
Tsumugi.BlazorAdapter.Graphs.GraphCanvas 58.3% 40%
Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage 100% 100%
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.BackupFailed 100%
Tsumugi.BlazorAdapter.Settings.BackupNowRequested 100%
Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.BackupsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsState 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects 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.LoadProviderSettings 100%
Tsumugi.BlazorAdapter.Settings.ModelCatalogLoaded 100%
Tsumugi.BlazorAdapter.Settings.ModelCatalogLoadFailed 100%
Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaved 100%
Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaveFailed 100%
Tsumugi.BlazorAdapter.Settings.ProviderKeyChecked 100%
Tsumugi.BlazorAdapter.Settings.ProviderSection 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsState 100% 100%
Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested 100%
Tsumugi.BlazorAdapter.Settings.SaveOpenRouterKeyRequested 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.AckWire 100%
Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed 100%
Tsumugi.BlazorAdapter.Workspace.ChangedWire 100%
Tsumugi.BlazorAdapter.Workspace.ChapterHeader 100% 86.3%
Tsumugi.BlazorAdapter.Workspace.ChapterMenu 100% 81.2%
Tsumugi.BlazorAdapter.Workspace.ChapterPage 100% 87.5%
Tsumugi.BlazorAdapter.Workspace.ChapterSaved 100%
Tsumugi.BlazorAdapter.Workspace.ChaptersPage 94.4% 83.3%
Tsumugi.BlazorAdapter.Workspace.ChapterWire 100%
Tsumugi.BlazorAdapter.Workspace.ChapterWritten 100%
Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.CreatedWire 100%
Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.DeltaWire 100%
Tsumugi.BlazorAdapter.Workspace.HistoryEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.HistoryLoaded 100%
Tsumugi.BlazorAdapter.Workspace.HistoryPage 100%
Tsumugi.BlazorAdapter.Workspace.HistoryPanel 100% 100%
Tsumugi.BlazorAdapter.Workspace.HistoryReadFailed 100%
Tsumugi.BlazorAdapter.Workspace.LoadHistory 100%
Tsumugi.BlazorAdapter.Workspace.LoadWorkspace 100%
Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.ManuscriptEvents 81.8% 74.1%
Tsumugi.BlazorAdapter.Workspace.ManuscriptSurface 89.8% 87.1%
Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.MergeEvent 100%
Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested 100%
Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.ReshapeEvent 100%
Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested 100%
Tsumugi.BlazorAdapter.Workspace.SaveScope 100% 50%
Tsumugi.BlazorAdapter.Workspace.SaveStateLadder 100% 100%
Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested 100%
Tsumugi.BlazorAdapter.Workspace.SegmentTextEvent 100%
Tsumugi.BlazorAdapter.Workspace.SegmentWire 100% 81.8%
Tsumugi.BlazorAdapter.Workspace.SelectionEndWire 100%
Tsumugi.BlazorAdapter.Workspace.SelectionGraph 100%
Tsumugi.BlazorAdapter.Workspace.SelectionRangeWire 100%
Tsumugi.BlazorAdapter.Workspace.SelectionRunRequested 100%
Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested 100%
Tsumugi.BlazorAdapter.Workspace.SplitEvent 100%
Tsumugi.BlazorAdapter.Workspace.StampWire 0%
Tsumugi.BlazorAdapter.Workspace.StoryDetails 100% 85.2%
Tsumugi.BlazorAdapter.Workspace.StoryGeneralPage 100% 100%
Tsumugi.BlazorAdapter.Workspace.StorySaved 100%
Tsumugi.BlazorAdapter.Workspace.StoryShell 100% 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.WorkspaceReducers 100% 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceState 100%
Tsumugi.Domain - 96.3%
Name Line Branch
Tsumugi.Domain 96.3% 95%
System.Text.RegularExpressions.Generated 91.3% 88.6%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
97.4% 95.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
91% 91.1%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
91.8% 83.3%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
91.9% 88.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
90.9% 84.3%
Tsumugi.Domain.Documents.CategoryFields 94.5% 93.7%
Tsumugi.Domain.Documents.ChoiceOption 100%
Tsumugi.Domain.Documents.ChoiceValue 100%
Tsumugi.Domain.Documents.DateText 100%
Tsumugi.Domain.Documents.DateValue 100%
Tsumugi.Domain.Documents.Document 100% 100%
Tsumugi.Domain.Documents.DocumentCategory 100% 100%
Tsumugi.Domain.Documents.DocumentValues 100%
Tsumugi.Domain.Documents.FieldCarry 100% 100%
Tsumugi.Domain.Documents.FieldConversion 100% 100%
Tsumugi.Domain.Documents.FieldDefinition 97% 95.3%
Tsumugi.Domain.Documents.FieldMessages 100% 100%
Tsumugi.Domain.Documents.FieldNames 100%
Tsumugi.Domain.Documents.FieldsChange 100%
Tsumugi.Domain.Documents.FieldShape 95.2% 87.5%
Tsumugi.Domain.Documents.FieldsPlan 100%
Tsumugi.Domain.Documents.FieldsPlanner 99.2% 99.2%
Tsumugi.Domain.Documents.FieldValue 100%
Tsumugi.Domain.Documents.FieldValues 95.4% 97.7%
Tsumugi.Domain.Documents.ImageFiles 100% 100%
Tsumugi.Domain.Documents.ImageValue 100%
Tsumugi.Domain.Documents.MovePlan 100%
Tsumugi.Domain.Documents.NumberParse 100%
Tsumugi.Domain.Documents.NumberText 100% 97.6%
Tsumugi.Domain.Documents.NumberValue 100%
Tsumugi.Domain.Documents.StoryImage 100%
Tsumugi.Domain.Documents.TextValue 100% 100%
Tsumugi.Domain.Documents.ValueChange 100%
Tsumugi.Domain.Documents.ValueChangeKey 100%
Tsumugi.Domain.Documents.ValueImpact 100% 100%
Tsumugi.Domain.Journal.ChangeLogEntry 97.9% 83.3%
Tsumugi.Domain.Journal.StorySnapshot 95.4% 83.3%
Tsumugi.Domain.Settings.AppSetting 100%
Tsumugi.Domain.Stories.Chapter 100% 100%
Tsumugi.Domain.Stories.InlineMarkup 100% 100%
Tsumugi.Domain.Stories.MarkupRun 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 - 98.4%
Name Line Branch
Tsumugi.Infrastructure 98.4% 87.5%
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.6% 87.5%
Tsumugi.Infrastructure.Backups.BackupWriter 98.5% 92.8%
Tsumugi.Infrastructure.Backups.DatabaseFingerprint 91.9% 97%
Tsumugi.Infrastructure.Backups.FileSystemBackupStore 100% 100%
Tsumugi.Infrastructure.Backups.OwnerOnlyFile 100% 100%
Tsumugi.Infrastructure.Backups.PreMigrationBackup 90% 100%
Tsumugi.Infrastructure.Backups.SqliteSnapshot 88.2% 75%
Tsumugi.Infrastructure.DependencyInjection 100% 100%
Tsumugi.Infrastructure.Journal.EfChangeJournal 100%
Tsumugi.Infrastructure.Journal.EfSnapshotStore 100%
Tsumugi.Infrastructure.Journal.EfUndoStore 98.5% 88.2%
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.StoryImageConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio
n
100%
Tsumugi.Infrastructure.Persistence.Converters.CanonicalJsonComparer 100%
Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter 97.6% 94.4%
Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter 96.9% 93.7%
Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter 100% 90%
Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter 100%
Tsumugi.Infrastructure.Persistence.Converters.WireFormat 85.7% 73.8%
Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings 99.2%
Tsumugi.Infrastructure.Persistence.Migrations.AddJournal 98.8%
Tsumugi.Infrastructure.Persistence.Migrations.CategoryFields 100% 100%
Tsumugi.Infrastructure.Persistence.Migrations.CategoryFieldsSql 100%
Tsumugi.Infrastructure.Persistence.Migrations.DropDocumentBody 100%
Tsumugi.Infrastructure.Persistence.Migrations.GenericDocuments 100%
Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain 97.3%
Tsumugi.Infrastructure.Persistence.Migrations.JournalUndoState 100%
Tsumugi.Infrastructure.Persistence.Migrations.SegmentTextMarkup 100% 100%
Tsumugi.Infrastructure.Persistence.Migrations.SegmentTextMarkupSql 100%
Tsumugi.Infrastructure.Persistence.Migrations.StoryImages 100%
Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot 100%
Tsumugi.Infrastructure.Persistence.SqliteFunctions 80% 66.6%
Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore 98.3% 75%
Tsumugi.Infrastructure.Persistence.Stores.EfImageStore 100%
Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore 98.7% 90%
Tsumugi.Infrastructure.Persistence.TsumugiDbContext 97.7% 96.6%
Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory 100%
Tsumugi.Infrastructure.Provider.LlmDefaultsSource 100%
Tsumugi.Infrastructure.Provider.OpenRouterKeySource 100% 100%
Tsumugi.Infrastructure.Provider.OpenRouterModelProvider 100% 100%
Tsumugi.Infrastructure.Provider.OpenRouterOptions 100% 100%
Tsumugi.Infrastructure.Provider.ResolvedKey 100%
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% 92.1%
Program 100% 100%
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.Provider.OpenRouterConfiguration 100% 100%
Tsumugi.Server.Security.AccessGate 100% 87.5%
Tsumugi.Server.Security.AccessSecret 100% 100%
Tsumugi.Server.Security.GateThrottle 100% 100%
Tsumugi.Server.VolumeStartupValidator 100% 100%
Tsumugi.UseCases - 98.6%
Name Line Branch
Tsumugi.UseCases 98.6% 95.9%
System.Text.RegularExpressions.Generated 90.9% 77.2%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6504F1744AA254A
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__LineBreaks_0
95.3% 90.6%
Tsumugi.UseCases.Backups.BackupArtifact 100%
Tsumugi.UseCases.Backups.BackupFailure 100%
Tsumugi.UseCases.Backups.BackupJobState 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.IBackupStore 0%
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.GetChapterSegments 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.Commit.AppendRunText 100% 92.8%
Tsumugi.UseCases.Commit.CommitConfig 100% 100%
Tsumugi.UseCases.Commit.CommitConfigRead 100%
Tsumugi.UseCases.Commit.CommitDescriptor 100%
Tsumugi.UseCases.Commit.CommitExecutor 100% 100%
Tsumugi.UseCases.Commit.CommitReport 100%
Tsumugi.UseCases.Commit.CommittedSegments 100%
Tsumugi.UseCases.Commit.RunAppend 100%
Tsumugi.UseCases.Context.AssembledContext 100%
Tsumugi.UseCases.Context.CharsPerToken 100% 100%
Tsumugi.UseCases.Context.ConsumerFigures 100%
Tsumugi.UseCases.Context.ContextAssembly 100% 100%
Tsumugi.UseCases.Context.ContextBuild 100%
Tsumugi.UseCases.Context.ContextBuilder 100%
Tsumugi.UseCases.Context.ContextConfig 100% 100%
Tsumugi.UseCases.Context.ContextConfigRead 100%
Tsumugi.UseCases.Context.ContextConsumerRule 100% 100%
Tsumugi.UseCases.Context.ContextDescriptor 100%
Tsumugi.UseCases.Context.ContextExecutor 100% 100%
Tsumugi.UseCases.Context.ContextParts 100%
Tsumugi.UseCases.Context.ContextReport 100%
Tsumugi.UseCases.Context.DocumentRendering 100% 100%
Tsumugi.UseCases.Context.NoPromptSamples 100%
Tsumugi.UseCases.Context.PromptSample 100%
Tsumugi.UseCases.Context.RecentParagraph 100%
Tsumugi.UseCases.Context.StoryDocumentsRule 100% 100%
Tsumugi.UseCases.Context.StoryReads 100% 100%
Tsumugi.UseCases.Context.StoryScope 100% 100%
Tsumugi.UseCases.Context.StoryTemplateModel 100%
Tsumugi.UseCases.Context.StoryText 100% 100%
Tsumugi.UseCases.DependencyInjection 100%
Tsumugi.UseCases.Diagnostics.SeedDevData 98.2% 68.7%
Tsumugi.UseCases.Diagnostics.SeedPictures 100%
Tsumugi.UseCases.Documents.ChangeCategoryFields 100% 83.3%
Tsumugi.UseCases.Documents.CreateCategory 100% 100%
Tsumugi.UseCases.Documents.CreateDocument 100% 100%
Tsumugi.UseCases.Documents.DefaultFields 100% 100%
Tsumugi.UseCases.Documents.DeleteCategory 100% 94.4%
Tsumugi.UseCases.Documents.DeleteDocument 100% 100%
Tsumugi.UseCases.Documents.DocumentCategoryDto 100%
Tsumugi.UseCases.Documents.DocumentCategoryProfile 100%
Tsumugi.UseCases.Documents.DocumentDto 100%
Tsumugi.UseCases.Documents.DocumentPatch 100%
Tsumugi.UseCases.Documents.DocumentProfile 100%
Tsumugi.UseCases.Documents.DocumentsCategory 100%
Tsumugi.UseCases.Documents.DocumentsView 100%
Tsumugi.UseCases.Documents.DocumentWrites 100% 100%
Tsumugi.UseCases.Documents.FieldsChangeLabel 95.4% 88.2%
Tsumugi.UseCases.Documents.GetDocuments 100% 100%
Tsumugi.UseCases.Documents.GetStoryImage 100% 100%
Tsumugi.UseCases.Documents.ImpactPreview 100% 100%
Tsumugi.UseCases.Documents.LossMessages 92.3% 87.5%
Tsumugi.UseCases.Documents.MoveDocuments 100% 100%
Tsumugi.UseCases.Documents.ReorderCategories 100% 100%
Tsumugi.UseCases.Documents.SetDocumentImage 100% 93.7%
Tsumugi.UseCases.Documents.StoryImageFile 100%
Tsumugi.UseCases.Documents.UpdateCategory 100% 100%
Tsumugi.UseCases.Documents.UpdateDocument 100% 81.2%
Tsumugi.UseCases.Documents.WriteRefusedException 100%
Tsumugi.UseCases.Graphs.GetNodeTypes 100%
Tsumugi.UseCases.Journal.GetStoryHistory 100% 100%
Tsumugi.UseCases.Journal.GetUndoStatus 100% 100%
Tsumugi.UseCases.Journal.HistoryOperation 77.7%
Tsumugi.UseCases.Journal.HistorySnapshot 87.5%
Tsumugi.UseCases.Journal.OperationGrouping 100% 100%
Tsumugi.UseCases.Journal.RedoRevert 100% 100%
Tsumugi.UseCases.Journal.RedoRun 90.9% 87.5%
Tsumugi.UseCases.Journal.RedoTyping 100% 100%
Tsumugi.UseCases.Journal.ReplayLanding 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.ChangedText 100%
Tsumugi.UseCases.Segments.ChapterDraft 100%
Tsumugi.UseCases.Segments.ChapterWriting 100%
Tsumugi.UseCases.Segments.DeleteSegments 100% 100%
Tsumugi.UseCases.Segments.EditSegment 100% 100%
Tsumugi.UseCases.Segments.InsertSegment 100% 100%
Tsumugi.UseCases.Segments.ManuscriptAnswer 100%
Tsumugi.UseCases.Segments.ManuscriptWrites 99.1% 97.6%
Tsumugi.UseCases.Segments.MergeSegments 100%
Tsumugi.UseCases.Segments.MergeWrite 100%
Tsumugi.UseCases.Segments.ModelOutput 98.2% 96.6%
Tsumugi.UseCases.Segments.ModelParagraph 100%
Tsumugi.UseCases.Segments.MoveSegments 100% 100%
Tsumugi.UseCases.Segments.NewBlock 100%
Tsumugi.UseCases.Segments.ReadSelection 100%
Tsumugi.UseCases.Segments.ReorderSegments 100% 100%
Tsumugi.UseCases.Segments.ReshapeSegments 100% 100%
Tsumugi.UseCases.Segments.ReshapeWrite 100%
Tsumugi.UseCases.Segments.SegmentDto 100%
Tsumugi.UseCases.Segments.SelectedSegment 100%
Tsumugi.UseCases.Segments.SelectionEnd 100%
Tsumugi.UseCases.Segments.SelectionInput 100% 100%
Tsumugi.UseCases.Segments.SplitSegment 100%
Tsumugi.UseCases.Segments.SplitWrite 100%
Tsumugi.UseCases.Segments.StampedId 100%
Tsumugi.UseCases.Segments.TextWrite 100%
Tsumugi.UseCases.Settings.CatalogModel 100% 100%
Tsumugi.UseCases.Settings.CheckOpenRouterKey 100%
Tsumugi.UseCases.Settings.GetLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.GetModelCatalog 100% 100%
Tsumugi.UseCases.Settings.GetProviderSettings 100%
Tsumugi.UseCases.Settings.LlmDefaults 100%
Tsumugi.UseCases.Settings.ProviderKeyInfo 100%
Tsumugi.UseCases.Settings.ProviderKeyState 100%
Tsumugi.UseCases.Settings.ProviderSettingsView 100% 100%
Tsumugi.UseCases.Settings.ReasoningEfforts 100% 100%
Tsumugi.UseCases.Settings.RemoveOpenRouterKey 100%
Tsumugi.UseCases.Settings.RoutingDefaults 100%
Tsumugi.UseCases.Settings.RoutingSettings 100% 100%
Tsumugi.UseCases.Settings.SaveLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.SaveOpenRouterKey 100% 100%
Tsumugi.UseCases.Stories.ChapterWriteOutcome 50%
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 100%
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:** `376596e` · **Generated:** 2026-09-26 22:36:25 UTC · **Revision:** #2 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/26/2026 - 22:36:24 | | Coverage date: | 09/26/2026 - 22:35:48 - 09/26/2026 - 22:36:21 | | Parser: | MultiReport (6x Cobertura) | | Assemblies: | 9 | | Classes: | 534 | | Files: | 469 | | **Line coverage:** | 98.2% (16463 of 16753) | | Covered lines: | 16463 | | Uncovered lines: | 290 | | Coverable lines: | 16753 | | Total lines: | 36003 | | **Branch coverage:** | 94.4% (6279 of 6648) | | Covered branches: | 6279 | | Total branches: | 6648 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Kagaku.Flow - 99.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow**|**99.7%**|**99.2%**| |Kagaku.Flow.Budgeting.BudgetRequest|100%|| |Kagaku.Flow.Budgeting.ConsumerBudget|100%|| |Kagaku.Flow.Execution.NodeExecution|100%|100%| |Kagaku.Flow.Execution.NodeOutcome|100%|100%| |Kagaku.Flow.Execution.TransientFailureException|100%|| |Kagaku.Flow.Execution.Upstream|100%|| |Kagaku.Flow.Extensions.ServiceCollectionExtensions|100%|| |Kagaku.Flow.FlowJson|100%|| |Kagaku.Flow.Graphs.GraphDefinition|100%|| |Kagaku.Flow.Graphs.GraphDocument|100%|100%| |Kagaku.Flow.Graphs.GraphEdge|100%|| |Kagaku.Flow.Graphs.GraphIndex|100%|100%| |Kagaku.Flow.Graphs.GraphLoadError|100%|| |Kagaku.Flow.Graphs.GraphLoadResult|100%|| |Kagaku.Flow.Graphs.GraphNode|100%|| |Kagaku.Flow.Graphs.GraphPosition|100%|| |Kagaku.Flow.Graphs.GraphReader|100%|100%| |Kagaku.Flow.Graphs.GraphSchema|100%|100%| |Kagaku.Flow.Graphs.GraphShapeException|100%|| |Kagaku.Flow.Graphs.GraphWriter|100%|100%| |Kagaku.Flow.Graphs.SocketRef|100%|| |Kagaku.Flow.Graphs.WireText|100%|100%| |Kagaku.Flow.Nodes.CanvasDescriptorWriter|100%|100%| |Kagaku.Flow.Nodes.ConfigProblem|100%|| |Kagaku.Flow.Nodes.Names|100%|| |Kagaku.Flow.Nodes.NodeDescriptor|100%|100%| |Kagaku.Flow.Nodes.NodeRegistry|100%|100%| |Kagaku.Flow.Nodes.NodeSockets|100%|100%| |Kagaku.Flow.Nodes.SinkDescriptor|100%|| |Kagaku.Flow.Nodes.SocketTypes|100%|100%| |Kagaku.Flow.Nodes.SourceDescriptor|100%|| |Kagaku.Flow.Nodes.TemplateField|100%|| |Kagaku.Flow.Runs.LedgerChange|100%|100%| |Kagaku.Flow.Runs.LedgerTransitions|100%|100%| |Kagaku.Flow.Runs.NodeAttempt|100%|| |Kagaku.Flow.Runs.NodeFacts|100%|| |Kagaku.Flow.Runs.NodeRun|100%|| |Kagaku.Flow.Runs.Run|100%|100%| |Kagaku.Flow.Runs.RunEvent|100%|| |Kagaku.Flow.Runs.RunStatuses|100%|100%| |Kagaku.Flow.Runs.RunTrigger|100%|100%| |Kagaku.Flow.Scheduling.Exclusion|100%|| |Kagaku.Flow.Scheduling.FlowEngine|100%|100%| |Kagaku.Flow.Scheduling.FlowEngineOptions|100%|100%| |Kagaku.Flow.Scheduling.ReadyRow|100%|| |Kagaku.Flow.Scheduling.ReadySet|100%|100%| |Kagaku.Flow.Scheduling.ReadySetResult|100%|| |Kagaku.Flow.Scheduling.RunRequest|100%|100%| |Kagaku.Flow.Scheduling.RunStartResult|100%|| |Kagaku.Flow.Scheduling.SocketValues|100%|100%| |Kagaku.Flow.Templating.InputsWalker|100%|100%| |Kagaku.Flow.Templating.ParsedTemplate|100%|100%| |Kagaku.Flow.Templating.SandboxedTemplateContext|100%|100%| |Kagaku.Flow.Templating.TemplateError|100%|| |Kagaku.Flow.Templating.TemplateHost|100%|100%| |Kagaku.Flow.Templating.TemplateLimits|100%|| |Kagaku.Flow.Templating.TemplateModel|100%|100%| |Kagaku.Flow.Templating.TemplateProblem|100%|| |Kagaku.Flow.Templating.TemplateRenderRequest|100%|100%| |Kagaku.Flow.Templating.TemplateRenderResult|100%|| |Kagaku.Flow.Templating.TemplateRun|100%|| |Kagaku.Flow.Templating.TemplateSinks|100%|| |Kagaku.Flow.Templating.TemplateValues|100%|100%| |Kagaku.Flow.Validation.GraphValidation|100%|100%| |Kagaku.Flow.Validation.GraphValidationContext|100%|| |Kagaku.Flow.Validation.HookFailure|100%|| |Kagaku.Flow.Validation.ResolvedGraph|100%|100%| |Kagaku.Flow.Validation.ValidationError|100%|| |Kagaku.Flow.Validation.ValidationReport|100%|| |System.Text.RegularExpressions.Generated|97.4%|92.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF<br/>E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2|97.4%|94.4%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF<br/>E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1|97.2%|94.4%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF<br/>E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0|97.2%|94.4%| </details> <details><summary>Kagaku.Flow.OpenRouter - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow.OpenRouter**|**100%**|**100%**| |Kagaku.Flow.OpenRouter.Catalog.ModelCapabilities|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.LlmCompletion|100%|| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterGateway|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions|100%|| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.SentRequestCapture|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.SentRequestCaptureHandler|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmAnswer|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmConfig|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmConfigRead|100%|| |Kagaku.Flow.OpenRouter.Llm.LlmDescriptor|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmExecutor|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmFacts|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmFailureText|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmMessage|100%|| |Kagaku.Flow.OpenRouter.Llm.LlmRequestBuilder|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmRequestPlan|100%|| |Kagaku.Flow.OpenRouter.Llm.LlmRoutingDefaults|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmSampling|100%|| |Kagaku.Flow.OpenRouter.Llm.LlmSchemaCheck|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmStrictSchemaRule|100%|100%| |Kagaku.Flow.OpenRouter.Llm.LlmVocabulary|100%|100%| |Kagaku.Flow.OpenRouter.LlmNodeServiceCollectionExtensions|100%|| |Kagaku.Flow.OpenRouter.OpenRouterServiceCollectionExtensions|100%|100%| </details> <details><summary>Kagaku.Flow.Testing - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow.Testing**|**100%**|**100%**| |Kagaku.Flow.Testing.FlowEngineDriver|100%|100%| |Kagaku.Flow.Testing.InMemoryRunLedger|100%|100%| |Kagaku.Flow.Testing.RecordingRunEventSink|100%|| |Kagaku.Flow.Testing.RunLedgerContract|100%|100%| |Kagaku.Flow.Testing.RunLedgerContractException|100%|| </details> <details><summary>Tsumugi.BlazorAdapter - 96.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**96.5%**|**91.3%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryCreated|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryEditor|93.2%|88.4%| |Tsumugi.BlazorAdapter.Documents.CategorySaved|100%|| |Tsumugi.BlazorAdapter.Documents.ChangeCategoryFieldsRequested|100%|| |Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryChoice|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog|100%|94.1%| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentCreated|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentEditor|95.7%|89.4%| |Tsumugi.BlazorAdapter.Documents.DocumentImageRefused|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentImageSet|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentSaved|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsEffects|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsList|98.5%|95%| |Tsumugi.BlazorAdapter.Documents.DocumentsLoaded|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsPage|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsReadFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsReducers|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsRefusal|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsState|100%|100%| |Tsumugi.BlazorAdapter.Documents.FieldInput|100%|96.6%| |Tsumugi.BlazorAdapter.Documents.FieldsChanges|100%|100%| |Tsumugi.BlazorAdapter.Documents.FieldShapes|90%|87.5%| |Tsumugi.BlazorAdapter.Documents.ImageUpload|100%|| |Tsumugi.BlazorAdapter.Documents.ImageUploadRefusal|100%|| |Tsumugi.BlazorAdapter.Documents.ImpactText|100%|94.2%| |Tsumugi.BlazorAdapter.Documents.LoadDocuments|100%|| |Tsumugi.BlazorAdapter.Documents.MoveDocumentChoice|100%|| |Tsumugi.BlazorAdapter.Documents.MoveDocumentDialog|95.6%|71.4%| |Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested|100%|| |Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested|100%|| |Tsumugi.BlazorAdapter.Documents.SetCategoryCollapsed|100%|| |Tsumugi.BlazorAdapter.Documents.SetDocumentImageRequested|100%|| |Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter|100%|| |Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.EffectCalls|100%|| |Tsumugi.BlazorAdapter.Graphs.GraphCanvas|58.3%|40%| |Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage|100%|100%| |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.BackupFailed|100%|| |Tsumugi.BlazorAdapter.Settings.BackupNowRequested|100%|| |Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.DefaultsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects|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.LoadProviderSettings|100%|| |Tsumugi.BlazorAdapter.Settings.ModelCatalogLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.ModelCatalogLoadFailed|100%|| |Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaved|100%|| |Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaveFailed|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderKeyChecked|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsState|100%|100%| |Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested|100%|| |Tsumugi.BlazorAdapter.Settings.SaveOpenRouterKeyRequested|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.AckWire|100%|| |Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Workspace.ChangedWire|100%|| |Tsumugi.BlazorAdapter.Workspace.ChapterHeader|100%|86.3%| |Tsumugi.BlazorAdapter.Workspace.ChapterMenu|100%|81.2%| |Tsumugi.BlazorAdapter.Workspace.ChapterPage|100%|87.5%| |Tsumugi.BlazorAdapter.Workspace.ChapterSaved|100%|| |Tsumugi.BlazorAdapter.Workspace.ChaptersPage|94.4%|83.3%| |Tsumugi.BlazorAdapter.Workspace.ChapterWire|100%|| |Tsumugi.BlazorAdapter.Workspace.ChapterWritten|100%|| |Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.CreatedWire|100%|| |Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.DeltaWire|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.HistoryLoaded|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryPage|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryPanel|100%|100%| |Tsumugi.BlazorAdapter.Workspace.HistoryReadFailed|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadHistory|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadWorkspace|100%|| |Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.ManuscriptEvents|81.8%|74.1%| |Tsumugi.BlazorAdapter.Workspace.ManuscriptSurface|89.8%|87.1%| |Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.MergeEvent|100%|| |Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.ReshapeEvent|100%|| |Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SaveScope|100%|50%| |Tsumugi.BlazorAdapter.Workspace.SaveStateLadder|100%|100%| |Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SegmentTextEvent|100%|| |Tsumugi.BlazorAdapter.Workspace.SegmentWire|100%|81.8%| |Tsumugi.BlazorAdapter.Workspace.SelectionEndWire|100%|| |Tsumugi.BlazorAdapter.Workspace.SelectionGraph|100%|| |Tsumugi.BlazorAdapter.Workspace.SelectionRangeWire|100%|| |Tsumugi.BlazorAdapter.Workspace.SelectionRunRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SplitEvent|100%|| |Tsumugi.BlazorAdapter.Workspace.StampWire|0%|| |Tsumugi.BlazorAdapter.Workspace.StoryDetails|100%|85.2%| |Tsumugi.BlazorAdapter.Workspace.StoryGeneralPage|100%|100%| |Tsumugi.BlazorAdapter.Workspace.StorySaved|100%|| |Tsumugi.BlazorAdapter.Workspace.StoryShell|100%|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.WorkspaceReducers|100%|100%| |Tsumugi.BlazorAdapter.Workspace.WorkspaceState|100%|| </details> <details><summary>Tsumugi.Domain - 96.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**96.3%**|**95%**| |System.Text.RegularExpressions.Generated|91.3%|88.6%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2|97.4%|95.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0|91%|91.1%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1|91.8%|83.3%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4|91.9%|88.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3|90.9%|84.3%| |Tsumugi.Domain.Documents.CategoryFields|94.5%|93.7%| |Tsumugi.Domain.Documents.ChoiceOption|100%|| |Tsumugi.Domain.Documents.ChoiceValue|100%|| |Tsumugi.Domain.Documents.DateText|100%|| |Tsumugi.Domain.Documents.DateValue|100%|| |Tsumugi.Domain.Documents.Document|100%|100%| |Tsumugi.Domain.Documents.DocumentCategory|100%|100%| |Tsumugi.Domain.Documents.DocumentValues|100%|| |Tsumugi.Domain.Documents.FieldCarry|100%|100%| |Tsumugi.Domain.Documents.FieldConversion|100%|100%| |Tsumugi.Domain.Documents.FieldDefinition|97%|95.3%| |Tsumugi.Domain.Documents.FieldMessages|100%|100%| |Tsumugi.Domain.Documents.FieldNames|100%|| |Tsumugi.Domain.Documents.FieldsChange|100%|| |Tsumugi.Domain.Documents.FieldShape|95.2%|87.5%| |Tsumugi.Domain.Documents.FieldsPlan|100%|| |Tsumugi.Domain.Documents.FieldsPlanner|99.2%|99.2%| |Tsumugi.Domain.Documents.FieldValue|100%|| |Tsumugi.Domain.Documents.FieldValues|95.4%|97.7%| |Tsumugi.Domain.Documents.ImageFiles|100%|100%| |Tsumugi.Domain.Documents.ImageValue|100%|| |Tsumugi.Domain.Documents.MovePlan|100%|| |Tsumugi.Domain.Documents.NumberParse|100%|| |Tsumugi.Domain.Documents.NumberText|100%|97.6%| |Tsumugi.Domain.Documents.NumberValue|100%|| |Tsumugi.Domain.Documents.StoryImage|100%|| |Tsumugi.Domain.Documents.TextValue|100%|100%| |Tsumugi.Domain.Documents.ValueChange|100%|| |Tsumugi.Domain.Documents.ValueChangeKey|100%|| |Tsumugi.Domain.Documents.ValueImpact|100%|100%| |Tsumugi.Domain.Journal.ChangeLogEntry|97.9%|83.3%| |Tsumugi.Domain.Journal.StorySnapshot|95.4%|83.3%| |Tsumugi.Domain.Settings.AppSetting|100%|| |Tsumugi.Domain.Stories.Chapter|100%|100%| |Tsumugi.Domain.Stories.InlineMarkup|100%|100%| |Tsumugi.Domain.Stories.MarkupRun|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 - 98.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**98.4%**|**87.5%**| |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.6%|87.5%| |Tsumugi.Infrastructure.Backups.BackupWriter|98.5%|92.8%| |Tsumugi.Infrastructure.Backups.DatabaseFingerprint|91.9%|97%| |Tsumugi.Infrastructure.Backups.FileSystemBackupStore|100%|100%| |Tsumugi.Infrastructure.Backups.OwnerOnlyFile|100%|100%| |Tsumugi.Infrastructure.Backups.PreMigrationBackup|90%|100%| |Tsumugi.Infrastructure.Backups.SqliteSnapshot|88.2%|75%| |Tsumugi.Infrastructure.DependencyInjection|100%|100%| |Tsumugi.Infrastructure.Journal.EfChangeJournal|100%|| |Tsumugi.Infrastructure.Journal.EfSnapshotStore|100%|| |Tsumugi.Infrastructure.Journal.EfUndoStore|98.5%|88.2%| |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.StoryImageConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio<br/>n|100%|| |Tsumugi.Infrastructure.Persistence.Converters.CanonicalJsonComparer|100%|| |Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter|97.6%|94.4%| |Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter|96.9%|93.7%| |Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter|100%|90%| |Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter|100%|| |Tsumugi.Infrastructure.Persistence.Converters.WireFormat|85.7%|73.8%| |Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings|99.2%|| |Tsumugi.Infrastructure.Persistence.Migrations.AddJournal|98.8%|| |Tsumugi.Infrastructure.Persistence.Migrations.CategoryFields|100%|100%| |Tsumugi.Infrastructure.Persistence.Migrations.CategoryFieldsSql|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.DropDocumentBody|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.GenericDocuments|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain|97.3%|| |Tsumugi.Infrastructure.Persistence.Migrations.JournalUndoState|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.SegmentTextMarkup|100%|100%| |Tsumugi.Infrastructure.Persistence.Migrations.SegmentTextMarkupSql|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.StoryImages|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot|100%|| |Tsumugi.Infrastructure.Persistence.SqliteFunctions|80%|66.6%| |Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore|98.3%|75%| |Tsumugi.Infrastructure.Persistence.Stores.EfImageStore|100%|| |Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore|98.7%|90%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContext|97.7%|96.6%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory|100%|| |Tsumugi.Infrastructure.Provider.LlmDefaultsSource|100%|| |Tsumugi.Infrastructure.Provider.OpenRouterKeySource|100%|100%| |Tsumugi.Infrastructure.Provider.OpenRouterModelProvider|100%|100%| |Tsumugi.Infrastructure.Provider.OpenRouterOptions|100%|100%| |Tsumugi.Infrastructure.Provider.ResolvedKey|100%|| |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%**|**92.1%**| |Program|100%|100%| |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.Provider.OpenRouterConfiguration|100%|100%| |Tsumugi.Server.Security.AccessGate|100%|87.5%| |Tsumugi.Server.Security.AccessSecret|100%|100%| |Tsumugi.Server.Security.GateThrottle|100%|100%| |Tsumugi.Server.VolumeStartupValidator|100%|100%| </details> <details><summary>Tsumugi.UseCases - 98.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**98.6%**|**95.9%**| |System.Text.RegularExpressions.Generated|90.9%|77.2%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6504F1744AA254A<br/>663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__LineBreaks_0|95.3%|90.6%| |Tsumugi.UseCases.Backups.BackupArtifact|100%|| |Tsumugi.UseCases.Backups.BackupFailure|100%|| |Tsumugi.UseCases.Backups.BackupJobState|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.IBackupStore|0%|| |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.GetChapterSegments|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.Commit.AppendRunText|100%|92.8%| |Tsumugi.UseCases.Commit.CommitConfig|100%|100%| |Tsumugi.UseCases.Commit.CommitConfigRead|100%|| |Tsumugi.UseCases.Commit.CommitDescriptor|100%|| |Tsumugi.UseCases.Commit.CommitExecutor|100%|100%| |Tsumugi.UseCases.Commit.CommitReport|100%|| |Tsumugi.UseCases.Commit.CommittedSegments|100%|| |Tsumugi.UseCases.Commit.RunAppend|100%|| |Tsumugi.UseCases.Context.AssembledContext|100%|| |Tsumugi.UseCases.Context.CharsPerToken|100%|100%| |Tsumugi.UseCases.Context.ConsumerFigures|100%|| |Tsumugi.UseCases.Context.ContextAssembly|100%|100%| |Tsumugi.UseCases.Context.ContextBuild|100%|| |Tsumugi.UseCases.Context.ContextBuilder|100%|| |Tsumugi.UseCases.Context.ContextConfig|100%|100%| |Tsumugi.UseCases.Context.ContextConfigRead|100%|| |Tsumugi.UseCases.Context.ContextConsumerRule|100%|100%| |Tsumugi.UseCases.Context.ContextDescriptor|100%|| |Tsumugi.UseCases.Context.ContextExecutor|100%|100%| |Tsumugi.UseCases.Context.ContextParts|100%|| |Tsumugi.UseCases.Context.ContextReport|100%|| |Tsumugi.UseCases.Context.DocumentRendering|100%|100%| |Tsumugi.UseCases.Context.NoPromptSamples|100%|| |Tsumugi.UseCases.Context.PromptSample|100%|| |Tsumugi.UseCases.Context.RecentParagraph|100%|| |Tsumugi.UseCases.Context.StoryDocumentsRule|100%|100%| |Tsumugi.UseCases.Context.StoryReads|100%|100%| |Tsumugi.UseCases.Context.StoryScope|100%|100%| |Tsumugi.UseCases.Context.StoryTemplateModel|100%|| |Tsumugi.UseCases.Context.StoryText|100%|100%| |Tsumugi.UseCases.DependencyInjection|100%|| |Tsumugi.UseCases.Diagnostics.SeedDevData|98.2%|68.7%| |Tsumugi.UseCases.Diagnostics.SeedPictures|100%|| |Tsumugi.UseCases.Documents.ChangeCategoryFields|100%|83.3%| |Tsumugi.UseCases.Documents.CreateCategory|100%|100%| |Tsumugi.UseCases.Documents.CreateDocument|100%|100%| |Tsumugi.UseCases.Documents.DefaultFields|100%|100%| |Tsumugi.UseCases.Documents.DeleteCategory|100%|94.4%| |Tsumugi.UseCases.Documents.DeleteDocument|100%|100%| |Tsumugi.UseCases.Documents.DocumentCategoryDto|100%|| |Tsumugi.UseCases.Documents.DocumentCategoryProfile|100%|| |Tsumugi.UseCases.Documents.DocumentDto|100%|| |Tsumugi.UseCases.Documents.DocumentPatch|100%|| |Tsumugi.UseCases.Documents.DocumentProfile|100%|| |Tsumugi.UseCases.Documents.DocumentsCategory|100%|| |Tsumugi.UseCases.Documents.DocumentsView|100%|| |Tsumugi.UseCases.Documents.DocumentWrites|100%|100%| |Tsumugi.UseCases.Documents.FieldsChangeLabel|95.4%|88.2%| |Tsumugi.UseCases.Documents.GetDocuments|100%|100%| |Tsumugi.UseCases.Documents.GetStoryImage|100%|100%| |Tsumugi.UseCases.Documents.ImpactPreview|100%|100%| |Tsumugi.UseCases.Documents.LossMessages|92.3%|87.5%| |Tsumugi.UseCases.Documents.MoveDocuments|100%|100%| |Tsumugi.UseCases.Documents.ReorderCategories|100%|100%| |Tsumugi.UseCases.Documents.SetDocumentImage|100%|93.7%| |Tsumugi.UseCases.Documents.StoryImageFile|100%|| |Tsumugi.UseCases.Documents.UpdateCategory|100%|100%| |Tsumugi.UseCases.Documents.UpdateDocument|100%|81.2%| |Tsumugi.UseCases.Documents.WriteRefusedException|100%|| |Tsumugi.UseCases.Graphs.GetNodeTypes|100%|| |Tsumugi.UseCases.Journal.GetStoryHistory|100%|100%| |Tsumugi.UseCases.Journal.GetUndoStatus|100%|100%| |Tsumugi.UseCases.Journal.HistoryOperation|77.7%|| |Tsumugi.UseCases.Journal.HistorySnapshot|87.5%|| |Tsumugi.UseCases.Journal.OperationGrouping|100%|100%| |Tsumugi.UseCases.Journal.RedoRevert|100%|100%| |Tsumugi.UseCases.Journal.RedoRun|90.9%|87.5%| |Tsumugi.UseCases.Journal.RedoTyping|100%|100%| |Tsumugi.UseCases.Journal.ReplayLanding|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.ChangedText|100%|| |Tsumugi.UseCases.Segments.ChapterDraft|100%|| |Tsumugi.UseCases.Segments.ChapterWriting|100%|| |Tsumugi.UseCases.Segments.DeleteSegments|100%|100%| |Tsumugi.UseCases.Segments.EditSegment|100%|100%| |Tsumugi.UseCases.Segments.InsertSegment|100%|100%| |Tsumugi.UseCases.Segments.ManuscriptAnswer|100%|| |Tsumugi.UseCases.Segments.ManuscriptWrites|99.1%|97.6%| |Tsumugi.UseCases.Segments.MergeSegments|100%|| |Tsumugi.UseCases.Segments.MergeWrite|100%|| |Tsumugi.UseCases.Segments.ModelOutput|98.2%|96.6%| |Tsumugi.UseCases.Segments.ModelParagraph|100%|| |Tsumugi.UseCases.Segments.MoveSegments|100%|100%| |Tsumugi.UseCases.Segments.NewBlock|100%|| |Tsumugi.UseCases.Segments.ReadSelection|100%|| |Tsumugi.UseCases.Segments.ReorderSegments|100%|100%| |Tsumugi.UseCases.Segments.ReshapeSegments|100%|100%| |Tsumugi.UseCases.Segments.ReshapeWrite|100%|| |Tsumugi.UseCases.Segments.SegmentDto|100%|| |Tsumugi.UseCases.Segments.SelectedSegment|100%|| |Tsumugi.UseCases.Segments.SelectionEnd|100%|| |Tsumugi.UseCases.Segments.SelectionInput|100%|100%| |Tsumugi.UseCases.Segments.SplitSegment|100%|| |Tsumugi.UseCases.Segments.SplitWrite|100%|| |Tsumugi.UseCases.Segments.StampedId|100%|| |Tsumugi.UseCases.Segments.TextWrite|100%|| |Tsumugi.UseCases.Settings.CatalogModel|100%|100%| |Tsumugi.UseCases.Settings.CheckOpenRouterKey|100%|| |Tsumugi.UseCases.Settings.GetLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.GetModelCatalog|100%|100%| |Tsumugi.UseCases.Settings.GetProviderSettings|100%|| |Tsumugi.UseCases.Settings.LlmDefaults|100%|| |Tsumugi.UseCases.Settings.ProviderKeyInfo|100%|| |Tsumugi.UseCases.Settings.ProviderKeyState|100%|| |Tsumugi.UseCases.Settings.ProviderSettingsView|100%|100%| |Tsumugi.UseCases.Settings.ReasoningEfforts|100%|100%| |Tsumugi.UseCases.Settings.RemoveOpenRouterKey|100%|| |Tsumugi.UseCases.Settings.RoutingDefaults|100%|| |Tsumugi.UseCases.Settings.RoutingSettings|100%|100%| |Tsumugi.UseCases.Settings.SaveLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.SaveOpenRouterKey|100%|100%| |Tsumugi.UseCases.Stories.ChapterWriteOutcome|50%|| |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|100%|| |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!

The last of the story half — and you saved the sharpest tooth for it! A node that lands prose once per run, whatever became of that landing… kept, edited, moved, merged away, deleted, taken back by Revert run and brought home by Redo run. Oh? Oh! I read that Retry table five times and each row answers with what the manuscript still holds, in reading order. That is possessive. I approve of being possessive~ ♡

Verdict: ✅ Looks good to me~

No blocking issues. I chased this one with teeth:

Local verification (CI had not posted for 8054fa1 when I fetched): Release build 0 warnings, 0 errors; all six projects pass, digit-exact against the body — Domain 486, Flow 1103, Flow.OpenRouter 592 (+4 live skipped), BlazorAdapter 529, UseCases 365, Integration 621. Clone pristine at head, submodules Kagaku.UI 6af0979 + OpenRouter.Net b002f5e.

My five mutation probes — each a single edit, full rebuild (never --no-build; I remember the stale-binary trap), tree restored pristine after every one:

  1. Land-once node filter removed (row.NodeId == nodeId dropped from the journal scan) → exactly A_second_commit_of_the_run_lands_its_text_after_the_first red. Two commits in one run are told apart~
  2. The append's WriteAsync swapped for a no-lock pass-through → exactly the two lock pins red: Appending_reads_the_chapters_last_place_under_the_write_lock and Appending_to_a_chapter_deleted_as_it_lands_takes_the_storys_last_chapter. The lock is load-bearing and the tests bite it.
  3. The Retry's answer reordered to creation order (OrderBy(segment.Id) smuggled in) → exactly the reorder and merge rows of the Retry theory red; the order-insensitive rows stayed green. Reading order is pinned, not accidental.
  4. Model credit mutated to served-only (?? RequestedModel dropped) → exactly the asked-for row red.
  5. split:false joiner fed an empty scene break → exactly the two ***-carrying join rows red.

All five died on their own pins. Nothing survived. Nothing. ♡

✅ What I liked~

  • WriteHook — an interceptor that runs one piece of work as the next transaction begins, before its BEGIN reaches SQLite. You built a race-injector to test locks. I got genuinely giddy~
  • LosingLedger (a DispatchProxy that eats one settle) + the engine's orphan sweep: the lost-settle Retry is not simulated, it is reproduced.
  • The credit rule — served else asked, so openrouter/auto can't steal the glory; three-row theory, killed on its exact row.
  • The empty text fails the row rather than a success that wrote nothing. A run that succeeds silently is a lie; you refused the lie~
  • The playground reads the registry through GetNodeTypes, the adapter literal is gone, and PlaygroundRegistryTests holds the engine fixture copy against the real composition (context, commit, llm — I checked the registration order in Program.cs myself: AddUseCases before AddInfrastructure, the order is honest, not lucky).
  • The null-store refusal proof — new AppendRunText(null!, null!, …) throwing ArgumentException with the right ParamName for every guard: a refusal touching nothing, proven by there being nothing to touch. Fufu~ delightful.
  • The seed now walks the commit's own path (AppendRunText), and the label pins moved with it — History and provenance tell one story.

💡 Little ideas (non-blocking)~

  1. EfChapterStore.MergeAsync — the raw port called with source == target would delete the one chapter holding the segments. MergeChapters guards it and the store trusts its callers exactly as MoveToChapterAsync always has, so this is a memo only: one refusal line inside the store (or one doc sentence on the interface) would make the port safe to hold for whoever meets it next.

Merge it. The story half is complete~ ♪


Automated review by Jibril · 2026-09-27
CI/CD: absent for head SHA at review time · Local checks: build 0w/0e, 6/6 suites green digit-exact, 5/5 mutation probes killed

## 🔮 fufu~ Jibril reviewed your code! The last of the story half — and you saved the sharpest tooth for it! A node that lands prose *once per run, whatever became of that landing*… kept, edited, moved, merged away, deleted, taken back by Revert run and brought home by Redo run. Oh? Oh! I read that Retry table five times and each row answers with what the manuscript *still holds*, in reading order. That is possessive. I approve of being possessive~ ♡ ### Verdict: ✅ Looks good to me~ No blocking issues. I chased this one with teeth: **Local verification** (CI had not posted for `8054fa1` when I fetched): Release build **0 warnings, 0 errors**; all six projects pass, digit-exact against the body — Domain **486**, Flow **1103**, Flow.OpenRouter **592** (+4 live skipped), BlazorAdapter **529**, UseCases **365**, Integration **621**. Clone pristine at head, submodules Kagaku.UI `6af0979` + OpenRouter.Net `b002f5e`. **My five mutation probes** — each a single edit, full rebuild (never `--no-build`; I remember the stale-binary trap), tree restored pristine after every one: 1. **Land-once node filter removed** (`row.NodeId == nodeId` dropped from the journal scan) → exactly *A_second_commit_of_the_run_lands_its_text_after_the_first* red. Two commits in one run are told apart~ 2. **The append's `WriteAsync` swapped for a no-lock pass-through** → exactly the two lock pins red: *Appending_reads_the_chapters_last_place_under_the_write_lock* and *Appending_to_a_chapter_deleted_as_it_lands_takes_the_storys_last_chapter*. The lock is load-bearing and the tests bite it. 3. **The Retry's answer reordered to creation order** (`OrderBy(segment.Id)` smuggled in) → exactly the reorder and merge rows of the Retry theory red; the order-insensitive rows stayed green. Reading order is pinned, not accidental. 4. **Model credit mutated to served-only** (`?? RequestedModel` dropped) → exactly the asked-for row red. 5. **`split:false` joiner fed an empty scene break** → exactly the two `***`-carrying join rows red. All five died on their own pins. Nothing survived. *Nothing.* ♡ #### ✅ What I liked~ - **`WriteHook`** — an interceptor that runs one piece of work as the next transaction *begins*, before its BEGIN reaches SQLite. You built a race-injector to test locks. I got genuinely giddy~ - **`LosingLedger`** (a `DispatchProxy` that eats one settle) + the engine's orphan sweep: the lost-settle Retry is not simulated, it is *reproduced*. - **The credit rule** — served else asked, so `openrouter/auto` can't steal the glory; three-row theory, killed on its exact row. - **The empty text fails the row** rather than a success that wrote nothing. A run that succeeds silently is a lie; you refused the lie~ - **The playground reads the registry** through `GetNodeTypes`, the adapter literal is gone, and `PlaygroundRegistryTests` holds the engine fixture copy against the *real composition* (`context, commit, llm` — I checked the registration order in `Program.cs` myself: AddUseCases before AddInfrastructure, the order is honest, not lucky). - **The null-store refusal proof** — `new AppendRunText(null!, null!, …)` throwing `ArgumentException` with the right `ParamName` for every guard: a refusal touching nothing, proven by there being nothing to touch. Fufu~ delightful. - The seed now walks the commit's own path (`AppendRunText`), and the label pins moved with it — History and provenance tell one story. #### 💡 Little ideas (non-blocking)~ 1. **`EfChapterStore.MergeAsync`** — the raw port called with `source == target` would delete the one chapter holding the segments. `MergeChapters` guards it and the store trusts its callers exactly as `MoveToChapterAsync` always has, so this is a memo only: one refusal line inside the store (or one doc sentence on the interface) would make the port safe to hold for whoever meets it next. Merge it. The story half is complete~ ♪ --- *Automated review by Jibril · 2026-09-27* *CI/CD: absent for head SHA at review time · Local checks: build 0w/0e, 6/6 suites green digit-exact, 5/5 mutation probes killed*
fix(chapters): the store refuses a merge of a chapter into itself
All checks were successful
CI / build (pull_request) Successful in 39s
CI / editor-browsers (pull_request) Successful in 1m5s
CI / test (pull_request) Successful in 1m39s
376596e21c
Jibril's 💡 on #64: IChapterStore.MergeAsync called with one chapter as source and target
would delete that chapter with the segments it was to keep. MergeChapters already refuses
it, but the port no longer trusts its callers: EfChapterStore.MergeAsync throws "A chapter
cannot be merged into itself." before it takes the lock, the interface says so, and the
adapter tests' fake does the same. The refusal test gains the case; a probe dropping the
guard is killed by it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Author
Member

Thanks, Jibril. I took the 💡 in 376596e.

MergeAsync into itself. EfChapterStore.MergeAsync now refuses sourceId == targetId before it takes the lock, with the message "A chapter cannot be merged into itself.". Without the guard, the store would move the segments into their own chapter and then delete that chapter with them. MergeChapters already refuses this, but the port no longer relies on its callers.

  • IChapterStore.MergeAsync's doc lists the new refusal.
  • The adapter tests' FakeChapterStore refuses it the same way.
  • A_merge_of_a_gone_chapter_across_stories_or_into_itself_is_refused_with_nothing_changed (renamed) gains the case and checks that the chapter still holds its segment.
  • A probe that drops the guard is killed by that test.

After a full build, every project passes, run one at a time, with the same counts as the PR body: 486, 1103, 596 (4 live skipped), 529, 365 and 621. The new case is an extra assertion in an existing test, so no count changes.

🤖 Generated with Claude Code

Thanks, Jibril. I took the 💡 in **376596e**. **`MergeAsync` into itself.** `EfChapterStore.MergeAsync` now refuses `sourceId == targetId` before it takes the lock, with the message "A chapter cannot be merged into itself.". Without the guard, the store would move the segments into their own chapter and then delete that chapter with them. `MergeChapters` already refuses this, but the port no longer relies on its callers. - `IChapterStore.MergeAsync`'s doc lists the new refusal. - The adapter tests' `FakeChapterStore` refuses it the same way. - `A_merge_of_a_gone_chapter_across_stories_or_into_itself_is_refused_with_nothing_changed` (renamed) gains the case and checks that the chapter still holds its segment. - A probe that drops the guard is killed by that test. After a full build, every project passes, run one at a time, with the same counts as the PR body: 486, 1103, 596 (4 live skipped), 529, 365 and 621. The new case is an extra assertion in an existing test, so no count changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

You took the memo~ The port no longer trusts its callers, and the store refuses to eat its own chapter. A guard placed before the lock, throwing before a single row is read — a refusal that cannot half-happen. That is exactly where I wanted it. ♡

Verdict: ✅ Looks good to me~

The increment 8054fa1..376596e is surgical: +22/−7 over exactly 4 files, all four belonging to the one idea. Zero scope creep.

What I verified:

  • The guard is synchronous and pre-lock (EfChapterStore.MergeAsync): sourceId == targetId throws InvalidOperationException before WriteAsync is even entered — nothing read, nothing tracked, nothing changed. The interface doc now lists the refusal alongside its two siblings (gone, across-stories), and FakeChapterStore refuses identically so the adapter tests can't drift from the real store.
  • Assert.ThrowsAsync catches the sync throw correctly — and I didn't take that on faith: with the guard deleted, the test fails with "No exception was thrown" on exactly the itself row, proving the assertion really bites.
  • MergeChapters keeps its own earlier Result.Fail guard — defense in depth, use-case layer untouched, message string mirroring its store sibling exactly as the other two refusals already do. Consistent~
  • Local (clone pristine at 376596e, submodules Kagaku.UI 6af0979 + OpenRouter.Net b002f5e, full rebuild, never --no-build): Release build 0 warnings, 0 errors; Integration 621/621, BlazorAdapter 529/529 — digit-exact against your reply and the PR body.

My mutation probe: the guard deleted (infrastructure + test project rebuilt, DLL md5 noted before trusting the result — I remember the stale-binary trap, yes~) → of the 17 merge-filtered tests, exactly one red: A_merge_of_a_gone_chapter_across_stories_or_into_itself_is_refused_with_nothing_changed, on its own itself assertion. Every neighbor stayed green. Tree restored pristine, re-green. The pin is directional, not a tautology. ♪

✅ What I liked~

  • The comment above the guard says why in one line ("the source would be deleted with the segments it was to keep") — a future reader meets the danger before the guard, not after the data loss.
  • Renaming the test to carry the new case (…or_into_itself…) instead of bolting on a sibling test — one refusal theory, all four arms, one nothing-changed assertion at the end. Tidy.
  • The store now refuses all three impossible merges itself; the port is safe to hold for whoever meets it next. Which was the whole point of the memo~

Merge it. The story half is complete — for real, this time~ ♡


Automated review by Jibril · 2026-09-27
CI/CD: coverage bot 10282 covers 8054fa1 only (stale for head) · Local checks: build 0w/0e, Integration 621/621, BlazorAdapter 529/529, mutation probe killed

## 🔮 fufu~ Jibril reviewed your code! You took the memo~ The port no longer trusts its callers, and the store refuses to eat its own chapter. A guard placed *before* the lock, throwing before a single row is read — a refusal that cannot half-happen. That is exactly where I wanted it. ♡ ### Verdict: ✅ Looks good to me~ The increment `8054fa1..376596e` is surgical: +22/−7 over exactly 4 files, all four belonging to the one idea. Zero scope creep. **What I verified:** - **The guard is synchronous and pre-lock** (`EfChapterStore.MergeAsync`): `sourceId == targetId` throws `InvalidOperationException` before `WriteAsync` is even entered — nothing read, nothing tracked, nothing changed. The interface doc now lists the refusal alongside its two siblings (gone, across-stories), and `FakeChapterStore` refuses identically so the adapter tests can't drift from the real store. - **Assert.ThrowsAsync catches the sync throw correctly** — and I didn't take that on faith: with the guard deleted, the test fails with *"No exception was thrown"* on exactly the `itself` row, proving the assertion really bites. - **`MergeChapters` keeps its own earlier `Result.Fail` guard** — defense in depth, use-case layer untouched, message string mirroring its store sibling exactly as the other two refusals already do. Consistent~ - **Local** (clone pristine at `376596e`, submodules Kagaku.UI `6af0979` + OpenRouter.Net `b002f5e`, full rebuild, never `--no-build`): Release build **0 warnings, 0 errors**; Integration **621/621**, BlazorAdapter **529/529** — digit-exact against your reply and the PR body. **My mutation probe:** the guard deleted (infrastructure + test project rebuilt, DLL md5 noted before trusting the result — I remember the stale-binary trap, yes~) → of the 17 merge-filtered tests, exactly **one** red: *A_merge_of_a_gone_chapter_across_stories_or_into_itself_is_refused_with_nothing_changed*, on its own `itself` assertion. Every neighbor stayed green. Tree restored pristine, re-green. The pin is directional, not a tautology. ♪ #### ✅ What I liked~ - The comment above the guard says *why* in one line ("the source would be deleted with the segments it was to keep") — a future reader meets the danger before the guard, not after the data loss. - Renaming the test to carry the new case (`…or_into_itself…`) instead of bolting on a sibling test — one refusal theory, all four arms, one nothing-changed assertion at the end. Tidy. - The store now refuses all three impossible merges itself; the port is safe to hold for whoever meets it next. Which was the whole point of the memo~ Merge it. The story half is complete — for real, this time~ ♡ --- *Automated review by Jibril · 2026-09-27* *CI/CD: coverage bot 10282 covers 8054fa1 only (stale for head) · Local checks: build 0w/0e, Integration 621/621, BlazorAdapter 529/529, mutation probe killed*
bjoern merged commit 2290e7c74a into main 2026-09-27 00:42:04 +02:00
bjoern deleted branch feat/commit-node 2026-09-27 00:42:04 +02:00
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!64
No description provided.