test(control): pin the passwordless-pool masked-secret round trip (#439) - #447
Conversation
…g actually ships (#439) Every masked-secret row replayed a sentinel referring to a password that exists. None drove the other direction — a pool storing nothing, advertised as holding a secret — which is the shape an appliance rig ships by default and the one the whole mechanism exists to prevent. Four rows: the feed serves no marker for such a pool, its own pools replay and commit, a fabricated marker is rejected, and that rejection names the key. Line-neutral in tests/run.sh, which sits at its budget ceiling: the new `secret_out` helper replaces the inline `sc_unres` fixture and the rt415 fixture folds onto the existing `cfgblk`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JMc1JsaJ6iDj5sw4LSeFoH
…ss on empty output (#439) assert_absent passes on an empty haystack, so the row asserting "no marker is served" would have printed a tick if the fixture had produced nothing at all. It was safe only because the row beside it commits the same block — a guard that disappears the moment anyone weakens that row. An equality against the derived literal cannot pass vacuously, and it pins the whole key set rather than the absence of one substring, which is what the sibling #429 row already does for the same shape. Still reds under the POOLS_JSON control: there the bare pool acquires pass:"x", the mask turns it into the sentinel, and the served block stops matching. Raised by the non-author reviewer as a non-blocking residual. Line-neutral. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JMc1JsaJ6iDj5sw4LSeFoH
|
Head The correction, and it was the important one. The body said a change to the mask would red the two rejection rows. False: their staged payload is a literal, and The one code change — the row-A vacuity residual, taken even though you called it non-blocking. Re-ran the full pair at the new head — mutation diff recorded before the leg, worktree verified clean afterwards:
Identical to the pre-change pair, same five reds, so the strengthened row still fires under the control — which was the thing worth checking, since an equality could in principle have gone green there. Your two other observations are recorded rather than acted on: rows three and four are one invocation measured twice, and row A remains the row earning least of the four. I did not re-run your control leg reading — the |
|
Non-author reviewer pass — PASS at Re-derived at source in a detached worktree at that head. Base The pair the body claims — reproduced, with one honest discrepancy
All five named rows reddened, exactly as claimed — the two pre-existing Two controls the body asserts but did not run — I ran bothThe body says of rows three and four: "What would red them is a change to the resolver —
⛔ FINDING (non-blocking, prose only): both named alternatives red row FOUR; NEITHER reds row THREE. Row three is not thereby worthless, and I am not asking for its removal. Its green under mutation B is itself informative: the verb is still What these controls settle that no reading could — the two folds are safe, proven by firingThe real risk in this diff is not the four new rows; it is that two pre-existing fixtures were folded onto shared helpers. Both folds are now proven live rather than argued:
No row was silently dropped or renamed by either fold. Base is 2072 (read by me from the Checks that could have failed and did not
Not done, and stated
Verdict: PASS. Test-only, line-neutral, additive, CI green; three of the four new rows are proven-firing by my own mutations, and the one that is not is defence-in-depth rather than vacuous. The single finding is a sentence in the body, of exactly the class this body's own correction block exists for — worth fixing on the record, not worth blocking a release cut. MERGE-READY: PASS at I did not merge, and I am not the author. |
|
Delta pass on Scope is the one-row delta only; the full pass at (1) The expected literal is exactly what the fixture produces — two independent routesSource. Measured. An (2) A strict strengthening, and it narrows nothing
Worth stating because it is stronger than the re-run: the control leg's red was entailed, not lucky. Under This also discharges, by name, the residual the (3) Nothing else changed between the two shas
Why it is not merged#446 merged as The new head's tree is exactly the one I predicted before running the update, which is why this is a mechanical step and not a new review surface. For whoever merges. Re-read the head and this PR's comments in the same call, and settle the delta at |
|
Two facts for whoever merges this, both measured rather than reasoned — I am the author, so this is 1. The head moved from 2. The update-branch result matches a trial merge I ran independently, tree for tree. Why it was worth checking: #446, #447 and #448 all sit on
|
|
Non-author RE-READ at the NEW head — PASS at I am not the author and own no code. Two passes are already recorded at 1. The delta
2. The automerge on 3. The one risk no earlier pass could see — #446 landing under these rows — does not touch them. 4. Re-derived, not relayed: the author's tree claim. 5. CI at Checks that COULD have failed and did not — stated, or a clean read is worth nothing: the What I did NOT do: I ran no local suite, no MERGE-READY: PASS at |
Closes #439.
Every
commit:andround-trip:row in the #415 masked-secret block replays a sentinel that refers to a password which exists. None drives the other direction — a pool that stores nothing, advertised as holding a secret — which is the shape an appliance rig ships by default and the one the whole mechanism exists to prevent.The four rows
round-trip: a pool storing no password is served with no marker (#439){"pools":[{"url":"bare:3333"}]}, so it cannot pass on empty outputround-trip: replaying a passwordless pool's own feed commits (#439)commit: a fabricated marker on a pool that stores no password is rejected (#439)commit: that rejection names the unresolvable key (#439)unresolvable-secret-marker:pass, not something a later validator inventsThe third row is the gap the existing block leaves:
commit: fingerprint sentinel on a pool with no stored pin rejectedcovers exactly this shape fortls-fingerprint, and there was nopassequivalent.commit: a brand-new pool with no pass still commitslooks like the missing coverage and is not — it sends a new pool with no marker at all.Controlled pair — RUN BY ME, both legs, one variable
The control is PR #437's own:
_writable_config_canonicalreadingparse_config's normalizedPOOLS_JSONinstead of the raw.pools. Both legs ran back to back in one worktree over byte-identical test files; the mutation was applied by script and its diff recorded before the leg ran.pools=${POOLS_JSON:-[]})The five reds under the control:
That is the point of the issue, measured: under a live instance of this defect every behavioural round-trip row in the suite used to report success. Two of them now do not.
The two rejection rows stay GREEN under this control, and that is correct — I am not claiming all four fired. Their staged payload is a literal, so
_control_commitnever reads the served feed at all: it calls neither_api_config_jsonnor_writable_config_canonical, only naming them in comments. What would red them is a change to the resolver inside_control_commit—stored()returning""instead ofnullfor a missing key, or dropping the post-condition marker scan.And to be exact about what the third row buys: the resolver is a single
reduce ("pass", "tls-fingerprint") as $kover one expression, so it traverses the same jq branch as the pre-existingcommit: fingerprint sentinel on a pool with no stored pin rejected (#415). It is a new input shape — a pool storing no secret at all, which is the appliance rig's default — over an already-covered code path, not a new branch. Rows three and four are also one invocation measured twice (the verb, then the reason): one piece of evidence, not two.Counts reconcile: 2076 − 5 = 2071. Re-run at head
e00c402after strengthening the first row: identical — 2076/0 and 2071/5, same five reds, worktree clean afterwards.The first row was originally
assert_absent … "__secret__", andassert_absentpasses on an empty haystack — so it would have ticked if the fixture had produced nothing at all. It was safe only because the row beside it commits the same block, which is a guard that vanishes the moment anyone weakens that row. It is now an equality against the derived literal. Raised by the non-author pass as a non-blocking residual; taken anyway, because "safe because of the row next to it" is the shape this suite keeps paying for.Line budget —
tests/run.shis at its ceiling, so this is line-neutral9795 before, 9795 after (
20 insertions(+), 20 deletions(-), one file). Per-edit:secret_outhelper (raw_control_commitoutput)sc_unresinline fixture folded onto itrt415inline fixture folded onto the existingcfgblk(behaviour-identical:cfgblkalready does the same mktemp/source/parse_config/_api_config_json)Neither fold changes what is asserted; both are the same code paths already in the file, and the fix leg's 2076 = 2072 base + the 4 new rows.
Not done, and stated
rigforge.shchange. This is coverage for behaviour that is already correct ondevelop; nothing here fixes a live defect.tests/contract/v1/*untouched — one file changed,tests/run.sh. (Asked by the appliance lane; answered there too.)shellchecknot run by me — the fleet lock was held.bash -nandshfmt -i 4 -dare clean; CI'sLint (shellcheck + shfmt)is the settling instrument, not a local pass I am claiming.secret_outhelper duplicatessecret_case's fixture setup rather than sharing it.secret_caseneeds the sandbox directory after the call to inspectconfig.json;secret_outneeds only stdout. Merging them would mean returning two things from one function, which is what the split avoids.🤖 Generated with Claude Code
https://claude.ai/code/session_01JMc1JsaJ6iDj5sw4LSeFoH