Skip to content

feat(kernel): canonical intermediate-state FSM, unknown-state validation, writer timestamps - #59

Merged
SollanSystems merged 1 commit into
mainfrom
roadmap/s1-fsm
Jul 13, 2026
Merged

SollanSystems merged 1 commit into
mainfrom
roadmap/s1-fsm

Conversation

@SollanSystems

Copy link
Copy Markdown
Owner

Refs #51 (FSM definition + validator half; the deterministic reducer half lands with #50).

What

  • loop/fsm.py (new, pure stdlib): canonical intermediate-state vocabulary — 9 non-terminal states (intake, plan, critique-plan, queue-tasks, execute-task, verify, repair, replan, approval-wait) + absorbing terminal marker — with the legal-transition table: terminate-anywhere, approval-anywhere-except-intake, approval-wait resume fan-out (never back to intake), explicit active→active edges, always-legal self-stay, fail-open when either endpoint is unknown.
  • loop/contract.py: new unknown_state error checked in both validation modes; contracts may declare extra domain states via a new optional manifest.yaml extra_states key.
  • loop/emit.py: append_iteration gains an optional state= kwarg that advances the FSM with EmitError on illegal transitions (checked before the RUNLOG write); terminate() now actually sets state: "terminal" (previously never set — a latent gap; the example fixtures were hand-authored); sync_state_to_terminal() reconciles the state field symmetrically; all writers stamp ISO-8601 UTC updated_at.
  • Schemas (all additive-optional): state.updated_at, terminal.terminated_at (already stamped by terminate(), never declared), manifest.extra_states.
  • reference/repo-os-contract.md: documents the 9+1 vocabulary and timestamp fields; loop/fsm.py is the normative transition table; no section renumbering.

Test evidence

  • Suite: 528 passed / 14 skipped (baseline 455/9 with full extras; like-for-like with this gate command: 450/12 → 528/14). +78 tests including negative controls: verify→replan illegal, queue-tasks→verify illegal, terminal absorbing, intake↛approval-wait, fail-open unknowns, both-validation-modes unknown_state.
  • Exactly three pre-existing test lines changed, all state-fixture drift fixes ("Planned"→"intake", "running"→"execute-task", "execute"→"execute-task" in test_loop_contract_core.py:587) — values that predate any vocabulary validation.
  • validate_frontmatter and self_eval green.

Provenance (Claudex governed lane)

Authored by gpt-5.6-sol (codex session 019f5c05-46fe-7222-b67a-1cc45417bd32), design by a read-only sonnet design pass, adversarially reviewed by two fresh claude-sonnet-5 reviewers (first review FAILed the packet on a mis-specified acceptance criterion — fixed via replan packet s1-fsm-b, applied byte-identically by gpt-5.6-terra, session 019f5c22-6711-7670-8d49-f862ea01f840). Receipts: cx_f606f60f67244b88 (replan_required), cx_2d39ba8308548f45 (accepted). Deterministic gates run outside the worker.

https://claude.ai/code/session_01EJ8zA8Cbi4o2amawpj8bZW

…ion, writer timestamps

Refs #51. loop/fsm.py (9+1 vocabulary, legal transitions), unknown_state
contract check in both modes, manifest extra_states, EmitError on illegal
transitions, terminate() now sets state=terminal, ISO-8601 UTC updated_at/
terminated_at as additive-optional schema fields.

Claude-Session: https://claude.ai/code/session_01EJ8zA8Cbi4o2amawpj8bZW
Copilot AI review requested due to automatic review settings July 13, 2026 15:45
@SollanSystems
SollanSystems enabled auto-merge (squash) July 13, 2026 15:46
@SollanSystems
SollanSystems merged commit e5c441c into main Jul 13, 2026
11 checks passed
@SollanSystems
SollanSystems deleted the roadmap/s1-fsm branch July 13, 2026 15:46

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61f2d6b0c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread loop/emit.py
Comment on lines +149 to +151
if not fsm.is_legal_transition(current.get("state"), state):
raise EmitError(f"illegal FSM transition {current.get('state')!r} -> {state!r}")
current["state"] = state

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject undeclared states before writing

