@pourkit/cli 0.0.0-next-20260607184355 → 0.0.0-next-20260608072322

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 CHANGED
@@ -376,6 +376,7 @@ var VerificationCommandSchema = z.object({
376
376
  command: d.command,
377
377
  label: d.label && d.label.trim() !== "" ? d.label : void 0
378
378
  }));
379
+ var PrdRunModeSchema = z.enum(["github", "local"]);
379
380
  var QueueConfigSchema = z.object({
380
381
  loop: z.boolean().optional()
381
382
  }).strict();
@@ -422,6 +423,7 @@ var ReviewRefactorLoopStrategySchema = z.object({
422
423
  maxAttempts: z.number().int().positive()
423
424
  }).strict(),
424
425
  prdRun: z.object({
426
+ mode: PrdRunModeSchema.optional(),
425
427
  // Uses promptTemplate (canonical StageAgentConfig field), not prompt as Issue contract may suggest
426
428
  finalReview: StageAgentConfigSchema,
427
429
  reconciliation: StageAgentConfigSchema.optional()
@@ -715,6 +717,7 @@ function parseConfig(raw) {
715
717
  },
716
718
  ...t.strategy.prdRun ? {
717
719
  prdRun: {
720
+ ...t.strategy.prdRun.mode ? { mode: t.strategy.prdRun.mode } : {},
718
721
  finalReview: t.strategy.prdRun.finalReview,
719
722
  ...t.strategy.prdRun.reconciliation ? { reconciliation: t.strategy.prdRun.reconciliation } : {}
720
723
  }
@@ -771,14 +774,24 @@ function assertKnownKeys(value, path9, knownKeys) {
771
774
  function getVerificationCommands(target) {
772
775
  return target.strategy.verify?.commands ?? [];
773
776
  }
777
+ function resolvePrdRunMode(target, opts) {
778
+ if (opts?.localOverride === true) {
779
+ return { mode: "local", source: "cli-override" };
780
+ }
781
+ const configMode = target.strategy.prdRun?.mode;
782
+ if (configMode) {
783
+ return { mode: configMode, source: "target-config" };
784
+ }
785
+ return { mode: "github", source: "default" };
786
+ }
774
787
  async function loadRepoConfig(repoRoot2, configFileName = "pourkit.config.ts") {
775
- const { existsSync: existsSync19 } = await import("fs");
788
+ const { existsSync: existsSync20 } = await import("fs");
776
789
  const { mkdir: mkdir6, writeFile: writeFile4, rm } = await import("fs/promises");
777
790
  const { join: pjoin, basename } = await import("path");
778
791
  const { pathToFileURL: pathToFileURL2 } = await import("url");
779
792
  const { build } = await import("esbuild");
780
793
  const configPath = pjoin(repoRoot2, configFileName);
781
- if (!existsSync19(configPath)) {
794
+ if (!existsSync20(configPath)) {
782
795
  throw new Error(
783
796
  `No config file found at ${configPath}. Create a ${configFileName} that exports a default PourkitConfig.`
784
797
  );
@@ -1142,6 +1155,77 @@ function prepareArtifactPath(artifactPath) {
1142
1155
  rmSync(artifactPath, { recursive: true, force: true });
1143
1156
  }
1144
1157
 
1158
+ // commands/run-verification.ts
1159
+ init_common();
1160
+ function buildRunVerificationCommand(target) {
1161
+ return `pourkit run-verification --target ${target.name}`;
1162
+ }
1163
+ async function runVerificationCommand(command, cwd, logger) {
1164
+ try {
1165
+ const result = await execCapture("bash", ["-lc", command.command], {
1166
+ cwd,
1167
+ logger,
1168
+ label: command.label
1169
+ });
1170
+ return {
1171
+ label: command.label,
1172
+ command: command.command,
1173
+ ok: true,
1174
+ stdout: result.stdout,
1175
+ stderr: result.stderr
1176
+ };
1177
+ } catch (error) {
1178
+ return {
1179
+ label: command.label,
1180
+ command: command.command,
1181
+ ok: false,
1182
+ error: error instanceof Error ? error.message : String(error)
1183
+ };
1184
+ }
1185
+ }
1186
+ async function runVerificationCommands(options) {
1187
+ const commands = getVerificationCommands(options.target);
1188
+ const diagnostics = [];
1189
+ if (commands.length === 0) {
1190
+ diagnostics.push(
1191
+ `No verification commands configured for target "${options.target.name}".`
1192
+ );
1193
+ return {
1194
+ ok: true,
1195
+ target: options.target.name,
1196
+ commands: [],
1197
+ diagnostics
1198
+ };
1199
+ }
1200
+ const results = [];
1201
+ for (const command of commands) {
1202
+ const result = await runVerificationCommand(
1203
+ command,
1204
+ options.cwd,
1205
+ options.logger
1206
+ );
1207
+ results.push(result);
1208
+ if (!result.ok) {
1209
+ diagnostics.push(`Verification failed for "${command.label}".`);
1210
+ return {
1211
+ ok: false,
1212
+ target: options.target.name,
1213
+ commands: results,
1214
+ diagnostics
1215
+ };
1216
+ }
1217
+ }
1218
+ diagnostics.push(
1219
+ `Verification passed for target "${options.target.name}".`
1220
+ );
1221
+ return {
1222
+ ok: true,
1223
+ target: options.target.name,
1224
+ commands: results,
1225
+ diagnostics
1226
+ };
1227
+ }
1228
+
1145
1229
  // shared/run-context.ts
1146
1230
  var RUN_CONTEXT_PATH_IN_WORKTREE = ".pourkit/.tmp/run-context.md";
1147
1231
  var ALL_RUN_CONTEXT_SECTIONS = [
@@ -1268,6 +1352,7 @@ function buildRunContextMarkdown(options) {
1268
1352
  if (sections.includes("verification-commands")) {
1269
1353
  parts.push(
1270
1354
  ...renderCommandList(
1355
+ target,
1271
1356
  getVerificationCommands(target),
1272
1357
  "Verification Commands"
1273
1358
  )
@@ -1289,13 +1374,26 @@ function buildRunContextMarkdown(options) {
1289
1374
  }
1290
1375
  return parts.join("\n");
1291
1376
  }
1292
- function renderCommandList(commands, heading) {
1377
+ function renderCommandList(target, commands, heading) {
1293
1378
  if (commands.length === 0) {
1294
- return [`## ${heading}`, "", "(none)", ""];
1379
+ return [
1380
+ `## ${heading}`,
1381
+ "",
1382
+ `Run this command from the repository root: \`${buildRunVerificationCommand(target)}\``,
1383
+ "",
1384
+ "Underlying project commands:",
1385
+ "",
1386
+ "(none configured)",
1387
+ ""
1388
+ ];
1295
1389
  }
1296
1390
  return [
1297
1391
  `## ${heading}`,
1298
1392
  "",
1393
+ `Run this command from the repository root: \`${buildRunVerificationCommand(target)}\``,
1394
+ "",
1395
+ "Underlying project commands:",
1396
+ "",
1299
1397
  "Run these commands from the repository root exactly as written. Do not substitute equivalent scripts from nested package.json files.",
1300
1398
  "",
1301
1399
  ...commands.map((command) => `- ${command.label}: \`${command.command}\``),
@@ -2079,7 +2177,7 @@ ${criteriaBlock}
2079
2177
 
2080
2178
  Write your review to: ${artifactPathInWorktree}
2081
2179
 
2082
- Before handoff, run: npm run pourkit:validate-reviewer -- ${artifactPathInWorktree} --iteration ${iteration}${priorRefactorArtifacts ? " --prior-refactor-artifacts" : ""}
2180
+ Before handoff, run: pourkit validate-artifact reviewer ${artifactPathInWorktree} --iteration ${iteration}${priorRefactorArtifacts ? " --prior-refactor-artifacts" : ""}
2083
2181
 
2084
2182
  Do not provide a separate chat response. The runner only reads the file above.
2085
2183
 
@@ -2615,7 +2713,7 @@ ${latestReview.trimEnd()}
2615
2713
 
2616
2714
  Write your refactor artifact to: ${artifactPathInWorktree}
2617
2715
 
2618
- Before handoff, run: npm run pourkit:validate-refactor -- ${artifactPathInWorktree} --iteration ${iteration}${findingArgs}
2716
+ Before handoff, run: pourkit validate-artifact refactor ${artifactPathInWorktree} --iteration ${iteration}${findingArgs}
2619
2717
 
2620
2718
  When you are done, finish with <promise>COMPLETE</promise>.`);
2621
2719
  });
@@ -2867,8 +2965,9 @@ function parseConflictResolutionArtifact(output) {
2867
2965
  import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
2868
2966
  import { dirname as dirname3, isAbsolute, join as join6, relative } from "path";
2869
2967
  import { z as z2 } from "zod";
2968
+ var PRD_REF_FOUR_DIGIT = /^PRD-\d{4,}$/;
2870
2969
  var PlanningArtifactManifestSchema = z2.object({
2871
- prdRef: z2.string().regex(/^PRD-\d+$/),
2970
+ prdRef: z2.string().regex(PRD_REF_FOUR_DIGIT),
2872
2971
  parentIssue: z2.object({
2873
2972
  number: z2.number().int().positive(),
2874
2973
  url: z2.string().url(),
@@ -2927,7 +3026,15 @@ function readPlanningArtifactManifest(repoRoot2, prdRef) {
2927
3026
  if (!parsed.ok) {
2928
3027
  return parsed;
2929
3028
  }
2930
- return validatePlanningArtifactManifestReadiness(parsed.manifest);
3029
+ const readiness = validatePlanningArtifactManifestReadiness(parsed.manifest);
3030
+ if (!readiness.ok) {
3031
+ return readiness;
3032
+ }
3033
+ return {
3034
+ ok: true,
3035
+ manifest: readiness.manifest,
3036
+ ...location.locationDiagnostics?.length ? { warnings: location.locationDiagnostics } : {}
3037
+ };
2931
3038
  }
2932
3039
  function validatePlanningArtifactManifestAtPath(repoRoot2, manifestPath) {
2933
3040
  let content;
@@ -2984,6 +3091,10 @@ function validatePlanningArtifactManifestReadiness(manifest) {
2984
3091
  }
2985
3092
  return { ok: true, manifest: schemaResult.data };
2986
3093
  }
3094
+ function deriveOldFormatRef(prdRef) {
3095
+ const match = prdRef.match(/^PRD-0(\d{3})$/);
3096
+ return match ? `PRD-${match[1]}` : null;
3097
+ }
2987
3098
  function findPlanningArtifactManifestLocation(repoRoot2, prdRef) {
2988
3099
  const initiativesRoot = join6(
2989
3100
  repoRoot2,
@@ -3010,26 +3121,35 @@ function findPlanningArtifactManifestLocation(repoRoot2, prdRef) {
3010
3121
  }).filter((entry) => entry.isDirectory()).map((entry) => join6(initiativesRoot, entry.name)).sort();
3011
3122
  const diagnostics = [];
3012
3123
  const collatedOffendingPaths = [];
3124
+ const oldFormatRef = deriveOldFormatRef(prdRef);
3013
3125
  for (const initiativeDir of initiativeDirs) {
3014
3126
  const indexPath = join6(initiativeDir, "INDEX.md");
3015
3127
  if (!existsSync5(indexPath)) continue;
3016
3128
  const indexContent = readFileSync5(indexPath, "utf-8");
3017
- const manifestLine = indexContent.split(/\r?\n/).find(
3129
+ const lines = indexContent.split(/\r?\n/);
3130
+ const fourDigitLine = lines.find(
3018
3131
  (line) => line.includes(prdRef) && line.includes("planning-manifest.md")
3019
3132
  );
3133
+ const threeDigitLine = oldFormatRef ? lines.find(
3134
+ (line) => line.includes(oldFormatRef) && line.includes("planning-manifest.md")
3135
+ ) : null;
3136
+ const manifestLine = fourDigitLine ?? threeDigitLine;
3020
3137
  if (!manifestLine) continue;
3021
3138
  const manifestPath = extractBacktickedPath(manifestLine);
3022
3139
  if (!manifestPath) {
3023
3140
  diagnostics.push(
3024
- `INDEX.md matched ${prdRef} but did not expose a manifest path: ${toRepoRelativePath(repoRoot2, indexPath)}`
3141
+ `INDEX.md matched ${prdRef}${oldFormatRef ? ` (or ${oldFormatRef})` : ""} but did not expose a manifest path: ${toRepoRelativePath(repoRoot2, indexPath)}`
3025
3142
  );
3026
3143
  collatedOffendingPaths.push(toRepoRelativePath(repoRoot2, indexPath));
3027
3144
  continue;
3028
3145
  }
3029
- const expectedPattern = new RegExp(
3146
+ const expectedFourDigitPattern = new RegExp(
3030
3147
  `^prds/${prdRef}-[^/]+/planning-manifest\\.md$`
3031
3148
  );
3032
- if (!expectedPattern.test(manifestPath)) {
3149
+ const expectedThreeDigitPattern = oldFormatRef ? new RegExp(`^prds/${oldFormatRef}-[^/]+/planning-manifest\\.md$`) : null;
3150
+ const matchedFourDigit = expectedFourDigitPattern.test(manifestPath);
3151
+ const matchedThreeDigit = expectedThreeDigitPattern?.test(manifestPath) ?? false;
3152
+ if (!matchedFourDigit && !matchedThreeDigit) {
3033
3153
  diagnostics.push(
3034
3154
  `INDEX.md path does not match expected pattern prds/${prdRef}-<slug>/planning-manifest.md. Found: ${manifestPath} (${toRepoRelativePath(repoRoot2, indexPath)})`
3035
3155
  );
@@ -3049,7 +3169,16 @@ function findPlanningArtifactManifestLocation(repoRoot2, prdRef) {
3049
3169
  );
3050
3170
  continue;
3051
3171
  }
3052
- return { ok: true, manifestPath: absoluteManifestPath };
3172
+ if (!matchedFourDigit && matchedThreeDigit) {
3173
+ diagnostics.push(
3174
+ `Old-format PRD ref ${oldFormatRef} found in INDEX.md at ${toRepoRelativePath(repoRoot2, indexPath)} for PRD ${prdRef}; should be migrated to ${prdRef}`
3175
+ );
3176
+ }
3177
+ return {
3178
+ ok: true,
3179
+ manifestPath: absoluteManifestPath,
3180
+ ...diagnostics.length > 0 ? { locationDiagnostics: [...diagnostics] } : {}
3181
+ };
3053
3182
  }
3054
3183
  if (diagnostics.length > 0) {
3055
3184
  return {
@@ -3464,10 +3593,10 @@ function normalizePrdRunRef(ref) {
3464
3593
  const match = trimmed.match(/^(?:PRD-)?(\d+)$/);
3465
3594
  if (!match) {
3466
3595
  throw new Error(
3467
- `Invalid PRD ref "${ref}". Expected format: PRD-<number> (e.g., PRD-043)`
3596
+ `Invalid PRD ref "${ref}". Expected format: PRD-<number> (e.g., PRD-0043)`
3468
3597
  );
3469
3598
  }
3470
- return `PRD-${match[1].padStart(3, "0")}`;
3599
+ return `PRD-${match[1].padStart(4, "0")}`;
3471
3600
  }
3472
3601
  function normalizeRepoPath(pathValue) {
3473
3602
  return pathValue.split("\\").join("/");
@@ -3867,8 +3996,8 @@ function validateLocalPrepare(prdPath, issuePaths, options) {
3867
3996
  return { ...triageResult, gate: "triage_consistency" };
3868
3997
  }
3869
3998
  const protectedBranchNames = ["dev", "next", "main"];
3870
- const barePrdPattern = /^PRD-\d{3}$/;
3871
- const localPrdBranchPattern = /^local\/PRD-\d{3}$/;
3999
+ const barePrdPattern = /^PRD-\d{4}$/;
4000
+ const localPrdBranchPattern = /^local\/PRD-\d{4}$/;
3872
4001
  const dirtyRaw = tryExecSync("git status --porcelain", options?.repoRoot);
3873
4002
  if (dirtyRaw !== null && dirtyRaw.length > 0) {
3874
4003
  return {
@@ -3903,7 +4032,7 @@ function validateLocalPrepare(prdPath, issuePaths, options) {
3903
4032
  ok: false,
3904
4033
  gate: "branch_safety",
3905
4034
  failureCode: "branch_unsafe",
3906
- message: `PRD localPrdBranch "${localPrdBranch}" matches bare PRD-00N pattern without local/ prefix`
4035
+ message: `PRD localPrdBranch "${localPrdBranch}" matches bare PRD-0000N pattern without local/ prefix`
3907
4036
  };
3908
4037
  }
3909
4038
  if (!localPrdBranchPattern.test(localPrdBranch)) {
@@ -3911,7 +4040,7 @@ function validateLocalPrepare(prdPath, issuePaths, options) {
3911
4040
  ok: false,
3912
4041
  gate: "branch_safety",
3913
4042
  failureCode: "branch_unsafe",
3914
- message: `PRD localPrdBranch "${localPrdBranch}" must match local/PRD-00N pattern`
4043
+ message: `PRD localPrdBranch "${localPrdBranch}" must match local/PRD-0000N pattern`
3915
4044
  };
3916
4045
  }
3917
4046
  const branchExists = tryExecSync(
@@ -3965,7 +4094,7 @@ function validateLocalPrepare(prdPath, issuePaths, options) {
3965
4094
  gate: "branch_safety",
3966
4095
  failureCode: "branch_unsafe",
3967
4096
  offendingPath: issuePath,
3968
- message: `Issue branch "${branchName}" matches bare PRD-00N pattern without local/ prefix`
4097
+ message: `Issue branch "${branchName}" matches bare PRD-0000N pattern without local/ prefix`
3969
4098
  };
3970
4099
  }
3971
4100
  }
@@ -4409,12 +4538,12 @@ function validateLocalIssue(path9) {
4409
4538
  message: "id must be a non-empty string"
4410
4539
  };
4411
4540
  }
4412
- if (typeof data.parentPrdId !== "string" || !/^PRD-\d{3}$/.test(data.parentPrdId.trim())) {
4541
+ if (typeof data.parentPrdId !== "string" || !/^PRD-\d{4}$/.test(data.parentPrdId.trim())) {
4413
4542
  return {
4414
4543
  ok: false,
4415
4544
  failureCode: "invalid_issue_artifact",
4416
4545
  offendingPath: path9,
4417
- message: "parentPrdId must match PRD-00N format"
4546
+ message: "parentPrdId must match PRD-0000N format"
4418
4547
  };
4419
4548
  }
4420
4549
  if (typeof data.title !== "string" || data.title.trim() === "") {
@@ -4507,12 +4636,12 @@ function validateLocalIssue(path9) {
4507
4636
  message: "dependencyIssueIds must be an array"
4508
4637
  };
4509
4638
  }
4510
- if (typeof data.branchName !== "string" || !/^local\/PRD-\d{3}\/I-\d{2}-[a-z0-9-]+$/.test(data.branchName)) {
4639
+ if (typeof data.branchName !== "string" || !/^local\/PRD-\d{4}\/I-\d{2}-[a-z0-9-]+$/.test(data.branchName)) {
4511
4640
  return {
4512
4641
  ok: false,
4513
4642
  failureCode: "invalid_issue_artifact",
4514
4643
  offendingPath: path9,
4515
- message: "branchName must match local/PRD-00N/I-0N-slug pattern"
4644
+ message: "branchName must match local/PRD-0000N/I-0N-slug pattern"
4516
4645
  };
4517
4646
  }
4518
4647
  if (!(data.blockedReason === null || typeof data.blockedReason === "string")) {
@@ -4628,7 +4757,7 @@ function validateLocalIssue(path9) {
4628
4757
  }
4629
4758
  return { ok: true };
4630
4759
  }
4631
- var PRD_REF_PATTERN = /^PRD-\d+$/;
4760
+ var PRD_REF_PATTERN = /^PRD-\d{4}$/;
4632
4761
  var EVIDENCE_HASH_PATTERN = /^[0-9a-f]{64}$/;
4633
4762
  var RECONCILIATION_TMP_PATH_PREFIX = ".pourkit/.tmp/reconciliation/";
4634
4763
  function validateReconciliationArtifact(artifact) {
@@ -4639,7 +4768,7 @@ function validateReconciliationArtifact(artifact) {
4639
4768
  const a = artifact;
4640
4769
  if (typeof a.prdRef !== "string" || !PRD_REF_PATTERN.test(a.prdRef)) {
4641
4770
  errors.push(
4642
- `prdRef must match PRD-00N format, got ${JSON.stringify(a.prdRef)}`
4771
+ `prdRef must match PRD-0000N format, got ${JSON.stringify(a.prdRef)}`
4643
4772
  );
4644
4773
  }
4645
4774
  if (a.stage !== "prdReconciliation") {
@@ -4651,7 +4780,7 @@ function validateReconciliationArtifact(artifact) {
4651
4780
  errors.push("checkoutBase must be a non-empty string");
4652
4781
  } else if (!PRD_REF_PATTERN.test(a.checkoutBase.trim())) {
4653
4782
  errors.push(
4654
- `checkoutBase must match active PRD branch format PRD-00N, got ${JSON.stringify(a.checkoutBase)}`
4783
+ `checkoutBase must match active PRD branch format PRD-0000N, got ${JSON.stringify(a.checkoutBase)}`
4655
4784
  );
4656
4785
  }
4657
4786
  if (typeof a.mergeBase !== "string" || a.mergeBase.trim() === "") {
@@ -4680,6 +4809,19 @@ function validateReconciliationArtifact(artifact) {
4680
4809
  );
4681
4810
  }
4682
4811
  }
4812
+ if (a.result === "no_changes_needed") {
4813
+ const paths = a.changedPlanningPaths ?? [];
4814
+ if (Array.isArray(paths) && paths.length > 0) {
4815
+ errors.push(
4816
+ `Reconciliation artifact result is "no_changes_needed" but changedPlanningPaths is not empty (${paths.length} paths).`
4817
+ );
4818
+ }
4819
+ if (!a.noChangeRationale || typeof a.noChangeRationale !== "string") {
4820
+ errors.push(
4821
+ 'Reconciliation artifact result is "no_changes_needed" but noChangeRationale is missing or not a string.'
4822
+ );
4823
+ }
4824
+ }
4683
4825
  if (!Array.isArray(a.completionArtifactPaths) || !a.completionArtifactPaths.every((p) => typeof p === "string")) {
4684
4826
  errors.push("completionArtifactPaths must be an array of strings");
4685
4827
  }
@@ -4835,8 +4977,8 @@ function validateLocalTriage(prdPath, issuePaths) {
4835
4977
  }
4836
4978
 
4837
4979
  // commands/issue-run.ts
4838
- import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
4839
- import { join as join13 } from "path";
4980
+ import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
4981
+ import { join as join14 } from "path";
4840
4982
 
4841
4983
  // pr/templates.ts
4842
4984
  init_common();
@@ -5537,7 +5679,7 @@ async function runFailureResolutionAgent(options) {
5537
5679
  "",
5538
5680
  "Allowed decisions: " + packet.allowedDecisions.join(", "),
5539
5681
  "",
5540
- `Before handoff, run: npm run pourkit:validate-failure-resolution -- ${artifactPath} ${packet.allowedDecisions.map((decision) => `--allowed-decision ${decision}`).join(" ")}`
5682
+ `Before handoff, run: pourkit validate-artifact failure-resolution ${artifactPath} ${packet.allowedDecisions.map((decision) => `--allowed-decision ${decision}`).join(" ")}`
5541
5683
  ].join("\n");
5542
5684
  const retryResult = await executeWithMissingOrEmptyArtifactRetry({
5543
5685
  executionProvider,
@@ -5809,7 +5951,7 @@ Rules:
5809
5951
 
5810
5952
  Write your finalizer output to: ${artifactPathInWorktree}
5811
5953
 
5812
- Before handoff, run: npm run pourkit:validate-finalizer -- ${artifactPathInWorktree}`;
5954
+ Before handoff, run: pourkit validate-artifact finalizer ${artifactPathInWorktree}`;
5813
5955
  }
5814
5956
 
5815
5957
  // commands/pr-description-agent.ts
@@ -6010,6 +6152,293 @@ function persistGeneratedArtifactEffect(worktreePath, output, fs) {
6010
6152
  });
6011
6153
  }
6012
6154
 
6155
+ // prd-run/local-merge-coordinator.ts
6156
+ import { execFileSync as execFileSync2 } from "child_process";
6157
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
6158
+ import { join as join13 } from "path";
6159
+
6160
+ // prd-run/local-branches.ts
6161
+ import { execFileSync } from "child_process";
6162
+ function getLocalPrdBranchName(prdId) {
6163
+ return `local/${prdId}`;
6164
+ }
6165
+ var PROTECTED_BRANCHES = /* @__PURE__ */ new Set(["dev", "next", "main"]);
6166
+ var LOCAL_BRANCH_PATTERN = /^local\/PRD-\d{4}(\/(I-\d{2}(-[a-z0-9-]+)?)?)?$/;
6167
+ function validateLocalBranchName(name) {
6168
+ if (!name || name.length === 0) {
6169
+ return {
6170
+ ok: false,
6171
+ failureCode: "invalid_format",
6172
+ message: "Branch name is empty."
6173
+ };
6174
+ }
6175
+ if (isProtectedBranch(name)) {
6176
+ return {
6177
+ ok: false,
6178
+ failureCode: "protected_branch",
6179
+ message: `Branch "${name}" is protected.`
6180
+ };
6181
+ }
6182
+ if (!LOCAL_BRANCH_PATTERN.test(name)) {
6183
+ return {
6184
+ ok: false,
6185
+ failureCode: "invalid_format",
6186
+ message: `Branch "${name}" does not match the local branch pattern.`
6187
+ };
6188
+ }
6189
+ return { ok: true };
6190
+ }
6191
+ function isProtectedBranch(name) {
6192
+ return PROTECTED_BRANCHES.has(name);
6193
+ }
6194
+ function hasRemoteBackedCollision(localName, repoRoot2) {
6195
+ const match = localName.match(/^local\/(PRD-\d{4})/);
6196
+ if (!match) {
6197
+ return Promise.resolve({
6198
+ ok: false,
6199
+ failureCode: "invalid_format",
6200
+ message: `Cannot extract PRD ref from "${localName}".`
6201
+ });
6202
+ }
6203
+ const prdRef = match[1];
6204
+ try {
6205
+ execFileSync(
6206
+ "git",
6207
+ ["show-ref", "--verify", "--quiet", `refs/heads/${prdRef}`],
6208
+ {
6209
+ cwd: repoRoot2 ?? process.cwd(),
6210
+ encoding: "utf8",
6211
+ stdio: "pipe"
6212
+ }
6213
+ );
6214
+ return Promise.resolve({
6215
+ ok: false,
6216
+ failureCode: "remote_backed_collision",
6217
+ message: `Branch "${prdRef}" exists locally.`
6218
+ });
6219
+ } catch {
6220
+ }
6221
+ try {
6222
+ execFileSync(
6223
+ "git",
6224
+ ["show-ref", "--verify", "--quiet", `refs/remotes/origin/${prdRef}`],
6225
+ {
6226
+ cwd: repoRoot2 ?? process.cwd(),
6227
+ encoding: "utf8",
6228
+ stdio: "pipe"
6229
+ }
6230
+ );
6231
+ return Promise.resolve({
6232
+ ok: false,
6233
+ failureCode: "remote_backed_collision",
6234
+ message: `Remote branch "origin/${prdRef}" exists.`
6235
+ });
6236
+ } catch {
6237
+ }
6238
+ return Promise.resolve({ ok: true });
6239
+ }
6240
+
6241
+ // prd-run/local-merge-coordinator.ts
6242
+ function getLocalStorePath(repoRoot2, prdId) {
6243
+ return join13(repoRoot2, ".pourkit", "local-prd-runs", prdId);
6244
+ }
6245
+ function getMergeReceiptPath(repoRoot2, prdId, issueId) {
6246
+ return join13(
6247
+ getLocalStorePath(repoRoot2, prdId),
6248
+ "merge-receipts",
6249
+ `${issueId}.json`
6250
+ );
6251
+ }
6252
+ function getIssueArtifactPath(repoRoot2, prdId, issueId) {
6253
+ return join13(getLocalStorePath(repoRoot2, prdId), "issues", `${issueId}.json`);
6254
+ }
6255
+ function readIssueBranchName(repoRoot2, prdId, issueId) {
6256
+ const issuePath = getIssueArtifactPath(repoRoot2, prdId, issueId);
6257
+ if (!existsSync9(issuePath)) return null;
6258
+ try {
6259
+ const content = readFileSync11(issuePath, "utf-8");
6260
+ const parsed = JSON.parse(content);
6261
+ return typeof parsed.branchName === "string" && parsed.branchName ? parsed.branchName : null;
6262
+ } catch {
6263
+ return null;
6264
+ }
6265
+ }
6266
+ async function hasLocalIssueMergeReceipt(prdId, issueId, repoRoot2) {
6267
+ const root = repoRoot2 ?? process.cwd();
6268
+ const receiptPath = getMergeReceiptPath(root, prdId, issueId);
6269
+ if (!existsSync9(receiptPath)) return null;
6270
+ try {
6271
+ const content = readFileSync11(receiptPath, "utf-8");
6272
+ const parsed = JSON.parse(content);
6273
+ if (typeof parsed.prdId === "string" && typeof parsed.issueId === "string" && typeof parsed.stage === "string" && typeof parsed.sourceBranch === "string" && typeof parsed.localPrdBranch === "string" && typeof parsed.mergeCommit === "string" && typeof parsed.completedAt === "string") {
6274
+ return parsed;
6275
+ }
6276
+ return null;
6277
+ } catch {
6278
+ return null;
6279
+ }
6280
+ }
6281
+ async function squashMergeLocalIssue(prdId, issueId, input, repoRoot2) {
6282
+ const root = repoRoot2 ?? process.cwd();
6283
+ const receiptPath = getMergeReceiptPath(root, prdId, issueId);
6284
+ if (existsSync9(receiptPath)) {
6285
+ try {
6286
+ const existing = JSON.parse(
6287
+ readFileSync11(receiptPath, "utf-8")
6288
+ );
6289
+ if (existing.prdId === prdId && existing.issueId === issueId && existing.mergeCommit) {
6290
+ return {
6291
+ ok: false,
6292
+ failureCode: "already_merged",
6293
+ repairGuidance: `Issue ${issueId} was already merged into ${existing.localPrdBranch}. Merge commit: ${existing.mergeCommit}`
6294
+ };
6295
+ }
6296
+ return {
6297
+ ok: false,
6298
+ failureCode: "invalid_receipt",
6299
+ repairGuidance: `Merge receipt for ${issueId} belongs to different prd/issue. Remove it manually: ${receiptPath}`
6300
+ };
6301
+ } catch {
6302
+ return {
6303
+ ok: false,
6304
+ failureCode: "invalid_receipt",
6305
+ repairGuidance: `Merge receipt for ${issueId} is corrupted. Remove it manually: ${receiptPath}`
6306
+ };
6307
+ }
6308
+ }
6309
+ const sourceBranch = input?.sourceBranch ?? readIssueBranchName(root, prdId, issueId);
6310
+ if (!sourceBranch) {
6311
+ return {
6312
+ ok: false,
6313
+ failureCode: "not_found",
6314
+ repairGuidance: `No source branch found for issue ${issueId} under PRD ${prdId}. Ensure the issue artifact exists with a valid branchName field.`
6315
+ };
6316
+ }
6317
+ try {
6318
+ execFileSync2(
6319
+ "git",
6320
+ ["show-ref", "--verify", "--quiet", `refs/heads/${sourceBranch}`],
6321
+ { cwd: root, encoding: "utf8", stdio: "pipe" }
6322
+ );
6323
+ } catch {
6324
+ return {
6325
+ ok: false,
6326
+ failureCode: "not_found",
6327
+ repairGuidance: `Source branch "${sourceBranch}" does not exist locally. Check that the branch was created and not deleted.`
6328
+ };
6329
+ }
6330
+ const targetBranch = getLocalPrdBranchName(prdId);
6331
+ try {
6332
+ execFileSync2(
6333
+ "git",
6334
+ ["show-ref", "--verify", "--quiet", `refs/heads/${targetBranch}`],
6335
+ { cwd: root, encoding: "utf8", stdio: "pipe" }
6336
+ );
6337
+ } catch {
6338
+ return {
6339
+ ok: false,
6340
+ failureCode: "not_found",
6341
+ repairGuidance: `Local PRD branch "${targetBranch}" does not exist. Run \`prd-run start\` first to create it.`
6342
+ };
6343
+ }
6344
+ try {
6345
+ execFileSync2("git", ["checkout", targetBranch], {
6346
+ cwd: root,
6347
+ encoding: "utf8",
6348
+ stdio: "pipe"
6349
+ });
6350
+ let preMergeHead;
6351
+ try {
6352
+ preMergeHead = execFileSync2("git", ["rev-parse", "HEAD"], {
6353
+ cwd: root,
6354
+ encoding: "utf8",
6355
+ stdio: "pipe"
6356
+ }).trim();
6357
+ } catch {
6358
+ return {
6359
+ ok: false,
6360
+ failureCode: "merge_error",
6361
+ repairGuidance: "Failed to read current HEAD before merge."
6362
+ };
6363
+ }
6364
+ execFileSync2("git", ["merge", "--squash", sourceBranch], {
6365
+ cwd: root,
6366
+ encoding: "utf8",
6367
+ stdio: "pipe"
6368
+ });
6369
+ if (input) {
6370
+ execFileSync2(
6371
+ "git",
6372
+ ["commit", "-m", input.finalizerTitle, "-m", input.finalizerBody],
6373
+ { cwd: root, encoding: "utf8", stdio: "pipe" }
6374
+ );
6375
+ } else {
6376
+ execFileSync2(
6377
+ "git",
6378
+ ["commit", "-m", `Squash merge ${sourceBranch} into ${targetBranch}`],
6379
+ { cwd: root, encoding: "utf8", stdio: "pipe" }
6380
+ );
6381
+ }
6382
+ const revParseResult = execFileSync2("git", ["rev-parse", "HEAD"], {
6383
+ cwd: root,
6384
+ encoding: "utf8",
6385
+ stdio: "pipe"
6386
+ });
6387
+ const mergeCommit = revParseResult.trim();
6388
+ const receipt = {
6389
+ prdId,
6390
+ issueId,
6391
+ stage: "child-issue",
6392
+ sourceBranch,
6393
+ localPrdBranch: targetBranch,
6394
+ mergeCommit,
6395
+ finalizerArtifactPath: input?.finalizerArtifactPath ?? "",
6396
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
6397
+ };
6398
+ try {
6399
+ const receiptsDir = join13(
6400
+ root,
6401
+ ".pourkit",
6402
+ "local-prd-runs",
6403
+ prdId,
6404
+ "merge-receipts"
6405
+ );
6406
+ mkdirSync7(receiptsDir, { recursive: true });
6407
+ writeFileSync4(receiptPath, JSON.stringify(receipt, null, 2), "utf-8");
6408
+ } catch {
6409
+ try {
6410
+ execFileSync2("git", ["reset", "--hard", preMergeHead], {
6411
+ cwd: root,
6412
+ encoding: "utf8",
6413
+ stdio: "pipe"
6414
+ });
6415
+ } catch {
6416
+ }
6417
+ return {
6418
+ ok: false,
6419
+ failureCode: "receipt_error",
6420
+ repairGuidance: "Failed to write merge receipt. Check disk space and permissions on .pourkit/local-prd-runs/. The merge commit has been rolled back."
6421
+ };
6422
+ }
6423
+ return { ok: true, receipt };
6424
+ } catch (error) {
6425
+ const message = error instanceof Error ? error.message : String(error);
6426
+ if (message.toLowerCase().includes("conflict")) {
6427
+ return {
6428
+ ok: false,
6429
+ failureCode: "conflict",
6430
+ repairGuidance: "Merge conflict during squash. Resolve conflicts in the working tree, then retry."
6431
+ };
6432
+ }
6433
+ const stderr = error instanceof Error && "stderr" in error ? error.stderr : void 0;
6434
+ return {
6435
+ ok: false,
6436
+ failureCode: "merge_error",
6437
+ repairGuidance: stderr ? `Merge failed: ${stderr}` : `Merge failed: ${message}`
6438
+ };
6439
+ }
6440
+ }
6441
+
6013
6442
  // pr/pr-body.ts
6014
6443
  import { readFile as readFile2 } from "fs/promises";
6015
6444
  var DEFAULT_MANUAL_PR_BODY = `## Summary
@@ -6379,7 +6808,8 @@ function parseAffectedCodePaths(body) {
6379
6808
  return Array.from(paths);
6380
6809
  }
6381
6810
  function normalizeParentRef(ref) {
6382
- return ref?.trim().toUpperCase();
6811
+ if (!ref) return void 0;
6812
+ return ref.trim().toUpperCase().replace(/^(PRD-)(\d+)$/, (_, p, n) => `${p}${n.padStart(4, "0")}`);
6383
6813
  }
6384
6814
  function looksLikeRepoPath(value) {
6385
6815
  return /[./][A-Za-z0-9_-]/.test(value) && !value.includes(":");
@@ -6741,44 +7171,75 @@ async function completeIssueRun(options) {
6741
7171
  let prTitle = issue.title;
6742
7172
  let prBody;
6743
7173
  let finalizerResult;
7174
+ const isLocalModeForFinalizer = options.prdRunMode?.mode === "local";
6744
7175
  const finalizerFromState = worktreeState?.finalizer?.completed ? worktreeState.finalizer : null;
6745
7176
  if (finalizerFromState) {
6746
- if (finalizerFromState.title && finalizerFromState.body) {
6747
- prTitle = finalizerFromState.title;
6748
- prBody = finalizerFromState.body;
6749
- } else if (finalizerFromState.artifactPath) {
6750
- if (!existsSync9(finalizerFromState.artifactPath)) {
7177
+ try {
7178
+ if (finalizerFromState.title && finalizerFromState.body) {
7179
+ prTitle = finalizerFromState.title;
7180
+ prBody = finalizerFromState.body;
7181
+ } else if (finalizerFromState.artifactPath) {
7182
+ if (!existsSync10(finalizerFromState.artifactPath)) {
7183
+ throw new FinalizerFailure({
7184
+ message: `Finalizer artifact missing at ${finalizerFromState.artifactPath}`
7185
+ });
7186
+ }
7187
+ const artifactContent = readFileSync12(
7188
+ finalizerFromState.artifactPath,
7189
+ "utf-8"
7190
+ );
7191
+ const parsed = parsePrDescription(artifactContent);
7192
+ prTitle = parsed.title;
7193
+ prBody = parsed.body;
7194
+ } else {
6751
7195
  throw new FinalizerFailure({
6752
- message: `Finalizer artifact missing at ${finalizerFromState.artifactPath}`
7196
+ message: "Finalizer state is incomplete: missing title, body, and artifactPath"
6753
7197
  });
6754
7198
  }
6755
- const artifactContent = readFileSync11(
6756
- finalizerFromState.artifactPath,
6757
- "utf-8"
6758
- );
6759
- const parsed = parsePrDescription(artifactContent);
6760
- prTitle = parsed.title;
6761
- prBody = parsed.body;
6762
- } else {
6763
- throw new FinalizerFailure({
6764
- message: "Finalizer state is incomplete: missing title, body, and artifactPath"
6765
- });
7199
+ } catch (error) {
7200
+ if (isLocalModeForFinalizer) {
7201
+ return {
7202
+ branchName,
7203
+ target,
7204
+ issue,
7205
+ noOp: false,
7206
+ mode: "local",
7207
+ failureCode: "finalizer_failed",
7208
+ repairGuidance: error instanceof Error ? error.message : "Finalizer state is invalid"
7209
+ };
7210
+ }
7211
+ throw error;
6766
7212
  }
6767
7213
  } else {
6768
- finalizerResult = await runEffectAndMapExit(
6769
- runFinalizerAgent({
6770
- executionProvider,
6771
- config,
6772
- target,
6773
- issue,
6774
- builderBranch: branchName,
6775
- targetBaseBranch: effectiveBaseBranch,
6776
- worktreePath: executionResult.worktreePath,
6777
- reviewArtifactPath,
6778
- repoRoot: ROOT,
6779
- logger
6780
- })
6781
- );
7214
+ try {
7215
+ finalizerResult = await runEffectAndMapExit(
7216
+ runFinalizerAgent({
7217
+ executionProvider,
7218
+ config,
7219
+ target,
7220
+ issue,
7221
+ builderBranch: branchName,
7222
+ targetBaseBranch: effectiveBaseBranch,
7223
+ worktreePath: executionResult.worktreePath,
7224
+ reviewArtifactPath,
7225
+ repoRoot: ROOT,
7226
+ logger
7227
+ })
7228
+ );
7229
+ } catch (error) {
7230
+ if (isLocalModeForFinalizer) {
7231
+ return {
7232
+ branchName,
7233
+ target,
7234
+ issue,
7235
+ noOp: false,
7236
+ mode: "local",
7237
+ failureCode: "finalizer_failed",
7238
+ repairGuidance: error instanceof Error ? error.message : "Finalizer execution failed"
7239
+ };
7240
+ }
7241
+ throw error;
7242
+ }
6782
7243
  prTitle = finalizerResult.title;
6783
7244
  prBody = finalizerResult.body;
6784
7245
  }
@@ -6823,6 +7284,113 @@ async function completeIssueRun(options) {
6823
7284
  });
6824
7285
  }
6825
7286
  }
7287
+ const isLocalMode = options.prdRunMode?.mode === "local";
7288
+ if (isLocalMode) {
7289
+ const parsed = parseStackedIssue(issue.title, issue.body);
7290
+ const prdId = parsed.parentRef;
7291
+ if (!prdId) {
7292
+ return {
7293
+ branchName,
7294
+ target,
7295
+ issue,
7296
+ noOp: false,
7297
+ mode: "local",
7298
+ failureCode: "finalizer_failed",
7299
+ repairGuidance: "Local mode requires a PRD reference (## Parent) in the issue body or title. Add the parent PRD reference and retry."
7300
+ };
7301
+ }
7302
+ const finalizerFromWorktreeState = worktreeState?.finalizer;
7303
+ if (finalizerFromWorktreeState && !finalizerFromWorktreeState.completed) {
7304
+ return {
7305
+ branchName,
7306
+ target,
7307
+ issue,
7308
+ noOp: false,
7309
+ mode: "local",
7310
+ failureCode: "finalizer_failed",
7311
+ repairGuidance: "Finalizer did not complete on previous run. Re-run the issue finalizer and try again."
7312
+ };
7313
+ }
7314
+ if (!prTitle || !finalBody) {
7315
+ return {
7316
+ branchName,
7317
+ target,
7318
+ issue,
7319
+ noOp: false,
7320
+ mode: "local",
7321
+ failureCode: "finalizer_failed",
7322
+ repairGuidance: "Finalizer produced empty title or body. Re-run the issue finalizer and try again."
7323
+ };
7324
+ }
7325
+ const existingReceipt = await hasLocalIssueMergeReceipt(
7326
+ prdId,
7327
+ `issue-${issueNumber}`,
7328
+ ROOT
7329
+ );
7330
+ if (existingReceipt) {
7331
+ return {
7332
+ branchName,
7333
+ target,
7334
+ issue,
7335
+ noOp: false,
7336
+ mode: "local",
7337
+ failureCode: "already_merged",
7338
+ localPrdBranch: existingReceipt.localPrdBranch,
7339
+ mergeCommit: existingReceipt.mergeCommit,
7340
+ receiptPath: join14(
7341
+ ROOT,
7342
+ ".pourkit",
7343
+ "local-prd-runs",
7344
+ prdId,
7345
+ "merge-receipts",
7346
+ `issue-${issueNumber}.json`
7347
+ )
7348
+ };
7349
+ }
7350
+ const mergeResult = await squashMergeLocalIssue(
7351
+ prdId,
7352
+ `issue-${issueNumber}`,
7353
+ {
7354
+ finalizerTitle: prTitle,
7355
+ finalizerBody: finalBody,
7356
+ finalizerArtifactPath: worktreeState?.finalizer?.artifactPath ?? "",
7357
+ sourceBranch: branchName
7358
+ },
7359
+ ROOT
7360
+ );
7361
+ if (!mergeResult.ok) {
7362
+ return {
7363
+ branchName,
7364
+ target,
7365
+ issue,
7366
+ noOp: false,
7367
+ mode: "local",
7368
+ failureCode: mergeResult.failureCode,
7369
+ repairGuidance: mergeResult.repairGuidance ?? `Local squash-merge failed: ${mergeResult.failureCode}`
7370
+ };
7371
+ }
7372
+ await issueProvider.removeLabel(
7373
+ issueNumber,
7374
+ config.labels.agentInProgress
7375
+ );
7376
+ return {
7377
+ branchName,
7378
+ target,
7379
+ issue,
7380
+ noOp: false,
7381
+ mode: "local",
7382
+ localPrdBranch: getLocalPrdBranchName(prdId),
7383
+ mergeCommit: mergeResult.receipt.mergeCommit,
7384
+ receiptPath: join14(
7385
+ ROOT,
7386
+ ".pourkit",
7387
+ "local-prd-runs",
7388
+ prdId,
7389
+ "merge-receipts",
7390
+ `issue-${issueNumber}.json`
7391
+ )
7392
+ };
7393
+ }
6826
7394
  let pr;
6827
7395
  if (worktreeState?.pr?.merged) {
6828
7396
  mergeCompleted = true;
@@ -7264,7 +7832,7 @@ async function resolveIssueWorktree(root, branchName, baseBranch, logger) {
7264
7832
  return { mode: "new", branchName, baseRef };
7265
7833
  }
7266
7834
  function issueWorktreePath(root, branchName) {
7267
- return join13(root, ".sandcastle", "worktrees", branchName.replace(/\//g, "-"));
7835
+ return join14(root, ".sandcastle", "worktrees", branchName.replace(/\//g, "-"));
7268
7836
  }
7269
7837
  function resolveRegisteredIssueWorktreePath(worktreeListPorcelain, root, branchName) {
7270
7838
  const branchWorktreePath = parseWorktreeListPorcelain(
@@ -7292,7 +7860,7 @@ async function syncTargetBranch(root, baseBranch, logger) {
7292
7860
  }
7293
7861
  function loadBuilderPrompt(repoRoot2, promptTemplate) {
7294
7862
  const promptPath = resolvePromptTemplatePath(repoRoot2, promptTemplate);
7295
- const promptBody = existsSync9(promptPath) ? readFileSync11(promptPath, "utf-8") : promptTemplate;
7863
+ const promptBody = existsSync10(promptPath) ? readFileSync12(promptPath, "utf-8") : promptTemplate;
7296
7864
  return appendProtectedWorkGuidance(`${promptBody}
7297
7865
 
7298
7866
  ## Shared Run Context
@@ -7735,33 +8303,36 @@ async function runIssueCreateCommand(args, issueProvider, logger) {
7735
8303
  // commands/prd-run.ts
7736
8304
  import {
7737
8305
  cpSync,
7738
- existsSync as existsSync16,
8306
+ existsSync as existsSync17,
7739
8307
  lstatSync,
7740
- mkdirSync as mkdirSync10,
8308
+ mkdirSync as mkdirSync11,
7741
8309
  mkdtempSync,
7742
8310
  readdirSync as readdirSync6,
7743
- readFileSync as readFileSync15,
8311
+ readFileSync as readFileSync16,
7744
8312
  realpathSync,
7745
8313
  rmSync as rmSync3
7746
8314
  } from "fs";
7747
8315
  import { spawnSync as spawnSync2 } from "child_process";
7748
- import { dirname as dirname5, join as join19, relative as relative2 } from "path";
8316
+ import { dirname as dirname5, join as join20, relative as relative2 } from "path";
7749
8317
  import { tmpdir } from "os";
7750
8318
 
7751
8319
  // prd-run/state.ts
7752
8320
  import {
7753
- existsSync as existsSync10,
7754
- mkdirSync as mkdirSync7,
7755
- readFileSync as readFileSync12,
8321
+ existsSync as existsSync11,
8322
+ mkdirSync as mkdirSync8,
8323
+ readFileSync as readFileSync13,
7756
8324
  readdirSync as readdirSync4,
7757
- writeFileSync as writeFileSync4
8325
+ writeFileSync as writeFileSync5
7758
8326
  } from "fs";
7759
8327
  import { mkdir as mkdir4, readFile as readFile4, writeFile } from "fs/promises";
7760
- import { join as join14 } from "path";
8328
+ import { join as join15 } from "path";
7761
8329
  import { z as z3 } from "zod";
7762
8330
  var PRD_RUN_STATE_DIR = ".pourkit/prd-runs";
7763
8331
  var PrdRunRecordSchema = z3.object({
7764
- prdRef: z3.string().regex(/^PRD-\d+$/),
8332
+ prdRef: z3.string().regex(
8333
+ /^PRD-\d{4}$/,
8334
+ "PRD ref must use four-digit format (e.g., PRD-0052)"
8335
+ ),
7765
8336
  status: z3.enum([
7766
8337
  "blocked",
7767
8338
  "preparing",
@@ -7890,20 +8461,20 @@ function normalizePrdRunRef2(ref) {
7890
8461
  const match = trimmed.match(/^(?:PRD-)?(\d+)$/);
7891
8462
  if (!match) {
7892
8463
  throw new Error(
7893
- `Invalid PRD ref "${ref}". Expected format: PRD-<number> (e.g., PRD-043)`
8464
+ `Invalid PRD ref "${ref}". Expected format: PRD-<number> (e.g., PRD-0043)`
7894
8465
  );
7895
8466
  }
7896
- return `PRD-${match[1].padStart(3, "0")}`;
8467
+ return `PRD-${match[1].padStart(4, "0")}`;
7897
8468
  }
7898
8469
  function readPrdRun(repoRoot2, prdRef) {
7899
8470
  const normalized = normalizePrdRunRef2(prdRef);
7900
8471
  const recordPath = getRecordPath(repoRoot2, normalized);
7901
- if (!existsSync10(recordPath)) {
8472
+ if (!existsSync11(recordPath)) {
7902
8473
  return { record: null, diagnostics: [] };
7903
8474
  }
7904
8475
  try {
7905
8476
  const parsed = PrdRunRecordSchema.parse(
7906
- JSON.parse(readFileSync12(recordPath, "utf-8"))
8477
+ JSON.parse(readFileSync13(recordPath, "utf-8"))
7907
8478
  );
7908
8479
  return { record: parsed, diagnostics: [] };
7909
8480
  } catch (error) {
@@ -7917,8 +8488,8 @@ function readPrdRun(repoRoot2, prdRef) {
7917
8488
  }
7918
8489
  }
7919
8490
  function listPrdRuns(repoRoot2) {
7920
- const stateDir = join14(repoRoot2, PRD_RUN_STATE_DIR);
7921
- if (!existsSync10(stateDir)) {
8491
+ const stateDir = join15(repoRoot2, PRD_RUN_STATE_DIR);
8492
+ if (!existsSync11(stateDir)) {
7922
8493
  return { records: [], diagnostics: [] };
7923
8494
  }
7924
8495
  const records = [];
@@ -7927,10 +8498,10 @@ function listPrdRuns(repoRoot2) {
7927
8498
  (left, right) => left.name.localeCompare(right.name)
7928
8499
  )) {
7929
8500
  if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
7930
- const recordPath = join14(stateDir, entry.name);
8501
+ const recordPath = join15(stateDir, entry.name);
7931
8502
  try {
7932
8503
  const record = PrdRunRecordSchema.parse(
7933
- JSON.parse(readFileSync12(recordPath, "utf-8"))
8504
+ JSON.parse(readFileSync13(recordPath, "utf-8"))
7934
8505
  );
7935
8506
  records.push(record);
7936
8507
  } catch (error) {
@@ -7943,10 +8514,10 @@ function listPrdRuns(repoRoot2) {
7943
8514
  }
7944
8515
  function writePrdRunRecord(repoRoot2, record) {
7945
8516
  const normalized = normalizePrdRunRef2(record.prdRef);
7946
- const stateDir = join14(repoRoot2, PRD_RUN_STATE_DIR);
8517
+ const stateDir = join15(repoRoot2, PRD_RUN_STATE_DIR);
7947
8518
  const recordPath = getRecordPath(repoRoot2, normalized);
7948
- mkdirSync7(stateDir, { recursive: true });
7949
- writeFileSync4(
8519
+ mkdirSync8(stateDir, { recursive: true });
8520
+ writeFileSync5(
7950
8521
  recordPath,
7951
8522
  JSON.stringify({ ...record, prdRef: normalized }, null, 2),
7952
8523
  "utf-8"
@@ -7954,7 +8525,7 @@ function writePrdRunRecord(repoRoot2, record) {
7954
8525
  }
7955
8526
  var LOCAL_PRD_RUN_STATE_DIR = ".pourkit/local-prd-runs";
7956
8527
  var LocalPrdRunRecordSchema = z3.object({
7957
- prdId: z3.string().regex(/^PRD-\d+$/),
8528
+ prdId: z3.string().regex(/^PRD-\d{4}$/, "PRD id must use four-digit format (e.g., PRD-0052)"),
7958
8529
  createdAt: z3.string().min(1),
7959
8530
  receipts: z3.object({
7960
8531
  prepare: z3.object({
@@ -7983,8 +8554,8 @@ var LocalPrdRunRecordSchema = z3.object({
7983
8554
  }),
7984
8555
  metadata: z3.record(z3.unknown())
7985
8556
  });
7986
- function getLocalStorePath(repoRoot2, prdId) {
7987
- return join14(
8557
+ function getLocalStorePath2(repoRoot2, prdId) {
8558
+ return join15(
7988
8559
  repoRoot2,
7989
8560
  LOCAL_PRD_RUN_STATE_DIR,
7990
8561
  `${normalizePrdRunRef2(prdId)}.json`
@@ -7992,8 +8563,8 @@ function getLocalStorePath(repoRoot2, prdId) {
7992
8563
  }
7993
8564
  async function readLocalPrdRun(repoRoot2, prdId) {
7994
8565
  const normalized = normalizePrdRunRef2(prdId);
7995
- const recordPath = getLocalStorePath(repoRoot2, normalized);
7996
- if (!existsSync10(recordPath)) {
8566
+ const recordPath = getLocalStorePath2(repoRoot2, normalized);
8567
+ if (!existsSync11(recordPath)) {
7997
8568
  return null;
7998
8569
  }
7999
8570
  try {
@@ -8005,16 +8576,16 @@ async function readLocalPrdRun(repoRoot2, prdId) {
8005
8576
  }
8006
8577
  async function writeLocalPrdRunRecord(repoRoot2, prdId, record) {
8007
8578
  const normalized = normalizePrdRunRef2(prdId);
8008
- const storeDir = join14(repoRoot2, LOCAL_PRD_RUN_STATE_DIR);
8579
+ const storeDir = join15(repoRoot2, LOCAL_PRD_RUN_STATE_DIR);
8009
8580
  await mkdir4(storeDir, { recursive: true });
8010
8581
  await writeFile(
8011
- getLocalStorePath(repoRoot2, normalized),
8582
+ getLocalStorePath2(repoRoot2, normalized),
8012
8583
  JSON.stringify({ ...record, prdId: normalized }, null, 2),
8013
8584
  "utf-8"
8014
8585
  );
8015
8586
  }
8016
8587
  function getRecordPath(repoRoot2, prdRef) {
8017
- return join14(
8588
+ return join15(
8018
8589
  repoRoot2,
8019
8590
  PRD_RUN_STATE_DIR,
8020
8591
  `${normalizePrdRunRef2(prdRef)}.json`
@@ -8147,9 +8718,9 @@ function redactSensitiveValues(input) {
8147
8718
  }
8148
8719
 
8149
8720
  // prd-run/final-review-validation.ts
8150
- import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
8721
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
8151
8722
  function parseFinalReviewArtifact(artifactPath) {
8152
- if (!existsSync11(artifactPath)) {
8723
+ if (!existsSync12(artifactPath)) {
8153
8724
  return {
8154
8725
  ok: false,
8155
8726
  reason: "Final Review artifact not found.",
@@ -8158,7 +8729,7 @@ function parseFinalReviewArtifact(artifactPath) {
8158
8729
  }
8159
8730
  let content;
8160
8731
  try {
8161
- content = readFileSync13(artifactPath, "utf-8");
8732
+ content = readFileSync14(artifactPath, "utf-8");
8162
8733
  } catch (error) {
8163
8734
  return {
8164
8735
  ok: false,
@@ -8338,8 +8909,8 @@ function isRetouchScopePath(path9) {
8338
8909
  init_common();
8339
8910
 
8340
8911
  // prd-run/local-artifacts.ts
8341
- import { existsSync as existsSync12 } from "fs";
8342
- import { join as join15 } from "path";
8912
+ import { existsSync as existsSync13 } from "fs";
8913
+ import { join as join16 } from "path";
8343
8914
  var REQUIRED_PRD_FIELDS = [
8344
8915
  "schemaVersion",
8345
8916
  "kind",
@@ -8372,13 +8943,13 @@ var REQUIRED_ISSUE_FIELDS = [
8372
8943
  "githubProjection"
8373
8944
  ];
8374
8945
  function prdStorePath(repoRoot2, prdId) {
8375
- return join15(repoRoot2, ".pourkit", "local-prd-runs", prdId);
8946
+ return join16(repoRoot2, ".pourkit", "local-prd-runs", prdId);
8376
8947
  }
8377
8948
  function prdArtifactPath(repoRoot2, prdId) {
8378
- return join15(prdStorePath(repoRoot2, prdId), "prd.json");
8949
+ return join16(prdStorePath(repoRoot2, prdId), "prd.json");
8379
8950
  }
8380
8951
  function issueArtifactPath(repoRoot2, prdId, issueId) {
8381
- return join15(prdStorePath(repoRoot2, prdId), "issues", `${issueId}.json`);
8952
+ return join16(prdStorePath(repoRoot2, prdId), "issues", `${issueId}.json`);
8382
8953
  }
8383
8954
  function hasRequiredFields(data, requiredFields) {
8384
8955
  for (const field of requiredFields) {
@@ -8391,7 +8962,7 @@ function hasRequiredFields(data, requiredFields) {
8391
8962
  async function resolveLocalPrdArtifact(prdId, repoRoot2) {
8392
8963
  const root = repoRoot2 ?? process.cwd();
8393
8964
  const prdPath = prdArtifactPath(root, prdId);
8394
- if (!existsSync12(prdPath)) {
8965
+ if (!existsSync13(prdPath)) {
8395
8966
  return {
8396
8967
  ok: false,
8397
8968
  failureCode: "missing_prd_artifact",
@@ -8436,7 +9007,7 @@ async function resolveLocalIssueArtifacts(prdId, repoRoot2) {
8436
9007
  const issues = [];
8437
9008
  for (const childId of childIssueIds) {
8438
9009
  const issuePath = issueArtifactPath(root, prdId, childId);
8439
- if (!existsSync12(issuePath)) {
9010
+ if (!existsSync13(issuePath)) {
8440
9011
  return {
8441
9012
  ok: false,
8442
9013
  failureCode: "missing_child_issue",
@@ -8572,97 +9143,14 @@ async function getRunnableLocalIssues(prdId, repoRoot2) {
8572
9143
  }
8573
9144
 
8574
9145
  // prd-run/local-final-review.ts
8575
- import { execFileSync as execFileSync2, execSync as execSync2 } from "child_process";
8576
- import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync5 } from "fs";
8577
- import { join as join16 } from "path";
8578
-
8579
- // prd-run/local-branches.ts
8580
- import { execFileSync } from "child_process";
8581
- function getLocalPrdBranchName(prdId) {
8582
- return `local/${prdId}`;
8583
- }
8584
- var PROTECTED_BRANCHES = /* @__PURE__ */ new Set(["dev", "next", "main"]);
8585
- var LOCAL_BRANCH_PATTERN = /^local\/PRD-\d{3}(\/(I-\d{2}(-[a-z0-9-]+)?)?)?$/;
8586
- function validateLocalBranchName(name) {
8587
- if (!name || name.length === 0) {
8588
- return {
8589
- ok: false,
8590
- failureCode: "invalid_format",
8591
- message: "Branch name is empty."
8592
- };
8593
- }
8594
- if (isProtectedBranch(name)) {
8595
- return {
8596
- ok: false,
8597
- failureCode: "protected_branch",
8598
- message: `Branch "${name}" is protected.`
8599
- };
8600
- }
8601
- if (!LOCAL_BRANCH_PATTERN.test(name)) {
8602
- return {
8603
- ok: false,
8604
- failureCode: "invalid_format",
8605
- message: `Branch "${name}" does not match the local branch pattern.`
8606
- };
8607
- }
8608
- return { ok: true };
8609
- }
8610
- function isProtectedBranch(name) {
8611
- return PROTECTED_BRANCHES.has(name);
8612
- }
8613
- function hasRemoteBackedCollision(localName, repoRoot2) {
8614
- const match = localName.match(/^local\/(PRD-\d{3})/);
8615
- if (!match) {
8616
- return Promise.resolve({
8617
- ok: false,
8618
- failureCode: "invalid_format",
8619
- message: `Cannot extract PRD ref from "${localName}".`
8620
- });
8621
- }
8622
- const prdRef = match[1];
8623
- try {
8624
- execFileSync(
8625
- "git",
8626
- ["show-ref", "--verify", "--quiet", `refs/heads/${prdRef}`],
8627
- {
8628
- cwd: repoRoot2 ?? process.cwd(),
8629
- encoding: "utf8",
8630
- stdio: "pipe"
8631
- }
8632
- );
8633
- return Promise.resolve({
8634
- ok: false,
8635
- failureCode: "remote_backed_collision",
8636
- message: `Branch "${prdRef}" exists locally.`
8637
- });
8638
- } catch {
8639
- }
8640
- try {
8641
- execFileSync(
8642
- "git",
8643
- ["show-ref", "--verify", "--quiet", `refs/remotes/origin/${prdRef}`],
8644
- {
8645
- cwd: repoRoot2 ?? process.cwd(),
8646
- encoding: "utf8",
8647
- stdio: "pipe"
8648
- }
8649
- );
8650
- return Promise.resolve({
8651
- ok: false,
8652
- failureCode: "remote_backed_collision",
8653
- message: `Remote branch "origin/${prdRef}" exists.`
8654
- });
8655
- } catch {
8656
- }
8657
- return Promise.resolve({ ok: true });
8658
- }
8659
-
8660
- // prd-run/local-final-review.ts
8661
- function getLocalStorePath2(repoRoot2, prdId) {
8662
- return join16(repoRoot2, ".pourkit", "local-prd-runs", prdId);
9146
+ import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
9147
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
9148
+ import { join as join17 } from "path";
9149
+ function getLocalStorePath3(repoRoot2, prdId) {
9150
+ return join17(repoRoot2, ".pourkit", "local-prd-runs", prdId);
8663
9151
  }
8664
9152
  function getFinalReviewReceiptPath(repoRoot2, prdId) {
8665
- return join16(getLocalStorePath2(repoRoot2, prdId), "final-review-receipt.json");
9153
+ return join17(getLocalStorePath3(repoRoot2, prdId), "final-review-receipt.json");
8666
9154
  }
8667
9155
  async function runLocalFinalReview(prdId, options) {
8668
9156
  const root = options?.repoRoot ?? process.cwd();
@@ -8719,8 +9207,8 @@ async function runLocalFinalReview(prdId, options) {
8719
9207
  branch
8720
9208
  };
8721
9209
  const receiptPath = getFinalReviewReceiptPath(root, prdId);
8722
- mkdirSync8(getLocalStorePath2(root, prdId), { recursive: true });
8723
- writeFileSync5(receiptPath, JSON.stringify(receipt, null, 2), "utf-8");
9210
+ mkdirSync9(getLocalStorePath3(root, prdId), { recursive: true });
9211
+ writeFileSync6(receiptPath, JSON.stringify(receipt, null, 2), "utf-8");
8724
9212
  const result = { ok: true, verdict, receipt };
8725
9213
  if (failures.length > 0) {
8726
9214
  result.message = failures.map(
@@ -8734,7 +9222,7 @@ async function squashFinalReviewRetouch(prdId, repoRoot2) {
8734
9222
  const targetBranch = `local/${prdId}`;
8735
9223
  const retouchBranch = `local/${prdId}/retouch`;
8736
9224
  try {
8737
- execFileSync2(
9225
+ execFileSync3(
8738
9226
  "git",
8739
9227
  ["show-ref", "--verify", "--quiet", `refs/heads/${retouchBranch}`],
8740
9228
  {
@@ -8747,7 +9235,7 @@ async function squashFinalReviewRetouch(prdId, repoRoot2) {
8747
9235
  return;
8748
9236
  }
8749
9237
  try {
8750
- execFileSync2(
9238
+ execFileSync3(
8751
9239
  "git",
8752
9240
  ["show-ref", "--verify", "--quiet", `refs/heads/${targetBranch}`],
8753
9241
  {
@@ -8760,18 +9248,18 @@ async function squashFinalReviewRetouch(prdId, repoRoot2) {
8760
9248
  return;
8761
9249
  }
8762
9250
  try {
8763
- execFileSync2("git", ["checkout", targetBranch], {
9251
+ execFileSync3("git", ["checkout", targetBranch], {
8764
9252
  cwd: root,
8765
9253
  encoding: "utf8",
8766
9254
  stdio: "pipe"
8767
9255
  });
8768
- execFileSync2("git", ["merge", "--squash", retouchBranch], {
9256
+ execFileSync3("git", ["merge", "--squash", retouchBranch], {
8769
9257
  cwd: root,
8770
9258
  encoding: "utf8",
8771
9259
  stdio: "pipe"
8772
9260
  });
8773
9261
  try {
8774
- execFileSync2("git", ["diff", "--cached", "--quiet"], {
9262
+ execFileSync3("git", ["diff", "--cached", "--quiet"], {
8775
9263
  cwd: root,
8776
9264
  encoding: "utf8",
8777
9265
  stdio: "pipe"
@@ -8779,7 +9267,7 @@ async function squashFinalReviewRetouch(prdId, repoRoot2) {
8779
9267
  return;
8780
9268
  } catch {
8781
9269
  }
8782
- execFileSync2(
9270
+ execFileSync3(
8783
9271
  "git",
8784
9272
  ["commit", "-m", `Squash merge ${retouchBranch} into ${targetBranch}`],
8785
9273
  {
@@ -8798,53 +9286,53 @@ async function squashFinalReviewRetouch(prdId, repoRoot2) {
8798
9286
  }
8799
9287
 
8800
9288
  // prd-run/local-reconciliation.ts
8801
- import { execFileSync as execFileSync3 } from "child_process";
9289
+ import { execFileSync as execFileSync4 } from "child_process";
8802
9290
  import {
8803
- existsSync as existsSync14,
8804
- mkdirSync as mkdirSync9,
8805
- readFileSync as readFileSync14,
9291
+ existsSync as existsSync15,
9292
+ mkdirSync as mkdirSync10,
9293
+ readFileSync as readFileSync15,
8806
9294
  readdirSync as readdirSync5,
8807
- writeFileSync as writeFileSync6
9295
+ writeFileSync as writeFileSync7
8808
9296
  } from "fs";
8809
- import { join as join17 } from "path";
8810
- function getLocalStorePath3(repoRoot2, prdId) {
8811
- return join17(repoRoot2, ".pourkit", "local-prd-runs", prdId);
9297
+ import { join as join18 } from "path";
9298
+ function getLocalStorePath4(repoRoot2, prdId) {
9299
+ return join18(repoRoot2, ".pourkit", "local-prd-runs", prdId);
8812
9300
  }
8813
9301
  function getFinalReviewReceiptPath2(repoRoot2, prdId) {
8814
- return join17(getLocalStorePath3(repoRoot2, prdId), "final-review-receipt.json");
9302
+ return join18(getLocalStorePath4(repoRoot2, prdId), "final-review-receipt.json");
8815
9303
  }
8816
9304
  function getHandoffArtifactPath(repoRoot2, prdId) {
8817
- return join17(repoRoot2, ".pourkit", ".tmp", "reconciliation", `${prdId}.json`);
9305
+ return join18(repoRoot2, ".pourkit", ".tmp", "reconciliation", `${prdId}.json`);
8818
9306
  }
8819
9307
  function getCompletionsDir(repoRoot2, prdId) {
8820
- const initiativesRoot = join17(
9308
+ const initiativesRoot = join18(
8821
9309
  repoRoot2,
8822
9310
  ".pourkit",
8823
9311
  "architecture",
8824
9312
  "initiatives"
8825
9313
  );
8826
- if (existsSync14(initiativesRoot)) {
8827
- const dirs = readdirSync5(initiativesRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join17(initiativesRoot, e.name));
9314
+ if (existsSync15(initiativesRoot)) {
9315
+ const dirs = readdirSync5(initiativesRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join18(initiativesRoot, e.name));
8828
9316
  for (const dir of dirs) {
8829
- const prdsDir = join17(dir, "prds");
8830
- if (!existsSync14(prdsDir)) continue;
9317
+ const prdsDir = join18(dir, "prds");
9318
+ if (!existsSync15(prdsDir)) continue;
8831
9319
  const prdDirs = readdirSync5(prdsDir, { withFileTypes: true }).filter(
8832
9320
  (e) => e.isDirectory() && e.name.startsWith(prdId.toLowerCase())
8833
9321
  );
8834
- if (prdDirs.length > 0) return join17(dir, "completions");
9322
+ if (prdDirs.length > 0) return join18(dir, "completions");
8835
9323
  }
8836
9324
  }
8837
- return join17(getLocalStorePath3(repoRoot2, prdId), "completions");
9325
+ return join18(getLocalStorePath4(repoRoot2, prdId), "completions");
8838
9326
  }
8839
9327
  function getReconciliationReceiptPath(repoRoot2, prdId) {
8840
- return join17(
8841
- getLocalStorePath3(repoRoot2, prdId),
9328
+ return join18(
9329
+ getLocalStorePath4(repoRoot2, prdId),
8842
9330
  "reconciliation-receipt.json"
8843
9331
  );
8844
9332
  }
8845
9333
  async function runLocalReconciliation(prdId, options) {
8846
9334
  const root = options?.repoRoot ?? process.cwd();
8847
- if (!existsSync14(getFinalReviewReceiptPath2(root, prdId))) {
9335
+ if (!existsSync15(getFinalReviewReceiptPath2(root, prdId))) {
8848
9336
  return {
8849
9337
  ok: false,
8850
9338
  failureCode: "missing_final_review",
@@ -8852,7 +9340,7 @@ async function runLocalReconciliation(prdId, options) {
8852
9340
  };
8853
9341
  }
8854
9342
  try {
8855
- execFileSync3("pourkit-architect", ["reconcile", prdId], {
9343
+ execFileSync4("pourkit-architect", ["reconcile", prdId], {
8856
9344
  cwd: root,
8857
9345
  encoding: "utf8",
8858
9346
  stdio: "pipe"
@@ -8868,7 +9356,7 @@ async function runLocalReconciliation(prdId, options) {
8868
9356
  }
8869
9357
  let handoff;
8870
9358
  try {
8871
- const content = readFileSync14(getHandoffArtifactPath(root, prdId), "utf-8");
9359
+ const content = readFileSync15(getHandoffArtifactPath(root, prdId), "utf-8");
8872
9360
  handoff = JSON.parse(content);
8873
9361
  } catch {
8874
9362
  return {
@@ -8894,11 +9382,11 @@ async function runLocalReconciliation(prdId, options) {
8894
9382
  if (handoff.result === "changes_produced") {
8895
9383
  try {
8896
9384
  const completionsDir = getCompletionsDir(root, prdId);
8897
- mkdirSync9(completionsDir, { recursive: true });
8898
- const existing = existsSync14(completionsDir) ? readdirSync5(completionsDir).filter((f) => f.endsWith(".md")) : [];
9385
+ mkdirSync10(completionsDir, { recursive: true });
9386
+ const existing = existsSync15(completionsDir) ? readdirSync5(completionsDir).filter((f) => f.endsWith(".md")) : [];
8899
9387
  const nextNum = String(existing.length + 1).padStart(3, "0");
8900
9388
  const filename = `${nextNum}-prd-${prdId.replace("PRD-", "").toLowerCase()}-reconciliation-completion.md`;
8901
- const filepath = join17(completionsDir, filename);
9389
+ const filepath = join18(completionsDir, filename);
8902
9390
  const changedPaths = Array.isArray(handoff.changedPlanningPaths) ? handoff.changedPlanningPaths : [];
8903
9391
  const summary = typeof handoff.summary === "string" ? handoff.summary : "";
8904
9392
  const lines = [
@@ -8914,13 +9402,13 @@ async function runLocalReconciliation(prdId, options) {
8914
9402
  lines.push("- (none)");
8915
9403
  }
8916
9404
  lines.push("");
8917
- writeFileSync6(filepath, lines.join("\n"), "utf-8");
8918
- execFileSync3("git", ["add", filepath], {
9405
+ writeFileSync7(filepath, lines.join("\n"), "utf-8");
9406
+ execFileSync4("git", ["add", filepath], {
8919
9407
  cwd: root,
8920
9408
  encoding: "utf8",
8921
9409
  stdio: "pipe"
8922
9410
  });
8923
- execFileSync3(
9411
+ execFileSync4(
8924
9412
  "git",
8925
9413
  ["commit", "-m", `docs: ${prdId} reconciliation completion`],
8926
9414
  { cwd: root, encoding: "utf8", stdio: "pipe" }
@@ -8937,7 +9425,7 @@ async function runLocalReconciliation(prdId, options) {
8937
9425
  try {
8938
9426
  const targetBranch = getLocalPrdBranchName(prdId);
8939
9427
  try {
8940
- execFileSync3(
9428
+ execFileSync4(
8941
9429
  "git",
8942
9430
  ["show-ref", "--verify", "--quiet", `refs/heads/${targetBranch}`],
8943
9431
  { cwd: root, encoding: "utf8", stdio: "pipe" }
@@ -8949,14 +9437,14 @@ async function runLocalReconciliation(prdId, options) {
8949
9437
  message: "Squash merge into local PRD Branch failed."
8950
9438
  };
8951
9439
  }
8952
- execFileSync3("git", ["checkout", targetBranch], {
9440
+ execFileSync4("git", ["checkout", targetBranch], {
8953
9441
  cwd: root,
8954
9442
  encoding: "utf8",
8955
9443
  stdio: "pipe"
8956
9444
  });
8957
9445
  const sourceBranch = `local/${prdId}/reconciliation`;
8958
9446
  try {
8959
- execFileSync3(
9447
+ execFileSync4(
8960
9448
  "git",
8961
9449
  ["show-ref", "--verify", "--quiet", `refs/heads/${sourceBranch}`],
8962
9450
  {
@@ -8972,19 +9460,19 @@ async function runLocalReconciliation(prdId, options) {
8972
9460
  message: "Squash merge into local PRD Branch failed."
8973
9461
  };
8974
9462
  }
8975
- execFileSync3("git", ["merge", "--squash", sourceBranch], {
9463
+ execFileSync4("git", ["merge", "--squash", sourceBranch], {
8976
9464
  cwd: root,
8977
9465
  encoding: "utf8",
8978
9466
  stdio: "pipe"
8979
9467
  });
8980
9468
  try {
8981
- execFileSync3("git", ["diff", "--cached", "--quiet"], {
9469
+ execFileSync4("git", ["diff", "--cached", "--quiet"], {
8982
9470
  cwd: root,
8983
9471
  encoding: "utf8",
8984
9472
  stdio: "pipe"
8985
9473
  });
8986
9474
  } catch {
8987
- execFileSync3(
9475
+ execFileSync4(
8988
9476
  "git",
8989
9477
  ["commit", "-m", `Squash merge ${sourceBranch} into ${targetBranch}`],
8990
9478
  { cwd: root, encoding: "utf8", stdio: "pipe" }
@@ -9003,8 +9491,8 @@ async function runLocalReconciliation(prdId, options) {
9003
9491
  reconciledAt: (/* @__PURE__ */ new Date()).toISOString(),
9004
9492
  branch
9005
9493
  };
9006
- mkdirSync9(getLocalStorePath3(root, prdId), { recursive: true });
9007
- writeFileSync6(
9494
+ mkdirSync10(getLocalStorePath4(root, prdId), { recursive: true });
9495
+ writeFileSync7(
9008
9496
  getReconciliationReceiptPath(root, prdId),
9009
9497
  JSON.stringify(receipt, null, 2),
9010
9498
  "utf-8"
@@ -9013,8 +9501,8 @@ async function runLocalReconciliation(prdId, options) {
9013
9501
  }
9014
9502
 
9015
9503
  // prd-run/local-prepare.ts
9016
- import { existsSync as existsSync15 } from "fs";
9017
- import { join as join18 } from "path";
9504
+ import { existsSync as existsSync16 } from "fs";
9505
+ import { join as join19 } from "path";
9018
9506
  var VALID_TRIAGE_LABELS2 = /* @__PURE__ */ new Set([
9019
9507
  "needs-triage",
9020
9508
  "ready-for-agent",
@@ -9032,8 +9520,8 @@ function fail(gate, failureCode, repairGuidance, gates) {
9032
9520
  async function validateLocalPrepareGates(prdId, repoRoot2) {
9033
9521
  const root = repoRoot2 ?? process.cwd();
9034
9522
  const gates = {};
9035
- const storePath = join18(root, ".pourkit", "local-prd-runs", prdId);
9036
- if (!existsSync15(storePath)) {
9523
+ const storePath = join19(root, ".pourkit", "local-prd-runs", prdId);
9524
+ if (!existsSync16(storePath)) {
9037
9525
  return fail(
9038
9526
  "store_shape",
9039
9527
  "local_prepare_store_shape_failed",
@@ -9459,6 +9947,7 @@ function runOneQueueIssueEffect(options) {
9459
9947
  }
9460
9948
  const { issue: selected } = outcome;
9461
9949
  const baseBranchOverride = options.queueRunContext?.prdBranch;
9950
+ const prdRunMode = options.prdRunMode ?? options.queueRunContext?.prdRunMode;
9462
9951
  const runResult = yield* Effect8.tryPromise({
9463
9952
  try: () => runIssueCommand({
9464
9953
  issueNumber: selected.number,
@@ -9470,7 +9959,8 @@ function runOneQueueIssueEffect(options) {
9470
9959
  force,
9471
9960
  logger,
9472
9961
  repoRoot: repoRoot2,
9473
- ...baseBranchOverride ? { baseBranchOverride } : {}
9962
+ ...baseBranchOverride ? { baseBranchOverride } : {},
9963
+ ...prdRunMode ? { prdRunMode } : {}
9474
9964
  }),
9475
9965
  catch: (e) => {
9476
9966
  if (e instanceof Error) return e;
@@ -9481,6 +9971,10 @@ function runOneQueueIssueEffect(options) {
9481
9971
  logger.raw(` Branch: ${runResult.branchName}`);
9482
9972
  if (runResult.noOp) {
9483
9973
  logger.raw(" Status: no-op (closed without PR)");
9974
+ } else if (runResult.mode === "local") {
9975
+ logger.raw(` Local PRD Branch: ${runResult.localPrdBranch}`);
9976
+ logger.raw(` Merge Commit: ${runResult.mergeCommit}`);
9977
+ logger.raw(` Receipt: ${runResult.receiptPath}`);
9484
9978
  } else {
9485
9979
  logger.raw(` PR Title: ${runResult.prTitle}`);
9486
9980
  logger.raw(` PR Number: ${runResult.prNumber}`);
@@ -9540,7 +10034,8 @@ async function runQueueCommand(options) {
9540
10034
  logger: options.logger,
9541
10035
  repoRoot: options.repoRoot,
9542
10036
  prdRef: options.prdRef,
9543
- queueRunContext: options.queueRunContext
10037
+ queueRunContext: options.queueRunContext,
10038
+ prdRunMode: options.queueRunContext?.prdRunMode
9544
10039
  };
9545
10040
  if (!options.loop) {
9546
10041
  return runEffectAndMapExit(runQueue(queueOptions));
@@ -9669,7 +10164,7 @@ function validateChangedPlanningPaths(paths, options) {
9669
10164
  continue;
9670
10165
  }
9671
10166
  if (options?.repoRoot) {
9672
- const fullPath = join19(options.repoRoot, p);
10167
+ const fullPath = join20(options.repoRoot, p);
9673
10168
  try {
9674
10169
  if (lstatSync(fullPath).isSymbolicLink()) {
9675
10170
  rejected.push(p);
@@ -9694,7 +10189,7 @@ function assessStaleSucceededReconciliationReceipt(options) {
9694
10189
  if (!receipt || receipt.status !== "succeeded" || receipt.prNumber || receipt.mergeCommit) {
9695
10190
  return { mode: "none" };
9696
10191
  }
9697
- const observedDiff = collectObservedReconciliationPlanningDiff({
10192
+ const observedDiff = collectObservedReconciliationDirtyPaths({
9698
10193
  worktreeCwd: options.worktreeCwd,
9699
10194
  ignoredPrdRunRecordRef: options.prdRef
9700
10195
  });
@@ -9705,23 +10200,14 @@ function assessStaleSucceededReconciliationReceipt(options) {
9705
10200
  offendingPaths: []
9706
10201
  };
9707
10202
  }
9708
- if (observedDiff.rejectedPaths.length > 0) {
9709
- return {
9710
- mode: "blocked",
9711
- diagnostics: [
9712
- "Reconciliation worktree contains unpublished changes outside safe planning scope."
9713
- ],
9714
- offendingPaths: observedDiff.rejectedPaths
9715
- };
9716
- }
9717
- if (observedDiff.changedPlanningPaths.length === 0) {
10203
+ if (observedDiff.dirtyPaths.length === 0) {
9718
10204
  return { mode: "none" };
9719
10205
  }
9720
10206
  return {
9721
10207
  mode: "recoverable",
9722
- changedPlanningPaths: observedDiff.changedPlanningPaths,
10208
+ dirtyPaths: observedDiff.dirtyPaths,
9723
10209
  diagnostics: [
9724
- "Recovered stale reconciliation success receipt from unpublished planning changes in worktree."
10210
+ "Recovered stale reconciliation success receipt from dirty reconciliation worktree."
9725
10211
  ]
9726
10212
  };
9727
10213
  }
@@ -9845,7 +10331,7 @@ function canRetryFinalReviewBlock(record) {
9845
10331
  }
9846
10332
  function loadFinalReviewPrompt(repoRoot2, promptTemplate) {
9847
10333
  const promptPath = resolvePromptTemplatePath(repoRoot2, promptTemplate);
9848
- return existsSync16(promptPath) ? readFileSync15(promptPath, "utf-8") : promptTemplate;
10334
+ return existsSync17(promptPath) ? readFileSync16(promptPath, "utf-8") : promptTemplate;
9849
10335
  }
9850
10336
  function formatVerificationCommands(commands) {
9851
10337
  if (commands.length === 0) return "No verification commands configured.";
@@ -9858,12 +10344,14 @@ function buildFinalReviewPrompt(options) {
9858
10344
  `Worktree checkout base: ${options.evidencePacket.prdBranch}`,
9859
10345
  `Review only this range: ${options.evidencePacket.mergeBase}..HEAD`,
9860
10346
  "Do not compare against current target branch HEAD; the merge base is the review baseline.",
9861
- `Before handoff, run: npm run pourkit:validate-final-review -- ${options.evidencePacket.prdRef} --checkout-base ${options.evidencePacket.prdBranch} --review-base ${options.evidencePacket.mergeBase}`,
10347
+ `Before handoff, run: pourkit prd-run validate-final-review ${options.evidencePacket.prdRef} --checkout-base ${options.evidencePacket.prdBranch} --review-base ${options.evidencePacket.mergeBase}`,
9862
10348
  "Fix any validation failures before handing off.",
9863
10349
  "",
9864
- "## Target Verification Commands",
10350
+ "## Verification",
10351
+ "",
10352
+ `Run this command before handoff when retouch changes are made: pourkit run-verification --target ${options.targetName}`,
9865
10353
  "",
9866
- "Run these commands before handoff when retouch changes are made:",
10354
+ "Underlying project commands:",
9867
10355
  formatVerificationCommands(options.verificationCommands),
9868
10356
  "",
9869
10357
  "Evidence Packet (do not infer PRD context from local state files):",
@@ -9890,14 +10378,14 @@ function buildReconciliationPrompt(options) {
9890
10378
  options.artifactPath,
9891
10379
  "",
9892
10380
  "Self-validation command:",
9893
- `validate-artifact reconciliation ${options.artifactPath}`
10381
+ `pourkit validate-artifact reconciliation ${options.artifactPath}`
9894
10382
  ].join("\n");
9895
10383
  }
9896
10384
  function buildReconciliationBranchName(prdRef) {
9897
10385
  return `${normalizePrdRunRef2(prdRef)}-reconciliation`;
9898
10386
  }
9899
10387
  function reconciliationWorktreePath(repoRoot2, branchName) {
9900
- return join19(repoRoot2, ".sandcastle", "worktrees", branchName);
10388
+ return join20(repoRoot2, ".sandcastle", "worktrees", branchName);
9901
10389
  }
9902
10390
  function reconciliationReceiptWorktreePath(branchName) {
9903
10391
  return `.sandcastle/worktrees/${branchName}`;
@@ -9909,7 +10397,7 @@ function buildFinalReviewBranchName(prdRef) {
9909
10397
  return `pourkit/${normalizePrdRunRef2(prdRef).toLowerCase()}-final-review`;
9910
10398
  }
9911
10399
  function finalReviewWorktreePath(repoRoot2, branchName) {
9912
- return join19(
10400
+ return join20(
9913
10401
  repoRoot2,
9914
10402
  ".sandcastle",
9915
10403
  "worktrees",
@@ -9964,7 +10452,7 @@ function buildFinalReviewFinalizerPrompt(options) {
9964
10452
  options.repoRoot,
9965
10453
  options.promptTemplate
9966
10454
  );
9967
- const promptBody = existsSync16(promptPath) ? readFileSync15(promptPath, "utf-8") : options.promptTemplate;
10455
+ const promptBody = existsSync17(promptPath) ? readFileSync16(promptPath, "utf-8") : options.promptTemplate;
9968
10456
  return [
9969
10457
  "# Final Review Retouch PR Finalizer",
9970
10458
  "",
@@ -9998,7 +10486,7 @@ function buildFinalReviewFinalizerPrompt(options) {
9998
10486
  }
9999
10487
  async function runFinalReviewPrFinalizer(options) {
10000
10488
  const finalizer = options.target.strategy.finalize.prDescriptionAgent;
10001
- const artifactPathInWorktree = join19(
10489
+ const artifactPathInWorktree = join20(
10002
10490
  ".pourkit",
10003
10491
  ".tmp",
10004
10492
  "finalizer",
@@ -10072,14 +10560,14 @@ function ensureFinalReviewWorktree(options) {
10072
10560
  }
10073
10561
  return { ok: true, worktreePath: registeredPath };
10074
10562
  }
10075
- if (existsSync16(worktreePath)) {
10563
+ if (existsSync17(worktreePath)) {
10076
10564
  return {
10077
10565
  ok: false,
10078
10566
  reason: "Final Review worktree path exists but is not registered with git.",
10079
10567
  diagnostics: [`stale worktree path: ${worktreePath}`]
10080
10568
  };
10081
10569
  }
10082
- mkdirSync10(dirname5(worktreePath), { recursive: true });
10570
+ mkdirSync11(dirname5(worktreePath), { recursive: true });
10083
10571
  const branchResult = spawnSync2(
10084
10572
  "git",
10085
10573
  ["branch", "-f", options.branchName, `origin/${options.checkoutBase}`],
@@ -10134,7 +10622,7 @@ async function createOrReuseFinalReviewRetouchPr(options) {
10134
10622
  if (existingPr && existingPr.state === "OPEN") {
10135
10623
  return existingPr;
10136
10624
  }
10137
- const worktreePath = mkdtempSync(join19(tmpdir(), "pourkit-retouch-"));
10625
+ const worktreePath = mkdtempSync(join20(tmpdir(), "pourkit-retouch-"));
10138
10626
  try {
10139
10627
  runGitOrThrow(
10140
10628
  options.repoRoot,
@@ -10152,9 +10640,9 @@ async function createOrReuseFinalReviewRetouchPr(options) {
10152
10640
  "create retouch branch"
10153
10641
  );
10154
10642
  for (const changedFile of options.changedPaths) {
10155
- const sourcePath = join19(options.sourceWorktreePath, changedFile);
10156
- const targetPath = join19(worktreePath, changedFile);
10157
- mkdirSync10(dirname5(targetPath), { recursive: true });
10643
+ const sourcePath = join20(options.sourceWorktreePath, changedFile);
10644
+ const targetPath = join20(worktreePath, changedFile);
10645
+ mkdirSync11(dirname5(targetPath), { recursive: true });
10158
10646
  cpSync(sourcePath, targetPath, { recursive: true });
10159
10647
  }
10160
10648
  runGitOrThrow(
@@ -10508,6 +10996,7 @@ async function runPrdRunFinalReviewCommand(options) {
10508
10996
  prompt: buildFinalReviewPrompt({
10509
10997
  repoRoot: options.repoRoot,
10510
10998
  promptTemplate: finalReviewConfig.promptTemplate,
10999
+ targetName,
10511
11000
  evidencePacket,
10512
11001
  verificationCommands
10513
11002
  }),
@@ -10583,7 +11072,7 @@ async function runPrdRunFinalReviewCommand(options) {
10583
11072
  };
10584
11073
  }
10585
11074
  const resolvedWorktreePath = executionResult.worktreePath;
10586
- const artifactPath = join19(
11075
+ const artifactPath = join20(
10587
11076
  resolvedWorktreePath,
10588
11077
  ".pourkit/final-review-artifact.json"
10589
11078
  );
@@ -11167,7 +11656,7 @@ async function runPrdRunFinalReviewCommand(options) {
11167
11656
  function runPrdRunValidateFinalReviewCommand(options) {
11168
11657
  const prdRef = normalizePrdRunRef2(options.prdRef);
11169
11658
  const checkoutBase = options.checkoutBase ?? prdRef;
11170
- const artifactPath = options.artifactPath ? options.artifactPath : join19(options.repoRoot, ".pourkit", "final-review-artifact.json");
11659
+ const artifactPath = options.artifactPath ? options.artifactPath : join20(options.repoRoot, ".pourkit", "final-review-artifact.json");
11171
11660
  const artifact = parseFinalReviewArtifact(artifactPath);
11172
11661
  if (!artifact.ok) {
11173
11662
  return {
@@ -11475,7 +11964,7 @@ async function runReconcilePreflightAndSetup(options) {
11475
11964
  );
11476
11965
  return { ok: false, diagnostics, branchAction: "blocked" };
11477
11966
  }
11478
- if (existsSync16(worktreePath)) {
11967
+ if (existsSync17(worktreePath)) {
11479
11968
  diagnostics.push(
11480
11969
  "Reconciliation worktree path exists but is not registered with git.",
11481
11970
  `stale worktree path: ${worktreePath}`
@@ -11496,7 +11985,7 @@ async function runReconcilePreflightAndSetup(options) {
11496
11985
  );
11497
11986
  return { ok: false, diagnostics, branchAction: "blocked" };
11498
11987
  }
11499
- mkdirSync10(dirname5(worktreePath), { recursive: true });
11988
+ mkdirSync11(dirname5(worktreePath), { recursive: true });
11500
11989
  const addResult = spawnSync2(
11501
11990
  "git",
11502
11991
  ["worktree", "add", worktreePath, reconciliationBranchName],
@@ -11551,7 +12040,7 @@ function buildReconciliationPrBody(options) {
11551
12040
  function commitAndPushReconciliationChanges(options) {
11552
12041
  const diagnostics = [];
11553
12042
  const existingPaths = options.changedPlanningPaths.filter((p) => {
11554
- return existsSync16(join19(options.worktreeCwd, p));
12043
+ return existsSync17(join20(options.worktreeCwd, p));
11555
12044
  });
11556
12045
  if (existingPaths.length === 0) {
11557
12046
  diagnostics.push(
@@ -11694,7 +12183,7 @@ async function runPrdRunReconcileCommand(options) {
11694
12183
  const record = preflightResult.record;
11695
12184
  const prdBranch = record?.prdBranch ?? prdRef;
11696
12185
  const mergeBase = preflightResult.mergeBase;
11697
- const worktreeCwd = preflightResult.worktreePath ? join19(options.repoRoot, preflightResult.worktreePath) : options.repoRoot;
12186
+ const worktreeCwd = preflightResult.worktreePath ? join20(options.repoRoot, preflightResult.worktreePath) : options.repoRoot;
11698
12187
  const staleSucceededRecovery = assessStaleSucceededReconciliationReceipt({
11699
12188
  receipt: record?.reconciliation,
11700
12189
  worktreeCwd,
@@ -11996,7 +12485,7 @@ async function runPrdRunReconcileCommand(options) {
11996
12485
  baseRef: prdBranch,
11997
12486
  checkoutBase,
11998
12487
  reviewBase: mergeBase,
11999
- worktreePath: join19(options.repoRoot, preflightResult.worktreePath),
12488
+ worktreePath: join20(options.repoRoot, preflightResult.worktreePath),
12000
12489
  sandbox: options.config.sandbox,
12001
12490
  artifactPath,
12002
12491
  logger: options.logger
@@ -12097,7 +12586,7 @@ async function runPrdRunReconcileCommand(options) {
12097
12586
  offendingPaths: []
12098
12587
  };
12099
12588
  }
12100
- const fullArtifactPath = join19(executionResult.worktreePath, artifactPath);
12589
+ const fullArtifactPath = join20(executionResult.worktreePath, artifactPath);
12101
12590
  let artifactContent;
12102
12591
  try {
12103
12592
  artifactContent = JSON.parse(retryResult.artifact.value);
@@ -12219,11 +12708,27 @@ async function runPrdRunReconcileCommand(options) {
12219
12708
  gitStatusShortLines,
12220
12709
  gitDiffNameStatusLines
12221
12710
  });
12711
+ const observedWorktreeDiff = collectObservedReconciliationDirtyPaths({
12712
+ worktreeCwd,
12713
+ ignoredPrdRunRecordRef: prdRef
12714
+ });
12222
12715
  let outcomeResult;
12223
12716
  let outcomeDiagnostics = [];
12224
12717
  let outcomeChangedPaths = [];
12225
12718
  let outcomeRationale;
12226
- if (artifactResult === "no_changes_needed") {
12719
+ if (!observedWorktreeDiff.ok) {
12720
+ outcomeResult = "blocked";
12721
+ outcomeDiagnostics.push(...observedWorktreeDiff.diagnostics);
12722
+ } else if (observedWorktreeDiff.dirtyPaths.length > 0) {
12723
+ outcomeResult = "changes_produced";
12724
+ outcomeChangedPaths = [...observedWorktreeDiff.dirtyPaths];
12725
+ outcomeRationale = void 0;
12726
+ if (artifactResult === "no_changes_needed") {
12727
+ outcomeDiagnostics.push(
12728
+ "Dirty reconciliation worktree observed after agent run; treating reconciliation result as changes_produced."
12729
+ );
12730
+ }
12731
+ } else if (artifactResult === "no_changes_needed") {
12227
12732
  outcomeResult = "no_changes_needed";
12228
12733
  outcomeChangedPaths = [];
12229
12734
  outcomeRationale = artifactNoChangeRationale ?? "No changes needed.";
@@ -12244,11 +12749,11 @@ async function runPrdRunReconcileCommand(options) {
12244
12749
  }
12245
12750
  if (staleSucceededRecovery.mode === "recoverable" && artifactResult === "no_changes_needed") {
12246
12751
  outcomeResult = "changes_produced";
12247
- outcomeChangedPaths = [...staleSucceededRecovery.changedPlanningPaths];
12752
+ outcomeChangedPaths = [...staleSucceededRecovery.dirtyPaths];
12248
12753
  outcomeRationale = void 0;
12249
12754
  outcomeDiagnostics.push(...staleSucceededRecovery.diagnostics);
12250
12755
  }
12251
- if (!pathValidation.ok && outcomeResult !== "blocked") {
12756
+ if (!pathValidation.ok && outcomeResult !== "blocked" && observedWorktreeDiff.ok && observedWorktreeDiff.dirtyPaths.length === 0) {
12252
12757
  outcomeResult = "blocked";
12253
12758
  outcomeDiagnostics.push(
12254
12759
  `Unsafe changed planning paths: ${(pathValidation.rejected ?? []).join(", ")}`
@@ -13099,58 +13604,58 @@ function validateReconciliationArtifact2(artifact, context) {
13099
13604
  }
13100
13605
  return { ok: true };
13101
13606
  }
13102
- var PRD_047_PRD_MIRROR_PATH = ".pourkit/architecture/initiatives/prd-run-lifecycle-and-integration-gate/prds/PRD-047-prd-reconciliation-run/PRD.md";
13103
- var PRD_047_CHILD_ISSUE_DIR = ".pourkit/architecture/initiatives/prd-run-lifecycle-and-integration-gate/prds/PRD-047-prd-reconciliation-run/issues/";
13607
+ var PRD_047_PRD_MIRROR_PATH = ".pourkit/architecture/initiatives/prd-run-lifecycle-and-integration-gate/prds/PRD-0047-prd-reconciliation-run/PRD.md";
13608
+ var PRD_047_CHILD_ISSUE_DIR = ".pourkit/architecture/initiatives/prd-run-lifecycle-and-integration-gate/prds/PRD-0047-prd-reconciliation-run/issues/";
13104
13609
  var PRD_047_ESCAPE_HATCH_PATH = ".pourkit/architecture/initiatives/prd-run-lifecycle-and-integration-gate/completions/006-prd-047-prd-reconciliation-run-status.md";
13105
- var PRD_047_MANIFEST_PATH = ".pourkit/architecture/initiatives/prd-run-lifecycle-and-integration-gate/prds/PRD-047-prd-reconciliation-run/planning-manifest.md";
13610
+ var PRD_047_MANIFEST_PATH = ".pourkit/architecture/initiatives/prd-run-lifecycle-and-integration-gate/prds/PRD-0047-prd-reconciliation-run/planning-manifest.md";
13106
13611
  function auditPrd047Implementation(repoRoot2, prdRef) {
13107
13612
  const normalizedRef = normalizePrdRunRef2(prdRef);
13108
13613
  const blockerBugs = [];
13109
13614
  const blockersFixed = [];
13110
13615
  const nonBlockers = [];
13111
- const prdMirrorPath = join19(repoRoot2, PRD_047_PRD_MIRROR_PATH);
13112
- const completionsDir = join19(repoRoot2, dirname5(PRD_047_ESCAPE_HATCH_PATH));
13616
+ const prdMirrorPath = join20(repoRoot2, PRD_047_PRD_MIRROR_PATH);
13617
+ const completionsDir = join20(repoRoot2, dirname5(PRD_047_ESCAPE_HATCH_PATH));
13113
13618
  let escapeHatchPresent = false;
13114
- if (existsSync16(completionsDir)) {
13619
+ if (existsSync17(completionsDir)) {
13115
13620
  const files = readdirSync6(completionsDir);
13116
13621
  escapeHatchPresent = files.some(
13117
13622
  (f) => f.endsWith(".md") && (f.includes("prd-047") || f.includes("prd-reconciliation"))
13118
13623
  );
13119
13624
  }
13120
13625
  if (!escapeHatchPresent) {
13121
- escapeHatchPresent = existsSync16(join19(repoRoot2, PRD_047_ESCAPE_HATCH_PATH));
13626
+ escapeHatchPresent = existsSync17(join20(repoRoot2, PRD_047_ESCAPE_HATCH_PATH));
13122
13627
  }
13123
- const prdMirrorExists = existsSync16(prdMirrorPath);
13628
+ const prdMirrorExists = existsSync17(prdMirrorPath);
13124
13629
  if (!prdMirrorExists) {
13125
13630
  nonBlockers.push(
13126
- `PRD-047 PRD mirror not found at ${PRD_047_PRD_MIRROR_PATH}. Cannot verify scope alignment from published mirrors.`
13631
+ `PRD-0047 PRD mirror not found at ${PRD_047_PRD_MIRROR_PATH}. Cannot verify scope alignment from published mirrors.`
13127
13632
  );
13128
13633
  }
13129
13634
  if (prdMirrorExists) {
13130
- const mirrorContent = readFileSync15(prdMirrorPath, "utf-8");
13635
+ const mirrorContent = readFileSync16(prdMirrorPath, "utf-8");
13131
13636
  const hasReconcileCommand = mirrorContent.includes("prd-run reconcile");
13132
13637
  const hasEscapeHatch = mirrorContent.includes("escape hatch");
13133
13638
  if (!hasReconcileCommand) {
13134
13639
  blockerBugs.push(
13135
- "PRD-047 PRD mirror does not reference prd-run reconcile command."
13640
+ "PRD-0047 PRD mirror does not reference prd-run reconcile command."
13136
13641
  );
13137
13642
  }
13138
13643
  if (!hasEscapeHatch) {
13139
13644
  nonBlockers.push(
13140
- "PRD-047 PRD mirror does not reference escape hatch mechanism."
13645
+ "PRD-0047 PRD mirror does not reference escape hatch mechanism."
13141
13646
  );
13142
13647
  }
13143
13648
  }
13144
- const childIssueDir = join19(repoRoot2, PRD_047_CHILD_ISSUE_DIR);
13145
- const childIssuesExist = existsSync16(childIssueDir);
13649
+ const childIssueDir = join20(repoRoot2, PRD_047_CHILD_ISSUE_DIR);
13650
+ const childIssuesExist = existsSync17(childIssueDir);
13146
13651
  if (!childIssuesExist) {
13147
13652
  nonBlockers.push(
13148
- `PRD-047 child Issue mirrors not found at ${PRD_047_CHILD_ISSUE_DIR}.`
13653
+ `PRD-0047 child Issue mirrors not found at ${PRD_047_CHILD_ISSUE_DIR}.`
13149
13654
  );
13150
13655
  }
13151
- const manifestPath = join19(repoRoot2, PRD_047_MANIFEST_PATH);
13152
- if (existsSync16(manifestPath)) {
13153
- const manifestContent = readFileSync15(manifestPath, "utf-8");
13656
+ const manifestPath = join20(repoRoot2, PRD_047_MANIFEST_PATH);
13657
+ if (existsSync17(manifestPath)) {
13658
+ const manifestContent = readFileSync16(manifestPath, "utf-8");
13154
13659
  if (!manifestContent.includes("ready_for_prepare")) {
13155
13660
  nonBlockers.push(
13156
13661
  "Planning Artifact Manifest is not marked as ready_for_prepare."
@@ -13216,16 +13721,16 @@ function auditPrd047Implementation(repoRoot2, prdRef) {
13216
13721
  }
13217
13722
  if (!prdMirrorExists && !childIssuesExist && !prdRunRecord.record) {
13218
13723
  nonBlockers.push(
13219
- "Insufficient evidence to verify PRD-047 scope alignment: mirrors and PRD Run record are missing."
13724
+ "Insufficient evidence to verify PRD-0047 scope alignment: mirrors and PRD Run record are missing."
13220
13725
  );
13221
13726
  }
13222
13727
  if (escapeHatchPresent) {
13223
13728
  blockersFixed.push(
13224
- "PRD-047 escape-hatch lifecycle violation record confirmed present."
13729
+ "PRD-0047 escape-hatch lifecycle violation record confirmed present."
13225
13730
  );
13226
13731
  } else {
13227
13732
  nonBlockers.push(
13228
- "PRD-047 escape-hatch lifecycle violation record is missing. Expected at " + PRD_047_ESCAPE_HATCH_PATH + "."
13733
+ "PRD-0047 escape-hatch lifecycle violation record is missing. Expected at " + PRD_047_ESCAPE_HATCH_PATH + "."
13229
13734
  );
13230
13735
  }
13231
13736
  return {
@@ -13398,13 +13903,13 @@ async function runPrdRunLaunchCommand(options) {
13398
13903
  let finalReviewResult;
13399
13904
  if (!skipped.includes("final-review")) {
13400
13905
  attempted.push("final-review");
13401
- const localStorePath = join19(
13906
+ const localStorePath = join20(
13402
13907
  options.repoRoot,
13403
13908
  ".pourkit",
13404
13909
  "local-prd-runs",
13405
13910
  prdRef
13406
13911
  );
13407
- if (existsSync16(localStorePath)) {
13912
+ if (existsSync17(localStorePath)) {
13408
13913
  const targetConfig = options.config?.targets?.find(
13409
13914
  (t) => t.name === options.targetName
13410
13915
  );
@@ -14703,6 +15208,13 @@ async function processStartResult(startResult, options) {
14703
15208
  start.queueStartedAt = (/* @__PURE__ */ new Date()).toISOString();
14704
15209
  start.queueCommand = "queue-run";
14705
15210
  const existingRecord = readPrdRun(repoRoot2, prdRef);
15211
+ let resolvedMode;
15212
+ try {
15213
+ const target = options.config ? resolveTarget(options.config, start.targetName) : null;
15214
+ resolvedMode = target?.strategy ? resolvePrdRunMode(target) : void 0;
15215
+ } catch {
15216
+ resolvedMode = void 0;
15217
+ }
14706
15218
  writePrdRunRecord(repoRoot2, {
14707
15219
  prdRef,
14708
15220
  status: "running",
@@ -14712,8 +15224,8 @@ async function processStartResult(startResult, options) {
14712
15224
  manifestPath: existingRecord.record?.manifestPath,
14713
15225
  planning: existingRecord.record?.planning
14714
15226
  });
14715
- const localStorePath = join19(repoRoot2, ".pourkit", "local-prd-runs", prdRef);
14716
- if (existsSync16(localStorePath)) {
15227
+ const localStorePath = join20(repoRoot2, ".pourkit", "local-prd-runs", prdRef);
15228
+ if (existsSync17(localStorePath)) {
14717
15229
  const localIssues = await getRunnableLocalIssues(prdRef, repoRoot2);
14718
15230
  start.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
14719
15231
  start.queueProcessedCount = localIssues.length;
@@ -14747,7 +15259,8 @@ async function processStartResult(startResult, options) {
14747
15259
  prdRef,
14748
15260
  queueRunContext: {
14749
15261
  prdRef,
14750
- prdBranch: start.prdBranch
15262
+ prdBranch: start.prdBranch,
15263
+ prdRunMode: resolvedMode
14751
15264
  }
14752
15265
  });
14753
15266
  if (outcome.selected === null && outcome.code === "drained") {
@@ -15292,7 +15805,7 @@ async function ensurePlanningBranchPublished(repoRoot2, branchName, changedFiles
15292
15805
  if (remoteResult.status !== 0) {
15293
15806
  return;
15294
15807
  }
15295
- const worktreePath = mkdtempSync(join19(tmpdir(), "pourkit-planning-"));
15808
+ const worktreePath = mkdtempSync(join20(tmpdir(), "pourkit-planning-"));
15296
15809
  try {
15297
15810
  runGitOrThrow(repoRoot2, ["fetch", "origin", "dev"], "fetch origin/dev");
15298
15811
  runGitOrThrow(
@@ -15306,9 +15819,9 @@ async function ensurePlanningBranchPublished(repoRoot2, branchName, changedFiles
15306
15819
  "create planning branch"
15307
15820
  );
15308
15821
  for (const changedFile of changedFiles) {
15309
- const sourcePath = join19(repoRoot2, changedFile);
15310
- const targetPath = join19(worktreePath, changedFile);
15311
- mkdirSync10(dirname5(targetPath), { recursive: true });
15822
+ const sourcePath = join20(repoRoot2, changedFile);
15823
+ const targetPath = join20(worktreePath, changedFile);
15824
+ mkdirSync11(dirname5(targetPath), { recursive: true });
15312
15825
  cpSync(sourcePath, targetPath, { recursive: true });
15313
15826
  }
15314
15827
  runGitOrThrow(
@@ -15544,7 +16057,7 @@ function collectGitChangedFiles(repoRoot2, ignoredPrdRunRecordRef) {
15544
16057
  changedFiles: Array.from(new Set(changedFiles))
15545
16058
  };
15546
16059
  }
15547
- function collectObservedReconciliationPlanningDiff(options) {
16060
+ function collectObservedReconciliationDirtyPaths(options) {
15548
16061
  const statusResult = spawnSync2("git", ["status", "--porcelain=v1", "-uall"], {
15549
16062
  cwd: options.worktreeCwd,
15550
16063
  encoding: "utf8"
@@ -15577,27 +16090,9 @@ function collectObservedReconciliationPlanningDiff(options) {
15577
16090
  )
15578
16091
  )
15579
16092
  );
15580
- const intrinsicallyRejectedPaths = candidatePaths.filter(
15581
- (path9) => !isAllowedPlanningPath(path9) || isBlockedPlanningPath(path9) || hasDirectoryTraversal(path9)
15582
- );
15583
- const allowedCandidatePaths = candidatePaths.filter(
15584
- (path9) => !intrinsicallyRejectedPaths.includes(path9)
15585
- );
15586
- const validation = validateChangedPlanningPaths(allowedCandidatePaths, {
15587
- repoRoot: options.worktreeCwd,
15588
- gitStatusShortLines,
15589
- gitDiffNameStatusLines
15590
- });
15591
- const rejectedPaths = Array.from(
15592
- /* @__PURE__ */ new Set([
15593
- ...intrinsicallyRejectedPaths,
15594
- ...validation.ok ? [] : validation.rejected ?? []
15595
- ])
15596
- ).sort();
15597
16093
  return {
15598
16094
  ok: true,
15599
- changedPlanningPaths: allowedCandidatePaths.filter((path9) => !rejectedPaths.includes(path9)).sort(),
15600
- rejectedPaths
16095
+ dirtyPaths: candidatePaths.sort()
15601
16096
  };
15602
16097
  }
15603
16098
  function gitRefExists(repoRoot2, ref) {
@@ -15622,7 +16117,7 @@ function parseGitStatusLine(line) {
15622
16117
  return { status, path: path9.replace(/^"|"$/g, "") };
15623
16118
  }
15624
16119
  function validateManifestArtifactExistence(repoRoot2, manifest) {
15625
- const missingPaths = listManifestArtifactPaths(repoRoot2, manifest).filter((path9) => !existsSync16(path9)).map((path9) => toRepoRelativePath2(repoRoot2, path9));
16120
+ const missingPaths = listManifestArtifactPaths(repoRoot2, manifest).filter((path9) => !existsSync17(path9)).map((path9) => toRepoRelativePath2(repoRoot2, path9));
15626
16121
  if (missingPaths.length > 0) {
15627
16122
  return {
15628
16123
  ok: false,
@@ -16038,7 +16533,7 @@ async function runPrMergeCommand(args, logger, prProvider, config) {
16038
16533
  }
16039
16534
 
16040
16535
  // commands/init.ts
16041
- import { existsSync as existsSync17, statSync } from "fs";
16536
+ import { existsSync as existsSync18, statSync } from "fs";
16042
16537
  import {
16043
16538
  copyFile,
16044
16539
  mkdir as mkdir5,
@@ -16541,7 +17036,7 @@ async function computeFileChecksum(filePath) {
16541
17036
  return createHash3("sha256").update(content).digest("hex");
16542
17037
  }
16543
17038
  function lockfileExists(root, name) {
16544
- return existsSync17(path5.join(root, name));
17039
+ return existsSync18(path5.join(root, name));
16545
17040
  }
16546
17041
  function detectPackageManager(root) {
16547
17042
  if (lockfileExists(root, "pnpm-lock.yaml")) return "pnpm";
@@ -16585,7 +17080,7 @@ async function discoverLocalSource(sourcePath) {
16585
17080
  async function discoverReadme(root) {
16586
17081
  for (const name of ["README.md", "readme.md"]) {
16587
17082
  const p = path5.join(root, name);
16588
- if (existsSync17(p)) {
17083
+ if (existsSync18(p)) {
16589
17084
  return p;
16590
17085
  }
16591
17086
  }
@@ -16595,7 +17090,7 @@ async function discoverAgentFiles(root) {
16595
17090
  const files = [];
16596
17091
  for (const name of ["AGENTS.md", "CLAUDE.md"]) {
16597
17092
  const p = path5.join(root, name);
16598
- if (existsSync17(p)) {
17093
+ if (existsSync18(p)) {
16599
17094
  files.push(p);
16600
17095
  }
16601
17096
  }
@@ -16603,7 +17098,7 @@ async function discoverAgentFiles(root) {
16603
17098
  }
16604
17099
  async function discoverMerlleState(root) {
16605
17100
  const p = path5.join(root, ".pourkit", "state.json");
16606
- return existsSync17(p) ? p : null;
17101
+ return existsSync18(p) ? p : null;
16607
17102
  }
16608
17103
  async function discoverAgentSkills(root) {
16609
17104
  const dirs = [
@@ -16612,7 +17107,7 @@ async function discoverAgentSkills(root) {
16612
17107
  ];
16613
17108
  const found = [];
16614
17109
  for (const d of dirs) {
16615
- if (existsSync17(d)) {
17110
+ if (existsSync18(d)) {
16616
17111
  found.push(d);
16617
17112
  }
16618
17113
  }
@@ -16622,12 +17117,12 @@ async function discoverRootDomainDocs(root) {
16622
17117
  const docs = [];
16623
17118
  for (const name of ["CONTEXT.md", "CONTEXT-MAP.md"]) {
16624
17119
  const p = path5.join(root, name);
16625
- if (existsSync17(p)) {
17120
+ if (existsSync18(p)) {
16626
17121
  docs.push(p);
16627
17122
  }
16628
17123
  }
16629
17124
  const adrDir = path5.join(root, "docs", "adr");
16630
- if (existsSync17(adrDir)) {
17125
+ if (existsSync18(adrDir)) {
16631
17126
  const entries = await readdir(adrDir, { withFileTypes: true });
16632
17127
  for (const entry of entries) {
16633
17128
  if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -16770,7 +17265,7 @@ async function planInit(options) {
16770
17265
  for (const file of skillFiles) {
16771
17266
  const relPath = path5.relative(s, file);
16772
17267
  const destPath = path5.join(targetRoot, ".agents", "skills", relPath);
16773
- if (!existsSync17(destPath)) {
17268
+ if (!existsSync18(destPath)) {
16774
17269
  operations.push({
16775
17270
  kind: "copy",
16776
17271
  sourcePath: file,
@@ -16833,7 +17328,7 @@ async function planInit(options) {
16833
17328
  });
16834
17329
  }
16835
17330
  if (sourceRoot) {
16836
- if (!existsSync17(sourceRoot) || !statSync(sourceRoot).isDirectory()) {
17331
+ if (!existsSync18(sourceRoot) || !statSync(sourceRoot).isDirectory()) {
16837
17332
  warnings.push(
16838
17333
  `--from-local path does not exist or is not a directory: ${sourceRoot}`
16839
17334
  );
@@ -16952,7 +17447,7 @@ async function planInit(options) {
16952
17447
  requiresConfirmation: false,
16953
17448
  destructive: false
16954
17449
  });
16955
- } else if (existsSync17(destPath)) {
17450
+ } else if (existsSync18(destPath)) {
16956
17451
  operations.push({
16957
17452
  kind: "skip",
16958
17453
  path: destPath,
@@ -17010,7 +17505,7 @@ async function planInit(options) {
17010
17505
  }
17011
17506
  }
17012
17507
  const contextPath = path5.join(targetRoot, ".pourkit", "CONTEXT.md");
17013
- if (!existsSync17(contextPath) && !merleDestPaths.has(contextPath)) {
17508
+ if (!existsSync18(contextPath) && !merleDestPaths.has(contextPath)) {
17014
17509
  operations.push({
17015
17510
  kind: "create",
17016
17511
  path: contextPath,
@@ -17028,7 +17523,7 @@ async function planInit(options) {
17028
17523
  "adr",
17029
17524
  ".gitkeep"
17030
17525
  );
17031
- if (!existsSync17(adrGitkeep)) {
17526
+ if (!existsSync18(adrGitkeep)) {
17032
17527
  operations.push({
17033
17528
  kind: "create",
17034
17529
  path: adrGitkeep,
@@ -17040,7 +17535,7 @@ async function planInit(options) {
17040
17535
  }
17041
17536
  const srcDocAgents = path5.join(sourceRoot, ".pourkit", "docs", "agents");
17042
17537
  const tgtDocAgents = path5.join(targetRoot, ".pourkit", "docs", "agents");
17043
- if (existsSync17(srcDocAgents) && !existsSync17(tgtDocAgents)) {
17538
+ if (existsSync18(srcDocAgents) && !existsSync18(tgtDocAgents)) {
17044
17539
  const docFiles = await walkDir(srcDocAgents);
17045
17540
  for (const file of docFiles) {
17046
17541
  const relPath = path5.relative(srcDocAgents, file);
@@ -17073,7 +17568,7 @@ async function planInit(options) {
17073
17568
  }
17074
17569
  const srcPrompts = path5.join(sourceRoot, ".pourkit", "prompts");
17075
17570
  const tgtPrompts = path5.join(targetRoot, ".pourkit", "prompts");
17076
- if (existsSync17(srcPrompts) && !existsSync17(tgtPrompts)) {
17571
+ if (existsSync18(srcPrompts) && !existsSync18(tgtPrompts)) {
17077
17572
  const promptFiles = await walkDir(srcPrompts);
17078
17573
  for (const file of promptFiles) {
17079
17574
  const relPath = path5.relative(srcPrompts, file);
@@ -17100,7 +17595,7 @@ async function planInit(options) {
17100
17595
  ".sandcastle",
17101
17596
  "Dockerfile"
17102
17597
  );
17103
- if (existsSync17(tgtSandboxDockerfile)) {
17598
+ if (existsSync18(tgtSandboxDockerfile)) {
17104
17599
  operations.push({
17105
17600
  kind: "skip",
17106
17601
  path: tgtSandboxDockerfile,
@@ -17109,7 +17604,7 @@ async function planInit(options) {
17109
17604
  requiresConfirmation: false,
17110
17605
  destructive: false
17111
17606
  });
17112
- } else if (existsSync17(srcSandboxDockerfile)) {
17607
+ } else if (existsSync18(srcSandboxDockerfile)) {
17113
17608
  const checksum = await computeFileChecksum(srcSandboxDockerfile);
17114
17609
  operations.push({
17115
17610
  kind: "copy",
@@ -17123,7 +17618,7 @@ async function planInit(options) {
17123
17618
  });
17124
17619
  }
17125
17620
  const configTsPath = path5.join(targetRoot, "pourkit.config.ts");
17126
- if (!existsSync17(configTsPath)) {
17621
+ if (!existsSync18(configTsPath)) {
17127
17622
  const verifyCommands = inferVerificationCommands(
17128
17623
  packageScripts,
17129
17624
  pm || "npm"
@@ -17160,7 +17655,7 @@ async function planInit(options) {
17160
17655
  const hasExistingAgents = operations.some(
17161
17656
  (op) => (op.kind === "skip" || op.kind === "update") && op.path?.endsWith("AGENTS.md")
17162
17657
  );
17163
- if ((agentFileMode === "agents" || agentFileMode === "both") && !hasExistingAgents && !existsSync17(path5.join(targetRoot, "AGENTS.md"))) {
17658
+ if ((agentFileMode === "agents" || agentFileMode === "both") && !hasExistingAgents && !existsSync18(path5.join(targetRoot, "AGENTS.md"))) {
17164
17659
  operations.push({
17165
17660
  kind: "create",
17166
17661
  path: path5.join(targetRoot, "AGENTS.md"),
@@ -17176,7 +17671,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
17176
17671
  const hasExistingClaude = operations.some(
17177
17672
  (op) => (op.kind === "skip" || op.kind === "update") && op.path?.endsWith("CLAUDE.md")
17178
17673
  );
17179
- if ((agentFileMode === "claude" || agentFileMode === "both") && !hasExistingClaude && !existsSync17(path5.join(targetRoot, "CLAUDE.md"))) {
17674
+ if ((agentFileMode === "claude" || agentFileMode === "both") && !hasExistingClaude && !existsSync18(path5.join(targetRoot, "CLAUDE.md"))) {
17180
17675
  operations.push({
17181
17676
  kind: "create",
17182
17677
  path: path5.join(targetRoot, "CLAUDE.md"),
@@ -17191,7 +17686,7 @@ ${managedAgentContent}${MANAGED_BLOCK_END}
17191
17686
  }
17192
17687
  const gitignoreTarget = path5.join(targetRoot, ".gitignore");
17193
17688
  const gitignoreContent = generateGitignoreBlock();
17194
- if (!existsSync17(gitignoreTarget)) {
17689
+ if (!existsSync18(gitignoreTarget)) {
17195
17690
  operations.push({
17196
17691
  kind: "create",
17197
17692
  path: gitignoreTarget,
@@ -17215,7 +17710,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
17215
17710
  });
17216
17711
  }
17217
17712
  const openCodePath = path5.join(targetRoot, "opencode.json");
17218
- if (!existsSync17(openCodePath)) {
17713
+ if (!existsSync18(openCodePath)) {
17219
17714
  operations.push({
17220
17715
  kind: "create",
17221
17716
  path: openCodePath,
@@ -17272,7 +17767,7 @@ ${gitignoreContent}${MANAGED_BLOCK_END}
17272
17767
  }
17273
17768
  }
17274
17769
  const manifestPath = path5.join(targetRoot, ".pourkit", "manifest.json");
17275
- if (existsSync17(manifestPath)) {
17770
+ if (existsSync18(manifestPath)) {
17276
17771
  operations.push({
17277
17772
  kind: "skip",
17278
17773
  path: manifestPath,
@@ -17591,7 +18086,7 @@ async function updateManagedBlock(filePath, content) {
17591
18086
  const blockContent = `${MANAGED_BLOCK_BEGIN}
17592
18087
  ${content}${MANAGED_BLOCK_END}
17593
18088
  `;
17594
- if (!existsSync17(filePath)) {
18089
+ if (!existsSync18(filePath)) {
17595
18090
  const dir = path5.dirname(filePath);
17596
18091
  await mkdir5(dir, { recursive: true });
17597
18092
  await writeFileAtomic(filePath, blockContent);
@@ -17620,7 +18115,7 @@ async function writeManifest(plan, sourceMeta, agentFiles, packageManager) {
17620
18115
  if (op.requiresConfirmation) continue;
17621
18116
  const relPath = path5.relative(plan.targetRoot, op.path);
17622
18117
  if (relPath === ".pourkit/manifest.json") continue;
17623
- if (existsSync17(op.path)) {
18118
+ if (existsSync18(op.path)) {
17624
18119
  const sha256 = await computeFileChecksum(op.path);
17625
18120
  assets[relPath] = {
17626
18121
  ownership: op.ownership || "managed",
@@ -17665,7 +18160,7 @@ async function applyInitPlan(plan, options) {
17665
18160
  skipped++;
17666
18161
  continue;
17667
18162
  }
17668
- if (existsSync17(op.path) && !op.destructive) {
18163
+ if (existsSync18(op.path) && !op.destructive) {
17669
18164
  skipped++;
17670
18165
  continue;
17671
18166
  }
@@ -17680,7 +18175,7 @@ async function applyInitPlan(plan, options) {
17680
18175
  skipped++;
17681
18176
  continue;
17682
18177
  }
17683
- if (existsSync17(op.path)) {
18178
+ if (existsSync18(op.path)) {
17684
18179
  skipped++;
17685
18180
  continue;
17686
18181
  }
@@ -17709,7 +18204,7 @@ async function applyInitPlan(plan, options) {
17709
18204
  skipped++;
17710
18205
  continue;
17711
18206
  }
17712
- if (existsSync17(op.path)) {
18207
+ if (existsSync18(op.path)) {
17713
18208
  skipped++;
17714
18209
  continue;
17715
18210
  }
@@ -17849,7 +18344,7 @@ async function applyInitFromSource(options) {
17849
18344
  if (!manifestSkipped) {
17850
18345
  const agentFiles = [];
17851
18346
  for (const name of ["AGENTS.md", "CLAUDE.md"]) {
17852
- if (existsSync17(path5.join(targetRoot, name))) {
18347
+ if (existsSync18(path5.join(targetRoot, name))) {
17853
18348
  agentFiles.push(path5.join(targetRoot, name));
17854
18349
  }
17855
18350
  }
@@ -18668,8 +19163,8 @@ function formatChecks2(checks) {
18668
19163
  init_common();
18669
19164
 
18670
19165
  // execution/sandcastle-execution.ts
18671
- import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync7 } from "fs";
18672
- import { join as join21 } from "path";
19166
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync8 } from "fs";
19167
+ import { join as join22 } from "path";
18673
19168
  import { createWorktree, opencode } from "@ai-hero/sandcastle";
18674
19169
  import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
18675
19170
 
@@ -18678,10 +19173,10 @@ init_common();
18678
19173
  import { mkdtempSync as mkdtempSync2 } from "fs";
18679
19174
  import { writeFile as writeFile3 } from "fs/promises";
18680
19175
  import { tmpdir as tmpdir2 } from "os";
18681
- import { dirname as dirname6, join as join20 } from "path";
19176
+ import { dirname as dirname6, join as join21 } from "path";
18682
19177
  async function writeExecutionArtifacts(worktreePath, artifacts) {
18683
19178
  for (const artifact of artifacts) {
18684
- const filePath = join20(worktreePath, artifact.path);
19179
+ const filePath = join21(worktreePath, artifact.path);
18685
19180
  await ensureDir(dirname6(filePath));
18686
19181
  await writeFile3(filePath, artifact.content, "utf-8");
18687
19182
  }
@@ -18693,17 +19188,17 @@ import path7 from "path";
18693
19188
 
18694
19189
  // execution/sandbox-image.ts
18695
19190
  import { createHash as createHash4 } from "crypto";
18696
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
19191
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
18697
19192
  import path6 from "path";
18698
19193
  function sandboxImageName(repoRoot2) {
18699
19194
  const dirName = path6.basename(repoRoot2.replace(/[\\/]+$/, "")) || "local";
18700
19195
  const sanitized = dirName.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
18701
19196
  const baseName = sanitized || "local";
18702
19197
  const dockerfilePath = path6.join(repoRoot2, ".sandcastle", "Dockerfile");
18703
- if (!existsSync18(dockerfilePath)) {
19198
+ if (!existsSync19(dockerfilePath)) {
18704
19199
  return `sandcastle:${baseName}`;
18705
19200
  }
18706
- const fingerprint = createHash4("sha256").update(readFileSync16(dockerfilePath)).digest("hex").slice(0, 8);
19201
+ const fingerprint = createHash4("sha256").update(readFileSync17(dockerfilePath)).digest("hex").slice(0, 8);
18707
19202
  return `sandcastle:${baseName}-${fingerprint}`;
18708
19203
  }
18709
19204
 
@@ -18853,11 +19348,7 @@ var SandcastleExecutionSession = class {
18853
19348
  type: "file",
18854
19349
  path: logPath,
18855
19350
  onAgentStreamEvent: (event) => {
18856
- if (event.type === "text") {
18857
- logger.raw(event.message);
18858
- } else if (event.type === "toolCall") {
18859
- logger.raw(`${event.name}(${event.formattedArgs})`);
18860
- }
19351
+ logger.raw(formatAgentStreamEvent(event));
18861
19352
  }
18862
19353
  },
18863
19354
  completionSignal: "<promise>COMPLETE</promise>",
@@ -18942,14 +19433,59 @@ function resolveSandboxProvider(provider, dockerFactory) {
18942
19433
  function sanitizeBranch(branchName) {
18943
19434
  return branchName.replace(/[^A-Za-z0-9._-]/g, "-");
18944
19435
  }
19436
+ function formatAgentStreamEvent(event) {
19437
+ if (event.type === "text") {
19438
+ return JSON.stringify({ type: "text", textCount: event.message.length });
19439
+ }
19440
+ const parsedArgs = parseToolCallArgs(event.formattedArgs);
19441
+ if (!parsedArgs.ok) {
19442
+ return JSON.stringify({
19443
+ type: "toolCall",
19444
+ name: event.name,
19445
+ argsTextCount: event.formattedArgs.length
19446
+ });
19447
+ }
19448
+ return JSON.stringify({
19449
+ type: "toolCall",
19450
+ name: event.name,
19451
+ args: summarizeToolCallArgs(event.name, parsedArgs.value)
19452
+ });
19453
+ }
19454
+ function parseToolCallArgs(formattedArgs) {
19455
+ try {
19456
+ return { ok: true, value: JSON.parse(formattedArgs) };
19457
+ } catch {
19458
+ return { ok: false };
19459
+ }
19460
+ }
19461
+ function summarizeToolCallArgs(name, args) {
19462
+ if (!isPlainObject(args)) {
19463
+ return args;
19464
+ }
19465
+ if (name.toLowerCase() !== "write") {
19466
+ return args;
19467
+ }
19468
+ const summarizedArgs = {};
19469
+ for (const [key, value] of Object.entries(args)) {
19470
+ if (key === "content" && typeof value === "string") {
19471
+ summarizedArgs.contentCount = value.length;
19472
+ continue;
19473
+ }
19474
+ summarizedArgs[key] = value;
19475
+ }
19476
+ return summarizedArgs;
19477
+ }
19478
+ function isPlainObject(value) {
19479
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19480
+ }
18945
19481
  function savePromptToFile(repoRoot2, stage, iteration, prompt) {
18946
- const promptsDir = join21(repoRoot2, ".pourkit", ".tmp", "prompts");
18947
- mkdirSync11(promptsDir, { recursive: true });
19482
+ const promptsDir = join22(repoRoot2, ".pourkit", ".tmp", "prompts");
19483
+ mkdirSync12(promptsDir, { recursive: true });
18948
19484
  const timestamp2 = Date.now();
18949
19485
  const iterationSuffix = iteration !== void 0 ? `-iteration-${iteration}` : "";
18950
19486
  const filename = `${stage}${iterationSuffix}-${timestamp2}.md`;
18951
- const filePath = join21(promptsDir, filename);
18952
- writeFileSync7(filePath, prompt, "utf-8");
19487
+ const filePath = join22(promptsDir, filename);
19488
+ writeFileSync8(filePath, prompt, "utf-8");
18953
19489
  }
18954
19490
 
18955
19491
  // cli.ts
@@ -18957,10 +19493,13 @@ function normalizePrdRef(ref) {
18957
19493
  const normalized = ref.trim().toUpperCase();
18958
19494
  if (!/^PRD-\d+$/.test(normalized)) {
18959
19495
  throw new Error(
18960
- `Invalid PRD ref "${ref}". Expected format: PRD-<number> (e.g., PRD-021)`
19496
+ `Invalid PRD ref "${ref}". Expected format: PRD-<number> (e.g., PRD-0021)`
18961
19497
  );
18962
19498
  }
18963
- return normalized;
19499
+ return normalized.replace(
19500
+ /^(PRD-)(\d+)$/,
19501
+ (_, p, n) => `${p}${n.padStart(4, "0")}`
19502
+ );
18964
19503
  }
18965
19504
  function buildPrCreateArgs(options) {
18966
19505
  const args = ["--target", options.target, "--title", options.title];
@@ -19015,6 +19554,35 @@ function createCliProgram(version) {
19015
19554
  const program = new Command();
19016
19555
  program.name("pourkit").version(version).exitOverride().description("AI-driven issue-to-PR workflow for GitHub repositories.");
19017
19556
  const issueCommand = program.command("issue");
19557
+ program.command("run-verification").description("Run configured verification commands for a target").option("--target <name>", "target name").option("--cwd <path>", "target repository directory").action(async (options) => {
19558
+ const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
19559
+ const logPath = path8.join(
19560
+ targetRepoRoot,
19561
+ ".pourkit",
19562
+ "logs",
19563
+ "run-verification.log"
19564
+ );
19565
+ const logger = createLogger("pourkit", logPath);
19566
+ let failed = false;
19567
+ try {
19568
+ const config = await loadRepoConfig(targetRepoRoot);
19569
+ const target = resolveTarget(config, options.target);
19570
+ const result = await runVerificationCommands({
19571
+ target,
19572
+ cwd: targetRepoRoot,
19573
+ logger
19574
+ });
19575
+ console.log(JSON.stringify(result, null, 2));
19576
+ failed = !result.ok;
19577
+ } catch (error) {
19578
+ await handleError(logger, error);
19579
+ } finally {
19580
+ await logger.close();
19581
+ }
19582
+ if (failed) {
19583
+ process.exit(1);
19584
+ }
19585
+ });
19018
19586
  program.command("validate-artifact").description("Validate an agent handoff artifact").argument(
19019
19587
  "<kind>",
19020
19588
  "artifact kind: reviewer, refactor, finalizer, conflict-resolution, failure-resolution, reconciliation, planning-manifest, local-prd, local-issue, local-triage, or local-prepare"
@@ -19790,11 +20358,11 @@ function createCliProgram(version) {
19790
20358
  return program;
19791
20359
  }
19792
20360
  async function resolveCliVersion() {
19793
- if (isPackageVersion("0.0.0-next-20260607184355")) {
19794
- return "0.0.0-next-20260607184355";
20361
+ if (isPackageVersion("0.0.0-next-20260608072322")) {
20362
+ return "0.0.0-next-20260608072322";
19795
20363
  }
19796
- if (isReleaseVersion("0.0.0-next-20260607184355")) {
19797
- return "0.0.0-next-20260607184355";
20364
+ if (isReleaseVersion("0.0.0-next-20260608072322")) {
20365
+ return "0.0.0-next-20260608072322";
19798
20366
  }
19799
20367
  try {
19800
20368
  const root = repoRoot();