Skip to content

v0.30.0 — state transition modeling

Choose a tag to compare

@wfukatsu wfukatsu released this 27 Aug 10:35
· 91 commits to main since this release

One skill, and the wiring that makes it worth having. 103 slash commands → 104.

A design document that says "an order can be cancelled" never says precisely which changes are legal from which state, who may make them, or what happens to the attempts that are not. That last gap is the expensive one: undecided, it is settled at runtime by whichever code path arrives first, and it surfaces as a double charge, a reservation nobody holds, or a retry treated as an error.

/architect:design-state-machine (opus)

Seven facilitated stages build, per aggregate with a lifecycle:

Element What the model fixes
States The invariant that holds in each, what it permits and forbids, which is initial and which are terminal
Events Commands, integration events and timers, each with its source
Transitions (from, event) [guard] → to, with an actor, an effect, a consistency class and an idempotency verdict
State × event matrix Every remaining combination decided — reject, ignore or defer — with no blank cell left for the runtime to fill
Concurrency Who races whom, who wins, and what the loser does; sagas name their compensating transition and its real target state

--auto derives the models from existing reports for automated runs and marks every cell it had to assume, rather than presenting a guess as a decision.

A transition is a transaction. Its guard is evaluated inside the transaction that writes the new state — a guard checked before the transaction begins is not a guard, it is a race with a comment. Under optimistic concurrency control two actors firing from one state means one commits and one fails; what the model must decide is what the loser does, and "retry" is only correct with the guard re-evaluated.

The model is checked, not trusted

reports/03_design/state-machines/state-machine-manifest.json is canonical and the Markdown is its projection, so tools/lib/state_machine_manifest.py (29-check suite) enforces the seven well-formedness rules of the new rules/state-modeling.md: exactly one initial state, every state reachable, no undeclared dead end, no two transitions sharing (state, event) without stated and distinct guards, every guard declaring its else branch, an actor and a consistency class on every transition, and a matrix that is neither undecided nor contradicting a transition. Every test case is a model that reads perfectly well and is wrong.

Wired where these things were already being decided implicitly

Phase What it now takes from the model
design-scalardb / design-data-layer The state column and its OCC scope, the transition history store, the per-transition consistency class
design-api Transitions become operations, reject cells become registered problem types, ignore cells become the idempotency contract
generate-test-specs A State Transition Coverage section that makes coverage measurable: every allow fires, every guard is exercised on both branches, every reject returns the contracted error, every ignore replays cleanly
review-consistency Runs the validator, then reviews whether the design documents still agree with the model, and checks state and event names against the ubiquitous language
report / review-report Each modeled aggregate renders as a Design subsection with its diagram and its matrix

/architect:start offers the phase after redesign and skips it explicitly — saying so — when no aggregate shows evidence of a lifecycle. /architect:pipeline now states how it treats optional: true phases at all: run them non-interactively, or record them skipped with the reason rather than emitting a document derived from nothing.

Full detail: CHANGELOG.md (日本語).