Repository navigation
fix(github): keep project availability live on busy repositories - #132
Conversation
Every observed webhook, CI status included, invalidated the scan and restarted it from page 1, so a repository with more than one page of history never completed and availability stayed pending. Split the scan: a history pass pages every issue and PR and resumes through webhooks, while availability depends only on an occupancy pass over open PRs and quest branches, restarted only by events that can open, close, or claim a quest. Paused projects also cleared only ProjectState.available while the board read project.available, so they still offered /quest-start. Drop the duplicate field so the board reads the cleared value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adapt the history/occupancy split to main's jobs and decision inbox: - Renumber the scan migration to 0011, after main's 0007_jobs..0010_inbox. - Jobs and the inbox gate new work on known availability (project.available !== null) instead of a completed history pass, so a busy repository with a long history can still start work. - They read project.available, the single availability field. - A pull_request synchronize webhook invalidates, so an iteration still binds the PR head the maintainer saw. - Use main's shared delivery() fixture in place of test/webhook.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c630dc045f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!state || state.project.available === null) | ||
| throw new GitHubError("Refresh project activity before starting work"); |
There was a problem hiding this comment.
Keep entity jobs gated on fresh entity observations
This gate now treats completed quest occupancy as sufficient for every workflow, although resolve() still authorizes issue and pull jobs from state.activity. For example, an issues: closed webhook only updates changedAt, leaving cached data.state === "OPEN" and availability non-null, so the jobs route can start a planning job for the closed issue; similarly, after a PR closes during a long history pass, one occupancy refresh restores availability while the stale activity row can still authorize iterate. Gate entity-targeted workflows on their authoritative pass or expose and check the current occupancy membership before creating the job.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Agreed. Fixed in f58a5bc: jobSource now rereads a job's issue or pull request from GitHub (/issues/N or /pulls/N) and refuses one that is no longer open, or a PR whose head is not the observed headRefOid. This covers both cases without gating on the history pass, which a busy repository may never finish, and without making issue webhooks invalidate availability. Regression test: refuses an issue or pull request that closed or moved since it was read.
(Written by Claude Opus 5.5)
Webhooks that close an issue or PR no longer restart the scan, so its activity row can still say open until history rereads it. jobSource now reads the target from GitHub and refuses one that closed or whose head moved since it was observed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merging via /quest-merge. Review round 1 (Codex, review): one P1 finding, fixed. With history no longer restarting on every webhook, a closed issue or PR could keep an OPEN activity row and still authorize Main: merged again (no new migrations; Decision: the live reread costs one REST call per issue or PR job and adds Follow-ups (in the PR body's "Not addressed"): unpaginated, never-pruned activity reads; the inbox still lists a closed issue or PR as open until history rereads it, though starting work on it is now refused. (Written by Claude Opus 5.5) |
Fixes two bugs from kixelated/quest#129 in
cloudflare/src/github, adapted to main's jobs (kixelated/quest#135) and decision inbox (kixelated/quest#144).1. Availability stuck on pending
Every observed webhook (check_run, check_suite, status, push, comments...) set
invalidated=1and bumpedrevision. Two things followed:Since main, the same gate also blocks every new job and the inbox's start actions, so a busy repository could never start work.
The fix splits the scan into two passes, stored in the existing
scancolumn:states:OPEN, 100 per page) andquest/branches, which is all availability depends on. It restarts on every refresh after it completes. It is usually one page, so candidates are current after a single refresh however long the history is.Only events that can change availability or a PR head invalidate it: PR opened, closed, reopened, or synchronized; a
quest/branch created or deleted; a push to the default branch. Other events still record activity, but they no longer touchgithub_project_state.changescounts only PRs seen in the latest occupancy pass. Without that, a closed PR whose activity row still said OPEN would block its quest until history reached it again.Jobs (
jobSource, thestartresolver) and the inbox (start actions, "Refresh needed") now gate on known availability,project.available !== null, instead of a completed history pass.ProjectState.completemeans the history pass finished and only drives the activity page's "Continue reconciliation".Migration
0011_project_scan.sqlsets old-shapescanrows to NULL. They're an observation cache, so the next refresh rebuilds them.Since closing an issue or PR no longer restarts the scan, its activity row can still say OPEN until history rereads it.
jobSourcetherefore rereads a job's issue or pull request from GitHub (/issues/Nor/pulls/N) and refuses one that closed, or a PR whose head moved, since observations read it.2. Paused projects offered /quest-start
#observationsclearedProjectState.available, but the board readsproject.available. I removed the duplicate top-level field, so the paused path now clears the value the board, jobs, and inbox all read.Decisions
Taken unattended in one
/quest-iterateround, recommended option each time:state.project.available, the single field (removes the duplicate that caused bug 2)ProjectState.availableand clear both when pausedproject.available !== null): occupancy complete and no invalidating webhook sinceProjectState.complete), which reintroduces the busy-repository bug for jobsProjectState.currentflag, which duplicatesproject.availablepull_requestsynchronizeinvalidate?0007_project_scan.sqlto0011, after main's0010_inbox.sql.issues: readandpull_requests: read, which refresh already needstest/webhook.tsfor main's identicaldelivery()intest/project-fixtures.ts.Tests
keeps candidates and history progress through activity that cannot occupy a quest: CI, comment, review, PR edits, and non-quest push events leave candidates known, the history cursor resumes, and a status event mid-refresh doesn't fail it.withholds candidates after ...: each invalidating event (now includingsynchronize) makes candidates pending until the next refresh, and history still resumes.starts work while history is still paging, but not before a refresh reads a new pull request head(jobs): a start succeeds with history incomplete, and an iterate after asynchronizewebhook is refused until a refresh.refuses an issue or pull request that closed or moved since it was read(jobs): plan on a closed issue, iterate on a closed PR, and iterate on a moved head are refused while observations still say open.project.availableand the board's available count.maintainerOf()intest/project-fixtures.tsuses the two-pass scan shape.just checkandjust testpass locally after merging main (232 CLI tests, 257 Worker tests).Not addressed
readStatestill reads every activity row, unpaginated, on each/board,/state,/data, and inbox view, and rows are never pruned. That needs a decision on retention for closed entities.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code