@indigoai-us/hq-cli 5.294.0 → 5.296.0

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 (72) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/command-catalog.generated.d.ts +70 -9
  3. package/dist/command-catalog.generated.js +88 -12
  4. package/dist/commands/auth.d.ts +1 -0
  5. package/dist/commands/auth.js +76 -1
  6. package/dist/commands/core-job-native.d.ts +7 -0
  7. package/dist/commands/core-job-native.js +20 -0
  8. package/dist/commands/core-job-probe-native.d.ts +45 -0
  9. package/dist/commands/core-job-probe-native.js +564 -0
  10. package/dist/commands/core-job-remote-launcher.d.ts +7 -0
  11. package/dist/commands/core-job-remote-launcher.js +51 -0
  12. package/dist/commands/core-job-run-native.d.ts +47 -0
  13. package/dist/commands/core-job-run-native.js +1215 -0
  14. package/dist/commands/core.js +1 -1
  15. package/dist/commands/doctor.js +0 -74
  16. package/dist/commands/lanes-message.js +24 -5
  17. package/dist/commands/lanes-resume.js +1 -3
  18. package/dist/commands/lanes-shared.d.ts +1 -2
  19. package/dist/commands/lanes-stories.d.ts +1 -0
  20. package/dist/commands/lanes-stories.js +40 -1
  21. package/dist/commands/lanes.d.ts +1 -9
  22. package/dist/commands/lanes.js +197 -174
  23. package/dist/commands/run.js +5 -0
  24. package/dist/fast-scaffold.d.ts +0 -5
  25. package/dist/fast-scaffold.js +0 -10
  26. package/dist/index.js +1 -10
  27. package/dist/lib/core-utils/sentry-report.d.ts +0 -19
  28. package/dist/lib/core-utils/sentry-report.js +8 -61
  29. package/dist/lib/doctor/checks/claude-wiring.js +9 -0
  30. package/dist/lib/lanes/flag-keys.d.ts +2 -0
  31. package/dist/lib/lanes/flag-keys.js +2 -0
  32. package/dist/lib/lanes/manager-status-gate.d.ts +0 -17
  33. package/dist/lib/lanes/manager-status-gate.js +2 -43
  34. package/dist/lib/lanes/mesh-events.js +3 -0
  35. package/dist/lib/lanes/outpost-dispatch.d.ts +2 -0
  36. package/dist/lib/lanes/outpost-dispatch.js +7 -1
  37. package/dist/lib/lanes/outpost-hire-cache.d.ts +0 -1
  38. package/dist/lib/lanes/outpost-hire-cache.js +0 -1
  39. package/dist/lib/lanes/outpost-snapshot-memo.d.ts +6 -0
  40. package/dist/lib/lanes/outpost-snapshot-memo.js +14 -0
  41. package/dist/lib/lanes/reconcile.d.ts +2 -3
  42. package/dist/lib/lanes/reconcile.js +67 -57
  43. package/dist/lib/lanes/spawn.d.ts +0 -2
  44. package/dist/lib/lanes/spawn.js +57 -5
  45. package/dist/lib/lanes/store.js +54 -0
  46. package/dist/lib/lanes/stories.d.ts +7 -1
  47. package/dist/lib/lanes/stories.js +16 -0
  48. package/dist/lib/lanes/types.d.ts +30 -1
  49. package/dist/lib/lanes/types.js +2 -2
  50. package/dist/lib/lanes/watcher.d.ts +4 -3
  51. package/dist/lib/lanes/watcher.js +122 -56
  52. package/dist/lib/mesh/live/daemon/run.js +18 -1
  53. package/dist/run/env-graph-guard.d.ts +2 -2
  54. package/dist/run/env-graph-guard.js +5 -2
  55. package/dist/run/hq-plugin.js +6 -5
  56. package/dist/utils/cognito-session.d.ts +6 -0
  57. package/dist/utils/cognito-session.js +9 -0
  58. package/dist/utils/self-update.js +31 -0
  59. package/dist/utils/version-gate.js +22 -3
  60. package/package.json +2 -2
  61. package/dist/fast-lanes-list-route.d.ts +0 -8
  62. package/dist/fast-lanes-list-route.js +0 -32
  63. package/dist/fast-lanes-list.d.ts +0 -3
  64. package/dist/fast-lanes-list.js +0 -224
  65. package/dist/lib/doctor/client-health-sentry-flag.d.ts +0 -23
  66. package/dist/lib/doctor/client-health-sentry-flag.js +0 -99
  67. package/dist/lib/doctor/client-health-sentry.d.ts +0 -71
  68. package/dist/lib/doctor/client-health-sentry.js +0 -286
  69. package/dist/lib/lanes/outpost-snapshot-cache.d.ts +0 -26
  70. package/dist/lib/lanes/outpost-snapshot-cache.js +0 -215
  71. package/dist/lib/lanes/outpost-snapshot-refresh.d.ts +0 -21
  72. package/dist/lib/lanes/outpost-snapshot-refresh.js +0 -329
