fix(source-control): D6 resolves the push remote instead of hardcoding origin - #2312
Conversation
…g origin Codex P1-2 on #2262: the new D6 reachability gate wrote `git fetch origin <branch> && git merge-base --is-ancestor <fix-sha> origin/<branch>` into review-discipline.md and SKILL.md -- a hardcoded remote this PR itself introduced (base 78dbb10 greps clean for "git fetch origin" in both files), while the same skill pushes through resolve-remote.sh --push. On a triangular/fork checkout that false-fails a successful push, and an `origin` base repo with a same-named branch can verify the wrong ref. Both gates now resolve the push remote through the existing resolve-remote.sh --push (reused, not reimplemented) and compare against FETCH_HEAD -- exactly what the resolved remote just served, immune to tracking-ref and same-name ambiguity. Live re-verification (the #2244 motivating branch was deleted from the remote -- where the new gate fails loudly rather than passing -- so an equivalent live case): on feat/2257-worktree-liveness-lock, fix commit d683d53 is not the tip (0f7be74 is). New form exits 0; the old tip-read returns 0f7be74 and reports the fix missing; repo-presence `commits/a223aee3` returns 200 for a commit NOT on that branch where the new form exits 1. Also folds the verifier's CHANGELOG wording nit: babysit_gh.py's `per_page=100` lives at fetch_paginated_api's call sites, not adjacent to the `--paginate` flag; the conformance claim is unchanged. And the verify_fix_commit comparison now claims a matching reachability guarantee, not a matching primitive: that helper proves the same ancestor property via the fork-aware compare API, not git merge-base (claude[bot] thread). Refs #2244 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… scoped to what it shipped The cherry-picked fix predated #2262's squash-merge, so its CHANGELOG hunks rewrote the now-shipped 0.51.12 entry to describe the resolved-remote form 0.51.12 never contained. The 0.51.12 entry now describes its own release again (hardcoded origin, with a forward note), keeps only the two factual corrections (guarantee-not-primitive; per_page location in babysit_gh.py), both declared in the new 0.51.15 entry that documents this fix. Refs #2310 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 52s —— View job Security Review
Scope: FindingsNo security issues found. This PR is documentation-only: it changes the instructional text of the D6 "verify commit pushed" gate (a checklist an agent follows), a changelog entry, and a semver bump. No executable scripts, workflow files, or config/permission settings are touched — Specifically considered and ruled out:
|
…he worktree liveness lock Completes the durable-but-unfinished d683d53: entry documents the lock arming, the BISECT_LOG probe, the in-progress-over-landed ranking, the doc rows, and the failing-without-fix test proof. Number assumes #2312 (0.51.15) lands first and is re-resolved against main immediately before merge. Refs #2257 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Warning Automated security review did not complete — this is an infrastructure failure, not a review verdict. Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."
The check is green on purpose, and it is not evidence. It certifies that a security pass ran, and this one did not complete — but the cause is outside this PR's control, so merging is deliberately left unblocked rather than locking every merge for the length of the outage. Nothing was reviewed at this head. Where this check is required, it is satisfied without that evidence; a human should review security-sensitive changes here before merging. Re-run the job to retry the review; a new push also retries it only if the caller's Re-running does NOT help for every class:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a20f1667c
ℹ️ 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".
| - [ ] **Verify reply exists — on the surface it was posted to:** inline threaded replies land on the review-comment surface — `gh api --paginate "repos/<owner>/<repo>/pulls/<N>/comments?per_page=100" --jq '.[] | select(.in_reply_to_id == <original-id>)'`; issue-level replies — `gh api --paginate "repos/<owner>/<repo>/issues/<N>/comments?per_page=100" --jq '.[].body'`. Querying only issues/comments false-fails a correctly posted inline reply; so does dropping `--paginate`, since these endpoints return 30 per page oldest-first and your reply is the newest item | ||
| - [ ] D6 — Fix if VALID (fix now) — edit, `git add <files>`, commit, push | ||
| - [ ] **Verify commit pushed:** `git fetch origin <branch> && git merge-base --is-ancestor <fix-sha> origin/<branch>` — exit 0 means the fix commit is on the remote PR branch; non-zero means it is not. A reachability check after fetch, never the branch-tip read (`commits?sha=<branch>&per_page=1` + `.[0]`), which any later push turns into a false "missing", and never a repository-scoped `commits/<fix-sha>` lookup alone, which can pass when the commit was force-pushed off the PR branch | ||
| - [ ] **Verify commit pushed:** `REMOTE=$(bash "${CLAUDE_PLUGIN_ROOT}/skills/pull-request/scripts/resolve-remote.sh" --push <branch>) && git fetch "$REMOTE" <branch> && git merge-base --is-ancestor <fix-sha> FETCH_HEAD` — exit 0 means the fix commit is on the PR branch as just fetched from the resolved push remote; non-zero means it is not. Three constraints, each closing a real false verdict: resolve the push remote (`resolve-remote.sh --push`, the same resolver `push-branch.sh` pushed through), never a hardcoded `origin` — a triangular/fork checkout pushes elsewhere, so `origin` false-fails a successful push or verifies a same-named branch on the wrong repository; a reachability check after fetch (`FETCH_HEAD` is exactly what the resolved remote just served), never the branch-tip read (`commits?sha=<branch>&per_page=1` + `.[0]`), which any later push turns into a false "missing"; and never a repository-scoped `commits/<fix-sha>` lookup alone, which can pass when the commit was force-pushed off the PR branch |
There was a problem hiding this comment.
Fetch the push URL rather than the remote's fetch URL
When the resolved remote has remote.<name>.pushurl pointing somewhere different from remote.<name>.url, push-branch.sh writes to the push URL but git fetch "$REMOTE" reads from the fetch URL, so a successful fix push can be checked against another repository and falsely block D7/thread resolution. This distinction is already documented in plugins/source-control/skills/babysit-prs/reference/loop.md:188-195 and :220-229, which uses git remote get-url --push --all because git push honors separate push URLs; the verification needs to fetch the actual PR-head push destination rather than assuming FETCH_HEAD came from where the push went.
Useful? React with 👍 / 👎.
Post-merge verification — posted because this PR merged with no verdict on itThis PR merged at This is a main-thread check by the same context that requested the work — it is NOT the independent verdict the process calls for. It is a floor, not a substitute. The regression is gone from
|
…he worktree liveness lock Completes the durable-but-unfinished d683d53: entry documents the lock arming, the BISECT_LOG probe, the in-progress-over-landed ranking, the doc rows, and the failing-without-fix test proof. Number assumes #2312 (0.51.15) lands first and is re-resolved against main immediately before merge. Refs #2257 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he worktree liveness lock Completes the durable-but-unfinished d683d53: entry documents the lock arming, the BISECT_LOG probe, the in-progress-over-landed ranking, the doc rows, and the failing-without-fix test proof. Number assumes #2312 (0.51.15) lands first and is re-resolved against main immediately before merge. Refs #2257 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 27s —— View job Code Review
Scope: Verification performed
FindingsNo correctness issues found. The change is a well-scoped, internally consistent doc fix:
One minor, non-blocking style note (not filed inline since it's not a defect): the 0.51.12 CHANGELOG entry's forward-reference clause — "0.51.15 replaces the hardcoded |
|
Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
…rogress above landed (#2315) Closes #2257 ## What `git worktree remove` deletes a worktree whose `status --porcelain` is empty even while an interactive rebase paused at a `break` is mid-flight — cleanliness cannot carry liveness. - **`worktree-create.sh`** arms `git worktree lock` the moment the worktree exists, reason naming the helper, host, and start time. The cleanup skill already honored a `locked` flag, but nothing in this repo ever set one — that input was structurally always absent. Lock failure warns rather than failing creation. - **`landed-work.sh`** adds `BISECT_LOG` to the in-progress probe (a bisect leaves porcelain completely clean) and ranks `in-progress` above `landed`: consumers read `landed` as safe-to-remove, and removal mid-operation destroys sequencer state and conflict resolutions even when every commit is durable. The stranded family still outranks it. - **`cleanup.md`** gains locked and in-progress candidate rows (a locked worktree is disarmed with `git worktree unlock` on explicit owner confirmation — never bypassed with `--force --force`); **`create.md`** documents the lock and its `git worktree move` interaction. - CHANGELOG 0.51.16 + plugin.json bump (assumes #2312 = 0.51.15 lands first; re-resolved against `main` immediately before merge). ## Test proof (both directions) With the fix — `landed-work.test.sh` 57/57 PASS; `worktree-create.test.sh` all PASS including new cases 155–160. Against `main`'s scripts (new tests + old engine, run in an isolated scratch copy): - `landed-work`: 53 (`bisect probed`), 54 (`risk=in-progress, never ok`), 57 (`in-progress outranks landed`) FAIL; 55/56 are fixture-sanity rows that pass either way. Suite exit 1. - `worktree-create`: 156–160 FAIL (not locked at creation; reason absent; plain removal succeeds; tree deleted). Suite exit 1. The initial lock-case fixture was named `feat/locked`, which put the substring `locked` into the worktree path and let assertion 156 pass spuriously against an unfixed helper — caught by exactly this revert-proof and fixed in `0f7be746` (`feat/liveness`). Draft until an independent verifier verdict is posted here, per the batch rule. ## Related - #2312 — sibling batch PR whose 0.51.15 this PR's 0.51.16 numbers above; re-resolved against `main` immediately before merge - #2264 — the changelog-parity absorption class this batch's scripted heading/tail checks guard against 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Related - No linked issue beyond the closing keyword above. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r-reason remedies (#2316) Closes #2265 ## What `branch_rules` computed `requireSignatures` and nothing consumed it (`babysit_merge.py` had zero matches for `.commit.verification`/`verified`): a head held only by an unsigned or mis-authored commit yielded `BLOCKED` plus the generic `mergeStateStatus` line naming four other causes — none of them the real one. - **`fetch_pull_request_commits`** (`babysit_gh.py`): reads `.commit.verification` per PR commit via `pulls/{n}/commits?per_page=100 --paginate`; a missing verification block reports `unverified`/`unreadable` rather than being skipped (the consumer may only over-report). - **`evaluate_required_signatures`** (`babysit_merge.py`): runs only when the rule is present (an ungoverned base pays no extra request), in the **read-only pass** (issue point 2), emitting one blocker per verification reason naming every offending commit. `unsigned`, `no_user`, and `unknown_key` carry distinct remedies — `no_user` states the signature IS valid and the author email is unlinked (#2162's recurring product; `--reset-author`, not keys). Fetch failure holds with its own "could not be read" blocker — fail closed, never a fabricated reason. Unrecognized reasons are reported verbatim. - The generic `mergeStateStatus` enumeration now names signatures (issue point 1, the one-line honesty fix). - `requiredSignatures` `{required, checked, unverified}` joins the JSON report. - CHANGELOG 0.51.17 + plugin.json bump (assumes #2312 = 0.51.15 and #2315 = 0.51.16 land first; re-resolved against `main` immediately before merge). ## Test proof (both directions) - With the fix: full babysit suite `python -m unittest discover -s tests` — **628 tests, OK** (re-run post-merge-forward at `cfbc5257`; 626 before the two new fetcher cases); `ruff check` clean. - Against `main`'s `babysit_merge.py` + `babysit_gh.py` (new tests + old modules in an isolated scratch copy): **11 failures/errors** — all 9 `RequiredSignaturesEnforcement` cases and both `FetchPullRequestCommitsTests` cases; suite exit FAILED. New tests pin: each reason's distinct message text (assertion bodies, not names), the distinct-blockers property under mixed reasons, the no-rule-makes-no-commit-read invariant, all-verified-is-ready, fail-closed fetch failure, verbatim unrecognized reasons, the generic-line honesty fix, and the fetcher's endpoint/pagination/projection including the missing-verification branch. Draft until an independent verifier verdict is posted here, per the batch rule. ## Related - #2312, #2315 — sibling batch PRs whose 0.51.15/0.51.16 this PR's 0.51.17 numbers above; re-resolved against `main` immediately before merge - #2162 — the harness bug that keeps producing the `no_user` state this PR names - #631 — unregistered signing keys producing `unknown_key` the same way - #2171 — the wrapper's previous rules-computed-but-not-acted-on fix, same file, same class 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Related - No linked issue beyond the closing keyword above. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Independent verifier verdict — post-hoc, PR already merged as
|
#2445) ## Summary Documents the #2388 decision in `scripts/check-changelog-parity.sh`: declared in-place corrections inside an already-released `## [<v>]` section are **sanctioned** when the correcting PR names each edit in its body **and** in the new release entry. `--check-preserved` continues to enforce heading survival; body fidelity is review discipline, not an automated gate. ## Decision brief (#2388) **RECOMMENDED:** Option 2 — declared in-place correction is sanctioned with explicit naming in the correcting PR (the de facto behavior #2312 followed). Option 1 (append-only errata only) is not adopted. Closes #2388. ## Related - Refs #2312 (the de facto declared-correction behavior this documents). Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Closes #2310
Successor to #2262, which squash-merged (9daeecb, 0.51.12) before Codex's P1-2 fix could land on it.
What
Both D6 verify-commit-pushed gates (
reference/review-discipline.md,skills/pull-request/SKILL.md) now resolve the branch's push remote through the existingskills/pull-request/scripts/resolve-remote.sh --push— the same resolverpush-branch.shpushes through — and compare the fix SHA againstFETCH_HEAD:The hardcoded
originwas new in 0.51.12 (base78dbb10egreps clean forgit fetch originin both files): on a non-origin/triangular-fork checkout it false-fails a successful push, and anoriginbase repo with a same-named branch can verify the wrong ref.CHANGELOG: new 0.51.15 entry; plugin.json 0.51.14 → 0.51.15. The shipped 0.51.12 entry keeps describing its own release (hardcoded-
originform, with a forward note) and takes exactly two in-place factual corrections, both declared in the 0.51.15 entry:verify_fix_commitmatches the reachability guarantee (it uses the fork-aware compare API, notgit merge-base), andbabysit_gh.py'sper_page=100sits atfetch_paginated_api's call sites, not adjacent to the--paginateflag.Live verification (both directions)
The #2244 motivating branch (
fix/babysit-merge-ruleset-context-union) no longer exists on the remote — where the new gate fails loudly at the fetch rather than passing, itself the fail-closed behavior we want. Equivalent live case onfeat/2257-worktree-liveness-lock(fix commitd683d535is not the tip;0f7be746is):--is-ancestor … FETCH_HEAD)d683d535(on branch, not tip)commits?sha=<branch>&per_page=10f7be746≠ fix → false "missing"a223aee3(sibling branch only)commits/a223aee3Draft until an independent verifier verdict is posted here, per the batch rule.
Related
originform; Codex P1-2 thread: fix(source-control): D6 verifies commit presence, not the branch tip; per_page=100 on every paginated read #2262 (comment)🤖 Generated with Claude Code