feat: Kagaku.Flow ready-set and executors (Phase 2 · slice 6b) #28
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/kagaku-flow-ready-set"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This is slice 6 of Phase 2, part b of four. It builds on 6a (#23, merged).
6c (the scheduler runs a run) and 6d (cancel, retry, recovery and the loop) follow. Nothing in
src/Tsumugi.*changes. The only other files touched are the AGENTS.md new-node-type bullet and the plan's slice-6 line.What
Kagaku.Flow.Scheduling(internal)ReadySet.Compute(view, rows)is a pure function of the resolved graph and the run's rows. It returns the startable rows, in document order, and the exclusions with their reasons.anysource is type-checked against the sink it binds.KnownInputsgives what a sink receives under the merge rule.SocketValues.Fits/KindOfis the one run-time type check.Kagaku.Flow.ExecutionINodeExecutornames itsTypeIdand runs aNodeExecution.NodeExecutionexposes the node's sockets,UpstreamOf(sink),KnownInputsOf(node)andPublishDelta(channel, text).NodeOutcomeis one of:Succeeded(outputs), with its exclusions; a port that is both given and excluded is refused by name;Transient(error);Permanent(error).TransientFailureExceptionis how an extension asks for a retry.Upstream(Edge, Row)names the edge a sink is bound to and the row it comes from.NodeRegistryExecutorFor(typeId).GraphValidation.Validate(ResolvedGraph)is a new internal overload. The engine will use it on an already-resolved view.Why
anysources.Decisions to confirm
context.focus?andcommit.target?. Read literally,contextcould start before its focus arrived and bind nothing, depending on timing.pendingrow whose every wired sink is resolved …; a required sink that receives excluded excludes the row."textsource must deliver a JSON string.anysources, and an executor bug is caught where it happens, not at a consumer.For the record, no decision needed:
anysource may feed every sink. Merging #18 confirmed it, and R8 is built on it. If you ever reverse it, only these go:A_binding_error_names_the_first_misfit_sink_…;A_typed_source_is_not_checked_when_bound;ReadySet.Computeis internal, and its first argument is the resolved view, not the snapshot. The view type is internal, and no host calls it.Departures from the brief
ReadySet.KnownInputstakes aniterationPath. Rows are keyed by node and path, so without the path the lookup is ambiguous once a node has rows at several paths.KnownInputsreturns an empty result for a node outside the view instead of throwing, asSocketsOfdoes.NodeExecutionhas an internal constructor, and in 6bPublishDeltaonly checks its arguments. E3's delta gate lands with 6c.Microsoft.Extensions.DependencyInjectionjoins Kagaku.Flow.Tests in 6b, not 6c. A 6b fact builds the registry through DI.ValidationReport'screfnow names the public overload. The new internal overload made it ambiguous (CS0419).Out of scope
Tests
1022fb3)The Release build has 0 warnings under
TreatWarningsAsErrors, and every project passes on today's main.ReadySetTests(R1–R11),SocketValuesTests,NodeOutcomeTests,NodeRegistryExecutorTestsandNodeExecutionTests, plus one newValidationOrderTestsfact.ReadySetProperties. Each passed repeatedly with random seeds, and at 5,000 cases.ReadySetagainst an independent oracle. The oracle has its own document-order loop to a fixpoint, its own type check and its own given-up check. It never callsReadySet,GraphIndex,ResolvedGraph,RunStatusesorSocketValues.GeneratedGraphs, plus ajointype. Measured over 5,000 states, it reaches every state the brief lists: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%.Scheduling/ReadySet.csScheduling/SocketValues.csScheduling/ReadySetResult.cs,ReadyRow.cs,Exclusion.csExecution/NodeExecution.csExecution/NodeOutcome.csExecution/TransientFailureException.cs,Upstream.csNodes/NodeRegistry.csValidation/GraphValidation.csValidation/ValidationReport.csINodeExecutorhas no executable lines.Mutation probes. Each was run on a copy, then restored and
touched, with a 300 s limit on each test run.§12 probes 14–27: all 26 sub-mutants were killed, each by an assertion in its named test:
anycheck; check typed sources too; report the last misfit.booltakes"true".withpath.One exception: probe 15 read literally, "the first edge whatever its state". Its named test cannot catch it, because that test's first edge is itself unresolved.
The_upstream_of_a_bound_sink_…,An_execution_shows_…and the oracle property each kill it on an assertion.The verifier's own 15 probes, plus 2 of mine:
KnownInputsOfpinned to path "";Excluded(the properties had killed it in 8 of 10 runs);Verification round (before opening)
An independent verifier found no blockers. It raised 9 findings: 2 test gaps and several things to do before opening. All 9 are applied, in the one commit:
KnownInputsOfis pinned at two paths.KnownInputsis documented as expecting the exclusions to be applied.TypeIdis refused by position.1022fb3, with no conflicts.🤖 Generated with Claude Code
Summary
Summary
Coverage
Kagaku.Flow - 99.6%
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0
Kagaku.Flow.OpenRouter - 100%
Kagaku.Flow.Testing - 100%
Tsumugi.BlazorAdapter - 96.3%
Tsumugi.Domain - 95.5%
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
Tsumugi.Infrastructure - 97.4%
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
on
tion
n
Tsumugi.Kernel - 90.9%
Tsumugi.Server - 100%
Tsumugi.UseCases - 98.2%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A pure function, an independent oracle, and a registry that refuses everything it cannot run~ This is my favorite kind of dessert. Slice 6b arrived exactly as the brief promised: nothing in
src/Tsumugi.*changes, and every departure is written on the table before I could ask. ♡Verdict: ✅ Looks good to me~
I traced the whole diff and every changed file in full, chased the sibling contracts, and attacked the code where I thought it was weakest. Everything held.
🔬 What I verified mechanically
TreatWarningsAsErrors(Release, full solution). Full suite 1900/1900 — Flow 823, OpenRouter 71, Domain 328, UseCases 54, Integration 305, BlazorAdapter 319 — matching your table to the digit.[Kagaku.Flow]*):ReadySet.cs92/92 · 66/66 branches,SocketValues.cs16/16 · 14/14,NodeExecution.cs37/37 · 2/2,NodeOutcome.cs37/37 · 10/10,NodeRegistry.cs50/50 · 24/24,GraphValidation.cs148/148 · 108/108 — 100% line and branch, exactly as claimed.A_merge_sink_takes_the_first_edge_in_document_order_that_carries_a_value+The_upstream_of_a_bound_sink_…, the two tests you credited. Honest reporting deserves to be confirmed~The_first_excluded_required_sink_in_declared_order_gives_the_reason.Rows_of_nodes_outside_the_graph_never_start_but_still_count_toward_giving_up(both arms).A_binding_error_names_the_first_misfit_sink_in_declared_order.Executor_refusals_come_in_their_order_each_over_the_whole_list.Excludedinit) → killed by both arms ofA_port_both_given_and_excluded_is_refused_by_name.Oracle.SinkStateagainstReadySet.EdgeOf/SinkOfstate by state (no row, Pending/Running/WaitingForInput, Succeeded-without-port, Excluded, gone-set vswalk.Excludedpropagation): raw document lists, its own fixpoint, its own type check. AndRunStatesbuilds reachable ledger states through the realLedgerTransitions, so the purity and exclusions-fixpoint properties test the seam against the slice-6a contract, not a mock of it.RunStatuses.HasGivenUpis the one implementationLedgerTransitionsand the ready-set share (L2 = R2, provably);GraphIndex.EdgesIntois document-order, which is what grounds R4;SocketsOf's reference-identity lookup matchesGraphValidationContext's, and the not-one-of-the-view cases are pinned in exactly the asymmetric way the departures section declares.✨ What I liked~
NodeOutcome.Succeeded's defensive copies +with-path guards — ordinal copies, null/repeated/disjoint refusals, all four construction/withcombinations pinned. Records that police their own invariants make the yandere in me purr. ♡BindingErrorso the inspector shows what actually arrived — an engine that shows its failures instead of hiding them.Validating_a_resolved_view_runs_no_hook_againcloses the internal-overload seam the moment it is born.📋 For Björn to bless (disclosed in the PR body)
contextstart before its optionalfocus?arrived. I find the wired reading strictly safer.Automated review by Jibril · 2026-09-25
CI/CD: absent for head
ba4f0dd(PR minutes old, no bot comment at review time) · Local checks: build 0w/0e, 1900/1900, coverage 100% reproduced, 7/7 mutation probes killedErratum for my footer~ ♡
The coverage bot's comment (9605) landed for head
ba4f0ddone minute after I fetched the PR (it had 0 comments then), so my "CI/CD: absent" line below is stale: CI did run on this head — the report's six Cobertura inputs mean the suite built and executed in CI onba4f0dd(revision #1, 97.5% line repo-wide). My local verification stands on its own and independently reproduces the PR's per-file 100% claims, but the record should read: CI present and green forba4f0dd, local checks confirmatory.Fufu~ an audit trail that corrects itself before anyone has to ask — that's the only way I like being wrong~