@webpieces/repo-workflow-core 0.4.975 → 0.4.977

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"main-sync-status.js","sourceRoot":"","sources":["../../../../../packages/tooling/repo-workflow-core/src/main-sync-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from 'child_process';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { AtomicFile } from '@webpieces/tooling-common';\nimport {\n MAIN_SYNC_STATUS_VERSION,\n MainSyncFileStore,\n MainSyncStatus,\n MainSyncStatusFile,\n PullRequestIndex,\n RefreshOutcome,\n} from './main-sync-file';\nimport { DotWebpieces, dotWebpieces, gitCheckout } from '@webpieces/tooling-common';\nimport { toError } from '@webpieces/tooling-common/to-error';\nimport { WorktreeService } from './worktrees';\nimport { CommandTimeouts } from './command-timeouts';\n\n// The on-disk shape lives in main-sync-file.ts; re-exported so every existing importer of\n// `MainSyncStatus` from this module keeps working.\nexport { MainSyncStatus, MainSyncStatusFile, PullRequestIndex, MAIN_SYNC_STATUS_VERSION, RefreshOutcome };\n\n// Shared \"is my feature branch healthy relative to origin/main?\" state. The SLOW signals (git fetch +\n// merge-base + same-file-overlap + a merged-PR lookup) are computed by the ai-hook-rules refresher in a\n// DETACHED background process; the feature-branch-guard only READS this cached file. pr-gate's merge\n// flow also writes it synchronously after a merge. Lives here (the shared dep of both).\n\n// How long a refresher may hold main-sync.lock.json before the next spawner assumes it hung, kills it\n// and reclaims the lock (see MainSyncLockService).\nexport const DEFAULT_HANG_TIMEOUT_MINUTES = 5;\n\nconst MAIN_SYNC_STATUS_FILE = 'main-sync-status.json';\n\n// Result of a captured git/gh invocation: ok=false on spawn failure or non-zero exit.\ninterface CmdCapture {\n ok: boolean;\n out: string;\n}\n\n/**\n * Reads/writes the main-sync cache and computes the slow \"is my branch healthy vs origin/main?\"\n * status. `@injectable(bindingScopeValues.Singleton)` so it's injectable and drawn in consumers’ DI designs.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class MainSyncStatusService {\n constructor(\n private readonly dotDir: DotWebpieces = dotWebpieces,\n private readonly atomicFile: AtomicFile = new AtomicFile(),\n private readonly store: MainSyncFileStore = new MainSyncFileStore(atomicFile),\n private readonly worktrees: WorktreeService = new WorktreeService(),\n private readonly timeouts: CommandTimeouts = new CommandTimeouts(),\n ) {}\n\n /**\n * SHARED scope: `<primary>/.webpieces/main-sync-status.json`, one per repo.\n *\n * It is the OUTPUT of the single-flight refresher below, so it has to live where the lock lives —\n * one `.git`, one `origin/main`, one fetch, one answer. A per-worktree copy under single-flight\n * would only ever be written for whichever worktree won the lock anyway.\n *\n * FORMERLY a known defect, now fixed: the file's CONTENT used to be keyed to ONE branch, and every\n * reader fails OPEN when the cached branch is not the branch being judged (`stale-cross-branch-cache\n * (fail-open)`). With several worktrees on several branches, only the winner of the lock was ever\n * described, so every other worktree's guards abstained — and which one was armed thrashed as the\n * lock changed hands. The file is now a MAP of branch -> status (see MainSyncStatusFile): the single\n * winning refresher enumerates every worktree and records all of their branches in one atomic write,\n * so one fetch arms everyone. Single-flight is unchanged; only what the winner writes changed.\n */\n mainSyncStatusPath(repoRoot: string): string {\n return this.dotDir.sharedFile(repoRoot, MAIN_SYNC_STATUS_FILE);\n }\n\n /**\n * The cached status FOR ONE BRANCH — the only read the four main-sync guards perform.\n *\n * `branch` is mandatory and is the branch being judged. A branch the last refresh never saw\n * returns null, which every guard already treats as `no-sync-cache (fail-open)`, so an unknown\n * branch degrades exactly as a missing file did. Any error (missing, truncated, malformed JSON)\n * also returns null — this must never throw on the blocking path.\n */\n readMainSyncStatus(repoRoot: string, branch: string): MainSyncStatus | null {\n return this.store.branchStatus(this.readMainSyncStatusFile(repoRoot), branch);\n }\n\n // The whole branch-keyed document, or null. Callers that need more than one branch use this.\n readMainSyncStatusFile(repoRoot: string): MainSyncStatusFile | null {\n return this.store.readFile(this.mainSyncStatusPath(repoRoot));\n }\n\n /**\n * Merge ONE branch's entry into the existing map, atomically, leaving siblings untouched.\n *\n * Read-modify-write rather than replace: this is the writer used by everyone who is NOT the\n * single-flight refresher (notably pr-gate's post-merge stamp, which holds no lock and knows about\n * exactly one branch). Replacing the document from there would erase every other worktree's entry\n * and silently disarm their guards until the next refresh.\n */\n writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void {\n this.store.mergeBranch(this.mainSyncStatusPath(repoRoot), status);\n }\n\n /**\n * Replace the WHOLE document — only the winning refresher may do this.\n *\n * ATOMIC (temp file + `rename()` in the same directory). The refresher writes this file while\n * guards on the blocking path read it, possibly from another worktree; a plain `writeFileSync`\n * truncates first, so a reader landing in that window gets a torn document. See AtomicFile.\n */\n writeMainSyncStatusFile(repoRoot: string, file: MainSyncStatusFile): void {\n this.store.writeFile(this.mainSyncStatusPath(repoRoot), file);\n }\n\n /**\n * Record that a refresh STARTED at `startedMs` and ended with `outcome`, keeping every branch entry\n * already on disk. The refresher's failure path: a run that produced no new branch data still\n * counts as a run, so the throttle retries it after MIN_REFRESH_INTERVAL_MINUTES rather than on\n * every tool call. A successful run stamps the whole document instead (writeMainSyncStatusFile).\n */\n recordRefreshOutcome(repoRoot: string, startedMs: number, outcome: RefreshOutcome): void {\n this.store.recordRefresh(this.mainSyncStatusPath(repoRoot), startedMs, outcome);\n }\n\n /**\n * The SLOW path for ONE branch: the branch checked out in `repoRoot`. Runs its own fetch and its\n * own `gh` query, so it is the right entry point for a single-worktree caller and for tests.\n * The refresher uses computeAllMainSyncStatuses instead — see there for why.\n */\n computeMainSyncStatus(repoRoot: string): MainSyncStatus {\n // Best-effort network refresh; offline just means we evaluate against the last-fetched ref.\n this.fetchOriginMain(repoRoot);\n return this.computeBranchStatus(repoRoot, this.gitBranch(repoRoot), this.loadPullRequestIndex(repoRoot));\n }\n\n /**\n * The SLOW path for EVERY branch this repo has checked out — what the detached refresher runs.\n *\n * The cache is shared by all worktrees but was written for one branch, so only one worktree's\n * guards were ever armed. Enumerating the worktrees here and writing all of their branches in one\n * atomic map arms every worktree off a single refresh.\n *\n * NETWORK COST DOES NOT SCALE WITH BRANCH COUNT, which is the whole reason this is a separate\n * method: ONE `git fetch` and ONE repo-wide `gh pr list` for all N branches (the old code asked\n * `gh` twice per branch, so looping it would have cost 2N round trips). Everything after that is\n * local git in each worktree — merge-base, rev-parse, diff, ls-files.\n *\n * The map is rebuilt from the LIVE worktree set every refresh, so a deleted branch or removed\n * worktree simply stops appearing. No pruning pass, no unbounded growth.\n */\n computeAllMainSyncStatuses(repoRoot: string): MainSyncStatusFile {\n this.fetchOriginMain(repoRoot);\n const index = this.loadPullRequestIndex(repoRoot);\n const branches: Record<string, MainSyncStatus> = {};\n for (const tree of this.worktrees.listWorktrees(repoRoot)) {\n // Detached HEAD (and a bare worktree) has no branch to key an entry on. Skipping it must\n // not stop the others being recorded, which is why this is a `continue`, not a bail-out.\n if (tree.branch === '' || this.hasBranch(branches, tree.branch)) continue;\n branches[tree.branch] = this.computeBranchStatus(tree.path, tree.branch, index);\n }\n // `main` always present, even with no worktree on it: read-stale-guard's state A looks itself\n // up under 'main', and an absent entry there would silently disarm the stale-main block.\n if (!this.hasBranch(branches, 'main')) branches['main'] = this.mainOnlyStatus(repoRoot);\n return new MainSyncStatusFile(MAIN_SYNC_STATUS_VERSION, new Date().toISOString(), branches);\n }\n\n private hasBranch(branches: Record<string, MainSyncStatus>, branch: string): boolean {\n return Object.prototype.hasOwnProperty.call(branches, branch);\n }\n\n /**\n * One branch's status, computed in `worktreePath` (the tree that has it checked out — the\n * working-tree overlap signals are only correct from there). Does NOT fetch and does NOT call\n * `gh`: both are done once per refresh by the caller.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive slow-path computation\n private computeBranchStatus(repoRoot: string, branch: string, index: PullRequestIndex): MainSyncStatus {\n return this.stampForge(this.computeBranchStatusInner(repoRoot, branch, index), index);\n }\n\n // Every return path of the computation below has to carry the forge flag, and threading it through\n // four constructions is how one of them would end up missing it. One wrapper, one assignment.\n private stampForge(status: MainSyncStatus, index: PullRequestIndex): MainSyncStatus {\n status.forgeReachable = index.forgeReachable;\n return status;\n }\n\n // webpieces-disable max-lines-new-methods -- one cohesive slow-path computation\n private computeBranchStatusInner(repoRoot: string, branch: string, index: PullRequestIndex): MainSyncStatus {\n const mergedPr = index.mergedFor(branch);\n const openPr = index.openFor(branch);\n\n const head = this.capture(repoRoot, 'git', ['rev-parse', 'HEAD']);\n const originMain = this.capture(repoRoot, 'git', ['rev-parse', 'origin/main']);\n const localMain = this.localMainHash(repoRoot);\n const featureHead = head.ok ? head.out : '';\n if (!head.ok || !originMain.ok) {\n const status = this.benignStatus(branch, featureHead);\n status.branchAlreadyMerged = mergedPr !== '';\n status.mergedPr = mergedPr;\n status.openPr = openPr;\n status.localMain = localMain;\n status.commitsBehind = this.commitDistance(repoRoot, localMain, originMain.ok ? originMain.out : '');\n return status;\n }\n\n const forkPoint = this.capture(repoRoot, 'git', ['merge-base', 'origin/main', 'HEAD']);\n if (!forkPoint.ok || forkPoint.out === '') {\n const noFork = new MainSyncStatus(branch, mergedPr !== '', mergedPr, false, null, originMain.out, featureHead, false, [], new Date().toISOString());\n noFork.openPr = openPr;\n noFork.localMain = localMain;\n noFork.commitsBehind = this.commitDistance(repoRoot, localMain, originMain.out);\n return noFork;\n }\n\n const featureFiles = new Set(this.featureChangedFiles(repoRoot, forkPoint.out));\n const mainFiles = this.changedFiles(repoRoot, forkPoint.out, 'origin/main');\n const conflictFiles = mainFiles.filter((file: string): boolean => featureFiles.has(file));\n\n const status = new MainSyncStatus(\n branch,\n mergedPr !== '',\n mergedPr,\n true,\n forkPoint.out,\n originMain.out,\n featureHead,\n conflictFiles.length > 0,\n conflictFiles,\n new Date().toISOString(),\n );\n status.openPr = openPr;\n status.localMain = localMain;\n status.commitsBehind = this.commitDistance(repoRoot, localMain, originMain.out);\n return status;\n }\n\n // The recovery steps when there is no fork point with origin/main (a bad merge of main into branch).\n squashRecoverySteps(currentBranch: string): string[] {\n return [\n '1. Fetch latest main: git fetch origin main',\n `2. New branch off origin/main: git checkout -b ${currentBranch}-v2 origin/main`,\n `3. Squash-merge old branch: git merge --squash ${currentBranch}`,\n ' ^^ HUMAN-ONLY. `git merge` is blocked for AI (redirect-how-to-merge-main). AI: ask the',\n ' human to run step 3, and warn them it is a raw merge — only correct here because the',\n ' branch is already broken. For a normal update from main they should push back and',\n ' tell you to use the gated 3-point merge instead: `pnpm wp-start-update` (paired with',\n ' `pnpm wp-finish-update`) when no PR is open, or `pnpm wp-start-upsert-pr` (paired with',\n ' `pnpm wp-finish-upsert-pr`) when a PR IS open — a PR MUST use the upsert-pr pair.',\n `4. Commit the squash: git add -A && git commit -m \"Squashed from ${currentBranch}\"`,\n '5. If a PR exists: open a NEW PR for the -v2 branch and close the old one.',\n ];\n }\n\n /**\n * Synchronously stamp a clean \"up to date with main\" status — call right after a successful merge.\n *\n * This is the one writer that takes NO lock, and it knows about exactly one branch. It therefore\n * goes through writeMainSyncStatus, which MERGES its entry into the existing map; writing a whole\n * document from here would wipe every other worktree's entry.\n */\n stampCleanMainSyncStatus(repoRoot: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const branch = this.gitBranch(repoRoot);\n const originMain = this.capture(repoRoot, 'git', ['rev-parse', 'origin/main']);\n const featureHead = this.capture(repoRoot, 'git', ['rev-parse', 'HEAD']);\n if (!originMain.ok || !featureHead.ok) return;\n const status = new MainSyncStatus(\n branch, false, '', true, originMain.out, originMain.out, featureHead.out, false, [], new Date().toISOString(),\n );\n status.localMain = this.localMainHash(repoRoot);\n status.commitsBehind = 0;\n this.writeMainSyncStatus(repoRoot, status);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n /**\n * Refresh `origin/main` WITHOUT writing `.git/FETCH_HEAD`.\n *\n * WHY the flag is not optional: this runs in a DETACHED background process while the agent is\n * running its own foreground `git fetch` / `git pull` in the very same repo. `.git/FETCH_HEAD` is\n * a single file that git takes no lock on, so two overlapping fetches interleave their writes and\n * can leave the SAME `for-merge` line twice. `git pull` then reads two for-merge entries and dies\n * with `fatal: Cannot fast-forward to multiple branches` — wedging the exact command the\n * read-stale-guard tells the agent to run. `--no-write-fetch-head` (git >= 2.29) still updates the\n * remote-tracking ref, which is all anything downstream reads (`origin/main`, merge-base), so\n * nothing here needs FETCH_HEAD written at all.\n *\n * Older git rejects the flag; only then do we retry the plain form, accepting the old behaviour\n * rather than losing the refresh entirely on a pre-2020 git.\n */\n private fetchOriginMain(repoRoot: string): void {\n const safe = spawnSync('git', ['fetch', '--no-write-fetch-head', 'origin', 'main'], {\n cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'ignore', 'pipe'], timeout: CommandTimeouts.NETWORK_MS,\n });\n if (safe.status === 0) return;\n if (!this.isUnknownGitOption(safe.stderr)) return; // a real failure (offline, auth) — not our business\n spawnSync('git', ['fetch', 'origin', 'main'], { cwd: repoRoot, stdio: 'ignore', timeout: CommandTimeouts.NETWORK_MS });\n }\n\n // Did git reject the flag itself (too old), as opposed to failing the network refresh?\n private isUnknownGitOption(stderr: string | null): boolean {\n const text = (stderr ?? '').toLowerCase();\n return text.includes('unknown option') || text.includes('unknown switch') || text.includes('unrecognized option');\n }\n\n // Run a command capturing trimmed stdout; ok=false on spawn failure or non-zero exit.\n private capture(repoRoot: string, cmd: string, args: string[]): CmdCapture {\n const result = spawnSync(cmd, args, { cwd: repoRoot, encoding: 'utf8', timeout: this.timeouts.forCommand(cmd) });\n if (result.status !== 0 || typeof result.stdout !== 'string') return { ok: false, out: '' };\n return { ok: true, out: result.stdout.trim() };\n }\n\n // The actual checked-out branch in repoRoot — cwd-correct so the cache's `branch` label matches.\n private gitBranch(repoRoot: string): string {\n return gitCheckout.branch(repoRoot) ?? '';\n }\n\n // The LOCAL main hash. `refs/heads/main` (not the bare name) so it can never resolve to a remote\n // ref or a tag. '' when main does not exist locally — which the guard treats as fail-open.\n private localMainHash(repoRoot: string): string {\n const result = this.capture(repoRoot, 'git', ['rev-parse', 'refs/heads/main']);\n return result.ok ? result.out : '';\n }\n\n // Slow-path only: the detached refresher calls this after fetching. Guards never count commits.\n private commitDistance(repoRoot: string, localMain: string, originMain: string): number | null {\n if (localMain === '' || originMain === '') return null;\n const result = this.capture(repoRoot, 'git', ['rev-list', '--count', `${localMain}..${originMain}`]);\n if (!result.ok || !/^\\d+$/.test(result.out)) return null;\n return Number(result.out);\n }\n\n private changedFiles(repoRoot: string, base: string, head: string): string[] {\n const result = this.capture(repoRoot, 'git', ['diff', '--name-only', base, head]);\n if (!result.ok || result.out === '') return [];\n return result.out.split('\\n').map((line: string): string => line.trim()).filter((line: string): boolean => line.length > 0);\n }\n\n // Every file this feature branch has touched since the fork point — committed AND still in the\n // working tree (staged / unstaged / untracked), so a conflict is visible WHILE editing.\n private featureChangedFiles(repoRoot: string, forkPoint: string): string[] {\n const out = new Set<string>();\n const add = (args: string[]): void => {\n const r = this.capture(repoRoot, 'git', args);\n if (!r.ok || r.out === '') return;\n for (const line of r.out.split('\\n')) {\n const f = line.trim();\n if (f.length > 0) out.add(f);\n }\n };\n add(['diff', '--name-only', forkPoint, 'HEAD']); // committed since the fork point\n add(['diff', '--name-only', 'HEAD']); // unstaged working-tree edits\n add(['diff', '--name-only', '--cached', 'HEAD']); // staged edits\n add(['ls-files', '--others', '--exclude-standard']); // untracked new files (respects .gitignore)\n return [...out];\n }\n\n /**\n * ONE repo-wide `gh pr list` giving every branch's merged/open PR — replaces the two per-branch\n * `--head <branch>` queries. `--limit 200` bounds it; a branch whose only PR is older than that\n * falls out of the index and reads as \"no PR\", which is the fail-OPEN direction (no merged PR =\n * no block), the same way a missing `gh` or an offline machine already degraded.\n */\n private loadPullRequestIndex(repoRoot: string): PullRequestIndex {\n const result = this.capture(repoRoot, 'gh', [\n 'pr', 'list', '--state', 'all', '--json', 'number,headRefName,state', '--limit', '200',\n ]);\n // `gh` could not be run, or exited non-zero (missing, unauthenticated, rate-limited, offline).\n // An EMPTY index is the same fail-open answer as before — but it is now labelled UNREACHABLE, so\n // the guards can report an abstention rather than an approval. Empty STDOUT on a zero exit is a\n // real answer (\"this repo has no PRs\"), so only the failure path clears the flag.\n if (!result.ok) return new PullRequestIndex({}, {}, false);\n if (result.out === '') return new PullRequestIndex({}, {}, true);\n return this.store.indexPullRequests(result.out);\n }\n\n /**\n * A `main` entry synthesised WITHOUT a worktree standing on main. Carries only the two hashes\n * read-stale-guard / stale-main-bash-guard actually compare (originMain vs the live tree's\n * ancestry) — never merged, never conflicting, so it can only ever produce the stale-main verdict.\n */\n private mainOnlyStatus(repoRoot: string): MainSyncStatus {\n const originMain = this.capture(repoRoot, 'git', ['rev-parse', 'origin/main']);\n const localMain = this.localMainHash(repoRoot);\n const status = new MainSyncStatus(\n 'main', false, '', true, localMain === '' ? null : localMain,\n originMain.ok ? originMain.out : '', localMain, false, [], new Date().toISOString(),\n );\n status.localMain = localMain;\n status.commitsBehind = this.commitDistance(repoRoot, localMain, originMain.ok ? originMain.out : '');\n return status;\n }\n\n // A benign status that never blocks — used when origin/main can't be resolved.\n private benignStatus(branch: string, featureHead: string): MainSyncStatus {\n return new MainSyncStatus(branch, false, '', true, null, '', featureHead, false, [], new Date().toISOString());\n }\n}\n\n// Temporary migration delegators to MainSyncStatusService — removed once consumers inject it.\nconst mainSyncSvc = new MainSyncStatusService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function mainSyncStatusPath(repoRoot: string): string { return mainSyncSvc.mainSyncStatusPath(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncStatus(repoRoot: string, branch: string): MainSyncStatus | null { return mainSyncSvc.readMainSyncStatus(repoRoot, branch); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncStatusFile(repoRoot: string): MainSyncStatusFile | null { return mainSyncSvc.readMainSyncStatusFile(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void { mainSyncSvc.writeMainSyncStatus(repoRoot, status); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncStatusFile(repoRoot: string, file: MainSyncStatusFile): void { mainSyncSvc.writeMainSyncStatusFile(repoRoot, file); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function computeMainSyncStatus(repoRoot: string): MainSyncStatus { return mainSyncSvc.computeMainSyncStatus(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function computeAllMainSyncStatuses(repoRoot: string): MainSyncStatusFile { return mainSyncSvc.computeAllMainSyncStatuses(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function squashRecoverySteps(currentBranch: string): string[] { return mainSyncSvc.squashRecoverySteps(currentBranch); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function stampCleanMainSyncStatus(repoRoot: string): void { mainSyncSvc.stampCleanMainSyncStatus(repoRoot); }\n"]}
1
+ {"version":3,"file":"main-sync-status.js","sourceRoot":"","sources":["../../../../../packages/tooling/repo-workflow-core/src/main-sync-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { injectable, bindingScopeValues } from 'inversify';\n\nimport { AtomicFile } from '@webpieces/tooling-common';\nimport {\n MAIN_SYNC_STATUS_VERSION,\n MainSyncFileStore,\n MainSyncStatus,\n MainSyncStatusFile,\n PullRequestIndex,\n RefreshOutcome,\n} from './main-sync-file';\nimport { DotWebpieces, dotWebpieces, gitCheckout } from '@webpieces/tooling-common';\nimport { toError } from '@webpieces/tooling-common/to-error';\nimport { WorktreeService } from './worktrees';\nimport { CommandTimeouts } from './command-timeouts';\n\n// The on-disk shape lives in main-sync-file.ts; re-exported so every existing importer of\n// `MainSyncStatus` from this module keeps working.\nexport { MainSyncStatus, MainSyncStatusFile, PullRequestIndex, MAIN_SYNC_STATUS_VERSION, RefreshOutcome };\n\n// Shared \"is my feature branch healthy relative to origin/main?\" state. The SLOW signals (git fetch +\n// merge-base + same-file-overlap + a merged-PR lookup) are computed by the ai-hook-rules refresher in a\n// DETACHED background process; the feature-branch-guard only READS this cached file. pr-gate's merge\n// flow also writes it synchronously after a merge. Lives here (the shared dep of both).\n\n// How long a refresher may hold main-sync.lock.json before the next spawner assumes it hung, kills it\n// and reclaims the lock (see MainSyncLockService).\nexport const DEFAULT_HANG_TIMEOUT_MINUTES = 5;\n\nconst MAIN_SYNC_STATUS_FILE = 'main-sync-status.json';\n\n// Internal: one bounded command's verdict and trimmed stdout. Data only.\nclass CapturedOutput {\n constructor(readonly ok: boolean, readonly out: string) {}\n}\n\n/**\n * Reads/writes the main-sync cache and computes the slow \"is my branch healthy vs origin/main?\"\n * status. `@injectable(bindingScopeValues.Singleton)` so it's injectable and drawn in consumers’ DI designs.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class MainSyncStatusService {\n constructor(\n private readonly dotDir: DotWebpieces = dotWebpieces,\n private readonly atomicFile: AtomicFile = new AtomicFile(),\n private readonly store: MainSyncFileStore = new MainSyncFileStore(atomicFile),\n private readonly worktrees: WorktreeService = new WorktreeService(),\n private readonly commands: CommandTimeouts = new CommandTimeouts(),\n ) {}\n\n /**\n * SHARED scope: `<primary>/.webpieces/main-sync-status.json`, one per repo.\n *\n * It is the OUTPUT of the single-flight refresher below, so it has to live where the lock lives —\n * one `.git`, one `origin/main`, one fetch, one answer. A per-worktree copy under single-flight\n * would only ever be written for whichever worktree won the lock anyway.\n *\n * FORMERLY a known defect, now fixed: the file's CONTENT used to be keyed to ONE branch, and every\n * reader fails OPEN when the cached branch is not the branch being judged (`stale-cross-branch-cache\n * (fail-open)`). With several worktrees on several branches, only the winner of the lock was ever\n * described, so every other worktree's guards abstained — and which one was armed thrashed as the\n * lock changed hands. The file is now a MAP of branch -> status (see MainSyncStatusFile): the single\n * winning refresher enumerates every worktree and records all of their branches in one atomic write,\n * so one fetch arms everyone. Single-flight is unchanged; only what the winner writes changed.\n */\n mainSyncStatusPath(repoRoot: string): string {\n return this.dotDir.sharedFile(repoRoot, MAIN_SYNC_STATUS_FILE);\n }\n\n /**\n * The cached status FOR ONE BRANCH — the only read the four main-sync guards perform.\n *\n * `branch` is mandatory and is the branch being judged. A branch the last refresh never saw\n * returns null, which every guard already treats as `no-sync-cache (fail-open)`, so an unknown\n * branch degrades exactly as a missing file did. Any error (missing, truncated, malformed JSON)\n * also returns null — this must never throw on the blocking path.\n */\n readMainSyncStatus(repoRoot: string, branch: string): MainSyncStatus | null {\n return this.store.branchStatus(this.readMainSyncStatusFile(repoRoot), branch);\n }\n\n // The whole branch-keyed document, or null. Callers that need more than one branch use this.\n readMainSyncStatusFile(repoRoot: string): MainSyncStatusFile | null {\n return this.store.readFile(this.mainSyncStatusPath(repoRoot));\n }\n\n /**\n * Merge ONE branch's entry into the existing map, atomically, leaving siblings untouched.\n *\n * Read-modify-write rather than replace: this is the writer used by everyone who is NOT the\n * single-flight refresher (notably pr-gate's post-merge stamp, which holds no lock and knows about\n * exactly one branch). Replacing the document from there would erase every other worktree's entry\n * and silently disarm their guards until the next refresh.\n */\n writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void {\n this.store.mergeBranch(this.mainSyncStatusPath(repoRoot), status);\n }\n\n /**\n * Replace the WHOLE document — only the winning refresher may do this.\n *\n * ATOMIC (temp file + `rename()` in the same directory). The refresher writes this file while\n * guards on the blocking path read it, possibly from another worktree; a plain `writeFileSync`\n * truncates first, so a reader landing in that window gets a torn document. See AtomicFile.\n */\n writeMainSyncStatusFile(repoRoot: string, file: MainSyncStatusFile): void {\n this.store.writeFile(this.mainSyncStatusPath(repoRoot), file);\n }\n\n /**\n * Record that a refresh STARTED at `startedMs` and ended with `outcome`, keeping every branch entry\n * already on disk. The refresher's failure path: a run that produced no new branch data still\n * counts as a run, so the throttle retries it after MIN_REFRESH_INTERVAL_MINUTES rather than on\n * every tool call. A successful run stamps the whole document instead (writeMainSyncStatusFile).\n */\n recordRefreshOutcome(repoRoot: string, startedMs: number, outcome: RefreshOutcome): void {\n this.store.recordRefresh(this.mainSyncStatusPath(repoRoot), startedMs, outcome);\n }\n\n /**\n * The SLOW path for ONE branch: the branch checked out in `repoRoot`. Runs its own fetch and its\n * own `gh` query, so it is the right entry point for a single-worktree caller and for tests.\n * The refresher uses computeAllMainSyncStatuses instead — see there for why.\n */\n async computeMainSyncStatus(repoRoot: string): Promise<MainSyncStatus> {\n // Best-effort network refresh; offline just means we evaluate against the last-fetched ref.\n await this.fetchOriginMain(repoRoot);\n return this.computeBranchStatus(repoRoot, this.gitBranch(repoRoot), await this.loadPullRequestIndex(repoRoot));\n }\n\n /**\n * The SLOW path for EVERY branch this repo has checked out — what the detached refresher runs.\n *\n * The cache is shared by all worktrees but was written for one branch, so only one worktree's\n * guards were ever armed. Enumerating the worktrees here and writing all of their branches in one\n * atomic map arms every worktree off a single refresh.\n *\n * NETWORK COST DOES NOT SCALE WITH BRANCH COUNT, which is the whole reason this is a separate\n * method: ONE `git fetch` and ONE repo-wide `gh pr list` for all N branches (the old code asked\n * `gh` twice per branch, so looping it would have cost 2N round trips). Everything after that is\n * local git in each worktree — merge-base, rev-parse, diff, ls-files.\n *\n * The map is rebuilt from the LIVE worktree set every refresh, so a deleted branch or removed\n * worktree simply stops appearing. No pruning pass, no unbounded growth.\n */\n async computeAllMainSyncStatuses(repoRoot: string): Promise<MainSyncStatusFile> {\n await this.fetchOriginMain(repoRoot);\n const index = await this.loadPullRequestIndex(repoRoot);\n const branches: Record<string, MainSyncStatus> = {};\n for (const tree of this.worktrees.listWorktrees(repoRoot)) {\n // Detached HEAD (and a bare worktree) has no branch to key an entry on. Skipping it must\n // not stop the others being recorded, which is why this is a `continue`, not a bail-out.\n if (tree.branch === '' || this.hasBranch(branches, tree.branch)) continue;\n branches[tree.branch] = await this.computeBranchStatus(tree.path, tree.branch, index);\n }\n // `main` always present, even with no worktree on it: read-stale-guard's state A looks itself\n // up under 'main', and an absent entry there would silently disarm the stale-main block.\n if (!this.hasBranch(branches, 'main')) branches['main'] = await this.mainOnlyStatus(repoRoot);\n return new MainSyncStatusFile(MAIN_SYNC_STATUS_VERSION, new Date().toISOString(), branches);\n }\n\n private hasBranch(branches: Record<string, MainSyncStatus>, branch: string): boolean {\n return Object.prototype.hasOwnProperty.call(branches, branch);\n }\n\n /**\n * One branch's status, computed in `worktreePath` (the tree that has it checked out — the\n * working-tree overlap signals are only correct from there). Does NOT fetch and does NOT call\n * `gh`: both are done once per refresh by the caller.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive slow-path computation\n private async computeBranchStatus(repoRoot: string, branch: string, index: PullRequestIndex): Promise<MainSyncStatus> {\n return this.stampForge(await this.computeBranchStatusInner(repoRoot, branch, index), index);\n }\n\n // Every return path of the computation below has to carry the forge flag, and threading it through\n // four constructions is how one of them would end up missing it. One wrapper, one assignment.\n private stampForge(status: MainSyncStatus, index: PullRequestIndex): MainSyncStatus {\n status.forgeReachable = index.forgeReachable;\n return status;\n }\n\n // webpieces-disable max-lines-new-methods -- one cohesive slow-path computation\n private async computeBranchStatusInner(repoRoot: string, branch: string, index: PullRequestIndex): Promise<MainSyncStatus> {\n const mergedPr = index.mergedFor(branch);\n const openPr = index.openFor(branch);\n\n const head = await this.git(repoRoot, ['rev-parse', 'HEAD']);\n const originMain = await this.git(repoRoot, ['rev-parse', 'origin/main']);\n const localMain = await this.localMainHash(repoRoot);\n const featureHead = head.ok ? head.out : '';\n if (!head.ok || !originMain.ok) {\n const status = this.benignStatus(branch, featureHead);\n status.branchAlreadyMerged = mergedPr !== '';\n status.mergedPr = mergedPr;\n status.openPr = openPr;\n status.localMain = localMain;\n status.commitsBehind = await this.commitDistance(repoRoot, localMain, originMain.ok ? originMain.out : '');\n return status;\n }\n\n const forkPoint = await this.git(repoRoot, ['merge-base', 'origin/main', 'HEAD']);\n if (!forkPoint.ok || forkPoint.out === '') {\n const noFork = new MainSyncStatus(branch, mergedPr !== '', mergedPr, false, null, originMain.out, featureHead, false, [], new Date().toISOString());\n noFork.openPr = openPr;\n noFork.localMain = localMain;\n noFork.commitsBehind = await this.commitDistance(repoRoot, localMain, originMain.out);\n return noFork;\n }\n\n const featureFiles = new Set(await this.featureChangedFiles(repoRoot, forkPoint.out));\n const mainFiles = await this.changedFiles(repoRoot, forkPoint.out, 'origin/main');\n const conflictFiles = mainFiles.filter((file: string): boolean => featureFiles.has(file));\n\n const status = new MainSyncStatus(\n branch,\n mergedPr !== '',\n mergedPr,\n true,\n forkPoint.out,\n originMain.out,\n featureHead,\n conflictFiles.length > 0,\n conflictFiles,\n new Date().toISOString(),\n );\n status.openPr = openPr;\n status.localMain = localMain;\n status.commitsBehind = await this.commitDistance(repoRoot, localMain, originMain.out);\n return status;\n }\n\n // The recovery steps when there is no fork point with origin/main (a bad merge of main into branch).\n squashRecoverySteps(currentBranch: string): string[] {\n return [\n '1. Fetch latest main: git fetch origin main',\n `2. New branch off origin/main: git checkout -b ${currentBranch}-v2 origin/main`,\n `3. Squash-merge old branch: git merge --squash ${currentBranch}`,\n ' ^^ HUMAN-ONLY. `git merge` is blocked for AI (redirect-how-to-merge-main). AI: ask the',\n ' human to run step 3, and warn them it is a raw merge — only correct here because the',\n ' branch is already broken. For a normal update from main they should push back and',\n ' tell you to use the gated 3-point merge instead: `pnpm wp-start-update` (paired with',\n ' `pnpm wp-finish-update`) when no PR is open, or `pnpm wp-start-upsert-pr` (paired with',\n ' `pnpm wp-finish-upsert-pr`) when a PR IS open — a PR MUST use the upsert-pr pair.',\n `4. Commit the squash: git add -A && git commit -m \"Squashed from ${currentBranch}\"`,\n '5. If a PR exists: open a NEW PR for the -v2 branch and close the old one.',\n ];\n }\n\n /**\n * Synchronously stamp a clean \"up to date with main\" status — call right after a successful merge.\n *\n * This is the one writer that takes NO lock, and it knows about exactly one branch. It therefore\n * goes through writeMainSyncStatus, which MERGES its entry into the existing map; writing a whole\n * document from here would wipe every other worktree's entry.\n */\n async stampCleanMainSyncStatus(repoRoot: string): Promise<void> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const branch = this.gitBranch(repoRoot);\n const originMain = await this.git(repoRoot, ['rev-parse', 'origin/main']);\n const featureHead = await this.git(repoRoot, ['rev-parse', 'HEAD']);\n if (!originMain.ok || !featureHead.ok) return;\n const status = new MainSyncStatus(\n branch, false, '', true, originMain.out, originMain.out, featureHead.out, false, [], new Date().toISOString(),\n );\n status.localMain = await this.localMainHash(repoRoot);\n status.commitsBehind = 0;\n this.writeMainSyncStatus(repoRoot, status);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n /**\n * Refresh `origin/main` WITHOUT writing `.git/FETCH_HEAD`.\n *\n * WHY the flag is not optional: this runs in a DETACHED background process while the agent is\n * running its own foreground `git fetch` / `git pull` in the very same repo. `.git/FETCH_HEAD` is\n * a single file that git takes no lock on, so two overlapping fetches interleave their writes and\n * can leave the SAME `for-merge` line twice. `git pull` then reads two for-merge entries and dies\n * with `fatal: Cannot fast-forward to multiple branches` — wedging the exact command the\n * read-stale-guard tells the agent to run. `--no-write-fetch-head` (git >= 2.29) still updates the\n * remote-tracking ref, which is all anything downstream reads (`origin/main`, merge-base), so\n * nothing here needs FETCH_HEAD written at all.\n *\n * Older git rejects the flag; only then do we retry the plain form, accepting the old behaviour\n * rather than losing the refresh entirely on a pre-2020 git.\n */\n private async fetchOriginMain(repoRoot: string): Promise<void> {\n const safe = await this.commands.networkGit(['fetch', '--no-write-fetch-head', 'origin', 'main'], repoRoot);\n if (safe.ok) return;\n if (!this.isUnknownGitOption(safe.stderr)) return; // a real failure (offline, auth) — not our business\n await this.commands.networkGit(['fetch', 'origin', 'main'], repoRoot);\n }\n\n // Did git reject the flag itself (too old), as opposed to failing the network refresh?\n private isUnknownGitOption(stderr: string): boolean {\n const text = stderr.toLowerCase();\n return text.includes('unknown option') || text.includes('unknown switch') || text.includes('unrecognized option');\n }\n\n // Local git, bounded (CommandTimeouts), stdout trimmed; ok=false on a non-zero exit, a kill or a failed start.\n private async git(repoRoot: string, args: string[]): Promise<CapturedOutput> {\n const result = await this.commands.localGit(args, repoRoot);\n return new CapturedOutput(result.ok, result.ok ? result.stdout.trim() : '');\n }\n\n // The actual checked-out branch in repoRoot — cwd-correct so the cache's `branch` label matches.\n private gitBranch(repoRoot: string): string {\n return gitCheckout.branch(repoRoot) ?? '';\n }\n\n // The LOCAL main hash. `refs/heads/main` (not the bare name) so it can never resolve to a remote\n // ref or a tag. '' when main does not exist locally — which the guard treats as fail-open.\n private async localMainHash(repoRoot: string): Promise<string> {\n const result = await this.git(repoRoot, ['rev-parse', 'refs/heads/main']);\n return result.ok ? result.out : '';\n }\n\n // Slow-path only: the detached refresher calls this after fetching. Guards never count commits.\n private async commitDistance(repoRoot: string, localMain: string, originMain: string): Promise<number | null> {\n if (localMain === '' || originMain === '') return null;\n const result = await this.git(repoRoot, ['rev-list', '--count', `${localMain}..${originMain}`]);\n if (!result.ok || !/^\\d+$/.test(result.out)) return null;\n return Number(result.out);\n }\n\n private async changedFiles(repoRoot: string, base: string, head: string): Promise<string[]> {\n const result = await this.git(repoRoot, ['diff', '--name-only', base, head]);\n if (!result.ok || result.out === '') return [];\n return result.out.split('\\n').map((line: string): string => line.trim()).filter((line: string): boolean => line.length > 0);\n }\n\n // Every file this feature branch has touched since the fork point — committed AND still in the\n // working tree (staged / unstaged / untracked), so a conflict is visible WHILE editing.\n private async featureChangedFiles(repoRoot: string, forkPoint: string): Promise<string[]> {\n const out = new Set<string>();\n const add = async (args: string[]): Promise<void> => {\n const r = await this.git(repoRoot, args);\n if (!r.ok || r.out === '') return;\n for (const line of r.out.split('\\n')) {\n const f = line.trim();\n if (f.length > 0) out.add(f);\n }\n };\n await add(['diff', '--name-only', forkPoint, 'HEAD']); // committed since the fork point\n await add(['diff', '--name-only', 'HEAD']); // unstaged working-tree edits\n await add(['diff', '--name-only', '--cached', 'HEAD']); // staged edits\n await add(['ls-files', '--others', '--exclude-standard']); // untracked new files (respects .gitignore)\n return [...out];\n }\n\n /**\n * ONE repo-wide `gh pr list` giving every branch's merged/open PR — replaces the two per-branch\n * `--head <branch>` queries. `--limit 200` bounds it; a branch whose only PR is older than that\n * falls out of the index and reads as \"no PR\", which is the fail-OPEN direction (no merged PR =\n * no block), the same way a missing `gh` or an offline machine already degraded.\n */\n private async loadPullRequestIndex(repoRoot: string): Promise<PullRequestIndex> {\n const gh = await this.commands.gh([\n 'pr', 'list', '--state', 'all', '--json', 'number,headRefName,state', '--limit', '200',\n ], repoRoot);\n const result = new CapturedOutput(gh.ok, gh.ok ? gh.stdout.trim() : '');\n // `gh` could not be run, or exited non-zero (missing, unauthenticated, rate-limited, offline).\n // An EMPTY index is the same fail-open answer as before — but it is now labelled UNREACHABLE, so\n // the guards can report an abstention rather than an approval. Empty STDOUT on a zero exit is a\n // real answer (\"this repo has no PRs\"), so only the failure path clears the flag.\n if (!result.ok) return new PullRequestIndex({}, {}, false);\n if (result.out === '') return new PullRequestIndex({}, {}, true);\n return this.store.indexPullRequests(result.out);\n }\n\n /**\n * A `main` entry synthesised WITHOUT a worktree standing on main. Carries only the two hashes\n * read-stale-guard / stale-main-bash-guard actually compare (originMain vs the live tree's\n * ancestry) — never merged, never conflicting, so it can only ever produce the stale-main verdict.\n */\n private async mainOnlyStatus(repoRoot: string): Promise<MainSyncStatus> {\n const originMain = await this.git(repoRoot, ['rev-parse', 'origin/main']);\n const localMain = await this.localMainHash(repoRoot);\n const status = new MainSyncStatus(\n 'main', false, '', true, localMain === '' ? null : localMain,\n originMain.ok ? originMain.out : '', localMain, false, [], new Date().toISOString(),\n );\n status.localMain = localMain;\n status.commitsBehind = await this.commitDistance(repoRoot, localMain, originMain.ok ? originMain.out : '');\n return status;\n }\n\n // A benign status that never blocks — used when origin/main can't be resolved.\n private benignStatus(branch: string, featureHead: string): MainSyncStatus {\n return new MainSyncStatus(branch, false, '', true, null, '', featureHead, false, [], new Date().toISOString());\n }\n}\n\n// Temporary migration delegators to MainSyncStatusService — removed once consumers inject it.\nconst mainSyncSvc = new MainSyncStatusService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function mainSyncStatusPath(repoRoot: string): string { return mainSyncSvc.mainSyncStatusPath(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncStatus(repoRoot: string, branch: string): MainSyncStatus | null { return mainSyncSvc.readMainSyncStatus(repoRoot, branch); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncStatusFile(repoRoot: string): MainSyncStatusFile | null { return mainSyncSvc.readMainSyncStatusFile(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void { mainSyncSvc.writeMainSyncStatus(repoRoot, status); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncStatusFile(repoRoot: string, file: MainSyncStatusFile): void { mainSyncSvc.writeMainSyncStatusFile(repoRoot, file); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function squashRecoverySteps(currentBranch: string): string[] { return mainSyncSvc.squashRecoverySteps(currentBranch); }\n\n"]}
@@ -10,8 +10,8 @@ export declare class MergedBranchesService {
10
10
  private readonly dotDir;
11
11
  private readonly atomicFile;
12
12
  private readonly locks;
13
- private readonly timeouts;
14
- constructor(worktrees?: WorktreeService, dotDir?: DotWebpieces, atomicFile?: AtomicFile, locks?: WorktreeLockVerdicts, timeouts?: CommandTimeouts);
13
+ private readonly commands;
14
+ constructor(worktrees?: WorktreeService, dotDir?: DotWebpieces, atomicFile?: AtomicFile, locks?: WorktreeLockVerdicts, commands?: CommandTimeouts);
15
15
  /**
16
16
  * SHARED scope — one per repo, in the primary clone, NOT one per worktree. This file's contents
17
17
  * describe every branch AND every worktree in the repo, so a per-worktree copy was a repo-wide fact
@@ -62,7 +62,7 @@ export declare class MergedBranchesService {
62
62
  * The SLOW path, run only inside the detached refresher. ONE bulk `gh` call, then a purely local
63
63
  * classification. Never run on the hook's blocking path.
64
64
  */
65
- computeMergedBranches(repoRoot: string, ignoreStaleAgentLocks: boolean): MergedBranchesCache;
65
+ computeMergedBranches(repoRoot: string, ignoreStaleAgentLocks: boolean): Promise<MergedBranchesCache>;
66
66
  /**
67
67
  * Verdicts for the worktree budget. The main worktree is excluded outright — it is the primary
68
68
  * clone and is not a thing you can remove.
@@ -138,5 +138,6 @@ export declare class MergedBranchesService {
138
138
  private fetchPrStates;
139
139
  private reviveList;
140
140
  private reviveWorktrees;
141
- private capture;
141
+ private git;
142
+ private gh;
142
143
  }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MergedBranchesService = exports.ADJUDICATED_CLASSIFICATIONS = exports.CLASSIFICATION_DETACHED = exports.CLASSIFICATION_CURRENT = exports.CLASSIFICATION_LOCKED = exports.CLASSIFICATION_PRUNABLE = exports.CLASSIFICATION_IN_USE = exports.CLASSIFICATION_NEVER_PROPOSED = exports.CLASSIFICATION_CONTENT_IN_MAIN = exports.CLASSIFICATION_SUPERSEDED = exports.CLASSIFICATION_NO_COMMITS = exports.CLASSIFICATION_BACKUP_OF_LIVE = exports.CLASSIFICATION_BACKUP_OF_MERGED = exports.CLASSIFICATION_MERGED_PR = exports.CACHE_STALE_AFTER_MS = exports.CacheFreshness = exports.MergedBranchesCache = exports.DeletableWorktree = exports.DeletableBranch = exports.MergedBranch = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const child_process_1 = require("child_process");
6
5
  const fs = tslib_1.__importStar(require("fs"));
7
6
  const inversify_1 = require("inversify");
8
7
  const tooling_common_1 = require("@webpieces/tooling-common");
@@ -95,20 +94,29 @@ class Verdict {
95
94
  this.entry = entry;
96
95
  }
97
96
  }
97
+ // Internal: one bounded command's verdict and trimmed stdout. Data only.
98
+ class CapturedOutput {
99
+ ok;
100
+ out;
101
+ constructor(ok, out) {
102
+ this.ok = ok;
103
+ this.out = out;
104
+ }
105
+ }
98
106
  let MergedBranchesService = class MergedBranchesService {
99
107
  worktrees;
100
108
  dotDir;
101
109
  atomicFile;
102
110
  locks;
103
- timeouts;
111
+ commands;
104
112
  // Defaulted so the non-DI call sites (`new MergedBranchesService()` in the guard and the detached
105
113
  // refresher) keep working, while inversify still injects the singleton when resolved from a container.
106
- constructor(worktrees = new worktrees_1.WorktreeService(), dotDir = tooling_common_2.dotWebpieces, atomicFile = new tooling_common_1.AtomicFile(), locks = new worktree_lock_verdicts_1.WorktreeLockVerdicts(), timeouts = new command_timeouts_1.CommandTimeouts()) {
114
+ constructor(worktrees = new worktrees_1.WorktreeService(), dotDir = tooling_common_2.dotWebpieces, atomicFile = new tooling_common_1.AtomicFile(), locks = new worktree_lock_verdicts_1.WorktreeLockVerdicts(), commands = new command_timeouts_1.CommandTimeouts()) {
107
115
  this.worktrees = worktrees;
108
116
  this.dotDir = dotDir;
109
117
  this.atomicFile = atomicFile;
110
118
  this.locks = locks;
111
- this.timeouts = timeouts;
119
+ this.commands = commands;
112
120
  }
113
121
  /**
114
122
  * SHARED scope — one per repo, in the primary clone, NOT one per worktree. This file's contents
@@ -189,10 +197,12 @@ let MergedBranchesService = class MergedBranchesService {
189
197
  * CREATION, so the guard would block the cleanup it just demanded.
190
198
  */
191
199
  localBranches(repoRoot) {
192
- const result = this.capture(repoRoot, 'git', ['for-each-ref', '--format=%(refname:short)', 'refs/heads/']);
193
- if (!result.ok || result.out === '')
200
+ // SYNCHRONOUS: the branch-creation guard counts parked branches on its blocking path.
201
+ const result = this.commands.localGitNow(['for-each-ref', '--format=%(refname:short)', 'refs/heads/'], repoRoot);
202
+ const out = result.stdout.trim();
203
+ if (!result.ok || out === '')
194
204
  return [];
195
- return result.out
205
+ return out
196
206
  .split('\n')
197
207
  .map((line) => line.trim())
198
208
  .filter((line) => line.length > 0 && line !== 'main');
@@ -201,8 +211,8 @@ let MergedBranchesService = class MergedBranchesService {
201
211
  * The SLOW path, run only inside the detached refresher. ONE bulk `gh` call, then a purely local
202
212
  * classification. Never run on the hook's blocking path.
203
213
  */
204
- computeMergedBranches(repoRoot, ignoreStaleAgentLocks) {
205
- const merged = this.fetchMergedPrs(repoRoot);
214
+ async computeMergedBranches(repoRoot, ignoreStaleAgentLocks) {
215
+ const merged = await this.fetchMergedPrs(repoRoot);
206
216
  const byBranch = new Map();
207
217
  for (const entry of merged)
208
218
  byBranch.set(entry.branch, entry.pr);
@@ -211,7 +221,7 @@ let MergedBranchesService = class MergedBranchesService {
211
221
  if (entry.pr > latestMergedPr)
212
222
  latestMergedPr = entry.pr;
213
223
  }
214
- const prs = new PrLookup(byBranch, this.fetchPrStates(repoRoot), latestMergedPr);
224
+ const prs = new PrLookup(byBranch, await this.fetchPrStates(repoRoot), latestMergedPr);
215
225
  const trees = this.worktrees.listWorktrees(repoRoot);
216
226
  const holder = new Map();
217
227
  for (const tree of trees) {
@@ -221,7 +231,7 @@ let MergedBranchesService = class MergedBranchesService {
221
231
  const deletable = [];
222
232
  const keep = [];
223
233
  for (const branch of this.localBranches(repoRoot)) {
224
- const verdict = this.classify(repoRoot, branch, prs);
234
+ const verdict = await this.classify(repoRoot, branch, prs);
225
235
  // A branch checked out in ANY worktree (including the branch we are standing on right here)
226
236
  // cannot be deleted — git refuses, and since the reap is ONE `git branch -D a b c`, a single
227
237
  // such branch would fail the entire command and strand the branches that would have deleted
@@ -237,7 +247,7 @@ let MergedBranchesService = class MergedBranchesService {
237
247
  else
238
248
  keep.push(verdict.entry);
239
249
  }
240
- const worktrees = this.classifyWorktrees(repoRoot, trees, prs, ignoreStaleAgentLocks);
250
+ const worktrees = await this.classifyWorktrees(repoRoot, trees, prs, ignoreStaleAgentLocks);
241
251
  return new merged_branch_verdicts_1.MergedBranchesCache(new Date().toISOString(), deletable, keep, worktrees);
242
252
  }
243
253
  /**
@@ -259,7 +269,7 @@ let MergedBranchesService = class MergedBranchesService {
259
269
  * and that cannot be weighed until it is known. Two extra local git calls per locked worktree, on
260
270
  * a path that only ever runs in the detached refresher and in wp-cleanup itself.
261
271
  */
262
- classifyWorktrees(repoRoot, trees, prs, ignoreStaleAgentLocks) {
272
+ async classifyWorktrees(repoRoot, trees, prs, ignoreStaleAgentLocks) {
263
273
  const out = [];
264
274
  for (const tree of trees) {
265
275
  if (tree.isMain)
@@ -268,10 +278,10 @@ let MergedBranchesService = class MergedBranchesService {
268
278
  out.push(new merged_branch_verdicts_1.DeletableWorktree(tree.path, tree.branch, 'its directory is gone — `git worktree prune` clears it', 0, true, merged_branch_verdicts_1.CLASSIFICATION_PRUNABLE));
269
279
  continue;
270
280
  }
271
- const verdict = tree.branch === '' ? null : this.classify(repoRoot, tree.branch, prs);
281
+ const verdict = tree.branch === '' ? null : await this.classify(repoRoot, tree.branch, prs);
272
282
  // An OVERRIDDEN lock leaves the worktree judged on its branch below, exactly like an
273
283
  // unlocked one — with `unlockBeforeRemove` set by annotate() so the reap clears the lock.
274
- const decision = tree.locked ? this.lockDecision(tree, verdict, ignoreStaleAgentLocks) : null;
284
+ const decision = tree.locked ? await this.lockDecision(tree, verdict, ignoreStaleAgentLocks) : null;
275
285
  if (decision !== null && decision.spare) {
276
286
  out.push(new merged_branch_verdicts_1.DeletableWorktree(tree.path, tree.branch, decision.reason, 0, false, merged_branch_verdicts_1.CLASSIFICATION_LOCKED));
277
287
  continue;
@@ -293,8 +303,8 @@ let MergedBranchesService = class MergedBranchesService {
293
303
  }
294
304
  // The lock verdict for one worktree. The evidence gathering (and what it deliberately declines to
295
305
  // gather) lives in WorktreeLockVerdicts — see `gather` there.
296
- lockDecision(tree, verdict, ignoreStaleAgentLocks) {
297
- return this.locks.decide(this.locks.gather(tree, verdict !== null && verdict.deletable, ignoreStaleAgentLocks));
306
+ async lockDecision(tree, verdict, ignoreStaleAgentLocks) {
307
+ return this.locks.decide(await this.locks.gather(tree, verdict !== null && verdict.deletable, ignoreStaleAgentLocks));
298
308
  }
299
309
  /**
300
310
  * Verdict for one branch: its own merged PR, else the base it was backed up from, else empty.
@@ -304,9 +314,9 @@ let MergedBranchesService = class MergedBranchesService {
304
314
  * rather than just counted. This runs only in the detached refresher, so the two extra local git
305
315
  * calls per branch cost the blocking hook path nothing.
306
316
  */
307
- classify(repoRoot, branch, prs) {
308
- const sha = this.tipSha(repoRoot, branch);
309
- const commits = this.commitsAheadOfMain(repoRoot, branch);
317
+ async classify(repoRoot, branch, prs) {
318
+ const sha = await this.tipSha(repoRoot, branch);
319
+ const commits = await this.commitsAheadOfMain(repoRoot, branch);
310
320
  const known = prs.state.get(branch);
311
321
  const prState = known ? known.state : '';
312
322
  const own = prs.merged.get(branch);
@@ -331,7 +341,7 @@ let MergedBranchesService = class MergedBranchesService {
331
341
  // every run, so the branch cap fills with copies nobody can be bothered to adjudicate.
332
342
  // The proof here is as strong as a merged PR (a live sibling ref holds the content), and
333
343
  // the reap archives to an `archive/<date>/<branch>` tag and logs a `recover=` first.
334
- if (this.branchExists(repoRoot, base)) {
344
+ if (await this.branchExists(repoRoot, base)) {
335
345
  const basePrState = prs.state.get(base);
336
346
  const liveness = basePrState
337
347
  ? `PR #${String(basePrState.number)} ${basePrState.state}`
@@ -350,7 +360,7 @@ let MergedBranchesService = class MergedBranchesService {
350
360
  + 'commit. wp-cleanup reaps this by default; a worktree holding it is spared when it has '
351
361
  + 'uncommitted files or a live lock, i.e. when somebody is working in it', 0, sha, 0, prState, merged_branch_verdicts_1.CLASSIFICATION_NO_COMMITS));
352
362
  }
353
- return new Verdict(false, this.classifySpared(repoRoot, branch, prs, sha, commits, prState));
363
+ return new Verdict(false, await this.classifySpared(repoRoot, branch, prs, sha, commits, prState));
354
364
  }
355
365
  /**
356
366
  * The three genuinely different reasons a branch survives the deletable proofs, ordered by how
@@ -360,7 +370,7 @@ let MergedBranchesService = class MergedBranchesService {
360
370
  * that changed is that wp-cleanup can now ask a question whose answer is knowable.
361
371
  */
362
372
  // eslint-disable-next-line @typescript-eslint/max-params
363
- classifySpared(repoRoot, branch, prs, sha, commits, prState) {
373
+ async classifySpared(repoRoot, branch, prs, sha, commits, prState) {
364
374
  const known = prs.state.get(branch);
365
375
  // A PR that CLOSED without merging, in a repo that has merged later PRs, is near-certainly the
366
376
  // abandoned first attempt at work that landed under a different number.
@@ -370,7 +380,7 @@ let MergedBranchesService = class MergedBranchesService {
370
380
  }
371
381
  // `git cherry` compares by PATCH-ID, so it survives cherry-picks and rebases. It does NOT
372
382
  // survive a squash — which is exactly why this is a spared verdict and not a deletable proof.
373
- if (this.contentAlreadyInMain(repoRoot, branch)) {
383
+ if (await this.contentAlreadyInMain(repoRoot, branch)) {
374
384
  return new merged_branch_verdicts_1.DeletableBranch(branch, `all ${String(commits)} commit(s) already have an equivalent in origin/main ` +
375
385
  `(git cherry) — content is not unique`, known ? known.number : 0, sha, commits, prState, merged_branch_verdicts_1.CLASSIFICATION_CONTENT_IN_MAIN);
376
386
  }
@@ -388,8 +398,8 @@ let MergedBranchesService = class MergedBranchesService {
388
398
  * cherry-pick or rebase-merge) even though the SHAs differ. Any failure returns false — "cannot
389
399
  * prove the content is in main" must never read as "safe".
390
400
  */
391
- contentAlreadyInMain(repoRoot, branch) {
392
- const result = this.capture(repoRoot, 'git', ['cherry', 'origin/main', branch]);
401
+ async contentAlreadyInMain(repoRoot, branch) {
402
+ const result = await this.git(repoRoot, ['cherry', 'origin/main', branch]);
393
403
  if (!result.ok || result.out === '')
394
404
  return false;
395
405
  const lines = result.out.split('\n')
@@ -404,20 +414,20 @@ let MergedBranchesService = class MergedBranchesService {
404
414
  * base branch being present is that proof. Verifies it is a BRANCH (`refs/heads/`), not just any
405
415
  * resolvable rev, so a tag or a stray SHA sharing the name cannot stand in for the base.
406
416
  */
407
- branchExists(repoRoot, branch) {
408
- return this.capture(repoRoot, 'git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`]).ok;
417
+ async branchExists(repoRoot, branch) {
418
+ return (await this.git(repoRoot, ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`])).ok;
409
419
  }
410
420
  // Short tip SHA — the value that makes any delete of this branch reversible.
411
- tipSha(repoRoot, branch) {
412
- const result = this.capture(repoRoot, 'git', ['rev-parse', '--short', branch]);
421
+ async tipSha(repoRoot, branch) {
422
+ const result = await this.git(repoRoot, ['rev-parse', '--short', branch]);
413
423
  return result.ok ? result.out : '';
414
424
  }
415
425
  /**
416
426
  * Commits on `branch` that are not on origin/main. Returns -1 ("assume it has work") whenever the
417
427
  * count cannot be established — an unresolvable origin/main must never read as "empty branch".
418
428
  */
419
- commitsAheadOfMain(repoRoot, branch) {
420
- const result = this.capture(repoRoot, 'git', ['rev-list', '--count', `origin/main..${branch}`]);
429
+ async commitsAheadOfMain(repoRoot, branch) {
430
+ const result = await this.git(repoRoot, ['rev-list', '--count', `origin/main..${branch}`]);
421
431
  if (!result.ok)
422
432
  return -1;
423
433
  const count = Number(result.out);
@@ -428,8 +438,8 @@ let MergedBranchesService = class MergedBranchesService {
428
438
  * lookups. Fails SOFT: if `gh` is missing, unauthenticated, or offline we return [], which makes
429
439
  * every branch a `keep`. The guard then deletes nothing rather than guessing.
430
440
  */
431
- fetchMergedPrs(repoRoot) {
432
- const result = this.capture(repoRoot, 'gh', [
441
+ async fetchMergedPrs(repoRoot) {
442
+ const result = await this.gh(repoRoot, [
433
443
  'pr', 'list',
434
444
  '--state', 'merged',
435
445
  '--limit', String(MERGED_PR_LOOKUP_LIMIT),
@@ -462,9 +472,9 @@ let MergedBranchesService = class MergedBranchesService {
462
472
  * flood of open/closed PRs push older MERGED ones out of the limit and silently stop reaping real
463
473
  * dead branches. Fails soft to an empty map: the verdicts do not depend on it.
464
474
  */
465
- fetchPrStates(repoRoot) {
475
+ async fetchPrStates(repoRoot) {
466
476
  const out = new Map();
467
- const result = this.capture(repoRoot, 'gh', [
477
+ const result = await this.gh(repoRoot, [
468
478
  'pr', 'list',
469
479
  '--state', 'all',
470
480
  '--limit', String(MERGED_PR_LOOKUP_LIMIT),
@@ -505,12 +515,15 @@ let MergedBranchesService = class MergedBranchesService {
505
515
  return verdict;
506
516
  });
507
517
  }
508
- // Run a command capturing trimmed stdout; ok=false on spawn failure or non-zero exit.
509
- capture(repoRoot, cmd, args) {
510
- const result = (0, child_process_1.spawnSync)(cmd, args, { cwd: repoRoot, encoding: 'utf8', timeout: this.timeouts.forCommand(cmd) });
511
- if (result.status !== 0 || typeof result.stdout !== 'string')
512
- return { ok: false, out: '' };
513
- return { ok: true, out: result.stdout.trim() };
518
+ // Local git, bounded (CommandTimeouts), stdout trimmed; ok=false on a non-zero exit, a kill or a failed start.
519
+ async git(repoRoot, args) {
520
+ const result = await this.commands.localGit(args, repoRoot);
521
+ return new CapturedOutput(result.ok, result.ok ? result.stdout.trim() : '');
522
+ }
523
+ // `gh`, bounded by the network ceiling, stdout trimmed.
524
+ async gh(repoRoot, args) {
525
+ const result = await this.commands.gh(args, repoRoot);
526
+ return new CapturedOutput(result.ok, result.ok ? result.stdout.trim() : '');
514
527
  }
515
528
  };
516
529
  exports.MergedBranchesService = MergedBranchesService;