@indigoai-us/hq-cli 5.282.0 → 5.283.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 (52) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/assets/scaffold/.claude/hooks/validate-policy-frontmatter.sh +485 -0
  3. package/assets/scaffold/core/scripts/eval-trigger.sh +193 -0
  4. package/assets/scaffold/core/scripts/policy-age-report.sh +156 -0
  5. package/assets/scaffold/core/scripts/policy-retire.sh +199 -0
  6. package/assets/scaffold/core/scripts/read-policy-frontmatter.sh +32 -0
  7. package/dist/command-catalog.generated.d.ts +67 -0
  8. package/dist/command-catalog.generated.js +83 -0
  9. package/dist/commands/core-knowledge-prefs.d.ts +10 -0
  10. package/dist/commands/core-knowledge-prefs.js +125 -0
  11. package/dist/commands/core-native-policy-flag.d.ts +9 -0
  12. package/dist/commands/core-native-policy-flag.js +12 -0
  13. package/dist/commands/core-ontology-native.d.ts +0 -1
  14. package/dist/commands/core-ontology-native.js +2 -30
  15. package/dist/commands/core-ontology.d.ts +4 -0
  16. package/dist/commands/core-ontology.js +8 -3
  17. package/dist/commands/core-policy-frontmatter.d.ts +8 -0
  18. package/dist/commands/core-policy-frontmatter.js +52 -0
  19. package/dist/commands/core-policy.d.ts +15 -0
  20. package/dist/commands/core-policy.js +982 -0
  21. package/dist/commands/core.js +14 -1
  22. package/dist/commands/lanes-shared.d.ts +4 -1
  23. package/dist/commands/lanes-shared.js +2 -1
  24. package/dist/commands/lanes.d.ts +8 -0
  25. package/dist/commands/lanes.js +294 -86
  26. package/dist/fast-lanes-list-route.d.ts +8 -0
  27. package/dist/fast-lanes-list-route.js +30 -0
  28. package/dist/fast-lanes-list.d.ts +3 -0
  29. package/dist/fast-lanes-list.js +252 -0
  30. package/dist/fast-scaffold.d.ts +5 -0
  31. package/dist/fast-scaffold.js +10 -0
  32. package/dist/index.js +10 -1
  33. package/dist/lib/checkpoint-ontology/merge-copies.d.ts +24 -1
  34. package/dist/lib/checkpoint-ontology/merge-copies.js +70 -1
  35. package/dist/lib/lanes/flag-keys.d.ts +3 -0
  36. package/dist/lib/lanes/flag-keys.js +3 -0
  37. package/dist/lib/lanes/index.d.ts +1 -1
  38. package/dist/lib/lanes/index.js +1 -1
  39. package/dist/lib/lanes/outpost-dispatch.d.ts +1 -1
  40. package/dist/lib/lanes/outpost-dispatch.js +4 -2
  41. package/dist/lib/lanes/outpost-hire-cache.d.ts +3 -0
  42. package/dist/lib/lanes/outpost-hire-cache.js +11 -3
  43. package/dist/lib/lanes/outpost-snapshot-cache.d.ts +26 -0
  44. package/dist/lib/lanes/outpost-snapshot-cache.js +215 -0
  45. package/dist/lib/lanes/store.d.ts +4 -0
  46. package/dist/lib/lanes/store.js +39 -0
  47. package/dist/lib/policy-validator.d.ts +2 -0
  48. package/dist/lib/policy-validator.js +136 -0
  49. package/dist/native-fast-route.d.ts +2 -0
  50. package/dist/native-fast-route.js +6 -2
  51. package/dist/utils/contribution-table.d.ts +1 -1
  52. package/package.json +1 -1
@@ -31,13 +31,14 @@ import { withCliFeatureUsed } from "../utils/cli-telemetry.js";
31
31
  import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
32
32
  import { isSsmRunCommandProcess } from "../lib/lanes/ssm-command-context.js";
33
33
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
34
+ import { getOrFetchOutpostSnapshot, OUTPOST_SNAPSHOT_CACHE_TTL_MS, outpostLaneListSnapshotKey, OutpostSnapshotInvocationMemo, } from "../lib/lanes/outpost-snapshot-cache.js";
34
35
  import { createOutpostHireGrantContext, parseOutpostHireGrantContext, selectOutpostHireGrantHierarchy, sameHireGrantSenior, } from "../lib/lanes/hire-grant-dispatch.js";
35
36
  import { createOutpostProfileRepinContext, parseOutpostProfileRepinContext, } from "../lib/lanes/outpost-profile-repin.js";
36
37
  import { createOutpostRegistryWorkerApprovalContext, parseOutpostRegistryWorkerApprovalContext, } from "../lib/lanes/outpost-registry-worker-approval.js";
37
38
  import { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity, resolveAdmissionHost, } from "../lib/lanes/admission-host.js";
38
39
  export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
39
40
  import { buildOutpostCapacityCommand, buildOutpostIdentityBackfillCommand, buildOutpostLaneCommand, buildOutpostQuestionListCommand, buildOutpostPickupCommand, createOutpostsClient, dispatchErrorMessage, dispatchOutpostPickup, buildOutpostLaneListCommand, outpostRejectedEffortOption, parseOutpostPickupPayload, PICKUP_WAIT_MS, stageOutpostPayload, } from "../lib/lanes/outpost-dispatch.js";
40
- import { outpostHireSummaryCacheKey, readOutpostHireSummaryCache, writeOutpostHireSummaryCache, } from "../lib/lanes/outpost-hire-cache.js";
41
+ import { outpostHireSummaryCacheKey, LANE_OUTPOST_SNAPSHOT_CACHE_FLAG, OUTPOST_HIRE_SUMMARY_CACHE_TTL_MS, readOutpostHireSummaryCache, readOutpostHireSummaryCacheIncludingStale, writeOutpostHireSummaryCache, } from "../lib/lanes/outpost-hire-cache.js";
41
42
  import { buildHostCapacitySnapshot, chooseHostForLane, parseHostCapacitySnapshot, } from "../lib/lanes/host-capacity.js";