@@ -36,7 +36,7 @@ import { registerCoreHandoffCommand } from "./core-handoff.js";
36
36
  import { registerCoreOntologyCommand } from "./core-ontology.js";
37
37
  import { registerCoreKnowledgePrefsCommand } from "./core-knowledge-prefs.js";
38
38
  import { registerCorePolicyCommand } from "./core-policy.js";
39
- import { registerCoreJobsNativeCommand } from "./core-jobs-native.js";
39
+ import { registerCoreJobsNativeCommand } from "./core-job-native.js";
40
40
  import { registerCoreCheckpointGateSettingCommand } from "./core-checkpoint-gate-setting.js";
41
41
  import { resolveLiveRoot } from "../utils/hq-roots.js";
42
42
  import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
@@ -33,9 +33,6 @@ import { PARITY_FAMILY_ID, PARITY_FAMILY_TITLE, runParityReplay, } from "../lib/
33
33
  import { applyFixes } from "../lib/doctor/fix/apply.js";
34
34
  import { renderDoctorFixJson } from "../lib/doctor/fix/json-output.js";
35
35
  import { clientHealthReasonCodesEnabled as isActiveCompanyClientHealthReasonCodesEnabled, clientHealthReasonCodesEnabledForCompany, } from "../lib/doctor/client-health-reason-codes-flag.js";
36
- import { CLIENT_HEALTH_SENTRY_MAX_INPUT_BYTES, CLIENT_HEALTH_SENTRY_NOT_HANDLED_EXIT_CODE, handleClientHealthSentryReport, } from "../lib/doctor/client-health-sentry.js";
37
- import { clientHealthSentryDirectEnabled, clientHealthSentryDirectEnabledForCompany, } from "../lib/doctor/client-health-sentry-flag.js";
38
- import { reportClientHealthEvents } from "../lib/core-utils/sentry-report.js";
39
36
  import { isCompanyJournalSlug } from "../lib/doctor/checks/sync-health.js";
40
37
  import { readCompaniesManifestMap } from "../lib/work-context/repo-remote.js";
41
38
  import * as readline from "node:readline";
@@ -244,26 +241,6 @@ function createClientHealthReasonCodeCompanyResolver(hqRoot) {
244
241
  });
245
242
  };
246
243
  }
