review::submit counted only the writes still waiting, so a write staged after an accepted one got that one's id and was lost: never listed, never acceptable. It now counts every Waiting line of the day. On a scratch tree with task 16's line applied, both new tests fail and then pass, the old code and a count of every line are both caught, and the gate passes (938 there; 941 after task 18). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.1 KiB
M5a task 19: a staged write never reuses an id
Branch: m5a (run git switch m5a; git status --short must be empty, otherwise stop)
Commit subject: memoryd: a staged write's id counts every write staged that day
Goal
A fix for a bug the M5a review found (2026-09-24). When review is on, review::submit gives each
staged write an id, p-<day>-NNNN, numbered by how many writes that day already have one. It
counted only the writes still waiting. So once p-2026-09-24-0001 had been accepted, the next
write that day was given p-2026-09-24-0001 again. waiting treats every id with an Accepted
line as done, so that write never showed in bxctl memory pending and could not be accepted or
rejected: it was lost, while the model was told it was waiting for review.
The fix counts every Waiting line in pending.jsonl, done or not, so an id is never used twice.
The comment above says the same, more plainly than before.
Files
- Copy: the new test
crates/memoryd/tests/review_ids.rs, fromdocs/plans/M5a/files/(the same path under it) - Modify:
crates/memoryd/src/review.rs,docs/implementer-log.md
The edits, exactly
Both are in submit in crates/memoryd/src/review.rs. Nothing else in the file changes;
waiting does not change, since list, accept and reject still use it.
1. The comment lines
// 2. `day` is the first 10 characters of `ctx.now.to_rfc3339()`. `taken` = how many Waiting
// lines have an id starting format!("p-{day}-"). The id is format!("p-{day}-{:04}", taken +
// 1).
become
// 2. `day` is the first 10 characters of `ctx.now.to_rfc3339()`. `taken` = how many Waiting
// lines of `lines(store)?`, done or not, have an id starting format!("p-{day}-"): an id is
// never used twice. The id is format!("p-{day}-{:04}", taken + 1).
2. The line
let taken = waiting(store)?
becomes
let taken = lines(store)?
The .iter(), .filter(..) and .count() lines after it stay as they are.
Steps
- 1. Copy the test:
cp docs/plans/M5a/files/crates/memoryd/tests/review_ids.rs crates/memoryd/tests/ - 2. See it fail.
cargo test -p memoryd --test review_ids. Expected: both tests fail,a_write_staged_after_an_accepted_one_gets_a_new_id_and_can_be_acceptedandids_count_every_write_staged_that_day_waiting_or_done. - 3. Make the two edits. Run
cargo fmt --all. - 4. See it pass.
cargo test -p memoryd --test review_ids: both pass. Thencargo test -p memoryd: every suite passes. - 5. Run the gate.
make gate. Expected last line:gate: ok, with 941 tests (939 before this task, plus the two new ones). - 6. Log and commit.
git add crates/memoryd docs/implementer-log.md && git commit
Done when
make gateprintsgate: okwith 941 tests.
Stop and report if
- Either quoted block is not in the file exactly as shown.
- Step 2 shows a different result than two failures.
- A test other than these two fails after the edits. Do not change a test.