Files
poimen/tasks/T1.3-run-executor.md

5.6 KiB

T1.3 — Run executor

Field Value
Phase P1 — Walking skeleton
Size L — over 3 days
Status Not started
Flags
Spec inlined below
Blocks

Goal

Drive a hardcoded three-step workflow through the full run lifecycle, emitting every kernel transition to the log. T3.5 later replaces the hardcoded workflow with the IR interpreter.

Facts (inlined — no spec read needed)

Run lifecycle — the states and the legal moves:

        spawn
          │
          v
      Scheduled ─────────────────┐
          │ admitted             │
          v                      │
       Running ⇄ Suspended ──────┤
          │ all steps terminal   │ cancel
          v                      │
      Verifying ─────────────────┤   ◄── rests here while verifier
          │                      │       futures are outstanding
          v                      │
      Verified{pass|fail}        │
          │                      │
          v                      v
       Grading ──────────────► Cancelled ●
          │   ◄── may wait for a tournament group to fill
          │
          ├──► Graded ●   ──┐
          │                 ├──► Archived ●
          └──► Ungraded ● ──┘
  • Cancel is reachable from every non-terminal state, not only Scheduled. A run cancelled mid-step is the ordinary case — it is what a user clicking stop does. Cancelling during Verifying or Grading is rarer and still legal.
  • Verifying and Grading are states the run rests in, not synchronous branches. Resolving pass/fail inside verify() collapsed this in the prior implementation and blocked three separate features at once: async verifiers, mid-run UI, and the snapshot barrier (T4.2).
  • Ungraded is terminal and sits beside Graded, not below it. Grading can legitimately end with no score. Without a terminal state saying so, those runs rest in Grading forever and become permanently irreducible (T4.4).
  • Suspended releases the worker: a run awaiting human approval or a webhook must not hold an executor slot across a human decision.
  • Concurrency shape: unbounded across runs; serial steps within a run; strictly serial attempts within a step; fan-out only for declared Parallel branches and for verifiers of one attempt. The serial spine is (TenantId, RunId).

Steps

  1. Define the run-state enum and its transition function, exhaustive like T0.2's. Include Cancelled as reachable from every non-terminal state.
  2. Build the executor loop: take the hardcoded three-step workflow, run steps serially, each step producing one or more attempts (T1.4).
  3. Emit a WorkEvent for every transition through EventLog::commit — run level and attempt level. One commit per transition, not one per run.
  4. Enter Verifying only when every step is terminal. Park there; do not resolve verification inline.
  5. Enter Grading as a resting state likewise; allow it to terminate as Graded or Ungraded.
  6. Run everything inside a RunScope (T1.1) so cancel propagates and no task outlives the run.
  7. Add Suspended with lease release wired as a no-op stub in embedded mode — the state must exist now, since T7.3 depends on it.

Acceptance

  • One run completes end to end against the stub model.
  • Verifying and Grading are observable as distinct states in the log, not skipped or collapsed.

Verify

Harness: embedded redb, stub model (T1.2), one throwaway verifier, one stub grader. This is the first test that exercises T0.5, T0.6, T0.8, T1.1 and T1.2 together — treat it as the P1 integration point.

Integration testtests/it_full_run.rs:

  1. Spawn one run of the three-step workflow; drive to completion.
  2. Read the log back and assert the run-state sequence contains Scheduled, Running, Verifying, Grading and a terminal state, in that order. Assert Verifying and Grading each appear as their own record — not inferred, not skipped.
  3. Assert every step produced attempt records and that no state appears out of order against T0.2's table.
  4. Call assert_refold_identical on the finished run.
  5. Cancel matrix: start a run, cancel it from each of Scheduled, Running, Verifying, Grading in turn; assert each reaches Cancelled.
  6. Ordering: launch 20 runs concurrently, assert each run's own records are totally ordered and that no two steps of one run overlap in time.

Command: cargo test -p executor --test it_full_run

False pass:

  • Asserting Verifying "happened" by checking a boolean on the run record. That passes against a synchronous verify() call, which is the collapse this task exists to prevent. The log record is the assertion.
  • A cancel test that only covers Scheduled. That is the one case a broken implementation gets right.
  • Step 6 with a single run, where serial execution is indistinguishable from accidental parallelism.

Traps

  • Making cancel legal only from Scheduled. Nobody would ship that lifecycle.
  • Treating Verifying as a function call. It removes the seam T4.2 needs.
  • Interleaving steps within a run "since they look independent". The serial spine is (TenantId, RunId); parallelism lives between runs and inside declared fan-out only.

Background (not required to do this task): rust-agentic-sys.md §5.2, §5.3 · rust-agentic-task.md