You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We've moved to always-headless, so any headless action should be a dedicated headless actor at the machine level — consistent with prx's grain (signed spawn@<role> = ocap, "actors own writes", the pilot/SDK executor pattern). Burying it in the delegate also blocks hermetic testing of prioritizeBulkActor/typePassActor (the wrapper can't inject around an inline executeAgentProfile).
Proposed change
Extract a headlessClassifierActor — one fromPromise over executeAgentProfile + the haiku runtime profile + the --output-format json envelope parse. The single headless-invocation actor (SDK-executor pattern).
Restructure the triage machine so the prioritize-bulk / type-pass states invoke the headless actor; its output flows through machine context to the apply step. The delegates drop their inline runClaude and become pure transforms (consume a classifier result).
Make it a signed + observable step (this issue's scope): wire the headless actor into the signed-spawn so each call is attested as spawn@classifier (ocap), and emit a leg-event so the headless LLM invocation is observable (telemetry seam). One attestation/telemetry site instead of two inline call sites.
Why now
Aligns headless LLM calls with the signed/observable actor model from the start (no unsigned/unobserved headless node).
Builds on #499 (triage actors↔machine cycle-break + the per-actor deps seam — the seam is what lets the machine inject the headless actor). Land after #499.
Acceptance criteria
headlessClassifierActor exists as a machine-level fromPromise actor wrapping executeAgentProfile + envelope parse.
prioritize-bulk / type-pass delegates no longer call executeAgentProfile inline; they consume the actor's result and are pure (no headless IO).
The triage machine invokes the headless actor in the classification states; result flows via context.
Each headless call is attested as spawn@classifier (signed) and emits a leg-event (observable).
prioritizeBulkActor / typePassActor wrappers are hermetically testable (machine-injected fake headless actor) and covered.
Problem
The headless LLM (haiku) call is buried inside two triage verb delegates as a "separate call", not modeled as a first-class actor:
triage/prioritize-bulk.ts→defaultClaudeRunner→buildTriageHaikuClassifierRuntimeProfile+executeAgentProfile(...)triage/type-pass.ts→ same patternWe've moved to always-headless, so any headless action should be a dedicated headless actor at the machine level — consistent with prx's grain (signed
spawn@<role>= ocap, "actors own writes", the pilot/SDK executor pattern). Burying it in the delegate also blocks hermetic testing ofprioritizeBulkActor/typePassActor(the wrapper can't inject around an inlineexecuteAgentProfile).Proposed change
headlessClassifierActor— onefromPromiseoverexecuteAgentProfile+ the haiku runtime profile + the--output-format jsonenvelope parse. The single headless-invocation actor (SDK-executor pattern).invokethe headless actor; its output flows through machine context to the apply step. The delegates drop their inlinerunClaudeand become pure transforms (consume a classifier result).spawn@classifier(ocap), and emit a leg-event so the headless LLM invocation is observable (telemetry seam). One attestation/telemetry site instead of two inline call sites.Why now
prioritizeBulkActor/typePassActorcouldn't be driven hermetically because the haiku call is inline).Depends on / sequencing
Builds on #499 (triage actors↔machine cycle-break + the per-actor
depsseam — the seam is what lets the machine inject the headless actor). Land after #499.Acceptance criteria
headlessClassifierActorexists as a machine-levelfromPromiseactor wrappingexecuteAgentProfile+ envelope parse.prioritize-bulk/type-passdelegates no longer callexecuteAgentProfileinline; they consume the actor's result and are pure (no headless IO).spawn@classifier(signed) and emits a leg-event (observable).prioritizeBulkActor/typePassActorwrappers are hermetically testable (machine-injected fake headless actor) and covered.🤖 Filed with Claude Code