Kernel: a change to the Factory's own rules or a Lens's inputs is reviewed by a model Lens

Merged
DO
4d ago
+1830-78

Closes #113.

A pull request that changes the Factory's own rules, or the inputs of a deterministic Lens, now converges only once every model Lens has reviewed its current head, whatever its class. The machine user then approves it, as it does every item. Before this, a Dependabot update to .github/workflows/verify.yml was judged by that Lens alone. Per the Maintainer's ruling on #113, no person is needed. The rulings are in the issue's comments, and decision 0020 records them.

What changes

  • Facts:
    • New changed_paths, one {path, previous} record per changed file, and complete.changed_paths.
    • A list read short defers (facts_incomplete).
    • A list that is malformed, or whose number of records is not exactly diff.files, holds for the Maintainer (paths_unreadable).
  • One list: config/hooks.json. Its protected entries and a new lens_inputs are read by both the session hooks and the Kernel's protected_paths, and a test pins the two matchers to each other.
    • Lens inputs: Makefile (in any case), GNUmakefile, every file or link at the repo root (*), a root package's __init__ and bytecode (*/__init__.*, __pycache__/, */__pycache__/), the root packages whole, the check folders, kernel/, .gitleaks.toml and .gitleaksignore.
    • Empty or malformed list: the Kernel defers (protected_paths_unreadable).
    • Where the list comes from: decision 0020 says it is built from the base, never the head. This waits on controller/.
  • Kernel: required_model_lenses() no longer excuses model_not_required_for when the change touches the list. A queued item on the list leaves the queue when its head has no model review.
  • Simulator:
    • Three actors make such changes: a Role, Dependabot and the Maintainer.
    • It asserts I-45 at approval, enqueue and merge.
    • One seed in ten is a dependency update; every other seed replays as on main.
  • Records:
    • New decision 0020, which decides 0018's workflow-file row differently: no person.
    • I-45 is added to the design. §3.3, §4.6 and §4.9 now say what 0020 decided.

Verified on a git archive export at a534a0f: make verify passes (501 tests); python3 -m tools.mutants kills 348 of 348; check_decisions is clean.

Reviewed by

  • A fresh-context Claude review and a security pass. Their findings were fixed in 799f8ee:
    • an empty path list marked complete;
    • the list must come from the base;
    • the missing Lens inputs;
    • design-doc drift;
    • 0018's Check cell.
  • The 400k-pair fuzz of the two matchers found no disagreement.
  • AGY, Gemini 4 Argon (799f8ee): two majors and four minors, all real and fixed in 7b5f160. The majors: the matcher's overlap check had no test or mutant, and a malformed pattern failed open.
  • CodeRabbit: rename names could fill the path count, so changed_paths is now one {path, previous} record per file and must equal diff.files. Fixed in 7b5f160.
  • AGY, Gemini 3.8 Flash High (7b5f160; Argon was out of credits): no findings.
  • Muse Wonder (muse-wonder-1.0, 7b5f160) and Codex: one blocker. A root-level package such as unittest/__init__.py shadowed the stdlib inside verify, so a dependency update converged unreviewed. Fixed in 95c7584. Codex's three other threads were already fixed in 7b5f160.
  • AGY, Gemini 4 Argon (95c7584, d29be3f, a534a0f): no findings. Muse could not re-read after 7b5f160 (its subscription quota was used up).
  • Codex (95c7584, 4e98ba5): two more gaps of the same class. Files inside an existing root package were closed in d29be3f, with a test that every root package is listed whole. A root symlink with no suffix was closed in a534a0f by making every root file or link an input.
  • CI: verify passed the 30-minute job timeout, which #136 raised to 40. Sharding is #112.

Left out: reading the fact from GitHub (adapters), the ruleset half (#62), the harness refusing such a push, and running each deterministic Lens from the base's inputs (next).

🤖 Generated with Claude Code