Files
poimen/tasks/T10.1-goalid-and-goal-scoped-query.md

3.1 KiB

T10.1 — GoalId and goal-scoped query

Field Value
Phase P10 — Orchestration
Size S — under 1 day
Status Not started
Flags
Spec inlined below
Blocks T10.2, T10.3, T10.4, T10.8

Goal

New identity GoalId(Ulid) grouping runs that share an objective, optional at spawn. Extend T1.7's query surface to read across RunIds sharing one GoalId.

Facts (inlined — no spec read needed)

  • GoalId is tenant-scoped like RunId, not TaskId. TaskId is already the grading comparison-group key (§11.5) — reusing it corrupts group-epoch/quorum semantics the learning loop depends on.
  • Attaching goal_id at spawn is optional. A run with no GoalId behaves exactly as today — this is additive, not a required field.
  • GoalView is a new, separate query. It does not replace per-run EpisodeView; it lists member RunIds and references their existing metadata, no duplication.
  • Same parity rule as T1.7: GoalView from materialized state must equal GoalView from a cold re-fold (T0.8's property applied here too).

Steps

  1. Declare GoalId(Ulid) in the ids crate, alongside RunId.
  2. Add optional goal_id: Option<GoalId> to run-spawn input and the run record.
  3. Index runs by (TenantId, GoalId) in materialized state.
  4. Define GoalView { goal: GoalId, runs: Vec<RunSummary> }, RunSummary referencing existing run metadata — do not duplicate EpisodeView fields.
  5. Implement the query against materialized state and against cold re-fold.
  6. Test: two runs sharing a GoalId, one plain run with none; assert GoalView returns exactly the two, in spawn order.

Acceptance

  • Two runs sharing a GoalId are both returned by GoalView; a run with no GoalId never appears in any GoalView.
  • GoalView from materialized state equals GoalView from cold re-fold.

Verify

Harness: two runs sharing a GoalId, one run with no GoalId, one branch/rewind case reused from T1.7's harness.

Integration testtests/it_goal_view_equivalence.rs:

  1. Spawn run A and run B both with goal_id=G; spawn run C with none.
  2. Query GoalView(G) from materialized state → view_a.
  3. Drop state tables; query again via cold re-fold → view_b.
  4. Assert serialize(view_a) == serialize(view_b).
  5. Assert view_a contains exactly {A, B}, never C.
  6. Assert order is spawn order, stable.

Command: cargo test -p query goal_view_equivalence

False pass:

  • Testing only one run per goal — a grouping bug is invisible with a single member.
  • Skipping the "run without goal_id" exclusion check. A filter that defaults to "all runs" passes silently without it.

Traps

  • Deriving GoalId from TaskId "since they're both groupings." Different lifecycle, different owner — grading vs orchestration.
  • Making goal_id required at spawn. Breaks every existing plain-workflow spawn call.

Background (not required to do this task): rust-agentic-sys.md §11.5 · T1.7-episode-query-surface.md · T0.8-fold-and-re-derive.md