@indigoai-us/hq-cli 5.272.0 → 5.273.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.
@@ -21,7 +21,6 @@ import { registerOutpostPrunerCommand } from "./outpost-pruner.js";
21
21
  import { registerLaneKpisCommand } from "./lanes-kpis.js";
22
22
  import { registerLaneChecksCommand } from "./lanes-checks.js";
23
23
  import { registerLaneReportCommands } from "./lanes-report.js";
24
- import { registerLaneTrackerImportCommand } from "./lanes-import.js";
25
24
  import { registerLaneRepoMergeHoldCommand } from "./lanes-hold.js";
26
25
  import { registerLaneReviewCommand } from "./lanes-review.js";
27
26
  import { ensureCognitoToken } from "../utils/cognito-session.js";
@@ -29,7 +28,6 @@ import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
29
28
  import { isSsmRunCommandProcess } from "../lib/lanes/ssm-command-context.js";
30
29
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
31
30
  import { resolveProcessFlagCompanyContext } from "../lib/flag-registry.js";
32
- import { defaultTrackerActor, LANES_TRACKER_FLAG, registerLaneTrackerCommands, } from "./lanes-tracker.js";
33
31
  import { createOutpostHireGrantContext, parseOutpostHireGrantContext, sameHireGrantSenior, } from "../lib/lanes/hire-grant-dispatch.js";
34
32
  import { createOutpostProfileRepinContext, parseOutpostProfileRepinContext, } from "../lib/lanes/outpost-profile-repin.js";
35
33
  import { createOutpostRegistryWorkerApprovalContext, parseOutpostRegistryWorkerApprovalContext, } from "../lib/lanes/outpost-registry-worker-approval.js";
@@ -109,9 +107,6 @@ async function readLanePlacementEnabled(ctx, company) {
109
107
  return false;
110
108
  }
111
109
  }
112
- function defaultLaneTrackerEnabled(company) {
113
- return readLanesRegistryFlag(LANES_TRACKER_FLAG, { company });
114
- }
115
110
  function processLanePlacementCompany(ctx) {
116
111
  const context = resolveProcessFlagCompanyContext(ctx.env);
117
112
  if (!context.companySlug)
@@ -328,9 +323,8 @@ function resolveDeps(deps = {}) {
328
323
  storyLedgerEnabled: deps.storyLedgerEnabled ?? laneStoryLedgerEnabled,
329
324
  isSsmOwnerDispatch: deps.isSsmOwnerDispatch ?? (() => isSsmRunCommandProcess()),
330
325
  withGrantHireLocks: deps.withGrantHireLocks ?? withLaneHireLocks,
331
- laneTrackerEnabled: deps.laneTrackerEnabled ?? defaultLaneTrackerEnabled,
332
326
  mergeReviewEnabled: deps.mergeReviewEnabled,
333
- trackerActor: deps.trackerActor ?? defaultTrackerActor,
327
+ mergeHoldEnabled: deps.mergeHoldEnabled,
334
328
  };
335
329
  }
336
330
  function livenessDepsFromCtx(ctx) {
@@ -3497,7 +3491,7 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options, behavior =
3497
3491
  }
3498
3492
  else if ((action === "resume" || action === "message") &&
3499
3493
  lane.senior.kind === "lane") {
3500
- grantContext = createOutpostHireGrantContext(lane, loadAllLanes(ctx.hqRoot()));
3494
+ grantContext = createOutpostHireGrantContext(lane, loadAllLanes(ctx.hqRoot()), action === "message" ? options.supersededMessageCallerId : undefined);
3501
3495
  }
3502
3496
  let dispatched;
3503
3497
  let grantContextRejected = false;
