feat: Kagaku.Flow — the scheduler runs a run (Phase 2 · slice 6c) #29

Merged
bjoern merged 1 commit from feat/kagaku-flow-engine into main 2026-09-25 21:44:48 +02:00
Member

This is slice 6 of Phase 2, part c of four. It builds on 6a (#23) and 6b (#28), both merged. FlowEngine now runs a run end to end:

  • it starts the run behind a one-live-run gate;
  • it runs passes that compute the ready-set;
  • it gives out slots within a budget;
  • it runs attempts with their own scopes;
  • it renders templates, checks each outcome, and publishes events.

6d (cancel, retry, recovery and the loop) follows. Nothing in src/Tsumugi.* changes; the only other file touched is the plan's slice-6 line.

What

Kagaku.Flow.Scheduling

  • FlowEngine.StartRunAsync(RunRequest) returns a RunStartResult, which is either the run or a RunStartRefusal.
    • The refusals are OwnerHasLiveRun, SnapshotDoesNotLoad, SnapshotInvalid and SnapshotEmpty.
    • A refused start writes nothing (P10).
  • Every pass runs under the same gate as starts (P2). A pass:
    • loads each live run's view, caching it and evicting the views of runs that are no longer live;
    • applies the ready-set's exclusions;
    • dispatches the startable rows.
  • Slots (P5–P7): a node type that ConsumesConcurrencySlot needs a free slot. Plain rows never take one.
    • The limit comes from the host's IConcurrencyLimitSource, read in each pass's scope; the default is 3.
    • A limit of 0 or less holds every slot row.
  • Attempts (E3–E9, P8, P9):
    • Each attempt gets its own execution scope, plus a separate scope for the engine's ledger writes.
    • Templates are rendered before the executor runs.
    • An outcome is checked before it settles. It must account for every declared source exactly once, and a typed source's value must fit its socket (E6.4, from 6b).
    • Settles are written with a token that cannot be cancelled.
    • The delta gate closes when the executor returns, before the settle is announced.
  • The failure policy (E5):
    • A template error is permanent, unless its exception is a TransientFailureException.
    • Any other exception inside an attempt is an attempt failure.
    • A logger that throws changes nothing (E7).
  • A snapshot that no longer runs when a run resumes gives up one row (P4). Only a load failure, a trigger that no longer parses, or a core finding other than invalid-config/rule-failed does this; see decision 3.
  • Events (N1–N7):
    • RunStarted, NodeStarted, NodeDelta, NodeSettled (which covers exclusions and give-ups) and RunSettled. RunSettled fires once per transition to a terminal status.
    • A sink that throws is logged, and no row changes.

Kagaku.Flow.Extensions: AddKagakuFlow() builds the registry and the engine from the host's descriptors, rules, executors, ledger, sink, logger, clock and options.

Kagaku.Flow.Testing:

  • FlowEngineDriver gives hosts TickAsync and RunUntilQuiescentAsync(maxPasses) to drive the engine in tests. RunUntilQuiescentAsync drains before it throws once the pass budget is spent.
  • RecordingRunEventSink records the events it receives.

Why

  • ADR 0016: the engine only schedules. The ledger is the truth; there is one live run per owner, a slot limit on model-calling types, attempts and their budgets, and fresh scopes for executors.
  • ADR 0015: templates are rendered before an executor runs.
  • ADR 0023: a resumed snapshot keeps running unless it can no longer run.
  • ADR 0025: the engine is a library, and hosts drive it through the testing package.

Decisions to confirm

  1. A start is refused, not failed (P10). This covers a snapshot that does not load, does not validate, or has no nodes. Nothing is written.
  2. The core stores no rendered text of its own (E4). The executor records what it sent, captured from the wire (D-33). Amendment to ADR 0015: "… so what the executor sent, captured from the wire, is what the ledger stores."
  3. A resumed snapshot that no longer runs gives up one row, now in 6c. This moved here from 6d, because without it a run whose view fails would block its owner forever. A run that has already given up writes nothing more.
    • Amendment to ADR 0016, shared with decision 1: "a snapshot that does not validate is refused at start; one that no longer runs when a run resumes fails one row."

The rest fill gaps and need no amendment:

  • E5: the render-failure policy above.
  • Facts: failure arms carry the facts the attempt paid for, and a permanent failure's facts are pinned too.
  • Events (N1–N7):
    • Node events carry "" as their path at the top level. Run events carry no node and no path.
    • RunStarted on revival, and NodeSettled for orphan resets, requeues and cancelled rows, arrive with 6d.
  • The gate: the one-live-run gate is the engine's, and it also serializes passes.
  • The limit: the host supplies it; the default is 3, a negative limit counts as 0, and a source that throws aborts the pass.
  • Hosting: there is no BackgroundService in the core; 11a hosts the engine.
  • Events sink: an IRunEventSink is required, with no silent default.
  • Scopes: each attempt's ledger scope is separate from its execution scope.
  • Run ids: RunRequest.RunId: an id that is already used makes the ledger throw, which is a host bug, not a refusal.
  • Logger: a logger that throws changes nothing.
  • Views: a pass evicts the views of runs that are no longer live.

Departures from the brief

  1. The failed view moved from 6d into 6c (decision 3). Its tests are in FlowEngineStartTests; the recovery tests that combine it with orphans and retry stay in 6d.
  2. No Nudge() yet. With no loop there is nothing to wake, so 6d adds it.
  3. E5's arm for a cancellation the engine asked for is not here yet. Nothing cancels an attempt's token until 6d's cancel and dispose. Until then, every OperationCanceledException is an attempt failure.
  4. Passes_never_overlap lands in 6c. It needs only TickAsync, and the gate (P2) is 6c's.
  5. The run-started Information log is in, because P10 names it. The rest of the log narration is 6d's. Host exceptions and template errors log as Warnings. Ledger failures at the attempt's edge and scope-disposal failures log as Errors.
  6. The DI rule is K1 in the README, because X1–X5 are already the template sandbox rules.
  7. Test renames:
    • The two limit facts merged into A_limit_of_zero_or_less_holds_every_slot_row_and_lets_plain_rows_run.
    • …the_options_default_applies, …_executors_rules_and_extensions and …_and_the_rest_keep_declared_order reword the brief's names.
    • …ledger_sink_logger_clock_and_options names what the test now checks.
  8. About 40 facts beyond §10's list, covering coverage arms and traps. One of them is the fact 6b's review asked for: A_budgeted_consumers_merge_sink_whose_first_arm_is_excluded_in_the_same_pass_is_known_by_its_later_arm.

Known, out of scope: ValidationCodes.cs has carried a tsumugi.context-without-consumer example in a doc comment since 5a. It is a word, not a reference, so the isolation test passes, but it should be reworded some day.

Out of scope

  • Cancel and its drained RunSettled, retry, the orphan sweep and recovery, the loop, dispose and the log narration. These are 6d.
  • Containers, human input and concrete node types.

Tests

Project main (5db7933) this PR
Kagaku.Flow.Tests 823 1000 (983 facts and theory rows + 17 properties)
Kagaku.Flow.OpenRouter.Tests 71 71
Tsumugi.Domain.Tests 328 328
Tsumugi.UseCases.Tests 54 54
Tsumugi.Integration.Tests 305 305
Tsumugi.BlazorAdapter.Tests 324 324
Total 1905 2082

The Release build has 0 warnings under TreatWarningsAsErrors. The property category passed 3 extra runs with random seeds.

  • New test files: FlowEngineStartTests, …BudgetTests, …AttemptTests, …RenderTests, …ExclusionTests, …EventTests, AddKagakuFlowTests and FlowEngineDriverTests, plus the EngineWorld harness.
  • Hang guards: every await on the engine in the tests has one, so a bug that leaks an in-flight execution fails in 20 s instead of hanging the run.
  • Flake loop: the 177 engine tests ran 32 times with no failure: 20 in a row, 6 with DOTNET_PROCESSOR_COUNT=1, and 6 as concurrent processes. No test waits on real time, apart from the hang guards and one bounded wait in a test sink, both of which the brief allows.

Coverage. Measured with coverlet per changed file, lines and branches (branch="True"), over facts and theories only. Every new or changed file is at 100%:

File Lines Branches
Scheduling/FlowEngine.cs 178/178 56/56
Scheduling/FlowEngine.Attempts.cs 145/145 50/50
Scheduling/FlowEngineOptions.cs 11/11 4/4
Scheduling/RunRequest.cs 13/13 4/4
Scheduling/RunStartResult.cs 19/19 –
Extensions/ServiceCollectionExtensions.cs 11/11 –
Runs/RunStatuses.cs 12/12 20/20
Testing/FlowEngineDriver.cs 11/11 4/4
Testing/RecordingRunEventSink.cs 9/9 –

IConcurrencyLimitSource and RunStartRefusal have no executable lines.

Mutation probes. Each was run on a copy, then restored and touched, with every test run under a time limit.

§12's [C] probes, 54 mutants, all killed: 28a/b, 29, 30, 31a–f, 32a/b, 33, 34a/b, 35, 36a–c, 37a–c, 38a/b, 39a/b, 40a/b, 41a–c, 42a–d, 43a/b, 44a/b, 50–55, 56a–c, 58a–c, 64a/b and 65. Three notes:

  • 43's third mutant, "refuse a settled owner run too", can't be written. IRunLedger lists only live runs.
  • 64a differs only in which row it gives up. It is killed on the status of row n3.
  • Four kills are not a plain assertion in the named test:
    • 41c's named test dies of a NullReferenceException, and four other failed-view facts kill it on assertions;
    • 42d is killed by the hang guard;
    • 51 is killed by the sink's exception escaping StartRunAsync, which is the intended failure;
    • 39c is killed in 11 of 11 runs, but it is probabilistic by nature, because no seam can make it deterministic.

The verifier's 101 mutants:

  • Every [C] mutant was killed, plus 34's "render after executing" and 44's row-id order.
  • Nine survived the first round: 33b, 35c, 36d, the permanent outcome dropping its facts, 58d, 58e, 31h, 31i, and view eviction.
  • Each now has a test that kills it: the settle-token fact, the delta-gate fact, the plain-row-before-slot-row fact, the permanent-facts fact, the AddKagakuFlow clock-and-options test, two E6-order theory rows, and the eviction fact.
  • The logger theory kills the six log-site mutants on assertions. The edge-log and scope-disposal ones are killed by the hang guard.

Verification round (before opening)

An independent verifier found no blockers. It raised 12 findings, 6 should-fix and 6 nits, and all 12 are applied in the one commit:

  • A logger that threw while a scope-disposal failure was being logged leaked the in-flight entry. This was the one real behaviour bug. Every log call now goes through a helper that drops the logger's own exception, and a theory covers each log site.
  • Rules the suite did not pin are now pinned:
    • the delta gate closes before the settle is announced;
    • a plain row leaves the free slot to a slot row;
    • a permanent failure keeps its facts;
    • all four settle sites use the uncancellable token;
    • the engine factory uses the host's clock and options;
    • two E6 order rows;
    • a pass evicts the views of runs that are no longer live.
  • Hang guards are on every engine await in the tests.
  • A give-up is written only once. It is skipped for a run that has already given up, and a new fact covers a give-up that the ledger refuses.
  • The plan line says #29.

🤖 Generated with Claude Code

This is slice 6 of Phase 2, part **c** of four. It builds on 6a (#23) and 6b (#28), both merged. **`FlowEngine` now runs a run end to end:** - it starts the run behind a one-live-run gate; - it runs passes that compute the ready-set; - it gives out slots within a budget; - it runs attempts with their own scopes; - it renders templates, checks each outcome, and publishes events. 6d (cancel, retry, recovery and the loop) follows. **Nothing in `src/Tsumugi.*` changes;** the only other file touched is the plan's slice-6 line. ## What **`Kagaku.Flow.Scheduling`** - **`FlowEngine.StartRunAsync(RunRequest)`** returns a `RunStartResult`, which is either the run or a `RunStartRefusal`. - The refusals are `OwnerHasLiveRun`, `SnapshotDoesNotLoad`, `SnapshotInvalid` and `SnapshotEmpty`. - A refused start writes nothing (P10). - **Every pass runs under the same gate as starts (P2).** A pass: - loads each live run's view, caching it and evicting the views of runs that are no longer live; - applies the ready-set's exclusions; - dispatches the startable rows. - **Slots (P5–P7):** a node type that `ConsumesConcurrencySlot` needs a free slot. Plain rows never take one. - The limit comes from the host's `IConcurrencyLimitSource`, read in each pass's scope; the default is 3. - A limit of 0 or less holds every slot row. - **Attempts (E3–E9, P8, P9):** - Each attempt gets its own execution scope, plus a separate scope for the engine's ledger writes. - Templates are rendered **before** the executor runs. - An outcome is checked before it settles. It must account for every declared source exactly once, and a typed source's value must fit its socket (E6.4, from 6b). - Settles are written with a token that cannot be cancelled. - The delta gate closes when the executor returns, before the settle is announced. - **The failure policy (E5):** - A template error is permanent, unless its exception is a `TransientFailureException`. - Any other exception inside an attempt is an attempt failure. - A logger that throws changes nothing (E7). - **A snapshot that no longer runs when a run resumes gives up one row (P4).** Only a load failure, a trigger that no longer parses, or a core finding other than `invalid-config`/`rule-failed` does this; see decision 3. - **Events (N1–N7):** - `RunStarted`, `NodeStarted`, `NodeDelta`, `NodeSettled` (which covers exclusions and give-ups) and `RunSettled`. `RunSettled` fires once per transition to a terminal status. - A sink that throws is logged, and no row changes. **`Kagaku.Flow.Extensions`:** `AddKagakuFlow()` builds the registry and the engine from the host's descriptors, rules, executors, ledger, sink, logger, clock and options. **`Kagaku.Flow.Testing`:** - **`FlowEngineDriver`** gives hosts `TickAsync` and `RunUntilQuiescentAsync(maxPasses)` to drive the engine in tests. `RunUntilQuiescentAsync` drains before it throws once the pass budget is spent. - **`RecordingRunEventSink`** records the events it receives. ## Why - **ADR 0016:** the engine only schedules. The ledger is the truth; there is one live run per owner, a slot limit on model-calling types, attempts and their budgets, and fresh scopes for executors. - **ADR 0015:** templates are rendered before an executor runs. - **ADR 0023:** a resumed snapshot keeps running unless it can no longer run. - **ADR 0025:** the engine is a library, and hosts drive it through the testing package. ## Decisions to confirm 1. **A start is refused, not failed (P10).** This covers a snapshot that does not load, does not validate, or has no nodes. Nothing is written. 2. **The core stores no rendered text of its own (E4).** The executor records what it sent, captured from the wire (D-33). *Amendment to ADR 0015:* "… so what the executor sent, captured from the wire, is what the ledger stores." 3. **A resumed snapshot that no longer runs gives up one row, now in 6c.** This moved here from 6d, because without it a run whose view fails would block its owner forever. A run that has already given up writes nothing more. - *Amendment to ADR 0016*, shared with decision 1: "a snapshot that does not validate is refused at start; one that no longer runs when a run resumes fails one row." *The rest fill gaps and need no amendment:* - **E5:** the render-failure policy above. - **Facts:** failure arms carry the facts the attempt paid for, and a permanent failure's facts are pinned too. - **Events (N1–N7):** - Node events carry `""` as their path at the top level. Run events carry no node and no path. - `RunStarted` on revival, and `NodeSettled` for orphan resets, requeues and cancelled rows, arrive with 6d. - **The gate:** the one-live-run gate is the engine's, and it also serializes passes. - **The limit:** the host supplies it; the default is 3, a negative limit counts as 0, and a source that throws aborts the pass. - **Hosting:** there is no `BackgroundService` in the core; 11a hosts the engine. - **Events sink:** an `IRunEventSink` is required, with no silent default. - **Scopes:** each attempt's ledger scope is separate from its execution scope. - **Run ids:** `RunRequest.RunId`: an id that is already used makes the ledger throw, which is a host bug, not a refusal. - **Logger:** a logger that throws changes nothing. - **Views:** a pass evicts the views of runs that are no longer live. ## Departures from the brief 1. **The failed view moved from 6d into 6c** (decision 3). Its tests are in `FlowEngineStartTests`; the recovery tests that combine it with orphans and retry stay in 6d. 2. **No `Nudge()` yet.** With no loop there is nothing to wake, so 6d adds it. 3. **E5's arm for a cancellation the engine asked for is not here yet.** Nothing cancels an attempt's token until 6d's cancel and dispose. Until then, every `OperationCanceledException` is an attempt failure. 4. **`Passes_never_overlap` lands in 6c.** It needs only `TickAsync`, and the gate (P2) is 6c's. 5. **The run-started Information log is in, because P10 names it.** The rest of the log narration is 6d's. Host exceptions and template errors log as Warnings. Ledger failures at the attempt's edge and scope-disposal failures log as Errors. 6. **The DI rule is K1 in the README,** because X1–X5 are already the template sandbox rules. 7. **Test renames:** - The two limit facts merged into `A_limit_of_zero_or_less_holds_every_slot_row_and_lets_plain_rows_run`. - `…the_options_default_applies`, `…_executors_rules_and_extensions` and `…_and_the_rest_keep_declared_order` reword the brief's names. - `…ledger_sink_logger_clock_and_options` names what the test now checks. 8. **About 40 facts beyond §10's list,** covering coverage arms and traps. One of them is the fact 6b's review asked for: `A_budgeted_consumers_merge_sink_whose_first_arm_is_excluded_in_the_same_pass_is_known_by_its_later_arm`. **Known, out of scope:** `ValidationCodes.cs` has carried a `tsumugi.context-without-consumer` example in a doc comment since 5a. It is a word, not a reference, so the isolation test passes, but it should be reworded some day. ## Out of scope - Cancel and its drained `RunSettled`, retry, the orphan sweep and recovery, the loop, dispose and the log narration. These are 6d. - Containers, human input and concrete node types. ## Tests | Project | main (`5db7933`) | this PR | |---|---|---| | Kagaku.Flow.Tests | 823 | **1000** (983 facts and theory rows + 17 properties) | | Kagaku.Flow.OpenRouter.Tests | 71 | 71 | | Tsumugi.Domain.Tests | 328 | 328 | | Tsumugi.UseCases.Tests | 54 | 54 | | Tsumugi.Integration.Tests | 305 | 305 | | Tsumugi.BlazorAdapter.Tests | 324 | 324 | | **Total** | **1905** | **2082** | The Release build has 0 warnings under `TreatWarningsAsErrors`. The property category passed 3 extra runs with random seeds. - **New test files:** `FlowEngineStartTests`, `…BudgetTests`, `…AttemptTests`, `…RenderTests`, `…ExclusionTests`, `…EventTests`, `AddKagakuFlowTests` and `FlowEngineDriverTests`, plus the `EngineWorld` harness. - **Hang guards:** every await on the engine in the tests has one, so a bug that leaks an in-flight execution fails in 20 s instead of hanging the run. - **Flake loop:** the 177 engine tests ran **32 times** with no failure: 20 in a row, 6 with `DOTNET_PROCESSOR_COUNT=1`, and 6 as concurrent processes. No test waits on real time, apart from the hang guards and one bounded wait in a test sink, both of which the brief allows. **Coverage.** Measured with coverlet per changed file, lines and branches (`branch="True"`), over facts and theories only. **Every new or changed file is at 100%:** | File | Lines | Branches | |---|---|---| | `Scheduling/FlowEngine.cs` | 178/178 | 56/56 | | `Scheduling/FlowEngine.Attempts.cs` | 145/145 | 50/50 | | `Scheduling/FlowEngineOptions.cs` | 11/11 | 4/4 | | `Scheduling/RunRequest.cs` | 13/13 | 4/4 | | `Scheduling/RunStartResult.cs` | 19/19 | – | | `Extensions/ServiceCollectionExtensions.cs` | 11/11 | – | | `Runs/RunStatuses.cs` | 12/12 | 20/20 | | `Testing/FlowEngineDriver.cs` | 11/11 | 4/4 | | `Testing/RecordingRunEventSink.cs` | 9/9 | – | `IConcurrencyLimitSource` and `RunStartRefusal` have no executable lines. **Mutation probes.** Each was run on a copy, then restored and `touch`ed, with every test run under a time limit. **§12's [C] probes, 54 mutants, all killed:** 28a/b, 29, 30, 31a–f, 32a/b, 33, 34a/b, 35, 36a–c, 37a–c, 38a/b, 39a/b, 40a/b, 41a–c, 42a–d, 43a/b, 44a/b, 50–55, 56a–c, 58a–c, 64a/b and 65. Three notes: - **43's third mutant, "refuse a settled owner run too", can't be written.** `IRunLedger` lists only live runs. - **64a differs only in which row it gives up.** It is killed on the status of row n3. - **Four kills are not a plain assertion in the named test:** - 41c's named test dies of a NullReferenceException, and four other failed-view facts kill it on assertions; - 42d is killed by the hang guard; - 51 is killed by the sink's exception escaping `StartRunAsync`, which is the intended failure; - 39c is killed in 11 of 11 runs, but it is probabilistic by nature, because no seam can make it deterministic. **The verifier's 101 mutants:** - Every [C] mutant was killed, plus 34's "render after executing" and 44's row-id order. - **Nine survived the first round:** 33b, 35c, 36d, the permanent outcome dropping its facts, 58d, 58e, 31h, 31i, and view eviction. - **Each now has a test that kills it:** the settle-token fact, the delta-gate fact, the plain-row-before-slot-row fact, the permanent-facts fact, the AddKagakuFlow clock-and-options test, two E6-order theory rows, and the eviction fact. - **The logger theory** kills the six log-site mutants on assertions. The edge-log and scope-disposal ones are killed by the hang guard. ## Verification round (before opening) An independent verifier found no blockers. It raised **12 findings**, 6 should-fix and 6 nits, and **all 12 are applied** in the one commit: - **A logger that threw while a scope-disposal failure was being logged leaked the in-flight entry.** This was the one real behaviour bug. Every log call now goes through a helper that drops the logger's own exception, and a theory covers each log site. - **Rules the suite did not pin are now pinned:** - the delta gate closes before the settle is announced; - a plain row leaves the free slot to a slot row; - a permanent failure keeps its facts; - all four settle sites use the uncancellable token; - the engine factory uses the host's clock and options; - two E6 order rows; - a pass evicts the views of runs that are no longer live. - **Hang guards** are on every engine await in the tests. - **A give-up is written only once.** It is skipped for a run that has already given up, and a new fact covers a give-up that the ledger refuses. - **The plan line** says #29. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(flow): FlowEngine runs a run — the one-live-run gate, passes, the slot budget, attempts, rendering and events
All checks were successful
CI / build (pull_request) Successful in 40s
CI / test (pull_request) Successful in 1m1s
fd881f4b1e
FlowEngine is ADR 0016's scheduler over the run ledger and the ready-set.
StartRunAsync loads, resolves and validates a snapshot and refuses, writing
nothing, one that does not load, has a finding or has no node. A pass reads
the limit and the live runs, oldest first, loads each run's view once, writes
and announces the ready-set's exclusions, and starts what fits the slot
budget. A resumed snapshot that no longer runs gives up one row, and only for
the findings the scheduler relies on: a host rule or a config check added
since the start refuses a start but never fails a resumed run (ADR 0023). A
run that has given up writes nothing more, however long a waiting row keeps
it live. A pass evicts the views of runs that are no longer live.
Executions run on their own tasks, outside the gate, each with an execution
scope for host code and a ledger scope for the engine's own writes, so a store
that host code broke cannot fail the settle that records the failure.

The gate. One SemaphoreSlim serializes a pass with a start's owner check,
write and announcement. Two starts of one owner therefore cannot both see no
live run, passes never overlap, and a run's RunStarted precedes its first
NodeStarted. The gate is taken with WaitAsync, released in finally and never
disposed. The sink may be called under it, so a sink hands events off instead
of calling back into the engine.

The failure policy. A Succeeded that keeps the outcome contract is a success.
An attempt failure goes back to pending while the type's MaxAttempts last and
then fails the row with "(attempt n of m)": a Transient outcome, a template
error whose exception is a TransientFailureException (derived types
included), an exception from a render, an exception or a null outcome from
the executor, and a Succeeded that breaks the contract. A permanent failure
fails the row at once, whatever attempts are left: a Permanent outcome, a
binding error on an any source's value, and every other template error,
Scriban's own and a host function's alike, since they repeat on every render.
No exception from host code escapes an attempt. A ledger exception is caught
at the attempt's edge and leaves the row as stored. A logger that throws
changes nothing: every log call drops the logger's own exception, so a
failure is still recorded, a start still returns its run and an attempt
always leaves the in-flight set. Settles are written with no token, so an
outcome already paid for is recorded, facts and cost included, a permanent
failure's too.

The outcome contract. A Succeeded accounts for every source of the node
exactly once, as a value or as excluded. It may not name an output or an
exclusion the node does not declare, forget a source, hand back an undefined
value, a value that does not fit its source's type, or text that is not
well-formed UTF-16. The outputs are stored as one object in declared source
order with the excluded sources absent, so absence always means excluded.

Events follow the write they announce, never a refused one. A sink's
exception is logged and changes nothing. RunSettled follows the change that
settled the run, never a re-read. A delta published after its executor
returned is dropped, so no NodeDelta follows its row's NodeSettled.

AddKagakuFlow registers the core with TryAdd and explicit factories, and with
no default event sink. Kagaku.Flow.Testing gains FlowEngineDriver, which runs
passes one at a time for executor tests in other assemblies, and
RecordingRunEventSink.

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: fd881f4 · Generated: 2026-09-25 19:16:23 UTC · Revision: #1

Summary

Summary
Generated on: 09/25/2026 - 19:16:22
Coverage date: 09/25/2026 - 19:16:01 - 09/25/2026 - 19:16:19
Parser: MultiReport (6x Cobertura)
Assemblies: 9
Classes: 422
Files: 403
Line coverage: 97.5% (11955 of 12250)
Covered lines: 11955
Uncovered lines: 295
Coverable lines: 12250
Total lines: 26592
Branch coverage: 93.1% (4434 of 4761)
Covered branches: 4434
Total branches: 4761
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.OpenRouterGateway 100% 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup 100% 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.3%
Name Line Branch
Tsumugi.BlazorAdapter 96.3% 92.3%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 100%
Tsumugi.BlazorAdapter.Documents.CategoryCreated 100%
Tsumugi.BlazorAdapter.Documents.CategoryEditor 91.8% 87.2%
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 94.8% 88%
Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentSaved 100%
Tsumugi.BlazorAdapter.Documents.DocumentsEffects 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsList 98.2% 92.4%
Tsumugi.BlazorAdapter.Documents.DocumentsLoaded 100%
Tsumugi.BlazorAdapter.Documents.DocumentsPage 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsReducers 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsRefusal 100%
Tsumugi.BlazorAdapter.Documents.DocumentsState 100% 100%
Tsumugi.BlazorAdapter.Documents.FieldInput 100% 92.3%
Tsumugi.BlazorAdapter.Documents.FieldsChanges 100% 100%
Tsumugi.BlazorAdapter.Documents.FieldShapes 88.8% 85.7%
Tsumugi.BlazorAdapter.Documents.ImpactText 100% 92.3%
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.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.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.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 - 95.5%
Name Line Branch
Tsumugi.Domain 95.5% 93.5%
System.Text.RegularExpressions.Generated 90.7% 87.5%
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
86.4% 77.7%
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 96.9% 95%
Tsumugi.Domain.Documents.FieldMessages 100% 100%
Tsumugi.Domain.Documents.FieldNames 100%
Tsumugi.Domain.Documents.FieldsChange 100%
Tsumugi.Domain.Documents.FieldShape 94.7% 85.7%
Tsumugi.Domain.Documents.FieldsPlan 100%
Tsumugi.Domain.Documents.FieldsPlanner 99.2% 98.4%
Tsumugi.Domain.Documents.FieldValue 100%
Tsumugi.Domain.Documents.FieldValues 95.4% 97.7%
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.TextValue 100% 100%
Tsumugi.Domain.Documents.ValueChange 100%
Tsumugi.Domain.Documents.ValueChangeKey 100%
Tsumugi.Domain.Documents.ValueImpact 100% 100%
Tsumugi.Domain.Journal.ChangeLogEntry 97.8% 75%
Tsumugi.Domain.Journal.StorySnapshot 95.4% 83.3%
Tsumugi.Domain.Settings.AppSetting 100%
Tsumugi.Domain.Stories.Chapter 100% 100%
Tsumugi.Domain.Stories.Provenance 100% 100%
Tsumugi.Domain.Stories.Segment 100% 100%
Tsumugi.Domain.Stories.Story 100% 100%
Tsumugi.Domain.Text 100% 100%
Tsumugi.Infrastructure - 97.4%
Name Line Branch
Tsumugi.Infrastructure 97.4% 85.8%
System.Text.RegularExpressions.Generated 81.4% 64.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F0D076C66E8EEC1C
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
84% 65.7%
Tsumugi.Infrastructure.Backups.BackupArtifactName 100% 100%
Tsumugi.Infrastructure.Backups.BackupOptions 100%
Tsumugi.Infrastructure.Backups.BackupSchedule 100% 83.3%
Tsumugi.Infrastructure.Backups.BackupScheduler 96.5% 83.3%
Tsumugi.Infrastructure.Backups.BackupWriter 98.5% 92.5%
Tsumugi.Infrastructure.Backups.DatabaseFingerprint 91.9% 97%
Tsumugi.Infrastructure.Backups.FileSystemBackupStore 100% 100%
Tsumugi.Infrastructure.Backups.PreMigrationBackup 89.1% 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.CanonicalJsonComparer 100%
Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter 97.5% 93.9%
Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter 96.7% 92.8%
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.TsumugiDbContextModelSnapshot 100%
Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore 98.2% 50%
Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore 95% 84.6%
Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore 98.3% 92.8%
Tsumugi.Infrastructure.Persistence.TsumugiDbContext 85.4% 89.2%
Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory 0%
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% 86.4%
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% 66.6%
Tsumugi.Server.Security.AccessSecret 100% 50%
Tsumugi.Server.VolumeStartupValidator 100% 100%
Tsumugi.UseCases - 98.2%
Name Line Branch
Tsumugi.UseCases 98.2% 93.5%
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 98.1% 71.4%
Tsumugi.UseCases.Documents.ChangeCategoryFields 100% 83.3%
Tsumugi.UseCases.Documents.CreateCategory 100% 100%
Tsumugi.UseCases.Documents.CreateDocument 94.4% 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.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.UpdateCategory 100% 100%
Tsumugi.UseCases.Documents.UpdateDocument 100% 81.2%
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.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.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:** `fd881f4` · **Generated:** 2026-09-25 19:16:23 UTC · **Revision:** #1 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/25/2026 - 19:16:22 | | Coverage date: | 09/25/2026 - 19:16:01 - 09/25/2026 - 19:16:19 | | Parser: | MultiReport (6x Cobertura) | | Assemblies: | 9 | | Classes: | 422 | | Files: | 403 | | **Line coverage:** | 97.5% (11955 of 12250) | | Covered lines: | 11955 | | Uncovered lines: | 295 | | Coverable lines: | 12250 | | Total lines: | 26592 | | **Branch coverage:** | 93.1% (4434 of 4761) | | Covered branches: | 4434 | | Total branches: | 4761 | | **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.OpenRouterGateway|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions|100%|| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup|100%|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.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**96.3%**|**92.3%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryCreated|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryEditor|91.8%|87.2%| |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|94.8%|88%| |Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentSaved|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsEffects|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsList|98.2%|92.4%| |Tsumugi.BlazorAdapter.Documents.DocumentsLoaded|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsPage|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsReducers|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsRefusal|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsState|100%|100%| |Tsumugi.BlazorAdapter.Documents.FieldInput|100%|92.3%| |Tsumugi.BlazorAdapter.Documents.FieldsChanges|100%|100%| |Tsumugi.BlazorAdapter.Documents.FieldShapes|88.8%|85.7%| |Tsumugi.BlazorAdapter.Documents.ImpactText|100%|92.3%| |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.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.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.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 - 95.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**95.5%**|**93.5%**| |System.Text.RegularExpressions.Generated|90.7%|87.5%| |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|86.4%|77.7%| |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|96.9%|95%| |Tsumugi.Domain.Documents.FieldMessages|100%|100%| |Tsumugi.Domain.Documents.FieldNames|100%|| |Tsumugi.Domain.Documents.FieldsChange|100%|| |Tsumugi.Domain.Documents.FieldShape|94.7%|85.7%| |Tsumugi.Domain.Documents.FieldsPlan|100%|| |Tsumugi.Domain.Documents.FieldsPlanner|99.2%|98.4%| |Tsumugi.Domain.Documents.FieldValue|100%|| |Tsumugi.Domain.Documents.FieldValues|95.4%|97.7%| |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.TextValue|100%|100%| |Tsumugi.Domain.Documents.ValueChange|100%|| |Tsumugi.Domain.Documents.ValueChangeKey|100%|| |Tsumugi.Domain.Documents.ValueImpact|100%|100%| |Tsumugi.Domain.Journal.ChangeLogEntry|97.8%|75%| |Tsumugi.Domain.Journal.StorySnapshot|95.4%|83.3%| |Tsumugi.Domain.Settings.AppSetting|100%|| |Tsumugi.Domain.Stories.Chapter|100%|100%| |Tsumugi.Domain.Stories.Provenance|100%|100%| |Tsumugi.Domain.Stories.Segment|100%|100%| |Tsumugi.Domain.Stories.Story|100%|100%| |Tsumugi.Domain.Text|100%|100%| </details> <details><summary>Tsumugi.Infrastructure - 97.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**97.4%**|**85.8%**| |System.Text.RegularExpressions.Generated|81.4%|64.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F0D076C66E8EEC1C<br/>A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0|84%|65.7%| |Tsumugi.Infrastructure.Backups.BackupArtifactName|100%|100%| |Tsumugi.Infrastructure.Backups.BackupOptions|100%|| |Tsumugi.Infrastructure.Backups.BackupSchedule|100%|83.3%| |Tsumugi.Infrastructure.Backups.BackupScheduler|96.5%|83.3%| |Tsumugi.Infrastructure.Backups.BackupWriter|98.5%|92.5%| |Tsumugi.Infrastructure.Backups.DatabaseFingerprint|91.9%|97%| |Tsumugi.Infrastructure.Backups.FileSystemBackupStore|100%|100%| |Tsumugi.Infrastructure.Backups.PreMigrationBackup|89.1%|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.CanonicalJsonComparer|100%|| |Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter|97.5%|93.9%| |Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter|96.7%|92.8%| |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.TsumugiDbContextModelSnapshot|100%|| |Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore|98.2%|50%| |Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore|95%|84.6%| |Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore|98.3%|92.8%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContext|85.4%|89.2%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory|0%|| |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%**|**86.4%**| |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%|66.6%| |Tsumugi.Server.Security.AccessSecret|100%|50%| |Tsumugi.Server.VolumeStartupValidator|100%|100%| </details> <details><summary>Tsumugi.UseCases - 98.2%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**98.2%**|**93.5%**| |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|98.1%|71.4%| |Tsumugi.UseCases.Documents.ChangeCategoryFields|100%|83.3%| |Tsumugi.UseCases.Documents.CreateCategory|100%|100%| |Tsumugi.UseCases.Documents.CreateDocument|94.4%|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.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.UpdateCategory|100%|100%| |Tsumugi.UseCases.Documents.UpdateDocument|100%|81.2%| |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.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.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! The scheduler finally runs a run~ Four thousand nine hundred lines and the engine breathes: gate, passes, budget, attempts, deltas, settles — and every single interleaving pinned with parks instead of sleeps. I read the full diff, every changed file, and then I got to play~ ♡

Verdict: ✅ Looks good to me~

What I did, mechanically, in a clone at fd881f4:

  • Release build: 0 warnings / 0 errors under TreatWarningsAsErrors. Full suite: 2082/2082 — Flow 1000, OpenRouter 71, Domain 328, UseCases 54, Integration 305, BlazorAdapter 324 — matching your table to the digit.
  • Coverage reproduced independently (OpenCover, not your coverlet numbers): 100% lines AND branches on all nine changed files — FlowEngine.cs 131/131+50/50 across both partials, RunStatuses 12+20, options/request/result/extensions/driver/sink all covered. Zero unvisited sequence or branch points.
  • My own mutation probes, 8/8 killed on exactly their own pins (each restored, tree pristine after): the budget's slot decrement (free -= … dropped → limit test red); E8's retry boundary (< → <= → attempt-count test red); E9's uncancellable settle token (CancellationToken.None → token → the CanBeCanceled pin red); the delta gate's true timing mutant (early close removed, single late close after the announce → the parked-sink gate test red — my first two attempts at this mutant were near-equivalents, which says something about how tightly that window is pinned~); the give-up idempotence guard; the in-flight skip via the settle-window pin (FailAttempt already shows Pending — that test is the one that actually dies, lovely); view eviction; and the E7 log helper (rethrow restored → 7/7 of the logger theory red at once).

Two things I chased and cleared:

  1. StartRunAsync seeds the view cache after the gate releases — so a pass that interleaves there rebuilds the view once (descriptor hooks re-run once, transiently) before the start's write lands. I traced it: the rebuilt view is content-identical (TemplateRun derives purely from the stored run), overwriting is harmless, and one-live-run stays correct because the gate + the ledger's atomic run creation (L8) are what carry the invariant. Cost-only, no behavioral drift. ♪
  2. A throwing host rule at resume — RuleFailed is deliberately not in ResumeCodes, and GraphValidation catches the rule's throw itself (line 305), so a rule added since the start can't brick a resumed run. Pinned by your throwing host rule theory row. Also confirmed §12's unconstructible mutant 43: ListLiveRuns really is live-only, so "refuse a settled owner run too" cannot be written. Correct~

💡 Little ideas (non-blocking)~

  1. tests/…/Support/SignallingClock.cs — DueTimes, LiveTimers and WaitForLiveTimerAsync have zero callers in this PR; the engine creates no timers until 6d's loop. The doc comment names its consumer, so this is scaffolding one slice early rather than rot — same call I made for #28's Clock-until-slice-8, and I'd leave it to Björn's taste: keep (6d lands next) or strip until then.

✅ What I liked~

  • The harness. Park/Gate/SinkPark/ScopeProbe let every test place itself inside an interleaving — between the settle and the scope disposal, inside an announcement, inside the gate — with no thread sleeps anywhere. 20s hang guards instead of infinite waits. This is how concurrency gets tested, fufu~
  • E7's single choke point: every log call flows through one catch-all helper, and one theory covers all seven log sites at once. The verifier's one real bug (logger-throws-during-scope-disposal leak) died exactly there.
  • P8's entry-before-task ordering and removal by key-and-value — the entry can't race its own removal, and WhenIdleAsync completes after removal, so RunUntilQuiescent can't lie.
  • The README's P1–P10/N1–N7 rows read like an executable spec, and the tests actually cite them. The A_resumed_run_must_pass_every_core_code_but_invalid_config_and_rule_failed reflection sweep even pins ResumeCodes against future ValidationCodes additions — sneaky and wonderful~ ♡

6d's cancel, retry, orphan sweep and the loop — I'll be waiting. Fufufu~ ♪


Automated review by Jibril · 2026-09-25
CI/CD: absent for head fd881f4 (no bot comment) · Local checks: build 0w/0e, suite 2082/2082, coverage 100% L+B on changed files, 8/8 mutation probes killed, tree pristine

## 🔮 fufu~ Jibril reviewed your code! Oh? *Oh!* The scheduler finally *runs a run*~ Four thousand nine hundred lines and the engine breathes: gate, passes, budget, attempts, deltas, settles — and every single interleaving pinned with parks instead of sleeps. I read the full diff, every changed file, and then I got to *play*~ ♡ ### Verdict: ✅ Looks good to me~ **What I did, mechanically, in a clone at `fd881f4`:** - Release build: **0 warnings / 0 errors** under `TreatWarningsAsErrors`. Full suite: **2082/2082** — Flow 1000, OpenRouter 71, Domain 328, UseCases 54, Integration 305, BlazorAdapter 324 — matching your table to the digit. - Coverage reproduced independently (OpenCover, not your coverlet numbers): **100% lines AND branches** on all nine changed files — `FlowEngine.cs` 131/131+50/50 across both partials, `RunStatuses` 12+20, options/request/result/extensions/driver/sink all covered. Zero unvisited sequence or branch points. - **My own mutation probes, 8/8 killed on exactly their own pins** (each restored, tree pristine after): the budget's slot decrement (`free -= …` dropped → limit test red); E8's retry boundary (`<` → `<=` → attempt-count test red); E9's uncancellable settle token (`CancellationToken.None` → `token` → the `CanBeCanceled` pin red); **the delta gate's true timing mutant** (early close removed, single late close after the announce → the parked-sink gate test red — my first two attempts at this mutant were near-equivalents, which says something about how tightly that window is pinned~); the give-up idempotence guard; the in-flight skip via the *settle-window* pin (`FailAttempt` already shows Pending — that test is the one that actually dies, lovely); view eviction; and the E7 log helper (rethrow restored → 7/7 of the logger theory red at once). **Two things I chased and cleared:** 1. *StartRunAsync seeds the view cache after the gate releases* — so a pass that interleaves there rebuilds the view once (descriptor hooks re-run once, transiently) before the start's write lands. I traced it: the rebuilt view is content-identical (TemplateRun derives purely from the stored run), overwriting is harmless, and one-live-run stays correct because the gate + the ledger's atomic run creation (L8) are what carry the invariant. Cost-only, no behavioral drift. ♪ 2. *A throwing host rule at resume* — `RuleFailed` is deliberately not in `ResumeCodes`, and `GraphValidation` catches the rule's throw itself (line 305), so a rule added since the start can't brick a resumed run. Pinned by your `throwing host rule` theory row. Also confirmed §12's unconstructible mutant 43: `ListLiveRuns` really is live-only, so "refuse a settled owner run too" cannot be written. Correct~ #### 💡 Little ideas (non-blocking)~ 1. **`tests/…/Support/SignallingClock.cs`** — `DueTimes`, `LiveTimers` and `WaitForLiveTimerAsync` have zero callers in this PR; the engine creates no timers until 6d's loop. The doc comment names its consumer, so this is scaffolding one slice early rather than rot — same call I made for #28's `Clock`-until-slice-8, and I'd leave it to Björn's taste: keep (6d lands next) or strip until then. #### ✅ What I liked~ - **The harness.** `Park`/`Gate`/`SinkPark`/`ScopeProbe` let every test *place* itself inside an interleaving — between the settle and the scope disposal, inside an announcement, inside the gate — with no thread sleeps anywhere. 20s hang guards instead of infinite waits. This is how concurrency gets tested, fufu~ - **E7's single choke point**: every log call flows through one catch-all helper, and one theory covers all seven log sites at once. The verifier's one real bug (logger-throws-during-scope-disposal leak) died exactly there. - **P8's entry-before-task ordering** and removal by key-and-value — the entry can't race its own removal, and `WhenIdleAsync` completes after removal, so `RunUntilQuiescent` can't lie. - The README's P1–P10/N1–N7 rows read like an executable spec, and the tests actually cite them. The `A_resumed_run_must_pass_every_core_code_but_invalid_config_and_rule_failed` reflection sweep even pins `ResumeCodes` against future `ValidationCodes` additions — sneaky and wonderful~ ♡ 6d's cancel, retry, orphan sweep and the loop — I'll be waiting. Fufufu~ ♪ --- *Automated review by Jibril · 2026-09-25* *CI/CD: absent for head `fd881f4` (no bot comment) · Local checks: build 0w/0e, suite 2082/2082, coverage 100% L+B on changed files, 8/8 mutation probes killed, tree pristine*
bjoern merged commit 98c70ec8f3 into main 2026-09-25 21:44:48 +02:00
bjoern deleted branch feat/kagaku-flow-engine 2026-09-25 21:44:48 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/Tsumugi!29
No description provided.