3.7 KiB
3.7 KiB
T5.1 — TaskId at spawn
| Field | Value |
|---|---|
| Phase | P5 — Grading |
| Size | S — under 1 day |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | T5.5 onward. Cannot be retrofitted |
Goal
Every run carries a TaskId from spawn. Required, no default, no derivation from
RunId.
Facts (inlined — no spec read needed)
TaskIdis the comparison-group key: a content hash of the task input before any workflow touches it.- Hashing the prompt does not work — the workflow changes the prompt by construction, which is the entire point of a variant.
TaskIdcannot be backfilled. A run recorded without one is permanently ungroupable and useless to the learning loop. Hence: required at spawn, noDefault, noFrom<RunId>(T0.1).- Land this during P1, even though nothing consumes it until P5. It is a small task deep in P5 and it is the one item on the critical path that a later phase cannot repair.
- What defines
TaskIdfor a given user is open — ticket id, input fixture, or a hash of the pre-workflow goal. The framework provides the type and a default hasher and lets it be overridden. Settle it before any run is recorded.
Steps
- Add
task: TaskIdas a required positional field of the spawn parameters. NotOption, not a builder method that can be omitted. - Provide a default hasher over the pre-workflow task input, and a hook to override it. Document that the choice is permanent for existing data.
- Record
TaskIdon the run's spawn event so it lands in the log, not only in materialized state. - Expose it on
EpisodeViewfor the grading path. - Add the
trybuildcompile-fail case: a spawn call omittingTaskId.
Acceptance
- Spawning without a
TaskIdfails to compile.
Verify
Harness: trybuild, plus a full run to prove the id reaches storage.
Integration test — tests/it_taskid_required.rs:
trybuildcompile-fail: a spawn call omittingTaskId. Assert the expected stderr names the missing field, not merely "does not compile".- Run a real run; read the log (not materialized state) and assert the spawn
event carries the
TaskId. - Assert
EpisodeViewexposes it, so the grading path can group on it. - Determinism: hash the same task input twice, in two processes; assert equal
TaskId. - Independence: assert two runs of the same task input share a
TaskId, and that two different inputs do not. Both directions. - Assert the
TaskIdis not derived from the prompt: change the workflow version (which changes the prompt) and assert theTaskIdis unchanged for the same input. trybuild:TaskId::default()andTaskId::from(run_id)both fail.
Command: cargo test -p kernel taskid && cargo test -p kernel --test compile_fail
False pass:
- Step 2 read from materialized state. If the id lives only in state it cannot be re-derived after a cold fold, and it cannot be backfilled — read the log.
- Step 6 omitted: hashing the prompt passes steps 1–5 completely and makes every variant its own group, which is the exact failure that renders the learning loop inert.
- A builder with a runtime
expect("task_id required"). It passes a test that checks for a panic and still allows runs to be recorded without one in any code path the test did not cover.
Traps
- A builder with
.task_id(...)optional and a runtime check. Runs recorded before someone notices are unrecoverable. - Deriving it from the prompt or from
RunId. Both compile; both make every run its own group of one.
Background (not required to do this task): rust-agentic-sys.md §3, §11.5, §18 · rust-agentic-task.md