Skip to content

Foundation: hygiene entropy rule accepts STAGE1/SHA256-style constant names in keyword arguments (#243) - #333

Merged
CompleteDotTech merged 5 commits into
mainfrom
foundation/hygiene-kwarg-entropy
Oct 21, 2026
Merged

CompleteDotTech merged 5 commits into
mainfrom
foundation/hygiene-kwarg-entropy

Conversation

@CompleteDotTech

Copy link
Copy Markdown
Contributor

Closes #243

What this adds

  • tools/check_hygiene.py and its mirrored copy src/jev_cookbook/fixtures/_scan.py: the bare name=VALUE exemption from the high-entropy-token rule (_IDENT_ASSIGNMENT) now accepts a run that is one-case letters followed by digits (STAGE1, SHA256, utf8, V2), on both sides. Before, a run could not mix letters and digits at all, so instructions=_STAGE1_INSTRUCTIONS (33 characters, scored as one token) was a finding. New helper regex _NAME_RUN; _RUN is built from it. Digits may only follow the letters of a run, so a1b2c3... style text still does not match. Both copies are byte-identical in these lines (tests/test_fixtures.py::test_the_packaged_scanner_has_the_same_rules_as_the_repository_scanner still passes unchanged).
  • Unchanged and re-tested: a left-hand name containing api key, secret, token, password, access key or credential is never exempt; quoted values are never exempt; mixed-case runs, a digit before a letter inside a run, and base64 characters (+, /, =) are still scored.
  • tests/test_hygiene.py: regression cases for the three probes of the issue (including through the real CLI), fail-closed cases (secret-shaped values in keyword position), two "mutant" tests, and a backtracking guard. Details below.
  • docs/development.md, docs/fixtures.md and the module docstring of tools/check_hygiene.py: the rule description now states the run grammar.

Recipe 39's _STAGE_1_INSTRUCTIONS naming is untouched (out of scope; it may stay as is or be reverted by its author).

Decisions for the reviewers

  • Chose a narrower grammar than either suggestion in the issue. "Both sides are valid Python identifiers" would exempt name= plus any 40-character alphanumeric secret that starts with a letter (the test test_a_looser_exemption_would_hide_a_secret... runs that mutant and shows instructions=<random base62> goes unreported); "a run may mix letters and digits in any order" does the same. Requiring digits to trail the letters of a run is the discriminator that keeps random text scored while covering STAGE1, SHA256, UTF8, V2, BASE64.
  • Accepted residual (a widening of the already documented hole for one-case values): an unquoted value that is a chain of one-case letter+digit-suffix runs joined by _ or . (for example STAGE1_SHA256_...) under a non-secret-named left side is exempt. In a 160 000-sample fuzz of random secrets (base62, base36 lower/upper, base64url, lower/upper plus _, 32 to 64 chars, assigned to seven non-secret names, scanned with the old and the new rule) exactly one sample went from flagged to exempt: x=sjtpcm32_xiroprmvbaooqgii_asprlpn, a random lowercase/digit/underscore string whose underscores happened to fall at word-like boundaries (1 of 20 000 samples of that alphabet; 0 of 20 000 for each of the other seven alphabets). Hex, all-letter and all-digit values were never findings of this rule before either.
  • The mirrored copy is outside the strict path list of the issue; it has to change in the same commit because a test fails when the two diverge.

How it was verified

Tests were written first and run against the unmodified scanners (red), then against the change (green).

  • Red: with tools/check_hygiene.py and src/jev_cookbook/fixtures/_scan.py restored to origin/main (eba0506), pytest tests/test_hygiene.py tests/test_fixtures.py gives 11 failed, 510 passed: the 9 test_keyword_arguments_with_letter_digit_constant_names_are_not_findings[...] cases, test_the_three_probes_of_the_issue_through_the_command_line, and test_the_repository_itself_is_clean (the docs now name _STAGE1_INSTRUCTIONS).
  • Green, Python 3.14 and 3.10: pytest tests/test_hygiene.py tests/test_check_hygiene_generic_usernames.py tests/test_fixtures.py gives 532 passed on each.
  • Issue probes, old scanner vs new, scan_text: build(instructions=_STAGE1_INSTRUCTIONS) and verify(digest_prefix=SHA256_DIGEST_PREFIX_NAME) were high-entropy-token findings (len 33, 39) and are now clean; the _STAGE_1_ probe was and stays clean.
  • Fails closed: test_secret_shaped_values_in_keyword_position_are_still_findings (40 cases: base62, base36 lower, base36 upper, base64, base64url, STAGE1_ + random, SHA256_ + random, settings. + random; each assigned to instructions, digest_prefix, SHA256_DIGEST, utf8_name, x) asserts a high-entropy-token finding from the script, a notebook scan, run() on a .py file and the packaged copy. test_secret_names_and_random_names_stay_scored_with_letter_digit_constants covers api_key=, auth_token=, db_password=, client_secret= with ordinary constants and a random left side with a constant. The two mutant tests monkeypatch _IDENT_ASSIGNMENT to each looser rule from the issue and show instructions=<KEY> becomes unreported, i.e. the fail-closed tests do bite.
  • Re-scan of the published set: python tools/check_hygiene.py on this branch: Hygiene check passed: 611 file(s) scanned., exit 0 (3.14 and 3.10). The origin/main scanner run on this branch flags exactly two lines, both in the docs added here (docs/development.md and docs/fixtures.md name instructions=_STAGE1_INSTRUCTIONS); it flags nothing in the other 609 files, so no existing finding is hidden by the new rule.
  • Whole suite on 3.14: ruff check . clean, ruff format --check . clean (401 files), full pytest: 5951 passed, 49 skipped, 50 deselected, 31 xfailed, python tools/check_recipe_assets.py passed (461 files), python tools/render_catalog.py --check exit 0, python tools/notebook_ci.py fixtures: 51 recipe folders, 0 failed.

CompleteDotTech and others added 2 commits October 21, 2026 21:38
…naming STAGE1/SHA256-style constants (#243)

The bare name=VALUE exemption now accepts a run of one-case letters followed
by digits (STAGE1, SHA256, UTF8, V2) on both sides. Digits stay after the
letters of a run, so random text is still scored. Mirrored copy and docs
updated; tests prove secret-shaped values in keyword position stay findings.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Opus review 2 of 2 — PR #333 (foundation/hygiene-kwarg-entropy) against issue #243

  • Agent ID: reviewer 2 of 2, independent (I have not seen reviewer 1's findings), worktree agent-af2525be5652330fd
  • Model: claude-opus-5[1m] (Opus 5, 1M context)
  • Head reviewed: a3b5c3bf2e5308db71b1c1c36eaa048789b91876 — signed merge of origin/main into builder head 56f99280258a7cfdc616d2d1b0d35ea55c51c1c5, fetched with git fetch origin pull/333/head
  • Base used: 39ddfcef2f8da7a40812b46998390dde3ca7eee2 (origin/main at review time). The two scanner files are byte-identical between eba0506 (the base the builder quotes) and 39ddfce, so the body's red run reproduces exactly against either.
  • Diff: 5 files, +199 / −27 — tools/check_hygiene.py, src/jev_cookbook/fixtures/_scan.py, tests/test_hygiene.py, docs/development.md, docs/fixtures.md

My own view, formed before reading anything else

The code change is the right change, made narrowly and documented honestly. _IDENT_ASSIGNMENT's
run grammar goes from "a run is letters of one case, or digits" to "a run is letters of one case
optionally followed by digits, or digits alone", with digits only ever trailing the letters of a
run. That is the smallest grammar that covers STAGE1/SHA256/UTF8/V2/BASE64 while leaving
interleaved letter-digit text — the shape of a random secret — scored. The builder rejected both
of the issue's own suggested fixes and proved by monkeypatched mutants that each would hide a real
secret, which is exactly the "proved by mutation rather than asserted" the issue asked for. The
mirrored copy in src/jev_cookbook/fixtures/_scan.py had to change in the same commit and the
parity test genuinely enforces it (I verified by divergence). All four descriptions of the rule
are updated and none is left stale. The tests are fast, deterministic, red-verified, and each
positive case carries its own guard against guarding nothing. The repository-wide effect is nil.

One thing is wrong, and it is in the single piece of evidence recorded for the issue's last
acceptance criterion: the body's full-repo re-scan claim miscounts the findings. That is my only
must-change and it needs no code change and no new head.

Must change

M1 — The PR body's full-repo re-scan record is wrong (counts and location)

Where: PR #333 body, "How it was verified" → "Re-scan of the published set".

Claim: "The origin/main scanner run on this branch flags exactly two lines, both in the docs
added here (docs/development.md and docs/fixtures.md) … it flags nothing in the other 609 files,
so no existing finding is hidden by the new rule."

Actual, measured on this exact head. I drove the 39ddfce scanner over all 611 tracked files
of the PR tree and the new scanner over the same list, and diffed the finding sets:

=== PR-tree: 611 files
  old scanner findings: 3
  new scanner findings: 0
  HIDDEN by new rule (old-only): 3
    - docs/development.md: line 339: [high-entropy-token] inst****** (len 33)
    - docs/fixtures.md: line 250: [high-entropy-token] inst****** (len 33)
    - tests/test_hygiene.py: line 1209: [high-entropy-token] inst****** (len 33)
  NEW-only findings: 0

Three lines in three files, not two in two; 608 other files, not 609. The third is a comment
this PR itself adds at tests/test_hygiene.py:1209
(# ``instructions=_STAGE1_INSTRUCTIONS`` is 33 characters and was scored as one token.). The
builder had this number in front of it: the red run it reports in the same body fails
test_the_repository_itself_is_clean, and when I reproduced that run verbatim the failure printed
Hygiene check failed: 3 finding(s).

The conclusion — that no pre-existing finding anywhere in the tree is hidden — is true, and I
verified it independently two further ways (see "Repository-wide effect"). The defect is the
record, not the safety property. I am treating it as must-change rather than a nit for three
reasons: this sentence is acceptance criterion 4 of #243 ("the published set is re-scanned
under the new rule and the result recorded here"); it is the permanent audit trail for relaxing a
rule in the repository's secret scanner, a gate every pull request passes; and a reader who
re-runs it gets a number the record says is impossible, with no way to tell without redoing the
work whether the extra finding is benign.

What to change. Do not edit the body: CONTRIBUTING.md ("Branches, commits, pull requests")
says a description is written once and never edited after the final push of the head that goes to
the merge gate, and an edit fires a second Scope row on the same head. Post the corrected record
as a pull-request comment instead — CONTRIBUTING directs fix-round notes and receipts there —
naming all three flagged lines with their files and the 611 / 3 / 608 split, and post the same on
issue #243, which is where AC4 says the result belongs ("recorded here"). #243 currently has
one comment and it is the 2026-10-20 triage note, not a re-scan record. No code change and no new
head, so correcting this does not void an approval of a3b5c3b.

Suggestions (not blocking)

S1 — Record the known residual in the suite, not only in the PR body

tests/test_hygiene.py has no case for the one sample the builder's own 160 000-case fuzz found
flipping from flagged to exempt. I reproduced it on this head:
x=sjtpcm32_xiroprmvbaooqgii_asprlpn (33-character value, 35-character word) was a
high-entropy-token finding under 39ddfce and is clean now, because its underscores happen to
fall where every segment is lowercase-then-digits. The body discloses this clearly and correctly,
but the body is not where the next person maintaining _IDENT_ASSIGNMENT will look. Consider a
documented case next to the fail-closed block (tests/test_hygiene.py:1286-1333) — an xfail, or
an asserted-exempt case with a comment naming it as the accepted residual — so a later widening
that makes this class larger shows up in the suite rather than in a reviewer's fuzz.

S2 — The docs under-state the left side's first run (wording inherited from the old text)

docs/development.md:339, docs/fixtures.md:246-251 and the docstring at
tools/check_hygiene.py:29-31 all say "both sides runs joined by _ … where a run is letters of
one case optionally followed by digits … or digits alone". The left side's first run may not be
digits alone — the regex is _*{_NAME_RUN}(?:_+{_RUN})* — so 1_X=Y is not exempt. The inline
comments (tools/check_hygiene.py:173-174, src/jev_cookbook/fixtures/_scan.py:105-106) do say
"the left side starts with a letter run"; the prose does not. The old wording had the same gap, so
this is not a regression introduced here, but seven words in the two docs files would close it.

S3 — Observation outside this PR's scope: _SECRET_NAME does not cover a bare key

Reporting rather than smuggling a fix (common brief rule 10). _SECRET_NAME
(tools/check_hygiene.py:184-186) matches api[_-]?key but not a bare key, so on 39ddfce
already key=, key_id=, signature= and session= followed by a long unquoted one-case value
are exempt, and this change widens that same declared hole in the letters-then-digits direction:

old=[]                     new=[]  key_id=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs   (pre-existing)
old=['high-entropy-token'] new=[]  key_id=abcdefghijklmnopqrstuvwxyz123456789012          (widened here)
old=['high-entropy-token'] new=[]  signature=abcdefghijklmnopqrstuvwxyz123456789012       (widened here)

This sits inside the hole the module docstring already declares ("a long all-lowercase or
all-uppercase secret assigned, unquoted, to a name that contains none of api key, secret, token,
password, access key or credential is not caught by it"), and the shapes are artificial — a random
secret essentially never spells out the alphabet and then digits — so I would not hold this PR for
it. It deserves its own issue: adding bare key, signature, nonce, salt and session to
_SECRET_NAME would close the whole family, old widening and new.

S4 — The stale branch and worktrees the builder mentions are pre-existing and fully merged

foundation/hygiene-kwarg-false-positive exists locally only (no refs/heads/...false-positive
on origin) at 2a44c4d, checked out at
/workspace/windows-import-20261007/home/timot/Documents/projects/jev/jev-engineering/wt/foundation-hygiene-kwarg,
with a second detached checkout of the same commit at .../wt/review-115. Its two commits
(2581d08 "do not score identifier keyword arguments as high-entropy tokens", 2a44c4d "close
the secret-name and long-left-side holes in the kwarg exemption") are the ones that originally
introduced _IDENT_ASSIGNMENT for PR #115, and git merge-base --is-ancestor 2a44c4d origin/main
returns 0 — the work is fully in main. So this is dead housekeeping from #115, not a parallel
attempt at #243 and not in conflict with this PR. I did not touch it (not mine to remove); whoever
owns those two worktrees should clean them up.

What I verified, and how

The rule change itself

Probe Result
Regex before / after old _*(?:[A-Z]+|[a-z]+)(?:_+(?:[A-Z]+|[a-z]+|[0-9]+))*_*=…, new _*(?:[A-Z]+|[a-z]+)[0-9]*(?:_+(?:(?:[A-Z]+|[a-z]+)[0-9]*|[0-9]+))*_*=… — digits only ever trail the letters of a run, as the comment claims
Issue probe 1, build(instructions=_STAGE1_INSTRUCTIONS) old [(1, 'high-entropy-token', 'inst****** (len 33)')] → new []
Issue probe 2, verify(digest_prefix=SHA256_DIGEST_PREFIX_NAME) old (len 39) finding → new []
Issue probe 3, build(instructions=_STAGE_1_INSTRUCTIONS) clean before and after (non-regression)
Quoted value x="…26 upper + 6 digits…" still high-entropy-token
Secret-named left side (api_key=, token=) still high-entropy-token plus secret-assignment
Base64 padding, x=QUJD…123456== still high-entropy-token
Mirrored rule blocks tools/check_hygiene.py:170-183 and src/jev_cookbook/fixtures/_scan.py:102-115 diff to zero bytes — the body's "byte-identical in these lines" holds

Fail-closed, independently of the builder's fuzz

My own harness, my own seeds: 8 alphabets (base62, base36-lower, base36-upper, base64, base64url,
lower+digits+_, upper+digits+_, lower+digits+.) × 20 000 samples = 160 000, lengths 32 to
64, each assigned unquoted to one of 7 non-secret names (x, instructions, digest_prefix,
SHA256_DIGEST, utf8_name, value, payload), every line scanned by both scanners:

base62: 0   base36-lower: 0   base36-upper: 0   base64: 0
base64url: 0   lower+digits+underscore: 0   upper+digits+underscore: 0   lower+digits+dot: 0

Zero flagged→exempt regressions on my seeds. The builder reports 1 in 160 000 on its own seeds;
I reproduced that exact sample (S1), so the two results are consistent rather than contradictory.

Repository-wide effect — the question the brief asks

Three ways, all on this head:

  1. Old scanner vs new scanner over the PR tree (611 files): old 3, new 0, no new-only
    findings. All three old-only findings are prose/comment lines this PR adds (M1). Nothing in a
    notebook, fixture, recipe or asset changes status.

  2. New scanner vs old scanner over the 39ddfce tree (611 files, exported with
    git archive): 0 findings either way, zero difference. The new rule hides nothing that
    exists on main — there is nothing on main to hide.

  3. Footprint, the sharper form of the question since main is clean. Across all 611 tracked
    files of the PR tree I enumerated every _WORD of 32+ characters that the new
    _IDENT_ASSIGNMENT exempts and the old one did not. There is exactly one such word in
    the whole repository:

    words >=32 chars newly matching the exemption across 611 tracked files: 1
        instructions=_STAGE1_INSTRUCTIONS -> ['docs/development.md', 'docs/fixtures.md', 'tests/test_hygiene.py']
    

    i.e. the PR's own documentation example. Nothing else in the tree newly stops being scored.

Scope, and the mirrored copy the issue's paths did not list

The issue names only tools/check_hygiene.py (plus tests and docs). The builder also edited
src/jev_cookbook/fixtures/_scan.py, saying it "has to change in the same commit because a test
fails when the two diverge". Necessary, and the parity test really does enforce it. I reverted
only the mirror to 39ddfce, left the new tools/check_hygiene.py in place, and ran the suite:

FAILED tests/test_fixtures.py::test_the_packaged_scanner_has_the_same_rules_as_the_repository_scanner
  - AssertionError: _IDENT_ASSIGNMENT
10 failed, 511 passed

test_the_packaged_scanner_has_the_same_rules_as_the_repository_scanner
(tests/test_fixtures.py:1447-1467) compares _IDENT_ASSIGNMENT.pattern, and because
_NAME_RUN/_RUN are interpolated into that pattern the new helpers are covered by it even
though they are plain strings not listed in SCANNER_PATTERNS. Belt and braces: every new test in
the exemption block also asserts through packaged_scan.scan_text, so a divergence fails in
tests/test_hygiene.py too. Tree restored to a3b5c3b afterwards — git status clean,
git rev-parse HEAD = a3b5c3bf….

No other path is touched. recipes/39-*'s _STAGE_1_INSTRUCTIONS naming is untouched, as the
issue permits and the body states. This is a foundation/* branch, so the recipe allowlist does
not apply, and tools/check_recipe_scope.py agrees: scope ok: foundation pull request: the recipe allowlist does not apply, exit 0.

Docs against the code

docs/development.md:339, docs/fixtures.md:246-251, the module docstring at
tools/check_hygiene.py:29-31 and the inline comments in both scanner copies all describe the new
grammar, and I found no stale description anywhere in the tree (grepped *.md, *.py, *.ipynb,
*.yml for "letters of one case", "no run mixes", KERNEL_START_TIMEOUT, IDENT_ASSIGNMENT,
STAGE1; .pre-commit-config.yaml, .github/workflows/ and the rest of docs/ carry no second
copy of the rule). Both docs examples
(startup_timeout=KERNEL_START_TIMEOUT, instructions=_STAGE1_INSTRUCTIONS) are exempt under the
shipped regex, as claimed, and instructions=_STAGE1_INSTRUCTIONS was a finding on main, which
makes the docs line self-enforcing. The only inaccuracy is S2, which the old wording shared.

Tests: do they bite, are they slow, are they flaky

  • Red, both scanner copies at 39ddfce: 11 failed, 510 passed — exactly the body's number.
    The failures are the 9 test_keyword_arguments_with_letter_digit_constant_names_are_not_findings
    cases, test_the_three_probes_of_the_issue_through_the_command_line, and
    test_the_repository_itself_is_clean. So the new tests prove the rule, not the status quo.
  • Every positive case is guarded against guarding nothing: _scores_without_the_exemption
    (tests/test_hygiene.py:1232) asserts each line in DIGIT_RUN_KEYWORDS would score if the
    exemption were removed, so none of the 9 cases can silently go vacuous.
  • The three issue probes go through the real CLI:
    test_the_three_probes_of_the_issue_through_the_command_line (tests/test_hygiene.py:1253)
    writes probe_bad.py, probe_good.py and probe_sha.py with the issue's exact contents and
    runs tools/check_hygiene.py as a subprocess under a neutral account, asserting exit 0 and
    Hygiene check passed. All three also appear in the in-process DIGIT_RUN_KEYWORDS cases, which
    additionally check a notebook scan, run() on a .py file and the packaged copy.
  • Fail-closed is proved by mutation: _LOOSE_EXEMPTIONS (tests/test_hygiene.py:1336)
    monkeypatches _IDENT_ASSIGNMENT to each of the issue's two suggested rules and shows
    instructions=<KEY> becomes unreported — and instructions=<KEY> is itself one of the 40
    SECRET_KEYWORD_LINES cases, so the mutants demonstrably break a test that is actually in the
    suite.
  • The 160 000-case fuzz is NOT in the suite. Confirmed by reading the whole
    tests/test_hygiene.py diff: the only new loops are 9 + 40 + 6 + 2 deterministic
    parametrizations plus a two-assertion backtracking guard. The fuzz lives only in the PR body.
  • Runtime (3.14, -p no:cacheprovider): tests/test_hygiene.py alone 9.63 s (309 passed);
    the three hygiene/fixture files together 13.28 s (532 passed). The slowest test is the
    pre-existing test_the_repository_itself_is_clean at 5.2-5.7 s. The slowest new test is
    test_the_three_probes_of_the_issue_through_the_command_line at 0.18 s; every other new test
    is under 0.01 s. Not slow.
  • Not flaky. Every value is derived from a fixed seed — _random_token(seed=7),
    _random_text(..., seed=21..24), base64.b64encode(random.Random(11).randbytes(36)). Nothing
    reads the clock, the environment or an unseeded RNG. I ran tests/test_hygiene.py four times
    (alone, combined, and twice under reverted scanners) with identical results, and it passes
    identically on 3.10 and 3.14.
  • Backtracking. The guard at tests/test_hygiene.py:1356 only exercises fullmatch, so I also
    timed the new rule through the real scanner on three long near-misses: scan_text on 10 001,
    15 001 and 24 001-character words returned in 0.001 s, 0.003 s and 0.004 s. The nested
    quantifiers are safe because neither _+ nor _RUN can match empty, so a partial run
    consumption fails at the next _+ in O(1) instead of multiplying.

Checks run

Environment: fresh venvs under /home/agent/pr333-r2/ (uv venv --python 3.14 and
--python 3.10, uv pip install --link-mode symlink -e ".[dev]"), everything under
flock .claude/heavy.lock nice -n 10 with no nested flock, pytest always -p no:cacheprovider,
long runs via a background wrapper with log polling. free -m showed 2.0-4.8 GB available
throughout. Both venvs and all scratch deleted at the end; nothing else removed.

Check (docs/development.md) Result
ruff check . All checks passed
ruff format --check . 401 files already formatted
python tools/check_hygiene.py (3.14) Hygiene check passed: 611 file(s) scanned. exit 0
python tools/check_hygiene.py (3.10) Hygiene check passed: 611 file(s) scanned. exit 0
python tools/render_catalog.py --check exit 0
python tools/check_recipe_assets.py Image-asset check passed: 461 file(s) under recipes/ checked. exit 0
full pytest (3.14) 5952 passed, 49 skipped, 50 deselected, 31 xfailed in 195.17s
pytest tests/test_hygiene.py tests/test_fixtures.py tests/test_check_hygiene_generic_usernames.py (3.14) 532 passed in 13.28s
pytest tests/test_hygiene.py tests/test_fixtures.py (3.10) 521 passed in 13.32s (+ 11 in test_check_hygiene_generic_usernames.py = the body's 532)
pytest tests/test_hygiene.py (3.14, timed) 309 passed in 9.63s
python tools/notebook_ci.py fixtures 51 recipe folders, 0 failed, exit 0
python tools/check_recipe_scope.py --base origin/main --head HEAD --branch foundation/hygiene-kwarg-entropy --body-file <body> scope ok: foundation pull request: the recipe allowlist does not apply, exit 0
red run, both scanners at 39ddfce 11 failed, 510 passed (matches the body, test for test)
divergence run, only the mirror at 39ddfce parity test fails AssertionError: _IDENT_ASSIGNMENT; 10 failed, 511 passed
old-vs-new cross-scan, PR tree and main tree see "Repository-wide effect"
newly-exempt-word footprint over 611 files exactly 1 word, the PR's own docs example
independent 160 000-sample fail-closed fuzz 0 flagged→exempt regressions

No notebook is affected by this diff (no path under recipes/ changes), so no notebook was
executed; the whole-tree hygiene scan and tools/notebook_ci.py fixtures cover the notebook and
fixture files as data. pytest was never run in the primary checkout and no branch there was
touched.

PR body against the diff, claim by claim

Claim Verdict
Closes #243 and nothing else ✅ Closes appears once; #243 is the only issue reference in the whole body. The other fix/closed hits are prose ("fails closed", "fix round") with no issue number after them, so nothing else can close
5 files: the two scanners, the tests, the two docs ✅ matches git diff --stat
exemption now accepts one-case letters followed by digits, on both sides ✅
new _NAME_RUN, with _RUN built from it ✅
digits may only follow letters, so a1b2c3… still does not match ✅ by fuzz and by targeted cases
both copies byte-identical in these lines ✅ zero-byte diff of the two blocks
secret-named left sides, quoted values, mixed-case runs, digit-before-letter, + / = still scored ✅ all re-verified independently
red: 11 failed, 510 passed, and which tests fail ✅ reproduced exactly, test names included
green on 3.14 and 3.10: 532 passed each ✅ (3.14 directly; 3.10 as 521 + 11)
issue probes old-vs-new, lengths 33 and 39 ✅ exact lengths reproduced
40 fail-closed cases; two mutant tests ✅ 8 values × 5 names = 40; 2 mutants
the 1-in-160 000 residual x=sjtpcm32_xiroprmvbaooqgii_asprlpn ✅ reproduced: flagged on 39ddfce, exempt now
re-scan: Hygiene check passed: 611 file(s) scanned., exit 0, on 3.14 and 3.10 ✅ both
old scanner on this branch "flags exactly two lines, both in the docs", "nothing in the other 609 files" ❌ three lines in three files, 608 others — M1. The conclusion ("no existing finding is hidden") is nevertheless true and I verified it independently
ruff check/ruff format --check clean, 401 files ✅
check_recipe_assets.py passed, 461 files ✅
full pytest: 5951 passed, 49 skipped, 50 deselected, 31 xfailed ✅ for the head it was written against. I measure 5952 passed with the other three numbers identical; the +1 is test_decision_result_membership_and_iteration_by_question_name, the single test #332 added, which arrived with the origin/main merge after the body was written. Expected drift, not an error — and CONTRIBUTING forbids editing the body to chase it
notebook_ci.py fixtures: 51 recipe folders, 0 failed ✅
render_catalog.py --check exit 0 ✅

Acceptance criteria of #243, one by one

  1. "instructions=_STAGE1_INSTRUCTIONS and digest_prefix=SHA256_DIGEST_PREFIX_NAME pass."
    MET. Both were high-entropy-token findings under 39ddfce (len 33 and len 39) and are
    clean on this head through scan_text, through a notebook scan, through run() on a .py
    file, through the packaged copy, and through the real CLI (exit 0, Hygiene check passed).

  2. "A real high-entropy literal still fails, proved by mutation rather than asserted."
    MET. test_secret_shaped_values_in_keyword_position_are_still_findings holds 40 cases
    (base62, base36 lower/upper, base64, base64url, STAGE1_+random, SHA256_+random,
    settings.+random, each × instructions, digest_prefix, SHA256_DIGEST, utf8_name, x),
    asserted through four entry points plus the packaged copy. The mutation is real:
    test_a_looser_exemption_would_hide_a_secret_which_is_what_the_tests_above_prove patches the
    exemption to each of the issue's own two suggestions and shows the secret goes unreported, so
    the fail-closed assertions provably bite. Quoted literals and secret-named left sides stay
    scored (test_secret_names_and_random_names_stay_scored_with_letter_digit_constants plus the
    pre-existing test_one_case_values_and_long_left_sides_cannot_hide_a_secret), and my own
    independent 160 000-sample fuzz found no flagged→exempt regression.

  3. "The three probes above exist as regression tests."
    MET, and through the real CLI. test_the_three_probes_of_the_issue_through_the_command_line
    (tests/test_hygiene.py:1253) writes the issue's probe_bad.py, probe_good.py and
    probe_sha.py verbatim and runs tools/check_hygiene.py as a subprocess on each. The two
    failing probes plus the 9 parametrized cases are 10 of the 11 red failures, so they are genuine
    regression tests and not tautologies.

  4. "The published set is re-scanned under the new rule and the result recorded here; recipe 39's
    _STAGE_1_INSTRUCTIONS naming may then be left as-is or reverted, as its author prefers."

    PARTLY MET. The re-scan was really done and I reproduced it (611 files, exit 0, zero
    findings on 3.14 and 3.10; zero status change for any file that exists on main). But the
    recorded result is wrong — three flagged lines in three files, not two in two, and 608 other
    files, not 609 (M1) — and nothing is recorded on Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256) #243 itself, whose only comment is the
    2026-10-20 triage note, while AC4 says "recorded here". The second half of the criterion is
    met: recipe 39 is untouched and the body says so explicitly.

Verdict

The code, the tests and the documentation are in good shape and I found nothing wrong with any of
them: the grammar is the narrowest one that fixes the issue, the mirror edit is necessary and
mechanically enforced, fail-closed is proved by mutation and survives an independent 160 000-case
fuzz, the repository-wide footprint is a single word this PR itself introduces, and the suite is
fast and deterministic on both 3.10 and 3.14. I would approve this head unchanged the moment the
re-scan record is corrected. I withhold approval only on M1: the miscounted re-scan is the whole
of the evidence recorded for acceptance criterion 4 of #243, it concerns a gate every pull request
passes, and the correction costs one pull-request comment and one issue comment — no code, no new
head, no re-run.

OPUS-REVIEW: CHANGES-NEEDED a3b5c3b

@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Correction to the PR description's re-scan record (the head is final, so this is a comment, not a body edit), for acceptance criterion 4 of #243 and as the audit trail for relaxing the secret scanner.

Run on head a3b5c3b (611 files): the origin/main scanner, run over this branch's tree, reports Hygiene check failed: 3 finding(s), in three files: docs/development.md:339, docs/fixtures.md:250 and tests/test_hygiene.py:1209. They are the new text this PR adds (the docs/test examples that name _STAGE1_INSTRUCTIONS and the like); the other 608 files are clean. The earlier sentence in the body said "exactly two lines, both in the docs ... nothing in the other 609 files"; the correct figures are three lines in three files and 608 others. The conclusion is unchanged and was independently re-derived by reviewer 2: with the new rule, the scanner reports 0 findings on 611 files, and on the main tree (without this PR's additions) both the old and the new scanner report 0 findings, so the relaxed rule hides no existing finding.

@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Opus review 2 — addendum on the re-scan correction, PR #333

M1 is resolved. I checked every figure and quotation in both comments against what I measured on
this head and all of it is exactly true: 611 tracked files; the origin/main scanner over this
branch's tree reports Hygiene check failed: 3 finding(s) (that is the verbatim string my red run
printed, and my old-vs-new cross-scan gave the same three old-only findings) at
docs/development.md:339, docs/fixtures.md:250 and tests/test_hygiene.py:1209; all three lines
are text this PR itself adds; the other 608 files are clean; the body's superseded sentence is
quoted accurately; the new scanner reports 0 findings over the same 611 files (exit 0 on both 3.14
and 3.10); and on the main tree both the old and the new scanner report 0 findings, so the
relaxed rule hides no existing finding. Nothing is overstated, nothing material is omitted, and
the correction now sits both on the pull request and on #243, which satisfies acceptance criterion
4's "recorded here". The correction is non-code, so the head is unchanged and my earlier
assessment of the diff stands: the grammar is the narrowest fix, the mirrored copy is necessary and
parity-enforced, fail-closed is proved by mutation and survived an independent 160 000-case fuzz,
and the repository-wide footprint is the single word this PR introduces. My S1-S4 suggestions
remain non-blocking and need no action before merge; S3 (bare key/signature/session missing
from _SECRET_NAME, pre-existing on main) is worth its own issue.

OPUS-REVIEW: APPROVE a3b5c3b

@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Review of PR #333 — reviewer 1 of 2 (independent)

  • Agent: fresh Opus reviewer for PR Foundation: hygiene entropy rule accepts STAGE1/SHA256-style constant names in keyword arguments (#243) #333, reviewer 1 of 2. I formed this view before reading
    anything from reviewer 2 (I was shown none of their findings) and before reading the PR body.
  • Resolved model ID: claude-opus-5[1m]
  • Head SHA reviewed: a3b5c3bf2e5308db71b1c1c36eaa048789b91876
    ("Merge origin/main into foundation/hygiene-kwarg-entropy"; GitHub reports
    verification.verified = true, reason valid)
  • Base SHA used: 39ddfcef2f8da7a40812b46998390dde3ca7eee2 (origin/main)
  • Builder head merged in: 56f9928
  • Branch foundation/hygiene-kwarg-entropy, body opens Closes #243.
  • Fetched with git fetch origin pull/333/head and checked out the head SHA in my own worktree.

Diff (vs 39ddfce): docs/development.md 1 line, docs/fixtures.md 10, src/jev_cookbook/fixtures/_scan.py
23, tests/test_hygiene.py +163, tools/check_hygiene.py 28. No recipe, notebook, workflow or
fixture path touched; recipe 39's _STAGE_1_INSTRUCTIONS is untouched, as #243 permits.

What the change actually is

tools/check_hygiene.py:181-183 (mirrored verbatim at src/jev_cookbook/fixtures/_scan.py:113-115):

_NAME_RUN = r"(?:[A-Z]+|[a-z]+)[0-9]*"
_RUN = rf"(?:{_NAME_RUN}|[0-9]+)"
_IDENT_ASSIGNMENT = re.compile(rf"_*{_NAME_RUN}(?:_+{_RUN})*_*=_*{_RUN}(?:[_.]+{_RUN})*_*")

used at line 207 as _IDENT_ASSIGNMENT.fullmatch(word) and not _SECRET_NAME.search(word.partition("=")[0])
→ continue, i.e. the whole _WORD is dropped from the high-entropy rule only. Previously
_RUN was (?:[A-Z]+|[a-z]+|[0-9]+) and the left side's first element was a bare letter run, so a
run could not mix letters and digits at all.

I read the grammar as: left side = _* + one-case-letters-then-optional-digits, then zero or more
_-joined runs (a run being one-case-letters-then-optional-digits, or digits alone); right side the
same but also .-joined. I confirmed the implementation matches that reading, including the parts
that are load-bearing for security:

  • One case per run is enforced. [A-Z]+|[a-z]+ cannot straddle a case change, so ABCdef,
    aB, ABC1def do not match. A base62 value is therefore still scored.
  • Digits really do only trail. [0-9]* sits only after the letters. 12ABCD fails ([0-9]+
    matches 12, then a separator is required and A is not one; _NAME_RUN cannot start with a
    digit). This is what keeps a1b2c3…-style random text scored, and it is the discriminator the
    PR chose over both suggestions in the issue — correctly, in my view (see "Is the loosening safe").
  • - is not a separator, so dash-grouped keys and base64url values (which carry -) cannot
    be exempt even when one-case.
  • = appears exactly once, so base64 padding (…==) cannot be exempt.
  • ASCII only. [A-Za-z]/[0-9] are ASCII ranges in a str pattern, so a non-ASCII character in
    the word makes fullmatch fail — the exemption gets stricter, never looser, under Unicode. (A
    non-ASCII character also splits _TOKEN runs, which is a pre-existing property of the scanner,
    unchanged here.)
  • The exemption is scoped to the entropy rule. I verified directly that vendor, bearer and
    secret-assignment rules still fire inside an exempt-shaped word:
    instructions= + a ghp_ prefix and 36 sequential-alphabet characters → github-token;
    instructions= + an AKIA prefix and 16 → aws-access-key-id; api_key=<one-case 36> →
    secret-assignment + high-entropy-token (the _SECRET_NAME guard blocks the exemption).
    Local-environment rules are in a different function and unaffected.

Is the loosening safe? (my own probing)

I tried to build a realistic secret that the new exemption hides. All probe values are synthetic,
generated locally under /home/agent/; none is or resembles a real credential, and none is written
into the repository. A few of the longer probe values below are split across two code spans
(`AAA_` + `BBB`) purely so this review file is itself clean under
python tools/check_hygiene.py; read them as one string.

Realistic vendor formats are all still caught. Mixed case (Stripe sk_live_…, base64,
base64url, JWT payload segments), --grouped license keys, +///= base64 and base32 TOTP
secrets (digits interleaved) all fail the grammar. AKIA…, ghp_…, AIza…, xox?-…, JWTs and
PEM blocks are caught by vendor rules regardless of the exemption. Hex was never a finding of this
rule and still is not.

Quoted literals are still caught — the usual way a secret reaches source. " and ' are
_WORD delimiters, so license_id="ABCD1234_EFGH5678_ + IJKL9012_MNOP3456" is scored as the bare
value and flagged; only the unquoted form is exempt. Position probes (all with the same 35-char,
entropy-4.65 grammar-shaped value): kwarg quoted FLAG, assignment quoted FLAG, dict literal FLAG,
bare value FLAG, secret-named kwarg FLAG; kwarg unquoted / assignment unquoted / default argument /
f"{x=…}" / export X=… / .env-style line / printed repr CLEAN. The one-case unquoted hole was
already documented before this PR; this widens it.

Fuzz, old rule vs new. 160,000 samples, 32-64 chars, 8 flat alphabets (base62, base36 lower,
base36 upper, base64url, each of those three plus _, hex), each assigned to one of 7 non-secret
names, scored through the real _WORD/_TOKEN/entropy path with the old and the new exemption:
0 samples went from flagged to exempt. That is consistent with the builder's disclosed
"1 of 20 000 for one alphabet" — the rate for a flat random value is at or below that order.

The leak class the builder describes ("a chain of one-case letter+digit-suffix runs joined by _ or
.") is real and I reproduced it, but only for values that carry _ at word-like boundaries. On
_-grouped one-case alphanumeric chunks (4/5/6/8-char groups, 20,000 samples each) the new rule
newly exempts 3 to 13 per 20,000 (0.015-0.065 %) of what the old rule flagged — about 20x the
flat-alphabet rate, still small. .-joined values are irrelevant: . is outside both _TOKEN
character classes, so a dotted value never reached 32 characters as one token before or after.

No new false positives: in every fuzz cell the new-rule finding count equalled the old-rule
count except for those leaks; nothing newly became a finding.

Backtracking. Linear, measured. _IDENT_ASSIGNMENT.fullmatch and a full scan_text on
adversarial near-misses up to 100,001 characters ("AB12_"*n + "%", "A_"*n, "A1_"*n + "!",
"x=" + "_"*n + "a!", "a1"*n + "=" + "a1"*n, "A__"*n + "=!"): worst case 0.0039 s for the regex
and 0.0175 s for the whole line scan at n = 20,000, scaling linearly in n. The mandatory separator
between runs makes the decomposition essentially unique, so the nested stars cannot blow up. No
concern here.

I also checked whether a tighter grammar was available and it is not. Bounding the digit suffix
to [0-9]{0,3} leaks identically (11 vs 11 per 20,000 on 4-char chunks) and [0-9]{0,2} would
reject SHA256, which the issue requires. So the shipped grammar is about as tight as this family
gets, and it is tighter than either suggestion in #243: I mutated _NAME_RUN to [A-Za-z0-9]+
(the issue's "a run may mix letters and digits in any order") in both copies and all 40 of the
PR's fail-closed cases then pass silently (see M1); the issue's other suggestion, "both sides are
valid Python identifiers", is covered on one line by the PR's own _LOOSE_EXEMPTIONS mutant test,
which asserts the line is a finding before the monkeypatch and == [] after it (it passes on this
head, and it is one of the tests that go red when the shipped exemption is widened).

Overall: I think the loosening is proportionate, the grammar is the right discriminator, and the
disclosed residual is accurate in kind. My objection is to the regression net, not to the rule.

Must change

M1. The one rule this PR invents is not pinned by any test

tools/check_hygiene.py:181 and src/jev_cookbook/fixtures/_scan.py:113.

The PR's whole justification over the issue's simpler suggestions is "Digits may only follow the
letters of a run". Nothing in the suite objects if that is dropped. I mutated both copies to

_NAME_RUN = r"[0-9]*(?:[A-Z]+|[a-z]+)[0-9]*"      # digits allowed before the letters too

and ran pytest tests/test_hygiene.py tests/test_fixtures.py: 521 passed, 0 failed. The
mutation is a real widening, not a no-op:

  • These are high-entropy-token findings on the shipped head and go completely unreported under
    that mutant (verified by monkeypatching _IDENT_ASSIGNMENT and re-running scan_text):
    • instructions=12ABCD34_56EFGH78_ + 90IJKL12_34MNOP56 (entropy 5.07)
    • instructions=12abcd34_56efgh78_ + 90ijkl12_34mnop56 (entropy 4.87)
    • x=9ZQWER1_4TYUIO7_ + 2PASDF8_6GHJKL3 (entropy 4.90)
  • Fuzz on _-grouped one-case alphanumeric values (8,000 samples, 4-char chunks, non-secret
    names): the shipped grammar newly exempts 4 of 7,926 old findings; the digits-before-letters
    mutant exempts 215 of 7,926 — roughly 50x the hole, with the suite still green.

Why the existing net misses it: all 8 values of SECRET_VALUES (tests/test_hygiene.py:1288-1297)
behind the 40 cases of test_secret_shaped_values_in_keyword_position_are_still_findings
(tests/test_hygiene.py:1305) are one long flat random run, three of them behind a single
STAGE1_ / SHA256_ / settings. prefix. A flat random run fails the digits-before-letters
grammar just as it fails the shipped one (its first digit is followed by a letter with no
separator), so no case in the set can distinguish "digits trail the letters" from "digits anywhere
in a run". What is missing is a value made of several short _-joined chunks — the exact shape of
the residual the PR body discloses. The same 40 do all fail under the broader mutant
_NAME_RUN = r"[A-Za-z0-9]+" (I ran it: 40 FAILED test_secret_shaped_values_in_keyword_position_are_still_findings, plus 4 in
test_assignments_of_secret_looking_values_are_still_caught, 3 in
test_one_case_values_and_long_left_sides_cannot_hide_a_secret, 2 in
test_secret_names_and_random_names_stay_scored_with_letter_digit_constants, 2 in
test_a_looser_exemption_would_hide_a_secret_… — 51 failures), and a mixed-case widening
(_NAME_RUN = r"[A-Za-z]+[0-9]*") is caught by the pre-existing MIXED_34 cases (3 failures).
So the net is good everywhere except at the boundary this PR actually drew.

Issue #243's acceptance criterion 2 is "A real high-entropy literal still fails, proved by
mutation rather than asserted
". For the rule the PR introduced, it is asserted (in the code
comment, in both docs and in the PR body) and not proved.

What to change — either is enough, both is better, and both are small:

  1. Add a _-separated, digit-leading-chunk value to SECRET_VALUES (tests/test_hygiene.py:1288),
    assembled from pieces under 32 characters as that block already does, e.g.
    "digit-leading-chunks": "12ABCD34" + "_56EFGH78" + "_90IJKL12" + "_34MNOP56" (and a lowercase
    twin). Each is a finding today and goes unreported the moment the trailing-digit restriction is
    relaxed.
  2. Add a third entry to _LOOSE_EXEMPTIONS (tests/test_hygiene.py:1336) for "a digit may sit
    before the letters of a run", built the same way the shipped constant is built, with a value
    from (1) as the line under test — so the mutant test covers the PR's own boundary and not only
    the issue's two suggestions.

M2. tests/test_hygiene.py is clean only because of the exemption, reversing a convention this

file was explicitly given

tests/test_hygiene.py:1209 writes the literal instructions=_STAGE1_INSTRUCTIONS (33 characters)
in a comment. Running the base (39ddfce) scanner over this head flags three lines, not two:

docs/development.md: line 339: [high-entropy-token] inst****** (len 33)
docs/fixtures.md: line 250: [high-entropy-token] inst****** (len 33)
tests/test_hygiene.py: line 1209: [high-entropy-token] inst****** (len 33)

(scanned 611 tracked files; the two docs hits are the intended doc examples, and docs already
carried startup_timeout=KERNEL_START_TIMEOUT under the old exemption, so I do not object to
those.) The test-file hit is different: PR #114's fix round 2 removed exactly this coupling from
exactly this file at an Opus reviewer's instruction — "test_the_repository_itself_is_clean
depended on the exemption to keep this file itself quiet … the file's own source no longer needs the
exemption to stay clean" — and the new block's own comment at tests/test_hygiene.py:1211 still
states the convention: "the constants are assembled from pieces under 32 characters". The test
constants below it do follow it; this one comment line does not.

Change: reword the comment so the token stays under 32 characters (e.g. ``instructions=_STAGE1_...``
or break it across the line). One line, no behaviour change.

While in there: the PR body says the origin/main scanner on this branch "flags exactly two lines,
both in the docs added here … it flags nothing in the other 609 files". It is three lines, in three
files added/changed here. The substance (no pre-existing finding anywhere else is hidden by the new
rule) is correct and I reproduced it. Since CONTRIBUTING.md forbids editing the description after
the final push, record the correction in a fix-round pull-request comment.

Suggestions (not blocking)

  • S1. A digit at a word boundary in a secret-ish name now escapes _SECRET_NAME.
    api1_key=<36 one-case letters> and access1_key=<36 one-case letters> were findings under the
    base rule and are exempt now: api1 is a valid run under the new grammar, while _SECRET_NAME
    (api[_-]?key, access[_-]?key) does not match api1_key. Verified that t0ken, secr3t,
    p4ssword, api2key, apikey2, token1, auth2_token, cred3ntial are all still scored —
    the trailing-digit restriction limits this to a digit immediately before the _. The names are
    contrived, so this is a hardening note, not a hole I would block on; searching _SECRET_NAME
    over the left side with digits stripped would close it in one line.
  • S2. tools/check_hygiene.py:29-33 — the "what it does not cover" sentence still ends "so a
    long all-lowercase or all-uppercase secret assigned, unquoted, to a name that contains none of
    api key, secret, token, password, access key or credential is not caught by it." The hole is now
    wider than all-one-case: license_id=ABCD1234_EFGH5678_ + IJKL9012_MNOP3456 (35 chars, entropy
    4.65) is also not caught. The grammar is stated correctly one sentence earlier, and docs/development.md
    and docs/fixtures.md are both accurate, but the consequence sentence should name this shape the
    way the PR body's "Accepted residual" paragraph does.
  • S3. Quantify the residual by shape. The disclosed "1 of 20 000" is right for flat random
    values (I measured 0 of 160,000 across 8 flat alphabets) but understates _-grouped values
    (0.015-0.065 %). One clause would stop the number being read as the rate for every shape.
  • S4. The backtracking guard at tests/test_hygiene.py:1356-1362 demonstrates little.
    "a1"*5000 fails on the left side's second character before any star runs, and
    "x=" + "A1_"*5000 + "!" backtracks linearly one iteration at a time. The regex genuinely is
    linear (I measured it), so this is cosmetic; "AB12_"*20000 + "%" or "A" + "_"*20000 + "!"
    would exercise the nested quantifiers instead.
  • S5. Pre-existing, out of scope: camelCase keyword arguments are still false positives
    (maxRetries=DefaultTimeoutSettingValue is scored) because a run must be one case. Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256) #243 does not
    ask for it; worth its own issue if it bites a builder.
  • S6. _NAME_RUN and _RUN are plain strings and so are not themselves in
    tests/test_fixtures.py::SCANNER_PATTERNS, but the compiled _IDENT_ASSIGNMENT embeds both, so
    parity is in fact enforced — I confirmed the two 17-line blocks are byte-identical and that
    reverting either copy alone fails
    test_the_packaged_scanner_has_the_same_rules_as_the_repository_scanner. No change needed; noted
    so the next reader does not have to re-derive it.

PR body against the diff

Accurate except as noted. Specifically checked:

  • "the bare name=VALUE exemption … now accepts a run that is one-case letters followed by
    digits … on both sides. New helper regex _NAME_RUN; _RUN is built from it." — matches the diff.
  • "Digits may only follow the letters of a run, so a1b2c3... style text still does not match." —
    true of the code; asserted, not tested (M1).
  • "Both copies are byte-identical in these lines" — verified, diff of the two blocks is empty.
  • "9 test_keyword_arguments_with_letter_digit_constant_names_are_not_findings cases" — 9, correct.
  • "(40 cases: … each assigned to instructions, digest_prefix, SHA256_DIGEST, utf8_name,
    x)" — 8 values x 5 names = 40, correct.
  • Red claim "11 failed, 510 passed: the 9 … cases, test_the_three_probes_…, and
    test_the_repository_itself_is_clean" — reproduced exactly by restoring both scanner files
    to 39ddfce and running pytest tests/test_hygiene.py tests/test_fixtures.py:
    11 failed, 510 passed, those same tests. Restored afterwards; git status --porcelain empty.
  • Green claim "532 passed" for tests/test_hygiene.py tests/test_check_hygiene_generic_usernames.py tests/test_fixtures.py — reproduced on 3.14: 532 passed.
  • "Hygiene check passed: 611 file(s) scanned." — reproduced, exit 0.
  • "ruff check . clean, ruff format --check . clean (401 files)" — reproduced.
  • Full suite "5951 passed, 49 skipped, 50 deselected, 31 xfailed" — I get
    5952 passed with the same skip/deselect/xfail counts. The merge of origin/main brought in
    tests/test_backends.py (+21 lines, Foundation: DecisionResult supports in and iteration by question name (#239) #332), so the body's number is right for the pre-merge head
    and off by one for the reviewed head. Not a defect; noted only so the next reader is not confused.
  • "The origin/main scanner run on this branch flags exactly two lines" — three (M2).
  • "The two mutant tests monkeypatch _IDENT_ASSIGNMENT to each looser rule from the issue and show
    instructions=<KEY> becomes unreported" — true and correctly scoped to the issue's two rules;
    it is not a claim about the PR's own boundary, which is why M1 is a gap in coverage rather than an
    overclaim in the body.
  • Scope: the body states the mirrored copy is outside the issue's path list and must move with it —
    correct, test_the_packaged_scanner_has_the_same_rules_as_the_repository_scanner fails otherwise.
    Nothing else is touched.

Acceptance criteria of #243

  1. instructions=_STAGE1_INSTRUCTIONS and digest_prefix=SHA256_DIGEST_PREFIX_NAME pass — MET.
    Verified through the real CLI on the issue's three probe files (probe_bad.py, probe_good.py,
    probe_sha.py): all three exit 0, "Hygiene check passed: 1 file(s) scanned." Also verified clean:
    UTF8_ENCODING_NAME_DEFAULT, API_V2_SCHEMA_VERSION_NAME, BASE64_URL_SAFE_ALPHABET shapes, and
    the pre-existing startup_timeout=KERNEL_START_TIMEOUT.
  2. A real high-entropy literal still fails, proved by mutation — PARTIALLY MET. Proved for both
    widenings the issue proposed (I ran the free-alnum mutant: all 40 fail-closed cases fail; the two
    _LOOSE_EXEMPTIONS tests bite). Not proved for the stricter boundary the PR chose instead; a
    mutation that relaxes it leaves the suite green (M1).
  3. The three probes exist as regression tests — MET.
    test_the_three_probes_of_the_issue_through_the_command_line runs all three through the real
    CLI, and the 9 exemption cases are each guarded by _scores_without_the_exemption, so none of
    them is a case that guards nothing.
  4. The published set re-scanned under the new rule and the result recorded — MET in substance.
    python tools/check_hygiene.py → Hygiene check passed: 611 file(s) scanned., exit 0 on both
    3.14 and 3.10. Recorded in the PR body rather than on issue Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256) #243 (which carries only the
    2026-10-20 triage comment); I would call that equivalent, but if the project wants the criterion
    met literally, a one-line comment on Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256) #243 at merge time would do it. Recipe 39's naming left
    as-is, which the criterion expressly allows.

Checks I ran

Fresh venvs under /home/agent/r333/ (uv venv --python 3.14 / --python 3.10,
uv pip install --link-mode symlink -e ".[dev]"), every heavy job serialised under
flock .claude/heavy.lock nice -n 10, pytest with -p no:cacheprovider, long runs backgrounded
with log polling. Available memory was above 2.5 GB throughout.

Python 3.14.7:

  • ruff check . → All checks passed.
  • ruff format --check . → 401 files already formatted.
  • python tools/check_hygiene.py → Hygiene check passed: 611 file(s) scanned. exit 0.
  • python tools/check_recipe_assets.py → passed, 461 files.
  • python tools/render_catalog.py --check → exit 0.
  • pytest tests/test_hygiene.py tests/test_fixtures.py tests/test_check_hygiene_generic_usernames.py
    → 532 passed.
  • pytest (whole suite) → 5952 passed, 49 skipped, 50 deselected, 31 xfailed in 197 s.

Python 3.10.21 (the package floor):

  • pytest tests/test_hygiene.py tests/test_fixtures.py tests/test_check_hygiene_generic_usernames.py
    → 532 passed.
  • python tools/check_hygiene.py → Hygiene check passed: 611 file(s) scanned. exit 0.

Mutation runs (both copies mutated together, restored after each; git status --porcelain empty
afterwards):

mutant of _NAME_RUN result
[A-Za-z0-9]+ (free alnum runs) 51 failed, 470 passed — all 40 secret-shaped cases fail
[A-Za-z]+[0-9]* (mixed case in a run) 3 failed, 518 passed — caught by the pre-existing MIXED_34 cases
`[0-9]*(?:[A-Z]+ [a-z]+)[0-9]*` (digit before the letters)

Base-scanner runs:

  • Both scanner files restored to 39ddfce, pytest tests/test_hygiene.py tests/test_fixtures.py
    → 11 failed, 510 passed, exactly the tests the PR body names.
  • Base scanner driven over the head tree → 611 files, 3 findings (listed in M2).

Independent probing (all synthetic, under /home/agent/r333/, nothing written into the repository):
issue-probe replay through the CLI; 13 hand-built grammar-shaped secret shapes; 13 position probes
(source and notebook-output) for kwarg/assignment/default-arg/dict-literal/f-string/export/.env/repr,
quoted and unquoted; vendor-rule interaction probes; a 160,000-sample flat-alphabet old-vs-new fuzz
plus 220,000 further samples of _- and .-grouped shapes; a secret-name-bypass sweep over 17
names; and a backtracking/timing sweep to 100,001 characters.

CI on the exact head a3b5c3bf…: Lint (ruff), Tests (py3.10), Tests (py3.14),
Catalog (README is current), Hygiene (secrets and notebook outputs), Fixtures (validate),
Scope (recipe pull requests), Notebooks (discover), Notebooks (execute) and all 29
per-notebook jobs: success. Commit signature verified.

Verdict

The rule is the right one and better than either option the issue suggested; the implementation
matches its stated grammar; it fails closed on every realistic secret format I could construct; it
does not backtrack; the mirrored copy and its parity test are correct; the red/green evidence in the
body reproduces. I am asking for changes only because the single discriminator this PR invented —
"digits may only follow the letters of a run" — is the one thing the 40-case fail-closed net cannot
see, in a gate that every pull request passes and whose false negatives are the expensive kind, and
because this file was explicitly made independent of its own exemption once before (M2). Both fixes
are a few lines and need no redesign.

OPUS-REVIEW: CHANGES-NEEDED a3b5c3b

CompleteDotTech and others added 2 commits October 21, 2026 22:48
…ets and keep its test file independent of it (#243)

Review fix round for PR #333. Adds chains of short underscore-joined chunks to
the secret-shaped values (digits before letters, interleaved, mixed case) and a
mutant test per relaxed property, so dropping "digits only trail the letters"
now fails. Rewrites the comment and the _STAGE_1 probe so tests/test_hygiene.py
scans clean with the exemption switched off, and adds a test that proves it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Opus review 2 — delta re-review, PR #333 new head

  • Agent ID: reviewer 2 of 2, independent, worktree agent-af2525be5652330fd
  • Model: claude-opus-5[1m] (Opus 5, 1M context)
  • Head reviewed: 400277eff7563afb5f22235fcf3cb13e9769f87b — signed merge of origin/main b627e3c2dd723f5b38b2e67a3c1ad0e0c3c94134 (Foundation C: public dump_responses, honest run-mode sentence, matrix and forced-metric template fixes #317) into d6667293c7eceec0727c866cec70e51b45b0f3f3
  • Previously approved: a3b5c3bf2e5308db71b1c1c36eaa048789b91876 (reviews pr333-r2.md, pr333-r2b.md)
  • Base: origin/main = b627e3c (its own CI green: Hygiene, CI, Notebooks all success)

I accept the delta. git diff a3b5c3b d666729 touches tests/test_hygiene.py and nothing else
(+78/−4); git diff --stat a3b5c3b 400277e -- tools/check_hygiene.py src/jev_cookbook/fixtures/_scan.py docs/development.md
is empty, so the scanner code and docs/development.md I approved are byte-identical, and
docs/fixtures.md differs from my approved copy only by #317's two dump_responses lines arriving
from main. The fix commit answers reviewer 1's two findings well. The run-grammar boundary is now
pinned from both directions: seven chained _-joined chunk values are added to SECRET_VALUES
(digits-before-letters upper/lower/short, interleaved upper/lower, mixed case, mixed-case letters),
and test_relaxing_one_property_of_a_run_would_hide_a_chunked_secret relaxes exactly one property
of a run at a time — digits before letters, interleaving, case change — asserting first that each
value is a finding under the shipped rule (so no case is vacuous) and then that the mutant hides
it, in both scanner copies; test_the_mutant_builder_reproduces_the_shipped_exemption asserts the
mutant builder with hygiene._NAME_RUN reproduces _IDENT_ASSIGNMENT.pattern in both copies, so
the mutants cannot drift from the real rule. The self-dependence is fixed and the fix is guarded:
the comment at :1209 and the probe_good literal at :1265 are rewritten so no line of the file
needs the exemption, and test_this_file_scans_clean_without_the_exemption (:1417) switches the
exemption to re.compile(r"(?!)"), proves the switch works on DIGIT_RUN_KEYWORDS, then requires
the file's own source to scan clean. I verified that guard bites by re-joining the split literal:
FAILED … test_this_file_scans_clean_without_the_exemption - AssertionError: assert [(1265, 'high… inst****** (len 34)')] == []; restored afterwards (git status clean, HEAD 400277e). All new
values are hard-coded literals and the pre-existing ones stay seeded (_random_token(seed=7),
_random_text(…, 21..24), random.Random(11)), so nothing is clock-, environment- or
RNG-dependent; tests/test_hygiene.py went from 309 tests / 9.6 s to 349 tests / 10.6 s and
10.1 s on two consecutive runs
(slowest new test 0.14 s, the 5.2 s test_the_repository_itself_is_clean
still dominating), so neither slow nor flaky. git show --cc 400277e is empty — a clean auto-merge
with no hand edits — and docs/fixtures.md merged coherently: #317's change is the
dump_responses worked example at lines 594/621, this PR's is the scanner-exemption paragraph at
lines 246-251, and the merged file carries both with no overlap or contradiction.
git diff --raw -M origin/main...HEAD is still exactly the PR's five files, every one
:100644 100644 … M — no add, rename, delete, mode or type change.

Checks rerun (fresh 3.14 and 3.10 venvs under /home/agent/pr333-r2c/, everything under
flock .claude/heavy.lock nice -n 10 with no nested flock, pytest -p no:cacheprovider, long runs
via a background wrapper with log polling): ruff check . clean; ruff format --check . 404 files;
python tools/check_hygiene.py Hygiene check passed: 614 file(s) scanned. exit 0 on both 3.14
and 3.10; render_catalog.py --check exit 0; check_recipe_assets.py 461 files; full pytest
3.14 6080 passed, 55 skipped, 50 deselected, 31 xfailed in 215.57s;
pytest tests/test_hygiene.py tests/test_fixtures.py tests/test_check_hygiene_generic_usernames.py
3.14 572 passed; pytest tests/test_hygiene.py tests/test_fixtures.py 3.10 561 passed;
notebook_ci.py fixtures 51 recipe folders, 0 failed; check_recipe_scope.py scope ok: foundation pull request: the recipe allowlist does not apply. CI on this exact head is complete
and green: 60 of 60 checks pass, no duplicate check name
(Lint, Catalog, Fixtures, Hygiene,
Scope, Tests py3.10, Tests py3.14, Notebooks discover/execute and 51 Notebook matrix rows).

One thing the delta changed that is worth recording, not blocking. I re-derived the re-scan on
this head (614 files): the b627e3c scanner over the PR tree now flags two lines, both in docs
(docs/development.md:339, docs/fixtures.md:250), the new scanner flags 0, old-only 2 and
new-only 0, and exactly one 32+ character word in the whole tree is newly exempt —
instructions=_STAGE1_INSTRUCTIONS, in those two docs files only. So the fix commit removed the
third finding by making the test file independent of the exemption: the body's original "exactly
two lines, both in the docs" is now true of the shipped head, and the correction comments
(explicitly scoped "Run on head a3b5c3b (611 files)") remain accurate for the head they name. Only
the "other 609 files" total has moved, to 612, because #317 added three files to main — the same
kind of merge drift as the body's 5951 full-suite count, and the body cannot be edited under
CONTRIBUTING. The substance I must confirm is confirmed on this head: the relaxed rule hides no
pre-existing finding anywhere in the tree. A one-line pull-request comment giving the final head's
figures (614 files, old scanner 2 docs lines, new scanner 0) would close the audit trail tidily;
non-blocking, and my earlier S1-S3 suggestions still stand as optional (S1 is now largely answered
by the chunk-chain cases, though the x=sjtpcm32_… residual itself is still only in the body).

Scratch and both venvs deleted; nothing I did not create was removed.

OPUS-REVIEW: APPROVE 400277e

@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Audit-trail update for acceptance criterion 4 of #243, with the final head's figures (400277e, which supersedes the figures in the earlier correction comment): the origin/main scanner run over the final head's tree reports 2 findings, both in this PR's documentation text (docs/development.md:339 and docs/fixtures.md:250, the lines that name the digit-run examples); the other 612 files are clean (the third line in the earlier count was a comment in tests/test_hygiene.py that the last fix commit rewrote, so that test file now scans clean without the exemption, and a test pins that). The new scanner reports 0 findings on the same tree, and both scanners report 0 on main's own tree, so the relaxed rule hides no finding that exists on main.

@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Re-review of PR #333 — reviewer 1 of 2 (independent), fix round 1

git diff a3b5c3b d666729 touches only tests/test_hygiene.py (+78 / −4).
git diff a3b5c3b 400277e -- tools/check_hygiene.py src/jev_cookbook/fixtures/_scan.py is empty,
so the shipped scanner behaviour is byte-identical to the head I reviewed; everything I verified
about the rule itself in pr333-r1.md still stands and I do not repeat it here.

The merge

git show --cc 400277e prints no diff hunks at all, i.e. the merge resolved every path by
taking one side and nothing was hand-edited. The only overlapping file is docs/fixtures.md, and
both sides' edits survive, in their own places:

Coherent, nothing lost, no stray conflict markers (grep -n '<<<<<<<\|>>>>>>>' docs/fixtures.md
is empty). The merge brings in the rest of #317 (159 of the 160 files in
git diff a3b5c3b 400277e --stat; the 160th is this PR's own tests/test_hygiene.py), which is
unrelated to this PR and
which I did not re-review; I did confirm the whole suite and every documented check pass on the
merged tree.

Finding M1 — resolved, more thoroughly than I asked

I asked for one _-chunked value in SECRET_VALUES and/or one more _LOOSE_EXEMPTIONS entry.
The fix adds seven chunk-chain values (tests/test_hygiene.py:1302-1308), a mutant builder
(_exemption_with_run, :1368), a structural guard
(test_the_mutant_builder_reproduces_the_shipped_exemption, :1374), three property mutants
(_RUN_MUTANTS, :1382) and test_relaxing_one_property_of_a_run_would_hide_a_chunked_secret
(:1402). SECRET_VALUES goes from 8 to 15 values, so
test_secret_shaped_values_in_keyword_position_are_still_findings goes from 40 to 75 cases,
each still run through four paths (scan_text, a notebook scan, run() on a .py file, and the
packaged copy).

I re-applied my own mutants to _NAME_RUN in both copies (not the builder's in-test mutants)
and ran pytest tests/test_hygiene.py tests/test_fixtures.py:

mutant of _NAME_RUN (applied to BOTH copies on disk) on a3b5c3b on 400277e
[0-9]*(?:[A-Z]+|[a-z]+)[0-9]* — a digit may precede the letters 521 passed, 0 failed 16 failed, 545 passed
[A-Za-z0-9]+ — letters and digits in any order 51 failed, 470 passed 89 failed, 472 passed
[A-Za-z]+[0-9]* — a run may change case 3 failed, 518 passed 9 failed, 552 passed

The failure sets are exactly what the design predicts, which is how I know the new values are doing
the work rather than something incidental:

  • digits-before-letters → 15 of the 75 test_secret_shaped_values_in_keyword_position_are_still_findings
    cases (the three digits-first chunk values x five names) plus
    test_relaxing_one_property_of_a_run_would_hide_a_chunked_secret[digits-before-letters].
  • [A-Za-z0-9]+ → all 75 secret-shaped cases, all 3 test_relaxing_… cases, plus 4 in
    test_assignments_of_secret_looking_values_are_still_caught, 3 in
    test_one_case_values_and_long_left_sides_cannot_hide_a_secret, 2 in
    test_secret_names_and_random_names_stay_scored_with_letter_digit_constants and 2 in
    test_a_looser_exemption_would_hide_a_secret_….
  • [A-Za-z]+[0-9]* → 5 secret-shaped cases (chunks-mixed-case-letters x five names), the 3
    pre-existing MIXED_34 cases, plus test_relaxing_…[mixed-case-run].

Each mutation was applied to tools/check_hygiene.py and src/jev_cookbook/fixtures/_scan.py
together and reverted immediately afterwards; git status --porcelain is empty and HEAD is
400277ef… now.

The mutant that was green on a3b5c3b — [0-9]*(?:[A-Z]+|[a-z]+)[0-9]*, a digit allowed in front
of the letters of a run — is now killed. That was the whole of M1.

The values are properly discriminated rather than all killed by the widest mutant, which is what
makes the net informative. I computed the whole matrix myself (every chunk value under every
mutant, both copies) and the _RUN_MUTANTS assignment is exactly right:

chunk value flagged as shipped hidden by
12ABCD34_… / 12abcd34_… / 9ZQWER1_… (digits first) yes digits-before-letters, interleaved
A1B2C3D4_… / a1b2c3d4_… (interleaved) yes interleaved
Ab12Cd34_… (case change + interleaved) yes interleaved
Abcd12_… (case change, digits trailing) yes interleaved, mixed-case

The three mutants are nested, so [A-Za-z0-9]+ hides everything; what matters is that each mutant
has at least one value that it alone among the narrower relaxations hides — digits-first for
[0-9]*L+[0-9]*, Abcd12_… for [A-Za-z]+[0-9]* — so none of the three can be relaxed without a
test going red, and the digits-first values are not merely collateral of the widest mutant.

test_the_mutant_builder_reproduces_the_shipped_exemption is a better idea than it looks: it
asserts _exemption_with_run(hygiene._NAME_RUN).pattern equals both scanners' _IDENT_ASSIGNMENT
patterns, so the structure around the run — the two separator classes _+ and [_.]+, the _*
paddings, the single =, the [0-9]+ digit-only alternative, and the left side starting with a
letter run — is now pinned too. That closes, as a tripwire, the one thing my first review noted was
unguarded besides _NAME_RUN itself: widening the separators to include - would now fail this
test instead of passing silently. I confirmed that by hand: substituting [_.-]+ for [_.]+ in
_IDENT_ASSIGNMENT makes the patterns unequal.

Finding M2 — resolved, and the builder found a second hit I had missed

Two things were wrong on a3b5c3b: the comment at tests/test_hygiene.py:1209 carried the full
33-character instructions=_STAGE1_INSTRUCTIONS, and the PR body recorded two base-scanner
findings when there were three.

  • The comment is reworded (tests/test_hygiene.py:1209-1213): it now says "a keyword argument
    naming a constant such as STAGE1 (instructions= plus that name, 33 characters in all)", with
    no 32-plus-character token in it.
  • The builder also found a hit I had missed, and it is a real one: the probe_good.py body in
    test_the_three_probes_of_the_issue_through_the_command_line held
    instructions=_STAGE_1_INSTRUCTIONS at 34 characters. That line is exempt under both the old
    and the new rule, so it never showed up in my base-scanner diff — but it does need the exemption,
    which is the stronger property the new test enforces. It is now "…=_STAGE_1" + "_INSTRUCTIONS)\n",
    byte-identical at run time, so the probe still tests the same file content.
  • test_this_file_scans_clean_without_the_exemption (:1417) switches _IDENT_ASSIGNMENT to
    re.compile(r"(?!)") and asserts the whole file scans clean, with a non-vacuity guard first
    (assert all(hygiene.scan_text(line) for line in DIGIT_RUN_KEYWORDS)). Its scope is right:
    scan_text with the default environment=False runs exactly the secret rules, which is what a
    repository scan applies to a .py file, so the test is neither narrower nor wider than the real
    check.

I verified both halves by routes of my own, not by running the builder's tests:

  • The current origin/main scanner (b627e3c) driven over all 614 tracked files of this head
    reports 2 findings, both the intended doc examples:
    docs/development.md:339 and docs/fixtures.md:250. tests/test_hygiene.py is gone from the
    list (it was the third finding on a3b5c3b).

  • Switching _IDENT_ASSIGNMENT off entirely and scanning all 614 tracked files gives 28 findings,
    of which none is in tests/test_hygiene.py and only 4 are in files this PR touches (the two
    doc lines, each carrying both startup_timeout=KERNEL_START_TIMEOUT and the new
    instructions=_STAGE1_… example). The other 24 are pre-existing dependents of the old exemption
    (orchestration/handoffs/…/evidence/*.json, recipes/28|32|39|40|48|50/…,
    and the rule's own comment in both scanner files). So the file that tests the exemption is
    genuinely independent of it, and I confirmed the test's claim without using the test.

  • Narrowed to the three files this PR edits, with the exemption switched off:
    tests/test_hygiene.py → 0 findings (0 with it on too), docs/development.md → 2,
    docs/fixtures.md → 2.

  • I also stress-tested the new test for vacuity the other way: with a comment line carrying a
    33-character name=VALUE token appended to tests/test_hygiene.py,
    test_this_file_scans_clean_without_the_exemption FAILED (1 failed, 1 passed, 347
    deselected), while test_the_repository_itself_is_clean still passed — so the new test, and only
    the new test, catches a reintroduced dependency. It is not a test that would stay quiet if
    someone pasted the literal back in. (Appended line reverted; the file is byte-identical to the
    head again.)

  • The record correction is posted, as CONTRIBUTING.md requires, as a pull-request comment rather
    than a body edit (comment of 2026-10-21T22:35:33Z: "three lines in three files and 608 others",
    naming all three). Reviewer 2 raised the same miscount independently; my M2 and their M1 are the
    same finding and both are now addressed.

The two docs lines left as they are — I agree

docs/development.md:339 and docs/fixtures.md:250 still need the exemption to scan clean, and the
builder left them deliberately. I think that is right:

  • The sentence's job is to show the reader the exempt shape. Splitting or obfuscating
    instructions=_STAGE1_INSTRUCTIONS in prose would make the documentation worse at the one thing
    it is there for, and the same docs have carried startup_timeout=KERNEL_START_TIMEOUT (36
    characters) under the old exemption since Foundation: hygiene scanner flags long keyword arguments of the form name=CONSTANT as high-entropy tokens #114 — so this is existing, deliberate practice, not a
    new dependency.
  • The concern behind Foundation: hygiene scanner flags long keyword arguments of the form name=CONSTANT as high-entropy tokens #114's instruction was narrower than "no file may depend on the exemption": it
    was that the file which tests the exemption must not need it, so that a regression in the
    exemption cannot hide a finding in its own regression net. That property now holds and is itself
    tested.
  • The cost is bounded and visible: if the exemption were ever removed,
    test_the_repository_itself_is_clean would fail on those two doc lines, which is a loud,
    correct signal that the documentation describes a rule that no longer exists — not a silent
    weakening. 24 pre-existing lines elsewhere in the tree already have the same relationship to the
    old exemption.

Did I find a secret the shipped rule hides that the new tests do not cover?

No new one. The scanner is unchanged, so the exempt set is exactly what I characterised last round:
an unquoted name=VALUE whose value is a chain of one-case letters-then-digits runs joined by
_ or ., under a name that does not contain api key / secret / token / password / access key /
credential. That is the residual the PR body discloses, I measured it again last round at
0.015-0.065 % of what the old rule flagged for _-grouped values and 0 of 160,000 for flat random
alphabets, and it cannot be closed without giving up the #243 fix. What changed this round is that
every boundary of it is now pinned, so it can no longer drift wider unnoticed. I re-ran my own
probe set against this head — the issue's three probe files through the real CLI (all exit 0), the
13 source-and-notebook-output position probes (quoted kwarg / assignment / dict literal / bare
value / secret-named kwarg / digit-leading chunks all FLAG; unquoted kwarg, unquoted assignment,
default argument, f"{x=…}", export X=, .env-style line and printed repr all CLEAN), the
vendor-rule interaction probes, the residual shapes and the _SECRET_NAME sweep — and every result
is byte-for-byte identical to a3b5c3b, as it must be given the scanner files did not change.

The one axis still unpinned is the _SECRET_NAME interaction I raised as S1 last round (below).

Suggestions (not blocking, carried over or new)

  • S1 (carried over). api1_key=<36 one-case letters> and access1_key=<36 one-case letters>
    were findings before this PR and are exempt now: api1 is a valid run under the new grammar
    while _SECRET_NAME (api[_-]?key, access[_-]?key) does not match api1_key. Verified again
    on this head; t0ken, secr3t, p4ssword, api2key, apikey2, token1, auth2_token and
    cred3ntial are all still scored, so the escape needs a digit immediately before the _ of a
    two-word secret name. Contrived, and the quoted form of that name was never caught by any rule,
    so I would not block on it; searching _SECRET_NAME over the left side with digits stripped
    would close it in one line, and one case in
    test_secret_names_and_random_names_stay_scored_with_letter_digit_constants would pin it.
  • S2 (carried over). tools/check_hygiene.py:29-33 still ends "so a long all-lowercase or
    all-uppercase secret … is not caught by it". The hole is now wider than one-case: a chain of
    one-case letters-then-digits chunks is also not caught. The grammar one sentence earlier is
    correct; only the consequence sentence lags.
  • S3 (new, records only). The posted correction is scoped to head a3b5c3b ("Run on head
    a3b5c3b (611 files) … three files"). On the final head the figures are different and better:
    614 tracked files, 2 base-scanner findings, both in docs, 612 clean, and 0 findings under the
    new rule.
    Since this is the permanent record for acceptance criterion 4, a one-line addendum
    comment for 400277e (and the same on issue Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256) #243, where AC4 says "recorded here") would leave
    the audit trail matching the head that merges. No code change.
  • S4 (carried over). The old backtracking guard at
    test_the_exemption_regex_does_not_backtrack_catastrophically still demonstrates little
    ("a1"*5000 fails on the second character). The regex is genuinely linear — I measured it to
    100,001 characters last round — so this is cosmetic.
  • S5 (carried over, pre-existing). camelCase keyword arguments remain false positives
    (maxRetries=DefaultTimeoutSettingValue is scored), because a run must be one case. Out of scope
    for Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256) #243; worth its own issue if it bites a builder.
  • S6 (new, micro). tests/test_hygiene.py:1426 — assert all(hygiene.scan_text(line) for line in DIGIT_RUN_KEYWORDS) is the non-vacuity guard for
    test_this_file_scans_clean_without_the_exemption, but all([]) is True, so the guard itself
    would go quiet if DIGIT_RUN_KEYWORDS were ever emptied. assert DIGIT_RUN_KEYWORDS and all(...)
    costs nothing. (It is a module-level literal list of 9 today, so this is theory, not a defect.)

PR body and fix-round record against the diff

  • The fix commit's message and the hand-off note describe the diff accurately: new SECRET_VALUES
    chunk chains, three mutant-relaxation tests, the rebuilt-grammar guard, the reworded comment, the
    split probe string, and the new file-independence test. Nothing else is in the commit; no
    assertion anywhere was loosened or deleted (the only deletions are the three reworded comment
    lines and the one split string, which is byte-identical at run time).
  • The builder's claimed mutant counts (16 / 89 / 9 failures) match what I measured myself
    independently — see the table above.
  • The PR body's original "exactly two lines … other 609 files" sentence is still in the body, as
    CONTRIBUTING.md requires (descriptions are not edited after the final push); the correction lives
    in a comment, which is the prescribed place. See S3 for the remaining staleness.

Acceptance criteria of #243

  1. instructions=_STAGE1_INSTRUCTIONS and digest_prefix=SHA256_DIGEST_PREFIX_NAME pass —
    MET.
    Re-verified on this head through the real CLI on the issue's three probe files; all three
    exit 0 with "Hygiene check passed: 1 file(s) scanned."
  2. A real high-entropy literal still fails, proved by mutation rather than asserted — MET. This
    was the partially-met criterion. All three ways a run could be relaxed are now proved by
    mutation, inside the suite and (independently) by me patching both scanner files on disk; the
    structure around the run is pinned by pattern equality.
  3. The three probes exist as regression tests — MET. Unchanged and still run through the real
    CLI; the probe_good.py body is byte-identical at run time after the string split.
  4. The published set re-scanned under the new rule and the result recorded — MET.
    python tools/check_hygiene.py → "Hygiene check passed: 614 file(s) scanned.", exit 0 on 3.14
    and 3.10. The base scanner flags only the two intended doc lines; nothing in the published
    recipes depends on the widening. Recorded on the PR (body plus the correction comment) rather
    than on issue Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256) #243; S3 suggests a one-line addendum for the final head.

Checks I ran on 400277e

Fresh venvs under /home/agent/r333/ (uv venv --python 3.14 and --python 3.10,
uv pip install --link-mode symlink -e ".[dev]"), every heavy job serialised under
flock .claude/heavy.lock nice -n 10 with no nested flock, pytest with -p no:cacheprovider, long
runs backgrounded with log polling. Available memory stayed above 3.5 GB throughout, well over the
1500 MB floor; the lock was contended by four other agents' jobs, so the installs and the test runs
queued behind them rather than running in parallel.

Python 3.14.7 — the three commands of docs/development.md and the other documented checks:

  • ruff check . → All checks passed.
  • ruff format --check . → 404 files already formatted.
  • python tools/check_hygiene.py → Hygiene check passed: 614 file(s) scanned. exit 0.
  • python tools/check_recipe_assets.py → passed, 461 files under recipes/.
  • python tools/render_catalog.py --check → exit 0.
  • pytest tests/test_hygiene.py tests/test_fixtures.py tests/test_check_hygiene_generic_usernames.py
    → 572 passed in 14.3 s (532 on a3b5c3b; +40 is exactly the 35 new parametrized
    secret-shaped cases plus the 3 mutant cases, the structural guard and the file-independence test)
  • pytest (whole suite) → 6080 passed, 55 skipped, 50 deselected, 31 xfailed in 214 s, exit 0
    (5952 passed / 49 skipped on a3b5c3b; the +128 and +6 come from Foundation C: public dump_responses, honest run-mode sentence, matrix and forced-metric template fixes #317's new test files, which
    the merge brings in, plus this fix's 40).

Python 3.10.21 (the package floor):

  • pytest tests/test_hygiene.py tests/test_fixtures.py → 561 passed in 13.8 s.
  • python tools/check_hygiene.py → Hygiene check passed: 614 file(s) scanned. exit 0.

Independent scans (standard library only, nice -n 19):

  • origin/main (b627e3c) scanner over all 614 tracked files of this head → 2 findings
    (docs/development.md:339, docs/fixtures.md:250).
  • New scanner with _IDENT_ASSIGNMENT switched off over the same 614 files → 28 findings, none in
    tests/test_hygiene.py, only 4 in files this PR touches.
  • New scanner as shipped over the same 614 files → 0 findings.

Merge inspection: git show --cc 400277e (no hunks), git diff b627e3c 400277e -- docs/fixtures.md
and git diff d666729 400277e -- docs/fixtures.md (one clean hunk each, as described above),
git diff a3b5c3b 400277e -- tools/check_hygiene.py src/jev_cookbook/fixtures/_scan.py (empty).

CI on the exact head 400277ef…: Lint (ruff), Tests (py3.10), Tests (py3.14),
Catalog (README is current), Hygiene (secrets and notebook outputs), Fixtures (validate),
Scope (recipe pull requests), Notebooks (discover), Notebooks (execute) and all 51
per-notebook jobs: success (0 non-success). Commit signature verified: true, reason valid.

Verdict

Both of my findings are resolved, and resolved better than the minimum I asked for: the boundary the
PR invented is now pinned from three directions with mutants that are individually discriminating,
the structure around it is pinned by pattern equality so a separator or alternation change cannot
slip through, and the file that tests the exemption is provably independent of it — which I
confirmed by scanning the tree with the exemption switched off rather than by running the test that
claims it. The builder also closed a hit I had missed. The scanner itself is unchanged from the head
I already satisfied myself was safe, the merge of #317 is clean and coherent in the one overlapping
file, every documented check passes on 3.14 and on the 3.10 floor, and the two remaining
exemption-dependent lines are documentation examples whose job is to show that shape. The open items
are suggestions only: a one-line records addendum (S3), a one-line docstring sentence (S2) and the
contrived api1_key escape (S1), none of which I would hold a merge for.

OPUS-REVIEW: APPROVE 400277e

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Opus review 2 — delta check, PR #333 new head (merge of #334)

  • Agent ID: reviewer 2 of 2, independent, worktree agent-af2525be5652330fd
  • Model: claude-opus-5[1m] (Opus 5, 1M context)
  • Head reviewed: 581d275489e1a905b558dedc93fcf714d9c3af7a — signed merge, first parent 400277eff7563afb5f22235fcf3cb13e9769f87b (which I approved in pr333-r2c.md), second parent feb57d041b0dc57247c99cc23cbc0ade2ef7c8cb
  • Base: origin/main = feb57d0 (Foundation: image-check hardening follow-ups (pin test, zlib end check, inflate cap) (#329) #334)
  • Earlier approvals: a3b5c3b (pr333-r2.md, pr333-r2b.md), 400277e (pr333-r2c.md)

I accept the delta. It is content-free with respect to this pull request. git show --cc 581d275
has an empty diff body — no conflict hunk, no hand edit in the merge. git diff 400277e 581d275 --stat
is exactly #334's three files and nothing else (docs/vision.md 3, tests/test_check_recipe_assets.py 83,
tools/check_recipe_assets.py 23 — 97 insertions, 12 deletions), none of them one of this PR's five.
git diff --raw -M origin/main...HEAD is still exactly the PR's five files, every row
:100644 100644 … M — no add, rename, delete, symlink, submodule, mode or type change — and the
five destination blobs are byte-identical to the ones I approved at 400277e:
docs/development.md ef870be, docs/fixtures.md 201419c,
src/jev_cookbook/fixtures/_scan.py 975e782, tests/test_hygiene.py fb7e47b,
tools/check_hygiene.py 49515c6. So nothing I reviewed changed, and #334 touches a disjoint part
of the tree (the recipe image-asset checker) with no interaction with the hygiene scanner's rules.

Checks (fresh 3.14 venv under /home/agent/pr333-r2d/, one run under
flock .claude/heavy.lock nice -n 10, no nested flock, pytest -p no:cacheprovider; CI covers the
full suite): python tools/check_hygiene.py → Hygiene check passed: 614 file(s) scanned., exit 0;
pytest tests/test_hygiene.py tests/test_fixtures.py → 561 passed in 12.80s, the same count and
runtime as at 400277e, so the merge changed neither.

CI on this exact head is complete and every check is success: 60 of 60 pass, no duplicate
check name
— Lint (ruff), Catalog (README is current), Fixtures (validate), Hygiene (secrets and
notebook outputs), Scope (recipe pull requests), Tests (py3.10), Tests (py3.14), Notebooks
(discover), Notebooks (execute) and all 51 Notebook matrix rows. origin/main feb57d0 is itself
green: Hygiene, CI and Notebooks all success.

Nothing from my earlier reviews is reopened, and nothing new is blocking. The optional notes from
pr333-r2c.md stand unchanged: the body's snapshot figures drift with each merge and cannot be
edited under CONTRIBUTING, and a one-line comment giving the final head's re-scan figures would
tidy the audit trail. Venv and scratch deleted; nothing I did not create was removed.

OPUS-REVIEW: APPROVE 581d275

@CompleteDotTech

Copy link
Copy Markdown
Contributor Author

Delta re-review of PR #333 — reviewer 1 of 2, round 2 merge of origin/main

I accept this delta. It changes nothing I reviewed: the merge is content-free, it brings in
only PR #334's three files (none of which this PR touches), and the five files this PR owns are
bit-for-bit what I approved on 400277e.

What I verified

  • Merge is content-free. git show --cc 581d275 prints no diff hunks at all (only the commit
    message and its Co-Authored-By trailer), so every path resolved by taking one side and nothing
    was hand-edited. Signature: %G? → G locally, GitHub verification.verified = true, reason
    valid. First parent is the approved head, so history is merge-only — no rebase, amend or
    force-push.
  • The merge brought in exactly Foundation: image-check hardening follow-ups (pin test, zlib end check, inflate cap) (#329) #334's three files. git diff 400277e 581d275 --stat:
    docs/vision.md (3), tests/test_check_recipe_assets.py (83), tools/check_recipe_assets.py
    (23) — 3 files, 97 insertions, 12 deletions. Disjoint from this PR's five paths.
  • This PR's scope is unchanged. git diff --raw -M origin/main...HEAD lists exactly five
    entries, every one :100644 100644 … M (no new file, no deletion, no mode change, no rename):
    docs/development.md, docs/fixtures.md, src/jev_cookbook/fixtures/_scan.py,
    tests/test_hygiene.py, tools/check_hygiene.py. git diff feb57d0 581d275 --stat agrees:
    5 files, +273 / −27.
  • The files I scrutinised are byte-identical to the approved head, by blob SHA:
    tools/check_hygiene.py 49515c6e… on both 400277e and 581d275;
    src/jev_cookbook/fixtures/_scan.py 975e782a… on both; tests/test_hygiene.py fb7e47bd…
    on both. So the exemption grammar, the mirrored copy, and the whole regression net — including
    the three run-property mutants and test_this_file_scans_clean_without_the_exemption that
    resolved M1 and M2 — are untouched, and none of my verification needs redoing.
  • CI on this exact head is complete and entirely green. I polled until no row was queued or
    in_progress: 60 check runs, 60 success, 0 non-success. Named rows: Lint (ruff),
    Tests (py3.10), Tests (py3.14), Catalog (README is current),
    Hygiene (secrets and notebook outputs), Fixtures (validate),
    Scope (recipe pull requests), Notebooks (discover), Notebooks (execute) — all success —
    plus 51 per-notebook jobs with 0 non-success.
  • origin/main (feb57d0) CI is green: 59 check runs, all success, 0 non-success.
  • Local run on this head, once under flock .claude/heavy.lock nice -n 10, fresh venv under
    /home/agent/r333c (uv venv --python 3.14, uv pip install --link-mode symlink -e ".[dev]"),
    pytest with -p no:cacheprovider, no nested flock:
    • pytest tests/test_hygiene.py tests/test_fixtures.py → 561 passed in 13.5 s, exit 0
      (identical to the approved head).
    • python tools/check_hygiene.py → Hygiene check passed: 614 file(s) scanned., exit 0.
    • No full suite and no re-run of the mutants: CI's Tests (py3.10) and Tests (py3.14) cover
      the suite on this head, and the three files whose behaviour the mutants probe have identical
      blobs to the head where I ran them (16 / 89 / 9 failures for the digits-before-letters,
      interleaved and mixed-case relaxations respectively).

Worktree clean at 581d275… afterwards (git status --porcelain empty); nothing in the tree was
modified by this review.

Carried-over suggestions (still non-blocking, unchanged)

The four non-blocking items from pr333-r1b.md stand as written: the re-scan correction comment is
scoped to the superseded head a3b5c3b so a one-line addendum for the merging head would leave
acceptance criterion 4's audit trail current; tools/check_hygiene.py's "does not cover" sentence
still says only "all-lowercase or all-uppercase"; the contrived api1_key= / access1_key=
_SECRET_NAME escape is open; and assert all(... for ... in DIGIT_RUN_KEYWORDS) would go quiet on
an empty list. None of them is a reason to hold the merge.

OPUS-REVIEW: APPROVE 581d275

@CompleteDotTech
CompleteDotTech merged commit 5366a02 into main Oct 21, 2026
60 checks passed
@CompleteDotTech
CompleteDotTech deleted the foundation/hygiene-kwarg-entropy branch October 21, 2026 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Foundation: check_hygiene rejects ordinary keyword arguments whose constant name contains a letter-digit run (STAGE1, SHA256)

1 participant