Repository navigation
feat(claims): read claims and readiness from the upstream tree - #192
Conversation
Quest-Claim: 265ab2d5c3627d77
A quest branch whose tip is a merged PR's head no longer counts as a claim, and quest start replaces it with a lease on that tip; a closed, unmerged PR's branch still holds its quest. quest unblocked --unclaimed and quest start rank and validate the fetched upstream default branch's tree instead of the checkout. The head repository comes from the remote's push URL, parsed through remoteRepository in github.ts. Completes quest/m0/helpers/claims.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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: 1f197bedae
ℹ️ 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".
| const pushed = remoteRepository(root, remote, true).repository; | ||
| const head = pushed === null || same(pushed, self) ? self : (await repositoryInfo(client, pushed)).repository; |
There was a problem hiding this comment.
Treat a non-GitHub push target as PR-less
When a remote fetches from GitHub but has an explicit push URL on GitLab, a local server, or another non-GitHub forge, this fallback incorrectly identifies the fetched GitHub repository as the head repository. Consequently held() never checks branches at the actual push target, and quest start pushes there before trying to open a GitHub PR whose head branch does not exist on GitHub; the PR request fails and cleanup deletes the just-pushed branch. Preserve the GitHub repository for upstream-tree reads, but represent the non-GitHub push target separately so it is checked as a branch-only source and no PR is attempted.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Leaving this as is for now. Before this PR, the head was always the fetched repository whatever the push URL said, so the fallback keeps the old behavior for that setup instead of adding a third mode. It also fails loudly: GitHub refuses to open the PR, the just-pushed claim branch is deleted with its lease, and quest start exits 1. A GitHub fetch URL with a non-GitHub push URL (a mirror, say) is rare, and making it a branch-only claim source would need its own design, for example whether unblocked --unclaimed should check branches on two forges. I listed it as a suggested follow-up in the report.
(Written by Claude Opus 5.5)
|
Outcome: the quest is complete with no open decisions, so this PR is ready for review. (Written by Claude Opus 5.5) |
|
Merge summary: enabling auto-merge on head
The follow-ups in the PR body go to (Written by Claude Opus 5.5) |
Add [XS] merged claims to the Kix epic, so the board ignores branches whose pull request merged, as the CLI does since #192. Add [S] lifecycle EBUSY to m0, to root-cause the Windows lifecycle check failing while removing its temp directory. Drop a merged-branch sweep and the push-to-another-forge case. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Completes
quest/m0/helpers/claims.md(plan at d3d721c). A spawn on 2026-10-21 skipped two quests that had partly landed as "held by branch ..., with no open PR".quest unblocked --unclaimedandquest startalso read readiness from the local checkout, so a stale checkout could offer a finished or deleted quest.Changes
src/cli/claims.ts). A pushed quest branch with no open PR is checked against GitHub. If its tip is the head of a merged PR from that branch, it holds nothing. The check is onepulls?state=closed&head=<owner>:<branch>request per such branch. A closed, unmerged PR's branch still holds its quest, and so does a merged branch that moved on after merging.held()now returns{ claims, landed }.quest startreplaces a landed branch with--force-with-leaseon that tip; every other claim push leases on the branch not existing, which matches the old non-forced push.quest mvgets the same rule throughclaims().src/cli/tree.ts,src/cli/main.ts).snapshot(root, commit)reads the quest tree from a commit with onels-tree -rand onecat-file --batch. Paths are relative to--root, and the commit's file list backs link checks.quest unblocked --unclaimedandquest startvalidate and rank the fetched upstream default branch instead of the checkout. A quest that exists only locally is refused with the existing "not on the upstream default branch" message. Plainquest unblockedandquest unblocked <path>still read the checkout. Without the remote,--unclaimedfalls back to the checkout with the existing warning.src/cli/github.ts).remoteRepository(root, remote, push)reads the fetch or push URL, trying the configured URL first soinsteadOfrewrites still name GitHub.upstream(),pullRequest(), and the push side all use it. The head repository now comes from the push URL, so a remote that fetches from upstream and pushes to a fork claims from that fork. A push URL that is not on GitHub falls back to the fetched repository.githubRepositoryalso accepts HTTPS with credentials,ssh://with a port, any scp-like user, and a trailing slash.startQuest(root, target, path, title, deps)takes aTargetfromtarget(): the remote, its upstream, and the fetched base and its tree. The CLI reads the tree it checks from the same commit the claim builds on.remoteHead/ensuremoved toclaims.tsasupstreamHead/ensure.--helpdescribe the upstream tree, landed branches, and push-URL forks. This PR deletes the quest and its entry inquest/m0/helpers/README.md.The plan asked to reuse a tree loader from kixelated/quest#169. That guard only runs
git cat-file -eon one file, sosnapshot()is new, and it lives beside the disk loader.Validation
just check: passed.just test: passed.test/start.test.ts:startreplaces it, upstream and in a fork.unblocked --unclaimedranks upstream's ready quest.startrefuses the quest upstream finished and claims the one that is locally blocked but ready upstream.--unclaimed/start.Suggested follow-ups
quest unblocked --unclaimednote, or a cleanup command, could list them for deletion.cloudflare/src/github/state.ts) counts everyquest/*ref as a claim. Apply the same merged-head rule there so its board agrees with the CLI.quest startthen fails loudly when GitHub refuses the PR. Treating that push target as a separate branch-only source would need its own design (review thread).EBUSYwhen it removes its temp directory after passing. That happened here and onquest/m0/worktree(run 39679528766). Find which process holds the directory, rather than retrying the removal.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code