247
- function createClientHealthSentryCompanyResolver(hqRoot) {
248
- const companies = readCompaniesManifestMap(hqRoot);
249
- return async (companySlug) => {
250
- if (companySlug === null)
251
- return clientHealthSentryDirectEnabled();
252
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(companySlug))
253
- return false;
254
- const manifestUid = companies?.[companySlug]?.cloud_uid;
255
- const activeSlug = process.env.HQ_COMPANY_SLUG?.trim() || "";
256
- const activeUid = process.env.HQ_COMPANY_UID?.trim() || "";
257
- const companyUid = typeof manifestUid === "string" && manifestUid.trim()
258
- ? manifestUid.trim()
259
- : companySlug === activeSlug
260
- ? activeUid
261
- : "";
262
- if (!/^cmp_[A-Za-z0-9]{3,128}$/.test(companyUid))
263
- return false;
264
- return clientHealthSentryDirectEnabledForCompany({ companyUid, companySlug });
265
- };
266
- }
267
244
  /** Register the top-level `hq doctor` command so it appears in `hq --help`. */
268
245
  export function registerDoctorCommand(program) {
269
246
  program
@@ -279,9 +256,6 @@ export function registerDoctorCommand(program) {
279
256
  .option("--only <families>", "Run only these comma-separated check families (e.g. hooks). Unknown names are rejected; --json then records the scope.")
280
257
  .option("--fix", "Apply the allowlisted safe repairs (backs up first; read-only without this flag).")
281
258
  .addOption(new Option("--client-health-report", "Resolve the opt-in hq-flags gate used by the client-health report hook.").hideHelp())
282
- .addOption(new Option("--client-health-sentry", "Send the opt-in client-health Sentry report from a doctor JSON document.").hideHelp())
283
- .addOption(new Option("--json-input <file>", "Doctor JSON input for the client-health report.").hideHelp())
284
- .addOption(new Option("--client-health-fix-outcome <outcome>", "Safe fix outcome label for the client-health report.").hideHelp())
285
259
  .option("--yes", "Skip the interactive --fix confirmation (non-interactive use).")
286
260
  .option("--force", "Let --fix run despite uncommitted changes under .claude/, .codex/, or .grok/.")
287
261
  .action(async (opts) => {
@@ -298,54 +272,6 @@ export function registerDoctorCommand(program) {
298
272
  process.exitCode = 1;
299
273
  return;
300
274
  }
301
- if (opts.clientHealthSentry === true) {
302
- const hqRoot = resolveHqRoot();
303
- if (!hqRoot || !opts.jsonInput) {
304
- process.stderr.write("hq doctor --client-health-sentry requires an HQ root and --json-input.\n");
305
- process.exitCode = 1;
306
- return;
307
- }
308
- try {
309
- const workspacePath = path.join(hqRoot, "workspace");
310
- const workspaceStat = fs.lstatSync(workspacePath);
311
- if (!workspaceStat.isDirectory() || workspaceStat.isSymbolicLink()) {
312
- throw new Error("workspace is not a local directory");
313
- }
314
- const statePath = path.join(hqRoot, "workspace", ".hq-client-health");
315
- const stateStat = fs.lstatSync(statePath);
316
- if (!stateStat.isDirectory() || stateStat.isSymbolicLink()) {
317
- throw new Error("client-health state is not a local directory");
318
- }
319
- const stateRoot = fs.realpathSync(statePath);
320
- const inputPath = fs.realpathSync(opts.jsonInput);
321
- const relativeInput = path.relative(stateRoot, inputPath);
322
- if (relativeInput === ".." || relativeInput.startsWith(`..${path.sep}`) || path.isAbsolute(relativeInput)) {
323
- throw new Error("input path is outside client-health state");
324
- }
325
- if (fs.statSync(inputPath).size > CLIENT_HEALTH_SENTRY_MAX_INPUT_BYTES) {
326
- throw new Error("input exceeds client-health size limit");
327
- }
328
- const result = await handleClientHealthSentryReport({
329
- hqRoot,
330
- doctorJson: fs.readFileSync(inputPath, "utf8"),
331
- fixOutcome: opts.clientHealthFixOutcome ?? "not_auto_fixable",
332
- }, {
333
- resolveFlagForCompany: createClientHealthSentryCompanyResolver(hqRoot),
334
- sendEvents: async (events) => (await reportClientHealthEvents(events)).code === 0,
335
- });
336
- process.exitCode =
337
- result === "not_handled"
338
- ? CLIENT_HEALTH_SENTRY_NOT_HANDLED_EXIT_CODE
339
- : result === "send_failed"
340
- ? 1
341
- : 0;
342
- }
343
- catch {
344
- process.stderr.write("hq doctor --client-health-sentry could not handle the report.\n");
345
- process.exitCode = 1;
346
- }
347
- return;
348
- }
349
275
  // `--fix` is the only write path. It resolves the tree, applies the
350
276
  // allowlisted repairs behind a backup + confirmation, and returns its own
351
277
  // exit code; the read-only report below never runs in this branch.
@@ -10,12 +10,11 @@ import { isPidAlive, runWatcherLoop, WATCHER_OUT_MAX_BYTES, watcherOutLogPath, w
10
10
  import { laneStories, laneStory, storyWarning } from "../lib/lanes/stories.js";
11
11
  import { laneRunDir, probePidRecord, readPidRecord, readProcStarttime, spawnLaneWorker } from "../lib/lanes/spawn.js";
12
12
  import { keepAliveExclusion, keepAliveSupersededBy, LANE_RUNNING_SUPERSEDED_MESSAGE_DELIVERY_FLAG, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds } from "../lib/lanes/keep-alive.js";
13
- import { managerStatusGateEnabledForLane } from "../lib/lanes/manager-status-gate.js";
14
13
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
15
14
  import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
16
15
  import { accountMoveSummary, accountRequestKeepsCurrentLane, assertCurrentResumeAccountAvailable, callerMatchesSessionSenior, recordedResumeEffort, resetKeepAliveProgress, resolveResumeEffort } from "./lanes-shared.js";
17
16
  export function createLaneMessageHandlers(deps) {
18
- const { archiveResumeMessage, assertLaneRunsHereForAdmission, assertLaneRunsHereForOperatorMutation, assessLaneAdmission, casUpdateLane, dispatchOutpostLaneCommand, drainHookStatusFor, laneIsHostedHere, laneWithRecoveredProviderSession, loadAllLanes, mintMessageId, persistClaudeAccountSelection, prepareAccountMove, printJson, recordResumeSetup, resolveActiveQuestionCompany, resolveResumeProfile, selectAutomaticClaudeResumeAccount, validateResumeProfileModel, withResumeHireAdmission } = deps;
17
+ const { archiveResumeMessage, assertLaneRunsHereForAdmission, assertLaneRunsHereForOperatorMutation, assessLaneAdmission, casUpdateLane, dispatchOutpostLaneCommand, dispatchPrimaryLaneMessage, drainHookStatusFor, laneIsHostedHere, laneWithRecoveredProviderSession, loadAllLanes, mintMessageId, persistClaudeAccountSelection, prepareAccountMove, printJson, recordResumeSetup, resolveActiveQuestionCompany, resolveResumeProfile, selectAutomaticClaudeResumeAccount, validateResumeProfileModel, withResumeHireAdmission } = deps;
19
18
  function currentSuccessorOf(lane, lanes, lineageAware, companyForLane) {
20
19
  const validLanes = lanes.filter((candidate) => Number.isFinite(Date.parse(candidate.created_at)));
21
20
  const newestFirst = [...validLanes].sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at));
@@ -221,7 +220,27 @@ export function createLaneMessageHandlers(deps) {
221
220
  if (!text) {
222
221
  throw new LanesError("missing_text", "--text is required", {});
223
222
  }
224
- const resolution = await deps.resolveCommandLane(ctx, laneId);
223
+ let resolution;
224
+ try {
225
+ resolution = await deps.resolveCommandLane(ctx, laneId);
226
+ }
227
+ catch (err) {
228
+ const identity = ctx.readOutpostIdentity();
229
+ const failedOutpostIds = err instanceof LanesError ? err.details.failed_outpost_ids : undefined;
230
+ const probedOutpostIds = err instanceof LanesError ? err.details.probed_outpost_ids : undefined;
231
+ const primaryLookupFailed = Array.isArray(failedOutpostIds) && failedOutpostIds.includes("primary");
232
+ const primaryReportedMissing = Array.isArray(probedOutpostIds) && probedOutpostIds.includes("primary");
233
+ if (!(err instanceof LanesError) ||
234
+ err.error !== "lane_not_found" ||
235
+ identity.kind !== "id" ||
236
+ identity.id === "primary" ||
237
+ primaryLookupFailed ||
238
+ primaryReportedMissing) {
239
+ throw err;
240
+ }
241
+ await dispatchPrimaryLaneMessage(ctx, laneId, { ...opts, text });
242
+ return;
243
+ }
225
244
  let lane = resolution.lane;
226
245
  if (resolution.remoteOnly || (lane.host.kind === "outpost" && !laneIsHostedHere(ctx, lane))) {
227
246
  const callerLaneId = ctx.env.HQ_LANE_ID?.trim();
@@ -465,7 +484,6 @@ export function createLaneMessageHandlers(deps) {
465
484
  markAccepted();
466
485
  }
467
486
  else {
468
- const managerStatusGateEnabled = await managerStatusGateEnabledForLane(hqRoot, lane, { env: ctx.env });
469
487
  const dispatched = await dispatchLaneDelivery(lane, {
470
488
  dropbox: () => {
471
489
  // Live (or about-to-be-live) process: the PreToolUse drain delivers it.
@@ -512,6 +530,7 @@ export function createLaneMessageHandlers(deps) {
512
530
  pending_resume: {
513
531
  session_id: sessionId,
514
532
  trigger: "message",
533
+ caller: ctx.env.HQ_LANE_ID?.trim() || "user",
515
534
  text: deliveryText,
516
535
  ...(opts.monitorEvent === undefined ? {} : { monitor_event: true }),
517
536
  message_id: messageId,
@@ -523,7 +542,6 @@ export function createLaneMessageHandlers(deps) {
523
542
  }
524
543
  const started = spawnLaneWorker(hqRoot, lane, {
525
544
  env: ctx.env,
526
- managerStatusGateEnabled,
527
545
  modelDeps: {
528
546
  readFileSync: ctx.readFileSync,
529
547
  probe: ctx.probeProvider,
@@ -624,6 +642,7 @@ export function createLaneMessageHandlers(deps) {
624
642
  accepted: true,
625
643
  delivered: false,
626
644
  delivery: mechanism,
645
+ delivered_to: { host: lane.host.id, lane_id: laneId, mechanism },
627
646
  delivery_status: deliveryStatus,
628
647
  inbox_path: mechanism === "dropbox" ? dropboxInboxPath(hqRoot, laneId) : null,
629
648
  drain_hook: drainHook,
@@ -5,7 +5,6 @@ import { parseCliEffort } from "../lib/lanes/effort.js";
5
5
  import { parseOutpostHireGrantContext } from "../lib/lanes/hire-grant-dispatch.js";
6
6
  import { assertNoSecretText } from "../lib/lanes/text-scan.js";
7
7
  import { laneStories, laneStory, storyWarning } from "../lib/lanes/stories.js";
8
- import { managerStatusGateEnabledForLane } from "../lib/lanes/manager-status-gate.js";
9
8
  import { spawnLaneWorker } from "../lib/lanes/spawn.js";
10
9
  import { accountMoveSummary, accountRequestKeepsCurrentLane, assertCurrentResumeAccountAvailable, hasKeepAliveProgress, recordedResumeEffort, resetKeepAliveProgress, resolveResumeEffort } from "./lanes-shared.js";
11
10
  export function createLaneResumeHandlers(deps) {
@@ -328,7 +327,6 @@ export function createLaneResumeHandlers(deps) {
328
327
  resumed = lane;
329
328
  }
330
329
  else {
331
- const managerStatusGateEnabled = await managerStatusGateEnabledForLane(hqRoot, lane, { env: ctx.env });
332
330
  resumed = await withLaneAdmissionLock(hqRoot, lane.provider, async () => {
333
331
  const admission = await assessLaneAdmission(ctx, lane, { queueAt: at });
334
332
  if (admission.action === "refuse" && admission.error)
@@ -361,6 +359,7 @@ export function createLaneResumeHandlers(deps) {
361
359
  pending_resume: {
362
360
  session_id: sessionId,
363
361
  trigger: "resume",
362
+ caller: ctx.env.HQ_LANE_ID?.trim() || "user",
364
363
  resumed_from: resumedFrom,
365
364
  ...(opts.fromFailed === true ? { from_failed: true } : {}),
366
365
  ...(failedResumeExpectedSenior !== undefined
@@ -376,7 +375,6 @@ export function createLaneResumeHandlers(deps) {
376
375
  }
377
376
  return spawnLaneWorker(hqRoot, lane, {
378
377
  env: ctx.env,
379
- managerStatusGateEnabled,
380
378
  modelDeps: {
381
379
  readFileSync: ctx.readFileSync,
382
380
  probe: ctx.probeProvider,
@@ -157,6 +157,7 @@ export interface LaneMessageHandlerDeps {
157
157
  }): Promise<AdmissionResult>;
158
158
  casUpdateLane(hqRoot: string, laneId: string, updater: (current: LaneRecord) => LaneRecord): LaneRecord;
159
159
  dispatchOutpostLaneCommand(ctx: Ctx, lane: LaneRecord, action: "message" | "resume", options: LaneMessageOptions | LaneResumeOptions, behavior?: OutpostLaneDispatchBehavior): Promise<void>;
160
+ dispatchPrimaryLaneMessage(ctx: Ctx, laneId: string, options: LaneMessageOptions): Promise<void>;
160
161
  drainHookStatusFor(ctx: Ctx, lane: LaneRecord): {
161
162
  registered: boolean;
162
163
  kind: string;
@@ -248,8 +249,6 @@ export interface Ctx {
248
249
  backfillOutpostIdentity: (expectedId: string) => "created" | "unchanged";
249
250
  fetchText: (url: string) => Promise<string>;
250
251
  lanePlacementEnabled: (company: LaneCompanyRef | undefined) => Promise<boolean>;
251
- outpostSnapshotCacheEnabled: () => Promise<boolean>;
252
- startOutpostLaneSnapshotRefresh: (hqRoot: string) => void;
253
252
  storyLedgerEnabled: () => boolean;
254
253
  isSsmOwnerDispatch: () => boolean;
255
254
  withGrantHireLocks: typeof withLaneHireLocks;
@@ -8,6 +8,7 @@ export declare function createLaneStoryHandlers(deps: LaneStoryHandlerDeps): {
8
8
  runStoryList: (ctx: Ctx, laneId: string) => Promise<void>;
9
9
  runStoryAdd: (ctx: Ctx, laneId: string, storyId: string, opts: StoryCommandOptions) => Promise<void>;
10
10
  runStoryStart: (ctx: Ctx, laneId: string, storyId: string, opts: StoryCommandOptions) => Promise<void>;
11
+ runStoryReview: (ctx: Ctx, laneId: string, storyId: string, opts: StoryCommandOptions) => Promise<void>;
11
12
  runStoryBlock: (ctx: Ctx, laneId: string, storyId: string, opts: StoryCommandOptions) => Promise<void>;
12
13
  runStoryDone: (ctx: Ctx, laneId: string, storyId: string, opts: StoryCommandOptions) => Promise<void>;
13
14
  };
@@ -18,6 +18,13 @@ export function createLaneStoryHandlers(deps) {
18
18
  }
19
19
  return { projectId, storyId: normalizedStoryId, story };
20
20
  }
21
+ function reviewableStoryTarget(lane, storyId, project) {
22
+ const target = storyCommandTarget(lane, storyId, project);
23
+ if (target.story.status === "done") {
24
+ throw new LanesError("story_already_done", `Story ${target.projectId}/${target.storyId} is done and cannot be moved to review`, { lane_id: lane.lane_id, project_id: target.projectId, story_id: target.storyId });
25
+ }
26
+ return target;
27
+ }
21
28
  function updateCarriedStory(lane, projectId, storyId, status, at, extras = {}) {
22
29
  const stories = laneStories(lane).map((story) => story.project_id === projectId && story.story_id === storyId
23
30
  ? {
@@ -229,6 +236,38 @@ export function createLaneStoryHandlers(deps) {
229
236
  const finalLane = readLane(hqRoot, laneId);
230
237
  printJson({ ok: true, action: "story.start", lane_id: laneId, project_id: target.projectId, story_id: target.storyId, status: "in_progress", story_warning: combinedStoryWarning(finalLane, warnings), lane: finalLane });
231
238
  }
239
+ async function runStoryReview(ctx, laneId, storyId, opts) {
240
+ const hqRoot = ctx.hqRoot();
241
+ let lane = readLane(hqRoot, laneId);
242
+ assertLaneRunsHereForOperatorMutation(ctx, lane, "update story status for");
243
+ const target = reviewableStoryTarget(lane, storyId, opts.project);
244
+ const at = ctx.now().toISOString();
245
+ const warnings = [];
246
+ const thread = await ensureStoryThreadForTransition(ctx, lane, target.projectId, target.storyId);
247
+ lane = thread.lane;
248
+ if (thread.warning !== undefined)
249
+ warnings.push(thread.warning);
250
+ const before = lane;
251
+ const next = casUpdateLane(hqRoot, laneId, (current) => {
252
+ assertLaneRunsHereForOperatorMutation(ctx, current, "update story status for");
253
+ reviewableStoryTarget(current, target.storyId, target.projectId);
254
+ return updateCarriedStory(current, target.projectId, target.storyId, "review", at);
255
+ });
256
+ const transitionWarning = await commitStoryTransition(ctx, before, next, target.projectId, target.storyId, "lane_progress", "review", { detail: "Story moved to review" });
257
+ if (transitionWarning !== undefined)
258
+ warnings.push(transitionWarning);
259
+ const finalLane = readLane(hqRoot, laneId);
260
+ printJson({
261
+ ok: true,
262
+ action: "story.review",
263
+ lane_id: laneId,
264
+ project_id: target.projectId,
265
+ story_id: target.storyId,
266
+ status: "review",
267
+ story_warning: combinedStoryWarning(finalLane, warnings),
268
+ lane: finalLane,
269
+ });
270
+ }
232
271
  async function runStoryBlock(ctx, laneId, storyId, opts) {
233
272
  const hqRoot = ctx.hqRoot();
234
273
  let lane = readLane(hqRoot, laneId);
@@ -303,6 +342,6 @@ export function createLaneStoryHandlers(deps) {
303
342
  const finalLane = readLane(hqRoot, laneId);
304
343
  printJson({ ok: true, action: "story.done", lane_id: laneId, project_id: target.projectId, story_id: target.storyId, status: "done", verified: true, story_warning: combinedStoryWarning(finalLane, warnings), lane: finalLane });
305
344
  }
306
- return { runStoryList, runStoryAdd, runStoryStart, runStoryBlock, runStoryDone };
345
+ return { runStoryList, runStoryAdd, runStoryStart, runStoryReview, runStoryBlock, runStoryDone };
307
346
  }
308
347
  //# sourceMappingURL=lanes-stories.js.map
@@ -14,7 +14,6 @@ import type { OutpostIdentity } from "../lib/lanes/admission-host.js";
14
14
  export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
15
15
  export type { OutpostIdentity } from "../lib/lanes/admission-host.js";
16
16
  import { type OutpostDispatchResult } from "../lib/lanes/outpost-dispatch.js";
17
- import { LANE_OUTPOST_SNAPSHOT_CACHE_FLAG } from "../lib/lanes/outpost-hire-cache.js";
18
17
  import { emitLaneDecisionEvents } from "../lib/lanes/mesh-events.js";
19
18
  import { MAX_LANE_DEPTH, withLaneHireLocks, type LaneCompanyRef, type QuestionBody, type LaneRecord, type QuestionRecord, type ResolvedLaneWorker, type SeniorRef, type LivenessDeps, type MachineIdentity, type MonitorEvidenceReader, type RunCommand, type ThrottleDeps } from "../lib/lanes/index.js";
20
19
  import { questionBodyAbsPath } from "../lib/lanes/questions.js";
@@ -62,7 +61,7 @@ export interface LanesCommandDeps {
62
61
  /** Test seam for worker PID identity checks. */
63
62
  readStarttime?: (pid: number) => number | null;
64
63
  /** Test seams for synchronous dead-worker classification and repair. */
65
- watcherLoopDeps?: Pick<WatcherLoopDeps, "sleep" | "spawnRepair" | "readManagerStatusGateFlag">;
64
+ watcherLoopDeps?: Pick<WatcherLoopDeps, "sleep" | "spawnRepair">;
66
65
  stopSleepMs?: (ms: number) => void;
67
66
  livenessDeps?: LivenessDeps;
68
67
  presenceConnected?: boolean;
@@ -88,10 +87,6 @@ export interface LanesCommandDeps {
88
87
  monitorEvidence?: MonitorEvidenceReader;
89
88
  /** Test seam for the default-off outpost placement rollout flag. */
90
89
  lanePlacementEnabled?: (company: LaneCompanyRef | undefined) => Promise<boolean>;
91
- /** Test seam for the default-off shared Outpost snapshot cache. */
92
- outpostSnapshotCacheEnabled?: () => Promise<boolean>;
93
- /** Test seam for the detached refresh started by the cache-first lane list. */
94
- startOutpostLaneSnapshotRefresh?: (hqRoot: string) => void;
95
90
  /** Test seam for the default-off story ledger flag. */
96
91
  storyLedgerEnabled?: () => boolean;
97
92
  /** Test seam for the SSM-only Outpost owner grant path. */
@@ -103,7 +98,6 @@ export interface LanesCommandDeps {
103
98
  /** Test seam for the repository-merge-hold registry flag. */
104
99
  mergeHoldEnabled?: (company: LaneCompanyRef) => boolean | Promise<boolean>;
105
100
  }
106
- export { LANE_OUTPOST_SNAPSHOT_CACHE_FLAG };
107
101
  export declare function parseSeniorRef(raw: string): SeniorRef;
108
102
  export declare function parseQuestionBody(raw: unknown): QuestionBody | null;
109
103
  /**
@@ -148,8 +142,6 @@ export declare function pickupFailureUpdater(reasonRaw: string, details: Record<
148
142
  */
149
143
  export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string, modelDeps?: KnownModelsDeps): LaneRecord;
150
144
  export declare function cancelPendingQuestionsForLane(hqRoot: string, laneId: string, nowIso: string, operatorReason?: string): QuestionRecord[];
151
- /** Keeps the placement-enabled cache-first path on the full lane command graph. */
152
- export declare function runFastCachedLanesListFromEntrypoint(): Promise<void>;
153
145
  export declare function stopCommittedState(state: LaneRecord["state"]): "cancelled" | "killed";
154
146
  /** Stderr notice that `stop` ends a lane for good, naming the resumable path. */
155
147
  export declare function stopTerminalWarning(laneId: string): string;