@indigoai-us/hq-cli 5.238.0 → 5.239.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,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.239.0] — 2026-09-27
6
+
7
+ ### Added
8
+
9
+ - `hq core checkpoint` can skip a knowledge link to a concept entity whose
10
+ name is also the name of an entity of another type, such as a concept
11
+ `hq-secrets` beside a project `HQ Secrets`. The report lists the skipped
12
+ link as rejected with reason `ambiguous_type`. Signal and session inputs,
13
+ other links and new entities are unchanged. Behind the hq-flags flag
14
+ `checkpoint.ontology.skip-ambiguous-concept-links`, off by default.
15
+
5
16
  ## [5.238.0] — 2026-09-27
6
17
 
7
18
  ### Added: Lane orphan reporting
@@ -262,6 +262,25 @@ export function applyOntologyRun(options) {
262
262
  }
263
263
  report.candidates = rawCandidates.length;
264
264
  const index = buildEntityIndex(company.dir);
265
+ // checkpoint.ontology.skip-ambiguous-concept-links: canonical names held by entities of more than one type.
266
+ // Names compare in the non-organization form for every type (lowercase, keeps + and #, no squashing),
267
+ // so "C++" and "C", or "AB" and "A B", stay distinct.
268
+ const crossTypeKey = (name) => normalizeEntityName(name, "concept");
269
+ const crossTypeNames = new Set();
270
+ if (plan.skip_ambiguous_concept_links === true) {
271
+ const typesByName = new Map();
272
+ for (const entity of index.byRel.values()) {
273
+ const name = crossTypeKey(entity.canonical_name);
274
+ if (name === "")
275
+ continue;
276
+ const types = typesByName.get(name) ?? new Set();
277
+ types.add(entity.type);
278
+ typesByName.set(name, types);
279
+ }
280
+ for (const [name, types] of typesByName)
281
+ if (types.size > 1)
282
+ crossTypeNames.add(name);
283
+ }
265
284
  const inputs = liveInputs(options.hqRoot, company, plan);
266
285
  const blocked = new Set();
267
286
  const updates = new Map();
@@ -313,8 +332,14 @@ export function applyOntologyRun(options) {
313
332
  continue;
314
333
  }
315
334
  if (resolved.tier === 1 || resolved.tier === 2) {
316
- report.matched += 1;
317
335
  const linked = index.byRel.get(resolved.target);
336
+ if (input.kind === "knowledge" &&
337
+ linked.type === "concept" &&
338
+ crossTypeNames.has(crossTypeKey(linked.canonical_name))) {
339
+ note(report.rejected, "ambiguous_type");
340
+ continue;
341
+ }
342
+ report.matched += 1;
318
343
  report.links.push({ input: candidate.input, action: "link", type: linked.type, canonical_name: linked.canonical_name, path: resolved.target });
319
344
  if (resolved.tier === 1 && resolved.duplicates.length > 0) {
320
345
  report.suspect_duplicates.push({ name: canonical, type: candidate.type, paths: [resolved.target, ...resolved.duplicates] });
@@ -68,6 +68,16 @@ export declare const NO_LINK_ALIASES_FLAG = "checkpoint.ontology.no-link-aliases
68
68
  * the model cannot see that `John Smith` already exists.
69
69
  */
70
70
  export declare const FIRST_NAME_PEOPLE_FLAG = "checkpoint.ontology.first-name-people";
71
+ /**
72
+ * A knowledge input does not link to a concept entity whose canonical name is
73
+ * also the canonical name of an entity of another type (`concept/hq-secrets`
74
+ * beside `project/hq-secrets`). The plan carries
75
+ * `skip_ambiguous_concept_links: true`, and apply rejects the link as
76
+ * `ambiguous_type`. CO-46: 34 of the 73 false positives on the knowledge
77
+ * fixtures were such concept links; skipping them raised precision from 0.733
78
+ * to 0.803 and cost recall 0.023 on a replay of six runs.
79
+ */
80
+ export declare const SKIP_AMBIGUOUS_CONCEPT_LINKS_FLAG = "checkpoint.ontology.skip-ambiguous-concept-links";
71
81
  export declare const PLAN_FILE = "ontology-plan.json";
72
82
  export declare const CANDIDATES_FILE = "ontology-candidates.json";
73
83
  export declare const REPORT_FILE = "ontology-report.json";
@@ -138,6 +148,8 @@ export interface OntologyPlan {
138
148
  link_aliases?: false;
139
149
  /** Set by checkpoint.ontology.first-name-people: apply defers a one-word person who may be an existing fuller name. */
140
150
  first_name_people?: true;
151
+ /** Set by checkpoint.ontology.skip-ambiguous-concept-links: apply rejects a knowledge link to a concept whose name another type also holds. */
152
+ skip_ambiguous_concept_links?: true;
141
153
  inputs: PlanInput[];
142
154
  /** The session's summary, decisions and learnings, for the `session` input. */
143
155
  session_text: string | null;
@@ -72,6 +72,16 @@ export const NO_LINK_ALIASES_FLAG = "checkpoint.ontology.no-link-aliases";
72
72
  * the model cannot see that `John Smith` already exists.
73
73
  */
74
74
  export const FIRST_NAME_PEOPLE_FLAG = "checkpoint.ontology.first-name-people";
75
+ /**
76
+ * A knowledge input does not link to a concept entity whose canonical name is
77
+ * also the canonical name of an entity of another type (`concept/hq-secrets`
78
+ * beside `project/hq-secrets`). The plan carries
79
+ * `skip_ambiguous_concept_links: true`, and apply rejects the link as
80
+ * `ambiguous_type`. CO-46: 34 of the 73 false positives on the knowledge
81
+ * fixtures were such concept links; skipping them raised precision from 0.733
82
+ * to 0.803 and cost recall 0.023 on a replay of six runs.
83
+ */
84
+ export const SKIP_AMBIGUOUS_CONCEPT_LINKS_FLAG = "checkpoint.ontology.skip-ambiguous-concept-links";
75
85
  export const PLAN_FILE = "ontology-plan.json";
76
86
  export const CANDIDATES_FILE = "ontology-candidates.json";
77
87
  export const REPORT_FILE = "ontology-report.json";
@@ -254,6 +264,7 @@ export async function planOntologyRun(options) {
254
264
  const hintsOn = await options.readFlag(ENTITY_HINTS_FLAG, company);
255
265
  const noLinkAliases = await options.readFlag(NO_LINK_ALIASES_FLAG, company);
256
266
  const firstNamePeople = await options.readFlag(FIRST_NAME_PEOPLE_FLAG, company);
267
+ const skipAmbiguousConceptLinks = await options.readFlag(SKIP_AMBIGUOUS_CONCEPT_LINKS_FLAG, company);
257
268
  const touched = sessionTouched(hqRoot, company, payload.files);
258
269
  // A first run takes its boundary from the entity tree, so the ledger is
259
270
  // refreshed before merge-back moves any `last_updated` forward.
@@ -368,6 +379,7 @@ export async function planOntologyRun(options) {
368
379
  ...(multiWriter ? { multi_writer: multiWriter } : {}),
369
380
  ...(noLinkAliases ? { link_aliases: false } : {}),
370
381
  ...(firstNamePeople ? { first_name_people: true } : {}),
382
+ ...(skipAmbiguousConceptLinks ? { skip_ambiguous_concept_links: true } : {}),
371
383
  inputs,
372
384
  session_text: sessionText,
373
385
  pending_after_run: remaining,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.238.0",
3
+ "version": "5.239.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {