21: the ledger raises a session's state by the stricter of the grant's label and the result's own, so a clean session that recalls a memory-tainted note becomes untrusted and cannot write it into memory.md (found on straylight, 2026-09-25). 22: the answers to a note name notes.md, and for an untrusted add the file that was not changed. The new given tests are ledger_label.rs and new versions of memoryd's edit.rs and serve.rs; the old copies are kept as -task06 and -task10. On a scratch tree at the end of task 20: the named tests fail and then pass, 5 and 4 mutations are caught, and the gate passes with 944 tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
4.8 KiB
M5a task 22: the answers to a note say where it went
Branch: m5a (run git switch m5a; git status --short must be empty, otherwise stop)
Commit subject: memoryd: note answers name notes.md and the file that was not changed
Goal
A fix for what the M5a device check showed (2026-09-25). In a session that has read untrusted
content, an add to memory.md or user.md is saved as an untrusted note instead. memoryd
answered saved as a note (untrusted): this session has read untrusted content, and a note
answered noted (untrusted). Both times the model told the owner "Added to memory.md", which was
false. The new answers name notes.md, and for an add also the file that was not changed (M5a
spec, section 3, as changed in 0e5b1d2):
| Case | Before | After |
|---|---|---|
note, clean session |
noted |
saved in notes.md |
note, untrusted session |
noted (untrusted) |
saved in notes.md, marked untrusted: this session has read untrusted content |
add, untrusted session |
saved as a note (untrusted): this session has read untrusted content |
not added to memory.md: this session has read untrusted content, so it was saved in notes.md as an untrusted note (user.md for the other file) |
Nothing else changes: the note written to notes.md, its header and the journal stay the same.
Files
- Copy: new versions of the given
crates/memoryd/tests/edit.rsandcrates/memoryd/tests/serve.rs, fromdocs/plans/M5a/files/(the same paths under it). Do not copyedit.rs-task06orserve.rs-task10; they are the older versions. - Modify:
crates/memoryd/src/edit.rs,docs/implementer-log.md
The edits, exactly
All in crates/memoryd/src/edit.rs. Nothing else in the file changes.
1. The three lines
/// The answer when an untrusted session's `add` became a note.
pub const SAVED_AS_NOTE: &str =
"saved as a note (untrusted): this session has read untrusted content";
become
/// The answer when an untrusted session's `add` became a note. It names the file it did not change:
/// on the device the model read a shorter answer as success.
pub fn saved_as_note(file: MemoryFile) -> String {
format!(
"not added to {}: this session has read untrusted content, so it was saved in notes.md as an untrusted note",
file.name()
)
}
/// The answer to a `note` from a session that has not read untrusted content.
pub const NOTED: &str = "saved in notes.md";
/// The answer to a `note` from a session that has read untrusted content.
pub const NOTED_UNTRUSTED: &str =
"saved in notes.md, marked untrusted: this session has read untrusted content";
The format! string stays on one line, longer than 100 characters; cargo fmt leaves it so.
2. In apply, the comment line
// replaced by SAVED_AS_NOTE; any other action -> Err(untrusted_refusal(file)).
becomes
// replaced by saved_as_note(file); any other action -> Err(untrusted_refusal(file)).
and, a few lines below it, the line
answer: SAVED_AS_NOTE.to_string(),
becomes
answer: saved_as_note(file),
3. In noted, the two lines
"noted (untrusted)"
and
"noted"
become NOTED_UNTRUSTED and NOTED, so the block reads:
answer: if ctx.untrusted {
NOTED_UNTRUSTED
} else {
NOTED
}
.to_string(),
After the edits, grep -rn SAVED_AS_NOTE crates/ prints nothing.
Steps
- 1. Copy the two tests:
cp docs/plans/M5a/files/crates/memoryd/tests/edit.rs docs/plans/M5a/files/crates/memoryd/tests/serve.rs crates/memoryd/tests/ - 2. See them fail.
cargo test --no-fail-fast -p memoryd --test edit --test serve. Expected: four tests fail,a_note_is_labelled_by_the_sessionandan_untrusted_session_can_take_notes_but_not_change_identityinedit, andan_untrusted_session_writes_notes_not_identityandreview_holds_identity_writes_but_not_notesinserve. Every other test passes. - 3. Make the edits. Run
cargo fmt --all. - 4. See them pass. The command from step 2: every test passes. Then
cargo test -p memoryd: every suite passes. - 5. Run the gate.
make gate, with a shell timeout of at least 600000 ms. Expected last line:gate: ok, with 944 tests, the same as after task 21 (this task changes tests, adds none). - 6. Log and commit.
git add crates/memoryd docs/implementer-log.md && git commit
Done when
make gateprintsgate: okwith 944 tests, andgrep -rn SAVED_AS_NOTE crates/prints nothing.
Stop and report if
- Any quoted block is not in the file exactly as shown.
- Step 2 shows a different set of failing tests than the four named.
- A test other than these four fails after the edits. Do not change a test.