@pourkit/cli 0.0.0-next-20260724200221 → 0.0.0-next-20260727021939

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
@@ -14,15 +14,17 @@ import { createWriteStream } from "fs";
14
14
  import { mkdirSync } from "fs";
15
15
  import path from "path";
16
16
  import { styleText } from "util";
17
- function createLogger(name, filePath) {
17
+ function createLogger(name, filePath, options = {}) {
18
18
  let fileStream;
19
19
  if (filePath) {
20
20
  mkdirSync(path.dirname(filePath), { recursive: true });
21
21
  fileStream = createWriteStream(filePath, { flags: "a" });
22
22
  }
23
- const write = (terminal, plain = terminal) => {
24
- process.stdout.write(`${terminal}
23
+ const write = (terminal, plain = terminal, detail = false) => {
24
+ if (!options.quiet || !detail) {
25
+ process.stdout.write(`${terminal}
25
26
  `);
27
+ }
26
28
  if (fileStream) {
27
29
  fileStream.write(`${plain}
28
30
  `);
@@ -34,7 +36,7 @@ function createLogger(name, filePath) {
34
36
  write(`${ts.terminal} ${msg}`, `${ts.plain} ${msg}`);
35
37
  },
36
38
  raw(msg) {
37
- write(msg);
39
+ write(msg, msg, true);
38
40
  },
39
41
  step(step, msg) {
40
42
  const ts = timestamp();
@@ -54,7 +56,8 @@ function createLogger(name, filePath) {
54
56
  const ts = timestamp();
55
57
  write(
56
58
  `${ts.terminal} ${color("dim", key)}=${formatValue(key, value)}`,
57
- `${ts.plain} ${key}=${value}`
59
+ `${ts.plain} ${key}=${value}`,
60
+ true
58
61
  );
59
62
  },
60
63
  async close() {
@@ -10022,6 +10025,12 @@ var PrdRunRepairAttemptSchema = z.object({
10022
10025
  failureCode: z.string().min(1),
10023
10026
  summary: z.string().min(1)
10024
10027
  }).strict();
10028
+ var PrdRunGuidanceTimeoutClassSchema = z.enum([
10029
+ "launcher-stale-lease",
10030
+ "sandcastle-idle-timeout",
10031
+ "verification-command-timeout",
10032
+ "agent-runtime-abort"
10033
+ ]);
10025
10034
  var PrdRunRepairGuidanceSchema = z.object({
10026
10035
  createdAt: z.string().min(1),
10027
10036
  updatedAt: z.string().min(1),
@@ -10045,6 +10054,8 @@ var PrdRunRepairGuidanceSchema = z.object({
10045
10054
  ]),
10046
10055
  nextAction: z.string().min(1),
10047
10056
  nextRecommendedCommand: z.string().min(1),
10057
+ nextRecommendedArgv: z.array(z.string().min(1)).optional(),
10058
+ timeoutClass: PrdRunGuidanceTimeoutClassSchema.optional(),
10048
10059
  diagnostics: z.array(z.string()).optional(),
10049
10060
  offendingPaths: z.array(z.string()).optional(),
10050
10061
  issue: z.object({
@@ -10076,6 +10087,8 @@ function buildPrdRunRepairGuidance(input) {
10076
10087
  repairability: input.repairability,
10077
10088
  nextAction: input.nextAction,
10078
10089
  nextRecommendedCommand: input.nextRecommendedCommand,
10090
+ nextRecommendedArgv: input.nextRecommendedArgv,
10091
+ timeoutClass: input.timeoutClass,
10079
10092
  diagnostics: input.diagnostics,
10080
10093
  offendingPaths: input.offendingPaths,
10081
10094
  issue: input.issue,
@@ -10118,6 +10131,7 @@ function validateActivePrdRunRepairGuidance(record) {
10118
10131
  repairability: "unsupported-state",
10119
10132
  nextAction: "inspect-prd-run-state",
10120
10133
  nextRecommendedCommand: `pourkit prd-run status ${record.prdRef}`,
10134
+ nextRecommendedArgv: ["pourkit", "prd-run", "status", record.prdRef],
10121
10135
  diagnostics: [
10122
10136
  `Validation error: ${parsed.error?.message ?? "Unknown schema error"}`
10123
10137
  ]
@@ -10136,6 +10150,7 @@ function validateActivePrdRunRepairGuidance(record) {
10136
10150
  repairability: "unsupported-state",
10137
10151
  nextAction: "inspect-prd-run-state",
10138
10152
  nextRecommendedCommand: `pourkit prd-run status ${record.prdRef}`,
10153
+ nextRecommendedArgv: ["pourkit", "prd-run", "status", record.prdRef],
10139
10154
  diagnostics: record.diagnostics ? [...record.diagnostics] : []
10140
10155
  }
10141
10156
  };
@@ -10149,6 +10164,10 @@ function renderPrdRunRepairGuidance(guidance) {
10149
10164
  lines.push(`Repairability: ${guidance.repairability}`);
10150
10165
  lines.push(`Next Action: ${guidance.nextAction}`);
10151
10166
  lines.push(`Command: ${guidance.nextRecommendedCommand}`);
10167
+ if (guidance.nextRecommendedArgv)
10168
+ lines.push(`Argv: ${JSON.stringify(guidance.nextRecommendedArgv)}`);
10169
+ if (guidance.timeoutClass)
10170
+ lines.push(`Timeout Class: ${guidance.timeoutClass}`);
10152
10171
  if (guidance.targetName) lines.push(`Target: ${guidance.targetName}`);
10153
10172
  if (guidance.baseBranch) lines.push(`Base: ${guidance.baseBranch}`);
10154
10173
  if (guidance.issue) {
@@ -10194,6 +10213,59 @@ function renderPrdRunRepairGuidance(guidance) {
10194
10213
 
10195
10214
  // prd-run/state.ts
10196
10215
  var PRD_RUN_STATE_DIR = ".pourkit/prd-runs";
10216
+ var prdRunCurrentIssueSchema = z2.object({
10217
+ number: z2.number().int().positive().optional(),
10218
+ title: z2.string().optional(),
10219
+ beadsChildId: z2.string().min(1).optional(),
10220
+ beadsChildRef: z2.string().min(1).optional()
10221
+ }).strict();
10222
+ var prdRunHeartbeatLeaseSchema = z2.object({
10223
+ launchId: z2.string().min(1),
10224
+ host: z2.string().min(1),
10225
+ pid: z2.number().int(),
10226
+ argv: z2.array(z2.string()),
10227
+ startedAt: z2.string().min(1),
10228
+ lastHeartbeatAt: z2.string().min(1),
10229
+ currentStage: z2.string().min(1),
10230
+ currentIssue: prdRunCurrentIssueSchema.optional(),
10231
+ logPath: z2.string().optional()
10232
+ }).strict();
10233
+ var prdRunTimeoutClassSchema = z2.enum([
10234
+ "launcher-stale-lease",
10235
+ "sandcastle-idle-timeout",
10236
+ "verification-command-timeout",
10237
+ "agent-runtime-abort"
10238
+ ]);
10239
+ var prdRunEventKindSchema = z2.enum([
10240
+ "launch-started",
10241
+ "active-launch-reported",
10242
+ "stale-lease-recovered",
10243
+ "projection-repaired",
10244
+ "projection-repair-failed",
10245
+ "blocked-guidance-written",
10246
+ "timeout-classified",
10247
+ "launch-completed",
10248
+ "launch-blocked"
10249
+ ]);
10250
+ var prdRunEventHistoryEntrySchema = z2.object({
10251
+ eventId: z2.string().min(1),
10252
+ kind: prdRunEventKindSchema,
10253
+ occurredAt: z2.string().min(1),
10254
+ launchId: z2.string().min(1).optional(),
10255
+ stage: z2.string().optional(),
10256
+ issue: prdRunCurrentIssueSchema.optional(),
10257
+ failureCode: z2.string().optional(),
10258
+ repairability: z2.enum(["automatic-retry-safe", "operator-action", "unsupported-state"]).optional(),
10259
+ timeoutClass: prdRunTimeoutClassSchema.optional(),
10260
+ summary: z2.string().min(1),
10261
+ diagnostics: z2.array(z2.string()).optional()
10262
+ }).strict();
10263
+ var prdRunQueueProgressSchema = z2.object({
10264
+ processedIssueNumbers: z2.array(z2.number().int().positive()),
10265
+ processedCount: z2.number().int().nonnegative(),
10266
+ lastIssueNumber: z2.number().int().positive().optional(),
10267
+ updatedAt: z2.string().min(1)
10268
+ }).strict();
10197
10269
  var PrdRunRecordSchema = z2.object({
10198
10270
  prdRef: z2.string().regex(
10199
10271
  /^PRD-\d{4}$/,
@@ -10316,7 +10388,10 @@ var PrdRunRecordSchema = z2.object({
10316
10388
  acceptedBy: z2.string().min(1),
10317
10389
  acceptedAt: z2.string().min(1)
10318
10390
  }).strict()
10319
- ).optional()
10391
+ ).optional(),
10392
+ heartbeatLease: prdRunHeartbeatLeaseSchema.optional(),
10393
+ eventHistory: z2.array(prdRunEventHistoryEntrySchema).optional(),
10394
+ queueProgress: prdRunQueueProgressSchema.optional()
10320
10395
  }).strict();
10321
10396
  function normalizePrdRunRef(ref) {
10322
10397
  const trimmed = ref.trim().toUpperCase();
@@ -11125,10 +11200,13 @@ async function runPrdPlanWorkspacePublishCommand(options) {
11125
11200
 
11126
11201
  // commands/prd-run.ts
11127
11202
  import { lstatSync, realpathSync } from "fs";
11203
+ import { hostname as hostname2 } from "os";
11128
11204
  import { join as join24 } from "path";
11129
11205
 
11130
11206
  // prd-run/coordinator.ts
11131
11207
  import { spawnSync as spawnSync2 } from "child_process";
11208
+ import { randomUUID } from "crypto";
11209
+ import { hostname } from "os";
11132
11210
  import { join as join23 } from "path";
11133
11211
  import { Match, pipe } from "effect";
11134
11212
  init_common();
@@ -12157,6 +12235,141 @@ async function buildIssueContextFromBeadsChild(options) {
12157
12235
  }
12158
12236
  }
12159
12237
 
12238
+ // prd-run/liveness.ts
12239
+ var PRD_RUN_HEARTBEAT_INTERVAL_MS = 3e4;
12240
+ var PRD_RUN_LEASE_STALE_AFTER_MS = 12e4;
12241
+ function classifyPrdRunLiveness(options) {
12242
+ const { record, now, currentHost, isPidAlive } = options;
12243
+ const diagnostics = [];
12244
+ if (record.status !== "starting" && record.status !== "running") {
12245
+ return {
12246
+ status: "unknown",
12247
+ reason: `Record status is "${record.status}", not starting or running`,
12248
+ diagnostics: [
12249
+ ...diagnostics,
12250
+ `Liveness classification requires starting or running status, got "${record.status}"`
12251
+ ]
12252
+ };
12253
+ }
12254
+ const lease = record.heartbeatLease;
12255
+ if (!lease) {
12256
+ return {
12257
+ status: "stale",
12258
+ reason: "No heartbeat lease exists",
12259
+ diagnostics: [
12260
+ ...diagnostics,
12261
+ "PRD Run record has no heartbeatLease field"
12262
+ ]
12263
+ };
12264
+ }
12265
+ if (!Number.isFinite(lease.pid) || !Number.isInteger(lease.pid) || lease.pid <= 0) {
12266
+ return {
12267
+ status: "unknown",
12268
+ reason: "Invalid PID in heartbeat lease",
12269
+ lease,
12270
+ diagnostics: [
12271
+ ...diagnostics,
12272
+ `PID value "${lease.pid}" is not a valid positive integer`
12273
+ ]
12274
+ };
12275
+ }
12276
+ const heartbeatTime = new Date(lease.lastHeartbeatAt);
12277
+ if (Number.isNaN(heartbeatTime.getTime())) {
12278
+ return {
12279
+ status: "unknown",
12280
+ reason: "Unparseable heartbeat timestamp",
12281
+ lease,
12282
+ diagnostics: [
12283
+ ...diagnostics,
12284
+ `Cannot parse lastHeartbeatAt "${lease.lastHeartbeatAt}" as a valid date`
12285
+ ]
12286
+ };
12287
+ }
12288
+ const heartbeatAgeMs = now.getTime() - heartbeatTime.getTime();
12289
+ if (heartbeatAgeMs < 0) {
12290
+ const futureDelta = Math.abs(heartbeatAgeMs);
12291
+ return {
12292
+ status: "unknown",
12293
+ reason: `Heartbeat timestamp is ${futureDelta}ms in the future`,
12294
+ lease,
12295
+ heartbeatAgeMs,
12296
+ diagnostics: [
12297
+ ...diagnostics,
12298
+ `lastHeartbeatAt "${lease.lastHeartbeatAt}" is ${futureDelta}ms ahead of reference time "${now.toISOString()}"`
12299
+ ]
12300
+ };
12301
+ }
12302
+ const isSameHost = lease.host === currentHost;
12303
+ if (isSameHost && isPidAlive !== void 0) {
12304
+ const pidAlive = isPidAlive(lease.pid);
12305
+ if (!pidAlive) {
12306
+ return {
12307
+ status: "stale",
12308
+ reason: "Same-host PID is not alive",
12309
+ lease,
12310
+ heartbeatAgeMs,
12311
+ diagnostics: [
12312
+ ...diagnostics,
12313
+ `PID ${lease.pid} on host "${lease.host}" is not alive (advisory same-host evidence)`
12314
+ ]
12315
+ };
12316
+ }
12317
+ }
12318
+ if (heartbeatAgeMs > PRD_RUN_LEASE_STALE_AFTER_MS) {
12319
+ return {
12320
+ status: "stale",
12321
+ reason: `Heartbeat age ${heartbeatAgeMs}ms exceeds stale threshold ${PRD_RUN_LEASE_STALE_AFTER_MS}ms`,
12322
+ lease,
12323
+ heartbeatAgeMs,
12324
+ diagnostics: [
12325
+ ...diagnostics,
12326
+ `lastHeartbeatAt "${lease.lastHeartbeatAt}" is ${heartbeatAgeMs}ms old (threshold: ${PRD_RUN_LEASE_STALE_AFTER_MS}ms)`
12327
+ ]
12328
+ };
12329
+ }
12330
+ return {
12331
+ status: "active",
12332
+ reason: isSameHost ? "Fresh heartbeat lease on current host" : `Fresh heartbeat lease on host "${lease.host}"`,
12333
+ lease,
12334
+ heartbeatAgeMs,
12335
+ diagnostics
12336
+ };
12337
+ }
12338
+
12339
+ // prd-run/failure-classification.ts
12340
+ function classifyPrdRunFailure(reason, _context) {
12341
+ if (/^Sandcastle failed:/.test(reason) && /(?:timeout|timed?\s+out|aborted)/i.test(reason)) {
12342
+ return {
12343
+ timeoutClass: "sandcastle-idle-timeout",
12344
+ diagnostics: [
12345
+ "Timeout class: sandcastle-idle-timeout",
12346
+ "Sandcastle execution timeout detected."
12347
+ ]
12348
+ };
12349
+ }
12350
+ if (/^Timeout waiting for/.test(reason) && /checks|to be green/.test(reason)) {
12351
+ return {
12352
+ timeoutClass: "verification-command-timeout",
12353
+ diagnostics: [
12354
+ "Timeout class: verification-command-timeout",
12355
+ "Verification wait timeout detected."
12356
+ ]
12357
+ };
12358
+ }
12359
+ if (/\b(?:agent|runtime)\b.*\b(?:abort|timeout|timed?\s+out|killed)\b/i.test(
12360
+ reason
12361
+ ) || /\b(?:abort|killed)\b.*\b(?:agent|runtime)\b/i.test(reason)) {
12362
+ return {
12363
+ timeoutClass: "agent-runtime-abort",
12364
+ diagnostics: [
12365
+ "Timeout class: agent-runtime-abort",
12366
+ "Agent/runtime abort detected."
12367
+ ]
12368
+ };
12369
+ }
12370
+ return { diagnostics: [] };
12371
+ }
12372
+
12160
12373
  // prd-run/coordinator.ts
12161
12374
  function canRetryFinalReviewBlock(record) {
12162
12375
  if (record.status !== "blocked" || record.blockedGate !== "final-review") {
@@ -12323,6 +12536,148 @@ function resolvePrdRunBaseBranch(options) {
12323
12536
  const source = options.baseBranchOverride !== void 0 ? "override" : "target";
12324
12537
  return { ok: true, baseBranch: trimmed, source };
12325
12538
  }
12539
+ function buildPrdRunLaunchRetryCommand(options, prdRef) {
12540
+ if (options.originalArgv && options.originalArgv.length > 0) {
12541
+ return {
12542
+ command: options.originalArgv.join(" "),
12543
+ argv: [...options.originalArgv]
12544
+ };
12545
+ }
12546
+ const argv = ["pourkit", "prd-run", "launch", prdRef];
12547
+ if (options.targetName) {
12548
+ argv.push("--target", options.targetName);
12549
+ }
12550
+ if (options.baseBranchOverride) {
12551
+ argv.push("--base", options.baseBranchOverride);
12552
+ }
12553
+ if (options.quiet === true) {
12554
+ argv.push("--quiet");
12555
+ }
12556
+ if (options.autoMerge === false) {
12557
+ argv.push("--no-auto-merge");
12558
+ }
12559
+ if (options.adoptExistingBranch === true) {
12560
+ argv.push("--adopt-existing-branch");
12561
+ }
12562
+ if (options.cwd) {
12563
+ argv.push("--cwd", options.cwd);
12564
+ }
12565
+ return { command: argv.join(" "), argv };
12566
+ }
12567
+ function isLaunchRecommendation(command) {
12568
+ return /\bprd-run\s+launch\b/.test(command);
12569
+ }
12570
+ function isStatusRecommendation(command) {
12571
+ return /\bprd-run\s+status\b/.test(command);
12572
+ }
12573
+ function buildFallbackLaunchRetryArgv(options) {
12574
+ if (options.heartbeatArgv && options.heartbeatArgv.length > 0) {
12575
+ return [...options.heartbeatArgv];
12576
+ }
12577
+ const argv = ["pourkit", "prd-run", "launch", options.prdRef];
12578
+ if (options.targetName) argv.push("--target", options.targetName);
12579
+ if (options.baseBranch) argv.push("--base", options.baseBranch);
12580
+ return argv;
12581
+ }
12582
+ function enrichLaunchRepairGuidance(options) {
12583
+ const { guidance, prdRef, targetName, baseBranch, heartbeatArgv } = options;
12584
+ if (guidance.nextAction === "rerun-prd-run-launch" || isLaunchRecommendation(guidance.nextRecommendedCommand)) {
12585
+ const argv = guidance.nextRecommendedArgv ?? buildFallbackLaunchRetryArgv({
12586
+ prdRef,
12587
+ targetName: guidance.targetName ?? targetName,
12588
+ baseBranch: guidance.baseBranch ?? baseBranch,
12589
+ heartbeatArgv
12590
+ });
12591
+ return {
12592
+ ...guidance,
12593
+ nextRecommendedCommand: argv.join(" "),
12594
+ nextRecommendedArgv: argv
12595
+ };
12596
+ }
12597
+ if (isStatusRecommendation(guidance.nextRecommendedCommand)) {
12598
+ const argv = guidance.nextRecommendedArgv ?? [
12599
+ "pourkit",
12600
+ "prd-run",
12601
+ "status",
12602
+ prdRef
12603
+ ];
12604
+ return {
12605
+ ...guidance,
12606
+ nextRecommendedCommand: argv.join(" "),
12607
+ nextRecommendedArgv: argv
12608
+ };
12609
+ }
12610
+ return guidance;
12611
+ }
12612
+ var CURRENT_HOST = hostname();
12613
+ function createPrdRunHeartbeatLease(options, prdRef, currentStage) {
12614
+ const now = (/* @__PURE__ */ new Date()).toISOString();
12615
+ return {
12616
+ launchId: randomUUID(),
12617
+ host: CURRENT_HOST,
12618
+ pid: process.pid,
12619
+ argv: buildPrdRunLaunchRetryCommand(options, prdRef).argv,
12620
+ startedAt: now,
12621
+ lastHeartbeatAt: now,
12622
+ currentStage
12623
+ };
12624
+ }
12625
+ function refreshPrdRunHeartbeat(repoRoot2, prdRef, launchId, update) {
12626
+ const current = readPrdRun(repoRoot2, prdRef).record;
12627
+ if (!current?.heartbeatLease) return;
12628
+ if (current.heartbeatLease.launchId !== launchId) return;
12629
+ writePrdRunRecord(repoRoot2, {
12630
+ ...current,
12631
+ heartbeatLease: {
12632
+ ...current.heartbeatLease,
12633
+ ...update
12634
+ },
12635
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
12636
+ });
12637
+ }
12638
+ async function withPrdRunHeartbeat(repoRoot2, prdRef, lease, fn) {
12639
+ const interval = setInterval(() => {
12640
+ refreshPrdRunHeartbeat(repoRoot2, prdRef, lease.launchId, {
12641
+ lastHeartbeatAt: (/* @__PURE__ */ new Date()).toISOString()
12642
+ });
12643
+ }, PRD_RUN_HEARTBEAT_INTERVAL_MS);
12644
+ try {
12645
+ return await fn({
12646
+ updateStage: (stage) => {
12647
+ refreshPrdRunHeartbeat(repoRoot2, prdRef, lease.launchId, {
12648
+ currentStage: stage,
12649
+ lastHeartbeatAt: (/* @__PURE__ */ new Date()).toISOString()
12650
+ });
12651
+ }
12652
+ });
12653
+ } finally {
12654
+ clearInterval(interval);
12655
+ }
12656
+ }
12657
+ var PRD_RUN_EVENT_HISTORY_LIMIT = 50;
12658
+ function appendPrdRunEvent(record, event) {
12659
+ const currentHistory = record?.eventHistory ?? [];
12660
+ const newEntry = {
12661
+ ...event,
12662
+ eventId: randomUUID(),
12663
+ occurredAt: (/* @__PURE__ */ new Date()).toISOString()
12664
+ };
12665
+ return [...currentHistory, newEntry].slice(-PRD_RUN_EVENT_HISTORY_LIMIT);
12666
+ }
12667
+ function updatePrdRunQueueProgress(record, issueNumbers) {
12668
+ const current = record?.queueProgress;
12669
+ const merged = new Set(current?.processedIssueNumbers ?? []);
12670
+ for (const n of issueNumbers) {
12671
+ merged.add(n);
12672
+ }
12673
+ const sorted = [...merged].sort((a, b) => a - b);
12674
+ return {
12675
+ processedIssueNumbers: sorted,
12676
+ processedCount: sorted.length,
12677
+ lastIssueNumber: sorted.length > 0 ? sorted[sorted.length - 1] : current?.lastIssueNumber,
12678
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
12679
+ };
12680
+ }
12326
12681
  function fetchOriginBranch(repoRoot2, baseBranch) {
12327
12682
  const fetchResult = spawnSync2("git", ["fetch", "origin", baseBranch], {
12328
12683
  cwd: repoRoot2,
@@ -12537,6 +12892,8 @@ function persistStartingPrdRunRecord(repoRoot2, prdRef, existingRecord, start, c
12537
12892
  });
12538
12893
  }
12539
12894
  function blocked(repoRoot2, prdRef, gate, failureCode, reason, diagnostics, targetName, stage, repairability = "operator-action") {
12895
+ const retryArgv = ["pourkit", "prd-run", "launch", prdRef];
12896
+ if (targetName) retryArgv.push("--target", targetName);
12540
12897
  return buildBlockedOutcome(
12541
12898
  repoRoot2,
12542
12899
  prdRef,
@@ -12547,7 +12904,8 @@ function blocked(repoRoot2, prdRef, gate, failureCode, reason, diagnostics, targ
12547
12904
  humanReadableReason: reason,
12548
12905
  repairability,
12549
12906
  nextAction: repairability === "automatic-retry-safe" ? "rerun-prd-run-launch" : "inspect-and-retry",
12550
- nextRecommendedCommand: `pourkit prd-run launch ${prdRef}`,
12907
+ nextRecommendedCommand: retryArgv.join(" "),
12908
+ nextRecommendedArgv: retryArgv,
12551
12909
  ...diagnostics?.length ? { diagnostics: [...diagnostics] } : {},
12552
12910
  ...targetName ? { targetName } : {}
12553
12911
  }),
@@ -12911,13 +13269,49 @@ async function startPrdRun(options) {
12911
13269
  };
12912
13270
  }
12913
13271
  function buildLaunchBlockedOutcome(repoRoot2, prdRef, guidance, resume, start, targetName, startReceipt) {
12914
- const enrichedGuidance = {
13272
+ const current = readPrdRun(repoRoot2, prdRef).record;
13273
+ const enrichedGuidanceBase = {
12915
13274
  ...guidance,
12916
13275
  targetName: guidance.targetName ?? targetName,
12917
13276
  baseBranch: guidance.baseBranch ?? startReceipt?.startBaseBranch
12918
13277
  };
12919
- const current = readPrdRun(repoRoot2, prdRef).record;
13278
+ const enrichedGuidance = enrichLaunchRepairGuidance({
13279
+ guidance: enrichedGuidanceBase,
13280
+ prdRef,
13281
+ targetName,
13282
+ baseBranch: startReceipt?.startBaseBranch,
13283
+ heartbeatArgv: current?.heartbeatLease?.argv
13284
+ });
12920
13285
  const postCompletionFinalReview = current?.postCompletionFinalReview;
13286
+ let eventHistory = current?.eventHistory ?? [];
13287
+ if (enrichedGuidance.timeoutClass) {
13288
+ eventHistory = appendPrdRunEvent(
13289
+ current ? { ...current, eventHistory } : null,
13290
+ {
13291
+ kind: "timeout-classified",
13292
+ launchId: current?.heartbeatLease?.launchId,
13293
+ stage: enrichedGuidance.blockedStage,
13294
+ failureCode: enrichedGuidance.failureCode,
13295
+ repairability: enrichedGuidance.repairability,
13296
+ timeoutClass: enrichedGuidance.timeoutClass,
13297
+ summary: `PRD Run ${prdRef} classified timeout as ${enrichedGuidance.timeoutClass}.`,
13298
+ diagnostics: enrichedGuidance.diagnostics?.length ? [...enrichedGuidance.diagnostics] : void 0
13299
+ }
13300
+ );
13301
+ }
13302
+ eventHistory = appendPrdRunEvent(
13303
+ current ? { ...current, eventHistory } : null,
13304
+ {
13305
+ kind: "launch-blocked",
13306
+ launchId: current?.heartbeatLease?.launchId,
13307
+ stage: enrichedGuidance.blockedStage,
13308
+ failureCode: enrichedGuidance.failureCode,
13309
+ repairability: enrichedGuidance.repairability,
13310
+ summary: enrichedGuidance.humanReadableReason,
13311
+ diagnostics: enrichedGuidance.diagnostics?.length ? [...enrichedGuidance.diagnostics] : void 0
13312
+ }
13313
+ );
13314
+ const queueProgress = current?.queueProgress;
12921
13315
  writePrdRunRecord(repoRoot2, {
12922
13316
  prdRef,
12923
13317
  status: "blocked",
@@ -12929,6 +13323,8 @@ function buildLaunchBlockedOutcome(repoRoot2, prdRef, guidance, resume, start, t
12929
13323
  ...targetName ? { targetName } : {},
12930
13324
  ...startReceipt ? { start: startReceipt } : {},
12931
13325
  ...postCompletionFinalReview ? { postCompletionFinalReview } : {},
13326
+ eventHistory,
13327
+ ...queueProgress ? { queueProgress } : {},
12932
13328
  repairGuidance: enrichedGuidance
12933
13329
  });
12934
13330
  return {
@@ -12940,22 +13336,37 @@ function buildLaunchBlockedOutcome(repoRoot2, prdRef, guidance, resume, start, t
12940
13336
  };
12941
13337
  }
12942
13338
  function writeDrainedRecord(repoRoot2, prdRef, startReceipt, targetName) {
13339
+ const current = readPrdRun(repoRoot2, prdRef).record;
13340
+ const eventHistory = current?.eventHistory;
13341
+ const queueProgress = current?.queueProgress;
12943
13342
  writePrdRunRecord(repoRoot2, {
12944
13343
  prdRef,
12945
13344
  status: "drained",
12946
13345
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
12947
13346
  targetName,
12948
- start: startReceipt
13347
+ start: startReceipt,
13348
+ ...eventHistory ? { eventHistory } : {},
13349
+ ...queueProgress ? { queueProgress } : {}
12949
13350
  });
12950
13351
  }
12951
13352
  function writeTerminalRecord(repoRoot2, prdRef, status, targetName, startReceipt, prdBranch) {
13353
+ const current = readPrdRun(repoRoot2, prdRef).record;
13354
+ const eventHistory = appendPrdRunEvent(current, {
13355
+ kind: "launch-completed",
13356
+ launchId: current?.heartbeatLease?.launchId,
13357
+ stage: "completed",
13358
+ summary: `PRD Run ${prdRef} completed with status ${status}. Branch: ${prdBranch}.`
13359
+ });
13360
+ const queueProgress = current?.queueProgress;
12952
13361
  writePrdRunRecord(repoRoot2, {
12953
13362
  prdRef,
12954
13363
  status,
12955
13364
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
12956
13365
  targetName,
12957
13366
  prdBranch,
12958
- start: startReceipt
13367
+ start: startReceipt,
13368
+ eventHistory,
13369
+ ...queueProgress ? { queueProgress } : {}
12959
13370
  });
12960
13371
  }
12961
13372
  function validateRecordedIssueResumeEvidence(options) {
@@ -13035,11 +13446,14 @@ var QUEUE_FAILURE_CODES_WITHOUT_SELECTED_ISSUE = /* @__PURE__ */ new Set([
13035
13446
  var ISSUE_FINAL_REVIEW_HANDOFF_PREFIX = "Issue Final Review requires human handoff";
13036
13447
  function buildQueueIssueRunRepairMetadata(message, fallbackFailureCode, prdRef) {
13037
13448
  if (!message.startsWith(ISSUE_FINAL_REVIEW_HANDOFF_PREFIX)) {
13449
+ const classification = classifyPrdRunFailure(message, { prdRef });
13038
13450
  return {
13039
13451
  failureCode: fallbackFailureCode,
13040
13452
  repairability: "automatic-retry-safe",
13041
13453
  nextAction: "rerun-prd-run-launch",
13042
- nextRecommendedCommand: "pourkit prd-run launch"
13454
+ nextRecommendedCommand: "pourkit prd-run launch",
13455
+ timeoutClass: classification.timeoutClass,
13456
+ classifierDiagnostics: classification.diagnostics
13043
13457
  };
13044
13458
  }
13045
13459
  return {
@@ -13105,13 +13519,22 @@ async function prdParentIssueIsClosed(options) {
13105
13519
  }
13106
13520
  }
13107
13521
  async function reconcileBlockedBeadsIssueProjection(options) {
13108
- const { issue, issueProvider, config, prdRef, logger, retryRepair } = options;
13522
+ const {
13523
+ issue,
13524
+ issueProvider,
13525
+ config,
13526
+ prdRef,
13527
+ repoRoot: repoRoot2,
13528
+ logger,
13529
+ retryRepair
13530
+ } = options;
13109
13531
  if (shouldRestoreRetryableBeadsProjection({ issue, config, retryRepair })) {
13110
13532
  logCoordinatorStep(
13111
13533
  logger,
13112
13534
  "info",
13113
13535
  `Restoring retryable GitHub projection for Beads Issue #${issue.number}.`
13114
13536
  );
13537
+ let repairFailed = false;
13115
13538
  for (const label of [
13116
13539
  config.labels.blocked,
13117
13540
  config.labels.agentInProgress,
@@ -13121,9 +13544,25 @@ async function reconcileBlockedBeadsIssueProjection(options) {
13121
13544
  try {
13122
13545
  await issueProvider.removeLabel(issue.number, label);
13123
13546
  } catch {
13547
+ repairFailed = true;
13124
13548
  }
13125
13549
  }
13126
13550
  await issueProvider.addLabels(issue.number, [config.labels.readyForAgent]);
13551
+ const currentRecord = readPrdRun(repoRoot2, prdRef).record;
13552
+ const projectionEvent = appendPrdRunEvent(currentRecord, {
13553
+ kind: repairFailed ? "projection-repair-failed" : "projection-repaired",
13554
+ launchId: currentRecord?.heartbeatLease?.launchId,
13555
+ stage: "queue-drain",
13556
+ issue: { number: issue.number, title: issue.title },
13557
+ summary: repairFailed ? `Projection repair failed for Issue #${issue.number}.` : `Projection repaired for Issue #${issue.number}.`
13558
+ });
13559
+ writePrdRunRecord(repoRoot2, {
13560
+ ...currentRecord,
13561
+ prdRef,
13562
+ status: currentRecord?.status ?? "running",
13563
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13564
+ eventHistory: projectionEvent
13565
+ });
13127
13566
  return await issueProvider.fetchIssue(issue.number);
13128
13567
  }
13129
13568
  if (!issue.labels.includes(config.labels.blocked)) {
@@ -13260,7 +13699,7 @@ async function attemptRecordedIssueLabelProjection(options, prdRef, resumeIssueN
13260
13699
  return true;
13261
13700
  } catch {
13262
13701
  const state = readPrdRun(options.repoRoot, prdRef).record;
13263
- const guidance = originalGuidance ?? state?.repairGuidance;
13702
+ const guidance = state?.repairGuidance ?? originalGuidance;
13264
13703
  if (guidance) {
13265
13704
  const updated = appendPrdRunRepairAttempt(guidance, {
13266
13705
  outcome: "projection-failed",
@@ -13638,7 +14077,9 @@ async function runPostCompletionAfterTerminal(options, prdRef, record, resumeFac
13638
14077
  prdBranch,
13639
14078
  start: record.start,
13640
14079
  postCompletionFinalReview: pcReceipt,
13641
- repairGuidance: record.repairGuidance
14080
+ repairGuidance: record.repairGuidance,
14081
+ ...current?.eventHistory ? { eventHistory: current.eventHistory } : {},
14082
+ ...current?.queueProgress ? { queueProgress: current.queueProgress } : {}
13642
14083
  });
13643
14084
  }
13644
14085
  };
@@ -13873,28 +14314,57 @@ async function launchPrdRun(options) {
13873
14314
  const plan = planPrdRunLaunchResume(existingRecord.record);
13874
14315
  const rec = existingRecord.record;
13875
14316
  if (rec && (rec.status === "starting" || rec.status === "running") && !rec.repairGuidance) {
13876
- const nextRecommendedCommand = `pourkit prd-run launch ${prdRef}${rec.targetName ? ` --target ${rec.targetName}` : ""}${rec.start?.startBaseBranch ? ` --base ${rec.start.startBaseBranch}` : ""}`;
13877
- return buildLaunchBlockedOutcome(
13878
- options.repoRoot,
13879
- prdRef,
13880
- buildPrdRunRepairGuidance({
13881
- blockedGate: "branch-state",
13882
- blockedStage: "start-validation",
13883
- failureCode: "prd-run-in-progress",
13884
- humanReadableReason: `PRD Run ${prdRef} is already ${rec.status}. A second launch cannot start another Queue drain while the run is in progress.`,
13885
- repairability: "automatic-retry-safe",
13886
- nextAction: "confirm-no-active-run-then-rerun-prd-run-launch",
13887
- nextRecommendedCommand,
13888
- diagnostics: [
13889
- `PRD Run ${prdRef} is in status "${rec.status}" without active repair guidance.`,
13890
- "If no pourkit/sandcastle agent process is still active for this PRD, rerun the launch command to resume from the preserved start receipt."
13891
- ]
13892
- }),
13893
- plan,
13894
- void 0,
13895
- rec.targetName ?? options.targetName,
13896
- rec.start
13897
- );
14317
+ const liveness = classifyPrdRunLiveness({
14318
+ record: rec,
14319
+ now: /* @__PURE__ */ new Date(),
14320
+ currentHost: CURRENT_HOST
14321
+ });
14322
+ if (liveness.status === "active") {
14323
+ return {
14324
+ kind: "active",
14325
+ prdRef,
14326
+ status: rec.status,
14327
+ liveness,
14328
+ resume: plan
14329
+ };
14330
+ }
14331
+ if (liveness.status === "unknown") {
14332
+ const retryCommand = buildPrdRunLaunchRetryCommand(options, prdRef);
14333
+ return buildLaunchBlockedOutcome(
14334
+ options.repoRoot,
14335
+ prdRef,
14336
+ buildPrdRunRepairGuidance({
14337
+ blockedGate: "branch-state",
14338
+ blockedStage: "start-validation",
14339
+ failureCode: "prd-run-liveness-unknown",
14340
+ humanReadableReason: `PRD Run ${prdRef} has unknown liveness for status "${rec.status}". The heartbeat lease data is malformed or unreadable.`,
14341
+ repairability: "operator-action",
14342
+ nextAction: "inspect-prd-run-state",
14343
+ nextRecommendedCommand: retryCommand.command,
14344
+ nextRecommendedArgv: retryCommand.argv,
14345
+ diagnostics: [
14346
+ ...liveness.diagnostics,
14347
+ `PRD Run ${prdRef} is in status "${rec.status}" with unreadable lease data.`
14348
+ ]
14349
+ }),
14350
+ plan,
14351
+ void 0,
14352
+ rec.targetName ?? options.targetName,
14353
+ rec.start
14354
+ );
14355
+ }
14356
+ const eventHistory = appendPrdRunEvent(rec, {
14357
+ kind: "stale-lease-recovered",
14358
+ timeoutClass: "launcher-stale-lease",
14359
+ summary: `Stale lease recovered for PRD Run ${prdRef}. Status: "${rec.status}", reason: "${liveness.reason}".`,
14360
+ diagnostics: liveness.diagnostics.length > 0 ? [...liveness.diagnostics] : void 0
14361
+ });
14362
+ writePrdRunRecord(options.repoRoot, {
14363
+ ...rec,
14364
+ heartbeatLease: void 0,
14365
+ eventHistory,
14366
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
14367
+ });
13898
14368
  }
13899
14369
  if (existingRecord.record === null && existingRecord.diagnostics.length > 0) {
13900
14370
  return buildLaunchBlockedOutcome(
@@ -14054,13 +14524,14 @@ async function launchPrdRun(options) {
14054
14524
  baseBranchOverride: options.baseBranchOverride
14055
14525
  });
14056
14526
  if (startOutcome.kind === "blocked") {
14057
- return {
14058
- kind: "blocked",
14527
+ return buildLaunchBlockedOutcome(
14528
+ options.repoRoot,
14059
14529
  prdRef,
14060
- guidance: startOutcome.guidance,
14061
- start: startOutcome,
14062
- resume: plan
14063
- };
14530
+ startOutcome.guidance,
14531
+ plan,
14532
+ startOutcome,
14533
+ options.targetName
14534
+ );
14064
14535
  }
14065
14536
  }
14066
14537
  const currentRecord = readPrdRun(options.repoRoot, prdRef).record;
@@ -14200,13 +14671,28 @@ async function launchPrdRun(options) {
14200
14671
  startReceipt.queueStartedAt = (/* @__PURE__ */ new Date()).toISOString();
14201
14672
  startReceipt.queueCommand = "queue-run";
14202
14673
  }
14674
+ const launchLease = createPrdRunHeartbeatLease(
14675
+ options,
14676
+ prdRef,
14677
+ "queue-drain"
14678
+ );
14679
+ const launchStartedEventHistory = appendPrdRunEvent(currentRecord, {
14680
+ kind: "launch-started",
14681
+ launchId: launchLease.launchId,
14682
+ stage: "queue-drain",
14683
+ summary: `PRD Run ${prdRef} launch started. Target: ${targetName}.`
14684
+ });
14685
+ const currentQueueProgress = currentRecord?.queueProgress;
14203
14686
  writePrdRunRecord(options.repoRoot, {
14204
14687
  prdRef,
14205
14688
  status: "running",
14206
14689
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
14207
14690
  targetName,
14208
14691
  start: startReceipt,
14209
- repairGuidance: currentRecord?.repairGuidance
14692
+ repairGuidance: currentRecord?.repairGuidance,
14693
+ heartbeatLease: launchLease,
14694
+ eventHistory: launchStartedEventHistory,
14695
+ ...currentQueueProgress ? { queueProgress: currentQueueProgress } : {}
14210
14696
  });
14211
14697
  const modeSelection = await selectPrdRunCoordinatorMode({
14212
14698
  repoRoot: options.repoRoot,
@@ -14228,25 +14714,33 @@ async function launchPrdRun(options) {
14228
14714
  startReceipt
14229
14715
  );
14230
14716
  }
14231
- if (modeSelection.mode === "github") {
14232
- return await launchGithubQueueDrain(
14233
- options,
14234
- prdRef,
14235
- startReceipt,
14236
- targetName,
14237
- plan,
14238
- startOutcome,
14239
- resumeIssueNumber
14240
- );
14241
- }
14242
- return await launchBeadsQueueDrain(
14243
- options,
14717
+ return await withPrdRunHeartbeat(
14718
+ options.repoRoot,
14244
14719
  prdRef,
14245
- startReceipt,
14246
- targetName,
14247
- plan,
14248
- startOutcome,
14249
- resumeIssueNumber
14720
+ launchLease,
14721
+ async (heartbeat) => {
14722
+ heartbeat.updateStage("queue-drain");
14723
+ if (modeSelection.mode === "github") {
14724
+ return await launchGithubQueueDrain(
14725
+ options,
14726
+ prdRef,
14727
+ startReceipt,
14728
+ targetName,
14729
+ plan,
14730
+ startOutcome,
14731
+ resumeIssueNumber
14732
+ );
14733
+ }
14734
+ return await launchBeadsQueueDrain(
14735
+ options,
14736
+ prdRef,
14737
+ startReceipt,
14738
+ targetName,
14739
+ plan,
14740
+ startOutcome,
14741
+ resumeIssueNumber
14742
+ );
14743
+ }
14250
14744
  );
14251
14745
  }
14252
14746
  async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName, plan, startOutcome, resumeIssueNumber) {
@@ -14384,6 +14878,21 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
14384
14878
  startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
14385
14879
  startReceipt.queueProcessedCount = outcome.processedCount;
14386
14880
  const prdBranch = startReceipt.prdBranch ?? prdRef;
14881
+ const drainCurrentRecord = readPrdRun(options.repoRoot, prdRef).record;
14882
+ if (drainCurrentRecord) {
14883
+ const drainIssueNumbers = outcome.results.map((r) => r.selected.number);
14884
+ const updatedQueueProgress = updatePrdRunQueueProgress(
14885
+ drainCurrentRecord,
14886
+ drainIssueNumbers
14887
+ );
14888
+ writePrdRunRecord(options.repoRoot, {
14889
+ ...drainCurrentRecord,
14890
+ prdRef,
14891
+ status: drainCurrentRecord.status,
14892
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
14893
+ queueProgress: updatedQueueProgress
14894
+ });
14895
+ }
14387
14896
  writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
14388
14897
  const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
14389
14898
  if (drainRecord) {
@@ -14531,13 +15040,13 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
14531
15040
  return result;
14532
15041
  } catch (error) {
14533
15042
  const msg = error instanceof Error ? error.message : String(error);
14534
- const diagnostics = [msg];
14535
15043
  const issue = extractIssueFromQueueError(error);
14536
15044
  const repair = buildQueueIssueRunRepairMetadata(
14537
15045
  msg,
14538
15046
  "queue-exception",
14539
15047
  prdRef
14540
15048
  );
15049
+ const diagnostics = [msg, ...repair.classifierDiagnostics ?? []];
14541
15050
  const result = buildLaunchBlockedOutcome(
14542
15051
  options.repoRoot,
14543
15052
  prdRef,
@@ -14549,6 +15058,7 @@ async function launchGithubQueueDrain(options, prdRef, startReceipt, targetName,
14549
15058
  repairability: repair.repairability,
14550
15059
  nextAction: repair.nextAction,
14551
15060
  nextRecommendedCommand: repair.nextRecommendedCommand,
15061
+ timeoutClass: repair.timeoutClass,
14552
15062
  diagnostics,
14553
15063
  issue
14554
15064
  }),
@@ -14760,6 +15270,7 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14760
15270
  issueProvider,
14761
15271
  config: options.config,
14762
15272
  prdRef,
15273
+ repoRoot: options.repoRoot,
14763
15274
  logger
14764
15275
  });
14765
15276
  if (issue.labels.includes(options.config.labels.blocked)) {
@@ -14808,6 +15319,10 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14808
15319
  const beadsChildClosed = msg.startsWith(
14809
15320
  "Projection failure after Beads close"
14810
15321
  );
15322
+ const guidanceDiagnostics = [
15323
+ msg,
15324
+ ...repair.classifierDiagnostics ?? []
15325
+ ];
14811
15326
  return buildLaunchBlockedOutcome(
14812
15327
  options.repoRoot,
14813
15328
  prdRef,
@@ -14819,7 +15334,8 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14819
15334
  repairability: repair.repairability,
14820
15335
  nextAction: repair.nextAction,
14821
15336
  nextRecommendedCommand: repair.nextRecommendedCommand,
14822
- diagnostics: [msg],
15337
+ timeoutClass: repair.timeoutClass,
15338
+ diagnostics: guidanceDiagnostics,
14823
15339
  issue: {
14824
15340
  number: contextResult.issue.number,
14825
15341
  title: contextResult.issue.title,
@@ -14873,6 +15389,7 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14873
15389
  issueProvider,
14874
15390
  config: options.config,
14875
15391
  prdRef,
15392
+ repoRoot: options.repoRoot,
14876
15393
  logger,
14877
15394
  ...resumeBeadsContext ? {
14878
15395
  retryRepair: {
@@ -14934,6 +15451,10 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14934
15451
  "resume-issue-failed",
14935
15452
  prdRef
14936
15453
  );
15454
+ const guidanceDiagnostics = [
15455
+ msg,
15456
+ ...repair.classifierDiagnostics ?? []
15457
+ ];
14937
15458
  return buildLaunchBlockedOutcome(
14938
15459
  options.repoRoot,
14939
15460
  prdRef,
@@ -14945,7 +15466,8 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
14945
15466
  repairability: repair.repairability,
14946
15467
  nextAction: repair.nextAction,
14947
15468
  nextRecommendedCommand: repair.nextRecommendedCommand,
14948
- diagnostics: [msg],
15469
+ timeoutClass: repair.timeoutClass,
15470
+ diagnostics: guidanceDiagnostics,
14949
15471
  issue: resumeIssue ? {
14950
15472
  number: fetchedIssue.number,
14951
15473
  title: fetchedIssue.title,
@@ -15050,6 +15572,7 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
15050
15572
  issueProvider,
15051
15573
  config: options.config,
15052
15574
  prdRef,
15575
+ repoRoot: options.repoRoot,
15053
15576
  logger
15054
15577
  });
15055
15578
  if (issue.labels.includes(options.config.labels.blocked)) {
@@ -15099,6 +15622,10 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
15099
15622
  const beadsChildClosed = msg.startsWith(
15100
15623
  "Projection failure after Beads close"
15101
15624
  );
15625
+ const guidanceDiagnostics = [
15626
+ msg,
15627
+ ...repair.classifierDiagnostics ?? []
15628
+ ];
15102
15629
  return buildLaunchBlockedOutcome(
15103
15630
  options.repoRoot,
15104
15631
  prdRef,
@@ -15110,7 +15637,8 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
15110
15637
  repairability: repair.repairability,
15111
15638
  nextAction: repair.nextAction,
15112
15639
  nextRecommendedCommand: repair.nextRecommendedCommand,
15113
- diagnostics: [msg],
15640
+ timeoutClass: repair.timeoutClass,
15641
+ diagnostics: guidanceDiagnostics,
15114
15642
  issue: {
15115
15643
  number: contextResult.issue.number,
15116
15644
  title: contextResult.issue.title,
@@ -15176,6 +15704,21 @@ async function launchBeadsQueueDrain(options, prdRef, startReceipt, targetName,
15176
15704
  startReceipt.queueDrainedAt = (/* @__PURE__ */ new Date()).toISOString();
15177
15705
  startReceipt.queueProcessedCount = processedResults.length;
15178
15706
  const prdBranch = startReceipt.prdBranch ?? prdRef;
15707
+ const beadsDrainCurrent = readPrdRun(options.repoRoot, prdRef).record;
15708
+ if (beadsDrainCurrent) {
15709
+ const beadsIssueNumbers = processedResults.map((r) => r.issueNumber);
15710
+ const beadsUpdatedProgress = updatePrdRunQueueProgress(
15711
+ beadsDrainCurrent,
15712
+ beadsIssueNumbers
15713
+ );
15714
+ writePrdRunRecord(options.repoRoot, {
15715
+ ...beadsDrainCurrent,
15716
+ prdRef,
15717
+ status: beadsDrainCurrent.status,
15718
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
15719
+ queueProgress: beadsUpdatedProgress
15720
+ });
15721
+ }
15179
15722
  writeDrainedRecord(options.repoRoot, prdRef, startReceipt, targetName);
15180
15723
  const drainRecord = readPrdRun(options.repoRoot, prdRef).record;
15181
15724
  if (drainRecord) {
@@ -15308,6 +15851,39 @@ function mapLaunchPrdRunOutcomeToCommandResult(outcome, options) {
15308
15851
  start: outcome.start ? mapStartPrdRunOutcomeToCommandResult(outcome.start) : void 0
15309
15852
  };
15310
15853
  }
15854
+ if (outcome.kind === "active") {
15855
+ const livenessDiagnostics = [
15856
+ `Liveness: ${outcome.liveness.status}`
15857
+ ];
15858
+ if (outcome.liveness.lease) {
15859
+ const lease = outcome.liveness.lease;
15860
+ if (outcome.liveness.heartbeatAgeMs !== void 0) {
15861
+ livenessDiagnostics.push(
15862
+ ` Heartbeat age: ${outcome.liveness.heartbeatAgeMs}ms`
15863
+ );
15864
+ }
15865
+ livenessDiagnostics.push(` Host: ${lease.host}`);
15866
+ livenessDiagnostics.push(` PID: ${lease.pid}`);
15867
+ livenessDiagnostics.push(` Launch ID: ${lease.launchId}`);
15868
+ livenessDiagnostics.push(` Stage: ${lease.currentStage}`);
15869
+ if (lease.currentIssue?.number) {
15870
+ livenessDiagnostics.push(
15871
+ ` Current issue: #${lease.currentIssue.number}`
15872
+ );
15873
+ }
15874
+ if (lease.logPath) {
15875
+ livenessDiagnostics.push(` Log path: ${lease.logPath}`);
15876
+ }
15877
+ }
15878
+ return {
15879
+ prdRef,
15880
+ status: outcome.status,
15881
+ attempted: [...outcome.resume.attempted],
15882
+ skipped: [...outcome.resume.skipped],
15883
+ resumed: [...outcome.resume.resumed],
15884
+ diagnostics: [...livenessDiagnostics, ...outcome.liveness.diagnostics]
15885
+ };
15886
+ }
15311
15887
  return {
15312
15888
  prdRef,
15313
15889
  status: outcome.status,
@@ -15361,6 +15937,44 @@ function runPrdRunStatusCommand(options) {
15361
15937
  ]
15362
15938
  };
15363
15939
  }
15940
+ if (record?.status === "starting" || record?.status === "running") {
15941
+ const liveness = classifyPrdRunLiveness({
15942
+ record,
15943
+ now: /* @__PURE__ */ new Date(),
15944
+ currentHost: hostname2()
15945
+ });
15946
+ const livenessDiagnostics = [`Liveness: ${liveness.status}`];
15947
+ if (liveness.lease) {
15948
+ const lease = liveness.lease;
15949
+ if (liveness.heartbeatAgeMs !== void 0) {
15950
+ livenessDiagnostics.push(
15951
+ ` Heartbeat age: ${liveness.heartbeatAgeMs}ms`
15952
+ );
15953
+ }
15954
+ livenessDiagnostics.push(` Host: ${lease.host}`);
15955
+ livenessDiagnostics.push(` PID: ${lease.pid}`);
15956
+ livenessDiagnostics.push(` Launch ID: ${lease.launchId}`);
15957
+ livenessDiagnostics.push(` Stage: ${lease.currentStage}`);
15958
+ if (lease.currentIssue?.number) {
15959
+ livenessDiagnostics.push(
15960
+ ` Current issue: #${lease.currentIssue.number}`
15961
+ );
15962
+ }
15963
+ if (lease.logPath) {
15964
+ livenessDiagnostics.push(` Log path: ${lease.logPath}`);
15965
+ }
15966
+ }
15967
+ return {
15968
+ prdRef,
15969
+ record,
15970
+ diagnostics: [
15971
+ ...diagnostics,
15972
+ ...livenessDiagnostics,
15973
+ ...liveness.diagnostics
15974
+ ],
15975
+ liveness
15976
+ };
15977
+ }
15364
15978
  return { prdRef, record, diagnostics };
15365
15979
  }
15366
15980
  function runPrdRunListCommand(options) {
@@ -15834,7 +16448,7 @@ import {
15834
16448
  rename,
15835
16449
  writeFile as writeFile2
15836
16450
  } from "fs/promises";
15837
- import { createHash, randomUUID } from "crypto";
16451
+ import { createHash, randomUUID as randomUUID2 } from "crypto";
15838
16452
  import path5 from "path";
15839
16453
  import { execFile as execFile2 } from "child_process";
15840
16454
  import { promisify as promisify2 } from "util";
@@ -15933,6 +16547,7 @@ var BASELINE_MANAGED_SKILLS = [
15933
16547
  "grill-with-docs",
15934
16548
  "handoff",
15935
16549
  "improve-codebase-architecture",
16550
+ "prd-run",
15936
16551
  "publish-prd",
15937
16552
  "secret-commit-guard",
15938
16553
  "security-review",
@@ -17890,7 +18505,7 @@ async function promptForInitChoices(plan, current) {
17890
18505
  };
17891
18506
  }
17892
18507
  async function writeFileAtomic(filePath, content) {
17893
- const tmpPath = `${filePath}.tmp.${randomUUID()}`;
18508
+ const tmpPath = `${filePath}.tmp.${randomUUID2()}`;
17894
18509
  await writeFile2(tmpPath, content, "utf-8");
17895
18510
  await rename(tmpPath, filePath);
17896
18511
  }
@@ -18551,7 +19166,7 @@ import { existsSync as existsSync21, readFileSync as readFileSync20 } from "fs";
18551
19166
  import { writeFile as writeFile3, rename as rename2 } from "fs/promises";
18552
19167
  import { resolve as resolve5, dirname as dirname6 } from "path";
18553
19168
  import { fileURLToPath as fileURLToPath3 } from "url";
18554
- import { randomUUID as randomUUID2 } from "crypto";
19169
+ import { randomUUID as randomUUID3 } from "crypto";
18555
19170
  import Ajv2 from "ajv";
18556
19171
  var __filename3 = fileURLToPath3(import.meta.url);
18557
19172
  var __dirname3 = dirname6(__filename3);
@@ -18828,7 +19443,7 @@ function checkPropertyExists(obj, instancePath, property) {
18828
19443
  return property in target;
18829
19444
  }
18830
19445
  async function writeFileAtomic2(filePath, content) {
18831
- const tmpPath = `${filePath}.tmp.${randomUUID2()}`;
19446
+ const tmpPath = `${filePath}.tmp.${randomUUID3()}`;
18832
19447
  await writeFile3(tmpPath, content, "utf-8");
18833
19448
  await rename2(tmpPath, filePath);
18834
19449
  }
@@ -22136,7 +22751,7 @@ function createCliProgram(version) {
22136
22751
  }
22137
22752
  }
22138
22753
  );
22139
- prdRun.command("launch").argument("<prdRef>", "PRD ref").requiredOption("--target <name>", "target name").option("--base <branch>", "override target baseBranch for this PRD Run").option("--cwd <path>", "target repository directory").option("--no-auto-merge", "skip auto-merge for manual review").option("--adopt-existing-branch", "adopt an existing PRD branch").action(
22754
+ prdRun.command("launch").argument("<prdRef>", "PRD ref").requiredOption("--target <name>", "target name").option("--base <branch>", "override target baseBranch for this PRD Run").option("--cwd <path>", "target repository directory").option("--no-auto-merge", "skip auto-merge for manual review").option("--adopt-existing-branch", "adopt an existing PRD branch").option("--quiet", "suppress detailed launch log output on stdout").action(
22140
22755
  async (prdRef, options) => {
22141
22756
  const targetRepoRoot = options.cwd ? repoRoot(options.cwd) : repoRoot();
22142
22757
  const normalizedPrdRef = normalizePrdRef2(prdRef);
@@ -22146,7 +22761,7 @@ function createCliProgram(version) {
22146
22761
  "logs",
22147
22762
  "prd-run-launch.log"
22148
22763
  );
22149
- const logger = createLogger("pourkit", logPath);
22764
+ const logger = options.quiet ? createLogger("pourkit", logPath, { quiet: true }) : createLogger("pourkit", logPath);
22150
22765
  try {
22151
22766
  const config = await loadRepoConfig(targetRepoRoot);
22152
22767
  resolveTarget(config, options.target);
@@ -22158,6 +22773,29 @@ function createCliProgram(version) {
22158
22773
  });
22159
22774
  const prProvider = new GitHubPRProvider(client, logger);
22160
22775
  const executionProvider = new SandcastleExecutionProvider();
22776
+ const launchArgv = [
22777
+ "pourkit",
22778
+ "prd-run",
22779
+ "launch",
22780
+ normalizedPrdRef,
22781
+ "--target",
22782
+ options.target
22783
+ ];
22784
+ if (options.base) {
22785
+ launchArgv.push("--base", options.base);
22786
+ }
22787
+ if (options.quiet) {
22788
+ launchArgv.push("--quiet");
22789
+ }
22790
+ if (options.autoMerge === false) {
22791
+ launchArgv.push("--no-auto-merge");
22792
+ }
22793
+ if (options.adoptExistingBranch) {
22794
+ launchArgv.push("--adopt-existing-branch");
22795
+ }
22796
+ if (options.cwd) {
22797
+ launchArgv.push("--cwd", options.cwd);
22798
+ }
22161
22799
  const result = await runPrdRunLaunchCommand({
22162
22800
  repoRoot: targetRepoRoot,
22163
22801
  prdRef: normalizedPrdRef,
@@ -22165,6 +22803,9 @@ function createCliProgram(version) {
22165
22803
  baseBranchOverride: options.base,
22166
22804
  autoMerge: options.autoMerge ?? true,
22167
22805
  adoptExistingBranch: options.adoptExistingBranch ?? false,
22806
+ quiet: options.quiet ?? false,
22807
+ cwd: options.cwd,
22808
+ originalArgv: launchArgv,
22168
22809
  config,
22169
22810
  issueProvider,
22170
22811
  prProvider,
@@ -22437,11 +23078,11 @@ function createCliProgram(version) {
22437
23078
  return program;
22438
23079
  }
22439
23080
  async function resolveCliVersion() {
22440
- if (isPackageVersion("0.0.0-next-20260724200221")) {
22441
- return "0.0.0-next-20260724200221";
23081
+ if (isPackageVersion("0.0.0-next-20260727021939")) {
23082
+ return "0.0.0-next-20260727021939";
22442
23083
  }
22443
- if (isReleaseVersion("0.0.0-next-20260724200221")) {
22444
- return "0.0.0-next-20260724200221";
23084
+ if (isReleaseVersion("0.0.0-next-20260727021939")) {
23085
+ return "0.0.0-next-20260727021939";
22445
23086
  }
22446
23087
  try {
22447
23088
  const root = repoRoot();