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

139 lines
4.8 KiB
Markdown

# 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
```rust
/// 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
```rust
/// 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
```rust
// replaced by SAVED_AS_NOTE; any other action -> Err(untrusted_refusal(file)).
```
becomes
```rust
// replaced by saved_as_note(file); any other action -> Err(untrusted_refusal(file)).
```
and, a few lines below it, the line
```rust
answer: SAVED_AS_NOTE.to_string(),
```
becomes
```rust
answer: saved_as_note(file),
```
**3.** In `noted`, the two lines
```rust
"noted (untrusted)"
```
and
```rust
"noted"
```
become `NOTED_UNTRUSTED` and `NOTED`, so the block reads:
```rust
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.