@h-rig/cli 0.0.6-alpha.63 → 0.0.6-alpha.65

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.
Files changed (46) hide show
  1. package/dist/bin/rig.js +1349 -1599
  2. package/dist/src/commands/_connection-state.js +14 -5
  3. package/dist/src/commands/_doctor-checks.js +71 -11
  4. package/dist/src/commands/_help-catalog.js +99 -60
  5. package/dist/src/commands/_json-output.js +56 -0
  6. package/dist/src/commands/_operator-view.js +97 -784
  7. package/dist/src/commands/_parsers.js +18 -9
  8. package/dist/src/commands/_pi-frontend.js +96 -788
  9. package/dist/src/commands/_policy.js +12 -3
  10. package/dist/src/commands/_preflight.js +73 -13
  11. package/dist/src/commands/_run-driver-helpers.js +31 -5
  12. package/dist/src/commands/_run-replay.js +2 -2
  13. package/dist/src/commands/_server-client.js +76 -16
  14. package/dist/src/commands/_snapshot-upload.js +70 -10
  15. package/dist/src/commands/agent.js +21 -11
  16. package/dist/src/commands/browser.js +24 -15
  17. package/dist/src/commands/connect.js +22 -17
  18. package/dist/src/commands/dist.js +15 -6
  19. package/dist/src/commands/doctor.js +70 -10
  20. package/dist/src/commands/github.js +79 -19
  21. package/dist/src/commands/inbox.js +215 -131
  22. package/dist/src/commands/init.js +202 -35
  23. package/dist/src/commands/inspect.js +77 -17
  24. package/dist/src/commands/inspector.js +18 -9
  25. package/dist/src/commands/pi.js +18 -9
  26. package/dist/src/commands/plugin.js +19 -10
  27. package/dist/src/commands/profile-and-review.js +22 -13
  28. package/dist/src/commands/queue.js +19 -9
  29. package/dist/src/commands/remote.js +25 -16
  30. package/dist/src/commands/repo-git-harness.js +18 -9
  31. package/dist/src/commands/run.js +158 -805
  32. package/dist/src/commands/server.js +85 -25
  33. package/dist/src/commands/setup.js +73 -13
  34. package/dist/src/commands/stats.js +681 -0
  35. package/dist/src/commands/task-report-bug.js +24 -15
  36. package/dist/src/commands/task-run-driver.js +121 -49
  37. package/dist/src/commands/task.js +254 -893
  38. package/dist/src/commands/test.js +12 -3
  39. package/dist/src/commands/workspace.js +14 -5
  40. package/dist/src/commands.js +1339 -1650
  41. package/dist/src/index.js +1349 -1599
  42. package/dist/src/launcher.js +72 -10
  43. package/dist/src/runner.js +14 -5
  44. package/package.json +10 -7
  45. package/dist/src/commands/_pi-remote-session.js +0 -771
  46. package/dist/src/commands/_pi-worker-bridge-extension.js +0 -834
@@ -7,10 +7,19 @@ import pc from "picocolors";
7
7
 
8
8
  // packages/cli/src/runner.ts
9
9
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
10
- import { CliError } from "@rig/runtime/control-plane/errors";
10
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
11
11
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
12
12
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
13
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
13
+
14
+ class CliError extends RuntimeCliError {
15
+ hint;
16
+ constructor(message, exitCode = 1, options = {}) {
17
+ super(message, exitCode);
18
+ if (options.hint?.trim()) {
19
+ this.hint = options.hint.trim();
20
+ }
21
+ }
22
+ }
14
23
  function formatCommand(parts) {
15
24
  return parts.map((part) => /[^a-zA-Z0-9_./:-]/.test(part) ? JSON.stringify(part) : part).join(" ");
16
25
  }
