Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
11 KiB
M5c-2 task 03: brokerd waits 170 s for ask_memory
Branch: m5c2. Check it: git branch --show-current prints m5c2 and git status --short
is empty; otherwise stop. Never switch branch.
Commit subject: brokerd: a wait of its own for ask_memory, 170 s
Log row: the Task cell is M5c2/03-brokerd-ask-memory-wait.
Goal
brokerd hands each decided memory call to memoryd through a MemoryPort
(crates/brokerd/src/memory_port.rs), and waits for the answer: today 30 s (MEMORY_WAIT), set
as the socket's read and write timeouts. Task 02 made ask_memory a memory tool. Its answer can
take much longer: memoryd asks Honcho's chat about each named workspace, both at once, with
150 s each (M5c spec, section 9). So ask_memory gets a wait of its own:
ASK_MEMORY_WAIT= 170 s, a constant, not configuration.MEMORY_WAITstays 30 s, forrecallandmemory.- The port chooses the wait by the call's tool (
wait_for), and sets it as the read and write timeouts of that call's connection only. - The tests need short waits, so the wait for
ask_memorycan be set on a port (with_ask_wait), asnewalready takes the wait for the other two.MemoryPort::newkeeps its signature, andbrokerd'smain.rsdoes not change:newgives every portASK_MEMORY_WAIT.
loopd waits 120 s for brokerd today, for any tool; task 04 gives ask_memory 180 s there.
Every place that changes
This is the complete list.
| Place | What happens there |
|---|---|
crates/brokerd/src/memory_port.rs |
the module's doc comment; a use line; ASK_MEMORY_WAIT; MemoryPort's new field ask_wait; new, with_ask_wait, wait_for; ask uses wait_for (edits W1 to W5) |
crates/brokerd/tests/memory_wait.rs |
new, given |
| everything else | no change: not main.rs, not args.rs, not the other tests, no Cargo.toml |
Files
- Copy, from
docs/plans/M5c2/files/(the same path under it): the new testcrates/brokerd/tests/memory_wait.rs. - Modify:
crates/brokerd/src/memory_port.rs,docs/implementer-log.md. - Do not touch: any other file.
Facts you need
- Struct update syntax.
MemoryPort { ask_wait: wait, ..self }builds a newMemoryPortthat takesask_waitfromwaitand every other field fromself(moved:with_ask_waittakesselfby value). It is what edit W3 writes. ToolNameiscrate::args::ToolName,Copy, with the variantAskMemorysince task 02.wait_formatches it with a_arm for every other tool: onlyask_memoryhas its own wait.- The given test
memory_wait.rs(3 tests):- the two constants; which tool gets which wait, with
new's own wait, with the default forask_memory, and withwith_ask_wait; - a fake
memorydthat answers after 1.5 s: with a 300 ms wait forrecallandmemoryand 20 s forask_memory, theask_memorycall gets the answer and the other two get theUNREACHABLEfailure; and the other way round forrecallandask_memory. A failed answer comes back within the short wait, and a good one as soon as it is written, so the file takes about 3 s.
- the two constants; which tool gets which wait, with
- The items the edits use. This table is complete; nothing from a crate's source (the Cargo
registry,
~/.cargo/registry) is needed or allowed.Item Where std::time::Duration::from_secs(u64) -> Duration(aconst fn)W3 crate::args::ToolNameW2, W3 UnixStream::set_read_timeout(&self, Option<Duration>),set_write_timeoutW5, as before
The edits, exactly
Nothing else in this file changes. Each quoted "before" block occurs exactly once in the file when its turn comes (make them in order). Keep the fields in the order shown.
crates/brokerd/src/memory_port.rs
W1. The module's doc comment, the lines
//! Where decided `recall` and `memory` calls go: to `memoryd` over its `brokerd` socket, with the
//! session's state, instead of a container (M5a spec, section 8). `Routed` sends every other call
//! to the configured runtime.
become
//! Where decided `recall`, `memory` and `ask_memory` calls go: to `memoryd` over its `brokerd`
//! socket, with the session's state, instead of a container (M5a spec, section 8). `Routed` sends
//! every other call to the configured runtime. `ask_memory` has a wait of its own (M5c spec,
//! section 9).
W2. Add one line. The line
use crate::policy::Label;
becomes
use crate::args::ToolName;
use crate::policy::Label;
W3. The lines
/// How long `brokerd` waits for `memoryd` to answer one call.
pub const MEMORY_WAIT: Duration = Duration::from_secs(30);
/// A client of `memoryd`'s `brokerd` socket. One connection per call.
pub struct MemoryPort {
socket: PathBuf,
wait: Duration,
}
impl MemoryPort {
pub fn new(socket: PathBuf, wait: Duration) -> MemoryPort {
MemoryPort { socket, wait }
}
become
/// How long `brokerd` waits for `memoryd` to answer one `recall` or `memory` call.
pub const MEMORY_WAIT: Duration = Duration::from_secs(30);
/// How long it waits for one `ask_memory` call: `memoryd` asks Honcho's chat about each
/// workspace, both at once, with 150 s each (M5c spec, section 9).
pub const ASK_MEMORY_WAIT: Duration = Duration::from_secs(170);
/// A client of `memoryd`'s `brokerd` socket. One connection per call.
pub struct MemoryPort {
socket: PathBuf,
wait: Duration,
ask_wait: Duration,
}
impl MemoryPort {
/// A port that waits `wait` for `recall` and `memory`, and `ASK_MEMORY_WAIT` for `ask_memory`.
pub fn new(socket: PathBuf, wait: Duration) -> MemoryPort {
MemoryPort {
socket,
wait,
ask_wait: ASK_MEMORY_WAIT,
}
}
/// The same port, waiting `wait` for `ask_memory`. The tests use a short one.
pub fn with_ask_wait(self, wait: Duration) -> MemoryPort {
MemoryPort {
ask_wait: wait,
..self
}
}
/// How long one call of `tool` waits for `memoryd`, to write the call and to read the answer.
pub fn wait_for(&self, tool: ToolName) -> Duration {
match tool {
ToolName::AskMemory => self.ask_wait,
_ => self.wait,
}
}
W4. In ask, the comment's lines
// untrusted from `spec.state()`. 2. `UnixStream::connect(&self.socket)`, then read and
// write timeouts of `self.wait`. 3. `write_frame` an Envelope { v: PROTOCOL_VERSION, id:
// spec.call().0, final: true, msg: Message::MemoryCall(call) }. 4. `read_frame`; a
// Message::ToolResponse is Some(it), any other message None.
become
// untrusted from `spec.state()`. 2. `UnixStream::connect(&self.socket)`, then read and
// write timeouts of `self.wait_for(spec.tool())`. 3. `write_frame` an Envelope { v:
// PROTOCOL_VERSION, id: spec.call().0, final: true, msg: Message::MemoryCall(call) }. 4.
// `read_frame`; a Message::ToolResponse is Some(it), any other message None.
W5. In ask, the lines
let mut stream = UnixStream::connect(&self.socket).ok()?;
stream.set_read_timeout(Some(self.wait)).ok()?;
stream.set_write_timeout(Some(self.wait)).ok()?;
become
let wait = self.wait_for(spec.tool());
let mut stream = UnixStream::connect(&self.socket).ok()?;
stream.set_read_timeout(Some(wait)).ok()?;
stream.set_write_timeout(Some(wait)).ok()?;
Steps
-
1. Copy the given test. From the repository root, one command:
cp docs/plans/M5c2/files/crates/brokerd/tests/memory_wait.rs crates/brokerd/tests/Then
git status --shortlists one new file,crates/brokerd/tests/memory_wait.rs. -
2. See it fail.
cargo build --tests --keep-going -p brokerd, with a shell timeout of 600000 ms: one test target,memory_wait, does not compile, with 10 errors: oneE0432(unresolved import `brokerd::memory_port::ASK_MEMORY_WAIT`), sixE0599(no method named `wait_for` found for struct `MemoryPort`) and threeE0599(no method named `with_ask_wait` found for struct `MemoryPort`). Every other target compiles. -
3. The edits. Make edits W1 to W5, in order.
cargo check -p brokerdprints no error and no warning. -
4. The tests. With a shell timeout of 600000 ms, every test passes: 3 in
memory_wait, 5 inmemory_port, 9 inask_memory_tool:cargo test -p brokerd --test memory_wait --test memory_port --test ask_memory_tool -
5. Check. Two commands:
git diff --statlists exactlycrates/brokerd/src/memory_port.rs.grep -n 'MEMORY_WAIT' crates/brokerd/src/main.rsprints the two lines it printed before this task: theuseline andMemoryPort::new(cfg.memory_socket(), MEMORY_WAIT).
-
6. Run the gate.
cargo fmt --all, thenmake gate, with a shell timeout of 600000 ms. Expected last line:gate: ok. The test count is the passed total ofcargo test --workspaceplus one, for the ignoredend_to_endtest that the gate runs on its own: 1384 after this task, 1381 before it; the 3 new ones are the tests ofmemory_wait.rs. To count, add up theN passedof everytest result:line the gate'scargo test --workspaceprinted, and add 1. Never run the gate again only to count. -
7. Log and commit. Add your row to
docs/implementer-log.md; its Task cell isM5c2/03-brokerd-ask-memory-wait.git status --shortlistsmemory_port.rsand your log as modified, andmemory_wait.rsas new. Stage withgit add crates/brokerd docs/implementer-log.md Cargo.lock, then commit asAGENTS.mdsays.Cargo.lockdoes not change in this task; thegit addis harmless.
Done when
make gateprintsgate: okwith 1384 tests.git diff HEAD~1 --statlists onlycrates/brokerd/src/memory_port.rs,crates/brokerd/tests/memory_wait.rsanddocs/implementer-log.md.
Stop and report if
- The branch check at the top fails.
- A "before" block is not in the file exactly as shown when its turn comes, or is there more than once.
- Step 2 shows a different result: another target failing, or other errors.
cargo checkin step 3 prints an error or a warning. Quote it, and stop.- A test fails in step 4. Do not change the test: stop, and give the failing tests' names and their messages.
- The fix seems to need a change anywhere else:
main.rs, a configuration key,MemoryPort::new's signature, another test. It does not; stop and say why you think so. - A test outside
memory_wait.rsfails in the gate: check once whether it fails without your change (git stash -u, run it,git stash pop), then stop and report both results. - The gate passes with a count other than 1384.
- A comment in an edit does not fit the code under it. Quote it and stop.
What the given tests pin
Each of these changes to the finished code makes at least one given test fail (10 tried, all
caught): either constant changed; new giving ask_memory its own wait instead of
ASK_MEMORY_WAIT; with_ask_wait doing nothing, or setting the other tools' wait too;
wait_for giving ask_memory the other tools' wait, or recall ask_memory's; ask using
the other tools' wait for every call, or ask_memory's for every call; the read timeout not set.