opus audit v4: verification of the v3 §3 fixes (slop miner + §5.2) #
read-only · auditor: opus-verify-slop-v4 · scope: the 6 fixes in audits/opus_audit_slop_miner_and_1c_v3.md §3, checked against PLAN.md (§5.2, §7.1, §2.7.A #26), results/phase2_pareto_analysis.json, scripts/assemble_corpus.py, mine_llm_isms_from_transcripts.py and results/banned_llm_isms.json
verdict: ISSUES, but narrow. the code-level fixes are real and they work: phantom n-grams, curly apostrophes, sub-tuple stop-lists, isSidechain, transcript user rows, > stripping, reasoning_text, ETF 80/77/111 + error counting, family grouping, I/(/[ token variants, and tokenizer fail-closed. the §5.2 claims are fixed. four things are still open:
- the prior fix only works for bigrams. 37 of the 108
c_hum = 0rows (every one a 3- or 4-gram) still sit at one constant δ = 2.548. - the banned list contradicts the plan's own abstention target. PLAN §2.4 L108 and §2.7 L193 say lost facts become "not sure / let me check". the list bans
let me checkplus 8 otherlet me …forms, and also bans honest-disclosure phrases (i haven't checked,i didn't run,i haven't run,i don't think). - the genre confound is still there, it just moved. the status words are gone (
is clean,still running, …), but 39/149 rows are now first-person action reports (i checked,i made,i tried,i moved,i filed…) and 37/149 are generic English. - the family filter is mostly a technicality. 45/149 rows pass
min_families ≥ 2on ≤ 3 non-Claude hits.
__pycache__/ is present and empty: the old v3 .pyc is gone. python3 -B … --self-test and my own -B / dont_write_bytecode runs wrote nothing. you can delete the empty directory.
1. PLAN §5.2 / §7.1 / pareto / assemble_corpus (v3 fixes 1 and 6) #
| check | status | evidence |
|---|---|---|
| §5.2 "52–73%" removed | done | L416 now gives the measured Phase 1b OOS lru λ 0→0.09 (j=3) cut, plus −36.4% (K320h) / −47.4% (K256 l2) vs K512 λ=0. both combined numbers recompute exactly from phase1b_heldout_cache_moce_sweep.json: 1−53.95/84.84 = 36.41%, 1−44.65/84.84 = 47.37%. |
| §5.2 "~1.3/1.0/0.9 s w/ MoCE" removed | done | L424/426/428 now say warm 256-tok decode 5.88 s @ 43.5 tok/s, 5.40 s @ 47.4, 5.01 s @ 51.0. that's 256/43.54, 256/47.44 and 256/51.05, matching pareto decode_256tok_sec_moce_pread4. the cold-prefill column is labelled pre-MoCE. |
| §5.2 range wording | nit | "17.4–21.1%": the upper bound appears nowhere in the sweep. lru λ=0.09 j=3 runs 17.37% (K320 l2) to 19.86% (K224 shrink) over all 10 variants. 21.1% matches no row (λ=0.12 lru gives 22.0–22.4%, near_pick gives 20.9–22.0%). change it to 17.4–19.9%. |
| ds4 −52…−73% elsewhere | acceptable | still at L144 (§3 table), L176, L206 (§2.7 #9), all [src: ds4 06] citations at λ 0.1 / top-2. they're cited, not claimed as ours. optional: add "(ours, OOS: 17–20%)" next to L206, since that line is a decision rationale. |
| §7.1 / pareto: MoCE quality not in Tier-1 | done | §7.1 L540: "Tier-1 KLD/top-1 and Tier-2 below were evaluated at λ = 0 …; … evaluated during Phase 5". each of the 5 pareto rows carries tier1_kld_routing_note saying the same. |
| §7.1 eviction wording (v3 nit a) | done | now "every other tested online eviction policy at λ = 0.09", with belady_min called an offline oracle. the K256 vs Belady line now says "unpruned no-MoCE … oracle" (v3 nit b addressed by labelling). |
assemble_corpus.py sha256 + per-source counts |
code done, hash not recorded | L258–271 now print per-source calib chars, eval_ops chars before the 80K cap (ukisai_25, tb_8), and sha256 of all 4 outputs. but the script hasn't been re-run since: no sha256 value exists anywhere in the repo (PLAN.md, results/), and calibration_mix_v2.txt only lives on /content. "bit-identical to Phase 1a" is still asserted, not shown. the next Colab run should paste the printed hash into §7.1 or a results JSON. |
| KLD debt (v3 #5) | still open, documented | unchanged, as v3 §3.6 said it would be. |
2. miner code (v3 fixes 2, 3, 4, 5) #
python3 -B mine_llm_isms_from_transcripts.py --self-test → PASS, exit 0, no bytecode written.
| requirement | status | evidence |
|---|---|---|
| hard boundary at removed spans | done | every strip regex (fenced/inline code, think, tool XML, tags, URL/path, md header/table, hyphen IDs, human quote/box lines) substitutes "\n". then CLAUSE_SPLIT_RE (now includes , / + * = …) and NON_WORD_SPAN_RE = [^a-z'\s]+ split on digits, CJK and symbols. the self-test covers the v3 cases (run then, fast tests, see now, hyd eu, break check). none of those show up in the output. |
| curly apostrophes | done | \u2018/\u2019 → ' and curly quotes → " before anything else. self-test don’t worry, I’ll check it → don't worry, i'll check it. |
sub-tuple PROTECTED_STOP_NGRAMS |
done | is_stylistic_candidate rejects any contiguous 2–4 sub-tuple in the set. the full suite is rejected in the self-test. abstention phrases (don't know, i don't know, not sure, i'm not sure, can't/cannot confirm/verify) are protected. but see §3.2: this protects only those exact phrases, not the plan's actual abstention target let me check. |
CC isSidechain |
done | L428 skips isMeta / isSidechain. <synthetic> model and tool_result user rows are skipped too. no self-test case for CC row parsing. |
albedo root-session transcript user rows |
done | reads row_class='user', skips sessions in session_family. checked against the live DB: the table holds only children (504 rows, all depth ≥ 1), so the query is correct. the OperationalError fallback would silently include children on an older schema (minor). summary: 2,842 human turns (v3: ~1,927 albedo + 126 CC). |
> quote stripping |
done | QUOTE_LINE_RE + BOX_LINE_RE, human only, for both CC and albedo (extract_clauses(…, is_human=True)). self-test ✓. |
reasoning_text exclusion |
done | replay items with type ∈ {reasoning, reasoning_text, function_call(_output), tool_use, tool_result} are skipped, role ∉ {None, assistant} is skipped, and content blocks of those types are dropped. |
| ETF 80 / 77 / 111 + error counting | done | tag 80 decompresses with a size check (only valid at top level, which is the only place ETF allows it). 77 and 111 are decoded. every decode except now increments etf_decode_errors, reported in the summary (= 0). 88/90/113/117 still raise, so they're counted, not swallowed ✓. payloads not starting with 131 are skipped uncounted (minor). ETF round-trip incl. tag 80 is in the self-test. |
| model-family grouping | done (weak in effect, §3.4) | provider_to_family maps clod/claude/anthropic → claude etc. min_families=2 is enforced. self-test: tangled-clod + cc:claude-opus-5 is rejected. |
| 50/50 pooled + unigram-product prior | partial (§3.1) | formula as documented. all 149 rows' δ and z recompute exactly from stored alpha_w, N_llm = 175,755, N_hum = 23,618, α₀ = 5000 (0 mismatches). δ is no longer one constant overall (bigrams range 0.96–2.55). |
I + (/[ token variants |
done | 4 bases (as-is, first-cap, pronoun-I, first-cap+I) × 9 prefixes ("", " ", "\n", '"', ' "', "(", " (", "[", " ["). re-encoded with tokenizers + Qwen/Qwen3.8-Flash-Next: all 2,808 surface variants are 2–8 tokens and map to 2,808 unique tuples, equal to the stored qwen_token_id_tuples set. per-phrase tuples are now stored (banned_ngrams[].qwen_token_id_tuples). " so I'll" → ĠI ✓, "(let me" → (let,Ġme ✓. 0 misses on spot checks. tokenizer load now raises unless --allow-no-tokenizer ✓. |
| genre-matched human reference (v3 #4) | not done; handled by filtering instead | --human-ref-dir is still optional and wasn't used. the confound was attacked with CONVERSATIONAL_ANCHORS + TECH_STATUS_WORDS vocabulary filters. that removed the status words but not the confound (§3.3). |
| self-test (v3 #5) | mostly done | added: ETF round-trip, phantom n-gram, curly, quote strip, sub-tuple, family filter, non-constant δ, I/paren variants. still missing: a CC row / isSidechain case, an albedo user-row case, a numeric z recompute, and a 3/4-gram c_hum=0 case at realistic α₀ (the δ test uses only bigrams at α₀=50, which is exactly why §3.1 slipped through). |
3. remaining issues #
3.1 the c_hum=0 cancellation survives for 3/4-grams #
p_indep is a product of unigram probabilities normalized over the whole mixed-order vocab. a 3- or 4-gram's product is orders of magnitude below a bigram's, so after normalization p_indep ≈ 0, α_w ≈ 0.5·α₀·c_llm/N, and c_llm cancels again:
δ → log((N_h+α₀)/(N_l+α₀)) + log(1 + 2N/α₀) = 2.548.
measured: 37/108 c_hum=0 rows have δ ∈ [2.53, 2.55], all of them order 3 (27) or order 4 (10). for those, z ∝ √c_llm, which is the same raw frequency floor v3 flagged. the reference pooled_log_odds_delta sits at 1.868 for every c_hum=0 row, which matches v3's analysis (the PLAN #26 text is right about the mechanism, wrong that it's fixed for all orders).
fix: normalize p_indep within each order (scale each order's p_indep to that order's pooled mass), or use an external background corpus as v3 suggested. then add a 3-gram case to the self-test.
3.2 conflict with abstention training (the most important one) #
PLAN §2.4 L108: "target = "not sure / let me check" in persona voice". §2.7 L193 says the same. the banned list contains let me (rank 1), let me check (rank 6), let me check whether/how/what, let me verify, let me confirm, let me see, let me look, let me find. FTPO in Phase 4 would push down exactly the tokens Phase 3 trained in as the honest-abstention response. separately, i haven't checked, i didn't run, i haven't run and i don't think are honest-uncertainty disclosures. the protected list covers i don't know / i'm not sure but not these.
this needs a decision from dawn: either (a) protect let me check and the honest-disclosure set (and keep them out of FTPO), or (b) change the abstention target wording to something not on the list (e.g. "not sure, i'd have to look it up" / a bare search call) and write that down in §2.4.
3.3 genre confound shifted, not removed #
by my hand classification of the 149 rows:
- ~73 plausible conversational LLM-isms:
let me …,want me to,(do) you want (me to),if you want/'d rather,so i'll/i'm/i'd,rather than,so far,two/three things,(is) exactly what,you're right,right away,your call,here's the/what,end to end,short version,one catch,catch is,i'd rather/go/keep/leave/add/make/want,i lean,my pick,what's actually,instead of guessing,whenever you want,lem me(a Qwen persona-voice tic; real text, e.g. "lem me look around"). - 39 first-person action reports:
i checked,i'll check/read/run/write/look/add/make/start/pick,i'll report (when),i made,i put,i used,i moved,i filed,i tried,i took,i went,i gave,i broke,i looked,i dropped,i started,i told (it),i said,i write,my earlier,what i checked, … these show up because dawn's human turns are requests and the LLM turns are work reports. banning them teaches an agent persona not to narrate what it did. - 37 generic English:
would make,would mean,what happens,which means/makes/matches/needs,can reach,can write,can actually,can never,keep their,to catch,it's cheap,and the summary,exact same,exactly one,you said/say/meant/wanted,that's fine/probably,there's nothing (to),check what,gives you, …
so the list is not yet "clean conversational/rhetorical LLM-isms". it's about half. fix: either a genre-matched human reference via --human-ref-dir (dawn's own reports/docs/commit messages, which contain i checked/i made), or an explicit ban on bare i <past-verb> / i'll <verb> patterns outside RHETORICAL_ALLOWLIST, plus a manual pass over the tail (z < ~2.5).
3.4 cross-family filter is weak #
74/149 rows are ≥ 90% Claude, and 45/149 clear min_families = 2 with ≤ 3 non-Claude occurrences (i lean 53:1, i broke 46:1, i used 53:1, i checked that 54:1, i haven't checked 99:2). Claude is 71% of assistant turns, so a stray hit from any other family is enough. fix: require ≥ 2 families with ≥ max(5, 5%) of the phrase's count each, or z-score per family and keep the phrases significant in ≥ 2 of them.
3.5 minor #
- memory says "2,898 Qwen token tuples". the JSON has 2,808 (149 phrases). memory drift, not a code bug.
--human-ref-diris still optional (PLAN §9 asked for required). accepted in v3 since dawn's turns load automatically.- the variance still treats overlapping mixed-order n-grams as independent, so z is overstated. no change since v3, and it doesn't affect ordering much.
4. fix list (ordered) #
- decide 3.2 (protect
let me check+ honest-disclosure phrases, or change the abstention wording in §2.4) before any FTPO run. - per-order
p_indepnormalization (3.1) + a 3-gram self-test case. - genre-matched human ref or a first-person-action pattern filter (3.3), then a manual tail pass.
- stronger family filter (3.4).
- PLAN §5.2 "17.4–21.1%" → "17.4–19.9%". paste the
calibration_mix_v2.txtsha256 from the next Colab run. - self-test: CC/
isSidechainrow, albedouserrow, numeric z recompute.
nothing was written except this report.