When a caller passes a typo or undeclared custom state such as state="verfiy", fsm.is_legal_transition() deliberately fails open for unknown endpoints, so this path accepts it and writes it into state.json; the next validate_contract() then fails with unknown_state. Since append_iteration(state=...) is now the writer path for advancing the FSM, it should also verify the target is canonical or listed in manifest.extra_states before mutating the contract.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a canonical intermediate-state FSM to the kernel, enforces/validates state vocabulary (including optional manifest-declared domain states), and adds ISO-8601 UTC timestamp stamping for state/terminal writers so state artifacts better reflect writer activity.

Changes:

  • Add loop/fsm.py defining the canonical 9+1 state vocabulary and legal transition table.
  • Enforce state vocabulary in loop/contract.py (both validation modes) with optional manifest.yaml: extra_states.
  • Update emit writers (append_iteration, terminate, sync_state_to_terminal) to advance/stamp FSM state and add updated_at/terminated_at timestamps; update schemas + docs and extend test coverage.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
scripts/test_template_roundtrip.py Updates template test fixture to new canonical state name.
scripts/test_state_vocabulary.py Adds tests for canonical state acceptance, unknown-state rejection, and manifest-declared extra states across both validation modes.
scripts/test_manifest_extra_states_schema.py Adds schema-level tests for manifest.extra_states.
scripts/test_loop_contract_core.py Updates a drifted fixture state string to the canonical vocabulary.
scripts/test_fsm.py Adds unit tests for FSM state set, adjacency rules, terminal absorbing behavior, approval-wait rules, and fail-open unknowns.
scripts/test_emit.py Adds tests for FSM advancement enforcement and UTC updated_at stamping in emit writers.
scripts/test_conformance.py Updates conformance fixture state to canonical vocabulary.
schemas/terminal.schema.json Adds optional terminated_at field to the terminal schema.
schemas/state.schema.json Adds optional updated_at field to the state schema.
schemas/manifest.schema.json Adds optional extra_states field to the manifest schema.
reference/repo-os-contract.md Documents the canonical 9+1 state vocabulary and new timestamp fields.
loop/fsm.py New canonical FSM vocabulary + transition logic.
loop/emit.py Adds optional state= to append_iteration, stamps updated_at, stamps terminal state marker on terminate, and syncs terminal marker symmetrically.
loop/contract.py Adds unknown_state validation against canonical states plus manifest-declared extras in both validation modes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread loop/emit.py
Comment on lines +143 to +151
if state is not None and (not isinstance(state, str) or not state.strip()):
raise EmitError("state must be a non-empty string when provided")
_require_iteration_id(iteration_id)
paths = _require_contract(target)
current = _read_state(paths)
if state is not None:
if not fsm.is_legal_transition(current.get("state"), state):
raise EmitError(f"illegal FSM transition {current.get('state')!r} -> {state!r}")
current["state"] = state
Comment thread loop/emit.py
Comment on lines +189 to +192
current["iteration_id"] = iteration_id
if task_id:
state["active_task"] = task_id
_write_state(paths, state)
current["active_task"] = task_id
_write_state(paths, current)
Comment thread loop/emit.py
Comment on lines +336 to +340
if not fsm.is_legal_transition(current.get("state"), fsm.TERMINAL_MARKER):
raise EmitError(
f"terminal written at {terminal_path} but state.json has no legal transition "
f"from {current.get('state')!r} to {fsm.TERMINAL_MARKER!r}"
)
Comment thread loop/contract.py
Comment on lines +231 to +239
extra_states = manifest.get("extra_states") if isinstance(manifest, dict) else None
declared = (
tuple(value for value in extra_states if isinstance(value, str))
if isinstance(extra_states, list)
else ()
)
value = state.get("state")
if value not in fsm.ALL_STATES + declared:
issues.append(ContractIssue("unknown_state", f"unknown state {value!r}", path))
"outputs": { "type": "object" },
"permissions": { "type": "array", "items": { "type": ["string", "object"] } },
"approval_gates": { "type": "array", "items": { "type": "string" } },
"extra_states": { "type": "array", "items": { "type": "string", "minLength": 1 } },
SollanSystems added a commit that referenced this pull request Jul 17, 2026
Version 0.8.0 -> 0.9.0 across pyproject.toml, plugin.json, README
(badge, action example, schema list 4 -> 10, event-sourced runtime
bullet, Status), CHANGELOG 0.9.0 entry covering #41-#48 and #59-#77,
and the version-pin test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants