@indigoai-us/hq-cli 5.308.0 → 5.309.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,7 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.309.0] — 2026-10-01
6
+ - `hq lanes autopilot run` now evaluates leads based on lane ownership rather than a host label, and reports registered leads assigned to another host as skipped.
7
+ - Allow non-overlapping story-prefix teams to share a project in lane checks and reports.
8
+
5
9
  ## [5.308.0] — 2026-10-01
10
+ - Added native `hq core audit-log`, `git-pack-extension-check`, `pin`, and `refresh-vault-access` commands behind the default-off `core-native-utilities` flag.
11
+ - CI now cancels superseded pull-request benchmarks and reserves the additional Windows and macOS checks for changes that affect those platform paths. Main pushes retain all platform checks.
12
+ - `hq lanes list` now settles legacy failed-dispatch mirrors as undispatched only after a successful strict owner snapshot returns no row; snapshot errors and mirrors with a host report remain unverified.
13
+ - Outpost pickup and remote database requests now time out, and failed pending DM lookups are reported.
14
+
15
+ ## [5.307.0] — 2026-10-01
6
16
  - Add native `hq core resolve-company` on the fast route behind the default-off `core-native-utilities` gate.
7
17
 
8
18
  - Stop immediately when hq-deploy repeats an app-list cursor while resolving apps.
@@ -1,6 +1,7 @@
1
1
  import type { Command } from "commander";
2
+ import { type TrackerCheckTeam } from "../lib/lanes/tracker-checks.js";
2
3
  import { type TrackerKind, type TrackerRecord } from "../lib/lanes/tracker-store.js";
3
- import type { LaneCompanyRef, QuestionRecord } from "../lib/lanes/types.js";
4
+ import type { LaneRecord, LaneCompanyRef, QuestionRecord } from "../lib/lanes/types.js";
4
5
  export interface LaneChecksDeps {
5
6
  hqRoot: () => string;
6
7
  resolveCompany: (company?: string) => Promise<LaneCompanyRef>;
@@ -14,7 +15,11 @@ export interface ProjectMapping {
14
15
  team: string;
15
16
  leadLaneId?: string;
16
17
  statusPath: string;
18
+ storyPrefix?: string;
19
+ excludePrefix?: string;
17
20
  }
21
+ export declare function mappingIncludesStory(mapping: ProjectMapping, storyId: string): boolean;
22
+ export declare function ambiguousProjectMappings(mappings: readonly ProjectMapping[]): Map<string, string[]>;
18
23
  export interface ProjectReadResult {
19
24
  stories: Array<Record<string, unknown>>;
20
25
  mtime: number;
@@ -28,6 +33,7 @@ export interface ProjectReadResult {
28
33
  export declare function loadProjectMappings(root: string, company: string, requestedProject?: string): Map<string, ProjectMapping>;
29
34
  export declare function readProject(root: string, company: string, prdPath: string, tolerantStories?: boolean): ProjectReadResult;
30
35
  export declare function loadCompanyQuestionRecords(root: string, company: LaneCompanyRef, projectId?: string): QuestionRecord[];
36
+ export declare function readTeamInputs(root: string, company: LaneCompanyRef, projects: readonly ProjectMapping[], lanes: readonly LaneRecord[]): TrackerCheckTeam[];
31
37
  export declare function pathForTrackerRecord(projectDirectory: string, kind: TrackerKind, id: string): string;
32
38
  export declare function listTrackerRecords(projectDirectory: string, kind: TrackerKind): TrackerRecord[];
33
39
  export interface TolerantTrackerReadIssue {
@@ -12,6 +12,37 @@ import { DEFAULT_ORPHAN_USER_IDLE_HOURS, findOrphanLanes, parseOrphanMinAge, par
12
12
  import { LANES_RUNS_REL } from "../lib/lanes/spawn.js";
13
13
  import { assertNoSecretText } from "../lib/lanes/text-scan.js";
14
14
  import { checkCompanyPath } from "../lib/lanes/checked-company-path.js";
15
+ export function mappingIncludesStory(mapping, storyId) {
16
+ return (mapping.storyPrefix === undefined || storyId.startsWith(mapping.storyPrefix))
17
+ && (mapping.excludePrefix === undefined || !storyId.startsWith(mapping.excludePrefix));
18
+ }
19
+ function mappingsOverlap(left, right) {
20
+ const includes = [left.storyPrefix, right.storyPrefix].filter((value) => value !== undefined);
21
+ if (includes.length === 2 && !includes[0].startsWith(includes[1]) && !includes[1].startsWith(includes[0]))
22
+ return false;
23
+ const commonPrefix = includes.sort((a, b) => b.length - a.length)[0] ?? "";
24
+ return ![left.excludePrefix, right.excludePrefix].some((excluded) => excluded !== undefined && commonPrefix.startsWith(excluded));
25
+ }
26
+ export function ambiguousProjectMappings(mappings) {
27
+ const byProject = new Map();
28
+ for (const mapping of mappings)
29
+ byProject.set(mapping.projectId, [...(byProject.get(mapping.projectId) ?? []), mapping]);
30
+ const ambiguous = new Map();
31
+ for (const [projectId, rows] of byProject) {
32
+ const conflicts = new Set();
33
+ for (let i = 0; i < rows.length; i++) {
34
+ for (let j = i + 1; j < rows.length; j++) {
35
+ if (mappingsOverlap(rows[i], rows[j])) {
36
+ conflicts.add(rows[i].team);
37
+ conflicts.add(rows[j].team);
38
+ }
39
+ }
40
+ }
41
+ if (conflicts.size > 0)
42
+ ambiguous.set(projectId, [...conflicts].sort());
43
+ }
44
+ return ambiguous;
45
+ }
15
46
  function fail(error, message, details = {}) {
16
47
  throw new LanesError(error, message, details);
17
48
  }
@@ -165,16 +196,26 @@ export function loadProjectMappings(root, company, requestedProject) {
165
196
  const team = validSegment(entry.team, "team");
166
197
  const leadLaneId = validSegment(entry.lane, "lead lane id");
167
198
  const statusPath = checkCompanyPath(root, company, entry.status, registryPath);
168
- if (mappings.has(projectId))
169
- return fail("tracker_checks_mapping_ambiguous", "More than one active team maps to a project", { path: registryPath, project_id: projectId });
170
- mappings.set(projectId, { projectId, team, leadLaneId, statusPath });
199
+ const parsePrefix = (field) => {
200
+ const value = entry[field];
201
+ if (value === undefined)
202
+ return undefined;
203
+ if (typeof value !== "string" || value.trim() === "") {
204
+ return fail("tracker_checks_mapping_invalid", `${field} must be a non-empty string`, { path: registryPath, index, field });
205
+ }
206
+ return value.trim();
207
+ };
208
+ const storyPrefix = parsePrefix("story_prefix");
209
+ const excludePrefix = parsePrefix("exclude_prefix");
210
+ const key = `${projectId}:${team}:${index}`;
211
+ mappings.set(key, { projectId, team, leadLaneId, statusPath, ...(storyPrefix === undefined ? {} : { storyPrefix }), ...(excludePrefix === undefined ? {} : { excludePrefix }) });
171
212
  }
172
213
  }
173
214
  const projectsRoot = path.join(root, "companies", company, "projects");
174
215
  const knownProjectIds = new Set(projectDirectories(root, company));
175
216
  for (const projectId of knownProjectIds) {
176
- if (projectHasTrackerRecords(root, company, projectId) && !mappings.has(projectId)) {
177
- mappings.set(projectId, {
217
+ if (projectHasTrackerRecords(root, company, projectId) && ![...mappings.values()].some((mapping) => mapping.projectId === projectId)) {
218
+ mappings.set(`${projectId}:tracker`, {
178
219
  projectId,
179
220
  team: projectId,
180
221
  statusPath: path.join(projectsRoot, projectId, "manager-status.json"),
@@ -183,10 +224,10 @@ export function loadProjectMappings(root, company, requestedProject) {
183
224
  }
184
225
  if (requestedProject !== undefined) {
185
226
  const projectId = validSegment(requestedProject, "project");
186
- if (!knownProjectIds.has(projectId) && !mappings.has(projectId)) {
227
+ if (!knownProjectIds.has(projectId) && ![...mappings.values()].some((mapping) => mapping.projectId === projectId)) {
187
228
  return fail("tracker_project_not_found", "Project directory was not found for this company", { project_id: projectId, path: path.join(projectsRoot, projectId) });
188
229
  }
189
- return new Map([...mappings].filter(([mappedProjectId]) => mappedProjectId === projectId));
230
+ return new Map([...mappings].filter(([, mapping]) => mapping.projectId === projectId));
190
231
  }
191
232
  return mappings;
192
233
  }
@@ -539,17 +580,18 @@ function readCompanyQuestions(root, company, projectId) {
539
580
  surfaced_at: surfacedAt.get(question.question_id) ?? null,
540
581
  }));
541
582
  }
542
- function readTeamInputs(root, company, projects, lanes) {
583
+ export function readTeamInputs(root, company, projects, lanes) {
543
584
  const inputs = [];
544
585
  for (const project of projects) {
545
586
  const projectPath = path.join(root, "companies", company.slug, "projects", project.projectId);
546
587
  const prdPath = path.join(projectPath, "prd.json");
547
588
  const { stories, mtime } = readProject(root, company.slug, prdPath);
548
- const teamLanes = lanes.filter((lane) => sameCompanyLane(lane, company) && laneMatchesProject(lane, project.projectId));
589
+ const teamLanes = lanes.filter((lane) => sameCompanyLane(lane, company) && laneMatchesProject(lane, project.projectId)
590
+ && (lane.lane_id === project.leadLaneId || mappingIncludesStory(project, lane.story_id)));
549
591
  const lead = project.leadLaneId === undefined
550
592
  ? undefined
551
593
  : teamLanes.find((lane) => lane.lane_id === project.leadLaneId);
552
- const openStories = stories.filter((story) => !storyIsDone(story));
594
+ const openStories = stories.filter((story) => mappingIncludesStory(project, String(story.id)) && !storyIsDone(story));
553
595
  const openPrRefs = new Set();
554
596
  for (const story of openStories) {
555
597
  for (const ref of pullRequestReferences(story.prs ?? []))
@@ -658,7 +700,7 @@ async function readTrackerCheckRecords(root, company, projects) {
658
700
  const blockers = [];
659
701
  const kpis = [];
660
702
  const directives = [];
661
- for (const project of projects) {
703
+ for (const project of [...new Map(projects.map((project) => [project.projectId, project])).values()]) {
662
704
  const absoluteDirectory = path.join(root, "companies", company.slug, "projects", project.projectId);
663
705
  const store = createLocalTrackerStore(absoluteDirectory);
664
706
  const records = {
@@ -715,7 +757,7 @@ export async function resolveTrackerCompany(deps, requested) {
715
757
  fail("tracker_company_unresolved", "The active HQ company could not be resolved", {});
716
758
  return company;
717
759
  }
718
- async function checkReport(root, company, projects, directiveProjects, projectFilter, minAgeMinutes, now, deps) {
760
+ async function checkReport(root, company, projects, directiveProjects, projectFilter, minAgeMinutes, now, deps, projectErrors) {
719
761
  const selectedProjects = [...projects.values()];
720
762
  const laneRecords = readScopedLanes(root, company, selectedProjects);
721
763
  const warnings = laneRecords.flatMap((lane) => {
@@ -773,6 +815,7 @@ async function checkReport(root, company, projects, directiveProjects, projectFi
773
815
  company: company.slug,
774
816
  generated_at: now.toISOString(),
775
817
  ...(warnings.length === 0 ? {} : { warnings }),
818
+ ...(projectErrors.length === 0 ? {} : { project_errors: projectErrors }),
776
819
  checks,
777
820
  };
778
821
  }
@@ -855,13 +898,20 @@ export function registerLaneChecksCommand(parent, deps, wrap) {
855
898
  const root = deps.hqRoot();
856
899
  const projectFilter = options.project === undefined ? undefined : validSegment(options.project, "project");
857
900
  const minAgeMinutes = parseOrphanMinAge(options.minAge);
858
- const projects = loadProjectMappings(root, company.slug, projectFilter);
859
- const directiveProjects = projectFilter === undefined
860
- ? projects
861
- : loadProjectMappings(root, company.slug);
862
- const report = await checkReport(root, company, projects, directiveProjects, projectFilter, minAgeMinutes, deps.now(), deps);
901
+ const allProjects = loadProjectMappings(root, company.slug);
902
+ const ambiguous = ambiguousProjectMappings([...allProjects.values()]);
903
+ const selectedAll = projectFilter === undefined ? [...allProjects.values()] : [...allProjects.values()].filter((mapping) => mapping.projectId === projectFilter);
904
+ const projectErrors = [...ambiguous].filter(([projectId]) => projectFilter === undefined || projectId === projectFilter).map(([project_id, teams]) => ({
905
+ project_id,
906
+ code: "tracker_checks_mapping_ambiguous",
907
+ teams,
908
+ message: "Team story prefixes overlap for this project",
909
+ }));
910
+ const projects = new Map(selectedAll.filter((mapping) => !ambiguous.has(mapping.projectId)).map((mapping, index) => [`${mapping.projectId}:${mapping.team}:${index}`, mapping]));
911
+ const directiveProjects = new Map([...allProjects.values()].filter((mapping) => !ambiguous.has(mapping.projectId)).map((mapping, index) => [`${mapping.projectId}:${mapping.team}:${index}`, mapping]));
912
+ const report = await checkReport(root, company, projects, directiveProjects, projectFilter, minAgeMinutes, deps.now(), deps, projectErrors);
863
913
  console.log(JSON.stringify(report, null, 2));
864
- if (report.checks.some((item) => item.result === "failing"))
914
+ if (report.checks.some((item) => item.result === "failing") || projectErrors.length > 0)
865
915
  process.exitCode = 2;
866
916
  }));
867
917
  parent.command("orphans")
@@ -6,7 +6,7 @@ import { normalizeIsoTimestamp } from "../lib/lanes/timestamp.js";
6
6
  import { mapSourceStoryStatus } from "../lib/mesh/story-status.js";
7
7
  import { buildTrackerOwnerReport, buildTrackerTeamPage, renderTrackerOwnerReportText, renderTrackerTeamPageText, } from "../lib/lanes/tracker-report.js";
8
8
  import { FIRST_TRACKER_REPORT_SINCE, readTrackerReportState, writeTrackerReportState, } from "../lib/lanes/tracker-report-state.js";
9
- import { listTrackerRecordsTolerant, loadCompanyQuestionRecords, loadProjectMappings, pathForTrackerRecord, readProject, resolveTrackerCompany, } from "./lanes-checks.js";
9
+ import { listTrackerRecordsTolerant, loadCompanyQuestionRecords, loadProjectMappings, mappingIncludesStory, pathForTrackerRecord, readProject, resolveTrackerCompany, } from "./lanes-checks.js";
10
10
  function fail(error, message, details = {}) {
11
11
  throw new LanesError(error, message, details);
12
12
  }
@@ -184,6 +184,8 @@ async function teamInput(root, company, project, questions, deps, errors = []) {
184
184
  errors.push({ path: prdPath, field: "id", message: "Story id must be a non-empty string" });
185
185
  continue;
186
186
  }
187
+ if (!mappingIncludesStory(project, id))
188
+ continue;
187
189
  const title = typeof story.title === "string" ? story.title : "";
188
190
  if (!title.trim()) {
189
191
  errors.push({ path: prdPath, field: "title", story_id: id, message: "Story title must be a non-empty string" });
@@ -217,7 +219,8 @@ async function teamInput(root, company, project, questions, deps, errors = []) {
217
219
  stories.push({ id, title, status, references: refs.references, ...(normalizedUpdatedAt ? { updated_at: normalizedUpdatedAt } : {}), source_path: prdPath });
218
220
  }
219
221
  const teamQuestions = [];
220
- for (const record of questions.filter((question) => question.project_id === project.projectId)) {
222
+ for (const record of questions.filter((question) => question.project_id === project.projectId
223
+ && (typeof question.story_id !== "string" || mappingIncludesStory(project, question.story_id)))) {
221
224
  const sourcePath = questionRecordPath(root, record.question_id);
222
225
  try {
223
226
  const body = deps.readQuestionBody(root, record);
@@ -41,10 +41,6 @@
41
41
  */
42
42
  import { type AllowedDivergenceLoad } from "../allowed-divergence.js";
43
43
  import type { CheckContext, CheckResult } from "../types.js";
44
- /** Basename of the single-adapter script the current architecture routes to. */
45
- export declare const CODEX_ADAPTER_BASENAME = "hq-codex-hook-adapter.sh";
46
- /** Library the adapter sources to dispatch `.claude/settings.json` live. */
47
- export declare const CODEX_DISPATCH_LIB_RELPATH: string;
48
44
  /**
49
45
  * Lifecycle events the shipped `.codex/config.toml` routes through the adapter.
50
46
  * A missing event means the Claude hooks for that event never run under Codex.
@@ -44,9 +44,9 @@ import * as path from "node:path";
44
44
  import { parse as parseToml } from "smol-toml";
45
45
  import { ALLOWED_DIVERGENCE_RELPATH, loadAllowedDivergence, } from "../allowed-divergence.js";
46
46
  /** Basename of the single-adapter script the current architecture routes to. */
47
- export const CODEX_ADAPTER_BASENAME = "hq-codex-hook-adapter.sh";
47
+ const CODEX_ADAPTER_BASENAME = "hq-codex-hook-adapter.sh";
48
48
  /** Library the adapter sources to dispatch `.claude/settings.json` live. */
49
- export const CODEX_DISPATCH_LIB_RELPATH = path.join("core", "scripts", "lib", "hook-adapter-core.sh");
49
+ const CODEX_DISPATCH_LIB_RELPATH = path.join("core", "scripts", "lib", "hook-adapter-core.sh");
50
50
  /**
51
51
  * Lifecycle events the shipped `.codex/config.toml` routes through the adapter.
52
52
  * A missing event means the Claude hooks for that event never run under Codex.
@@ -28,16 +28,6 @@
28
28
  import type { CheckContext, CheckFamily, CheckResult } from "../types.js";
29
29
  /** The id of the AI-runtime-health family. */
30
30
  export declare const RUNTIMES_FAMILY_ID = "runtimes";
31
- /** Common id prefix for every result this family emits. */
32
- export declare const RUNTIMES_PREFIX = "runtimes";
33
- /**
34
- * The deterministic one-line probe prompt. Health is proven by a round-trip
35
- * (exit 0 plus non-empty output), not by exact-matching the reply, so a model
36
- * that answers with anything at all still passes.
37
- */
38
- export declare const PROBE_PROMPT = "Reply with exactly: OK";
39
- /** How long each live probe may run before it is killed and marked UNKNOWN. */
40
- export declare const DEFAULT_PROBE_TIMEOUT_MS = 120000;
41
31
  /** One AI runtime the doctor knows how to find and probe. */
42
32
  export interface RuntimeSpec {
43
33
  /** Stable key used in check ids, e.g. `runtimes.codex.binary`. */
@@ -94,11 +84,4 @@ export interface RuntimeHealthDeps {
94
84
  export declare function checkRuntimeHealth(context: CheckContext, deps?: RuntimeHealthDeps): Promise<CheckResult[]>;
95
85
  /** The registered family object. */
96
86
  export declare const runtimeHealthFamily: CheckFamily;
97
- /**
98
- * Resolve an executable name against PATH with a pure filesystem scan — no
99
- * process is spawned, keeping the doctor's default run a function of on-disk
100
- * shape. First PATH entry containing an executable regular file wins, which is
101
- * exactly the copy a shell would run.
102
- */
103
- export declare function resolveOnPath(binary: string): string | null;
104
87
  //# sourceMappingURL=runtime-health.d.ts.map
@@ -33,15 +33,15 @@ import { planCommandSpawn } from "../../../utils/windows-spawn.js";
33
33
  /** The id of the AI-runtime-health family. */
34
34
  export const RUNTIMES_FAMILY_ID = "runtimes";
35
35
  /** Common id prefix for every result this family emits. */
36
- export const RUNTIMES_PREFIX = "runtimes";
36
+ const RUNTIMES_PREFIX = "runtimes";
37
37
  /**
38
38
  * The deterministic one-line probe prompt. Health is proven by a round-trip
39
39
  * (exit 0 plus non-empty output), not by exact-matching the reply, so a model
40
40
  * that answers with anything at all still passes.
41
41
  */
42
- export const PROBE_PROMPT = "Reply with exactly: OK";
42
+ const PROBE_PROMPT = "Reply with exactly: OK";
43
43
  /** How long each live probe may run before it is killed and marked UNKNOWN. */
44
- export const DEFAULT_PROBE_TIMEOUT_MS = 120_000;
44
+ const DEFAULT_PROBE_TIMEOUT_MS = 120_000;
45
45
  /**
46
46
  * The three runtimes HQ wires hooks for. Codex runs its probe under its own
47
47
  * read-only sandbox and outside-a-repo mode so the probe can never write; the
@@ -243,7 +243,7 @@ export const runtimeHealthFamily = {
243
243
  * shape. First PATH entry containing an executable regular file wins, which is
244
244
  * exactly the copy a shell would run.
245
245
  */
246
- export function resolveOnPath(binary) {
246
+ function resolveOnPath(binary) {
247
247
  const pathVar = process.env.PATH ?? "";
248
248
  for (const dir of pathVar.split(path.delimiter)) {
249
249
  if (!dir)
@@ -1,6 +1,5 @@
1
1
  import type { CheckContext, CheckFamily, CheckResult } from "../types.js";
2
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
3
  export declare const SCAFFOLD_CLI_FLOOR_CHECK_ID = "scaffold-cli-floor.requires-hq-cli";
5
4
  export declare const scaffoldCliFloorFamily: CheckFamily;
6
5
  export declare function checkScaffoldCliFloor(context: CheckContext): CheckResult[];
@@ -4,7 +4,7 @@ import * as yaml from "js-yaml";
4
4
  import { CLI_VERSION } from "../../../cli-version.js";
5
5
  import { compareScaffoldCliFloor } from "../../scaffold-cli-floor.js";
6
6
  export const SCAFFOLD_CLI_FLOOR_FAMILY_ID = "scaffold-cli-floor";
7
- export const SCAFFOLD_CLI_FLOOR_FAMILY_TITLE = "Scaffold CLI compatibility";
7
+ const SCAFFOLD_CLI_FLOOR_FAMILY_TITLE = "Scaffold CLI compatibility";
8
8
  export const SCAFFOLD_CLI_FLOOR_CHECK_ID = "scaffold-cli-floor.requires-hq-cli";
9
9
  export const scaffoldCliFloorFamily = {
10
10
  id: SCAFFOLD_CLI_FLOOR_FAMILY_ID,
@@ -31,22 +31,6 @@ import type { DoctorJsonDocument } from "./json-output.js";
31
31
  export declare const CHECK_HQ_HOOKS_PASS_EXIT = 0;
32
32
  /** Exit code check-hq-hooks.sh returns on a health failure. Preserved. */
33
33
  export declare const CHECK_HQ_HOOKS_FAIL_EXIT = 2;
34
- /** Exit code check-hq-hooks.sh returns on a usage error. Preserved. */
35
- export declare const CHECK_HQ_HOOKS_USAGE_EXIT = 64;
36
- /** The runtime-probe result id: whether hooks actually fired (the ledger). */
37
- export declare const RUNTIME_ENFORCEMENT_CHECK_ID = "hooks.runtime.enforcement";
38
- /**
39
- * The `hq doctor` check ids that correspond to check-hq-hooks.sh's *settings
40
- * load* scope — the subset of the hooks family the wrapper is allowed to fail
41
- * on. Deliberately narrow: it covers a missing/invalid settings file, an
42
- * unquoted `$CLAUDE_PROJECT_DIR` that word-splits on a spaced root, and a hook
43
- * command that runs a script which is not on disk — and nothing else. The many
44
- * richer doctor findings (executable bits, orphaned scripts, three-profile
45
- * membership, Codex/Grok parity, fixture coverage) are intentionally OUT of
46
- * scope, so the wrapper stays byte-for-byte compatible with callers that only
47
- * ever cared whether the hook configuration could load.
48
- */
49
- export declare const CHECK_HQ_HOOKS_SETTINGS_SCOPE: readonly string[];
50
34
  /**
51
35
  * Whether hook dispatch was observed this session. Mirrors the wrapper's
52
36
  * `HQ runtime enforcement: …` line. `NOT CHECKED` is the default when
@@ -31,9 +31,9 @@ export const CHECK_HQ_HOOKS_PASS_EXIT = 0;
31
31
  /** Exit code check-hq-hooks.sh returns on a health failure. Preserved. */
32
32
  export const CHECK_HQ_HOOKS_FAIL_EXIT = 2;
33
33
  /** Exit code check-hq-hooks.sh returns on a usage error. Preserved. */
34
- export const CHECK_HQ_HOOKS_USAGE_EXIT = 64;
34
+ const CHECK_HQ_HOOKS_USAGE_EXIT = 64;
35
35
  /** The runtime-probe result id: whether hooks actually fired (the ledger). */
36
- export const RUNTIME_ENFORCEMENT_CHECK_ID = "hooks.runtime.enforcement";
36
+ const RUNTIME_ENFORCEMENT_CHECK_ID = "hooks.runtime.enforcement";
37
37
  /**
38
38
  * The `hq doctor` check ids that correspond to check-hq-hooks.sh's *settings
39
39
  * load* scope — the subset of the hooks family the wrapper is allowed to fail
@@ -45,7 +45,7 @@ export const RUNTIME_ENFORCEMENT_CHECK_ID = "hooks.runtime.enforcement";
45
45
  * scope, so the wrapper stays byte-for-byte compatible with callers that only
46
46
  * ever cared whether the hook configuration could load.
47
47
  */
48
- export const CHECK_HQ_HOOKS_SETTINGS_SCOPE = [
48
+ const CHECK_HQ_HOOKS_SETTINGS_SCOPE = [
49
49
  "hooks.settings-present",
50
50
  "hooks.settings-required-command-hooks",
51
51
  "hooks.settings-valid-json",
@@ -126,24 +126,8 @@ export declare function claudeCodeFlagEvidence(env: NodeJS.ProcessEnv): Platform
126
126
  * `com.googlecode.iterm2`), so this fires only under a real desktop host.
127
127
  */
128
128
  export declare function bundleIdEvidence(env: NodeJS.ProcessEnv): PlatformEvidence | null;
129
- /**
130
- * Codex runtime environment variables, kept in parity with the canonical shell
131
- * detector `core/scripts/lib/detect-codex.sh`. Presence of any is a strong Codex
132
- * signal. One evidence entry is produced per variable found so a single Codex
133
- * signal can be tested in isolation.
134
- */
135
- export declare const CODEX_ENV_VARS: readonly ["CODEX_SANDBOX", "CODEX_SESSION_ID", "CODEX_EXECUTION_ID", "CODEX_AGENT_ID", "CODEX_THREAD_ID", "CODEX_SHELL", "CODEX_CI", "CODEX_INTERNAL_ORIGINATOR_OVERRIDE", "OPENAI_CODEX"];
136
129
  /** Every Codex environment signal present in `env`, one entry per variable. */
137
130
  export declare function codexEnvEvidence(env: NodeJS.ProcessEnv): PlatformEvidence[];
138
- /**
139
- * Grok runtime environment variables. Restricted to variables the Grok host
140
- * exports into the session (a session id, its workspace root) — HQ's own Grok
141
- * *launcher* variables (`GROK_BIN`, `GROK_CONFIG`, `XAI_API_KEY`, …) are
142
- * excluded on purpose: those can be set inside a Claude or Codex session that is
143
- * merely preparing to launch Grok, and treating them as "we are running under
144
- * Grok" would be a false positive.
145
- */
146
- export declare const GROK_ENV_VARS: readonly ["GROK_SESSION_ID", "GROK_WORKSPACE_ROOT"];
147
131
  /** Every Grok environment signal present in `env`, one entry per variable. */
148
132
  export declare function grokEnvEvidence(env: NodeJS.ProcessEnv): PlatformEvidence[];
149
133
  /**
@@ -157,10 +141,6 @@ export declare function grokEnvEvidence(env: NodeJS.ProcessEnv): PlatformEvidenc
157
141
  * on its environment signals instead, and this function does not guess it.
158
142
  */
159
143
  export declare function ancestryEvidence(ancestors: AncestorProcess[]): PlatformEvidence[];
160
- /** The one command this module ever launches to read process ancestry. */
161
- export declare const PS_COMMAND = "ps";
162
- /** `ps` arguments: every process as `pid ppid comm`, header-suppressed. */
163
- export declare const PS_ARGS: readonly ["-Ao", "pid=,ppid=,comm="];
164
144
  /**
165
145
  * Low-level process-table provider: runs a command and returns its stdout, or
166
146
  * `null` on failure. This is the single seam through which any process is
@@ -186,12 +166,6 @@ export interface ReadAncestryOptions {
186
166
  * environment signals rather than throwing.
187
167
  */
188
168
  export declare function readProcessAncestry(options?: ReadAncestryOptions): AncestorProcess[];
189
- /**
190
- * Collect the environment signals, in a stable order: entrypoint, the Claude
191
- * flag, bundle id, Codex vars, then Grok vars. Process ancestry is collected
192
- * separately (and lazily) by {@link detectPlatform}.
193
- */
194
- export declare function collectEnvEvidence(env: NodeJS.ProcessEnv): PlatformEvidence[];
195
169
  /**
196
170
  * Collect every signal from an environment and an explicit ancestor chain, in a
197
171
  * stable order: environment signals first, then process ancestry. Exposed for
@@ -150,7 +150,7 @@ function looksLikeClaudeDesktopBundle(id) {
150
150
  * signal. One evidence entry is produced per variable found so a single Codex
151
151
  * signal can be tested in isolation.
152
152
  */
153
- export const CODEX_ENV_VARS = [
153
+ const CODEX_ENV_VARS = [
154
154
  "CODEX_SANDBOX",
155
155
  "CODEX_SESSION_ID",
156
156
  "CODEX_EXECUTION_ID",
@@ -185,7 +185,7 @@ export function codexEnvEvidence(env) {
185
185
  * merely preparing to launch Grok, and treating them as "we are running under
186
186
  * Grok" would be a false positive.
187
187
  */
188
- export const GROK_ENV_VARS = ["GROK_SESSION_ID", "GROK_WORKSPACE_ROOT"];
188
+ const GROK_ENV_VARS = ["GROK_SESSION_ID", "GROK_WORKSPACE_ROOT"];
189
189
  /** Every Grok environment signal present in `env`, one entry per variable. */
190
190
  export function grokEnvEvidence(env) {
191
191
  const found = [];
@@ -249,9 +249,9 @@ const DEFAULT_MAX_HOPS = 20;
249
249
  /** Wall-clock bound for the single `ps` snapshot, keeping startup fast. */
250
250
  const PS_TIMEOUT_MS = 250;
251
251
  /** The one command this module ever launches to read process ancestry. */
252
- export const PS_COMMAND = "ps";
252
+ const PS_COMMAND = "ps";
253
253
  /** `ps` arguments: every process as `pid ppid comm`, header-suppressed. */
254
- export const PS_ARGS = ["-Ao", "pid=,ppid=,comm="];
254
+ const PS_ARGS = ["-Ao", "pid=,ppid=,comm="];
255
255
  /**
256
256
  * Read this process's ancestor chain (nearest parent first) by taking one `ps`
257
257
  * snapshot of the process table and walking parent pointers upward. A single
@@ -330,7 +330,7 @@ function defaultPsSpawner(command, args) {
330
330
  * flag, bundle id, Codex vars, then Grok vars. Process ancestry is collected
331
331
  * separately (and lazily) by {@link detectPlatform}.
332
332
  */
333
- export function collectEnvEvidence(env) {
333
+ function collectEnvEvidence(env) {
334
334
  const evidence = [];
335
335
  const entrypoint = entrypointEvidence(env);
336
336
  if (entrypoint)
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { laneRunsOnHost } from "./admission.js";
3
4
  import { appendDropboxMessage } from "./dropbox.js";
4
5
  import { LanesError } from "./errors.js";
5
6
  import { readLanesRegistryFlag } from "./registry-flag.js";
@@ -406,7 +407,7 @@ function recordOperatorEscalation(hqRoot, record) {
406
407
  async function runLeadAutopilotPassUnlocked(input) {
407
408
  const { hqRoot, host, now } = input;
408
409
  const result = { ok: true, host, evaluated: 0, sent: [], started: [], escalated: [], skipped: [] };
409
- const leads = parseRegistry(hqRoot).filter((lead) => lead.host === host || (host === "local" && lead.host === input.hostname));
410
+ const leads = parseRegistry(hqRoot);
410
411
  const lanes = readAllLanes(hqRoot);
411
412
  const byId = new Map(lanes.map((lane) => [lane.lane_id, lane]));
412
413
  const stateFile = path.join(hqRoot, "workspace", "lane-briefs", "autopilot-state.json");
@@ -422,131 +423,150 @@ async function runLeadAutopilotPassUnlocked(input) {
422
423
  continue;
423
424
  }
424
425
  const registeredLocalHost = registered.host === "local" || registered.host === input.hostname;
425
- if ((registeredLocalHost && (lead.host.kind !== "local" || lead.host.id !== input.hostname)) ||
426
- (!registeredLocalHost && (lead.host.kind !== "outpost" || lead.host.id !== registered.host || input.outpostId !== registered.host))) {
426
+ const hostMatchesRegistry = registeredLocalHost
427
+ ? lead.host.kind === "local"
428
+ : lead.host.kind === "outpost" && lead.host.id === registered.host && input.outpostId === registered.host;
429
+ if (!hostMatchesRegistry ||
430
+ !laneRunsOnHost(lead, { hostname: input.hostname, outpostId: input.outpostId })) {
427
431
  result.skipped.push({ lead_lane: lead.lane_id, reason: "lead_host_mismatch" });
428
432
  continue;
429
433
  }
430
- const enabled = await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_FLAG, { company: lead.company });
431
- if (!enabled) {
432
- result.skipped.push({ lead_lane: lead.lane_id, reason: "flag_off" });
433
- continue;
434
- }
435
- const status = readStatus(hqRoot, registered.status, lead.company.slug);
436
- const statusUpdatedAt = typeof status.updated_at === "string" && Number.isFinite(Date.parse(status.updated_at))
437
- ? new Date(Date.parse(status.updated_at)).toISOString()
438
- : undefined;
439
- result.evaluated += 1;
440
- const stories = projectStories(hqRoot, registered.prd, lead.company.slug, lead.project_id);
441
- const children = lanes.filter((lane) => lane.senior.kind === "lane" && lane.senior.id === lead.lane_id);
442
- const planned = planLeadAutopilotWakes({
443
- lead,
444
- children,
445
- stories,
446
- now,
447
- managerStatus: status,
448
- keepAliveResumed: input.keepAliveResumedLeads?.has(lead.lane_id) === true,
449
- });
450
434
  let stateChanged = false;
451
- const activeKeys = new Set(planned.map((wake) => `${lead.lane_id}:${wake.condition}`));
452
- for (const key of Object.keys(state.sent)) {
453
- if (key.startsWith(`${lead.lane_id}:`) && !activeKeys.has(key)) {
454
- delete state.sent[key];
455
- stateChanged = true;
435
+ try {
436
+ const enabled = await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_FLAG, { company: lead.company });
437
+ if (!enabled) {
438
+ result.skipped.push({ lead_lane: lead.lane_id, reason: "flag_off" });
439
+ continue;
456
440
  }
457
- }
458
- for (const wake of planned) {
459
- const key = `${lead.lane_id}:${wake.condition}`;
460
- const prior = state.sent[key];
461
- if (prior?.fingerprint === wake.fingerprint) {
462
- if (input.dryRun)
463
- continue;
464
- let current = prior;
465
- const lastWakeAt = Date.parse(prior.last_wake_at ?? prior.at);
466
- if (!Number.isFinite(lastWakeAt)) {
467
- throw new LanesError("lead_autopilot_state_invalid", `Invalid wake time for ${lead.lane_id}`, { lead_lane: lead.lane_id, story_id: wake.story_id });
468
- }
469
- if (statusUpdatedAt === undefined || prior.status_updated_at === undefined) {
470
- throw new LanesError("lead_autopilot_status_activity_unverifiable", `Cannot verify whether lead ${lead.lane_id} acted after its autopilot wake`, { lead_lane: lead.lane_id, story_id: wake.story_id });
471
- }
472
- if (!prior.acted && Date.parse(statusUpdatedAt) > Date.parse(prior.status_updated_at)) {
473
- current = { ...prior, acted: true };
474
- state.sent[key] = current;
441
+ const status = readStatus(hqRoot, registered.status, lead.company.slug);
442
+ const statusUpdatedAt = typeof status.updated_at === "string" && Number.isFinite(Date.parse(status.updated_at))
443
+ ? new Date(Date.parse(status.updated_at)).toISOString()
444
+ : undefined;
445
+ const stories = projectStories(hqRoot, registered.prd, lead.company.slug, lead.project_id);
446
+ const children = lanes.filter((lane) => lane.senior.kind === "lane" && lane.senior.id === lead.lane_id);
447
+ const planned = planLeadAutopilotWakes({
448
+ lead,
449
+ children,
450
+ stories,
451
+ now,
452
+ managerStatus: status,
453
+ keepAliveResumed: input.keepAliveResumedLeads?.has(lead.lane_id) === true,
454
+ });
455
+ const activeKeys = new Set(planned.map((wake) => `${lead.lane_id}:${wake.condition}`));
456
+ for (const key of Object.keys(state.sent)) {
457
+ if (key.startsWith(`${lead.lane_id}:`) && !activeKeys.has(key)) {
458
+ delete state.sent[key];
475
459
  stateChanged = true;
476
460
  }
477
- if (wake.condition === "fill_grant" && input.startWorker && !current.acted && !current.auto_started) {
478
- const firstWakeAt = Date.parse(current.at);
479
- if (!Number.isFinite(firstWakeAt)) {
480
- throw new LanesError("lead_autopilot_state_invalid", `Invalid fill-grant wake time for ${lead.lane_id}`, { lead_lane: lead.lane_id, story_id: wake.story_id });
461
+ }
462
+ for (const wake of planned) {
463
+ const key = `${lead.lane_id}:${wake.condition}`;
464
+ const prior = state.sent[key];
465
+ if (prior?.fingerprint === wake.fingerprint) {
466
+ if (input.dryRun)
467
+ continue;
468
+ let current = prior;
469
+ const lastWakeAt = Date.parse(prior.last_wake_at ?? prior.at);
470
+ if (!Number.isFinite(lastWakeAt)) {
471
+ throw new LanesError("lead_autopilot_state_invalid", `Invalid wake time for ${lead.lane_id}`, { lead_lane: lead.lane_id, story_id: wake.story_id });
472
+ }
473
+ if (statusUpdatedAt === undefined || prior.status_updated_at === undefined) {
474
+ throw new LanesError("lead_autopilot_status_activity_unverifiable", `Cannot verify whether lead ${lead.lane_id} acted after its autopilot wake`, { lead_lane: lead.lane_id, story_id: wake.story_id });
475
+ }
476
+ if (!prior.acted && Date.parse(statusUpdatedAt) > Date.parse(prior.status_updated_at)) {
477
+ current = { ...prior, acted: true };
478
+ state.sent[key] = current;
479
+ stateChanged = true;
481
480
  }
482
- if (now.getTime() - firstWakeAt >= LEAD_AUTOPILOT_FILL_DELAY_MS) {
483
- const story = stories.find((candidate) => candidate.id === wake.story_id && candidate.status === "todo");
484
- if (!story?.briefPath) {
485
- throw new LanesError("lead_autopilot_story_brief_missing", `Cannot auto-start story ${wake.story_id}: its project record has no valid brief path`, { lead_lane: lead.lane_id, story_id: wake.story_id, project_id: lead.project_id });
481
+ if (wake.condition === "fill_grant" && input.startWorker && !current.acted && !current.auto_started) {
482
+ const firstWakeAt = Date.parse(current.at);
483
+ if (!Number.isFinite(firstWakeAt)) {
484
+ throw new LanesError("lead_autopilot_state_invalid", `Invalid fill-grant wake time for ${lead.lane_id}`, { lead_lane: lead.lane_id, story_id: wake.story_id });
486
485
  }
487
- if (lead.state !== "running" && lead.state !== "queued") {
488
- current = { ...current, acted: true };
486
+ if (now.getTime() - firstWakeAt >= LEAD_AUTOPILOT_FILL_DELAY_MS) {
487
+ const story = stories.find((candidate) => candidate.id === wake.story_id && candidate.status === "todo");
488
+ if (!story?.briefPath) {
489
+ throw new LanesError("lead_autopilot_story_brief_missing", `Cannot auto-start story ${wake.story_id}: its project record has no valid brief path`, { lead_lane: lead.lane_id, story_id: wake.story_id, project_id: lead.project_id });
490
+ }
491
+ if (lead.state !== "running" && lead.state !== "queued") {
492
+ current = { ...current, acted: true };
493
+ state.sent[key] = current;
494
+ stateChanged = true;
495
+ result.skipped.push({ lead_lane: lead.lane_id, reason: "auto_start_lead_not_active" });
496
+ }
497
+ else {
498
+ const started = await input.startWorker({ lead, story });
499
+ current = { ...current, auto_started: true, worker_lane: started.lane_id };
500
+ state.sent[key] = current;
501
+ stateChanged = true;
502
+ result.started.push({ lead_lane: lead.lane_id, story_id: story.id, worker_lane: started.lane_id });
503
+ }
504
+ }
505
+ }
506
+ if (!current.acted && !current.auto_started && current.escalated_at === undefined && now.getTime() - lastWakeAt >= LEAD_AUTOPILOT_FILL_DELAY_MS) {
507
+ const wakeCount = current.wake_count ?? 1;
508
+ if (wakeCount < 2) {
509
+ appendDropboxMessage(hqRoot, lead.lane_id, {
510
+ at: now.toISOString(),
511
+ message_id: `lead-autopilot:${wake.fingerprint}:wake-2`,
512
+ kind: "system_notice",
513
+ text: wake.prompt,
514
+ });
515
+ current = { ...current, wake_count: 2, last_wake_at: now.toISOString() };
489
516
  state.sent[key] = current;
490
517
  stateChanged = true;
491
- result.skipped.push({ lead_lane: lead.lane_id, reason: "auto_start_lead_not_active" });
518
+ result.sent.push(wake);
492
519
  }
493
520
  else {
494
- const started = await input.startWorker({ lead, story });
495
- current = { ...current, auto_started: true, worker_lane: started.lane_id };
521
+ const escalation = { team: registered.team, lead_lane: lead.lane_id, condition: wake.condition, story_id: wake.story_id, at: now.toISOString(), fingerprint: wake.fingerprint };
522
+ recordOperatorEscalation(hqRoot, escalation);
523
+ current = { ...current, escalated_at: now.toISOString() };
496
524
  state.sent[key] = current;
497
525
  stateChanged = true;
498
- result.started.push({ lead_lane: lead.lane_id, story_id: story.id, worker_lane: started.lane_id });
526
+ result.escalated.push(escalation);
499
527
  }
500
528
  }
529
+ continue;
501
530
  }
502
- if (!current.acted && !current.auto_started && current.escalated_at === undefined && now.getTime() - lastWakeAt >= LEAD_AUTOPILOT_FILL_DELAY_MS) {
503
- const wakeCount = current.wake_count ?? 1;
504
- if (wakeCount < 2) {
505
- appendDropboxMessage(hqRoot, lead.lane_id, {
506
- at: now.toISOString(),
507
- message_id: `lead-autopilot:${wake.fingerprint}:wake-2`,
508
- kind: "system_notice",
509
- text: wake.prompt,
510
- });
511
- current = { ...current, wake_count: 2, last_wake_at: now.toISOString() };
512
- state.sent[key] = current;
513
- stateChanged = true;
514
- result.sent.push(wake);
515
- }
516
- else {
517
- const escalation = { team: registered.team, lead_lane: lead.lane_id, condition: wake.condition, story_id: wake.story_id, at: now.toISOString(), fingerprint: wake.fingerprint };
518
- recordOperatorEscalation(hqRoot, escalation);
519
- current = { ...current, escalated_at: now.toISOString() };
520
- state.sent[key] = current;
521
- stateChanged = true;
522
- result.escalated.push(escalation);
523
- }
531
+ if (!input.dryRun) {
532
+ appendDropboxMessage(hqRoot, lead.lane_id, {
533
+ at: now.toISOString(),
534
+ message_id: `lead-autopilot:${wake.fingerprint}`,
535
+ kind: "system_notice",
536
+ text: wake.prompt,
537
+ });
538
+ state.sent[key] = {
539
+ fingerprint: wake.fingerprint,
540
+ at: now.toISOString(),
541
+ ...(statusUpdatedAt ? { status_updated_at: statusUpdatedAt } : {}),
542
+ wake_count: 1,
543
+ last_wake_at: now.toISOString(),
544
+ };
545
+ stateChanged = true;
524
546
  }
525
- continue;
547
+ result.sent.push(wake);
526
548
  }
527
- if (!input.dryRun) {
528
- appendDropboxMessage(hqRoot, lead.lane_id, {
529
- at: now.toISOString(),
530
- message_id: `lead-autopilot:${wake.fingerprint}`,
531
- kind: "system_notice",
532
- text: wake.prompt,
533
- });
534
- state.sent[key] = {
535
- fingerprint: wake.fingerprint,
536
- at: now.toISOString(),
537
- ...(statusUpdatedAt ? { status_updated_at: statusUpdatedAt } : {}),
538
- wake_count: 1,
539
- last_wake_at: now.toISOString(),
540
- };
541
- stateChanged = true;
542
- }
543
- result.sent.push(wake);
549
+ if (!input.dryRun && stateChanged)
550
+ writeState(stateFile, state);
551
+ result.evaluated += 1;
552
+ }
553
+ catch (error) {
554
+ if (!input.dryRun && stateChanged)
555
+ writeState(stateFile, state);
556
+ if (!(error instanceof LanesError) || LEAD_AUTOPILOT_PASS_FATAL_ERRORS.has(error.error))
557
+ throw error;
558
+ result.skipped.push({ lead_lane: lead.lane_id, reason: error.error });
544
559
  }
545
- if (!input.dryRun && stateChanged)
546
- writeState(stateFile, state);
547
560
  }
548
561
  return result;
549
562
  }
563
+ // Tenant-boundary and path-safety failures stop the whole pass; other per-lead
564
+ // data errors skip only that lead.
565
+ const LEAD_AUTOPILOT_PASS_FATAL_ERRORS = new Set([
566
+ "lead_autopilot_tenant_mismatch",
567
+ "lead_autopilot_status_path_invalid",
568
+ "lead_autopilot_escalation_record_invalid",
569
+ ]);
550
570
  const LEAD_AUTOPILOT_STALE_LOCK_MAX_AGE_MS = 60 * 60_000;
551
571
  function processStartIdentity(pid) {
552
572
  try {
@@ -4,7 +4,7 @@
4
4
  * Each failure class maps to a distinct step in the onboarding flow,
5
5
  * making it easy for the installer to show targeted recovery hints.
6
6
  */
7
- export declare class OnboardingError extends Error {
7
+ declare class OnboardingError extends Error {
8
8
  readonly step: string;
9
9
  readonly cause?: Error | undefined;
10
10
  constructor(message: string, step: string, cause?: Error | undefined);
@@ -30,4 +30,5 @@ export declare class InviteAcceptError extends OnboardingError {
30
30
  export declare class StsVerifyError extends OnboardingError {
31
31
  constructor(message: string, cause?: Error);
32
32
  }
33
+ export {};
33
34
  //# sourceMappingURL=errors.d.ts.map
@@ -4,7 +4,7 @@
4
4
  * Each failure class maps to a distinct step in the onboarding flow,
5
5
  * making it easy for the installer to show targeted recovery hints.
6
6
  */
7
- export class OnboardingError extends Error {
7
+ class OnboardingError extends Error {
8
8
  step;
9
9
  cause;
10
10
  constructor(message, step, cause) {
@@ -7,11 +7,7 @@
7
7
  * invite/accept into create-company and join-company flows with
8
8
  * checkpoint/resume.
9
9
  */
10
- export { createCompanyFlow, joinCompanyFlow, resumeOnboarding, onboardingContract, } from "./orchestrator.js";
11
- export { readCheckpoint, writeCheckpoint, deleteCheckpoint, getCheckpointPath, isStepComplete, } from "./checkpoint.js";
12
- export { OnboardingError, PersonCreationError, CompanyCreationError, ProvisioningError, MembershipBootstrapError, FirstSyncError, InviteAcceptError, StsVerifyError, } from "./errors.js";
13
- export type { OnboardingInput, CreateCompanyInput, JoinCompanyInput, OnboardingConfig, OnboardingResult, OnboardingProgress, OnboardingStep, StepStatus, ProgressCallback, OnboardingCheckpoint, DesktopInstallerContract, HqConfig, } from "./types.js";
10
+ export { readCheckpoint } from "./checkpoint.js";
11
+ export type { OnboardingCheckpoint } from "./types.js";
14
12
  export { runOnboardCli } from "./cli/onboard.js";
15
- export type { OnboardCliOptions, OnboardCliResult } from "./cli/onboard.js";
16
- export { formatProgress, formatSummary, formatError, validateSlug, validateEmail } from "./cli/prompts.js";
17
13
  //# sourceMappingURL=index.d.ts.map
@@ -7,10 +7,7 @@
7
7
  * invite/accept into create-company and join-company flows with
8
8
  * checkpoint/resume.
9
9
  */
10
- export { createCompanyFlow, joinCompanyFlow, resumeOnboarding, onboardingContract, } from "./orchestrator.js";
11
- export { readCheckpoint, writeCheckpoint, deleteCheckpoint, getCheckpointPath, isStepComplete, } from "./checkpoint.js";
12
- export { OnboardingError, PersonCreationError, CompanyCreationError, ProvisioningError, MembershipBootstrapError, FirstSyncError, InviteAcceptError, StsVerifyError, } from "./errors.js";
13
- // CLI
10
+ // The CLI command and identity guard are the only consumers of this barrel.
11
+ export { readCheckpoint } from "./checkpoint.js";
14
12
  export { runOnboardCli } from "./cli/onboard.js";
15
- export { formatProgress, formatSummary, formatError, validateSlug, validateEmail } from "./cli/prompts.js";
16
13
  //# sourceMappingURL=index.js.map
@@ -11,13 +11,6 @@ export type EmbedLockProcessEvents = Pick<NodeJS.Process, 'once'> & {
11
11
  removeListener?: NodeJS.Process['removeListener'];
12
12
  };
13
13
  export type EmbedLockRunResult = 'ran' | 'skipped' | 'busy' | 'unavailable';
14
- /**
15
- * Serializes QMD writes started through HQ-managed hq-cli paths. A bare `qmd
16
- * update` / `qmd embed` (or another tool that does not call this module) cannot
17
- * participate, because qmd is a third-party package and does not know about
18
- * HQ's lock protocol.
19
- */
20
- export declare const EMBED_LOCK_NAME = "qmd-reindex-bg.lock";
21
14
  export declare function defaultEmbedLockDependencies(env?: NodeJS.ProcessEnv): EmbedLockDependencies;
22
15
  export declare function lockRoot(home: string): string;
23
16
  export declare function lockPath(home: string): string;
@@ -6,7 +6,7 @@ import * as path from 'node:path';
6
6
  * participate, because qmd is a third-party package and does not know about
7
7
  * HQ's lock protocol.
8
8
  */
9
- export const EMBED_LOCK_NAME = 'qmd-reindex-bg.lock';
9
+ const EMBED_LOCK_NAME = 'qmd-reindex-bg.lock';
10
10
  export function defaultEmbedLockDependencies(env = process.env) {
11
11
  return {
12
12
  env,
@@ -22,10 +22,6 @@ export type LoadGateDependencies = {
22
22
  freemem: () => number;
23
23
  totalmem: () => number;
24
24
  };
25
- type CpuCounters = {
26
- total: number;
27
- idle: number;
28
- };
29
25
  export type EmbedLoadDecision = {
30
26
  action: 'embed';
31
27
  forced: false;
@@ -44,11 +40,8 @@ export declare class LoadGateConfigurationError extends Error {
44
40
  constructor(value: string);
45
41
  }
46
42
  export declare function defaultLoadGateDependencies(env?: NodeJS.ProcessEnv): LoadGateDependencies;
47
- export declare function parseProcStatAggregate(contents: string): CpuCounters | undefined;
48
- export declare function cpuBusyPercent(before: CpuCounters, after: CpuCounters): number | undefined;
49
43
  /** Sample aggregate CPU busy time over 200 ms, falling back only without /proc/stat. */
50
44
  export declare function measureCpuPercent(dependencies: LoadGateDependencies): number;
51
- export declare function memAvailableFraction(contents: string): number | undefined;
52
45
  /** Prefer Linux MemAvailable, because MemFree alone treats reclaimable cache as used memory. */
53
46
  export declare function measureMemoryPercent(dependencies: LoadGateDependencies): number;
54
47
  export declare function parseLoadThreshold(env: NodeJS.ProcessEnv): number | undefined;
@@ -61,5 +54,4 @@ export declare function loadGateStatePath(home: string): string;
61
54
  export declare function checkEmbedLoad(home: string, dependencies: LoadGateDependencies): EmbedLoadDecision;
62
55
  /** Reset the starvation counter only after qmd embed has actually returned successfully. */
63
56
  export declare function recordSuccessfulEmbed(home: string, dependencies: LoadGateDependencies): void;
64
- export {};
65
57
  //# sourceMappingURL=load-gate.d.ts.map
@@ -46,7 +46,7 @@ function errorCode(error) {
46
46
  function missing(error) {
47
47
  return errorCode(error) === 'ENOENT';
48
48
  }
49
- export function parseProcStatAggregate(contents) {
49
+ function parseProcStatAggregate(contents) {
50
50
  const line = contents.split('\n').find((candidate) => /^cpu\s+/.test(candidate));
51
51
  if (!line)
52
52
  return undefined;
@@ -58,7 +58,7 @@ export function parseProcStatAggregate(contents) {
58
58
  const idle = values[3] + values[4];
59
59
  return total > 0 && Number.isFinite(idle) ? { total, idle } : undefined;
60
60
  }
61
- export function cpuBusyPercent(before, after) {
61
+ function cpuBusyPercent(before, after) {
62
62
  const totalDelta = after.total - before.total;
63
63
  const idleDelta = after.idle - before.idle;
64
64
  if (totalDelta <= 0 || idleDelta < 0)
@@ -117,7 +117,7 @@ export function measureCpuPercent(dependencies) {
117
117
  throw new Error('Cannot determine CPU busy time from /proc/stat.');
118
118
  return busy;
119
119
  }
120
- export function memAvailableFraction(contents) {
120
+ function memAvailableFraction(contents) {
121
121
  const fields = new Map();
122
122
  for (const line of contents.split('\n')) {
123
123
  const match = /^(MemTotal|MemAvailable):\s+(\d+)\s+kB$/.exec(line.trim());
@@ -53,8 +53,6 @@ import * as fs from "node:fs";
53
53
  * median and is, in practice, always generated output rather than prose.
54
54
  */
55
55
  export declare const DEFAULT_INDEX_MAX_DOC_BYTES: number;
56
- /** Where this module records the ignore entries it generated. */
57
- export declare function sizeIgnoreRecordPath(hqRoot: string): string;
58
56
  /**
59
57
  * qmd's config file, resolved the way qmd's own CLI resolves it.
60
58
  *
@@ -166,7 +164,7 @@ export declare function findOversizedFiles(collectionPath: string, mask: string,
166
164
  */
167
165
  export declare function prunablePrefixes(patterns: readonly string[]): string[];
168
166
  /** What a single collection ended up ignoring on account of the size cap. */
169
- export type CappedCollection = {
167
+ type CappedCollection = {
170
168
  name: string;
171
169
  ignored: string[];
172
170
  };
@@ -217,4 +215,5 @@ export type ApplyIndexSizeLimitOptions = {
217
215
  * take the lock, so the residual race is documented rather than papered over.
218
216
  */
219
217
  export declare function applyIndexSizeLimit(hqRoot: string, options?: ApplyIndexSizeLimitOptions): IndexSizeLimitResult;
218
+ export {};
220
219
  //# sourceMappingURL=max-doc-bytes.d.ts.map
@@ -69,7 +69,7 @@ const EXCLUDED_DIRECTORIES = new Set([
69
69
  "build",
70
70
  ]);
71
71
  /** Where this module records the ignore entries it generated. */
72
- export function sizeIgnoreRecordPath(hqRoot) {
72
+ function sizeIgnoreRecordPath(hqRoot) {
73
73
  return path.join(hqRoot, ".hq", "index-size-ignores.json");
74
74
  }
75
75
  /** qmd's default index name; the config file and store are both named after it. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.308.0",
3
+ "version": "5.309.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {