3.8 KiB
3.8 KiB
T3.6 — Version pinning at spawn
| Field | Value |
|---|---|
| Phase | P3 — Workflow as data |
| Size | S — under 1 day |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | — |
Goal
VersionSelector::Latest resolves exactly once, at run spawn, and the resolved
hash is recorded.
Facts (inlined — no spec read needed)
SubWorkflowpins byVersionSelector:Exact(hash)orLatest.Latestresolves once, at run spawn, and the resolved hash is recorded. A run whose sub-workflow version can change mid-execution is a run whose results attribute to nothing.- The same rule applies to the top-level workflow: the run records the exact
WorkflowVersionit executed, and a version published mid-run does not affect it. - Related failure the type system prevents: a placeholder that type-checks is
invisible. A hardcoded
"current"version hash compiled, passed tests, and made every result unattributable — which is whyWorkflowVersionhas noDefault(T0.1). - Versions are never edited in place; a new version is a new hash with a parent pointer.
Steps
- At spawn, resolve the top-level workflow and every reachable
SubWorkflowselector to concrete hashes. - Record the resolved set on the run's spawn event — the top-level version plus
a map of
(WorkflowId → WorkflowVersion)for sub-workflows. - Make the interpreter (T3.5) read only from that recorded map. It must have no
path back to the registry's current
Latest. - Resolve
Latestagainst the registry once; if a new version is published afterwards, nothing re-reads it. - Test: start a run, publish a new version of the same workflow mid-run, assert the running run's recorded version is unchanged and it finishes on the pinned one.
Acceptance
- Publishing a new version mid-run does not change the running run's recorded version.
Phase gate
P3 closes when a user-authored YAML workflow runs with no framework recompile.
Verify
Harness: a workflow registry that can publish a new version while a run is in flight, plus a run long enough to straddle the publish (stub latency on a mid-run step).
Integration test — tests/it_version_pinning.rs:
- Register workflow
Wv1, containing aSubWorkflowpinnedLatest. - Spawn a run; park it mid-execution before the sub-workflow step.
- Publish
Wv2 and a new version of the sub-workflow. - Release the run.
- Assert the run's recorded top-level
WorkflowVersionis v1. - Assert the sub-workflow executed is the version resolved at spawn, not the newly published one — check the recorded resolution map, then check which version actually ran (they must agree).
- Assert the spawn event carries the full
(WorkflowId → WorkflowVersion)map, not a selector. - Registry-access audit: assert the interpreter makes zero registry lookups after spawn — instrument the registry with a call counter.
Command: cargo test -p executor version_pinning
False pass:
- Parking the run after the sub-workflow step, so nothing could have re-resolved. The park point must precede the step.
- Asserting only step 5. The top-level version is usually recorded correctly; the lazy-resolution bug lives in the sub-workflow path. Steps 6 and 8 are the test.
- Step 6 checking the recorded map only. A map that is recorded and then ignored passes — assert what actually ran.
Traps
- Resolving
Latestlazily at the point the sub-workflow is entered. It is the natural implementation and it makes long runs non-attributable. - Storing the selector rather than the resolved hash on the run record.
Background (not required to do this task): rust-agentic-sys.md §4.4, §12.2, §19 · rust-agentic-task.md