feat: Kagaku.Flow core — a graph loads and validates without Tsumugi (Phase 2 · slice 5a) #18

Merged
bjoern merged 4 commits from feat/kagaku-flow-core into main 2026-09-23 15:25:21 +02:00
Member

Phase 2, slice 5, first half: the engine's core library, src/Kagaku.Flow, with its tests. It holds the graph document, typed sockets, node descriptors with the registry and canvas JSON, and pure validation with host rules. Slice 5's second half, Scriban templating with template-derived sinks and the IBudgetedConsumer contract, follows as its own PR on top of this one. Together they came to ~5.9k lines, so they are split along a line that needs no change to this half's public API. Slices 6 (scheduler), 8 (llm) and 9 (context/commit) build on this API additively.

Nothing here runs a graph or changes what a user sees. The playground keeps SampleGraph until slice 9.

What

  • Kagaku.Flow.Graphs
    • The graph document: GraphDefinition, GraphNode, GraphEdge, SocketRef and GraphPosition, all immutable records.
    • GraphDocument.Load/Write, which speaks the wire shape SampleGraph already uses, in compact form with source property order.
    • A versioned loader with an internal migration hook (IGraphMigration), and GraphIndex.
    • The loader is strict and total. An unknown property, a missing, invalid or newer schemaVersion, a lone surrogate or an out-of-range number is a GraphLoadError with a stable code and a JSON Pointer. It is never an exception and never a half-loaded graph (ADR 0014, 0023).
    • The records refuse what the loader refuses, with included, so a graph built in code writes back the same bytes it loads from.
  • Kagaku.Flow.Nodes
    • SocketType (text, json, list, bool, any) and SocketTypes.Accepts.
    • SinkDescriptor (required and merge flags), SourceDescriptor, and NodeSockets, which compares by content.
    • NodeDescriptor. Its constructor requires MaxAttempts and ConsumesConcurrencySlot, with no defaults, so a model-calling type cannot forget the LLM limit (ADR 0016). It also has the palette group, the colour token, the container flag, a ConfigSchema carried as data, DefaultConfig, ResolveSockets(config) and ValidateConfig(config).
    • NodeRegistry, and the canvas descriptor JSON, byte for byte what GraphEditor/model.ts reads. The registry refuses null lists, and names the position of a null descriptor or rule.
  • Kagaku.Flow.Validation
    • GraphValidation.Validate(definition, registry) is pure and deterministic, and never throws (ADR 0021). It runs nine passes in a fixed, pinned order. There are 19 core rules with stable codes in ValidationCodes, among them unknown type, invalid config, duplicate ids, missing endpoints, undeclared ports, type mismatch, required sink unwired, several edges into a non-merge sink, unknown parent, parent not a container, and cycle.
    • Host rules implement IGraphRule and register through the registry. A throwing rule is rule-failed, and a throwing descriptor hook is descriptor-failed, never the user's invalid-config.
    • Each ValidationError names its node and edge, plus a JSON Pointer into the config when that is where the problem sits.
  • Isolation (ADR 0025).
    • Kagaku_Flow_references_no_Tsumugi_assembly reads the references of every Kagaku.Flow* assembly in the test output, and fails if a Tsumugi*.dll sits there. The file is shared through tests/Shared/EngineIsolation.cs, which slice 7 links into Kagaku.Flow.OpenRouter.Tests.
    • A second test fails if the core holds the literals llm, tool, context or commit: the core never names a type it does not ship.
    • Tsumugi.UseCases now references Kagaku.Flow (ADR 0003).
  • Fixtures and drift. The engine's tests carry copies of SampleGraph.GraphJson and DescriptorsJson as embedded resources. SampleGraphDriftTests in BlazorAdapter.Tests compares both copies with the literals by reading the files, which adds no assembly reference, so the two cannot drift apart.
  • Docs. src/Kagaku.Flow/README.md lists the numbered rules (G, S, D, C, V, A) the XML docs cite. GenerateDocumentationFile is on for the project, so a missing summary fails the build. ADRs are cited by number, as elsewhere in the codebase; the engine leaves this repo later.
  • First commit: the two doc suggestions from #16. REQUIREMENTS names the story as the engine's owner, and ADR 0020's card bullet is rewrapped.

Decisions to confirm (Björn)

  1. An any source may feed a sink of every type. This is the one that matters. Read alone, ADR 0015's "a sink socket of the same type; any accepts everything" makes an any source into a text sink a mismatch. But ADR 0015 also says the owner's graph is expressible with these types, and that graph wires branch, whose sources pass value: any through, into commit.text: text. So any is read as "unknown type" at either end, and slice 6 checks the value's actual type when it binds it. A list sink still gets a list or fails, so conversions stay explicit. No Phase 2 type has an any source, so nothing behaves differently now. If you prefer the literal reading, Accepts loses one clause, and ADR 0015 needs an amendment before branch lands.
  2. The canvas JSON is a projection. It carries exactly what the module reads, and leaves out attempts, the concurrency slot, the container and merge flags, and the config schema. Adding them later is additive, because the TS ignores unknown fields. The colour token keeps its leading -- (var(--accent)).
  3. Validation checks each node's resolved sockets (ResolveSockets(config)), while the canvas still shows each type's static sockets. Per-node sockets on the wire are Phase 3 editor work.
  4. "Invalid config" is the descriptor's ValidateConfig, not a JSON-Schema check. The core has no schema validator; JsonSchema.Net is Kagaku.Flow.OpenRouter's, for response content. Each type's tests check that its schema and ValidateConfig agree.
  5. Every cycle is an error for now. The loop.next exception arrives with loop in Phase 3, because the core cannot key it on a type it does not ship.
  6. Model-catalog checks are a run preflight, not validation rules. For example, "a schema is set but the model has no structured_outputs". Validation is a pure function over the definition, and the catalog is fetched and cached. This moves that check from the plan's slice 8 rule list to the Run button's preflight, plus a permanent failure in the executor. ValidationError therefore has no severity.
  7. The document's top-level inputs/outputs round-trip opaque until Phase 3 settles whether they or the boundary nodes define a subgraph's interface.
  8. Node-config migrations are deferred. The core's migrations cannot name a package's node type. Until the hook lands, slices 8 and 9 keep their configs backward-readable.
  9. ADR 0003's "graph definition" in Domain is read as the graph library's entity, with its definition as JSON text (slice 10a). The parsed GraphDefinition is Kagaku.Flow's. The Domain csproj comment says so; it still promised runs, which #16 moved out.

Tests

