From 7a2647f32a11864d0c2f98bd2278d18fdf524f9a Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 12 Sep 2026 01:20:49 +0200 Subject: [PATCH] docs(agent): define pico state tasks and tool boundaries --- .../agent/docs/pico/pico-simple-handoff.md | 392 +++++++++++++++++- 1 file changed, 372 insertions(+), 20 deletions(-) diff --git a/packages/agent/docs/pico/pico-simple-handoff.md b/packages/agent/docs/pico/pico-simple-handoff.md index b593b86c0..a9a3bf7ed 100644 --- a/packages/agent/docs/pico/pico-simple-handoff.md +++ b/packages/agent/docs/pico/pico-simple-handoff.md @@ -35,8 +35,10 @@ Recoverability does not preserve a JavaScript stack and does not imply exactly-o A crash after an unkeyed external action but before recording its identity/result remains uncertain. Required exclusions: no imports from another harness implementation; no worker pool, polling scheduler, -lease, effect gate, author-defined lifecycle graph, `patch`, `settle`, status epoch, workflow replay, -generator DSL, returned step plan or specialized core verbs such as `startShell`. +lease, effect gate, kernel-interpreted global author lifecycle graph, `patch`, `settle`, status epoch, +workflow replay, generator DSL, one-plan-at-end restriction or specialized core verbs such as `startShell`. +The optional per-kind state-indexed authoring adapter in section 5 is explicitly allowed; it compiles to an +ordinary `TaskKind` and changes no kernel lifecycle or scheduler behavior. ## 2. Core data and identifiers @@ -467,6 +469,279 @@ and open reject a mismatch before invocation. With no live instance, a future re capability for newly created tasks. Terminal history is not rewritten. Payload and checkpoint compatibility beyond these runtime-checkable properties is the replacement author's responsibility. +### 5.1 Optional state-indexed task authoring + +`defineStateTask` is an optional authoring adapter for kinds with several durable recovery phases. It +returns an ordinary `TaskKind`; `defineTask` remains available for simple or advanced kinds. The adapter +makes one kind's checkpoint dispatch exhaustive without adding author phases to stored `Task.status` or to +the scheduler. + +```ts +type PhaseOf = C["phase"]; +type CheckpointAt> = + Extract; +type PhasePayload> = + Omit, "phase">; + +type PhaseTask> = + Omit, "checkpoint"> & { + readonly checkpoint: CheckpointAt; + }; + +type InitialTask = + Omit, "checkpoint"> & { + readonly checkpoint?: never; + }; + +type StateTransitionTx = + Omit : BaseTaskTx, "checkpoint">; +type StatePhaseTx, T extends false | true> = + StateTransitionTx & { + checkpoint(value: CheckpointAt): void; + }; +type InitialStateRuntime = + Omit, "commit">; +type StateRuntimeFor> = + Omit, "commit"> & { + commit( + build: (tx: StatePhaseTx, current: PhaseTask) => V | Promise, + ctx: Context, + ): Promise; + }; + +interface PhaseTransition> { + readonly type: "transition"; + readonly phase: P; + readonly commit: ( + tx: StateTransitionTx, + current: RunningTask, + ) => PhasePayload | Promise>; +} +interface StateTerminal { + readonly type: "terminal"; + readonly closure: TerminalClosure; +} +type StateResult = + | PhaseTransition> + | StateTerminal; + +type ExactPhasePayload, + Actual extends PhasePayload> = + NoExtra, Actual>; +interface StateActions { + transition

