Skip to content

acts[].ok is documented in two supervision legends and absent from the payload; delegation budgets can enter 'exhausted' with no exit #594

Description

@serge-ivo

Two supervision legends tell the model to read acts[].ok; the payload has never carried it. And a budget can enter a state it cannot leave.

1. acts[].ok is promised twice and delivered never

subordinate-payload.ts:270 (subordinate_status) and connectors/supervision.ts:756 (check_delegation) both state:

An act with ok: false FAILED and one with ok: null was not observed to succeed.

ActItem (lib/instance-work.ts:60-73) declares instanceId, kind, summary, command, irreversible, traceId, atno ok. toActItem (:281-289) never reads it, though engine-acts.ts:595 writes it into agent_events.context. The payload map (supervision.ts:457) spreads that same object.

The outcome does survive inside summarydescribeEngineAct appends " — FAILED" / " — outcome not observed" (engine-acts.ts:527) — so no information is lost. But a model told to check a key that is absent reads absence as "fine", which inverts the intended default for an unobserved act.

2. delegation_budgets: 'closed' has no writer and 'exhausted' has no exit

grep -rn "closeBudget\|raiseBudget" workers store packages docs platform-docs11 hits, all in .test.ts, plus the two definitions (delegation-budget-store.ts:421,442).

  • raiseBudget is the only path out of exhausted — reachable from no route. delegation-budget.test.ts:171 already records it as "reachable from no route".
  • closeBudget is the only writer of 'closed' — so that state is unreachable.

markExhausted's docblock (:394-395) says "the work is intact and resumable once a human raises the limit" — describing a path with no caller.

Worse, the refusal a user actually sees names a state the system cannot enter (delegation-budget-store.ts:356):

if (current.status !== "open") return { ok: false, reason: "closed", message: "This run's budget is already closed." }

It is reached only when the status is exhausted. So a delegated run that hits its budget stops with a message describing the wrong state, and there is no supported way to resume it.

Why this misleads into a wrong action

Both are the same shape: a contract stated to the reader that the code does not honour. The first makes a supervisor treat unobserved acts as successful; the second tells an owner their budget is "closed" when it is exhausted-and-recoverable-in-principle, with no way to recover it.

Acceptance criteria

  1. ActItem carries ok, or both legends stop naming it. Prefer carrying it — engine-acts.ts:595 already writes it and summary already encodes it, so the two would agree.
  2. exhausted gains a supported exit, or markExhausted's docblock stops promising one.
  3. The refusal message names the state the budget is actually in.
  4. 'closed' gains a writer or is removed from the declared values.
  5. A guard asserts every field named in a tool description exists in that tool's payload type, denominator stated per ADR 0002 over all descriptions — this is the same class as list_instance_tools and the published docs still tell operators to audit external reach by filtering on connector — the proxy #584 disproved #585 (prose recommending a field the code had replaced) and warrants one check rather than three fixes.

Verified vs inferred

  • Verified: both legend strings; ActItem's declaration and toActItem; the grep with its count; all four delegation-budget-store.ts line references.
  • Inferred: that no caller exists outside the swept paths — the grep covered workers, store, packages, docs, platform-docs. A caller outside those trees would falsify it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2: correctnessReal defect, no live harm today — inert fields, miscounts, missing guardsbackendBackend / Worker / API workbugSomething isn't workingcoordinationAgent coordination / delegation primitivesobservabilityA displayed value the code cannot produce, or that means something other than its label

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions