(tasks) add tasks for harness
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# 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)
|
||||
|
||||
- `SubWorkflow` pins by `VersionSelector`: `Exact(hash)` or `Latest`.
|
||||
- **`Latest` resolves 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
|
||||
`WorkflowVersion` it 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 why `WorkflowVersion` has no
|
||||
`Default` (T0.1).
|
||||
- Versions are never edited in place; a new version is a new hash with a parent
|
||||
pointer.
|
||||
|
||||
## Steps
|
||||
|
||||
1. At spawn, resolve the top-level workflow and every reachable `SubWorkflow`
|
||||
selector to concrete hashes.
|
||||
2. Record the resolved set on the run's spawn event — the top-level version plus
|
||||
a map of `(WorkflowId → WorkflowVersion)` for sub-workflows.
|
||||
3. Make the interpreter (T3.5) read only from that recorded map. It must have no
|
||||
path back to the registry's current `Latest`.
|
||||
4. Resolve `Latest` against the registry once; if a new version is published
|
||||
afterwards, nothing re-reads it.
|
||||
5. 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`:
|
||||
1. Register workflow `W` v1, containing a `SubWorkflow` pinned `Latest`.
|
||||
2. Spawn a run; park it mid-execution **before** the sub-workflow step.
|
||||
3. Publish `W` v2 and a new version of the sub-workflow.
|
||||
4. Release the run.
|
||||
5. Assert the run's recorded top-level `WorkflowVersion` is v1.
|
||||
6. 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).
|
||||
7. Assert the spawn event carries the full `(WorkflowId → WorkflowVersion)` map,
|
||||
not a selector.
|
||||
8. 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 `Latest` lazily 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](../../../rust-agentic-sys.md) §4.4, §12.2, §19 ·
|
||||
[rust-agentic-task.md](../../../rust-agentic-task.md)
|
||||
Reference in New Issue
Block a user