@indigoai-us/hq-cli 5.226.0 → 5.227.1

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.
@@ -6,6 +6,7 @@ import { LanesError } from "../lib/lanes/errors.js";
6
6
  import { resolveProjectStoryLedger } from "../lib/lanes/story-ledger.js";
7
7
  import { assertNoSecretText } from "../lib/lanes/text-scan.js";
8
8
  import { createLocalTrackerStore } from "../lib/lanes/tracker-store.js";
9
+ import { createWorkMeshTrackerStore } from "../lib/lanes/tracker-mesh.js";
9
10
  import { peekIdToken } from "../utils/id-token.js";
10
11
  export const LANES_TRACKER_FLAG = "lanes.tracker";
11
12
  export const LANES_TRACKER_MESH_FLAG = "lanes.tracker-mesh";
@@ -72,9 +73,7 @@ async function target(deps, options) {
72
73
  if (!await deps.trackerEnabled(company)) {
73
74
  fail("tracker_disabled", "hq lanes tracker commands require the lanes.tracker flag", { company: company.slug });
74
75
  }
75
- if (await deps.trackerMeshEnabled(company)) {
76
- fail("tracker_mesh_unavailable", "Work Mesh tracker storage is not available in this CLI version", { company: company.slug });
77
- }
76
+ const useMesh = await deps.trackerMeshEnabled(company);
78
77
  const project = validSegment(options.project, "project");
79
78
  const ledger = resolveProjectStoryLedger(deps.hqRoot(), project, company.slug);
80
79
  const projectCompany = readProjectCompany(ledger.path);
@@ -106,7 +105,10 @@ async function target(deps, options) {
106
105
  return {
107
106
  company,
108
107
  project,
109
- store: (deps.store ?? ((dir) => createLocalTrackerStore(dir, deps.now)))(realDirectory),
108
+ usesMesh: useMesh,
109
+ store: useMesh
110
+ ? (deps.meshStore ?? ((resolvedCompany, resolvedProject) => createWorkMeshTrackerStore({ company: resolvedCompany, projectId: resolvedProject, now: deps.now })))(company, project)
111
+ : deps.store?.(realDirectory) ?? createLocalTrackerStore(realDirectory, deps.now),
110
112
  };
111
113
  }
112
114
  function requireActor(deps) {
@@ -184,8 +186,8 @@ function addProjectOptions(command) {
184
186
  .requiredOption("--project <slug>", "HQ team project slug")
185
187
  .option("--company <slug>", "Select the company by slug");
186
188
  }
187
- function createRecord(store, kind, company, project, fields) {
188
- return store.create({ kind, id: randomUUID(), company, project_id: project, ...fields });
189
+ async function createRecord(store, kind, company, project, fields) {
190
+ return await store.create({ kind, id: randomUUID(), company, project_id: project, ...fields });
189
191
  }
190
192
  export function registerLaneTrackerCommands(parent, deps, wrap) {
191
193
  const blocker = parent.command("blocker").description("Record and manage project blockers");
@@ -200,7 +202,7 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
200
202
  fail("tracker_input_invalid", "story must not be empty", { field: "story" });
201
203
  for (const [field, value] of [["what", options.what], ["waiting_on", options.waitingOn], ["next_step", options.nextStep]])
202
204
  checkedText(field, value);
203
- const record = createRecord(store, "blocker", company.slug, project, {
205
+ const record = await createRecord(store, "blocker", company.slug, project, {
204
206
  story_id: options.story.trim(), what: options.what, waiting_on: options.waitingOn,
205
207
  next_step: options.nextStep, opened_at: deps.now().toISOString(), resolved_at: null, resolution: null,
206
208
  });
@@ -217,12 +219,12 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
217
219
  checkedText("waiting_on", options.waitingOn);
218
220
  if (options.nextStep !== undefined)
219
221
  checkedText("next_step", options.nextStep);
220
- const current = store.read("blocker", id);
222
+ const current = await store.read("blocker", id);
221
223
  if (!current)
222
224
  fail("tracker_record_not_found", `Tracker blocker record ${id} was not found`, { kind: "blocker", id });
223
225
  if (current.resolved_at !== null)
224
226
  fail("tracker_already_resolved", "Blocker is already resolved", { id });
225
- const record = store.update("blocker", id, current.version, (value) => ({
227
+ const record = await store.update("blocker", id, current.version, (value) => ({
226
228
  ...value,
227
229
  ...(options.waitingOn === undefined ? {} : { waiting_on: options.waitingOn }),
228
230
  ...(options.nextStep === undefined ? {} : { next_step: options.nextStep }),
@@ -234,18 +236,18 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
234
236
  .action(wrap(async (id, options) => {
235
237
  const { store } = await target(deps, options);
236
238
  checkedText("resolution", options.resolution);
237
- const current = store.read("blocker", id);
239
+ const current = await store.read("blocker", id);
238
240
  if (!current)
239
241
  fail("tracker_record_not_found", `Tracker blocker record ${id} was not found`, { kind: "blocker", id });
240
242
  if (current.resolved_at !== null)
241
243
  fail("tracker_already_resolved", "Blocker is already resolved", { id });
242
- const record = store.update("blocker", id, current.version, (value) => ({ ...value, resolved_at: deps.now().toISOString(), resolution: options.resolution }));
244
+ const record = await store.update("blocker", id, current.version, (value) => ({ ...value, resolved_at: deps.now().toISOString(), resolution: options.resolution }));
243
245
  console.log(JSON.stringify({ ok: true, action: "blocker.resolve", record }, null, 2));
244
246
  }));
245
247
  addProjectOptions(blocker.command("list").description("List blockers")).option("--all", "Include resolved blockers")
246
248
  .action(wrap(async (options) => {
247
249
  const { store } = await target(deps, options);
248
- const records = store.list("blocker").filter((record) => options.all || record.resolved_at === null);
250
+ const records = (await store.list("blocker")).filter((record) => options.all || record.resolved_at === null);
249
251
  console.log(JSON.stringify({ ok: true, action: "blocker.list", records }, null, 2));
250
252
  }));
251
253
  const kpi = parent.command("kpi").description("Define KPIs and record values");
@@ -262,7 +264,7 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
262
264
  checkedText("name", options.name);
263
265
  checkedText("definition", options.definition);
264
266
  requiredText("unit", options.unit);
265
- const record = createRecord(store, "kpi", company.slug, project, {
267
+ const record = await createRecord(store, "kpi", company.slug, project, {
266
268
  name: options.name, definition: options.definition, unit: options.unit,
267
269
  target: numberValue(options.target, "target"), direction: options.direction,
268
270
  status: "proposed", approved_by: null, approved_at: null, values: [],
@@ -278,31 +280,34 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
278
280
  checkedText("source", options.source);
279
281
  const value = numberValue(options.value, "value");
280
282
  const at = options.at === undefined ? deps.now() : validTimestamp(options.at);
281
- const current = store.read("kpi", id);
283
+ const current = await store.read("kpi", id);
282
284
  if (!current)
283
285
  fail("tracker_record_not_found", `Tracker KPI record ${id} was not found`, { kind: "kpi", id });
284
286
  const values = current.values;
285
287
  if (!Array.isArray(values))
286
288
  fail("tracker_record_invalid", "KPI values must be an array", { id });
287
- const record = store.update("kpi", id, current.version, (record) => ({ ...record, values: [...values, { at: at.toISOString(), value, source: options.source }] }));
289
+ const record = await store.update("kpi", id, current.version, (record) => ({ ...record, values: [...values, { at: at.toISOString(), value, source: options.source }] }));
288
290
  console.log(JSON.stringify({ ok: true, action: "kpi.record", record }, null, 2));
289
291
  }));
290
292
  addProjectOptions(kpi.command("approve").description("Approve a KPI proposal").argument("<kpi-id>", "KPI id"))
293
+ .option("--question <id>", "Answered owner question authorizing this approval")
291
294
  .action(wrap(async (id, options) => {
292
- const { store } = await target(deps, options);
293
- const current = store.read("kpi", id);
295
+ const { store, usesMesh } = await target(deps, options);
296
+ if (usesMesh && !options.question)
297
+ fail("tracker_approval_question_required", "Work Mesh KPI approval requires --question <answered-owner-question-id>", { id });
298
+ const current = await store.read("kpi", id);
294
299
  if (!current)
295
300
  fail("tracker_record_not_found", `Tracker KPI record ${id} was not found`, { kind: "kpi", id });
296
- const record = store.update("kpi", id, current.version, (value) => {
301
+ const record = await store.update("kpi", id, current.version, (value) => {
297
302
  if (value.status === "approved")
298
303
  fail("tracker_already_approved", "KPI is already approved", { id });
299
304
  return { ...value, status: "approved", approved_by: requireActor(deps), approved_at: deps.now().toISOString() };
300
- });
305
+ }, options.question ? { approvalQuestionId: options.question } : undefined);
301
306
  console.log(JSON.stringify({ ok: true, action: "kpi.approve", record }, null, 2));
302
307
  }));
303
308
  addProjectOptions(kpi.command("list").description("List KPIs")).action(wrap(async (options) => {
304
309
  const { store } = await target(deps, options);
305
- console.log(JSON.stringify({ ok: true, action: "kpi.list", records: store.list("kpi") }, null, 2));
310
+ console.log(JSON.stringify({ ok: true, action: "kpi.list", records: await store.list("kpi") }, null, 2));
306
311
  }));
307
312
  const okr = parent.command("okr").description("Set and approve OKRs");
308
313
  addProjectOptions(okr.command("set").description("Create a proposed OKR"))
@@ -319,11 +324,11 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
319
324
  for (const keyResult of keyResults)
320
325
  checkedText("key_result", keyResult.text);
321
326
  for (const keyResult of keyResults) {
322
- if (!store.read("kpi", keyResult.kpi_id)) {
327
+ if (!await store.read("kpi", keyResult.kpi_id)) {
323
328
  fail("tracker_kpi_not_found", `Referenced KPI ${keyResult.kpi_id} was not found in this project`, { kpi_id: keyResult.kpi_id, project_id: project });
324
329
  }
325
330
  }
326
- const record = createRecord(store, "okr", company.slug, project, {
331
+ const record = await createRecord(store, "okr", company.slug, project, {
327
332
  objective: options.objective, period: options.period,
328
333
  key_results: keyResults.map((value) => ({ kr_id: randomUUID(), ...value })),
329
334
  status: "proposed", approved_by: null, approved_at: null,
@@ -331,21 +336,24 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
331
336
  console.log(JSON.stringify({ ok: true, action: "okr.set", record }, null, 2));
332
337
  }));
333
338
  addProjectOptions(okr.command("approve").description("Approve an OKR proposal").argument("<okr-id>", "OKR id"))
339
+ .option("--question <id>", "Answered owner question authorizing this approval")
334
340
  .action(wrap(async (id, options) => {
335
- const { store } = await target(deps, options);
336
- const current = store.read("okr", id);
341
+ const { store, usesMesh } = await target(deps, options);
342
+ if (usesMesh && !options.question)
343
+ fail("tracker_approval_question_required", "Work Mesh OKR approval requires --question <answered-owner-question-id>", { id });
344
+ const current = await store.read("okr", id);
337
345
  if (!current)
338
346
  fail("tracker_record_not_found", `Tracker OKR record ${id} was not found`, { kind: "okr", id });
339
- const record = store.update("okr", id, current.version, (value) => {
347
+ const record = await store.update("okr", id, current.version, (value) => {
340
348
  if (value.status === "approved")
341
349
  fail("tracker_already_approved", "OKR is already approved", { id });
342
350
  return { ...value, status: "approved", approved_by: requireActor(deps), approved_at: deps.now().toISOString() };
343
- });
351
+ }, options.question ? { approvalQuestionId: options.question } : undefined);
344
352
  console.log(JSON.stringify({ ok: true, action: "okr.approve", record }, null, 2));
345
353
  }));
346
354
  addProjectOptions(okr.command("list").description("List OKRs")).action(wrap(async (options) => {
347
355
  const { store } = await target(deps, options);
348
- console.log(JSON.stringify({ ok: true, action: "okr.list", records: store.list("okr") }, null, 2));
356
+ console.log(JSON.stringify({ ok: true, action: "okr.list", records: await store.list("okr") }, null, 2));
349
357
  }));
350
358
  const directive = parent.command("directive").description("Record directives and acknowledgements");
351
359
  addProjectOptions(directive.command("record").description("Record a directive"))
@@ -373,7 +381,7 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
373
381
  }
374
382
  if (url.protocol !== "https:" && url.protocol !== "http:")
375
383
  fail("tracker_source_url_invalid", "Source URL must use HTTP or HTTPS", {});
376
- const record = createRecord(store, "directive", company.slug, project, {
384
+ const record = await createRecord(store, "directive", company.slug, project, {
377
385
  text: options.text, date: validDate(options.date), source_url: options.sourceUrl,
378
386
  teams: [...new Set(teams)].sort(), policy_path: policyPath, acknowledgements: [],
379
387
  });
@@ -386,7 +394,7 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
386
394
  const { store } = await target(deps, options);
387
395
  const team = validSegment(options.team, "team");
388
396
  const lane = validSegment(options.lane, "lane");
389
- const current = store.read("directive", id);
397
+ const current = await store.read("directive", id);
390
398
  if (!current)
391
399
  fail("tracker_record_not_found", `Tracker directive record ${id} was not found`, { kind: "directive", id });
392
400
  const teams = current.teams;
@@ -395,7 +403,7 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
395
403
  fail("tracker_team_not_targeted", "Team is not listed on this directive", { id, team });
396
404
  if (!Array.isArray(acknowledgements))
397
405
  fail("tracker_record_invalid", "Directive acknowledgements must be an array", { id });
398
- const record = store.update("directive", id, current.version, (value) => ({
406
+ const record = await store.update("directive", id, current.version, (value) => ({
399
407
  ...value,
400
408
  acknowledgements: [...acknowledgements.filter((ack) => !(ack && typeof ack === "object" && ack.team === team && ack.lane_id === lane)), { team, lane_id: lane, at: deps.now().toISOString() }],
401
409
  }));
@@ -403,7 +411,7 @@ export function registerLaneTrackerCommands(parent, deps, wrap) {
403
411
  }));
404
412
  addProjectOptions(directive.command("list").description("List directives")).action(wrap(async (options) => {
405
413
  const { store } = await target(deps, options);
406
- console.log(JSON.stringify({ ok: true, action: "directive.list", records: store.list("directive") }, null, 2));
414
+ console.log(JSON.stringify({ ok: true, action: "directive.list", records: await store.list("directive") }, null, 2));
407
415
  }));
408
416
  }
409
417
  export function defaultTrackerActor() {
@@ -6,6 +6,7 @@
6
6
  * `watch` and the monitor reminder are the line-oriented text surfaces.
7
7
  */
8
8
  import { Command } from "commander";
9
+ import { type TrackerMeshRequest } from "../lib/lanes/tracker-mesh.js";
9
10
  import type { OutpostIdentity } from "../lib/lanes/admission-host.js";
10
11
  export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
11
12
  export type { OutpostIdentity } from "../lib/lanes/admission-host.js";
@@ -102,6 +103,8 @@ export interface LanesCommandDeps {
102
103
  laneTrackerEnabled?: (company: LaneCompanyRef) => boolean | Promise<boolean>;
103
104
  /** The local-only PR 2 implementation refuses if mesh storage is enabled. */
104
105
  laneTrackerMeshEnabled?: (company: LaneCompanyRef) => boolean | Promise<boolean>;
106
+ /** Fake transport seam used by the disposable-root Work Mesh contract check. */
107
+ trackerMeshRequest?: TrackerMeshRequest;
105
108
  /** Test seam for the default-off host-scoped account-home writer gate. */
106
109
  laneHostScopedAccountHomesEnabled?: (company?: LaneCompanyRef | null) => boolean | Promise<boolean>;
107
110
  /** Test seam for recording approvals against the active identity. */