sub-agents: a spawn cannot take the last of a window

The failure this prevents is specific — a sub-agent burns the end of a limit window and the session the
owner is actually sitting in dies mid-turn, because the credits went somewhere nobody was watching. So
it is a condition on spawning rather than a refinement: an endpoint that can start unattended work
without checking is wrong from its first call, and the symptom lands on someone else's conversation.

Wired into POST /command-hq/commanders/new, which — this is the finding — already IS the spawn
mechanism the sub-agents item was asking for. It starts a turn with ws: null, waits only for
session:claude, and returns the transcript id, with command_hq_sessions naming, listing and costing it.
So "commander or something lighter" answers itself: a commander costs nothing extra and a lighter thing
would need its own registry to be visible at all. What is still missing is only the agent-callable
surface.

Refuses rather than queues or downgrades. Queueing means holding work against a window Officer does not
control, and the reset it would wait for is a claim about a shared pool — every claude on the machine
spends these, including terminals and pipelines. A refusal naming the window, the percentage and the
reset is something a caller can act on now.

The floor is 15% and the reasoning is in the file: the interactive failure is not "cannot start a chat",
it is "a turn already in flight is cut off", and a turn costs a fraction of a window.

Fails OPEN, deliberately. The numbers come from an external endpoint with a credential Officer does not
own, so a blip or a signed-out CLI must not stop every spawn on the machine with an error about usage
that has nothing to do with usage. The problem is reported so a caller can say "started, but unchecked".

Seven tests on the pure rule — tightest window decides rather than the average, no limits allowed rather
than blocked, exactly-at-the-ceiling refuses. Verified against the live account, where it correctly
refuses right now at Session 90%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 06:42:27 +00:00
co-authored by Claude Opus 5
parent 4c26e13535
commit ff7358188d
6 changed files with 216 additions and 3 deletions
@@ -27,3 +27,19 @@ what Officer fixes.
**Bounded by the budget rule in `sub-agents/spawning-must-respect-the-account-s-own`, which is not
optional.** An endpoint that spawns freely is exactly how a weekly limit gets spent by something nobody
was watching.
---
**2026-08-21, narrowing this considerably.** The endpoint largely exists:
`POST /api/command-hq/commanders/new` takes a prompt, starts a turn with `ws: null`, waits only for
`session:claude` rather than the whole turn, and returns Claude's own transcript id. A commander is named,
listed and costed by `command_hq_sessions` already.
So the open question "commander or something lighter" answers itself from the code: making a spawned agent
a commander costs nothing and gets all three for free, where a lighter thing needs its own registry to
become visible at all. **The budget condition is now on that route**, so anything spawned through it
already respects the window.
What is genuinely left is the SURFACE an agent can call — MCP tool or authenticated route — and how output
returns to the caller. That is the remaining work on this item; the mechanism underneath it is done.
@@ -2,7 +2,7 @@
id: sub-agents/spawning-must-respect-the-account-s-own
title: "Spawning must respect the account's own session and weekly limits"
theme: sub-agents
status: open
status: done
created: 2026-08-21
date_source: stated
order: 2
@@ -30,3 +30,28 @@ Two facts from that file that shape this, both stated in its own header:
Open: what the endpoint does when the room is thin — refuse, queue until the window resets (the reset
time is in the same payload), or downgrade the model; and whether the reserve is a fixed floor or a
share the owner sets.
---
**Built 2026-08-21.** `api/sub-agents/budget.ts`, wired into `POST /command-hq/commanders/new` — the one
door that starts unattended work.
The open questions, answered:
- **Refuse, not queue or downgrade.** Queueing means holding work against a window Officer does not
control, and the reset time it would wait for is a claim about a shared pool. Refusing with the window
name, the percentage and the reset is something the caller can act on immediately.
- **A fixed floor, 15%.** The interactive failure is not "cannot start a new chat", it is "a turn already
in flight is cut off" — and a turn costs a small fraction of a window, so the reserve only has to cover
what might be running. Adjustable per call; the default lives in one constant.
**It fails open**, deliberately: the numbers come from an external endpoint with a credential Officer does
not own, so a blip or a signed-out CLI must not stop every spawn on the machine with an error about usage
that has nothing to do with usage. The problem is reported rather than swallowed.
Seven tests on the pure rule — the tightest window decides rather than the average, no limits is allowed
rather than blocked, and the refusal names something actionable. Verified live: it refused at Session 90%.
**Still true and unsolved:** this cannot answer "how much has THIS user left". `claude-usage.ts` takes no
identity, structurally, so a per-account budget needs something that does not exist yet.