@indigoai-us/hq-cli 5.345.77 → 5.345.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.78] — 2026-10-10
6
+ - `hq lanes` no longer fails every list, message, or admission command when
7
+ one lane still has leftover queue details from an older worker-profile wait.
8
+ Those leftover details are dropped on read, cleared when the queue reason
9
+ changes, and one unreadable lane is skipped so the rest of the host stays
10
+ usable.
11
+
5
12
  ## [5.345.77] — 2026-10-10
6
13
  - Local bots no longer fail a turn when the installed Codex is too old for the
7
14
  model it was asked to use (often the model a newer Codex app set in your own
@@ -677,6 +677,7 @@ export async function retryLaneOnAlternateAccount(hqRoot, lane, reason, writer =
677
677
  ...lane,
678
678
  state: "queued",
679
679
  queued_reason: "account_retry",
680
+ queued_details: undefined,
680
681
  queued_at: at,
681
682
  terminal_reason: undefined,
682
683
  terminal_details: undefined,
@@ -386,6 +386,28 @@ function parseLaneStories(raw, filePath) {
386
386
  }
387
387
  }
388
388
  }
389
+ /** Profile, disk, and admit details are only valid for their matching queue reason. */
390
+ function queuedDetailsAreStale(state, queuedReason, detailsReason) {
391
+ if (detailsReason === "worker_profile_changed") {
392
+ return queuedReason !== "worker_profile_changed";
393
+ }
394
+ if (detailsReason === "low_disk") {
395
+ return state !== "queued" || queuedReason !== "low_disk";
396
+ }
397
+ if (detailsReason === "admit_failed") {
398
+ return state !== "queued" || queuedReason !== "admit_failed";
399
+ }
400
+ return false;
401
+ }
402
+ function withoutStaleQueuedDetails(record) {
403
+ const details = record.queued_details;
404
+ if (details === undefined)
405
+ return record;
406
+ if (!queuedDetailsAreStale(record.state, record.queued_reason, details.reason)) {
407
+ return record;
408
+ }
409
+ return { ...record, queued_details: undefined };
410
+ }
389
411
  export function parseLaneRecord(raw, filePath) {
390
412
  if (!isPlainObject(raw)) {
391
413
  throw new LaneSchemaError("Lane record must be a JSON object", {
@@ -638,7 +660,11 @@ export function parseLaneRecord(raw, filePath) {
638
660
  });
639
661
  }
640
662
  const detailsRecord = details;
641
- if (detailsRecord.reason === "worker_profile_changed") {
663
+ if (queuedDetailsAreStale(raw.state, raw.queued_reason, detailsRecord.reason)) {
664
+ normalized = { ...raw };
665
+ delete normalized.queued_details;
666
+ }
667
+ else if (detailsRecord.reason === "worker_profile_changed") {
642
668
  if ((detailsRecord.expected_hash !== null && typeof detailsRecord.expected_hash !== "string") ||
643
669
  typeof detailsRecord.actual_hash !== "string" || detailsRecord.actual_hash === "" ||
644
670
  typeof detailsRecord.recovery_command !== "string" || detailsRecord.recovery_command.trim() === "") {
@@ -647,17 +673,6 @@ export function parseLaneRecord(raw, filePath) {
647
673
  field: "queued_details",
648
674
  });
649
675
  }
650
- if (raw.queued_reason !== "worker_profile_changed") {
651
- if (raw.queued_reason === undefined) {
652
- throw new LaneSchemaError("queued_details requires queued_reason worker_profile_changed", {
653
- path: filePath,
654
- field: "queued_details",
655
- queued_reason: raw.queued_reason,
656
- });
657
- }
658
- normalized = { ...raw };
659
- delete normalized.queued_details;
660
- }
661
676
  }
662
677
  else if (detailsRecord.reason === "outpost_dispatch_unknown") {
663
678
  if (raw.state !== "queued" ||
@@ -1505,10 +1520,10 @@ export function readAllLanes(hqRoot, deps = defaultStoreDeps()) {
1505
1520
  const identity = indexedDeps.outpostIdentity();
1506
1521
  const isRemoteMirror = outpostId !== undefined && (identity.kind === "not_outpost" ||
1507
1522
  (identity.kind === "id" && identity.id !== outpostId));
1508
- if (!isRemoteMirror)
1509
- throw err;
1510
1523
  const field = typeof err.details.field === "string" ? err.details.field : "unknown_field";
1511
- process.stderr.write(`lanes store: skipping malformed mirrored lane ${laneId} field ${field}: ${err.message}\n`);
1524
+ process.stderr.write(isRemoteMirror
1525
+ ? `lanes store: skipping malformed mirrored lane ${laneId} field ${field}: ${err.message}\n`
1526
+ : `lanes store: skipping unreadable lane ${laneId} field ${field}: ${err.message}\n`);
1512
1527
  }
1513
1528
  }
1514
1529
  return records.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at) ||
@@ -1631,13 +1646,14 @@ export function createLane(hqRoot, init, deps = defaultStoreDeps()) {
1631
1646
  senior_history: init.senior_history ?? [],
1632
1647
  attribution_history: init.attribution_history ?? [],
1633
1648
  };
1634
- parseLaneRecord(record, filePath);
1649
+ const createdRecord = withoutStaleQueuedDetails(record);
1650
+ parseLaneRecord(createdRecord, filePath);
1635
1651
  const created = withRecordLock(filePath, deps, () => {
1636
1652
  if (deps.existsSync(filePath)) {
1637
1653
  throw new LanesError("lane_exists", `Lane ${lane_id} already exists`, { lane_id });
1638
1654
  }
1639
- atomicWriteJson(filePath, record, deps);
1640
- return record;
1655
+ atomicWriteJson(filePath, createdRecord, deps);
1656
+ return createdRecord;
1641
1657
  });
1642
1658
  return syncStoryLedgerForLane(hqRoot, created, deps);
1643
1659
  }
@@ -1689,30 +1705,30 @@ export function updateLane(hqRoot, laneId, expectedVersion, writer, updater, dep
1689
1705
  }
1690
1706
  if (!isTerminalLaneState(current.state) && isTerminalLaneState(updated.state)) {
1691
1707
  const releasedGrant = updated.hire_grant ?? current.hire_grant ?? 0;
1692
- return {
1708
+ return withoutStaleQueuedDetails({
1693
1709
  ...updated,
1694
1710
  released_hire_grant: releasedGrant,
1695
1711
  hire_grant: 0,
1696
- };
1712
+ });
1697
1713
  }
1698
1714
  if (isTerminalLaneState(current.state) && !isTerminalLaneState(updated.state) &&
1699
1715
  current.released_hire_grant !== undefined) {
1700
- return {
1716
+ return withoutStaleQueuedDetails({
1701
1717
  ...updated,
1702
1718
  hire_grant: current.released_hire_grant,
1703
1719
  released_hire_grant: undefined,
1704
- };
1720
+ });
1705
1721
  }
1706
1722
  if (isTerminalLaneState(updated.state) &&
1707
1723
  (updated.hire_grant !== current.hire_grant ||
1708
1724
  updated.released_hire_grant !== current.released_hire_grant)) {
1709
- return {
1725
+ return withoutStaleQueuedDetails({
1710
1726
  ...updated,
1711
1727
  hire_grant: 0,
1712
1728
  released_hire_grant: updated.released_hire_grant ?? updated.hire_grant ?? 0,
1713
- };
1729
+ });
1714
1730
  }
1715
- return updated;
1731
+ return withoutStaleQueuedDetails(updated);
1716
1732
  }, deps, {
1717
1733
  beforeWrite: (current) => assertLaneStoreWriteAllowed(current, laneId, writer),
1718
1734
  stamp: (current) => ({ written_by_version: newestLaneStoreVersion(current.written_by_version) }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.77",
3
+ "version": "5.345.78",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {