v2.10.0
Release notes for orun v2.10.0.
Highlights
- Trigger-first, revision-first state model. Every plan compiled by
orun planis now filed under aPlanRevisiontogether with theTriggerOccurrencethat produced it. Everyorun runis filed under that revision as a deterministicrun-NNN. The legacy.orun/plans/<sha>.jsonand.orun/executions/<id>/paths are preserved as a compatibility mirror — every existing reader keeps working untouched. - Hidden
orun state migrate. Idempotently rehomes a pre-v2.10.0 workspace into the new layout. Synthesizes asystem.migratedtrigger per legacy plan, attaches matching executions, and updates refs / indexes. - New describe aliases.
orun describe revision,orun describe trigger, andorun describe executionrender the three documents that make up the new chain.describe runanddescribe plancontinue to work, now via the new resolver with a legacy fallback. - End-to-end + property gates.
make test-state-redesignruns four coverage floors (statestore≥ 95 %,revision≥ 90 %,executionstate≥ 90 %,triggerctx≥ 90 %) under-race. A newcmd/orun/state_e2e_test.gowalksplan → run → status → logs → describe revision latestagainst a temp workspace.
The new on-disk layout
.orun/
├── version.json
├── revisions/<rev-key>/
│ ├── trigger.json ← TriggerOccurrence (why)
│ ├── plan.json ← compiled plan (what)
│ ├── revision.json ← PlanRevision summary
│ ├── manifest.json ← latest-execution summary
│ └── executions/run-NNN/
│ ├── execution.json
│ ├── snapshot.latest.json
│ └── logs/<job>/<step>.log
├── refs/
│ ├── latest-revision.json
│ ├── latest-execution.json
│ └── triggers/<name>/{latest,<scope>}.json
├── indexes/
│ ├── revisions/<key>.json
│ └── executions/<key>.json
├── plans/ ← legacy mirror
└── executions/ ← legacy mirror
Revision keys have the form rev-<scope>-<shortHeadSha>-p<planHash8>,
so rev-pr139-def456a-p8f31c09 reads as the plan compiled for PR 139
at commit def456a whose plan hashes to 8f31c09….
See State model for the full layout, field definitions, and resolution chain.
What changed in the CLI
| Command | What's new |
|---|---|
orun plan | Always resolves a TriggerOccurrence (system-manual for ad-hoc runs), emits a revision-summary block on success, writes .orun/revisions/<key>/{trigger,plan,revision,manifest}.json plus the legacy mirror. |
orun run | New --revision <key> flag executes a specific revision, skipping the resolution chain. Bridges runner output through executionstate.Bridge so the canonical record lives under the revision while the legacy .orun/executions/ tree is hardlink-mirrored. |
orun status | New --revision <key> flag lists all executions under a revision. Default lookup uses refs/latest-execution.json. |
orun logs | New --revision <key> flag for exact lookup when combined with --exec-id. Legacy .orun/executions/<id>/logs/ remains a fallback. |
orun describe | New revision, trigger, and execution aliases. Existing describe run/describe plan continue to work through the new resolver. |
orun get plans | When the new layout has revisions, lists revision keys + trigger + jobs + latest exec; otherwise falls back to a legacy plan-hash table. |
orun state migrate | Hidden command. Walks .orun/plans/ and .orun/executions/, rehomes each into the new layout. --dry-run previews. |
The CLI surface is fully backward-compatible. Every previously
accepted invocation still works; new flags and aliases are additive.
The compatibility mirror (stateCompatibilityWrites) is on by default.
TriggerOccurrence — system.* constructors
Every plan now resolves one of:
| Trigger | When |
|---|---|
system.manual | Bare orun plan / orun run with no --trigger and no --from-ci. |
system.manual-changed | Manual invocation with --changed. |
system.replay | A re-plan from an existing revision. |
system.api | Plan compiled by the backend / a programmatic caller. |
system.migrated | Synthesized by orun state migrate for legacy plans. |
declared | Matched a triggerBindings: entry in intent.yaml via --trigger NAME or --from-ci. |
This closes the largest semantic gap in the old layout: a plan with no
declared trigger now still has a trigger.json and a revision key,
so every later command (status, logs, describe, the future
TUI cockpit pane, the future R2/Cloud driver) can answer "why was
this compiled?" without heuristics.
Architecture: StateStore + Bridge
Phase 1 introduces three new internal packages:
internal/triggerctx— theTriggerOccurrencemodel, ULID generator, system constructors, andResolveTriggerContextdispatcher.internal/statestore— theStateStoreinterface (Read,Write,CreateIfAbsent,CompareAndSwap,List,Delete) with a local driver implementing atomic temp+rename writes, orphan cleanup on startup, typed error taxonomy (ErrNotFound,ErrExists,ErrConflict,ErrInvalid), and typed ref/index helpers. The contract is frozen — Phase 2 will add R2/S3 drivers that satisfy the same interface without callers changing.internal/revision+internal/executionstate— the writers, resolvers, and theexecutionstate.Bridgethat mirrors live runner output into the revision-first layout via hardlinks (with copy fallback on cross-device file systems).
The runner did not change. The bridge subscribes to the existing state-update hook so Phase 1 stays a pure additive layer over the runtime.
Compatibility
All of these continue to work without code changes on your side:
orun status/orun logs/orun describe runagainst a workspace that has no revisions yet (legacy.orun/executions/is read transparently).--plan <hash|name>resolution.- Reading and writing
.orun/plans/<sha>.json,latest.json, and.orun/executions/<id>/. orun get plansagainst legacy state.
To opt out of the legacy mirror after migrating, pass
--state-compat-writes=false (or set the equivalent in
~/.orun/config.yaml). Phase 2 will add --prune-legacy to delete the
mirror.
Migration
orun state migrate --dry-run # preview
orun state migrate # apply
The command is idempotent: re-running it after new state has landed fills in the gaps without touching anything already rehomed.
What's next
Phase 2 will add the remote StateStore drivers (R2/S3, then Orun
Cloud) on the same frozen interface, plus --persist-revision,
orun ref pin, and --prune-legacy. Phase 3 will wire DO-backed
coordination, Supabase indexes, and cross-plan evidence reuse for the
backend. The CLI surface and on-disk schemas published in v2.10.0 will
not change between phases.
References
- State model — layout, schemas, resolution chain.
orun state— the hiddenmigratecommand.orun plan,orun run,orun status,orun logs,orun describe— updated CLI references.- Trigger bindings — declared triggers and CI event normalization.