Kernel: a Lens is read only from the App that reports it
Part of the epic "Epic: nine gaps in the Kernel, found in review". Closes #60.
The facts keyed a check by name alone, so any App that can write checks could post a passing check under a required Lens's name, and among several entries with one name the last won: a probe with [code-scanning: failure, …, code-scanning: success] converged and was approved.
The rule
-
facts.checks[]carriesapp_id, the check run'sapp.id. Each entry ofrequired_lenses.deterministicinconfig/defaults.jsonnames its Lens, its exact GitHub check name and its App:Lens Check App verifyverify15368 (GitHub Actions; the mainruleset pins it)secret-scanSecret scan (gitleaks)15368 code-scanningCodeQL57789 (GitHub Advanced Security) -
A check under a Lens's name from any other App, or with an
app_idthat is not a whole number, makes the item unreadable:check_unreadable,needs_you, evidence<lens>:from_app:<id>. It is read before any Lens's failure and before a conflict, so it admits no fix Run. A check run cannot be deleted, so only a push clears it. A queued item dequeues on it. -
Every entry from the Lens's own App is read. Any failure fails the Lens; then any entry with no verdict makes it unreadable; then any pending one is a wait. It passes only when every entry passes.
-
Only a check from the Lens's own App moves an item (#94).
-
The Factory's own status has one fixed name,
status_on_item: "factory".tools/check_authority.pyrefuses it if it equals a required Lens's name or check name, ignoring case and spaces, and refuses a Lens or check named twice.
The port's contract (waiting on adapters/)
A check run whose name equals a Lens's check is emitted with that Lens's name and its real app.id, for every App, never dropped or merged across Apps. Only a re-run of the same job in the same check suite replaces an earlier run: GitHub's filter=latest is per check suite, so two workflow runs on one commit both stay, and the Kernel reads both, which fails closed. Both are in the facts.checks schema description, design §3.3, sim/grounding.py (unverified) and decision 0012.
What the App pin does not do
15368 is every GitHub Actions job, including one a pull request adds with the same name. Reading every entry, all of which must pass, is what makes an extra passing check harmless; replacing the real job needs a change under .github/, which only the Maintainer authors (decisions 0004, 0005). The design's threat row and decision 0005 say so. Pinning each Lens to its workflow file is "A Lens run by GitHub Actions is read only from its own workflow file" (#97), which also records whether a code-scanning upload can replace the CodeQL verdict.
Tests
test_a_check_from_another_app_is_unreadable_for_its_lens, test_every_check_a_lens_reports_from_its_own_app_is_read, test_a_check_from_another_app_or_a_failure_beside_a_pass_takes_it_out_of_the_queue, test_only_a_check_from_its_lens_app_moves_an_item; in tests/test_tools.py, test_the_status_on_the_item_never_carries_a_required_lens_name and test_a_lens_or_a_check_named_twice_is_refused. Counter-examples for a check without its App, a required Lens with no App, and one with no check. The simulator lets another App post a passing check under a failing Lens's name; nothing but a dequeue may act while it stands, and a push clears it. 166 of 166 mutants killed.
Read by
A fresh-context review and a separate security pass (Claude), on the first commit; the second commit answers them. The security pass found that once the facts reach the Kernel no check from another App and no failing run lets an item converge, be approved or stay queued; what remained was the port's contract, now written down. AGY (Gemini) to follow; Codex and CodeRabbit are at their usage limits. make verify passes on a git archive export (349 tests).
🤖 Generated with Claude Code
Summary by CodeRabbit
- Behavior Changes
- Required checks are now matched to both their exact check name and reporting GitHub App. Checks from other Apps cannot satisfy a requirement; they make the item’s check status unreadable.
- When an App reports multiple matching checks, all are evaluated: any failure fails the requirement, and all must pass for it to pass.
- Only required checks from their configured App count toward item activity timing.
- Validation
- Configurations now require an item status name and explicit App IDs for required checks. Duplicate names and status names that conflict with required checks are rejected.