@webpieces/pr-gate 0.4.592 → 0.4.594

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":"finish-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/finish-upsert-pr-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync, spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate, prDirFor, reviewJsonPath, ReviewJson, RequiredChecklist, ChecklistVerdict,\n writeTemplate, RepoRootFinder, ReviewJsonService,\n GateTokenService, SubagentProvenanceService, PROVENANCE_OK, PROVENANCE_MISSING, PROVENANCE_SKIPPED,\n ProvenanceResult, ReviewerEvidence, EvidenceRequest, PrGateConfig,\n ReviewProvenanceService, ProvenanceWriteRequest, ReviewerTranscript, ReviewerPaths, OfferedContext,\n ReviewerInstructionsService,\n InformAiError, toError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { ChecklistScan, ChecklistScanOptions, ChecklistScanner } from '../workflow/checklist-scanner';\nimport { ReviewerVerdictGate } from '../workflow/reviewer-verdict-gate';\nimport { GitExec } from '../workflow/git-exec';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { MergeState } from '../workflow/merge-state';\nimport { ReviewStageReceiptService } from '../workflow/review-stage-receipt';\nimport { PrMerger, MergeIntent, MergeOutcome, MERGE_RESULT_FAILED } from '../workflow/pr-merger';\nimport { FinishBanner, FinishBannerInput } from '../workflow/finish-banner';\nimport { MergeBodyFiler, MergeBodyRequest } from '../workflow/merge-body-filer';\nimport { GatedPrPublisher } from '../workflow/gated-pr-publisher';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport {\n Dashboard, DashboardInput, ChecklistRow, CHECKLIST_COMMENT_MARKER,\n} from '../../dashboard/dashboard';\nimport { ChecklistCommentRow } from '../../dashboard/checklist-comment-row';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * The provenance outcome: whether each reviewer was VERIFIED to have run (the integrity check, which\n * blocks) plus what each one actually read (the quality signal, which is published). Data-only.\n */\nclass ProvenanceReport {\n verified: boolean;\n evidence: ReviewerEvidence[];\n\n constructor(verified: boolean, evidence: ReviewerEvidence[]) {\n this.verified = verified;\n this.evidence = evidence;\n }\n}\n\n// A resolved PR's number + web URL. Both '' when the PR can't be resolved (e.g. create failed).\nclass PrRef {\n number: string;\n url: string;\n\n constructor(number: string, url: string) {\n this.number = number;\n this.url = url;\n }\n}\n\n// The outcome of the whole upsert: the PR number + web URL ('' each when unresolved) plus what actually\n// happened to the merge, so the final summary reports the REAL result rather than assuming success. The\n// URL is carried so the closing block can hand the AI a clickable link to relay to the user verbatim.\nclass UpsertResult {\n prNumber: string;\n prUrl: string;\n merge: MergeOutcome;\n\n constructor(prNumber: string, prUrl: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.prUrl = prUrl;\n this.merge = merge;\n }\n}\n\n// STAGE ③ — FINISH of the AI-first PR flow, and the ONLY command that posts PRs. Runs after\n// `wp-review-upsert-pr` verified the branch and the AI spawned the reviewers + wrote review.json.\n//\n// In order: (1) REFUSE on an unvalidated 3-point merge and REQUIRE stage ②'s receipt; (2) REQUIRE\n// review.json + every reviewer's verdict + provenance; (3) run the build gate UNLESS the receipt already\n// covers this exact HEAD; (4) render the dashboard; (5) create/update the PR via `gh`.\n//\n// It no longer FINALIZES a merge — stage ② does. Two commands owning conflict-resolution validation is two\n// implementations that drift, and finalizing here came too late to help anyway: the reviewers had already\n// reviewed the pre-merge tree by then.\n@injectable(bindingScopeValues.Singleton)\nexport class FinishUpsertPrCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly branchNaming: BranchNaming,\n private readonly gitExec: GitExec,\n private readonly buildAffected: BuildAffected,\n private readonly mergeState: MergeState,\n private readonly prMerger: PrMerger,\n private readonly publisher: GatedPrPublisher,\n private readonly dashboard: Dashboard,\n private readonly checklistScanner: ChecklistScanner,\n private readonly verdictGate: ReviewerVerdictGate,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n private readonly provenance: SubagentProvenanceService,\n private readonly provenanceRecord: ReviewProvenanceService,\n private readonly reviewerInstructions: ReviewerInstructionsService,\n // NOTE: ChecklistInstructionsService is deliberately NOT injected here any more. Its \"You MUST run\n // these N reviewer subagent(s)\" block is now rendered by ReviewerVerdictGate and ONLY for checklists\n // that genuinely never ran, so no other code path in this command can print it at a refusal.\n private readonly receipts: ReviewStageReceiptService,\n private readonly banner: FinishBanner,\n // The MACHINE-GLOBAL home for the one artifact whose scope is bigger than this tree: the gated\n // squash-commit body, which `wp-land-pr` must find from any tree (see MergeBodyFiler).\n private readonly mergeBodyFiler: MergeBodyFiler,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n // 1. REQUIRE stage ② — see assertStageTwoRan. Returns true when its receipt covers THIS commit,\n // which is what lets the build gate below be skipped rather than re-run for a foregone answer.\n const buildAlreadyGreen = this.assertStageTwoRan(repoRoot);\n\n // 2. REQUIRE the AI-authored review.json (throws InformAiError with the schema if missing/invalid).\n // Compute the consumer checklists this diff triggered FIRST so an unacknowledged BLOCK throws\n // here — BEFORE any `gh pr create` — matching the guarantee buildCommand already provides.\n // The SAME scan wp-review-upsert-pr runs, with filterAlreadyReviewed:true so `outstanding` is exactly what\n // still owes a verdict (Z of N of X). Sharing the computation is the point: the command that REPORTS\n // and the command that GATES must not be able to disagree about what is owed. review-<id>.json files\n // persist locally, so a re-run re-validates the EXISTING verdicts against the (possibly changed)\n // applicable set for free — an unchanged checklist needs no re-review, a newly-applicable one refuses\n // until its file is written.\n const featureName = this.aiBranchName.getFeatureName();\n const scan = this.checklistScanner.scan(repoRoot, loadAndValidate(repoRoot).prGate.checklists, new ChecklistScanOptions(true, 'stage3-finish'));\n const required = scan.applicable;\n // The applicable checklists that are supposed to HAVE a verdict — everything except the optional ones\n // nobody ran. Used for provenance and for the dashboard rows, both of which ask \"who reviewed this?\"\n // and would otherwise demand an answer from a review the human declined: provenance would refuse the\n // PR because a subagent that was never meant to run cannot be proven to have run, and the dashboard\n // would render a MISSING verdict for it.\n const verdicted = this.verdictedOf(scan);\n // FAIL FAST on an unclear checklist, BEFORE review.json is parsed and before the build gate runs. A\n // missing reviewer is not a review.json defect (folding it in made the AI fix the wrong thing), and\n // nobody should wait on a build to be told a reviewer never ran. ReviewerVerdictGate owns the\n // distinction between unreadable / REFUSED / never-ran, and retires the red verdicts it acts on.\n this.verdictGate.assertEveryReviewerRan(scan);\n const review = this.reviewJsonService.loadReviewJson(reviewJsonPath(repoRoot, featureName), required);\n\n // 2c. For any BLOCK checklist that names a reviewer `subagent`, VERIFY (from the harness's own\n // artifacts) that such a subagent actually ran on this branch — the coding agent may not\n // self-certify. Absent CLAUDE_CODE_SESSION_ID this skips with a warning (CI / plain terminal).\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n const provenance = this.enforceProvenance(verdicted, currentBranch, repoRoot, loadAndValidate(repoRoot).prGate);\n\n // 2b. The build gate validates the WORKING TREE but we push HEAD — so they MUST be identical.\n this.gitExec.assertCleanTree(repoRoot);\n\n // 3. Build gate, then post the gated body, then push (that ORDER — see GatedPrPublisher).\n this.runOrSkipBuildGate(repoRoot, buildAlreadyGreen);\n const base = this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n\n process.stdout.write('\\n' + SEP + '📋 Dashboard + PR\\n' + SEP + '\\n');\n const title = this.prTitleFrom(review);\n const input = this.computeDashboardInput(repoRoot, true, review, title, verdicted);\n // Append the hidden HMAC gate token bound to the LOCAL HEAD sha — computed BEFORE the push, because\n // GatedPrPublisher writes the body first so CI's `synchronize` read can never see a stale token. A\n // valid token in the PR body is proof this gated flow ran + passed on this exact commit, which CI\n // (`wp-check-pr`) recomputes. We reach here only after the build gate + every BLOCK checklist\n // passed, so minting is legitimate. Nothing about HMAC(salt, HEAD) needs the remote to have it.\n const gateSalt = loadAndValidate(repoRoot).prGate.gateSalt;\n const headSha = this.gitOut(['rev-parse', 'HEAD']);\n const body = this.dashboard.renderDashboard(input) + this.gateTokenBody(gateSalt, headSha);\n const result = this.upsertPr(repoRoot, base, body, title, input);\n // Publish each reviewer's full output as ONE combined PR comment (idempotent, opt-out-aware). Never\n // fatal — the PR is already up by now, so a comment failure only warns.\n this.postChecklistComment(repoRoot, result.prNumber, scan, review, provenance);\n this.archiveConsumedReview(repoRoot, featureName, result);\n\n // The closing recap + the clickable-link directive, BOTH derived from the real merge outcome.\n // Nothing here may hard-code success: a stranded PR under a green checkmark is how PRs got\n // abandoned (see FinishBanner).\n const bannerInput = new FinishBannerInput(result.prNumber, result.prUrl, title, base, result.merge);\n process.stdout.write(this.banner.render(bannerInput));\n process.stdout.write(this.banner.linkDirective(bannerInput));\n }\n\n // Validate + commit + finalize a 3-point merge the AI resolved, if one is in progress. Finalizing here\n // does NOT push (pushRemote=false): this command pushes exactly ONCE, from GatedPrPublisher, and only\n // after review.json + every BLOCK checklist + the build gate pass and the gated PR body is written.\n /**\n * The build gate — SKIPPED when stage ②'s receipt already covers this exact HEAD.\n *\n * The gate is authoritative per-COMMIT, not per-command: re-running it on a tree that has not moved\n * since stage ② verified it buys nothing and costs a full `nx affected`. That skip is what makes the\n * three-stage flow cost ONE build rather than two, and it is why moving the gate earlier was affordable.\n */\n private runOrSkipBuildGate(repoRoot: string, alreadyGreen: boolean): void {\n if (alreadyGreen) {\n process.stdout.write('\\n🛠️ Build gate: already green for this commit (stage ② receipt) — skipping the rebuild.\\n');\n return;\n }\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed — no PR created/updated.',\n ));\n }\n\n /**\n * The two stage-② preconditions, together: no unvalidated merge, and a receipt proving stage ② ran.\n * Returns true when that receipt covers the CURRENT HEAD, i.e. the build gate can be skipped.\n */\n private assertStageTwoRan(repoRoot: string): boolean {\n this.assertNoUnvalidatedMerge(repoRoot);\n return this.assertReviewStageRan(\n repoRoot, this.aiBranchName.getFeatureName(), this.gitOut(['rev-parse', 'HEAD']));\n }\n\n /**\n * REFUSE while a 3-point merge is still unvalidated — do not finalize it here.\n *\n * This command used to do the finalizing itself. Finalizing means validating a conflict resolution, and\n * two commands owning that means two implementations that can drift; `PrContextWriter`'s docstring\n * records this codebase already paying for exactly that once. It also came too late to matter: by the\n * time finish ran, the reviewers had already reviewed the pre-merge tree.\n *\n * So stage ② owns it, and finish only checks. Nothing is lost — the recovery is one command away, and\n * running it also builds the merged tree and re-briefs the reviewers against it, which finalizing here\n * never did.\n */\n private assertNoUnvalidatedMerge(repoRoot: string): void {\n const home = this.mergeState.mergeDirFor(repoRoot, this.aiBranchName.getFeatureName());\n const activeDir = this.mergeState.findActiveMergeRunDir(home);\n const marker = activeDir ? this.mergeState.readMergeMarker(activeDir) : null;\n if (!activeDir || !marker || marker.validated) return;\n throw new InformAiError(\n '⛔ NO PR — a 3-point merge on this branch is still unvalidated, so nothing here has been\\n' +\n 'verified: the conflict resolution is unchecked, the merged tree was never built, and any\\n' +\n 'reviewer that ran judged the PRE-merge code.\\n\\n' +\n `Conflicted file(s): ${marker.conflictedFiles.join(', ')}\\n\\n` +\n 'Resolve them (see .webpieces/instruct-ai/webpieces.mergeprocess.md), then run:\\n' +\n ' pnpm wp-review-upsert-pr\\n' +\n 'It validates the resolution, commits it, builds it, and re-briefs the reviewers.',\n );\n }\n\n /**\n * REQUIRE stage ② (`wp-review-upsert-pr`) to have run, and report whether it ran on THIS commit.\n *\n * Returns true when the receipt matches the current HEAD — the caller then skips its own build, because\n * the receipt IS the gate for that sha and rebuilding an unchanged tree is a second full `nx affected`\n * run for a foregone answer.\n *\n * Without this check a repo with NO checklists has nothing forcing stage ②: `assertEveryReviewerRan`\n * is vacuous, and review.json — the only other interlock — is a file the AI writes itself. It could\n * write it and come straight here, skipping the merge validation and the build entirely.\n */\n private assertReviewStageRan(repoRoot: string, featureName: string, headSha: string): boolean {\n const receipt = this.receipts.read(repoRoot, featureName);\n if (receipt === null) {\n throw new InformAiError(\n '⛔ NO PR — stage ② never ran on this branch. That means the 3-point merge is unvalidated,\\n' +\n 'the build gate has not run, no diff was extracted, and no reviewer was briefed.\\n\\n' +\n 'Run: pnpm wp-review-upsert-pr\\n' +\n '(then spawn the reviewers it names, write review.json, and re-run this command)',\n );\n }\n if (receipt.headSha === headSha) return true;\n // Not fatal. Re-reviewing on every follow-up commit would be intolerable, and most drift is a typo\n // fix. But it is never silent: the build re-runs here, and the PR says the reviewers saw an older tree.\n process.stderr.write(\n `\\n⚠️ HEAD moved since stage ② ran (reviewed ${receipt.headSha.slice(0, 8)}, now ${headSha.slice(0, 8)}).\\n` +\n ' The build gate will re-run, and the PR will record that reviewers judged an earlier tree.\\n' +\n ' If the change was substantive, re-run pnpm wp-review-upsert-pr and re-spawn the reviewers.\\n\\n');\n return false;\n }\n\n /**\n * Retire the review this run just used — move review.json to old-review.json beside it, stamped as\n * audit-only (see ReviewJsonService.archiveReviewJson).\n *\n * WHY it moves rather than staying put: review.json left behind is a live-looking file describing a\n * review that has already shipped, and the next `wp-review-upsert-pr` on this branch finds it sitting\n * there. A reviewer subagent that judges the PR's stated INTENT — its title, summary or risk level —\n * then reads the previous round's review and can return GREEN against a title that no longer exists,\n * with nothing in the verdict distinguishing that from a real pass. Moving it makes the only route back\n * to this command a freshly-written review.\n *\n * ONLY on a PR that actually went up. A run that died before publishing must stay re-runnable without\n * making the AI rewrite a review that was never used, and prNumber === '' is exactly that case.\n * Non-fatal: the PR is live by here, so an unwritable archive warns rather than failing the command.\n */\n private archiveConsumedReview(repoRoot: string, featureName: string, result: UpsertResult): void {\n if (result.prNumber === '') return;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: the PR is already up; a failed archive must not fail the command\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const archived = this.reviewJsonService.archiveReviewJson(reviewJsonPath(repoRoot, featureName));\n if (archived !== '') process.stdout.write(` archived this run's review.json → ${archived} (audit only) ✓\\n`);\n // Beside it, so an archived review keeps the transcript links belonging to the round that\n // produced it — a review whose provenance was overwritten by the NEXT round audits nothing.\n this.provenanceRecord.archive(prDirFor(repoRoot, featureName));\n } catch (err: unknown) {\n const error = toError(err);\n process.stderr.write(`⚠️ Could not archive review.json (non-fatal — the PR is already up): ${error.message}\\n`);\n }\n }\n\n private gitOut(args: string[]): string {\n const result = spawnSync('git', args, { encoding: 'utf8' });\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n }\n\n // The user-facing PR title: the AI-authored review.title, or — if omitted — a readable fallback\n // derived from the stable feature name (NEVER the internal `Squash merge of <branch>` commit subject).\n private prTitleFrom(review: ReviewJson): string {\n if (review.title !== '') return review.title;\n return this.aiBranchName.getFeatureName().replace(/[-/]+/g, ' ').trim();\n }\n\n // eslint-disable-next-line @typescript-eslint/max-params\n private computeDashboardInput(repoRoot: string, buildPassed: boolean, review: ReviewJson, title: string, required: readonly RequiredChecklist[]): DashboardInput {\n const config = loadAndValidate(repoRoot).prGate;\n const forkPoint = this.gitOut(['merge-base', 'origin/main', 'HEAD']);\n const featureHead = this.gitOut(['rev-parse', 'HEAD']);\n const mainHead = this.gitOut(['rev-parse', 'origin/main']);\n const range = `${forkPoint}..${featureHead}`;\n const changedFiles = this.gitOut(['diff', range, '--name-only']).split('\\n').filter((f: string): boolean => f.trim() !== '');\n const patch = this.gitOut(['diff', range]);\n\n const gateResults = this.dashboard.computeGateResults(config.gates, changedFiles);\n const disables = this.dashboard.countAddedDisables(patch);\n const rows = this.checklistRows(required, review);\n return new DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows);\n }\n\n /**\n * The applicable checklists MINUS the optional ones nobody ran.\n *\n * Not folded into `ChecklistScan` as yet another field: `applicable` (what matched) and `optionalNotRun`\n * (what was declined) are both facts about the scan, whereas this is one command's view of them, and the\n * roster comment deliberately wants the OTHER view — it lists the declined ones precisely so the PR does\n * not imply they passed.\n */\n private verdictedOf(scan: ChecklistScan): RequiredChecklist[] {\n const skipped = new Set(scan.optionalNotRun.map((r: RequiredChecklist): string => r.id));\n return scan.applicable.filter((r: RequiredChecklist): boolean => !skipped.has(r.id));\n }\n\n // Pair each matched checklist with its resolved verdict for the dashboard.\n //\n // A checklist reaching this point is always PASS, WARN or OVERRIDDEN, and the reason is ReviewerVerdictGate\n // — NOT loadReviewJson. The gate runs one line earlier and throws on every other state (FAIL, MISSING,\n // BAD_FORMAT), so loadReviewJson's own checklist validation can no longer be the thing that rejects them;\n // it re-validates the same set and finds it clean. This comment used to credit loadReviewJson, which made\n // the ordering look deliberate while the gate's generic \"no verdict yet\" message masked every refusal.\n // WARN belongs in that list: yellow SHIPS, so it is not outstanding and reaches the dashboard.\n private checklistRows(required: readonly RequiredChecklist[], review: ReviewJson): ChecklistRow[] {\n return required.map((req: RequiredChecklist): ChecklistRow => {\n const verdict = this.reviewJsonService.resolveVerdict(req, review.results);\n return new ChecklistRow(req.id, verdict.status, verdict.detail);\n });\n }\n\n /**\n * One comment row per DEFINED checklist — matched or not — pairing the roster's match evidence with the\n * resolved verdict. Built from `scan.roster` rather than from the applicable set: the skipped ones are\n * absent from `applicable` by construction, and their \"why not\" evidence (the configured globs and the\n * changed-file total) exists nowhere else without recomputing the diff a second way.\n */\n private commentRows(scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): ChecklistCommentRow[] {\n // agentType -> did it open the diff. Absent ⇒ not assessed, which prints nothing (see ChecklistCommentRow.diffRead).\n const readByAgent = new Map<string, boolean>();\n for (const e of provenance.evidence) readByAgent.set(e.agentType, e.readDiff);\n return scan.roster.entries.map((entry: TriggeredChecklist): ChecklistCommentRow => {\n const ran = entry.matchedFiles.length > 0;\n const req = new RequiredChecklist(\n entry.def.id, entry.def.subagent, entry.def.doc, entry.matchedFiles, entry.matchedPatterns,\n entry.def.required);\n // A skipped checklist has no verdict to resolve — asking for one would report it as MISSING,\n // i.e. as an unreviewed obligation, when in fact it never had one.\n const verdict = ran\n ? this.reviewJsonService.resolveVerdict(req, review.results)\n : new ChecklistVerdict(entry.def.id, '', '');\n const row = new ChecklistCommentRow(\n entry.def.subagent, verdict.status, verdict.detail, ran,\n entry.def.patterns, entry.matchedPatterns, entry.matchedFiles, scan.roster.changedFileCount);\n row.required = entry.def.required;\n const read = readByAgent.get(entry.def.subagent);\n row.diffRead = read === undefined ? '' : (read ? 'yes' : 'no');\n return row;\n });\n }\n\n // Hidden HMAC gate-token marker (with a leading blank line) to append to the PR body, or '' when the\n // repo sets no gateSalt (byte-identical body to before this feature). Bound to the pushed HEAD sha.\n private gateTokenBody(gateSalt: string, headSha: string): string {\n const marker = this.gateTokenService.gateTokenMarker(gateSalt, headSha);\n return marker === '' ? '' : `\\n\\n${marker}\\n`;\n }\n\n // Enforce that EACH matched checklist was reviewed by its OWN named subagent, as a DISTINCT run —\n // the coding agent may not self-certify, and one reviewer may not stand in for several. A verified set\n // passes silently; no session id warns but passes; any missing reviewer throws so the PR does not open.\n // eslint-disable-next-line @typescript-eslint/max-params\n private enforceProvenance(required: readonly RequiredChecklist[], branch: string, repoRoot: string, config: PrGateConfig): ProvenanceReport {\n const errors: string[] = [];\n const report = new ProvenanceReport(true, []); // no reviewers to verify ⇒ vacuously verified\n const subagents = required.map((r: RequiredChecklist): string => r.subagent.trim()).filter((s: string): boolean => s !== '');\n // verifyDistinct short-circuits to OK on an empty set, so this runs unconditionally: a repo with no\n // checklists still gets a provenance record naming the session and the main agent's own transcript.\n const result = this.provenance.verifyDistinct(subagents, branch);\n report.verified = result.status === PROVENANCE_OK;\n if (result.status === PROVENANCE_MISSING) {\n errors.push(result.detail);\n } else if (result.status === PROVENANCE_SKIPPED) {\n process.stderr.write(`⚠️ ${result.detail}\\n`);\n }\n report.evidence = this.gatherEvidence(repoRoot, required, result, branch);\n errors.push(...this.evidenceErrors(report.evidence, config));\n // BEFORE the throw below, deliberately. A refused round is the one most worth auditing, and a record\n // that only ever appeared on success could not answer what the reviewers did the time it was refused.\n this.writeProvenanceRecord(repoRoot, required, result, report.evidence);\n if (errors.length > 0) {\n throw new InformAiError(\n `${errors.length} checklist(s) require an independent reviewer subagent that did not run — fix, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` • ${e}`).join('\\n') +\n `\\n\\nSpawn the named reviewer subagent to review the checklist on THIS branch, then re-run.`,\n );\n }\n return report;\n }\n\n /**\n * What each credited reviewer actually read. Purely observational here — {@link evidenceErrors} decides\n * whether any of it blocks, and by default none of it does.\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private gatherEvidence(repoRoot: string, required: readonly RequiredChecklist[], result: ProvenanceResult, branch: string): ReviewerEvidence[] {\n const docPaths: Record<string, string> = {};\n for (const req of required) {\n if (req.subagent.trim() !== '') docPaths[req.subagent] = req.doc.trim() === '' ? '' : path.resolve(repoRoot, req.doc);\n }\n const diffDir = path.join(prDirFor(repoRoot, this.aiBranchName.getFeatureName()), 'diff');\n return this.provenance.evidenceFor(new EvidenceRequest(branch, result.agentIds, diffDir, docPaths));\n }\n\n /**\n * Write this round's audit record: `.webpieces/pr-review/<branch>/provenance.json`, linking each verdict\n * to the transcript of the subagent that produced it.\n *\n * A SEPARATE file rather than a field inside review.json / review-<id>.json, for two reasons. The\n * reviewer cannot supply this itself — a subagent's environment exposes the PARENT session id and no\n * agent id, so a self-reported transcript link would be invented — and keeping the AI-authored files\n * byte-untouched means nothing in the record can be mistaken for something a reviewer claimed about\n * itself. Every path here is derived by the tooling from the harness's own artifacts.\n *\n * Never fatal: an unwritable record is a lost audit trail, not a reason to refuse a PR.\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private writeProvenanceRecord(repoRoot: string, required: readonly RequiredChecklist[], result: ProvenanceResult, evidence: readonly ReviewerEvidence[]): void {\n const featureName = this.aiBranchName.getFeatureName();\n const prDir = prDirFor(repoRoot, featureName);\n const request = new ProvenanceWriteRequest(prDir, featureName, this.gitOut(['rev-parse', 'HEAD']), result.status);\n request.offered = new OfferedContext(\n path.join(prDir, 'diff'), this.reviewerInstructions.instructionsDirFor(repoRoot, featureName));\n request.reviewers = evidence.map((e: ReviewerEvidence): ReviewerTranscript =>\n new ReviewerTranscript(e, this.reviewerPathsFor(repoRoot, featureName, required, e.agentType)));\n const written = this.provenanceRecord.write(request);\n if (written !== '') process.stdout.write(` transcript provenance → ${written}\\n`);\n }\n\n // Where ONE reviewer's verdict, instructions and checklist doc live. Keyed by agentType, which IS the\n // checklist id: ChecklistDefinition sets `id = subagent`, so the two never diverge.\n // eslint-disable-next-line @typescript-eslint/max-params\n private reviewerPathsFor(repoRoot: string, featureName: string, required: readonly RequiredChecklist[], agentType: string): ReviewerPaths {\n const req = required.find((r: RequiredChecklist): boolean => r.subagent.trim() === agentType);\n const doc = req !== undefined && req.doc.trim() !== '' ? path.resolve(repoRoot, req.doc) : '';\n return new ReviewerPaths(\n this.reviewJsonService.checklistResultPath(reviewJsonPath(repoRoot, featureName), req?.id ?? agentType),\n this.reviewerInstructions.pathFor(repoRoot, featureName, agentType),\n doc,\n );\n }\n\n /**\n * WARN (default) or REFUSE (opt-in) on a reviewer that wrote a verdict without opening the diff.\n *\n * Default-warn because the signal is derived from undocumented Claude Code transcript internals: if the\n * format shifts, a blocking check wedges every PR in every consumer repo with no self-service recovery.\n * `requireDiffEvidence` lets a repo that has watched the warning promote it deliberately.\n */\n private evidenceErrors(evidence: readonly ReviewerEvidence[], config: PrGateConfig): string[] {\n const blind = evidence.filter((e: ReviewerEvidence): boolean => !e.readDiff);\n if (blind.length === 0) return [];\n const names = blind.map((e: ReviewerEvidence): string => e.agentType).join(', ');\n if (!config.requireDiffEvidence) {\n process.stderr.write(\n `\\n⚠️ ${blind.length} reviewer(s) wrote a verdict with no record of opening the extracted diff: ${names}\\n` +\n ' Published on the PR as a note. Not blocking — set pr-gate.requireDiffEvidence:true to make it one.\\n');\n return [];\n }\n return [`these reviewers wrote a verdict without opening the diff (pr-gate.requireDiffEvidence is on): ${names}`];\n }\n\n /**\n * Publish the roster + every reviewer's full `output` as ONE combined PR comment, idempotently (find the\n * marker comment → PATCH it, else POST). Never fatal: by here the PR is already created/updated, so a\n * `gh` failure only warns.\n *\n * Posted on EVERY run of a repo that defines checklists — including one where nothing matched, because\n * \"all five were evaluated and none applied\" is the good news the old comment could not deliver. The\n * guard is `scan.defined.length`, deliberately NOT the number of rows that ran: a repo with no\n * checklists configured must still see no comment at all (see ChecklistNotice / renderDashboard).\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n // eslint-disable-next-line @typescript-eslint/max-params\n private postChecklistComment(repoRoot: string, prNumber: string, scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): void {\n if (prNumber === '' || scan.defined.length === 0) return;\n if (!loadAndValidate(repoRoot).prGate.checklistComments) return;\n const body = this.dashboard.renderChecklistComment(\n this.commentRows(scan, review, provenance), provenance.verified, scan.roster.baseResolved);\n const prDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const payload = path.join(prDir, 'checklist-comment.json');\n fs.writeFileSync(payload, JSON.stringify({ body }));\n const commentId = this.findChecklistCommentId(prNumber);\n const args = commentId !== ''\n ? ['api', '--method', 'PATCH', `repos/{owner}/{repo}/issues/comments/${commentId}`, '--input', payload]\n : ['api', '--method', 'POST', `repos/{owner}/{repo}/issues/${prNumber}/comments`, '--input', payload];\n const res = spawnSync('gh', args, { encoding: 'utf8' });\n if (res.status !== 0) {\n process.stderr.write('⚠️ Could not post the checklist review comment (non-fatal — the PR is already up).\\n');\n } else {\n process.stdout.write(` ${commentId !== '' ? 'updated' : 'posted'} the checklist review comment ✓\\n`);\n }\n }\n\n // The id of THIS tool's existing checklist comment on the PR (by the hidden marker), or '' if none.\n private findChecklistCommentId(prNumber: string): string {\n const res = spawnSync('gh', [\n 'api', '--paginate', `repos/{owner}/{repo}/issues/${prNumber}/comments`,\n '--jq', `.[] | select(.body | contains(\"${CHECKLIST_COMMENT_MARKER}\")) | .id`,\n ], { encoding: 'utf8' });\n if (res.status !== 0) return '';\n return (res.stdout ?? '').trim().split('\\n')[0] ?? '';\n }\n\n // The PR, the remote branch, and the local branch all share the one stable feature name. Look up /\n // create / merge against `baseBranch` (baseBranchName tolerates a leftover `…wpN` mid-transition).\n // GatedPrPublisher owns the edit/push/create half and its ORDERING — the gated body goes up before\n // the push, so CI's `synchronize` read cannot see the previous run's token.\n private upsertPr(repoRoot: string, baseBranch: string, body: string, title: string, input: DashboardInput): UpsertResult {\n const prDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const bodyFile = path.join(prDir, 'pr-body.md');\n fs.writeFileSync(bodyFile, body + '\\n');\n\n const published = this.publisher.publish(baseBranch, title, bodyFile);\n if (published.createFailed) {\n process.stderr.write('⚠️ gh pr create failed — create the PR manually with the body in:\\n ' + bodyFile + '\\n');\n return new UpsertResult('', '', new MergeOutcome(false, false,\n '⚠️ did NOT merge — there is no PR to merge (gh pr create failed above)', MERGE_RESULT_FAILED));\n }\n const num = published.number;\n\n // Set the squash-merge SUBJECT to the PR title (+ the `(#N)` GitHub normally appends, which an\n // explicit --subject would otherwise drop) and the BODY to the compact commit summary, so main's\n // history carries the PR title + risk/flags/link — NOT the internal `Squash merge of <branch>`\n // subject GitHub would inherit from the single squash commit on the branch.\n const ref = this.prRef(baseBranch);\n const subject = ref.number !== '' ? `${title} (#${ref.number})` : title;\n // MACHINE-GLOBAL, keyed by the PR's identity, so `wp-land-pr` finds it from ANY tree — see\n // MergeBodyFiler for the incident that moved it out of this worktree's pr-review/ dir.\n const bodyRequest = new MergeBodyRequest();\n bodyRequest.treeRoot = repoRoot;\n bodyRequest.branch = baseBranch;\n bodyRequest.feature = this.aiBranchName.getFeatureName();\n bodyRequest.prNumber = ref.number;\n bodyRequest.prUrl = ref.url;\n bodyRequest.body = this.dashboard.renderCommitBody(input, ref.url) + '\\n';\n const mergeBodyFile = this.mergeBodyFiler.file(bodyRequest);\n // PrMerger owns the direct-merge / auto-merge-fallback decision AND checks every gh status, so a\n // merge that did not happen is reported as such instead of being swallowed (see pr-merger.ts).\n // REQUIRED config — no default here on purpose. A missing value (an older published\n // rules-config that has no such field) reaches PrMerger as '' and is treated as \"do not merge\".\n const mergeMode = loadAndValidate(repoRoot).prGate.mergeMode ?? '';\n // `false`: this caller is POLICY-driven — only a config that really says AUTO merges here.\n const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, new MergeIntent(mergeMode, false));\n return new UpsertResult(ref.number !== '' ? ref.number : num, ref.url, outcome);\n }\n\n // The PR's number + web URL (for the merge subject `(#N)` and the commit-body back-link). Both ''\n // if it can't be resolved. Rendered via jq into one tab-separated line so no JSON parsing is needed.\n private prRef(baseBranch: string): PrRef {\n const result = spawnSync(\n 'gh', ['pr', 'view', baseBranch, '--json', 'number,url', '--jq', '\"\\\\(.number)\\\\t\\\\(.url)\"'],\n { encoding: 'utf8' },\n );\n if (result.status !== 0) {\n return new PrRef('', '');\n }\n const parts = (result.stdout ?? '').trim().split('\\t');\n return new PrRef(parts[0] ?? '', parts[1] ?? '');\n }\n}\n"]}
1
+ {"version":3,"file":"finish-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/finish-upsert-pr-command.ts"],"names":[],"mappings":";;;;AAAA,iDAAoD;AACpD,+CAAyB;AACzB,mDAA6B;AAC7B,0DAIiC;AACjC,yCAA2D;AAC3D,2EAAgE;AAChE,6DAAyD;AACzD,qEAAsG;AACtG,6EAAwE;AACxE,mDAA+C;AAC/C,+DAA6E;AAC7E,yDAAqD;AACrD,2EAA6E;AAC7E,qDAAiG;AACjG,6DAA4E;AAC5E,mEAAgF;AAChF,uEAAkE;AAClE,yEAAuF;AACvF,yEAAsF;AAEtF,yDAEmC;AACnC,2FAEoD;AACpD,iFAA4E;AAE5E,MAAM,GAAG,GAAG,0DAA0D,CAAC;AAEvE;;;GAGG;AACH,MAAM,gBAAgB;IAClB,IAAI,CAAgB;IACpB,MAAM,CAAa;IACnB,UAAU,CAAmB;IAE7B,YAAY,IAAmB,EAAE,MAAkB,EAAE,UAA4B;QAC7E,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IACjC,CAAC;CACJ;AAED;;;;GAIG;AACH,MAAM,eAAe;IACjB,QAAQ,GAAG,EAAE,CAAC;IACd,UAAU,GAAG,EAAE,CAAC;IAChB,KAAK,GAAG,EAAE,CAAC;IACX,6FAA6F;IAC7F,IAAI,GAAG,EAAE,CAAC;IACV;;;;OAIG;IACH,WAAW,GAAG,EAAE,CAAC;IACjB,KAAK,CAAiB;IAEtB,YAAY,KAAqB;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;CACJ;AAED,gGAAgG;AAChG,MAAM,KAAK;IACP,MAAM,CAAS;IACf,GAAG,CAAS;IAEZ,YAAY,MAAc,EAAE,GAAW;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACnB,CAAC;CACJ;AAED,wGAAwG;AACxG,wGAAwG;AACxG,sGAAsG;AACtG,MAAM,YAAY;IACd,QAAQ,CAAS;IACjB,KAAK,CAAS;IACd,KAAK,CAAe;IAEpB,YAAY,QAAgB,EAAE,KAAa,EAAE,KAAmB;QAC5D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;CACJ;AAED,4FAA4F;AAC5F,kGAAkG;AAClG,EAAE;AACF,kGAAkG;AAClG,yGAAyG;AACzG,uFAAuF;AACvF,EAAE;AACF,2GAA2G;AAC3G,0GAA0G;AAC1G,uCAAuC;AAEhC,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAET;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAEA;IACA;IACA;IACA;IACA;IAEA;IAIA;IACA;IAGA;IAEA;IA3BrB,YACqB,cAA8B,EAC9B,YAA0B,EAC1B,YAA0B,EAC1B,OAAgB,EAChB,aAA4B,EAC5B,UAAsB,EACtB,QAAkB,EAClB,SAA2B,EAC3B,SAAoB;IACrC,kGAAkG;IACjF,gBAA0C,EAC1C,gBAAkC,EAClC,WAAgC,EAChC,iBAAoC,EACpC,gBAAkC;IACnD,8FAA8F;IAC7E,kBAAsC;IACvD,mGAAmG;IACnG,qGAAqG;IACrG,6FAA6F;IAC5E,QAAmC,EACnC,MAAoB;IACrC,+FAA+F;IAC/F,uFAAuF;IACtE,cAA8B;IAC/C,8FAA8F;IAC7E,eAAkC;QA1BlC,mBAAc,GAAd,cAAc,CAAgB;QAC9B,iBAAY,GAAZ,YAAY,CAAc;QAC1B,iBAAY,GAAZ,YAAY,CAAc;QAC1B,YAAO,GAAP,OAAO,CAAS;QAChB,kBAAa,GAAb,aAAa,CAAe;QAC5B,eAAU,GAAV,UAAU,CAAY;QACtB,aAAQ,GAAR,QAAQ,CAAU;QAClB,cAAS,GAAT,SAAS,CAAkB;QAC3B,cAAS,GAAT,SAAS,CAAW;QAEpB,qBAAgB,GAAhB,gBAAgB,CAA0B;QAC1C,qBAAgB,GAAhB,gBAAgB,CAAkB;QAClC,gBAAW,GAAX,WAAW,CAAqB;QAChC,sBAAiB,GAAjB,iBAAiB,CAAmB;QACpC,qBAAgB,GAAhB,gBAAgB,CAAkB;QAElC,uBAAkB,GAAlB,kBAAkB,CAAoB;QAItC,aAAQ,GAAR,QAAQ,CAA2B;QACnC,WAAM,GAAN,MAAM,CAAc;QAGpB,mBAAc,GAAd,cAAc,CAAgB;QAE9B,oBAAe,GAAf,eAAe,CAAmB;IACpD,CAAC;IAEJ,KAAK,CAAC,GAAG;QACL,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACpE,gGAAgG;QAChG,IAAA,4BAAa,EAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;QACrD,gGAAgG;QAChG,kGAAkG;QAClG,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAE3D,oGAAoG;QACpG,iGAAiG;QACjG,8FAA8F;QAC9F,2GAA2G;QAC3G,qGAAqG;QACrG,qGAAqG;QACrG,iGAAiG;QACjG,sGAAsG;QACtG,6BAA6B;QAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,wCAAoB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;QAChJ,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;QACjC,sGAAsG;QACtG,qGAAqG;QACrG,qGAAqG;QACrG,oGAAoG;QACpG,yCAAyC;QACzC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACzC,oGAAoG;QACpG,oGAAoG;QACpG,8FAA8F;QAC9F,iGAAiG;QACjG,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,IAAA,6BAAc,EAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,QAAQ,CAAC,CAAC;QAEtG,+FAA+F;QAC/F,6FAA6F;QAC7F,mGAAmG;QACnG,MAAM,aAAa,GAAG,IAAA,wBAAQ,EAAC,2BAA2B,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACzF,MAAM,UAAU,GAAG,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;QAEzH,8FAA8F;QAC9F,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEvC,0FAA0F;QAC1F,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,IAAA,wBAAQ,EAAC,2BAA2B,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAElH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,GAAG,qBAAqB,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC;QACtE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QACnF,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;QACtG,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;QAE1D,8FAA8F;QAC9F,2FAA2F;QAC3F,gCAAgC;QAChC,MAAM,WAAW,GAAG,IAAI,iCAAiB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACpG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,uGAAuG;IACvG,sGAAsG;IACtG,oGAAoG;IACpG;;;;;;OAMG;IACK,kBAAkB,CAAC,QAAgB,EAAE,YAAqB;QAC9D,IAAI,YAAY,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8FAA8F,CAAC,CAAC;YACrH,OAAO;QACX,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,iCAAgB,CAC1D,iCAAiC,EAAE,0BAA0B,EAAE,uCAAuC,CACzG,CAAC,CAAC;IACP,CAAC;IAED;;;OAGG;IACK,iBAAiB,CAAC,QAAgB;QACtC,IAAI,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC,oBAAoB,CAC5B,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1F,CAAC;IAED;;;;;;;;;;;OAWG;IACK,wBAAwB,CAAC,QAAgB;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,CAAC;QACvF,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7E,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO;QACtD,MAAM,IAAI,4BAAa,CACnB,2FAA2F;YAC3F,4FAA4F;YAC5F,kDAAkD;YAClD,uBAAuB,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;YAC9D,kFAAkF;YAClF,8BAA8B;YAC9B,kFAAkF,CACrF,CAAC;IACN,CAAC;IAED;;;;;;;;;;OAUG;IACK,oBAAoB,CAAC,QAAgB,EAAE,WAAmB,EAAE,OAAe;QAC/E,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC1D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,4BAAa,CACnB,4FAA4F;gBAC5F,qFAAqF;gBACrF,kCAAkC;gBAClC,iFAAiF,CACpF,CAAC;QACN,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAC7C,mGAAmG;QACnG,wGAAwG;QACxG,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,gDAAgD,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM;YAC7G,gGAAgG;YAChG,mGAAmG,CAAC,CAAC;QACzG,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,qBAAqB,CAAC,QAAgB,EAAE,WAAmB,EAAE,MAAoB;QACrF,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE;YAAE,OAAO;QACnC,4HAA4H;QAC5H,8DAA8D;QAC9D,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,IAAA,6BAAc,EAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;YACjG,IAAI,QAAQ,KAAK,EAAE;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,QAAQ,mBAAmB,CAAC,CAAC;YAC/G,0FAA0F;YAC1F,4FAA4F;YAC5F,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,IAAA,uBAAQ,EAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,sBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yEAAyE,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;QACrH,CAAC;IACL,CAAC;IAEO,MAAM,CAAC,IAAc;QACzB,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5D,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,CAAC;IAED,gGAAgG;IAChG,uGAAuG;IAC/F,WAAW,CAAC,MAAkB;QAClC,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC;QAC7C,OAAO,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5E,CAAC;IAED,yDAAyD;IACjD,qBAAqB,CAAC,QAAgB,EAAE,WAAoB,EAAE,MAAkB,EAAE,KAAa,EAAE,QAAsC;QAC3I,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;QAChD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;QACrE,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,GAAG,SAAS,KAAK,WAAW,EAAE,CAAC;QAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAS,EAAW,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7H,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAE3C,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAClD,kGAAkG;QAClG,kGAAkG;QAClG,gDAAgD;QAChD,OAAO,IAAI,0BAAc,CACrB,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EACzF,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;;;OAOG;IACK,WAAW,CAAC,IAAmB;QACnC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAoB,EAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAoB,EAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzF,CAAC;IAED,2EAA2E;IAC3E,EAAE;IACF,4GAA4G;IAC5G,uGAAuG;IACvG,0GAA0G;IAC1G,0GAA0G;IAC1G,uGAAuG;IACvG,+FAA+F;IACvF,aAAa,CAAC,QAAsC,EAAE,MAAkB;QAC5E,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAsB,EAAgB,EAAE;YACzD,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAC3E,OAAO,IAAI,wBAAY,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACK,WAAW,CAAC,IAAmB,EAAE,MAAkB,EAAE,UAA4B;QACrF,qHAAqH;QACrH,MAAM,WAAW,GAAG,IAAI,GAAG,EAAmB,CAAC;QAC/C,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,QAAQ;YAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAyB,EAAuB,EAAE;YAC9E,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;YAC1C,MAAM,GAAG,GAAG,IAAI,gCAAiB,CAC7B,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,eAAe,EAC1F,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,6FAA6F;YAC7F,mEAAmE;YACnE,MAAM,OAAO,GAAG,GAAG;gBACf,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC;gBAC5D,CAAC,CAAC,IAAI,+BAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YACjD,MAAM,GAAG,GAAG,IAAI,2CAAmB,CAC/B,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,EACvD,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACjG,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACjD,GAAG,CAAC,QAAQ,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/D,OAAO,GAAG,CAAC;QACf,CAAC,CAAC,CAAC;IACP,CAAC;IAED,qGAAqG;IACrG,oGAAoG;IAC5F,aAAa,CAAC,QAAgB,EAAE,OAAe;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACxE,OAAO,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,MAAM,IAAI,CAAC;IAClD,CAAC;IAGD;;;;;;;;;OASG;IACH,yDAAyD;IACjD,oBAAoB,CAAC,QAAgB,EAAE,QAAgB,EAAE,IAAmB,EAAE,MAAkB,EAAE,UAA4B;QAClI,IAAI,QAAQ,KAAK,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzD,IAAI,CAAC,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,iBAAiB;YAAE,OAAO;QAChE,MAAM,OAAO,GAAG,IAAI,sCAAgB,EAAE,CAAC;QACvC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC5B,OAAO,CAAC,MAAM,GAAG,qDAAwB,CAAC;QAC1C,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CACvC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC/F,OAAO,CAAC,UAAU,GAAG,IAAA,uBAAQ,EAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,CAAC;QAC5E,OAAO,CAAC,WAAW,GAAG,wBAAwB,CAAC;QAC/C,OAAO,CAAC,KAAK,GAAG,0BAA0B,CAAC;QAC3C,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,mGAAmG;IACnG,mGAAmG;IACnG,mGAAmG;IACnG,4EAA4E;IACpE,QAAQ,CAAC,OAAwB;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,MAAM,KAAK,GAAG,IAAA,uBAAQ,EAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,CAAC;QACrE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAChD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QACtE,IAAI,SAAS,CAAC,YAAY,EAAE,CAAC;YACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wEAAwE,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC;YACjH,OAAO,IAAI,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,wBAAY,CAAC,KAAK,EAAE,KAAK,EACzD,yEAAyE,EAAE,+BAAmB,CAAC,CAAC,CAAC;QACzG,CAAC;QACD,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC;QAE7B,+FAA+F;QAC/F,iGAAiG;QACjG,+FAA+F;QAC/F,4EAA4E;QAC5E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QAExE;;;;;;;;;;;;;;WAcG;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;QACpF,IAAI,SAAS,KAAK,OAAO,CAAC,IAAI;YAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAErF,2FAA2F;QAC3F,uFAAuF;QACvF,MAAM,WAAW,GAAG,IAAI,mCAAgB,EAAE,CAAC;QAC3C,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAChC,WAAW,CAAC,MAAM,GAAG,UAAU,CAAC;QAChC,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC;QACzD,WAAW,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC;QAClC,WAAW,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;QAC5B,WAAW,CAAC,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5D,iGAAiG;QACjG,+FAA+F;QAC/F,oFAAoF;QACpF,gGAAgG;QAChG,MAAM,SAAS,GAAG,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;QACnE,2FAA2F;QAC3F,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,uBAAW,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3G,OAAO,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACpF,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACK,UAAU,CAAC,QAAgB,EAAE,IAAY,EAAE,KAAqB,EAAE,OAAyB;QAC/F,MAAM,QAAQ,GAAG,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC3B,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC3B,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3D,wFAAwF;QACxF,6EAA6E;QAC7E,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC;QAChG,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAErC,iGAAiG;QACjG,8FAA8F;QAC9F,0DAA0D;QAC1D,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACzD,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;QACvG,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;;;;;OAQG;IACK,cAAc,CAAC,QAAgB,EAAE,QAAgB,EAAE,SAAiB;QACxE,IAAI,QAAQ,KAAK,EAAE;YAAE,OAAO;QAC5B,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,IAAA,yBAAS,EAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACrG,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,4FAA4F;gBAC5F,sEAAsE,CAAC,CAAC;YAC5E,OAAO;QACX,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACpF,CAAC;IAED;;;;OAIG;IACK,aAAa,CAAC,UAAkB;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC;IACtC,CAAC;IAED;;;;;;;;;OASG;IACK,iBAAiB,CAAC,QAAgB,EAAE,QAAgB,EAAE,KAAqB;QAC/E,IAAI,QAAQ,KAAK,EAAE;YAAE,OAAO;QAC5B,MAAM,OAAO,GAAG,IAAI,sCAAgB,EAAE,CAAC;QACvC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC5B,OAAO,CAAC,MAAM,GAAG,iCAAqB,CAAC;QACvC,OAAO,CAAC,IAAI,GAAG,iCAAqB,GAAG,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACxF,OAAO,CAAC,UAAU,GAAG,IAAA,uBAAQ,EAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,CAAC;QAC5E,OAAO,CAAC,WAAW,GAAG,qBAAqB,CAAC;QAC5C,OAAO,CAAC,KAAK,GAAG,wBAAwB,CAAC;QACzC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,kGAAkG;IAClG,qGAAqG;IAC7F,KAAK,CAAC,UAAkB;QAC5B,MAAM,MAAM,GAAG,IAAA,yBAAS,EACpB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,0BAA0B,CAAC,EAC5F,EAAE,QAAQ,EAAE,MAAM,EAAE,CACvB,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,IAAI,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;CACJ,CAAA;AA1fY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;6CAGA,6BAAc;QAChB,mCAAY;QACZ,4BAAY;QACjB,kBAAO;QACD,8BAAa;QAChB,wBAAU;QACZ,oBAAQ;QACP,qCAAgB;QAChB,qBAAS;QAEF,qDAAwB;QACxB,oCAAgB;QACrB,2CAAmB;QACb,gCAAiB;QAClB,+BAAgB;QAEd,wCAAkB;QAI5B,gDAAyB;QAC3B,4BAAY;QAGJ,iCAAc;QAEb,uCAAiB;GA5B9C,qBAAqB,CA0fjC","sourcesContent":["import { execSync, spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate, prDirFor, reviewJsonPath, ReviewJson, RequiredChecklist, ChecklistVerdict,\n writeTemplate, RepoRootFinder, ReviewJsonService, GateTokenService,\n InformAiError, toError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { ChecklistScan, ChecklistScanOptions, ChecklistScanner } from '../workflow/checklist-scanner';\nimport { ReviewerVerdictGate } from '../workflow/reviewer-verdict-gate';\nimport { GitExec } from '../workflow/git-exec';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { MergeState } from '../workflow/merge-state';\nimport { ReviewStageReceiptService } from '../workflow/review-stage-receipt';\nimport { PrMerger, MergeIntent, MergeOutcome, MERGE_RESULT_FAILED } from '../workflow/pr-merger';\nimport { FinishBanner, FinishBannerInput } from '../workflow/finish-banner';\nimport { MergeBodyFiler, MergeBodyRequest } from '../workflow/merge-body-filer';\nimport { GatedPrPublisher } from '../workflow/gated-pr-publisher';\nimport { ProvenanceEnforcer, ProvenanceReport } from '../workflow/provenance-enforcer';\nimport { PrCommentRequest, PrCommentUpserter } from '../workflow/pr-comment-upserter';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport {\n Dashboard, DashboardInput, ChecklistRow, DETAIL_COMMENT_MARKER,\n} from '../../dashboard/dashboard';\nimport {\n ChecklistCommentRenderer, CHECKLIST_COMMENT_MARKER,\n} from '../../dashboard/checklist-comment-renderer';\nimport { ChecklistCommentRow } from '../../dashboard/checklist-comment-row';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * The three inputs the PR COMMENTS are rendered from, bundled so `publishAll` takes one parameter for\n * them instead of three it only forwards. Data-only, per CLAUDE.md.\n */\nclass PrCommentSources {\n scan: ChecklistScan;\n review: ReviewJson;\n provenance: ProvenanceReport;\n\n constructor(scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport) {\n this.scan = scan;\n this.review = review;\n this.provenance = provenance;\n }\n}\n\n/**\n * Everything the PR upsert needs. Data-only, per CLAUDE.md — it replaced a 5-positional-parameter method\n * once `tokenSuffix` had to travel too, so the body can be re-rendered with the PR's own URL after\n * `gh pr create` returns it (see the INVARIANT comment in upsertPr).\n */\nclass PrUpsertRequest {\n repoRoot = '';\n baseBranch = '';\n title = '';\n /** The description as first published — rendered with whatever URL was known at the time. */\n body = '';\n /**\n * The hidden gate-token marker exactly as appended to `body`, so a re-render can reproduce the same\n * bytes. Kept separate from `body` because Dashboard renders the human/git-log half and must not know\n * about HMACs.\n */\n tokenSuffix = '';\n input: DashboardInput;\n\n constructor(input: DashboardInput) {\n this.input = input;\n }\n}\n\n// A resolved PR's number + web URL. Both '' when the PR can't be resolved (e.g. create failed).\nclass PrRef {\n number: string;\n url: string;\n\n constructor(number: string, url: string) {\n this.number = number;\n this.url = url;\n }\n}\n\n// The outcome of the whole upsert: the PR number + web URL ('' each when unresolved) plus what actually\n// happened to the merge, so the final summary reports the REAL result rather than assuming success. The\n// URL is carried so the closing block can hand the AI a clickable link to relay to the user verbatim.\nclass UpsertResult {\n prNumber: string;\n prUrl: string;\n merge: MergeOutcome;\n\n constructor(prNumber: string, prUrl: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.prUrl = prUrl;\n this.merge = merge;\n }\n}\n\n// STAGE ③ — FINISH of the AI-first PR flow, and the ONLY command that posts PRs. Runs after\n// `wp-review-upsert-pr` verified the branch and the AI spawned the reviewers + wrote review.json.\n//\n// In order: (1) REFUSE on an unvalidated 3-point merge and REQUIRE stage ②'s receipt; (2) REQUIRE\n// review.json + every reviewer's verdict + provenance; (3) run the build gate UNLESS the receipt already\n// covers this exact HEAD; (4) render the dashboard; (5) create/update the PR via `gh`.\n//\n// It no longer FINALIZES a merge — stage ② does. Two commands owning conflict-resolution validation is two\n// implementations that drift, and finalizing here came too late to help anyway: the reviewers had already\n// reviewed the pre-merge tree by then.\n@injectable(bindingScopeValues.Singleton)\nexport class FinishUpsertPrCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly branchNaming: BranchNaming,\n private readonly gitExec: GitExec,\n private readonly buildAffected: BuildAffected,\n private readonly mergeState: MergeState,\n private readonly prMerger: PrMerger,\n private readonly publisher: GatedPrPublisher,\n private readonly dashboard: Dashboard,\n // The 2nd PR comment. Its own class because it is its own surface — see ChecklistCommentRenderer.\n private readonly checklistComment: ChecklistCommentRenderer,\n private readonly checklistScanner: ChecklistScanner,\n private readonly verdictGate: ReviewerVerdictGate,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n // Owns the reviewer-provenance integrity check and its audit record — see ProvenanceEnforcer.\n private readonly provenanceEnforcer: ProvenanceEnforcer,\n // NOTE: ChecklistInstructionsService is deliberately NOT injected here any more. Its \"You MUST run\n // these N reviewer subagent(s)\" block is now rendered by ReviewerVerdictGate and ONLY for checklists\n // that genuinely never ran, so no other code path in this command can print it at a refusal.\n private readonly receipts: ReviewStageReceiptService,\n private readonly banner: FinishBanner,\n // The MACHINE-GLOBAL home for the one artifact whose scope is bigger than this tree: the gated\n // squash-commit body, which `wp-land-pr` must find from any tree (see MergeBodyFiler).\n private readonly mergeBodyFiler: MergeBodyFiler,\n // ONE marker-keyed upsert, shared by both PR comments (the full dashboard and the checklist).\n private readonly commentUpserter: PrCommentUpserter,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n // 1. REQUIRE stage ② — see assertStageTwoRan. Returns true when its receipt covers THIS commit,\n // which is what lets the build gate below be skipped rather than re-run for a foregone answer.\n const buildAlreadyGreen = this.assertStageTwoRan(repoRoot);\n\n // 2. REQUIRE the AI-authored review.json (throws InformAiError with the schema if missing/invalid).\n // Compute the consumer checklists this diff triggered FIRST so an unacknowledged BLOCK throws\n // here — BEFORE any `gh pr create` — matching the guarantee buildCommand already provides.\n // The SAME scan wp-review-upsert-pr runs, with filterAlreadyReviewed:true so `outstanding` is exactly what\n // still owes a verdict (Z of N of X). Sharing the computation is the point: the command that REPORTS\n // and the command that GATES must not be able to disagree about what is owed. review-<id>.json files\n // persist locally, so a re-run re-validates the EXISTING verdicts against the (possibly changed)\n // applicable set for free — an unchanged checklist needs no re-review, a newly-applicable one refuses\n // until its file is written.\n const featureName = this.aiBranchName.getFeatureName();\n const scan = this.checklistScanner.scan(repoRoot, loadAndValidate(repoRoot).prGate.checklists, new ChecklistScanOptions(true, 'stage3-finish'));\n const required = scan.applicable;\n // The applicable checklists that are supposed to HAVE a verdict — everything except the optional ones\n // nobody ran. Used for provenance and for the dashboard rows, both of which ask \"who reviewed this?\"\n // and would otherwise demand an answer from a review the human declined: provenance would refuse the\n // PR because a subagent that was never meant to run cannot be proven to have run, and the dashboard\n // would render a MISSING verdict for it.\n const verdicted = this.verdictedOf(scan);\n // FAIL FAST on an unclear checklist, BEFORE review.json is parsed and before the build gate runs. A\n // missing reviewer is not a review.json defect (folding it in made the AI fix the wrong thing), and\n // nobody should wait on a build to be told a reviewer never ran. ReviewerVerdictGate owns the\n // distinction between unreadable / REFUSED / never-ran, and retires the red verdicts it acts on.\n this.verdictGate.assertEveryReviewerRan(scan);\n const review = this.reviewJsonService.loadReviewJson(reviewJsonPath(repoRoot, featureName), required);\n\n // 2c. For any BLOCK checklist that names a reviewer `subagent`, VERIFY (from the harness's own\n // artifacts) that such a subagent actually ran on this branch — the coding agent may not\n // self-certify. Absent CLAUDE_CODE_SESSION_ID this skips with a warning (CI / plain terminal).\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n const provenance = this.provenanceEnforcer.enforce(verdicted, currentBranch, repoRoot, loadAndValidate(repoRoot).prGate);\n\n // 2b. The build gate validates the WORKING TREE but we push HEAD — so they MUST be identical.\n this.gitExec.assertCleanTree(repoRoot);\n\n // 3. Build gate, then post the gated body, then push (that ORDER — see GatedPrPublisher).\n this.runOrSkipBuildGate(repoRoot, buildAlreadyGreen);\n const base = this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n\n process.stdout.write('\\n' + SEP + '📋 Dashboard + PR\\n' + SEP + '\\n');\n const title = this.prTitleFrom(review);\n const input = this.computeDashboardInput(repoRoot, true, review, title, verdicted);\n const result = this.publishAll(repoRoot, base, input, new PrCommentSources(scan, review, provenance));\n this.archiveConsumedReview(repoRoot, featureName, result);\n\n // The closing recap + the clickable-link directive, BOTH derived from the real merge outcome.\n // Nothing here may hard-code success: a stranded PR under a green checkmark is how PRs got\n // abandoned (see FinishBanner).\n const bannerInput = new FinishBannerInput(result.prNumber, result.prUrl, title, base, result.merge);\n process.stdout.write(this.banner.render(bannerInput));\n process.stdout.write(this.banner.linkDirective(bannerInput));\n }\n\n // Validate + commit + finalize a 3-point merge the AI resolved, if one is in progress. Finalizing here\n // does NOT push (pushRemote=false): this command pushes exactly ONCE, from GatedPrPublisher, and only\n // after review.json + every BLOCK checklist + the build gate pass and the gated PR body is written.\n /**\n * The build gate — SKIPPED when stage ②'s receipt already covers this exact HEAD.\n *\n * The gate is authoritative per-COMMIT, not per-command: re-running it on a tree that has not moved\n * since stage ② verified it buys nothing and costs a full `nx affected`. That skip is what makes the\n * three-stage flow cost ONE build rather than two, and it is why moving the gate earlier was affordable.\n */\n private runOrSkipBuildGate(repoRoot: string, alreadyGreen: boolean): void {\n if (alreadyGreen) {\n process.stdout.write('\\n🛠️ Build gate: already green for this commit (stage ② receipt) — skipping the rebuild.\\n');\n return;\n }\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed — no PR created/updated.',\n ));\n }\n\n /**\n * The two stage-② preconditions, together: no unvalidated merge, and a receipt proving stage ② ran.\n * Returns true when that receipt covers the CURRENT HEAD, i.e. the build gate can be skipped.\n */\n private assertStageTwoRan(repoRoot: string): boolean {\n this.assertNoUnvalidatedMerge(repoRoot);\n return this.assertReviewStageRan(\n repoRoot, this.aiBranchName.getFeatureName(), this.gitOut(['rev-parse', 'HEAD']));\n }\n\n /**\n * REFUSE while a 3-point merge is still unvalidated — do not finalize it here.\n *\n * This command used to do the finalizing itself. Finalizing means validating a conflict resolution, and\n * two commands owning that means two implementations that can drift; `PrContextWriter`'s docstring\n * records this codebase already paying for exactly that once. It also came too late to matter: by the\n * time finish ran, the reviewers had already reviewed the pre-merge tree.\n *\n * So stage ② owns it, and finish only checks. Nothing is lost — the recovery is one command away, and\n * running it also builds the merged tree and re-briefs the reviewers against it, which finalizing here\n * never did.\n */\n private assertNoUnvalidatedMerge(repoRoot: string): void {\n const home = this.mergeState.mergeDirFor(repoRoot, this.aiBranchName.getFeatureName());\n const activeDir = this.mergeState.findActiveMergeRunDir(home);\n const marker = activeDir ? this.mergeState.readMergeMarker(activeDir) : null;\n if (!activeDir || !marker || marker.validated) return;\n throw new InformAiError(\n '⛔ NO PR — a 3-point merge on this branch is still unvalidated, so nothing here has been\\n' +\n 'verified: the conflict resolution is unchecked, the merged tree was never built, and any\\n' +\n 'reviewer that ran judged the PRE-merge code.\\n\\n' +\n `Conflicted file(s): ${marker.conflictedFiles.join(', ')}\\n\\n` +\n 'Resolve them (see .webpieces/instruct-ai/webpieces.mergeprocess.md), then run:\\n' +\n ' pnpm wp-review-upsert-pr\\n' +\n 'It validates the resolution, commits it, builds it, and re-briefs the reviewers.',\n );\n }\n\n /**\n * REQUIRE stage ② (`wp-review-upsert-pr`) to have run, and report whether it ran on THIS commit.\n *\n * Returns true when the receipt matches the current HEAD — the caller then skips its own build, because\n * the receipt IS the gate for that sha and rebuilding an unchanged tree is a second full `nx affected`\n * run for a foregone answer.\n *\n * Without this check a repo with NO checklists has nothing forcing stage ②: `assertEveryReviewerRan`\n * is vacuous, and review.json — the only other interlock — is a file the AI writes itself. It could\n * write it and come straight here, skipping the merge validation and the build entirely.\n */\n private assertReviewStageRan(repoRoot: string, featureName: string, headSha: string): boolean {\n const receipt = this.receipts.read(repoRoot, featureName);\n if (receipt === null) {\n throw new InformAiError(\n '⛔ NO PR — stage ② never ran on this branch. That means the 3-point merge is unvalidated,\\n' +\n 'the build gate has not run, no diff was extracted, and no reviewer was briefed.\\n\\n' +\n 'Run: pnpm wp-review-upsert-pr\\n' +\n '(then spawn the reviewers it names, write review.json, and re-run this command)',\n );\n }\n if (receipt.headSha === headSha) return true;\n // Not fatal. Re-reviewing on every follow-up commit would be intolerable, and most drift is a typo\n // fix. But it is never silent: the build re-runs here, and the PR says the reviewers saw an older tree.\n process.stderr.write(\n `\\n⚠️ HEAD moved since stage ② ran (reviewed ${receipt.headSha.slice(0, 8)}, now ${headSha.slice(0, 8)}).\\n` +\n ' The build gate will re-run, and the PR will record that reviewers judged an earlier tree.\\n' +\n ' If the change was substantive, re-run pnpm wp-review-upsert-pr and re-spawn the reviewers.\\n\\n');\n return false;\n }\n\n /**\n * Retire the review this run just used — move review.json to old-review.json beside it, stamped as\n * audit-only (see ReviewJsonService.archiveReviewJson).\n *\n * WHY it moves rather than staying put: review.json left behind is a live-looking file describing a\n * review that has already shipped, and the next `wp-review-upsert-pr` on this branch finds it sitting\n * there. A reviewer subagent that judges the PR's stated INTENT — its title, summary or risk level —\n * then reads the previous round's review and can return GREEN against a title that no longer exists,\n * with nothing in the verdict distinguishing that from a real pass. Moving it makes the only route back\n * to this command a freshly-written review.\n *\n * ONLY on a PR that actually went up. A run that died before publishing must stay re-runnable without\n * making the AI rewrite a review that was never used, and prNumber === '' is exactly that case.\n * Non-fatal: the PR is live by here, so an unwritable archive warns rather than failing the command.\n */\n private archiveConsumedReview(repoRoot: string, featureName: string, result: UpsertResult): void {\n if (result.prNumber === '') return;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: the PR is already up; a failed archive must not fail the command\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const archived = this.reviewJsonService.archiveReviewJson(reviewJsonPath(repoRoot, featureName));\n if (archived !== '') process.stdout.write(` archived this run's review.json → ${archived} (audit only) ✓\\n`);\n // Beside it, so an archived review keeps the transcript links belonging to the round that\n // produced it — a review whose provenance was overwritten by the NEXT round audits nothing.\n this.provenanceEnforcer.archiveRecord(prDirFor(repoRoot, featureName));\n } catch (err: unknown) {\n const error = toError(err);\n process.stderr.write(`⚠️ Could not archive review.json (non-fatal — the PR is already up): ${error.message}\\n`);\n }\n }\n\n private gitOut(args: string[]): string {\n const result = spawnSync('git', args, { encoding: 'utf8' });\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n }\n\n // The user-facing PR title: the AI-authored review.title, or — if omitted — a readable fallback\n // derived from the stable feature name (NEVER the internal `Squash merge of <branch>` commit subject).\n private prTitleFrom(review: ReviewJson): string {\n if (review.title !== '') return review.title;\n return this.aiBranchName.getFeatureName().replace(/[-/]+/g, ' ').trim();\n }\n\n // eslint-disable-next-line @typescript-eslint/max-params\n private computeDashboardInput(repoRoot: string, buildPassed: boolean, review: ReviewJson, title: string, required: readonly RequiredChecklist[]): DashboardInput {\n const config = loadAndValidate(repoRoot).prGate;\n const forkPoint = this.gitOut(['merge-base', 'origin/main', 'HEAD']);\n const featureHead = this.gitOut(['rev-parse', 'HEAD']);\n const mainHead = this.gitOut(['rev-parse', 'origin/main']);\n const range = `${forkPoint}..${featureHead}`;\n const changedFiles = this.gitOut(['diff', range, '--name-only']).split('\\n').filter((f: string): boolean => f.trim() !== '');\n const patch = this.gitOut(['diff', range]);\n\n const gateResults = this.dashboard.computeGateResults(config.gates, changedFiles);\n const disables = this.dashboard.countAddedDisables(patch);\n const rows = this.checklistRows(required, review);\n // buildCommand travels into the dashboard so the PR-body footer can NAME the command that vouched\n // for this commit. The footer used to assert \"build ran via nx affected\" on every repo, which was\n // simply false wherever buildCommand is not nx.\n return new DashboardInput(\n title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows,\n config.buildCommand);\n }\n\n /**\n * The applicable checklists MINUS the optional ones nobody ran.\n *\n * Not folded into `ChecklistScan` as yet another field: `applicable` (what matched) and `optionalNotRun`\n * (what was declined) are both facts about the scan, whereas this is one command's view of them, and the\n * roster comment deliberately wants the OTHER view — it lists the declined ones precisely so the PR does\n * not imply they passed.\n */\n private verdictedOf(scan: ChecklistScan): RequiredChecklist[] {\n const skipped = new Set(scan.optionalNotRun.map((r: RequiredChecklist): string => r.id));\n return scan.applicable.filter((r: RequiredChecklist): boolean => !skipped.has(r.id));\n }\n\n // Pair each matched checklist with its resolved verdict for the dashboard.\n //\n // A checklist reaching this point is always PASS, WARN or OVERRIDDEN, and the reason is ReviewerVerdictGate\n // — NOT loadReviewJson. The gate runs one line earlier and throws on every other state (FAIL, MISSING,\n // BAD_FORMAT), so loadReviewJson's own checklist validation can no longer be the thing that rejects them;\n // it re-validates the same set and finds it clean. This comment used to credit loadReviewJson, which made\n // the ordering look deliberate while the gate's generic \"no verdict yet\" message masked every refusal.\n // WARN belongs in that list: yellow SHIPS, so it is not outstanding and reaches the dashboard.\n private checklistRows(required: readonly RequiredChecklist[], review: ReviewJson): ChecklistRow[] {\n return required.map((req: RequiredChecklist): ChecklistRow => {\n const verdict = this.reviewJsonService.resolveVerdict(req, review.results);\n return new ChecklistRow(req.id, verdict.status, verdict.detail);\n });\n }\n\n /**\n * One comment row per DEFINED checklist — matched or not — pairing the roster's match evidence with the\n * resolved verdict. Built from `scan.roster` rather than from the applicable set: the skipped ones are\n * absent from `applicable` by construction, and their \"why not\" evidence (the configured globs and the\n * changed-file total) exists nowhere else without recomputing the diff a second way.\n */\n private commentRows(scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): ChecklistCommentRow[] {\n // agentType -> did it open the diff. Absent ⇒ not assessed, which prints nothing (see ChecklistCommentRow.diffRead).\n const readByAgent = new Map<string, boolean>();\n for (const e of provenance.evidence) readByAgent.set(e.agentType, e.readDiff);\n return scan.roster.entries.map((entry: TriggeredChecklist): ChecklistCommentRow => {\n const ran = entry.matchedFiles.length > 0;\n const req = new RequiredChecklist(\n entry.def.id, entry.def.subagent, entry.def.doc, entry.matchedFiles, entry.matchedPatterns,\n entry.def.required);\n // A skipped checklist has no verdict to resolve — asking for one would report it as MISSING,\n // i.e. as an unreviewed obligation, when in fact it never had one.\n const verdict = ran\n ? this.reviewJsonService.resolveVerdict(req, review.results)\n : new ChecklistVerdict(entry.def.id, '', '');\n const row = new ChecklistCommentRow(\n entry.def.subagent, verdict.status, verdict.detail, ran,\n entry.def.patterns, entry.matchedPatterns, entry.matchedFiles, scan.roster.changedFileCount);\n row.required = entry.def.required;\n const read = readByAgent.get(entry.def.subagent);\n row.diffRead = read === undefined ? '' : (read ? 'yes' : 'no');\n return row;\n });\n }\n\n // Hidden HMAC gate-token marker (with a leading blank line) to append to the PR body, or '' when the\n // repo sets no gateSalt (byte-identical body to before this feature). Bound to the pushed HEAD sha.\n private gateTokenBody(gateSalt: string, headSha: string): string {\n const marker = this.gateTokenService.gateTokenMarker(gateSalt, headSha);\n return marker === '' ? '' : `\\n\\n${marker}\\n`;\n }\n\n\n /**\n * Publish the roster + every reviewer's full `output` as ONE combined PR comment, idempotently (find the\n * marker comment → PATCH it, else POST). Never fatal: by here the PR is already created/updated, so a\n * `gh` failure only warns.\n *\n * Posted on EVERY run of a repo that defines checklists — including one where nothing matched, because\n * \"all five were evaluated and none applied\" is the good news the old comment could not deliver. The\n * guard is `scan.defined.length`, deliberately NOT the number of rows that ran: a repo with no\n * checklists configured must still see no comment at all (see ChecklistCommentRenderer).\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private postChecklistComment(repoRoot: string, prNumber: string, scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): void {\n if (prNumber === '' || scan.defined.length === 0) return;\n if (!loadAndValidate(repoRoot).prGate.checklistComments) return;\n const request = new PrCommentRequest();\n request.prNumber = prNumber;\n request.marker = CHECKLIST_COMMENT_MARKER;\n request.body = this.checklistComment.render(\n this.commentRows(scan, review, provenance), provenance.verified, scan.roster.baseResolved);\n request.payloadDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n request.payloadName = 'checklist-comment.json';\n request.label = 'checklist review comment';\n this.commentUpserter.upsert(request);\n }\n\n // The PR, the remote branch, and the local branch all share the one stable feature name. Look up /\n // create / merge against `baseBranch` (baseBranchName tolerates a leftover `…wpN` mid-transition).\n // GatedPrPublisher owns the edit/push/create half and its ORDERING — the gated body goes up before\n // the push, so CI's `synchronize` read cannot see the previous run's token.\n private upsertPr(request: PrUpsertRequest): UpsertResult {\n const repoRoot = request.repoRoot;\n const baseBranch = request.baseBranch;\n const title = request.title;\n const input = request.input;\n const prDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const bodyFile = path.join(prDir, 'pr-body.md');\n fs.writeFileSync(bodyFile, request.body + '\\n');\n\n const published = this.publisher.publish(baseBranch, title, bodyFile);\n if (published.createFailed) {\n process.stderr.write('⚠️ gh pr create failed — create the PR manually with the body in:\\n ' + bodyFile + '\\n');\n return new UpsertResult('', '', new MergeOutcome(false, false,\n '⚠️ did NOT merge — there is no PR to merge (gh pr create failed above)', MERGE_RESULT_FAILED));\n }\n const num = published.number;\n\n // Set the squash-merge SUBJECT to the PR title (+ the `(#N)` GitHub normally appends, which an\n // explicit --subject would otherwise drop) and the BODY to the compact commit summary, so main's\n // history carries the PR title + risk/flags/link — NOT the internal `Squash merge of <branch>`\n // subject GitHub would inherit from the single squash commit on the branch.\n const ref = this.prRef(baseBranch);\n const subject = ref.number !== '' ? `${title} (#${ref.number})` : title;\n\n /*\n * THE INVARIANT: the merge body IS the PR description, byte for byte.\n *\n * That is what makes every landing route agree — the GitHub Merge button and a bare `gh pr merge`\n * copy the description via `squash_merge_commit_message: PR_BODY`, while `wp-land-pr` and finish's\n * own auto-merge pass these bytes with `--body-file`. If the two strings could differ, which route\n * landed the commit would change what history says, which is precisely the bug this whole change\n * exists to remove. `pr-body-is-merge-body.spec.ts` pins it.\n *\n * The one wrinkle is the self-link. A brand-new PR has no URL until `gh pr create` returns, so the\n * body published a moment ago was rendered with `prUrl: ''`. Re-render now that the URL is known\n * and push the corrected description back, so the two strings match and the commit carries its own\n * link. Only the CREATE path pays that extra edit: on every later run the URL was already known\n * before publishing, `finalBody` matches what went up, and nothing is re-sent.\n */\n const finalBody = this.dashboard.renderPrBody(input, ref.url) + request.tokenSuffix;\n if (finalBody !== request.body) this.backfillPrBody(ref.number, bodyFile, finalBody);\n\n // MACHINE-GLOBAL, keyed by the PR's identity, so `wp-land-pr` finds it from ANY tree — see\n // MergeBodyFiler for the incident that moved it out of this worktree's pr-review/ dir.\n const bodyRequest = new MergeBodyRequest();\n bodyRequest.treeRoot = repoRoot;\n bodyRequest.branch = baseBranch;\n bodyRequest.feature = this.aiBranchName.getFeatureName();\n bodyRequest.prNumber = ref.number;\n bodyRequest.prUrl = ref.url;\n bodyRequest.body = finalBody + '\\n';\n const mergeBodyFile = this.mergeBodyFiler.file(bodyRequest);\n // PrMerger owns the direct-merge / auto-merge-fallback decision AND checks every gh status, so a\n // merge that did not happen is reported as such instead of being swallowed (see pr-merger.ts).\n // REQUIRED config — no default here on purpose. A missing value (an older published\n // rules-config that has no such field) reaches PrMerger as '' and is treated as \"do not merge\".\n const mergeMode = loadAndValidate(repoRoot).prGate.mergeMode ?? '';\n // `false`: this caller is POLICY-driven — only a config that really says AUTO merges here.\n const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, new MergeIntent(mergeMode, false));\n return new UpsertResult(ref.number !== '' ? ref.number : num, ref.url, outcome);\n }\n\n /**\n * Everything the gated flow PUBLISHES, in the one order that is safe — the three surfaces plus the\n * merge, as a single unit.\n *\n * 1. the PR DESCRIPTION = the compact git-log body (+ the hidden gate token), written before the\n * push so CI's `synchronize` read can never see the previous run's token,\n * 2. the 1st comment = the full dashboard,\n * 3. the 2nd comment = each reviewer's output.\n *\n * The token is bound to the LOCAL HEAD sha and minted here because we only reach this line after the\n * build gate and every BLOCK checklist passed, so minting is legitimate. Nothing about\n * HMAC(salt, HEAD) needs the remote to have the commit yet.\n *\n * Extracted from `run` when the two comments made it 82 lines against a hard 80-line rule — and it\n * earns its own name: these four steps have a REQUIRED order, and the comment explaining that order\n * belongs with them rather than inside a method that also loads config and runs a build gate.\n */\n private publishAll(repoRoot: string, base: string, input: DashboardInput, sources: PrCommentSources): UpsertResult {\n const gateSalt = loadAndValidate(repoRoot).prGate.gateSalt;\n const headSha = this.gitOut(['rev-parse', 'HEAD']);\n const upsert = new PrUpsertRequest(input);\n upsert.repoRoot = repoRoot;\n upsert.baseBranch = base;\n upsert.title = input.title;\n upsert.tokenSuffix = this.gateTokenBody(gateSalt, headSha);\n // `existingPrUrl` is '' only for a brand-new PR — there is no URL to self-link to until\n // `gh pr create` returns one, and upsertPr back-fills it the moment it does.\n upsert.body = this.dashboard.renderPrBody(input, this.existingPrUrl(base)) + upsert.tokenSuffix;\n const result = this.upsertPr(upsert);\n\n // In the order the PR body's pointer promises: full dashboard 1st, reviewer output 2nd. Both are\n // idempotent by hidden marker and both non-fatal — the PR is up by now, so a `gh` hiccup on a\n // comment must not turn a finished run into a failed one.\n this.postDetailComment(repoRoot, result.prNumber, input);\n this.postChecklistComment(repoRoot, result.prNumber, sources.scan, sources.review, sources.provenance);\n return result;\n }\n\n /**\n * Re-publish the description now that the PR's own URL is known (create path only).\n *\n * Non-fatal on purpose. The PR exists, its body already carries a valid gate token for the pushed\n * head, and the code is up — the ONLY thing missing is the self-link on the first line. Aborting a\n * finished run over a cosmetic back-link would be a worse outcome than the missing link, and the very\n * next `wp-finish-upsert-pr` writes it (by then the URL is known before publishing, so it goes up with\n * the body). The merge body still gets the linked version regardless: it is filed from `finalBody`.\n */\n private backfillPrBody(prNumber: string, bodyFile: string, finalBody: string): void {\n if (prNumber === '') return;\n fs.writeFileSync(bodyFile, finalBody + '\\n');\n const res = spawnSync('gh', ['pr', 'edit', prNumber, '--body-file', bodyFile], { encoding: 'utf8' });\n if (res.status !== 0) {\n process.stderr.write(\n '⚠️ Could not add the PR\\'s own link to its description (non-fatal — the PR and the gate\\n' +\n ' token are already up). The next wp-finish-upsert-pr writes it.\\n');\n return;\n }\n process.stdout.write(' back-filled the PR description with its own link ✓\\n');\n }\n\n /**\n * The PR's web URL if one is already open for this branch, else '' — read BEFORE publishing so the\n * description can carry its own link on the first render. Only a brand-new PR gets '' (and then\n * {@link backfillPrBody} fixes it up after `gh pr create` returns a URL).\n */\n private existingPrUrl(baseBranch: string): string {\n return this.prRef(baseBranch).url;\n }\n\n /**\n * The 1st PR comment: the FULL dashboard — every row including the green ones, the whole summary, the\n * 3-point hash points. This is what the PR description used to be, and moving it here is the entire\n * point of the change: the description is now the git-log body, and a squash commit must not carry a\n * risk table.\n *\n * Posted unconditionally (any repo, checklists or not), because the description's last bullet PROMISES\n * a 1st comment holding the detail. A pointer to a comment that does not exist is worse than no\n * pointer. Non-fatal — the PR is already up, and the description alone is a complete, readable record.\n */\n private postDetailComment(repoRoot: string, prNumber: string, input: DashboardInput): void {\n if (prNumber === '') return;\n const request = new PrCommentRequest();\n request.prNumber = prNumber;\n request.marker = DETAIL_COMMENT_MARKER;\n request.body = DETAIL_COMMENT_MARKER + '\\n' + this.dashboard.renderDetailComment(input);\n request.payloadDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n request.payloadName = 'detail-comment.json';\n request.label = 'full dashboard comment';\n this.commentUpserter.upsert(request);\n }\n\n // The PR's number + web URL (for the merge subject `(#N)` and the commit-body back-link). Both ''\n // if it can't be resolved. Rendered via jq into one tab-separated line so no JSON parsing is needed.\n private prRef(baseBranch: string): PrRef {\n const result = spawnSync(\n 'gh', ['pr', 'view', baseBranch, '--json', 'number,url', '--jq', '\"\\\\(.number)\\\\t\\\\(.url)\"'],\n { encoding: 'utf8' },\n );\n if (result.status !== 0) {\n return new PrRef('', '');\n }\n const parts = (result.stdout ?? '').trim().split('\\t');\n return new PrRef(parts[0] ?? '', parts[1] ?? '');\n }\n}\n"]}
@@ -177,7 +177,15 @@ let FinishBanner = class FinishBanner {
177
177
  return ' Nothing else is owed: GitHub lands it when the checks go green. You can stop here.\n';
178
178
  }
