kibi-cli 2.5.1 → 2.6.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.
Files changed (33) hide show
  1. package/README.md +41 -0
  2. package/dist/intent-search.d.ts.map +1 -1
  3. package/dist/intent-search.js +12 -32
  4. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  5. package/dist/operations/bootstrap/generate.js +108 -8
  6. package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
  7. package/dist/operations/bootstrap/intent-claims.js +161 -4
  8. package/dist/operations/bootstrap/presentation.d.ts +8 -0
  9. package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
  10. package/dist/operations/bootstrap/presentation.js +92 -8
  11. package/dist/operations/bootstrap/types.d.ts +21 -0
  12. package/dist/operations/bootstrap/types.d.ts.map +1 -1
  13. package/dist/operations/bootstrap/types.js +10 -0
  14. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +7 -2
  15. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
  16. package/dist/operations/semantic-advisor/ingestion-boundary.js +61 -24
  17. package/dist/plugins/approved-source-analyzers.generated.d.ts +5 -5
  18. package/dist/plugins/approved-source-analyzers.generated.js +5 -5
  19. package/dist/public/operations/catalog.d.ts +46 -4
  20. package/dist/public/operations/catalog.d.ts.map +1 -1
  21. package/dist/public/operations/discovery-entities.d.ts +5 -3
  22. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  23. package/dist/public/operations/discovery-entities.js +8 -4
  24. package/dist/public/operations/specs/bootstrap.d.ts +46 -4
  25. package/dist/public/operations/specs/bootstrap.d.ts.map +1 -1
  26. package/dist/public/operations/specs/bootstrap.js +41 -5
  27. package/dist/public/skills/kibi-bootstrap/SKILL.md +13 -7
  28. package/dist/public/skills/kibi-bootstrap/resources/bootstrap.md +4 -1
  29. package/package.json +11 -6
  30. package/src/public/operations/discovery-entities.ts +14 -5
  31. package/src/public/operations/specs/bootstrap.ts +44 -4
  32. package/src/public/skills/kibi-bootstrap/SKILL.md +13 -7
  33. package/src/public/skills/kibi-bootstrap/resources/bootstrap.md +4 -1
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # kibi-cli
2
+
3
+ The `kibi` command-line interface for [Kibi](https://looted.github.io/kibi/), an agent-native requirements compiler and enforcement layer. Kibi keeps a branch-local knowledge base of requirements, scenarios, tests, facts and code-symbol links in your repository's `.kb/` directory and checks it with Prolog.
4
+
5
+ Most people install `kibi-cli` together with `kibi-core` and `kibi-mcp`, then let their coding agent drive it.
6
+
7
+ ## Install
8
+
9
+ Requires Node.js 22+. SWI-Prolog is bundled for Linux (x64/arm64, glibc 2.28+) and macOS (Apple silicon/Intel); elsewhere, install SWI-Prolog 9.0+ and put `swipl` on your `PATH`.
10
+
11
+ ```bash
12
+ npm install --save-dev kibi-core kibi-cli kibi-mcp
13
+ npm exec -- kibi init
14
+ ```
15
+
16
+ Do not install with `--omit=optional`: the bundled SWI-Prolog runtime is an optional dependency. The [installation guide](https://looted.github.io/kibi/guide/install.html) covers pnpm, Yarn and Bun.
17
+
18
+ ## Common commands
19
+
20
+ ```bash
21
+ kibi init # create the .kb/ layout and Git hooks
22
+ kibi doctor # diagnose the environment, including which SWI-Prolog is used
23
+ kibi sync # rebuild the KB from the checkout
24
+ kibi status # freshness and health of the current branch's KB
25
+ kibi search # ask a question of the KB
26
+ kibi check # validate traceability, contradictions and semantics
27
+ kibi report # write the kibi-report/ health page and badge
28
+ kibi migrate # preview and apply KB schema upgrades
29
+ ```
30
+
31
+ Agents without MCP can call the same operations through JSON routes, for example `printf '%s\n' '{...}' | npx --no-install kibi <route> --input -`. See the [CLI reference](https://looted.github.io/kibi/reference/cli.html) for every command and route.
32
+
33
+ ## Links
34
+
35
+ - [Documentation](https://looted.github.io/kibi/)
36
+ - [Source](https://github.com/Looted/kibi)
37
+ - [Issues](https://github.com/Looted/kibi/issues)
38
+
39
+ ## License
40
+
41
+ AGPL-3.0-or-later
@@ -1 +1 @@
1
- {"version":3,"file":"intent-search.d.ts","sourceRoot":"","sources":["../src/intent-search.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"intent-search.d.ts","sourceRoot":"","sources":["../src/intent-search.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { escapeAtom, normalizeEntityId, parseTriples } from "./prolog/codec.js";
3
- import { SEARCH_CANDIDATE_PAGE_SIZE, listSearchCandidates, loadEntities, loadEntityRows, loadSearchCandidates, } from "./public/operations/discovery-entities.js";
3
+ import { listSearchCandidates, loadEntityRows, loadSearchCandidates, } from "./public/operations/discovery-entities.js";
4
4
  import { loadMarkdownBody } from "./search-ranking.js";
5
5
  const GRAPH_RELATIONSHIPS = [
6
6
  "implements",
@@ -502,28 +502,18 @@ async function loadIntentCandidates(prolog, options) {
502
502
  candidates.set(key, { ...entity });
503
503
  }
504
504
  };
505
+ // Source-located candidates are projected rows too, read through the
506
+ // same source filter kb_query_entities applies, so a file with many
507
+ // receipt-bearing tests does not ship their complete payloads.
505
508
  const sourceLocations = options.sourceLocations ?? [];
506
- if (sourceLocations.length > 0 && prolog.queryEntities) {
507
- for (const location of sourceLocations) {
508
- if (candidates.size >= MAX_CANDIDATES)
509
- break;
510
- let offset = 0;
511
- let total = Number.POSITIVE_INFINITY;
512
- while (offset < total && candidates.size < MAX_CANDIDATES) {
513
- const limit = Math.min(SEARCH_CANDIDATE_PAGE_SIZE, MAX_CANDIDATES - candidates.size);
514
- const page = await prolog.queryEntities({
515
- ...(options.type !== undefined ? { type: options.type } : {}),
516
- sourceFile: location.path,
517
- limit,
518
- offset,
519
- });
520
- if (page.entities.length === 0)
521
- break;
522
- addCandidates(page.entities);
523
- total = page.count;
524
- offset += page.entities.length;
525
- }
526
- }
509
+ for (const location of sourceLocations) {
510
+ if (candidates.size >= MAX_CANDIDATES)
511
+ break;
512
+ addCandidates(await listSearchCandidates(prolog, {
513
+ ...(options.type !== undefined ? { type: options.type } : {}),
514
+ sourceFile: location.path,
515
+ maxCandidates: MAX_CANDIDATES - candidates.size,
516
+ }));
527
517
  }
528
518
  // Lexical candidates are projected rows (no receipt histories or other
529
519
  // large structured properties); ports without the engine method run the
@@ -544,16 +534,6 @@ async function loadIntentCandidates(prolog, options) {
544
534
  maxCandidates: MAX_CANDIDATES,
545
535
  }));
546
536
  }
547
- else if (sourceLocations.length > 0 && !prolog.queryEntities) {
548
- for (const location of sourceLocations) {
549
- if (candidates.size >= MAX_CANDIDATES)
550
- break;
551
- addCandidates(await loadEntities(prolog, {
552
- ...(options.type !== undefined ? { type: options.type } : {}),
553
- sourceFile: location.path,
554
- }));
555
- }
556
- }
557
537
  // An unfamiliar host-agent alias may not exist in the lexical index at all.
558
538
  // For small facet-bearing corpora, scan the bounded entity set so a zero
559
539
  // lexical hit does not turn a valid semantic query into a false abstention.
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/generate.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAOjF,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACzC,SAAS,gBAAgB,EAAE,CAI7B;AA8BD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM,GACd;IACD,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAClE,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,CAsFA;AAiHD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CA6F9B"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/generate.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAUjF,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAGpB,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACzC,SAAS,gBAAgB,EAAE,CAI7B;AA+BD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM,GACd;IACD,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAClE,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,CA6IA;AA+JD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAqG9B"}
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { loadEntityIds } from "../../public/operations/discovery-entities.js";
4
4
  import { executeStatus } from "../../public/operations/discovery-executors.js";
5
5
  import { readWorkspaceSnapshot } from "../../public/operations/workspace-snapshot.js";
6
+ import { stagedLogicalGroundingError, } from "../semantic-advisor/ingestion-boundary.js";
6
7
  import { buildBootstrapCandidates } from "./candidates.js";
7
8
  import { discoverBootstrap } from "./discovery.js";
8
9
  import { buildIntentClaimCandidates } from "./intent-claims.js";
@@ -35,6 +36,7 @@ function firstUpsertId(candidate) {
35
36
  function sha256(value) {
36
37
  return createHash("sha256").update(value).digest("hex");
37
38
  }
39
+ // implements REQ-bootstrap-write-safety, REQ-bootstrap-intent-claim-accounting, REQ-bootstrap-discovered-candidate-budget
38
40
  export function selectBootstrapCandidates(input, existingIds, entityTypes, maximum) {
39
41
  const allowed = entityTypes?.length ? new Set(entityTypes) : null;
40
42
  const lane = (candidate) => {
@@ -59,8 +61,12 @@ export function selectBootstrapCandidates(input, existingIds, entityTypes, maxim
59
61
  };
60
62
  const writable = input
61
63
  .filter((candidate) => {
62
- if (allowed && !allowed.has(candidate.entityType))
64
+ if (allowed && !allowed.has(candidate.entityType)) {
65
+ // A filtered claim is the caller's choice, not a dropped claim.
66
+ if (candidate.sourceKind === "intent_claim")
67
+ suppress(candidate, "filtered_by_entity_type");
63
68
  return false;
69
+ }
64
70
  if (existingIds.has(firstUpsertId(candidate))) {
65
71
  suppress(candidate, "entity_exists");
66
72
  return false;
@@ -105,16 +111,100 @@ export function selectBootstrapCandidates(input, existingIds, entityTypes, maxim
105
111
  else
106
112
  selected.set(key, candidate);
107
113
  }
108
- const unique = [...selected.values()];
109
- for (const candidate of unique.slice(maximum))
110
- suppress(candidate, "over_limit");
114
+ // Declared intent claims are never capped and sit outside the arithmetic:
115
+ // they are what the human pointed at, and the schema already bounds them.
116
+ // maxCandidates is the budget for candidates Kibi discovers itself, so a
117
+ // long list of declared intent never starves the code, test and document
118
+ // evidence that bootstrap is meant to link it to.
119
+ const written = new Map();
120
+ const staged = new Map();
121
+ const candidates = [];
122
+ let discoveredCount = 0;
123
+ let overLimit = 0;
124
+ for (const candidate of selected.values()) {
125
+ const discovered = candidate.sourceKind !== "intent_claim";
126
+ if (discovered && discoveredCount >= maximum) {
127
+ suppress(candidate, "over_limit");
128
+ overLimit += 1;
129
+ continue;
130
+ }
131
+ // Two candidates that write one entity ID with different content would
132
+ // overwrite each other mid-apply and break the earlier one's grounding.
133
+ const conflict = candidate.applyPlan.find((payload) => {
134
+ const id = payloadId(payload);
135
+ const previous = id ? written.get(id) : undefined;
136
+ return previous !== undefined && previous !== JSON.stringify(payload);
137
+ });
138
+ if (conflict) {
139
+ suppress(candidate, "duplicate_entity", `Entity ${payloadId(conflict)} is already written by another candidate with different content.`);
140
+ continue;
141
+ }
142
+ const groundingError = stagedGroundingError(candidate, staged);
143
+ if (groundingError) {
144
+ suppress(candidate, "invalid_write", groundingError);
145
+ diagnostics.push(`Invalid bootstrap candidate at ${candidate.sourcePath}: ${groundingError}`);
146
+ continue;
147
+ }
148
+ for (const payload of candidate.applyPlan) {
149
+ const id = payloadId(payload);
150
+ if (!id)
151
+ continue;
152
+ written.set(id, JSON.stringify(payload));
153
+ staged.set(id, stagedEntity(payload));
154
+ }
155
+ if (discovered)
156
+ discoveredCount += 1;
157
+ candidates.push(candidate);
158
+ }
159
+ if (overLimit > 0)
160
+ diagnostics.push(`${overLimit} discovered candidate(s) exceeded maxCandidates ${maximum} and are suppressed as over_limit; declared intent claims do not count against it.`);
111
161
  return {
112
- candidates: unique.slice(0, maximum),
162
+ candidates,
113
163
  suppressed,
114
164
  sourceOnlySignals,
115
165
  diagnostics,
116
166
  };
117
167
  }
168
+ function payloadId(payload) {
169
+ if (typeof payload.id === "string")
170
+ return payload.id;
171
+ const properties = payload.properties;
172
+ return properties !== null &&
173
+ typeof properties === "object" &&
174
+ "id" in properties &&
175
+ typeof properties.id === "string"
176
+ ? properties.id
177
+ : "";
178
+ }
179
+ function stagedEntity(payload) {
180
+ const properties = payload.properties !== null && typeof payload.properties === "object"
181
+ ? payload.properties
182
+ : {};
183
+ return { ...properties, id: payloadId(payload), type: payload.type };
184
+ }
185
+ /**
186
+ * Run the write-time claim_key grounding check against the plan's own
187
+ * earlier writes, so a mismatch is a suppressed candidate at plan time rather
188
+ * than a terminal partial apply. Targets outside the plan are left to apply.
189
+ */
190
+ // implements REQ-bootstrap-intent-claim-accounting
191
+ function stagedGroundingError(candidate, planned) {
192
+ const local = new Map(planned);
193
+ for (const payload of candidate.applyPlan) {
194
+ const id = payloadId(payload);
195
+ if (id)
196
+ local.set(id, stagedEntity(payload));
197
+ }
198
+ for (const payload of candidate.applyPlan) {
199
+ const relationships = Array.isArray(payload.relationships)
200
+ ? payload.relationships
201
+ : [];
202
+ const error = stagedLogicalGroundingError(payload, relationships, local);
203
+ if (error)
204
+ return error;
205
+ }
206
+ return null;
207
+ }
118
208
  async function existingEntityIds(context) {
119
209
  if (!context.prolog)
120
210
  return new Set();
@@ -202,10 +292,15 @@ async function expectedSnapshots(context, evidencePaths) {
202
292
  };
203
293
  }
204
294
  }
205
- // implements REQ-KIBI-BOOTSTRAP-PLAN, REQ-kibi-operation-interface-parity
295
+ // implements REQ-KIBI-BOOTSTRAP-PLAN, REQ-kibi-operation-interface-parity, REQ-bootstrap-discovered-candidate-budget
206
296
  // implements REQ-KIBI-BOOTSTRAP-PLAN
207
297
  export async function executePlanBootstrap(args, context) {
208
- const includeGenericMarkdown = args.includeGenericMarkdown ?? true;
298
+ const declared = normalizeBootstrapContext(args.bootstrapContext);
299
+ // Declared intent claims already carry the human's sources of truth, so
300
+ // generic Markdown would mostly restate them as duplicate_title noise.
301
+ const genericMarkdownDefaultOff = args.includeGenericMarkdown === undefined &&
302
+ (declared.intentClaims?.length ?? 0) > 0;
303
+ const includeGenericMarkdown = args.includeGenericMarkdown ?? !genericMarkdownDefaultOff;
209
304
  const minConfidence = clamp(args.minConfidence ?? 0.8, 0.6, 0.95);
210
305
  const maxCandidates = clamp(Math.trunc(args.maxCandidates ?? 50), 1, 200);
211
306
  const entityBindingDiagnostics = [];
@@ -232,7 +327,7 @@ export async function executePlanBootstrap(args, context) {
232
327
  // Intent harvested from declared knowledge sources joins the repository
233
328
  // evidence under the same activation policy, filters, and selection.
234
329
  const claimed = discovery.activation.allowCandidateGeneration
235
- ? buildIntentClaimCandidates(normalizeBootstrapContext(args.bootstrapContext), existingIds, minConfidence)
330
+ ? buildIntentClaimCandidates(declared, existingIds, minConfidence)
236
331
  : {
237
332
  candidates: [],
238
333
  sourceOnlySignals: [],
@@ -270,6 +365,11 @@ export async function executePlanBootstrap(args, context) {
270
365
  expected,
271
366
  bindingDiagnostics: [...bindingDiagnostics, ...entityBindingDiagnostics],
272
367
  contextDiagnostics: [
368
+ ...(genericMarkdownDefaultOff
369
+ ? [
370
+ "Generic Markdown candidates are off by default because intent claims were declared; pass includeGenericMarkdown: true to include them.",
371
+ ]
372
+ : []),
273
373
  ...claimed.diagnostics,
274
374
  ...built.diagnostics,
275
375
  ...selected.diagnostics,
@@ -1 +1 @@
1
- {"version":3,"file":"intent-claims.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/intent-claims.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,wBAAwB,EAGxB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAQpB,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAClE,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,CAAC;AAcF;;;;;;;GAOG;AAEH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,wBAAwB,EAClC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,GACpB,sBAAsB,CA2FxB"}
1
+ {"version":3,"file":"intent-claims.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/intent-claims.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,wBAAwB,EAGxB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAQpB,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAClE,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,CAAC;AAuKF;;;;;;;GAOG;AAEH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,wBAAwB,EAClC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,GACpB,sBAAsB,CA8HxB"}
@@ -1,5 +1,6 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { buildStrictWriteSet } from "../../utils/strict-modeling.js";
2
- import { confidenceBand, strictPlan } from "./candidate-helpers.js";
3
+ import { confidenceBand, strictPlan, upsert } from "./candidate-helpers.js";
3
4
  import { claimFor } from "./requirement-claims.js";
4
5
  /** How much a claim is trusted follows the authority the human assigned. */
5
6
  const AUTHORITY_CONFIDENCE = {
@@ -14,6 +15,132 @@ function citation(source, claim) {
14
15
  ...(source.connector ? [`connector:${source.connector}`] : []),
15
16
  ];
16
17
  }
18
+ function digest(...parts) {
19
+ return createHash("sha256")
20
+ .update(parts.join("\u0000"))
21
+ .digest("hex")
22
+ .slice(0, 16)
23
+ .toUpperCase();
24
+ }
25
+ /**
26
+ * An observation or open question from a declared source is evidence for the
27
+ * human, not a requirement: it becomes a cited, non-blocking observation fact
28
+ * so the plan keeps it without entering the contradiction lane.
29
+ */
30
+ // implements REQ-bootstrap-claim-kinds-and-conflicts
31
+ function reviewClaimCandidate(source, claim, kind, confidence) {
32
+ const id = `FACT-BOOTSTRAP-${kind === "open_question" ? "QUESTION" : "OBSERVATION"}-${digest(source.id, claim.reference, claim.statement)}`;
33
+ const entity = {
34
+ type: "fact",
35
+ id,
36
+ title: claim.statement,
37
+ status: "active",
38
+ fact_kind: "observation",
39
+ source: `knowledge-source:${source.id}`,
40
+ text_ref: `${source.id}:${claim.reference}`,
41
+ tags: [
42
+ "bootstrap",
43
+ `knowledge-source:${source.id}`,
44
+ kind === "open_question" ? "review:open-question" : "claim:observation",
45
+ ],
46
+ };
47
+ return {
48
+ candidateId: `claim:${source.id}:${id.toLowerCase()}`,
49
+ entityType: "fact",
50
+ title: claim.statement,
51
+ sourceKind: "intent_claim",
52
+ sourcePath: claim.reference,
53
+ confidence,
54
+ confidenceBand: confidenceBand(confidence),
55
+ evidence: [
56
+ ...citation(source, claim),
57
+ `claim_kind:${kind}`,
58
+ ...(claim.excerpt ? [`excerpt:${claim.excerpt}`] : []),
59
+ ],
60
+ relationships: [],
61
+ applyPlan: [upsert(entity)],
62
+ };
63
+ }
64
+ /**
65
+ * A conflict the agent declared between claims becomes an observation fact
66
+ * tagged review:conflict that cites every conflicting claim, so the
67
+ * disagreement stays explicit in the KB instead of an external report.
68
+ */
69
+ // implements REQ-bootstrap-claim-kinds-and-conflicts
70
+ function conflictCandidates(declared, sources, existingIds, minConfidence) {
71
+ const claims = new Set((declared.intentClaims ?? []).map((claim) => `${claim.sourceId}\u0000${claim.reference}`));
72
+ const candidates = [];
73
+ const suppressed = [];
74
+ const diagnostics = [];
75
+ const confidence = AUTHORITY_CONFIDENCE.authoritative;
76
+ for (const conflict of declared.conflicts ?? []) {
77
+ const refs = conflict.claimReferences.map((ref) => `${ref.sourceId}:${ref.reference}`);
78
+ const undeclared = conflict.claimReferences.filter((ref) => !claims.has(`${ref.sourceId}\u0000${ref.reference}`));
79
+ if (undeclared.length > 0) {
80
+ diagnostics.push(`Conflict "${conflict.note}" cites claim(s) not declared in intentClaims: ${undeclared.map((ref) => `${ref.sourceId}:${ref.reference}`).join(", ")}; declare them or correct the reference.`);
81
+ continue;
82
+ }
83
+ const id = `FACT-BOOTSTRAP-CONFLICT-${digest(...[...refs].sort(), conflict.note)}`;
84
+ const candidateId = `conflict:${id.toLowerCase()}`;
85
+ if (confidence < minConfidence) {
86
+ suppressed.push({
87
+ candidateId,
88
+ reason: "below_min_confidence",
89
+ sourcePath: refs.join(", "),
90
+ entityType: "fact",
91
+ });
92
+ continue;
93
+ }
94
+ if (existingIds.has(id)) {
95
+ suppressed.push({
96
+ candidateId,
97
+ reason: "entity_exists",
98
+ sourcePath: refs.join(", "),
99
+ entityType: "fact",
100
+ });
101
+ continue;
102
+ }
103
+ const title = `Conflict between ${refs.join(" and ")}: ${conflict.note}`;
104
+ candidates.push({
105
+ candidateId,
106
+ entityType: "fact",
107
+ title,
108
+ sourceKind: "intent_claim",
109
+ sourcePath: refs.join(", "),
110
+ confidence,
111
+ confidenceBand: confidenceBand(confidence),
112
+ evidence: conflict.claimReferences.flatMap((ref) => {
113
+ const source = sources.get(ref.sourceId);
114
+ return [
115
+ `intent_claim:${ref.sourceId}:${ref.reference}`,
116
+ ...(source
117
+ ? [`knowledge_source:${source.kind}:${source.locator}`]
118
+ : []),
119
+ ];
120
+ }),
121
+ relationships: [],
122
+ applyPlan: [
123
+ upsert({
124
+ type: "fact",
125
+ id,
126
+ title,
127
+ status: "active",
128
+ fact_kind: "observation",
129
+ source: "bootstrap:declared-conflict",
130
+ text_ref: refs.join("; "),
131
+ tags: [
132
+ "bootstrap",
133
+ "review:conflict",
134
+ ...[
135
+ ...new Set(conflict.claimReferences.map((ref) => `knowledge-source:${ref.sourceId}`)),
136
+ ],
137
+ ],
138
+ }),
139
+ ],
140
+ });
141
+ }
142
+ return { candidates, sourceOnlySignals: [], suppressed, diagnostics };
143
+ }
17
144
  /**
18
145
  * Turn intent the agent harvested from declared knowledge sources into cited
19
146
  * bootstrap candidates. Kibi never contacts those sources: it only records
@@ -22,7 +149,7 @@ function citation(source, claim) {
22
149
  * strict modeler cannot ground stays an explicit authoring follow-up instead
23
150
  * of becoming unverifiable knowledge.
24
151
  */
25
- // implements REQ-kibi-bootstrap-knowledge-sources
152
+ // implements REQ-kibi-bootstrap-knowledge-sources, REQ-bootstrap-claim-kinds-and-conflicts
26
153
  export function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
27
154
  const sources = new Map((declared.knowledgeSources ?? []).map((source) => [source.id, source]));
28
155
  const candidates = [];
@@ -35,18 +162,35 @@ export function buildIntentClaimCandidates(declared, existingIds, minConfidence)
35
162
  diagnostics.push(`Intent claim cites an undeclared knowledge source "${claim.sourceId}" (${claim.reference}); declare it in knowledgeSources or drop the claim.`);
36
163
  continue;
37
164
  }
165
+ const kind = claim.kind ?? "intent";
38
166
  if (source.authority === "stale") {
39
167
  suppressed.push({
40
168
  candidateId: "",
41
169
  reason: "stale_knowledge_source",
42
170
  sourcePath: claim.reference,
43
- entityType: "req",
171
+ entityType: kind === "intent" ? "req" : "fact",
44
172
  statement: claim.statement,
45
173
  sourceId: source.id,
46
174
  });
47
175
  continue;
48
176
  }
49
177
  const confidence = AUTHORITY_CONFIDENCE[source.authority];
178
+ if (kind !== "intent") {
179
+ const review = reviewClaimCandidate(source, claim, kind, confidence);
180
+ const id = String(review.applyPlan[0]?.id ?? "");
181
+ if (confidence < minConfidence || existingIds.has(id))
182
+ suppressed.push({
183
+ candidateId: review.candidateId,
184
+ reason: confidence < minConfidence
185
+ ? "below_min_confidence"
186
+ : "entity_exists",
187
+ sourcePath: claim.reference,
188
+ entityType: "fact",
189
+ });
190
+ else
191
+ candidates.push(review);
192
+ continue;
193
+ }
50
194
  const evidence = citation(source, claim);
51
195
  const provenance = `${source.id}:${claim.reference}`;
52
196
  let writeSet = null;
@@ -82,6 +226,13 @@ export function buildIntentClaimCandidates(declared, existingIds, minConfidence)
82
226
  });
83
227
  continue;
84
228
  }
229
+ if (writeSet?.isStrict && confidence < minConfidence)
230
+ suppressed.push({
231
+ candidateId: `claim:${source.id}:${writeSet.req.id.toLowerCase()}`,
232
+ reason: "below_min_confidence",
233
+ sourcePath: claim.reference,
234
+ entityType: "req",
235
+ });
85
236
  if (writeSet?.isStrict && existingIds.has(writeSet.req.id)) {
86
237
  suppressed.push({
87
238
  candidateId: `claim:${source.id}:${writeSet.req.id.toLowerCase()}`,
@@ -99,5 +250,11 @@ export function buildIntentClaimCandidates(declared, existingIds, minConfidence)
99
250
  evidence,
100
251
  });
101
252
  }
102
- return { candidates, sourceOnlySignals, suppressed, diagnostics };
253
+ const declaredConflicts = conflictCandidates(declared, sources, existingIds, minConfidence);
254
+ return {
255
+ candidates: [...candidates, ...declaredConflicts.candidates],
256
+ sourceOnlySignals,
257
+ suppressed: [...suppressed, ...declaredConflicts.suppressed],
258
+ diagnostics: [...diagnostics, ...declaredConflicts.diagnostics],
259
+ };
103
260
  }
@@ -1,5 +1,13 @@
1
1
  import type { ActivationPolicy, BootstrapContext, BootstrapDeclaredContext, BootstrapPlanV1, Candidate, DiscoverySummary, PlanBootstrapResult, SourceOnlySignal } from "./types.js";
2
2
  export declare function normalizeBootstrapContext(input?: BootstrapContext): BootstrapDeclaredContext;
3
+ /**
4
+ * Suppressions per reason, most frequent first, so a plan with hundreds of
5
+ * suppressed rows still reads as a handful of lines.
6
+ */
7
+ export declare function suppressionCounts(rows: readonly Readonly<Record<string, unknown>>[]): readonly {
8
+ readonly reason: string;
9
+ readonly count: number;
10
+ }[];
3
11
  export declare function presentBootstrap(input: {
4
12
  readonly root: string;
5
13
  readonly activation: ActivationPolicy;
@@ -1 +1 @@
1
- {"version":3,"file":"presentation.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/presentation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAEhB,gBAAgB,EAChB,wBAAwB,EAGxB,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAwEpB,wBAAgB,yBAAyB,CACvC,KAAK,CAAC,EAAE,gBAAgB,GACvB,wBAAwB,CAa1B;AAiBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,oBAAoB,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAC5E,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,wEAAwE;IACxE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjD,GAAG,mBAAmB,CAsLtB"}
1
+ {"version":3,"file":"presentation.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/presentation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAGhB,gBAAgB,EAChB,wBAAwB,EAGxB,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAuGpB,wBAAgB,yBAAyB,CACvC,KAAK,CAAC,EAAE,gBAAgB,GACvB,wBAAwB,CAe1B;AAgBD;;;GAGG;AAEH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,GACjD,SAAS;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAYhE;AA0CD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,oBAAoB,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAC5E,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IAC/C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,wEAAwE;IACxE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjD,GAAG,mBAAmB,CAmNtB"}