Addresses the root cause of a pattern I have now measured four times. ## The finding **Every fleet worker pushes as the same GitHub account.** `gh pr list --author "@me"` returns **all 17 open PRs** — mine and teammates' are indistinguishable. So "is this file already being converted?" can only be answered by fetching every open PR's file list by hand: 25+ API calls that no worker makes before starting. I didn't either. ## The measured cost | PR | Outcome | Landed instead as | |---|---|---| | #3096 | shrank to a test | teammate's serialisation + union | | #3116 | shrank to a test | `preExecLiveColumns`, `starvedWaitingColumns`, … | | #3140 | shrank to a test | #3137 | | #3125 | **shrank to nothing — closed** | #3135 | Plus #3118, a teammate independently writing the same coverage I wrote for #3112. **In every case both implementations were correct and independently reached the same design** — #3137 chose payload-first-with-sync-fallback for the same reason I did. This is not carelessness; the fleet is doing correct work twice and discovering coverage gaps by accident, when rebases collide. ## What this adds ``` $ node scripts/check-file-claimed.mjs packages/engine/src/self-healing.ts CLAIMED packages/engine/src/self-healing.ts #3152 fix(self-healing): 18 recovery rebounds hardcoded `todo` … ``` **On its first run it reported `self-healing.ts` claimed by #3152 — which I had no way to know a moment earlier.** Exits non-zero when claimed, so it can gate work: `node scripts/check-file-claimed.mjs <path> && start-work`. ## Deliberate limits - **It cannot see unpushed work**, so it narrows the collision window rather than closing it. Two workers starting the same file minutes apart still collide. Closing that needs **distinguishable authorship** — a per-worker `Co-Authored-By` or a title prefix — which is a coordination decision, not a script. - **A `gh` failure exits 2 and says UNKNOWN, not unclaimed.** A claim check that fails open is worse than none, which is the same false-green shape this program keeps finding elsewhere. Also adds a short AGENTS.md pointer next to the other standing rules. ## Verification Run against a claimed and an unclaimed path; both answers correct, exit codes as documented. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a command-line check for open pull requests that may already be modifying specified files. * Reports matching pull request details and clearly indicates whether each file is claimed. * Returns distinct statuses for claimed files, unclaimed files, and unavailable GitHub checks. * **Documentation** * Added guidance for checking file ownership before beginning conversion work. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
94 lines
4.1 KiB
JavaScript
94 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/*
|
|
FNXC:FleetCoordination 2026-07-31-06:10 (fleet):
|
|
|
|
WHY THIS EXISTS. Every fleet worker pushes as the SAME GitHub account, so `gh pr list --author "@me"`
|
|
returns all 17 open PRs and no worker can tell their own from a teammate's. There is no way to ask "is
|
|
this file already being converted?" short of fetching every open PR's file list by hand — 25+ API calls
|
|
that a worker will not make before starting, and I did not make either.
|
|
|
|
MEASURED COST, not a hypothetical: four of my PRs were superseded by teammates landing the same work
|
|
first (#3096, #3116, #3140 shrank to tests; #3125 to nothing and was closed). In every case both
|
|
implementations were correct and independently reached the same design. The fleet is not making
|
|
mistakes — it is doing correct work twice, and finding coverage gaps only by accident when the rebases
|
|
collide.
|
|
|
|
WHAT THIS DOES. One command, one answer:
|
|
|
|
node scripts/check-file-claimed.mjs packages/engine/src/self-healing.ts
|
|
|
|
It prints the open PRs touching that path, so "claimed?" is answerable before the work starts rather
|
|
than at rebase time.
|
|
|
|
WHAT IT DOES NOT DO, deliberately. It cannot see work that is in progress and unpushed, so it narrows
|
|
the window rather than closing it. Closing it needs distinguishable authorship — a per-worker
|
|
`Co-Authored-By` or a title prefix — which is a coordination decision, not a script. This is the part
|
|
that can be fixed from inside the repo.
|
|
*/
|
|
import { execFileSync } from "node:child_process";
|
|
|
|
const targets = process.argv.slice(2).filter((a) => !a.startsWith("-"));
|
|
if (targets.length === 0) {
|
|
console.error("usage: node scripts/check-file-claimed.mjs <path> [<path>...]");
|
|
console.error(" paths are matched as substrings of each PR's changed-file list");
|
|
process.exit(2);
|
|
}
|
|
|
|
function gh(args) {
|
|
try {
|
|
return execFileSync("gh", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
} catch (err) {
|
|
console.error(`claim-check: gh failed — ${err?.message ?? err}`);
|
|
console.error("claim-check: cannot prove a file is unclaimed without it. Treat as UNKNOWN, not free.");
|
|
process.exit(2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
FNXC:FleetClaims 2026-07-31-21:10 (#3175 review — coderabbitai, "detect or eliminate truncation"):
|
|
A SILENT CAP TURNS THIS TOOL INTO THE BUG IT PREVENTS.
|
|
|
|
`--limit 100` returns at most 100 PRs. Past that, claims live in PRs this never sees and a claimed file
|
|
reports UNCLAIMED — the one answer that must never be wrong here, because a worker acts on it by
|
|
starting work someone else already started. This fleet ran 50+ open PRs at once, so the cap is not
|
|
hypothetical.
|
|
|
|
Fails closed rather than warning: a warning printed above an `UNCLAIMED` line is read as noise next to
|
|
a verdict. Same reasoning as `gh()` exiting rather than returning empty.
|
|
*/
|
|
const PR_LIMIT = 300;
|
|
const open = JSON.parse(gh(["pr", "list", "--state", "open", "--limit", String(PR_LIMIT), "--json", "number,title"]));
|
|
if (open.length >= PR_LIMIT) {
|
|
console.error(`claim-check: ${open.length} open PRs hit the --limit ${PR_LIMIT} cap, so the list may be truncated.`);
|
|
console.error("claim-check: cannot prove a file is unclaimed from a partial list. Treat as UNKNOWN, not free.");
|
|
process.exit(2);
|
|
}
|
|
const hits = new Map(targets.map((t) => [t, []]));
|
|
|
|
for (const pr of open) {
|
|
/*
|
|
FNXC:FleetClaims 2026-07-31-21:10 (#3175 review — coderabbitai, "unreachable catch"): `gh()` exits on
|
|
failure, so this try/catch could never fire. Removed rather than made reachable: skipping a PR whose
|
|
diff failed is exactly how a claim goes unseen, and the fail-closed exit is already the right answer.
|
|
*/
|
|
const files = gh(["pr", "diff", String(pr.number), "--name-only"]).split("\n").filter(Boolean);
|
|
for (const t of targets) {
|
|
if (files.some((f) => f.includes(t))) hits.get(t).push(pr);
|
|
}
|
|
}
|
|
|
|
let claimed = false;
|
|
for (const t of targets) {
|
|
const prs = hits.get(t);
|
|
if (prs.length === 0) {
|
|
console.log(`UNCLAIMED ${t}`);
|
|
continue;
|
|
}
|
|
claimed = true;
|
|
console.log(`CLAIMED ${t}`);
|
|
for (const pr of prs) console.log(` #${pr.number} ${pr.title}`);
|
|
}
|
|
|
|
/* Exit 1 when anything is claimed, so a worker can gate on it: `... && start-work`. */
|
|
process.exit(claimed ? 1 : 0);
|