HOMELAB-2218: fix(redact): keep optional-chaining expressions and header docs unmasked #66
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "plane/HOMELAB-2218-redact-fp"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part 2 of HOMELAB-2218 (child of HOMELAB-2194). Part 1 landed as PR #65 (
efdec6c).Why
An independent false-positive sweep of the merged engine over the repo's tracked files found two shapes that are not secrets being masked:
Both are source/doc text: a masked source line corrupts a
readand aneditcan write the mask back into the file — the damage the ticket's must-not-mask requirement exists to prevent.What (final state after five review rounds)
redact/patterns.tslooksLikeOptionalChainCall(): inkey: valueand spacedkey = valueform, a value that contains?., contains a(or[, and is a well-formed identifier/dot/call/index chain (env.MCP_TOKEN?.trim(),obj?.["k"],fn?.(1),cfg?.keys[0]) is source, not a credential.KEY=valuebranch has no?.guard, on purpose: aKEY=<expr>?.<call>()line does not occur in real.env/INI/git-credential/query text (69k-file grep, none) and that branch is the densest credential surface in the engine, so it keepsmain's behaviour.authorizationrulevalidate: skips a value followed by a(<placeholder>tail (32-char window) — the documentation shape only.Basic Zm9vY2Jhcg==(x)and…<note>are still masked.redact/tests/credential-cost.test.ts(new): the deterministic population behind the rate claims, committed so a reader can reproduce them. Fails a revision that leaks more — it fails 7/7 one034292.redact/tests/corpus.test.ts— both directions, verified load-bearing on every prior engine: run againstmain→ 2 MUST KEEP failures;8719167→ 2 MUST MASK;1ed1aaa→ 1 MUST KEEP;a6d7e60→ 1 MUST MASK + 1 MUST KEEP; HEAD → 0.redact/README.md— the final rule, the measured cost, and every residue.Verification
npx vitest run99/99;tsc --noEmitclean.?.-bearing passwords, six assignment shapes, asserted as exact counts intests/credential-cost.test.ts): the engine's mask set is identical tomain's — 2.3 % un-masked inPASSWORD=/?password=/INI/git-credential, 17.5 % inkey: value/key = value, same asmainon every shape. The earlier revisions of this PR measured 33.0 % / 27.5 % / 36.3 % on it, which is why the guard was narrowed twice.*/tests/*onlyredact/README.md(10) andredact/patterns.ts(3 — its ownscheme://user:…@hostdescription strings, the same 3 thatmainproduces).mcp-bridge/index.tsandpi-otel-telemetry/settings.tsare clean.Accepted residues (measured, documented, not hidden)
token: cfg?.apiKey— a?.chain with no call and no index is masked; identical tomain, indistinguishable from a password.password: Pa?.ss(word)— chain-shaped, so kept. The cost of the guard, and §Verification above bounds it.password: Ab9(zzKlm2—looksLikeCode()treats anyident(prefix as a call. Pre-existing onmain.MCP_TOKEN=env.MCP_TOKEN?.trim()— masked, by design (tight branch keepsmain).redact/README.mdandredact/patterns.tsremain: the engine masks its own documentation about credentials. Pre-existing onmain.Nothing changes for vendor-prefixed tokens, URL credentials, JWTs, PEM keys, k8s Secret data, OpenBao/Vault tokens, or any
KEY=value/key: valuecredential assignment beyond the residues above.Response to review round 1 (REQUEST_CHANGES on
8719167) — fixed in1ed1aaa/\?\./alone unmasked real passwords (db_password: Xu7?.ka9PmZ2)?.and(—looksLikeOptionalChainCall(). Verified:db_password: Xu7?.ka9PmZ2,API_KEY = Xu7?.ka9PmZ2,PASSWORD=…,password: Pässwörd?.ß123allcount=1again; the twomcp-bridge/settings.tsFPs staycount=0. Corpus test added for all four.?.alone is not enough (the password trade-off) and thatlooksLikeCode()already covered the no-?.call case.(or<(<placeholder>tail (input[i] === "("&&/^\s*</), the documentation shape. Verified masked again:Basic …==(x),Basic …==<note>,Bearer …(real). Corpus test added.(<placeholder>tail, not a claim about "real header values", and drops the<-opener claim (that shape never reachesvalidate).looksLikeCode).redact/README.mddocuments both guards and the?.trade-off.Verification at
1ed1aaanpx vitest run92/92 (was 90),tsc --noEmitclean.redact/README.md(8, documentation examples) andredact/patterns.ts(3, the pre-existingscheme://user:secret@hostdoc line). No source file is masked;mcp-bridge/index.tsandpi-otel-telemetry/settings.tsare clean.mcp-bridge/index.ts:63line quoted in full istoken: env.AGENT_SANDBOX_MCP_TOKEN?.trim() || env.MCP_TOKEN?.trim() || undefined,.redact/README.md.New head
1ed1aaa; a delta re-review is running.🤖 Generated with Claude Code
Round-2 (delta) review findings: - The guard only recognised `?.` followed by `(`, so the shape this PR exists for in its most common form — a pretty-printed object literal `{ token: env.MCP_TOKEN?.trim() }` — is kept only by accident and a digit-free `cfg?.apiKey` chain was still masked. The predicate now accepts `?.` + a call, `?.[`, or a digit-free member chain (`cfg?.apiKey`); a password carrying digits (`Xu7?.ka9PmZ2`) stays masked. - The tight `KEY=value` branch returned before the guards, so `MCP_TOKEN=env.MCP_TOKEN?.trim()` was masked while the spaced form was kept. Same source shape, different answer — the guard now applies there too. Real secrets in that branch (no `?.`) are unaffected: `PASSWORD=Zx9!kLm(2`, `DB_PASSWORD=Ab9(zzKlm2`, `PASSWORD=Xu7?.ka9PmZ2` all still mask. - Comment honesty: the residue (`looksLikeCode()` skips any `ident(` prefix, so `password: Ab9(zzKlm2` is not masked) is now stated instead of implied safe. - Placeholder window widened from 8 to 32 chars so a longer `(<placeholder>` spacing does not defeat the documentation test. - Cosmetic: unused `s` parameter is `_s`. - Corpus: MUST KEEP gains `{ token: env.MCP_TOKEN?.trim() }`, `token: cfg?.apiKey`, `obj?.[k]`, `fn?.(x)` and the tight `MCP_TOKEN=…` form; MUST MASK gains the tight-branch secrets. README documents the digit tiebreak and its cost. 92/92 tests, typecheck clean, sweep 582 files: 43 masks, none outside tests/docs.Response to review round 2 (delta, on
1ed1aaa) — fixed ina6d7e60First, one correction — finding 1 did not reproduce at
1ed1aaa. Measured directly against the head engine:1ed1aaaalready hadv.includes("?.") && v.includes("(")in the unquoted branch, which is exactly this shape.origin/mainmasks it;8719167and1ed1aaakeep it. So the "re-masks ordinary source code" blocker is a measurement artifact, most likely the reviewer's own live (unfixed) engine re-masking the reproducer in transit. The underlying concern was still valid, though, so I widened the predicate (below).{ token: env.MCP_TOKEN?.trim() }re-masked1ed1aaa, verified again above). Predicate widened anyway so the shape is locked by a test rather than by luck:?.+ call,?.[, or a digit-free member chain (cfg?.apiKey).KEY=valuebranch bypasses the guardsMCP_TOKEN=env.MCP_TOKEN?.trim()→ kept;PASSWORD=Zx9!kLm(2,DB_PASSWORD=Ab9(zzKlm2,PASSWORD=Xu7?.ka9PmZ2→ still masked (corpus).(still unmasked; comment implies safelooksLikeCode()skips anyident(prefix) instead of implying safety, and names it as a limitation this guard cannot fix. Behaviour unchanged (pre-existing onmain).?.-only /?.[/?.(lines{ token: env.MCP_TOKEN?.trim() },token: cfg?.apiKey,obj?.[k],fn?.(x),MCP_TOKEN=env.MCP_TOKEN?.trim(); MUST MASK gains both tight-branch secrets.sparameter_s.Verification at
a6d7e60npx vitest run92/92,tsc --noEmitclean.{ token: env.X?.trim() },token: cfg?.apiKey,obj?.[k],fn?.(x),vault.read(p)?.data.conn_str,MCP_TOKEN=env.X?.trim()→ all count 0;db_password: Xu7?.ka9PmZ2→ count 1.redact/README.md(8) andredact/patterns.ts(3) outside test fixtures.?.(Ab?.kLmZq) is treated as a code chain. That is the tiebreak the reviewer's suggested predicate implies; it is written intoredact/README.mdrather than left implicit.CI is green on both prior commits and running on
a6d7e60; a further delta re-check is running.🤖 Generated with Claude Code
Response to review round 4 (on
e034292) — fixed in3510d29Finding 1 was correct and well measured:
?.plus any delimiter still un-masked 31.5 % (env), 35.1 % (key: value) and 26.2 % (query) of?.-bearing passwords. Both proposed remedies are applied, plus a stricter shape test:KEY=valuebranch now has no?.guard (back tomain's behaviour). Confirmed your grep: no realKEY=<expr>?.<call>()assignment exists.env.MCP_TOKEN?.trim(),obj?.[k],fn?.(x),cfg?.keys[0]). A password that merely contains a matched pair (aB3!x?.yz(cd) is not a chain and stays masked.Measured with my own deterministic 2 000-password fuzz (
?.planted in 12-20-char symbol strings):PASSWORD=<pw>password: <pw>?password=<pw>password=<pw>The credential cost is now indistinguishable from
mainin every form.Other findings:
KEY=value/key: valuesecret") are gone. Residues are listed explicitly, including the delimiter/chain cost.cfg?.keys[0],was non-load-bearingtoken: cfg?.keys[0]andtoken: env.MCP_TOKEN?.trim(). Verified by running the current corpus against each prior engine —mainfails 2 MUST KEEP,87191672 MUST MASK,1ed1aaa1 MUST KEEP,a6d7e601 MUST MASK + 1 MUST KEEP, HEAD 0.cfg?.apiKeyoverclaimcfg?.apiKeya kept chain (it is masked, as onmain, and stated as a residue).token: env.MCP_TOKEN?.trim()).redact/README.md: "up to 32 characters of tail are inspected".npx vitest run92/92,tsc --noEmitclean, 16/16 matrix, sweep 582 files → 51 masks (outside tests only README 10 + patterns.ts 3, both self-documentation, pre-existing onmain).A round-5 delta re-check is running on
3510d29.🤖 Generated with Claude Code
Independent review — round 5 (delta
e034292..3510d29)VERDICT: APPROVE — no blocker remains. The engine at
3510d29is verified correct in both directions by my own measurements (six engines compared:main,8719167,1ed1aaa,a6d7e60,e034292, head). Findings below are [CONCERN]/[SUGGESTION] (prose accuracy) only.1. Round-4 [BLOCKER] 1 is gone — measured, not quoted
2000 deterministic
?.-bearing passwords, un-masked count per context, with set hashes compared (not just counts):maine034292PASSWORD=<pw>password: <pw>?password=<pw>password = <pw>(spaced)set(head) == set(main)isTruein all nine buckets (no-delim / has-delim / all). Targeted matrix (5?.values ×.env, git-credential, INI[db],?password=,PASSWORD=,password:, JSON"password":) → count 1 in 34 of 35; the single 0 (password: aZ9?.bQ, 7 chars) is the pre-existingv.length < 8rule and behaves identically onmain.2. KEEP direction at head (count)
{ token: env.MCP_TOKEN?.trim() }0 (main 1) ·token: env.MCP_TOKEN?.trim()EOL no comma 0 (main 1 — now load-bearing) ·cfg?.keys[0]without comma 0 (main 1) ·obj?.[k]0 ·fn?.(x)0 ·vault.read(path)?.data.conn_str0 · long||chain 0 (main 1). Residues behave as documented:MCP_TOKEN=env.MCP_TOKEN?.trim()1,token: cfg?.apiKey1 (both same asmain).3. No new leak class
40-case credential matrix (PEM, JWT, URL creds,
Authorization, k8sdata:/stringData, hvs., AWS, age,--password, .netrc, HCLprivate_key, query params). Nothing leaks at head thate034292masked — head is a strict improvement in the credential direction. The only cases that leak at head and not onmainare well-formed chains with?.and a matched delimiter inkey: valueform (password: Pa?.ss(word),db_password: svc.acct?.get(pw),password: cfg?.get(1)?.x) — exactly the rule/residue the README states. Same-value check on the densest surfaces (=, query): all masked.4. No new false positives — 1689 tracked files, 5 repos
head 149 masks vs main 160. No file masks more at head than main. Per-line comparison on the four files that differ:
head-only masked lines = []in every one. Masked-output hashes: head vs main differ only inmcp-bridge/index.ts(1→0) andpi-otel-telemetry/settings.ts(2→0) — the two FPs fixed — plus redact's own README/patterns/tests (text changed). Of 36 realname = expr?.call(...)lines found in the corpus, exactly 2 are masked at head and both are inside redact's own docs/tests — no real code line. The "69k-file grep found none" claim holds for the credential-keyed case in my 1689-file sample.5. Test adequacy — reproduces exactly
Current corpus against each engine:
main2 fails (both MUST KEEP) ·87191672 (both MUST MASK) ·1ed1aaa1 (MUST KEEP) ·a6d7e602 (1 MASK + 1 KEEP) ·e0342921 (MUST MASK) · head 0 (vitest run92/92,tsc --noEmitclean). The new MASK lines (PASSWORD=Pa?.ss(word),DB_PASSWORD=a?.[b]) do fail ona6d7e60(and one034292), so they are load-bearing as claimed. No new line is inert except the over-reach pins the comment already declares (obj?.[k],fn?.(x),cfg.get(...),?? process.env...).[CONCERN] A — the fuzz table in the commit message and this PR body claims an improvement over
mainthat is arithmetically impossibleredact/patterns.ts—credentialValueOk(),sep === "="branch.Reproducer:
git show main:redact/patterns.ts— main's=branch is byte-identical to head's (return !/^[A-Za-z_$][\w$.]*[,;:)\]}]+$/.test(v);, no guard either side). Forkey: value, head's mask set ⊆ main's by construction. So head can only be equal or worse thanmain, never better. Measured: identical leak sets.Yet "Verification" says
PASSWORD=3.9 % → 2.8 %,?password=4.5 % → 2.8 %,password:14.5 % → 13.8 %, and the commit table repeats it. It is also self-contradictory:redact/README.mdin the same commit says "the same as onmain(2.8 % vs 2.8 %) …key: valueis 13.8 % vs 13.7 %". Correct statement: "un-masked rate is unchanged frommainin every=/query form (identical sets on 2 000 samples) and 0.1 pp worse inkey: value; versus the previous revision of this PR it falls 22.2 % → 6.4 % (PASSWORD=), 31.1 % → 20.4 % (password:), 20.0 % → 6.4 % (?password=)."[CONCERN] B — README calls the guarded branch "
key: valueform"; it also covers spacedkey = valueredact/README.md, "Optional-chained expressions" bullet;redact/patterns.ts:172(sepmatches[ \t]*=[ \t]*and:[ \t]+).Reproducer:
password = Pa?.ss(word)→ count 0 at head, count 1 onmain. Un-masked rate forpassword = <pw>and INIpassword = <pw>is 20.4 % vs 6.4 % for the tight form.patterns.ts's own comment says "key: value/key = value" — the README should say the same, otherwise the documented "the tight branch keepsmain" reassurance reads as if INI-with-spaces were safe.[SUGGESTION] C — load-bearing comment in
corpus.test.tsovercountsredact/tests/corpus.test.ts:501— "LOAD-BEARING … the first three lines plus the four …". Per-line measurement onmain: masked lines are L1, L2, L4, L5 only. L3 (const secret = vault.read(path)?.data.conn_str;) is count 0 on all six engines, so it is an over-reach pin, not load-bearing. Either reword or move it below.Measured with absolute-path
.mtsprobes importing each engine'sredactor.ts(vite-node), engines materialised fromgit archiveinto/tmp; masks counted numerically, never judged from displayed text. No blocker found — nothing in this delta breaks the ticket's must-mask or must-not-mask requirements.?.guard (review round 5)Round 6 approved the engine and found two committed *claims* that the code does not support. Both are one-line fixes, no engine change. - `tests/credential-cost.test.ts`: the "main baseline" comment quoted 6.4 % / 20.4 % / 6.4 % — numbers this generator does not produce (main gives 2.3 % / 17.5 % / 2.3 %, i.e. 9/400 and 70/400). The comment now quotes the measured counts, and the test asserts the EXACT unmasked count per shape as well as the ceiling, so a stale figure cannot survive in this file. The `key: value` ceiling drops 0.25 → 0.20 (the old band tolerated a 24.9 % regression silently). - `tests/corpus.test.ts`: the load-bearing list was still wrong. Measured per line against `origin/main`: the `||` chain, the comma-less `token: env…?.trim()`, `{ token: …?.trim() }` and the comma-less `token: cfg?.keys[0]` are masked there; `vault.read(...)`, `obj?.[k]`, `fn?.(1)`, `cfg.get(...)` and `?? process.env…` are kept on every engine and pin against over-reach instead. The comment now says exactly that. - `README.md`: the "does not spend a credential that `main` kept" clause is scoped to the measured population, with the `password: Pa?.ss(word)` residue named in the same breath, so the property claim and the residue list no longer contradict each other. - `patterns.ts` header: `k8s-secret.ts` → `structural.ts` (the structural rules live there; no such file exists). Pre-existing doc drift on `main`. 99/99 tests, typecheck clean. Engine files (`redactor.ts`, `structural.ts`, `index.ts`, pattern rules) untouched.Round 7 found that the previous commit message claimed a `patterns.ts` edit that was not in the diff, and that the "33.0 %" figure only holds for `.env`/INI/ git-credential. Both corrected here, plus the remaining quote drift and a stronger assertion. - `patterns.ts` (now actually in the diff): - `obj?.[k]` / `fn?.(x)` → `obj?.["k"]` / `fn?.(1)`, matching the corpus lines and the README. - the round-4 measurement is stated per surface: 33.0 % un-masked in `.env`/INI/git-credential, 27.5 % in query strings, 36.3 % in `key: value` form (the previous "31 %" / "33.0 %" single figure was wrong for the union). - `tests/credential-cost.test.ts`: same per-surface wording, and the test now asserts the SHA-256 of the un-masked index set (`4ce321960436f828` tight, `3b837123dc24652b` spaced, both equal to `main`'s) in addition to the exact counts — the README claims set equality, so the suite should check a set, not a size. 99/99 tests, typecheck clean. No engine behaviour change.Independent review — round 8 (delta re-check of
830ddc7, freshpi -p --no-session)VERDICT: APPROVE — nothing blocks. All four round-8 claims are true as written, and every number in the commit message and PR body reproduces from my own measurements.
Working tree clean at
830ddc7; all scratch work in/tmp/rv6.1. Mask-set equality head vs
main(measured, per shape)Population reconstructed from the committed generator (LCG,
SYM, seed 2218, n=400 — I verified all 400 contain?., 400/400 unique). Engines: head,git archive origin/main, ande034292(round-3).mainun-maskedPASSWORD=<pw>4ce321960436f828both?password=<pw>4ce321960436f828password=4ce321960436f8284ce321960436f828password: <pw>3b837123dc24652bbothpassword = <pw>3b837123dc24652b3b837123dc24652bHead differs from
mainnowhere — identical index sets, not merely equal counts. The two committed hash constants equalmain's measured sets exactly.2. The committed test is real
main(materialised with the current tests) and fails 7/7 one034292(un-masked 132/400,110/400,145/400vs baselines, plus thePa?.ss(word)residual).99 passedboth times; generator verified deterministic (LCG, no clock/random).toBe(shape.exactUnmasked)— exact counts, so a 33 %- or 30 %-leak revision cannot pass./tmp(one password forced un-masked, one forced masked) — all six shapes kept their exact counts (9/9/9/9/70/70) and all six failed on the hash (9832481afe5f9019≠4ce321960436f828,18f2c48f03263e46≠3b837123dc24652b). That is precisely the class of regression the README's "set equality" claim needed covered.count === 0metric is not weak here: over all 2400 (shape, password) pairs it is exactly equivalent to!text.includes(pw)(0 mismatches, 0 partially-masked), so the rate is measuring what it says.3. B and C verified
e034292the delimiter-only guard (v.includes("?.") && /[([]/.test(v)) un-masks 33.0 % in.env/INI/git-credential (132/400), 27.5 % in query strings (110/400), 36.3 % inkey: value(145/400) — exactly the triple now stated atredact/patterns.ts:114-116andredact/tests/credential-cost.test.ts:6-7. The removed "~31 %" single figure was indeed wrong: query's un-masked set is a strict subset of the tight set, so any single number spanning both surfaces is wrong for at least one of them.redact/patterns.ts:106now reads`obj?.["k"]`, `fn?.(1)`, `cfg?.keys[0]`; the PR body andREADME.mduse the same forms;grepfinds no remainingobj?.[k]orfn?.(x)anywhere outsidenode_modules. Both corrected forms are genuine engine behaviour inkey: value(head=0), so the comment is truthful — see finding 1 for one nuance.4. Suite and CI
npx vitest run→ 99/99 (4 files), twice.npx tsc --noEmit→ exit 0, no output. Forgejo run #139 on830ddc7a07(PR #66): all 14 jobssuccess, includingredact(29 s).5. No behavioural change in this commit
git diff 830ddc7~1..HEAD -- redact/{patterns,redactor,structural,index}.ts= one hunk inpatterns.ts, 0 non-comment lines (7+/5−, all inside the/** */block abovelooksLikeOptionalChainCall). Wider proof: head vs3510d29over all 583 tracked text files → 0 differing files, identical total (51 masks); since3510d29the engine files have 0 non-comment changed lines. The commit's "No engine behaviour change" is literally true for the whole post-round-4 tail.6. Honesty of the commit message and PR body — every claim reproduced
patterns.tsis now in the diff (12 lines)git show --numstat 830ddc7→7 5 redact/patterns.ts= 12 diff lines, content exactly as describedpatterns.tsedit not in the diff5b13bf4says "patterns.ts: the same 31 % → 33.0 %…" andgit diff --name-only 5b13bf4~1..5b13bf4= README, corpus, credential-cost onlymainevery shapee034292"main→2 KEEP,8719167→2 MASK,1ed1aaa→1 KEEP,a6d7e60→1 MASK+1 KEEP, HEAD→0README.md(10) andpatterns.ts(3)mcp-bridge/index.ts,pi-otel-telemetry/settings.tsclean (FP premise)main, kept on head; 3,6–9 kept on both; "adding the trailing comma flips the last two"main(count 0)input.slice(i + 1, i + 33)in theauthorizationvalidateKEY=<expr>?.<call>()does not occur in real.env/INI/query text"~/repos+~/.config: 49 files, all minified JS bundles, no.env/.ini/credential-file hitFindings (non-blocking)
redact/patterns.ts:106— the two "quote-drift" examples are now consistent with corpus/README (which was the round-7 ask), but note they are the one pair in that list thatmainalready kept:token: obj?.["k"]ishead=0 / main=0, while the unquoted index formtoken: obj?.[k]ishead=0 / main=1— i.e. the unquoted form is the one the guard actually rescues, and it no longer appears in the comment. Nothing false is stated (both are expressions, the guard covers both,cfg?.keys[0]also covers a numeric index), so this is an example-selection nit: the comment's example set now contains no rescued form exceptenv.MCP_TOKEN?.trim().redact/tests/credential-cost.test.ts:65— the "un-masked" predicate iscount === 0. Today that is provably identical to!text.includes(pw)(2400/2400 pairs, 0 mismatches), so nothing is being hidden; but if a future rule ever masked only part of a value, a partial mask would score as masked and the pinned rate would understate the leak.expectMasked()incorpus.test.ts:19-25already uses the substring form, so using it here too would keep the two directions of the contract on one definition. Does not change any number in this revision.No [BLOCKER] or [CONCERN] findings. The round-7 blocker (a claimed-but-absent
patterns.tsedit) is genuinely fixed: the edit is in this commit, and its content matches the message word for word.Blocker findings resolved — merge gate closes at
830ddc7All blocker findings raised across the seven review rounds are resolved in the diff now under review (
830ddc7). None are open; the final verdict on this head is APPROVE (round 8, posted above).?.-guard blocker (round 1,1ed1aaa) — resolved./\?\./no longer decides anything; the guard requires a whole-value identifier chain with a call or an index. Pinned bytests/credential-cost.test.ts(7/7 fail one034292) and the MUST MASK corpus.1ed1aaa, re-measured at head) and locked by a MUST KEEP line so it cannot regress silently.KEY=valuedigit-tiebreak leak (round 3,a6d7e60) — resolved ine034292and finally in3510d29by removing the guard from the tight branch entirely; measured mask set now identical tomain.e034292) — resolved in3510d29by the whole-value chain predicate; per-surface rates and index-set hashes are asserted in the committed test.patterns.tsedit (round 7,5b13bf4) — resolved in830ddc7: the edit is in the diff (7+/5−, comments only) and the message matches it.Open finding count: zero. CI run #139 on
830ddc7: 14/14 green. Landing lock legs: CI green ✔ · review verdict post-dates head ✔ · no open blocker ✔.