From e27e06604be2367cfe9c4aa3a7a218c2b49b1c43 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 17:04:20 +0000 Subject: [PATCH 1/8] docs(ledger): record babysit sweep for PRs #1124 #1131 #1146 #1157 #1162 #1167 #1169 #1170 Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 4808a2600..3a6227ab9 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -747,3 +747,11 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-24 | remediate-audit-system-issues (PR #1160) | bdf530fc8c6faaa4491c510396b47872fc39bf25 | Run PR sweep: CI fix + threads + drift | second re-merge after main moved to 2e68888f3 during first push; clean ort merge (ledger + layout.tsx); taskkill /T retained; sitemap prettier retained | merge only; no provider-backed checks run | | 2026-07-24 | codex/query-ribbon-search-headings (PR #1166) | c94e89f392f578c4b2c749195dd485b74959074c | Run PR re-sync sweep | Before: CONFLICTING. After: merged origin/main clean. CI re-running. | merge origin/main and/or conflict re-check only; no provider-backed checks run | | 2026-07-24 | cursor/comprehensive-repo-review-ledger-d9a1 (PR #1150) | 345c02cdbaefb13aeb951a14674aedfe4648a50e | Run PR re-sync sweep | Before: CONFLICTING. After: merged origin/main clean. | merge origin/main and/or conflict re-check only; no provider-backed checks run | +| 2026-07-24 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | 6ec7a852 | Babysit sweep: CI fix + Codex/CodeRabbit threads | Before: mergeable, PR required green, 8 unresolved agent-guidance threads. After: fixed pr-babysit/pr-bugbot agents (fetch origin/main, no Run PR live-gate auth, pin target head SHA, exact bot identity, ledger-after-every-sweep). Thread reply/resolve 403 on this token — fixes pushed. | typecheck on agent files; no provider-backed checks run | +| 2026-07-24 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | 48fcb485 | Babysit sweep: CI fix | Before: Static PR checks FAIL (docs:check-links missing legacy route paths). After: expanded check-docs-links allowlist for pre-(search-app) paths. Production UI re-running. | npm run docs:check-links PASS; no provider-backed checks run | +| 2026-07-24 | codex/document-clinical-summary-20260725 (PR #1169) | ef4ffca5 | Babysit sweep: Codex P2 threads | Before: 2 unresolved Codex P2s (missing pages guard, placeholder summary filter). After: profileItemPages helper + usefulSummaryText placeholder filter. CI re-running. | npm run typecheck PASS; no provider-backed checks run | +| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | fb39314e | Babysit sweep: drift | Before: behind main, PR required cancelled. After: merged origin/main cleanly. CI re-running. | merge origin/main only; no provider-backed checks run | +| 2026-07-24 | cursor/pr1135-native-disabled-followup-6780 (PR #1157) | 05ec10d5 | Babysit sweep: CI fix + drift | Before: CONFLICTING, Static PR + PR required FAIL (duplicate ledger row). After: merged origin/main; removed duplicate PR #1149 ledger row. CI re-running. | check-branch-review-ledger PASS; merge origin/main; no provider-backed checks run | +| 2026-07-24 | codex/hydration-fixes (PR #1131) | 21bdde7d | Babysit sweep: CI fix + drift | Before: CONFLICTING, Static PR + PR required FAIL. After: merged origin/main; removed duplicate PR #1149 ledger row; lint already green on head. CI re-running. | npm run lint PASS; check-branch-review-ledger PASS; no provider-backed checks run | +| 2026-07-24 | cursor/frontend-ui-review-docs-e8d9 (PR #1146) | c29d57c5 | Babysit sweep: drift | Before: CONFLICTING. After: merged origin/main cleanly. 1 unresolved Codex P2 (ledger insert vs append) left for human. | merge origin/main only; no provider-backed checks run | +| 2026-07-24 | execute-audit-code-remediation (PR #1162) | b4675d7b | Babysit sweep: drift skipped | Before: CONFLICTING, PR policy FAIL. Merge origin/main aborted: 20+ conflict files across clinical/auth/API surfaces — needs human resolution. | merge --abort; no provider-backed checks run | From a5ca2ad41865664398b02907f5fff183365bd560 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 17:22:29 +0000 Subject: [PATCH 2/8] docs(ledger): record open-PR conflict-resolution sweep and #1162 Bugbot Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index bd7a88883..bb3e67669 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -763,3 +763,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-24 | cursor/frontend-ui-review-docs-e8d9 (PR #1146) | c29d57c5 | Babysit sweep: drift | Before: CONFLICTING. After: merged origin/main cleanly. 1 unresolved Codex P2 (ledger insert vs append) left for human. | merge origin/main only; no provider-backed checks run | | 2026-07-24 | execute-audit-code-remediation (PR #1162) | b4675d7b | Babysit sweep: drift skipped | Before: CONFLICTING, PR policy FAIL. Merge origin/main aborted: 20+ conflict files across clinical/auth/API surfaces — needs human resolution. | merge --abort; no provider-backed checks run | | 2026-07-25 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | e979fc892f11a17b1a8f2ef1ab058ef40d629182 | Open-PR maintenance: CI fix + threads + drift | Before: Static PR checks failed because route-group moves made nine valid legacy `src/app/*` documentation references appear missing; 0 unresolved threads; branch already contained current main. After: docs-link resolution checks known App Router route groups and the focused failure is fixed. | `node scripts/check-docs-links.mjs` pass (1162 references); Prettier check pass; `git diff --check` pass; no provider-backed checks run. | +| 2026-07-24 | open-PR conflict sweep (13 clean + 5 conflicted) | multi-head | Conflict fix sweep | Merged origin/main into #1124 #1140-1142 #1146-1148 #1153 #1156-1158 #1167 #1169 #1171 #1172 (clean). Resolved conflicts on #1131 #1134 #1153 #1167 #1162. All 18 open PRs mergeable after sweep. | merge-tree classify + per-PR merge; focused private-access tests on #1162; Bugbot on #1162; no provider-backed checks | From a0c06d78304df7b52cd7546f12ea5aebe232074e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 17:23:57 +0000 Subject: [PATCH 3/8] docs(ledger): record #1162 conflict resolution and Bugbot outcome Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 980429803..cc74a44fd 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -767,3 +767,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-24 | execute-audit-code-remediation (PR #1162) | b4675d7b | Babysit sweep: drift skipped | Before: CONFLICTING, PR policy FAIL. Merge origin/main aborted: 20+ conflict files across clinical/auth/API surfaces — needs human resolution. | merge --abort; no provider-backed checks run | | 2026-07-25 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | e979fc892f11a17b1a8f2ef1ab058ef40d629182 | Open-PR maintenance: CI fix + threads + drift | Before: Static PR checks failed because route-group moves made nine valid legacy `src/app/*` documentation references appear missing; 0 unresolved threads; branch already contained current main. After: docs-link resolution checks known App Router route groups and the focused failure is fixed. | `node scripts/check-docs-links.mjs` pass (1162 references); Prettier check pass; `git diff --check` pass; no provider-backed checks run. | | 2026-07-24 | open-PR conflict sweep (13 clean + 5 conflicted) | multi-head | Conflict fix sweep | Merged origin/main into #1124 #1140-1142 #1146-1148 #1153 #1156-1158 #1167 #1169 #1171 #1172 (clean). Resolved conflicts on #1131 #1134 #1153 #1167 #1162. All 18 open PRs mergeable after sweep. | merge-tree classify + per-PR merge; focused private-access tests on #1162; Bugbot on #1162; no provider-backed checks | +| 2026-07-24 | execute-audit-code-remediation (PR #1162) | 3cb7c977 | Conflict fix + Bugbot + local review | Before: CONFLICTING (21 files). After: mergeable. Restored atomic upload RPC; aligned private-access tests (133/133). Bugbot 2 medium left open. | private-access-routes 133/133; no provider-backed checks | From 54308bb5360069be8cddaa029aab2dc3fe0f0cef Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 17:37:36 +0000 Subject: [PATCH 4/8] docs(ledger): record open-PR conflict sync (all mergeable) Merged origin/main into remaining behind/dirty PR heads so every open PR is MERGEABLE with behind=0. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 517a86c97..e04d6bb1a 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -771,3 +771,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-25 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | ee44812aae9dad1973d8302eba5bfca5000dffb6 | Open-PR maintenance: review-thread fixes | Before: 8 unresolved Codex/CodeRabbit threads; branch current with main. After: target-head pinning, fresh-main verification, exact `cursor[bot]` identity checks, explicit mutation/provider authorization, direct reply-then-resolve semantics, and no-op ledger bookkeeping are documented. | Prettier check on both agent files pass; `git diff --check` pass; GitHub author probe confirmed `cursor[bot]` account type `Bot`; no provider-backed checks run. | | 2026-07-25 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | e979fc892f11a17b1a8f2ef1ab058ef40d629182 | Open-PR maintenance: CI fix + threads + drift | Before: Static PR checks failed because route-group moves made nine valid legacy `src/app/*` documentation references appear missing; 0 unresolved threads; branch already contained current main. After: docs-link resolution checks known App Router route groups and the focused failure is fixed. | `node scripts/check-docs-links.mjs` pass (1162 references); Prettier check pass; `git diff --check` pass; no provider-backed checks run. | | 2026-07-25 | cursor/search-performance-review-4ee9 (PR #1134) | 692834a86e612cc8b311dc6895e007f182f5c5b8 | Open-PR maintenance: superseded docs-link thread and clean main sync | Before: branch was behind current main with one outdated docs-link thread; its product tree already matched main. After: merged current main cleanly and verified the route-group-aware docs-link fix now covers legacy route references. RAG impact: no retrieval behaviour change — history sync and docs tooling verification only. | `node scripts/check-docs-links.mjs` pass (1154 references); clean merge-tree; no live RAG canary or provider-backed check run. | +| 2026-07-24 | open-PR conflict sync (20 PRs) | multi-head | Conflict resolution pass | Before: 8 PRs behind/dirty (#1124 #1131 #1162 #1169 #1174-1177). After: merged origin/main into all; all 20 open PRs MERGEABLE behind=0 (BLOCKED only by CI/reviews). | merge origin/main per branch; no provider-backed checks run | From 020859b9dbb8084334182f0a68dd9c298d67df7f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 17:37:53 +0000 Subject: [PATCH 5/8] docs(ledger): drop exact duplicate #1170 review row Remove the second identical PR #1170 ledger record so check:branch-review-ledger passes. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index e04d6bb1a..7aeb6e663 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -769,6 +769,5 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-24 | open-PR conflict sweep (13 clean + 5 conflicted) | multi-head | Conflict fix sweep | Merged origin/main into #1124 #1140-1142 #1146-1148 #1153 #1156-1158 #1167 #1169 #1171 #1172 (clean). Resolved conflicts on #1131 #1134 #1153 #1167 #1162. All 18 open PRs mergeable after sweep. | merge-tree classify + per-PR merge; focused private-access tests on #1162; Bugbot on #1162; no provider-backed checks | | 2026-07-24 | execute-audit-code-remediation (PR #1162) | 3cb7c977 | Conflict fix + Bugbot + local review | Before: CONFLICTING (21 files). After: mergeable. Restored atomic upload RPC; aligned private-access tests (133/133). Bugbot 2 medium left open. | private-access-routes 133/133; no provider-backed checks | | 2026-07-25 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | ee44812aae9dad1973d8302eba5bfca5000dffb6 | Open-PR maintenance: review-thread fixes | Before: 8 unresolved Codex/CodeRabbit threads; branch current with main. After: target-head pinning, fresh-main verification, exact `cursor[bot]` identity checks, explicit mutation/provider authorization, direct reply-then-resolve semantics, and no-op ledger bookkeeping are documented. | Prettier check on both agent files pass; `git diff --check` pass; GitHub author probe confirmed `cursor[bot]` account type `Bot`; no provider-backed checks run. | -| 2026-07-25 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | e979fc892f11a17b1a8f2ef1ab058ef40d629182 | Open-PR maintenance: CI fix + threads + drift | Before: Static PR checks failed because route-group moves made nine valid legacy `src/app/*` documentation references appear missing; 0 unresolved threads; branch already contained current main. After: docs-link resolution checks known App Router route groups and the focused failure is fixed. | `node scripts/check-docs-links.mjs` pass (1162 references); Prettier check pass; `git diff --check` pass; no provider-backed checks run. | | 2026-07-25 | cursor/search-performance-review-4ee9 (PR #1134) | 692834a86e612cc8b311dc6895e007f182f5c5b8 | Open-PR maintenance: superseded docs-link thread and clean main sync | Before: branch was behind current main with one outdated docs-link thread; its product tree already matched main. After: merged current main cleanly and verified the route-group-aware docs-link fix now covers legacy route references. RAG impact: no retrieval behaviour change — history sync and docs tooling verification only. | `node scripts/check-docs-links.mjs` pass (1154 references); clean merge-tree; no live RAG canary or provider-backed check run. | | 2026-07-24 | open-PR conflict sync (20 PRs) | multi-head | Conflict resolution pass | Before: 8 PRs behind/dirty (#1124 #1131 #1162 #1169 #1174-1177). After: merged origin/main into all; all 20 open PRs MERGEABLE behind=0 (BLOCKED only by CI/reviews). | merge origin/main per branch; no provider-backed checks run | From d3b98f1729b9c3938ebbab881f9219eb4bcef138 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 19:05:36 +0000 Subject: [PATCH 6/8] docs(ledger): record open-PR conflict sync for 22 PRs Merged origin/main into every open PR head so all are current with main. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 2623804fe..01edee3cb 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -775,3 +775,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-25 | codex/document-clinical-summary-20260725 (PR #1169) | 605a47b551a03774fab41416bf980dfbc9610221 | Open-PR maintenance: malformed persisted profile guard | Before: one actionable thread showed non-array or malformed persisted summary groups could throw during render. After: every priority group is normalized through an array/item guard and malformed values are ignored while valid items still render. | Focused Vitest 7/7 pass; Prettier and diff checks pass; no provider-backed checks run. | | 2026-07-25 | cursor/search-performance-review-4ee9 (PR #1134) | 692834a86e612cc8b311dc6895e007f182f5c5b8 | Open-PR maintenance: superseded docs-link thread and clean main sync | Before: branch was behind current main with one outdated docs-link thread; its product tree already matched main. After: merged current main cleanly and verified the route-group-aware docs-link fix now covers legacy route references. RAG impact: no retrieval behaviour change — history sync and docs tooling verification only. | `node scripts/check-docs-links.mjs` pass (1154 references); clean merge-tree; no live RAG canary or provider-backed check run. | | 2026-07-24 | open-PR conflict sync (20 PRs) | multi-head | Conflict resolution pass | Before: 8 PRs behind/dirty (#1124 #1131 #1162 #1169 #1174-1177). After: merged origin/main into all; all 20 open PRs MERGEABLE behind=0 (BLOCKED only by CI/reviews). | merge origin/main per branch; no provider-backed checks run | +| 2026-07-24 | open-PR conflict sync (22 PRs) | multi-head | Conflict resolution pass | Before: all 22 open PRs behind/dirty vs main (several CONFLICTING/DIRTY). After: merged origin/main into every open head; all pushes OK; merge-tree classified 22/22 clean. | merge origin/main per branch; check:branch-review-ledger on #1172; no provider-backed checks run | From e2bb96532769c81b7db291e5f380e3d3024f8ab5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 19:05:45 +0000 Subject: [PATCH 7/8] docs(ledger): drop exact duplicate review rows Remove duplicate ledger records so check:branch-review-ledger passes after the conflict sync. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 01edee3cb..c600ac8d6 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -770,8 +770,6 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-24 | execute-audit-code-remediation (PR #1162) | 3cb7c977 | Conflict fix + Bugbot + local review | Before: CONFLICTING (21 files). After: mergeable. Restored atomic upload RPC; aligned private-access tests (133/133). Bugbot 2 medium left open. | private-access-routes 133/133; no provider-backed checks | | 2026-07-25 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | ee44812aae9dad1973d8302eba5bfca5000dffb6 | Open-PR maintenance: review-thread fixes | Before: 8 unresolved Codex/CodeRabbit threads; branch current with main. After: target-head pinning, fresh-main verification, exact `cursor[bot]` identity checks, explicit mutation/provider authorization, direct reply-then-resolve semantics, and no-op ledger bookkeeping are documented. | Prettier check on both agent files pass; `git diff --check` pass; GitHub author probe confirmed `cursor[bot]` account type `Bot`; no provider-backed checks run. | | 2026-07-25 | codex/document-clinical-summary-20260725 (PR #1169) | 6bbce2b97477cb4497624abe2a37c74864e872c8 | Open-PR maintenance: review-thread verification | Before: 2 unresolved Codex threads; branch current with main and required CI running. After: both persisted-profile/placeholder-summary fixes confirmed on the exact head and ready for reply-then-resolve; no further code change required. | `node scripts/run-vitest.mjs run tests/document-clinical-summary.test.ts tests/document-clinical-summary.dom.test.tsx --reporter=dot` pass (5/5); `git diff --check` pass; no provider-backed checks run. | -| 2026-07-25 | cursor/pr-babysit-bugbot-agents-6c52 (PR #1167) | ee44812aae9dad1973d8302eba5bfca5000dffb6 | Open-PR maintenance: review-thread fixes | Before: 8 unresolved Codex/CodeRabbit threads; branch current with main. After: target-head pinning, fresh-main verification, exact `cursor[bot]` identity checks, explicit mutation/provider authorization, direct reply-then-resolve semantics, and no-op ledger bookkeeping are documented. | Prettier check on both agent files pass; `git diff --check` pass; GitHub author probe confirmed `cursor[bot]` account type `Bot`; no provider-backed checks run. | -| 2026-07-25 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | e979fc892f11a17b1a8f2ef1ab058ef40d629182 | Open-PR maintenance: CI fix + threads + drift | Before: Static PR checks failed because route-group moves made nine valid legacy `src/app/*` documentation references appear missing; 0 unresolved threads; branch already contained current main. After: docs-link resolution checks known App Router route groups and the focused failure is fixed. | `node scripts/check-docs-links.mjs` pass (1162 references); Prettier check pass; `git diff --check` pass; no provider-backed checks run. | | 2026-07-25 | codex/document-clinical-summary-20260725 (PR #1169) | 605a47b551a03774fab41416bf980dfbc9610221 | Open-PR maintenance: malformed persisted profile guard | Before: one actionable thread showed non-array or malformed persisted summary groups could throw during render. After: every priority group is normalized through an array/item guard and malformed values are ignored while valid items still render. | Focused Vitest 7/7 pass; Prettier and diff checks pass; no provider-backed checks run. | | 2026-07-25 | cursor/search-performance-review-4ee9 (PR #1134) | 692834a86e612cc8b311dc6895e007f182f5c5b8 | Open-PR maintenance: superseded docs-link thread and clean main sync | Before: branch was behind current main with one outdated docs-link thread; its product tree already matched main. After: merged current main cleanly and verified the route-group-aware docs-link fix now covers legacy route references. RAG impact: no retrieval behaviour change — history sync and docs tooling verification only. | `node scripts/check-docs-links.mjs` pass (1154 references); clean merge-tree; no live RAG canary or provider-backed check run. | | 2026-07-24 | open-PR conflict sync (20 PRs) | multi-head | Conflict resolution pass | Before: 8 PRs behind/dirty (#1124 #1131 #1162 #1169 #1174-1177). After: merged origin/main into all; all 20 open PRs MERGEABLE behind=0 (BLOCKED only by CI/reviews). | merge origin/main per branch; no provider-backed checks run | From 581febd20c3478763c7af63dd12b305d9054f2f3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 19:14:24 +0000 Subject: [PATCH 8/8] fix: auto-sync open PR branches and clear postcss audit Add a main-push workflow and local helper that update behind PR heads so GitHub stops falsely marking the queue CONFLICTING/DIRTY after each land. Bump postcss to a non-vulnerable release so Safety npm audit stays green. --- .claude/skills/run-pr/SKILL.md | 12 ++- .cursor/agents/pr-babysit.md | 2 +- .github/workflows/pr-branch-sync.yml | 124 +++++++++++++++++++++++++++ AGENTS.md | 28 ++++++ docs/branch-review-ledger.md | 1 + docs/codebase-index.md | 28 +++--- docs/process-hardening.md | 14 +++ package-lock.json | 16 ++-- package.json | 8 +- scripts/sync-open-pr-branches.mjs | 122 ++++++++++++++++++++++++++ tests/sync-open-pr-branches.test.ts | 37 ++++++++ 11 files changed, 364 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/pr-branch-sync.yml create mode 100644 scripts/sync-open-pr-branches.mjs create mode 100644 tests/sync-open-pr-branches.test.ts diff --git a/.claude/skills/run-pr/SKILL.md b/.claude/skills/run-pr/SKILL.md index 76c7deda9..5f485646b 100644 --- a/.claude/skills/run-pr/SKILL.md +++ b/.claude/skills/run-pr/SKILL.md @@ -71,11 +71,17 @@ required checks, unresolved-thread count, and behind/ahead relative to `main`. ### Step 2 — branch drift first (so CI fixes target the merged state) +- Hosted mitigation: `.github/workflows/pr-branch-sync.yml` updates open PR branches + after each push to `main`. Do not treat a fresh `DIRTY` state as a product bug until + you confirm the tip is still behind or `git merge-tree` reports real conflicts. - Behind `main` but cleanly mergeable, with no local checkout otherwise needed → - `mcp__github__update_pull_request_branch`, then re-fetch. + `mcp__github__update_pull_request_branch` (or `npm run sync:pr-branches:apply` / + `gh api .../update-branch`), then re-fetch. - Conflicting (`mergeable_state: dirty`), or the branch is being checked out anyway → - `git switch ` after fetch, then `git merge origin/main`. If the merge brings dependency - changes or touches `package-lock.json`, run `npm install` before verification. + classify with `git merge-tree --write-tree origin/main ` first. If clean, merge + `origin/main` (or update-branch) and push. If conflicted, `git switch ` after + fetch, then `git merge origin/main`. If the merge brings dependency changes or touches + `package-lock.json`, run `npm install` before verification. - Mechanically resolvable conflicts (adjacent hunks, import lists, lockfile → regenerate via `npm install`, generated files → re-run their generator, e.g. `sitemap:update`) → resolve, then run the narrowest gate covering the conflicted files. diff --git a/.cursor/agents/pr-babysit.md b/.cursor/agents/pr-babysit.md index bcbbd8044..aa93a47f8 100644 --- a/.cursor/agents/pr-babysit.md +++ b/.cursor/agents/pr-babysit.md @@ -13,7 +13,7 @@ When invoked: 1. Identify the target PR (number, URL, or current branch), resolve and fetch its exact head SHA, then check out or isolate that head and assert the checkout still matches before editing or pushing. Fetch mergeability, check rollup, and unresolved review threads only. 2. Prefer the smallest safe fix that unblocks merge. Do not rewrite unrelated code. -3. Run `git fetch origin --prune` and verify the latest `origin/main` immediately before evaluating drift. Merge it into the feature branch when the PR is behind or conflicts are trivial; abort and ask when intents conflict. Never rebase. +3. Run `git fetch origin --prune` and verify the latest `origin/main` immediately before evaluating drift. Treat GitHub `DIRTY`/`CONFLICTING` as stale-behind until `git merge-tree` proves a real content conflict. Prefer GitHub update-branch / the `pr-branch-sync` workflow when available; otherwise merge `origin/main` into the feature branch when behind or conflicts are trivial; abort and ask when intents conflict. Never rebase. 4. Fix CI failures caused by this PR's scope. Never weaken workflows or delete required checks to force green. Ignore advisory jobs (`ui-advisory`, `release-browser-matrix`). 5. Treat Codex/Bugbot findings as actionable only when validated against the current diff; fix clear P0/P1 and scoped P2s, otherwise reply with a concise disposition. Prefer the `pr-bugbot` agent when Bugbot threads dominate. 6. After fixing a review thread, reply first (never resolve silently), then use the authorized direct resolution tool. Delegated Bugbot threads follow the same path. Only the trusted Codex autofix identity may fall back to ``; otherwise leave the thread open and report the missing capability. diff --git a/.github/workflows/pr-branch-sync.yml b/.github/workflows/pr-branch-sync.yml new file mode 100644 index 000000000..904562817 --- /dev/null +++ b/.github/workflows/pr-branch-sync.yml @@ -0,0 +1,124 @@ +name: Sync open PR branches + +# When main moves, open PR heads go stale and GitHub often reports them as +# CONFLICTING/DIRTY even when a clean merge exists. That blocks squash +# auto-merge and creates a perpetual "merge conflict" churn across the queue. +# +# This workflow updates every open same-repo PR branch from main after each +# push to main (and on manual dispatch). It uses GitHub's update-branch API +# (merge commit into the head), never rebases, and never merges PRs into main. + +on: + push: + branches: [main] + workflow_dispatch: {} + +concurrency: + group: sync-open-pr-branches + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + +jobs: + sync: + name: Update behind PR branches + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - name: Update open PR branches from main + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const base = "main"; + const skipLabels = new Set(["hold", "do-not-merge", "skip-branch-sync"]); + + const pulls = await github.paginate(github.rest.pulls.list, { + owner, + repo, + state: "open", + base, + per_page: 100, + }); + + const results = { updated: [], skipped: [], failed: [] }; + + for (const pr of pulls) { + const labels = new Set((pr.labels || []).map((l) => String(l.name || "").toLowerCase())); + const title = String(pr.title || ""); + const headRepo = pr.head?.repo?.full_name; + const reasonSkip = + headRepo && headRepo !== `${owner}/${repo}` + ? "fork-head" + : [...skipLabels].find((l) => labels.has(l)) + ? `label:${[...skipLabels].find((l) => labels.has(l))}` + : /\b(WIP|do not merge)\b/i.test(title) + ? "wip-title" + : null; + if (reasonSkip) { + results.skipped.push(`#${pr.number} ${reasonSkip}`); + continue; + } + + // Cheap behind check — avoid update-branch noise when already current. + let behindBy = null; + try { + const comparison = await github.rest.repos.compareCommitsWithBasehead({ + owner, + repo, + basehead: `${base}...${pr.head.ref}`, + }); + behindBy = comparison.data.behind_by ?? 0; + } catch (error) { + results.failed.push(`#${pr.number} compare:${error.status || error.message}`); + continue; + } + if (behindBy === 0) { + results.skipped.push(`#${pr.number} already-current`); + continue; + } + + try { + await github.rest.pulls.updateBranch({ + owner, + repo, + pull_number: pr.number, + expected_head_sha: pr.head.sha, + }); + results.updated.push(`#${pr.number} behind=${behindBy}`); + core.info(`Updated #${pr.number} (${pr.head.ref}) from ${base} (was behind ${behindBy})`); + } catch (error) { + const status = error.status || "err"; + const msg = error.message || String(error); + // 422 = not behind / merge conflict / head moved — report, do not fail the job. + results.failed.push(`#${pr.number} ${status}:${msg.split("\n")[0]}`); + core.warning(`Could not update #${pr.number}: ${status} ${msg}`); + } + + // Small pacing to reduce secondary-rate-limit pressure on large queues. + await new Promise((r) => setTimeout(r, 500)); + } + + core.summary + .addHeading("Open PR branch sync") + .addRaw( + [ + `Updated: ${results.updated.length}`, + `Skipped: ${results.skipped.length}`, + `Failed/blocked: ${results.failed.length}`, + "", + results.updated.length ? `### Updated\n${results.updated.map((x) => `- ${x}`).join("\n")}` : "", + results.failed.length ? `### Failed/blocked\n${results.failed.map((x) => `- ${x}`).join("\n")}` : "", + ] + .filter(Boolean) + .join("\n"), + ) + .write(); + + // Soft-success: real conflicts need humans; stale behind heads are the happy path. + if (results.failed.some((line) => /5\d\d:/.test(line))) { + core.setFailed("One or more PR branch updates failed with a server error."); + } diff --git a/AGENTS.md b/AGENTS.md index 50a3bd579..3025ebffe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -395,6 +395,33 @@ After completing `upload`, summarize the current branch and worktree state, whet + + +## Open PR branch sync (anti-churn) + +Open PR heads go stale whenever `main` advances. GitHub frequently labels those +branches `CONFLICTING` / `DIRTY` even when `git merge-tree` is clean — that is +staleness, not an unresolvable content fight, and it blocks squash auto-merge. + +Durable mitigations in this repo: + +- `.github/workflows/pr-branch-sync.yml` runs on every push to `main` (and on + `workflow_dispatch`) and calls GitHub's update-branch API for every open + same-repo PR that is behind `main`. Opt out per PR with labels `hold`, + `do-not-merge`, or `skip-branch-sync`, or a `WIP` / `do not merge` title. +- Local/operator dry-run: `npm run sync:pr-branches`. Apply: `npm run sync:pr-branches:apply`. +- Prefer fewer long-lived open PRs; land or close queue items rather than + repeatedly re-merging `main` by hand. +- `docs/branch-review-ledger.md` stays append-only with the `union` merge driver; + do not rewrite existing rows during syncs. + +When diagnosing "merge conflicts on every PR", first compare `behind_by` and +`git merge-tree --write-tree origin/main `. If the tree merge is clean, +sync the branch (workflow, `update-branch`, or `git merge origin/main` + push) +instead of rewriting product code. + + + ## Run PR shortcut When the user types exactly `Run PR` (case-insensitive, entire task message after trimming surrounding whitespace), treat it as a shortcut for a one-shot open-PR maintenance sweep on `bigsimmo/database`. This is a chat shortcut, not an app feature, script, automation, or CI workflow. @@ -418,6 +445,7 @@ Hard guardrails (never, even during a sweep): - Respect the `skip-codex-review` label as a full per-PR opt-out. - Preserve unrelated staged, unstaged, and untracked work; never commit secrets. - Resolve branch drift with `git merge origin/main` only; skip and report non-trivial conflicts instead of guessing. +- Before treating GitHub `DIRTY`/`CONFLICTING` as a real conflict, confirm with `git merge-tree` (see "## Open PR branch sync (anti-churn)"). Prefer the hosted `pr-branch-sync` workflow / update-branch API when the token can write; otherwise merge `origin/main` in a worktree and push. Procedure: in Claude Code sessions, invoke the `run-pr` skill (`.claude/skills/run-pr/SKILL.md`) — it is the canonical detailed procedure. In sessions without GitHub MCP write tooling, degrade to read-only diagnosis and a per-PR report; do not attempt pushes or thread resolution through other means. diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index d33df56f7..4dba35a17 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -765,3 +765,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-25 | codex/fix-merge-conflicts-and-ci-on-open-prs (PR #1170) | e979fc892f11a17b1a8f2ef1ab058ef40d629182 | Open-PR maintenance: CI fix + threads + drift | Before: Static PR checks failed because route-group moves made nine valid legacy `src/app/*` documentation references appear missing; 0 unresolved threads; branch already contained current main. After: docs-link resolution checks known App Router route groups and the focused failure is fixed. | `node scripts/check-docs-links.mjs` pass (1162 references); Prettier check pass; `git diff --check` pass; no provider-backed checks run. | | 2026-07-25 | codex/document-clinical-summary-20260725 (PR #1169) | 605a47b551a03774fab41416bf980dfbc9610221 | Open-PR maintenance: malformed persisted profile guard | Before: one actionable thread showed non-array or malformed persisted summary groups could throw during render. After: every priority group is normalized through an array/item guard and malformed values are ignored while valid items still render. | Focused Vitest 7/7 pass; Prettier and diff checks pass; no provider-backed checks run. | | 2026-07-25 | cursor/search-performance-review-4ee9 (PR #1134) | 692834a86e612cc8b311dc6895e007f182f5c5b8 | Open-PR maintenance: superseded docs-link thread and clean main sync | Before: branch was behind current main with one outdated docs-link thread; its product tree already matched main. After: merged current main cleanly and verified the route-group-aware docs-link fix now covers legacy route references. RAG impact: no retrieval behaviour change — history sync and docs tooling verification only. | `node scripts/check-docs-links.mjs` pass (1154 references); clean merge-tree; no live RAG canary or provider-backed check run. | +| 2026-07-24 | cursor/pr-queue-hygiene-72ec | pending-push | PR queue hygiene | Add pr-branch-sync workflow + sync:pr-branches helper; bump postcss to clear npm audit high; document anti-churn guidance in AGENTS/process-hardening/pr-babysit/run-pr. | check:github-actions PASS; docs:check-scripts/index PASS; vitest sync-open-pr-branches 3/3; npm audit high clean; no provider-backed checks run | diff --git a/docs/codebase-index.md b/docs/codebase-index.md index d2b9b597f..062074337 100644 --- a/docs/codebase-index.md +++ b/docs/codebase-index.md @@ -314,19 +314,21 @@ One shared composer (`master-search-header.tsx`) serves every mode. Placement: ## Key config files -| File | Role | -| ----------------------------------- | ------------------------------------------- | -| `package.json` | Scripts, deps, Node 24 / npm 11 | -| `.env.example` | Full env template | -| `next.config.ts` | CSP, security headers, build config | -| `tsconfig.json` | Strict TS; excludes `supabase/functions/**` | -| `eslint.config.mjs` | Lint scope | -| `AGENTS.md` | Agent rules, verification gates, shortcuts | -| `.github/workflows/ci.yml` | CI pipeline | -| `docs/process-hardening.md` | Verification pyramid | -| `docs/clinical-governance.md` | Clinical safety governance | -| `docs/reindex-runbook.md` | Reindex operations | -| `docs/retrieval-quality-runbook.md` | Retrieval tuning | +| File | Role | +| -------------------------------------- | ---------------------------------------------- | +| `package.json` | Scripts, deps, Node 24 / npm 11 | +| `.env.example` | Full env template | +| `next.config.ts` | CSP, security headers, build config | +| `tsconfig.json` | Strict TS; excludes `supabase/functions/**` | +| `eslint.config.mjs` | Lint scope | +| `AGENTS.md` | Agent rules, verification gates, shortcuts | +| `.github/workflows/ci.yml` | CI pipeline | +| `.github/workflows/pr-branch-sync.yml` | Auto-update open PR branches when `main` moves | +| `scripts/sync-open-pr-branches.mjs` | Local dry-run/apply helper for PR branch sync | +| `docs/process-hardening.md` | Verification pyramid | +| `docs/clinical-governance.md` | Clinical safety governance | +| `docs/reindex-runbook.md` | Reindex operations | +| `docs/retrieval-quality-runbook.md` | Retrieval tuning | --- diff --git a/docs/process-hardening.md b/docs/process-hardening.md index f65a224b8..2466a10d4 100644 --- a/docs/process-hardening.md +++ b/docs/process-hardening.md @@ -40,6 +40,20 @@ gates. Run the standalone manual/nightly workflow and attach a recent green evid artifact before release; see [`docs/staging-tenancy-release-evidence.md`](staging-tenancy-release-evidence.md). +## Open PR branch sync (active) + +- **Problem:** landing one PR advances `main` and leaves the rest of a large open + queue behind. GitHub then marks many heads `CONFLICTING`/`DIRTY` even when the + merge tree is clean, which stalls squash auto-merge and creates endless manual + re-sync churn. +- **Mitigation:** `.github/workflows/pr-branch-sync.yml` updates open same-repo PR + branches from `main` after every push to `main`. Local dry-run/apply helpers: + `npm run sync:pr-branches` / `npm run sync:pr-branches:apply`. Opt out with + `hold`, `do-not-merge`, or `skip-branch-sync`. +- **Operator rule:** prefer clearing the open queue (merge or close) over keeping + dozens of long-lived feature branches that all touch shared docs like the + branch-review ledger. + ## Phase 1 - Active now - `npm run verify:cheap` is the default broad local gate for source/config/test changes: `check:runtime`, `sitemap:check`, lint, typecheck, and unit tests. diff --git a/package-lock.json b/package-lock.json index 0db2b6389..e0beaad4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "pdf-parse": "^2.4.5", "pdfjs-dist": "^6.1.200", "pdfkit": "^0.19.1", - "postcss": "^8.5.15", + "postcss": "^8.5.18", "react": "19.2.7", "react-dom": "19.2.7", "server-only": "^0.0.1", @@ -9155,9 +9155,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "funding": [ { "type": "github", @@ -10117,9 +10117,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -10136,7 +10136,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/package.json b/package.json index 4b74888f2..ce7229033 100644 --- a/package.json +++ b/package.json @@ -182,7 +182,9 @@ "check:skills": "node scripts/list-database-skills.mjs --check", "check:drift": "node scripts/run-tsx.mjs scripts/check-drift.ts", "check:migration-history": "node scripts/run-tsx.mjs scripts/check-migration-history-alignment.ts", - "drift:manifest": "node scripts/run-tsx.mjs scripts/generate-drift-manifest.ts" + "drift:manifest": "node scripts/run-tsx.mjs scripts/generate-drift-manifest.ts", + "sync:pr-branches": "node scripts/sync-open-pr-branches.mjs", + "sync:pr-branches:apply": "node scripts/sync-open-pr-branches.mjs --apply" }, "dependencies": { "@next/env": "16.2.11", @@ -197,14 +199,14 @@ "pdf-parse": "^2.4.5", "pdfjs-dist": "^6.1.200", "pdfkit": "^0.19.1", - "postcss": "^8.5.15", + "postcss": "^8.5.18", "react": "19.2.7", "react-dom": "19.2.7", "server-only": "^0.0.1", "zod": "^4.4.3" }, "overrides": { - "postcss": "^8.5.15", + "postcss": "^8.5.18", "esbuild": "0.28.1", "sharp": "0.35.3", "tmp": "^0.2.7", diff --git a/scripts/sync-open-pr-branches.mjs b/scripts/sync-open-pr-branches.mjs new file mode 100644 index 000000000..c0220f772 --- /dev/null +++ b/scripts/sync-open-pr-branches.mjs @@ -0,0 +1,122 @@ +#!/usr/bin/env node +/** + * sync-open-pr-branches — local/operator helper that mirrors + * `.github/workflows/pr-branch-sync.yml`. + * + * Default is dry-run (report only). Pass `--apply` to call GitHub's + * update-branch API for every open same-repo PR that is behind `main`. + * + * Requires `gh` auth with pull-requests:write (or a token in GITHUB_TOKEN / + * GH_TOKEN). Never rebases, never merges into main, never force-pushes. + * + * Run: + * node scripts/sync-open-pr-branches.mjs + * node scripts/sync-open-pr-branches.mjs --apply + * npm run sync:pr-branches + * npm run sync:pr-branches -- --apply + */ +import { spawnSync } from "node:child_process"; +import { pathToFileURL } from "node:url"; + +const APPLY = process.argv.includes("--apply"); +const BASE = "main"; +const SKIP_LABELS = new Set(["hold", "do-not-merge", "skip-branch-sync"]); + +function ghJson(args) { + const result = spawnSync("gh", args, { encoding: "utf8" }); + if (result.status !== 0) { + throw new Error((result.stderr || result.stdout || `gh ${args.join(" ")} failed`).trim()); + } + return JSON.parse(result.stdout || "null"); +} + +function shouldSkip(pr) { + const labels = new Set((pr.labels || []).map((l) => String(l.name || "").toLowerCase())); + for (const label of SKIP_LABELS) { + if (labels.has(label)) return `label:${label}`; + } + if (/\b(WIP|do not merge)\b/i.test(String(pr.title || ""))) return "wip-title"; + if (pr.isCrossRepository) return "fork-head"; + return null; +} + +export function classifyPr(pr, behindBy) { + const skip = shouldSkip(pr); + if (skip) return { action: "skip", reason: skip }; + if ((behindBy ?? 0) <= 0) return { action: "skip", reason: "already-current" }; + return { action: "update", reason: `behind=${behindBy}` }; +} + +function main() { + const repo = ghJson(["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"]); + const prs = ghJson([ + "pr", + "list", + "--repo", + repo, + "--state", + "open", + "--base", + BASE, + "--limit", + "100", + "--json", + "number,title,headRefName,headRefOid,labels,isCrossRepository,isDraft,url", + ]); + + const plan = []; + for (const pr of prs) { + const cmp = ghJson(["api", `repos/${repo}/compare/${BASE}...${pr.headRefName}`]); + const behindBy = cmp.behind_by ?? 0; + const decision = classifyPr(pr, behindBy); + plan.push({ pr, behindBy, ...decision }); + } + + console.log(`Open PRs against ${BASE}: ${plan.length} (${APPLY ? "APPLY" : "dry-run"})`); + for (const row of plan) { + console.log( + `#${row.pr.number} draft=${row.pr.isDraft} behind=${row.behindBy} -> ${row.action} (${row.reason}) ${row.pr.headRefName}`, + ); + } + + if (!APPLY) { + console.log("\nDry-run only. Re-run with --apply to update behind branches."); + return; + } + + let updated = 0; + let failed = 0; + for (const row of plan) { + if (row.action !== "update") continue; + const result = spawnSync( + "gh", + [ + "api", + "-X", + "PUT", + `repos/${repo}/pulls/${row.pr.number}/update-branch`, + "-f", + `expected_head_sha=${row.pr.headRefOid}`, + ], + { encoding: "utf8" }, + ); + if (result.status === 0) { + updated += 1; + console.log(`UPDATED #${row.pr.number}`); + } else { + failed += 1; + console.error(`FAILED #${row.pr.number}: ${(result.stderr || result.stdout || "").trim()}`); + } + } + console.log(`Done. updated=${updated} failed=${failed}`); + if (failed > 0) process.exitCode = 1; +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + try { + main(); + } catch (error) { + console.error(error instanceof Error ? error.message : error); + process.exit(1); + } +} diff --git a/tests/sync-open-pr-branches.test.ts b/tests/sync-open-pr-branches.test.ts new file mode 100644 index 000000000..2700d73aa --- /dev/null +++ b/tests/sync-open-pr-branches.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; +import { classifyPr } from "../scripts/sync-open-pr-branches.mjs"; + +describe("sync-open-pr-branches classifyPr", () => { + it("skips hold / do-not-merge / skip-branch-sync labels", () => { + expect(classifyPr({ title: "x", labels: [{ name: "hold" }] }, 3)).toEqual({ + action: "skip", + reason: "label:hold", + }); + expect(classifyPr({ title: "x", labels: [{ name: "skip-branch-sync" }] }, 3)).toEqual({ + action: "skip", + reason: "label:skip-branch-sync", + }); + }); + + it("skips WIP titles and fork heads", () => { + expect(classifyPr({ title: "WIP: not yet", labels: [] }, 2)).toEqual({ + action: "skip", + reason: "wip-title", + }); + expect(classifyPr({ title: "ready", labels: [], isCrossRepository: true }, 2)).toEqual({ + action: "skip", + reason: "fork-head", + }); + }); + + it("skips already-current branches and updates behind ones", () => { + expect(classifyPr({ title: "ready", labels: [] }, 0)).toEqual({ + action: "skip", + reason: "already-current", + }); + expect(classifyPr({ title: "ready", labels: [] }, 12)).toEqual({ + action: "update", + reason: "behind=12", + }); + }); +});