M5b plan: task 14, the runbook entry sanitizer-rules
The last runbook entry, the rule-change procedure of spec 6.5; the entries memory-write-refused and memory-write-flagged now send the reader to it instead of to the spec. Tasks 06, 08 and 12 added the other five, because the gate needs an entry for every pointer. Checks: the edits, applied from the task's text to ref 13, give a runbook byte-identical to the reference; each before block found once; the gate passes on the reference with 1118 tests; /private/tmp unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
# M5b task 14: the runbook entry `sanitizer-rules`
|
||||||
|
|
||||||
|
**Branch:** `m5b` (run `git switch m5b`; `git status --short` must be empty, otherwise stop)
|
||||||
|
**Commit subject:** `docs: runbook: sanitizer-rules, how to change a sanitizer rule`
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
The M5b spec, section 6.5, gives the procedure for changing a sanitizer rule. Tasks 06, 08 and 12
|
||||||
|
added the runbook entries that code points to; this task adds the last one, `sanitizer-rules`,
|
||||||
|
which no code points to, and makes the entries `memory-write-refused` and `memory-write-flagged`
|
||||||
|
send the reader to it instead of to the spec. Only `docs/runbook.md` changes. No code changes.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- Given: `docs/plans/M5b/files/runbook-sanitizer-rules.md`, appended verbatim.
|
||||||
|
- Modify: `docs/runbook.md` (edits R1 and R2, then the append).
|
||||||
|
|
||||||
|
## The edits, exactly
|
||||||
|
|
||||||
|
**R1.** `docs/runbook.md`, in the entry `memory-write-refused`, under **Fix.**: the lines
|
||||||
|
|
||||||
|
```text
|
||||||
|
Ordinary text refused by mistake: add it to the code-work corpus and narrow the rule, as the M5b
|
||||||
|
spec's section 6.5 says; the change reaches the agent when it is rebuilt.
|
||||||
|
```
|
||||||
|
|
||||||
|
become
|
||||||
|
|
||||||
|
```text
|
||||||
|
Ordinary text refused by mistake: add it to the code-work corpus and narrow the rule, as
|
||||||
|
[`sanitizer-rules`](#sanitizer-rules) says; the change reaches the agent when it is rebuilt.
|
||||||
|
```
|
||||||
|
|
||||||
|
**R2.** `docs/runbook.md`, in the entry `memory-write-flagged`, under **Fix.**: the lines
|
||||||
|
|
||||||
|
```text
|
||||||
|
stays in `notes.md`, which is append-only. If the rule catches ordinary text, narrow it as the M5b
|
||||||
|
spec's section 6.5 says.
|
||||||
|
```
|
||||||
|
|
||||||
|
become
|
||||||
|
|
||||||
|
```text
|
||||||
|
stays in `notes.md`, which is append-only. If the rule catches ordinary text, narrow it as
|
||||||
|
[`sanitizer-rules`](#sanitizer-rules) says.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
- [ ] **1. Edits R1 and R2.** Each "before" block is in `docs/runbook.md` exactly once. Change
|
||||||
|
nothing else in the file. Then `grep -c "section 6.5" docs/runbook.md` prints `0`.
|
||||||
|
- [ ] **2. The entry.** From the repository root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
{ echo; cat docs/plans/M5b/files/runbook-sanitizer-rules.md; } >> docs/runbook.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Then `grep -c '^## sanitizer-rules$' docs/runbook.md` prints `1`, and
|
||||||
|
`grep -c '(#sanitizer-rules)' docs/runbook.md` prints `2`.
|
||||||
|
- [ ] **3. Run the gate.** `make gate`, with a shell timeout of 600000 ms. Expected last line:
|
||||||
|
`gate: ok`, with 1118 tests, the same count as before this task: no test changes.
|
||||||
|
- [ ] **4. Log and commit.** `git status --short` lists `docs/runbook.md` and your log as
|
||||||
|
modified, and nothing else. Stage with `git add docs/runbook.md docs/implementer-log.md`, then
|
||||||
|
commit as `AGENTS.md` says.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
- `make gate` prints `gate: ok` with 1118 tests.
|
||||||
|
- `git diff HEAD~1 --stat` lists only `docs/runbook.md` and `docs/implementer-log.md`.
|
||||||
|
|
||||||
|
## Stop and report if
|
||||||
|
|
||||||
|
- A "before" block of R1 or R2 is not in `docs/runbook.md` exactly as shown, or is there more than
|
||||||
|
once.
|
||||||
|
- `docs/runbook.md` already has a line `## sanitizer-rules`.
|
||||||
|
- A grep in step 1 or 2 prints another number.
|
||||||
|
- The gate fails, or passes with a count other than 1118. This task changes no code, so do not
|
||||||
|
try to fix it: stop, and give the gate's last lines.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
## sanitizer-rules
|
||||||
|
|
||||||
|
**What you see.** Not a failure: the procedure for changing a sanitizer rule, which the entries
|
||||||
|
`memory-write-refused` and `memory-write-flagged` send you to when a rule catches ordinary text,
|
||||||
|
or which you follow when an attack gets through. The rules are in `crates/bxcore/src/sanitize/`:
|
||||||
|
`refuse.rs` (6.1 of the M5b spec), `tags.rs` (the chat-template tags that are neutralised, 6.2)
|
||||||
|
and `flag.rs` with `normalise.rs` (6.3).
|
||||||
|
|
||||||
|
**Why.** The rules are compiled in rather than read from a file, so that every change passes the
|
||||||
|
two corpora and review, and a typo cannot switch a rule off (M5b spec, section 6.5). A rule that
|
||||||
|
refuses code is as much a bug as one that lets an attack through: the agent works on code
|
||||||
|
projects, and a flagged note makes every session that recalls it untrusted.
|
||||||
|
|
||||||
|
**Confirm.** The evidence, before anything changes:
|
||||||
|
|
||||||
|
- a rule's name in `bxctl memory log` (`flagged <rule>`), in `memoryd`'s log, or in a refusal the
|
||||||
|
agent passed on;
|
||||||
|
- `bxctl sanitize check` with the text, which names each rule that fires and writes nothing;
|
||||||
|
- a device check, a review or new research that shows an attack the rules miss.
|
||||||
|
|
||||||
|
**Fix.** In this order, one commit:
|
||||||
|
|
||||||
|
1. **The case first.** Add the text to a corpus, with a comment saying where it was seen: the
|
||||||
|
attack corpus if it must be caught, the code-work corpus if it must pass. The corpora are the
|
||||||
|
given tests `crates/bxcore/tests/sanitize_refuse_corpus.rs` (refuse),
|
||||||
|
`sanitize_corpus.rs` (neutralise) and `sanitize_flag_corpus.rs` (flag). Build a tag or a key
|
||||||
|
from pieces, as the files already do: never write one whole. Run `cargo test -p bxcore` and see
|
||||||
|
the new case fail.
|
||||||
|
2. **The rule.** Add or narrow the entry in the rule's table, with a comment naming its source.
|
||||||
|
Keep it as narrow as the evidence: a flag rule ported from Hermes keeps its attribution.
|
||||||
|
3. **The gate.** `make gate` runs both corpora for every action, so a rule that catches code
|
||||||
|
fails before it lands.
|
||||||
|
4. **The record.** A policy change (a new kind of refusal, or a rule moved from flag to refuse)
|
||||||
|
gets a row in `docs/decisions.md`; an ordinary rule change is its commit.
|
||||||
|
|
||||||
|
A change reaches an agent when it is rebuilt and restarted. Stored memory is never rewritten: an
|
||||||
|
identity entry that breaks a new refuse rule leaves the memory block at once (see
|
||||||
|
`memory-entry-excluded`), and a note flagged under an old rule stays as it was written.
|
||||||
|
|
||||||
|
**Check.** `bxctl sanitize check` with the text gives the new answer (nothing, and exit 0, for a
|
||||||
|
text that must pass). After the rebuild, the agent's next write of it is answered as expected.
|
||||||
Reference in New Issue
Block a user