# opus audit — phase 3 prep v4 (read-only verification of v3 fixes)

**verdict: PASS** — all 1 major + 3 minor fixes from `opus_audit_phase3_prep_v3.md` verified. no files edited besides this report.

## 1. `PRE_TOOL_HYPOTHESIS_CLAUSE_RE` (major) — PASS
`scripts/prepare_phase3_sft_data.py` L83 is now
`[^?:.!?\n]*?(?:,\s*(?:since|because|as|then)\b\s+|\s+\b(?:because|since)\b\s+|:\s+)(.+)$`.
bare `as` and `so that` are gone; `as` only fires after a comma; `because`/`since` are `\b`-bounded.

probed directly via `apply_speech_act_surgery(text, has_following_tool_call=True)`. the hypothesis regex does **not** match any of these and the whole sentence strips to `''`:
- `Let me check whether this was the cause.`
- `Let me look at the alias table.`
- `Let me read what the config has to say.`
- `Let me run the build as root.`
- `Let me check the logs as well.`
- `Let me verify the alias works.`
- extra: `I'll check what the log has to say.`, `Let me check if it was cached so that we know.`

real hypotheses are kept (group 1 → output):
- `...because the cache may be stale.` → `the cache may be stale.`
- `...since the port changed.` → `the port changed.`
- `Let me test this hypothesis: the lock is never released.` → `the lock is never released.`
- `...cache, then rebuild the index.` → `rebuild the index.`
- `...logs, as the daemon crashed.` / `..., since it was flaky.` → kept clause

trailing path matches too: `Patched it. Let me verify the alias works.` → `patched it.`; `Here is the fix. Let me check whether this was the cause.` → `here is the fix.`.
falsifier (a split in the middle of a word on was/alias/has/as root/as well): **not triggered**.

## 2. `CHOICE_FORK_QUESTION_RE` — PASS
the negative lookahead `(?!(?:not|anything else|something else|would you|do you|should i|shall i|wait|skip|leave it)\b)` after `or` works:
- stripped (not a fork → solicitation removed): `Want me to push it or not?`, `Shall I keep it or not?`, `Do you want me to apply it, or would you rather I wait?`, `Should I apply the patch or would you rather review first?`
- kept (real fork): `Want me to write it in Rust or Go?`, `Do you want to keep or migrate?`, `Should I keep or migrate the old table?`

## 3a. inline code emoji protection — PASS
L369 splits on `` (`[^`\n]+`) `` and skips odd indices. ``Run `print('🚀')` to test 🚀 nice.`` → ``run `print('🚀')` to test nice.`` (1 stripped). ``Shipped 🚀 (◕‿◕✿) `a 🎉 b` and ```x='🔥'``` ok ✨`` → kaomoji, inline 🎉, fenced 🔥 all preserved; 2 prose emoji stripped. inline code case is also kept as-is (not lowercased).

## 3b. `phase3_lora_and_transplant.py` L93 comment — PASS
reads `(96 trunk writers + 96 router gates = 192 tensors)`. matches the regex for 48 hybrid trunk layers: 1 residual writer (`ssm_out` xor `attn_output`/`attn_out`) + `ffn_down_shexp` = 96, and `ffn_gate_inp` + `ffn_gate_inp_shexp` = 96. blk.48 (MTP) is correctly excluded. (i didn't check this against a real GGUF tensor list; i only checked the regex arithmetic.)

## 4. self-tests with `python3 -B` — PASS
| script | exit | output |
|---|---|---|
| `scripts/prepare_phase3_sft_data.py --self-test` | 0 | `self-test: PASS` |
| `scripts/phase3_lora_and_transplant.py --self-test` | 0 | `self-test: PASS` (C Q2_0 golden compiled + ran) |
| `scripts/slice_qwen4exp_gguf.py --self-test` | 0 | `self-test: PASS` |

`glob("**/__pycache__")` was empty before and after the runs (and after my own importlib probes with `sys.dont_write_bytecode=True`).

## remaining issues
none blocking. one nit: an opener whose only content is a hypothesis is reduced to that clause in lowercase without a subject (`the cache may be stale.`). that's intended per M2 and fine for SFT.