42
43
  import { BALANCE_MOVABLE_STATES, selectClaudeAccount, } from "../lib/lanes/claude-balance.js";
43
44
  import { parseUserYaml } from "../utils/user-yaml-error.js";
@@ -51,7 +52,7 @@ import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, lan
51
52
  import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
52
53
  import { listLaneReviews } from "../lib/lanes/reviews.js";
53
54
  import { effectiveAccountHome } from "../lib/lanes/account-home.js";
54
- import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readLane, sanitizeHostComponent, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
55
+ import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readAllLanes, readLane, sanitizeHostComponent, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
55
56
  import { activeLaneStories, laneStories, laneStory, storyWarning, } from "../lib/lanes/stories.js";
56
57
  import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
57
58
  import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
@@ -79,6 +80,7 @@ export const LANE_REMOTE_MIRROR_SYNC_FLAG = "lanes.remote-done-mirror";
79
80
  const REMOTE_MIRROR_REFRESH_BATCH_SIZE = 100;
80
81
  /** The lane → mesh seam. Defined in lib/lanes/mesh-events.ts; the watcher shares it. */
81
82
  export { emitLaneDecisionEvents };
83
+ export { LANE_OUTPOST_SNAPSHOT_CACHE_FLAG };
82
84
  function withoutProviderHome(value) {
83
85
  if (Array.isArray(value))
84
86
  return value.map(withoutProviderHome);
@@ -294,6 +296,8 @@ function resolveDeps(deps = {}) {
294
296
  })),
295
297
  fetchText: deps.fetchText ?? defaultFetchText,
296
298
  lanePlacementEnabled: deps.lanePlacementEnabled ?? defaultLanePlacementEnabled,
299
+ outpostSnapshotCacheEnabled: deps.outpostSnapshotCacheEnabled ?? (() => readLanesRegistryFlag(LANE_OUTPOST_SNAPSHOT_CACHE_FLAG, { env })),
300
+ startOutpostLaneSnapshotRefresh: deps.startOutpostLaneSnapshotRefresh ?? ((root) => startOutpostLaneSnapshotRefresh(root, env)),
297
301
  storyLedgerEnabled: deps.storyLedgerEnabled ?? laneStoryLedgerEnabled,
298
302
  isSsmOwnerDispatch: deps.isSsmOwnerDispatch ?? (() => isSsmRunCommandProcess()),
299
303
  withGrantHireLocks: deps.withGrantHireLocks ?? withLaneHireLocks,
@@ -460,7 +464,7 @@ function readQuestionBody(hqRoot, question) {
460
464
  return stored.body;
461
465
  }
462
466
  function loadAllLanes(hqRoot) {
463
- return listLaneIds(hqRoot).map((id) => readLane(hqRoot, id));
467
+ return readAllLanes(hqRoot);
464
468
  }
465
469
  function laneHireFields(lane, lanes, aliveChildrenByParent) {
466
470
  const aliveChildCount = aliveChildrenByParent
@@ -697,7 +701,7 @@ function laneListQueryFlags(query) {
697
701
  return [
698
702
  ...(query.laneIds?.length ? ["--lane"] : []),
699
703
  ...(query.active ? ["--active"] : []),
700
- ...(query.hierarchy !== undefined ? ["--hierarchy"] : []),
704
+ ...(query.hierarchy?.length ? ["--hierarchy"] : []),
701
705
  ...(query.snapshot ? ["--snapshot"] : []),
702
706
  ];
703
707
  }
@@ -726,7 +730,7 @@ async function dispatchOutpostLaneList(ctx, outpostId, query, timeoutSeconds, wa
726
730
  waitMs,
727
731
  });
728
732
  }
