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>
This commit is contained in:
2026-10-03 21:39:31 -07:00
co-authored by Grok 4.6
parent 09580c705a
commit 50e9cc2037
15 changed files with 2887 additions and 0 deletions
+205
View File
@@ -0,0 +1,205 @@
# M6b task 02: `CONSULT_WAIT` through `timeout_for`
**Branch:** `m6b`. Check it: `git branch --show-current` prints `m6b` and `git status --short`
is empty; otherwise stop. Never switch branch.
**Commit subject:** `loopd: consult waits one hundred and eighty seconds`
**Log row:** the Task cell is `M6b/02-loopd-consult-wait`.
**First action.** Run this before any Read of crate source. Do not `ls` `crates/loopd/tests/`.
Do not Read `tests/support/`, `config.rs`, `docs/implementer-log.md` (except the first 12
lines at the log step). Do not edit `crates/loopd/tests/broker_port_ask_wait.rs` or
`crates/loopd/src/config.rs`. The quoted blocks are the only edits.
```sh
cp docs/plans/M6b/files/crates/loopd/tests/broker_port_consult_wait.rs crates/loopd/tests/
```
## Goal
`BrokerPort::timeout_for("consult")` is 180 s, or `[broker] timeout_ms` if that is longer
(spec 4.3). The default `[broker] timeout_ms` stays 120_000. `ask_memory` is unchanged.
No test sleeps (T27). This task does not talk to `brokerd`.
## Every place that changes
| Place | What happens there |
|---|---|
| `crates/loopd/src/broker_port.rs` | `CONSULT_WAIT`; `consult_timeout` on `new` and `with_log`; `timeout_for` arm |
| `crates/loopd/tests/broker_port_consult_wait.rs` | new, given |
| everything else | **no change**: not `config.rs`, not `broker_port_ask_wait.rs` |
## Files
- Copy, from `docs/plans/M6b/files/` (the same path under it): the new test
`crates/loopd/tests/broker_port_consult_wait.rs`.
- Modify: `crates/loopd/src/broker_port.rs`, `docs/implementer-log.md`.
- Do not touch: any other file. Do not edit `crates/loopd/tests/broker_port_ask_wait.rs`
(`the_wait_for_each_tool` does not list `consult`; leaving it unedited is required).
Do not change the default `timeout_ms: 120_000` in `crates/loopd/src/config.rs`.
## Facts you need
- **Floor, not a replacement.** `timeout_for("consult")` is
`self.timeout.max(self.consult_timeout)`. A configured 300 s wait stays 300 s.
- **Both constructors.** `new` and `with_log` each name `consult_timeout: CONSULT_WAIT`.
`with_ask_wait` and `with_pending_cap` use `..self` and need no edit.
- **`ASK_MEMORY_WAIT` stays.** Do not change its value or its arm.
- The items the edits use. This table is complete.
| Item | Signature |
|---|---|
| `BrokerPort::new` | `new(socket: PathBuf, timeout: Duration) -> BrokerPort` |
| `BrokerPort::with_log` | `with_log(socket: PathBuf, timeout: Duration, log: Box<dyn Fn(&str) + Send + Sync>) -> BrokerPort` |
| `BrokerPort::timeout_for` | `timeout_for(&self, tool: &str) -> Duration` |
| `crate::consult::CONSULT` | `&str`, `"consult"` (task 01) |
| `crate::memory::ASK_MEMORY` | `&str`, `"ask_memory"` |
## The edits, exactly
Each quoted "before" block occurs exactly once after task 01.
### `crates/loopd/src/broker_port.rs`
**W1.** After `pub const ASK_MEMORY_WAIT: Duration = Duration::from_secs(180);` add:
```rust
/// The longest `BrokerPort` waits for a `consult` answer, unless `[broker] timeout_ms` is
/// longer: `brokerd`'s `[consult] timeout_ms` defaults to 180 s (M6 spec, section 4.3).
pub const CONSULT_WAIT: Duration = Duration::from_secs(180);
```
**W2.** The struct fields
```rust
socket: PathBuf,
timeout: Duration,
ask_timeout: Duration,
pending_cap: Duration,
log: Box<dyn Fn(&str) + Send + Sync>,
```
become
```rust
socket: PathBuf,
timeout: Duration,
ask_timeout: Duration,
consult_timeout: Duration,
pending_cap: Duration,
log: Box<dyn Fn(&str) + Send + Sync>,
```
**W3.** Inside `new`, the fields
```rust
socket,
timeout,
ask_timeout: ASK_MEMORY_WAIT,
pending_cap: MAX_PENDING_WAIT,
log: Box::new(|l| eprintln!("{l}")),
```
become
```rust
socket,
timeout,
ask_timeout: ASK_MEMORY_WAIT,
consult_timeout: CONSULT_WAIT,
pending_cap: MAX_PENDING_WAIT,
log: Box::new(|l| eprintln!("{l}")),
```
**W4.** The comment and body of `timeout_for`
```rust
/// The wait for one call of `tool`, used wherever the port waits for that call: the timeout,
/// or for `ask_memory` the longer of the timeout and its own wait.
pub fn timeout_for(&self, tool: &str) -> Duration {
if tool == crate::memory::ASK_MEMORY {
self.timeout.max(self.ask_timeout)
} else {
self.timeout
}
}
```
become
```rust
/// The wait for one call of `tool`, used wherever the port waits for that call: the timeout,
/// or for `ask_memory` and `consult` the longer of the timeout and that tool's own wait.
pub fn timeout_for(&self, tool: &str) -> Duration {
if tool == crate::memory::ASK_MEMORY {
self.timeout.max(self.ask_timeout)
} else if tool == crate::consult::CONSULT {
self.timeout.max(self.consult_timeout)
} else {
self.timeout
}
}
```
**W5.** Inside `with_log`, the fields
```rust
socket,
timeout,
ask_timeout: ASK_MEMORY_WAIT,
pending_cap: MAX_PENDING_WAIT,
log,
```
become
```rust
socket,
timeout,
ask_timeout: ASK_MEMORY_WAIT,
consult_timeout: CONSULT_WAIT,
pending_cap: MAX_PENDING_WAIT,
log,
```
## Steps
- [ ] **1. Copy the given test.**
```sh
cp docs/plans/M6b/files/crates/loopd/tests/broker_port_consult_wait.rs crates/loopd/tests/
```
- [ ] **2. See it fail.** `cargo test -p loopd --test broker_port_consult_wait`, timeout
600000 ms: the test target does not compile (`E0432` unresolved `CONSULT_WAIT`).
- [ ] **3. The edits.** W1–W5. `cargo check -p loopd` prints no error and no warning.
`wc -l crates/loopd/src/broker_port.rs` ≤ 500.
- [ ] **4. The tests.** `cargo test -p loopd --test broker_port_consult_wait --test
broker_port_ask_wait`: every test passes, 1 in `broker_port_consult_wait`.
`the_wait_for_each_tool` still passes.
- [ ] **5. Run the gate.** `cargo fmt --all`, then `make gate`, timeout 600000 ms. Last
line `gate: ok`. Test count **1593** (1592 before, plus 1). After `gate: ok`, do not add
up `test result:` lines.
- [ ] **6. Log and commit.** Task cell `M6b/02-loopd-consult-wait`. Read only the first 12
lines of `docs/implementer-log.md`; insert the row under the header. Stage with
`git add crates/loopd docs/implementer-log.md Cargo.lock`, then commit as `AGENTS.md`
says.
## Done when
- `make gate` prints `gate: ok` with 1593 tests.
- `git diff HEAD~1 --stat` lists only `crates/loopd/src/broker_port.rs`,
`crates/loopd/tests/broker_port_consult_wait.rs` and the log.
## Stop and report if
- The branch check fails; a "before" block is missing or duplicated; step 2 compiles; you
edit `broker_port_ask_wait.rs` or `config.rs`; `cargo check` warns; `the_wait_for_each_tool`
fails; a test outside the named files fails in the gate (`git stash -u`, rerun,
`git stash pop`, report both).
## What the given tests pin
`CONSULT_WAIT` is 180 s. `timeout_for("consult")` is 180 s when the port timeout is 120 s,
and 300 s when the port timeout is 300 s. `timeout_for("recall")` stays the port timeout.
`with_log` matches `new`.