Skip to content

feat(cmd/crucible): add simulate subcommand to trace events through an IR#184

Merged
joshua-temple merged 1 commit into
mainfrom
feat/cli-simulate
Jun 17, 2026
Merged

feat(cmd/crucible): add simulate subcommand to trace events through an IR#184
joshua-temple merged 1 commit into
mainfrom
feat/cli-simulate

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Closes #175. Follow-up tracked in #183 (CEL-from-context guard evaluation).

What

crucible simulate <ir.json> fires an ordered event sequence through a machine assembled from the IR and prints the per-event step trace (from/to state, outcome, emitted effects) and the final state — for debugging and CI snapshot tests.

crucible simulate machine.json -events "checkout,paid" -guard hasItems=true -guard isPaid=true
crucible simulate machine.json -events-file scenario.json -format json
  • Events: -events (comma list) or -events-file (a bare ["a","b"] array or a conformance.Scenario object); exactly one required.
  • Guards: a headless IR has no real behavior, so guards return seeded verdicts — -guard name=bool (repeatable); unseeded guards default to false. Actions/reducers/services are no-ops.
  • -initial overrides the IR's declared start state; -format text|json.

Design notes

  • Reuses state/conformance.RunAgainst (no reimplemented Fire loop).
  • Exit codes: a guard-blocked or invalid transition is a legitimate observable trace → exit 0; an unknown event (*conformance.ErrUnknownEvent) or a real replay/action failure → exit 1; arg/validation errors → exit 2. The result error is classified with errors.As so blocked-but-valid traces stay distinct from failures.
  • JSON shape mirrors conformance.TraceStep (scalar states, real outcome enum values).

Verification

  • go build / go vet / go test (86 pass) / golangci-lint (0 issues) — all green in cmd/crucible.
  • End-to-end against the binary (12 assertions): seeded guards reach the final state; unseeded guards block (outcome GuardFailed, exit 0); -events-file, -initial, JSON output, unknown-event→exit 1, and all validation errors→exit 2 behave as specified.

…n IR

crucible simulate fires an ordered event sequence against a machine assembled
from an IR and prints the per-event step trace (from/to state, outcome, emitted
effects) and the final state, in text or -format json.

- Events come from -events (comma list) or -events-file (a bare name array or a
  conformance scenario JSON); exactly one is required.
- A headless IR carries no real behavior, so guards return seeded verdicts:
  -guard name=bool (repeatable), unseeded guards default to false; actions,
  reducers, and services are no-ops. -initial overrides the declared start state.
- A guard-blocked or invalid transition is a normal observable outcome (exit 0);
  an unknown event or action failure exits non-zero.
- Reuses state/conformance.RunAgainst and classifies the result error with
  errors.As to keep blocked-but-valid traces distinct from real failures.

Closes #175.
@joshua-temple joshua-temple merged commit 8e11e3f into main Jun 17, 2026
121 checks passed
@joshua-temple joshua-temple deleted the feat/cli-simulate branch June 17, 2026 16:16
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.

CLI: simulate / trace subcommand to step events through an IR

1 participant