kibi-runtime 2.8.0 → 2.10.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,226 @@ 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
27567
+ };
27568
+ }
27569
+ function stepProperties2(step) {
27570
+ return step.properties !== null && typeof step.properties === "object" ? step.properties : {};
27571
+ }
27572
+ function subjectStepKey(step) {
27573
+ const properties = stepProperties2(step);
27574
+ return step.type === "fact" && properties.fact_kind === "subject" && typeof properties.subject_key === "string" && typeof step.id === "string" ? properties.subject_key : null;
27575
+ }
27576
+ function retarget(relationships, renamed) {
27577
+ return relationships.map((relationship) => {
27578
+ const to = renamed.get(relationship.to);
27579
+ return to === undefined ? relationship : { ...relationship, to };
27580
+ });
27581
+ }
27582
+ function retargetStep(step, renamed) {
27583
+ if (!Array.isArray(step.relationships))
27584
+ return step;
27585
+ const relationships = step.relationships;
27586
+ return { ...step, relationships: retarget(relationships, renamed) };
27587
+ }
27588
+ function shareSubjectFacts(candidates) {
27589
+ const owners = new Map;
27590
+ const renamed = new Map;
27591
+ candidates.forEach((candidate, candidateIndex) => {
27592
+ for (const step of candidate.applyPlan) {
27593
+ const key = subjectStepKey(step);
27594
+ if (key === null)
27595
+ continue;
27596
+ const factId = String(step.id);
27597
+ const source = String(stepProperties2(step).text_ref ?? candidate.sourcePath);
27598
+ const owner = owners.get(key);
27599
+ if (owner === undefined) {
27600
+ owners.set(key, { factId, candidateIndex, sources: [source] });
27601
+ continue;
27602
+ }
27603
+ if (owner.factId === factId)
27604
+ continue;
27605
+ renamed.set(factId, owner.factId);
27606
+ if (!owner.sources.includes(source))
27607
+ owner.sources.push(source);
27608
+ }
27609
+ });
27610
+ if (renamed.size === 0)
27611
+ return { candidates: [...candidates], diagnostics: [] };
27612
+ const shared = [...owners.entries()].filter(([, owner]) => owner.sources.length > 1);
27613
+ const sharedFacts = new Map(shared.map(([key, owner]) => [owner.factId, { key, owner }]));
27614
+ const result = candidates.map((candidate) => ({
27615
+ ...candidate,
27616
+ relationships: retarget(candidate.relationships, renamed),
27617
+ applyPlan: candidate.applyPlan.filter((step) => !renamed.has(String(step.id))).map((step) => {
27618
+ const sharing = sharedFacts.get(String(step.id));
27619
+ const retargeted = retargetStep(step, renamed);
27620
+ if (sharing === undefined || subjectStepKey(step) === null)
27621
+ return retargeted;
27622
+ const properties = stepProperties2(step);
27623
+ const tags = Array.isArray(properties.tags) ? properties.tags.map(String) : [];
27624
+ return {
27625
+ ...retargeted,
27626
+ properties: {
27627
+ ...properties,
27628
+ tags: [
27629
+ ...new Set([
27630
+ ...tags,
27631
+ ...sharing.owner.sources.map((source) => `provenance:${normalizeSourceKey(source)}`)
27632
+ ])
27633
+ ]
27634
+ },
27635
+ document: {
27636
+ body: `Subject ${sharing.key}, shared by the requirements bootstrap planned from ${sharing.owner.sources.join(", ")}. Each of them links to this fact through constrains.
27637
+ `
27638
+ }
27639
+ };
27640
+ })
27641
+ }));
27642
+ return {
27643
+ candidates: result,
27644
+ diagnostics: shared.map(([key, owner]) => `subject-key-shared: claims at ${owner.sources.join(", ")} name the same subject ${key}, so the plan writes one subject fact ${owner.factId} and links every requirement to it through constrains.`)
27480
27645
  };
27481
27646
  }
27647
+ var MAX_ASPECT_WORDS = 4, MAX_ASPECT_CHARS = 40, ASPECT_BOUNDARY_WORDS, ASPECT_STOP_WORDS;
27482
27648
  var init_requirement_claims = __esm(() => {
27483
27649
  init_strict_modeling();
27650
+ ASPECT_BOUNDARY_WORDS = new Set([
27651
+ "when",
27652
+ "whenever",
27653
+ "while",
27654
+ "if",
27655
+ "unless",
27656
+ "without",
27657
+ "after",
27658
+ "before",
27659
+ "until",
27660
+ "because",
27661
+ "once",
27662
+ "as",
27663
+ "by",
27664
+ "via",
27665
+ "during",
27666
+ "except",
27667
+ "so",
27668
+ "that",
27669
+ "which",
27670
+ "who",
27671
+ "where",
27672
+ "since",
27673
+ "for",
27674
+ "and",
27675
+ "or"
27676
+ ]);
27677
+ ASPECT_STOP_WORDS = new Set([
27678
+ "a",
27679
+ "an",
27680
+ "the",
27681
+ "be",
27682
+ "is",
27683
+ "are",
27684
+ "was",
27685
+ "were",
27686
+ "been",
27687
+ "being",
27688
+ "to",
27689
+ "of",
27690
+ "in",
27691
+ "on",
27692
+ "at",
27693
+ "with",
27694
+ "from",
27695
+ "into",
27696
+ "its",
27697
+ "it",
27698
+ "their",
27699
+ "they",
27700
+ "them",
27701
+ "this",
27702
+ "these",
27703
+ "those",
27704
+ "can",
27705
+ "could",
27706
+ "will",
27707
+ "would",
27708
+ "may",
27709
+ "might",
27710
+ "must",
27711
+ "shall",
27712
+ "should",
27713
+ "do",
27714
+ "does",
27715
+ "has",
27716
+ "have",
27717
+ "had",
27718
+ "any",
27719
+ "all",
27720
+ "each",
27721
+ "every",
27722
+ "some",
27723
+ "only",
27724
+ "also",
27725
+ "then",
27726
+ "than",
27727
+ "not",
27728
+ "no"
27729
+ ]);
27484
27730
  });
27485
27731
 
27486
27732
  // ../cli/dist/operations/bootstrap/markdown-candidates.js
@@ -27550,7 +27796,7 @@ function markdownComponentHint(relativePath) {
27550
27796
  }
27551
27797
  return;
27552
27798
  }
27553
- function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence) {
27799
+ function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics) {
27554
27800
  const relativePath = item.relativePath ?? item.label;
27555
27801
  const base = /\bshall\b/i.test(statement) ? 0.86 : /\bmust\b/i.test(statement) ? 0.84 : 0.78;
27556
27802
  const confidence = Math.min(0.95, base + (heading && /requirements?|constraints?|polic(?:y|ies)|rules?/i.test(heading) ? 0.08 : 0));
@@ -27559,7 +27805,8 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27559
27805
  const claim = claimFor(statement, relativePath, confidence, `${relativePath}#L${line}`);
27560
27806
  if (!claim)
27561
27807
  return null;
27562
- const subjectKey = resolveBootstrapSubjectKey(claim.subjectKey, claim.propertyKey, markdownComponentHint(relativePath));
27808
+ const planned = planSubjectKey(claim.subjectKey, claim.propertyKey, markdownComponentHint(relativePath), subjectKeys, `${relativePath}#L${line}`);
27809
+ const subjectKey = planned.resolution;
27563
27810
  if (!subjectKey.ok)
27564
27811
  throw new Error(`${subjectKey.reason}; author it with kb_model and a component.aspect subjectKey`);
27565
27812
  const writeSet = buildStrictWriteSet({
@@ -27568,6 +27815,7 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27568
27815
  });
27569
27816
  if (!writeSet.isStrict)
27570
27817
  return null;
27818
+ diagnostics.push(...planned.diagnostics);
27571
27819
  return {
27572
27820
  candidateId: `norm:${writeSet.req.id.toLowerCase()}`,
27573
27821
  entityType: "req",
@@ -27588,7 +27836,7 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27588
27836
  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
27837
  };
27590
27838
  }
27591
- function markdownCandidates(item, existingIds, minConfidence) {
27839
+ function markdownCandidates(item, existingIds, minConfidence, subjectKeys) {
27592
27840
  const candidates = [];
27593
27841
  const sourceOnlySignals = [];
27594
27842
  const diagnostics = [];
@@ -27617,7 +27865,7 @@ function markdownCandidates(item, existingIds, minConfidence) {
27617
27865
  if (!statement || !/\b(must|shall|should)\b/i.test(statement))
27618
27866
  continue;
27619
27867
  try {
27620
- const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence);
27868
+ const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics);
27621
27869
  if (candidate) {
27622
27870
  if (!candidate.applyPlan.some((payload) => payload.type === "req" && existingIds.has(String(payload.id))))
27623
27871
  candidates.push(candidate);
@@ -27748,7 +27996,7 @@ function providerCandidate(item, existingIds, minConfidence) {
27748
27996
  }
27749
27997
  ];
27750
27998
  }
27751
- function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown) {
27999
+ function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown, subjectKeys) {
27752
28000
  const candidates = [];
27753
28001
  const sourceOnlySignals = [];
27754
28002
  const diagnostics = [];
@@ -27771,7 +28019,7 @@ function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeG
27771
28019
  }
27772
28020
  }
