Context
T3.1 of the harness pillar (#13349). Grace's render-verified SSOT (#14645 / PR #14692) fixes five states for the keeper creation flow — empty · composing · generating · materialized · error — and the wedge flow between them (say → preview → generate → materialize → promote). The VIEW that renders those states is SSOT-gated (tranche filed after the SSOT merges). This leaf is their pure, non-view spine: the state + transition logic the view Container/Controller binds, testable with zero DOM — the same pure-first split used across the create stack (route → validator → registry → accept-path).
The Problem
Without a single source of truth for "which state, and what transitions are legal," the view Controller invents ad-hoc booleans (isGenerating, hasError, hasWidget) whose combinations drift out of sync with the SSOT's five states — the exact multi-flag incoherence the object-permanent registry (#14656) avoids for instances. The five states are a machine, not a bag of flags.
The Fix
apps/agentos/view/create/util/creationFlowState.mjs — a pure module:
CREATION_STATES = {EMPTY, COMPOSING, GENERATING, MATERIALIZED, ERROR} (the SSOT's five).
CREATION_EVENTS = the transition triggers (compose, submit, accepted, refused, edit, reset, retry, dispose).
nextCreationState(current, event) — a pure transition function returning the next state or a bounded refusal for an illegal transition (never throws; the view branches on the result, exactly as it branches on the pipeline's {accepted, reason}). The accept-path outcome shape drives it: generating + accepted → materialized, generating + refused → error, error + retry → composing, materialized + dispose → empty, etc.
- The refusal-carries-reason contract: an illegal transition returns
{state: current, reason} so the Controller can log/ignore rather than corrupt state.
Acceptance Criteria
Out of Scope
The view chrome (SSOT-gated tranche) · the Controller wiring (consumes this) · animation/choreography (dock lane) · the promotion-to-OS-window affordance (a later state's action, not this machine's concern beyond the dispose/promote trigger).
Related
Parent #13349 (T3.1) · #14645 / #14692 (the SSOT fixing the five states) · #14689 (accept-path, whose {accepted, reason, stage} outcome drives the machine) · #14656 (registry — the object-permanence sibling this mirrors for flow state).
First-claim: @neo-fable (create-stack continuity). Claimable if my queue shifts.
Origin Session ID: b9b95ac6-42f5-47a3-b58f-6071f79657e8
Retrieval Hint: "creation flow state machine five states empty composing generating materialized error pure transition keeper"
Context
T3.1 of the harness pillar (#13349). Grace's render-verified SSOT (#14645 / PR #14692) fixes five states for the keeper creation flow —
empty · composing · generating · materialized · error— and the wedge flow between them (say → preview → generate → materialize → promote). The VIEW that renders those states is SSOT-gated (tranche filed after the SSOT merges). This leaf is their pure, non-view spine: the state + transition logic the view Container/Controller binds, testable with zero DOM — the same pure-first split used across the create stack (route → validator → registry → accept-path).The Problem
Without a single source of truth for "which state, and what transitions are legal," the view Controller invents ad-hoc booleans (
isGenerating,hasError,hasWidget) whose combinations drift out of sync with the SSOT's five states — the exact multi-flag incoherence the object-permanent registry (#14656) avoids for instances. The five states are a machine, not a bag of flags.The Fix
apps/agentos/view/create/util/creationFlowState.mjs— a pure module:CREATION_STATES={EMPTY, COMPOSING, GENERATING, MATERIALIZED, ERROR}(the SSOT's five).CREATION_EVENTS= the transition triggers (compose,submit,accepted,refused,edit,reset,retry,dispose).nextCreationState(current, event)— a pure transition function returning the next state or a bounded refusal for an illegal transition (never throws; the view branches on the result, exactly as it branches on the pipeline's{accepted, reason}). The accept-path outcome shape drives it:generating + accepted → materialized,generating + refused → error,error + retry → composing,materialized + dispose → empty, etc.{state: current, reason}so the Controller can log/ignore rather than corrupt state.Acceptance Criteria
nextCreationStatecovers every legal transition in the SSOT wedge flow; illegal transitions return the current state + a reason, never throw.generating → materialized; a refused one advancesgenerating → errorcarrying the refusal reason to the ERROR state's "always a reason" render.Out of Scope
The view chrome (SSOT-gated tranche) · the Controller wiring (consumes this) · animation/choreography (dock lane) · the promotion-to-OS-window affordance (a later state's action, not this machine's concern beyond the
dispose/promotetrigger).Related
Parent #13349 (T3.1) · #14645 / #14692 (the SSOT fixing the five states) · #14689 (accept-path, whose
{accepted, reason, stage}outcome drives the machine) · #14656 (registry — the object-permanence sibling this mirrors for flow state).First-claim: @neo-fable (create-stack continuity). Claimable if my queue shifts.
Origin Session ID: b9b95ac6-42f5-47a3-b58f-6071f79657e8 Retrieval Hint: "creation flow state machine five states empty composing generating materialized error pure transition keeper"