@pourkit/cli 0.0.0-next-20260608221925 → 0.0.0-next-20260609213420
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/dist/cli.js +492 -253
- package/dist/cli.js.map +1 -1
- package/dist/e2e/run-live-e2e.js +23 -26
- package/dist/e2e/run-live-e2e.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1354,13 +1354,7 @@ function buildRunContextMarkdown(options) {
|
|
|
1354
1354
|
);
|
|
1355
1355
|
}
|
|
1356
1356
|
if (sections.includes("verification-commands")) {
|
|
1357
|
-
parts.push(
|
|
1358
|
-
...renderCommandList(
|
|
1359
|
-
target,
|
|
1360
|
-
getVerificationCommands(target),
|
|
1361
|
-
"Verification Commands"
|
|
1362
|
-
)
|
|
1363
|
-
);
|
|
1357
|
+
parts.push(...renderCommandList(target, "Verification Commands"));
|
|
1364
1358
|
}
|
|
1365
1359
|
if (sections.includes("review-criteria")) {
|
|
1366
1360
|
parts.push(...renderCriteria(reviewerCriteria));
|
|
@@ -1378,29 +1372,11 @@ function buildRunContextMarkdown(options) {
|
|
|
1378
1372
|
}
|
|
1379
1373
|
return parts.join("\n");
|
|
1380
1374
|
}
|
|
1381
|
-
function renderCommandList(target,
|
|
1382
|
-
if (commands.length === 0) {
|
|
1383
|
-
return [
|
|
1384
|
-
`## ${heading}`,
|
|
1385
|
-
"",
|
|
1386
|
-
`Run this command from the repository root: \`${buildRunVerificationCommand(target)}\``,
|
|
1387
|
-
"",
|
|
1388
|
-
"Underlying project commands:",
|
|
1389
|
-
"",
|
|
1390
|
-
"(none configured)",
|
|
1391
|
-
""
|
|
1392
|
-
];
|
|
1393
|
-
}
|
|
1375
|
+
function renderCommandList(target, heading) {
|
|
1394
1376
|
return [
|
|
1395
1377
|
`## ${heading}`,
|
|
1396
1378
|
"",
|
|
1397
1379
|
`Run this command from the repository root: \`${buildRunVerificationCommand(target)}\``,
|
|
1398
|
-
"",
|
|
1399
|
-
"Underlying project commands:",
|
|
1400
|
-
"",
|
|
1401
|
-
"Run these commands from the repository root exactly as written. Do not substitute equivalent scripts from nested package.json files.",
|
|
1402
|
-
"",
|
|
1403
|
-
...commands.map((command) => `- ${command.label}: \`${command.command}\``),
|
|
1404
1380
|
""
|
|
1405
1381
|
];
|
|
1406
1382
|
}
|
|
@@ -7424,6 +7400,27 @@ async function completeIssueRun(options) {
|
|
|
7424
7400
|
issueNumber,
|
|
7425
7401
|
config.labels.agentInProgress
|
|
7426
7402
|
);
|
|
7403
|
+
await issueProvider.removeLabel(
|
|
7404
|
+
issueNumber,
|
|
7405
|
+
config.labels.prOpenAwaitingMerge
|
|
7406
|
+
);
|
|
7407
|
+
let childCloseSucceeded2 = false;
|
|
7408
|
+
try {
|
|
7409
|
+
await issueProvider.closeIssue(issueNumber);
|
|
7410
|
+
childCloseSucceeded2 = true;
|
|
7411
|
+
} catch (error) {
|
|
7412
|
+
logger.step(
|
|
7413
|
+
"warn",
|
|
7414
|
+
`Issue #${issueNumber} could not be closed: ${error instanceof Error ? error.message : String(error)}`
|
|
7415
|
+
);
|
|
7416
|
+
}
|
|
7417
|
+
if (childCloseSucceeded2) {
|
|
7418
|
+
await maybeCloseParentAfterChildCompletion(
|
|
7419
|
+
issueProvider,
|
|
7420
|
+
issueNumber,
|
|
7421
|
+
logger
|
|
7422
|
+
);
|
|
7423
|
+
}
|
|
7427
7424
|
return {
|
|
7428
7425
|
branchName,
|
|
7429
7426
|
target,
|
|
@@ -8597,7 +8594,20 @@ var LocalPrdRunRecordSchema = z3.object({
|
|
|
8597
8594
|
baseCommit: z3.string().min(1)
|
|
8598
8595
|
}).optional(),
|
|
8599
8596
|
queue: z3.object({ completedAt: z3.string().min(1) }).optional(),
|
|
8600
|
-
finalReview: z3.object({
|
|
8597
|
+
finalReview: z3.object({
|
|
8598
|
+
completedAt: z3.string().min(1),
|
|
8599
|
+
targetName: z3.string().optional(),
|
|
8600
|
+
prdBranch: z3.string().optional(),
|
|
8601
|
+
mergeBase: z3.string().optional(),
|
|
8602
|
+
verdict: z3.enum([
|
|
8603
|
+
"pass_no_changes",
|
|
8604
|
+
"pass_with_retouch",
|
|
8605
|
+
"needs_human_review",
|
|
8606
|
+
"blocked"
|
|
8607
|
+
]).optional(),
|
|
8608
|
+
diagnostics: z3.array(z3.string()).optional(),
|
|
8609
|
+
artifactPath: z3.string().optional()
|
|
8610
|
+
}).optional(),
|
|
8601
8611
|
reconciliation: z3.object({ completedAt: z3.string().min(1) }).optional(),
|
|
8602
8612
|
complete: z3.object({
|
|
8603
8613
|
completedAt: z3.string().min(1),
|
|
@@ -9278,7 +9288,7 @@ async function runLocalFinalReview(prdId, options) {
|
|
|
9278
9288
|
}
|
|
9279
9289
|
let verdict;
|
|
9280
9290
|
if (commands.length === 0 || failures.length === 0) {
|
|
9281
|
-
const retouchBranch = `local
|
|
9291
|
+
const retouchBranch = `local-${prdId}-final-review-retouch`;
|
|
9282
9292
|
try {
|
|
9283
9293
|
execSync2(`git show-ref --verify --quiet refs/heads/${retouchBranch}`, {
|
|
9284
9294
|
cwd: root,
|
|
@@ -9292,7 +9302,7 @@ async function runLocalFinalReview(prdId, options) {
|
|
|
9292
9302
|
} else if (failures.length === commands.length) {
|
|
9293
9303
|
verdict = "blocked";
|
|
9294
9304
|
} else {
|
|
9295
|
-
const retouchBranch = `local
|
|
9305
|
+
const retouchBranch = `local-${prdId}-final-review-retouch`;
|
|
9296
9306
|
try {
|
|
9297
9307
|
execSync2(`git show-ref --verify --quiet refs/heads/${retouchBranch}`, {
|
|
9298
9308
|
cwd: root,
|
|
@@ -9321,10 +9331,10 @@ async function runLocalFinalReview(prdId, options) {
|
|
|
9321
9331
|
}
|
|
9322
9332
|
return result;
|
|
9323
9333
|
}
|
|
9324
|
-
async function squashFinalReviewRetouch(prdId, repoRoot2) {
|
|
9334
|
+
async function squashFinalReviewRetouch(prdId, repoRoot2, title, body) {
|
|
9325
9335
|
const root = repoRoot2 ?? process.cwd();
|
|
9326
9336
|
const targetBranch = `local/${prdId}`;
|
|
9327
|
-
const retouchBranch = `local
|
|
9337
|
+
const retouchBranch = `local-${prdId}-final-review-retouch`;
|
|
9328
9338
|
try {
|
|
9329
9339
|
execFileSync3(
|
|
9330
9340
|
"git",
|
|
@@ -9371,15 +9381,26 @@ async function squashFinalReviewRetouch(prdId, repoRoot2) {
|
|
|
9371
9381
|
return;
|
|
9372
9382
|
} catch {
|
|
9373
9383
|
}
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9384
|
+
if (title) {
|
|
9385
|
+
const commitBody = body ? `${title}
|
|
9386
|
+
|
|
9387
|
+
${body}` : title;
|
|
9388
|
+
execFileSync3("git", ["commit", "-m", commitBody], {
|
|
9378
9389
|
cwd: root,
|
|
9379
9390
|
encoding: "utf8",
|
|
9380
9391
|
stdio: "pipe"
|
|
9381
|
-
}
|
|
9382
|
-
|
|
9392
|
+
});
|
|
9393
|
+
} else {
|
|
9394
|
+
execFileSync3(
|
|
9395
|
+
"git",
|
|
9396
|
+
["commit", "-m", `Squash merge ${retouchBranch} into ${targetBranch}`],
|
|
9397
|
+
{
|
|
9398
|
+
cwd: root,
|
|
9399
|
+
encoding: "utf8",
|
|
9400
|
+
stdio: "pipe"
|
|
9401
|
+
}
|
|
9402
|
+
);
|
|
9403
|
+
}
|
|
9383
9404
|
const mergeCommit = execFileSync3("git", ["rev-parse", "HEAD"], {
|
|
9384
9405
|
cwd: root,
|
|
9385
9406
|
encoding: "utf8",
|
|
@@ -10722,10 +10743,6 @@ function loadFinalReviewPrompt(repoRoot2, promptTemplate) {
|
|
|
10722
10743
|
const promptPath = resolvePromptTemplatePath(repoRoot2, promptTemplate);
|
|
10723
10744
|
return existsSync17(promptPath) ? readFileSync17(promptPath, "utf-8") : promptTemplate;
|
|
10724
10745
|
}
|
|
10725
|
-
function formatVerificationCommands(commands) {
|
|
10726
|
-
if (commands.length === 0) return "No verification commands configured.";
|
|
10727
|
-
return commands.map((cmd) => `- ${cmd.label}: ${cmd.command}`).join("\n");
|
|
10728
|
-
}
|
|
10729
10746
|
function buildFinalReviewPrompt(options) {
|
|
10730
10747
|
return [
|
|
10731
10748
|
"# Active PRD Final Review Context",
|
|
@@ -10740,9 +10757,6 @@ function buildFinalReviewPrompt(options) {
|
|
|
10740
10757
|
"",
|
|
10741
10758
|
`Run this command before handoff when retouch changes are made: pourkit run-verification --target ${options.targetName}`,
|
|
10742
10759
|
"",
|
|
10743
|
-
"Underlying project commands:",
|
|
10744
|
-
formatVerificationCommands(options.verificationCommands),
|
|
10745
|
-
"",
|
|
10746
10760
|
"Evidence Packet (do not infer PRD context from local state files):",
|
|
10747
10761
|
JSON.stringify(options.evidencePacket, null, 2),
|
|
10748
10762
|
"",
|
|
@@ -10867,9 +10881,6 @@ function buildFinalReviewFinalizerPrompt(options) {
|
|
|
10867
10881
|
"Changed paths:",
|
|
10868
10882
|
...options.changedPaths.map((p) => `- ${p}`),
|
|
10869
10883
|
"",
|
|
10870
|
-
"Verification commands given to Final Review:",
|
|
10871
|
-
formatVerificationCommands(options.verificationCommands),
|
|
10872
|
-
"",
|
|
10873
10884
|
promptBody
|
|
10874
10885
|
].join("\n");
|
|
10875
10886
|
}
|
|
@@ -10896,8 +10907,7 @@ async function runFinalReviewPrFinalizer(options) {
|
|
|
10896
10907
|
prdBranch: options.prdBranch,
|
|
10897
10908
|
mergeBase: options.mergeBase,
|
|
10898
10909
|
summary: options.summary,
|
|
10899
|
-
changedPaths: options.changedPaths
|
|
10900
|
-
verificationCommands: options.verificationCommands
|
|
10910
|
+
changedPaths: options.changedPaths
|
|
10901
10911
|
}),
|
|
10902
10912
|
branchName: options.branchName,
|
|
10903
10913
|
repoRoot: options.repoRoot,
|
|
@@ -10957,17 +10967,32 @@ function ensureFinalReviewWorktree(options) {
|
|
|
10957
10967
|
};
|
|
10958
10968
|
}
|
|
10959
10969
|
mkdirSync11(dirname5(worktreePath), { recursive: true });
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10970
|
+
if (options.isLocal) {
|
|
10971
|
+
const branchResult = spawnSync3(
|
|
10972
|
+
"git",
|
|
10973
|
+
["branch", "-f", options.branchName, options.checkoutBase],
|
|
10974
|
+
{ cwd: options.repoRoot, encoding: "utf8" }
|
|
10975
|
+
);
|
|
10976
|
+
if (branchResult.status !== 0) {
|
|
10977
|
+
return {
|
|
10978
|
+
ok: false,
|
|
10979
|
+
reason: `Final Review failed to prepare branch ${options.branchName}.`,
|
|
10980
|
+
diagnostics: collectSpawnDiagnostics(branchResult, "git branch failed")
|
|
10981
|
+
};
|
|
10982
|
+
}
|
|
10983
|
+
} else {
|
|
10984
|
+
const branchResult = spawnSync3(
|
|
10985
|
+
"git",
|
|
10986
|
+
["branch", "-f", options.branchName, `origin/${options.checkoutBase}`],
|
|
10987
|
+
{ cwd: options.repoRoot, encoding: "utf8" }
|
|
10988
|
+
);
|
|
10989
|
+
if (branchResult.status !== 0) {
|
|
10990
|
+
return {
|
|
10991
|
+
ok: false,
|
|
10992
|
+
reason: `Final Review failed to prepare branch ${options.branchName}.`,
|
|
10993
|
+
diagnostics: collectSpawnDiagnostics(branchResult, "git branch failed")
|
|
10994
|
+
};
|
|
10995
|
+
}
|
|
10971
10996
|
}
|
|
10972
10997
|
const addResult = spawnSync3(
|
|
10973
10998
|
"git",
|
|
@@ -11079,6 +11104,156 @@ async function createOrReuseFinalReviewRetouchPr(options) {
|
|
|
11079
11104
|
})
|
|
11080
11105
|
});
|
|
11081
11106
|
}
|
|
11107
|
+
async function writeAndVerifyLocalDualReceipt(repoRoot2, prdRef, record, targetName, prdBranch, mergeBase, verdict, reviewedAt, isLocalMode) {
|
|
11108
|
+
if (!isLocalMode) return null;
|
|
11109
|
+
let currentRecord = await readLocalPrdRun(repoRoot2, prdRef);
|
|
11110
|
+
if (!currentRecord) {
|
|
11111
|
+
currentRecord = {
|
|
11112
|
+
prdId: normalizePrdRunRef2(prdRef),
|
|
11113
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11114
|
+
receipts: {},
|
|
11115
|
+
metadata: {}
|
|
11116
|
+
};
|
|
11117
|
+
}
|
|
11118
|
+
const localStoreReceipt = {
|
|
11119
|
+
completedAt: reviewedAt,
|
|
11120
|
+
targetName,
|
|
11121
|
+
prdBranch,
|
|
11122
|
+
mergeBase,
|
|
11123
|
+
verdict,
|
|
11124
|
+
diagnostics: [],
|
|
11125
|
+
artifactPath: ".pourkit/final-review-artifact.json"
|
|
11126
|
+
};
|
|
11127
|
+
await writeLocalPrdRunRecord(repoRoot2, prdRef, {
|
|
11128
|
+
...currentRecord,
|
|
11129
|
+
receipts: {
|
|
11130
|
+
...currentRecord.receipts,
|
|
11131
|
+
finalReview: localStoreReceipt
|
|
11132
|
+
}
|
|
11133
|
+
});
|
|
11134
|
+
const prdRunStateResult = readPrdRun(repoRoot2, prdRef);
|
|
11135
|
+
const localStoreResult = await readLocalPrdRun(repoRoot2, prdRef);
|
|
11136
|
+
if (!prdRunStateResult.record?.finalReview) {
|
|
11137
|
+
const reason = `PRD Run State missing finalReview receipt after write for ${prdRef}.`;
|
|
11138
|
+
writePrdRunRecord(repoRoot2, {
|
|
11139
|
+
...record,
|
|
11140
|
+
prdRef,
|
|
11141
|
+
status: "blocked",
|
|
11142
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11143
|
+
blockedGate: "final-review",
|
|
11144
|
+
blockedReason: reason,
|
|
11145
|
+
diagnostics: [reason],
|
|
11146
|
+
targetName,
|
|
11147
|
+
offendingPaths: []
|
|
11148
|
+
});
|
|
11149
|
+
return {
|
|
11150
|
+
prdRef,
|
|
11151
|
+
status: "blocked",
|
|
11152
|
+
blockedGate: "final-review",
|
|
11153
|
+
blockedReason: reason,
|
|
11154
|
+
diagnostics: [reason],
|
|
11155
|
+
offendingPaths: []
|
|
11156
|
+
};
|
|
11157
|
+
}
|
|
11158
|
+
if (!localStoreResult?.receipts.finalReview) {
|
|
11159
|
+
const reason = `Local PRD Run store missing finalReview receipt after write for ${prdRef}.`;
|
|
11160
|
+
writePrdRunRecord(repoRoot2, {
|
|
11161
|
+
...record,
|
|
11162
|
+
prdRef,
|
|
11163
|
+
status: "blocked",
|
|
11164
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11165
|
+
blockedGate: "final-review",
|
|
11166
|
+
blockedReason: reason,
|
|
11167
|
+
diagnostics: [reason],
|
|
11168
|
+
targetName,
|
|
11169
|
+
offendingPaths: []
|
|
11170
|
+
});
|
|
11171
|
+
return {
|
|
11172
|
+
prdRef,
|
|
11173
|
+
status: "blocked",
|
|
11174
|
+
blockedGate: "final-review",
|
|
11175
|
+
blockedReason: reason,
|
|
11176
|
+
diagnostics: [reason],
|
|
11177
|
+
offendingPaths: []
|
|
11178
|
+
};
|
|
11179
|
+
}
|
|
11180
|
+
const stateFr = prdRunStateResult.record.finalReview;
|
|
11181
|
+
const localFr = localStoreResult.receipts.finalReview;
|
|
11182
|
+
const mismatchFields = [];
|
|
11183
|
+
if (stateFr.targetName !== localFr.targetName)
|
|
11184
|
+
mismatchFields.push("targetName");
|
|
11185
|
+
if (stateFr.prdBranch !== localFr.prdBranch) mismatchFields.push("prdBranch");
|
|
11186
|
+
if (stateFr.mergeBase !== localFr.mergeBase) mismatchFields.push("mergeBase");
|
|
11187
|
+
if (stateFr.verdict !== localFr.verdict) mismatchFields.push("verdict");
|
|
11188
|
+
if (!stateFr.artifactPath !== !localFr.artifactPath)
|
|
11189
|
+
mismatchFields.push("artifactPath");
|
|
11190
|
+
if (!stateFr.diagnostics !== !localFr.diagnostics)
|
|
11191
|
+
mismatchFields.push("diagnostics");
|
|
11192
|
+
if (!stateFr.reviewedAt !== !localFr.completedAt)
|
|
11193
|
+
mismatchFields.push("reviewedTimestamp");
|
|
11194
|
+
if (mismatchFields.length > 0) {
|
|
11195
|
+
const reason = `Dual receipt mismatch on fields: ${mismatchFields.join(", ")}`;
|
|
11196
|
+
writePrdRunRecord(repoRoot2, {
|
|
11197
|
+
...record,
|
|
11198
|
+
prdRef,
|
|
11199
|
+
status: "blocked",
|
|
11200
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11201
|
+
blockedGate: "final-review",
|
|
11202
|
+
blockedReason: reason,
|
|
11203
|
+
diagnostics: [reason],
|
|
11204
|
+
targetName,
|
|
11205
|
+
finalReview: stateFr,
|
|
11206
|
+
offendingPaths: []
|
|
11207
|
+
});
|
|
11208
|
+
return {
|
|
11209
|
+
prdRef,
|
|
11210
|
+
status: "blocked",
|
|
11211
|
+
blockedGate: "final-review",
|
|
11212
|
+
blockedReason: reason,
|
|
11213
|
+
diagnostics: [reason],
|
|
11214
|
+
offendingPaths: []
|
|
11215
|
+
};
|
|
11216
|
+
}
|
|
11217
|
+
return null;
|
|
11218
|
+
}
|
|
11219
|
+
async function verifyLocalDualFinalReviewReceipts(repoRoot2, prdRef) {
|
|
11220
|
+
const prdRunStateResult = readPrdRun(repoRoot2, prdRef);
|
|
11221
|
+
const stateFr = prdRunStateResult.record?.finalReview;
|
|
11222
|
+
const localStoreResult = await readLocalPrdRun(repoRoot2, prdRef);
|
|
11223
|
+
const localFr = localStoreResult?.receipts.finalReview;
|
|
11224
|
+
const diagnostics = [];
|
|
11225
|
+
const validFinalReviewStatuses = /* @__PURE__ */ new Set(["final_reviewed", "succeeded"]);
|
|
11226
|
+
if (!stateFr || !validFinalReviewStatuses.has(stateFr.status)) {
|
|
11227
|
+
diagnostics.push(
|
|
11228
|
+
stateFr ? `PRD Run State finalReview status is "${stateFr.status}". Expected "final_reviewed" or "succeeded".` : "PRD Run State has no finalReview receipt."
|
|
11229
|
+
);
|
|
11230
|
+
return {
|
|
11231
|
+
ok: false,
|
|
11232
|
+
reason: diagnostics[0],
|
|
11233
|
+
diagnostics
|
|
11234
|
+
};
|
|
11235
|
+
}
|
|
11236
|
+
if (!localFr) {
|
|
11237
|
+
diagnostics.push("Local PRD Run Store has no finalReview receipt.");
|
|
11238
|
+
return {
|
|
11239
|
+
ok: false,
|
|
11240
|
+
reason: diagnostics[0],
|
|
11241
|
+
diagnostics
|
|
11242
|
+
};
|
|
11243
|
+
}
|
|
11244
|
+
const mismatchFields = [];
|
|
11245
|
+
if (stateFr.targetName !== localFr.targetName)
|
|
11246
|
+
mismatchFields.push("targetName");
|
|
11247
|
+
if (stateFr.prdBranch !== localFr.prdBranch) mismatchFields.push("prdBranch");
|
|
11248
|
+
if (stateFr.mergeBase !== localFr.mergeBase) mismatchFields.push("mergeBase");
|
|
11249
|
+
if (stateFr.verdict !== localFr.verdict) mismatchFields.push("verdict");
|
|
11250
|
+
if (mismatchFields.length > 0) {
|
|
11251
|
+
const reason = `Dual receipt mismatch on fields: ${mismatchFields.join(", ")}`;
|
|
11252
|
+
diagnostics.push(reason);
|
|
11253
|
+
return { ok: false, reason, diagnostics };
|
|
11254
|
+
}
|
|
11255
|
+
return { ok: true };
|
|
11256
|
+
}
|
|
11082
11257
|
async function runPrdRunFinalReviewCommand(options) {
|
|
11083
11258
|
const prdRef = normalizePrdRunRef2(options.prdRef);
|
|
11084
11259
|
const targetName = options.targetName.trim();
|
|
@@ -11143,6 +11318,39 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11143
11318
|
offendingPaths: []
|
|
11144
11319
|
};
|
|
11145
11320
|
}
|
|
11321
|
+
if (record.mode === "local") {
|
|
11322
|
+
const runnableIssues = await getRunnableLocalIssues(
|
|
11323
|
+
prdRef,
|
|
11324
|
+
options.repoRoot
|
|
11325
|
+
);
|
|
11326
|
+
if (runnableIssues.length > 0) {
|
|
11327
|
+
const reason = `PRD Run ${prdRef} is marked "drained" but ${runnableIssues.length} runnable local Issues remain. Queue drain receipt may be premature.`;
|
|
11328
|
+
writePrdRunRecord(options.repoRoot, {
|
|
11329
|
+
...record,
|
|
11330
|
+
status: "blocked",
|
|
11331
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11332
|
+
blockedGate: "final-review",
|
|
11333
|
+
blockedReason: reason,
|
|
11334
|
+
diagnostics: [
|
|
11335
|
+
`Current status: ${record.status}`,
|
|
11336
|
+
`Runnable issues remaining: ${runnableIssues.length}`
|
|
11337
|
+
],
|
|
11338
|
+
targetName,
|
|
11339
|
+
offendingPaths: []
|
|
11340
|
+
});
|
|
11341
|
+
return {
|
|
11342
|
+
prdRef,
|
|
11343
|
+
status: "blocked",
|
|
11344
|
+
blockedGate: "final-review",
|
|
11345
|
+
blockedReason: reason,
|
|
11346
|
+
diagnostics: [
|
|
11347
|
+
`Current status: ${record.status}`,
|
|
11348
|
+
`Runnable issues remaining: ${runnableIssues.length}`
|
|
11349
|
+
],
|
|
11350
|
+
offendingPaths: []
|
|
11351
|
+
};
|
|
11352
|
+
}
|
|
11353
|
+
}
|
|
11146
11354
|
if (!options.issueProvider) {
|
|
11147
11355
|
const reason = "Missing IssueProvider. Cannot validate child completeness.";
|
|
11148
11356
|
writePrdRunRecord(options.repoRoot, {
|
|
@@ -11251,8 +11459,19 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11251
11459
|
offendingPaths: []
|
|
11252
11460
|
};
|
|
11253
11461
|
}
|
|
11254
|
-
const
|
|
11255
|
-
|
|
11462
|
+
const isLocalMode = record.mode === "local";
|
|
11463
|
+
let mergeBaseResult;
|
|
11464
|
+
let prdBranch;
|
|
11465
|
+
if (isLocalMode) {
|
|
11466
|
+
prdBranch = getLocalPrdBranchName(prdRef);
|
|
11467
|
+
mergeBaseResult = computeLocalFinalReviewMergeBase(
|
|
11468
|
+
options.repoRoot,
|
|
11469
|
+
prdBranch
|
|
11470
|
+
);
|
|
11471
|
+
} else {
|
|
11472
|
+
prdBranch = record.prdBranch ?? prdRef;
|
|
11473
|
+
mergeBaseResult = computeFinalReviewMergeBase(options.repoRoot, prdRef);
|
|
11474
|
+
}
|
|
11256
11475
|
if (!mergeBaseResult.ok) {
|
|
11257
11476
|
writePrdRunRecord(options.repoRoot, {
|
|
11258
11477
|
...record,
|
|
@@ -11265,7 +11484,7 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11265
11484
|
finalReview: {
|
|
11266
11485
|
status: "blocked",
|
|
11267
11486
|
targetName,
|
|
11268
|
-
prdBranch
|
|
11487
|
+
prdBranch,
|
|
11269
11488
|
mergeBase: mergeBaseResult.mergeBase,
|
|
11270
11489
|
diagnostics: mergeBaseResult.diagnostics,
|
|
11271
11490
|
reviewedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -11281,7 +11500,6 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11281
11500
|
offendingPaths: []
|
|
11282
11501
|
};
|
|
11283
11502
|
}
|
|
11284
|
-
const prdBranch = record.prdBranch ?? prdRef;
|
|
11285
11503
|
const manifestResult = readPlanningArtifactManifest(options.repoRoot, prdRef);
|
|
11286
11504
|
if (!manifestResult.ok) {
|
|
11287
11505
|
const reason = `Evidence Packet construction failed: ${manifestResult.reason}`;
|
|
@@ -11341,11 +11559,12 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11341
11559
|
planning: record.planning,
|
|
11342
11560
|
finalReview: startReceipt
|
|
11343
11561
|
});
|
|
11344
|
-
const finalReviewBranchName = buildFinalReviewBranchName(prdBranch);
|
|
11562
|
+
const finalReviewBranchName = isLocalMode ? `${prdBranch.replace(/\//g, "-")}-final-review-retouch` : buildFinalReviewBranchName(prdBranch);
|
|
11345
11563
|
const worktreeResult = ensureFinalReviewWorktree({
|
|
11346
11564
|
repoRoot: options.repoRoot,
|
|
11347
11565
|
branchName: finalReviewBranchName,
|
|
11348
|
-
checkoutBase: prdBranch
|
|
11566
|
+
checkoutBase: prdBranch,
|
|
11567
|
+
isLocal: isLocalMode
|
|
11349
11568
|
});
|
|
11350
11569
|
if (!worktreeResult.ok) {
|
|
11351
11570
|
writePrdRunRecord(options.repoRoot, {
|
|
@@ -11386,8 +11605,7 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11386
11605
|
repoRoot: options.repoRoot,
|
|
11387
11606
|
promptTemplate: finalReviewConfig.promptTemplate,
|
|
11388
11607
|
targetName,
|
|
11389
|
-
evidencePacket
|
|
11390
|
-
verificationCommands
|
|
11608
|
+
evidencePacket
|
|
11391
11609
|
}),
|
|
11392
11610
|
target: targetConfig,
|
|
11393
11611
|
repoRoot: options.repoRoot,
|
|
@@ -11530,6 +11748,7 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11530
11748
|
}
|
|
11531
11749
|
const { verdict, summary, diagnostics: artifactDiagnostics } = artifactResult;
|
|
11532
11750
|
if (verdict === "pass_no_changes") {
|
|
11751
|
+
const reviewedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11533
11752
|
const receipt = {
|
|
11534
11753
|
status: "succeeded",
|
|
11535
11754
|
targetName,
|
|
@@ -11538,17 +11757,29 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11538
11757
|
verdict: "pass_no_changes",
|
|
11539
11758
|
artifactPath: ".pourkit/final-review-artifact.json",
|
|
11540
11759
|
diagnostics: [],
|
|
11541
|
-
reviewedAt
|
|
11760
|
+
reviewedAt
|
|
11542
11761
|
};
|
|
11543
11762
|
writePrdRunRecord(options.repoRoot, {
|
|
11544
11763
|
...record,
|
|
11545
11764
|
status: "final_reviewed",
|
|
11546
|
-
updatedAt:
|
|
11765
|
+
updatedAt: reviewedAt,
|
|
11547
11766
|
targetName,
|
|
11548
11767
|
start: record.start,
|
|
11549
11768
|
planning: record.planning,
|
|
11550
11769
|
finalReview: receipt
|
|
11551
11770
|
});
|
|
11771
|
+
const blocked = await writeAndVerifyLocalDualReceipt(
|
|
11772
|
+
options.repoRoot,
|
|
11773
|
+
prdRef,
|
|
11774
|
+
record,
|
|
11775
|
+
targetName,
|
|
11776
|
+
prdBranch,
|
|
11777
|
+
mergeBaseResult.mergeBase,
|
|
11778
|
+
"pass_no_changes",
|
|
11779
|
+
reviewedAt,
|
|
11780
|
+
isLocalMode
|
|
11781
|
+
);
|
|
11782
|
+
if (blocked) return blocked;
|
|
11552
11783
|
return {
|
|
11553
11784
|
prdRef,
|
|
11554
11785
|
status: "final_reviewed",
|
|
@@ -11557,7 +11788,7 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11557
11788
|
};
|
|
11558
11789
|
}
|
|
11559
11790
|
if (verdict === "pass_with_retouch") {
|
|
11560
|
-
if (!options.prProvider) {
|
|
11791
|
+
if (!options.prProvider && !isLocalMode) {
|
|
11561
11792
|
const reason = "Missing PRProvider. Cannot create retouch PR without a PR provider.";
|
|
11562
11793
|
writePrdRunRecord(options.repoRoot, {
|
|
11563
11794
|
...record,
|
|
@@ -11588,7 +11819,7 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11588
11819
|
};
|
|
11589
11820
|
}
|
|
11590
11821
|
const autoMerge = options.autoMerge ?? true;
|
|
11591
|
-
const existingRetouchPrNumber = record.finalReview?.retouchPrNumber;
|
|
11822
|
+
const existingRetouchPrNumber = !isLocalMode ? record.finalReview?.retouchPrNumber : void 0;
|
|
11592
11823
|
if (existingRetouchPrNumber && autoMerge) {
|
|
11593
11824
|
const existingPr = await getPrByNumberIfAvailable(
|
|
11594
11825
|
options.prProvider,
|
|
@@ -11674,7 +11905,6 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11674
11905
|
mergeBase: mergeBaseResult.mergeBase,
|
|
11675
11906
|
summary,
|
|
11676
11907
|
changedPaths: scopeResult.changedPaths,
|
|
11677
|
-
verificationCommands,
|
|
11678
11908
|
logger: options.logger
|
|
11679
11909
|
});
|
|
11680
11910
|
if (!finalizerResult.ok) {
|
|
@@ -11706,6 +11936,116 @@ async function runPrdRunFinalReviewCommand(options) {
|
|
|
11706
11936
|
offendingPaths: []
|
|
11707
11937
|
};
|
|
11708
11938
|
}
|
|
11939
|
+
if (isLocalMode) {
|
|
11940
|
+
let localResult;
|
|
11941
|
+
try {
|
|
11942
|
+
const result = await squashFinalReviewRetouch(
|
|
11943
|
+
prdRef,
|
|
11944
|
+
options.repoRoot,
|
|
11945
|
+
finalizerResult.title,
|
|
11946
|
+
finalizerResult.body
|
|
11947
|
+
);
|
|
11948
|
+
if (!result) {
|
|
11949
|
+
const reason = "Retouch branch missing or empty. Cannot squash-merge.";
|
|
11950
|
+
writePrdRunRecord(options.repoRoot, {
|
|
11951
|
+
...record,
|
|
11952
|
+
status: "blocked",
|
|
11953
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11954
|
+
blockedGate: "final-review",
|
|
11955
|
+
blockedReason: reason,
|
|
11956
|
+
diagnostics: [reason],
|
|
11957
|
+
targetName,
|
|
11958
|
+
finalReview: {
|
|
11959
|
+
status: "blocked",
|
|
11960
|
+
targetName,
|
|
11961
|
+
prdBranch,
|
|
11962
|
+
mergeBase: mergeBaseResult.mergeBase,
|
|
11963
|
+
verdict: "pass_with_retouch",
|
|
11964
|
+
diagnostics: [reason],
|
|
11965
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11966
|
+
},
|
|
11967
|
+
offendingPaths: []
|
|
11968
|
+
});
|
|
11969
|
+
return {
|
|
11970
|
+
prdRef,
|
|
11971
|
+
status: "blocked",
|
|
11972
|
+
blockedGate: "final-review",
|
|
11973
|
+
blockedReason: reason,
|
|
11974
|
+
diagnostics: [reason],
|
|
11975
|
+
offendingPaths: []
|
|
11976
|
+
};
|
|
11977
|
+
}
|
|
11978
|
+
localResult = result;
|
|
11979
|
+
} catch (error) {
|
|
11980
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
11981
|
+
writePrdRunRecord(options.repoRoot, {
|
|
11982
|
+
...record,
|
|
11983
|
+
status: "blocked",
|
|
11984
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11985
|
+
blockedGate: "final-review",
|
|
11986
|
+
blockedReason: `Local retouch squash-merge failed: ${msg}`,
|
|
11987
|
+
diagnostics: [msg],
|
|
11988
|
+
targetName,
|
|
11989
|
+
finalReview: {
|
|
11990
|
+
status: "blocked",
|
|
11991
|
+
targetName,
|
|
11992
|
+
prdBranch,
|
|
11993
|
+
mergeBase: mergeBaseResult.mergeBase,
|
|
11994
|
+
verdict: "pass_with_retouch",
|
|
11995
|
+
diagnostics: [msg],
|
|
11996
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11997
|
+
},
|
|
11998
|
+
offendingPaths: []
|
|
11999
|
+
});
|
|
12000
|
+
return {
|
|
12001
|
+
prdRef,
|
|
12002
|
+
status: "blocked",
|
|
12003
|
+
blockedGate: "final-review",
|
|
12004
|
+
blockedReason: `Local retouch squash-merge failed: ${msg}`,
|
|
12005
|
+
diagnostics: [msg],
|
|
12006
|
+
offendingPaths: []
|
|
12007
|
+
};
|
|
12008
|
+
}
|
|
12009
|
+
const receipt2 = {
|
|
12010
|
+
status: "final_reviewed",
|
|
12011
|
+
targetName,
|
|
12012
|
+
prdBranch,
|
|
12013
|
+
mergeBase: mergeBaseResult.mergeBase,
|
|
12014
|
+
verdict: "pass_with_retouch",
|
|
12015
|
+
artifactPath: ".pourkit/final-review-artifact.json",
|
|
12016
|
+
diagnostics: [],
|
|
12017
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12018
|
+
retouchMergeCommit: localResult.mergeCommit,
|
|
12019
|
+
changedPaths: localResult.changedPaths
|
|
12020
|
+
};
|
|
12021
|
+
writePrdRunRecord(options.repoRoot, {
|
|
12022
|
+
...record,
|
|
12023
|
+
status: "final_reviewed",
|
|
12024
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12025
|
+
targetName,
|
|
12026
|
+
start: record.start,
|
|
12027
|
+
planning: record.planning,
|
|
12028
|
+
finalReview: receipt2
|
|
12029
|
+
});
|
|
12030
|
+
const blocked = await writeAndVerifyLocalDualReceipt(
|
|
12031
|
+
options.repoRoot,
|
|
12032
|
+
prdRef,
|
|
12033
|
+
record,
|
|
12034
|
+
targetName,
|
|
12035
|
+
prdBranch,
|
|
12036
|
+
mergeBaseResult.mergeBase,
|
|
12037
|
+
"pass_with_retouch",
|
|
12038
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
12039
|
+
isLocalMode
|
|
12040
|
+
);
|
|
12041
|
+
if (blocked) return blocked;
|
|
12042
|
+
return {
|
|
12043
|
+
prdRef,
|
|
12044
|
+
status: "final_reviewed",
|
|
12045
|
+
finalReview: receipt2,
|
|
12046
|
+
diagnostics: []
|
|
12047
|
+
};
|
|
12048
|
+
}
|
|
11709
12049
|
let retouchPr;
|
|
11710
12050
|
try {
|
|
11711
12051
|
retouchPr = await createOrReuseFinalReviewRetouchPr({
|
|
@@ -13895,6 +14235,37 @@ function computeFinalReviewMergeBase(repoRoot2, prdRef) {
|
|
|
13895
14235
|
}
|
|
13896
14236
|
return { ok: true, mergeBase, diagnostics: fetchDiagnostics };
|
|
13897
14237
|
}
|
|
14238
|
+
function computeLocalFinalReviewMergeBase(repoRoot2, prdBranch) {
|
|
14239
|
+
const mergeBaseResult = spawnSync3("git", ["merge-base", "dev", prdBranch], {
|
|
14240
|
+
cwd: repoRoot2,
|
|
14241
|
+
encoding: "utf8"
|
|
14242
|
+
});
|
|
14243
|
+
if (mergeBaseResult.status !== 0) {
|
|
14244
|
+
const diagnostics = [];
|
|
14245
|
+
if (mergeBaseResult.stderr?.toString?.()?.trim()) {
|
|
14246
|
+
diagnostics.push(mergeBaseResult.stderr.toString().trim());
|
|
14247
|
+
}
|
|
14248
|
+
if (mergeBaseResult.stdout?.toString?.()?.trim()) {
|
|
14249
|
+
diagnostics.push(mergeBaseResult.stdout.toString().trim());
|
|
14250
|
+
}
|
|
14251
|
+
return {
|
|
14252
|
+
ok: false,
|
|
14253
|
+
gate: "final-review",
|
|
14254
|
+
reason: `Final Review merge-base computation failed for dev and ${prdBranch}.`,
|
|
14255
|
+
diagnostics: diagnostics.length > 0 ? diagnostics : ["git merge-base returned non-zero exit status"]
|
|
14256
|
+
};
|
|
14257
|
+
}
|
|
14258
|
+
const mergeBase = mergeBaseResult.stdout?.toString?.()?.trim();
|
|
14259
|
+
if (!mergeBase) {
|
|
14260
|
+
return {
|
|
14261
|
+
ok: false,
|
|
14262
|
+
gate: "final-review",
|
|
14263
|
+
reason: `Final Review merge-base returned empty result for dev and ${prdBranch}.`,
|
|
14264
|
+
diagnostics: [`merge-base stdout was empty for dev..${prdBranch}`]
|
|
14265
|
+
};
|
|
14266
|
+
}
|
|
14267
|
+
return { ok: true, mergeBase, diagnostics: [] };
|
|
14268
|
+
}
|
|
13898
14269
|
function validateReconciliationArtifact2(artifact, context) {
|
|
13899
14270
|
const errors = [];
|
|
13900
14271
|
if (!artifact.prdRef) {
|
|
@@ -14339,201 +14710,47 @@ async function runPrdRunLaunchCommand(options) {
|
|
|
14339
14710
|
let finalReviewResult;
|
|
14340
14711
|
if (!skipped.includes("final-review")) {
|
|
14341
14712
|
attempted.push("final-review");
|
|
14342
|
-
|
|
14343
|
-
|
|
14344
|
-
|
|
14345
|
-
|
|
14346
|
-
prdRef
|
|
14347
|
-
);
|
|
14348
|
-
if (existsSync17(localStorePath2)) {
|
|
14349
|
-
const prdRecord = readPrdRun(options.repoRoot, prdRef);
|
|
14350
|
-
if (prdRecord.record && prdRecord.record.status !== "drained" && !canRetryFinalReviewBlock(prdRecord.record)) {
|
|
14351
|
-
const reason = `Final Review blocked: Queue drain not completed. Status is "${prdRecord.record.status}". Finish all child Issues before Final Review.`;
|
|
14352
|
-
writePrdRunRecord(options.repoRoot, {
|
|
14353
|
-
prdRef,
|
|
14354
|
-
status: "blocked",
|
|
14355
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14356
|
-
blockedGate: "final-review",
|
|
14357
|
-
blockedReason: reason,
|
|
14358
|
-
diagnostics: [
|
|
14359
|
-
`Current status: ${prdRecord.record.status}. Queue drain receipt is missing. Run child Issues through queue-run before Final Review.`
|
|
14360
|
-
],
|
|
14361
|
-
targetName: options.targetName,
|
|
14362
|
-
offendingPaths: []
|
|
14363
|
-
});
|
|
14364
|
-
return {
|
|
14365
|
-
prdRef,
|
|
14366
|
-
status: "blocked",
|
|
14367
|
-
attempted,
|
|
14368
|
-
skipped: [
|
|
14369
|
-
...skipped.includes("prepare") ? ["prepare"] : [],
|
|
14370
|
-
...skipped.includes("start") ? ["queue"] : [],
|
|
14371
|
-
"reconcile"
|
|
14372
|
-
],
|
|
14373
|
-
resumed,
|
|
14374
|
-
diagnostics: [
|
|
14375
|
-
`Current status: ${prdRecord.record.status}. Queue drain receipt is missing. Run child Issues through queue-run before Final Review.`
|
|
14376
|
-
],
|
|
14377
|
-
blockedGate: "final-review",
|
|
14378
|
-
blockedReason: reason,
|
|
14379
|
-
offendingPaths: [],
|
|
14380
|
-
prepare: prepareResult,
|
|
14381
|
-
start: startResult
|
|
14382
|
-
};
|
|
14383
|
-
}
|
|
14384
|
-
const targetConfig = options.config?.targets?.find(
|
|
14385
|
-
(t) => t.name === options.targetName
|
|
14713
|
+
if (existingRecord.record?.mode === "local") {
|
|
14714
|
+
const runnableIssues = await getRunnableLocalIssues(
|
|
14715
|
+
prdRef,
|
|
14716
|
+
options.repoRoot
|
|
14386
14717
|
);
|
|
14387
|
-
|
|
14388
|
-
|
|
14389
|
-
repoRoot: options.repoRoot,
|
|
14390
|
-
verificationCommands
|
|
14391
|
-
});
|
|
14392
|
-
if (!localResult.ok) {
|
|
14393
|
-
const reason = localResult.message ?? "Local Final Review blocked.";
|
|
14394
|
-
writePrdRunRecord(options.repoRoot, {
|
|
14395
|
-
prdRef,
|
|
14396
|
-
status: "blocked",
|
|
14397
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14398
|
-
blockedGate: "final-review",
|
|
14399
|
-
blockedReason: reason,
|
|
14400
|
-
diagnostics: [reason],
|
|
14401
|
-
targetName: options.targetName,
|
|
14402
|
-
offendingPaths: []
|
|
14403
|
-
});
|
|
14404
|
-
return {
|
|
14405
|
-
prdRef,
|
|
14406
|
-
status: "blocked",
|
|
14407
|
-
attempted,
|
|
14408
|
-
skipped: [
|
|
14409
|
-
...skipped.includes("prepare") ? ["prepare"] : [],
|
|
14410
|
-
...skipped.includes("start") ? ["queue"] : [],
|
|
14411
|
-
"reconcile"
|
|
14412
|
-
],
|
|
14413
|
-
resumed,
|
|
14414
|
-
diagnostics: [reason],
|
|
14415
|
-
blockedGate: "final-review",
|
|
14416
|
-
blockedReason: reason,
|
|
14417
|
-
offendingPaths: [],
|
|
14418
|
-
prepare: prepareResult,
|
|
14419
|
-
start: startResult
|
|
14420
|
-
};
|
|
14421
|
-
}
|
|
14422
|
-
if (localResult.verdict === "blocked" || localResult.verdict === "needs_human_review") {
|
|
14423
|
-
const reason = localResult.message ?? `Local Final Review verdict: ${localResult.verdict}`;
|
|
14424
|
-
writePrdRunRecord(options.repoRoot, {
|
|
14425
|
-
prdRef,
|
|
14426
|
-
status: "blocked",
|
|
14427
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14428
|
-
blockedGate: "final-review",
|
|
14429
|
-
blockedReason: reason,
|
|
14430
|
-
diagnostics: [reason],
|
|
14431
|
-
targetName: options.targetName,
|
|
14432
|
-
offendingPaths: []
|
|
14433
|
-
});
|
|
14718
|
+
if (runnableIssues.length > 0) {
|
|
14719
|
+
const reason = `Config-local Final Review blocked: Queue not drained (${runnableIssues.length} runnable Issues remain).`;
|
|
14434
14720
|
return {
|
|
14435
14721
|
prdRef,
|
|
14436
14722
|
status: "blocked",
|
|
14437
14723
|
attempted,
|
|
14438
|
-
skipped: [
|
|
14439
|
-
...skipped.includes("prepare") ? ["prepare"] : [],
|
|
14440
|
-
...skipped.includes("start") ? ["queue"] : [],
|
|
14441
|
-
"reconcile"
|
|
14442
|
-
],
|
|
14724
|
+
skipped: ["start", "queue", "reconcile"],
|
|
14443
14725
|
resumed,
|
|
14444
14726
|
diagnostics: [reason],
|
|
14445
14727
|
blockedGate: "final-review",
|
|
14446
14728
|
blockedReason: reason,
|
|
14447
14729
|
offendingPaths: [],
|
|
14448
14730
|
prepare: prepareResult,
|
|
14449
|
-
start: startResult
|
|
14450
|
-
|
|
14451
|
-
}
|
|
14452
|
-
let retouchMergeCommit;
|
|
14453
|
-
let changedPaths;
|
|
14454
|
-
if (localResult.verdict === "pass_with_retouch") {
|
|
14455
|
-
try {
|
|
14456
|
-
const evidence = await squashFinalReviewRetouch(
|
|
14457
|
-
prdRef,
|
|
14458
|
-
options.repoRoot
|
|
14459
|
-
);
|
|
14460
|
-
if (evidence) {
|
|
14461
|
-
retouchMergeCommit = evidence.mergeCommit;
|
|
14462
|
-
changedPaths = evidence.changedPaths;
|
|
14463
|
-
}
|
|
14464
|
-
} catch (error) {
|
|
14465
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
14466
|
-
writePrdRunRecord(options.repoRoot, {
|
|
14731
|
+
start: startResult,
|
|
14732
|
+
finalReview: {
|
|
14467
14733
|
prdRef,
|
|
14468
14734
|
status: "blocked",
|
|
14469
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14470
14735
|
blockedGate: "final-review",
|
|
14471
14736
|
blockedReason: reason,
|
|
14472
14737
|
diagnostics: [reason],
|
|
14473
|
-
targetName: options.targetName,
|
|
14474
14738
|
offendingPaths: []
|
|
14475
|
-
}
|
|
14476
|
-
|
|
14477
|
-
prdRef,
|
|
14478
|
-
status: "blocked",
|
|
14479
|
-
attempted,
|
|
14480
|
-
skipped: [
|
|
14481
|
-
...skipped.includes("prepare") ? ["prepare"] : [],
|
|
14482
|
-
...skipped.includes("start") ? ["queue"] : [],
|
|
14483
|
-
"reconcile"
|
|
14484
|
-
],
|
|
14485
|
-
resumed,
|
|
14486
|
-
diagnostics: [reason],
|
|
14487
|
-
blockedGate: "final-review",
|
|
14488
|
-
blockedReason: reason,
|
|
14489
|
-
offendingPaths: [],
|
|
14490
|
-
prepare: prepareResult,
|
|
14491
|
-
start: startResult
|
|
14492
|
-
};
|
|
14493
|
-
}
|
|
14739
|
+
}
|
|
14740
|
+
};
|
|
14494
14741
|
}
|
|
14495
|
-
const receipt = {
|
|
14496
|
-
status: "final_reviewed",
|
|
14497
|
-
targetName: options.targetName,
|
|
14498
|
-
prdBranch: `local/${prdRef}`,
|
|
14499
|
-
mergeBase: localResult.receipt?.mergeBase,
|
|
14500
|
-
diagnostics: [],
|
|
14501
|
-
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14502
|
-
verdict: localResult.verdict ?? "pass_no_changes",
|
|
14503
|
-
retouchMergeCommit,
|
|
14504
|
-
changedPaths
|
|
14505
|
-
};
|
|
14506
|
-
const existingRecord2 = readPrdRun(options.repoRoot, prdRef);
|
|
14507
|
-
writePrdRunRecord(options.repoRoot, {
|
|
14508
|
-
prdRef,
|
|
14509
|
-
status: "final_reviewed",
|
|
14510
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14511
|
-
targetName: options.targetName,
|
|
14512
|
-
manifestPath: existingRecord2.record?.manifestPath,
|
|
14513
|
-
planning: existingRecord2.record?.planning,
|
|
14514
|
-
start: existingRecord2.record?.start,
|
|
14515
|
-
finalReview: receipt
|
|
14516
|
-
});
|
|
14517
|
-
finalReviewResult = {
|
|
14518
|
-
prdRef,
|
|
14519
|
-
status: "final_reviewed",
|
|
14520
|
-
finalReview: receipt,
|
|
14521
|
-
diagnostics: localResult.message ? [localResult.message] : []
|
|
14522
|
-
};
|
|
14523
|
-
diagnostics.push(...localResult.message ? [localResult.message] : []);
|
|
14524
|
-
} else {
|
|
14525
|
-
finalReviewResult = await runPrdRunFinalReviewCommand({
|
|
14526
|
-
repoRoot: options.repoRoot,
|
|
14527
|
-
prdRef,
|
|
14528
|
-
targetName: options.targetName,
|
|
14529
|
-
autoMerge: options.autoMerge,
|
|
14530
|
-
issueProvider: options.issueProvider,
|
|
14531
|
-
prProvider: options.prProvider,
|
|
14532
|
-
executionProvider: options.executionProvider,
|
|
14533
|
-
config: options.config,
|
|
14534
|
-
logger: options.logger
|
|
14535
|
-
});
|
|
14536
14742
|
}
|
|
14743
|
+
finalReviewResult = await runPrdRunFinalReviewCommand({
|
|
14744
|
+
repoRoot: options.repoRoot,
|
|
14745
|
+
prdRef,
|
|
14746
|
+
targetName: options.targetName,
|
|
14747
|
+
autoMerge: options.autoMerge,
|
|
14748
|
+
issueProvider: options.issueProvider,
|
|
14749
|
+
prProvider: options.prProvider,
|
|
14750
|
+
executionProvider: options.executionProvider,
|
|
14751
|
+
config: options.config,
|
|
14752
|
+
logger: options.logger
|
|
14753
|
+
});
|
|
14537
14754
|
const skippedAfterFr = [
|
|
14538
14755
|
...skipped.includes("prepare") ? ["prepare"] : [],
|
|
14539
14756
|
...skipped.includes("start") ? ["queue"] : [],
|
|
@@ -14603,6 +14820,28 @@ async function runPrdRunLaunchCommand(options) {
|
|
|
14603
14820
|
finalReview: finalReviewResult
|
|
14604
14821
|
};
|
|
14605
14822
|
}
|
|
14823
|
+
if (prdRecord.record?.mode === "local") {
|
|
14824
|
+
const dualCheck = await verifyLocalDualFinalReviewReceipts(
|
|
14825
|
+
options.repoRoot,
|
|
14826
|
+
prdRef
|
|
14827
|
+
);
|
|
14828
|
+
if (!dualCheck.ok) {
|
|
14829
|
+
return {
|
|
14830
|
+
prdRef,
|
|
14831
|
+
status: "blocked",
|
|
14832
|
+
attempted,
|
|
14833
|
+
skipped: [...skipped],
|
|
14834
|
+
resumed,
|
|
14835
|
+
diagnostics: dualCheck.diagnostics,
|
|
14836
|
+
blockedGate: "reconciliation",
|
|
14837
|
+
blockedReason: dualCheck.reason,
|
|
14838
|
+
offendingPaths: [],
|
|
14839
|
+
prepare: prepareResult,
|
|
14840
|
+
start: startResult,
|
|
14841
|
+
finalReview: finalReviewResult
|
|
14842
|
+
};
|
|
14843
|
+
}
|
|
14844
|
+
}
|
|
14606
14845
|
const localResult = await runLocalReconciliation(prdRef, {
|
|
14607
14846
|
repoRoot: options.repoRoot
|
|
14608
14847
|
});
|
|
@@ -21169,11 +21408,11 @@ function createCliProgram(version) {
|
|
|
21169
21408
|
return program;
|
|
21170
21409
|
}
|
|
21171
21410
|
async function resolveCliVersion() {
|
|
21172
|
-
if (isPackageVersion("0.0.0-next-
|
|
21173
|
-
return "0.0.0-next-
|
|
21411
|
+
if (isPackageVersion("0.0.0-next-20260609213420")) {
|
|
21412
|
+
return "0.0.0-next-20260609213420";
|
|
21174
21413
|
}
|
|
21175
|
-
if (isReleaseVersion("0.0.0-next-
|
|
21176
|
-
return "0.0.0-next-
|
|
21414
|
+
if (isReleaseVersion("0.0.0-next-20260609213420")) {
|
|
21415
|
+
return "0.0.0-next-20260609213420";
|
|
21177
21416
|
}
|
|
21178
21417
|
try {
|
|
21179
21418
|
const root = repoRoot();
|