Files
boxmaker/docs/plans/M6b/README.md
T
kyleandGrok 4.6 50e9cc2037 docs: M6b implementation plan
Six closed tasks for consult: registry, CONSULT_WAIT, [consult]/[secrets],
ToolName and grants, Decision body, loopback HTTP. Reference is the git bundle
~/boxmaker-refs/m6b-ref.bundle at 5fb05f0. Gate after task 06 is 1611.

Co-Authored-By: Grok 4.6 <noreply@x.ai>
2026-10-03 21:39:31 -07:00

6.7 KiB

M6b implementation plan: consult

For the implementer: each task file is one closed task for one fresh session. Read AGENTS.md, then the task file, and do exactly that task. The driver is tools/run-plan.sh.

Goal: a consult tool, not core, decided by brokerd. The grant's max_taint is the class gate. brokerd POSTs an allowlisted chat-completions body (question only, no history, no tools) over loopback HTTP/1.1. Every decision records that JSON body, including a denial. No vendor names in code.

Architecture: loopd registers consult after subagent and Ports it to brokerd. timeout_for("consult") is 180 s or [broker] timeout_ms, whichever is longer. brokerd loads [consult] and the named bxcore::secrets key at start. No [consult] denies with consult-not-configured. The HTTP client is not a container and not http_fetch.

Tech stack: Rust 1.98.1, edition 2024; no new crate. HTTPS is later.

Spec: docs/specs/2026-10-04-m6-subagents-consult.md, sections 4, 5, 6, 7 (M6b tests), and 8 (consult-not-configured, consult-secret-unreadable).

The owner's decisions (2026-10-04)

  1. Given tests use an in-process fake. The device check starts a loopback dummy on straylight. No live vendor in M6.
  2. Cloud-led sessions are later, not in M6. consult stays text-in, text-out, no tools.

What the code already has, and what it lacks

  • subagent is in m5c(), not core. dispatch Ports every other non-core name to brokerd. There is no consult tool.
  • BrokerPort::timeout_for special-cases only ask_memory. [broker] timeout_ms defaults to 120_000; do not change that default.
  • brokerd.toml has no [consult] and no [secrets]. brokerd does not enable bxcore's secrets feature.
  • ToolName has no Consult. Grant.untrusted already defaults true.
  • AuditEvent::Decision has no body.

Global constraints

  • Roles depend on bxcore only. No unsafe, no async runtime, no new crate.
  • No file the implementer reads holds a chat-template tag whole (T33): build tags from pieces (concat!("<", "think", ">")).
  • Every path a given test creates is removed by a guard (T44); no test depends on timing (T27).
  • Fail-closed states consult-not-configured and consult-secret-unreadable have runbook entries, added by the task that adds the pointer (T49).
  • Each task gives its baseline and its gate count (T50, T53); the branch line is a check, never git switch (T51).
  • Cargo.lock is in the git add line (T24). Gate timeout 600000 ms (T40).
  • docs/implementer-log.md: at log-and-commit, read only the first 12 lines (T62).
  • Do not edit existing given tests the task does not list (broker_port_ask_wait.rs, crates/loopd/tests/config.rs, crates/brokerd/tests/grants.rs rule_3, crates/brokerd/tests/config.rs).

Tasks

Every task below has been written to be replayed from its text. The gate count is the passed total of cargo test --workspace plus one, for the ignored end_to_end test the gate runs on its own. Before task 01 it is 1590 (straylight, rustc 1.98.1 / clippy 0.1.98; M6a).

# Delivers Crates Gate after Tests added Mutations tried, caught
01 consult in m5c(), not core; epoch tools array unchanged loopd 1592 2 1, 1
02 CONSULT_WAIT 180 s via timeout_for; default [broker] timeout_ms stays 120_000 loopd 1593 1 1, 1
03 [consult] + [secrets]; start fails closed; runbook both consult entries brokerd 1602 9 2, 2
04 ToolName::Consult; parse; grants covers as Some(None); no paths/hosts brokerd 1606 4 2, 2
05 Decision body; not-configured and TaintTooHigh record the body bxcore, brokerd 1609 3 2, 2
06 Fake HTTP 200; audit body equals posted JSON; tool result is the answer brokerd 1611 2 2, 2

Mutations (reference, one line each, restored after): core: true fails both task-01 tests; dropping the consult arm of timeout_for fails consult_waits_one_hundred_and_eighty_seconds and leaves the_wait_for_each_tool green; skipping parse_loopback_http in validate fails a_non_loopback_url_is_invalid; skipping the 0.6 check in de_temperature_milli fails temperature_above_zero_point_six_is_invalid; omitting Consult from parse fails consult_parses_and_is_not_a_container_tool; omitting the paths arm fails consult_does_not_take_paths_or_hosts; omitting decide_forced when [consult] is absent fails no_consult_table_is_denied_with_the_runbook_and_records_the_body; omitting body on the event fails both deny tests that assert the body; skipping neutralise fails the &lt; assert in an_allowed_consult_posts_only_the_question_and_returns_the_answer; posting a second message in wire_body_parts fails the_consult_body_is_only_the_question.

After task 06, the design model runs the M6b device check (spec 7) on straylight: a loopback OpenAI-compatible dummy the check starts; a clean session's audit body matches a captured request; a session that first recalled an untrusted note is refused. Slot 1 is left busy. No internet consult.

Review Focus

  • consult is not in the epoch tools array (task 01).
  • [broker] timeout_ms still defaults to 120_000 (task 02).
  • URL is loopback http:// only (task 03). Temperature ≤ 0.6.
  • A consult grant with paths or hosts is invalid; unknown tool names still fail with the four-tool sentence (task 04; do not edit rule_3).
  • A denial still records the body that would have been sent (task 05).
  • The posted bytes equal the audit body; messages has one user item, no tools (task 06). The answer is tag-neutralised.

Known limits

  • No internet consult in M6. A live URL later is its own egress row, grant, and secret.
  • No HTTPS in M6. The client is HTTP/1.1 to 127.0.0.1 or localhost.
  • Cloud-led sessions are not in M6.

Before hand-over

Work through docs/plans/CHECKLIST.md with a reference in a scratch worktree with its own build directory, replayed from its task text; mutations; /tmp compared around a gate run; the reference moved off this machine as a bundle before the run (T18). Task files name their log row (T47).

Running it

The reference (m6b-ref at 5fb05f0, tasks 01 to 06) is a git bundle ~/boxmaker-refs/m6b-ref.bundle. Copy it to straylight and delete the local worktree (/home/kyle/boxmaker-refs/m6b-ref) and branch before the run (T18). Then, from the repository root, with the OpenCode TUI closed, after M6a has landed on master:

git switch -c m6b master
OPENCODE_MODEL=llama.cpp/ornith-1.5-35b-a3b RESUME=1 \
  SLOTS_URL='http://straylight:11434/slots?model=ornith-1.5-35b-a3b' \
  tools/run-plan.sh docs/plans/M6b 01

After task 06, the gate count is 1611. Then the device check above.