Uh oh!
There was an error while loading. Please reload this page.
feat(conformance): assert the quality files and the armed gitleaks check - #236
Merged
Conversation
Adds one property family to repo-inventory.yml and the guard that reads it: `quality_files`, the files every repo must CARRY at a fixed path. Two members, both measured develop-first on 2026-08-12: CLAUDE.md (19/20) and .cursor/BUGBOT.md (17/20). Both are read by a TOOL rather than by a reviewer who would notice them missing, so a repo without one does not go red - it quietly gets worse review than its peers, and nothing in the org reported that until now. Presence is not the whole assertion: a required file must be a REGULAR file and NON-EMPTY. A zero-byte CLAUDE.md and a symlink both satisfy "the path exists" while carrying no guidance, which would make the family inert on arrival. Fail-closed, per this guard's first design rule. The facts come out of the tree read_repo already fetches, so a 403, an unparseable payload, a truncated tree or a blob whose size the API did not report all return exit 2 with the row recorded unreadable. There is no path from a failed read to "the file is absent". Three exemptions, each with a written reason: claude-skills, rfcs .cursor/BUGBOT.md absent - UNREMEDIATED, shared anchor devex-bootstrap both files absent - reuses devex_bootstrap_undisposed, widened from protection-only to cover the same open disposition question (backend#1597) TWO THINGS DELIBERATELY NOT DONE. `quality / gitleaks` armed as a required status check needs NO new family: it is already asserted by protection_policy.required_checks on develop, staging and prod. Verified live - 16/16 train repos have it armed and 16/16 are asserted, with zero mismatches. A parallel family would duplicate a live assertion and then drift from it. Mutation-checked instead: neutralising the required_checks comparison turns the suite red, so that mechanism is not inert. `.gitleaks.toml` is not modelled. It is 7/20 and that is correct - a per-repo allowlist you add on a false positive, not a control. Modelling it would add nine exemption rows for zero security value and make a tuning file read as a security gap, which is the inert-verification pattern backend#1729 exists to catch. Verified: make check green (ruff, shellcheck, house-rules, action-pins, actionlint, four selftests); selftest 136 -> 160 cases, all passing; the live audit exits 0 with all 20 repos OK in the new column; 11 mutations applied one at a time, all 11 caught. backend#1608 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds one property family to
repo-inventory.ymland the guard that reads it:quality_files— the files every repo must carry at a fixed path. Two members, both measured develop-first on 2026-08-12 across all 20 active repos:CLAUDE.mddevex-bootstrap.cursor/BUGBOT.mdclaude-skills,devex-bootstrap,rfcsBoth files are read by a tool, not by a reviewer who would notice them missing:
CLAUDE.mdis what an AI session is instructed to follow,.cursor/BUGBOT.mdis what Bugbot reads before reviewing a diff. A repo without one does not go red — it quietly gets worse review than its peers, and nothing in the org reported that until now.backend#1608Why this is not a presence check
A bare "the path is in the tree" test is the easiest thing in this repo to write as inert verification. A
requiredfile must therefore be present, a regular file, and non-empty:CLAUDE.mdsatisfies existence and carries nothing → finding120000) resolves forcatand is not the guidance being in the repo → findingexemptcell whose file turns up → stale exemption, the same staleness rule the caller, copy, protection and ruleset families each applyFail-closed
The facts come out of the tree
read_repoalready fetches, so the family costs no extra API call and inherits the existing fail-closed properties. Every one of these is exit 2, never "the file is absent":There is no code path from a failed read to a finding or to an all-clear.
Exemptions written, with reasons
claude-skills.cursor/BUGBOT.md*bugbot_guide_missing— new shared anchor. UNREMEDIATED; the exemption is the finding, not a justification.rfcs.cursor/BUGBOT.md*bugbot_guide_missing(same anchor)devex-bootstrap*devex_bootstrap_undisposed— the existing anchor, widened from protection-only to cover the same open disposition question (backend#1597)The
devex_bootstrap_undisposedtext was broadened rather than duplicated: one repo, one open question, one reason. Two texts about one decision is how two reasons start disagreeing. Its protection claims are unchanged and still accurate.Deliberately narrow. Exemptions were written only where the file is genuinely absent — 4 cells.
release-traincarries both files andclaude-skills/rfcscarryCLAUDE.md, so blanket-exempting every non-train repo would have produced 3 stale-exemption findings and a red audit.Two things deliberately NOT done
1.
quality / gitleaksarmed as a required check needs no new family. It is already asserted byprotection_policy.required_checksondevelop,stagingandprod. Verified live: 16/16 train repos have it armed and 16/16 are asserted, zero mismatches — every repo where the inventory does not assert it is anexempt/divergentcell with a written reason. A parallel family would duplicate a live assertion and then drift from it. It is mutation-checked instead (below), so this PR proves the mechanism is not inert rather than adding a second one.2.
.gitleaks.tomlis not modelled. It is 7/20 and that is correct: a per-repo allowlist you add when you hit a false positive, not a control. Modelling it would add nine exemption rows for zero security value and make a tuning file read as a security gap — the inert-verification patternbackend#1729exists to catch.caller-drift.yml's header now also records whyquality_filesis not remediable by--create-prs, and that this one is a deliberate refusal rather than a technical limit: auto-generating a placeholder would turn every finding green while adding nothing a tool can use.Verification
Live audit, EXIT=0, all 20 repos OK in the new column (
make audit, the same invocationcaller-drift.ymluses):The baseline audit on
developwas also EXIT=0 before this change, so the green above is the new family passing rather than an unchanged verdict.make check: green — ruff, shellcheck (clean), house-rules (0 findings), action-pins, actionlint (0 findings), and all four selftests.Selftest: 136 → 160 cases, 0 failures. 24 new cases: 4 on
read_repo's fail-closed paths, 7 on the evaluator (both exemption directions included), 12 on the schema, 1 on the matrix column.Not vacuous against the live fleet. Removing the three exemptions and re-evaluating the real repos produces exactly the three expected findings (
rfcs×1,devex-bootstrap×2) — the family bites on reality, not only on fixtures.Mutation-checked: 11 mutations, all 11 caught (applied one at a time, reverted after each; tree verified back at 160/160):
quality_filessection no longer requiredquality_filesmatrix column removedrequired_checkscomparison neutralisedNotes for review
gatejob name is untouched — it is the armed required check.quality_filesis an added key, andload_inventoryrejects a repo entry that lacks the section, so an inventory without it cannot be read as "nothing required"..., trailing slash, whitespace-padded) are rejected at load: they would assert nothing while looking like an assertion.🤖 Generated with Claude Code
Note
Low Risk
Changes extend the existing read-only org audit and inventory schema; no runtime services, auth, or auto-remediation paths are altered beyond new reporting rules.
Overview
Adds a
quality_filesconformance family so the org audit tracks whether each repo carriesCLAUDE.mdand.cursor/BUGBOT.mdat fixed paths—tool-facing guidance that was previously invisible when missing.repo-inventory.ymldefines the two paths for all 20 repos withrequired/exemptcells and documented reasons (e.g. missing Bugbot guide onclaude-skills/rfcs,devex-bootstrapdisposition)..gitleaks.tomlis explicitly not modeled;quality / gitleaksstays covered by existing protection assertions.scripts/caller-drift.pyloads and validates the family, reads file metadata from the same develop-first git tree as callers/copies, and evaluates presence, regular file (not symlink/dir), and non-empty content. Failed or truncated tree reads fail the repo row (exit 2), not “file absent.” The conformance matrix and report gain aquality_filescolumn.caller-drift.ymldocuments thatquality_filesis not auto-remediated by--create-prs(placeholders would be inert verification).Selftests add schema, read, evaluator, and matrix coverage for the new family.
Reviewed by Cursor Bugbot for commit d0bcd19. Bugbot is set up for automated code reviews on this repo. Configure here.