landing: verdict window from PR creation; worker test runs become commit statuses

GitHub skips pull_request workflows for commits made with the Actions token,
so a Venice worker's own test run is recorded as a commit status and counted
as the PR's check. A verdict posted in the same beat as the pr beat now counts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UpT7eWzeLhRwuYmsSQnbRb
This commit is contained in:
claude committed 2026-10-11 12:53:40 -07:00
1 parent c5b20a8654
commit adae855675
2 files changed
+28 -7

No files matched your search

+14 -1
View File
@@ -80,6 +80,9 @@ class World:
return d["pr"]
if "/check-runs" in path:
return {"check_runs": next(d.get("checks", []) for d in self.gh_pr.values() if d["pr"]["head"]["sha"] in path)}
if path.endswith("/status"):
return {"statuses": next(d.get("statuses", []) for d in self.gh_pr.values() if d["pr"]["head"]["sha"] in path)}
if "/statuses/" in path and method == "POST": self.statuses = getattr(self, "statuses", []) + [body]; return {}
if "/issues?state=open" in path:
lab = path.split("labels=")[1].split("&")[0]
return [{"number": i["number"], "title": i.get("title", ""), "body": i.get("body", ""), "state": "open"}
@@ -199,7 +202,7 @@ assert "couldn't find cards" in w.posted[0][1] and w.issues[1]["labels"] == []
# 13 landing: Approve from a trusted reviewer + green checks -> squash merge, card closed, next card dispatched
def pr_world(verdict_body="**Approve** looks right", assoc="NONE", bot=True, checks=None, files=None, extra=None):
prd = {"number": 7, "state": "open", "merged_at": None, "head": {"sha": "abc"}, "html_url": "u"}
prd = {"number": 7, "state": "open", "merged_at": None, "head": {"sha": "abc"}, "html_url": "u", "created_at": iso(2)}
issues = {2: {"number": 2, "title": "next", "body": "Depends on #1\n\nDONE: d", "state": "open", "labels": ["queued"]}}
cm = {1: [beat("pr", 1, pr=7)],
7: [{"created_at": iso(), "author_association": assoc, "user": {"type": "Bot" if bot else "User"}, "body": verdict_body}]}
@@ -249,3 +252,13 @@ assert "PR #42" in w2.posted[0][1]
w3 = run(World([card(1, D)], {1: [beat("would-dispatch")]}), {"TIME_MODE": "live", "TIME_WORKER": "venice:kimi"})
assert "VENICE_API_KEY" in w3.posted[-1][1]
print("ALL OK 4")
# 15 a commit status from the worker's own test run counts as the check
w = pr_world(checks=[]); w.gh_pr[7]["statuses"] = [{"state": "success", "context": "time/worker-run"}]
w = run(w, {**K, "TIME_AUTOMERGE": "on"}); assert any(c[0] == "PUT" for c in w.calls), "status should count as a green check"
w = pr_world(checks=[]); w.gh_pr[7]["statuses"] = [{"state": "failure", "context": "time/worker-run"}]
w = run(w, {**K, "TIME_AUTOMERGE": "on"}); assert not any(c[0] == "PUT" for c in w.calls)
# a verdict posted in the same run as the `pr` beat still counts (window starts at PR creation)
w = pr_world(); w.comments[1] = [beat("pr", 0, pr=7)]
w = run(w, {**K, "TIME_AUTOMERGE": "on"}); assert any(c[0] == "PUT" for c in w.calls)
print("ALL OK 5")
+14 -6
View File
@@ -309,16 +309,19 @@ def touches_protected(pr_number: int) -> list[str]:
def checks(sha: str) -> str:
"""pass | pending | fail | none, over every check run on the PR head."""
runs = gh("GET", f"/repos/{REPO}/commits/{sha}/check-runs?per_page=100").get("check_runs", [])
if not runs:
states = gh("GET", f"/repos/{REPO}/commits/{sha}/status").get("statuses", []) # commit statuses count too
if not runs and not states:
return "none"
if any(r["status"] != "completed" for r in runs):
if any(r["status"] != "completed" for r in runs) or any(st["state"] == "pending" for st in states):
return "pending"
return "pass" if all(r["conclusion"] in ("success", "neutral", "skipped") for r in runs) else "fail"
ok = all(r["conclusion"] in ("success", "neutral", "skipped") for r in runs) and all(st["state"] == "success" for st in states)
return "pass" if ok else "fail"
def land(card: dict, last: dict) -> None:
"""Move a card whose PR is up toward merged. Silent while nothing has changed."""
n, pr = card["number"], gh("GET", f"/repos/{REPO}/pulls/{last['pr']}")
since = pr["created_at"] # any verdict after the PR exists counts; a stale beat time would hide same-run reviews
if pr.get("merged_at"):
if card.get("state", "open") == "open":
gh("PATCH", f"/repos/{REPO}/issues/{n}", {"state": "closed"})
@@ -326,7 +329,7 @@ def land(card: dict, last: dict) -> None:
return
if pr["state"] == "closed" or (os.environ.get("TIME_AUTOMERGE") or "off").strip() != "on":
return
v = verdict(pr, last["at"])
v = verdict(pr, since)
if v is None:
return
if v == "object":
@@ -393,7 +396,7 @@ WORKER_RULES = """You are the worker for card #{n} in {repo}: "{title}".
You have hands: list_files, read_file, write_file (one file per call, full content), run (shell on a checkout of your
branch), open_pr. Work on the branch `{branch}`; everything you write lands there. Keep the change small and focused.
Read before you write. Run the project's tests or checks if any exist. When it is done and checked, call open_pr once
Read before you write. Always `run` the project's tests or checks before opening the PR (that run is the PR's check). When it is done and checked, call open_pr once
with a short summary; the PR must close the card, and Time adds that line for you. Do not touch .github/workflows.
The card's finish line is: DONE: {done}"""
@@ -441,8 +444,13 @@ class Hands:
self.checkout = None
return f"wrote {a['path']}"
if name == "run":
self.checkout = self.checkout or self._checkout()
self.checkout = self._checkout() # fresh: see the latest writes
p = subprocess.run(["bash", "-lc", a["command"]], cwd=self.checkout, capture_output=True, text=True, timeout=600)
sha = subprocess.run(["git", "rev-parse", "HEAD"], cwd=self.checkout, capture_output=True, text=True).stdout.strip()
# GitHub does not run pull_request workflows for commits made with the Actions token, so the worker's own
# test run is recorded as a commit status; land() counts statuses as checks.
gh("POST", f"/repos/{REPO}/statuses/{sha}", {"state": "success" if p.returncode == 0 else "failure",
"context": "time/worker-run", "description": a["command"][:100]})
return f"exit {p.returncode}\n{(p.stdout + p.stderr)[-6000:]}"
if name == "open_pr":
pr = gh("POST", f"/repos/{REPO}/pulls", {"title": a["title"][:200], "head": self.branch, "base": default_branch(),