179
179
  if (merge.result === pr_merger_1.MERGE_RESULT_LEFT_TO_HUMAN) {
180
- return ' Nothing else is owed by the tooling — a person merges it. You can stop here.\n';
180
+ return ' Nothing else is owed by the tooling — a person merges it. You can stop here.\n' +
181
+ '\n' +
182
+ ' ℹ️ Clicking Merge in the GitHub UI is CORRECT here and produces the right history, as\n' +
183
+ ' long as this repo has both settings: squash_merge_commit_title=PR_TITLE and\n' +
184
+ ' squash_merge_commit_message=PR_BODY. The PR description IS the commit body this\n' +
185
+ ' flow rendered — compact, non-green flags only, with the PR link on top — so PR_BODY\n' +
186
+ ' copies exactly the right text into main.\n' +
187
+ ' (`pnpm wp-land-pr` lands the identical bytes from the CLI, and also archives the\n' +
188
+ ' pre-squash tip and reaps the worktree. Either route is fine.)\n';
181
189
  }
182
190
  return '';
183
191
  }
@@ -1 +1 @@
1
- {"version":3,"file":"finish-banner.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/finish-banner.ts"],"names":[],"mappings":"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","sourcesContent":["import { injectable, bindingScopeValues } from 'inversify';\nimport {\n MergeOutcome, MERGE_RESULT_MERGED, MERGE_RESULT_AUTO_QUEUED, MERGE_RESULT_LEFT_TO_HUMAN,\n MERGE_RESULT_BEHIND_CONFLICTING, MERGE_RESULT_BEHIND_UNKNOWN,\n} from './pr-merger';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// Everything the closing block of wp-finish-upsert-pr needs to describe what actually happened. Both\n// `prNumber` and `prUrl` are '' when the PR could not be resolved (e.g. `gh pr create` failed).\nexport class FinishBannerInput {\n prNumber: string;\n prUrl: string;\n title: string;\n base: string;\n merge: MergeOutcome;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(prNumber: string, prUrl: string, title: string, base: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.prUrl = prUrl;\n this.title = title;\n this.base = base;\n this.merge = merge;\n }\n}\n\n/**\n * Renders the closing block of `wp-finish-upsert-pr` — and it is the FRAME, not the merge message, that\n * this class exists to get right.\n *\n * PrMerger has long been honest in its `message`. The banner around it was not: it printed a hard-coded\n * `✅ PR finished` on every path, so a run whose merge failed still looked like a completed one at a\n * glance. The worst case is `mergeStateStatus: BEHIND` — unlike BLOCKED (waiting on checks), which\n * auto-merge resolves on its own, a BEHIND branch NEVER lands. Under a green checkmark, agents walked\n * away from stranded PRs. Three of them independently reported the output as untrustworthy.\n *\n * So the header is derived from `MergeOutcome.result`, and a not-done outcome is loud, distinct, and\n * carries the exact commands that fix it — including in the clickable-link directive, whose whole job\n * is to be the last thing the AI says.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class FinishBanner {\n // The full closing recap: header keyed to the real outcome, the four things this command did, and —\n // when the PR is not done — what must happen next.\n render(input: FinishBannerInput): string {\n const prNum = input.prNumber;\n return '\\n' + SEP + this.header(input.merge) + SEP + '\\n' +\n ` 1. validated the build gate (authoritative)\\n` +\n ` 2. ${prNum ? `wrote the gated body to PR #${prNum}` : 'composed the gated PR body'} titled: \"${input.title}\"\\n` +\n ` 3. force-pushed your work to origin/${input.base} (after the body, so CI reads the right token)\\n` +\n ` 4. ${input.merge.message}\\n` +\n ` You are on ${input.base} — same name as the remote branch and the PR head.\\n` +\n this.whatIsOwed(input) + '\\n';\n }\n\n // TRUE only for the outcomes where nothing further is owed: merged, queued behind green-able checks,\n // or deliberately left for a human. Everything else — BEHIND, config mismatch, gh failure, no PR —\n // is unfinished work, and the banner must not decorate it with a checkmark.\n isDone(merge: MergeOutcome): boolean {\n return merge.result === MERGE_RESULT_MERGED\n || merge.result === MERGE_RESULT_AUTO_QUEUED\n || merge.result === MERGE_RESULT_LEFT_TO_HUMAN;\n }\n\n private header(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_MERGED) return '✅ PR finished AND MERGED — here is exactly what I did\\n';\n if (merge.result === MERGE_RESULT_AUTO_QUEUED) {\n return '✅ PR finished — auto-merge is ON; it lands itself when the checks pass\\n';\n }\n if (merge.result === MERGE_RESULT_LEFT_TO_HUMAN) {\n return '✅ PR finished — posted for a human to merge (that is this repo\\'s policy)\\n';\n }\n // NOT \"PR NOT FINISHED\". Everything this command owns SUCCEEDED — the branch is pushed, the body\n // is written, the build gate is green. What happened is that another author landed on main\n // between our fetch and our push. Reporting that as the author's failure sent agents off\n // re-auditing their own diff and hunting build flakes for a race they did not cause.\n if (merge.isBehind()) return this.behindHeader(merge);\n return '⚠️ PR NOT FINISHED — the PR is up, but the merge did NOT happen\\n';\n }\n\n private behindHeader(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) {\n return '⏸️ PR IS UP AND GREEN — someone landed on main first, and it CONFLICTS with your work\\n';\n }\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return '⏸️ PR IS UP AND GREEN — GitHub has not finished computing mergeability yet\\n';\n }\n return '⏸️ PR IS UP AND GREEN — someone landed on main first (no conflicts with your work)\\n';\n }\n\n // The follow-up block. '' for a done outcome — a finished run should not invent chores.\n private whatIsOwed(input: FinishBannerInput): string {\n if (this.isDone(input.merge)) return this.doneNote(input.merge);\n if (input.merge.isBehind()) return this.behindRemedy(input);\n return '\\n' + SEP +\n ' ⚠️ DO NOT report this PR as done. The merge failed for the reason in step 4 above.\\n' +\n ' Fix that, then re-run: pnpm wp-finish-upsert-pr\\n' +\n ` Confirm for yourself: gh pr view ${input.prNumber === '' ? '<n>' : input.prNumber} --json mergeable,mergeStateStatus,state\\n`;\n }\n\n /**\n * The BEHIND follow-up. Two rules govern every word here.\n *\n * FIRST: the remedy is the FULL ①②③, never a shortcut. `gh pr update-branch` looks like the obvious\n * one-command fix and is a trap — it rewrites the REMOTE branch while every fork-point consumer\n * (`ForkPoint.resolveForkPoint`, `nx affected --base=$(git merge-base ...)`, the review diff) computes\n * against the LOCAL HEAD. That splits reality in two: stage ③ force-pushes local over the remote and\n * silently reverts it, the recorded hash points describe a tree that is no longer the PR head, and the\n * rebased tree never passes a build gate. Only ① moves the fork point AND records it; only ② rebuilds\n * and re-receipts against the new one.\n *\n * SECOND: it ASKS, it does not order. An imperative command list is what turns an agent into a loop —\n * it complies, main moves again, it complies again. Asking forces a stop at a human, which is the only\n * thing that reliably terminates a race we cannot win by retrying.\n */\n private behindRemedy(input: FinishBannerInput): string {\n const num = input.prNumber === '' ? '<n>' : input.prNumber;\n return '\\n' + SEP + this.behindSituation(input.merge) + '\\n' +\n ' ⚠️ STOP HERE AND ASK THE HUMAN. Do NOT run these yourself — if main keeps moving,\\n' +\n ' running them on your own is an infinite loop with a full build inside it.\\n\\n' +\n ' Ask, in your own words:\\n' +\n ` \"${this.behindAsk(input.merge)}\\n` +\n ` ${this.behindAskClose(input.merge)}\"\\n\\n` +\n ' Only once they say yes:\\n\\n' +\n ' pnpm wp-start-upsert-pr # 3-point merge from main — re-forks onto the new main\\n' +\n ' pnpm wp-review-upsert-pr # re-validates the merge + REBUILDS on the new fork point\\n' +\n ' pnpm wp-finish-upsert-pr # gated body + merge, now up to date\\n\\n' +\n ' Do NOT skip ②. It is the only stage that validates the new merge and rebuilds against\\n' +\n ' the new fork point; skipping it publishes a PR whose tree was never gated.\\n' +\n ' Do NOT reach for `gh pr update-branch`. It rewrites the REMOTE branch only, which stage\\n' +\n ' ③ then force-pushes over — and it lands a tree no build gate ever saw.\\n' +\n ` Verify independently, do not trust this banner:\\n` +\n ` gh pr view ${num} --json mergeable,mergeStateStatus,state\\n`;\n }\n\n // What is actually true right now, per kind. The CLEAN case gets the caveat that it may not even\n // matter: \"out of date\" only blocks a merge on repos that require branches be up to date.\n private behindSituation(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) {\n return ' Your PR is pushed, its body is written, and the build gate passed. Then someone\\n' +\n ' else landed on main, and their change CONFLICTS with yours — same lines. Real\\n' +\n ' resolution is owed, and if people keep landing ahead of you it genuinely repeats.\\n' +\n ' That is inherent to concurrent editing, not a bug and not something you did wrong.\\n';\n }\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return ' Your PR is pushed, its body is written, and the build gate passed. GitHub has not\\n' +\n ' finished computing mergeability yet (it is asynchronous, and we asked seconds after\\n' +\n ' the push), so we do NOT know whether this conflicts. Re-check before doing anything:\\n' +\n ' a few seconds later the answer is usually CLEAN and no work is owed at all.\\n';\n }\n return ' Your PR is pushed, its body is written, and the build gate passed. Someone else simply\\n' +\n ' landed on main first. There are NO conflicts — nobody touched your lines.\\n' +\n ' This may not even matter: \"out of date\" blocks a merge only on repos that REQUIRE\\n' +\n ' branches be up to date. If yours does not, this PR can merge as-is.\\n';\n }\n\n // The one sentence to put to the human, in their terms.\n private behindAsk(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) {\n return 'Someone beat me to landing on main and there are conflicts. We MUST run a\\n' +\n ' 3-point merge so I can resolve them properly.';\n }\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return 'Someone beat me to landing on main. GitHub has not said yet whether it\\n' +\n ' conflicts, so I have not touched anything.';\n }\n return 'Someone beat me to landing on main. There are no conflicts, so this is just\\n' +\n ' a re-sync — but it costs a full rebuild.';\n }\n\n // The actual question. UNKNOWN gets a DIFFERENT one: with mergeability still uncomputed, proposing a\n // full re-run is proposing work we cannot yet show is needed — a re-check is free and often ends it.\n private behindAskClose(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return 'Shall I re-check in a moment, or start the wp-*-upsert-pr process over?';\n }\n return 'May I start the wp-*-upsert-pr process over again?';\n }\n\n // A short, positive \"you are free to stop\" line for the two non-merged-but-fine outcomes, so an AI\n // reading a queued PR does not go hunting for work that is not owed.\n private doneNote(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_AUTO_QUEUED) {\n return ' Nothing else is owed: GitHub lands it when the checks go green. You can stop here.\\n';\n }\n if (merge.result === MERGE_RESULT_LEFT_TO_HUMAN) {\n return ' Nothing else is owed by the tooling — a person merges it. You can stop here.\\n';\n }\n return '';\n }\n\n /**\n * The closing AI directive: the resolved PR's number/title/URL and an instruction to end the\n * user-facing summary with EXACTLY `[#N title](url)` as the final line, nothing after it.\n *\n * The link text CARRIES the outcome. Ending on a bare cheerful link is exactly how a stranded PR got\n * reported as finished, and the directive says the link must be last — so the truth has to live\n * inside the link itself, not merely above it.\n *\n * '' when the PR could not be resolved: better to say nothing than to point at a link that is not real.\n */\n linkDirective(input: FinishBannerInput): string {\n if (input.prNumber === '' || input.prUrl === '') return '';\n const label = this.isDone(input.merge) ? input.title : `${input.title} — ${this.notDoneTag(input.merge)}`;\n const preamble = this.isDone(input.merge)\n ? ''\n : `AI — this PR is NOT done (${this.notDoneTag(input.merge)}). Your summary MUST say so plainly.\\n` +\n (input.merge.isBehind()\n ? `MUST END BY ASKING the human for permission to re-run the flow, and MUST NOT run any of\\n` +\n `the commands above until they answer. Say plainly that nothing they did caused this —\\n` +\n `another author landed on main first — and that the PR itself is pushed and gate-green.\\n\\n`\n : `MUST include the remedy commands printed above. Do not describe this run as complete.\\n\\n`);\n return SEP + `🔗 PR #${input.prNumber}: ${input.title}\\n ${input.prUrl}\\n\\n` + SEP + preamble +\n `AI — the LAST line of your summary to the user MUST be EXACTLY this clickable link, with\\n` +\n `NOTHING printed after it (no sign-off, no next steps, no blank commentary):\\n\\n` +\n ` [#${input.prNumber} ${label}](${input.prUrl})\\n\\n`;\n }\n\n // The tag rides INSIDE the clickable link, which the directive above forces to be the last line the\n // AI prints — so it is the one piece of wording guaranteed to reach the user. It says who is waiting\n // on whom: for every BEHIND flavour the answer is \"a human\", not \"more automation\".\n private notDoneTag(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) return 'NOT MERGED — main moved and it conflicts, needs your OK to re-sync';\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) return 'NOT MERGED — main moved, GitHub still computing mergeability';\n if (merge.isBehind()) return 'NOT MERGED — main moved (no conflicts), needs your OK to re-sync';\n return 'NOT MERGED';\n }\n}\n"]}
1
+ {"version":3,"file":"finish-banner.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/finish-banner.ts"],"names":[],"mappings":"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","sourcesContent":["import { injectable, bindingScopeValues } from 'inversify';\nimport {\n MergeOutcome, MERGE_RESULT_MERGED, MERGE_RESULT_AUTO_QUEUED, MERGE_RESULT_LEFT_TO_HUMAN,\n MERGE_RESULT_BEHIND_CONFLICTING, MERGE_RESULT_BEHIND_UNKNOWN,\n} from './pr-merger';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// Everything the closing block of wp-finish-upsert-pr needs to describe what actually happened. Both\n// `prNumber` and `prUrl` are '' when the PR could not be resolved (e.g. `gh pr create` failed).\nexport class FinishBannerInput {\n prNumber: string;\n prUrl: string;\n title: string;\n base: string;\n merge: MergeOutcome;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(prNumber: string, prUrl: string, title: string, base: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.prUrl = prUrl;\n this.title = title;\n this.base = base;\n this.merge = merge;\n }\n}\n\n/**\n * Renders the closing block of `wp-finish-upsert-pr` — and it is the FRAME, not the merge message, that\n * this class exists to get right.\n *\n * PrMerger has long been honest in its `message`. The banner around it was not: it printed a hard-coded\n * `✅ PR finished` on every path, so a run whose merge failed still looked like a completed one at a\n * glance. The worst case is `mergeStateStatus: BEHIND` — unlike BLOCKED (waiting on checks), which\n * auto-merge resolves on its own, a BEHIND branch NEVER lands. Under a green checkmark, agents walked\n * away from stranded PRs. Three of them independently reported the output as untrustworthy.\n *\n * So the header is derived from `MergeOutcome.result`, and a not-done outcome is loud, distinct, and\n * carries the exact commands that fix it — including in the clickable-link directive, whose whole job\n * is to be the last thing the AI says.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class FinishBanner {\n // The full closing recap: header keyed to the real outcome, the four things this command did, and —\n // when the PR is not done — what must happen next.\n render(input: FinishBannerInput): string {\n const prNum = input.prNumber;\n return '\\n' + SEP + this.header(input.merge) + SEP + '\\n' +\n ` 1. validated the build gate (authoritative)\\n` +\n ` 2. ${prNum ? `wrote the gated body to PR #${prNum}` : 'composed the gated PR body'} titled: \"${input.title}\"\\n` +\n ` 3. force-pushed your work to origin/${input.base} (after the body, so CI reads the right token)\\n` +\n ` 4. ${input.merge.message}\\n` +\n ` You are on ${input.base} — same name as the remote branch and the PR head.\\n` +\n this.whatIsOwed(input) + '\\n';\n }\n\n // TRUE only for the outcomes where nothing further is owed: merged, queued behind green-able checks,\n // or deliberately left for a human. Everything else — BEHIND, config mismatch, gh failure, no PR —\n // is unfinished work, and the banner must not decorate it with a checkmark.\n isDone(merge: MergeOutcome): boolean {\n return merge.result === MERGE_RESULT_MERGED\n || merge.result === MERGE_RESULT_AUTO_QUEUED\n || merge.result === MERGE_RESULT_LEFT_TO_HUMAN;\n }\n\n private header(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_MERGED) return '✅ PR finished AND MERGED — here is exactly what I did\\n';\n if (merge.result === MERGE_RESULT_AUTO_QUEUED) {\n return '✅ PR finished — auto-merge is ON; it lands itself when the checks pass\\n';\n }\n if (merge.result === MERGE_RESULT_LEFT_TO_HUMAN) {\n return '✅ PR finished — posted for a human to merge (that is this repo\\'s policy)\\n';\n }\n // NOT \"PR NOT FINISHED\". Everything this command owns SUCCEEDED — the branch is pushed, the body\n // is written, the build gate is green. What happened is that another author landed on main\n // between our fetch and our push. Reporting that as the author's failure sent agents off\n // re-auditing their own diff and hunting build flakes for a race they did not cause.\n if (merge.isBehind()) return this.behindHeader(merge);\n return '⚠️ PR NOT FINISHED — the PR is up, but the merge did NOT happen\\n';\n }\n\n private behindHeader(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) {\n return '⏸️ PR IS UP AND GREEN — someone landed on main first, and it CONFLICTS with your work\\n';\n }\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return '⏸️ PR IS UP AND GREEN — GitHub has not finished computing mergeability yet\\n';\n }\n return '⏸️ PR IS UP AND GREEN — someone landed on main first (no conflicts with your work)\\n';\n }\n\n // The follow-up block. '' for a done outcome — a finished run should not invent chores.\n private whatIsOwed(input: FinishBannerInput): string {\n if (this.isDone(input.merge)) return this.doneNote(input.merge);\n if (input.merge.isBehind()) return this.behindRemedy(input);\n return '\\n' + SEP +\n ' ⚠️ DO NOT report this PR as done. The merge failed for the reason in step 4 above.\\n' +\n ' Fix that, then re-run: pnpm wp-finish-upsert-pr\\n' +\n ` Confirm for yourself: gh pr view ${input.prNumber === '' ? '<n>' : input.prNumber} --json mergeable,mergeStateStatus,state\\n`;\n }\n\n /**\n * The BEHIND follow-up. Two rules govern every word here.\n *\n * FIRST: the remedy is the FULL ①②③, never a shortcut. `gh pr update-branch` looks like the obvious\n * one-command fix and is a trap — it rewrites the REMOTE branch while every fork-point consumer\n * (`ForkPoint.resolveForkPoint`, `nx affected --base=$(git merge-base ...)`, the review diff) computes\n * against the LOCAL HEAD. That splits reality in two: stage ③ force-pushes local over the remote and\n * silently reverts it, the recorded hash points describe a tree that is no longer the PR head, and the\n * rebased tree never passes a build gate. Only ① moves the fork point AND records it; only ② rebuilds\n * and re-receipts against the new one.\n *\n * SECOND: it ASKS, it does not order. An imperative command list is what turns an agent into a loop —\n * it complies, main moves again, it complies again. Asking forces a stop at a human, which is the only\n * thing that reliably terminates a race we cannot win by retrying.\n */\n private behindRemedy(input: FinishBannerInput): string {\n const num = input.prNumber === '' ? '<n>' : input.prNumber;\n return '\\n' + SEP + this.behindSituation(input.merge) + '\\n' +\n ' ⚠️ STOP HERE AND ASK THE HUMAN. Do NOT run these yourself — if main keeps moving,\\n' +\n ' running them on your own is an infinite loop with a full build inside it.\\n\\n' +\n ' Ask, in your own words:\\n' +\n ` \"${this.behindAsk(input.merge)}\\n` +\n ` ${this.behindAskClose(input.merge)}\"\\n\\n` +\n ' Only once they say yes:\\n\\n' +\n ' pnpm wp-start-upsert-pr # 3-point merge from main — re-forks onto the new main\\n' +\n ' pnpm wp-review-upsert-pr # re-validates the merge + REBUILDS on the new fork point\\n' +\n ' pnpm wp-finish-upsert-pr # gated body + merge, now up to date\\n\\n' +\n ' Do NOT skip ②. It is the only stage that validates the new merge and rebuilds against\\n' +\n ' the new fork point; skipping it publishes a PR whose tree was never gated.\\n' +\n ' Do NOT reach for `gh pr update-branch`. It rewrites the REMOTE branch only, which stage\\n' +\n ' ③ then force-pushes over — and it lands a tree no build gate ever saw.\\n' +\n ` Verify independently, do not trust this banner:\\n` +\n ` gh pr view ${num} --json mergeable,mergeStateStatus,state\\n`;\n }\n\n // What is actually true right now, per kind. The CLEAN case gets the caveat that it may not even\n // matter: \"out of date\" only blocks a merge on repos that require branches be up to date.\n private behindSituation(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) {\n return ' Your PR is pushed, its body is written, and the build gate passed. Then someone\\n' +\n ' else landed on main, and their change CONFLICTS with yours — same lines. Real\\n' +\n ' resolution is owed, and if people keep landing ahead of you it genuinely repeats.\\n' +\n ' That is inherent to concurrent editing, not a bug and not something you did wrong.\\n';\n }\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return ' Your PR is pushed, its body is written, and the build gate passed. GitHub has not\\n' +\n ' finished computing mergeability yet (it is asynchronous, and we asked seconds after\\n' +\n ' the push), so we do NOT know whether this conflicts. Re-check before doing anything:\\n' +\n ' a few seconds later the answer is usually CLEAN and no work is owed at all.\\n';\n }\n return ' Your PR is pushed, its body is written, and the build gate passed. Someone else simply\\n' +\n ' landed on main first. There are NO conflicts — nobody touched your lines.\\n' +\n ' This may not even matter: \"out of date\" blocks a merge only on repos that REQUIRE\\n' +\n ' branches be up to date. If yours does not, this PR can merge as-is.\\n';\n }\n\n // The one sentence to put to the human, in their terms.\n private behindAsk(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) {\n return 'Someone beat me to landing on main and there are conflicts. We MUST run a\\n' +\n ' 3-point merge so I can resolve them properly.';\n }\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return 'Someone beat me to landing on main. GitHub has not said yet whether it\\n' +\n ' conflicts, so I have not touched anything.';\n }\n return 'Someone beat me to landing on main. There are no conflicts, so this is just\\n' +\n ' a re-sync — but it costs a full rebuild.';\n }\n\n // The actual question. UNKNOWN gets a DIFFERENT one: with mergeability still uncomputed, proposing a\n // full re-run is proposing work we cannot yet show is needed — a re-check is free and often ends it.\n private behindAskClose(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) {\n return 'Shall I re-check in a moment, or start the wp-*-upsert-pr process over?';\n }\n return 'May I start the wp-*-upsert-pr process over again?';\n }\n\n // A short, positive \"you are free to stop\" line for the two non-merged-but-fine outcomes, so an AI\n // reading a queued PR does not go hunting for work that is not owed.\n private doneNote(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_AUTO_QUEUED) {\n return ' Nothing else is owed: GitHub lands it when the checks go green. You can stop here.\\n';\n }\n if (merge.result === MERGE_RESULT_LEFT_TO_HUMAN) {\n return ' Nothing else is owed by the tooling — a person merges it. You can stop here.\\n' +\n '\\n' +\n ' ℹ️ Clicking Merge in the GitHub UI is CORRECT here and produces the right history, as\\n' +\n ' long as this repo has both settings: squash_merge_commit_title=PR_TITLE and\\n' +\n ' squash_merge_commit_message=PR_BODY. The PR description IS the commit body this\\n' +\n ' flow rendered — compact, non-green flags only, with the PR link on top — so PR_BODY\\n' +\n ' copies exactly the right text into main.\\n' +\n ' (`pnpm wp-land-pr` lands the identical bytes from the CLI, and also archives the\\n' +\n ' pre-squash tip and reaps the worktree. Either route is fine.)\\n';\n }\n return '';\n }\n\n /**\n * The closing AI directive: the resolved PR's number/title/URL and an instruction to end the\n * user-facing summary with EXACTLY `[#N title](url)` as the final line, nothing after it.\n *\n * The link text CARRIES the outcome. Ending on a bare cheerful link is exactly how a stranded PR got\n * reported as finished, and the directive says the link must be last — so the truth has to live\n * inside the link itself, not merely above it.\n *\n * '' when the PR could not be resolved: better to say nothing than to point at a link that is not real.\n */\n linkDirective(input: FinishBannerInput): string {\n if (input.prNumber === '' || input.prUrl === '') return '';\n const label = this.isDone(input.merge) ? input.title : `${input.title} — ${this.notDoneTag(input.merge)}`;\n const preamble = this.isDone(input.merge)\n ? ''\n : `AI — this PR is NOT done (${this.notDoneTag(input.merge)}). Your summary MUST say so plainly.\\n` +\n (input.merge.isBehind()\n ? `MUST END BY ASKING the human for permission to re-run the flow, and MUST NOT run any of\\n` +\n `the commands above until they answer. Say plainly that nothing they did caused this —\\n` +\n `another author landed on main first — and that the PR itself is pushed and gate-green.\\n\\n`\n : `MUST include the remedy commands printed above. Do not describe this run as complete.\\n\\n`);\n return SEP + `🔗 PR #${input.prNumber}: ${input.title}\\n ${input.prUrl}\\n\\n` + SEP + preamble +\n `AI — the LAST line of your summary to the user MUST be EXACTLY this clickable link, with\\n` +\n `NOTHING printed after it (no sign-off, no next steps, no blank commentary):\\n\\n` +\n ` [#${input.prNumber} ${label}](${input.prUrl})\\n\\n`;\n }\n\n // The tag rides INSIDE the clickable link, which the directive above forces to be the last line the\n // AI prints — so it is the one piece of wording guaranteed to reach the user. It says who is waiting\n // on whom: for every BEHIND flavour the answer is \"a human\", not \"more automation\".\n private notDoneTag(merge: MergeOutcome): string {\n if (merge.result === MERGE_RESULT_BEHIND_CONFLICTING) return 'NOT MERGED — main moved and it conflicts, needs your OK to re-sync';\n if (merge.result === MERGE_RESULT_BEHIND_UNKNOWN) return 'NOT MERGED — main moved, GitHub still computing mergeability';\n if (merge.isBehind()) return 'NOT MERGED — main moved (no conflicts), needs your OK to re-sync';\n return 'NOT MERGED';\n }\n}\n"]}
@@ -0,0 +1,54 @@
1
+ /** One marker-keyed PR comment to upsert. Data-only, per CLAUDE.md. */
2
+ export declare class PrCommentRequest {
3
+ /** The PR number. '' means "no PR resolved" and the upsert is a silent no-op. */
4
+ prNumber: string;
5
+ /** Hidden HTML-comment marker that identifies THIS tool's comment among all comments on the PR. */
6
+ marker: string;
7
+ /** The full comment body, marker included. */
8
+ body: string;
9
+ /** Directory to stage the JSON payload in — `gh api --input` needs a file. */
10
+ payloadDir: string;
11
+ /** Payload filename, unique per comment kind so two upserts in one run cannot clobber each other. */
12
+ payloadName: string;
13
+ /** Human words for the progress/warning lines, e.g. 'checklist review comment'. */
14
+ label: string;
15
+ }
16
+ /** What an upsert did, so the caller can report it without re-deriving anything. */
17
+ export declare class PrCommentResult {
18
+ /** '' when nothing was posted (no PR number, or `gh` failed). */
19
+ commentId: string;
20
+ /** True when an existing comment was PATCHed rather than a new one POSTed. */
21
+ updated: boolean;
22
+ ok: boolean;
23
+ constructor(commentId: string, updated: boolean, ok: boolean);
24
+ }
25
+ /**
26
+ * Upserts a hidden-marker-keyed comment on a PR: find ours, PATCH it if it exists, POST it if not.
27
+ *
28
+ * ONE implementation for BOTH of the gated flow's comments — the full dashboard (1st) and the reviewer
29
+ * checklist (2nd). It exists because the PR description became the git-log body: everything long-form or
30
+ * machine-facing moved into comments, so what had been one inline block of gh plumbing inside
31
+ * FinishUpsertPrCommand became something two callers needed. Per the repo's consolidate-don't-duplicate
32
+ * rule that is one parameterized class, not a copied private method.
33
+ *
34
+ * Idempotency is the whole contract. `wp-finish-upsert-pr` re-runs on every push, and a PR that
35
+ * accumulated a new dashboard comment per push would be unreadable — worse, `wp-check-pr` would have
36
+ * several gate tokens to choose between, and picking the wrong one is a red check on good code.
37
+ *
38
+ * NEVER FATAL by itself. Every caller reaches this after the PR is already up, so a `gh` hiccup warns and
39
+ * returns `ok: false`; it is the CALLER's job to decide whether that matters. It matters for the
40
+ * dashboard comment (it carries the token) and does not for the checklist comment, and those two
41
+ * judgements do not belong in here.
42
+ */
43
+ export declare class PrCommentUpserter {
44
+ upsert(request: PrCommentRequest): PrCommentResult;
45
+ /**
46
+ * The id of THIS tool's comment on the PR, found by hidden marker, or '' when absent.
47
+ *
48
+ * Takes the FIRST match. A PR should never have two, but if an older release ever left a duplicate,
49
+ * consistently patching the earliest keeps the comment in a stable position in the thread — which is
50
+ * what the PR body's "1st comment / 2nd comment" pointer relies on.
51
+ */
52
+ protected findCommentId(prNumber: string, marker: string): string;
53
+ protected gh(args: string[]): number;
54
+ }
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PrCommentUpserter = exports.PrCommentResult = exports.PrCommentRequest = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const child_process_1 = require("child_process");
6
+ const fs = tslib_1.__importStar(require("fs"));
7
+ const path = tslib_1.__importStar(require("path"));
8
+ const inversify_1 = require("inversify");
9
+ /** One marker-keyed PR comment to upsert. Data-only, per CLAUDE.md. */
10
+ class PrCommentRequest {
11
+ /** The PR number. '' means "no PR resolved" and the upsert is a silent no-op. */
12
+ prNumber = '';
13
+ /** Hidden HTML-comment marker that identifies THIS tool's comment among all comments on the PR. */
14
+ marker = '';
15
+ /** The full comment body, marker included. */
16
+ body = '';
17
+ /** Directory to stage the JSON payload in — `gh api --input` needs a file. */
18
+ payloadDir = '';
19
+ /** Payload filename, unique per comment kind so two upserts in one run cannot clobber each other. */
20
+ payloadName = '';
21
+ /** Human words for the progress/warning lines, e.g. 'checklist review comment'. */
22
+ label = '';
23
+ }
24
+ exports.PrCommentRequest = PrCommentRequest;
25
+ /** What an upsert did, so the caller can report it without re-deriving anything. */
26
+ class PrCommentResult {
27
+ /** '' when nothing was posted (no PR number, or `gh` failed). */
28
+ commentId;
29
+ /** True when an existing comment was PATCHed rather than a new one POSTed. */
30
+ updated;
31
+ ok;
32
+ constructor(commentId, updated, ok) {
33
+ this.commentId = commentId;
34
+ this.updated = updated;
35
+ this.ok = ok;
36
+ }
37
+ }
38
+ exports.PrCommentResult = PrCommentResult;
39
+ /**
40
+ * Upserts a hidden-marker-keyed comment on a PR: find ours, PATCH it if it exists, POST it if not.
41
+ *
42
+ * ONE implementation for BOTH of the gated flow's comments — the full dashboard (1st) and the reviewer
43
+ * checklist (2nd). It exists because the PR description became the git-log body: everything long-form or
44
+ * machine-facing moved into comments, so what had been one inline block of gh plumbing inside
45
+ * FinishUpsertPrCommand became something two callers needed. Per the repo's consolidate-don't-duplicate
46
+ * rule that is one parameterized class, not a copied private method.
47
+ *
48
+ * Idempotency is the whole contract. `wp-finish-upsert-pr` re-runs on every push, and a PR that
49
+ * accumulated a new dashboard comment per push would be unreadable — worse, `wp-check-pr` would have
50
+ * several gate tokens to choose between, and picking the wrong one is a red check on good code.
51
+ *
52
+ * NEVER FATAL by itself. Every caller reaches this after the PR is already up, so a `gh` hiccup warns and
53
+ * returns `ok: false`; it is the CALLER's job to decide whether that matters. It matters for the
54
+ * dashboard comment (it carries the token) and does not for the checklist comment, and those two
55
+ * judgements do not belong in here.
56
+ */
57
+ let PrCommentUpserter = class PrCommentUpserter {
58
+ upsert(request) {
59
+ if (request.prNumber === '')
60
+ return new PrCommentResult('', false, false);
61
+ fs.mkdirSync(request.payloadDir, { recursive: true });
62
+ const payload = path.join(request.payloadDir, request.payloadName);
63
+ fs.writeFileSync(payload, JSON.stringify({ body: request.body }));
64
+ const existing = this.findCommentId(request.prNumber, request.marker);
65
+ const args = existing !== ''
66
+ ? ['api', '--method', 'PATCH', `repos/{owner}/{repo}/issues/comments/${existing}`, '--input', payload]
67
+ : ['api', '--method', 'POST', `repos/{owner}/{repo}/issues/${request.prNumber}/comments`, '--input', payload];
68
+ if (this.gh(args) !== 0) {
69
+ process.stderr.write(`⚠️ Could not post the ${request.label} (non-fatal here — see the caller).\n`);
70
+ return new PrCommentResult(existing, existing !== '', false);
71
+ }
72
+ process.stdout.write(` ${existing !== '' ? 'updated' : 'posted'} the ${request.label} ✓\n`);
73
+ return new PrCommentResult(existing, existing !== '', true);
74
+ }
75
+ /**
76
+ * The id of THIS tool's comment on the PR, found by hidden marker, or '' when absent.
77
+ *
78
+ * Takes the FIRST match. A PR should never have two, but if an older release ever left a duplicate,
79
+ * consistently patching the earliest keeps the comment in a stable position in the thread — which is
80
+ * what the PR body's "1st comment / 2nd comment" pointer relies on.
81
+ */
82
+ findCommentId(prNumber, marker) {
83
+ const res = (0, child_process_1.spawnSync)('gh', [
84
+ 'api', '--paginate', `repos/{owner}/{repo}/issues/${prNumber}/comments`,
85
+ '--jq', `.[] | select(.body | contains("${marker}")) | .id`,
86
+ ], { encoding: 'utf8' });
87
+ if (res.status !== 0)
88
+ return '';
89
+ return (res.stdout ?? '').trim().split('\n')[0] ?? '';
90
+ }
91
+ // The gh seam, protected so specs can drive the upsert with no gh, no network and no PR.
92
+ gh(args) {
93
+ return (0, child_process_1.spawnSync)('gh', args, { encoding: 'utf8' }).status ?? -1;
94
+ }
95
+ };
96
+ exports.PrCommentUpserter = PrCommentUpserter;
97
+ exports.PrCommentUpserter = PrCommentUpserter = tslib_1.__decorate([
98
+ (0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
99
+ ], PrCommentUpserter);
100
+ //# sourceMappingURL=pr-comment-upserter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-comment-upserter.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/pr-comment-upserter.ts"],"names":[],"mappings":";;;;AAAA,iDAA0C;AAC1C,+CAAyB;AACzB,mDAA6B;AAC7B,yCAA2D;AAE3D,uEAAuE;AACvE,MAAa,gBAAgB;IACzB,iFAAiF;IACjF,QAAQ,GAAG,EAAE,CAAC;IACd,mGAAmG;IACnG,MAAM,GAAG,EAAE,CAAC;IACZ,8CAA8C;IAC9C,IAAI,GAAG,EAAE,CAAC;IACV,8EAA8E;IAC9E,UAAU,GAAG,EAAE,CAAC;IAChB,qGAAqG;IACrG,WAAW,GAAG,EAAE,CAAC;IACjB,mFAAmF;IACnF,KAAK,GAAG,EAAE,CAAC;CACd;AAbD,4CAaC;AAED,oFAAoF;AACpF,MAAa,eAAe;IACxB,iEAAiE;IACjE,SAAS,CAAS;IAClB,8EAA8E;IAC9E,OAAO,CAAU;IACjB,EAAE,CAAU;IAEZ,YAAY,SAAiB,EAAE,OAAgB,EAAE,EAAW;QACxD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACjB,CAAC;CACJ;AAZD,0CAYC;AAED;;;;;;;;;;;;;;;;;GAiBG;AAEI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC1B,MAAM,CAAC,OAAyB;QAC5B,IAAI,OAAO,CAAC,QAAQ,KAAK,EAAE;YAAE,OAAO,IAAI,eAAe,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAE1E,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QACnE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAElE,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,QAAQ,KAAK,EAAE;YACxB,CAAC,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,wCAAwC,QAAQ,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC;YACtG,CAAC,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,+BAA+B,OAAO,CAAC,QAAQ,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAClH,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,OAAO,CAAC,KAAK,uCAAuC,CAAC,CAAC;YACrG,OAAO,IAAI,eAAe,CAAC,QAAQ,EAAE,QAAQ,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,QAAQ,OAAO,CAAC,KAAK,MAAM,CAAC,CAAC;QAC9F,OAAO,IAAI,eAAe,CAAC,QAAQ,EAAE,QAAQ,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;OAMG;IACO,aAAa,CAAC,QAAgB,EAAE,MAAc;QACpD,MAAM,GAAG,GAAG,IAAA,yBAAS,EAAC,IAAI,EAAE;YACxB,KAAK,EAAE,YAAY,EAAE,+BAA+B,QAAQ,WAAW;YACvE,MAAM,EAAE,kCAAkC,MAAM,WAAW;SAC9D,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACzB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,CAAC;IAED,yFAAyF;IAC/E,EAAE,CAAC,IAAc;QACvB,OAAO,IAAA,yBAAS,EAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;CACJ,CAAA;AAxCY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,iBAAiB,CAwC7B","sourcesContent":["import { spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/** One marker-keyed PR comment to upsert. Data-only, per CLAUDE.md. */\nexport class PrCommentRequest {\n /** The PR number. '' means \"no PR resolved\" and the upsert is a silent no-op. */\n prNumber = '';\n /** Hidden HTML-comment marker that identifies THIS tool's comment among all comments on the PR. */\n marker = '';\n /** The full comment body, marker included. */\n body = '';\n /** Directory to stage the JSON payload in — `gh api --input` needs a file. */\n payloadDir = '';\n /** Payload filename, unique per comment kind so two upserts in one run cannot clobber each other. */\n payloadName = '';\n /** Human words for the progress/warning lines, e.g. 'checklist review comment'. */\n label = '';\n}\n\n/** What an upsert did, so the caller can report it without re-deriving anything. */\nexport class PrCommentResult {\n /** '' when nothing was posted (no PR number, or `gh` failed). */\n commentId: string;\n /** True when an existing comment was PATCHed rather than a new one POSTed. */\n updated: boolean;\n ok: boolean;\n\n constructor(commentId: string, updated: boolean, ok: boolean) {\n this.commentId = commentId;\n this.updated = updated;\n this.ok = ok;\n }\n}\n\n/**\n * Upserts a hidden-marker-keyed comment on a PR: find ours, PATCH it if it exists, POST it if not.\n *\n * ONE implementation for BOTH of the gated flow's comments — the full dashboard (1st) and the reviewer\n * checklist (2nd). It exists because the PR description became the git-log body: everything long-form or\n * machine-facing moved into comments, so what had been one inline block of gh plumbing inside\n * FinishUpsertPrCommand became something two callers needed. Per the repo's consolidate-don't-duplicate\n * rule that is one parameterized class, not a copied private method.\n *\n * Idempotency is the whole contract. `wp-finish-upsert-pr` re-runs on every push, and a PR that\n * accumulated a new dashboard comment per push would be unreadable — worse, `wp-check-pr` would have\n * several gate tokens to choose between, and picking the wrong one is a red check on good code.\n *\n * NEVER FATAL by itself. Every caller reaches this after the PR is already up, so a `gh` hiccup warns and\n * returns `ok: false`; it is the CALLER's job to decide whether that matters. It matters for the\n * dashboard comment (it carries the token) and does not for the checklist comment, and those two\n * judgements do not belong in here.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class PrCommentUpserter {\n upsert(request: PrCommentRequest): PrCommentResult {\n if (request.prNumber === '') return new PrCommentResult('', false, false);\n\n fs.mkdirSync(request.payloadDir, { recursive: true });\n const payload = path.join(request.payloadDir, request.payloadName);\n fs.writeFileSync(payload, JSON.stringify({ body: request.body }));\n\n const existing = this.findCommentId(request.prNumber, request.marker);\n const args = existing !== ''\n ? ['api', '--method', 'PATCH', `repos/{owner}/{repo}/issues/comments/${existing}`, '--input', payload]\n : ['api', '--method', 'POST', `repos/{owner}/{repo}/issues/${request.prNumber}/comments`, '--input', payload];\n if (this.gh(args) !== 0) {\n process.stderr.write(`⚠️ Could not post the ${request.label} (non-fatal here — see the caller).\\n`);\n return new PrCommentResult(existing, existing !== '', false);\n }\n process.stdout.write(` ${existing !== '' ? 'updated' : 'posted'} the ${request.label} ✓\\n`);\n return new PrCommentResult(existing, existing !== '', true);\n }\n\n /**\n * The id of THIS tool's comment on the PR, found by hidden marker, or '' when absent.\n *\n * Takes the FIRST match. A PR should never have two, but if an older release ever left a duplicate,\n * consistently patching the earliest keeps the comment in a stable position in the thread — which is\n * what the PR body's \"1st comment / 2nd comment\" pointer relies on.\n */\n protected findCommentId(prNumber: string, marker: string): string {\n const res = spawnSync('gh', [\n 'api', '--paginate', `repos/{owner}/{repo}/issues/${prNumber}/comments`,\n '--jq', `.[] | select(.body | contains(\"${marker}\")) | .id`,\n ], { encoding: 'utf8' });\n if (res.status !== 0) return '';\n return (res.stdout ?? '').trim().split('\\n')[0] ?? '';\n }\n\n // The gh seam, protected so specs can drive the upsert with no gh, no network and no PR.\n protected gh(args: string[]): number {\n return spawnSync('gh', args, { encoding: 'utf8' }).status ?? -1;\n }\n}\n"]}
@@ -180,8 +180,20 @@ let PrMerger = class PrMerger {
180
180
  // posted/updated by this point, which is the whole job in this mode.
181
181
  if (!intent.shouldMerge()) {
182
182
  const mergeMode = intent.mergeMode;
183
+ // What a UI merge produces, in terms of the repo's OWN two settings.
184
+ //
185
+ // This message said the opposite one release ago: "a UI merge pastes the whole dashboard into
186
+ // git log, land it with wp-land-pr instead". That was true while the PR description held the
187
+ // long dashboard. It is now the compact git-log body (see Dashboard.renderPrBody), so
188
+ // `squash_merge_commit_message: PR_BODY` copies exactly the right text and a UI merge is
189
+ // CORRECT — provided both settings are right. Naming them is the useful thing to say; telling
190
+ // a mergeMode=NONE repo to stop using the UI is not, since using it is the whole policy.
183
191
  return new MergeOutcome(false, false, `did NOT merge — pr-gate.mergeMode is ${mergeMode === rules_config_1.MERGE_MODE_NONE ? 'NONE' : `"${mergeMode}"`}, so the PR is left for a human to merge.\n` +
184
- ` Subject GitHub will use is its own (squash_merge_commit_title), NOT: "${subject}"`, exports.MERGE_RESULT_LEFT_TO_HUMAN);
192
+ ` A UI merge takes the commit message from this repo's own settings, and both must be\n` +
193
+ ` set for main's history to read well: squash_merge_commit_title=PR_TITLE (else the\n` +
194
+ ` subject is not "${subject}") and squash_merge_commit_message=PR_BODY (which copies\n` +
195
+ ` the gated description verbatim — that IS the commit body this flow rendered).\n` +
196
+ ` \`pnpm wp-land-pr\` lands the same bytes from the CLI if you would rather not click.`, exports.MERGE_RESULT_LEFT_TO_HUMAN);
185
197
  }
186
198
  // A direct `gh pr merge --squash --subject --body-file` writes exactly this subject/body to
187
199
  // main's history regardless of the repo's squash_merge_commit_title/message defaults — and