diff --git a/docs/PHASE3.md b/docs/PHASE3.md index c2cf8f1..c2413d8 100644 --- a/docs/PHASE3.md +++ b/docs/PHASE3.md @@ -254,8 +254,10 @@ local DESTRUCTIVE_PATTERNS = { { pat = "^%s*eval%s", reason = "eval (dynamic shell)" }, { pat = "^%s*python3?%s+%-c%s", reason = "python -c (inline script)" }, { pat = "^%s*perl%s+%-e%s", reason = "perl -e (inline script)" }, - { pat = "|%s*sh%f[%s]", reason = "pipe-to-sh" }, - { pat = "|%s*bash%f[%s]", reason = "pipe-to-bash" }, + { pat = "|%s*sh%s", reason = "pipe-to-sh" }, + { pat = "|%s*sh%s*$", reason = "pipe-to-sh (eol)" }, + { pat = "|%s*bash%s", reason = "pipe-to-bash" }, + { pat = "|%s*bash%s*$", reason = "pipe-to-bash (eol)" }, { pat = "xargs%s+.-rm", reason = "xargs ... rm" }, -- ── Filesystem destructive @@ -292,7 +294,10 @@ local DESTRUCTIVE_PATTERNS = { The 9 wrapper patterns are the conservative floor against R-B1 bypass classes. Norris emits `bash -c '...'` → wrapper hit → HALT (user can proceed if they read the inner). LLM second-opinion still runs as a backup but the static net catches the obvious cases first. -Patterns are Lua patterns (not regex), `ci = true` enables case-insensitive match. +Patterns are Lua patterns (not regex). `ci = true` enables case-insensitive +match — the matcher loop lowercases the input string when `ci` is set on +the rule, so `DROP TABLE` and `drop table x` and `Drop Table` all match +the same rule. Without `ci`, patterns are case-sensitive (the default). ### LLM second-opinion (when static doesn't HALT) @@ -522,11 +527,17 @@ Bottom-up, same cadence as Phase 0/1/2. Six commits expected: 5. **`repl.lua` — Norris driver + `\C-n` real binding + `:norris` meta.** The while-loop driver consuming `safety.norris_step`, the rebound `\C-n` (replacing Phase 1 placeholder), the `:norris ` / - `:norris off` meta cmds, and `\C-x\C-c` abort handler. Also extends - the interactive `CMD:` confirm path to consult `is_destructive` - first (per Q24 resolution). **Test**: mocked-broker end-to-end — - submit a multi-step goal, verify driver loops correctly, hits - GOAL:complete, returns to interactive. + `:norris off` meta cmds, and `\C-x\C-c` abort handler. **Interactive + `CMD:` extraction is UNCHANGED** — `is_destructive` runs ONLY when + `norris_active == true` (R-B3 resolution of Q24); `confirm_cmd` + semantics from PHASE0 §10 are preserved exactly. Bundled with this + commit: `ffi/readline.lua` extension per §3 row — `rl_insert_text` + + `rl_redisplay` cdefs + `M.insert_text` / `M.redisplay` wrappers, + AND removal of the `_bound[seq]:free()` call from `M.bind` (R-C4 — + small Phase 1 amendment, called out here so the commit body cites + it). **Test**: mocked-broker end-to-end — submit a multi-step goal, + verify driver loops correctly, hits GOAL:complete, returns to + interactive. 6. **`config.lua` — `safety` example block.** Commented-out example showing `llm_second_opinion`, `llm_model`, `destructive_patterns`, @@ -547,11 +558,10 @@ Bottom-up, same cadence as Phase 0/1/2. Six commits expected: round-trip plus optionally an LLM second-opinion. A 16-step Norris goal could be ~32 LLM calls on the fast model. Visible as latency but no economic surprise on local models. -- **Destructive check on interactive CMD: extraction (Q24)** is a - behavior change to Phase 0/1 (`confirm_cmd` users will see the - prompt automatically for destructive commands even with - `confirm_cmd=false`). Documented in §9. Defensible: the worst case - is a confirm prompt the user dismisses. +- **Q24 resolution (R-B3)**: `is_destructive` runs ONLY in Norris + mode. Interactive `CMD:` extraction continues to honor `confirm_cmd` + exactly as Phase 0 specified. No substrate amendment; no surprises + for users of `confirm_cmd=false` setups. - **`GOAL: complete` extraction** uses the same `^GOAL: complete$` regex on emitted text. Substrate-aligned with CMD: extraction.