@@ -7083,23 +7077,9 @@ export function registerLanesCommand(parent, deps = {}) {
7083
7077
  const lanes = parent
7084
7078
  .command("lanes", { hidden: true })
7085
7079
  .description("Create, supervise, and steer worker lanes; routed decision queue");
7086
- registerLaneTrackerCommands(lanes, {
7087
- hqRoot: ctx.hqRoot,
7088
- resolveCompany: ctx.resolveCompany,
7089
- trackerEnabled: ctx.laneTrackerEnabled,
7090
- now: ctx.now,
7091
- actor: ctx.trackerActor,
7092
- }, wrap);
7093
- registerLaneTrackerImportCommand(lanes, {
7094
- hqRoot: ctx.hqRoot,
7095
- resolveCompany: ctx.resolveCompany,
7096
- trackerEnabled: ctx.laneTrackerEnabled,
7097
- now: ctx.now,
7098
- }, wrap);
7099
7080
  registerLaneChecksCommand(lanes, {
7100
7081
  hqRoot: ctx.hqRoot,
7101
7082
  resolveCompany: ctx.resolveCompany,
7102
- trackerEnabled: ctx.laneTrackerEnabled,
7103
7083
  now: ctx.now,
7104
7084
  stopLane: (laneId, reason) => runStop(ctx, laneId, reason, true),
7105
7085
  sessionState: (root, sessionId, now) => sessionLiveness(root, sessionId, {
@@ -7111,7 +7091,6 @@ export function registerLanesCommand(parent, deps = {}) {
7111
7091
  registerLaneReportCommands(lanes, {
7112
7092
  hqRoot: ctx.hqRoot,
7113
7093
  resolveCompany: ctx.resolveCompany,
7114
- trackerEnabled: ctx.laneTrackerEnabled,
7115
7094
  now: ctx.now,
7116
7095
  readQuestionBody: (root, question) => {
7117
7096
  const body = questionHandlers.requireQuestionBody(root, question);
@@ -7124,6 +7103,7 @@ export function registerLanesCommand(parent, deps = {}) {
7124
7103
  resolveCompany: ctx.resolveCompany,
7125
7104
  now: ctx.now,
7126
7105
  env: ctx.env,
7106
+ mergeHoldEnabled: ctx.mergeHoldEnabled,
7127
7107
  mergeReviewEnabled: ctx.mergeReviewEnabled,
7128
7108
  }, wrap);
7129
7109
  registerLaneReviewCommand(lanes, {
@@ -0,0 +1,7 @@
1
+ import type { CheckContext, CheckFamily, CheckResult } from "../types.js";
2
+ export declare const SCAFFOLD_CLI_FLOOR_FAMILY_ID = "scaffold-cli-floor";
3
+ export declare const SCAFFOLD_CLI_FLOOR_FAMILY_TITLE = "Scaffold CLI compatibility";
4
+ export declare const SCAFFOLD_CLI_FLOOR_CHECK_ID = "scaffold-cli-floor.requires-hq-cli";
5
+ export declare const scaffoldCliFloorFamily: CheckFamily;
6
+ export declare function checkScaffoldCliFloor(context: CheckContext): CheckResult[];
7
+ //# sourceMappingURL=scaffold-cli-floor.d.ts.map
@@ -0,0 +1,73 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import * as yaml from "js-yaml";
4
+ import { CLI_VERSION } from "../../../cli-version.js";
5
+ import { compareScaffoldCliFloor } from "../../scaffold-cli-floor.js";
6
+ export const SCAFFOLD_CLI_FLOOR_FAMILY_ID = "scaffold-cli-floor";
7
+ export const SCAFFOLD_CLI_FLOOR_FAMILY_TITLE = "Scaffold CLI compatibility";
8
+ export const SCAFFOLD_CLI_FLOOR_CHECK_ID = "scaffold-cli-floor.requires-hq-cli";
9
+ export const scaffoldCliFloorFamily = {
10
+ id: SCAFFOLD_CLI_FLOOR_FAMILY_ID,
11
+ title: SCAFFOLD_CLI_FLOOR_FAMILY_TITLE,
12
+ run: (context) => Promise.resolve(checkScaffoldCliFloor(context)),
13
+ };
14
+ export function checkScaffoldCliFloor(context) {
15
+ const coreYamlPath = path.join(context.hqRoot, "core", "core.yaml");
16
+ if (!fs.existsSync(coreYamlPath)) {
17
+ return [skipped(coreYamlPath, "core/core.yaml is missing")];
18
+ }
19
+ let document;
20
+ try {
21
+ document = yaml.load(fs.readFileSync(coreYamlPath, "utf8"));
22
+ }
23
+ catch {
24
+ return [
25
+ {
26
+ status: "WARN",
27
+ checkId: SCAFFOLD_CLI_FLOOR_CHECK_ID,
28
+ target: coreYamlPath,
29
+ message: "core/core.yaml could not be parsed, so the hq-cli floor was not checked.",
30
+ },
31
+ ];
32
+ }
33
+ if (!document || typeof document !== "object" || Array.isArray(document)) {
34
+ if (document === undefined || document === null) {
35
+ return [skipped(coreYamlPath, "core/core.yaml does not declare requiresHqCli")];
36
+ }
37
+ return [
38
+ {
39
+ status: "WARN",
40
+ checkId: SCAFFOLD_CLI_FLOOR_CHECK_ID,
41
+ target: coreYamlPath,
42
+ message: "core/core.yaml is not a mapping, so the hq-cli floor was not checked.",
43
+ },
44
+ ];
45
+ }
46
+ const required = document.requiresHqCli;
47
+ const comparison = compareScaffoldCliFloor(required, CLI_VERSION);
48
+ if (comparison.status === "skip") {
49
+ return [skipped(coreYamlPath, "core/core.yaml does not declare requiresHqCli")];
50
+ }
51
+ return [
52
+ {
53
+ status: comparison.status === "pass"
54
+ ? "PASS"
55
+ : comparison.status === "fail"
56
+ ? "FAIL"
57
+ : "WARN",
58
+ checkId: SCAFFOLD_CLI_FLOOR_CHECK_ID,
59
+ target: coreYamlPath,
60
+ message: comparison.message,
61
+ ...(comparison.remediation ? { remediation: comparison.remediation } : {}),
62
+ },
63
+ ];
64
+ }
65
+ function skipped(target, reason) {
66
+ return {
67
+ status: "NA",
68
+ checkId: SCAFFOLD_CLI_FLOOR_CHECK_ID,
69
+ target,
70
+ message: `Skipped: ${reason}; no hq-cli floor was checked.`,
71
+ };
72
+ }
73
+ //# sourceMappingURL=scaffold-cli-floor.js.map
@@ -23,6 +23,7 @@ import { integrationsFamily } from "./checks/integrations.js";
23
23
  import { syncHealthFamily } from "./checks/sync-health.js";
24
24
  import { workContextFamily } from "./checks/work-context.js";
25
25
  import { botsFamily } from "./checks/bots.js";
26
+ import { scaffoldCliFloorFamily } from "./checks/scaffold-cli-floor.js";
26
27
  import { fixtureCoverageFamily } from "./fixtures/discover.js";
27
28
  import { checkClaudeWiring } from "./checks/claude-wiring.js";
28
29
  /**
@@ -236,6 +237,7 @@ function hasCommandHook(settings, event) {
236
237
  export function createDefaultRegistry() {
237
238
  const registry = new DoctorRegistry();
238
239
  registry.register(hooksFamily);
240
+ registry.register(scaffoldCliFloorFamily);
239
241
  // Fixture coverage (US-007): reports UNTESTED for every registered hook with
240
242
  // no fixture and prints the tested/total coverage line. A separate family so
241
243
  // it needs no change to the hooks tier, per the registry's extensibility
@@ -5,15 +5,20 @@ export interface HireGrantLaneSnapshot {
5
5
  state: LaneState;
6
6
  senior: SeniorRef;
7
7
  hire_grant?: number;
8
+ created_at?: string;
9
+ company?: LaneRecord["company"];
10
+ project_id?: string;
11
+ worker_profile?: string;
8
12
  }
9
13
  export interface OutpostHireGrantContext {
10
14
  schema_version: typeof HIRE_GRANT_CONTEXT_SCHEMA;
11
15
  enforcement_enabled: boolean;
12
16
  expected_senior: SeniorRef;
13
17
  hierarchy: HireGrantLaneSnapshot[];
18
+ superseded_message_caller_id?: string;
14
19
  }
15
20
  /** Encode only the owner-relevant hierarchy from the primary's complete store. */
16
- export declare function createOutpostHireGrantContext(target: LaneRecord, lanes: readonly LaneRecord[]): string;
21
+ export declare function createOutpostHireGrantContext(target: LaneRecord, lanes: readonly LaneRecord[], supersededMessageCallerId?: string): string;
17
22
  /** Decode and validate an owner grant context, binding it to the target lane. */
18
23
  export declare function parseOutpostHireGrantContext(encoded: string, targetLaneId: string): OutpostHireGrantContext;
19
24
  export declare function sameHireGrantSenior(a: SeniorRef, b: SeniorRef): boolean;
@@ -20,11 +20,17 @@ function snapshot(lane) {
20
20
  lane_id: lane.lane_id,
21
21
  state: lane.state,
22
22
  senior: copySenior(lane.senior),
23
+ created_at: lane.created_at,
24
+ ...(lane.company === undefined
25
+ ? {}
26
+ : { company: lane.company === null ? null : { ...lane.company } }),
27
+ ...(lane.project_id === undefined ? {} : { project_id: lane.project_id }),
28
+ ...(lane.worker_profile === undefined ? {} : { worker_profile: lane.worker_profile }),
23
29
  ...(lane.hire_grant === undefined ? {} : { hire_grant: lane.hire_grant }),
24
30
  };
25
31
  }
26
32
  /** Encode only the owner-relevant hierarchy from the primary's complete store. */
27
- export function createOutpostHireGrantContext(target, lanes) {
33
+ export function createOutpostHireGrantContext(target, lanes, supersededMessageCallerId) {
28
34
  const current = lanes.find((lane) => lane.lane_id === target.lane_id);
29
35
  if (!current || !sameSenior(current.senior, target.senior)) {
30
36
  throw new LanesError("hire_grant_hierarchy_unavailable", `Cannot build an authoritative hire-grant hierarchy for lane ${target.lane_id}`, { lane_id: target.lane_id });
@@ -45,6 +51,20 @@ export function createOutpostHireGrantContext(target, lanes) {
45
51
  for (const child of aliveDirectChildren(lanes, current.lane_id)) {
46
52
  relevant.set(child.lane_id, child);
47
53
  }
54
+ if (supersededMessageCallerId !== undefined) {
55
+ const messageCaller = lanes.find((lane) => lane.lane_id === supersededMessageCallerId);
56
+ const companyUid = current.company?.uid;
57
+ const projectId = current.project_id;
58
+ if (!messageCaller || !companyUid || !projectId ||
59
+ messageCaller.company?.uid !== companyUid || messageCaller.project_id !== projectId) {
60
+ throw new LanesError("hire_grant_message_caller_invalid", `Cannot include an unverified message caller ${supersededMessageCallerId} in lane ${target.lane_id}'s Outpost context`, { lane_id: target.lane_id, caller_lane_id: supersededMessageCallerId });
61
+ }
62
+ for (const candidate of lanes) {
63
+ if (candidate.company?.uid === companyUid && candidate.project_id === projectId) {
64
+ relevant.set(candidate.lane_id, candidate);
65
+ }
66
+ }
67
+ }
48
68
  const hierarchy = [...relevant.values()]
49
69
  .map(snapshot)
50
70
  .sort((a, b) => a.lane_id.localeCompare(b.lane_id));
@@ -56,6 +76,9 @@ export function createOutpostHireGrantContext(target, lanes) {
56
76
  enforcement_enabled: true,
57
77
  expected_senior: copySenior(current.senior),
58
78
  hierarchy,
79
+ ...(supersededMessageCallerId === undefined
80
+ ? {}
81
+ : { superseded_message_caller_id: supersededMessageCallerId }),
59
82
  };
60
83
  const encoded = Buffer.from(JSON.stringify(context), "utf8").toString("base64url");
61
84
  if (Buffer.byteLength(encoded, "utf8") > MAX_CONTEXT_BYTES) {
@@ -86,7 +109,9 @@ function parseLaneSnapshot(value) {
86
109
  if (value === null || typeof value !== "object" || Array.isArray(value))
87
110
  return null;
88
111
  const record = value;
89
- if (Object.keys(record).some((key) => !["lane_id", "state", "senior", "hire_grant"].includes(key)))
112
+ if (Object.keys(record).some((key) => ![
113
+ "lane_id", "state", "senior", "hire_grant", "created_at", "company", "project_id", "worker_profile",
114
+ ].includes(key)))
90
115
  return null;
91
116
  if (typeof record.lane_id !== "string" || !record.lane_id.trim())
92
117
  return null;
@@ -99,11 +124,44 @@ function parseLaneSnapshot(value) {
99
124
  (typeof record.hire_grant !== "number" || !Number.isSafeInteger(record.hire_grant) || record.hire_grant < 0)) {
100
125
  return null;
101
126
  }
127
+ if (record.created_at !== undefined &&
128
+ (typeof record.created_at !== "string" || !record.created_at.trim() || !Number.isFinite(Date.parse(record.created_at)))) {
129
+ return null;
130
+ }
131
+ if (record.project_id !== undefined &&
132
+ (typeof record.project_id !== "string" || !record.project_id.trim() || record.project_id.trim() !== record.project_id)) {
133
+ return null;
134
+ }
135
+ if (record.worker_profile !== undefined &&
136
+ (typeof record.worker_profile !== "string" || !record.worker_profile.trim())) {
137
+ return null;
138
+ }
139
+ let company;
140
+ if (Object.hasOwn(record, "company")) {
141
+ if (record.company === null) {
142
+ company = null;
143
+ }
144
+ else if (record.company !== null && typeof record.company === "object" && !Array.isArray(record.company)) {
145
+ const companyRecord = record.company;
146
+ if (Object.keys(companyRecord).some((key) => key !== "slug" && key !== "uid") ||
147
+ typeof companyRecord.slug !== "string" || !companyRecord.slug.trim() ||
148
+ typeof companyRecord.uid !== "string" || !companyRecord.uid.trim())
149
+ return null;
150
+ company = { slug: companyRecord.slug, uid: companyRecord.uid };
151
+ }
152
+ else {
153
+ return null;
154
+ }
155
+ }
102
156
  return {
103
157
  lane_id: record.lane_id,
104
158
  state: record.state,
105
159
  senior,
106
160
  ...(record.hire_grant === undefined ? {} : { hire_grant: record.hire_grant }),
161
+ ...(record.created_at === undefined ? {} : { created_at: record.created_at }),
162
+ ...(company === undefined ? {} : { company }),
163
+ ...(record.project_id === undefined ? {} : { project_id: record.project_id }),
164
+ ...(record.worker_profile === undefined ? {} : { worker_profile: record.worker_profile }),
107
165
  };
108
166
  }
109
167
  /** Decode and validate an owner grant context, binding it to the target lane. */
@@ -125,7 +183,7 @@ export function parseOutpostHireGrantContext(encoded, targetLaneId) {
125
183
  throw contextError("Outpost grant context must be an object");
126
184
  }
127
185
  const record = raw;
128
- if (Object.keys(record).some((key) => !["schema_version", "enforcement_enabled", "expected_senior", "hierarchy"].includes(key))) {
186
+ if (Object.keys(record).some((key) => !["schema_version", "enforcement_enabled", "expected_senior", "hierarchy", "superseded_message_caller_id"].includes(key))) {
129
187
  throw contextError("Outpost grant context contains unknown fields");
130
188
  }
131
189
  if (record.schema_version !== HIRE_GRANT_CONTEXT_SCHEMA ||
@@ -135,6 +193,11 @@ export function parseOutpostHireGrantContext(encoded, targetLaneId) {
135
193
  throw contextError("Outpost grant context has an unsupported or incomplete shape");
136
194
  }
137
195
  const expectedSenior = parseSenior(record.expected_senior);
196
+ const messageCallerId = record.superseded_message_caller_id;
197
+ if (messageCallerId !== undefined &&
198
+ (typeof messageCallerId !== "string" || !messageCallerId.trim() || messageCallerId.trim() !== messageCallerId)) {
199
+ throw contextError("Outpost grant context contains an invalid superseded-message caller");
200
+ }
138
201
  const hierarchy = record.hierarchy.map(parseLaneSnapshot);
139
202
  if (!expectedSenior || hierarchy.some((lane) => lane === null)) {
140
203
  throw contextError("Outpost grant context contains an invalid senior or lane snapshot");
@@ -152,11 +215,23 @@ export function parseOutpostHireGrantContext(encoded, targetLaneId) {
152
215
  (!expectedSenior.id || !byId.has(expectedSenior.id))) {
153
216
  throw contextError("Outpost grant context is missing the target's senior lane");
154
217
  }
218
+ if (messageCallerId !== undefined && !byId.has(messageCallerId)) {
219
+ throw contextError("Outpost grant context is missing its superseded-message caller lane");
220
+ }
221
+ if (messageCallerId !== undefined) {
222
+ const caller = byId.get(messageCallerId);
223
+ if (snapshots.some((lane) => lane.created_at === undefined || !Object.hasOwn(lane, "company")) ||
224
+ !target.company?.uid || !target.project_id ||
225
+ caller?.company?.uid !== target.company.uid || caller.project_id !== target.project_id) {
226
+ throw contextError("Outpost grant context is missing its superseded-message hierarchy metadata");
227
+ }
228
+ }
155
229
  return {
156
230
  schema_version: HIRE_GRANT_CONTEXT_SCHEMA,
157
231
  enforcement_enabled: record.enforcement_enabled,
158
232
  expected_senior: expectedSenior,
159
233
  hierarchy: snapshots,
234
+ ...(messageCallerId === undefined ? {} : { superseded_message_caller_id: messageCallerId }),
160
235
  };
161
236
  }
162
237
  export function sameHireGrantSenior(a, b) {
@@ -7,6 +7,7 @@ export declare const LANE_SUPERSESSION_LINEAGE_AWARE_DEFAULT_ENABLED = false;
7
7
  export declare const LEAD_LANE_KEEP_ALIVE_MAX_RESUMES = 24;
8
8
  export declare const LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS: number;
9
9
  export declare const LEAD_LANE_KEEP_ALIVE_MAX_BACKOFF_MS: number;
10
+ export type KeepAliveLane = Pick<LaneRecord, "lane_id" | "created_at" | "senior" | "company" | "project_id"> & Partial<Pick<LaneRecord, "mesh_company" | "worker_profile" | "hire_grant" | "provider_session_id" | "last_resume" | "state">>;
10
11
  export interface LaneTerminalResumeResult {
11
12
  lane: LaneRecord;
12
13
  resumeQueued: boolean;
@@ -40,7 +41,7 @@ export declare function retiredLaneRegistryPath(hqRoot: string): string;
40
41
  export declare function loadRetiredLaneIds(hqRoot: string): ReadonlySet<string>;
41
42
  /** IDs may be stored bare or with an owner-host suffix after an underscore. */
42
43
  export declare function isRetiredLaneId(laneId: string, retiredLaneIds: ReadonlySet<string>): boolean;
43
- export declare function keepAliveSupersededBy(lane: LaneRecord, lanes: readonly LaneRecord[], lineageAware?: boolean, companyForLane?: (lane: LaneRecord) => LaneRecord["company"]): LaneRecord | undefined;
44
- export declare function keepAliveExclusion(lane: LaneRecord, lanes: readonly LaneRecord[], retiredLaneIds: ReadonlySet<string>, lineageAware?: boolean, companyForLane?: (lane: LaneRecord) => LaneRecord["company"]): "retired" | "superseded" | undefined;
44
+ export declare function keepAliveSupersededBy<T extends KeepAliveLane>(lane: T, lanes: readonly T[], lineageAware?: boolean, companyForLane?: (lane: T) => T["company"]): T | undefined;
45
+ export declare function keepAliveExclusion<T extends KeepAliveLane>(lane: T, lanes: readonly T[], retiredLaneIds: ReadonlySet<string>, lineageAware?: boolean, companyForLane?: (lane: T) => T["company"]): "retired" | "superseded" | undefined;
45
46
  export declare function leadLaneKeepAliveBackoffMs(attempt: number): number;
46
47
  //# sourceMappingURL=keep-alive.d.ts.map
@@ -204,7 +204,7 @@ export function keepAliveSupersededBy(lane, lanes, lineageAware = LANE_SUPERSESS
204
204
  if (!Number.isFinite(createdAt))
205
205
  return undefined;
206
206
  const companyOf = companyForLane ?? ((candidate) => candidate.company);
207
- const laneCompany = companyOf(lane);
207
+ const laneCompany = companyOf(lane) ?? null;
208
208
  const lanesById = new Map(lanes.map((candidate) => [candidate.lane_id, candidate]));
209
209
  lanesById.set(lane.lane_id, lane);
210
210
  const sessionOwners = new Map();
@@ -223,7 +223,7 @@ export function keepAliveSupersededBy(lane, lanes, lineageAware = LANE_SUPERSESS
223
223
  return false;
224
224
  if (candidate.state === "cancelled" || candidate.state === "killed" || candidate.state === "failed")
225
225
  return false;
226
- const candidateCompany = companyOf(candidate);
226
+ const candidateCompany = companyOf(candidate) ?? null;
227
227
  const sameCompany = laneCompany === null
228
228
  ? candidateCompany === null
229
229
  : candidateCompany?.uid === laneCompany.uid;
@@ -8,6 +8,7 @@ export interface MergeCommandTarget {
8
8
  export interface MergeCommandParseResult {
9
9
  targets: MergeCommandTarget[];
10
10
  unresolved: boolean;
11
+ unparseableMerge: boolean;
11
12
  }
12
13
  export declare function canonicalRepo(raw: string): string;
13
14
  export declare function parseMergeCommands(command: string): MergeCommandParseResult;