@@ -34,7 +43,7 @@ function takeOption(args, option) {
34
43
  if (current === option) {
35
44
  const next = args[index + 1];
36
45
  if (!next || next.startsWith("-")) {
37
- throw new CliError(`Missing value for ${option}`);
46
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
38
47
  }
39
48
  value = next;
40
49
  index += 1;
@@ -342,7 +351,7 @@ async function executeTaskReportBug(context, args) {
342
351
  const noBrowserFlag = takeFlag(pending, "--no-browser");
343
352
  pending = noBrowserFlag.rest;
344
353
  if (browserFlag.value && noBrowserFlag.value) {
345
- throw new CliError2("Pass only one of --browser or --no-browser.");
354
+ throw new CliError("Pass only one of --browser or --no-browser.", 1, { hint: "Re-run `rig task report-bug` with just one of the flags." });
346
355
  }
347
356
  const overwriteFlag = takeFlag(pending, "--overwrite");
348
357
  pending = overwriteFlag.rest;
@@ -426,7 +435,7 @@ async function executeTaskReportBug(context, args) {
426
435
  const shouldPrompt = !noPromptFlag.value;
427
436
  if (shouldPrompt) {
428
437
  if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
429
- throw new CliError2("Interactive bug reporting requires a TTY in text mode. Pass --no-prompt with flags for automation.");
438
+ throw new CliError("Interactive bug reporting requires a TTY in text mode. Pass --no-prompt with flags for automation.");
430
439
  }
431
440
  draft = await promptForBugReportDetails(draft);
432
441
  }
@@ -530,7 +539,7 @@ function validateBugReportInputFiles(input) {
530
539
  for (const { path, label } of files.map((item) => ({ ...item, path: item.path.trim() })).filter((item) => item.path)) {
531
540
  const source = resolve3(input.projectRoot, path);
532
541
  if (!existsSync2(source)) {
533
- throw new CliError2(`${label} does not exist: ${source}`);
542
+ throw new CliError(`${label} does not exist: ${source}`, 1, { hint: "Check the path and re-run `rig task report-bug` with an existing file." });
534
543
  }
535
544
  }
536
545
  }
@@ -570,7 +579,7 @@ function createBugReportBeadsTask(monorepoRoot, input, draft) {
570
579
  const issue = tryRunBrJson(monorepoRoot, command);
571
580
  if (!issue.ok) {
572
581
  if (!isRecoverableBeadsJsonlFailure(issue.error)) {
573
- throw new CliError2(issue.error);
582
+ throw new CliError(issue.error);
574
583
  }
575
584
  return {
576
585
  issueId: generateBugReportIssueId(monorepoRoot, input.title),
@@ -581,7 +590,7 @@ function createBugReportBeadsTask(monorepoRoot, input, draft) {
581
590
  }
582
591
  const issueId = issue.value.id?.trim();
583
592
  if (!issueId) {
584
- throw new CliError2("br create did not return an issue id.");
593
+ throw new CliError("br create did not return an issue id.", 1, { hint: "Re-run `rig task report-bug`; if it persists, file the bug via your task source directly." });
585
594
  }
586
595
  return {
587
596
  issueId,
@@ -688,12 +697,12 @@ ${serializedEntry}
688
697
  }
689
698
  const insertAt = raw.lastIndexOf("}");
690
699
  if (insertAt < 0) {
691
- throw new CliError2(`Invalid task config JSON object: ${taskConfigPath}`);
700
+ throw new CliError(`Invalid task config JSON object: ${taskConfigPath}`, 1, { hint: "Fix or reset that file (it must be a JSON object), then re-run `rig task report-bug`." });
692
701
  }
693
702
  const before = raw.slice(0, insertAt).replace(/\s*$/, "");
694
703
  const after = raw.slice(insertAt + 1).trim();
695
704
  if (after) {
696
- throw new CliError2(`Invalid trailing content in task config: ${taskConfigPath}`);
705
+ throw new CliError(`Invalid trailing content in task config: ${taskConfigPath}`, 1, { hint: "Remove the trailing content so the file is a single JSON object, then retry." });
697
706
  }
698
707
  const comma = before.trim() === "{" ? "" : ",";
699
708
  writeFileSync2(taskConfigPath, `${before}${comma}
@@ -761,7 +770,7 @@ function generateBugReportIssueId(monorepoRoot, title) {
761
770
  return id;
762
771
  }
763
772
  }
764
- throw new CliError2("Could not generate a unique bug task id.");
773
+ throw new CliError("Could not generate a unique bug task id.", 1, { hint: "Re-run `rig task report-bug`; pass an explicit title to vary the generated id." });
765
774
  }
766
775
  function isRecoverableBeadsJsonlFailure(message) {
767
776
  return /Invalid JSON|missing field|JSONL is newer than DB|stale database/i.test(message);
@@ -769,7 +778,7 @@ function isRecoverableBeadsJsonlFailure(message) {
769
778
  function runBrJson(cwd, command) {
770
779
  const result = tryRunBrJson(cwd, command);
771
780
  if (!result.ok) {
772
- throw new CliError2(result.error);
781
+ throw new CliError(result.error);
773
782
  }
774
783
  return result.value;
775
784
  }
@@ -967,7 +976,7 @@ async function promptBugSelect(message, options, initialValue) {
967
976
  function unwrapClackPrompt(result) {
968
977
  if (clack.isCancel(result)) {
969
978
  clack.cancel("Bug report cancelled.");
970
- throw new CliError2("Bug report cancelled by user.");
979
+ throw new CliError("Bug report cancelled by user.");
971
980
  }
972
981
  return result;
973
982
  }
@@ -977,7 +986,7 @@ function normalizeBugEnvironmentChoice(environment) {
977
986
  function requireReportBugValue(value, option) {
978
987
  const normalized = value?.trim();
979
988
  if (!normalized) {
980
- throw new CliError2(`Missing ${option}. Run interactively or pass ${option} <value>.`);
989
+ throw new CliError(`Missing ${option}. Run interactively or pass ${option} <value>.`);
981
990
  }
982
991
  return normalized;
983
992
  }
@@ -1058,7 +1067,7 @@ function takeRepeatedOption(args, option) {
1058
1067
  if (current === option) {
1059
1068
  const next = args[index + 1];
1060
1069
  if (!next || next.startsWith("-")) {
1061
- throw new CliError2(`Missing value for ${option}`);
1070
+ throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
1062
1071
  }
1063
1072
  values.push(next);
1064
1073
  index += 1;
@@ -7,10 +7,19 @@ import { createInterface as createLineInterface } from "readline";
7
7
 
8
8
  // packages/cli/src/runner.ts
9
9
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
10
- import { CliError } from "@rig/runtime/control-plane/errors";
10
+ import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
11
11
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
12
12
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
13
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
13
+
14
+ class CliError extends RuntimeCliError {
15
+ hint;
16
+ constructor(message, exitCode = 1, options = {}) {
17
+ super(message, exitCode);
18
+ if (options.hint?.trim()) {
19
+ this.hint = options.hint.trim();
20
+ }
21
+ }
22
+ }
14
23
  function formatCommand(parts) {
15
24
  return parts.map((part) => /[^a-zA-Z0-9_./:-]/.test(part) ? JSON.stringify(part) : part).join(" ");
16
25
  }
@@ -52,12 +61,13 @@ import {
52
61
  import { readFileSync } from "fs";
53
62
  import { resolve as resolve3 } from "path";
54
63
  import {
55
- appendRunLifecycleEvent,
64
+ appendRunJournalEvent,
56
65
  appendRunLogEntry,
57
66
  appendRunTimelineEntry,
58
67
  patchAuthorityRunRecord,
59
68
  readAuthorityRun as readAuthorityRun2,
60
69
  resolveAuthorityRunDir,
70
+ setAuthorityRunStatus,
61
71
  writeJsonFile
62
72
  } from "@rig/runtime/control-plane/authority-files";
63
73
  import { taskLookup } from "@rig/runtime/control-plane/native/task-ops";
@@ -177,7 +187,17 @@ function upsertAgentAuthorityRun(projectRoot, input) {
177
187
  function patchAuthorityRun(projectRoot, runId, patch) {
178
188
  const next = patchAuthorityRunRecord(projectRoot, runId, patch);
179
189
  if (!next) {
180
- throw new CliError2(`Run not found: ${runId}`, 2);
190
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
191
+ }
192
+ return next;
193
+ }
194
+ function setRunStatusOrFail(projectRoot, runId, to, options = {}) {
195
+ const next = setAuthorityRunStatus(projectRoot, runId, to, {
196
+ ...options,
197
+ actor: { kind: "agent" }
198
+ });
199
+ if (!next) {
200
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
181
201
  }
182
202
  return next;
183
203
  }
@@ -282,7 +302,12 @@ function emitServerRunEvent(event) {
282
302
  console.log(`__RIG_RUN_EVENT__${JSON.stringify({ ...event, at: new Date().toISOString() })}`);
283
303
  if (runEventSinkProjectRoot) {
284
304
  try {
285
- appendRunLifecycleEvent(runEventSinkProjectRoot, event.runId, { ...event });
305
+ if (event.type === "status" || event.type === "completed" || event.type === "failed") {
306
+ appendRunJournalEvent(runEventSinkProjectRoot, event.runId, {
307
+ type: "timeline-entry",
308
+ payload: { kind: "driver-event", ...event }
309
+ });
310
+ }
286
311
  } catch {}
287
312
  ringServerRunDoorbell(event.runId);
288
313
  }
@@ -441,7 +466,7 @@ function readJsonFile(path) {
441
466
  try {
442
467
  return JSON.parse(readFileSync2(path, "utf8"));
443
468
  } catch (error) {
444
- throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
469
+ throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
445
470
  }
446
471
  }
447
472
  function writeJsonFile2(path, value) {
@@ -505,7 +530,7 @@ function resolveSelectedConnection(projectRoot, options = {}) {
505
530
  const global = readGlobalConnections(options);
506
531
  const connection = global.connections[repo.selected];
507
532
  if (!connection) {
508
- throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
533
+ throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
509
534
  }
510
535
  return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
511
536
  }
@@ -578,7 +603,7 @@ async function ensureServerForCli(projectRoot) {
578
603
  };
579
604
  } catch (error) {
580
605
  if (error instanceof Error) {
581
- throw new CliError2(error.message, 1);
606
+ throw new CliError(error.message, 1);
582
607
  }
583
608
  throw error;
584
609
  }
@@ -628,15 +653,64 @@ function diagnosticMessage(payload) {
628
653
  });
629
654
  return messages.length > 0 ? messages.join("; ") : null;
630
655
  }
656
+ var serverReachabilityCache = new Map;
657
+ async function probeServerReachability(baseUrl, authToken) {
658
+ try {
659
+ const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
660
+ headers: mergeHeaders(undefined, authToken),
661
+ signal: AbortSignal.timeout(1500)
662
+ });
663
+ return response.ok;
664
+ } catch {
665
+ return false;
666
+ }
667
+ }
668
+ function cachedServerReachability(projectRoot, baseUrl, authToken) {
669
+ const key = resolve5(projectRoot);
670
+ const cached = serverReachabilityCache.get(key);
671
+ if (cached)
672
+ return cached;
673
+ const probe = probeServerReachability(baseUrl, authToken);
674
+ serverReachabilityCache.set(key, probe);
675
+ return probe;
676
+ }
677
+ function describeSelectedServer(projectRoot, server) {
678
+ try {
679
+ const selected = resolveSelectedConnection(projectRoot);
680
+ if (selected) {
681
+ return {
682
+ alias: selected.alias,
683
+ target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
684
+ };
685
+ }
686
+ } catch {}
687
+ return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
688
+ }
689
+ async function buildServerFailureContext(projectRoot, server) {
690
+ const { alias, target } = describeSelectedServer(projectRoot, server);
691
+ const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
692
+ const reachability = reachable ? "server is reachable" : "server is unreachable";
693
+ return {
694
+ contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
695
+ hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
696
+ };
697
+ }
631
698
  async function requestServerJson(context, pathname, init = {}) {
632
699
  const server = await ensureServerForCli(context.projectRoot);
633
700
  const headers = mergeHeaders(init.headers, server.authToken);
634
701
  if (server.serverProjectRoot)
635
702
  headers.set("x-rig-project-root", server.serverProjectRoot);
636
- const response = await fetch(`${server.baseUrl}${pathname}`, {
637
- ...init,
638
- headers
639
- });
703
+ let response;
704
+ try {
705
+ response = await fetch(`${server.baseUrl}${pathname}`, {
706
+ ...init,
707
+ headers
708
+ });
709
+ } catch (error) {
710
+ const failure = await buildServerFailureContext(context.projectRoot, server);
711
+ throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
712
+ ${failure.contextLine}`, 1, { hint: failure.hint });
713
+ }
640
714
  const text = await response.text();
641
715
  const payload = text.trim().length > 0 ? (() => {
642
716
  try {
@@ -648,7 +722,9 @@ async function requestServerJson(context, pathname, init = {}) {
648
722
  if (!response.ok) {
649
723
  const diagnostics = diagnosticMessage(payload);
650
724
  const detail = diagnostics ?? (text || response.statusText);
651
- throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
725
+ const failure = await buildServerFailureContext(context.projectRoot, server);
726
+ throw new CliError(`Rig server request failed (${response.status}): ${detail}
727
+ ${failure.contextLine}`, 1, { hint: failure.hint });
652
728
  }
653
729
  return payload;
654
730
  }
@@ -1164,7 +1240,7 @@ function parsePositiveInt(value, option, fallback) {
1164
1240
  }
1165
1241
  const parsed = Number.parseInt(value, 10);
1166
1242
  if (!Number.isFinite(parsed) || parsed <= 0) {
1167
- throw new CliError2(`Invalid ${option} value: ${value}`);
1243
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
1168
1244
  }
1169
1245
  return parsed;
1170
1246
  }
@@ -1581,8 +1657,8 @@ async function executeRigOwnedTaskRun(context, input) {
1581
1657
  runtimeAdapter: input.runtimeAdapter,
1582
1658
  status: resumeMode ? "preparing" : "created"
1583
1659
  });
1660
+ setRunStatusOrFail(context.projectRoot, input.runId, "preparing", { reason: "driver-start" });
1584
1661
  patchAuthorityRun(context.projectRoot, input.runId, {
1585
- status: "preparing",
1586
1662
  startedAt,
1587
1663
  completedAt: null,
1588
1664
  errorText: null,
@@ -1720,7 +1796,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1720
1796
  if (verificationStarted)
1721
1797
  return;
1722
1798
  verificationStarted = true;
1723
- patchAuthorityRun(context.projectRoot, input.runId, { status: "validating" });
1799
+ setRunStatusOrFail(context.projectRoot, input.runId, "validating");
1724
1800
  emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null, ...sessionAnchor() });
1725
1801
  verificationAction = startRunAction(context.projectRoot, input.runId, {
1726
1802
  actionId: `action:${input.runId}:completion-verification`,
@@ -1733,7 +1809,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1733
1809
  if (reviewStarted)
1734
1810
  return;
1735
1811
  reviewStarted = true;
1736
- patchAuthorityRun(context.projectRoot, input.runId, { status: "reviewing" });
1812
+ setRunStatusOrFail(context.projectRoot, input.runId, "reviewing");
1737
1813
  emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null, ...sessionAnchor() });
1738
1814
  reviewAction = startRunAction(context.projectRoot, input.runId, {
1739
1815
  actionId: `action:${input.runId}:review-closeout`,
@@ -1767,8 +1843,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1767
1843
  logsDir: latestLogsDir,
1768
1844
  contextFile: payload.contextFile ?? null
1769
1845
  });
1846
+ setRunStatusOrFail(context.projectRoot, input.runId, "running");
1770
1847
  patchAuthorityRun(context.projectRoot, input.runId, {
1771
- status: "running",
1772
1848
  worktreePath: latestRuntimeWorkspace,
1773
1849
  artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
1774
1850
  logRoot: latestLogsDir,
@@ -2258,7 +2334,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
2258
2334
  }
2259
2335
  process.off("SIGTERM", forwardSigterm);
2260
2336
  if (attemptExit.error) {
2261
- throw new CliError2(`Task run failed to start: ${attemptExit.error.message}`, 1);
2337
+ throw new CliError(`Task run failed to start: ${attemptExit.error.message}`, 1);
2262
2338
  }
2263
2339
  const retryContext = resolveTaskRunRetryContext({
2264
2340
  projectRoot: context.projectRoot,
@@ -2280,8 +2356,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
2280
2356
  currentPrompt = [retryContext.nextPrompt, "", "Rig Pi steering message:", retrySteering].join(`
2281
2357
  `);
2282
2358
  reviewFailureDetail = failureDetail;
2359
+ setRunStatusOrFail(context.projectRoot, input.runId, "validating", { reason: "validation-retry" });
2283
2360
  patchAuthorityRun(context.projectRoot, input.runId, {
2284
- status: "validating",
2285
2361
  completedAt: null,
2286
2362
  errorText: null
2287
2363
  });
@@ -2317,10 +2393,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
2317
2393
  break;
2318
2394
  }
2319
2395
  if (!exit) {
2320
- throw new CliError2(`Task run for ${runtimeTaskId} exhausted ${maxAttempts} attempts without a terminal result.`, 1);
2396
+ throw new CliError(`Task run for ${runtimeTaskId} exhausted ${maxAttempts} attempts without a terminal result.`, 1);
2321
2397
  }
2322
2398
  if (exit.error) {
2323
- throw new CliError2(`Task run failed to start: ${exit.error.message}`, 1);
2399
+ throw new CliError(`Task run failed to start: ${exit.error.message}`, 1);
2324
2400
  }
2325
2401
  if (assistantText.trim()) {
2326
2402
  appendRunTimeline(context.projectRoot, input.runId, {
@@ -2347,9 +2423,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
2347
2423
  reviewAction.fail(failureDetail);
2348
2424
  }
2349
2425
  let terminalFailureDetail = failureDetail;
2350
- const terminalRunStatus = reviewFailureDetail ? "needs_attention" : "failed";
2426
+ const terminalRunStatus = reviewFailureDetail ? "needs-attention" : "failed";
2351
2427
  try {
2352
- await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "needs_attention" ? "Rig task run needs attention." : "Rig task run failed.", {
2428
+ await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "needs-attention" ? "Rig task run needs attention." : "Rig task run failed.", {
2353
2429
  latestRuntimeWorkspace,
2354
2430
  latestLogsDir,
2355
2431
  latestSessionDir,
@@ -2359,14 +2435,14 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
2359
2435
  terminalFailureDetail = `${failureDetail}
2360
2436
  Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${error instanceof Error ? error.message : String(error)}`;
2361
2437
  }
2362
- patchAuthorityRun(context.projectRoot, input.runId, {
2363
- status: terminalRunStatus,
2364
- completedAt: failedAt,
2438
+ setRunStatusOrFail(context.projectRoot, input.runId, terminalRunStatus, {
2439
+ reason: "driver-terminal-failure",
2365
2440
  errorText: terminalFailureDetail
2366
2441
  });
2442
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
2367
2443
  appendRunLog(context.projectRoot, input.runId, {
2368
2444
  id: `log:${input.runId}:${terminalRunStatus}`,
2369
- title: terminalRunStatus === "needs_attention" ? "Task run needs attention" : "Task run failed",
2445
+ title: terminalRunStatus === "needs-attention" ? "Task run needs attention" : "Task run failed",
2370
2446
  detail: terminalFailureDetail,
2371
2447
  tone: "error",
2372
2448
  status: terminalRunStatus,
@@ -2377,7 +2453,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2377
2453
  runId: input.runId,
2378
2454
  error: terminalFailureDetail
2379
2455
  });
2380
- throw new CliError2(terminalFailureDetail, exit.code ?? 1);
2456
+ throw new CliError(terminalFailureDetail, exit.code ?? 1);
2381
2457
  }
2382
2458
  if (planningClassification.planningRequired) {
2383
2459
  const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
@@ -2385,11 +2461,11 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2385
2461
  if (!existsSync4(expectedPlanPath)) {
2386
2462
  const failedAt = new Date().toISOString();
2387
2463
  const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
2388
- patchAuthorityRun(context.projectRoot, input.runId, {
2389
- status: "needs_attention",
2390
- completedAt: failedAt,
2464
+ setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
2465
+ reason: "plan-artifact-missing",
2391
2466
  errorText: failureDetail
2392
2467
  });
2468
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
2393
2469
  appendRunLog(context.projectRoot, input.runId, {
2394
2470
  id: `log:${input.runId}:plan-artifact-missing`,
2395
2471
  title: "Required plan artifact missing",
@@ -2399,7 +2475,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2399
2475
  createdAt: failedAt
2400
2476
  });
2401
2477
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
2402
- throw new CliError2(failureDetail, 1);
2478
+ throw new CliError(failureDetail, 1);
2403
2479
  }
2404
2480
  }
2405
2481
  if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
@@ -2420,14 +2496,13 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2420
2496
  reviewAction.complete("Provider work accepted; server-owned closeout requested.");
2421
2497
  }
2422
2498
  const requestedAt = new Date().toISOString();
2499
+ setRunStatusOrFail(context.projectRoot, input.runId, "closing-out", { reason: "server-closeout-requested" });
2423
2500
  patchAuthorityRun(context.projectRoot, input.runId, {
2424
- status: "reviewing",
2425
2501
  completedAt: null,
2426
2502
  errorText: null,
2427
2503
  serverCloseout: {
2428
2504
  status: "pending",
2429
2505
  phase: "queued",
2430
- requestedAt,
2431
2506
  updatedAt: requestedAt,
2432
2507
  runtimeWorkspace: latestRuntimeWorkspace,
2433
2508
  branch: latestRuntimeBranch,
@@ -2444,7 +2519,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2444
2519
  payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
2445
2520
  });
2446
2521
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
2447
- emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested.", ...sessionAnchor() });
2522
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "closing-out", detail: "Server-owned closeout requested.", ...sessionAnchor() });
2448
2523
  await context.emitEvent("command.finished", {
2449
2524
  command: [
2450
2525
  "rig",
@@ -2612,25 +2687,25 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2612
2687
  const failureDetail = postValidation.pr?.actionableFeedback.join(`
2613
2688
  `) || "PR automation needs attention.";
2614
2689
  const failedAt = new Date().toISOString();
2615
- patchAuthorityRun(context.projectRoot, input.runId, {
2616
- status: "needs_attention",
2617
- completedAt: failedAt,
2690
+ setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
2691
+ reason: "pr-automation-needs-attention",
2618
2692
  errorText: failureDetail
2619
2693
  });
2694
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
2620
2695
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
2621
- throw new CliError2(failureDetail, 1);
2696
+ throw new CliError(failureDetail, 1);
2622
2697
  }
2623
2698
  } catch (error) {
2624
- if (error instanceof CliError2) {
2699
+ if (error instanceof CliError) {
2625
2700
  throw error;
2626
2701
  }
2627
2702
  const failureDetail = `Failed to complete PR/issue closeout for ${input.taskId ?? runtimeTaskId}: ${error instanceof Error ? error.message : String(error)}`;
2628
2703
  const failedAt = new Date().toISOString();
2629
- patchAuthorityRun(context.projectRoot, input.runId, {
2630
- status: "failed",
2631
- completedAt: failedAt,
2704
+ setRunStatusOrFail(context.projectRoot, input.runId, "failed", {
2705
+ reason: "pr-closeout-failed",
2632
2706
  errorText: failureDetail
2633
2707
  });
2708
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
2634
2709
  appendRunLog(context.projectRoot, input.runId, {
2635
2710
  id: `log:${input.runId}:pr-closeout-failed`,
2636
2711
  title: "PR/issue closeout failed",
@@ -2640,7 +2715,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2640
2715
  createdAt: failedAt
2641
2716
  });
2642
2717
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
2643
- throw new CliError2(failureDetail, 1);
2718
+ throw new CliError(failureDetail, 1);
2644
2719
  }
2645
2720
  if (verificationAction && !reviewStarted) {
2646
2721
  verificationAction.complete("Completion verification checks finished.");
@@ -2651,10 +2726,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2651
2726
  if (reviewAction) {
2652
2727
  reviewAction.complete("Run marked completed.");
2653
2728
  }
2654
- patchAuthorityRun(context.projectRoot, input.runId, {
2655
- status: "completed",
2656
- completedAt: new Date().toISOString()
2657
- });
2729
+ setRunStatusOrFail(context.projectRoot, input.runId, "completed", { reason: "driver-completed" });
2658
2730
  emitServerRunEvent({ type: "completed", runId: input.runId });
2659
2731
  await context.emitEvent("command.finished", {
2660
2732
  command: [