docs: runbook: sanitizer-rules, how to change a sanitizer rule
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
+46
-4
@@ -672,8 +672,8 @@ standard input) names each rule that fires and writes nothing. The session's log
|
||||
arguments.
|
||||
|
||||
**Fix.** A real secret: rotate it, since it has been in the conversation; keep it out of memory.
|
||||
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.
|
||||
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.
|
||||
|
||||
**Check.** `bxctl sanitize check` with the text prints nothing and exits 0, and the agent's next
|
||||
write of it is answered `… updated` or `saved in notes.md`.
|
||||
@@ -703,8 +703,8 @@ write is not staged: it is a note at once. The log names the rule, never the tex
|
||||
reaches the prompt. If it is a true fact wrongly flagged, move it into identity by hand: add it to
|
||||
`<home>/memory/memory.md` (or `user.md`) with an editor, as an entry of its own, with a line
|
||||
holding only `§` between it and the entry before, keeping the file within its limit. The note
|
||||
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.
|
||||
stays in `notes.md`, which is append-only. If the rule catches ordinary text, narrow it as
|
||||
[`sanitizer-rules`](#sanitizer-rules) says.
|
||||
|
||||
**Check.** Start a new conversation and ask the agent what `memory.md` holds; the entry is there.
|
||||
|
||||
@@ -830,3 +830,45 @@ does no harm, and does not help either.
|
||||
**Check.** `pgrep -x <role>` prints one process id, and the daemon prints its start line
|
||||
(`loopd: serving on …`, `inferproxy: <socket> -> <upstream>`, `brokerd: serving tools on …`,
|
||||
`memoryd: serving`, `gatewayd: connected to …`).
|
||||
|
||||
## 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