729
- async function fetchOutpostListHireSummaries(ctx, outpostId, lanes) {
733
+ async function fetchOutpostListHireSummaries(ctx, outpostId, lanes, localLanes) {
730
734
  const fallback = (error) => new Map(lanes.map((lane) => [lane.lane_id, {
731
735
  error: new LanesError(error.error, error.message, {
732
736
  ...error.details,
@@ -734,65 +738,91 @@ async function fetchOutpostListHireSummaries(ctx, outpostId, lanes) {
734
738
  lane_id: lane.lane_id,
735
739
  }),
736
740
  }]));
737
- let dispatched;
741
+ const summaryReadError = (err) => {
742
+ if (isLanesError(err)) {
743
+ if (err.error === "outpost_lane_list_invalid") {
744
+ return new LanesError("outpost_lane_hire_summary_invalid", err.message, err.details);
745
+ }
746
+ if (err.error === "outpost_lane_list_unavailable") {
747
+ return new LanesError("outpost_lane_hire_summary_unavailable", err.message, err.details);
748
+ }
749
+ return err;
750
+ }
751
+ return new LanesError("outpost_lane_hire_summary_unavailable", `Could not read hire summaries from Outpost ${outpostId}: ${dispatchErrorMessage(err)}`, { outpost_id: outpostId });
752
+ };
753
+ const dispatchOptions = {
754
+ timeoutSeconds: OUTPOST_HIRE_SUMMARY_TIMEOUT_SECONDS,
755
+ waitMs: OUTPOST_HIRE_SUMMARY_WAIT_MS,
756
+ };
757
+ let ownerLanes;
738
758
  try {
739
- dispatched = await dispatchOutpostLaneList(ctx, outpostId, { laneIds: lanes.map((lane) => lane.lane_id) }, OUTPOST_HIRE_SUMMARY_TIMEOUT_SECONDS, OUTPOST_HIRE_SUMMARY_WAIT_MS);
759
+ ownerLanes = await readOutpostLaneRecords(ctx, outpostId, {
760
+ hierarchy: lanes.map((lane) => lane.lane_id),
761
+ snapshot: true,
762
+ }, dispatchOptions);
740
763
  }
741
764
  catch (err) {
742
- if (isLanesError(err))
743
- return fallback(err);
744
- return fallback(new LanesError("outpost_lane_hire_summary_unavailable", `Could not read hire summaries from Outpost ${outpostId}: ${dispatchErrorMessage(err)}`, { outpost_id: outpostId }));
745
- }
746
- if (dispatched.done !== true || dispatched.status !== "Success" || dispatched.exitCode !== 0) {
747
- return fallback(new LanesError("outpost_lane_hire_summary_unavailable", `Outpost ${outpostId} did not confirm its lane list for hire summaries`, { outpost_id: outpostId, command_id: dispatched.commandId }));
748
- }
749
- const remote = parseRemoteJson(dispatched.stdout, "Outpost lane hire summary list");
750
- if (!remote || remote.ok !== true || remote.action !== "list" || !Array.isArray(remote.lanes)) {
751
- return fallback(new LanesError("outpost_lane_hire_summary_invalid", `Outpost ${outpostId} returned an invalid lane list for hire summaries`, { outpost_id: outpostId, command_id: dispatched.commandId }));
765
+ return fallback(summaryReadError(err));
766
+ }
767
+ const requestedOwnerIds = new Set(lanes.map((lane) => lane.lane_id));
768
+ const returnedOwnerIds = new Set(ownerLanes
769
+ .filter((lane) => requestedOwnerIds.has(lane.lane_id))
770
+ .map((lane) => lane.lane_id));
771
+ const missingOwnerLanes = lanes.filter((lane) => !returnedOwnerIds.has(lane.lane_id));
772
+ const retryErrors = new Map();
773
+ if (returnedOwnerIds.size > 0 && missingOwnerLanes.length > 0) {
774
+ const retries = await mapWithConcurrency(missingOwnerLanes, OUTPOST_HIRE_SUMMARY_CONCURRENCY, async (lane) => {
775
+ try {
776
+ const rows = await readOutpostLaneRecords(ctx, outpostId, {
777
+ hierarchy: [lane.lane_id],
778
+ snapshot: true,
779
+ }, dispatchOptions);
780
+ return { laneId: lane.lane_id, rows };
781
+ }
782
+ catch (err) {
783
+ return { laneId: lane.lane_id, error: summaryReadError(err) };
784
+ }
785
+ });
786
+ for (const retry of retries) {
787
+ if (retry.error)
788
+ retryErrors.set(retry.laneId, retry.error);
789
+ else if (retry.rows)
790
+ ownerLanes.push(...retry.rows);
791
+ }
752
792
  }
793
+ const hierarchy = mergeOwnerLaneHierarchy(localLanes, ownerLanes);
753
794
  const outcomes = new Map();
754
795
  for (const lane of lanes) {
755
- const matchingRows = remote.lanes.filter((row) => row !== null && typeof row === "object" && !Array.isArray(row) &&
756
- row.lane_id === lane.lane_id);
757
- if (matchingRows.length === 0) {
758
- outcomes.set(lane.lane_id, { error: new LanesError("outpost_lane_hire_summary_missing", `Outpost ${outpostId} did not return lane ${lane.lane_id} in its lane list`, { outpost_id: outpostId, lane_id: lane.lane_id, command_id: dispatched.commandId }) });
796
+ const matchingLanes = ownerLanes.filter((ownerLane) => ownerLane.lane_id === lane.lane_id);
797
+ if (matchingLanes.length === 0) {
798
+ const error = retryErrors.get(lane.lane_id) ?? new LanesError("outpost_lane_hire_summary_missing", `Outpost ${outpostId} did not return lane ${lane.lane_id} in its lane list`, { outpost_id: outpostId, lane_id: lane.lane_id });
799
+ outcomes.set(lane.lane_id, { error });
759
800
  continue;
760
801
  }
761
- if (matchingRows.length !== 1) {
762
- outcomes.set(lane.lane_id, { error: new LanesError("outpost_lane_hire_summary_invalid", `Outpost ${outpostId} returned duplicate lane rows for ${lane.lane_id}`, { outpost_id: outpostId, lane_id: lane.lane_id, command_id: dispatched.commandId }) });
802
+ if (matchingLanes.length !== 1) {
803
+ outcomes.set(lane.lane_id, { error: new LanesError("outpost_lane_hire_summary_invalid", `Outpost ${outpostId} returned duplicate lane rows for ${lane.lane_id}`, { outpost_id: outpostId, lane_id: lane.lane_id }) });
763
804
  continue;
764
805
  }
765
- const row = matchingRows[0];
766
- try {
767
- outcomes.set(lane.lane_id, { fields: validateOwnerLaneHireSummary({
768
- outpostId,
769
- laneId: lane.lane_id,
770
- rawLane: row,
771
- hireGrant: row.hire_grant,
772
- hireAliveChildren: row.hire_alive_children,
773
- hire: row.hire,
774
- hireSummary: row.hire_summary,
775
- commandId: dispatched.commandId,
776
- }) });
777
- }
778
- catch (err) {
779
- outcomes.set(lane.lane_id, { error: isLanesError(err)
780
- ? err
781
- : new LanesError("outpost_lane_hire_summary_invalid", `Outpost ${outpostId} returned an invalid hire summary for lane ${lane.lane_id}`, { outpost_id: outpostId, lane_id: lane.lane_id, command_id: dispatched.commandId }) });
806
+ const ownerLane = matchingLanes[0];
807
+ if (ownerLane.host.kind !== "outpost" || ownerLane.host.id !== outpostId) {
808
+ outcomes.set(lane.lane_id, { error: new LanesError("outpost_lane_hire_summary_invalid", `Outpost ${outpostId} returned a lane owned by a different host`, { outpost_id: outpostId, lane_id: lane.lane_id }) });
809
+ continue;
782
810
  }
811
+ outcomes.set(lane.lane_id, { fields: laneHireFields(ownerLane, hierarchy) });
783
812
  }
784
813
  return outcomes;
785
814
  }
786
- async function batchOwnerLaneHireFields(ctx, lanes) {
815
+ async function batchOwnerLaneHireFields(ctx, lanes, localLanes, options = {}) {
787
816
  const outcomes = new Map();
788
817
  const groups = new Map();
789
818
  const nowMs = ctx.now().getTime();
790
- const cached = readOutpostHireSummaryCache(ctx.hqRoot(), nowMs);
819
+ const cached = readOutpostHireSummaryCacheIncludingStale(ctx.hqRoot(), nowMs);
791
820
  for (const lane of lanes) {
792
821
  if (lane.host.kind !== "outpost")
793
822
  continue;
794
823
  const cachedSummary = cached.get(outpostHireSummaryCacheKey(lane.host.id, lane.lane_id));
795
- if (cachedSummary) {
824
+ if (cachedSummary && !options.forceRefresh &&
825
+ nowMs - Date.parse(cachedSummary.cached_at) <= OUTPOST_HIRE_SUMMARY_CACHE_TTL_MS) {
796
826
  outcomes.set(lane.lane_id, {
797
827
  fields: {
798
828
  hire_grant: cachedSummary.hire_grant,
@@ -807,7 +837,7 @@ async function batchOwnerLaneHireFields(ctx, lanes) {
807
837
  group.push(lane);
808
838
  groups.set(lane.host.id, group);
809
839
  }
810
- const fetchedGroups = await mapWithConcurrency([...groups.entries()], OUTPOST_HIRE_SUMMARY_CONCURRENCY, ([outpostId, group]) => fetchOutpostListHireSummaries(ctx, outpostId, group));
840
+ const fetchedGroups = await mapWithConcurrency([...groups.entries()], OUTPOST_HIRE_SUMMARY_CONCURRENCY, ([outpostId, group]) => fetchOutpostListHireSummaries(ctx, outpostId, group, localLanes));
811
841
  const cacheUpdates = [];
812
842
  for (const groupOutcomes of fetchedGroups) {
813
843
  for (const [laneId, outcome] of groupOutcomes) {
@@ -947,6 +977,9 @@ function laneHostFromCtx(ctx, expectedId) {
947
977
  return { kind: "local", id: hostname };
948
978
  }
949
979
  async function readOutpostCapacity(ctx, outpostId) {
980
+ return readOutpostSnapshotWithCache(ctx, JSON.stringify(["capacity", outpostId]), (value) => isOutpostCapacitySnapshot(value, outpostId), () => readOutpostCapacityUncached(ctx, outpostId));
981
+ }
982
+ async function readOutpostCapacityUncached(ctx, outpostId) {
950
983
  let result;
951
984
  try {
952
985
  result = await ctx.dispatchOutpostExec({
@@ -980,7 +1013,68 @@ async function readOutpostCapacity(ctx, outpostId) {
980
1013
  }
981
1014
  return snapshot;
982
1015
  }
983
- async function loadHostCapacitySnapshots(ctx, hqRoot, config = loadLanesConfig(hqRoot)) {
1016
+ async function readOutpostSnapshotWithCache(ctx, key, isValue, fetch) {
1017
+ let enabled = false;
1018
+ try {
1019
+ enabled = await ctx.outpostSnapshotCacheEnabled();
1020
+ }
1021
+ catch (err) {
1022
+ process.stderr.write(`lanes snapshot cache flag read failed; using uncached Outpost read: ${dispatchErrorMessage(err)}\n`);
1023
+ }
1024
+ if (!enabled)
1025
+ return fetch();
1026
+ return getOrFetchOutpostSnapshot({ hqRoot: ctx.hqRoot(), key, isValue, fetch });
1027
+ }
1028
+ function startOutpostLaneSnapshotRefresh(root, env) {
1029
+ let entry;
1030
+ try {
1031
+ const currentEntry = process.argv[1];
1032
+ if (!currentEntry)
1033
+ throw new Error("process.argv[1] is empty");
1034
+ entry = fs.realpathSync(currentEntry);
1035
+ }
1036
+ catch (err) {
1037
+ process.stderr.write(`lanes snapshot refresh could not resolve the running CLI entry: ${dispatchErrorMessage(err)}\n`);
1038
+ return;
1039
+ }
1040
+ try {
1041
+ const child = spawn(process.execPath, [...process.execArgv, entry, "lanes", "list"], {
1042
+ cwd: root,
1043
+ detached: true,
1044
+ stdio: ["ignore", "ignore", "inherit"],
1045
+ env: { ...env, HQ_ROOT: root, HQ_NO_UPDATE_CHECK: "1", HQ_INTERNAL_LANES_REFRESH_REMOTE_SNAPSHOT: "1" },
1046
+ });
1047
+ child.once("error", (err) => {
1048
+ process.stderr.write(`lanes snapshot refresh could not start: ${dispatchErrorMessage(err)}\n`);
1049
+ });
1050
+ child.unref();
1051
+ }
1052
+ catch (err) {
1053
+ process.stderr.write(`lanes snapshot refresh could not start: ${dispatchErrorMessage(err)}\n`);
1054
+ }
1055
+ }
1056
+ function isLaneRecordArray(value) {
1057
+ if (!Array.isArray(value))
1058
+ return false;
1059
+ try {
1060
+ for (const lane of value)
1061
+ parseLaneRecord(lane);
1062
+ return true;
1063
+ }
1064
+ catch {
1065
+ return false;
1066
+ }
1067
+ }
1068
+ function isOutpostCapacitySnapshot(value, outpostId) {
1069
+ try {
1070
+ const snapshot = parseHostCapacitySnapshot(value);
1071
+ return snapshot.host.kind === "outpost" && snapshot.host.id === outpostId;
1072
+ }
1073
+ catch {
1074
+ return false;
1075
+ }
1076
+ }
1077
+ async function loadHostCapacitySnapshots(ctx, hqRoot, config = loadLanesConfig(hqRoot), localLanes = loadAllLanes(hqRoot)) {
984
1078
  const localHost = laneHostFromCtx(ctx);
985
1079
  let outpostIds;
986
1080
  try {
@@ -1007,7 +1101,7 @@ async function loadHostCapacitySnapshots(ctx, hqRoot, config = loadLanesConfig(h
1007
1101
  .sort();
1008
1102
  const local = buildHostCapacitySnapshot({
1009
1103
  host: localHost,
1010
- lanes: loadAllLanes(hqRoot),
1104
+ lanes: localLanes,
1011
1105
  config,
1012
1106
  existsSync: ctx.existsSync,
1013
1107
  hostname: ctx.hostname(),
@@ -1053,25 +1147,30 @@ function normalizedLaneListQuery(query) {
1053
1147
  const laneIds = query.laneIds === undefined
1054
1148
  ? undefined
1055
1149
  : [...new Set(query.laneIds)].sort((a, b) => a.localeCompare(b));
1150
+ const hierarchy = query.hierarchy === undefined
1151
+ ? undefined
1152
+ : [...new Set(query.hierarchy)].sort((a, b) => a.localeCompare(b));
1056
1153
  return {
1057
1154
  ...(laneIds === undefined ? {} : { laneIds }),
1058
1155
  ...(query.active ? { active: true } : {}),
1059
- ...(query.hierarchy === undefined ? {} : { hierarchy: query.hierarchy }),
1156
+ ...(hierarchy === undefined ? {} : { hierarchy }),
1060
1157
  ...(query.snapshot ? { snapshot: true } : {}),
1061
1158
  ...(query.strictSnapshot ? { strictSnapshot: true } : {}),
1062
1159
  };
1063
1160
  }
1064
- async function readOutpostLaneRecordsUncached(ctx, outpostId, query) {
1161
+ async function readOutpostLaneRecordsUncached(ctx, outpostId, query, dispatchOptions = {}) {
1065
1162
  const targetedLookup = query.hierarchy !== undefined || query.laneIds !== undefined;
1066
1163
  const waitMs = targetedLookup ? outpostSingleLaneWaitMs(ctx.env) : CAPACITY_PROBE_TIMEOUT_MS;
1164
+ const dispatchWaitMs = dispatchOptions.waitMs ?? waitMs;
1165
+ const dispatchTimeoutSeconds = dispatchOptions.timeoutSeconds ?? Math.ceil(dispatchWaitMs / 1000);
1067
1166
  let result;
1068
1167
  try {
1069
- result = await dispatchOutpostLaneList(ctx, outpostId, query, Math.ceil(waitMs / 1000), waitMs);
1168
+ result = await dispatchOutpostLaneList(ctx, outpostId, query, dispatchTimeoutSeconds, dispatchWaitMs);
1070
1169
  }
1071
1170
  catch (err) {
1072
1171
  const message = dispatchErrorMessage(err);
1073
1172
  if (/timed out waiting for outpost pickup/i.test(message)) {
1074
- throw new LanesError("outpost_lane_list_unavailable", outpostPickupTimeoutMessage(outpostId, waitMs, "reading the requested lane"), { outpost_id: outpostId, wait_ms: waitMs, wait_env: OUTPOST_SINGLE_LANE_WAIT_ENV });
1173
+ throw new LanesError("outpost_lane_list_unavailable", outpostPickupTimeoutMessage(outpostId, dispatchWaitMs, "reading the requested lane"), { outpost_id: outpostId, wait_ms: dispatchWaitMs, wait_env: OUTPOST_SINGLE_LANE_WAIT_ENV });
1075
1174
  }
1076
1175
  if (isLanesError(err))
1077
1176
  throw err;
@@ -1130,24 +1229,14 @@ async function readOutpostLaneRecordsUncached(ctx, outpostId, query) {
1130
1229
  }
1131
1230
  return lanes;
1132
1231
  }
1133
- async function readOutpostLaneRecords(ctx, outpostId, requestedQuery = {}) {
1232
+ async function readOutpostLaneRecords(ctx, outpostId, requestedQuery = {}, dispatchOptions = {}) {
1134
1233
  const query = normalizedLaneListQuery(requestedQuery);
1135
- const queryKey = JSON.stringify(query);
1136
- let byOutpost = outpostLaneListMemo.get(ctx);
1137
- if (!byOutpost) {
1138
- byOutpost = new Map();
1139
- outpostLaneListMemo.set(ctx, byOutpost);
1140
- }
1141
- const existing = byOutpost.get(outpostId);
1142
- if (existing) {
1143
- if (existing.queryKey !== queryKey) {
1144
- throw new LanesError("outpost_lane_list_query_conflict", `Outpost ${outpostId} was already read with a different lane-list query in this invocation`, { outpost_id: outpostId });
1145
- }
1146
- return existing.promise;
1234
+ let invocationMemo = outpostLaneListMemo.get(ctx);
1235
+ if (!invocationMemo) {
1236
+ invocationMemo = new OutpostSnapshotInvocationMemo();
1237
+ outpostLaneListMemo.set(ctx, invocationMemo);
1147
1238
  }
1148
- const promise = readOutpostLaneRecordsUncached(ctx, outpostId, query);
1149
- byOutpost.set(outpostId, { queryKey, promise });
1150
- return promise;
1239
+ return invocationMemo.getOrCreate(outpostId, query, () => readOutpostSnapshotWithCache(ctx, outpostLaneListSnapshotKey(outpostId, query), isLaneRecordArray, () => readOutpostLaneRecordsUncached(ctx, outpostId, query, dispatchOptions)));
1151
1240
  }
1152
1241
  /** Refresh only primary-created, non-terminal Outpost mirrors in one bounded owner query per Outpost. */
1153
1242
  async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds) {
@@ -1307,6 +1396,16 @@ async function resolveCommandLaneTarget(ctx, laneId, options = {}) {
1307
1396
  if (!(err instanceof LanesError) || err.error !== "lane_not_found")
1308
1397
  throw err;
1309
1398
  }
1399
+ const requestedUlid = laneUlidPart(laneId);
1400
+ if (requestedUlid) {
1401
+ const localMatches = listLaneIds(ctx.hqRoot()).filter((storedId) => laneUlidPart(storedId) === requestedUlid);
1402
+ if (localMatches.length > 1) {
1403
+ throw new LanesError("lane_ulid_ambiguous", `More than one local lane record has ULID ${requestedUlid}`, { lane_id: laneId, matches: localMatches });
1404
+ }
1405
+ const localMatch = localMatches[0];
1406
+ if (localMatch)
1407
+ return { lane: readLane(ctx.hqRoot(), localMatch), remoteOnly: false };
1408
+ }
1310
1409
  const separator = laneId.lastIndexOf("_");
1311
1410
  const hostSuffix = separator < 0 ? "" : laneId.slice(separator + 1);
1312
1411
  if (!hostSuffix || sanitizeHostComponent(hostSuffix) !== hostSuffix) {
@@ -1373,7 +1472,7 @@ async function resolveCommandLaneTarget(ctx, laneId, options = {}) {
1373
1472
  let ownerLanes;
1374
1473
  try {
1375
1474
  ownerLanes = await readOutpostLaneRecords(ctx, outpostId, {
1376
- hierarchy: laneId,
1475
+ hierarchy: [laneId],
1377
1476
  snapshot: true,
1378
1477
  });
1379
1478
  }
@@ -1407,6 +1506,13 @@ async function resolveCommandLaneTarget(ctx, laneId, options = {}) {
1407
1506
  ownerHost: match.lane.host,
1408
1507
  };
1409
1508
  }
1509
+ function laneUlidPart(laneId) {
1510
+ const separator = laneId.indexOf("_");
1511
+ if (separator !== 26)
1512
+ return undefined;
1513
+ const ulid = laneId.slice(0, separator);
1514
+ return /^[0-9A-HJKMNP-TV-Z]{26}$/i.test(ulid) ? ulid.toUpperCase() : undefined;
1515
+ }
1410
1516
  function commandLaneNotFound(laneId, outpostHint, details = {}) {
1411
1517
  const hint = outpostHint
1412
1518
  ? "; pass --outpost <id> to confirm a registered Outpost"
@@ -3802,7 +3908,7 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options, behavior =
3802
3908
  let grantHierarchy;
3803
3909
  if (action === "grant") {
3804
3910
  const hierarchy = mergeOwnerLaneRecords(loadAllLanes(ctx.hqRoot()), await readOutpostLaneRecords(ctx, outpostId, {
3805
- hierarchy: lane.lane_id,
3911
+ hierarchy: [lane.lane_id],
3806
3912
  snapshot: true,
3807
3913
  }));
3808
3914
  grantHierarchy = hierarchy;
@@ -3811,7 +3917,7 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options, behavior =
3811
3917
  else if ((action === "resume" && lane.senior.kind === "lane") ||
3812
3918
  (action === "message" && (lane.senior.kind === "lane" || lane.senior.kind === "session"))) {
3813
3919
  const hierarchy = mergeOwnerLaneRecords(loadAllLanes(ctx.hqRoot()), await readOutpostLaneRecords(ctx, outpostId, {
3814
- hierarchy: lane.lane_id,
3920
+ hierarchy: [lane.lane_id],
3815
3921
  snapshot: true,
3816
3922
  }));
3817
3923
  grantContext = createOutpostHireGrantContext(lane, hierarchy, action === "message" ? options.supersededMessageCallerId : undefined, action === "message" ? options.supersededMessageSessionId : undefined);
@@ -4359,7 +4465,7 @@ async function runPickup(ctx, opts) {
4359
4465
  lane: started,
4360
4466
  });
4361
4467
  }
4362
- async function runList(ctx, opts) {
4468
+ async function runList(ctx, opts, fastRouteFlags) {
4363
4469
  if (opts.json && opts.human) {
4364
4470
  throw new LanesError("conflicting_output_format", "Use either --json or --human for hq lanes list", { json: true, human: true });
4365
4471
  }
@@ -4369,8 +4475,11 @@ async function runList(ctx, opts) {
4369
4475
  new Set(requestedLaneIds).size !== requestedLaneIds.length) {
4370
4476
  throw new LanesError("lane_list_filter_invalid", "--lane must be repeated with distinct, non-empty lane ids", { lane_ids: requestedLaneIds });
4371
4477
  }
4372
- if (opts.hierarchy !== undefined && (!opts.hierarchy.trim() || opts.hierarchy.trim() !== opts.hierarchy)) {
4373
- throw new LanesError("lane_list_hierarchy_invalid", "--hierarchy requires one non-empty lane id without surrounding whitespace", {});
4478
+ if (opts.hierarchy !== undefined &&
4479
+ (!Array.isArray(opts.hierarchy) || opts.hierarchy.length === 0 ||
4480
+ opts.hierarchy.some((laneId) => typeof laneId !== "string" || !laneId.trim() || laneId.trim() !== laneId) ||
4481
+ new Set(opts.hierarchy).size !== opts.hierarchy.length)) {
4482
+ throw new LanesError("lane_list_hierarchy_invalid", "--hierarchy must be repeated with distinct, non-empty lane ids without surrounding whitespace", {});
4374
4483
  }
4375
4484
  if (opts.snapshot && (opts.human || opts.company || opts.project || opts.story || opts.state || opts.senior)) {
4376
4485
  throw new LanesError("lane_list_snapshot_invalid", "--snapshot only supports owner lane filters and JSON output", {});
@@ -4395,7 +4504,7 @@ async function runList(ctx, opts) {
4395
4504
  : loadAllLanes(hqRoot);
4396
4505
  const hierarchyIds = opts.hierarchy === undefined
4397
4506
  ? undefined
4398
- : new Set(selectOutpostHireGrantHierarchy(opts.hierarchy, laneSource).map((lane) => lane.lane_id));
4507
+ : new Set(opts.hierarchy.flatMap((laneId) => selectOutpostHireGrantHierarchy(laneId, laneSource).map((lane) => lane.lane_id)));
4399
4508
  let selectedLanes = laneSource
4400
4509
  .filter((lane) => {
4401
4510
  if (requestedLaneIds.length > 0 && !requestedIds.has(lane.lane_id))
@@ -4462,7 +4571,33 @@ async function runList(ctx, opts) {
4462
4571
  ownerEligibleLaneIds.add(lane.lane_id);
4463
4572
  }
4464
4573
  const ownerLanes = selectedLanes.filter((lane) => ownerEligibleLaneIds.has(lane.lane_id));
4574
+ const refreshRemote = ctx.env.HQ_INTERNAL_LANES_REFRESH_REMOTE_SNAPSHOT === "1";
4575
+ let snapshotCacheEnabled = fastRouteFlags?.snapshotCacheEnabled ?? false;
4576
+ if (fastRouteFlags === undefined &&
4577
+ (refreshRemote || (!opts.fresh && ownerLanes.length > 0))) {
4578
+ try {
4579
+ snapshotCacheEnabled = await ctx.outpostSnapshotCacheEnabled();
4580
+ }
4581
+ catch (err) {
4582
+ process.stderr.write(`lanes snapshot cache flag read failed; using wait-for-remote list: ${dispatchErrorMessage(err)}\n`);
4583
+ }
4584
+ }
4585
+ if (refreshRemote) {
4586
+ if (snapshotCacheEnabled && ownerLanes.length > 0) {
4587
+ const outcomes = await batchOwnerLaneHireFields(ctx, ownerLanes, allLanes, { forceRefresh: true });
4588
+ const failures = [...outcomes.entries()].filter(([, outcome]) => "error" in outcome);
4589
+ if (failures.length > 0) {
4590
+ const firstOutcome = failures[0][1];
4591
+ const firstError = "error" in firstOutcome ? firstOutcome.error : undefined;
4592
+ process.stderr.write(`lanes snapshot refresh failed for ${failures.length} lane(s): ${firstError?.error ?? "outpost_lane_hire_summary_missing"}: ${firstError?.message ?? "owner summary outcome was missing"}\n`);
4593
+ throw firstError ?? new LanesError("outpost_lane_hire_summary_missing", "Background Outpost lane summary refresh returned a missing outcome", { lane_ids: failures.map(([laneId]) => laneId) });
4594
+ }
4595
+ }
4596
+ return;
4597
+ }
4465
4598
  const hireByLane = new Map();
4599
+ const remoteSnapshotMetadata = new Map();
4600
+ let remoteLanesLoading = false;
4466
4601
  for (const lane of selectedLanes) {
4467
4602
  if (!ownerEligibleLaneIds.has(lane.lane_id)) {
4468
4603
  hireByLane.set(lane.lane_id, {
@@ -4472,8 +4607,48 @@ async function runList(ctx, opts) {
4472
4607
  continue;
4473
4608
  }
4474
4609
  }
4475
- if (ownerLanes.length > 0) {
4476
- const ownerSummaries = await batchOwnerLaneHireFields(ctx, ownerLanes);
4610
+ if (snapshotCacheEnabled && !opts.fresh && ownerLanes.length > 0) {
4611
+ const nowMs = ctx.now().getTime();
4612
+ const cached = readOutpostHireSummaryCacheIncludingStale(hqRoot, nowMs);
4613
+ let refreshNeeded = false;
4614
+ for (const lane of ownerLanes) {
4615
+ if (lane.host.kind !== "outpost")
4616
+ continue;
4617
+ const summary = cached.get(outpostHireSummaryCacheKey(lane.host.id, lane.lane_id));
4618
+ if (!summary) {
4619
+ hireByLane.set(lane.lane_id, {
4620
+ ...laneHireFields(lane, allLanes, aliveChildrenByParent),
4621
+ hire_source: "local",
4622
+ });
4623
+ remoteSnapshotMetadata.set(lane.lane_id, { ageSeconds: null, stale: true, loading: true });
4624
+ refreshNeeded = true;
4625
+ remoteLanesLoading = true;
4626
+ continue;
4627
+ }
4628
+ const ageMs = Math.max(0, nowMs - Date.parse(summary.cached_at));
4629
+ const stale = ageMs >= OUTPOST_SNAPSHOT_CACHE_TTL_MS;
4630
+ hireByLane.set(lane.lane_id, {
4631
+ hire_grant: summary.hire_grant,
4632
+ hire_alive_children: summary.hire_alive_children,
4633
+ hire: summary.hire,
4634
+ hire_summary: summary.hire_summary,
4635
+ hire_source: "owner",
4636
+ });
4637
+ remoteSnapshotMetadata.set(lane.lane_id, {
4638
+ ageSeconds: Math.floor(ageMs / 1_000),
4639
+ stale,
4640
+ loading: stale,
4641
+ });
4642
+ if (stale) {
4643
+ refreshNeeded = true;
4644
+ remoteLanesLoading = true;
4645
+ }
4646
+ }
4647
+ if (refreshNeeded)
4648
+ ctx.startOutpostLaneSnapshotRefresh(hqRoot);
4649
+ }
4650
+ else if (ownerLanes.length > 0) {
4651
+ const ownerSummaries = await batchOwnerLaneHireFields(ctx, ownerLanes, allLanes);
4477
4652
  for (const lane of ownerLanes) {
4478
4653
  const outcome = ownerSummaries.get(lane.lane_id);
4479
4654
  if (outcome && "fields" in outcome) {
@@ -4506,6 +4681,15 @@ async function runList(ctx, opts) {
4506
4681
  ...(remoteQueued
4507
4682
  ? { state: "remote", remote_state: lane.state, queued_reason: undefined, queued_details: undefined }
4508
4683
  : {}),
4684
+ ...(remoteSnapshotMetadata.has(lane.lane_id)
4685
+ ? {
4686
+ remote_snapshot_age_s: remoteSnapshotMetadata.get(lane.lane_id).ageSeconds,
4687
+ remote_snapshot_stale: remoteSnapshotMetadata.get(lane.lane_id).stale,
4688
+ ...(remoteSnapshotMetadata.get(lane.lane_id).loading
4689
+ ? { remote_snapshot_loading: true }
4690
+ : {}),
4691
+ }
4692
+ : {}),
4509
4693
  overdue: Boolean(lane.overdue) || isOverdue(hqRoot, lane.lane_id, now),
4510
4694
  stalled: Boolean(lane.stalled),
4511
4695
  // Listing is observational. Reconcile owns durable liveness transitions.
@@ -4529,8 +4713,10 @@ async function runList(ctx, opts) {
4529
4713
  };
4530
4714
  });
4531
4715
  const placementCompany = company ?? processLanePlacementCompany(ctx);
4532
- const host_capacity = await readLanePlacementEnabled(ctx, placementCompany)
4533
- ? (await loadHostCapacitySnapshots(ctx, hqRoot)).entries
4716
+ const placementEnabled = fastRouteFlags?.lanePlacementEnabled ??
4717
+ await readLanePlacementEnabled(ctx, placementCompany);
4718
+ const host_capacity = placementEnabled
4719
+ ? (await loadHostCapacitySnapshots(ctx, hqRoot, undefined, allLanes)).entries
4534
4720
  : undefined;
4535
4721
  const result = {
4536
4722
  ok: true,
@@ -4539,24 +4725,44 @@ async function runList(ctx, opts) {
4539
4725
  ...(remoteRefresh.unverifiedRemote.size > 0
4540
4726
  ? { unverified_remote_lanes: [...remoteRefresh.unverifiedRemote.keys()] }
4541
4727
  : {}),
4728
+ ...(remoteLanesLoading ? { remote_lanes_loading: true } : {}),
4542
4729
  ...(host_capacity !== undefined ? { host_capacity } : {}),
4543
4730
  };
4544
4731
  if (opts.human)
4545
- console.log(formatLaneListHuman(lanes));
4732
+ console.log(formatLaneListHuman(lanes, remoteLanesLoading));
4546
4733
  else
4547
4734
  printJson(result);
4548
4735
  }
4549
- function formatLaneListHuman(lanes) {
4550
- if (lanes.length === 0)
4551
- return "No lanes.";
4552
- return lanes.map((lane) => {
4736
+ /** Keeps the placement-enabled cache-first path on the full lane command graph. */
4737
+ export async function runFastCachedLanesListFromEntrypoint() {
4738
+ try {
4739
+ await runList(resolveDeps(), { json: true }, {
4740
+ snapshotCacheEnabled: true,
4741
+ lanePlacementEnabled: true,
4742
+ });
4743
+ }
4744
+ catch (err) {
4745
+ failJson(err);
4746
+ }
4747
+ }
4748
+ function formatLaneListHuman(lanes, remoteLanesLoading = false) {
4749
+ const rows = lanes.map((lane) => {
4553
4750
  const ids = lane.story_ids;
4554
4751
  const storyLabel = ids.length > 0
4555
4752
  ? "stories: " + ids.join(", ")
4556
4753
  : "no story: " + (lane.no_story_reason ?? "reason not recorded");
4754
+ const snapshotNote = lane.remote_snapshot_stale && lane.remote_snapshot_age_s !== null &&
4755
+ lane.remote_snapshot_age_s !== undefined
4756
+ ? ` remote snapshot stale (${lane.remote_snapshot_age_s}s)`
4757
+ : "";
4557
4758
  return lane.lane_id + " " + lane.state + " " + lane.provider + " " + lane.project_id +
4558
- " " + storyLabel;
4559
- }).join("\n");
4759
+ " " + storyLabel + snapshotNote;
4760
+ });
4761
+ if (rows.length === 0)
4762
+ rows.push("No lanes.");
4763
+ if (remoteLanesLoading)
4764
+ rows.push("Remote lanes loading.");
4765
+ return rows.join("\n");
4560
4766
  }
4561
4767
  async function runStatus(ctx, opts) {
4562
4768
  if (!ctx.storyLedgerEnabled()) {
@@ -5238,7 +5444,7 @@ async function runGrant(ctx, laneId, opts) {
5238
5444
  await dispatchOutpostLaneCommand(ctx, initial, "grant", { hire: String(hireGrant) });
5239
5445
  return;
5240
5446
  }
5241
- const updated = await applyHireGrant(ctx, laneId, hireGrant, "senior");
5447
+ const updated = await applyHireGrant(ctx, initial.lane_id, hireGrant, "senior");
5242
5448
  printGrantResult(hqRoot, updated);
5243
5449
  }
5244
5450
  async function runApprove(ctx, laneId, opts) {
@@ -7765,8 +7971,10 @@ export function registerLanesCommand(parent, deps = {}) {
7765
7971
  .option("--senior <ref>", "Filter by senior ref")
7766
7972
  .option("--lane <id>", "Filter by lane id (repeatable)", (value, previous = []) => [...previous, value], [])
7767
7973
  .option("--active", "Include only lanes that are not terminal")
7768
- .addOption(new Option("--hierarchy <lane-id>").hideHelp())
7974
+ .addOption(new Option("--hierarchy <lane-id>").hideHelp()
7975
+ .argParser((value, previous = []) => [...previous, value]))
7769
7976
  .addOption(new Option("--snapshot").hideHelp())
7977
+ .option("--fresh", "Wait for fresh remote owner summaries")
7770
7978
  .option("--json", "Emit JSON on stdout (default)")
7771
7979
  .option("--human", "Emit human-readable output")
7772
7980
  .action(wrap(async (opts) => {
@@ -0,0 +1,8 @@
1
+ export interface FastLanesListRouteDeps {
2
+ flagEnabled: (flagKey: string, env: NodeJS.ProcessEnv) => Promise<boolean>;
3
+ runFastList: () => Promise<void>;
4
+ runPlacementList: () => Promise<void>;
5
+ }
6
+ /** Returns false unless this exact list form is safely enabled in the local flag snapshot. */
7
+ export declare function runFastLanesListRequest(argv: readonly string[], env?: NodeJS.ProcessEnv, deps?: FastLanesListRouteDeps): Promise<boolean>;
8
+ //# sourceMappingURL=fast-lanes-list-route.d.ts.map