@webpieces/pr-gate 0.4.511 → 0.4.513
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.
- package/package.json +2 -2
- package/src/scripts/commands/finish-upsert-pr-command.js +1 -1
- package/src/scripts/commands/finish-upsert-pr-command.js.map +1 -1
- package/src/scripts/commands/review-upsert-pr-command.d.ts +3 -3
- package/src/scripts/commands/review-upsert-pr-command.js +14 -39
- package/src/scripts/commands/review-upsert-pr-command.js.map +1 -1
- package/src/scripts/commands/start-upsert-pr-command.d.ts +0 -1
- package/src/scripts/commands/start-upsert-pr-command.js +5 -17
- package/src/scripts/commands/start-upsert-pr-command.js.map +1 -1
- package/src/scripts/workflow/build-affected.d.ts +6 -1
- package/src/scripts/workflow/build-affected.js +10 -5
- package/src/scripts/workflow/build-affected.js.map +1 -1
- package/src/scripts/workflow/checklist-scanner.d.ts +15 -1
- package/src/scripts/workflow/checklist-scanner.js +19 -2
- package/src/scripts/workflow/checklist-scanner.js.map +1 -1
- package/src/scripts/workflow/diff-basis.d.ts +16 -1
- package/src/scripts/workflow/diff-basis.js +29 -3
- package/src/scripts/workflow/diff-basis.js.map +1 -1
- package/src/scripts/workflow/diff-materializer.d.ts +4 -1
- package/src/scripts/workflow/diff-materializer.js +11 -2
- package/src/scripts/workflow/diff-materializer.js.map +1 -1
- package/src/scripts/workflow/pr-context-writer.d.ts +4 -1
- package/src/scripts/workflow/pr-context-writer.js +17 -4
- package/src/scripts/workflow/pr-context-writer.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/pr-gate",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.513",
|
|
4
4
|
"description": "Gated PR system: 3-point squash-merge, merge validation gate, and red/yellow/green PR dashboard. Standalone scripts, no Nx dependency required.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"directory": "packages/tooling/pr-gate"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@webpieces/rules-config": "0.4.
|
|
29
|
+
"@webpieces/rules-config": "0.4.513",
|
|
30
30
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
31
31
|
"inversify": "7.10.4",
|
|
32
32
|
"reflect-metadata": "0.2.2"
|
|
@@ -115,7 +115,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
|
|
|
115
115
|
// applicable set for free — an unchanged checklist needs no re-review, a newly-applicable one refuses
|
|
116
116
|
// until its file is written.
|
|
117
117
|
const featureName = this.aiBranchName.getFeatureName();
|
|
118
|
-
const scan = this.checklistScanner.scan(repoRoot, (0, rules_config_1.loadAndValidate)(repoRoot).prGate.checklists, new checklist_scanner_1.ChecklistScanOptions(true));
|
|
118
|
+
const scan = this.checklistScanner.scan(repoRoot, (0, rules_config_1.loadAndValidate)(repoRoot).prGate.checklists, new checklist_scanner_1.ChecklistScanOptions(true, 'stage3-finish'));
|
|
119
119
|
const required = scan.applicable;
|
|
120
120
|
// FAIL FAST on missing reviewers, BEFORE review.json is parsed and before the build gate runs. A
|
|
121
121
|
// missing reviewer is not a review.json defect (folding it in made the AI fix the wrong thing), and
|
|
@@ -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 ChecklistInstructionsService, InformAiError,\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 { 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, MergeOutcome, MERGE_RESULT_FAILED } from '../workflow/pr-merger';\nimport { FinishBanner, FinishBannerInput } from '../workflow/finish-banner';\nimport { GatedPrPublisher } from '../workflow/gated-pr-publisher';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport {\n Dashboard, DashboardInput, ChecklistRow, ChecklistCommentRow, CHECKLIST_COMMENT_MARKER,\n} from '../../dashboard/dashboard';\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 reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n private readonly provenance: SubagentProvenanceService,\n private readonly instructions: ChecklistInstructionsService,\n private readonly receipts: ReviewStageReceiptService,\n private readonly banner: FinishBanner,\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));\n const required = scan.applicable;\n // FAIL FAST on missing reviewers, 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.\n this.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(required, 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, required);\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\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 * Refuse the PR while ANY applicable checklist still owes a verdict, naming exactly those reviewers and\n * exactly what to tell them — via the same renderer `wp-review-upsert-pr` uses, so the AI sees the identical\n * block it would have seen there. Lists ONLY the outstanding ones: re-instructing an already-reviewed\n * checklist invites a redundant second run and reads as though the earlier verdict did not count.\n * No-op for a repo with no applicable checklists.\n */\n private assertEveryReviewerRan(scan: ChecklistScan): void {\n if (scan.outstanding.length === 0) return;\n // Unreadable verdict files come FIRST. A reviewer that wrote its verdict in the removed `success`\n // format is otherwise indistinguishable from one that never ran, and the AI would go re-run a\n // subagent instead of correcting four characters of JSON.\n const format = scan.formatErrors.length === 0\n ? ''\n : `${scan.formatErrors.length} verdict file(s) are in an UNREADABLE format:\\n\\n` +\n scan.formatErrors.map((e: string): string => ` • ${e}`).join('\\n') + '\\n\\n';\n throw new InformAiError(\n `⛔ NO PR — ${scan.outstanding.length} of ${scan.applicable.length} review checklist(s) that apply to this ` +\n `branch have no passing verdict yet: ${this.instructions.names(scan.outstanding)}\\n\\n` +\n format +\n `${this.instructions.render(scan.outstanding, scan.reviewPath, scan.context)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr\\n` +\n `(Each reviewer's generated instructions file is already written — re-running wp-review-upsert-pr\\n` +\n ` is only needed if the code changed since it ran.)`,\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 // Pair each matched checklist with its resolved verdict for the dashboard. (A checklist reaching this\n // point is always PASS/OVERRIDDEN — loadReviewJson already threw on FAIL/MISSING.)\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 // 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 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 if (subagents.length > 0) {\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 }\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 * 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 const mergeBodyFile = path.join(prDir, 'merge-commit-body.md');\n fs.writeFileSync(mergeBodyFile, this.dashboard.renderCommitBody(input, ref.url) + '\\n');\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 const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, mergeMode);\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":"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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 ChecklistInstructionsService, InformAiError,\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 { 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, MergeOutcome, MERGE_RESULT_FAILED } from '../workflow/pr-merger';\nimport { FinishBanner, FinishBannerInput } from '../workflow/finish-banner';\nimport { GatedPrPublisher } from '../workflow/gated-pr-publisher';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport {\n Dashboard, DashboardInput, ChecklistRow, ChecklistCommentRow, CHECKLIST_COMMENT_MARKER,\n} from '../../dashboard/dashboard';\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 reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n private readonly provenance: SubagentProvenanceService,\n private readonly instructions: ChecklistInstructionsService,\n private readonly receipts: ReviewStageReceiptService,\n private readonly banner: FinishBanner,\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 // FAIL FAST on missing reviewers, 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.\n this.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(required, 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, required);\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\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 * Refuse the PR while ANY applicable checklist still owes a verdict, naming exactly those reviewers and\n * exactly what to tell them — via the same renderer `wp-review-upsert-pr` uses, so the AI sees the identical\n * block it would have seen there. Lists ONLY the outstanding ones: re-instructing an already-reviewed\n * checklist invites a redundant second run and reads as though the earlier verdict did not count.\n * No-op for a repo with no applicable checklists.\n */\n private assertEveryReviewerRan(scan: ChecklistScan): void {\n if (scan.outstanding.length === 0) return;\n // Unreadable verdict files come FIRST. A reviewer that wrote its verdict in the removed `success`\n // format is otherwise indistinguishable from one that never ran, and the AI would go re-run a\n // subagent instead of correcting four characters of JSON.\n const format = scan.formatErrors.length === 0\n ? ''\n : `${scan.formatErrors.length} verdict file(s) are in an UNREADABLE format:\\n\\n` +\n scan.formatErrors.map((e: string): string => ` • ${e}`).join('\\n') + '\\n\\n';\n throw new InformAiError(\n `⛔ NO PR — ${scan.outstanding.length} of ${scan.applicable.length} review checklist(s) that apply to this ` +\n `branch have no passing verdict yet: ${this.instructions.names(scan.outstanding)}\\n\\n` +\n format +\n `${this.instructions.render(scan.outstanding, scan.reviewPath, scan.context)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr\\n` +\n `(Each reviewer's generated instructions file is already written — re-running wp-review-upsert-pr\\n` +\n ` is only needed if the code changed since it ran.)`,\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 // Pair each matched checklist with its resolved verdict for the dashboard. (A checklist reaching this\n // point is always PASS/OVERRIDDEN — loadReviewJson already threw on FAIL/MISSING.)\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 // 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 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 if (subagents.length > 0) {\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 }\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 * 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 const mergeBodyFile = path.join(prDir, 'merge-commit-body.md');\n fs.writeFileSync(mergeBodyFile, this.dashboard.renderCommitBody(input, ref.url) + '\\n');\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 const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, mergeMode);\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"]}
|
|
@@ -8,6 +8,7 @@ import { DiffMaterializer } from '../workflow/diff-materializer';
|
|
|
8
8
|
import { GitExec } from '../workflow/git-exec';
|
|
9
9
|
import { MergeEnd } from '../workflow/merge-end';
|
|
10
10
|
import { MergeState } from '../workflow/merge-state';
|
|
11
|
+
import { PrContextWriter } from '../workflow/pr-context-writer';
|
|
11
12
|
import { ReviewerBriefingBuilder } from '../workflow/reviewer-briefing-builder';
|
|
12
13
|
import { ReviewStageReceiptService } from '../workflow/review-stage-receipt';
|
|
13
14
|
/**
|
|
@@ -45,10 +46,11 @@ export declare class ReviewUpsertPrCommand {
|
|
|
45
46
|
private readonly checklistNotice;
|
|
46
47
|
private readonly instructions;
|
|
47
48
|
private readonly materializer;
|
|
49
|
+
private readonly prContextWriter;
|
|
48
50
|
private readonly briefingBuilder;
|
|
49
51
|
private readonly reviewerInstructions;
|
|
50
52
|
private readonly receipts;
|
|
51
|
-
constructor(repoRootFinder: RepoRootFinder, aiBranchName: AiBranchName, gitExec: GitExec, buildAffected: BuildAffected, buildArtifactGate: BuildArtifactGate, mergeState: MergeState, mergeEnd: MergeEnd, checklistScanner: ChecklistScanner, checklistNotice: ChecklistNotice, instructions: ChecklistInstructionsService, materializer: DiffMaterializer, briefingBuilder: ReviewerBriefingBuilder, reviewerInstructions: ReviewerInstructionsService, receipts: ReviewStageReceiptService);
|
|
53
|
+
constructor(repoRootFinder: RepoRootFinder, aiBranchName: AiBranchName, gitExec: GitExec, buildAffected: BuildAffected, buildArtifactGate: BuildArtifactGate, mergeState: MergeState, mergeEnd: MergeEnd, checklistScanner: ChecklistScanner, checklistNotice: ChecklistNotice, instructions: ChecklistInstructionsService, materializer: DiffMaterializer, prContextWriter: PrContextWriter, briefingBuilder: ReviewerBriefingBuilder, reviewerInstructions: ReviewerInstructionsService, receipts: ReviewStageReceiptService);
|
|
52
54
|
run(): Promise<void>;
|
|
53
55
|
/**
|
|
54
56
|
* Validate + commit + finalize a conflict resolution, if one is in flight. MOVED here from
|
|
@@ -76,8 +78,6 @@ export declare class ReviewUpsertPrCommand {
|
|
|
76
78
|
* reviewer reads covers exactly the files its checklist matched against.
|
|
77
79
|
*/
|
|
78
80
|
private briefReviewers;
|
|
79
|
-
private rewritePrContext;
|
|
80
|
-
private printDiffSummary;
|
|
81
81
|
/** What the AI must do next: spawn each reviewer, then write review.json. */
|
|
82
82
|
private report;
|
|
83
83
|
/**
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ReviewUpsertPrCommand = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
-
const path = tslib_1.__importStar(require("path"));
|
|
7
6
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
8
7
|
const inversify_1 = require("inversify");
|
|
9
8
|
const git_readAiBranchName_1 = require("../workflow/git-readAiBranchName");
|
|
@@ -16,6 +15,7 @@ const git_exec_1 = require("../workflow/git-exec");
|
|
|
16
15
|
const merge_start_1 = require("../workflow/merge-start");
|
|
17
16
|
const merge_end_1 = require("../workflow/merge-end");
|
|
18
17
|
const merge_state_1 = require("../workflow/merge-state");
|
|
18
|
+
const pr_context_writer_1 = require("../workflow/pr-context-writer");
|
|
19
19
|
const reviewer_briefing_builder_1 = require("../workflow/reviewer-briefing-builder");
|
|
20
20
|
const review_stage_receipt_1 = require("../workflow/review-stage-receipt");
|
|
21
21
|
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
@@ -54,11 +54,12 @@ let ReviewUpsertPrCommand = class ReviewUpsertPrCommand {
|
|
|
54
54
|
checklistNotice;
|
|
55
55
|
instructions;
|
|
56
56
|
materializer;
|
|
57
|
+
prContextWriter;
|
|
57
58
|
briefingBuilder;
|
|
58
59
|
reviewerInstructions;
|
|
59
60
|
receipts;
|
|
60
61
|
// eslint-disable-next-line @typescript-eslint/max-params
|
|
61
|
-
constructor(repoRootFinder, aiBranchName, gitExec, buildAffected, buildArtifactGate, mergeState, mergeEnd, checklistScanner, checklistNotice, instructions, materializer, briefingBuilder, reviewerInstructions, receipts) {
|
|
62
|
+
constructor(repoRootFinder, aiBranchName, gitExec, buildAffected, buildArtifactGate, mergeState, mergeEnd, checklistScanner, checklistNotice, instructions, materializer, prContextWriter, briefingBuilder, reviewerInstructions, receipts) {
|
|
62
63
|
this.repoRootFinder = repoRootFinder;
|
|
63
64
|
this.aiBranchName = aiBranchName;
|
|
64
65
|
this.gitExec = gitExec;
|
|
@@ -70,6 +71,7 @@ let ReviewUpsertPrCommand = class ReviewUpsertPrCommand {
|
|
|
70
71
|
this.checklistNotice = checklistNotice;
|
|
71
72
|
this.instructions = instructions;
|
|
72
73
|
this.materializer = materializer;
|
|
74
|
+
this.prContextWriter = prContextWriter;
|
|
73
75
|
this.briefingBuilder = briefingBuilder;
|
|
74
76
|
this.reviewerInstructions = reviewerInstructions;
|
|
75
77
|
this.receipts = receipts;
|
|
@@ -83,7 +85,7 @@ let ReviewUpsertPrCommand = class ReviewUpsertPrCommand {
|
|
|
83
85
|
const mergeValidated = await this.finalizeAnyInProgressMerge(repoRoot);
|
|
84
86
|
this.gitExec.assertCleanTree(repoRoot);
|
|
85
87
|
const buildPassedAt = this.runBuildGate(repoRoot);
|
|
86
|
-
const scan = this.checklistScanner.scan(repoRoot, config.checklists, new checklist_scanner_1.ChecklistScanOptions(false));
|
|
88
|
+
const scan = this.checklistScanner.scan(repoRoot, config.checklists, new checklist_scanner_1.ChecklistScanOptions(false, '')); // '' — THIS command writes the context itself, after materializing
|
|
87
89
|
const briefings = this.briefReviewers(repoRoot, featureName, scan, config);
|
|
88
90
|
this.receipts.write(repoRoot, featureName, new review_stage_receipt_1.ReviewStageReceipt(scan.basis.headSha, mergeValidated, this.buildAffected.resolveBuildCommand(repoRoot), buildPassedAt, briefings.map((b) => b.subagent)));
|
|
89
91
|
this.report(repoRoot, featureName, scan, briefings);
|
|
@@ -117,7 +119,7 @@ let ReviewUpsertPrCommand = class ReviewUpsertPrCommand {
|
|
|
117
119
|
* earlier did not turn one build into two.
|
|
118
120
|
*/
|
|
119
121
|
runBuildGate(repoRoot) {
|
|
120
|
-
this.buildAffected.runBuildGate(repoRoot, new build_affected_1.BuildGateOptions('🛠️ Build gate
|
|
122
|
+
this.buildAffected.runBuildGate(repoRoot, new build_affected_1.BuildGateOptions('🛠️ Build gate', 'pnpm wp-review-upsert-pr', 'Build failed — NO reviewer was briefed and no diff was extracted. Fix it, then re-run.'));
|
|
121
123
|
// Repo-wide: the build must not have left anything uncommitted AND unstaged. Runs HERE, not in
|
|
122
124
|
// finish, because this is the stage that ran buildCommand and is therefore holding the dirty
|
|
123
125
|
// tree at the exact moment the question is answerable. Replaces the per-project
|
|
@@ -136,9 +138,11 @@ let ReviewUpsertPrCommand = class ReviewUpsertPrCommand {
|
|
|
136
138
|
return [];
|
|
137
139
|
const manifest = this.materializer.materialize(repoRoot, featureName, scan.basis, scan.changedFiles, config.reviewDiffExclude);
|
|
138
140
|
const diffDir = this.materializer.diffDirFor(repoRoot, featureName);
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
141
|
+
// Write pr-context.json ONCE, here — after materializing, so `diffDir` is populated on the first
|
|
142
|
+
// write. The scan deliberately did not write it (ChecklistScanOptions.contextStage === ''); it used
|
|
143
|
+
// to, which meant this command wrote the same file twice, the first time with an empty diffDir.
|
|
144
|
+
// `scan.changedFiles` is passed through so the context is not recomputed from a second git call.
|
|
145
|
+
scan.context = this.prContextWriter.ensure(repoRoot, featureName, scan.basis, 'stage2-review', scan.changedFiles, diffDir);
|
|
142
146
|
const briefings = this.briefingBuilder.build(repoRoot, scan, manifest, diffDir, config);
|
|
143
147
|
const dir = this.reviewerInstructions.instructionsDirFor(repoRoot, featureName);
|
|
144
148
|
fs.rmSync(dir, { recursive: true, force: true }); // stale instructions read as current are worse than none
|
|
@@ -146,48 +150,18 @@ let ReviewUpsertPrCommand = class ReviewUpsertPrCommand {
|
|
|
146
150
|
for (const b of briefings) {
|
|
147
151
|
fs.writeFileSync(this.reviewerInstructions.pathFor(repoRoot, featureName, b.subagent), this.reviewerInstructions.render(b));
|
|
148
152
|
}
|
|
149
|
-
this.printDiffSummary(manifest, diffDir);
|
|
150
153
|
return briefings;
|
|
151
154
|
}
|
|
152
|
-
// pr-context.json is written by the scan (before the diff exists). Patch in the dir rather than
|
|
153
|
-
// re-deriving the whole context: re-deriving means a second basis resolution, which is the drift.
|
|
154
|
-
rewritePrContext(repoRoot, featureName, diffDir) {
|
|
155
|
-
const p = path.join(repoRoot, '.webpieces', 'pr-review', featureName, 'pr-context.json');
|
|
156
|
-
if (!fs.existsSync(p))
|
|
157
|
-
return;
|
|
158
|
-
// webpieces-disable no-unmanaged-exceptions -- chokepoint: an unreadable context is left as-is, never fatal
|
|
159
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
160
|
-
try {
|
|
161
|
-
// webpieces-disable no-any-unknown -- opaque parsed JSON, one key added
|
|
162
|
-
const raw = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
163
|
-
raw['diffDir'] = diffDir;
|
|
164
|
-
fs.writeFileSync(p, JSON.stringify(raw, null, 2) + '\n');
|
|
165
|
-
}
|
|
166
|
-
catch (err) {
|
|
167
|
-
const error = (0, rules_config_1.toError)(err);
|
|
168
|
-
void error; // leave the file as-is — the instructions files carry the same paths directly
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
printDiffSummary(manifest, diffDir) {
|
|
172
|
-
process.stdout.write('\n' + SEP + '② Diff extracted for the reviewers\n' + SEP + '\n');
|
|
173
|
-
process.stdout.write(` ${manifest.entries.length} file(s) → ${diffDir}\n`);
|
|
174
|
-
if (manifest.excluded.length > 0) {
|
|
175
|
-
process.stdout.write(` ${manifest.excluded.length} excluded by pr-gate.reviewDiffExclude (stubbed, still matched)\n`);
|
|
176
|
-
}
|
|
177
|
-
const truncated = manifest.entries.filter((e) => e.truncated).length;
|
|
178
|
-
if (truncated > 0)
|
|
179
|
-
process.stdout.write(` ${truncated} truncated at the per-file cap (each says so in its footer)\n`);
|
|
180
|
-
}
|
|
181
155
|
/** What the AI must do next: spawn each reviewer, then write review.json. */
|
|
182
156
|
report(repoRoot, featureName, scan, briefings) {
|
|
183
|
-
process.stdout.write('\n' + SEP + '
|
|
157
|
+
process.stdout.write('\n' + SEP + '② Spawn Subagent Reviews, then finish\n' + SEP + '\n');
|
|
184
158
|
if (scan.applicable.length === 0) {
|
|
185
159
|
process.stdout.write(this.checklistNotice.build(scan.defined.length, 'wp-finish-upsert-pr'));
|
|
186
160
|
}
|
|
187
161
|
else {
|
|
188
162
|
process.stdout.write(this.spawnBlocks(repoRoot, featureName, scan, briefings));
|
|
189
163
|
}
|
|
190
|
-
process.stdout.write(`\
|
|
164
|
+
process.stdout.write(`\nWhile your subagents are running, review your own changes and\n${(0, rules_config_1.reviewJsonSchemaHint)(scan.reviewPath)}\n\n` +
|
|
191
165
|
`Finally run: pnpm wp-finish-upsert-pr\n` +
|
|
192
166
|
`(The build gate is already green for this commit — finish reuses it unless HEAD moves.)\n\n`);
|
|
193
167
|
}
|
|
@@ -246,6 +220,7 @@ exports.ReviewUpsertPrCommand = ReviewUpsertPrCommand = tslib_1.__decorate([
|
|
|
246
220
|
checklist_notice_1.ChecklistNotice,
|
|
247
221
|
rules_config_1.ChecklistInstructionsService,
|
|
248
222
|
diff_materializer_1.DiffMaterializer,
|
|
223
|
+
pr_context_writer_1.PrContextWriter,
|
|
249
224
|
reviewer_briefing_builder_1.ReviewerBriefingBuilder,
|
|
250
225
|
rules_config_1.ReviewerInstructionsService,
|
|
251
226
|
review_stage_receipt_1.ReviewStageReceiptService])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/review-upsert-pr-command.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate, reviewJsonSchemaHint, writeTemplate, PrGateConfig, RepoRootFinder,\n ChecklistInstructionsService, RequiredChecklist, ReviewerBriefing, ReviewerInstructionsService, toError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { BuildArtifactGate } from '../workflow/build-artifact-gate';\nimport { ChecklistNotice } from '../workflow/checklist-notice';\nimport { ChecklistScan, ChecklistScanOptions, ChecklistScanner } from '../workflow/checklist-scanner';\nimport { DiffManifest, DiffManifestEntry, DiffMaterializer } from '../workflow/diff-materializer';\nimport { GitExec } from '../workflow/git-exec';\nimport { MergeContext } from '../workflow/merge-start';\nimport { MergeEnd, MergeEndOptions } from '../workflow/merge-end';\nimport { MergeState } from '../workflow/merge-state';\nimport { ReviewerBriefingBuilder } from '../workflow/reviewer-briefing-builder';\nimport { ReviewStageReceipt, ReviewStageReceiptService } from '../workflow/review-stage-receipt';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * `wp-review-upsert-pr` — STAGE ② of the three-stage PR flow, between `wp-start-upsert-pr` (sync from main)\n * and `wp-finish-upsert-pr` (post the PR).\n *\n * It exists because reviewing came before verifying. Under the old two-command flow the 3-point merge was\n * finalized and the build gate ran inside `wp-finish-upsert-pr` — i.e. AFTER the reviewers had already run.\n * A reviewer could therefore spend a full review on an unresolved merge, or on code that does not compile,\n * and only afterwards would the branch be checked. This command puts the verification first:\n *\n * 1. finalize + VALIDATE any in-progress 3-point merge (moved here from finish)\n * 2. assert a clean tree (meaningful now — step 1 committed the resolution)\n * 3. run the build gate (on the merged, committed tree)\n * 4. scan the checklists, EXTRACT the diff, BRIEF the reviewers\n * 5. write the stage receipt, then print what to spawn + the review.json schema\n *\n * Unlike the report-only command it replaces — which always exited 0, because reporting is not gating — this\n * command CAN FAIL, at steps 1, 2 and 3. That is the point: it fails BEFORE any reviewer is spawned, so a\n * branch with an unresolved merge or a broken build costs zero reviewer tokens.\n *\n * The ordering of 1 → 2 → 3 is load-bearing. MergeEnd stages and commits the conflict resolution, so the\n * tree is clean by the time the build runs; building first would either build a dirty tree (proving nothing\n * about what ships) or refuse a tree that is legitimately mid-resolution.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class ReviewUpsertPrCommand {\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly gitExec: GitExec,\n private readonly buildAffected: BuildAffected,\n private readonly buildArtifactGate: BuildArtifactGate,\n private readonly mergeState: MergeState,\n private readonly mergeEnd: MergeEnd,\n private readonly checklistScanner: ChecklistScanner,\n private readonly checklistNotice: ChecklistNotice,\n private readonly instructions: ChecklistInstructionsService,\n private readonly materializer: DiffMaterializer,\n private readonly briefingBuilder: ReviewerBriefingBuilder,\n private readonly reviewerInstructions: ReviewerInstructionsService,\n private readonly receipts: ReviewStageReceiptService,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n writeTemplate(repoRoot, 'webpieces.review-checklists.md');\n const featureName = this.aiBranchName.getFeatureName();\n const config = loadAndValidate(repoRoot).prGate;\n\n const mergeValidated = await this.finalizeAnyInProgressMerge(repoRoot);\n this.gitExec.assertCleanTree(repoRoot);\n const buildPassedAt = this.runBuildGate(repoRoot);\n\n const scan = this.checklistScanner.scan(repoRoot, config.checklists, new ChecklistScanOptions(false));\n const briefings = this.briefReviewers(repoRoot, featureName, scan, config);\n this.receipts.write(repoRoot, featureName, new ReviewStageReceipt(\n scan.basis.headSha, mergeValidated, this.buildAffected.resolveBuildCommand(repoRoot), buildPassedAt,\n briefings.map((b: ReviewerBriefing): string => b.subagent),\n ));\n this.report(repoRoot, featureName, scan, briefings);\n }\n\n /**\n * Validate + commit + finalize a conflict resolution, if one is in flight. MOVED here from\n * `wp-finish-upsert-pr` so that exactly one command owns merge finalization — two owners is the drift\n * failure `PrContextWriter`'s own docstring records having already happened once in this codebase.\n *\n * MergeEnd's three checks are what \"did the AI do the 3-point merge right?\" means concretely: no\n * conflict markers left in the scoped files, `git diff --diff-filter=U` empty, and a non-empty\n * merge-explanation.md beside every conflicted file.\n *\n * Returns true when a merge was finalized OR there was none to finalize; it throws rather than returning\n * false when validation fails, so the receipt can never record an unvalidated merge as validated.\n */\n private async finalizeAnyInProgressMerge(repoRoot: string): Promise<boolean> {\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 true;\n process.stdout.write('\\n' + SEP + '① Validating your 3-point merge\\n' + SEP + '\\n');\n // pushRemote:false — finish still owns the single push.\n await this.mergeEnd.mergeEnd(\n repoRoot, 'wp-review-upsert-pr', activeDir,\n new MergeContext(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber),\n new MergeEndOptions(marker.conflictedFiles, false),\n );\n return true;\n }\n\n /**\n * The build gate, run BEFORE any reviewer is briefed. Returns the ISO time it passed, for the receipt —\n * which is what lets `wp-finish-upsert-pr` skip its own gate when HEAD has not moved, so moving the gate\n * earlier did not turn one build into two.\n */\n private runBuildGate(repoRoot: string): string {\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate (pre-review)',\n 'pnpm wp-review-upsert-pr',\n 'Build failed — NO reviewer was briefed and no diff was extracted. Fix it, then re-run.',\n ));\n // Repo-wide: the build must not have left anything uncommitted AND unstaged. Runs HERE, not in\n // finish, because this is the stage that ran buildCommand and is therefore holding the dirty\n // tree at the exact moment the question is answerable. Replaces the per-project\n // validate-di-graph-unchanged nx target — see BuildArtifactGate for why that one had to go.\n this.buildArtifactGate.assertBuildLeftNothingUncommitted(repoRoot);\n return new Date().toISOString();\n }\n\n /**\n * Extract the diff once, then write one instructions file per reviewer.\n *\n * The changed-file set and the basis both come off the SCAN rather than being recomputed, so the diff a\n * reviewer reads covers exactly the files its checklist matched against.\n */\n private briefReviewers(repoRoot: string, featureName: string, scan: ChecklistScan, config: PrGateConfig): ReviewerBriefing[] {\n if (scan.basis.unresolved) return [];\n const manifest = this.materializer.materialize(\n repoRoot, featureName, scan.basis, scan.changedFiles, config.reviewDiffExclude);\n const diffDir = this.materializer.diffDirFor(repoRoot, featureName);\n // Re-point pr-context.json at the extracted diff now that there is one — the scan wrote it before\n // materialization, so without this the reviewers' own context file would not mention the diff dir.\n this.rewritePrContext(repoRoot, featureName, diffDir);\n const briefings = this.briefingBuilder.build(repoRoot, scan, manifest, diffDir, config);\n const dir = this.reviewerInstructions.instructionsDirFor(repoRoot, featureName);\n fs.rmSync(dir, { recursive: true, force: true }); // stale instructions read as current are worse than none\n fs.mkdirSync(dir, { recursive: true });\n for (const b of briefings) {\n fs.writeFileSync(this.reviewerInstructions.pathFor(repoRoot, featureName, b.subagent),\n this.reviewerInstructions.render(b));\n }\n this.printDiffSummary(manifest, diffDir);\n return briefings;\n }\n\n // pr-context.json is written by the scan (before the diff exists). Patch in the dir rather than\n // re-deriving the whole context: re-deriving means a second basis resolution, which is the drift.\n private rewritePrContext(repoRoot: string, featureName: string, diffDir: string): void {\n const p = path.join(repoRoot, '.webpieces', 'pr-review', featureName, 'pr-context.json');\n if (!fs.existsSync(p)) return;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: an unreadable context is left as-is, never fatal\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- opaque parsed JSON, one key added\n const raw = JSON.parse(fs.readFileSync(p, 'utf8')) as Record<string, unknown>;\n raw['diffDir'] = diffDir;\n fs.writeFileSync(p, JSON.stringify(raw, null, 2) + '\\n');\n } catch (err: unknown) {\n const error = toError(err);\n void error; // leave the file as-is — the instructions files carry the same paths directly\n }\n }\n\n private printDiffSummary(manifest: DiffManifest, diffDir: string): void {\n process.stdout.write('\\n' + SEP + '② Diff extracted for the reviewers\\n' + SEP + '\\n');\n process.stdout.write(` ${manifest.entries.length} file(s) → ${diffDir}\\n`);\n if (manifest.excluded.length > 0) {\n process.stdout.write(` ${manifest.excluded.length} excluded by pr-gate.reviewDiffExclude (stubbed, still matched)\\n`);\n }\n const truncated = manifest.entries.filter((e: DiffManifestEntry): boolean => e.truncated).length;\n if (truncated > 0) process.stdout.write(` ${truncated} truncated at the per-file cap (each says so in its footer)\\n`);\n }\n\n /** What the AI must do next: spawn each reviewer, then write review.json. */\n private report(repoRoot: string, featureName: string, scan: ChecklistScan, briefings: readonly ReviewerBriefing[]): void {\n process.stdout.write('\\n' + SEP + '③ Review, then finish\\n' + SEP + '\\n');\n if (scan.applicable.length === 0) {\n process.stdout.write(this.checklistNotice.build(scan.defined.length, 'wp-finish-upsert-pr'));\n } else {\n process.stdout.write(this.spawnBlocks(repoRoot, featureName, scan, briefings));\n }\n process.stdout.write(\n `\\nThen review your own changes and\\n${reviewJsonSchemaHint(scan.reviewPath)}\\n\\n` +\n `Finally run: pnpm wp-finish-upsert-pr\\n` +\n `(The build gate is already green for this commit — finish reuses it unless HEAD moves.)\\n\\n`);\n }\n\n /**\n * One copy-paste block per owed reviewer. The prompt is deliberately a POINTER and nothing else: the\n * generated instructions file is the contract, so anything restated here is a second copy that can go\n * stale — which is exactly how a removed `success` field outlived its own removal in print.\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private spawnBlocks(repoRoot: string, featureName: string, scan: ChecklistScan, briefings: readonly ReviewerBriefing[]): string {\n const reviewedIds = new Set(scan.reviewed.map((r: RequiredChecklist): string => r.id));\n const owed = briefings.filter((b: ReviewerBriefing): boolean => !reviewedIds.has(b.checklistId));\n const lines: string[] = [];\n for (const r of scan.reviewed) {\n lines.push(` ✓ ${r.subagent} — already reviewed on this branch (reusing its review-${r.id}.json)`);\n }\n // A verdict file that EXISTS but is unreadable as a verdict is called out here. Without it this\n // reports the checklist as simply owed, and the AI re-runs a reviewer that already ran instead of\n // correcting the file sitting right there.\n for (const e of scan.formatErrors) lines.push(` ⛔ ${e}`);\n if (owed.length === 0) {\n lines.push('', '✅ Every checklist that applies is already reviewed — nothing to spawn.');\n return lines.join('\\n') + '\\n';\n }\n lines.push('',\n `Spawn these ${owed.length} reviewer subagent(s) — a SEPARATE one each. You may NOT review your own`,\n 'work, and you may NOT write a reviewer\\'s verdict file on its behalf.',\n '');\n for (const b of owed) {\n lines.push(` ▶ ${b.subagent} — ${this.why(b)}`);\n lines.push(` subagent_type: ${b.subagent}`);\n lines.push(' prompt: Read your instructions file FIRST and follow it exactly:');\n lines.push(` ${this.reviewerInstructions.pathFor(repoRoot, featureName, b.subagent)}`);\n lines.push('');\n }\n return lines.join('\\n');\n }\n\n // Why this one is in scope. A patternless checklist is NOT \"matched\" — it always runs, over the whole\n // diff, and saying so is what tells a repo its checklist is firing on docs-only PRs by design.\n private why(b: ReviewerBriefing): string {\n if (b.matchedPatterns.length === 0) {\n return `ALWAYS RUNS (no \"patterns\" configured), whole diff in scope — ${b.myFiles.length} file(s)`;\n }\n return `${b.myFiles.length} file(s) matched ${b.matchedPatterns.map((p: string): string => `\"${p}\"`).join(', ')}`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"review-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/review-upsert-pr-command.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate, reviewJsonSchemaHint, writeTemplate, PrGateConfig, RepoRootFinder,\n ChecklistInstructionsService, RequiredChecklist, ReviewerBriefing, ReviewerInstructionsService, toError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { BuildArtifactGate } from '../workflow/build-artifact-gate';\nimport { ChecklistNotice } from '../workflow/checklist-notice';\nimport { ChecklistScan, ChecklistScanOptions, ChecklistScanner } from '../workflow/checklist-scanner';\nimport { DiffMaterializer } from '../workflow/diff-materializer';\nimport { GitExec } from '../workflow/git-exec';\nimport { MergeContext } from '../workflow/merge-start';\nimport { MergeEnd, MergeEndOptions } from '../workflow/merge-end';\nimport { MergeState } from '../workflow/merge-state';\nimport { PrContextWriter } from '../workflow/pr-context-writer';\nimport { ReviewerBriefingBuilder } from '../workflow/reviewer-briefing-builder';\nimport { ReviewStageReceipt, ReviewStageReceiptService } from '../workflow/review-stage-receipt';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * `wp-review-upsert-pr` — STAGE ② of the three-stage PR flow, between `wp-start-upsert-pr` (sync from main)\n * and `wp-finish-upsert-pr` (post the PR).\n *\n * It exists because reviewing came before verifying. Under the old two-command flow the 3-point merge was\n * finalized and the build gate ran inside `wp-finish-upsert-pr` — i.e. AFTER the reviewers had already run.\n * A reviewer could therefore spend a full review on an unresolved merge, or on code that does not compile,\n * and only afterwards would the branch be checked. This command puts the verification first:\n *\n * 1. finalize + VALIDATE any in-progress 3-point merge (moved here from finish)\n * 2. assert a clean tree (meaningful now — step 1 committed the resolution)\n * 3. run the build gate (on the merged, committed tree)\n * 4. scan the checklists, EXTRACT the diff, BRIEF the reviewers\n * 5. write the stage receipt, then print what to spawn + the review.json schema\n *\n * Unlike the report-only command it replaces — which always exited 0, because reporting is not gating — this\n * command CAN FAIL, at steps 1, 2 and 3. That is the point: it fails BEFORE any reviewer is spawned, so a\n * branch with an unresolved merge or a broken build costs zero reviewer tokens.\n *\n * The ordering of 1 → 2 → 3 is load-bearing. MergeEnd stages and commits the conflict resolution, so the\n * tree is clean by the time the build runs; building first would either build a dirty tree (proving nothing\n * about what ships) or refuse a tree that is legitimately mid-resolution.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class ReviewUpsertPrCommand {\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly gitExec: GitExec,\n private readonly buildAffected: BuildAffected,\n private readonly buildArtifactGate: BuildArtifactGate,\n private readonly mergeState: MergeState,\n private readonly mergeEnd: MergeEnd,\n private readonly checklistScanner: ChecklistScanner,\n private readonly checklistNotice: ChecklistNotice,\n private readonly instructions: ChecklistInstructionsService,\n private readonly materializer: DiffMaterializer,\n private readonly prContextWriter: PrContextWriter,\n private readonly briefingBuilder: ReviewerBriefingBuilder,\n private readonly reviewerInstructions: ReviewerInstructionsService,\n private readonly receipts: ReviewStageReceiptService,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n writeTemplate(repoRoot, 'webpieces.review-checklists.md');\n const featureName = this.aiBranchName.getFeatureName();\n const config = loadAndValidate(repoRoot).prGate;\n\n const mergeValidated = await this.finalizeAnyInProgressMerge(repoRoot);\n this.gitExec.assertCleanTree(repoRoot);\n const buildPassedAt = this.runBuildGate(repoRoot);\n\n const scan = this.checklistScanner.scan(repoRoot, config.checklists, new ChecklistScanOptions(false, '')); // '' — THIS command writes the context itself, after materializing\n const briefings = this.briefReviewers(repoRoot, featureName, scan, config);\n this.receipts.write(repoRoot, featureName, new ReviewStageReceipt(\n scan.basis.headSha, mergeValidated, this.buildAffected.resolveBuildCommand(repoRoot), buildPassedAt,\n briefings.map((b: ReviewerBriefing): string => b.subagent),\n ));\n this.report(repoRoot, featureName, scan, briefings);\n }\n\n /**\n * Validate + commit + finalize a conflict resolution, if one is in flight. MOVED here from\n * `wp-finish-upsert-pr` so that exactly one command owns merge finalization — two owners is the drift\n * failure `PrContextWriter`'s own docstring records having already happened once in this codebase.\n *\n * MergeEnd's three checks are what \"did the AI do the 3-point merge right?\" means concretely: no\n * conflict markers left in the scoped files, `git diff --diff-filter=U` empty, and a non-empty\n * merge-explanation.md beside every conflicted file.\n *\n * Returns true when a merge was finalized OR there was none to finalize; it throws rather than returning\n * false when validation fails, so the receipt can never record an unvalidated merge as validated.\n */\n private async finalizeAnyInProgressMerge(repoRoot: string): Promise<boolean> {\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 true;\n process.stdout.write('\\n' + SEP + '① Validating your 3-point merge\\n' + SEP + '\\n');\n // pushRemote:false — finish still owns the single push.\n await this.mergeEnd.mergeEnd(\n repoRoot, 'wp-review-upsert-pr', activeDir,\n new MergeContext(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber),\n new MergeEndOptions(marker.conflictedFiles, false),\n );\n return true;\n }\n\n /**\n * The build gate, run BEFORE any reviewer is briefed. Returns the ISO time it passed, for the receipt —\n * which is what lets `wp-finish-upsert-pr` skip its own gate when HEAD has not moved, so moving the gate\n * earlier did not turn one build into two.\n */\n private runBuildGate(repoRoot: string): string {\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate',\n 'pnpm wp-review-upsert-pr',\n 'Build failed — NO reviewer was briefed and no diff was extracted. Fix it, then re-run.',\n ));\n // Repo-wide: the build must not have left anything uncommitted AND unstaged. Runs HERE, not in\n // finish, because this is the stage that ran buildCommand and is therefore holding the dirty\n // tree at the exact moment the question is answerable. Replaces the per-project\n // validate-di-graph-unchanged nx target — see BuildArtifactGate for why that one had to go.\n this.buildArtifactGate.assertBuildLeftNothingUncommitted(repoRoot);\n return new Date().toISOString();\n }\n\n /**\n * Extract the diff once, then write one instructions file per reviewer.\n *\n * The changed-file set and the basis both come off the SCAN rather than being recomputed, so the diff a\n * reviewer reads covers exactly the files its checklist matched against.\n */\n private briefReviewers(repoRoot: string, featureName: string, scan: ChecklistScan, config: PrGateConfig): ReviewerBriefing[] {\n if (scan.basis.unresolved) return [];\n const manifest = this.materializer.materialize(\n repoRoot, featureName, scan.basis, scan.changedFiles, config.reviewDiffExclude);\n const diffDir = this.materializer.diffDirFor(repoRoot, featureName);\n // Write pr-context.json ONCE, here — after materializing, so `diffDir` is populated on the first\n // write. The scan deliberately did not write it (ChecklistScanOptions.contextStage === ''); it used\n // to, which meant this command wrote the same file twice, the first time with an empty diffDir.\n // `scan.changedFiles` is passed through so the context is not recomputed from a second git call.\n scan.context = this.prContextWriter.ensure(\n repoRoot, featureName, scan.basis, 'stage2-review', scan.changedFiles, diffDir);\n const briefings = this.briefingBuilder.build(repoRoot, scan, manifest, diffDir, config);\n const dir = this.reviewerInstructions.instructionsDirFor(repoRoot, featureName);\n fs.rmSync(dir, { recursive: true, force: true }); // stale instructions read as current are worse than none\n fs.mkdirSync(dir, { recursive: true });\n for (const b of briefings) {\n fs.writeFileSync(this.reviewerInstructions.pathFor(repoRoot, featureName, b.subagent),\n this.reviewerInstructions.render(b));\n }\n return briefings;\n }\n\n /** What the AI must do next: spawn each reviewer, then write review.json. */\n private report(repoRoot: string, featureName: string, scan: ChecklistScan, briefings: readonly ReviewerBriefing[]): void {\n process.stdout.write('\\n' + SEP + '② Spawn Subagent Reviews, then finish\\n' + SEP + '\\n');\n if (scan.applicable.length === 0) {\n process.stdout.write(this.checklistNotice.build(scan.defined.length, 'wp-finish-upsert-pr'));\n } else {\n process.stdout.write(this.spawnBlocks(repoRoot, featureName, scan, briefings));\n }\n process.stdout.write(\n `\\nWhile your subagents are running, review your own changes and\\n${reviewJsonSchemaHint(scan.reviewPath)}\\n\\n` +\n `Finally run: pnpm wp-finish-upsert-pr\\n` +\n `(The build gate is already green for this commit — finish reuses it unless HEAD moves.)\\n\\n`);\n }\n\n /**\n * One copy-paste block per owed reviewer. The prompt is deliberately a POINTER and nothing else: the\n * generated instructions file is the contract, so anything restated here is a second copy that can go\n * stale — which is exactly how a removed `success` field outlived its own removal in print.\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private spawnBlocks(repoRoot: string, featureName: string, scan: ChecklistScan, briefings: readonly ReviewerBriefing[]): string {\n const reviewedIds = new Set(scan.reviewed.map((r: RequiredChecklist): string => r.id));\n const owed = briefings.filter((b: ReviewerBriefing): boolean => !reviewedIds.has(b.checklistId));\n const lines: string[] = [];\n for (const r of scan.reviewed) {\n lines.push(` ✓ ${r.subagent} — already reviewed on this branch (reusing its review-${r.id}.json)`);\n }\n // A verdict file that EXISTS but is unreadable as a verdict is called out here. Without it this\n // reports the checklist as simply owed, and the AI re-runs a reviewer that already ran instead of\n // correcting the file sitting right there.\n for (const e of scan.formatErrors) lines.push(` ⛔ ${e}`);\n if (owed.length === 0) {\n lines.push('', '✅ Every checklist that applies is already reviewed — nothing to spawn.');\n return lines.join('\\n') + '\\n';\n }\n lines.push('',\n `Spawn these ${owed.length} reviewer subagent(s) — a SEPARATE one each. You may NOT review your own`,\n 'work, and you may NOT write a reviewer\\'s verdict file on its behalf.',\n '');\n for (const b of owed) {\n lines.push(` ▶ ${b.subagent} — ${this.why(b)}`);\n lines.push(` subagent_type: ${b.subagent}`);\n lines.push(' prompt: Read your instructions file FIRST and follow it exactly:');\n lines.push(` ${this.reviewerInstructions.pathFor(repoRoot, featureName, b.subagent)}`);\n lines.push('');\n }\n return lines.join('\\n');\n }\n\n // Why this one is in scope. A patternless checklist is NOT \"matched\" — it always runs, over the whole\n // diff, and saying so is what tells a repo its checklist is firing on docs-only PRs by design.\n private why(b: ReviewerBriefing): string {\n if (b.matchedPatterns.length === 0) {\n return `ALWAYS RUNS (no \"patterns\" configured), whole diff in scope — ${b.myFiles.length} file(s)`;\n }\n return `${b.myFiles.length} file(s) matched ${b.matchedPatterns.map((p: string): string => `\"${p}\"`).join(', ')}`;\n }\n}\n"]}
|
|
@@ -43,11 +43,10 @@ let StartUpsertPrCommand = class StartUpsertPrCommand {
|
|
|
43
43
|
const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());
|
|
44
44
|
// Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.
|
|
45
45
|
(0, rules_config_1.writeTemplate)(repoRoot, 'webpieces.git-workflow.md');
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
this.scaffoldCiWorkflow(repoRoot);
|
|
46
|
+
// NOTE: the server-side CI gate is NOT scaffolded here any more — `wp-install-ai-hooks` owns it.
|
|
47
|
+
// This printed copy-to-.github + branch-protection instructions on EVERY run, at an agent doing
|
|
48
|
+
// feature work that could not act on them: marking a check required needs a repo admin. Setup
|
|
49
|
+
// belongs in the setup command; see templates/webpieces.ci-gate-setup.md.
|
|
51
50
|
// Precondition: a fully-committed tree. This flow squash-updates the branch and builds it — the
|
|
52
51
|
// tooling must not commit your work for you, and building a dirty working tree would let an
|
|
53
52
|
// uncommitted change build green over a different commit than the one that ships. Fail if dirty.
|
|
@@ -75,7 +74,7 @@ let StartUpsertPrCommand = class StartUpsertPrCommand {
|
|
|
75
74
|
(0, rules_config_1.writeTemplate)(repoRoot, 'webpieces.review-checklists.md');
|
|
76
75
|
// Persist the PR diff context (base sha + the full changed-file set) so it exists even if the AI
|
|
77
76
|
// stops here. Stage ② rewrites it with the materialized-diff dir once it has one.
|
|
78
|
-
const context = this.prContextWriter.ensure(repoRoot, this.aiBranchName.getFeatureName(), this.diffBasisResolver.resolve(repoRoot));
|
|
77
|
+
const context = this.prContextWriter.ensure(repoRoot, this.aiBranchName.getFeatureName(), this.diffBasisResolver.resolve(repoRoot), 'stage1-start');
|
|
79
78
|
process.stdout.write('\n' + SEP + '② Review the PR, then finish\n' + SEP + '\n');
|
|
80
79
|
process.stdout.write(`Branch is updated (nothing pushed yet — finish does the one push, behind the build gate).\n` +
|
|
81
80
|
`${this.contextLines(context)}\n` +
|
|
@@ -93,17 +92,6 @@ let StartUpsertPrCommand = class StartUpsertPrCommand {
|
|
|
93
92
|
return (`${cmd === '' ? '' : `Your diff: ${cmd}\n`}` +
|
|
94
93
|
`Full changed-file set + base/head sha: ${context.prContextPath}\n`);
|
|
95
94
|
}
|
|
96
|
-
// Scaffold the server-side CI check when (and only when) this repo set a gateSalt. Written to the
|
|
97
|
-
// gitignored instruct-ai dir so it never dirties the tree; the human copies it to .github/workflows
|
|
98
|
-
// and marks it required (webpieces can't set branch protection). No-op for repos with no gateSalt.
|
|
99
|
-
scaffoldCiWorkflow(repoRoot) {
|
|
100
|
-
if ((0, rules_config_1.loadAndValidate)(repoRoot).prGate.gateSalt.trim() === '')
|
|
101
|
-
return;
|
|
102
|
-
(0, rules_config_1.writeTemplateIfMissing)(repoRoot, 'webpieces-pr-gate.yml');
|
|
103
|
-
process.stdout.write(`\nℹ️ Server-side gate enforcement is ON (gateSalt set). If you have not already:\n` +
|
|
104
|
-
` • copy .webpieces/instruct-ai/webpieces-pr-gate.yml → .github/workflows/ and commit it\n` +
|
|
105
|
-
` • mark the "webpieces-pr-gate" check REQUIRED in branch protection (repo admin only)\n`);
|
|
106
|
-
}
|
|
107
95
|
// Bring the branch up to date with main via the shared 3-point engine (in-process). On conflict the
|
|
108
96
|
// merge process doc it writes names `wp-review-upsert-pr` as the finish command — that is the command
|
|
109
97
|
// that now validates and commits a conflict resolution, so it is what the doc must send the AI to.
|