From e8a74fa2060394d839953a6e8be6fd9bb8bda737 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Sat, 1 Aug 2026 18:25:29 -0600 Subject: [PATCH 1/3] fix(runtime): report unmetered CLI spend as unknown, not as measured zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `cli` backend spawns a subprocess and reads its stdout. It sees no usage receipt, and it recorded that as `zeroSpend()` — a value byte-identical to a run that genuinely cost nothing. Every consumer read it as a measurement: the budget pool kept reporting `readout().tokensKnown === true`, the journal totals stayed clean, the OTEL span recorded a priced zero. A caller enforcing a token-priced ceiling over this backend therefore held a ceiling that could never fire, while believing it was protected. That is worse than no ceiling, because it reads as protection. The budget-exempt `cli-worktree` path had the same defect. `Spend.tokensKnown` is the marker this substrate already threads end to end for exactly this case, so both paths now set it and the surviving zero is legible as a floor. Named `unmeteredSpend` so the next runtime that cannot see its worker's cost reaches for it instead of a bare zero. `usdKnown: false` is deliberately NOT set, and the reason is written down at the function: on the dollar channel that flag is not a marker but a refusal — `budget.ts` treats unknown dollars under a dollar-capped root as a reconcile violation and fails the child. Applying it would contradict `budgetExempt`, whose documented contract is that such a worker settles OUT of the conserved pool, and would make an explicitly-exempt worker start failing after its work had already burned. That is a policy change about which configurations are allowed, not a fix to how honestly spend is reported. `CliSeam` now says UNMETERED in its first line and names the metered alternatives, so the backend cannot be chosen for a metered worker by accident. Also documents the harness-control channel on `BridgeSeam`, which had no argv field and no pointer to what replaces one. A caller needing a harness started in a known state declares it on the `AgentProfile`: materializing any profile already isolates the run from ambient workspace state, and `AgentProfile.extensions.` is the named per-harness control (`extensions: { pi: { load: [] } }` is pi's `--no-extensions`). Verified against cli-bridge: an `agent_profile` carrying that field spawns pi with `--no-extensions --no-context-files --no-skills --no-prompt-templates`. The absence of an argv field is a boundary, not a gap — `bridgeUrl` addresses a process-spawning server that confines its workers with a filesystem jail and deny-by-default network egress, and forwarding caller argv is a channel for unwinding those confinements. The threat model is stated at the type. Tests pin the wire contract the isolation depends on, because dropping `agent_profile.extensions` fails no request — it just silently starts the harness with its ambient extensions loaded. Calibrated: reverting the marker fails the metering tests with `expected undefined to be false` and `expected true to be false`; filtering `extensions` out of the wire body fails the forwarding tests and leaves the other 14 bridge tests green. --- src/runtime/supervise/bridge-executor.test.ts | 86 +++++++++++++++++ src/runtime/supervise/runtime.ts | 96 ++++++++++++++++++- .../supervise/worktree-cli-executor.ts | 12 ++- tests/runtime/cli-executor-metering.test.ts | 79 +++++++++++++++ tests/runtime/worktree-cli-executor.test.ts | 31 ++++++ 5 files changed, 297 insertions(+), 7 deletions(-) create mode 100644 tests/runtime/cli-executor-metering.test.ts diff --git a/src/runtime/supervise/bridge-executor.test.ts b/src/runtime/supervise/bridge-executor.test.ts index f6b1b0f6..dbabfd00 100644 --- a/src/runtime/supervise/bridge-executor.test.ts +++ b/src/runtime/supervise/bridge-executor.test.ts @@ -594,3 +594,89 @@ describe('bridgeExecutor upstream-error propagation', () => { }) }) }) + +/** + * The harness-control channel. There is no argv field on `BridgeSeam` on purpose (see its doc): + * a worker isolates its harness by DECLARING it on the `AgentProfile`, and cli-bridge maps that + * declaration onto each harness's native flags. + * + * These pin the half of that contract this package owns — the declaration reaching the wire + * unmodified. It is worth pinning because the failure is SILENT: a body-shaping change that drops + * or filters `agent_profile.extensions` does not fail a request, it just starts the harness with + * its ambient extensions loaded. For a paired experiment whose arms must not share state, that is + * a state leak between arms that no assertion downstream can see. + */ +describe('bridgeExecutor harness control rides the profile, not argv', () => { + let server: Server | undefined + afterEach(async () => { + if (server) await new Promise((resolve) => server?.close(resolve)) + server = undefined + }) + + const okFrame = `data: ${JSON.stringify({ + choices: [{ delta: { content: 'done' } }], + usage: { prompt_tokens: 1, completion_tokens: 1 }, + })}\n\ndata: [DONE]\n\n` + + it('forwards the spawn profile’s per-harness extension controls verbatim', async () => { + const bodies: Record[] = [] + const stub = await startBridgeStub(okFrame, { onRequest: (body) => bodies.push(body) }) + server = stub.server + // `extensions: { pi: { load: [] } }` is how a caller says "load NO ambient extensions" — + // the declarative form of pi's `--no-extensions`, and the reason a rig that must not carry + // state between arms does not need its own executor. + const profile: AgentProfile = { + name: 'isolated-worker', + extensions: { pi: { load: [] } }, + } + const executor = bridgeExecutor( + { profile, harness: null }, + { + signal: new AbortController().signal, + seams: { + bridge: { bridgeUrl: stub.url, bridgeBearer: 'test-bearer', model: 'pi/glm-5.2' }, + }, + }, + ) + await drain( + executor.execute('do the task', new AbortController().signal) as AsyncIterable, + ) + + expect(bodies).toHaveLength(1) + expect(bodies[0]?.agent_profile).toMatchObject({ + name: 'isolated-worker', + extensions: { pi: { load: [] } }, + }) + // And no argv channel was invented alongside it. + expect(Object.keys(bodies[0] ?? {})).not.toContain('args') + }) + + it('forwards the seam overlay’s extension controls after the profile merge', async () => { + const bodies: Record[] = [] + const stub = await startBridgeStub(okFrame, { onRequest: (body) => bodies.push(body) }) + server = stub.server + const executor = bridgeExecutor( + { profile: { name: 'worker', prompt: { systemPrompt: 'be exact' } }, harness: null }, + { + signal: new AbortController().signal, + seams: { + bridge: { + bridgeUrl: stub.url, + bridgeBearer: 'test-bearer', + model: 'pi/glm-5.2', + agentProfile: { name: 'worker', extensions: { pi: { load: ['pi-zai-glm'] } } }, + }, + }, + }, + ) + await drain( + executor.execute('do the task', new AbortController().signal) as AsyncIterable, + ) + + // The merge must not drop either side: the spawn profile's prompt AND the overlay's controls. + expect(bodies[0]?.agent_profile).toMatchObject({ + prompt: { systemPrompt: 'be exact' }, + extensions: { pi: { load: ['pi-zai-glm'] } }, + }) + }) +}) diff --git a/src/runtime/supervise/runtime.ts b/src/runtime/supervise/runtime.ts index c041a56d..7526aa49 100644 --- a/src/runtime/supervise/runtime.ts +++ b/src/runtime/supervise/runtime.ts @@ -145,7 +145,24 @@ export interface SandboxSeam { steering?: SandboxSteeringOptions } -/** CLI subprocess seam. `bin` + `args` describe the Halo/RLM process to spawn. */ +/** + * UNMETERED CLI subprocess seam. `bin` + `args` describe the process to spawn. + * + * READ THIS BEFORE CHOOSING `backend: 'cli'`. This backend pipes a prompt to a subprocess's stdin + * and reads its stdout. It has no usage receipt of any kind, so it reports its spend with + * `Spend.tokensKnown: false`: the work is recorded, its `{0,0}` tokens and `$0` are a FLOOR rather + * than a measurement, and a ceiling priced from either is a ceiling that cannot fire. The executor + * is also `budgetExempt: true`, which is why `driveHarnessFromBackend` refuses it outright rather + * than pretending to budget it. + * + * If you need a metered harness worker, use `backend: 'bridge'` (a cli-bridge session, which + * reports the harness's real per-turn tokens and cost) or `backend: 'cli-worktree'` with + * `codexReproducible`. Reach for this seam only when the subprocess genuinely is not an inference + * agent, or when you have accepted that its cost is invisible. + * + * `args` is argv for a LOCAL, in-process spawn under this process's own privileges. It is not a + * remote channel and nothing forwards it over a wire. + */ export interface CliSeam { bin: string args?: string[] @@ -215,6 +232,40 @@ export interface CliWorktreeBridgeSeam { * harness conversation across turns; each turn also receives its own durable run id. * A dropped HTTP reader reattaches to that exact run and explicit cancel is the only * operation allowed to stop it. Omit `sessionId` and the executor mints one per spawn. + * + * ── HOW TO CONTROL WHAT THE HARNESS LOADS (there is no argv field, by design) ── + * + * A worker often needs the harness started in a KNOWN state — no ambient extensions, skills, + * context files, or prompt templates — because ambient state is how a paired experiment silently + * loses its pairing: an installed extension that persists memory across runs carries arm A's state + * into arm B, and nothing reports it. + * + * That is what the `AgentProfile` on this seam (and on the spawn spec) is FOR. `agent_profile` + * rides every request verbatim, and cli-bridge maps it onto each harness's own native controls: + * + * - Materializing any profile at all already starts the harness isolated from ambient + * workspace state — for pi that is `--no-context-files --no-skills --no-prompt-templates`, + * applied to every request that carries an `agent_profile`. + * - `AgentProfile.extensions.` is the named, per-harness control channel. An explicit + * `extensions: { pi: { load: [] } }` disables ambient extension discovery outright + * (pi's `--no-extensions`); listing package names loads exactly those and nothing else. + * - `permissions` / `tools` / `mcp` map onto the harness's native tool and server controls. + * + * A caller therefore does NOT need to hand-roll an `Executor` to isolate a harness run, and the + * profile expressing it stays portable: the same declaration means the same thing on a different + * harness, whereas an argv string means nothing anywhere else. + * + * WHY NOT A GENERAL ARGV PASSTHROUGH. `bridgeUrl` addresses a process-spawning server. Forwarding + * an arbitrary argv array to it would let any caller holding a bearer token choose the flags of a + * process on the bridge host — which for real harness CLIs includes flags that load code from a + * path, read a file into the prompt, redirect the working directory, or turn off the isolation the + * bridge applies. cli-bridge deliberately confines workers (a filesystem jail and deny-by-default + * network egress), and every one of those confinements is expressed as spawn configuration, so an + * argv channel is a channel for unwinding them. It would also break this executor's own contract: + * the durable-run replay protocol, session pinning, and streaming mode are all argv the bridge + * owns, and a caller-supplied duplicate silently wins or corrupts the parse. The structured profile + * channel is validated, per-harness, portable, and refuses controls it does not understand — keep + * new harness capability there. */ export interface BridgeSeam { bridgeUrl: string @@ -278,8 +329,33 @@ function contentRef(prefix: string, value: unknown): string { return `${prefix}:${(h >>> 0).toString(16).padStart(8, '0')}` } -function zeroSpend(): Spend { - return { iterations: 0, tokens: zeroTokenUsage(), usd: 0, ms: 0 } +/** + * The spend of work that HAPPENED and reported no usage receipt. + * + * Not the same value as a plain zero even though both carry `{0,0}` tokens and `$0`. A bare zero + * asserts a MEASUREMENT — "this ran and cost nothing" — and every consumer downstream reads it that + * way: the pool keeps reporting `readout().tokensKnown === true`, the journal totals stay clean, the + * OTEL span records a priced zero, and a caller's token-denominated ceiling can never fire no matter + * how much the work really burned. A ceiling that cannot fire is worse than no ceiling, because it + * reads as protection. + * + * `Spend.tokensKnown` is the marker the substrate already threads end to end for exactly this case + * (`budget.ts`, `otel-spans.ts`, `spawn-journal.ts`, `supervisor.ts`): the work is recorded, the + * zero is labelled a floor rather than a total, and every rollup that touches it reports its balance + * as a ceiling rather than a measurement. Use this — never a bare zero — whenever a runtime cannot + * see what its worker spent. + * + * DELIBERATELY NOT `usdKnown: false`, and this is not an oversight. On the dollar channel that flag + * is not a marker but a REFUSAL: `budget.ts` treats unknown dollars under a dollar-capped root as a + * reconcile violation and fails the child. Applying it here would contradict `budgetExempt`, whose + * whole documented contract is that such a worker settles OUT of the conserved pool rather than + * against it (`scope.ts`) — a worker the kernel already agreed not to budget would start failing + * after its work had burned, which is a policy change about which configurations are allowed, not a + * fix to how honestly spend is reported. The token marker already taints the readout, so no caller + * can read the surviving `usd: 0` as a measured total. + */ +function unmeteredSpend(ms: number): Spend { + return { iterations: 0, tokens: zeroTokenUsage(), tokensKnown: false, usd: 0, ms } } // ── router/inline executor (harness === null) ────────────────────────────────── @@ -956,6 +1032,10 @@ function leafVerdict(result: { winner?: { output?: unknown } }): DefaultVerdict * `budgetExempt: true`: it remains usable as a direct executor, while budgeted supervision * refuses it before process execution because the CLI exposes no usage receipt. teardown is SIGTERM → SIGKILL * with a grace window. Streaming: yields one `iteration` event on clean exit. + * + * Its terminal spend is `unmeteredSpend`, NOT a zero: an unmetered runtime that reports a plain + * `0` is indistinguishable from one that measured zero, and every ceiling downstream then reads + * as enforced while enforcing nothing. */ export const cliExecutor: ExecutorFactory = (_spec, ctx) => { const seam = readSeam(ctx, cliSeamKey, 'cli') @@ -1045,6 +1125,7 @@ interface StreamCliArgs { } async function* streamCliLeaf(args: StreamCliArgs): AsyncIterable { + const started = Date.now() const prompt = taskToPrompt(args.task) const proc = spawn(args.seam.bin, args.seam.args ?? [], { ...(args.seam.cwd ? { cwd: args.seam.cwd } : {}), @@ -1090,8 +1171,13 @@ async function* streamCliLeaf(args: StreamCliArgs): AsyncIterable { ) } const out = { content: chunks.join('') } as unknown - // budgetExempt: spend is recorded zero (not metered) — never a fabricated cost. - args.onArtifact({ outRef: contentRef('cli', out), out, spent: zeroSpend() }) + // A raw subprocess exposes no usage receipt, so its spend is UNKNOWN — not zero. Wall-clock is + // the one thing this runtime did measure, so that is the one field reported as measured. + args.onArtifact({ + outRef: contentRef('cli', out), + out, + spent: unmeteredSpend(Date.now() - started), + }) yield { kind: 'iteration' } } diff --git a/src/runtime/supervise/worktree-cli-executor.ts b/src/runtime/supervise/worktree-cli-executor.ts index e39b2526..8e126a87 100644 --- a/src/runtime/supervise/worktree-cli-executor.ts +++ b/src/runtime/supervise/worktree-cli-executor.ts @@ -12,10 +12,11 @@ * result onto the `Executor` port (artifact + spend) and owns the teardown point. The complete * profile delivery — direct prompt/model plus materialized file-backed resources — lives there. * - * Token accounting: ordinary harness CLI runs remain `budgetExempt`. Reproducible Codex mode + * Token accounting: ordinary harness CLI runs remain `budgetExempt`, and their `Spend` marks + * `tokensKnown: false` — the `{0,0}` is a floor, never a measured-free run. Reproducible Codex mode * parses the CLI's terminal JSONL usage and is metered by default; an absent usage event fails the * run instead of recording fabricated zero tokens. Codex does not report dollar cost, so that - * channel is explicitly marked unknown on the resulting `Spend`. + * channel is explicitly marked unknown on the metered path. * * @experimental */ @@ -195,11 +196,18 @@ export function createWorktreeCliExecutor( 'createWorktreeCliExecutor: metered harness run returned no token usage', ) } + // A budget-exempt run has no usage receipt, so its `{0,0}` is a FLOOR, not a measurement. + // Left unmarked it is byte-identical to a run that truly cost nothing, and a token-priced + // ceiling over this leaf then reads as enforced while enforcing nothing. `tokensKnown` + // is the marker; the dollar channel is left alone because `usdKnown: false` under a + // dollar-capped root is a reconcile REFUSAL in `budget.ts`, which would contradict the + // exemption this leaf was granted (see `unmeteredSpend` in ./runtime). const spent: Spend = { iterations: 1, tokens: usage ? { input: usage.inputTokens, output: usage.outputTokens } : { input: 0, output: 0 }, + ...(usage ? {} : { tokensKnown: false }), usd: 0, ...(usage ? { usdKnown: false } : {}), ms: Date.now() - started, diff --git a/tests/runtime/cli-executor-metering.test.ts b/tests/runtime/cli-executor-metering.test.ts new file mode 100644 index 00000000..66bef446 --- /dev/null +++ b/tests/runtime/cli-executor-metering.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from 'vitest' +import { createBudgetPool } from '../../src/runtime/supervise/budget' +import { createExecutor } from '../../src/runtime/supervise/runtime' +import type { AgentSpec, ExecutorContext, UsageEvent } from '../../src/runtime/supervise/types' + +const spec: AgentSpec = { profile: { name: 'raw-cli-worker' }, harness: null } +const context: ExecutorContext = { signal: new AbortController().signal, seams: {} } + +async function drain(stream: AsyncIterable): Promise { + const events: UsageEvent[] = [] + for await (const event of stream) events.push(event) + return events +} + +/** + * The `cli` backend spawns a subprocess and reads its stdout. It sees no usage receipt, so the + * only honest report is "this work happened and its cost is unknown" — NOT `{0,0} $0`. + * + * The distinction is the whole point. A plain zero is a MEASUREMENT, and every consumer treats it + * as one: the pool keeps reporting `tokensKnown: true`, so a caller enforcing a token-priced + * ceiling over this backend holds a ceiling that can never fire while believing it is protected. + * `Spend.tokensKnown` is the marker that separates the two cases, and the pool already propagates + * it into its readout. + */ +describe('cli backend reports unmetered work as unknown, never as measured zero', () => { + it('marks the token channel unknown on the terminal artifact', async () => { + const executor = createExecutor({ backend: 'cli', bin: 'cat' })(spec, context) + await drain( + executor.execute('hello', new AbortController().signal) as AsyncIterable, + ) + + const { spent } = executor.resultArtifact() + expect(spent.tokens).toEqual({ input: 0, output: 0 }) + expect(spent.usd).toBe(0) + // The load-bearing assertion: a bare zero here is the trap, not the fix. + expect(spent.tokensKnown).toBe(false) + // Wall-clock IS measured by this runtime, so it is not marked unknown. + expect(spent.ms).toBeGreaterThanOrEqual(0) + await executor.teardown('brutalKill') + }) + + it('taints a budget pool’s readout so a token ceiling cannot read as enforced', async () => { + const pool = createBudgetPool({ maxIterations: 4, maxTokens: 1_000 }) + const reservation = pool.reserve({ maxIterations: 1, maxTokens: 100 }) + if (!reservation.ok) throw new Error(`reservation rejected: ${reservation.reason}`) + const executor = createExecutor({ backend: 'cli', bin: 'cat' })(spec, context) + await drain( + executor.execute('hello', new AbortController().signal) as AsyncIterable, + ) + pool.reconcile(reservation.ticket, executor.resultArtifact().spent) + + // The balance is now a CEILING on what might remain, not a measurement of what does. + expect(pool.readout().tokensKnown).toBe(false) + await executor.teardown('brutalKill') + }) + + it('does NOT mark the dollar channel, which would refuse the exemption it was granted', async () => { + // A deliberate boundary, pinned so it reads as a decision rather than a missed field. + // `usdKnown: false` is not a marker on this channel but a REFUSAL: under a dollar-capped root + // `budget.ts` treats it as a reconcile violation and fails the child. `backend: 'cli'` is + // `budgetExempt`, i.e. the kernel already agreed to settle it OUT of the conserved pool, so + // marking it would make an explicitly-exempt worker fail after its work had already burned. + // Changing that is a policy decision about allowed configurations, not a reporting fix. + const pool = createBudgetPool({ maxIterations: 4, maxTokens: 1_000, maxUsd: 5 }) + const reservation = pool.reserve({ maxIterations: 1, maxTokens: 100, maxUsd: 1 }) + if (!reservation.ok) throw new Error(`reservation rejected: ${reservation.reason}`) + const executor = createExecutor({ backend: 'cli', bin: 'cat' })(spec, context) + await drain( + executor.execute('hello', new AbortController().signal) as AsyncIterable, + ) + + const { spent } = executor.resultArtifact() + expect(spent.usdKnown).toBeUndefined() + expect(() => pool.reconcile(reservation.ticket, spent)).not.toThrow() + // The token taint still reaches the readout, so the accounting is not silently trusted. + expect(pool.readout().tokensKnown).toBe(false) + await executor.teardown('brutalKill') + }) +}) diff --git a/tests/runtime/worktree-cli-executor.test.ts b/tests/runtime/worktree-cli-executor.test.ts index 994937f1..08c20b76 100644 --- a/tests/runtime/worktree-cli-executor.test.ts +++ b/tests/runtime/worktree-cli-executor.test.ts @@ -267,6 +267,37 @@ describe('createWorktreeCliExecutor', () => { expect(exec.budgetExempt).toBe(true) }) + it('a budgetExempt run reports its tokens as UNKNOWN, not as a measured zero', async () => { + // The trap this closes: `{ tokens: {0,0} }` with no unknown marker is what a run that truly + // cost nothing looks like, so a caller enforcing a token-priced ceiling over this leaf holds + // a ceiling that can never fire while believing it is protected. + const exec = createWorktreeCliExecutor({ + repoRoot: '/workspace', + profile: authoredProfile, + harness: 'claude-code', + taskPrompt: 'x', + runGit: makeFakeGit(freshGitState()), + runHarness: vi.fn(async () => ({ + exitCode: 0, + stdout: 'done', + stderr: '', + killedBySignal: null, + durationMs: 3, + timedOut: false, + })), + }) + + const result = await exec.execute(undefined, new AbortController().signal) + expect(result.spent).toMatchObject({ + tokens: { input: 0, output: 0 }, + tokensKnown: false, + usd: 0, + }) + // The dollar channel is deliberately NOT marked here: `usdKnown: false` under a dollar-capped + // root is a reconcile refusal in `budget.ts`, which would contradict this leaf's exemption. + expect(result.spent.usdKnown).toBeUndefined() + }) + it('rejects caller read-denial paths outside reproducible Codex mode', () => { expect(() => createWorktreeCliExecutor({ From 4832fcd83d288b5d68732b24518930a162764145 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Sat, 1 Aug 2026 19:30:21 -0600 Subject: [PATCH 2/3] docs(api): regenerate the API reference for the seam docs this branch changed The docs freshness gate regenerates `docs/api` from TSDoc and fails when the committed reference differs. `CliSeam` gaining its UNMETERED warning and `BridgeSeam` gaining the harness-control channel both change generated output, so the reference moves with them. --- docs/api/primitive-catalog.md | 2 +- docs/api/runtime.md | 51 ++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/docs/api/primitive-catalog.md b/docs/api/primitive-catalog.md index a360d70a..90659cba 100644 --- a/docs/api/primitive-catalog.md +++ b/docs/api/primitive-catalog.md @@ -780,7 +780,7 @@ Import from `@tangle-network/agent-runtime/kernel` — 688 exports. | `CheckRunner` | interface | Executes the frozen checks against one candidate. Implementations MUST fail loud | | `CheckSource` | interface | Produces the task's visible checks. MUST derive them from agent-visible information | | `CheckSourceCtx` | interface | What a CheckSource composes with. `consult` is the strategy family's raw analyst | -| `CliSeam` | interface | CLI subprocess seam. `bin` + `args` describe the Halo/RLM process to spawn. | +| `CliSeam` | interface | UNMETERED CLI subprocess seam. `bin` + `args` describe the process to spawn. | | `CliWorktreeSeam` | interface | cli-worktree seam. A supervisor-authored `AgentProfile` driving a local coding-harness CLI | | `CollectedAgentTurn` | interface | A drained turn: the terminal summary plus every event the stream yielded. | | `CompletionAnalyst` | interface | Reads a node's trace → a completion verdict. Same input shape as the `analyze` hook, so | diff --git a/docs/api/runtime.md b/docs/api/runtime.md index fa102fdb..6a6aa23e 100644 --- a/docs/api/runtime.md +++ b/docs/api/runtime.md @@ -11499,7 +11499,22 @@ which is a different resource profile from a fire-and-forget shot. ### CliSeam -CLI subprocess seam. `bin` + `args` describe the Halo/RLM process to spawn. +UNMETERED CLI subprocess seam. `bin` + `args` describe the process to spawn. + +READ THIS BEFORE CHOOSING `backend: 'cli'`. This backend pipes a prompt to a subprocess's stdin +and reads its stdout. It has no usage receipt of any kind, so it reports its spend with +`Spend.tokensKnown: false`: the work is recorded, its `{0,0}` tokens and `$0` are a FLOOR rather +than a measurement, and a ceiling priced from either is a ceiling that cannot fire. The executor +is also `budgetExempt: true`, which is why `driveHarnessFromBackend` refuses it outright rather +than pretending to budget it. + +If you need a metered harness worker, use `backend: 'bridge'` (a cli-bridge session, which +reports the harness's real per-turn tokens and cost) or `backend: 'cli-worktree'` with +`codexReproducible`. Reach for this seam only when the subprocess genuinely is not an inference +agent, or when you have accepted that its cost is invisible. + +`args` is argv for a LOCAL, in-process spawn under this process's own privileges. It is not a +remote channel and nothing forwards it over a wire. #### Properties @@ -11670,6 +11685,40 @@ harness conversation across turns; each turn also receives its own durable run i A dropped HTTP reader reattaches to that exact run and explicit cancel is the only operation allowed to stop it. Omit `sessionId` and the executor mints one per spawn. +── HOW TO CONTROL WHAT THE HARNESS LOADS (there is no argv field, by design) ── + +A worker often needs the harness started in a KNOWN state — no ambient extensions, skills, +context files, or prompt templates — because ambient state is how a paired experiment silently +loses its pairing: an installed extension that persists memory across runs carries arm A's state +into arm B, and nothing reports it. + +That is what the `AgentProfile` on this seam (and on the spawn spec) is FOR. `agent_profile` +rides every request verbatim, and cli-bridge maps it onto each harness's own native controls: + + - Materializing any profile at all already starts the harness isolated from ambient + workspace state — for pi that is `--no-context-files --no-skills --no-prompt-templates`, + applied to every request that carries an `agent_profile`. + - `AgentProfile.extensions.` is the named, per-harness control channel. An explicit + `extensions: { pi: { load: [] } }` disables ambient extension discovery outright + (pi's `--no-extensions`); listing package names loads exactly those and nothing else. + - `permissions` / `tools` / `mcp` map onto the harness's native tool and server controls. + +A caller therefore does NOT need to hand-roll an `Executor` to isolate a harness run, and the +profile expressing it stays portable: the same declaration means the same thing on a different +harness, whereas an argv string means nothing anywhere else. + +WHY NOT A GENERAL ARGV PASSTHROUGH. `bridgeUrl` addresses a process-spawning server. Forwarding +an arbitrary argv array to it would let any caller holding a bearer token choose the flags of a +process on the bridge host — which for real harness CLIs includes flags that load code from a +path, read a file into the prompt, redirect the working directory, or turn off the isolation the +bridge applies. cli-bridge deliberately confines workers (a filesystem jail and deny-by-default +network egress), and every one of those confinements is expressed as spawn configuration, so an +argv channel is a channel for unwinding them. It would also break this executor's own contract: +the durable-run replay protocol, session pinning, and streaming mode are all argv the bridge +owns, and a caller-supplied duplicate silently wins or corrupts the parse. The structured profile +channel is validated, per-harness, portable, and refuses controls it does not understand — keep +new harness capability there. + #### Properties ##### bridgeUrl From ae1506334983fbc822e7c56d2c16d122d277c254 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Sun, 2 Aug 2026 04:26:49 -0600 Subject: [PATCH 3/3] docs(runtime): state unmetered dollar limitation --- src/runtime/supervise/runtime.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/runtime/supervise/runtime.ts b/src/runtime/supervise/runtime.ts index 7526aa49..0643f16c 100644 --- a/src/runtime/supervise/runtime.ts +++ b/src/runtime/supervise/runtime.ts @@ -351,8 +351,9 @@ function contentRef(prefix: string, value: unknown): string { * whole documented contract is that such a worker settles OUT of the conserved pool rather than * against it (`scope.ts`) — a worker the kernel already agreed not to budget would start failing * after its work had burned, which is a policy change about which configurations are allowed, not a - * fix to how honestly spend is reported. The token marker already taints the readout, so no caller - * can read the surviving `usd: 0` as a measured total. + * fix to how honestly spend is reported. The token marker taints only token accounting. Under the + * current `budgetExempt` policy the surviving `usd: 0` remains dollar-known; callers that require + * dollar accounting must use a backend that returns priced usage. */ function unmeteredSpend(ms: number): Spend { return { iterations: 0, tokens: zeroTokenUsage(), tokensKnown: false, usd: 0, ms }