Facts port: the checks, the queue and the one snapshot held to facts/1, with the port read against the fake (#191)
Merged
DO
6d ago+738+738+012345678901234567890123456789-35-35-01234567890123456789
Part of #191: its third and last PR by the ruling. It adds the checks, the queue and the one snapshot held to facts/1, and a contract test that reads the port against what the fake assumes.
What it adds
adapters/checks.pymakes one constant GraphQL read of the head's status rollup:- It gives every check run with its name, App id, workflow path, lower-cased conclusion or status, and completion time.
- It reports whether the pull request is in the merge queue, and how many review threads it holds.
- The list is whole only when the last page says so, the count stays the same across pages, and every counted context was read.
- It refuses a rollup of another commit, and a head that moved while it was read.
- A status context is no check.
adapters/facts.pygivessnapshot(...) -> (facts/1, title):- Every part is read in turn, and the pull request is read again last (
same_head). A move refuses the whole. - It takes its own time after the last read.
- It is validated against
schemas/facts.schema.json, and a snapshot that fails is refused.
- Every part is read in turn, and the pull request is read again last (
adapters/pull.pyaddsbase(protected? and the base branch's head) andlast_push(who and when).- The contract test (
Contract) checks each fake rule that a fixture grounds and the port reads. The port must read that same fixture as the rule states. The fake itself is not run. - Rows re-pointed: decisions 0012, 0015, 0018, 0022, 0026, lessons L-51 and L-53, and
sim/AGENTS.md.
Known limits, stated in the code
threadsare whole only when the pull request holds none. Their state and category wait onadapters/threads.py.acceptanceis null and never whole. It waits onadapters/acceptance.py. Until a rule says when an issue asks for a contract, every pull request defers on it. This is flagged to the Factory Builder.last_push_atfalls back to the read's time when the base's activity does not name the head. A fork's head is never named, so its wait from the push starts again each pass.- A head pushed away and back between the two reads is not seen. The diff and the files are the pull request's own and are not pinned to the head commit. Pinning the diff by
compare base...shais grounded: P-29 shows the bytes are the same. The file list is not, because compare caps it at 300. That is left to a follow-up. - Two checks are still
@pending. P-42 asks a re-run's scope. A dynamic workflow's path needs a REST read.
Gates
| Gate | Result |
|---|---|
| Claude review + security pass (network boundary) | done; one fix round applied (77b9440) |
| AGY Flash | not run: quota reached ("Individual quota reached… Resets in 51h") |
| Muse | not run |
| Capped mutant run, by hand | every adapters/checks.py and adapters/facts.py mutant killed, 7 new |
Export verify (git archive, PATH=/usr/bin:/bin SIM_SEEDS=40 make verify) | exit 0, 915 tests |
🤖 Generated with Claude Code
Summary by CodeRabbit
-
New Features
- Added reliable pull request snapshots covering metadata, branches, reviews, labels, files, timelines, and check results.
- Added comprehensive check reporting, including workflow details, statuses, conclusions, and merge-queue information.
- Added safeguards to ensure snapshots reflect a consistent pull request state.
-
Bug Fixes
- Incomplete, malformed, or inconsistent check data is now detected instead of being accepted.
- Updates or head changes during snapshot collection are rejected.
-
Documentation
- Clarified check-reporting, workflow, snapshot, and simulator behavior.