kibi-runtime 2.8.0 → 2.9.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/dist/index.js CHANGED
@@ -577,6 +577,10 @@ var init_contracts = __esm(() => {
577
577
  },
578
578
  existingGrounding: recordArray,
579
579
  replacementPlan: { type: ["object", "null"] },
580
+ bindingHints: {
581
+ ...recordArray,
582
+ description: "On provide_argument_bindings: one entry per unbound argument of the recommended candidate with its type, declared constants (allowedValues) when the vocabulary is closed, schema example values and the reason the current value was not accepted. Empty otherwise."
583
+ },
580
584
  warnings: stringArray
581
585
  }),
582
586
  kb_plan_bootstrap: objectData({
@@ -27400,6 +27404,7 @@ var init_candidate_helpers = __esm(() => {
27400
27404
  });
27401
27405
 
27402
27406
  // ../cli/dist/operations/bootstrap/requirement-claims.js
27407
+ import { createHash as createHash15 } from "node:crypto";
27403
27408
  function normalizeClaimStatement(statement) {
27404
27409
  return statement.replace(/^\s*(?:[-*+]|\d+[.)])\s+/, "").replace(/^\s*\[(?: |x)\]\s*/i, "").trim();
27405
27410
  }
@@ -27449,6 +27454,46 @@ function claimFor(statement, source, confidence, provenance) {
27449
27454
  function snakeSegment(value) {
27450
27455
  return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
27451
27456
  }
27457
+ function fitsAspect(words) {
27458
+ return words.length > 0 && words.length <= MAX_ASPECT_WORDS && words.join("_").length <= MAX_ASPECT_CHARS;
27459
+ }
27460
+ function trimStopWords(words) {
27461
+ let start = 0;
27462
+ let end = words.length;
27463
+ while (start < end && ASPECT_STOP_WORDS.has(words[start] ?? ""))
27464
+ start += 1;
27465
+ while (end > start && ASPECT_STOP_WORDS.has(words[end - 1] ?? ""))
27466
+ end -= 1;
27467
+ return words.slice(start, end);
27468
+ }
27469
+ function shortenSubjectAspect(aspect) {
27470
+ const words = aspect.split("_").filter(Boolean);
27471
+ if (fitsAspect(words))
27472
+ return aspect;
27473
+ const boundary = words.findIndex((word, index) => index > 0 && ASPECT_BOUNDARY_WORDS.has(word) && trimStopWords(words.slice(0, index)).length > 0);
27474
+ const head = trimStopWords(boundary < 0 ? words : words.slice(0, boundary));
27475
+ if (fitsAspect(head))
27476
+ return head.join("_");
27477
+ const content = head.filter((word) => !ASPECT_STOP_WORDS.has(word) && !(word.length > 4 && word.endsWith("ly")));
27478
+ if (fitsAspect(content))
27479
+ return content.join("_");
27480
+ const picked = Array.from(new Set([content[0], ...content.slice(-(MAX_ASPECT_WORDS - 2))])).filter((word) => typeof word === "string");
27481
+ while (picked.length > 1 && picked.join("_").length > MAX_ASPECT_CHARS)
27482
+ picked.splice(1, 1);
27483
+ const short = picked.join("_");
27484
+ return short.length > 0 ? short.slice(0, MAX_ASPECT_CHARS).replace(/_+$/, "") : (words[0] ?? aspect).slice(0, MAX_ASPECT_CHARS);
27485
+ }
27486
+ function withShortAspect(component, aspect) {
27487
+ const full = `${component}.${aspect}`;
27488
+ const short = shortenSubjectAspect(aspect);
27489
+ const candidate = `${component}.${short}`;
27490
+ if (!isConventionalSubjectKey(candidate))
27491
+ return {
27492
+ ok: false,
27493
+ reason: `subject key "${candidate}" does not follow component.aspect[.sub]`
27494
+ };
27495
+ return short === aspect ? { ok: true, subjectKey: candidate } : { ok: true, subjectKey: candidate, shortenedFrom: full };
27496
+ }
27452
27497
  function resolveBootstrapSubjectKey(subject, propertyKey, component) {
27453
27498
  const stripped = subject.trim().replace(/^(?:the|a|an)\s+/i, "");
27454
27499
  let key;
@@ -27462,25 +27507,148 @@ function resolveBootstrapSubjectKey(subject, propertyKey, component) {
27462
27507
  const flat = key.replace(/\./g, "_");
27463
27508
  const comp = component === undefined ? "" : snakeSegment(component);
27464
27509
  const property = snakeSegment(propertyKey);
27465
- let candidate;
27466
27510
  if (comp) {
27467
27511
  const aspect = flat === comp ? property : flat.startsWith(`${comp}_`) ? flat.slice(comp.length + 1) : flat;
27468
- candidate = `${comp}.${aspect}`;
27469
- } else if (/^[a-z][a-z0-9]*$/.test(flat) && property) {
27470
- candidate = `${flat}.${property}`;
27471
- } else {
27472
- return {
27473
- ok: false,
27474
- reason: `subject key "${flat}" names no component, so it would violate component.aspect[.sub]`
27475
- };
27512
+ return withShortAspect(comp, aspect);
27476
27513
  }
27477
- return isConventionalSubjectKey(candidate) ? { ok: true, subjectKey: candidate } : {
27514
+ if (/^[a-z][a-z0-9]*$/.test(flat) && property)
27515
+ return withShortAspect(flat, property);
27516
+ return {
27478
27517
  ok: false,
27479
- reason: `subject key "${candidate}" does not follow component.aspect[.sub]`
27518
+ reason: `subject key "${flat}" names no component, so it would violate component.aspect[.sub]`
27519
+ };
27520
+ }
27521
+
27522
+ class SubjectKeyRegistry {
27523
+ owners = new Map;
27524
+ assigned = new Map;
27525
+ assign(resolution) {
27526
+ const identity = resolution.shortenedFrom ?? resolution.subjectKey;
27527
+ const known = this.assigned.get(identity);
27528
+ if (known !== undefined)
27529
+ return { subjectKey: known };
27530
+ const owner = this.owners.get(resolution.subjectKey);
27531
+ if (owner === undefined || owner === identity) {
27532
+ this.owners.set(resolution.subjectKey, identity);
27533
+ this.assigned.set(identity, resolution.subjectKey);
27534
+ return { subjectKey: resolution.subjectKey };
27535
+ }
27536
+ const used = new Set(resolution.subjectKey.split(/[._]/));
27537
+ const fullWords = identity.split(".").slice(1).join("_").split("_");
27538
+ const qualifier = fullWords.find((word) => /^[a-z][a-z0-9]*$/.test(word) && !used.has(word) && !ASPECT_STOP_WORDS.has(word) && !ASPECT_BOUNDARY_WORDS.has(word) && !this.owners.has(`${resolution.subjectKey}.${word}`));
27539
+ const subjectKey = `${resolution.subjectKey}.${qualifier ?? `k${digest(identity)}`}`;
27540
+ this.owners.set(subjectKey, identity);
27541
+ this.assigned.set(identity, subjectKey);
27542
+ return { subjectKey, collidedWith: resolution.subjectKey };
27543
+ }
27544
+ }
27545
+ function digest(value) {
27546
+ return createHash15("sha256").update(value).digest("hex").slice(0, 6);
27547
+ }
27548
+ function planSubjectKey(subject, propertyKey, component, registry, location) {
27549
+ const resolved = resolveBootstrapSubjectKey(subject, propertyKey, component);
27550
+ if (!resolved.ok)
27551
+ return { resolution: resolved, diagnostics: [] };
27552
+ const diagnostics = [];
27553
+ const assigned = registry?.assign(resolved) ?? {
27554
+ subjectKey: resolved.subjectKey
27555
+ };
27556
+ if (resolved.shortenedFrom)
27557
+ diagnostics.push(`subject-key-shortened: claim at ${location} would have the subject key ${resolved.shortenedFrom}; its aspect is longer than ${MAX_ASPECT_WORDS} words or ${MAX_ASPECT_CHARS} characters, so the plan uses ${assigned.subjectKey}. Rename the subject after apply if another noun phrase fits better.`);
27558
+ if (assigned.collidedWith)
27559
+ diagnostics.push(`subject-key-disambiguated: claim at ${location} names a different subject than an earlier claim keyed ${assigned.collidedWith}, so the plan uses ${assigned.subjectKey} instead of reusing that key.`);
27560
+ return {
27561
+ resolution: {
27562
+ ok: true,
27563
+ subjectKey: assigned.subjectKey,
27564
+ ...resolved.shortenedFrom ? { shortenedFrom: resolved.shortenedFrom } : {}
27565
+ },
27566
+ diagnostics
27480
27567
  };
27481
27568
  }
27569
+ var MAX_ASPECT_WORDS = 4, MAX_ASPECT_CHARS = 40, ASPECT_BOUNDARY_WORDS, ASPECT_STOP_WORDS;
27482
27570
  var init_requirement_claims = __esm(() => {
27483
27571
  init_strict_modeling();
27572
+ ASPECT_BOUNDARY_WORDS = new Set([
27573
+ "when",
27574
+ "whenever",
27575
+ "while",
27576
+ "if",
27577
+ "unless",
27578
+ "without",
27579
+ "after",
27580
+ "before",
27581
+ "until",
27582
+ "because",
27583
+ "once",
27584
+ "as",
27585
+ "by",
27586
+ "via",
27587
+ "during",
27588
+ "except",
27589
+ "so",
27590
+ "that",
27591
+ "which",
27592
+ "who",
27593
+ "where",
27594
+ "since",
27595
+ "for",
27596
+ "and",
27597
+ "or"
27598
+ ]);
27599
+ ASPECT_STOP_WORDS = new Set([
27600
+ "a",
27601
+ "an",
27602
+ "the",
27603
+ "be",
27604
+ "is",
27605
+ "are",
27606
+ "was",
27607
+ "were",
27608
+ "been",
27609
+ "being",
27610
+ "to",
27611
+ "of",
27612
+ "in",
27613
+ "on",
27614
+ "at",
27615
+ "with",
27616
+ "from",
27617
+ "into",
27618
+ "its",
27619
+ "it",
27620
+ "their",
27621
+ "they",
27622
+ "them",
27623
+ "this",
27624
+ "these",
27625
+ "those",
27626
+ "can",
27627
+ "could",
27628
+ "will",
27629
+ "would",
27630
+ "may",
27631
+ "might",
27632
+ "must",
27633
+ "shall",
27634
+ "should",
27635
+ "do",
27636
+ "does",
27637
+ "has",
27638
+ "have",
27639
+ "had",
27640
+ "any",
27641
+ "all",
27642
+ "each",
27643
+ "every",
27644
+ "some",
27645
+ "only",
27646
+ "also",
27647
+ "then",
27648
+ "than",
27649
+ "not",
27650
+ "no"
27651
+ ]);
27484
27652
  });
27485
27653
 
27486
27654
  // ../cli/dist/operations/bootstrap/markdown-candidates.js
@@ -27550,7 +27718,7 @@ function markdownComponentHint(relativePath) {
27550
27718
  }
27551
27719
  return;
27552
27720
  }
27553
- function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence) {
27721
+ function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics) {
27554
27722
  const relativePath = item.relativePath ?? item.label;
27555
27723
  const base = /\bshall\b/i.test(statement) ? 0.86 : /\bmust\b/i.test(statement) ? 0.84 : 0.78;
27556
27724
  const confidence = Math.min(0.95, base + (heading && /requirements?|constraints?|polic(?:y|ies)|rules?/i.test(heading) ? 0.08 : 0));
@@ -27559,7 +27727,8 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27559
27727
  const claim = claimFor(statement, relativePath, confidence, `${relativePath}#L${line}`);
27560
27728
  if (!claim)
27561
27729
  return null;
27562
- const subjectKey = resolveBootstrapSubjectKey(claim.subjectKey, claim.propertyKey, markdownComponentHint(relativePath));
27730
+ const planned = planSubjectKey(claim.subjectKey, claim.propertyKey, markdownComponentHint(relativePath), subjectKeys, `${relativePath}#L${line}`);
27731
+ const subjectKey = planned.resolution;
27563
27732
  if (!subjectKey.ok)
27564
27733
  throw new Error(`${subjectKey.reason}; author it with kb_model and a component.aspect subjectKey`);
27565
27734
  const writeSet = buildStrictWriteSet({
@@ -27568,6 +27737,7 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27568
27737
  });
27569
27738
  if (!writeSet.isStrict)
27570
27739
  return null;
27740
+ diagnostics.push(...planned.diagnostics);
27571
27741
  return {
27572
27742
  candidateId: `norm:${writeSet.req.id.toLowerCase()}`,
27573
27743
  entityType: "req",
@@ -27588,7 +27758,7 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27588
27758
  applyPlan: strictPlan(writeSet).map((step) => step.type === "req" ? withDocumentBody(step, provenanceRequirementBody(statement, bootstrapProvenance("generic markdown", `${relativePath} line ${line}${heading ? ` under the heading "${heading}"` : ""}`, confidence), `${relativePath}#L${line}`)) : step)
27589
27759
  };
27590
27760
  }
27591
- function markdownCandidates(item, existingIds, minConfidence) {
27761
+ function markdownCandidates(item, existingIds, minConfidence, subjectKeys) {
27592
27762
  const candidates = [];
27593
27763
  const sourceOnlySignals = [];
27594
27764
  const diagnostics = [];
@@ -27617,7 +27787,7 @@ function markdownCandidates(item, existingIds, minConfidence) {
27617
27787
  if (!statement || !/\b(must|shall|should)\b/i.test(statement))
27618
27788
  continue;
27619
27789
  try {
27620
- const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence);
27790
+ const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics);
27621
27791
  if (candidate) {
27622
27792
  if (!candidate.applyPlan.some((payload) => payload.type === "req" && existingIds.has(String(payload.id))))
27623
27793
  candidates.push(candidate);
@@ -27748,7 +27918,7 @@ function providerCandidate(item, existingIds, minConfidence) {
27748
27918
  }
27749
27919
  ];
27750
27920
  }
27751
- function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown) {
27921
+ function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown, subjectKeys) {
27752
27922
  const candidates = [];
27753
27923
  const sourceOnlySignals = [];
27754
27924
  const diagnostics = [];
@@ -27771,7 +27941,7 @@ function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeG
27771
27941
  }
27772
27942
  }
27773
27943
  if (item.kind === "generic_markdown" && includeGenericMarkdown) {
27774
- const built = markdownCandidates(item, existingIds, minConfidence);
27944
+ const built = markdownCandidates(item, existingIds, minConfidence, subjectKeys);
27775
27945
  candidates.push(...built.candidates);
27776
27946
  sourceOnlySignals.push(...built.sourceOnlySignals);
27777
27947
  diagnostics.push(...built.diagnostics);
@@ -28019,13 +28189,13 @@ var init_discovery_evidence = __esm(() => {
28019
28189
  });
28020
28190
 
28021
28191
  // ../cli/dist/operations/bootstrap/types.js
28022
- import { createHash as createHash15 } from "node:crypto";
28192
+ import { createHash as createHash16 } from "node:crypto";
28023
28193
  function bootstrapPlanHash(plan) {
28024
28194
  const { planHash: _ignored, ...body } = plan;
28025
- return createHash15("sha256").update(canonicalize(body)).digest("hex");
28195
+ return createHash16("sha256").update(canonicalize(body)).digest("hex");
28026
28196
  }
28027
28197
  function bootstrapEmptyKbSnapshotId(input) {
28028
- return `empty-source-state-${createHash15("sha256").update(canonicalize({
28198
+ return `empty-source-state-${createHash16("sha256").update(canonicalize({
28029
28199
  version: "kibi.empty-source-state.v1",
28030
28200
  branch: input.branch,
28031
28201
  workspaceSnapshot: input.workspaceSnapshot,
@@ -28162,7 +28332,7 @@ var init_discovery = __esm(() => {
28162
28332
  });
28163
28333
 
28164
28334
  // ../cli/dist/operations/bootstrap/intent-claims.js
28165
- import { createHash as createHash16 } from "node:crypto";
28335
+ import { createHash as createHash17 } from "node:crypto";
28166
28336
  function citation(source, claim) {
28167
28337
  return [
28168
28338
  `intent_claim:${source.id}:${claim.reference}`,
@@ -28171,8 +28341,8 @@ function citation(source, claim) {
28171
28341
  ...source.connector ? [`connector:${source.connector}`] : []
28172
28342
  ];
28173
28343
  }
28174
- function digest(...parts) {
28175
- return createHash16("sha256").update(parts.join("\x00")).digest("hex").slice(0, 16).toUpperCase();
28344
+ function digest2(...parts) {
28345
+ return createHash17("sha256").update(parts.join("\x00")).digest("hex").slice(0, 16).toUpperCase();
28176
28346
  }
28177
28347
  function claimBody(source, claim) {
28178
28348
  return renderRequirementBody({
@@ -28195,7 +28365,7 @@ function withRationale(step, rationale) {
28195
28365
  return { ...step, properties: { ...properties, rationale } };
28196
28366
  }
28197
28367
  function reviewClaimCandidate(source, claim, kind, confidence) {
28198
- const id = `FACT-BOOTSTRAP-${kind === "open_question" ? "QUESTION" : "OBSERVATION"}-${digest(source.id, claim.reference, claim.statement)}`;
28368
+ const id = `FACT-BOOTSTRAP-${kind === "open_question" ? "QUESTION" : "OBSERVATION"}-${digest2(source.id, claim.reference, claim.statement)}`;
28199
28369
  const entity = {
28200
28370
  type: "fact",
28201
28371
  id,
@@ -28240,7 +28410,7 @@ function conflictCandidates(declared, sources, existingIds, minConfidence) {
28240
28410
  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.`);
28241
28411
  continue;
28242
28412
  }
28243
- const id = `FACT-BOOTSTRAP-CONFLICT-${digest(...[...refs].sort(), conflict.note)}`;
28413
+ const id = `FACT-BOOTSTRAP-CONFLICT-${digest2(...[...refs].sort(), conflict.note)}`;
28244
28414
  const candidateId = `conflict:${id.toLowerCase()}`;
28245
28415
  if (confidence < minConfidence) {
28246
28416
  suppressed.push({
@@ -28315,7 +28485,7 @@ ${evidenceLines.join(`
28315
28485
  }
28316
28486
  return { candidates, sourceOnlySignals: [], suppressed, diagnostics };
28317
28487
  }
28318
- function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
28488
+ function buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys) {
28319
28489
  const sources = new Map((declared.knowledgeSources ?? []).map((source) => [source.id, source]));
28320
28490
  const candidates = [];
28321
28491
  const sourceOnlySignals = [];
@@ -28371,7 +28541,9 @@ function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
28371
28541
  let writeSet = null;
28372
28542
  try {
28373
28543
  const modeled = claimFor(claim.statement, `knowledge-source:${source.id}`, confidence, provenance);
28374
- const subjectKey = modeled ? resolveBootstrapSubjectKey(modeled.subjectKey, modeled.propertyKey, claim.component ?? source.component) : null;
28544
+ const planned = modeled ? planSubjectKey(modeled.subjectKey, modeled.propertyKey, claim.component ?? source.component, subjectKeys, `${source.id}:${claim.reference}`) : null;
28545
+ const subjectKey = planned?.resolution ?? null;
28546
+ diagnostics.push(...planned?.diagnostics ?? []);
28375
28547
  writeSet = modeled && subjectKey?.ok ? buildStrictWriteSet({
28376
28548
  claim: { ...modeled, subjectKey: subjectKey.subjectKey },
28377
28549
  statement: claim.statement
@@ -30208,7 +30380,6 @@ var init_analysis_receipt = __esm(() => {
30208
30380
  // ../cli/dist/operations/semantic-advisor/observation-plan.js
30209
30381
  function observationPlan(payload, title, tags) {
30210
30382
  const factId = `FACT-OBS-${shortHash(`${stringValue2(payload.id)}.${title}.${statementOf(payload)}`)}`;
30211
- const target = tags.includes("review:ambiguity") ? "review:ambiguous-claim" : tags.includes("review:keyword-false-positive") ? "review:keyword-false-positive" : tags.includes("review:ontology-gap") ? "review:ontology-gap" : tags.includes("review:nonlogical") ? "review:nonlogical" : undefined;
30212
30383
  return [
30213
30384
  {
30214
30385
  type: "fact",
@@ -30221,7 +30392,13 @@ function observationPlan(payload, title, tags) {
30221
30392
  text_ref: statementOf(payload),
30222
30393
  tags
30223
30394
  },
30224
- relationships: target === undefined ? [] : [relationship(factId, target, "relates_to")]
30395
+ document: {
30396
+ body: `${title}. The semantic advisor suggested this review observation because the prose below is not safely grounded by a typed fact; it stays open until a reviewer models, rewords or dismisses the claim.
30397
+
30398
+ > ${statementOf(payload).trim().replace(/\n/g, `
30399
+ > `)}`
30400
+ },
30401
+ relationships: []
30225
30402
  }
30226
30403
  ];
30227
30404
  }
@@ -31006,7 +31183,7 @@ var init_validation2 = __esm(() => {
31006
31183
  });
31007
31184
 
31008
31185
  // ../cli/dist/operations/bootstrap/generate.js
31009
- import { createHash as createHash17 } from "node:crypto";
31186
+ import { createHash as createHash18 } from "node:crypto";
31010
31187
  import path31 from "node:path";
31011
31188
  function filterSourceOnlySignals(signals, entityTypes) {
31012
31189
  return entityTypes && entityTypes.length > 0 ? signals.filter((signal) => entityTypes.includes(signal.kind)) : signals;
@@ -31025,7 +31202,7 @@ function firstUpsertId(candidate) {
31025
31202
  return properties !== null && typeof properties === "object" && "id" in properties && typeof properties.id === "string" ? properties.id : "";
31026
31203
  }
31027
31204
  function sha2562(value) {
31028
- return createHash17("sha256").update(value).digest("hex");
31205
+ return createHash18("sha256").update(value).digest("hex");
31029
31206
  }
31030
31207
  function selectBootstrapCandidates(input, existingIds, entityTypes, maximum) {
31031
31208
  const allowed = entityTypes?.length ? new Set(entityTypes) : null;
@@ -31240,13 +31417,14 @@ async function executePlanBootstrap(args, context) {
31240
31417
  })
31241
31418
  ]);
31242
31419
  const bound = await expectedSnapshots(context, discovery.evidence.map((evidence) => evidence.relativePath).filter((value) => Boolean(value)));
31243
- const built = discovery.activation.allowCandidateGeneration ? buildBootstrapCandidates(discovery.evidence, new Set, minConfidence, includeGenericMarkdown) : {
31420
+ const subjectKeys = new SubjectKeyRegistry;
31421
+ const built = discovery.activation.allowCandidateGeneration ? buildBootstrapCandidates(discovery.evidence, new Set, minConfidence, includeGenericMarkdown, subjectKeys) : {
31244
31422
  candidates: [],
31245
31423
  sourceOnlySignals: [],
31246
31424
  suppressed: [],
31247
31425
  diagnostics: []
31248
31426
  };
31249
- const claimed = discovery.activation.allowCandidateGeneration ? buildIntentClaimCandidates(declared, existingIds, minConfidence) : {
31427
+ const claimed = discovery.activation.allowCandidateGeneration ? buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys) : {
31250
31428
  candidates: [],
31251
31429
  sourceOnlySignals: [],
31252
31430
  suppressed: [],
@@ -31298,6 +31476,7 @@ var init_generate = __esm(() => {
31298
31476
  init_discovery();
31299
31477
  init_intent_claims();
31300
31478
  init_presentation();
31479
+ init_requirement_claims();
31301
31480
  init_validation2();
31302
31481
  init_types();
31303
31482
  });
@@ -31570,7 +31749,7 @@ var init_aggregated_checks = __esm(() => {
31570
31749
  });
31571
31750
 
31572
31751
  // ../cli/dist/traceability/impact-review.js
31573
- import { createHash as createHash18 } from "node:crypto";
31752
+ import { createHash as createHash19 } from "node:crypto";
31574
31753
  function canonicalJson2(value) {
31575
31754
  const active = new Set;
31576
31755
  const encode = (item) => {
@@ -31604,10 +31783,10 @@ function canonicalJson2(value) {
31604
31783
  return encode(value);
31605
31784
  }
31606
31785
  function fingerprint(value) {
31607
- return `sha256:${createHash18("sha256").update(canonicalJson2(value), "utf8").digest("hex")}`;
31786
+ return `sha256:${createHash19("sha256").update(canonicalJson2(value), "utf8").digest("hex")}`;
31608
31787
  }
31609
31788
  function fingerprintBytes(value) {
31610
- return `sha256:${createHash18("sha256").update(value).digest("hex")}`;
31789
+ return `sha256:${createHash19("sha256").update(value).digest("hex")}`;
31611
31790
  }
31612
31791
  function isObject(value) {
31613
31792
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -32237,7 +32416,7 @@ var init_source_classification = __esm(() => {
32237
32416
  });
32238
32417
 
32239
32418
  // ../cli/dist/plugins/source-analysis-service.js
32240
- import { createHash as createHash19 } from "node:crypto";
32419
+ import { createHash as createHash20 } from "node:crypto";
32241
32420
  import * as path33 from "node:path";
32242
32421
  function detectSourceLanguage(filePath) {
32243
32422
  return extensionLanguage(filePath);
@@ -32245,7 +32424,7 @@ function detectSourceLanguage(filePath) {
32245
32424
  function createV2Request(filePath, content, explicitLanguageHint) {
32246
32425
  const classification = classifySource(filePath, content, explicitLanguageHint);
32247
32426
  const language = classification.language;
32248
- const inputFingerprint = createHash19("sha256").update(filePath).update("\x00").update(content).update("\x00").update(classification.kind).update("\x00").update(classification.language).update("\x00").update(explicitLanguageHint?.trim().toLowerCase() ?? "").digest("hex");
32427
+ const inputFingerprint = createHash20("sha256").update(filePath).update("\x00").update(content).update("\x00").update(classification.kind).update("\x00").update(classification.language).update("\x00").update(explicitLanguageHint?.trim().toLowerCase() ?? "").digest("hex");
32249
32428
  return {
32250
32429
  classification,
32251
32430
  input: {
@@ -32407,7 +32586,7 @@ class SourceAnalysisService {
32407
32586
  providerId: entry.capability.id,
32408
32587
  stamp: entry.stamp,
32409
32588
  inputFingerprint: request.inputFingerprint,
32410
- providerFingerprint: this.options.providerFingerprints?.[entry.pluginId] ?? createHash19("sha256").update(`${entry.pluginId}\x00${entry.pluginVersion}\x00${entry.stamp.capability}`).digest("hex"),
32589
+ providerFingerprint: this.options.providerFingerprints?.[entry.pluginId] ?? createHash20("sha256").update(`${entry.pluginId}\x00${entry.pluginVersion}\x00${entry.stamp.capability}`).digest("hex"),
32411
32590
  shadowComparisons: []
32412
32591
  };
32413
32592
  } catch (error) {
@@ -32527,7 +32706,7 @@ var init_source_analysis_service = __esm(() => {
32527
32706
  });
32528
32707
 
32529
32708
  // ../cli/dist/plugins/maintenance-source-analysis.js
32530
- import { createHash as createHash20 } from "node:crypto";
32709
+ import { createHash as createHash21 } from "node:crypto";
32531
32710
  import { readFileSync as readFileSync16, realpathSync as realpathSync5 } from "node:fs";
32532
32711
  import { createRequire as createRequire4 } from "node:module";
32533
32712
  import { dirname as dirname7, isAbsolute as isAbsolute6, join as join13, relative as relative5, resolve as resolve7 } from "node:path";
@@ -32540,7 +32719,7 @@ function digestFile(root, file) {
32540
32719
  const actualRel = relative5(realpathSync5(root), actual);
32541
32720
  if (actualRel.startsWith("..") || isAbsolute6(actualRel))
32542
32721
  throw new Error(`Approved asset escapes its package: ${file}`);
32543
- return createHash20("sha256").update(readFileSync16(actual)).digest("hex");
32722
+ return createHash21("sha256").update(readFileSync16(actual)).digest("hex");
32544
32723
  }
32545
32724
  function verifyFiles(root, files) {
32546
32725
  if (Object.keys(files).length === 0)
@@ -32609,7 +32788,7 @@ function verifyApprovedSourceAnalyzer(root, approval) {
32609
32788
  if (metadata.version !== dependency.version)
32610
32789
  throw new Error(`Unapproved source runtime version: ${dependency.packageName}`);
32611
32790
  }
32612
- return createHash20("sha256").update(JSON.stringify(approval)).digest("hex");
32791
+ return createHash21("sha256").update(JSON.stringify(approval)).digest("hex");
32613
32792
  }
32614
32793
  function createMaintenanceSourceAnalysisService(workspaceRoot, projectConfig) {
32615
32794
  const config = projectConfig ?? readProjectKibiConfig(workspaceRoot);
@@ -40581,7 +40760,7 @@ async function enrichSymbolCoordinatesWithTsMorph(entries, workspaceRoot) {
40581
40760
  var init_symbols_ts = () => {};
40582
40761
 
40583
40762
  // ../cli/dist/traceability/symbol-extract.js
40584
- import { createHash as createHash22 } from "node:crypto";
40763
+ import { createHash as createHash23 } from "node:crypto";
40585
40764
  function createManifestLookupSentinelKey(manifestPath) {
40586
40765
  return `${MANIFEST_SENTINEL_PREFIX}${manifestPath}`;
40587
40766
  }
@@ -40593,7 +40772,7 @@ function getCandidateManifestPaths(filePath) {
40593
40772
  return LOCAL_MANIFEST_NAMES.map((manifestName) => `${dir}/${manifestName}`);
40594
40773
  }
40595
40774
  function createHashFallbackId(filePath, name) {
40596
- const h = createHash22("sha256");
40775
+ const h = createHash23("sha256");
40597
40776
  h.update(`${filePath}:${name}`);
40598
40777
  return h.digest("hex").slice(0, 16);
40599
40778
  }
@@ -40681,7 +40860,7 @@ function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifes
40681
40860
  };
40682
40861
  }
40683
40862
  function computeContentSha(content) {
40684
- const h = createHash22("sha256");
40863
+ const h = createHash23("sha256");
40685
40864
  h.update(content);
40686
40865
  return h.digest("hex");
40687
40866
  }
@@ -51991,7 +52170,7 @@ var require_out4 = __commonJS(function(exports2, module2) {
51991
52170
 
51992
52171
  // ../cli/dist/commands/sync/discovery.js
51993
52172
  import { execFile } from "node:child_process";
51994
- import { createHash as createHash23 } from "node:crypto";
52173
+ import { createHash as createHash24 } from "node:crypto";
51995
52174
  import * as fs5 from "node:fs";
51996
52175
  import { existsSync as existsSync23, readFileSync as readFileSync24, readdirSync as readdirSync6, unlinkSync as unlinkSync4 } from "node:fs";
51997
52176
  import * as path47 from "node:path";
@@ -52026,13 +52205,13 @@ function pendingSourcePaths(cwd, recoverMissing) {
52026
52205
  receiptPath,
52027
52206
  path: relative,
52028
52207
  afterHash: receipt.afterHash,
52029
- rawHash: createHash23("sha256").update(raw).digest("hex")
52208
+ rawHash: createHash24("sha256").update(raw).digest("hex")
52030
52209
  });
52031
52210
  continue;
52032
52211
  }
52033
52212
  throw new Error(`Pending source is missing: ${relative}. If the file was deleted on purpose, run 'kibi branch recover --apply' to rebuild the branch KB from the sources that remain.`);
52034
52213
  }
52035
- const actual = createHash23("sha256").update(readFileSync24(absolute)).digest("hex");
52214
+ const actual = createHash24("sha256").update(readFileSync24(absolute)).digest("hex");
52036
52215
  if (actual !== receipt.afterHash) {
52037
52216
  throw new Error(`Pending source hash drift blocks sync for ${relative}; expected ${receipt.afterHash}, found ${actual}`);
52038
52217
  }
@@ -52080,7 +52259,7 @@ function clearRecoveredPendingSourceReceipts(cwd, receipts) {
52080
52259
  const raw = readTextOrEnoent(absolute);
52081
52260
  if (raw === null)
52082
52261
  continue;
52083
- const rawHash = createHash23("sha256").update(raw).digest("hex");
52262
+ const rawHash = createHash24("sha256").update(raw).digest("hex");
52084
52263
  if (rawHash !== receipt.rawHash) {
52085
52264
  throw new Error(`Pending source receipt changed during recovery for ${receipt.path}; refusing to retire newer receipt`);
52086
52265
  }
@@ -53868,7 +54047,7 @@ function scoreExactPredicates3(schema, text) {
53868
54047
  }
53869
54048
 
53870
54049
  // ../cli/dist/operations/modeling/predicate-utils.js
53871
- import { createHash as createHash24 } from "node:crypto";
54050
+ import { createHash as createHash25 } from "node:crypto";
53872
54051
  function normalizeText2(text) {
53873
54052
  const normalized = String(text ?? "").trim();
53874
54053
  if (!normalized) {
@@ -53892,7 +54071,7 @@ function slug2(value) {
53892
54071
  return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
53893
54072
  }
53894
54073
  function hashId(prefix, parts) {
53895
- const digest = createHash24("sha256").update(parts.join("\x00")).digest("hex").slice(0, 12).toUpperCase();
54074
+ const digest = createHash25("sha256").update(parts.join("\x00")).digest("hex").slice(0, 12).toUpperCase();
53896
54075
  return `${prefix}-${digest}`;
53897
54076
  }
53898
54077
  function inferSubject(text, subjectHint) {
@@ -54556,11 +54735,43 @@ function isGenericPlaceholder(value) {
54556
54735
  const normalized = value.trim().toLowerCase();
54557
54736
  return PLACEHOLDER_VALUES.has(normalized) || normalized === "unknown" || normalized.startsWith("requirement.") || normalized.endsWith("_policy") && ["executable", "cwd", "environment", "stdio", "termination"].some((kind) => normalized.startsWith(kind));
54558
54737
  }
54559
- function classifyBinding(value, text, explicit, canonical = false) {
54738
+ function bindingToken(value) {
54739
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
54740
+ }
54741
+ function claimNames(text, token) {
54742
+ if (!text || !token)
54743
+ return false;
54744
+ const words = token.split("_").filter(Boolean);
54745
+ if (words.length === 0)
54746
+ return false;
54747
+ return new RegExp(`\\b${words.join("[\\s_-]+")}\\b`, "i").test(text);
54748
+ }
54749
+ function matchesConstant(value, context) {
54750
+ const token = bindingToken(value);
54751
+ return context.constants?.some((constant) => bindingToken(constant) === token) ?? false;
54752
+ }
54753
+ function nameOrStopWordReason(value, context = {}) {
54754
+ const token = bindingToken(value);
54755
+ if (token === "true" || token === "false")
54756
+ return null;
54757
+ if (matchesConstant(value, context))
54758
+ return null;
54759
+ if (STOP_WORD_BINDINGS.has(token))
54760
+ return `"${token}" is a stop word`;
54761
+ const own = context.argumentName ? bindingToken(context.argumentName) : "";
54762
+ const repeatsName = own !== "" && token === own || (context.argumentNames ?? []).some((name) => bindingToken(name) === token);
54763
+ if (repeatsName && !claimNames(context.text, token))
54764
+ return `it repeats the argument name ${token} and the claim does not name it`;
54765
+ return null;
54766
+ }
54767
+ function classifyBinding(value, text, explicit, canonical = false, context = {}) {
54560
54768
  const normalized = value.trim();
54561
- if (canonical && !isGenericPlaceholder(normalized))
54769
+ if (nameOrStopWordReason(normalized, { ...context, text }) !== null)
54770
+ return "placeholder";
54771
+ const constant = matchesConstant(normalized, context);
54772
+ if (canonical && (constant || !isGenericPlaceholder(normalized)))
54562
54773
  return "extracted";
54563
- if (explicit && (!isGenericPlaceholder(normalized) || ["true", "false"].includes(normalized.toLowerCase())))
54774
+ if (explicit && (constant || !isGenericPlaceholder(normalized) || ["true", "false"].includes(normalized.toLowerCase())))
54564
54775
  return "explicit";
54565
54776
  if (!normalized || isGenericPlaceholder(normalized))
54566
54777
  return "placeholder";
@@ -54574,7 +54785,7 @@ function aggregateBindingProvenance(values) {
54574
54785
  function bindingCanBeApplied(value) {
54575
54786
  return value === "explicit" || value === "extracted";
54576
54787
  }
54577
- var PLACEHOLDER_VALUES, DERIVED_CUE_ALIASES, PROVENANCE_ORDER;
54788
+ var PLACEHOLDER_VALUES, DERIVED_CUE_ALIASES, STOP_WORD_BINDINGS, PROVENANCE_ORDER;
54578
54789
  var init_predicate_bindings = __esm(() => {
54579
54790
  PLACEHOLDER_VALUES = new Set([
54580
54791
  "unknown",
@@ -54646,6 +54857,62 @@ var init_predicate_bindings = __esm(() => {
54646
54857
  actionable_error: ["actionable", "clear error", "report", "error"],
54647
54858
  exception: ["exception", "except", "package-manager", "package manager"]
54648
54859
  };
54860
+ STOP_WORD_BINDINGS = new Set([
54861
+ "a",
54862
+ "an",
54863
+ "the",
54864
+ "be",
54865
+ "is",
54866
+ "are",
54867
+ "was",
54868
+ "were",
54869
+ "been",
54870
+ "being",
54871
+ "am",
54872
+ "do",
54873
+ "does",
54874
+ "did",
54875
+ "done",
54876
+ "have",
54877
+ "has",
54878
+ "had",
54879
+ "it",
54880
+ "its",
54881
+ "this",
54882
+ "that",
54883
+ "these",
54884
+ "those",
54885
+ "they",
54886
+ "them",
54887
+ "there",
54888
+ "thing",
54889
+ "something",
54890
+ "anything",
54891
+ "some",
54892
+ "any",
54893
+ "of",
54894
+ "to",
54895
+ "in",
54896
+ "at",
54897
+ "by",
54898
+ "for",
54899
+ "with",
54900
+ "and",
54901
+ "or",
54902
+ "not",
54903
+ "null",
54904
+ "nil",
54905
+ "n_a",
54906
+ "na",
54907
+ "tbd",
54908
+ "todo",
54909
+ "x",
54910
+ "xxx",
54911
+ "foo",
54912
+ "bar",
54913
+ "example",
54914
+ "placeholder"
54915
+ ]);
54649
54916
  PROVENANCE_ORDER = [
54650
54917
  "explicit",
54651
54918
  "extracted",
@@ -55362,7 +55629,11 @@ function buildSuggestion(schema, text, subject, score, argumentBindings = {}, po
55362
55629
  const undeclaredArguments = new Set(conformance.undeclared.map((value) => value.argumentName));
55363
55630
  const bindingProvenanceByArgument = Object.fromEntries(schema.argument_names.map((name, index) => [
55364
55631
  name,
55365
- classifyBinding(predicateArgs[index] ?? "unknown", text, typeof argumentBindings[name] === "string" && argumentBindings[name].trim().length > 0 || diagnostics?.explicitSubject === true && index === 0, canonicalLauncherArgs?.[index] === predicateArgs[index] && !(index === 0 && predicateArgs[index] === "launcher" && diagnostics?.explicitSubject !== true && typeof argumentBindings[name] !== "string"))
55632
+ classifyBinding(predicateArgs[index] ?? "unknown", text, typeof argumentBindings[name] === "string" && argumentBindings[name].trim().length > 0 || diagnostics?.explicitSubject === true && index === 0, canonicalLauncherArgs?.[index] === predicateArgs[index] && !(index === 0 && predicateArgs[index] === "launcher" && diagnostics?.explicitSubject !== true && typeof argumentBindings[name] !== "string"), index === 0 && diagnostics?.explicitSubject === true && typeof argumentBindings[name] !== "string" ? { constants: schema.argument_constants?.[name] } : {
55633
+ argumentName: name,
55634
+ argumentNames: schema.argument_names,
55635
+ constants: schema.argument_constants?.[name]
55636
+ })
55366
55637
  ]));
55367
55638
  const bindingProvenance = aggregateBindingProvenance(Object.values(bindingProvenanceByArgument));
55368
55639
  const unboundArguments = schema.argument_names.filter((name) => undeclaredArguments.has(name) || !bindingCanBeApplied(bindingProvenanceByArgument[name] ?? "placeholder"));
@@ -55452,8 +55723,21 @@ function buildRelationshipPlan(factId, requirementId, claimText, existingLogicCl
55452
55723
  instructions: `Apply the predicate fact ${factId} first, update the requirement with the returned merged logicClaims manifest, then attach this relationship without overwriting other requirement metadata. The relationship target is the planned fact id ${factId}, never a candidates[].id (SUGGEST-...).`
55453
55724
  };
55454
55725
  }
55726
+ function gapObservationBody(text, args) {
55727
+ const origin = [
55728
+ args.requirementId ? `requirement ${args.requirementId}` : null,
55729
+ args.source ? `source ${args.source}` : null
55730
+ ].filter((value) => value !== null);
55731
+ return [
55732
+ "No available predicate schema fits this claim, so kb_model mode predicates recorded it as an open ontology gap. The observation does not ground the claim; it stays visible for review until a reviewed predicate_schema covers this kind of statement.",
55733
+ ...origin.length > 0 ? [`Claim from ${origin.join(", ")}:`] : ["Claim:"],
55734
+ `> ${text.trim().replace(/\n/g, `
55735
+ > `)}`
55736
+ ].join(`
55737
+
55738
+ `);
55739
+ }
55455
55740
  function buildGapApplyPlan(text, args) {
55456
- const claimKey = semanticClaimKey(text);
55457
55741
  const factId = hashId("FACT-ONTOLOGY-GAP", [
55458
55742
  args.requirementId ?? "",
55459
55743
  args.source ?? "",
@@ -55467,20 +55751,14 @@ function buildGapApplyPlan(text, args) {
55467
55751
  title: "Ontology gap: predicate schema needed",
55468
55752
  status: "active",
55469
55753
  source: args.source ?? "mcp://kibi/suggest-predicates",
55470
- text_ref: args.source,
55754
+ ...args.source ? { text_ref: args.source } : {},
55471
55755
  tags: ["review:ontology-gap", "needs_schema_extension"],
55472
55756
  fact_kind: "observation",
55473
55757
  value_string: text,
55474
- claim_key: claimKey,
55475
55758
  claim_text: text
55476
55759
  },
55477
- relationships: [
55478
- {
55479
- type: "relates_to",
55480
- from: factId,
55481
- to: "review:ontology-gap"
55482
- }
55483
- ]
55760
+ document: { body: gapObservationBody(text, args) },
55761
+ relationships: []
55484
55762
  }
55485
55763
  ];
55486
55764
  }
@@ -55543,6 +55821,84 @@ var init_predicate_applyplan = __esm(() => {
55543
55821
  init_predicate_utils();
55544
55822
  });
55545
55823
 
55824
+ // ../cli/dist/operations/modeling/predicate-binding-hints.js
55825
+ function exampleArguments(example) {
55826
+ const open = example.indexOf("(");
55827
+ const close = example.lastIndexOf(")");
55828
+ if (open < 0 || close <= open)
55829
+ return [];
55830
+ const inner = example.slice(open + 1, close);
55831
+ const args = [];
55832
+ let depth = 0;
55833
+ let current = "";
55834
+ for (const char of inner) {
55835
+ if (char === "(" || char === "[")
55836
+ depth += 1;
55837
+ if (char === ")" || char === "]")
55838
+ depth -= 1;
55839
+ if (char === "," && depth === 0) {
55840
+ args.push(current.trim());
55841
+ current = "";
55842
+ continue;
55843
+ }
55844
+ current += char;
55845
+ }
55846
+ args.push(current.trim());
55847
+ return args.filter(Boolean);
55848
+ }
55849
+ function unboundReason(candidate, name, value, text) {
55850
+ const schema = candidate.schema;
55851
+ const provenance = candidate.binding_provenance_by_argument[name];
55852
+ const nameReason = nameOrStopWordReason(value, {
55853
+ argumentName: name,
55854
+ argumentNames: schema.argument_names,
55855
+ constants: schema.argument_constants?.[name],
55856
+ text
55857
+ });
55858
+ if (nameReason !== null)
55859
+ return `The value "${value}" is not a binding: ${nameReason}.`;
55860
+ if (!value.trim() || isGenericPlaceholder(value))
55861
+ return "The claim text names no value for this argument.";
55862
+ if (provenance === "inferred")
55863
+ return `The value "${value}" was inferred and does not appear in the claim text; confirm it or pass the exact value.`;
55864
+ if (schema.argument_constants?.[name])
55865
+ return `The value "${value}" is not one of this argument's declared constants.`;
55866
+ return `The value "${value}" needs review.`;
55867
+ }
55868
+ function buildBindingHints(candidate, text) {
55869
+ const schema = candidate.schema;
55870
+ const parsedExamples = schema.examples.map(exampleArguments);
55871
+ return candidate.unbound_arguments.map((name) => {
55872
+ const position = schema.argument_names.indexOf(name);
55873
+ const constants = schema.argument_constants?.[name];
55874
+ const exampleValues = parsedExamples.map((args) => args[position]).filter((value) => typeof value === "string");
55875
+ const examples = Array.from(new Set([...constants ?? [], ...exampleValues])).slice(0, MAX_EXAMPLES);
55876
+ const current = candidate.predicate_args[position] ?? "";
55877
+ const description = schema.argument_descriptions?.[position];
55878
+ return {
55879
+ argument: name,
55880
+ position,
55881
+ type: schema.argument_types[position] ?? "unknown",
55882
+ ...description ? { description } : {},
55883
+ ...constants ? { allowedValues: [...constants] } : {},
55884
+ examples,
55885
+ currentValue: current,
55886
+ provenance: candidate.binding_provenance_by_argument[name] ?? "placeholder",
55887
+ reason: unboundReason(candidate, name, current, text)
55888
+ };
55889
+ });
55890
+ }
55891
+ function describeBindingHints(hints) {
55892
+ return hints.map((hint) => {
55893
+ const values = hint.allowedValues ? `one of ${hint.allowedValues.join(", ")}` : hint.examples.length > 0 ? `e.g. ${hint.examples.join(", ")}` : "no example in the schema";
55894
+ return `${hint.argument} (${hint.type}, ${values})`;
55895
+ }).join("; ");
55896
+ }
55897
+ var MAX_EXAMPLES = 5;
55898
+ var init_predicate_binding_hints = __esm(() => {
55899
+ init_predicate_bindings();
55900
+ });
55901
+
55546
55902
  // ../cli/dist/operations/modeling/suggest-predicates.js
55547
55903
  function analyzeInputPropositions(text) {
55548
55904
  return analyzeSemanticAdvisorInput({
@@ -55769,15 +56125,16 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55769
56125
  const applyPlan = completeCandidate ? alreadyGrounded ? [] : predicatePlan : !recommendedCandidate && !unavailableSchema ? buildGapApplyPlan(text, args) : [];
55770
56126
  const plannedFactId = typeof predicatePlan[0]?.id === "string" ? predicatePlan[0].id : null;
55771
56127
  const relationshipPlan = completeCandidate && !alreadyGrounded ? buildRelationshipPlan(plannedFactId ?? "", args.requirementId, text, args.existingLogicClaims) : null;
55772
- const replacementPlan = alreadyGrounded && plannedFactId !== null && args.requirementId ? buildGroundingReplacementPlan(args.requirementId, plannedFactId, predicatePlan, existingGrounding) : null;
56128
+ const replacementPlan = alreadyGrounded && plannedFactId !== null && args.requirementId ? buildGroundingReplacementPlan(args.requirementId, plannedFactId, predicatePlan, existingGrounding, await storedRequirementProperties(prolog, args.requirementId, warnings)) : null;
55773
56129
  const relationshipTarget = relationshipPlan !== null || replacementPlan !== null ? plannedFactId : null;
55774
56130
  const groundingSummary = existingGrounding.map((row) => `${row.relationship.type} -> ${row.factId}`).join(", ");
55775
56131
  if (alreadyGrounded) {
55776
56132
  warnings.push(`${args.requirementId} already grounds this claim (${claimKey}) through ${groundingSummary}. A modeled claim takes exactly one logical grounding relationship, so adding requires_predicate beside it fails the proposition-complete rule. ${replacementPlan ? "Keep the existing grounding, or follow replacementPlan to swap it for the predicate." : "Keep the existing grounding; no predicate replacement is available yet."}`);
55777
56133
  }
56134
+ const bindingHints = recommendedAction === "provide_argument_bindings" && recommendedCandidate ? buildBindingHints(recommendedCandidate, text) : [];
55778
56135
  const recommendedPredicateSchema = !recommendedCandidate && !unavailableSchema ? buildPredicateSchemaDraft(text, subject) : null;
55779
56136
  const groundedPrefix = alreadyGrounded ? `${args.requirementId} already grounds this claim through ${groundingSummary}. ` : "";
55780
- const textSummary = groundedPrefix + (replacementPlan ? `To use ${completeCandidate?.predicate_name} instead, follow replacementPlan and link requires_predicate to ${plannedFactId}; otherwise keep the existing grounding.` : completeCandidate ? `Suggested ${candidates.length} predicate candidate(s). Top applicable match: ${completeCandidate.predicate_name}. Apply the predicate fact ${plannedFactId}, then link requires_predicate to that fact id (not a candidate id).` : recommendedCandidate ? `Matched ${recommendedCandidate.predicate_name}, but exact reviewed values are still required for: ${recommendedCandidate.unbound_arguments.join(", ")}. No apply plan was generated.` : unavailableSchema ? `Requested predicate schema ${args.schemaId} is unavailable or semantically inapplicable. No apply plan was generated.` : alreadyGrounded ? "No predicate candidate passed the semantic applicability gate; keep the existing grounding and record the ontology-gap observation in applyPlan so the missing schema stays visible." : "No predicate candidate passed the semantic applicability gate; record an ontology gap and review the generated schema draft instead of silently writing prose.");
56137
+ const textSummary = groundedPrefix + (replacementPlan ? `To use ${completeCandidate?.predicate_name} instead, follow replacementPlan and link requires_predicate to ${plannedFactId}; otherwise keep the existing grounding.` : completeCandidate ? `Suggested ${candidates.length} predicate candidate(s). Top applicable match: ${completeCandidate.predicate_name}. Apply the predicate fact ${plannedFactId}, then link requires_predicate to that fact id (not a candidate id).` : recommendedCandidate ? `Matched ${recommendedCandidate.predicate_name}, but exact reviewed values are still required for: ${recommendedCandidate.unbound_arguments.join(", ")}. Bind them from the claim text: ${describeBindingHints(bindingHints)}. A value that repeats an argument name or a stop word stays unbound. No apply plan was generated.` : unavailableSchema ? `Requested predicate schema ${args.schemaId} is unavailable or semantically inapplicable. No apply plan was generated.` : alreadyGrounded ? "No predicate candidate passed the semantic applicability gate; keep the existing grounding and record the ontology-gap observation in applyPlan so the missing schema stays visible." : "No predicate candidate passed the semantic applicability gate; record an ontology gap and review the generated schema draft instead of silently writing prose.");
55781
56138
  const logicClaims = Array.from(new Set([...args.existingLogicClaims ?? [], claimKey]));
55782
56139
  return {
55783
56140
  content: [{ type: "text", text: textSummary }],
@@ -55796,12 +56153,40 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55796
56153
  relationshipTarget,
55797
56154
  existingGrounding,
55798
56155
  replacementPlan,
56156
+ bindingHints,
55799
56157
  warnings
55800
56158
  },
55801
56159
  applyPlan
55802
56160
  };
55803
56161
  }
55804
- function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing) {
56162
+ async function storedRequirementProperties(prolog, requirementId, warnings) {
56163
+ if (prolog === null)
56164
+ return null;
56165
+ try {
56166
+ const [stored] = await loadEntities(prolog, {
56167
+ id: requirementId,
56168
+ type: "req"
56169
+ });
56170
+ if (stored === undefined)
56171
+ return null;
56172
+ const restated = {};
56173
+ for (const field of RESTATED_REQUIREMENT_FIELDS)
56174
+ if (stored[field] !== undefined && stored[field] !== null)
56175
+ restated[field] = stored[field];
56176
+ return typeof restated.title === "string" && typeof restated.status === "string" ? restated : null;
56177
+ } catch (error) {
56178
+ warnings.push(`Requirement ${requirementId} could not be read (${error instanceof Error ? error.message : String(error)}); add its stored title and status to the last replacementPlan step before applying it.`);
56179
+ return null;
56180
+ }
56181
+ }
56182
+ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing, stored) {
56183
+ const requirementUpsert = (relationships) => ({
56184
+ type: "req",
56185
+ id: requirementId,
56186
+ ...stored === null ? {} : { properties: stored },
56187
+ relationships
56188
+ });
56189
+ const restate = stored === null ? `add the requirement's stored title and status as properties (they could not be read), keep this relationship only` : "the properties restate the stored title, status and metadata; kb_upsert keeps the stored proposition ledger";
55805
56190
  return {
55806
56191
  relationshipTarget: factId,
55807
56192
  steps: [
@@ -55819,30 +56204,33 @@ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, exi
55819
56204
  },
55820
56205
  {
55821
56206
  operation: "kb_upsert",
55822
- input: {
55823
- type: "req",
55824
- id: requirementId,
55825
- relationships: [
55826
- { type: "requires_predicate", from: requirementId, to: factId }
55827
- ]
55828
- },
55829
- reason: `Link requires_predicate to ${factId}; upsert the requirement with its stored title and status and this relationship only.`
56207
+ input: requirementUpsert([
56208
+ { type: "requires_predicate", from: requirementId, to: factId }
56209
+ ]),
56210
+ reason: `Link requires_predicate to ${factId}; ${restate}.`
55830
56211
  }
55831
56212
  ],
55832
- instructions: `Only replace the grounding when the predicate states the claim at least as precisely as ${existing.map((row) => row.factId).join(", ")}. Run kb_check after the last step.`
56213
+ rollback: {
56214
+ operation: "kb_upsert",
56215
+ input: requirementUpsert(existing.map((row) => row.relationship)),
56216
+ reason: "Only if the last step fails: restore the retracted grounding link so the claim is grounded again."
56217
+ },
56218
+ instructions: `Only replace the grounding when the predicate states the claim at least as precisely as ${existing.map((row) => row.factId).join(", ")}. Apply the steps unchanged, in order and back to back: linking requires_predicate before the retraction is rejected because the claim would have two groundings, and between the retraction and the link the claim is ungrounded, so kb_check reports logic-coverage for ${requirementId} until the last step lands. If the last step fails, apply rollback. Run kb_check after the last step.`
55833
56219
  };
55834
56220
  }
55835
56221
  async function executeSuggestPredicates(args, context) {
55836
56222
  return handleKbSuggestPredicates(context.prolog ?? null, args, context);
55837
56223
  }
55838
- var DEFAULT_MAX_CANDIDATES = 5, DEFAULT_MIN_SCORE2 = 0.35, NON_ASSERTIVE_PROPOSITION_ROLES, GROUNDING_RELATIONSHIP_TYPES;
56224
+ var DEFAULT_MAX_CANDIDATES = 5, DEFAULT_MIN_SCORE2 = 0.35, NON_ASSERTIVE_PROPOSITION_ROLES, GROUNDING_RELATIONSHIP_TYPES, RESTATED_REQUIREMENT_FIELDS;
55839
56225
  var init_suggest_predicates = __esm(() => {
56226
+ init_discovery_entities();
55840
56227
  init_serialization();
55841
56228
  init_analyze_prose();
55842
56229
  init_clauses();
55843
56230
  init_plugin_orchestration();
55844
56231
  init_predicate_applicability();
55845
56232
  init_predicate_applyplan();
56233
+ init_predicate_binding_hints();
55846
56234
  init_predicate_catalog();
55847
56235
  init_predicate_inference();
55848
56236
  init_predicate_loader();
@@ -55858,6 +56246,13 @@ var init_suggest_predicates = __esm(() => {
55858
56246
  "requires_predicate",
55859
56247
  "requires_rule"
55860
56248
  ];
56249
+ RESTATED_REQUIREMENT_FIELDS = [
56250
+ "title",
56251
+ "status",
56252
+ "priority",
56253
+ "owner",
56254
+ "tags"
56255
+ ];
55861
56256
  });
55862
56257
 
55863
56258
  // ../cli/dist/public/operations/specs/modeling.js
@@ -59870,7 +60265,7 @@ var init_skills = __esm(() => {
59870
60265
  });
59871
60266
 
59872
60267
  // ../cli/dist/public/operations/specs/skills.js
59873
- import { createHash as createHash25 } from "node:crypto";
60268
+ import { createHash as createHash26 } from "node:crypto";
59874
60269
  function assertNonEmptyString(value, field) {
59875
60270
  if (typeof value !== "string" || value.trim() === "") {
59876
60271
  throw new Error(`${field} must be a non-empty string`);
@@ -59891,7 +60286,7 @@ async function executeSkillsLoad(input, _context) {
59891
60286
  assertNonEmptyString(id, "id");
59892
60287
  const bundle = loadBundledSkill(id);
59893
60288
  const resources = bundle.manifest.resources ?? [];
59894
- const contentHash = createHash25("sha256").update(bundle.body, "utf8").digest("hex");
60289
+ const contentHash = createHash26("sha256").update(bundle.body, "utf8").digest("hex");
59895
60290
  const resourceList = resources.length === 0 ? "none" : resources.join(", ");
59896
60291
  return {
59897
60292
  content: [
@@ -61648,16 +62043,16 @@ var init_audit = __esm(() => {
61648
62043
  });
61649
62044
 
61650
62045
  // ../cli/dist/operations/mutation/source-authoring.js
61651
- import { createHash as createHash26 } from "node:crypto";
62046
+ import { createHash as createHash27 } from "node:crypto";
61652
62047
  import { randomUUID as randomUUID2 } from "node:crypto";
61653
62048
  import fs6 from "node:fs";
61654
62049
  import path50 from "node:path";
61655
62050
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
61656
- function digest2(value) {
61657
- return createHash26("sha256").update(value).digest("hex");
62051
+ function digest3(value) {
62052
+ return createHash27("sha256").update(value).digest("hex");
61658
62053
  }
61659
62054
  function pendingReceiptPath(workspaceRoot, relativePath) {
61660
- return path50.join(workspaceRoot, ".kb", "recovery", "pending-sources", `${digest2(relativePath)}.json`);
62055
+ return path50.join(workspaceRoot, ".kb", "recovery", "pending-sources", `${digest3(relativePath)}.json`);
61661
62056
  }
61662
62057
  function writePendingSourceReceipt(workspaceRoot, relativePath, afterHash) {
61663
62058
  const receiptPath = pendingReceiptPath(workspaceRoot, relativePath);
@@ -62164,8 +62559,8 @@ async function writeSourceForUpsert(input, entity, existing, context, sourceDocu
62164
62559
  const receipt = {
62165
62560
  path: relative,
62166
62561
  mode: "write",
62167
- beforeHash: before === undefined ? null : digest2(before),
62168
- afterHash: digest2(after),
62562
+ beforeHash: before === undefined ? null : digest3(before),
62563
+ afterHash: digest3(after),
62169
62564
  created: before === undefined
62170
62565
  };
62171
62566
  const rollback = async () => {
@@ -62178,7 +62573,7 @@ async function writeSourceForUpsert(input, entity, existing, context, sourceDocu
62178
62573
  } catch {
62179
62574
  current = undefined;
62180
62575
  }
62181
- if (current !== undefined && digest2(current) !== digest2(after)) {
62576
+ if (current !== undefined && digest3(current) !== digest3(after)) {
62182
62577
  writePendingSourceReceipt(context.workspaceRoot, relative, receipt.afterHash);
62183
62578
  console.warn(`Skipped source rollback for ${relative}: file changed after the Kibi write; kept concurrent content and recorded recovery metadata.`);
62184
62579
  return;
@@ -62248,7 +62643,7 @@ var init_source_authoring = __esm(() => {
62248
62643
  });
62249
62644
 
62250
62645
  // ../cli/dist/operations/mutation/plan-apply-journal.js
62251
- import { createHash as createHash27, randomUUID as randomUUID3 } from "node:crypto";
62646
+ import { createHash as createHash28, randomUUID as randomUUID3 } from "node:crypto";
62252
62647
  import { closeSync as closeSync3, existsSync as existsSync26, fsyncSync as fsyncSync2, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync27, readdirSync as readdirSync8, renameSync as renameSync5, statSync as statSync10, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "node:fs";
62253
62648
  import path51 from "node:path";
62254
62649
  function planApplyJournalId(planHash) {
@@ -62258,7 +62653,7 @@ function isPlanApplyJournalId(value) {
62258
62653
  return JOURNAL_ID_PATTERN.test(value);
62259
62654
  }
62260
62655
  function contentHash(value) {
62261
- return createHash27("sha256").update(value).digest("hex");
62656
+ return createHash28("sha256").update(value).digest("hex");
62262
62657
  }
62263
62658
  function errorText(error) {
62264
62659
  return error instanceof Error ? error.message : String(error);
@@ -63035,7 +63430,7 @@ var init_workspace_mutation_lock = __esm(() => {
63035
63430
  });
63036
63431
 
63037
63432
  // ../cli/dist/operations/mutation/delete.js
63038
- import { createHash as createHash28, randomUUID as randomUUID4 } from "node:crypto";
63433
+ import { createHash as createHash29, randomUUID as randomUUID4 } from "node:crypto";
63039
63434
  import { existsSync as existsSync27, readFileSync as readFileSync29, writeFileSync as writeFileSync9 } from "node:fs";
63040
63435
  import path53 from "node:path";
63041
63436
  function requireProlog3(context) {
@@ -63073,7 +63468,7 @@ function sourceIsAuthored(source) {
63073
63468
  function fileHash(pathname) {
63074
63469
  if (!existsSync27(pathname))
63075
63470
  return null;
63076
- return createHash28("sha256").update(readFileSync29(pathname)).digest("hex");
63471
+ return createHash29("sha256").update(readFileSync29(pathname)).digest("hex");
63077
63472
  }
63078
63473
  async function executeRelationshipDeleteUnlocked(selectors, context, onCommitted = () => {
63079
63474
  return;
@@ -63267,8 +63662,8 @@ async function executeRelationshipDeleteUnlocked(selectors, context, onCommitted
63267
63662
  ];
63268
63663
  });
63269
63664
  sourceWrites.push(...Array.from(sourcePatches, ([relative, patch]) => {
63270
- const beforeHash = createHash28("sha256").update(patch.before).digest("hex");
63271
- const afterHash = createHash28("sha256").update(patch.after).digest("hex");
63665
+ const beforeHash = createHash29("sha256").update(patch.before).digest("hex");
63666
+ const afterHash = createHash29("sha256").update(patch.after).digest("hex");
63272
63667
  writePendingSourceReceipt(context.workspaceRoot, relative, afterHash);
63273
63668
  return {
63274
63669
  path: relative,
@@ -63400,8 +63795,8 @@ async function executeDelete(input, context) {
63400
63795
  }
63401
63796
  const deletion = renderSourceDeletion(relativeSource, id, String(entity.type), contents);
63402
63797
  evolvingSourceBodies.set(relativeSource, deletion.mode === "write" ? deletion.body : undefined);
63403
- const beforeHash = createHash28("sha256").update(original).digest("hex");
63404
- const afterHash = deletion.body === undefined ? null : createHash28("sha256").update(deletion.body).digest("hex");
63798
+ const beforeHash = createHash29("sha256").update(original).digest("hex");
63799
+ const afterHash = deletion.body === undefined ? null : createHash29("sha256").update(deletion.body).digest("hex");
63405
63800
  sourceHashes[relativeSource] = beforeHash;
63406
63801
  sourcePlansByPath.set(relativeSource, {
63407
63802
  path: relativeSource,
@@ -63427,13 +63822,13 @@ async function executeDelete(input, context) {
63427
63822
  const relativeShard = path53.relative(context.workspaceRoot, shardPath).replaceAll("\\", "/");
63428
63823
  const original = readFileSync29(shardPath, "utf8");
63429
63824
  const body = renderShardWithout(original, selectors, shardPath);
63430
- const beforeHash = createHash28("sha256").update(original).digest("hex");
63825
+ const beforeHash = createHash29("sha256").update(original).digest("hex");
63431
63826
  sourceHashes[relativeShard] = beforeHash;
63432
63827
  sourcePlansByPath.set(relativeShard, {
63433
63828
  path: relativeShard,
63434
63829
  mode: "write",
63435
63830
  beforeHash,
63436
- afterHash: createHash28("sha256").update(body).digest("hex"),
63831
+ afterHash: createHash29("sha256").update(body).digest("hex"),
63437
63832
  body
63438
63833
  });
63439
63834
  }
@@ -63447,7 +63842,7 @@ async function executeDelete(input, context) {
63447
63842
  sourceWrites: sourcePlans,
63448
63843
  supersessionRequired
63449
63844
  };
63450
- const planHash = createHash28("sha256").update(JSON.stringify(planBody)).digest("hex");
63845
+ const planHash = createHash29("sha256").update(JSON.stringify(planBody)).digest("hex");
63451
63846
  const payload = {
63452
63847
  deleted: 0,
63453
63848
  skipped: authoredIds.length,
@@ -64381,7 +64776,7 @@ var init_symbols_coordinator2 = __esm(() => {
64381
64776
  });
64382
64777
 
64383
64778
  // ../cli/dist/operations/mutation/symbol-refresh.js
64384
- import { createHash as createHash29, randomUUID as randomUUID5 } from "node:crypto";
64779
+ import { createHash as createHash30, randomUUID as randomUUID5 } from "node:crypto";
64385
64780
  import { existsSync as existsSync29, readFileSync as readFileSync33, renameSync as renameSync7, unlinkSync as unlinkSync8, writeFileSync as writeFileSync11 } from "node:fs";
64386
64781
  import * as path58 from "node:path";
64387
64782
  function record2(value) {
@@ -64408,7 +64803,7 @@ function sourceTextFor(sourceFile, workspaceRoot) {
64408
64803
  }
64409
64804
  }
64410
64805
  function contentHash2(content) {
64411
- return createHash29("sha256").update(content).digest("hex");
64806
+ return createHash30("sha256").update(content).digest("hex");
64412
64807
  }
64413
64808
  function restoreOrUnlinkCoordinateArtifact(targetPath, before) {
64414
64809
  if (before === null) {
@@ -64608,7 +65003,7 @@ async function scenarioCoverageWarnings(prolog, relationships, entityType, entit
64608
65003
  var init_warnings = () => {};
64609
65004
 
64610
65005
  // ../cli/dist/operations/mutation/upsert.js
64611
- import { createHash as createHash30 } from "node:crypto";
65006
+ import { createHash as createHash31 } from "node:crypto";
64612
65007
  import { existsSync as existsSync30, readFileSync as readFileSync34, unlinkSync as unlinkSync9, writeFileSync as writeFileSync12 } from "node:fs";
64613
65008
  import path59 from "node:path";
64614
65009
  function requireProlog4(context) {
@@ -64629,10 +65024,10 @@ function parseChangeKind(value) {
64629
65024
  function fileHash2(pathname) {
64630
65025
  if (!existsSync30(pathname))
64631
65026
  return null;
64632
- return createHash30("sha256").update(readFileSync34(pathname)).digest("hex");
65027
+ return createHash31("sha256").update(readFileSync34(pathname)).digest("hex");
64633
65028
  }
64634
65029
  function textHash(value) {
64635
- return createHash30("sha256").update(value).digest("hex");
65030
+ return createHash31("sha256").update(value).digest("hex");
64636
65031
  }
64637
65032
  function assertRelationshipShardContained(workspaceRoot, shardPath) {
64638
65033
  const root = path59.resolve(workspaceRoot);
@@ -66926,7 +67321,7 @@ var init_staging = __esm(() => {
66926
67321
 
66927
67322
  // ../cli/dist/commands/sync.js
66928
67323
  import { execSync as execSync4 } from "node:child_process";
66929
- import { createHash as createHash31 } from "node:crypto";
67324
+ import { createHash as createHash32 } from "node:crypto";
66930
67325
  import { existsSync as existsSync34, mkdirSync as mkdirSync12, readFileSync as readFileSync37, readdirSync as readdirSync9, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "node:fs";
66931
67326
  import * as path64 from "node:path";
66932
67327
  function relationshipKey2(relationship) {
@@ -66999,14 +67394,14 @@ function trackedRelationshipFiles(workspaceRoot, relationshipsDir, recoverMissin
66999
67394
  receiptPath,
67000
67395
  path: relative,
67001
67396
  afterHash: receipt.afterHash,
67002
- rawHash: createHash31("sha256").update(readFileSync37(receiptPath)).digest("hex")
67397
+ rawHash: createHash32("sha256").update(readFileSync37(receiptPath)).digest("hex")
67003
67398
  });
67004
67399
  }
67005
67400
  continue;
67006
67401
  }
67007
67402
  throw new SyncError(`Pending source is missing: ${relative}.${PENDING_SOURCE_RECOVERY_HINT}`);
67008
67403
  }
67009
- const actual = createHash31("sha256").update(readFileSync37(absolute)).digest("hex");
67404
+ const actual = createHash32("sha256").update(readFileSync37(absolute)).digest("hex");
67010
67405
  if (actual !== receipt.afterHash) {
67011
67406
  throw new SyncError(`Pending source hash drift blocks sync for ${relative}`);
67012
67407
  }
@@ -67867,14 +68262,14 @@ var init_sync = __esm(() => {
67867
68262
  });
67868
68263
 
67869
68264
  // ../cli/dist/commands/branch.js
67870
- import { createHash as createHash32 } from "node:crypto";
68265
+ import { createHash as createHash33 } from "node:crypto";
67871
68266
  import * as fs8 from "node:fs";
67872
68267
  import * as path65 from "node:path";
67873
68268
  function isSupportedLegacyBranchMigration(from, to) {
67874
68269
  return from === to;
67875
68270
  }
67876
68271
  function hashLegacyStore(sourcePath) {
67877
- const hash = createHash32("sha256");
68272
+ const hash = createHash33("sha256");
67878
68273
  const visit = (directory, relative = "") => {
67879
68274
  for (const entry of fs8.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
67880
68275
  const absolute = path65.join(directory, entry.name);
@@ -67894,7 +68289,7 @@ function hashLegacyStore(sourcePath) {
67894
68289
  }
67895
68290
  function branchMigrationApprovalHash(from, to, sourcePath, targetPath) {
67896
68291
  const sourceHash = hashLegacyStore(sourcePath);
67897
- return createHash32("sha256").update(JSON.stringify({
68292
+ return createHash33("sha256").update(JSON.stringify({
67898
68293
  version: 2,
67899
68294
  from,
67900
68295
  to,
@@ -68187,7 +68582,7 @@ var init_branch = __esm(() => {
68187
68582
  });
68188
68583
 
68189
68584
  // ../cli/dist/operations/migration/inventory-rederive.js
68190
- import { createHash as createHash33 } from "node:crypto";
68585
+ import { createHash as createHash34 } from "node:crypto";
68191
68586
  import path66 from "node:path";
68192
68587
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
68193
68588
  function isRecord17(value) {
@@ -68404,7 +68799,7 @@ function planOne(workspaceRoot, relativePath, entity, extracted, shards, facts)
68404
68799
  summary: summarize2(changes, inventory),
68405
68800
  changes,
68406
68801
  contract,
68407
- contractHash: createHash33("sha256").update(canonicalJson3(contract)).digest("hex")
68802
+ contractHash: createHash34("sha256").update(canonicalJson3(contract)).digest("hex")
68408
68803
  };
68409
68804
  }
68410
68805
  function readShardRelationships(workspaceRoot) {
@@ -68977,7 +69372,7 @@ ${KIBI_BIN_RESOLVER}
68977
69372
  });
68978
69373
 
68979
69374
  // ../cli/dist/commands/legacy-storage-migration.js
68980
- import { createHash as createHash34 } from "node:crypto";
69375
+ import { createHash as createHash35 } from "node:crypto";
68981
69376
  import { existsSync as existsSync37, mkdirSync as mkdirSync15, readFileSync as readFileSync40, readdirSync as readdirSync11, renameSync as renameSync12, rmSync as rmSync5, statSync as statSync12, writeFileSync as writeFileSync18 } from "node:fs";
68982
69377
  import * as path70 from "node:path";
68983
69378
  function stripGlobToRoot(pattern) {
@@ -69163,7 +69558,7 @@ function rewritePendingSourceReceiptPaths(cwd, moves) {
69163
69558
  const absolute = path70.resolve(cwd, destination);
69164
69559
  if (!existsSync37(absolute))
69165
69560
  continue;
69166
- const afterHash = typeof receipt.afterHash === "string" && /^[a-f0-9]{64}$/i.test(receipt.afterHash) ? receipt.afterHash : createHash34("sha256").update(readFileSync40(absolute)).digest("hex");
69561
+ const afterHash = typeof receipt.afterHash === "string" && /^[a-f0-9]{64}$/i.test(receipt.afterHash) ? receipt.afterHash : createHash35("sha256").update(readFileSync40(absolute)).digest("hex");
69167
69562
  writeFileSync18(receiptPath, `${JSON.stringify({ ...receipt, path: destination, afterHash }, null, 2)}
69168
69563
  `, "utf8");
69169
69564
  rewritten += 1;
@@ -69900,7 +70295,7 @@ var init_ignore_policy = __esm(() => {
69900
70295
 
69901
70296
  // ../cli/dist/public/operations/node-ports.js
69902
70297
  import { execFile as execFile2, execFileSync as execFileSync8 } from "node:child_process";
69903
- import { createHash as createHash35 } from "node:crypto";
70298
+ import { createHash as createHash36 } from "node:crypto";
69904
70299
  import * as fsSync from "node:fs";
69905
70300
  import * as fs9 from "node:fs/promises";
69906
70301
  import path72 from "node:path";
@@ -69930,7 +70325,7 @@ async function workspaceSnapshot(workspaceRoot) {
69930
70325
  "-z"
69931
70326
  ], { encoding: "buffer", maxBuffer: 64 * 1024 * 1024 });
69932
70327
  const paths = listed.toString("utf8").split("\x00").filter(Boolean).filter(includedSnapshotPath).map((raw) => ({ raw, key: raw.normalize("NFC") })).sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0);
69933
- const digest = createHash35("sha256");
70328
+ const digest = createHash36("sha256");
69934
70329
  digest.update("kibi.workspace-snapshot.v2\x00");
69935
70330
  for (const { raw, key: relativePath } of paths) {
69936
70331
  digest.update(relativePath);
@@ -70321,7 +70716,7 @@ var init_cli_runtime = __esm(() => {
70321
70716
  });
70322
70717
 
70323
70718
  // ../cli/dist/public/operations/legacy-migration-plan.js
70324
- import { createHash as createHash36 } from "node:crypto";
70719
+ import { createHash as createHash37 } from "node:crypto";
70325
70720
  function isRecord20(value) {
70326
70721
  return value !== null && typeof value === "object" && !Array.isArray(value);
70327
70722
  }
@@ -70356,7 +70751,7 @@ function stableStringify(value) {
70356
70751
  return JSON.stringify(stableValue3(value));
70357
70752
  }
70358
70753
  function hash(value) {
70359
- return createHash36("sha256").update(stableStringify(value)).digest("hex");
70754
+ return createHash37("sha256").update(stableStringify(value)).digest("hex");
70360
70755
  }
70361
70756
  function migrationRepairBatches(plan) {
70362
70757
  const seen = new Set;
@@ -70678,7 +71073,7 @@ var init_legacy_migration_plan = __esm(() => {
70678
71073
  });
70679
71074
 
70680
71075
  // ../cli/dist/public/operations/repair-plan.js
70681
- import { createHash as createHash37 } from "node:crypto";
71076
+ import { createHash as createHash38 } from "node:crypto";
70682
71077
  function isRecord21(value) {
70683
71078
  return value !== null && typeof value === "object" && !Array.isArray(value);
70684
71079
  }
@@ -70821,7 +71216,7 @@ function buildRepairPlan(payload, input, codeSnapshot) {
70821
71216
  },
70822
71217
  batches
70823
71218
  };
70824
- const planId = `repair-plan-${createHash37("sha256").update(stableStringify2(planCore)).digest("hex").slice(0, 24)}`;
71219
+ const planId = `repair-plan-${createHash38("sha256").update(stableStringify2(planCore)).digest("hex").slice(0, 24)}`;
70825
71220
  return {
70826
71221
  version: REPAIR_PLAN_VERSION,
70827
71222
  planId,
@@ -73923,7 +74318,7 @@ var init_migrate = __esm(() => {
73923
74318
  });
73924
74319
 
73925
74320
  // ../cli/dist/operations/planning/compile-intent.js
73926
- import { createHash as createHash38 } from "node:crypto";
74321
+ import { createHash as createHash39 } from "node:crypto";
73927
74322
  import path77 from "node:path";
73928
74323
  function compilePlanHash(plan) {
73929
74324
  const { planHash: _ignored, ...body } = plan;
@@ -73933,7 +74328,7 @@ function text7(value) {
73933
74328
  return typeof value === "string" ? value.trim() : "";
73934
74329
  }
73935
74330
  function hash2(value) {
73936
- return createHash38("sha256").update(canonicalize(value)).digest("hex");
74331
+ return createHash39("sha256").update(canonicalize(value)).digest("hex");
73937
74332
  }
73938
74333
  function shortHash2(value, length = 8) {
73939
74334
  return hash2(value).slice(0, length);
@@ -74080,7 +74475,7 @@ async function sourceHashes(context, locations) {
74080
74475
  for (const relative of sourceFilesFor(locations)) {
74081
74476
  try {
74082
74477
  const contents = context.fs ? await context.fs.readFile(path77.join(context.workspaceRoot, relative)) : null;
74083
- hashes[relative] = contents === null ? null : createHash38("sha256").update(contents).digest("hex");
74478
+ hashes[relative] = contents === null ? null : createHash39("sha256").update(contents).digest("hex");
74084
74479
  } catch {
74085
74480
  hashes[relative] = null;
74086
74481
  }
@@ -74665,7 +75060,7 @@ var init_compile_intent = __esm(() => {
74665
75060
  });
74666
75061
 
74667
75062
  // ../cli/dist/operations/planning/apply-plan.js
74668
- import { createHash as createHash39, randomUUID as randomUUID6 } from "node:crypto";
75063
+ import { createHash as createHash40, randomUUID as randomUUID6 } from "node:crypto";
74669
75064
  import path78 from "node:path";
74670
75065
  function text8(value) {
74671
75066
  return typeof value === "string" ? value.trim() : "";
@@ -74673,8 +75068,8 @@ function text8(value) {
74673
75068
  function isRecord23(value) {
74674
75069
  return value !== null && typeof value === "object" && !Array.isArray(value);
74675
75070
  }
74676
- function digest3(value) {
74677
- return createHash39("sha256").update(value).digest("hex");
75071
+ function digest4(value) {
75072
+ return createHash40("sha256").update(value).digest("hex");
74678
75073
  }
74679
75074
  function relationships(step) {
74680
75075
  if (!Array.isArray(step.relationships))
@@ -74838,7 +75233,7 @@ function validateEntityDeletionPlan(args) {
74838
75233
  throw new Error("Entity deletion apply failed: approvedPlanHash does not match planHash");
74839
75234
  }
74840
75235
  const { planHash: _ignored, ...body } = args.plan;
74841
- if (createHash39("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
75236
+ if (createHash40("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
74842
75237
  throw new Error("Entity deletion apply failed: planHash does not match the canonical plan body");
74843
75238
  }
74844
75239
  if (!Array.isArray(args.plan.entityIds) || args.plan.entityIds.length === 0) {
@@ -74917,7 +75312,7 @@ async function validateSources(context, sourceHashes) {
74917
75312
  throw new Error("Apply plan failed: source hashes require a filesystem-capable runtime");
74918
75313
  let actual;
74919
75314
  try {
74920
- actual = digest3(await context.fs.readFile(path78.join(context.workspaceRoot, relative)));
75315
+ actual = digest4(await context.fs.readFile(path78.join(context.workspaceRoot, relative)));
74921
75316
  } catch {
74922
75317
  actual = null;
74923
75318
  }
@@ -74946,12 +75341,12 @@ async function resolveSourceWriteTarget(workspaceRoot, fsPort, write) {
74946
75341
  const existing = await fsPort.readFile(absolute).catch(() => {
74947
75342
  return;
74948
75343
  });
74949
- const beforeHash = existing === undefined ? null : digest3(existing);
75344
+ const beforeHash = existing === undefined ? null : digest4(existing);
74950
75345
  if (beforeHash !== write.beforeHash) {
74951
75346
  throw new Error(`Apply plan failed: source hash changed for ${write.path}`);
74952
75347
  }
74953
75348
  const mode = write.mode ?? "write";
74954
- if (mode === "write" && (write.body === undefined || write.afterHash === null || digest3(write.body) !== write.afterHash)) {
75349
+ if (mode === "write" && (write.body === undefined || write.afterHash === null || digest4(write.body) !== write.afterHash)) {
74955
75350
  throw new Error(`Apply plan failed: afterHash does not match staged body for ${write.path}`);
74956
75351
  }
74957
75352
  if (mode === "delete" && write.afterHash !== null) {
@@ -74995,7 +75390,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
74995
75390
  for (const entry of prior.entries) {
74996
75391
  try {
74997
75392
  const current = await context.fs.readFile(path78.resolve(context.workspaceRoot, entry.path));
74998
- if (entry.afterHash === null || digest3(current) !== entry.afterHash) {
75393
+ if (entry.afterHash === null || digest4(current) !== entry.afterHash) {
74999
75394
  allAfter = false;
75000
75395
  }
75001
75396
  } catch {
@@ -75023,7 +75418,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
75023
75418
  } catch {
75024
75419
  current = undefined;
75025
75420
  }
75026
- const currentHash = current === undefined ? null : digest3(current);
75421
+ const currentHash = current === undefined ? null : digest4(current);
75027
75422
  if (currentHash !== entry.beforeHash && currentHash !== entry.afterHash) {
75028
75423
  throw new Error(`Apply plan recovery refused: ${entry.path} changed outside its journal`);
75029
75424
  }
@@ -77915,7 +78310,7 @@ init_generate();
77915
78310
  init_presentation();
77916
78311
  init_ir();
77917
78312
  // src/skill-operations.ts
77918
- import { createHash as createHash40 } from "node:crypto";
78313
+ import { createHash as createHash41 } from "node:crypto";
77919
78314
 
77920
78315
  // src/skill-system/loader.ts
77921
78316
  import { existsSync as existsSync44, readFileSync as readFileSync47, readdirSync as readdirSync13, realpathSync as realpathSync10 } from "node:fs";
@@ -78187,7 +78582,7 @@ async function executeSkillsLoad2(input, _context) {
78187
78582
  assertNonEmptyString2(id, "id");
78188
78583
  const bundle = loadBundledSkill2(id);
78189
78584
  const resources = bundle.manifest.resources ?? [];
78190
- const contentHash = createHash40("sha256").update(bundle.body, "utf8").digest("hex");
78585
+ const contentHash = createHash41("sha256").update(bundle.body, "utf8").digest("hex");
78191
78586
  const resourceList = resources.length === 0 ? "none" : resources.join(", ");
78192
78587
  return {
78193
78588
  content: [