, Actual extends PhasePayload>( + phase: P, + commit: ( + tx: StateTransitionTx, + current: RunningTask, + ) => ExactPhasePayload | Promise>, + ): PhaseTransition; + terminal( + closure: TerminalClosure, + ): StateTerminal; +} + +type PhaseHandler> = + | { + readonly role: "start"; + readonly recover?: never; + run( + task: PhaseTask, + runtime: StateRuntimeFor, + actions: StateActions, + ctx: Context, + ): Promise>; + } + | { + readonly role: "inflight"; + run( + task: PhaseTask, + runtime: StateRuntimeFor, + actions: StateActions, + ctx: Context, + ): Promise>; + recover( + task: PhaseTask, + runtime: StateRuntimeFor, + actions: StateActions, + ctx: Context, + ): Promise>; + }; + +type PhaseMap = { + readonly [P in PhaseOf]: PhaseHandler; +}; +type ExactPhaseHandler = + Actual extends { readonly role: infer Role } + ? Actual extends Extract + ? NoExtra, Actual> + : never + : never; +type ExactPhaseMap = + Actual & Record, never> & { + readonly [P in keyof Expected]: ExactPhaseHandler; + }; +type LiteralCheckpoint = + string extends PhaseOf ? never : C; + +type StateTaskDefinition = { + readonly kind: string; + readonly turn: T; + readonly initial: { + readonly role: "start"; + run( + task: InitialTask, + runtime: InitialStateRuntime, + actions: StateActions, + ctx: Context, + ): Promise>; + }; + readonly phases: PhaseMap; + abort( + task: RunningTask, + runtime: AbortRuntimeFor, + ctx: Context, + ): Promise>; +} & TaskOutputDefinition; + +type NonTurnStateTaskDefinition = + Omit, "turn"> & { readonly turn?: false }; +type TurnStateTaskDefinition = + StateTaskDefinition; + +type ExactStateTaskDefinition = + NoExtra & { + readonly phases: ExactPhaseMap; + }; + +interface StateTaskFactory { + >( + definition: [LiteralCheckpoint] extends [never] + ? never + : ExactStateTaskDefinition, D>, + ): D & TaskKind; + >( + definition: [LiteralCheckpoint] extends [never] + ? never + : ExactStateTaskDefinition, D>, + ): D & TaskKind; +} + +declare const commitStateTransition: unique symbol; +interface StateAdapterRuntime { + [commitStateTransition]

>( + expectedSource: PhaseOf | undefined, + phase: P, + build: ( + tx: StateTransitionTx, + current: RunningTask, + ) => PhasePayload | Promise>, + ctx: Context, + ): Promise>; +} + +// Curried generics and turn/output inference mirror defineTask. +declare function defineStateTask(): + StateTaskFactory; +``` + +`StateActions` is passed immediately before the final `ctx`; authors call `actions.transition(...)` and +`actions.terminal(...)`. The factory's outer and nested exact helpers capture actual phase handlers and +transition callback payloads. This declaration pattern was prototyped under the repository TypeScript +compiler with the positive and negative cases below; WP8A moves those cases into maintained compile tests. +Its compile contract is exact: + +- `C` is a finite discriminated union whose `phase` values are string literals; broad `string` rejects. +- Every `C["phase"]` has exactly one handler and no extra phase key exists. +- A handler's task contains the complete checkpoint variant for its phase. +- `initial` receives a running task with no checkpoint. Task creation remains checkpoint-free. +- A `start` phase has `run` and no `recover`; an `inflight` phase requires both. +- The target passed to `transition` determines the complete target payload. Missing or visible extra fields + reject for literals, variables and spreads. +- `terminal(closure)` preserves the existing typed `TerminalClosure`; terminal author phases do not exist. +- Literal turn selection, output inference and fresh kind-level abort match `defineTask` exactly. + +The factory generates `TaskKind.execute` and `TaskKind.recover` around this internal loop: + +```text +scheduler reserves pending -> running and calls generated execute once + no checkpoint -> assert current unmarked invocation; initial.run + handler returns actions.transition(P, commit) + call the module-private commitStateTransition capability + on the line: validate expected source and invocation, run commit(tx, fresh current), + write the complete P checkpoint, persist/apply, return applied PhaseTask

+ assert current unmarked invocation; dispatch P.run within the same outer TaskKind invocation + repeat until a handler returns terminal(closure) +generated execute returns closure to the existing terminal path + +reopen reserves the restored running task and calls generated recover once + no checkpoint -> fenced initial.run + start checkpoint P -> fenced P.run + inflight checkpoint P -> fenced P.recover + after any committed transition, entry becomes normal and the next fenced handler uses run +``` + +The scheduler never calls `execute` again for an internal transition. `commitStateTransition` is a +module-private symbol implemented by the task-kernel runtime and consumed only by the generated adapter; it +is not exported and grants no task-author capability. It performs one ordinary line transaction, then reads +the already-applied live-task projection, including checkpoint and `owns`. There is no second empty commit +and the adapter never fabricates a task from the stale handler argument. Transition callbacks run on the +line against fresh current state, may perform transaction reads and compose same-batch entries/tasks/state, +and cannot perform effects, waits, hooks, sleeps or nested commits. + +`InitialStateRuntime` has no commit method. With no durable phase, allowing initial to commit child work would +let a crash rerun initial and duplicate them. Initial child/task/conversation creation therefore occurs in +its first returned transition callback, atomically with the first complete checkpoint. The next phase +resolves handles and waits only after that transition has committed. + +Checkpoint-backed handlers retain `runtime.commit` for durable bookkeeping that must precede a wait or +effect. The wrapped transaction may replace only its complete current-phase variant; phase changes use +returned transitions. A same-phase commit may atomically create a child task/conversation and record its +ID/policy in the current checkpoint, then the handler waits after the commit resolves. The handler's later +transition callback still receives fresh current state. + +`start` means the checkpoint does not represent an uncertain external effect, so reopen runs it normally. +`inflight` means an effect may have started, so reopen requires its explicit recovery path. This is a small +named state machine per task kind, not positional replay: only the latest complete checkpoint is durable. +There is no workflow history, deterministic replay requirement, generator, returned-plan journal, +one-commit-per-phase rule or global kernel lifecycle graph. + +Immediately before every internal handler call, the adapter synchronously validates the exact invocation +object, Context, session phase, durable mark and abort signal. If transition persistence wins first, the next +handler may be admitted and a later mark signals it normally. If the mark wins before the check, the next +handler is never invoked; the ordinary cancellation unwind returns from the outer execute/recover, which is +joined before fresh abort starts. This fence does not forcibly stop already-admitted third-party code; +handlers remain cooperatively cancellable. + ## 6. Mutation algebra and storage ### 6.1 Mutations @@ -816,7 +1091,9 @@ type RuntimeFor