27773
28021
  if (item.kind === "generic_markdown" && includeGenericMarkdown) {
27774
- const built = markdownCandidates(item, existingIds, minConfidence);
28022
+ const built = markdownCandidates(item, existingIds, minConfidence, subjectKeys);
27775
28023
  candidates.push(...built.candidates);
27776
28024
  sourceOnlySignals.push(...built.sourceOnlySignals);
27777
28025
  diagnostics.push(...built.diagnostics);
@@ -28019,13 +28267,13 @@ var init_discovery_evidence = __esm(() => {
28019
28267
  });
28020
28268
 
28021
28269
  // ../cli/dist/operations/bootstrap/types.js
28022
- import { createHash as createHash15 } from "node:crypto";
28270
+ import { createHash as createHash16 } from "node:crypto";
28023
28271
  function bootstrapPlanHash(plan) {
28024
28272
  const { planHash: _ignored, ...body } = plan;
28025
- return createHash15("sha256").update(canonicalize(body)).digest("hex");
28273
+ return createHash16("sha256").update(canonicalize(body)).digest("hex");
28026
28274
  }
28027
28275
  function bootstrapEmptyKbSnapshotId(input) {
28028
- return `empty-source-state-${createHash15("sha256").update(canonicalize({
28276
+ return `empty-source-state-${createHash16("sha256").update(canonicalize({
28029
28277
  version: "kibi.empty-source-state.v1",
28030
28278
  branch: input.branch,
28031
28279
  workspaceSnapshot: input.workspaceSnapshot,
@@ -28162,7 +28410,7 @@ var init_discovery = __esm(() => {
28162
28410
  });
28163
28411
 
28164
28412
  // ../cli/dist/operations/bootstrap/intent-claims.js
28165
- import { createHash as createHash16 } from "node:crypto";
28413
+ import { createHash as createHash17 } from "node:crypto";
28166
28414
  function citation(source, claim) {
28167
28415
  return [
28168
28416
  `intent_claim:${source.id}:${claim.reference}`,
@@ -28171,8 +28419,8 @@ function citation(source, claim) {
28171
28419
  ...source.connector ? [`connector:${source.connector}`] : []
28172
28420
  ];
28173
28421
  }
28174
- function digest(...parts) {
28175
- return createHash16("sha256").update(parts.join("\x00")).digest("hex").slice(0, 16).toUpperCase();
28422
+ function digest2(...parts) {
28423
+ return createHash17("sha256").update(parts.join("\x00")).digest("hex").slice(0, 16).toUpperCase();
28176
28424
  }
28177
28425
  function claimBody(source, claim) {
28178
28426
  return renderRequirementBody({
@@ -28195,7 +28443,7 @@ function withRationale(step, rationale) {
28195
28443
  return { ...step, properties: { ...properties, rationale } };
28196
28444
  }
28197
28445
  function reviewClaimCandidate(source, claim, kind, confidence) {
28198
- const id = `FACT-BOOTSTRAP-${kind === "open_question" ? "QUESTION" : "OBSERVATION"}-${digest(source.id, claim.reference, claim.statement)}`;
28446
+ const id = `FACT-BOOTSTRAP-${kind === "open_question" ? "QUESTION" : "OBSERVATION"}-${digest2(source.id, claim.reference, claim.statement)}`;
28199
28447
  const entity = {
28200
28448
  type: "fact",
28201
28449
  id,
@@ -28240,7 +28488,7 @@ function conflictCandidates(declared, sources, existingIds, minConfidence) {
28240
28488
  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
28489
  continue;
28242
28490
  }
28243
- const id = `FACT-BOOTSTRAP-CONFLICT-${digest(...[...refs].sort(), conflict.note)}`;
28491
+ const id = `FACT-BOOTSTRAP-CONFLICT-${digest2(...[...refs].sort(), conflict.note)}`;
28244
28492
  const candidateId = `conflict:${id.toLowerCase()}`;
28245
28493
  if (confidence < minConfidence) {
28246
28494
  suppressed.push({
@@ -28315,7 +28563,7 @@ ${evidenceLines.join(`
28315
28563
  }
28316
28564
  return { candidates, sourceOnlySignals: [], suppressed, diagnostics };
28317
28565
  }
28318
- function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
28566
+ function buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys) {
28319
28567
  const sources = new Map((declared.knowledgeSources ?? []).map((source) => [source.id, source]));
28320
28568
  const candidates = [];
28321
28569
  const sourceOnlySignals = [];
@@ -28371,7 +28619,9 @@ function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
28371
28619
  let writeSet = null;
28372
28620
  try {
28373
28621
  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;
28622
+ const planned = modeled ? planSubjectKey(modeled.subjectKey, modeled.propertyKey, claim.component ?? source.component, subjectKeys, `${source.id}:${claim.reference}`) : null;
28623
+ const subjectKey = planned?.resolution ?? null;
28624
+ diagnostics.push(...planned?.diagnostics ?? []);
28375
28625
  writeSet = modeled && subjectKey?.ok ? buildStrictWriteSet({
28376
28626
  claim: { ...modeled, subjectKey: subjectKey.subjectKey },
28377
28627
  statement: claim.statement
@@ -30208,7 +30458,6 @@ var init_analysis_receipt = __esm(() => {
30208
30458
  // ../cli/dist/operations/semantic-advisor/observation-plan.js
30209
30459
  function observationPlan(payload, title, tags) {
30210
30460
  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
30461
  return [
30213
30462
  {
30214
30463
  type: "fact",
@@ -30221,7 +30470,13 @@ function observationPlan(payload, title, tags) {
30221
30470
  text_ref: statementOf(payload),
30222
30471
  tags
30223
30472
  },
30224
- relationships: target === undefined ? [] : [relationship(factId, target, "relates_to")]
30473
+ document: {
30474
+ 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.
30475
+
30476
+ > ${statementOf(payload).trim().replace(/\n/g, `
30477
+ > `)}`
30478
+ },
30479
+ relationships: []
30225
30480
  }
30226
30481
  ];
30227
30482
  }
@@ -31006,7 +31261,7 @@ var init_validation2 = __esm(() => {
31006
31261
  });
31007
31262
 
31008
31263
  // ../cli/dist/operations/bootstrap/generate.js
31009
- import { createHash as createHash17 } from "node:crypto";
31264
+ import { createHash as createHash18 } from "node:crypto";
31010
31265
  import path31 from "node:path";
31011
31266
  function filterSourceOnlySignals(signals, entityTypes) {
31012
31267
  return entityTypes && entityTypes.length > 0 ? signals.filter((signal) => entityTypes.includes(signal.kind)) : signals;
@@ -31025,7 +31280,7 @@ function firstUpsertId(candidate) {
31025
31280
  return properties !== null && typeof properties === "object" && "id" in properties && typeof properties.id === "string" ? properties.id : "";
31026
31281
  }
31027
31282
  function sha2562(value) {
31028
- return createHash17("sha256").update(value).digest("hex");
31283
+ return createHash18("sha256").update(value).digest("hex");
31029
31284
  }
31030
31285
  function selectBootstrapCandidates(input, existingIds, entityTypes, maximum) {
31031
31286
  const allowed = entityTypes?.length ? new Set(entityTypes) : null;
@@ -31240,13 +31495,14 @@ async function executePlanBootstrap(args, context) {
31240
31495
  })
31241
31496
  ]);
31242
31497
  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) : {
31498
+ const subjectKeys = new SubjectKeyRegistry;
31499
+ const built = discovery.activation.allowCandidateGeneration ? buildBootstrapCandidates(discovery.evidence, new Set, minConfidence, includeGenericMarkdown, subjectKeys) : {
31244
31500
  candidates: [],
31245
31501
  sourceOnlySignals: [],
31246
31502
  suppressed: [],
31247
31503
  diagnostics: []
31248
31504
  };
31249
- const claimed = discovery.activation.allowCandidateGeneration ? buildIntentClaimCandidates(declared, existingIds, minConfidence) : {
31505
+ const claimed = discovery.activation.allowCandidateGeneration ? buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys) : {
31250
31506
  candidates: [],
31251
31507
  sourceOnlySignals: [],
31252
31508
  suppressed: [],
@@ -31254,6 +31510,7 @@ async function executePlanBootstrap(args, context) {
31254
31510
  };
31255
31511
  const filteredSignals = filterSourceOnlySignals([...built.sourceOnlySignals, ...claimed.sourceOnlySignals], args.entityTypes);
31256
31512
  const selected = selectBootstrapCandidates([...built.candidates, ...claimed.candidates].filter((candidate) => candidate.confidence >= minConfidence), existingIds, args.entityTypes, maxCandidates);
31513
+ const shared = shareSubjectFacts(selected.candidates);
31257
31514
  const ignored = discovery.ignoredSources.map((sourcePath) => ({
31258
31515
  candidateId: "",
31259
31516
  reason: "ignored_source",
@@ -31267,7 +31524,7 @@ async function executePlanBootstrap(args, context) {
31267
31524
  discoverySummary: discovery.summary,
31268
31525
  migrationWarning: discovery.migrationWarning,
31269
31526
  ...args.bootstrapContext ? { bootstrapContext: args.bootstrapContext } : {},
31270
- candidates: selected.candidates,
31527
+ candidates: shared.candidates,
31271
31528
  sourceOnlySignals: [
31272
31529
  ...filteredSignals,
31273
31530
  ...filterSourceOnlySignals(selected.sourceOnlySignals, args.entityTypes)
@@ -31286,7 +31543,8 @@ async function executePlanBootstrap(args, context) {
31286
31543
  ] : [],
31287
31544
  ...claimed.diagnostics,
31288
31545
  ...built.diagnostics,
31289
- ...selected.diagnostics
31546
+ ...selected.diagnostics,
31547
+ ...shared.diagnostics
31290
31548
  ]
31291
31549
  });
31292
31550
  }
@@ -31298,6 +31556,7 @@ var init_generate = __esm(() => {
31298
31556
  init_discovery();
31299
31557
  init_intent_claims();
31300
31558
  init_presentation();
31559
+ init_requirement_claims();
31301
31560
  init_validation2();
31302
31561
  init_types();
31303
31562
  });
@@ -31570,7 +31829,7 @@ var init_aggregated_checks = __esm(() => {
31570
31829
  });
31571
31830
 
31572
31831
  // ../cli/dist/traceability/impact-review.js
31573
- import { createHash as createHash18 } from "node:crypto";
31832
+ import { createHash as createHash19 } from "node:crypto";
31574
31833
  function canonicalJson2(value) {
31575
31834
  const active = new Set;
31576
31835
  const encode = (item) => {
@@ -31604,10 +31863,10 @@ function canonicalJson2(value) {
31604
31863
  return encode(value);
31605
31864
  }
31606
31865
  function fingerprint(value) {
31607
- return `sha256:${createHash18("sha256").update(canonicalJson2(value), "utf8").digest("hex")}`;
31866
+ return `sha256:${createHash19("sha256").update(canonicalJson2(value), "utf8").digest("hex")}`;
31608
31867
  }
31609
31868
  function fingerprintBytes(value) {
31610
- return `sha256:${createHash18("sha256").update(value).digest("hex")}`;
31869
+ return `sha256:${createHash19("sha256").update(value).digest("hex")}`;
31611
31870
  }
31612
31871
  function isObject(value) {
31613
31872
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -32237,7 +32496,7 @@ var init_source_classification = __esm(() => {
32237
32496
  });
32238
32497
 
32239
32498
  // ../cli/dist/plugins/source-analysis-service.js
32240
- import { createHash as createHash19 } from "node:crypto";
32499
+ import { createHash as createHash20 } from "node:crypto";
32241
32500
  import * as path33 from "node:path";
32242
32501
  function detectSourceLanguage(filePath) {
32243
32502
  return extensionLanguage(filePath);
@@ -32245,7 +32504,7 @@ function detectSourceLanguage(filePath) {
32245
32504
  function createV2Request(filePath, content, explicitLanguageHint) {
32246
32505
  const classification = classifySource(filePath, content, explicitLanguageHint);
32247
32506
  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");
32507
+ 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
32508
  return {
32250
32509
  classification,
32251
32510
  input: {
@@ -32407,7 +32666,7 @@ class SourceAnalysisService {
32407
32666
  providerId: entry.capability.id,
32408
32667
  stamp: entry.stamp,
32409
32668
  inputFingerprint: request.inputFingerprint,
32410
- providerFingerprint: this.options.providerFingerprints?.[entry.pluginId] ?? createHash19("sha256").update(`${entry.pluginId}\x00${entry.pluginVersion}\x00${entry.stamp.capability}`).digest("hex"),
32669
+ providerFingerprint: this.options.providerFingerprints?.[entry.pluginId] ?? createHash20("sha256").update(`${entry.pluginId}\x00${entry.pluginVersion}\x00${entry.stamp.capability}`).digest("hex"),
32411
32670
  shadowComparisons: []
32412
32671
  };
32413
32672
  } catch (error) {
@@ -32527,7 +32786,7 @@ var init_source_analysis_service = __esm(() => {
32527
32786
  });
32528
32787
 
32529
32788
  // ../cli/dist/plugins/maintenance-source-analysis.js
32530
- import { createHash as createHash20 } from "node:crypto";
32789
+ import { createHash as createHash21 } from "node:crypto";
32531
32790
  import { readFileSync as readFileSync16, realpathSync as realpathSync5 } from "node:fs";
32532
32791
  import { createRequire as createRequire4 } from "node:module";
32533
32792
  import { dirname as dirname7, isAbsolute as isAbsolute6, join as join13, relative as relative5, resolve as resolve7 } from "node:path";
@@ -32540,7 +32799,7 @@ function digestFile(root, file) {
32540
32799
  const actualRel = relative5(realpathSync5(root), actual);
32541
32800
  if (actualRel.startsWith("..") || isAbsolute6(actualRel))
32542
32801
  throw new Error(`Approved asset escapes its package: ${file}`);
32543
- return createHash20("sha256").update(readFileSync16(actual)).digest("hex");
32802
+ return createHash21("sha256").update(readFileSync16(actual)).digest("hex");
32544
32803
  }
32545
32804
  function verifyFiles(root, files) {
32546
32805
  if (Object.keys(files).length === 0)
@@ -32609,7 +32868,7 @@ function verifyApprovedSourceAnalyzer(root, approval) {
32609
32868
  if (metadata.version !== dependency.version)
32610
32869
  throw new Error(`Unapproved source runtime version: ${dependency.packageName}`);
32611
32870
  }
32612
- return createHash20("sha256").update(JSON.stringify(approval)).digest("hex");
32871
+ return createHash21("sha256").update(JSON.stringify(approval)).digest("hex");
32613
32872
  }
32614
32873
  function createMaintenanceSourceAnalysisService(workspaceRoot, projectConfig) {
32615
32874
  const config = projectConfig ?? readProjectKibiConfig(workspaceRoot);
@@ -40581,7 +40840,7 @@ async function enrichSymbolCoordinatesWithTsMorph(entries, workspaceRoot) {
40581
40840
  var init_symbols_ts = () => {};
40582
40841
 
40583
40842
  // ../cli/dist/traceability/symbol-extract.js
40584
- import { createHash as createHash22 } from "node:crypto";
40843
+ import { createHash as createHash23 } from "node:crypto";
40585
40844
  function createManifestLookupSentinelKey(manifestPath) {
40586
40845
  return `${MANIFEST_SENTINEL_PREFIX}${manifestPath}`;
40587
40846
  }
@@ -40593,7 +40852,7 @@ function getCandidateManifestPaths(filePath) {
40593
40852
  return LOCAL_MANIFEST_NAMES.map((manifestName) => `${dir}/${manifestName}`);
40594
40853
  }
40595
40854
  function createHashFallbackId(filePath, name) {
40596
- const h = createHash22("sha256");
40855
+ const h = createHash23("sha256");
40597
40856
  h.update(`${filePath}:${name}`);
40598
40857
  return h.digest("hex").slice(0, 16);
40599
40858
  }
@@ -40681,7 +40940,7 @@ function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifes
40681
40940
  };
40682
40941
  }
40683
40942
  function computeContentSha(content) {
40684
- const h = createHash22("sha256");
40943
+ const h = createHash23("sha256");
40685
40944
  h.update(content);
40686
40945
  return h.digest("hex");
40687
40946
  }
@@ -44180,9 +44439,88 @@ var init_origin_review = __esm(() => {
44180
44439
  RATIONALE_HEADING = /^#{1,6}[ \t]+(?:rationale|why|context)\b/im;
44181
44440
  });
44182
44441
 
44442
+ // ../cli/dist/operations/modeling/predicate-closed-vocabularies.js
44443
+ function closedArguments(byArgument) {
44444
+ const constants = {};
44445
+ const aliases = {};
44446
+ for (const [name, vocabulary] of Object.entries(byArgument)) {
44447
+ constants[name] = [...vocabulary.constants];
44448
+ if (Object.keys(vocabulary.aliases).length > 0)
44449
+ aliases[name] = { ...vocabulary.aliases };
44450
+ }
44451
+ return Object.keys(aliases).length > 0 ? { argument_constants: constants, argument_aliases: aliases } : { argument_constants: constants };
44452
+ }
44453
+ var TRIGGER_VOCABULARY, DECISION_VOCABULARY, REFRESH_POLICY_VOCABULARY, ENVIRONMENT_DECISION_VOCABULARY, OPERATOR_VOCABULARY, RETENTION_UNIT_VOCABULARY, CODING_STANDARD_ACTION_VOCABULARY, LIFECYCLE_ACTION_VOCABULARY;
44454
+ var init_predicate_closed_vocabularies = __esm(() => {
44455
+ TRIGGER_VOCABULARY = {
44456
+ constants: ["escape", "cancel", "submit", "navigation", "click", "timeout"],
44457
+ aliases: {
44458
+ esc: "escape",
44459
+ escape_key: "escape",
44460
+ cancellation: "cancel",
44461
+ submission: "submit",
44462
+ navigate: "navigation",
44463
+ navigates: "navigation",
44464
+ navigating: "navigation",
44465
+ navigation_away: "navigation",
44466
+ clicks: "click",
44467
+ time_out: "timeout",
44468
+ timed_out: "timeout"
44469
+ }
44470
+ };
44471
+ DECISION_VOCABULARY = {
44472
+ constants: ["allow", "deny"],
44473
+ aliases: {
44474
+ assert: "allow",
44475
+ allowed: "allow",
44476
+ permit: "allow",
44477
+ permitted: "allow",
44478
+ denied: "deny",
44479
+ forbid: "deny",
44480
+ forbidden: "deny"
44481
+ }
44482
+ };
44483
+ REFRESH_POLICY_VOCABULARY = {
44484
+ constants: ["automatic", "manual", "on_demand"],
44485
+ aliases: {
44486
+ auto: "automatic",
44487
+ automatically: "automatic",
44488
+ manually: "manual",
44489
+ "on-demand": "on_demand"
44490
+ }
44491
+ };
44492
+ ENVIRONMENT_DECISION_VOCABULARY = {
44493
+ constants: ["allowed", "forbidden", "read-only"],
44494
+ aliases: {
44495
+ allow: "allowed",
44496
+ deny: "forbidden",
44497
+ denied: "forbidden",
44498
+ read_only: "read-only",
44499
+ readonly: "read-only"
44500
+ }
44501
+ };
44502
+ OPERATOR_VOCABULARY = {
44503
+ constants: ["lt", "lte", "eq", "neq", "gte", "gt"],
44504
+ aliases: { le: "lte", ge: "gte", ne: "neq" }
44505
+ };
44506
+ RETENTION_UNIT_VOCABULARY = {
44507
+ constants: ["days", "months", "years"],
44508
+ aliases: { day: "days", month: "months", year: "years" }
44509
+ };
44510
+ CODING_STANDARD_ACTION_VOCABULARY = {
44511
+ constants: ["use", "avoid"],
44512
+ aliases: {}
44513
+ };
44514
+ LIFECYCLE_ACTION_VOCABULARY = {
44515
+ constants: ["archived", "deleted", "expired"],
44516
+ aliases: { archive: "archived", delete: "deleted", expire: "expired" }
44517
+ };
44518
+ });
44519
+
44183
44520
  // ../cli/dist/operations/modeling/predicate-catalog-1.js
44184
44521
  var PREDICATE_CATALOG_1;
44185
44522
  var init_predicate_catalog_1 = __esm(() => {
44523
+ init_predicate_closed_vocabularies();
44186
44524
  PREDICATE_CATALOG_1 = [
44187
44525
  {
44188
44526
  id: "FACT-SCHEMA-STATE",
@@ -44202,6 +44540,7 @@ var init_predicate_catalog_1 = __esm(() => {
44202
44540
  description: "A subject transitions between states because of a trigger.",
44203
44541
  argument_names: ["subject", "from_state", "to_state", "trigger"],
44204
44542
  argument_types: ["entity", "state", "state", "trigger"],
44543
+ ...closedArguments({ trigger: TRIGGER_VOCABULARY }),
44205
44544
  keywords: [
44206
44545
  "transition",
44207
44546
  "enter",
@@ -44377,6 +44716,7 @@ var init_predicate_catalog_1 = __esm(() => {
44377
44716
  description: "An entity must be archived, deleted, expired, or lifecycle-transitioned after a duration.",
44378
44717
  argument_names: ["subject", "action", "duration", "unit"],
44379
44718
  argument_types: ["entity", "action", "number", "unit"],
44719
+ ...closedArguments({ action: LIFECYCLE_ACTION_VOCABULARY }),
44380
44720
  keywords: ["archive", "archived", "delete", "deleted", "expire", "after"],
44381
44721
  examples: ["lifecycle_rule(expired_sessions, archived, 30, days)"],
44382
44722
  tags: ["lifecycle", "retention"]
@@ -44387,6 +44727,7 @@ var init_predicate_catalog_1 = __esm(() => {
44387
44727
  // ../cli/dist/operations/modeling/predicate-catalog-2.js
44388
44728
  var PREDICATE_CATALOG_2;
44389
44729
  var init_predicate_catalog_2 = __esm(() => {
44730
+ init_predicate_closed_vocabularies();
44390
44731
  PREDICATE_CATALOG_2 = [
44391
44732
  {
44392
44733
  id: "FACT-SCHEMA-CONFLICT-RESOLUTION-RULE",
@@ -44454,6 +44795,7 @@ var init_predicate_catalog_2 = __esm(() => {
44454
44795
  description: "A named action is allowed or forbidden in a deployment environment.",
44455
44796
  argument_names: ["action", "decision", "environment"],
44456
44797
  argument_types: ["action", "decision", "environment"],
44798
+ ...closedArguments({ decision: ENVIRONMENT_DECISION_VOCABULARY }),
44457
44799
  keywords: ["production", "staging", "forbidden", "destructive"],
44458
44800
  examples: [
44459
44801
  "environment_safety_rule(destructive_operations, forbidden, production)"
@@ -44478,6 +44820,7 @@ var init_predicate_catalog_2 = __esm(() => {
44478
44820
  description: "Developer-facing code must use or avoid a framework API, pattern, or documentation practice.",
44479
44821
  argument_names: ["subject", "action", "target"],
44480
44822
  argument_types: ["entity", "action", "api"],
44823
+ ...closedArguments({ action: CODING_STANDARD_ACTION_VOCABULARY }),
44481
44824
  keywords: ["computed", "signals", "must use", "must not use"],
44482
44825
  examples: ["coding_standard_rule(derived_state, use, computed_signals)"],
44483
44826
  tags: ["coding-standard", "agent-guidance"]
@@ -44604,6 +44947,7 @@ var init_predicate_catalog_2 = __esm(() => {
44604
44947
  // ../cli/dist/operations/modeling/predicate-catalog-3.js
44605
44948
  var PREDICATE_CATALOG_3;
44606
44949
  var init_predicate_catalog_3 = __esm(() => {
44950
+ init_predicate_closed_vocabularies();
44607
44951
  PREDICATE_CATALOG_3 = [
44608
44952
  {
44609
44953
  id: "FACT-SCHEMA-ORDERED-STRATEGY-RULE",
@@ -44625,6 +44969,7 @@ var init_predicate_catalog_3 = __esm(() => {
44625
44969
  description: "A subject refreshes a target automatically or by a named policy.",
44626
44970
  argument_names: ["subject", "target", "policy"],
44627
44971
  argument_types: ["entity", "target", "policy"],
44972
+ ...closedArguments({ policy: REFRESH_POLICY_VOCABULARY }),
44628
44973
  keywords: ["automatically refresh", "manual page reload", "refresh"],
44629
44974
  examples: ["refresh_policy_rule(dashboard, processing_videos, automatic)"],
44630
44975
  tags: ["refresh", "ui"]
@@ -44636,6 +44981,7 @@ var init_predicate_catalog_3 = __esm(() => {
44636
44981
  description: "A scoped actor is allowed or denied an action because of assignment, ownership, or membership.",
44637
44982
  argument_names: ["actor_scope", "action", "decision"],
44638
44983
  argument_types: ["actor_scope", "action", "decision"],
44984
+ ...closedArguments({ decision: DECISION_VOCABULARY }),
44639
44985
  keywords: ["unassigned", "assigned", "denied", "scoped"],
44640
44986
  examples: [
44641
44987
  "scoped_authorization_rule(unassigned_instructors, signed_url_generation, deny)"
@@ -44736,6 +45082,7 @@ var init_predicate_catalog_3 = __esm(() => {
44736
45082
  description: "A trigger commits, saves, or persists a subject within a scope.",
44737
45083
  argument_names: ["subject", "trigger", "scope"],
44738
45084
  argument_types: ["entity", "trigger", "scope"],
45085
+ ...closedArguments({ trigger: TRIGGER_VOCABULARY }),
44739
45086
  keywords: [
44740
45087
  "save",
44741
45088
  "saves",
@@ -44758,6 +45105,7 @@ var init_predicate_catalog_3 = __esm(() => {
44758
45105
  description: "A trigger discards or cancels changes for a subject within a scope.",
44759
45106
  argument_names: ["subject", "trigger", "scope"],
44760
45107
  argument_types: ["entity", "trigger", "scope"],
45108
+ ...closedArguments({ trigger: TRIGGER_VOCABULARY }),
44761
45109
  keywords: ["discard", "cancel", "escape", "revert", "without save"],
44762
45110
  examples: ["discard_action(editor.annotation, escape, active_annotation)"],
44763
45111
  tags: ["persistence", "workflow"]
@@ -44780,6 +45128,7 @@ var init_predicate_catalog_3 = __esm(() => {
44780
45128
  description: "A subject is retained for a bounded duration.",
44781
45129
  argument_names: ["subject", "duration", "unit"],
44782
45130
  argument_types: ["entity", "number", "unit"],
45131
+ ...closedArguments({ unit: RETENTION_UNIT_VOCABULARY }),
44783
45132
  keywords: ["retain", "retained", "retention", "days", "months", "years"],
44784
45133
  examples: ["retention_policy(customer.data, 7, years)"],
44785
45134
  tags: ["data", "policy"]
@@ -44791,6 +45140,7 @@ var init_predicate_catalog_3 = __esm(() => {
44791
45140
  description: "A subject constrains a resource by operator, threshold, and unit.",
44792
45141
  argument_names: ["subject", "resource", "operator", "threshold", "unit"],
44793
45142
  argument_types: ["entity", "resource", "operator", "number", "unit"],
45143
+ ...closedArguments({ operator: OPERATOR_VOCABULARY }),
44794
45144
  keywords: [
44795
45145
  "limit",
44796
45146
  "maximum",
@@ -44817,6 +45167,7 @@ var init_predicate_catalog_3 = __esm(() => {
44817
45167
  // ../cli/dist/operations/modeling/predicate-catalog-4.js
44818
45168
  var PREDICATE_CATALOG_4;
44819
45169
  var init_predicate_catalog_4 = __esm(() => {
45170
+ init_predicate_closed_vocabularies();
44820
45171
  PREDICATE_CATALOG_4 = [
44821
45172
  {
44822
45173
  id: "FACT-SCHEMA-DEPENDENCY-RESOLUTION-POLICY",
@@ -45019,6 +45370,7 @@ var init_predicate_catalog_4 = __esm(() => {
45019
45370
  description: "An actor is allowed or denied an action against a resource.",
45020
45371
  argument_names: ["actor", "action", "resource", "decision"],
45021
45372
  argument_types: ["actor", "action", "resource", "decision"],
45373
+ ...closedArguments({ decision: DECISION_VOCABULARY }),
45022
45374
  keywords: [
45023
45375
  "may",
45024
45376
  "can",
@@ -51991,7 +52343,7 @@ var require_out4 = __commonJS(function(exports2, module2) {
51991
52343
 
51992
52344
  // ../cli/dist/commands/sync/discovery.js
51993
52345
  import { execFile } from "node:child_process";
51994
- import { createHash as createHash23 } from "node:crypto";
52346
+ import { createHash as createHash24 } from "node:crypto";
51995
52347
  import * as fs5 from "node:fs";
51996
52348
  import { existsSync as existsSync23, readFileSync as readFileSync24, readdirSync as readdirSync6, unlinkSync as unlinkSync4 } from "node:fs";
51997
52349
  import * as path47 from "node:path";
@@ -52026,13 +52378,13 @@ function pendingSourcePaths(cwd, recoverMissing) {
52026
52378
  receiptPath,
52027
52379
  path: relative,
52028
52380
  afterHash: receipt.afterHash,
52029
- rawHash: createHash23("sha256").update(raw).digest("hex")
52381
+ rawHash: createHash24("sha256").update(raw).digest("hex")
52030
52382
  });
52031
52383
  continue;
52032
52384
  }
52033
52385
  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
52386
  }
52035
- const actual = createHash23("sha256").update(readFileSync24(absolute)).digest("hex");
52387
+ const actual = createHash24("sha256").update(readFileSync24(absolute)).digest("hex");
52036
52388
  if (actual !== receipt.afterHash) {
52037
52389
  throw new Error(`Pending source hash drift blocks sync for ${relative}; expected ${receipt.afterHash}, found ${actual}`);
52038
52390
  }
@@ -52080,7 +52432,7 @@ function clearRecoveredPendingSourceReceipts(cwd, receipts) {
52080
52432
  const raw = readTextOrEnoent(absolute);
52081
52433
  if (raw === null)
52082
52434
  continue;
52083
- const rawHash = createHash23("sha256").update(raw).digest("hex");
52435
+ const rawHash = createHash24("sha256").update(raw).digest("hex");
52084
52436
  if (rawHash !== receipt.rawHash) {
52085
52437
  throw new Error(`Pending source receipt changed during recovery for ${receipt.path}; refusing to retire newer receipt`);
52086
52438
  }
@@ -53868,7 +54220,7 @@ function scoreExactPredicates3(schema, text) {
53868
54220
  }
53869
54221
 
53870
54222
  // ../cli/dist/operations/modeling/predicate-utils.js
53871
- import { createHash as createHash24 } from "node:crypto";
54223
+ import { createHash as createHash25 } from "node:crypto";
53872
54224
  function normalizeText2(text) {
53873
54225
  const normalized = String(text ?? "").trim();
53874
54226
  if (!normalized) {
@@ -53892,13 +54244,18 @@ function slug2(value) {
53892
54244
  return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
53893
54245
  }
53894
54246
  function hashId(prefix, parts) {
53895
- const digest = createHash24("sha256").update(parts.join("\x00")).digest("hex").slice(0, 12).toUpperCase();
54247
+ const digest = createHash25("sha256").update(parts.join("\x00")).digest("hex").slice(0, 12).toUpperCase();
53896
54248
  return `${prefix}-${digest}`;
53897
54249
  }
53898
- function inferSubject(text, subjectHint) {
54250
+ function inferSubject(text, subjectHint, requirement) {
53899
54251
  const explicit = normalizeOptionalString2(subjectHint);
53900
54252
  if (explicit)
53901
54253
  return explicit;
54254
+ const grounded = normalizeOptionalString2(requirement?.subjectKey);
54255
+ if (grounded)
54256
+ return grounded;
54257
+ if (normalizeOptionalString2(requirement?.requirementId))
54258
+ return "requirement.subject";
53902
54259
  const lower = text.toLowerCase();
53903
54260
  if (lower.includes("annotation"))
53904
54261
  return "editor.annotation";
@@ -53922,6 +54279,10 @@ function inferTrigger(text) {
53922
54279
  return "cancel";
53923
54280
  if (lower.includes("submit"))
53924
54281
  return "submit";
54282
+ if (/\bclick/.test(lower))
54283
+ return "click";
54284
+ if (/\b(?:times?|timed)[\s-]?out\b/.test(lower))
54285
+ return "timeout";
53925
54286
  return "unspecified_trigger";
53926
54287
  }
53927
54288
  function inferScope(text) {
@@ -54556,11 +54917,43 @@ function isGenericPlaceholder(value) {
54556
54917
  const normalized = value.trim().toLowerCase();
54557
54918
  return PLACEHOLDER_VALUES.has(normalized) || normalized === "unknown" || normalized.startsWith("requirement.") || normalized.endsWith("_policy") && ["executable", "cwd", "environment", "stdio", "termination"].some((kind) => normalized.startsWith(kind));
54558
54919
  }
54559
- function classifyBinding(value, text, explicit, canonical = false) {
54920
+ function bindingToken(value) {
54921
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
54922
+ }
54923
+ function claimNames(text, token) {
54924
+ if (!text || !token)
54925
+ return false;
54926
+ const words = token.split("_").filter(Boolean);
54927
+ if (words.length === 0)
54928
+ return false;
54929
+ return new RegExp(`\\b${words.join("[\\s_-]+")}\\b`, "i").test(text);
54930
+ }
54931
+ function matchesConstant(value, context) {
54932
+ const token = bindingToken(value);
54933
+ return context.constants?.some((constant) => bindingToken(constant) === token) ?? false;
54934
+ }
54935
+ function nameOrStopWordReason(value, context = {}) {
54936
+ const token = bindingToken(value);
54937
+ if (token === "true" || token === "false")
54938
+ return null;
54939
+ if (matchesConstant(value, context))
54940
+ return null;
54941
+ if (STOP_WORD_BINDINGS.has(token))
54942
+ return `"${token}" is a stop word`;
54943
+ const own = context.argumentName ? bindingToken(context.argumentName) : "";
54944
+ const repeatsName = own !== "" && token === own || (context.argumentNames ?? []).some((name) => bindingToken(name) === token);
54945
+ if (repeatsName && !claimNames(context.text, token))
54946
+ return `it repeats the argument name ${token} and the claim does not name it`;
54947
+ return null;
54948
+ }
54949
+ function classifyBinding(value, text, explicit, canonical = false, context = {}) {
54560
54950
  const normalized = value.trim();
54561
- if (canonical && !isGenericPlaceholder(normalized))
54951
+ if (nameOrStopWordReason(normalized, { ...context, text }) !== null)
54952
+ return "placeholder";
54953
+ const constant = matchesConstant(normalized, context);
54954
+ if (canonical && (constant || !isGenericPlaceholder(normalized)))
54562
54955
  return "extracted";
54563
- if (explicit && (!isGenericPlaceholder(normalized) || ["true", "false"].includes(normalized.toLowerCase())))
54956
+ if (explicit && (constant || !isGenericPlaceholder(normalized) || ["true", "false"].includes(normalized.toLowerCase())))
54564
54957
  return "explicit";
54565
54958
  if (!normalized || isGenericPlaceholder(normalized))
54566
54959
  return "placeholder";
@@ -54572,9 +54965,9 @@ function aggregateBindingProvenance(values) {
54572
54965
  return values.reduce((worst, value) => PROVENANCE_ORDER.indexOf(value) > PROVENANCE_ORDER.indexOf(worst) ? value : worst, "explicit");
54573
54966
  }
54574
54967
  function bindingCanBeApplied(value) {
54575
- return value === "explicit" || value === "extracted";
54968
+ return value === "explicit" || value === "requirement" || value === "extracted";
54576
54969
  }
54577
- var PLACEHOLDER_VALUES, DERIVED_CUE_ALIASES, PROVENANCE_ORDER;
54970
+ var PLACEHOLDER_VALUES, DERIVED_CUE_ALIASES, STOP_WORD_BINDINGS, PROVENANCE_ORDER;
54578
54971
  var init_predicate_bindings = __esm(() => {
54579
54972
  PLACEHOLDER_VALUES = new Set([
54580
54973
  "unknown",
@@ -54644,10 +55037,80 @@ var init_predicate_bindings = __esm(() => {
54644
55037
  "missing module"
54645
55038
  ],
54646
55039
  actionable_error: ["actionable", "clear error", "report", "error"],
54647
- exception: ["exception", "except", "package-manager", "package manager"]
55040
+ exception: ["exception", "except", "package-manager", "package manager"],
55041
+ deny: [
55042
+ "denied",
55043
+ "must not",
55044
+ "must never",
55045
+ "forbidden",
55046
+ "prohibited",
55047
+ "not allowed",
55048
+ "not be allowed"
55049
+ ],
55050
+ allow: ["is allowed", "are allowed", "permitted"],
55051
+ on_demand: ["on demand", "on request"],
55052
+ timeout: ["times out", "timed out", "time out"],
55053
+ submit: ["submission", "submits", "submitted"]
54648
55054
  };
55055
+ STOP_WORD_BINDINGS = new Set([
55056
+ "a",
55057
+ "an",
55058
+ "the",
55059
+ "be",
55060
+ "is",
55061
+ "are",
55062
+ "was",
55063
+ "were",
55064
+ "been",
55065
+ "being",
55066
+ "am",
55067
+ "do",
55068
+ "does",
55069
+ "did",
55070
+ "done",
55071
+ "have",
55072
+ "has",
55073
+ "had",
55074
+ "it",
55075
+ "its",
55076
+ "this",
55077
+ "that",
55078
+ "these",
55079
+ "those",
55080
+ "they",
55081
+ "them",
55082
+ "there",
55083
+ "thing",
55084
+ "something",
55085
+ "anything",
55086
+ "some",
55087
+ "any",
55088
+ "of",
55089
+ "to",
55090
+ "in",
55091
+ "at",
55092
+ "by",
55093
+ "for",
55094
+ "with",
55095
+ "and",
55096
+ "or",
55097
+ "not",
55098
+ "null",
55099
+ "nil",
55100
+ "n_a",
55101
+ "na",
55102
+ "tbd",
55103
+ "todo",
55104
+ "x",
55105
+ "xxx",
55106
+ "foo",
55107
+ "bar",
55108
+ "example",
55109
+ "placeholder"
55110
+ ]);
54649
55111
  PROVENANCE_ORDER = [
54650
55112
  "explicit",
55113
+ "requirement",
54651
55114
  "extracted",
54652
55115
  "inferred",
54653
55116
  "placeholder"
@@ -55349,8 +55812,12 @@ var init_predicate_loader = __esm(() => {
55349
55812
  function buildSuggestion(schema, text, subject, score, argumentBindings = {}, polarityHint, diagnostics) {
55350
55813
  const inferredArgs = inferArgs(schema, text, subject);
55351
55814
  const canonicalLauncherArgs = exactLauncherPredicateArgs(schema.predicate_name, text);
55815
+ const hasExactBinding = (name) => typeof argumentBindings[name] === "string" && argumentBindings[name].trim().length > 0;
55816
+ const requirementSubjectArgument = diagnostics?.requirementSubject && diagnostics.explicitSubject !== true && !hasExactBinding("subject") ? schema.argument_names.indexOf("subject") : -1;
55352
55817
  const boundArgs = schema.argument_names.map((name, index) => {
55353
55818
  const exactBinding = argumentBindings[name];
55819
+ if (index === requirementSubjectArgument)
55820
+ return diagnostics?.requirementSubject ?? "unknown";
55354
55821
  return typeof exactBinding === "string" && exactBinding.trim().length > 0 ? exactBinding.trim() : inferredArgs[index] ?? "unknown";
55355
55822
  });
55356
55823
  const vocabulary = {
@@ -55362,7 +55829,11 @@ function buildSuggestion(schema, text, subject, score, argumentBindings = {}, po
55362
55829
  const undeclaredArguments = new Set(conformance.undeclared.map((value) => value.argumentName));
55363
55830
  const bindingProvenanceByArgument = Object.fromEntries(schema.argument_names.map((name, index) => [
55364
55831
  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"))
55832
+ index === requirementSubjectArgument && !undeclaredArguments.has(name) ? "requirement" : 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] } : {
55833
+ argumentName: name,
55834
+ argumentNames: schema.argument_names,
55835
+ constants: schema.argument_constants?.[name]
55836
+ })
55366
55837
  ]));
55367
55838
  const bindingProvenance = aggregateBindingProvenance(Object.values(bindingProvenanceByArgument));
55368
55839
  const unboundArguments = schema.argument_names.filter((name) => undeclaredArguments.has(name) || !bindingCanBeApplied(bindingProvenanceByArgument[name] ?? "placeholder"));
@@ -55452,8 +55923,21 @@ function buildRelationshipPlan(factId, requirementId, claimText, existingLogicCl
55452
55923
  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
55924
  };
55454
55925
  }
55926
+ function gapObservationBody(text, args) {
55927
+ const origin = [
55928
+ args.requirementId ? `requirement ${args.requirementId}` : null,
55929
+ args.source ? `source ${args.source}` : null
55930
+ ].filter((value) => value !== null);
55931
+ return [
55932
+ "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.",
55933
+ ...origin.length > 0 ? [`Claim from ${origin.join(", ")}:`] : ["Claim:"],
55934
+ `> ${text.trim().replace(/\n/g, `
55935
+ > `)}`
55936
+ ].join(`
55937
+
55938
+ `);
55939
+ }
55455
55940
  function buildGapApplyPlan(text, args) {
55456
- const claimKey = semanticClaimKey(text);
55457
55941
  const factId = hashId("FACT-ONTOLOGY-GAP", [
55458
55942
  args.requirementId ?? "",
55459
55943
  args.source ?? "",
@@ -55467,20 +55951,14 @@ function buildGapApplyPlan(text, args) {
55467
55951
  title: "Ontology gap: predicate schema needed",
55468
55952
  status: "active",
55469
55953
  source: args.source ?? "mcp://kibi/suggest-predicates",
55470
- text_ref: args.source,
55954
+ ...args.source ? { text_ref: args.source } : {},
55471
55955
  tags: ["review:ontology-gap", "needs_schema_extension"],
55472
55956
  fact_kind: "observation",
55473
55957
  value_string: text,
55474
- claim_key: claimKey,
55475
55958
  claim_text: text
55476
55959
  },
55477
- relationships: [
55478
- {
55479
- type: "relates_to",
55480
- from: factId,
55481
- to: "review:ontology-gap"
55482
- }
55483
- ]
55960
+ document: { body: gapObservationBody(text, args) },
55961
+ relationships: []
55484
55962
  }
55485
55963
  ];
55486
55964
  }
@@ -55543,6 +56021,88 @@ var init_predicate_applyplan = __esm(() => {
55543
56021
  init_predicate_utils();
55544
56022
  });
55545
56023
 
56024
+ // ../cli/dist/operations/modeling/predicate-binding-hints.js
56025
+ function exampleArguments(example) {
56026
+ const open = example.indexOf("(");
56027
+ const close = example.lastIndexOf(")");
56028
+ if (open < 0 || close <= open)
56029
+ return [];
56030
+ const inner = example.slice(open + 1, close);
56031
+ const args = [];
56032
+ let depth = 0;
56033
+ let current = "";
56034
+ for (const char of inner) {
56035
+ if (char === "(" || char === "[")
56036
+ depth += 1;
56037
+ if (char === ")" || char === "]")
56038
+ depth -= 1;
56039
+ if (char === "," && depth === 0) {
56040
+ args.push(current.trim());
56041
+ current = "";
56042
+ continue;
56043
+ }
56044
+ current += char;
56045
+ }
56046
+ args.push(current.trim());
56047
+ return args.filter(Boolean);
56048
+ }
56049
+ function unboundReason(candidate, name, value, text) {
56050
+ const schema = candidate.schema;
56051
+ const provenance = candidate.binding_provenance_by_argument[name];
56052
+ const nameReason = nameOrStopWordReason(value, {
56053
+ argumentName: name,
56054
+ argumentNames: schema.argument_names,
56055
+ constants: schema.argument_constants?.[name],
56056
+ text
56057
+ });
56058
+ if (nameReason !== null)
56059
+ return `The value "${value}" is not a binding: ${nameReason}.`;
56060
+ if (!value.trim() || isGenericPlaceholder(value))
56061
+ return "The claim text names no value for this argument.";
56062
+ if (provenance === "inferred")
56063
+ return `The value "${value}" was inferred and does not appear in the claim text; confirm it or pass the exact value.`;
56064
+ if (schema.argument_constants?.[name])
56065
+ return `The value "${value}" is not one of this argument's declared constants.`;
56066
+ return `The value "${value}" needs review.`;
56067
+ }
56068
+ function buildBindingHints(candidate, text, requirementSubjects = []) {
56069
+ const schema = candidate.schema;
56070
+ const parsedExamples = schema.examples.map(exampleArguments);
56071
+ return candidate.unbound_arguments.map((name) => {
56072
+ const position = schema.argument_names.indexOf(name);
56073
+ const constants = schema.argument_constants?.[name];
56074
+ const exampleValues = parsedExamples.map((args) => args[position]).filter((value) => typeof value === "string");
56075
+ const examples = Array.from(new Set([
56076
+ ...name === "subject" ? requirementSubjects : [],
56077
+ ...constants ?? [],
56078
+ ...exampleValues
56079
+ ])).slice(0, MAX_EXAMPLES);
56080
+ const current = candidate.predicate_args[position] ?? "";
56081
+ const description = schema.argument_descriptions?.[position];
56082
+ return {
56083
+ argument: name,
56084
+ position,
56085
+ type: schema.argument_types[position] ?? "unknown",
56086
+ ...description ? { description } : {},
56087
+ ...constants ? { allowedValues: [...constants] } : {},
56088
+ examples,
56089
+ currentValue: current,
56090
+ provenance: candidate.binding_provenance_by_argument[name] ?? "placeholder",
56091
+ reason: unboundReason(candidate, name, current, text)
56092
+ };
56093
+ });
56094
+ }
56095
+ function describeBindingHints(hints) {
56096
+ return hints.map((hint) => {
56097
+ const values = hint.allowedValues ? `one of ${hint.allowedValues.join(", ")}` : hint.examples.length > 0 ? `e.g. ${hint.examples.join(", ")}` : "no example in the schema";
56098
+ return `${hint.argument} (${hint.type}, ${values})`;
56099
+ }).join("; ");
56100
+ }
56101
+ var MAX_EXAMPLES = 5;
56102
+ var init_predicate_binding_hints = __esm(() => {
56103
+ init_predicate_bindings();
56104
+ });
56105
+
55546
56106
  // ../cli/dist/operations/modeling/suggest-predicates.js
55547
56107
  function analyzeInputPropositions(text) {
55548
56108
  return analyzeSemanticAdvisorInput({
@@ -55696,9 +56256,31 @@ async function existingClaimGrounding(prolog, requirementId, claimKey, warnings)
55696
56256
  }
55697
56257
  return found;
55698
56258
  }
56259
+ function hasSubjectHint(value) {
56260
+ return typeof value === "string" && value.trim().length > 0;
56261
+ }
56262
+ async function constrainedSubjectKeys(prolog, requirementId, warnings) {
56263
+ if (prolog === null || !requirementId?.trim())
56264
+ return [];
56265
+ try {
56266
+ const result = await prolog.query(`findall(Key, (kb_relationship(constrains, '${escapeAtom(requirementId.trim())}', _SubjectFact), kb:fact_subject_key(_SubjectFact, Key)), Keys)`);
56267
+ if (!result.success)
56268
+ return [];
56269
+ return Array.from(new Set(parsePrologList(result.bindings.Keys ?? "[]").map(unquoted).filter((key) => key.length > 0))).sort();
56270
+ } catch (error) {
56271
+ warnings.push(`The subject facts ${requirementId} constrains could not be read (${error instanceof Error ? error.message : String(error)}); pass subjectHint with the requirement's subject_key.`);
56272
+ return [];
56273
+ }
56274
+ }
55699
56275
  async function handleKbSuggestPredicates(prolog, args, context) {
55700
56276
  const text = normalizeText2(args.text);
55701
- const subject = inferSubject(text, args.subjectHint);
56277
+ const warnings = [];
56278
+ const requirementSubjects = hasSubjectHint(args.subjectHint) ? [] : await constrainedSubjectKeys(prolog, args.requirementId, warnings);
56279
+ const requirementSubject = requirementSubjects.length === 1 ? requirementSubjects[0] : undefined;
56280
+ const subject = inferSubject(text, args.subjectHint, {
56281
+ requirementId: args.requirementId,
56282
+ subjectKey: requirementSubject
56283
+ });
55702
56284
  const propositions = analyzeInputPropositions(text);
55703
56285
  const assertivePropositionCount = propositions.filter((proposition) => !NON_ASSERTIVE_PROPOSITION_ROLES.has(proposition.role)).length;
55704
56286
  if (propositions.length > 0 && assertivePropositionCount === 0) {
@@ -55709,7 +56291,6 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55709
56291
  }
55710
56292
  const maxCandidates = clampInteger(args.maxCandidates, DEFAULT_MAX_CANDIDATES, 1, 20);
55711
56293
  const minScore = clampScore(args.minScore ?? DEFAULT_MIN_SCORE2);
55712
- const warnings = [];
55713
56294
  const existingSchemas = await loadExistingPredicateSchemas(prolog, args.includeExistingSchemas ?? true, warnings);
55714
56295
  const composedCatalog = await composeOntologyCatalogForOperation(context, "kb_suggest_predicates");
55715
56296
  const packSchemas = composedCatalog ? ontologyPackSchemasToCandidates(composedCatalog.schemas) : [];
@@ -55740,7 +56321,8 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55740
56321
  rejectionReasons: applicability.reasons,
55741
56322
  applicabilityScore: applicability.applicabilityScore,
55742
56323
  scoreComponents: ranked.components,
55743
- explicitSubject: Boolean(args.subjectHint?.trim())
56324
+ explicitSubject: Boolean(args.subjectHint?.trim()),
56325
+ ...requirementSubject ? { requirementSubject } : {}
55744
56326
  });
55745
56327
  });
55746
56328
  const evaluatedCandidates = withMarginRejection(initialCandidates).sort((left, right) => {
@@ -55769,15 +56351,16 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55769
56351
  const applyPlan = completeCandidate ? alreadyGrounded ? [] : predicatePlan : !recommendedCandidate && !unavailableSchema ? buildGapApplyPlan(text, args) : [];
55770
56352
  const plannedFactId = typeof predicatePlan[0]?.id === "string" ? predicatePlan[0].id : null;
55771
56353
  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;
56354
+ const replacementPlan = alreadyGrounded && plannedFactId !== null && args.requirementId ? buildGroundingReplacementPlan(args.requirementId, plannedFactId, predicatePlan, existingGrounding, await storedRequirementProperties(prolog, args.requirementId, warnings)) : null;
55773
56355
  const relationshipTarget = relationshipPlan !== null || replacementPlan !== null ? plannedFactId : null;
55774
56356
  const groundingSummary = existingGrounding.map((row) => `${row.relationship.type} -> ${row.factId}`).join(", ");
55775
56357
  if (alreadyGrounded) {
55776
56358
  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
56359
  }
56360
+ const bindingHints = recommendedAction === "provide_argument_bindings" && recommendedCandidate ? buildBindingHints(recommendedCandidate, text, requirementSubjects) : [];
55778
56361
  const recommendedPredicateSchema = !recommendedCandidate && !unavailableSchema ? buildPredicateSchemaDraft(text, subject) : null;
55779
56362
  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.");
56363
+ 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
56364
  const logicClaims = Array.from(new Set([...args.existingLogicClaims ?? [], claimKey]));
55782
56365
  return {
55783
56366
  content: [{ type: "text", text: textSummary }],
@@ -55796,12 +56379,40 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55796
56379
  relationshipTarget,
55797
56380
  existingGrounding,
55798
56381
  replacementPlan,
56382
+ bindingHints,
55799
56383
  warnings
55800
56384
  },
55801
56385
  applyPlan
55802
56386
  };
55803
56387
  }
55804
- function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing) {
56388
+ async function storedRequirementProperties(prolog, requirementId, warnings) {
56389
+ if (prolog === null)
56390
+ return null;
56391
+ try {
56392
+ const [stored] = await loadEntities(prolog, {
56393
+ id: requirementId,
56394
+ type: "req"
56395
+ });
56396
+ if (stored === undefined)
56397
+ return null;
56398
+ const restated = {};
56399
+ for (const field of RESTATED_REQUIREMENT_FIELDS)
56400
+ if (stored[field] !== undefined && stored[field] !== null)
56401
+ restated[field] = stored[field];
56402
+ return typeof restated.title === "string" && typeof restated.status === "string" ? restated : null;
56403
+ } catch (error) {
56404
+ 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.`);
56405
+ return null;
56406
+ }
56407
+ }
56408
+ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing, stored) {
56409
+ const requirementUpsert = (relationships) => ({
56410
+ type: "req",
56411
+ id: requirementId,
56412
+ ...stored === null ? {} : { properties: stored },
56413
+ relationships
56414
+ });
56415
+ 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
56416
  return {
55806
56417
  relationshipTarget: factId,
55807
56418
  steps: [
@@ -55819,30 +56430,33 @@ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, exi
55819
56430
  },
55820
56431
  {
55821
56432
  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.`
56433
+ input: requirementUpsert([
56434
+ { type: "requires_predicate", from: requirementId, to: factId }
56435
+ ]),
56436
+ reason: `Link requires_predicate to ${factId}; ${restate}.`
55830
56437
  }
55831
56438
  ],
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.`
56439
+ rollback: {
56440
+ operation: "kb_upsert",
56441
+ input: requirementUpsert(existing.map((row) => row.relationship)),
56442
+ reason: "Only if the last step fails: restore the retracted grounding link so the claim is grounded again."
56443
+ },
56444
+ 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
56445
  };
55834
56446
  }
55835
56447
  async function executeSuggestPredicates(args, context) {
55836
56448
  return handleKbSuggestPredicates(context.prolog ?? null, args, context);
55837
56449
  }
55838
- var DEFAULT_MAX_CANDIDATES = 5, DEFAULT_MIN_SCORE2 = 0.35, NON_ASSERTIVE_PROPOSITION_ROLES, GROUNDING_RELATIONSHIP_TYPES;
56450
+ var DEFAULT_MAX_CANDIDATES = 5, DEFAULT_MIN_SCORE2 = 0.35, NON_ASSERTIVE_PROPOSITION_ROLES, GROUNDING_RELATIONSHIP_TYPES, RESTATED_REQUIREMENT_FIELDS;
55839
56451
  var init_suggest_predicates = __esm(() => {
56452
+ init_discovery_entities();
55840
56453
  init_serialization();
55841
56454
  init_analyze_prose();
55842
56455
  init_clauses();
55843
56456
  init_plugin_orchestration();
55844
56457
  init_predicate_applicability();
55845
56458
  init_predicate_applyplan();
56459
+ init_predicate_binding_hints();
55846
56460
  init_predicate_catalog();
55847
56461
  init_predicate_inference();
55848
56462
  init_predicate_loader();
@@ -55858,6 +56472,13 @@ var init_suggest_predicates = __esm(() => {
55858
56472
  "requires_predicate",
55859
56473
  "requires_rule"
55860
56474
  ];
56475
+ RESTATED_REQUIREMENT_FIELDS = [
56476
+ "title",
56477
+ "status",
56478
+ "priority",
56479
+ "owner",
56480
+ "tags"
56481
+ ];
55861
56482
  });
55862
56483
 
55863
56484
  // ../cli/dist/public/operations/specs/modeling.js
@@ -59870,7 +60491,7 @@ var init_skills = __esm(() => {
59870
60491
  });
59871
60492
 
59872
60493
  // ../cli/dist/public/operations/specs/skills.js
59873
- import { createHash as createHash25 } from "node:crypto";
60494
+ import { createHash as createHash26 } from "node:crypto";
59874
60495
  function assertNonEmptyString(value, field) {
59875
60496
  if (typeof value !== "string" || value.trim() === "") {
59876
60497
  throw new Error(`${field} must be a non-empty string`);
@@ -59891,7 +60512,7 @@ async function executeSkillsLoad(input, _context) {
59891
60512
  assertNonEmptyString(id, "id");
59892
60513
  const bundle = loadBundledSkill(id);
59893
60514
  const resources = bundle.manifest.resources ?? [];
59894
- const contentHash = createHash25("sha256").update(bundle.body, "utf8").digest("hex");
60515
+ const contentHash = createHash26("sha256").update(bundle.body, "utf8").digest("hex");
59895
60516
  const resourceList = resources.length === 0 ? "none" : resources.join(", ");
59896
60517
  return {
59897
60518
  content: [
@@ -61648,16 +62269,16 @@ var init_audit = __esm(() => {
61648
62269
  });
61649
62270
 
61650
62271
  // ../cli/dist/operations/mutation/source-authoring.js
61651
- import { createHash as createHash26 } from "node:crypto";
62272
+ import { createHash as createHash27 } from "node:crypto";
61652
62273
  import { randomUUID as randomUUID2 } from "node:crypto";
61653
62274
  import fs6 from "node:fs";
61654
62275
  import path50 from "node:path";
61655
62276
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
61656
- function digest2(value) {
61657
- return createHash26("sha256").update(value).digest("hex");
62277
+ function digest3(value) {
62278
+ return createHash27("sha256").update(value).digest("hex");
61658
62279
  }
61659
62280
  function pendingReceiptPath(workspaceRoot, relativePath) {
61660
- return path50.join(workspaceRoot, ".kb", "recovery", "pending-sources", `${digest2(relativePath)}.json`);
62281
+ return path50.join(workspaceRoot, ".kb", "recovery", "pending-sources", `${digest3(relativePath)}.json`);
61661
62282
  }
61662
62283
  function writePendingSourceReceipt(workspaceRoot, relativePath, afterHash) {
61663
62284
  const receiptPath = pendingReceiptPath(workspaceRoot, relativePath);
@@ -62164,8 +62785,8 @@ async function writeSourceForUpsert(input, entity, existing, context, sourceDocu
62164
62785
  const receipt = {
62165
62786
  path: relative,
62166
62787
  mode: "write",
62167
- beforeHash: before === undefined ? null : digest2(before),
62168
- afterHash: digest2(after),
62788
+ beforeHash: before === undefined ? null : digest3(before),
62789
+ afterHash: digest3(after),
62169
62790
  created: before === undefined
62170
62791
  };
62171
62792
  const rollback = async () => {
@@ -62178,7 +62799,7 @@ async function writeSourceForUpsert(input, entity, existing, context, sourceDocu
62178
62799
  } catch {
62179
62800
  current = undefined;
62180
62801
  }
62181
- if (current !== undefined && digest2(current) !== digest2(after)) {
62802
+ if (current !== undefined && digest3(current) !== digest3(after)) {
62182
62803
  writePendingSourceReceipt(context.workspaceRoot, relative, receipt.afterHash);
62183
62804
  console.warn(`Skipped source rollback for ${relative}: file changed after the Kibi write; kept concurrent content and recorded recovery metadata.`);
62184
62805
  return;
@@ -62248,7 +62869,7 @@ var init_source_authoring = __esm(() => {
62248
62869
  });
62249
62870
 
62250
62871
  // ../cli/dist/operations/mutation/plan-apply-journal.js
62251
- import { createHash as createHash27, randomUUID as randomUUID3 } from "node:crypto";
62872
+ import { createHash as createHash28, randomUUID as randomUUID3 } from "node:crypto";
62252
62873
  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
62874
  import path51 from "node:path";
62254
62875
  function planApplyJournalId(planHash) {
@@ -62258,7 +62879,7 @@ function isPlanApplyJournalId(value) {
62258
62879
  return JOURNAL_ID_PATTERN.test(value);
62259
62880
  }
62260
62881
  function contentHash(value) {
62261
- return createHash27("sha256").update(value).digest("hex");
62882
+ return createHash28("sha256").update(value).digest("hex");
62262
62883
  }
62263
62884
  function errorText(error) {
62264
62885
  return error instanceof Error ? error.message : String(error);
@@ -63035,7 +63656,7 @@ var init_workspace_mutation_lock = __esm(() => {
63035
63656
  });
63036
63657
 
63037
63658
  // ../cli/dist/operations/mutation/delete.js
63038
- import { createHash as createHash28, randomUUID as randomUUID4 } from "node:crypto";
63659
+ import { createHash as createHash29, randomUUID as randomUUID4 } from "node:crypto";
63039
63660
  import { existsSync as existsSync27, readFileSync as readFileSync29, writeFileSync as writeFileSync9 } from "node:fs";
63040
63661
  import path53 from "node:path";
63041
63662
  function requireProlog3(context) {
@@ -63073,7 +63694,7 @@ function sourceIsAuthored(source) {
63073
63694
  function fileHash(pathname) {
63074
63695
  if (!existsSync27(pathname))
63075
63696
  return null;
63076
- return createHash28("sha256").update(readFileSync29(pathname)).digest("hex");
63697
+ return createHash29("sha256").update(readFileSync29(pathname)).digest("hex");
63077
63698
  }
63078
63699
  async function executeRelationshipDeleteUnlocked(selectors, context, onCommitted = () => {
63079
63700
  return;
@@ -63267,8 +63888,8 @@ async function executeRelationshipDeleteUnlocked(selectors, context, onCommitted
63267
63888
  ];
63268
63889
  });
63269
63890
  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");
63891
+ const beforeHash = createHash29("sha256").update(patch.before).digest("hex");
63892
+ const afterHash = createHash29("sha256").update(patch.after).digest("hex");
63272
63893
  writePendingSourceReceipt(context.workspaceRoot, relative, afterHash);
63273
63894
  return {
63274
63895
  path: relative,
@@ -63400,8 +64021,8 @@ async function executeDelete(input, context) {
63400
64021
  }
63401
64022
  const deletion = renderSourceDeletion(relativeSource, id, String(entity.type), contents);
63402
64023
  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");
64024
+ const beforeHash = createHash29("sha256").update(original).digest("hex");
64025
+ const afterHash = deletion.body === undefined ? null : createHash29("sha256").update(deletion.body).digest("hex");
63405
64026
  sourceHashes[relativeSource] = beforeHash;
63406
64027
  sourcePlansByPath.set(relativeSource, {
63407
64028
  path: relativeSource,
@@ -63427,13 +64048,13 @@ async function executeDelete(input, context) {
63427
64048
  const relativeShard = path53.relative(context.workspaceRoot, shardPath).replaceAll("\\", "/");
63428
64049
  const original = readFileSync29(shardPath, "utf8");
63429
64050
  const body = renderShardWithout(original, selectors, shardPath);
63430
- const beforeHash = createHash28("sha256").update(original).digest("hex");
64051
+ const beforeHash = createHash29("sha256").update(original).digest("hex");
63431
64052
  sourceHashes[relativeShard] = beforeHash;
63432
64053
  sourcePlansByPath.set(relativeShard, {
63433
64054
  path: relativeShard,
63434
64055
  mode: "write",
63435
64056
  beforeHash,
63436
- afterHash: createHash28("sha256").update(body).digest("hex"),
64057
+ afterHash: createHash29("sha256").update(body).digest("hex"),
63437
64058
  body
63438
64059
  });
63439
64060
  }
@@ -63447,7 +64068,7 @@ async function executeDelete(input, context) {
63447
64068
  sourceWrites: sourcePlans,
63448
64069
  supersessionRequired
63449
64070
  };
63450
- const planHash = createHash28("sha256").update(JSON.stringify(planBody)).digest("hex");
64071
+ const planHash = createHash29("sha256").update(JSON.stringify(planBody)).digest("hex");
63451
64072
  const payload = {
63452
64073
  deleted: 0,
63453
64074
  skipped: authoredIds.length,
@@ -64381,7 +65002,7 @@ var init_symbols_coordinator2 = __esm(() => {
64381
65002
  });
64382
65003
 
64383
65004
  // ../cli/dist/operations/mutation/symbol-refresh.js
64384
- import { createHash as createHash29, randomUUID as randomUUID5 } from "node:crypto";
65005
+ import { createHash as createHash30, randomUUID as randomUUID5 } from "node:crypto";
64385
65006
  import { existsSync as existsSync29, readFileSync as readFileSync33, renameSync as renameSync7, unlinkSync as unlinkSync8, writeFileSync as writeFileSync11 } from "node:fs";
64386
65007
  import * as path58 from "node:path";
64387
65008
  function record2(value) {
@@ -64408,7 +65029,7 @@ function sourceTextFor(sourceFile, workspaceRoot) {
64408
65029
  }
64409
65030
  }
64410
65031
  function contentHash2(content) {
64411
- return createHash29("sha256").update(content).digest("hex");
65032
+ return createHash30("sha256").update(content).digest("hex");
64412
65033
  }
64413
65034
  function restoreOrUnlinkCoordinateArtifact(targetPath, before) {
64414
65035
  if (before === null) {
@@ -64608,7 +65229,7 @@ async function scenarioCoverageWarnings(prolog, relationships, entityType, entit
64608
65229
  var init_warnings = () => {};
64609
65230
 
64610
65231
  // ../cli/dist/operations/mutation/upsert.js
64611
- import { createHash as createHash30 } from "node:crypto";
65232
+ import { createHash as createHash31 } from "node:crypto";
64612
65233
  import { existsSync as existsSync30, readFileSync as readFileSync34, unlinkSync as unlinkSync9, writeFileSync as writeFileSync12 } from "node:fs";
64613
65234
  import path59 from "node:path";
64614
65235
  function requireProlog4(context) {
@@ -64629,10 +65250,10 @@ function parseChangeKind(value) {
64629
65250
  function fileHash2(pathname) {
64630
65251
  if (!existsSync30(pathname))
64631
65252
  return null;
64632
- return createHash30("sha256").update(readFileSync34(pathname)).digest("hex");
65253
+ return createHash31("sha256").update(readFileSync34(pathname)).digest("hex");
64633
65254
  }
64634
65255
  function textHash(value) {
64635
- return createHash30("sha256").update(value).digest("hex");
65256
+ return createHash31("sha256").update(value).digest("hex");
64636
65257
  }
64637
65258
  function assertRelationshipShardContained(workspaceRoot, shardPath) {
64638
65259
  const root = path59.resolve(workspaceRoot);
@@ -66926,7 +67547,7 @@ var init_staging = __esm(() => {
66926
67547
 
66927
67548
  // ../cli/dist/commands/sync.js
66928
67549
  import { execSync as execSync4 } from "node:child_process";
66929
- import { createHash as createHash31 } from "node:crypto";
67550
+ import { createHash as createHash32 } from "node:crypto";
66930
67551
  import { existsSync as existsSync34, mkdirSync as mkdirSync12, readFileSync as readFileSync37, readdirSync as readdirSync9, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "node:fs";
66931
67552
  import * as path64 from "node:path";
66932
67553
  function relationshipKey2(relationship) {
@@ -66999,14 +67620,14 @@ function trackedRelationshipFiles(workspaceRoot, relationshipsDir, recoverMissin
66999
67620
  receiptPath,
67000
67621
  path: relative,
67001
67622
  afterHash: receipt.afterHash,
67002
- rawHash: createHash31("sha256").update(readFileSync37(receiptPath)).digest("hex")
67623
+ rawHash: createHash32("sha256").update(readFileSync37(receiptPath)).digest("hex")
67003
67624
  });
67004
67625
  }
67005
67626
  continue;
67006
67627
  }
67007
67628
  throw new SyncError(`Pending source is missing: ${relative}.${PENDING_SOURCE_RECOVERY_HINT}`);
67008
67629
  }
67009
- const actual = createHash31("sha256").update(readFileSync37(absolute)).digest("hex");
67630
+ const actual = createHash32("sha256").update(readFileSync37(absolute)).digest("hex");
67010
67631
  if (actual !== receipt.afterHash) {
67011
67632
  throw new SyncError(`Pending source hash drift blocks sync for ${relative}`);
67012
67633
  }
@@ -67867,14 +68488,14 @@ var init_sync = __esm(() => {
67867
68488
  });
67868
68489
 
67869
68490
  // ../cli/dist/commands/branch.js
67870
- import { createHash as createHash32 } from "node:crypto";
68491
+ import { createHash as createHash33 } from "node:crypto";
67871
68492
  import * as fs8 from "node:fs";
67872
68493
  import * as path65 from "node:path";
67873
68494
  function isSupportedLegacyBranchMigration(from, to) {
67874
68495
  return from === to;
67875
68496
  }
67876
68497
  function hashLegacyStore(sourcePath) {
67877
- const hash = createHash32("sha256");
68498
+ const hash = createHash33("sha256");
67878
68499
  const visit = (directory, relative = "") => {
67879
68500
  for (const entry of fs8.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
67880
68501
  const absolute = path65.join(directory, entry.name);
@@ -67894,7 +68515,7 @@ function hashLegacyStore(sourcePath) {
67894
68515
  }
67895
68516
  function branchMigrationApprovalHash(from, to, sourcePath, targetPath) {
67896
68517
  const sourceHash = hashLegacyStore(sourcePath);
67897
- return createHash32("sha256").update(JSON.stringify({
68518
+ return createHash33("sha256").update(JSON.stringify({
67898
68519
  version: 2,
67899
68520
  from,
67900
68521
  to,
@@ -68187,7 +68808,7 @@ var init_branch = __esm(() => {
68187
68808
  });
68188
68809
 
68189
68810
  // ../cli/dist/operations/migration/inventory-rederive.js
68190
- import { createHash as createHash33 } from "node:crypto";
68811
+ import { createHash as createHash34 } from "node:crypto";
68191
68812
  import path66 from "node:path";
68192
68813
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
68193
68814
  function isRecord17(value) {
@@ -68404,7 +69025,7 @@ function planOne(workspaceRoot, relativePath, entity, extracted, shards, facts)
68404
69025
  summary: summarize2(changes, inventory),
68405
69026
  changes,
68406
69027
  contract,
68407
- contractHash: createHash33("sha256").update(canonicalJson3(contract)).digest("hex")
69028
+ contractHash: createHash34("sha256").update(canonicalJson3(contract)).digest("hex")
68408
69029
  };
68409
69030
  }
68410
69031
  function readShardRelationships(workspaceRoot) {
@@ -68977,7 +69598,7 @@ ${KIBI_BIN_RESOLVER}
68977
69598
  });
68978
69599
 
68979
69600
  // ../cli/dist/commands/legacy-storage-migration.js
68980
- import { createHash as createHash34 } from "node:crypto";
69601
+ import { createHash as createHash35 } from "node:crypto";
68981
69602
  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
69603
  import * as path70 from "node:path";
68983
69604
  function stripGlobToRoot(pattern) {
@@ -69163,7 +69784,7 @@ function rewritePendingSourceReceiptPaths(cwd, moves) {
69163
69784
  const absolute = path70.resolve(cwd, destination);
69164
69785
  if (!existsSync37(absolute))
69165
69786
  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");
69787
+ const afterHash = typeof receipt.afterHash === "string" && /^[a-f0-9]{64}$/i.test(receipt.afterHash) ? receipt.afterHash : createHash35("sha256").update(readFileSync40(absolute)).digest("hex");
69167
69788
  writeFileSync18(receiptPath, `${JSON.stringify({ ...receipt, path: destination, afterHash }, null, 2)}
69168
69789
  `, "utf8");
69169
69790
  rewritten += 1;
@@ -69900,7 +70521,7 @@ var init_ignore_policy = __esm(() => {
69900
70521
 
69901
70522
  // ../cli/dist/public/operations/node-ports.js
69902
70523
  import { execFile as execFile2, execFileSync as execFileSync8 } from "node:child_process";
69903
- import { createHash as createHash35 } from "node:crypto";
70524
+ import { createHash as createHash36 } from "node:crypto";
69904
70525
  import * as fsSync from "node:fs";
69905
70526
  import * as fs9 from "node:fs/promises";
69906
70527
  import path72 from "node:path";
@@ -69930,7 +70551,7 @@ async function workspaceSnapshot(workspaceRoot) {
69930
70551
  "-z"
69931
70552
  ], { encoding: "buffer", maxBuffer: 64 * 1024 * 1024 });
69932
70553
  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");
70554
+ const digest = createHash36("sha256");
69934
70555
  digest.update("kibi.workspace-snapshot.v2\x00");
69935
70556
  for (const { raw, key: relativePath } of paths) {
69936
70557
  digest.update(relativePath);
@@ -70321,7 +70942,7 @@ var init_cli_runtime = __esm(() => {
70321
70942
  });
70322
70943
 
70323
70944
  // ../cli/dist/public/operations/legacy-migration-plan.js
70324
- import { createHash as createHash36 } from "node:crypto";
70945
+ import { createHash as createHash37 } from "node:crypto";
70325
70946
  function isRecord20(value) {
70326
70947
  return value !== null && typeof value === "object" && !Array.isArray(value);
70327
70948
  }
@@ -70356,7 +70977,7 @@ function stableStringify(value) {
70356
70977
  return JSON.stringify(stableValue3(value));
70357
70978
  }
70358
70979
  function hash(value) {
70359
- return createHash36("sha256").update(stableStringify(value)).digest("hex");
70980
+ return createHash37("sha256").update(stableStringify(value)).digest("hex");
70360
70981
  }
70361
70982
  function migrationRepairBatches(plan) {
70362
70983
  const seen = new Set;
@@ -70678,7 +71299,7 @@ var init_legacy_migration_plan = __esm(() => {
70678
71299
  });
70679
71300
 
70680
71301
  // ../cli/dist/public/operations/repair-plan.js
70681
- import { createHash as createHash37 } from "node:crypto";
71302
+ import { createHash as createHash38 } from "node:crypto";
70682
71303
  function isRecord21(value) {
70683
71304
  return value !== null && typeof value === "object" && !Array.isArray(value);
70684
71305
  }
@@ -70821,7 +71442,7 @@ function buildRepairPlan(payload, input, codeSnapshot) {
70821
71442
  },
70822
71443
  batches
70823
71444
  };
70824
- const planId = `repair-plan-${createHash37("sha256").update(stableStringify2(planCore)).digest("hex").slice(0, 24)}`;
71445
+ const planId = `repair-plan-${createHash38("sha256").update(stableStringify2(planCore)).digest("hex").slice(0, 24)}`;
70825
71446
  return {
70826
71447
  version: REPAIR_PLAN_VERSION,
70827
71448
  planId,
@@ -73923,7 +74544,7 @@ var init_migrate = __esm(() => {
73923
74544
  });
73924
74545
 
73925
74546
  // ../cli/dist/operations/planning/compile-intent.js
73926
- import { createHash as createHash38 } from "node:crypto";
74547
+ import { createHash as createHash39 } from "node:crypto";
73927
74548
  import path77 from "node:path";
73928
74549
  function compilePlanHash(plan) {
73929
74550
  const { planHash: _ignored, ...body } = plan;
@@ -73933,7 +74554,7 @@ function text7(value) {
73933
74554
  return typeof value === "string" ? value.trim() : "";
73934
74555
  }
73935
74556
  function hash2(value) {
73936
- return createHash38("sha256").update(canonicalize(value)).digest("hex");
74557
+ return createHash39("sha256").update(canonicalize(value)).digest("hex");
73937
74558
  }
73938
74559
  function shortHash2(value, length = 8) {
73939
74560
  return hash2(value).slice(0, length);
@@ -74080,7 +74701,7 @@ async function sourceHashes(context, locations) {
74080
74701
  for (const relative of sourceFilesFor(locations)) {
74081
74702
  try {
74082
74703
  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");
74704
+ hashes[relative] = contents === null ? null : createHash39("sha256").update(contents).digest("hex");
74084
74705
  } catch {
74085
74706
  hashes[relative] = null;
74086
74707
  }
@@ -74665,7 +75286,7 @@ var init_compile_intent = __esm(() => {
74665
75286
  });
74666
75287
 
74667
75288
  // ../cli/dist/operations/planning/apply-plan.js
74668
- import { createHash as createHash39, randomUUID as randomUUID6 } from "node:crypto";
75289
+ import { createHash as createHash40, randomUUID as randomUUID6 } from "node:crypto";
74669
75290
  import path78 from "node:path";
74670
75291
  function text8(value) {
74671
75292
  return typeof value === "string" ? value.trim() : "";
@@ -74673,8 +75294,8 @@ function text8(value) {
74673
75294
  function isRecord23(value) {
74674
75295
  return value !== null && typeof value === "object" && !Array.isArray(value);
74675
75296
  }
74676
- function digest3(value) {
74677
- return createHash39("sha256").update(value).digest("hex");
75297
+ function digest4(value) {
75298
+ return createHash40("sha256").update(value).digest("hex");
74678
75299
  }
74679
75300
  function relationships(step) {
74680
75301
  if (!Array.isArray(step.relationships))
@@ -74838,7 +75459,7 @@ function validateEntityDeletionPlan(args) {
74838
75459
  throw new Error("Entity deletion apply failed: approvedPlanHash does not match planHash");
74839
75460
  }
74840
75461
  const { planHash: _ignored, ...body } = args.plan;
74841
- if (createHash39("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
75462
+ if (createHash40("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
74842
75463
  throw new Error("Entity deletion apply failed: planHash does not match the canonical plan body");
74843
75464
  }
74844
75465
  if (!Array.isArray(args.plan.entityIds) || args.plan.entityIds.length === 0) {
@@ -74917,7 +75538,7 @@ async function validateSources(context, sourceHashes) {
74917
75538
  throw new Error("Apply plan failed: source hashes require a filesystem-capable runtime");
74918
75539
  let actual;
74919
75540
  try {
74920
- actual = digest3(await context.fs.readFile(path78.join(context.workspaceRoot, relative)));
75541
+ actual = digest4(await context.fs.readFile(path78.join(context.workspaceRoot, relative)));
74921
75542
  } catch {
74922
75543
  actual = null;
74923
75544
  }
@@ -74946,12 +75567,12 @@ async function resolveSourceWriteTarget(workspaceRoot, fsPort, write) {
74946
75567
  const existing = await fsPort.readFile(absolute).catch(() => {
74947
75568
  return;
74948
75569
  });
74949
- const beforeHash = existing === undefined ? null : digest3(existing);
75570
+ const beforeHash = existing === undefined ? null : digest4(existing);
74950
75571
  if (beforeHash !== write.beforeHash) {
74951
75572
  throw new Error(`Apply plan failed: source hash changed for ${write.path}`);
74952
75573
  }
74953
75574
  const mode = write.mode ?? "write";
74954
- if (mode === "write" && (write.body === undefined || write.afterHash === null || digest3(write.body) !== write.afterHash)) {
75575
+ if (mode === "write" && (write.body === undefined || write.afterHash === null || digest4(write.body) !== write.afterHash)) {
74955
75576
  throw new Error(`Apply plan failed: afterHash does not match staged body for ${write.path}`);
74956
75577
  }
74957
75578
  if (mode === "delete" && write.afterHash !== null) {
@@ -74995,7 +75616,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
74995
75616
  for (const entry of prior.entries) {
74996
75617
  try {
74997
75618
  const current = await context.fs.readFile(path78.resolve(context.workspaceRoot, entry.path));
74998
- if (entry.afterHash === null || digest3(current) !== entry.afterHash) {
75619
+ if (entry.afterHash === null || digest4(current) !== entry.afterHash) {
74999
75620
  allAfter = false;
75000
75621
  }
75001
75622
  } catch {
@@ -75023,7 +75644,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
75023
75644
  } catch {
75024
75645
  current = undefined;
75025
75646
  }
75026
- const currentHash = current === undefined ? null : digest3(current);
75647
+ const currentHash = current === undefined ? null : digest4(current);
75027
75648
  if (currentHash !== entry.beforeHash && currentHash !== entry.afterHash) {
75028
75649
  throw new Error(`Apply plan recovery refused: ${entry.path} changed outside its journal`);
75029
75650
  }
@@ -77915,7 +78536,7 @@ init_generate();
77915
78536
  init_presentation();
77916
78537
  init_ir();
77917
78538
  // src/skill-operations.ts
77918
- import { createHash as createHash40 } from "node:crypto";
78539
+ import { createHash as createHash41 } from "node:crypto";
77919
78540
 
77920
78541
  // src/skill-system/loader.ts
77921
78542
  import { existsSync as existsSync44, readFileSync as readFileSync47, readdirSync as readdirSync13, realpathSync as realpathSync10 } from "node:fs";
@@ -78187,7 +78808,7 @@ async function executeSkillsLoad2(input, _context) {
78187
78808
  assertNonEmptyString2(id, "id");
78188
78809
  const bundle = loadBundledSkill2(id);
78189
78810
  const resources = bundle.manifest.resources ?? [];
78190
- const contentHash = createHash40("sha256").update(bundle.body, "utf8").digest("hex");
78811
+ const contentHash = createHash41("sha256").update(bundle.body, "utf8").digest("hex");
78191
78812
  const resourceList = resources.length === 0 ? "none" : resources.join(", ");
78192
78813
  return {
78193
78814
  content: [