The repo's rulesets are recorded and held to the Lenses, and code scanning's threshold with them (#62, #95)
The Factory repo's two rulesets are now recorded as data. A check holds them to the Lenses, and a hand-run tool compares them with what GitHub has live. Code scanning's threshold is recorded with them.
What changes
-
config/rulesets.jsonrecords two rulesets, as GitHub answered the Maintainer's read of them on 2026-10-21. Each rule is keyed by its type.main(24886320): deletion, non-fast-forward, the pull-request rule,verifypinned to GitHub Actions (15368), the merge queue, and CodeQL's code-scanning rule.factory release tags(25183656): creating, updating, deleting or force-pushing afactory/*tag is refused to everyone but the repository's admin role.
-
schemas/rulesets.schema.jsonholds as a constant what a ruling fixed:- no bypass on
main; required_review_thread_resolution: true, applied 2026-10-18;- squash merges and the merge queue;
- a required check pinned to its App;
security_alerts_threshold: all(#95, set 2026-10-20);- the tag rules and their bypass, which the admin role alone holds until P-30 names the release identity.
Nine counter-examples in
examples/invalid/rulesets.*.json, each wrong in one way. - no bypass on
-
tools/check_rulesets.pyis a new check run bymake verify. It holds the values written both here and inconfig/defaults.json:- each check
mainrequires is a deterministic Lens's check, pinned to that Lens'sapp_id; verifyis among them;- each code-scanning tool is a Lens that no workflow runs.
- each check
-
tools/rulesets_drift.shcompares the live rulesets with the file. It is run by hand, never in CI, and callstools/rulesets_drift.py.- It reads through whoever is logged in to gh and mints nothing.
- It writes each answer outside the repo, refusing a scratch directory that resolves inside it.
- It redacts each answer with
tools/redact_trial.pyand deletes the raw one. - A ruleset is known by its name and id together, so an org ruleset that shares a recorded name is reported as drift.
- Run on the final head: no drift.
-
The design names the threshold where it describes the deterministic Lenses (#95's third box). The §9.9 gap row now says what is set and what no trial has seen. It also says the merge queue's group stays open: the code-scanning rule does not hold it, as the oversight session saw on 2026-10-21.
-
Decision rows. One Boundaries row on decision 0014: CodeQL's threshold is stricter than the org's and needs no exception. One on decision 0020: the
mainruleset requires no review, as the file records it. No new decision record: no rule changes.
Not done here
- The live check in CI. CI fetches nothing, so the drift check stays manual until
controller/exists (the 2026-10-19 plan review). - Requiring the machine user's approval. This ruleset change stays the Maintainer's, with #89.
Reviews
- Export verify.
make verifypassed on a freshgit archiveexport withPATH=/usr/bin:/bin SIM_SEEDS=40, on both heads. - Mutants. No full
python3 -m tools.mutantsran locally. All 14 new mutants were applied by hand on scratch copies, and each was killed by its named test. - Claude review (fresh context). One major finding and five minor ones, all fixed in the second commit. The major one: two live rulesets that share a name hid one of them from the drift check.
- Security pass. No blocker and no major finding. Six minor findings, all fixed: the scratch-directory guard now resolves links, the cleanup runs on a signal under dash, the test proves the stand-in gh is the one that answers, and the admin role and the gap row are worded no stronger than the data holds.
- AGY (Gemini 3.8 Flash High). No findings.
- Muse. Not run (Maintainer, 2026-10-20: Kernel/hook only).
🤖 Generated with Claude Code