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>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user