@indigoai-us/hq-cli 5.226.0 → 5.227.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.227.0] — 2026-09-27
6
+
7
+ ### Added
8
+
9
+ - `hq lanes tracker`, `checks`, `import`, and `report` use the Work Mesh tracker
10
+ store when `lanes.tracker-mesh` is enabled. Company questions are indexed and
11
+ shared across hosts; reconcile polls for remote answers, wakes the owning lane,
12
+ and stamps delivery once.
13
+
5
14
  ## [5.226.0] — 2026-09-26
6
15
 
7
16
  ### Added
@@ -7069,6 +7069,9 @@ export declare const COMMAND_CATALOG: readonly [{
7069
7069
  }, {
7070
7070
  readonly flags: "--company <slug>";
7071
7071
  readonly description: "Select the company by slug";
7072
+ }, {
7073
+ readonly flags: "--question <id>";
7074
+ readonly description: "Answered owner question authorizing this approval";
7072
7075
  }, {
7073
7076
  readonly flags: "--json";
7074
7077
  readonly description: "JSON on stdout (always)";
@@ -7145,6 +7148,9 @@ export declare const COMMAND_CATALOG: readonly [{
7145
7148
  }, {
7146
7149
  readonly flags: "--company <slug>";
7147
7150
  readonly description: "Select the company by slug";
7151
+ }, {
7152
+ readonly flags: "--question <id>";
7153
+ readonly description: "Answered owner question authorizing this approval";
7148
7154
  }, {
7149
7155
  readonly flags: "--json";
7150
7156
  readonly description: "JSON on stdout (always)";
@@ -9137,6 +9137,10 @@ export const COMMAND_CATALOG = [
9137
9137
  "flags": "--company <slug>",
9138
9138
  "description": "Select the company by slug"
9139
9139
  },
9140
+ {
9141
+ "flags": "--question <id>",
9142
+ "description": "Answered owner question authorizing this approval"
9143
+ },
9140
9144
  {
9141
9145
  "flags": "--json",
9142
9146
  "description": "JSON on stdout (always)"
@@ -9235,6 +9239,10 @@ export const COMMAND_CATALOG = [
9235
9239
  "flags": "--company <slug>",
9236
9240
  "description": "Select the company by slug"
9237
9241
  },
9242
+ {
9243
+ "flags": "--question <id>",
9244
+ "description": "Answered owner question authorizing this approval"
9245
+ },
9238
9246
  {
9239
9247
  "flags": "--json",
9240
9248
  "description": "JSON on stdout (always)"
@@ -1,5 +1,6 @@
1
1
  import type { Command } from "commander";
2
2
  import { type TrackerKind, type TrackerRecord } from "../lib/lanes/tracker-store.js";
3
+ import type { TrackerStore } from "../lib/lanes/tracker-store.js";
3
4
  import type { LaneCompanyRef, QuestionRecord } from "../lib/lanes/types.js";
4
5
  export interface LaneChecksDeps {
5
6
  hqRoot: () => string;
@@ -7,6 +8,7 @@ export interface LaneChecksDeps {
7
8
  trackerEnabled: (company: LaneCompanyRef) => boolean | Promise<boolean>;
8
9
  trackerMeshEnabled: (company: LaneCompanyRef) => boolean | Promise<boolean>;
9
10
  now: () => Date;
11
+ meshStore?: (company: LaneCompanyRef, projectId: string) => TrackerStore;
10
12
  }
11
13
  type WrapHandler = <Args extends unknown[]>(action: (...args: Args) => Promise<void>) => (...args: Args) => Promise<void>;
12
14
  export interface ProjectMapping {
@@ -5,7 +5,8 @@ import { LanesError, isLanesError } from "../lib/lanes/errors.js";
5
5
  import { evaluateTrackerChecks } from "../lib/lanes/tracker-checks.js";
6
6
  import { laneRecordPath, listLaneIds, listQuestionIds, questionRecordPath, readLane, readQuestion } from "../lib/lanes/store.js";
7
7
  import { createLocalTrackerStore } from "../lib/lanes/tracker-store.js";
8
- import { LANES_TRACKER_FLAG, LANES_TRACKER_MESH_FLAG } from "./lanes-tracker.js";
8
+ import { createWorkMeshTrackerStore } from "../lib/lanes/tracker-mesh.js";
9
+ import { LANES_TRACKER_FLAG } from "./lanes-tracker.js";
9
10
  function fail(error, message, details = {}) {
10
11
  throw new LanesError(error, message, details);
11
12
  }
@@ -503,16 +504,20 @@ export function listTrackerRecordsTolerant(projectDirectory, kind) {
503
504
  records.sort((a, b) => b.updated_at.localeCompare(a.updated_at) || a.id.localeCompare(b.id));
504
505
  return { records, errors };
505
506
  }
506
- function readTrackerCheckRecords(root, company, projects) {
507
+ async function readTrackerCheckRecords(root, company, projects, deps) {
507
508
  const blockers = [];
508
509
  const kpis = [];
509
510
  const directives = [];
511
+ const useMesh = await deps.trackerMeshEnabled(company);
510
512
  for (const project of projects) {
511
513
  const absoluteDirectory = path.join(root, "companies", company.slug, "projects", project.projectId);
514
+ const store = useMesh
515
+ ? (deps.meshStore ?? ((resolvedCompany, projectId) => createWorkMeshTrackerStore({ company: resolvedCompany, projectId, now: deps.now })))(company, project.projectId)
516
+ : createLocalTrackerStore(absoluteDirectory);
512
517
  const records = {
513
- blocker: listTrackerRecords(absoluteDirectory, "blocker"),
514
- kpi: listTrackerRecords(absoluteDirectory, "kpi"),
515
- directive: listTrackerRecords(absoluteDirectory, "directive"),
518
+ blocker: await store.list("blocker"),
519
+ kpi: await store.list("kpi"),
520
+ directive: await store.list("directive"),
516
521
  };
517
522
  for (const record of records.blocker) {
518
523
  if (record.company !== company.slug || record.project_id !== project.projectId)
@@ -564,17 +569,14 @@ export async function resolveTrackerCompany(deps, requested) {
564
569
  if (!await deps.trackerEnabled(company)) {
565
570
  fail("tracker_disabled", `hq lanes tracker commands require the ${LANES_TRACKER_FLAG} flag`, { company: company.slug });
566
571
  }
567
- if (await deps.trackerMeshEnabled(company)) {
568
- fail("tracker_mesh_unavailable", `Work Mesh tracker storage is not available while ${LANES_TRACKER_MESH_FLAG} is enabled`, { company: company.slug });
569
- }
570
572
  return company;
571
573
  }
572
- function checkReport(root, company, projects, directiveProjects, projectFilter, now) {
574
+ async function checkReport(root, company, projects, directiveProjects, projectFilter, now, deps) {
573
575
  const selectedProjects = [...projects.values()];
574
576
  const laneRecords = readScopedLanes(root, company, selectedProjects);
575
577
  const teams = readTeamInputs(root, company, selectedProjects, laneRecords);
576
578
  const questionRecords = readCompanyQuestions(root, company, projectFilter);
577
- const trackerRecords = readTrackerCheckRecords(root, company, selectedProjects);
579
+ const trackerRecords = await readTrackerCheckRecords(root, company, selectedProjects, deps);
578
580
  const directiveTeams = [...directiveProjects.values()].map((project) => ({
579
581
  team: project.team,
580
582
  ...(project.leadLaneId === undefined ? {} : { lead_lane_id: project.leadLaneId }),
@@ -607,7 +609,7 @@ export function registerLaneChecksCommand(parent, deps, wrap) {
607
609
  const directiveProjects = projectFilter === undefined
608
610
  ? projects
609
611
  : loadProjectMappings(root, company.slug);
610
- const report = checkReport(root, company, projects, directiveProjects, projectFilter, deps.now());
612
+ const report = await checkReport(root, company, projects, directiveProjects, projectFilter, deps.now(), deps);
611
613
  console.log(JSON.stringify(report, null, 2));
612
614
  if (report.checks.some((item) => item.result === "failing"))
613
615
  process.exitCode = 2;
@@ -9,6 +9,7 @@ export interface LaneTrackerImportDeps {
9
9
  trackerMeshEnabled: (company: LaneCompanyRef) => boolean | Promise<boolean>;
10
10
  now: () => Date;
11
11
  store?: (projectDirectory: string) => TrackerStore;
12
+ meshStore?: (company: LaneCompanyRef, projectId: string) => TrackerStore;
12
13
  }
13
14
  export declare function registerLaneTrackerImportCommand(parent: Command, deps: LaneTrackerImportDeps, wrap: WrapHandler): void;
14
15
  export {};
@@ -5,7 +5,8 @@ import { isDeepStrictEqual } from "node:util";
5
5
  import { LanesError } from "../lib/lanes/errors.js";
6
6
  import { assertNoSecretText } from "../lib/lanes/text-scan.js";
7
7
  import { createLocalTrackerStore, } from "../lib/lanes/tracker-store.js";
8
- import { LANES_TRACKER_FLAG, LANES_TRACKER_MESH_FLAG } from "./lanes-tracker.js";
8
+ import { createWorkMeshTrackerStore } from "../lib/lanes/tracker-mesh.js";
9
+ import { LANES_TRACKER_FLAG } from "./lanes-tracker.js";
9
10
  const IMPORT_DATE = "2026-09-25";
10
11
  const IMPORT_KEY_PREFIX = "trk7";
11
12
  const MAX_TEXT_BYTES = 32_000;
@@ -946,7 +947,7 @@ async function executeCandidates(work, candidates, mode, company, now, kpiIds) {
946
947
  const existing = new Map();
947
948
  try {
948
949
  for (const kind of kinds)
949
- existing.set(kind, work.store.list(kind));
950
+ existing.set(kind, await work.store.list(kind));
950
951
  }
951
952
  catch (err) {
952
953
  addTeamError(work.result, err);
@@ -997,7 +998,7 @@ async function executeCandidates(work, candidates, mode, company, now, kpiIds) {
997
998
  let record;
998
999
  let action;
999
1000
  if (!current) {
1000
- record = work.store.create({
1001
+ record = await work.store.create({
1001
1002
  kind: candidate.kind,
1002
1003
  id: importedRecordId(candidate),
1003
1004
  company,
@@ -1011,7 +1012,7 @@ async function executeCandidates(work, candidates, mode, company, now, kpiIds) {
1011
1012
  action = "unchanged";
1012
1013
  }
1013
1014
  else {
1014
- record = work.store.update(candidate.kind, current.id, current.version, (live) => ({
1015
+ record = await work.store.update(candidate.kind, current.id, current.version, (live) => ({
1015
1016
  ...live,
1016
1017
  ...fields,
1017
1018
  ...(candidate.kind === "kpi" ? { values: mergeImportedKpiValues(live, candidate) } : {}),
@@ -1048,7 +1049,7 @@ function statusOkrCandidates(status, statusSource, result) {
1048
1049
  function makeEmptyTeamResult(team) {
1049
1050
  return { team, status: "error", sources: { prd: null, status: null, brief: null, directives: null }, actions: [], skipped: [], errors: [], unlinked_key_results: [] };
1050
1051
  }
1051
- async function prepareTeam(root, company, row, index, globalMarkdown, globalDirectivePath, deps) {
1052
+ async function prepareTeam(root, company, companyRef, useMesh, row, index, globalMarkdown, globalDirectivePath, deps) {
1052
1053
  const team = typeof row.team === "string" && row.team.trim() ? row.team.trim() : `unknown-${index}`;
1053
1054
  const result = makeEmptyTeamResult(team);
1054
1055
  let prdPath = "";
@@ -1097,7 +1098,9 @@ async function prepareTeam(root, company, row, index, globalMarkdown, globalDire
1097
1098
  addSkip(result, { kind: "okr", source_path: statusSource, reason: "no_explicit_goal_or_okr_source" });
1098
1099
  }
1099
1100
  candidates.push(...okrCandidates([...statusOkrs, ...briefOkrs], kpis, result, team));
1100
- const store = (deps.store ?? ((directory) => createLocalTrackerStore(directory, deps.now)))(path.dirname(prdPath));
1101
+ const store = useMesh
1102
+ ? (deps.meshStore ?? ((resolvedCompany, projectId) => createWorkMeshTrackerStore({ company: resolvedCompany, projectId, now: deps.now })))(companyRef, resolved.projectId)
1103
+ : deps.store?.(path.dirname(prdPath)) ?? createLocalTrackerStore(path.dirname(prdPath), deps.now);
1101
1104
  const importCandidates = uniqueCandidates(candidates, result);
1102
1105
  result.status = result.errors.length ? "imported_with_errors" : "imported";
1103
1106
  return { result, company, projectId: resolved.projectId, store, candidates: importCandidates };
@@ -1129,9 +1132,7 @@ export function registerLaneTrackerImportCommand(parent, deps, wrap) {
1129
1132
  if (!await deps.trackerEnabled(company)) {
1130
1133
  fail("tracker_disabled", `hq lanes import requires the ${LANES_TRACKER_FLAG} flag`, { company: company.slug });
1131
1134
  }
1132
- if (await deps.trackerMeshEnabled(company)) {
1133
- fail("tracker_mesh_unavailable", `hq lanes import cannot write while ${LANES_TRACKER_MESH_FLAG} is enabled`, { company: company.slug });
1134
- }
1135
+ const trackerMeshEnabled = await deps.trackerMeshEnabled(company);
1135
1136
  const root = deps.hqRoot();
1136
1137
  const registryPath = resolveSourcePath(root, "workspace/lane-briefs/juniors.json", "juniors registry");
1137
1138
  const registry = readJsonObject(registryPath, "juniors registry");
@@ -1151,13 +1152,13 @@ export function registerLaneTrackerImportCommand(parent, deps, wrap) {
1151
1152
  const globalMarkdown = readText(globalPath, "operator-session-directives.md");
1152
1153
  const works = [];
1153
1154
  for (const [index, row] of selectedRows.entries()) {
1154
- works.push(await prepareTeam(root, company.slug, row, index, globalMarkdown, globalPath, deps));
1155
+ works.push(await prepareTeam(root, company.slug, company, trackerMeshEnabled, row, index, globalMarkdown, globalPath, deps));
1155
1156
  }
1156
1157
  const mode = options.apply ? "apply" : "dry-run";
1157
1158
  const kpiIds = new Map();
1158
1159
  for (const work of works) {
1159
1160
  try {
1160
- for (const record of work.store.list("kpi")) {
1161
+ for (const record of await work.store.list("kpi")) {
1161
1162
  if (typeof record.import_key === "string")
1162
1163
  kpiIds.set(record.import_key, record.id);
1163
1164
  }
@@ -4,6 +4,7 @@ import { assertQuestionBodySecretSafe } from "../lib/lanes/questions.js";
4
4
  import { questionRecordPath } from "../lib/lanes/store.js";
5
5
  import { normalizeIsoTimestamp } from "../lib/lanes/timestamp.js";
6
6
  import { mapSourceStoryStatus } from "../lib/mesh/story-status.js";
7
+ import { createWorkMeshTrackerStore } from "../lib/lanes/tracker-mesh.js";
7
8
  import { buildTrackerOwnerReport, buildTrackerTeamPage, renderTrackerOwnerReportText, renderTrackerTeamPageText, } from "../lib/lanes/tracker-report.js";
8
9
  import { FIRST_TRACKER_REPORT_SINCE, readTrackerReportState, writeTrackerReportState, } from "../lib/lanes/tracker-report-state.js";
9
10
  import { listTrackerRecordsTolerant, loadCompanyQuestionRecords, loadProjectMappings, pathForTrackerRecord, readProject, resolveTrackerCompany, } from "./lanes-checks.js";
@@ -142,8 +143,14 @@ function currentAudience(question, sourcePath) {
142
143
  default: return fail("tracker_report_question_route_invalid", "Question current audience is unsupported", { question_id: question.question_id, path: sourcePath });
143
144
  }
144
145
  }
145
- function trackerRecords(root, company, project, kind, errors) {
146
+ async function trackerRecords(root, company, project, kind, errors, deps) {
146
147
  const projectDirectory = path.join(root, "companies", company.slug, "projects", project.projectId);
148
+ if (await deps.trackerMeshEnabled(company)) {
149
+ const store = (deps.meshStore ?? ((resolvedCompany, projectId) => createWorkMeshTrackerStore({ company: resolvedCompany, projectId, now: deps.now })))(company, project.projectId);
150
+ return (await store.list(kind))
151
+ .filter((record) => record.company === company.slug && record.project_id === project.projectId)
152
+ .map((record) => ({ ...record, source_path: `work-mesh://${company.uid}/projects/${project.projectId}/tracker/${kind}/${record.id}` }));
153
+ }
147
154
  const loaded = listTrackerRecordsTolerant(projectDirectory, kind);
148
155
  for (const issue of loaded.errors) {
149
156
  errors.push({ path: issue.path, field: issue.field, message: issue.message, ...(issue.record_id ? { record_id: issue.record_id } : {}) });
@@ -163,7 +170,7 @@ function issueFromError(err, fallbackPath, fallbackField, ids = {}) {
163
170
  ...(typeof details.question_id === "string" ? { question_id: details.question_id } : ids.question_id ? { question_id: ids.question_id } : {}),
164
171
  };
165
172
  }
166
- function teamInput(root, company, project, questions, deps, errors = []) {
173
+ async function teamInput(root, company, project, questions, deps, errors = []) {
167
174
  const projectDirectory = path.join(root, "companies", company.slug, "projects", project.projectId);
168
175
  const prdPath = path.join(projectDirectory, "prd.json");
169
176
  let prd = { stories: [], mtime: 0 };
@@ -235,19 +242,18 @@ function teamInput(root, company, project, questions, deps, errors = []) {
235
242
  stories,
236
243
  pull_requests: pullRequests,
237
244
  releases,
238
- blockers: trackerRecords(root, company, project, "blocker", errors),
239
- kpis: trackerRecords(root, company, project, "kpi", errors),
240
- okrs: trackerRecords(root, company, project, "okr", errors),
241
- directives: trackerRecords(root, company, project, "directive", errors),
245
+ blockers: await trackerRecords(root, company, project, "blocker", errors, deps),
246
+ kpis: await trackerRecords(root, company, project, "kpi", errors, deps),
247
+ okrs: await trackerRecords(root, company, project, "okr", errors, deps),
248
+ directives: await trackerRecords(root, company, project, "directive", errors, deps),
242
249
  questions: teamQuestions,
243
250
  errors,
244
251
  };
245
252
  }
246
- function allTeamInputs(root, company, deps) {
253
+ async function allTeamInputs(root, company, deps) {
247
254
  const mappings = loadProjectMappings(root, company.slug);
248
- return [...mappings.values()]
249
- .sort((a, b) => a.team.localeCompare(b.team) || a.projectId.localeCompare(b.projectId))
250
- .map((project) => {
255
+ const teams = [];
256
+ for (const project of [...mappings.values()].sort((a, b) => a.team.localeCompare(b.team) || a.projectId.localeCompare(b.projectId))) {
251
257
  const errors = [];
252
258
  let questions = [];
253
259
  try {
@@ -256,8 +262,9 @@ function allTeamInputs(root, company, deps) {
256
262
  catch (err) {
257
263
  errors.push(issueFromError(err, path.join(root, "companies", company.slug, "projects", project.projectId), "questions"));
258
264
  }
259
- return teamInput(root, company, project, questions, deps, errors);
260
- });
265
+ teams.push(await teamInput(root, company, project, questions, deps, errors));
266
+ }
267
+ return teams;
261
268
  }
262
269
  function sinceForTeam(root, company) {
263
270
  return readTrackerReportState(root, company)?.last_report_at ?? FIRST_TRACKER_REPORT_SINCE;
@@ -286,7 +293,7 @@ export function registerLaneReportCommands(parent, deps, wrap) {
286
293
  generated_at: generatedAt,
287
294
  since,
288
295
  since_source: options.since !== undefined ? "explicit" : previous ? "previous_report" : "first_run",
289
- teams: allTeamInputs(root, company, deps),
296
+ teams: await allTeamInputs(root, company, deps),
290
297
  };
291
298
  const report = buildTrackerOwnerReport(input);
292
299
  const hasErrors = report.teams.some((team) => team.errors.length > 0);
@@ -320,7 +327,7 @@ export function registerLaneReportCommands(parent, deps, wrap) {
320
327
  catch (err) {
321
328
  errors.push(issueFromError(err, path.join(root, "companies", company.slug, "projects", candidates[0].projectId), "questions"));
322
329
  }
323
- const input = teamInput(root, company, candidates[0], questions, deps, errors);
330
+ const input = await teamInput(root, company, candidates[0], questions, deps, errors);
324
331
  const page = buildTrackerTeamPage(input, company.slug, since, generatedAt);
325
332
  if (page.errors.length > 0)
326
333
  process.exitCode = 2;
@@ -12,6 +12,7 @@ export interface LaneTrackerCommandDeps {
12
12
  actor: () => string | null;
13
13
  /** Test seam; production uses the same local store used by the public commands. */
14
14
  store?: (projectDirectory: string) => TrackerStore;
15
+ meshStore?: (company: LaneCompanyRef, projectId: string) => TrackerStore;
15
16
  }
16
17
  type WrapHandler = <Args extends unknown[]>(action: (...args: Args) => Promise<void>) => (...args: Args) => Promise<void>;
17
18
  export declare function registerLaneTrackerCommands(parent: Command, deps: LaneTrackerCommandDeps, wrap: WrapHandler): void;
@@ -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. */