chore(code-quality): code-hygiene keystone — anti-laundering meta-gate, complete coverage, fail-closed parse#1337
Conversation
…fail-closed parse Adversarial re-verification of the ADR-0025 ratchet (run against itself) proved three degradation vectors; this closes them. - V1 anti-laundering meta-gate: scripts/ci/check_debt_baseline_monotonic.py fails if any baseline total grows vs origin/main, so the gate's own --write can no longer launder debt upward (proven: broad-except 326->327 was reported "held" before, now rejected). Fail-closed on unreadable base. - V5 complete classification: every top-level .py package must be in runtime_roots or excluded_roots; check_code_hygiene.py fails on any unclassified package. The implicit ~39 ungated packages are now explicit. - V4 fail-closed parse: a runtime file that won't AST-parse is recorded as `unparseable` debt instead of a silent zero. Wired into code-hygiene-gate.yml (fetch-depth: 0 for the meta-gate) and make code-quality. ADR 0026 documents the closures and the residual vectors (V2/V3/V6/V7/V8/V11) left open honestly. Gate only, no runtime change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7dfaf9baca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| directories (``.github``, ``.claude``) are tooling, not packages. | ||
| """ | ||
| roots, excluded, _ = _load_manifest() | ||
| classified = set(roots) | {e.split("/", 1)[0] for e in excluded} |
There was a problem hiding this comment.
Do not collapse nested exclusions to top-level roots
When excluded_roots contains a nested entry such as tools/vendor, this set marks the entire tools top-level as classified. The tree already has tracked Python files under tools/ outside tools/vendor, so the completeness gate reports no gap even though those files are neither in runtime_roots nor actually excluded by _iter_py; adding or moving more tools/*.py remains silently ungated.
Useful? React with 👍 / 👎.
| run: python scripts/ci/check_skip_ratchet.py | ||
|
|
||
| - name: Enforce debt-baseline monotonicity (anti-laundering meta-gate) | ||
| run: python scripts/ci/check_debt_baseline_monotonic.py --base-ref origin/main |
There was a problem hiding this comment.
Compare push checks against the pre-push revision
For the push workflow on main, the preceding fetch makes origin/main refer to the just-pushed commit, so this invocation compares the committed baselines against themselves. If a larger baseline is pushed directly to main (or the post-merge push check is relied on), the anti-laundering gate passes instead of detecting growth; the base ref needs to come from the event's previous/base SHA for push runs.
Useful? React with 👍 / 👎.
What
Adversarial re-verification of the ADR-0025 ratchet (PR #1336), run against itself, proved three vectors by which it would report "held" while the system decays. This closes them. Gate only — no runtime behaviour, feature, claim, or physics maturity change.
Vectors closed (each demonstrated empirically before fixing)
V1 —
--writedebt laundering (keystone)_write_baselineregenerates the baseline from the current tree with no comparison to the prior one. Proven: appendexcept Exception: pass, run the gate's own--write, commit → broad-except total 326→327 andverifyprinted "No new debt." Under deadline pressure, re-baselining is the cheapest response to any red gate → the ratchet decays to a rubber stamp.Fix:
scripts/ci/check_debt_baseline_monotonic.pydiffs baseline totals vsorigin/mainand fails if any dimension grew.--writethat pays debt down passes;--writethat admits debt cannot land. Fail-closed if the base ref is unreadable. Re-tested: the laundered 326→327 is now rejected even thoughcheck_code_hygienestill says "held."V5 — silent coverage shrink
runtime_rootswas a static 7-package list; the repo has 50+ top-level Python packages. A new top-level package was fully ungated and the gate still said "held."Fix: every top-level
.pypackage must be classified inruntime_rootsorexcluded_roots;check_code_hygiene.pyfails on any unclassified package. The previously-implicit ~39 ungated packages are now an explicit, reviewable surface.V4 — silent zero on unparseable
A file failing
ast.parse(e.g. newer syntax under the 3.12 runner) contributed zero debt and vanished.Fix: new
unparseabledebt dimension — it surfaces as visible debt.Residual vectors (left open honestly, tracked in ADR 0026)
V2 migration friction, V3 rotation-in-place, V6 laundering-into-excluded, V7 aliased ambient calls, V8 Goodhart on exception-width, V11 path-filter bypass. None silently assumed closed; each is a candidate for a follow-up PR.
Enforcement
code-hygiene-gate.yml: adds the meta-gate step (withfetch-depth: 0to readorigin/main).make code-quality: runs all three ratchets.Verification
make code-quality✅ ;black/ruff/mypy --strict/flake8clean; 0 newtype:ignore/noqa🤖 Generated with Claude Code