Files
boxmaker/docs/plans/M5a/22-plain-note-answers.md
kyleandClaude Opus 5.5 f389a9a43a M5a plan: tasks 21 and 22, from the device check
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>
2026-09-25 17:55:41 -07:00

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.rs and crates/memoryd/tests/serve.rs, from docs/plans/M5a/files/ (the same paths under it). Do not copy edit.rs-task06 or serve.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_session and an_untrusted_session_can_take_notes_but_not_change_identity in edit, and an_untrusted_session_writes_notes_not_identity and review_holds_identity_writes_but_not_notes in serve. 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 gate prints gate: ok with 944 tests, and grep -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.