diff --git a/CLAUDE.md b/CLAUDE.md index 05d7024..12a9334 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,8 +37,8 @@ reviewed in `docs/implementer-log.md` and checked on straylight (spec 7); no fin M5d-2 (heartbeat, cron, ToolSummary, live-slot isolation) is done and on master, reviewed in `docs/implementer-log.md`; finding 1 closed by task 11; spec 7 device-checked on straylight (heartbeat on slot 2 left a following slot-0 turn's `cache_n` at 1398 of 1399; `jobs/tier.toml` -promoted only a trusted note). M6 (subagents and cloud consult) is proposed in -`docs/specs/2026-10-04-m6-subagents-consult.md`, awaiting the owner's answers in section 11. +promoted only a trusted note). M6 (subagents and cloud consult) is approved +(`docs/specs/2026-10-04-m6-subagents-consult.md`); plans not yet written. P22 (proposed, not applied) makes Boxmaker a fleet of independent agents, one per host, deployed with Nix in a last milestone, M8; `flake.nix` builds every binary, the tool image and a dev shell. Work happens on titan (macOS, no Nix); build and run diff --git a/docs/specs/2026-10-04-m6-subagents-consult.md b/docs/specs/2026-10-04-m6-subagents-consult.md index c6c81b4..bd8303e 100644 --- a/docs/specs/2026-10-04-m6-subagents-consult.md +++ b/docs/specs/2026-10-04-m6-subagents-consult.md @@ -1,7 +1,7 @@ # M6 design: subagents and cloud consult -Status: proposed. Section 11 records the owner's 2026-10-04 answers; questions 4 and 6 are -still open. The implementer is Ornith (through `tools/run-plan.sh`), as for M5d. +Status: approved by the owner, 2026-10-04, with section 11's answers. The implementer is Ornith +(through `tools/run-plan.sh`), as for M5d. It rests on the brief (`docs/design.md`, Inference contract 5 and 6, Authority 6, Cloud consult), on M5d's scheduler (`docs/specs/2026-10-03-m5d-scheduler.md`, out of scope: subagents join that @@ -164,8 +164,9 @@ brief means the owner should not expect a heartbeat-speed reply, not a second ch ### 4.4 Egress -A new `docs/egress.md` row: run time, `brokerd`, the configured consult URL, the allowlisted -JSON body. The secret is `bxcore::secrets`, same store as Mattermost and Honcho. +M6 does not send consult traffic off the host. The device check's dummy is loopback. A live +URL later is a new `docs/egress.md` row, a grant, and a `bxcore::secrets` key, the same store +as Mattermost and Honcho. ## 5. Config @@ -229,9 +230,10 @@ Given tests, first, against the fake llama server and a fake `brokerd`: - M6a: a `bxctl` session calls `subagent` with a short task; the child log is on slot 2; the parent's next turn has `cache_n` near its previous total. -- M6b: against a private OpenAI-compatible stub (or the configured consult URL if the owner - names one): a clean session's payload in the audit log matches a captured request; a session - that first recalled an untrusted note is refused. +- M6b: against a loopback OpenAI-compatible dummy on straylight (the check starts it; it is + not a standing service): a clean session's payload in the audit log matches a captured + request; a session that first recalled an untrusted note is refused. No internet consult in + this milestone. ## 8. Runbook @@ -253,13 +255,14 @@ New entries, each with a pointer from the message: ## 10. Changes to the brief and decisions -With this spec, as their own commits, once 4 and 6 are settled: +With this spec, as their own commits when the plans are written: - `docs/decisions.md` open row "cloud-led sessions": not in M6; later, leaning only at `public` taint. Leave the row open until that later design. -- `docs/egress.md`: the consult row in section 4.4, once the URL is chosen. -- Inference contract 5 already says subagents share the background slot; no wording change - unless question 6 puts heartbeat on that slot ahead of a waiting live child. +- `docs/egress.md`: no internet consult row in M6. The M6b device check talks to a loopback + dummy. A live URL later is its own row, grant, and secret. +- Inference contract 5 already says subagents share the background slot. Queue order is + section 3.3; no brief wording change. ## 11. The owner's answers (2026-10-04) @@ -271,53 +274,10 @@ With this spec, as their own commits, once 4 and 6 are settled: 3. **Who waits.** The parent (or middle child) stays in the tool call. Completion arrives as a `ToolResult`: session id, completed/failed, and on success the capped summary. Same for `consult`. No new turn protocol. -4. **Consult URL.** Open; discussion below. -5. **Name.** `subagent`. No reason to pick `delegate`. -6. **Queue.** Open; discussion below. - -### Consult URL (question 4) - -Three shapes, and they can combine. - -- **Given tests** always hit a fake HTTP server in-process. That does not need a network or a - secret. -- **Device check, stub.** A private OpenAI-compatible listener on straylight (loopback, one - shot) so M6b proves audit-body == wire bytes without sending owner text off the host. Enough - to close the milestone's "payload log matches what was sent". -- **Device check, live.** A real `[consult] url` and secret on straylight. The brief's - candidate is GLM-5.3-Flash over Z.ai pay-as-you-go (Singapore entity; they state they do not - store API content). That is the first new internet egress since `http_fetch`: it needs a - grant, an `egress.md` row, and a secret in the store. It also means the device check sends a - planted `question` to that host. - -Recommendation: stub for the M6b device check (the audit match is a property of `brokerd`, not -of GLM). Add a live URL later, when you want Ornith to actually consult, as config and a grant, -without another milestone. A live check can still be a follow-up task if you want the first -real call witnessed. - -If we do live in M6b, the planted question should be boring and public (`What is 2+2, one -word.`), and the grant `max_taint = "public"` so a private session cannot use it until you -raise that on purpose. - -### Heartbeat vs subagent (question 6) - -M5d's order on a contended background slot is compaction, then cron, then heartbeat, then a -tool-output summary. A subagent serving a **held live parent** is the owner's current turn -waiting on the slot. Heartbeat is the pulse for when the owner is not talking. - -Heartbeat-first would mean a Mattermost turn that called `subagent` waits out a heartbeat -(often several tool calls; the M5d-2 check saw ~20 s of `recall`s) and, with `last_heartbeat` -stamped at tick start, a long pulse can come due again immediately. The live user pays that. - -Subagent-first (the spec's current list) delays heartbeat until the child finishes. That is -the same as today when a compaction summary or a cron run holds the slot: heartbeat skips or -waits. Heartbeat is already written to skip when its own session is busy. - -Cron is owner-scheduled and can be a long tiering turn. Putting cron ahead of a held live -child has the same stall. Compaction in flight should stay first: it is already holding a -session the way a child is, and it unblocks that session's next live turn. - -Recommendation: keep compaction first, then a subagent whose parent is held, then cron, then -heartbeat, then tool-output summaries. Heartbeat should not take precedence over a child that -a live turn is waiting on. It should still beat a child whose parent is **not** held, if we -ever queue one; under answer 3 we do not, because the parent stays in the tool call. +4. **Consult URL.** Given tests use an in-process fake. The M6b device check uses a loopback + OpenAI-compatible dummy on straylight (the check starts it; it need not be a standing + service). No live vendor in this milestone. A real URL is config and a grant later. +5. **Name.** `subagent`. +6. **Queue.** Heartbeat does not take precedence over a child a live turn is waiting on. + Compaction in flight, then that child, then cron, then heartbeat, then a tool-output + summary (section 3.3).