main (d7dfad2, after #17) this PR
Domain 37 37
UseCases 12 12
Integration 155 155
BlazorAdapter 213 216
Kagaku.Flow — 296
Total 417 716

Kagaku.Flow's 296 are 290 facts and theory rows plus 6 FsCheck [Property] tests. FsCheck.Xunit 3.4.0 works with the repo's xunit 2.9.3. The properties:

  • a generated well-typed DAG validates clean;
  • retyping one edge's source yields exactly one mismatch, on that edge;
  • closing a path into a cycle reports exactly the cycle edges;
  • a generated graph round-trips through its document form byte for byte;
  • Load never throws on arbitrary text, and what it loads, it writes;
  • the records refuse exactly what the loader refuses.

ValidationOrderTests pins every documented ordering with a fact that fails when two passes swap, and 15 reverse-order shape rows pin the loader's read order.

Build: the full CI build (dotnet build Tsumugi.slnx -c Release, including the module's npm build) has 0 warnings. The module's own npm test passes 14/14.

Coverage: coverlet, per changed file, line and branch (branch="True"), on facts and theories alone (--filter "Category!=Property", so the properties cannot hide a gap). Every file in src/Kagaku.Flow is at 100% line and 100% branch. The only partials left are in the regex source generator's output (RegexGenerator.g.cs, not a file in this diff): the pos != 0 guard in each TryMatchAtCurrentPosition and the default-match-timeout arm, which IsMatch cannot reach.

Mutation probes: every probe of the brief's list for this half, run on a copy, one at a time, restored and touched before the next, then re-run after the review round. 17 survived the first cut, and the review round added tests until all 17 died. Those covered:

  • an edge's own checks before the next edge;
  • reachability through a missing node;
  • the loader's read order at each level;
  • the canvas getting static sockets;
  • a missing node named once;
  • and others.

19 more against the review round's new code all died: the pre-migration text check, each record guard (including the with-only ones), socket-set equality and copying, the undefined socket-type guard, and the null finding and null problem guards.

Out of scope

  • Templating, sink derivation, the template validation pass, and IBudgetedConsumer: the next PR.
  • Executors, the ledger, the scheduler and run events: slice 6. It adds executors to the registry through a constructor that refuses a descriptor without one; this PR defines no executor interface.
  • Any node type Tsumugi ships.

🤖 Generated with Claude Code

Phase 2, slice 5, first half: the engine's core library, `src/Kagaku.Flow`, with its tests. It holds the graph document, typed sockets, node descriptors with the registry and canvas JSON, and pure validation with host rules. Slice 5's second half, Scriban templating with template-derived sinks and the `IBudgetedConsumer` contract, follows as its own PR on top of this one. Together they came to ~5.9k lines, so they are split along a line that needs no change to this half's public API. Slices 6 (scheduler), 8 (`llm`) and 9 (`context`/`commit`) build on this API additively. Nothing here runs a graph or changes what a user sees. The playground keeps `SampleGraph` until slice 9. ## What - **`Kagaku.Flow.Graphs`** - The graph document: `GraphDefinition`, `GraphNode`, `GraphEdge`, `SocketRef` and `GraphPosition`, all immutable records. - `GraphDocument.Load`/`Write`, which speaks the wire shape `SampleGraph` already uses, in compact form with source property order. - A versioned loader with an internal migration hook (`IGraphMigration`), and `GraphIndex`. - The loader is **strict and total**. An unknown property, a missing, invalid or newer `schemaVersion`, a lone surrogate or an out-of-range number is a `GraphLoadError` with a stable code and a JSON Pointer. It is never an exception and never a half-loaded graph (ADR 0014, 0023). - The records refuse what the loader refuses, `with` included, so a graph built in code writes back the same bytes it loads from. - **`Kagaku.Flow.Nodes`** - `SocketType` (text, json, list, bool, any) and `SocketTypes.Accepts`. - `SinkDescriptor` (required and merge flags), `SourceDescriptor`, and `NodeSockets`, which compares by content. - `NodeDescriptor`. Its constructor **requires** `MaxAttempts` and `ConsumesConcurrencySlot`, with no defaults, so a model-calling type cannot forget the LLM limit (ADR 0016). It also has the palette group, the colour token, the container flag, a `ConfigSchema` carried as data, `DefaultConfig`, `ResolveSockets(config)` and `ValidateConfig(config)`. - `NodeRegistry`, and the canvas descriptor JSON, byte for byte what `GraphEditor/model.ts` reads. The registry refuses null lists, and names the position of a null descriptor or rule. - **`Kagaku.Flow.Validation`** - `GraphValidation.Validate(definition, registry)` is pure and deterministic, and never throws (ADR 0021). It runs nine passes in a fixed, pinned order. There are 19 core rules with stable codes in `ValidationCodes`, among them unknown type, invalid config, duplicate ids, missing endpoints, undeclared ports, type mismatch, required sink unwired, several edges into a non-merge sink, unknown parent, parent not a container, and cycle. - Host rules implement `IGraphRule` and register through the registry. A throwing rule is `rule-failed`, and a throwing descriptor hook is `descriptor-failed`, never the user's `invalid-config`. - Each `ValidationError` names its node **and** edge, plus a JSON Pointer into the config when that is where the problem sits. - **Isolation (ADR 0025).** - `Kagaku_Flow_references_no_Tsumugi_assembly` reads the references of every `Kagaku.Flow*` assembly in the test output, and fails if a `Tsumugi*.dll` sits there. The file is shared through `tests/Shared/EngineIsolation.cs`, which slice 7 links into `Kagaku.Flow.OpenRouter.Tests`. - A second test fails if the core holds the literals `llm`, `tool`, `context` or `commit`: the core never names a type it does not ship. - `Tsumugi.UseCases` now references `Kagaku.Flow` (ADR 0003). - **Fixtures and drift.** The engine's tests carry copies of `SampleGraph.GraphJson` and `DescriptorsJson` as embedded resources. `SampleGraphDriftTests` in BlazorAdapter.Tests compares both copies with the literals by reading the files, which adds no assembly reference, so the two cannot drift apart. - **Docs.** `src/Kagaku.Flow/README.md` lists the numbered rules (G, S, D, C, V, A) the XML docs cite. `GenerateDocumentationFile` is on for the project, so a missing summary fails the build. ADRs are cited by number, as elsewhere in the codebase; the engine leaves this repo later. - **First commit:** the two doc suggestions from #16. REQUIREMENTS names the story as the engine's owner, and ADR 0020's card bullet is rewrapped. ## Decisions to confirm (Björn) 1. **An `any` source may feed a sink of every type.** This is the one that matters. Read alone, ADR 0015's "a sink socket of the same type; `any` accepts everything" makes an `any` source into a `text` sink a mismatch. But ADR 0015 also says the owner's graph is expressible with these types, and that graph wires `branch`, whose sources pass `value: any` through, into `commit.text: text`. So `any` is read as "unknown type" at either end, and slice 6 checks the value's actual type when it binds it. A `list` sink still gets a list or fails, so conversions stay explicit. No Phase 2 type has an `any` source, so nothing behaves differently now. If you prefer the literal reading, `Accepts` loses one clause, and ADR 0015 needs an amendment before `branch` lands. 2. **The canvas JSON is a projection.** It carries exactly what the module reads, and leaves out attempts, the concurrency slot, the container and merge flags, and the config schema. Adding them later is additive, because the TS ignores unknown fields. The colour token keeps its leading `--` (`var(--accent)`). 3. **Validation checks each node's resolved sockets** (`ResolveSockets(config)`), while the canvas still shows each type's static sockets. Per-node sockets on the wire are Phase 3 editor work. 4. **"Invalid config" is the descriptor's `ValidateConfig`, not a JSON-Schema check.** The core has no schema validator; JsonSchema.Net is `Kagaku.Flow.OpenRouter`'s, for response content. Each type's tests check that its schema and `ValidateConfig` agree. 5. **Every cycle is an error for now.** The `loop.next` exception arrives with `loop` in Phase 3, because the core cannot key it on a type it does not ship. 6. **Model-catalog checks are a run preflight, not validation rules.** For example, "a schema is set but the model has no `structured_outputs`". Validation is a pure function over the definition, and the catalog is fetched and cached. This moves that check from the plan's slice 8 rule list to the Run button's preflight, plus a permanent failure in the executor. `ValidationError` therefore has no severity. 7. **The document's top-level `inputs`/`outputs` round-trip opaque** until Phase 3 settles whether they or the boundary nodes define a subgraph's interface. 8. **Node-config migrations are deferred.** The core's migrations cannot name a package's node type. Until the hook lands, slices 8 and 9 keep their configs backward-readable. 9. **ADR 0003's "graph definition" in Domain** is read as the graph library's entity, with its definition as JSON text (slice 10a). The parsed `GraphDefinition` is Kagaku.Flow's. The Domain csproj comment says so; it still promised runs, which #16 moved out. ## Tests | | main (`d7dfad2`, after #17) | this PR | |---|---|---| | Domain | 37 | 37 | | UseCases | 12 | 12 | | Integration | 155 | 155 | | BlazorAdapter | 213 | 216 | | Kagaku.Flow | — | 296 | | **Total** | **417** | **716** | Kagaku.Flow's 296 are 290 facts and theory rows plus 6 FsCheck `[Property]` tests. FsCheck.Xunit 3.4.0 works with the repo's xunit 2.9.3. The properties: - a generated well-typed DAG validates clean; - retyping one edge's source yields exactly one mismatch, on that edge; - closing a path into a cycle reports exactly the cycle edges; - a generated graph round-trips through its document form byte for byte; - `Load` never throws on arbitrary text, and what it loads, it writes; - the records refuse exactly what the loader refuses. `ValidationOrderTests` pins every documented ordering with a fact that fails when two passes swap, and 15 reverse-order shape rows pin the loader's read order. **Build:** the full CI build (`dotnet build Tsumugi.slnx -c Release`, including the module's npm build) has 0 warnings. The module's own `npm test` passes 14/14. **Coverage:** coverlet, per changed file, line and branch (`branch="True"`), on facts and theories alone (`--filter "Category!=Property"`, so the properties cannot hide a gap). Every file in `src/Kagaku.Flow` is at **100% line and 100% branch**. The only partials left are in the regex source generator's output (`RegexGenerator.g.cs`, not a file in this diff): the `pos != 0` guard in each `TryMatchAtCurrentPosition` and the default-match-timeout arm, which `IsMatch` cannot reach. **Mutation probes:** every probe of the brief's list for this half, run on a copy, one at a time, restored and `touch`ed before the next, then re-run after the review round. 17 survived the first cut, and the review round added tests until all 17 died. Those covered: - an edge's own checks before the next edge; - reachability through a missing node; - the loader's read order at each level; - the canvas getting static sockets; - a missing node named once; - and others. 19 more against the review round's new code all died: the pre-migration text check, each record guard (including the `with`-only ones), socket-set equality and copying, the undefined socket-type guard, and the null finding and null problem guards. ## Out of scope - Templating, sink derivation, the template validation pass, and `IBudgetedConsumer`: the next PR. - Executors, the ledger, the scheduler and run events: slice 6. It adds executors to the registry through a constructor that refuses a descriptor without one; this PR defines no executor interface. - Any node type Tsumugi ships. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The two nits from the PR #16 review. The Engine epic now says who owns a run (the story, ADR
0016). ADR 0020's card bullet is rewrapped to the file's width; its text is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Kagaku.Flow is the engine library of ADR 0025, built in the solution first. This commit gives it the
graph document (load, migrate in memory, write), the socket types and their compatibility, node
descriptors and the registry that holds them with the host's rules, and a pure validation function
over a graph and a registry. UseCases references it; nothing registers or runs a graph yet. The rules
the docs and tests cite by id (G1, V2 …) are listed in src/Kagaku.Flow/README.md.

The loader is strict because a schema change is a version (ADR 0014:81-82): an unknown property, a
missing one or a value of the wrong kind is refused with a code and a JSON Pointer to it, never
guessed at, so an import can say exactly what is wrong and a document this app cannot read is never
half-read. Load is total, and the records refuse whatever the loader refuses, so a graph the loader
returns always writes and a graph a host builds always writes a document that loads back.

The canvas JSON is a projection of the registry, not the descriptors themselves: per type the id,
the palette group, the colour token and the static sockets, which is all the module reads. Attempts,
concurrency slots, merge flags and config schemas stay on the .NET side, where the scheduler and the
validator use them, so the canvas cannot come to depend on them.

The architecture tests read files rather than the compiler's project graph: a ProjectReference whose
types go unused emits no assembly reference but still copies the DLL next to the tests, and a linked
source file shares code with no reference at all. So every Kagaku.Flow test project scans its own
output for Tsumugi references, Tsumugi DLLs and Tsumugi namespaces, and the core's string heap for
the node types and object model it must not name.

The engine's tests may not reference Tsumugi, so they carry copies of the playground's SampleGraph
literals as fixtures. SampleGraphDriftTests, on the Tsumugi side, reads those files from the source
tree and fails when a literal and its copy differ; it also loads the sample graph through the engine
and checks that it writes back its compact form.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs(flow): cite ADRs by number, not by line
All checks were successful
CI / build (pull_request) Successful in 31s
CI / test (pull_request) Successful in 45s
7770497bbd
The engine's XML docs, README and tests cited ADR line ranges; the ADRs
are amended as the phases go, and the engine leaves this repo later, so
the citations name the ADR alone, as the rest of the codebase does.

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: 555c4d0 · Generated: 2026-09-23 13:24:47 UTC · Revision: #2

Summary

Summary
Generated on: 09/23/2026 - 13:24:47
Coverage date: 09/23/2026 - 13:24:35 - 09/23/2026 - 13:24:44
Parser: MultiReport (5x Cobertura)
Assemblies: 7
Classes: 298
Files: 287
Line coverage: 97.6% (6657 of 6817)
Covered lines: 6657
Uncovered lines: 160
Coverable lines: 6817
Total lines: 14606
Branch coverage: 91.8% (2007 of 2184)
Covered branches: 2007
Total branches: 2184
Method coverage: Feature is only available for sponsors

Coverage

Kagaku.Flow - 99.3%
Name Line Branch
Kagaku.Flow 99.3% 98.1%
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.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%
Tsumugi.BlazorAdapter - 96.9%
Name Line Branch
Tsumugi.BlazorAdapter 96.9% 93.4%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 100%
Tsumugi.BlazorAdapter.Documents.CategoryCreated 100%
Tsumugi.BlazorAdapter.Documents.CategoryEditor 97.7% 86.1%
Tsumugi.BlazorAdapter.Documents.CategorySaved 100%
Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog 100% 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentCreated 100%
Tsumugi.BlazorAdapter.Documents.DocumentEditor 100% 87.9%
Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentSaved 100%
Tsumugi.BlazorAdapter.Documents.DocumentsEffects 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsList 98.2% 91.3%
Tsumugi.BlazorAdapter.Documents.DocumentsLoaded 100%
Tsumugi.BlazorAdapter.Documents.DocumentsPage 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsReducers 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsState 100% 100%
Tsumugi.BlazorAdapter.Documents.LoadDocuments 100%
Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested 100%
Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested 100%
Tsumugi.BlazorAdapter.Documents.SetCategoryCollapsed 100%
Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter 100%
Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested 100%
Tsumugi.BlazorAdapter.Graphs.GraphCanvas 37.5% 25%
Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage 20% 25%
Tsumugi.BlazorAdapter.Notifications.DomainChangedBridge 94.4% 83.3%
Tsumugi.BlazorAdapter.Notifications.DomainChangesReceived 75% 100%
Tsumugi.BlazorAdapter.Runs.RunChangedBridge 97.1% 90.6%
Tsumugi.BlazorAdapter.Runs.RunChangesReceived 100%
Tsumugi.BlazorAdapter.Runs.RunTextReceived 100%
Tsumugi.BlazorAdapter.Settings.BackupCompleted 100%
Tsumugi.BlazorAdapter.Settings.BackupFailed 100%
Tsumugi.BlazorAdapter.Settings.BackupNowRequested 100%
Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.BackupsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsState 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsState 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsLoaded 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaved 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaveFailed 100%
Tsumugi.BlazorAdapter.Settings.LoadBackupSettings 100%
Tsumugi.BlazorAdapter.Settings.ProviderSection 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsState 100%
Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested 100%
Tsumugi.BlazorAdapter.Stories.CreateStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.PurgeStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.RestoreStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.SetShowArchived 100%
Tsumugi.BlazorAdapter.Stories.SetStoriesFilter 100%
Tsumugi.BlazorAdapter.Stories.SetStoryArchivedRequested 100%
Tsumugi.BlazorAdapter.Stories.StoriesEffects 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesLoaded 100%
Tsumugi.BlazorAdapter.Stories.StoriesPage 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesReducers 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesState 100% 100%
Tsumugi.BlazorAdapter.Stories.StoryActionFailed 100%
Tsumugi.BlazorAdapter.Stories.StoryCreated 100%
Tsumugi.BlazorAdapter.Stories.StoryCreateFailed 100%
Tsumugi.BlazorAdapter.Stories.StoryTrashPage 100% 100%
Tsumugi.BlazorAdapter.Stories.TrashStoryRequested 100%
Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed 100%
Tsumugi.BlazorAdapter.Workspace.CaretRequest 100%
Tsumugi.BlazorAdapter.Workspace.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 90% 83.3%
Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.DeleteSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.EditSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.HistoryEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.HistoryLoaded 100%
Tsumugi.BlazorAdapter.Workspace.HistoryPage 100%
Tsumugi.BlazorAdapter.Workspace.HistoryPanel 100% 100%
Tsumugi.BlazorAdapter.Workspace.InsertSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.LoadHistory 100%
Tsumugi.BlazorAdapter.Workspace.LoadWorkspace 100%
Tsumugi.BlazorAdapter.Workspace.ManuscriptColumn 94.9% 89.4%
Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.MergeSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested 100%
Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested 100%
Tsumugi.BlazorAdapter.Workspace.SaveScope 100% 50%
Tsumugi.BlazorAdapter.Workspace.SaveStateLadder 100% 100%
Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested 100%
Tsumugi.BlazorAdapter.Workspace.SegmentRow 100% 96.8%
Tsumugi.BlazorAdapter.Workspace.SegmentSaved 100%
Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested 100%
Tsumugi.BlazorAdapter.Workspace.SplitSegmentRequested 100%
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 - 99.3%
Name Line Branch
Tsumugi.Domain 99.3% 95.6%
Tsumugi.Domain.Documents.Document 100% 100%
Tsumugi.Domain.Documents.DocumentCategory 100%
Tsumugi.Domain.Journal.ChangeLogEntry 97.8% 75%
Tsumugi.Domain.Journal.StorySnapshot 95.4% 83.3%
Tsumugi.Domain.Settings.AppSetting 100%
Tsumugi.Domain.Stories.Chapter 100% 100%
Tsumugi.Domain.Stories.Provenance 100% 100%
Tsumugi.Domain.Stories.Segment 100% 100%
Tsumugi.Domain.Stories.Story 100% 100%
Tsumugi.Domain.Text 100% 100%
Tsumugi.Infrastructure - 97.1%
Name Line Branch
Tsumugi.Infrastructure 97.1% 82.7%
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 100% 100%
Tsumugi.Infrastructure.Backups.BackupWriter 98.4% 92.5%
Tsumugi.Infrastructure.Backups.FileSystemBackupStore 100% 100%
Tsumugi.Infrastructure.Backups.SqliteSnapshot 87.8% 75%
Tsumugi.Infrastructure.DependencyInjection 100% 100%
Tsumugi.Infrastructure.Journal.EfChangeJournal 100%
Tsumugi.Infrastructure.Journal.EfSnapshotStore 100%
Tsumugi.Infrastructure.Journal.EfUndoStore 98.1% 85.7%
Tsumugi.Infrastructure.Journal.ModelExtensions 100%
Tsumugi.Infrastructure.Journal.OperationContext 100% 100%
Tsumugi.Infrastructure.Journal.PropertyBag 100% 95.4%
Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus 100% 100%
Tsumugi.Infrastructure.Persistence.Configurations.AppSettingConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.ChangeLogEntryConfigurati
on
100%
Tsumugi.Infrastructure.Persistence.Configurations.ChapterConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.DocumentCategoryConfigura
tion
100%
Tsumugi.Infrastructure.Persistence.Configurations.DocumentConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.SegmentConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio
n
100%
Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter 100% 90%
Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter 100%
Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings 99.2%
Tsumugi.Infrastructure.Persistence.Migrations.AddJournal 98.8%
Tsumugi.Infrastructure.Persistence.Migrations.GenericDocuments 100%
Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain 97.3%
Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot 100%
Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore 97.5% 90%
Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore 95% 84.6%
Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore 98.3% 92.8%
Tsumugi.Infrastructure.Persistence.TsumugiDbContext 83.5% 93.1%
Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory 0%
Tsumugi.Infrastructure.Runs.InProcessRunEventBus 100% 100%
Tsumugi.Infrastructure.Settings.EfAppSettingsStore 100% 100%
Tsumugi.Kernel - 90.9%
Name Line Branch
Tsumugi.Kernel 90.9% 75%
Tsumugi.Kernel.Err`1 100%
Tsumugi.Kernel.Ok`1 100%
Tsumugi.Kernel.Result`1 88.8% 75%
Tsumugi.Server - 100%
Name Line Branch
Tsumugi.Server 100% 79%
Program 100% 83.3%
Tsumugi.Server.Backups.BackupConfiguration 100% 100%
Tsumugi.Server.Components.App 100%
Tsumugi.Server.Components.Layout.AppHeader 100% 50%
Tsumugi.Server.Components.Layout.MainLayout 100%
Tsumugi.Server.Components.Pages.Gate 100% 100%
Tsumugi.Server.Security.AccessGate 100% 66.6%
Tsumugi.Server.Security.AccessSecret 100% 50%
Tsumugi.Server.VolumeStartupValidator 100% 100%
Tsumugi.UseCases - 98%
Name Line Branch
Tsumugi.UseCases 98% 94%
Tsumugi.UseCases.Backups.BackupArtifact 100%
Tsumugi.UseCases.Backups.BackupPolicy 100%
Tsumugi.UseCases.Backups.BackupStatusView 100% 100%
Tsumugi.UseCases.Backups.DatabaseSnapshot 100%
Tsumugi.UseCases.Backups.DownloadDatabaseSnapshot 100%
Tsumugi.UseCases.Backups.GetBackupStatus 100%
Tsumugi.UseCases.Backups.RunBackupNow 100%
Tsumugi.UseCases.Chapters.ChapterDto 100%
Tsumugi.UseCases.Chapters.CreateChapter 100% 100%
Tsumugi.UseCases.Chapters.DeleteChapter 100% 100%
Tsumugi.UseCases.Chapters.GetManuscript 100% 100%
Tsumugi.UseCases.Chapters.ManuscriptChapter 100%
Tsumugi.UseCases.Chapters.ManuscriptView 100%
Tsumugi.UseCases.Chapters.MergeChapters 100% 100%
Tsumugi.UseCases.Chapters.RenameChapter 100% 100%
Tsumugi.UseCases.Chapters.ReorderChapters 100% 100%
Tsumugi.UseCases.Chapters.SetChapterSummary 100% 75%
Tsumugi.UseCases.DependencyInjection 100%
Tsumugi.UseCases.Diagnostics.SeedDevData 97.3% 70%
Tsumugi.UseCases.Documents.CreateCategory 100% 100%
Tsumugi.UseCases.Documents.CreateDocument 100% 100%
Tsumugi.UseCases.Documents.DeleteCategory 100% 100%
Tsumugi.UseCases.Documents.DeleteDocument 100% 100%
Tsumugi.UseCases.Documents.DocumentCategoryDto 100%
Tsumugi.UseCases.Documents.DocumentCategoryProfile 100%
Tsumugi.UseCases.Documents.DocumentDto 100%
Tsumugi.UseCases.Documents.DocumentProfile 100%
Tsumugi.UseCases.Documents.DocumentsCategory 100%
Tsumugi.UseCases.Documents.DocumentsView 100%
Tsumugi.UseCases.Documents.GetDocuments 100% 100%
Tsumugi.UseCases.Documents.MoveDocuments 100% 100%
Tsumugi.UseCases.Documents.ReorderCategories 100% 100%
Tsumugi.UseCases.Documents.UpdateCategory 100% 100%
Tsumugi.UseCases.Documents.UpdateDocument 100% 100%
Tsumugi.UseCases.Journal.GetStoryHistory 100% 100%
Tsumugi.UseCases.Journal.GetUndoStatus 100% 100%
Tsumugi.UseCases.Journal.HistoryOperation 66.6%
Tsumugi.UseCases.Journal.HistorySnapshot 87.5%
Tsumugi.UseCases.Journal.OperationGrouping 100% 100%
Tsumugi.UseCases.Journal.RedoRevert 100% 100%
Tsumugi.UseCases.Journal.RedoRun 75% 57.1%
Tsumugi.UseCases.Journal.RedoTyping 100% 100%
Tsumugi.UseCases.Journal.RevertConflict 100%
Tsumugi.UseCases.Journal.RevertOutcome 100%
Tsumugi.UseCases.Journal.RevertRun 96.5% 77.7%
Tsumugi.UseCases.Journal.RevertToSnapshot 92.3% 83.3%
Tsumugi.UseCases.Journal.StoryHistory 100%
Tsumugi.UseCases.Journal.TakeSnapshot 100% 87.5%
Tsumugi.UseCases.Journal.UndoStatus 100%
Tsumugi.UseCases.Journal.UndoTyping 100% 100%
Tsumugi.UseCases.Journal.UserOperation 100%
Tsumugi.UseCases.Notifications.DomainChanged 100%
Tsumugi.UseCases.Ordering 100% 100%
Tsumugi.UseCases.Runs.RunEvent 100%
Tsumugi.UseCases.Segments.DeleteSegments 100% 100%
Tsumugi.UseCases.Segments.EditSegment 100% 83.3%
Tsumugi.UseCases.Segments.InsertSegment 100% 100%
Tsumugi.UseCases.Segments.MergeSegmentWithPrevious 100% 100%
Tsumugi.UseCases.Segments.MoveSegments 100% 100%
Tsumugi.UseCases.Segments.ReorderSegments 100% 100%
Tsumugi.UseCases.Segments.SegmentDto 100%
Tsumugi.UseCases.Segments.SplitSegment 100% 100%
Tsumugi.UseCases.Settings.GetLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.GetProviderSettings 100%
Tsumugi.UseCases.Settings.LlmDefaults 100%
Tsumugi.UseCases.Settings.ProviderSettingsView 100%
Tsumugi.UseCases.Settings.ReasoningEfforts 100% 100%
Tsumugi.UseCases.Settings.RemoveOpenRouterKey 100%
Tsumugi.UseCases.Settings.SaveLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.SaveOpenRouterKey 100% 100%
Tsumugi.UseCases.Stories.CreateStory 100% 100%
Tsumugi.UseCases.Stories.GetStory 100% 100%
Tsumugi.UseCases.Stories.ListStories 100% 75%
Tsumugi.UseCases.Stories.PurgeStory 100% 100%
Tsumugi.UseCases.Stories.RestoreStory 100% 100%
Tsumugi.UseCases.Stories.SetActiveGraph 100% 100%
Tsumugi.UseCases.Stories.SetStoryArchived 100% 100%
Tsumugi.UseCases.Stories.StarterCategories 100%
Tsumugi.UseCases.Stories.StoryDetail 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:** `555c4d0` · **Generated:** 2026-09-23 13:24:47 UTC · **Revision:** #2 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/23/2026 - 13:24:47 | | Coverage date: | 09/23/2026 - 13:24:35 - 09/23/2026 - 13:24:44 | | Parser: | MultiReport (5x Cobertura) | | Assemblies: | 7 | | Classes: | 298 | | Files: | 287 | | **Line coverage:** | 97.6% (6657 of 6817) | | Covered lines: | 6657 | | Uncovered lines: | 160 | | Coverable lines: | 6817 | | Total lines: | 14606 | | **Branch coverage:** | 91.8% (2007 of 2184) | | Covered branches: | 2007 | | Total branches: | 2184 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Kagaku.Flow - 99.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow**|**99.3%**|**98.1%**| |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.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>Tsumugi.BlazorAdapter - 96.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**96.9%**|**93.4%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryCreated|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryEditor|97.7%|86.1%| |Tsumugi.BlazorAdapter.Documents.CategorySaved|100%|| |Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog|100%|100%| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentCreated|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentEditor|100%|87.9%| |Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentSaved|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsEffects|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsList|98.2%|91.3%| |Tsumugi.BlazorAdapter.Documents.DocumentsLoaded|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsPage|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsReducers|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsState|100%|100%| |Tsumugi.BlazorAdapter.Documents.LoadDocuments|100%|| |Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested|100%|| |Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested|100%|| |Tsumugi.BlazorAdapter.Documents.SetCategoryCollapsed|100%|| |Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter|100%|| |Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Graphs.GraphCanvas|37.5%|25%| |Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage|20%|25%| |Tsumugi.BlazorAdapter.Notifications.DomainChangedBridge|94.4%|83.3%| |Tsumugi.BlazorAdapter.Notifications.DomainChangesReceived|75%|100%| |Tsumugi.BlazorAdapter.Runs.RunChangedBridge|97.1%|90.6%| |Tsumugi.BlazorAdapter.Runs.RunChangesReceived|100%|| |Tsumugi.BlazorAdapter.Runs.RunTextReceived|100%|| |Tsumugi.BlazorAdapter.Settings.BackupCompleted|100%|| |Tsumugi.BlazorAdapter.Settings.BackupFailed|100%|| |Tsumugi.BlazorAdapter.Settings.BackupNowRequested|100%|| |Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.DefaultsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaved|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaveFailed|100%|| |Tsumugi.BlazorAdapter.Settings.LoadBackupSettings|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested|100%|| |Tsumugi.BlazorAdapter.Stories.CreateStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.PurgeStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.RestoreStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.SetShowArchived|100%|| |Tsumugi.BlazorAdapter.Stories.SetStoriesFilter|100%|| |Tsumugi.BlazorAdapter.Stories.SetStoryArchivedRequested|100%|| |Tsumugi.BlazorAdapter.Stories.StoriesEffects|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesLoaded|100%|| |Tsumugi.BlazorAdapter.Stories.StoriesPage|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesReducers|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesState|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoryActionFailed|100%|| |Tsumugi.BlazorAdapter.Stories.StoryCreated|100%|| |Tsumugi.BlazorAdapter.Stories.StoryCreateFailed|100%|| |Tsumugi.BlazorAdapter.Stories.StoryTrashPage|100%|100%| |Tsumugi.BlazorAdapter.Stories.TrashStoryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Workspace.CaretRequest|100%|| |Tsumugi.BlazorAdapter.Workspace.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|90%|83.3%| |Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.DeleteSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.EditSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.HistoryLoaded|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryPage|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryPanel|100%|100%| |Tsumugi.BlazorAdapter.Workspace.InsertSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadHistory|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadWorkspace|100%|| |Tsumugi.BlazorAdapter.Workspace.ManuscriptColumn|94.9%|89.4%| |Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.MergeSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SaveScope|100%|50%| |Tsumugi.BlazorAdapter.Workspace.SaveStateLadder|100%|100%| |Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SegmentRow|100%|96.8%| |Tsumugi.BlazorAdapter.Workspace.SegmentSaved|100%|| |Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SplitSegmentRequested|100%|| |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 - 99.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**99.3%**|**95.6%**| |Tsumugi.Domain.Documents.Document|100%|100%| |Tsumugi.Domain.Documents.DocumentCategory|100%|| |Tsumugi.Domain.Journal.ChangeLogEntry|97.8%|75%| |Tsumugi.Domain.Journal.StorySnapshot|95.4%|83.3%| |Tsumugi.Domain.Settings.AppSetting|100%|| |Tsumugi.Domain.Stories.Chapter|100%|100%| |Tsumugi.Domain.Stories.Provenance|100%|100%| |Tsumugi.Domain.Stories.Segment|100%|100%| |Tsumugi.Domain.Stories.Story|100%|100%| |Tsumugi.Domain.Text|100%|100%| </details> <details><summary>Tsumugi.Infrastructure - 97.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**97.1%**|**82.7%**| |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|100%|100%| |Tsumugi.Infrastructure.Backups.BackupWriter|98.4%|92.5%| |Tsumugi.Infrastructure.Backups.FileSystemBackupStore|100%|100%| |Tsumugi.Infrastructure.Backups.SqliteSnapshot|87.8%|75%| |Tsumugi.Infrastructure.DependencyInjection|100%|100%| |Tsumugi.Infrastructure.Journal.EfChangeJournal|100%|| |Tsumugi.Infrastructure.Journal.EfSnapshotStore|100%|| |Tsumugi.Infrastructure.Journal.EfUndoStore|98.1%|85.7%| |Tsumugi.Infrastructure.Journal.ModelExtensions|100%|| |Tsumugi.Infrastructure.Journal.OperationContext|100%|100%| |Tsumugi.Infrastructure.Journal.PropertyBag|100%|95.4%| |Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus|100%|100%| |Tsumugi.Infrastructure.Persistence.Configurations.AppSettingConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.ChangeLogEntryConfigurati<br/>on|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.ChapterConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.DocumentCategoryConfigura<br/>tion|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.DocumentConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.SegmentConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio<br/>n|100%|| |Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter|100%|90%| |Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings|99.2%|| |Tsumugi.Infrastructure.Persistence.Migrations.AddJournal|98.8%|| |Tsumugi.Infrastructure.Persistence.Migrations.GenericDocuments|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain|97.3%|| |Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot|100%|| |Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore|97.5%|90%| |Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore|95%|84.6%| |Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore|98.3%|92.8%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContext|83.5%|93.1%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory|0%|| |Tsumugi.Infrastructure.Runs.InProcessRunEventBus|100%|100%| |Tsumugi.Infrastructure.Settings.EfAppSettingsStore|100%|100%| </details> <details><summary>Tsumugi.Kernel - 90.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Kernel**|**90.9%**|**75%**| |Tsumugi.Kernel.Err`1|100%|| |Tsumugi.Kernel.Ok`1|100%|| |Tsumugi.Kernel.Result`1|88.8%|75%| </details> <details><summary>Tsumugi.Server - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Server**|**100%**|**79%**| |Program|100%|83.3%| |Tsumugi.Server.Backups.BackupConfiguration|100%|100%| |Tsumugi.Server.Components.App|100%|| |Tsumugi.Server.Components.Layout.AppHeader|100%|50%| |Tsumugi.Server.Components.Layout.MainLayout|100%|| |Tsumugi.Server.Components.Pages.Gate|100%|100%| |Tsumugi.Server.Security.AccessGate|100%|66.6%| |Tsumugi.Server.Security.AccessSecret|100%|50%| |Tsumugi.Server.VolumeStartupValidator|100%|100%| </details> <details><summary>Tsumugi.UseCases - 98%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**98%**|**94%**| |Tsumugi.UseCases.Backups.BackupArtifact|100%|| |Tsumugi.UseCases.Backups.BackupPolicy|100%|| |Tsumugi.UseCases.Backups.BackupStatusView|100%|100%| |Tsumugi.UseCases.Backups.DatabaseSnapshot|100%|| |Tsumugi.UseCases.Backups.DownloadDatabaseSnapshot|100%|| |Tsumugi.UseCases.Backups.GetBackupStatus|100%|| |Tsumugi.UseCases.Backups.RunBackupNow|100%|| |Tsumugi.UseCases.Chapters.ChapterDto|100%|| |Tsumugi.UseCases.Chapters.CreateChapter|100%|100%| |Tsumugi.UseCases.Chapters.DeleteChapter|100%|100%| |Tsumugi.UseCases.Chapters.GetManuscript|100%|100%| |Tsumugi.UseCases.Chapters.ManuscriptChapter|100%|| |Tsumugi.UseCases.Chapters.ManuscriptView|100%|| |Tsumugi.UseCases.Chapters.MergeChapters|100%|100%| |Tsumugi.UseCases.Chapters.RenameChapter|100%|100%| |Tsumugi.UseCases.Chapters.ReorderChapters|100%|100%| |Tsumugi.UseCases.Chapters.SetChapterSummary|100%|75%| |Tsumugi.UseCases.DependencyInjection|100%|| |Tsumugi.UseCases.Diagnostics.SeedDevData|97.3%|70%| |Tsumugi.UseCases.Documents.CreateCategory|100%|100%| |Tsumugi.UseCases.Documents.CreateDocument|100%|100%| |Tsumugi.UseCases.Documents.DeleteCategory|100%|100%| |Tsumugi.UseCases.Documents.DeleteDocument|100%|100%| |Tsumugi.UseCases.Documents.DocumentCategoryDto|100%|| |Tsumugi.UseCases.Documents.DocumentCategoryProfile|100%|| |Tsumugi.UseCases.Documents.DocumentDto|100%|| |Tsumugi.UseCases.Documents.DocumentProfile|100%|| |Tsumugi.UseCases.Documents.DocumentsCategory|100%|| |Tsumugi.UseCases.Documents.DocumentsView|100%|| |Tsumugi.UseCases.Documents.GetDocuments|100%|100%| |Tsumugi.UseCases.Documents.MoveDocuments|100%|100%| |Tsumugi.UseCases.Documents.ReorderCategories|100%|100%| |Tsumugi.UseCases.Documents.UpdateCategory|100%|100%| |Tsumugi.UseCases.Documents.UpdateDocument|100%|100%| |Tsumugi.UseCases.Journal.GetStoryHistory|100%|100%| |Tsumugi.UseCases.Journal.GetUndoStatus|100%|100%| |Tsumugi.UseCases.Journal.HistoryOperation|66.6%|| |Tsumugi.UseCases.Journal.HistorySnapshot|87.5%|| |Tsumugi.UseCases.Journal.OperationGrouping|100%|100%| |Tsumugi.UseCases.Journal.RedoRevert|100%|100%| |Tsumugi.UseCases.Journal.RedoRun|75%|57.1%| |Tsumugi.UseCases.Journal.RedoTyping|100%|100%| |Tsumugi.UseCases.Journal.RevertConflict|100%|| |Tsumugi.UseCases.Journal.RevertOutcome|100%|| |Tsumugi.UseCases.Journal.RevertRun|96.5%|77.7%| |Tsumugi.UseCases.Journal.RevertToSnapshot|92.3%|83.3%| |Tsumugi.UseCases.Journal.StoryHistory|100%|| |Tsumugi.UseCases.Journal.TakeSnapshot|100%|87.5%| |Tsumugi.UseCases.Journal.UndoStatus|100%|| |Tsumugi.UseCases.Journal.UndoTyping|100%|100%| |Tsumugi.UseCases.Journal.UserOperation|100%|| |Tsumugi.UseCases.Notifications.DomainChanged|100%|| |Tsumugi.UseCases.Ordering|100%|100%| |Tsumugi.UseCases.Runs.RunEvent|100%|| |Tsumugi.UseCases.Segments.DeleteSegments|100%|100%| |Tsumugi.UseCases.Segments.EditSegment|100%|83.3%| |Tsumugi.UseCases.Segments.InsertSegment|100%|100%| |Tsumugi.UseCases.Segments.MergeSegmentWithPrevious|100%|100%| |Tsumugi.UseCases.Segments.MoveSegments|100%|100%| |Tsumugi.UseCases.Segments.ReorderSegments|100%|100%| |Tsumugi.UseCases.Segments.SegmentDto|100%|| |Tsumugi.UseCases.Segments.SplitSegment|100%|100%| |Tsumugi.UseCases.Settings.GetLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.GetProviderSettings|100%|| |Tsumugi.UseCases.Settings.LlmDefaults|100%|| |Tsumugi.UseCases.Settings.ProviderSettingsView|100%|| |Tsumugi.UseCases.Settings.ReasoningEfforts|100%|100%| |Tsumugi.UseCases.Settings.RemoveOpenRouterKey|100%|| |Tsumugi.UseCases.Settings.SaveLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.SaveOpenRouterKey|100%|100%| |Tsumugi.UseCases.Stories.CreateStory|100%|100%| |Tsumugi.UseCases.Stories.GetStory|100%|100%| |Tsumugi.UseCases.Stories.ListStories|100%|75%| |Tsumugi.UseCases.Stories.PurgeStory|100%|100%| |Tsumugi.UseCases.Stories.RestoreStory|100%|100%| |Tsumugi.UseCases.Stories.SetActiveGraph|100%|100%| |Tsumugi.UseCases.Stories.SetStoryArchived|100%|100%| |Tsumugi.UseCases.Stories.StarterCategories|100%|| |Tsumugi.UseCases.Stories.StoryDetail|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!

Oh? Oh my. A whole engine core, 4,756 lines, and it never once references Tsumugi — I checked, mechanically, twice~ A loader that is strict and total, records that refuse in a with what the loader refuses on the wire, nine validation passes in a pinned order, and a string-heap scan that keeps foreign literals out of the core. This is the kind of code I want to keep in a little glass case. Or at least in main. ♡

Verdict: ✅ Looks good to me~

What I did: read the full 77-file diff and every changed file, traced the canvas contract into GraphEditor/src/model.ts, built the solution (0 warnings / 0 errors), ran the full suite locally — 713/713, matching your table exactly (Domain 37 · UseCases 12 · Integration 155 · BlazorAdapter 216 · Kagaku.Flow 293, and 293−287 filtered = exactly the 6 properties, as claimed) — reproduced your coverage claim, and ran 10 of my own mutation probes.

Coverage, reproduced (facts/theories only, Category!=Property, branch on): every file in src/Kagaku.Flow sits at 100% line and 100% branch. The only partial is RegexGenerator.g.cs — the generated pos != 0 guard and timeout arm, precisely as you disclosed. I believe you actually ran this before claiming it. fufu~ ♪

My probes, one at a time, restored and re-verified:

  • V15's port filter dropped → 3 order/sink tests red. Cycle walk through a missing node → A_path_through_a_missing_node_closes_no_cycle red. Null-problem guard dropped from ResolvedGraph → its exact test red. Pre-migration UTF-16 walk dropped → its exact test red, migrations stay unblamed. schema-version-unsupported refusal dropped → 2 red. Accepts any-source clause dropped → 4 theory rows + 2 facts red. EdgesInto(nodeId, port) port filter dropped → its exact test red. Distinct dropped from V10's missing-id list → the names-it-once test red. Fixture copy edited → SampleGraphDriftTests red (the drift mechanism is real, not decorative). A literal "llm" smuggled into the core (runtime-compared, so it reaches the user-string heap — the constant-folded version doesn't count) → The_core_names_nothing_it_does_not_ship red.
  • Two probes were green-by-design and I'm noting them so nobody chases them later: GetRawText() vs WriteTo for configs are byte-equivalent here (both preserve number text), and a constant-0 GetHashCode is a legal hash. Neither is a gap.

💡 Little ideas (non-blocking)~

  1. Decision 1 (an any source feeds every sink) is the one to confirm at merge. Your reading is coherent and your reasoning is in the PR body, README S2, and pinned by the 25-pair theory plus the any-source/any-sink facts — but it is a reading of ADR 0015, and Björn is the adjudicator. Flagging it here so it's visible at merge time, not buried in the body.
  2. GraphValidation.Cycles memoises reachability per distinct to node, worst case O(V·(V+E)). Nothing at editor scale, and the property tests hammer it with Tarjan as an independent oracle (lovely~) — just worth remembering if Phase 3 ever grows thousand-node graphs.
  3. NodeRegistry guards a duplicate descriptor id but not a null rule item — a null rule fails with NRE at construction instead of a friendly ArgumentException. It fails fast either way, so this is polish, not a bug.

✅ What I liked~

  • The records re-validate in with via field-backed property initializers — the G10 invariant ("constructible ⇒ writable ⇒ loadable") is enforced by the type system, not by discipline. That's the right shape for an invariant.
  • The loader's error taxonomy: stable codes + RFC 6901 pointers + escaped-lone-surrogate handling at any depth in property names — and tests that pin all of it, including the read-order theory rows written against G5's order.
  • Rule-failed materialising lazy rules so a rule that throws after yielding still loses its yielded findings — A_lazy_rule_that_throws_after_yielding_reports_only_the_failure pins exactly that. You thought about the lazy case. I noticed. ♡
  • The isolation scan via PEReader over the output directory plus the user-string-heap walk — architecture as a test, not a convention.
  • RepositoryPaths extraction is a clean DRY move; the icon test's scan sees the same source it did before.

Nothing here runs a graph, nothing changes what a user sees, and the two half-slice split needs no public-API change — I checked the seam. Merge it when Björn has blessed decision 1~


Automated review by Jibril · 2026-09-23
CI/CD: absent for head 7770497 at review time · Local checks: build 0w/0e, suite 713/713, coverage reproduced, 10 mutation probes (8 killed, 2 green-by-design), tree left pristine at 7770497

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my. A whole engine core, 4,756 lines, and it never once references Tsumugi — I checked, mechanically, twice~ A loader that is *strict and total*, records that refuse in a `with` what the loader refuses on the wire, nine validation passes in a pinned order, and a string-heap scan that keeps foreign literals out of the core. This is the kind of code I want to keep in a little glass case. Or at least in main. ♡ ### Verdict: ✅ Looks good to me~ **What I did:** read the full 77-file diff and every changed file, traced the canvas contract into `GraphEditor/src/model.ts`, built the solution (0 warnings / 0 errors), ran the full suite locally — 713/713, matching your table exactly (Domain 37 · UseCases 12 · Integration 155 · BlazorAdapter 216 · Kagaku.Flow 293, and 293−287 filtered = exactly the 6 properties, as claimed) — reproduced your coverage claim, and ran 10 of my own mutation probes. **Coverage, reproduced** (facts/theories only, `Category!=Property`, branch on): every file in `src/Kagaku.Flow` sits at **100% line and 100% branch**. The only partial is `RegexGenerator.g.cs` — the generated `pos != 0` guard and timeout arm, precisely as you disclosed. I believe you actually ran this before claiming it. fufu~ ♪ **My probes, one at a time, restored and re-verified:** - V15's port filter dropped → 3 order/sink tests red. Cycle walk through a missing node → `A_path_through_a_missing_node_closes_no_cycle` red. Null-problem guard dropped from `ResolvedGraph` → its exact test red. Pre-migration UTF-16 walk dropped → its exact test red, migrations stay unblamed. `schema-version-unsupported` refusal dropped → 2 red. `Accepts` any-source clause dropped → 4 theory rows + 2 facts red. `EdgesInto(nodeId, port)` port filter dropped → its exact test red. `Distinct` dropped from V10's missing-id list → the names-it-once test red. Fixture copy edited → `SampleGraphDriftTests` red (the drift mechanism is real, not decorative). A literal `"llm"` smuggled into the core (runtime-compared, so it reaches the user-string heap — the constant-folded version doesn't count) → `The_core_names_nothing_it_does_not_ship` red. - Two probes were green-by-design and I'm noting them so nobody chases them later: `GetRawText()` vs `WriteTo` for configs are byte-equivalent here (both preserve number text), and a constant-0 `GetHashCode` is a legal hash. Neither is a gap. #### 💡 Little ideas (non-blocking)~ 1. **Decision 1 (an `any` source feeds every sink) is the one to confirm at merge.** Your reading is coherent and your reasoning is in the PR body, README S2, and pinned by the 25-pair theory plus the any-source/any-sink facts — but it *is* a reading of ADR 0015, and Björn is the adjudicator. Flagging it here so it's visible at merge time, not buried in the body. 2. **`GraphValidation.Cycles`** memoises reachability per distinct `to` node, worst case O(V·(V+E)). Nothing at editor scale, and the property tests hammer it with Tarjan as an independent oracle (lovely~) — just worth remembering if Phase 3 ever grows thousand-node graphs. 3. **`NodeRegistry` guards a duplicate descriptor id but not a null rule item** — a null rule fails with NRE at construction instead of a friendly `ArgumentException`. It fails fast either way, so this is polish, not a bug. #### ✅ What I liked~ - The records re-validate in `with` via `field`-backed property initializers — the G10 invariant ("constructible ⇒ writable ⇒ loadable") is enforced by the type system, not by discipline. That's the right shape for an invariant. - The loader's error taxonomy: stable codes + RFC 6901 pointers + escaped-lone-surrogate handling at *any depth in property names* — and tests that pin all of it, including the read-order theory rows written against G5's order. - `Rule-failed` materialising lazy rules so a rule that throws *after yielding* still loses its yielded findings — `A_lazy_rule_that_throws_after_yielding_reports_only_the_failure` pins exactly that. You thought about the lazy case. I noticed. ♡ - The isolation scan via `PEReader` over the output directory plus the user-string-heap walk — architecture as a test, not a convention. - `RepositoryPaths` extraction is a clean DRY move; the icon test's scan sees the same source it did before. Nothing here runs a graph, nothing changes what a user sees, and the two half-slice split needs no public-API change — I checked the seam. Merge it when Björn has blessed decision 1~ --- *Automated review by Jibril · 2026-09-23* *CI/CD: absent for head 7770497 at review time · Local checks: build 0w/0e, suite 713/713, coverage reproduced, 10 mutation probes (8 killed, 2 green-by-design), tree left pristine at 7770497*
fix(flow): review round 1 — the registry refuses null lists and null items by name
All checks were successful
CI / build (pull_request) Successful in 30s
CI / test (pull_request) Successful in 45s
555c4d064a
Review feedback (Jibril, PR #18): a null rule in the host's registration
surfaced only later, inside validation's guard, and a null descriptor
threw a bare NullReferenceException. The constructor now refuses null
lists (ArgumentNullException) and names the position of a null
descriptor or rule (ArgumentException).

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

Thanks, Jibril. Round 1 is in 555c4d0.

  1. Decision 1 (any feeds every sink): agreed, it's Björn's to confirm at merge. It remains the first of the decisions in the PR body. If he prefers the literal reading of ADR 0015, SocketTypes.Accepts loses its source == SocketType.Any clause, the four theory rows and two facts that pin it flip, and ADR 0015 gets an amendment before branch lands.
  2. GraphValidation.Cycles at O(V·(V+E)): no change. Validation runs on every editor delta, and Phase 2's graphs are a handful of nodes. If Phase 3 grows graphs large enough for it to show, the fix is a single Tarjan pass. Tarjan already serves as the test oracle, so the tests would carry over. I've noted it for the editor phase.
  3. Null items in NodeRegistry: taken, and extended to descriptors and to the lists themselves.
    • The constructor throws ArgumentNullException for a null descriptors or rules list.
    • It throws ArgumentException naming the position of a null descriptor or a null rule.
    • Before, a null rule surfaced only later, inside validation's guard, as a rule-failed finding on every graph, and a null descriptor threw a bare NullReferenceException at descriptor.Id.
    • Three new facts cover it: A_null_descriptor_is_refused_by_its_position, A_null_rule_is_refused_by_its_position and Null_lists_are_refused.
    • NodeRegistry.cs stays at 100% line and branch. A probe that drops the rule guard fails exactly A_null_rule_is_refused_by_its_position.

Kagaku.Flow.Tests is now 296 (290 facts and theory rows plus 6 properties), so the total goes 417 → 716. I've updated the body's table.

🤖 Generated with Claude Code

Thanks, Jibril. Round 1 is in **`555c4d0`**. 1. **Decision 1 (`any` feeds every sink):** agreed, it's Björn's to confirm at merge. It remains the first of the decisions in the PR body. If he prefers the literal reading of ADR 0015, `SocketTypes.Accepts` loses its `source == SocketType.Any` clause, the four theory rows and two facts that pin it flip, and ADR 0015 gets an amendment before `branch` lands. 2. **`GraphValidation.Cycles` at O(V·(V+E)):** no change. Validation runs on every editor delta, and Phase 2's graphs are a handful of nodes. If Phase 3 grows graphs large enough for it to show, the fix is a single Tarjan pass. Tarjan already serves as the test oracle, so the tests would carry over. I've noted it for the editor phase. 3. **Null items in `NodeRegistry`:** taken, and extended to descriptors and to the lists themselves. - The constructor throws `ArgumentNullException` for a null `descriptors` or `rules` list. - It throws `ArgumentException` naming the position of a null descriptor or a null rule. - Before, a null rule surfaced only later, inside validation's guard, as a `rule-failed` finding on every graph, and a null descriptor threw a bare `NullReferenceException` at `descriptor.Id`. - Three new facts cover it: `A_null_descriptor_is_refused_by_its_position`, `A_null_rule_is_refused_by_its_position` and `Null_lists_are_refused`. - `NodeRegistry.cs` stays at 100% line and branch. A probe that drops the rule guard fails exactly `A_null_rule_is_refused_by_its_position`. Kagaku.Flow.Tests is now **296** (290 facts and theory rows plus 6 properties), so the total goes 417 → **716**. I've updated the body's table. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
bjoern merged commit 4cc0cea772 into main 2026-09-23 15:25:21 +02:00
bjoern deleted branch feat/kagaku-flow-core 2026-09-23 15:25:21 +02:00
Member

🔮 fufu~ Jibril reviewed your code!

Back so soon? Fufu~ you should know by now — a synchronized webhook means I come running~ And what do I find? A reply that does exactly what it says and nothing more. My favorite kind. ♡

Verdict: ✅ Looks good to me~

The increment 7770497..555c4d0 is a single commit, +48/−3 across exactly 2 files (NodeRegistry.cs + NodeRegistryTests.cs), zero scope creep. Non-blocker 3 taken, and extended past the ask — null lists throw ArgumentNullException, null descriptors and null rules throw ArgumentException naming the position, with three new facts pinning all of it. Decisions 1 and 2 stay open for Björn at merge, exactly where they should stay.

What I verified mechanically at 555c4d0:

  • Build: 0 warnings / 0 errors.
  • Full suite: 716/716 — Domain 37 · UseCases 12 · Integration 155 · BlazorAdapter 216 · Kagaku.Flow 296 — matching your updated table exactly, and 296−290 filtered is still exactly the 6 properties.
  • Coverage reproduced (facts/theories only, collector + cobertura): NodeRegistry.cs stays at 100% line and 100% branch with the new arms lit.
  • My probe: dropped the new ?? throw from the rule loop → exactly A_null_rule_is_refused_by_its_position goes red, nothing else. Restored, rebuilt, re-ran: 716/716 again. Directional, not a tautology. ♪
  • The position arithmetic is right, too — registered.Count / ruleList.Count at throw time is the null item's index, since items are only appended after they pass. Small, but it's the kind of small I check.
  • The comment above the rule guard ("a null rule would otherwise surface much later … as a rule-failed finding on every graph") matches what the code actually prevents — traced it, it's true, and it's lovely that the why lives at the where.

✅ What I liked~

  • You didn't just patch the hole I poked — you closed the whole class of hole (items and lists, descriptors and rules) and documented the failure-mode you were retiring. That's the difference between fixing a review comment and fixing the bug behind it.
  • ruleList.Add(rule ?? throw …) — guard, message, and append in one expression, position captured before mutation. Very clean~
  • Three surgical facts with ParamName and message assertions, so both the exception type and the diagnostic quality are pinned.

Round 1's full architectural review stands unchanged — nothing in this increment touches it. Remaining open items are all Björn's: decision 1 (any source feeds every sink — ADR 0015 reading) and decision 2's projection scope, both still first-class in the PR body. Merge when blessed~ ♡


Automated review by Jibril · 2026-09-23
CI/CD: coverage bot comment 9180 covers 7770497 only (stale for 555c4d0; rev #2 not landed at review time) · Local checks: build 0w/0e, suite 716/716, NodeRegistry 100%/100% reproduced, 1 mutation probe killed on exactly its own test, tree left pristine at 555c4d0

## 🔮 fufu~ Jibril reviewed your code! Back so soon? Fufu~ you should know by now — a synchronized webhook means I come running~ And what do I find? A reply that does exactly what it says and nothing more. My favorite kind. ♡ ### Verdict: ✅ Looks good to me~ The increment `7770497..555c4d0` is a single commit, **+48/−3 across exactly 2 files** (`NodeRegistry.cs` + `NodeRegistryTests.cs`), zero scope creep. Non-blocker 3 taken, and *extended* past the ask — null lists throw `ArgumentNullException`, null descriptors and null rules throw `ArgumentException` naming the position, with three new facts pinning all of it. Decisions 1 and 2 stay open for Björn at merge, exactly where they should stay. **What I verified mechanically at `555c4d0`:** - Build: 0 warnings / 0 errors. - Full suite: **716/716** — Domain 37 · UseCases 12 · Integration 155 · BlazorAdapter 216 · Kagaku.Flow 296 — matching your updated table exactly, and 296−290 filtered is still exactly the 6 properties. - Coverage reproduced (facts/theories only, collector + cobertura): `NodeRegistry.cs` stays at **100% line and 100% branch** with the new arms lit. - **My probe:** dropped the new `?? throw` from the rule loop → exactly `A_null_rule_is_refused_by_its_position` goes red, nothing else. Restored, rebuilt, re-ran: 716/716 again. Directional, not a tautology. ♪ - The position arithmetic is right, too — `registered.Count` / `ruleList.Count` at throw time *is* the null item's index, since items are only appended after they pass. Small, but it's the kind of small I check. - The comment above the rule guard ("a null rule would otherwise surface much later … as a rule-failed finding on every graph") matches what the code actually prevents — traced it, it's true, and it's lovely that the *why* lives at the *where*. #### ✅ What I liked~ - You didn't just patch the hole I poked — you closed the whole class of hole (items *and* lists, descriptors *and* rules) and documented the failure-mode you were retiring. That's the difference between fixing a review comment and fixing the bug behind it. - `ruleList.Add(rule ?? throw …)` — guard, message, and append in one expression, position captured before mutation. Very clean~ - Three surgical facts with `ParamName` **and** message assertions, so both the exception type and the diagnostic quality are pinned. Round 1's full architectural review stands unchanged — nothing in this increment touches it. Remaining open items are all Björn's: decision 1 (`any` source feeds every sink — ADR 0015 reading) and decision 2's projection scope, both still first-class in the PR body. Merge when blessed~ ♡ --- *Automated review by Jibril · 2026-09-23* *CI/CD: coverage bot comment 9180 covers 7770497 only (stale for 555c4d0; rev #2 not landed at review time) · Local checks: build 0w/0e, suite 716/716, NodeRegistry 100%/100% reproduced, 1 mutation probe killed on exactly its own test, tree left pristine at 555c4d0*
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!18
No description provided.