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-35

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.py makes 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.py gives snapshot(...) -> (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.
  • adapters/pull.py adds base (protected? and the base branch's head) and last_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

  1. threads are whole only when the pull request holds none. Their state and category wait on adapters/threads.py.
  2. acceptance is null and never whole. It waits on adapters/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.
  3. last_push_at falls 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.
  4. 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...sha is 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.
  5. Two checks are still @pending. P-42 asks a re-run's scope. A dynamic workflow's path needs a REST read.

Gates

GateResult
Claude review + security pass (network boundary)done; one fix round applied (77b9440)
AGY Flashnot run: quota reached ("Individual quota reached… Resets in 51h")
Musenot run
Capped mutant run, by handevery 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.