Skip to main content

Goal Lifecycle

This page documents the canonical goal state machine as it exists in scripts/goal-runner/state_machine.py and the orchestrator's transition logic.

Canonical states

The normal Goal Runner state machine:

DRAFT → READY_FOR_PLAN → PLANNED → READY_FOR_BUILD → READY_FOR_AUDIT → READY_FOR_RELEASE → FROZEN_PASS

DRAFT is the initial state of a freshly committed goal. After materialization, the goal moves to READY_FOR_PLAN and the orchestrator dispatches a PLANNER task.

FromToTrigger
DRAFTREADY_FOR_PLANFirst materialization.
READY_FOR_PLANPLANNEDPLANNER role emits plan + receipts.
PLANNEDREADY_FOR_BUILDAuto-advance; no agent dispatch.
READY_FOR_BUILDREADY_FOR_AUDITBUILDER PASS + evidence.
READY_FOR_AUDITREADY_FOR_RELEASEINDEPENDENT_AUDIT_HIGH PASS at exact head.
READY_FOR_RELEASEFROZEN_PASSGoverned merge + production acceptance.
READY_FOR_*REPAIR_REQUIREDAdapter FAIL or canonical repair trigger.
REPAIR_REQUIREDREADY_FOR_PLANBounded repair goal PASS.
READY_FOR_*PAUSEDOwner goal-pause.ps1.
PAUSEDREADY_FOR_*Owner goal-resume.ps1 with PHASE_00_RESUME_APPROVAL (if applicable).

Illegal transitions

FromToWhy rejected
DRAFTREADY_FOR_BUILDMust pass through planning.
READY_FOR_PLANFROZEN_PASSSkips build and audit.
FROZEN_PASSany non-terminalTerminal state is immutable; revision needs a repair goal.
READY_FOR_RELEASEREADY_FOR_AUDITAudit cannot follow release.
CANCELEDanyCancelled goals cannot be revived.

Program Goal states

The Program Goal has its own state set:

DRAFT → READY_FOR_PLAN → PLANNED → READY_FOR_BUILD → READY_FOR_AUDIT → READY_FOR_RELEASE → FROZEN_PASS → PROGRAM_PRODUCTION_CERTIFIED

PROGRAM_PRODUCTION_CERTIFIED is the program-level terminal state, distinct from FROZEN_PASS (which is the per-IMP terminal state).

Materialization

GoalRegistry.materialize(goal_id) is the entry point:

from goal_registry import GoalRegistry
from runtime_store import RuntimeStore
from pathlib import Path

store = RuntimeStore(Path("scripts/goal-runner/local/runtime"))
registry = GoalRegistry(store, Path("."))
result = registry.materialize("ASF50_DOCS_PORTAL_001")

materialize is idempotent. Running it twice does not duplicate state.

Transitions in receipts

Every transition is recorded in state_transitions with created_at, the previous state, and the new state. The orchestrator refuses to perform a transition that is not in the legal set.