kibi-runtime 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -565,7 +565,7 @@ var init_contracts = __esm(() => {
565
565
  "resolve_schema_reference",
566
566
  "record_ontology_gap",
567
567
  "review_nonlogical",
568
- "already_grounded"
568
+ "replace_grounding"
569
569
  ]
570
570
  },
571
571
  recommendedPredicateSchema: { type: ["object", "null"] },
@@ -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
  }
@@ -27446,6 +27451,205 @@ function claimFor(statement, source, confidence, provenance) {
27446
27451
  provenance
27447
27452
  };
27448
27453
  }
27454
+ function snakeSegment(value) {
27455
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
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
+ }
27497
+ function resolveBootstrapSubjectKey(subject, propertyKey, component) {
27498
+ const stripped = subject.trim().replace(/^(?:the|a|an)\s+/i, "");
27499
+ let key;
27500
+ try {
27501
+ key = normalizeSubjectKey2(stripped);
27502
+ } catch {
27503
+ return { ok: false, reason: `subject "${subject}" has no usable words` };
27504
+ }
27505
+ if (isConventionalSubjectKey(key))
27506
+ return { ok: true, subjectKey: key };
27507
+ const flat = key.replace(/\./g, "_");
27508
+ const comp = component === undefined ? "" : snakeSegment(component);
27509
+ const property = snakeSegment(propertyKey);
27510
+ if (comp) {
27511
+ const aspect = flat === comp ? property : flat.startsWith(`${comp}_`) ? flat.slice(comp.length + 1) : flat;
27512
+ return withShortAspect(comp, aspect);
27513
+ }
27514
+ if (/^[a-z][a-z0-9]*$/.test(flat) && property)
27515
+ return withShortAspect(flat, property);
27516
+ return {
27517
+ ok: false,
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
+ var MAX_ASPECT_WORDS = 4, MAX_ASPECT_CHARS = 40, ASPECT_BOUNDARY_WORDS, ASPECT_STOP_WORDS;
27570
+ var init_requirement_claims = __esm(() => {
27571
+ init_strict_modeling();
27572
+ ASPECT_BOUNDARY_WORDS = new Set([
27573
+ "when",
27574
+ "whenever",
27575
+ "while",
27576
+ "if",
27577
+ "unless",
27578
+ "without",
27579
+ "after",
27580
+ "before",
27581
+ "until",
27582
+ "because",
27583
+ "once",
27584
+ "as",
27585
+ "by",
27586
+ "via",
27587
+ "during",
27588
+ "except",
27589
+ "so",
27590
+ "that",
27591
+ "which",
27592
+ "who",
27593
+ "where",
27594
+ "since",
27595
+ "for",
27596
+ "and",
27597
+ "or"
27598
+ ]);
27599
+ ASPECT_STOP_WORDS = new Set([
27600
+ "a",
27601
+ "an",
27602
+ "the",
27603
+ "be",
27604
+ "is",
27605
+ "are",
27606
+ "was",
27607
+ "were",
27608
+ "been",
27609
+ "being",
27610
+ "to",
27611
+ "of",
27612
+ "in",
27613
+ "on",
27614
+ "at",
27615
+ "with",
27616
+ "from",
27617
+ "into",
27618
+ "its",
27619
+ "it",
27620
+ "their",
27621
+ "they",
27622
+ "them",
27623
+ "this",
27624
+ "these",
27625
+ "those",
27626
+ "can",
27627
+ "could",
27628
+ "will",
27629
+ "would",
27630
+ "may",
27631
+ "might",
27632
+ "must",
27633
+ "shall",
27634
+ "should",
27635
+ "do",
27636
+ "does",
27637
+ "has",
27638
+ "have",
27639
+ "had",
27640
+ "any",
27641
+ "all",
27642
+ "each",
27643
+ "every",
27644
+ "some",
27645
+ "only",
27646
+ "also",
27647
+ "then",
27648
+ "than",
27649
+ "not",
27650
+ "no"
27651
+ ]);
27652
+ });
27449
27653
 
27450
27654
  // ../cli/dist/operations/bootstrap/markdown-candidates.js
27451
27655
  import path25 from "node:path";
@@ -27501,7 +27705,20 @@ function genericHeadingCandidate(item, heading, line, existingIds, minConfidence
27501
27705
  ]
27502
27706
  };
27503
27707
  }
27504
- function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence) {
27708
+ function markdownComponentHint(relativePath) {
27709
+ const segments = relativePath.split(/[\\/]+/).filter((segment) => segment.length > 0);
27710
+ const stem = (segments.pop() ?? "").replace(/\.[^.]*$/, "");
27711
+ for (const name of [stem, ...segments.reverse()]) {
27712
+ const normalized = name.toLowerCase().replace(/^[._-]+/, "");
27713
+ if (!/^[a-z]/.test(normalized))
27714
+ continue;
27715
+ if (GENERIC_DOCUMENT_NAMES.has(normalized))
27716
+ continue;
27717
+ return normalized;
27718
+ }
27719
+ return;
27720
+ }
27721
+ function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics) {
27505
27722
  const relativePath = item.relativePath ?? item.label;
27506
27723
  const base = /\bshall\b/i.test(statement) ? 0.86 : /\bmust\b/i.test(statement) ? 0.84 : 0.78;
27507
27724
  const confidence = Math.min(0.95, base + (heading && /requirements?|constraints?|polic(?:y|ies)|rules?/i.test(heading) ? 0.08 : 0));
@@ -27510,9 +27727,17 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27510
27727
  const claim = claimFor(statement, relativePath, confidence, `${relativePath}#L${line}`);
27511
27728
  if (!claim)
27512
27729
  return null;
27513
- const writeSet = buildStrictWriteSet({ claim, statement });
27730
+ const planned = planSubjectKey(claim.subjectKey, claim.propertyKey, markdownComponentHint(relativePath), subjectKeys, `${relativePath}#L${line}`);
27731
+ const subjectKey = planned.resolution;
27732
+ if (!subjectKey.ok)
27733
+ throw new Error(`${subjectKey.reason}; author it with kb_model and a component.aspect subjectKey`);
27734
+ const writeSet = buildStrictWriteSet({
27735
+ claim: { ...claim, subjectKey: subjectKey.subjectKey },
27736
+ statement
27737
+ });
27514
27738
  if (!writeSet.isStrict)
27515
27739
  return null;
27740
+ diagnostics.push(...planned.diagnostics);
27516
27741
  return {
27517
27742
  candidateId: `norm:${writeSet.req.id.toLowerCase()}`,
27518
27743
  entityType: "req",
@@ -27533,7 +27758,7 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
27533
27758
  applyPlan: strictPlan(writeSet).map((step) => step.type === "req" ? withDocumentBody(step, provenanceRequirementBody(statement, bootstrapProvenance("generic markdown", `${relativePath} line ${line}${heading ? ` under the heading "${heading}"` : ""}`, confidence), `${relativePath}#L${line}`)) : step)
27534
27759
  };
27535
27760
  }
27536
- function markdownCandidates(item, existingIds, minConfidence) {
27761
+ function markdownCandidates(item, existingIds, minConfidence, subjectKeys) {
27537
27762
  const candidates = [];
27538
27763
  const sourceOnlySignals = [];
27539
27764
  const diagnostics = [];
@@ -27562,7 +27787,7 @@ function markdownCandidates(item, existingIds, minConfidence) {
27562
27787
  if (!statement || !/\b(must|shall|should)\b/i.test(statement))
27563
27788
  continue;
27564
27789
  try {
27565
- const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence);
27790
+ const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics);
27566
27791
  if (candidate) {
27567
27792
  if (!candidate.applyPlan.some((payload) => payload.type === "req" && existingIds.has(String(payload.id))))
27568
27793
  candidates.push(candidate);
@@ -27589,9 +27814,33 @@ function markdownCandidates(item, existingIds, minConfidence) {
27589
27814
  }
27590
27815
  return { candidates, sourceOnlySignals, diagnostics, suppressed: [] };
27591
27816
  }
27817
+ var GENERIC_DOCUMENT_NAMES;
27592
27818
  var init_markdown_candidates = __esm(() => {
27593
27819
  init_strict_modeling();
27594
27820
  init_candidate_helpers();
27821
+ init_requirement_claims();
27822
+ GENERIC_DOCUMENT_NAMES = new Set([
27823
+ "readme",
27824
+ "index",
27825
+ "overview",
27826
+ "requirements",
27827
+ "requirement",
27828
+ "spec",
27829
+ "specs",
27830
+ "specification",
27831
+ "notes",
27832
+ "changelog",
27833
+ "contributing",
27834
+ "agents",
27835
+ "claude",
27836
+ "todo",
27837
+ "docs",
27838
+ "doc",
27839
+ "documentation",
27840
+ "src",
27841
+ "packages",
27842
+ "github"
27843
+ ]);
27595
27844
  });
27596
27845
 
27597
27846
  // ../cli/dist/operations/bootstrap/candidates.js
@@ -27669,7 +27918,7 @@ function providerCandidate(item, existingIds, minConfidence) {
27669
27918
  }
27670
27919
  ];
27671
27920
  }
27672
- function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown) {
27921
+ function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown, subjectKeys) {
27673
27922
  const candidates = [];
27674
27923
  const sourceOnlySignals = [];
27675
27924
  const diagnostics = [];
@@ -27692,7 +27941,7 @@ function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeG
27692
27941
  }
27693
27942
  }
27694
27943
  if (item.kind === "generic_markdown" && includeGenericMarkdown) {
27695
- const built = markdownCandidates(item, existingIds, minConfidence);
27944
+ const built = markdownCandidates(item, existingIds, minConfidence, subjectKeys);
27696
27945
  candidates.push(...built.candidates);
27697
27946
  sourceOnlySignals.push(...built.sourceOnlySignals);
27698
27947
  diagnostics.push(...built.diagnostics);
@@ -27940,13 +28189,13 @@ var init_discovery_evidence = __esm(() => {
27940
28189
  });
27941
28190
 
27942
28191
  // ../cli/dist/operations/bootstrap/types.js
27943
- import { createHash as createHash15 } from "node:crypto";
28192
+ import { createHash as createHash16 } from "node:crypto";
27944
28193
  function bootstrapPlanHash(plan) {
27945
28194
  const { planHash: _ignored, ...body } = plan;
27946
- return createHash15("sha256").update(canonicalize(body)).digest("hex");
28195
+ return createHash16("sha256").update(canonicalize(body)).digest("hex");
27947
28196
  }
27948
28197
  function bootstrapEmptyKbSnapshotId(input) {
27949
- return `empty-source-state-${createHash15("sha256").update(canonicalize({
28198
+ return `empty-source-state-${createHash16("sha256").update(canonicalize({
27950
28199
  version: "kibi.empty-source-state.v1",
27951
28200
  branch: input.branch,
27952
28201
  workspaceSnapshot: input.workspaceSnapshot,
@@ -28083,7 +28332,7 @@ var init_discovery = __esm(() => {
28083
28332
  });
28084
28333
 
28085
28334
  // ../cli/dist/operations/bootstrap/intent-claims.js
28086
- import { createHash as createHash16 } from "node:crypto";
28335
+ import { createHash as createHash17 } from "node:crypto";
28087
28336
  function citation(source, claim) {
28088
28337
  return [
28089
28338
  `intent_claim:${source.id}:${claim.reference}`,
@@ -28092,12 +28341,13 @@ function citation(source, claim) {
28092
28341
  ...source.connector ? [`connector:${source.connector}`] : []
28093
28342
  ];
28094
28343
  }
28095
- function digest(...parts) {
28096
- return createHash16("sha256").update(parts.join("\x00")).digest("hex").slice(0, 16).toUpperCase();
28344
+ function digest2(...parts) {
28345
+ return createHash17("sha256").update(parts.join("\x00")).digest("hex").slice(0, 16).toUpperCase();
28097
28346
  }
28098
28347
  function claimBody(source, claim) {
28099
28348
  return renderRequirementBody({
28100
28349
  statement: claim.statement,
28350
+ context: claim.rationale,
28101
28351
  source: {
28102
28352
  excerpt: claim.excerpt,
28103
28353
  title: source.title,
@@ -28108,8 +28358,14 @@ function claimBody(source, claim) {
28108
28358
  function withClaimBody(step, body) {
28109
28359
  return { ...step, document: { body } };
28110
28360
  }
28361
+ function withRationale(step, rationale) {
28362
+ if (!rationale)
28363
+ return step;
28364
+ const properties = step.properties !== null && typeof step.properties === "object" ? step.properties : {};
28365
+ return { ...step, properties: { ...properties, rationale } };
28366
+ }
28111
28367
  function reviewClaimCandidate(source, claim, kind, confidence) {
28112
- const id = `FACT-BOOTSTRAP-${kind === "open_question" ? "QUESTION" : "OBSERVATION"}-${digest(source.id, claim.reference, claim.statement)}`;
28368
+ const id = `FACT-BOOTSTRAP-${kind === "open_question" ? "QUESTION" : "OBSERVATION"}-${digest2(source.id, claim.reference, claim.statement)}`;
28113
28369
  const entity = {
28114
28370
  type: "fact",
28115
28371
  id,
@@ -28154,7 +28410,7 @@ function conflictCandidates(declared, sources, existingIds, minConfidence) {
28154
28410
  diagnostics.push(`Conflict "${conflict.note}" cites claim(s) not declared in intentClaims: ${undeclared.map((ref) => `${ref.sourceId}:${ref.reference}`).join(", ")}; declare them or correct the reference.`);
28155
28411
  continue;
28156
28412
  }
28157
- const id = `FACT-BOOTSTRAP-CONFLICT-${digest(...[...refs].sort(), conflict.note)}`;
28413
+ const id = `FACT-BOOTSTRAP-CONFLICT-${digest2(...[...refs].sort(), conflict.note)}`;
28158
28414
  const candidateId = `conflict:${id.toLowerCase()}`;
28159
28415
  if (confidence < minConfidence) {
28160
28416
  suppressed.push({
@@ -28229,7 +28485,7 @@ ${evidenceLines.join(`
28229
28485
  }
28230
28486
  return { candidates, sourceOnlySignals: [], suppressed, diagnostics };
28231
28487
  }
28232
- function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
28488
+ function buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys) {
28233
28489
  const sources = new Map((declared.knowledgeSources ?? []).map((source) => [source.id, source]));
28234
28490
  const candidates = [];
28235
28491
  const sourceOnlySignals = [];
@@ -28285,9 +28541,17 @@ function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
28285
28541
  let writeSet = null;
28286
28542
  try {
28287
28543
  const modeled = claimFor(claim.statement, `knowledge-source:${source.id}`, confidence, provenance);
28288
- writeSet = modeled ? buildStrictWriteSet({ claim: modeled, statement: claim.statement }) : null;
28544
+ const planned = modeled ? planSubjectKey(modeled.subjectKey, modeled.propertyKey, claim.component ?? source.component, subjectKeys, `${source.id}:${claim.reference}`) : null;
28545
+ const subjectKey = planned?.resolution ?? null;
28546
+ diagnostics.push(...planned?.diagnostics ?? []);
28547
+ writeSet = modeled && subjectKey?.ok ? buildStrictWriteSet({
28548
+ claim: { ...modeled, subjectKey: subjectKey.subjectKey },
28549
+ statement: claim.statement
28550
+ }) : null;
28289
28551
  if (!modeled)
28290
28552
  diagnostics.push(`Intent claim needs authoring at ${source.id}:${claim.reference}: ${claim.statement}`);
28553
+ else if (subjectKey && !subjectKey.ok)
28554
+ diagnostics.push(`Intent claim at ${source.id}:${claim.reference} was not planned as a requirement: its ${subjectKey.reason}. Declare component on the claim or on knowledge source ${source.id} and re-plan, or author it with kb_model: ${claim.statement}`);
28291
28555
  } catch (error) {
28292
28556
  diagnostics.push(`Intent claim extraction failed at ${source.id}:${claim.reference}: ${error instanceof Error ? error.message : String(error)}`);
28293
28557
  }
@@ -28306,7 +28570,7 @@ function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
28306
28570
  from,
28307
28571
  to
28308
28572
  })),
28309
- applyPlan: strictPlan(writeSet).map((step) => step.type === "req" ? withClaimBody(step, claimBody(source, claim)) : step)
28573
+ applyPlan: strictPlan(writeSet).map((step) => step.type === "req" ? withClaimBody(withRationale(step, claim.rationale), claimBody(source, claim)) : step)
28310
28574
  });
28311
28575
  continue;
28312
28576
  }
@@ -28347,6 +28611,7 @@ var init_intent_claims = __esm(() => {
28347
28611
  init_entity_body_context();
28348
28612
  init_strict_modeling();
28349
28613
  init_candidate_helpers();
28614
+ init_requirement_claims();
28350
28615
  AUTHORITY_CONFIDENCE = {
28351
28616
  authoritative: 0.9,
28352
28617
  supporting: 0.82
@@ -28554,6 +28819,7 @@ function knowledgeSources(values) {
28554
28819
  seen.add(id);
28555
28820
  const connector = value.connector?.trim();
28556
28821
  const notes = value.notes?.trim();
28822
+ const component = value.component?.trim();
28557
28823
  result.push({
28558
28824
  id,
28559
28825
  kind: value.kind,
@@ -28561,7 +28827,8 @@ function knowledgeSources(values) {
28561
28827
  locator,
28562
28828
  authority: value.authority,
28563
28829
  ...connector ? { connector } : {},
28564
- ...notes ? { notes } : {}
28830
+ ...notes ? { notes } : {},
28831
+ ...component ? { component } : {}
28565
28832
  });
28566
28833
  }
28567
28834
  return result;
@@ -28583,11 +28850,15 @@ function intentClaims(values) {
28583
28850
  continue;
28584
28851
  seen.add(key);
28585
28852
  const excerpt = value.excerpt?.trim();
28853
+ const component = value.component?.trim();
28854
+ const rationale = value.rationale?.trim().replace(/\s+/g, " ");
28586
28855
  result.push({
28587
28856
  statement,
28588
28857
  sourceId,
28589
28858
  reference,
28590
28859
  ...excerpt ? { excerpt } : {},
28860
+ ...component ? { component } : {},
28861
+ ...rationale ? { rationale } : {},
28591
28862
  ...kind !== "intent" ? { kind } : {}
28592
28863
  });
28593
28864
  }
@@ -30109,7 +30380,6 @@ var init_analysis_receipt = __esm(() => {
30109
30380
  // ../cli/dist/operations/semantic-advisor/observation-plan.js
30110
30381
  function observationPlan(payload, title, tags) {
30111
30382
  const factId = `FACT-OBS-${shortHash(`${stringValue2(payload.id)}.${title}.${statementOf(payload)}`)}`;
30112
- 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;
30113
30383
  return [
30114
30384
  {
30115
30385
  type: "fact",
@@ -30122,7 +30392,13 @@ function observationPlan(payload, title, tags) {
30122
30392
  text_ref: statementOf(payload),
30123
30393
  tags
30124
30394
  },
30125
- relationships: target === undefined ? [] : [relationship(factId, target, "relates_to")]
30395
+ document: {
30396
+ body: `${title}. The semantic advisor suggested this review observation because the prose below is not safely grounded by a typed fact; it stays open until a reviewer models, rewords or dismisses the claim.
30397
+
30398
+ > ${statementOf(payload).trim().replace(/\n/g, `
30399
+ > `)}`
30400
+ },
30401
+ relationships: []
30126
30402
  }
30127
30403
  ];
30128
30404
  }
@@ -30907,7 +31183,7 @@ var init_validation2 = __esm(() => {
30907
31183
  });
30908
31184
 
30909
31185
  // ../cli/dist/operations/bootstrap/generate.js
30910
- import { createHash as createHash17 } from "node:crypto";
31186
+ import { createHash as createHash18 } from "node:crypto";
30911
31187
  import path31 from "node:path";
30912
31188
  function filterSourceOnlySignals(signals, entityTypes) {
30913
31189
  return entityTypes && entityTypes.length > 0 ? signals.filter((signal) => entityTypes.includes(signal.kind)) : signals;
@@ -30926,7 +31202,7 @@ function firstUpsertId(candidate) {
30926
31202
  return properties !== null && typeof properties === "object" && "id" in properties && typeof properties.id === "string" ? properties.id : "";
30927
31203
  }
30928
31204
  function sha2562(value) {
30929
- return createHash17("sha256").update(value).digest("hex");
31205
+ return createHash18("sha256").update(value).digest("hex");
30930
31206
  }
30931
31207
  function selectBootstrapCandidates(input, existingIds, entityTypes, maximum) {
30932
31208
  const allowed = entityTypes?.length ? new Set(entityTypes) : null;
@@ -31141,13 +31417,14 @@ async function executePlanBootstrap(args, context) {
31141
31417
  })
31142
31418
  ]);
31143
31419
  const bound = await expectedSnapshots(context, discovery.evidence.map((evidence) => evidence.relativePath).filter((value) => Boolean(value)));
31144
- const built = discovery.activation.allowCandidateGeneration ? buildBootstrapCandidates(discovery.evidence, new Set, minConfidence, includeGenericMarkdown) : {
31420
+ const subjectKeys = new SubjectKeyRegistry;
31421
+ const built = discovery.activation.allowCandidateGeneration ? buildBootstrapCandidates(discovery.evidence, new Set, minConfidence, includeGenericMarkdown, subjectKeys) : {
31145
31422
  candidates: [],
31146
31423
  sourceOnlySignals: [],
31147
31424
  suppressed: [],
31148
31425
  diagnostics: []
31149
31426
  };
31150
- const claimed = discovery.activation.allowCandidateGeneration ? buildIntentClaimCandidates(declared, existingIds, minConfidence) : {
31427
+ const claimed = discovery.activation.allowCandidateGeneration ? buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys) : {
31151
31428
  candidates: [],
31152
31429
  sourceOnlySignals: [],
31153
31430
  suppressed: [],
@@ -31199,6 +31476,7 @@ var init_generate = __esm(() => {
31199
31476
  init_discovery();
31200
31477
  init_intent_claims();
31201
31478
  init_presentation();
31479
+ init_requirement_claims();
31202
31480
  init_validation2();
31203
31481
  init_types();
31204
31482
  });
@@ -31300,7 +31578,12 @@ var init_bootstrap = __esm(() => {
31300
31578
  type: "string",
31301
31579
  description: "Optional name of the MCP server or tool the agent used to read the source."
31302
31580
  },
31303
- notes: { type: "string" }
31581
+ notes: { type: "string" },
31582
+ component: {
31583
+ type: "string",
31584
+ minLength: 1,
31585
+ description: "Optional component this source's claims are about (for example recorder). It becomes the first segment of each generated subject_key (component.aspect); a claim's own component overrides it."
31586
+ }
31304
31587
  }
31305
31588
  }
31306
31589
  },
@@ -31337,6 +31620,16 @@ var init_bootstrap = __esm(() => {
31337
31620
  minLength: 1,
31338
31621
  description: "Verbatim passage from the source the claim came from. Required for intent and observation claims (optional for open_question): Kibi persists it with the knowledge source title and reference in the created entity's '## Source' section, because the ticket or page may be gone by the next migration."
31339
31622
  },
31623
+ component: {
31624
+ type: "string",
31625
+ minLength: 1,
31626
+ description: "Optional component the claim is about (for example recorder). Generated subject keys take the shape component.aspect with the aspect from the claim's subject. Without a component on the claim or its knowledge source, a claim whose subject is not a single word or already dotted becomes an authoring follow-up with a diagnostic instead of a malformed subject key."
31627
+ },
31628
+ rationale: {
31629
+ type: "string",
31630
+ minLength: 1,
31631
+ description: "Optional reason the requirement exists, as the source states it or the human answered when asked. It becomes the requirement's rationale and its body's '## Context' section. Never invent one."
31632
+ },
31340
31633
  kind: {
31341
31634
  type: "string",
31342
31635
  enum: [...INTENT_CLAIM_KINDS],
@@ -31456,7 +31749,7 @@ var init_aggregated_checks = __esm(() => {
31456
31749
  });
31457
31750
 
31458
31751
  // ../cli/dist/traceability/impact-review.js
31459
- import { createHash as createHash18 } from "node:crypto";
31752
+ import { createHash as createHash19 } from "node:crypto";
31460
31753
  function canonicalJson2(value) {
31461
31754
  const active = new Set;
31462
31755
  const encode = (item) => {
@@ -31490,10 +31783,10 @@ function canonicalJson2(value) {
31490
31783
  return encode(value);
31491
31784
  }
31492
31785
  function fingerprint(value) {
31493
- return `sha256:${createHash18("sha256").update(canonicalJson2(value), "utf8").digest("hex")}`;
31786
+ return `sha256:${createHash19("sha256").update(canonicalJson2(value), "utf8").digest("hex")}`;
31494
31787
  }
31495
31788
  function fingerprintBytes(value) {
31496
- return `sha256:${createHash18("sha256").update(value).digest("hex")}`;
31789
+ return `sha256:${createHash19("sha256").update(value).digest("hex")}`;
31497
31790
  }
31498
31791
  function isObject(value) {
31499
31792
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -32123,7 +32416,7 @@ var init_source_classification = __esm(() => {
32123
32416
  });
32124
32417
 
32125
32418
  // ../cli/dist/plugins/source-analysis-service.js
32126
- import { createHash as createHash19 } from "node:crypto";
32419
+ import { createHash as createHash20 } from "node:crypto";
32127
32420
  import * as path33 from "node:path";
32128
32421
  function detectSourceLanguage(filePath) {
32129
32422
  return extensionLanguage(filePath);
@@ -32131,7 +32424,7 @@ function detectSourceLanguage(filePath) {
32131
32424
  function createV2Request(filePath, content, explicitLanguageHint) {
32132
32425
  const classification = classifySource(filePath, content, explicitLanguageHint);
32133
32426
  const language = classification.language;
32134
- const inputFingerprint = createHash19("sha256").update(filePath).update("\x00").update(content).update("\x00").update(classification.kind).update("\x00").update(classification.language).update("\x00").update(explicitLanguageHint?.trim().toLowerCase() ?? "").digest("hex");
32427
+ const inputFingerprint = createHash20("sha256").update(filePath).update("\x00").update(content).update("\x00").update(classification.kind).update("\x00").update(classification.language).update("\x00").update(explicitLanguageHint?.trim().toLowerCase() ?? "").digest("hex");
32135
32428
  return {
32136
32429
  classification,
32137
32430
  input: {
@@ -32293,7 +32586,7 @@ class SourceAnalysisService {
32293
32586
  providerId: entry.capability.id,
32294
32587
  stamp: entry.stamp,
32295
32588
  inputFingerprint: request.inputFingerprint,
32296
- providerFingerprint: this.options.providerFingerprints?.[entry.pluginId] ?? createHash19("sha256").update(`${entry.pluginId}\x00${entry.pluginVersion}\x00${entry.stamp.capability}`).digest("hex"),
32589
+ providerFingerprint: this.options.providerFingerprints?.[entry.pluginId] ?? createHash20("sha256").update(`${entry.pluginId}\x00${entry.pluginVersion}\x00${entry.stamp.capability}`).digest("hex"),
32297
32590
  shadowComparisons: []
32298
32591
  };
32299
32592
  } catch (error) {
@@ -32413,7 +32706,7 @@ var init_source_analysis_service = __esm(() => {
32413
32706
  });
32414
32707
 
32415
32708
  // ../cli/dist/plugins/maintenance-source-analysis.js
32416
- import { createHash as createHash20 } from "node:crypto";
32709
+ import { createHash as createHash21 } from "node:crypto";
32417
32710
  import { readFileSync as readFileSync16, realpathSync as realpathSync5 } from "node:fs";
32418
32711
  import { createRequire as createRequire4 } from "node:module";
32419
32712
  import { dirname as dirname7, isAbsolute as isAbsolute6, join as join13, relative as relative5, resolve as resolve7 } from "node:path";
@@ -32426,7 +32719,7 @@ function digestFile(root, file) {
32426
32719
  const actualRel = relative5(realpathSync5(root), actual);
32427
32720
  if (actualRel.startsWith("..") || isAbsolute6(actualRel))
32428
32721
  throw new Error(`Approved asset escapes its package: ${file}`);
32429
- return createHash20("sha256").update(readFileSync16(actual)).digest("hex");
32722
+ return createHash21("sha256").update(readFileSync16(actual)).digest("hex");
32430
32723
  }
32431
32724
  function verifyFiles(root, files) {
32432
32725
  if (Object.keys(files).length === 0)
@@ -32495,7 +32788,7 @@ function verifyApprovedSourceAnalyzer(root, approval) {
32495
32788
  if (metadata.version !== dependency.version)
32496
32789
  throw new Error(`Unapproved source runtime version: ${dependency.packageName}`);
32497
32790
  }
32498
- return createHash20("sha256").update(JSON.stringify(approval)).digest("hex");
32791
+ return createHash21("sha256").update(JSON.stringify(approval)).digest("hex");
32499
32792
  }
32500
32793
  function createMaintenanceSourceAnalysisService(workspaceRoot, projectConfig) {
32501
32794
  const config = projectConfig ?? readProjectKibiConfig(workspaceRoot);
@@ -40467,7 +40760,7 @@ async function enrichSymbolCoordinatesWithTsMorph(entries, workspaceRoot) {
40467
40760
  var init_symbols_ts = () => {};
40468
40761
 
40469
40762
  // ../cli/dist/traceability/symbol-extract.js
40470
- import { createHash as createHash22 } from "node:crypto";
40763
+ import { createHash as createHash23 } from "node:crypto";
40471
40764
  function createManifestLookupSentinelKey(manifestPath) {
40472
40765
  return `${MANIFEST_SENTINEL_PREFIX}${manifestPath}`;
40473
40766
  }
@@ -40479,7 +40772,7 @@ function getCandidateManifestPaths(filePath) {
40479
40772
  return LOCAL_MANIFEST_NAMES.map((manifestName) => `${dir}/${manifestName}`);
40480
40773
  }
40481
40774
  function createHashFallbackId(filePath, name) {
40482
- const h = createHash22("sha256");
40775
+ const h = createHash23("sha256");
40483
40776
  h.update(`${filePath}:${name}`);
40484
40777
  return h.digest("hex").slice(0, 16);
40485
40778
  }
@@ -40567,7 +40860,7 @@ function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifes
40567
40860
  };
40568
40861
  }
40569
40862
  function computeContentSha(content) {
40570
- const h = createHash22("sha256");
40863
+ const h = createHash23("sha256");
40571
40864
  h.update(content);
40572
40865
  return h.digest("hex");
40573
40866
  }
@@ -51877,7 +52170,7 @@ var require_out4 = __commonJS(function(exports2, module2) {
51877
52170
 
51878
52171
  // ../cli/dist/commands/sync/discovery.js
51879
52172
  import { execFile } from "node:child_process";
51880
- import { createHash as createHash23 } from "node:crypto";
52173
+ import { createHash as createHash24 } from "node:crypto";
51881
52174
  import * as fs5 from "node:fs";
51882
52175
  import { existsSync as existsSync23, readFileSync as readFileSync24, readdirSync as readdirSync6, unlinkSync as unlinkSync4 } from "node:fs";
51883
52176
  import * as path47 from "node:path";
@@ -51912,13 +52205,13 @@ function pendingSourcePaths(cwd, recoverMissing) {
51912
52205
  receiptPath,
51913
52206
  path: relative,
51914
52207
  afterHash: receipt.afterHash,
51915
- rawHash: createHash23("sha256").update(raw).digest("hex")
52208
+ rawHash: createHash24("sha256").update(raw).digest("hex")
51916
52209
  });
51917
52210
  continue;
51918
52211
  }
51919
52212
  throw new Error(`Pending source is missing: ${relative}. If the file was deleted on purpose, run 'kibi branch recover --apply' to rebuild the branch KB from the sources that remain.`);
51920
52213
  }
51921
- const actual = createHash23("sha256").update(readFileSync24(absolute)).digest("hex");
52214
+ const actual = createHash24("sha256").update(readFileSync24(absolute)).digest("hex");
51922
52215
  if (actual !== receipt.afterHash) {
51923
52216
  throw new Error(`Pending source hash drift blocks sync for ${relative}; expected ${receipt.afterHash}, found ${actual}`);
51924
52217
  }
@@ -51966,7 +52259,7 @@ function clearRecoveredPendingSourceReceipts(cwd, receipts) {
51966
52259
  const raw = readTextOrEnoent(absolute);
51967
52260
  if (raw === null)
51968
52261
  continue;
51969
- const rawHash = createHash23("sha256").update(raw).digest("hex");
52262
+ const rawHash = createHash24("sha256").update(raw).digest("hex");
51970
52263
  if (rawHash !== receipt.rawHash) {
51971
52264
  throw new Error(`Pending source receipt changed during recovery for ${receipt.path}; refusing to retire newer receipt`);
51972
52265
  }
@@ -53754,7 +54047,7 @@ function scoreExactPredicates3(schema, text) {
53754
54047
  }
53755
54048
 
53756
54049
  // ../cli/dist/operations/modeling/predicate-utils.js
53757
- import { createHash as createHash24 } from "node:crypto";
54050
+ import { createHash as createHash25 } from "node:crypto";
53758
54051
  function normalizeText2(text) {
53759
54052
  const normalized = String(text ?? "").trim();
53760
54053
  if (!normalized) {
@@ -53778,7 +54071,7 @@ function slug2(value) {
53778
54071
  return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
53779
54072
  }
53780
54073
  function hashId(prefix, parts) {
53781
- const digest = createHash24("sha256").update(parts.join("\x00")).digest("hex").slice(0, 12).toUpperCase();
54074
+ const digest = createHash25("sha256").update(parts.join("\x00")).digest("hex").slice(0, 12).toUpperCase();
53782
54075
  return `${prefix}-${digest}`;
53783
54076
  }
53784
54077
  function inferSubject(text, subjectHint) {
@@ -54442,11 +54735,43 @@ function isGenericPlaceholder(value) {
54442
54735
  const normalized = value.trim().toLowerCase();
54443
54736
  return PLACEHOLDER_VALUES.has(normalized) || normalized === "unknown" || normalized.startsWith("requirement.") || normalized.endsWith("_policy") && ["executable", "cwd", "environment", "stdio", "termination"].some((kind) => normalized.startsWith(kind));
54444
54737
  }
54445
- function classifyBinding(value, text, explicit, canonical = false) {
54738
+ function bindingToken(value) {
54739
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
54740
+ }
54741
+ function claimNames(text, token) {
54742
+ if (!text || !token)
54743
+ return false;
54744
+ const words = token.split("_").filter(Boolean);
54745
+ if (words.length === 0)
54746
+ return false;
54747
+ return new RegExp(`\\b${words.join("[\\s_-]+")}\\b`, "i").test(text);
54748
+ }
54749
+ function matchesConstant(value, context) {
54750
+ const token = bindingToken(value);
54751
+ return context.constants?.some((constant) => bindingToken(constant) === token) ?? false;
54752
+ }
54753
+ function nameOrStopWordReason(value, context = {}) {
54754
+ const token = bindingToken(value);
54755
+ if (token === "true" || token === "false")
54756
+ return null;
54757
+ if (matchesConstant(value, context))
54758
+ return null;
54759
+ if (STOP_WORD_BINDINGS.has(token))
54760
+ return `"${token}" is a stop word`;
54761
+ const own = context.argumentName ? bindingToken(context.argumentName) : "";
54762
+ const repeatsName = own !== "" && token === own || (context.argumentNames ?? []).some((name) => bindingToken(name) === token);
54763
+ if (repeatsName && !claimNames(context.text, token))
54764
+ return `it repeats the argument name ${token} and the claim does not name it`;
54765
+ return null;
54766
+ }
54767
+ function classifyBinding(value, text, explicit, canonical = false, context = {}) {
54446
54768
  const normalized = value.trim();
54447
- if (canonical && !isGenericPlaceholder(normalized))
54769
+ if (nameOrStopWordReason(normalized, { ...context, text }) !== null)
54770
+ return "placeholder";
54771
+ const constant = matchesConstant(normalized, context);
54772
+ if (canonical && (constant || !isGenericPlaceholder(normalized)))
54448
54773
  return "extracted";
54449
- if (explicit && (!isGenericPlaceholder(normalized) || ["true", "false"].includes(normalized.toLowerCase())))
54774
+ if (explicit && (constant || !isGenericPlaceholder(normalized) || ["true", "false"].includes(normalized.toLowerCase())))
54450
54775
  return "explicit";
54451
54776
  if (!normalized || isGenericPlaceholder(normalized))
54452
54777
  return "placeholder";
@@ -54460,7 +54785,7 @@ function aggregateBindingProvenance(values) {
54460
54785
  function bindingCanBeApplied(value) {
54461
54786
  return value === "explicit" || value === "extracted";
54462
54787
  }
54463
- var PLACEHOLDER_VALUES, DERIVED_CUE_ALIASES, PROVENANCE_ORDER;
54788
+ var PLACEHOLDER_VALUES, DERIVED_CUE_ALIASES, STOP_WORD_BINDINGS, PROVENANCE_ORDER;
54464
54789
  var init_predicate_bindings = __esm(() => {
54465
54790
  PLACEHOLDER_VALUES = new Set([
54466
54791
  "unknown",
@@ -54532,6 +54857,62 @@ var init_predicate_bindings = __esm(() => {
54532
54857
  actionable_error: ["actionable", "clear error", "report", "error"],
54533
54858
  exception: ["exception", "except", "package-manager", "package manager"]
54534
54859
  };
54860
+ STOP_WORD_BINDINGS = new Set([
54861
+ "a",
54862
+ "an",
54863
+ "the",
54864
+ "be",
54865
+ "is",
54866
+ "are",
54867
+ "was",
54868
+ "were",
54869
+ "been",
54870
+ "being",
54871
+ "am",
54872
+ "do",
54873
+ "does",
54874
+ "did",
54875
+ "done",
54876
+ "have",
54877
+ "has",
54878
+ "had",
54879
+ "it",
54880
+ "its",
54881
+ "this",
54882
+ "that",
54883
+ "these",
54884
+ "those",
54885
+ "they",
54886
+ "them",
54887
+ "there",
54888
+ "thing",
54889
+ "something",
54890
+ "anything",
54891
+ "some",
54892
+ "any",
54893
+ "of",
54894
+ "to",
54895
+ "in",
54896
+ "at",
54897
+ "by",
54898
+ "for",
54899
+ "with",
54900
+ "and",
54901
+ "or",
54902
+ "not",
54903
+ "null",
54904
+ "nil",
54905
+ "n_a",
54906
+ "na",
54907
+ "tbd",
54908
+ "todo",
54909
+ "x",
54910
+ "xxx",
54911
+ "foo",
54912
+ "bar",
54913
+ "example",
54914
+ "placeholder"
54915
+ ]);
54535
54916
  PROVENANCE_ORDER = [
54536
54917
  "explicit",
54537
54918
  "extracted",
@@ -55248,7 +55629,11 @@ function buildSuggestion(schema, text, subject, score, argumentBindings = {}, po
55248
55629
  const undeclaredArguments = new Set(conformance.undeclared.map((value) => value.argumentName));
55249
55630
  const bindingProvenanceByArgument = Object.fromEntries(schema.argument_names.map((name, index) => [
55250
55631
  name,
55251
- classifyBinding(predicateArgs[index] ?? "unknown", text, typeof argumentBindings[name] === "string" && argumentBindings[name].trim().length > 0 || diagnostics?.explicitSubject === true && index === 0, canonicalLauncherArgs?.[index] === predicateArgs[index] && !(index === 0 && predicateArgs[index] === "launcher" && diagnostics?.explicitSubject !== true && typeof argumentBindings[name] !== "string"))
55632
+ classifyBinding(predicateArgs[index] ?? "unknown", text, typeof argumentBindings[name] === "string" && argumentBindings[name].trim().length > 0 || diagnostics?.explicitSubject === true && index === 0, canonicalLauncherArgs?.[index] === predicateArgs[index] && !(index === 0 && predicateArgs[index] === "launcher" && diagnostics?.explicitSubject !== true && typeof argumentBindings[name] !== "string"), index === 0 && diagnostics?.explicitSubject === true && typeof argumentBindings[name] !== "string" ? { constants: schema.argument_constants?.[name] } : {
55633
+ argumentName: name,
55634
+ argumentNames: schema.argument_names,
55635
+ constants: schema.argument_constants?.[name]
55636
+ })
55252
55637
  ]));
55253
55638
  const bindingProvenance = aggregateBindingProvenance(Object.values(bindingProvenanceByArgument));
55254
55639
  const unboundArguments = schema.argument_names.filter((name) => undeclaredArguments.has(name) || !bindingCanBeApplied(bindingProvenanceByArgument[name] ?? "placeholder"));
@@ -55338,8 +55723,21 @@ function buildRelationshipPlan(factId, requirementId, claimText, existingLogicCl
55338
55723
  instructions: `Apply the predicate fact ${factId} first, update the requirement with the returned merged logicClaims manifest, then attach this relationship without overwriting other requirement metadata. The relationship target is the planned fact id ${factId}, never a candidates[].id (SUGGEST-...).`
55339
55724
  };
55340
55725
  }
55726
+ function gapObservationBody(text, args) {
55727
+ const origin = [
55728
+ args.requirementId ? `requirement ${args.requirementId}` : null,
55729
+ args.source ? `source ${args.source}` : null
55730
+ ].filter((value) => value !== null);
55731
+ return [
55732
+ "No available predicate schema fits this claim, so kb_model mode predicates recorded it as an open ontology gap. The observation does not ground the claim; it stays visible for review until a reviewed predicate_schema covers this kind of statement.",
55733
+ ...origin.length > 0 ? [`Claim from ${origin.join(", ")}:`] : ["Claim:"],
55734
+ `> ${text.trim().replace(/\n/g, `
55735
+ > `)}`
55736
+ ].join(`
55737
+
55738
+ `);
55739
+ }
55341
55740
  function buildGapApplyPlan(text, args) {
55342
- const claimKey = semanticClaimKey(text);
55343
55741
  const factId = hashId("FACT-ONTOLOGY-GAP", [
55344
55742
  args.requirementId ?? "",
55345
55743
  args.source ?? "",
@@ -55353,20 +55751,14 @@ function buildGapApplyPlan(text, args) {
55353
55751
  title: "Ontology gap: predicate schema needed",
55354
55752
  status: "active",
55355
55753
  source: args.source ?? "mcp://kibi/suggest-predicates",
55356
- text_ref: args.source,
55754
+ ...args.source ? { text_ref: args.source } : {},
55357
55755
  tags: ["review:ontology-gap", "needs_schema_extension"],
55358
55756
  fact_kind: "observation",
55359
55757
  value_string: text,
55360
- claim_key: claimKey,
55361
55758
  claim_text: text
55362
55759
  },
55363
- relationships: [
55364
- {
55365
- type: "relates_to",
55366
- from: factId,
55367
- to: "review:ontology-gap"
55368
- }
55369
- ]
55760
+ document: { body: gapObservationBody(text, args) },
55761
+ relationships: []
55370
55762
  }
55371
55763
  ];
55372
55764
  }
@@ -55429,6 +55821,84 @@ var init_predicate_applyplan = __esm(() => {
55429
55821
  init_predicate_utils();
55430
55822
  });
55431
55823
 
55824
+ // ../cli/dist/operations/modeling/predicate-binding-hints.js
55825
+ function exampleArguments(example) {
55826
+ const open = example.indexOf("(");
55827
+ const close = example.lastIndexOf(")");
55828
+ if (open < 0 || close <= open)
55829
+ return [];
55830
+ const inner = example.slice(open + 1, close);
55831
+ const args = [];
55832
+ let depth = 0;
55833
+ let current = "";
55834
+ for (const char of inner) {
55835
+ if (char === "(" || char === "[")
55836
+ depth += 1;
55837
+ if (char === ")" || char === "]")
55838
+ depth -= 1;
55839
+ if (char === "," && depth === 0) {
55840
+ args.push(current.trim());
55841
+ current = "";
55842
+ continue;
55843
+ }
55844
+ current += char;
55845
+ }
55846
+ args.push(current.trim());
55847
+ return args.filter(Boolean);
55848
+ }
55849
+ function unboundReason(candidate, name, value, text) {
55850
+ const schema = candidate.schema;
55851
+ const provenance = candidate.binding_provenance_by_argument[name];
55852
+ const nameReason = nameOrStopWordReason(value, {
55853
+ argumentName: name,
55854
+ argumentNames: schema.argument_names,
55855
+ constants: schema.argument_constants?.[name],
55856
+ text
55857
+ });
55858
+ if (nameReason !== null)
55859
+ return `The value "${value}" is not a binding: ${nameReason}.`;
55860
+ if (!value.trim() || isGenericPlaceholder(value))
55861
+ return "The claim text names no value for this argument.";
55862
+ if (provenance === "inferred")
55863
+ return `The value "${value}" was inferred and does not appear in the claim text; confirm it or pass the exact value.`;
55864
+ if (schema.argument_constants?.[name])
55865
+ return `The value "${value}" is not one of this argument's declared constants.`;
55866
+ return `The value "${value}" needs review.`;
55867
+ }
55868
+ function buildBindingHints(candidate, text) {
55869
+ const schema = candidate.schema;
55870
+ const parsedExamples = schema.examples.map(exampleArguments);
55871
+ return candidate.unbound_arguments.map((name) => {
55872
+ const position = schema.argument_names.indexOf(name);
55873
+ const constants = schema.argument_constants?.[name];
55874
+ const exampleValues = parsedExamples.map((args) => args[position]).filter((value) => typeof value === "string");
55875
+ const examples = Array.from(new Set([...constants ?? [], ...exampleValues])).slice(0, MAX_EXAMPLES);
55876
+ const current = candidate.predicate_args[position] ?? "";
55877
+ const description = schema.argument_descriptions?.[position];
55878
+ return {
55879
+ argument: name,
55880
+ position,
55881
+ type: schema.argument_types[position] ?? "unknown",
55882
+ ...description ? { description } : {},
55883
+ ...constants ? { allowedValues: [...constants] } : {},
55884
+ examples,
55885
+ currentValue: current,
55886
+ provenance: candidate.binding_provenance_by_argument[name] ?? "placeholder",
55887
+ reason: unboundReason(candidate, name, current, text)
55888
+ };
55889
+ });
55890
+ }
55891
+ function describeBindingHints(hints) {
55892
+ return hints.map((hint) => {
55893
+ const values = hint.allowedValues ? `one of ${hint.allowedValues.join(", ")}` : hint.examples.length > 0 ? `e.g. ${hint.examples.join(", ")}` : "no example in the schema";
55894
+ return `${hint.argument} (${hint.type}, ${values})`;
55895
+ }).join("; ");
55896
+ }
55897
+ var MAX_EXAMPLES = 5;
55898
+ var init_predicate_binding_hints = __esm(() => {
55899
+ init_predicate_bindings();
55900
+ });
55901
+
55432
55902
  // ../cli/dist/operations/modeling/suggest-predicates.js
55433
55903
  function analyzeInputPropositions(text) {
55434
55904
  return analyzeSemanticAdvisorInput({
@@ -55650,18 +56120,21 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55650
56120
  const existingGrounding = await existingClaimGrounding(prolog, args.requirementId, claimKey, warnings);
55651
56121
  const alreadyGrounded = existingGrounding.length > 0;
55652
56122
  const completeCandidate = recommendedCandidate && recommendedCandidate.binding_status === "complete" ? recommendedCandidate : undefined;
55653
- const recommendedAction = alreadyGrounded ? "already_grounded" : !recommendedCandidate ? unavailableSchema ? "resolve_schema_reference" : "record_ontology_gap" : completeCandidate ? "apply_requires_predicate" : "provide_argument_bindings";
56123
+ const recommendedAction = !recommendedCandidate ? unavailableSchema ? "resolve_schema_reference" : "record_ontology_gap" : completeCandidate ? alreadyGrounded ? "replace_grounding" : "apply_requires_predicate" : "provide_argument_bindings";
55654
56124
  const predicatePlan = completeCandidate ? buildPredicateApplyPlan(completeCandidate, args) : [];
55655
- const applyPlan = alreadyGrounded ? [] : completeCandidate ? predicatePlan : !recommendedCandidate && !unavailableSchema ? buildGapApplyPlan(text, args) : [];
56125
+ const applyPlan = completeCandidate ? alreadyGrounded ? [] : predicatePlan : !recommendedCandidate && !unavailableSchema ? buildGapApplyPlan(text, args) : [];
55656
56126
  const plannedFactId = typeof predicatePlan[0]?.id === "string" ? predicatePlan[0].id : null;
55657
56127
  const relationshipPlan = completeCandidate && !alreadyGrounded ? buildRelationshipPlan(plannedFactId ?? "", args.requirementId, text, args.existingLogicClaims) : null;
55658
- const replacementPlan = alreadyGrounded && plannedFactId !== null && args.requirementId ? buildGroundingReplacementPlan(args.requirementId, plannedFactId, predicatePlan, existingGrounding) : null;
56128
+ const replacementPlan = alreadyGrounded && plannedFactId !== null && args.requirementId ? buildGroundingReplacementPlan(args.requirementId, plannedFactId, predicatePlan, existingGrounding, await storedRequirementProperties(prolog, args.requirementId, warnings)) : null;
55659
56129
  const relationshipTarget = relationshipPlan !== null || replacementPlan !== null ? plannedFactId : null;
56130
+ const groundingSummary = existingGrounding.map((row) => `${row.relationship.type} -> ${row.factId}`).join(", ");
55660
56131
  if (alreadyGrounded) {
55661
- warnings.push(`${args.requirementId} already grounds this claim (${claimKey}) through ${existingGrounding.map((row) => `${row.relationship.type} -> ${row.factId}`).join(", ")}. A modeled claim takes exactly one logical grounding relationship, so adding requires_predicate beside it fails the proposition-complete rule. Keep the existing grounding, or follow replacementPlan to swap it for the predicate.`);
56132
+ warnings.push(`${args.requirementId} already grounds this claim (${claimKey}) through ${groundingSummary}. A modeled claim takes exactly one logical grounding relationship, so adding requires_predicate beside it fails the proposition-complete rule. ${replacementPlan ? "Keep the existing grounding, or follow replacementPlan to swap it for the predicate." : "Keep the existing grounding; no predicate replacement is available yet."}`);
55662
56133
  }
55663
- const recommendedPredicateSchema = !recommendedCandidate && !unavailableSchema && !alreadyGrounded ? buildPredicateSchemaDraft(text, subject) : null;
55664
- const textSummary = alreadyGrounded ? `${args.requirementId} already grounds this claim through ${existingGrounding.map((row) => `${row.relationship.type} -> ${row.factId}`).join(", ")}; no write plan was generated.${replacementPlan ? ` To use ${completeCandidate?.predicate_name} instead, follow replacementPlan and link requires_predicate to ${plannedFactId}.` : ""}` : 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.` : "No predicate candidate passed the semantic applicability gate; record an ontology gap and review the generated schema draft instead of silently writing prose.";
56134
+ const bindingHints = recommendedAction === "provide_argument_bindings" && recommendedCandidate ? buildBindingHints(recommendedCandidate, text) : [];
56135
+ const recommendedPredicateSchema = !recommendedCandidate && !unavailableSchema ? buildPredicateSchemaDraft(text, subject) : null;
56136
+ const groundedPrefix = alreadyGrounded ? `${args.requirementId} already grounds this claim through ${groundingSummary}. ` : "";
56137
+ const textSummary = groundedPrefix + (replacementPlan ? `To use ${completeCandidate?.predicate_name} instead, follow replacementPlan and link requires_predicate to ${plannedFactId}; otherwise keep the existing grounding.` : completeCandidate ? `Suggested ${candidates.length} predicate candidate(s). Top applicable match: ${completeCandidate.predicate_name}. Apply the predicate fact ${plannedFactId}, then link requires_predicate to that fact id (not a candidate id).` : recommendedCandidate ? `Matched ${recommendedCandidate.predicate_name}, but exact reviewed values are still required for: ${recommendedCandidate.unbound_arguments.join(", ")}. Bind them from the claim text: ${describeBindingHints(bindingHints)}. A value that repeats an argument name or a stop word stays unbound. No apply plan was generated.` : unavailableSchema ? `Requested predicate schema ${args.schemaId} is unavailable or semantically inapplicable. No apply plan was generated.` : alreadyGrounded ? "No predicate candidate passed the semantic applicability gate; keep the existing grounding and record the ontology-gap observation in applyPlan so the missing schema stays visible." : "No predicate candidate passed the semantic applicability gate; record an ontology gap and review the generated schema draft instead of silently writing prose.");
55665
56138
  const logicClaims = Array.from(new Set([...args.existingLogicClaims ?? [], claimKey]));
55666
56139
  return {
55667
56140
  content: [{ type: "text", text: textSummary }],
@@ -55680,12 +56153,40 @@ async function handleKbSuggestPredicates(prolog, args, context) {
55680
56153
  relationshipTarget,
55681
56154
  existingGrounding,
55682
56155
  replacementPlan,
56156
+ bindingHints,
55683
56157
  warnings
55684
56158
  },
55685
56159
  applyPlan
55686
56160
  };
55687
56161
  }
55688
- function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing) {
56162
+ async function storedRequirementProperties(prolog, requirementId, warnings) {
56163
+ if (prolog === null)
56164
+ return null;
56165
+ try {
56166
+ const [stored] = await loadEntities(prolog, {
56167
+ id: requirementId,
56168
+ type: "req"
56169
+ });
56170
+ if (stored === undefined)
56171
+ return null;
56172
+ const restated = {};
56173
+ for (const field of RESTATED_REQUIREMENT_FIELDS)
56174
+ if (stored[field] !== undefined && stored[field] !== null)
56175
+ restated[field] = stored[field];
56176
+ return typeof restated.title === "string" && typeof restated.status === "string" ? restated : null;
56177
+ } catch (error) {
56178
+ warnings.push(`Requirement ${requirementId} could not be read (${error instanceof Error ? error.message : String(error)}); add its stored title and status to the last replacementPlan step before applying it.`);
56179
+ return null;
56180
+ }
56181
+ }
56182
+ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing, stored) {
56183
+ const requirementUpsert = (relationships) => ({
56184
+ type: "req",
56185
+ id: requirementId,
56186
+ ...stored === null ? {} : { properties: stored },
56187
+ relationships
56188
+ });
56189
+ const restate = stored === null ? `add the requirement's stored title and status as properties (they could not be read), keep this relationship only` : "the properties restate the stored title, status and metadata; kb_upsert keeps the stored proposition ledger";
55689
56190
  return {
55690
56191
  relationshipTarget: factId,
55691
56192
  steps: [
@@ -55703,30 +56204,33 @@ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, exi
55703
56204
  },
55704
56205
  {
55705
56206
  operation: "kb_upsert",
55706
- input: {
55707
- type: "req",
55708
- id: requirementId,
55709
- relationships: [
55710
- { type: "requires_predicate", from: requirementId, to: factId }
55711
- ]
55712
- },
55713
- reason: `Link requires_predicate to ${factId}; upsert the requirement with its stored title and status and this relationship only.`
56207
+ input: requirementUpsert([
56208
+ { type: "requires_predicate", from: requirementId, to: factId }
56209
+ ]),
56210
+ reason: `Link requires_predicate to ${factId}; ${restate}.`
55714
56211
  }
55715
56212
  ],
55716
- instructions: `Only replace the grounding when the predicate states the claim at least as precisely as ${existing.map((row) => row.factId).join(", ")}. Run kb_check after the last step.`
56213
+ rollback: {
56214
+ operation: "kb_upsert",
56215
+ input: requirementUpsert(existing.map((row) => row.relationship)),
56216
+ reason: "Only if the last step fails: restore the retracted grounding link so the claim is grounded again."
56217
+ },
56218
+ instructions: `Only replace the grounding when the predicate states the claim at least as precisely as ${existing.map((row) => row.factId).join(", ")}. Apply the steps unchanged, in order and back to back: linking requires_predicate before the retraction is rejected because the claim would have two groundings, and between the retraction and the link the claim is ungrounded, so kb_check reports logic-coverage for ${requirementId} until the last step lands. If the last step fails, apply rollback. Run kb_check after the last step.`
55717
56219
  };
55718
56220
  }
55719
56221
  async function executeSuggestPredicates(args, context) {
55720
56222
  return handleKbSuggestPredicates(context.prolog ?? null, args, context);
55721
56223
  }
55722
- var DEFAULT_MAX_CANDIDATES = 5, DEFAULT_MIN_SCORE2 = 0.35, NON_ASSERTIVE_PROPOSITION_ROLES, GROUNDING_RELATIONSHIP_TYPES;
56224
+ var DEFAULT_MAX_CANDIDATES = 5, DEFAULT_MIN_SCORE2 = 0.35, NON_ASSERTIVE_PROPOSITION_ROLES, GROUNDING_RELATIONSHIP_TYPES, RESTATED_REQUIREMENT_FIELDS;
55723
56225
  var init_suggest_predicates = __esm(() => {
56226
+ init_discovery_entities();
55724
56227
  init_serialization();
55725
56228
  init_analyze_prose();
55726
56229
  init_clauses();
55727
56230
  init_plugin_orchestration();
55728
56231
  init_predicate_applicability();
55729
56232
  init_predicate_applyplan();
56233
+ init_predicate_binding_hints();
55730
56234
  init_predicate_catalog();
55731
56235
  init_predicate_inference();
55732
56236
  init_predicate_loader();
@@ -55742,6 +56246,13 @@ var init_suggest_predicates = __esm(() => {
55742
56246
  "requires_predicate",
55743
56247
  "requires_rule"
55744
56248
  ];
56249
+ RESTATED_REQUIREMENT_FIELDS = [
56250
+ "title",
56251
+ "status",
56252
+ "priority",
56253
+ "owner",
56254
+ "tags"
56255
+ ];
55745
56256
  });
55746
56257
 
55747
56258
  // ../cli/dist/public/operations/specs/modeling.js
@@ -59754,7 +60265,7 @@ var init_skills = __esm(() => {
59754
60265
  });
59755
60266
 
59756
60267
  // ../cli/dist/public/operations/specs/skills.js
59757
- import { createHash as createHash25 } from "node:crypto";
60268
+ import { createHash as createHash26 } from "node:crypto";
59758
60269
  function assertNonEmptyString(value, field) {
59759
60270
  if (typeof value !== "string" || value.trim() === "") {
59760
60271
  throw new Error(`${field} must be a non-empty string`);
@@ -59775,7 +60286,7 @@ async function executeSkillsLoad(input, _context) {
59775
60286
  assertNonEmptyString(id, "id");
59776
60287
  const bundle = loadBundledSkill(id);
59777
60288
  const resources = bundle.manifest.resources ?? [];
59778
- const contentHash = createHash25("sha256").update(bundle.body, "utf8").digest("hex");
60289
+ const contentHash = createHash26("sha256").update(bundle.body, "utf8").digest("hex");
59779
60290
  const resourceList = resources.length === 0 ? "none" : resources.join(", ");
59780
60291
  return {
59781
60292
  content: [
@@ -61532,16 +62043,16 @@ var init_audit = __esm(() => {
61532
62043
  });
61533
62044
 
61534
62045
  // ../cli/dist/operations/mutation/source-authoring.js
61535
- import { createHash as createHash26 } from "node:crypto";
62046
+ import { createHash as createHash27 } from "node:crypto";
61536
62047
  import { randomUUID as randomUUID2 } from "node:crypto";
61537
62048
  import fs6 from "node:fs";
61538
62049
  import path50 from "node:path";
61539
62050
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
61540
- function digest2(value) {
61541
- return createHash26("sha256").update(value).digest("hex");
62051
+ function digest3(value) {
62052
+ return createHash27("sha256").update(value).digest("hex");
61542
62053
  }
61543
62054
  function pendingReceiptPath(workspaceRoot, relativePath) {
61544
- return path50.join(workspaceRoot, ".kb", "recovery", "pending-sources", `${digest2(relativePath)}.json`);
62055
+ return path50.join(workspaceRoot, ".kb", "recovery", "pending-sources", `${digest3(relativePath)}.json`);
61545
62056
  }
61546
62057
  function writePendingSourceReceipt(workspaceRoot, relativePath, afterHash) {
61547
62058
  const receiptPath = pendingReceiptPath(workspaceRoot, relativePath);
@@ -62048,8 +62559,8 @@ async function writeSourceForUpsert(input, entity, existing, context, sourceDocu
62048
62559
  const receipt = {
62049
62560
  path: relative,
62050
62561
  mode: "write",
62051
- beforeHash: before === undefined ? null : digest2(before),
62052
- afterHash: digest2(after),
62562
+ beforeHash: before === undefined ? null : digest3(before),
62563
+ afterHash: digest3(after),
62053
62564
  created: before === undefined
62054
62565
  };
62055
62566
  const rollback = async () => {
@@ -62062,7 +62573,7 @@ async function writeSourceForUpsert(input, entity, existing, context, sourceDocu
62062
62573
  } catch {
62063
62574
  current = undefined;
62064
62575
  }
62065
- if (current !== undefined && digest2(current) !== digest2(after)) {
62576
+ if (current !== undefined && digest3(current) !== digest3(after)) {
62066
62577
  writePendingSourceReceipt(context.workspaceRoot, relative, receipt.afterHash);
62067
62578
  console.warn(`Skipped source rollback for ${relative}: file changed after the Kibi write; kept concurrent content and recorded recovery metadata.`);
62068
62579
  return;
@@ -62132,7 +62643,7 @@ var init_source_authoring = __esm(() => {
62132
62643
  });
62133
62644
 
62134
62645
  // ../cli/dist/operations/mutation/plan-apply-journal.js
62135
- import { createHash as createHash27, randomUUID as randomUUID3 } from "node:crypto";
62646
+ import { createHash as createHash28, randomUUID as randomUUID3 } from "node:crypto";
62136
62647
  import { closeSync as closeSync3, existsSync as existsSync26, fsyncSync as fsyncSync2, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync27, readdirSync as readdirSync8, renameSync as renameSync5, statSync as statSync10, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "node:fs";
62137
62648
  import path51 from "node:path";
62138
62649
  function planApplyJournalId(planHash) {
@@ -62142,7 +62653,7 @@ function isPlanApplyJournalId(value) {
62142
62653
  return JOURNAL_ID_PATTERN.test(value);
62143
62654
  }
62144
62655
  function contentHash(value) {
62145
- return createHash27("sha256").update(value).digest("hex");
62656
+ return createHash28("sha256").update(value).digest("hex");
62146
62657
  }
62147
62658
  function errorText(error) {
62148
62659
  return error instanceof Error ? error.message : String(error);
@@ -62919,7 +63430,7 @@ var init_workspace_mutation_lock = __esm(() => {
62919
63430
  });
62920
63431
 
62921
63432
  // ../cli/dist/operations/mutation/delete.js
62922
- import { createHash as createHash28, randomUUID as randomUUID4 } from "node:crypto";
63433
+ import { createHash as createHash29, randomUUID as randomUUID4 } from "node:crypto";
62923
63434
  import { existsSync as existsSync27, readFileSync as readFileSync29, writeFileSync as writeFileSync9 } from "node:fs";
62924
63435
  import path53 from "node:path";
62925
63436
  function requireProlog3(context) {
@@ -62957,7 +63468,7 @@ function sourceIsAuthored(source) {
62957
63468
  function fileHash(pathname) {
62958
63469
  if (!existsSync27(pathname))
62959
63470
  return null;
62960
- return createHash28("sha256").update(readFileSync29(pathname)).digest("hex");
63471
+ return createHash29("sha256").update(readFileSync29(pathname)).digest("hex");
62961
63472
  }
62962
63473
  async function executeRelationshipDeleteUnlocked(selectors, context, onCommitted = () => {
62963
63474
  return;
@@ -63151,8 +63662,8 @@ async function executeRelationshipDeleteUnlocked(selectors, context, onCommitted
63151
63662
  ];
63152
63663
  });
63153
63664
  sourceWrites.push(...Array.from(sourcePatches, ([relative, patch]) => {
63154
- const beforeHash = createHash28("sha256").update(patch.before).digest("hex");
63155
- const afterHash = createHash28("sha256").update(patch.after).digest("hex");
63665
+ const beforeHash = createHash29("sha256").update(patch.before).digest("hex");
63666
+ const afterHash = createHash29("sha256").update(patch.after).digest("hex");
63156
63667
  writePendingSourceReceipt(context.workspaceRoot, relative, afterHash);
63157
63668
  return {
63158
63669
  path: relative,
@@ -63284,8 +63795,8 @@ async function executeDelete(input, context) {
63284
63795
  }
63285
63796
  const deletion = renderSourceDeletion(relativeSource, id, String(entity.type), contents);
63286
63797
  evolvingSourceBodies.set(relativeSource, deletion.mode === "write" ? deletion.body : undefined);
63287
- const beforeHash = createHash28("sha256").update(original).digest("hex");
63288
- const afterHash = deletion.body === undefined ? null : createHash28("sha256").update(deletion.body).digest("hex");
63798
+ const beforeHash = createHash29("sha256").update(original).digest("hex");
63799
+ const afterHash = deletion.body === undefined ? null : createHash29("sha256").update(deletion.body).digest("hex");
63289
63800
  sourceHashes[relativeSource] = beforeHash;
63290
63801
  sourcePlansByPath.set(relativeSource, {
63291
63802
  path: relativeSource,
@@ -63311,13 +63822,13 @@ async function executeDelete(input, context) {
63311
63822
  const relativeShard = path53.relative(context.workspaceRoot, shardPath).replaceAll("\\", "/");
63312
63823
  const original = readFileSync29(shardPath, "utf8");
63313
63824
  const body = renderShardWithout(original, selectors, shardPath);
63314
- const beforeHash = createHash28("sha256").update(original).digest("hex");
63825
+ const beforeHash = createHash29("sha256").update(original).digest("hex");
63315
63826
  sourceHashes[relativeShard] = beforeHash;
63316
63827
  sourcePlansByPath.set(relativeShard, {
63317
63828
  path: relativeShard,
63318
63829
  mode: "write",
63319
63830
  beforeHash,
63320
- afterHash: createHash28("sha256").update(body).digest("hex"),
63831
+ afterHash: createHash29("sha256").update(body).digest("hex"),
63321
63832
  body
63322
63833
  });
63323
63834
  }
@@ -63331,7 +63842,7 @@ async function executeDelete(input, context) {
63331
63842
  sourceWrites: sourcePlans,
63332
63843
  supersessionRequired
63333
63844
  };
63334
- const planHash = createHash28("sha256").update(JSON.stringify(planBody)).digest("hex");
63845
+ const planHash = createHash29("sha256").update(JSON.stringify(planBody)).digest("hex");
63335
63846
  const payload = {
63336
63847
  deleted: 0,
63337
63848
  skipped: authoredIds.length,
@@ -64265,7 +64776,7 @@ var init_symbols_coordinator2 = __esm(() => {
64265
64776
  });
64266
64777
 
64267
64778
  // ../cli/dist/operations/mutation/symbol-refresh.js
64268
- import { createHash as createHash29, randomUUID as randomUUID5 } from "node:crypto";
64779
+ import { createHash as createHash30, randomUUID as randomUUID5 } from "node:crypto";
64269
64780
  import { existsSync as existsSync29, readFileSync as readFileSync33, renameSync as renameSync7, unlinkSync as unlinkSync8, writeFileSync as writeFileSync11 } from "node:fs";
64270
64781
  import * as path58 from "node:path";
64271
64782
  function record2(value) {
@@ -64292,7 +64803,7 @@ function sourceTextFor(sourceFile, workspaceRoot) {
64292
64803
  }
64293
64804
  }
64294
64805
  function contentHash2(content) {
64295
- return createHash29("sha256").update(content).digest("hex");
64806
+ return createHash30("sha256").update(content).digest("hex");
64296
64807
  }
64297
64808
  function restoreOrUnlinkCoordinateArtifact(targetPath, before) {
64298
64809
  if (before === null) {
@@ -64492,7 +65003,7 @@ async function scenarioCoverageWarnings(prolog, relationships, entityType, entit
64492
65003
  var init_warnings = () => {};
64493
65004
 
64494
65005
  // ../cli/dist/operations/mutation/upsert.js
64495
- import { createHash as createHash30 } from "node:crypto";
65006
+ import { createHash as createHash31 } from "node:crypto";
64496
65007
  import { existsSync as existsSync30, readFileSync as readFileSync34, unlinkSync as unlinkSync9, writeFileSync as writeFileSync12 } from "node:fs";
64497
65008
  import path59 from "node:path";
64498
65009
  function requireProlog4(context) {
@@ -64513,10 +65024,10 @@ function parseChangeKind(value) {
64513
65024
  function fileHash2(pathname) {
64514
65025
  if (!existsSync30(pathname))
64515
65026
  return null;
64516
- return createHash30("sha256").update(readFileSync34(pathname)).digest("hex");
65027
+ return createHash31("sha256").update(readFileSync34(pathname)).digest("hex");
64517
65028
  }
64518
65029
  function textHash(value) {
64519
- return createHash30("sha256").update(value).digest("hex");
65030
+ return createHash31("sha256").update(value).digest("hex");
64520
65031
  }
64521
65032
  function assertRelationshipShardContained(workspaceRoot, shardPath) {
64522
65033
  const root = path59.resolve(workspaceRoot);
@@ -66810,7 +67321,7 @@ var init_staging = __esm(() => {
66810
67321
 
66811
67322
  // ../cli/dist/commands/sync.js
66812
67323
  import { execSync as execSync4 } from "node:child_process";
66813
- import { createHash as createHash31 } from "node:crypto";
67324
+ import { createHash as createHash32 } from "node:crypto";
66814
67325
  import { existsSync as existsSync34, mkdirSync as mkdirSync12, readFileSync as readFileSync37, readdirSync as readdirSync9, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "node:fs";
66815
67326
  import * as path64 from "node:path";
66816
67327
  function relationshipKey2(relationship) {
@@ -66883,14 +67394,14 @@ function trackedRelationshipFiles(workspaceRoot, relationshipsDir, recoverMissin
66883
67394
  receiptPath,
66884
67395
  path: relative,
66885
67396
  afterHash: receipt.afterHash,
66886
- rawHash: createHash31("sha256").update(readFileSync37(receiptPath)).digest("hex")
67397
+ rawHash: createHash32("sha256").update(readFileSync37(receiptPath)).digest("hex")
66887
67398
  });
66888
67399
  }
66889
67400
  continue;
66890
67401
  }
66891
67402
  throw new SyncError(`Pending source is missing: ${relative}.${PENDING_SOURCE_RECOVERY_HINT}`);
66892
67403
  }
66893
- const actual = createHash31("sha256").update(readFileSync37(absolute)).digest("hex");
67404
+ const actual = createHash32("sha256").update(readFileSync37(absolute)).digest("hex");
66894
67405
  if (actual !== receipt.afterHash) {
66895
67406
  throw new SyncError(`Pending source hash drift blocks sync for ${relative}`);
66896
67407
  }
@@ -67751,14 +68262,14 @@ var init_sync = __esm(() => {
67751
68262
  });
67752
68263
 
67753
68264
  // ../cli/dist/commands/branch.js
67754
- import { createHash as createHash32 } from "node:crypto";
68265
+ import { createHash as createHash33 } from "node:crypto";
67755
68266
  import * as fs8 from "node:fs";
67756
68267
  import * as path65 from "node:path";
67757
68268
  function isSupportedLegacyBranchMigration(from, to) {
67758
68269
  return from === to;
67759
68270
  }
67760
68271
  function hashLegacyStore(sourcePath) {
67761
- const hash = createHash32("sha256");
68272
+ const hash = createHash33("sha256");
67762
68273
  const visit = (directory, relative = "") => {
67763
68274
  for (const entry of fs8.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
67764
68275
  const absolute = path65.join(directory, entry.name);
@@ -67778,7 +68289,7 @@ function hashLegacyStore(sourcePath) {
67778
68289
  }
67779
68290
  function branchMigrationApprovalHash(from, to, sourcePath, targetPath) {
67780
68291
  const sourceHash = hashLegacyStore(sourcePath);
67781
- return createHash32("sha256").update(JSON.stringify({
68292
+ return createHash33("sha256").update(JSON.stringify({
67782
68293
  version: 2,
67783
68294
  from,
67784
68295
  to,
@@ -68071,7 +68582,7 @@ var init_branch = __esm(() => {
68071
68582
  });
68072
68583
 
68073
68584
  // ../cli/dist/operations/migration/inventory-rederive.js
68074
- import { createHash as createHash33 } from "node:crypto";
68585
+ import { createHash as createHash34 } from "node:crypto";
68075
68586
  import path66 from "node:path";
68076
68587
  import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
68077
68588
  function isRecord17(value) {
@@ -68288,7 +68799,7 @@ function planOne(workspaceRoot, relativePath, entity, extracted, shards, facts)
68288
68799
  summary: summarize2(changes, inventory),
68289
68800
  changes,
68290
68801
  contract,
68291
- contractHash: createHash33("sha256").update(canonicalJson3(contract)).digest("hex")
68802
+ contractHash: createHash34("sha256").update(canonicalJson3(contract)).digest("hex")
68292
68803
  };
68293
68804
  }
68294
68805
  function readShardRelationships(workspaceRoot) {
@@ -68861,7 +69372,7 @@ ${KIBI_BIN_RESOLVER}
68861
69372
  });
68862
69373
 
68863
69374
  // ../cli/dist/commands/legacy-storage-migration.js
68864
- import { createHash as createHash34 } from "node:crypto";
69375
+ import { createHash as createHash35 } from "node:crypto";
68865
69376
  import { existsSync as existsSync37, mkdirSync as mkdirSync15, readFileSync as readFileSync40, readdirSync as readdirSync11, renameSync as renameSync12, rmSync as rmSync5, statSync as statSync12, writeFileSync as writeFileSync18 } from "node:fs";
68866
69377
  import * as path70 from "node:path";
68867
69378
  function stripGlobToRoot(pattern) {
@@ -69047,7 +69558,7 @@ function rewritePendingSourceReceiptPaths(cwd, moves) {
69047
69558
  const absolute = path70.resolve(cwd, destination);
69048
69559
  if (!existsSync37(absolute))
69049
69560
  continue;
69050
- const afterHash = typeof receipt.afterHash === "string" && /^[a-f0-9]{64}$/i.test(receipt.afterHash) ? receipt.afterHash : createHash34("sha256").update(readFileSync40(absolute)).digest("hex");
69561
+ const afterHash = typeof receipt.afterHash === "string" && /^[a-f0-9]{64}$/i.test(receipt.afterHash) ? receipt.afterHash : createHash35("sha256").update(readFileSync40(absolute)).digest("hex");
69051
69562
  writeFileSync18(receiptPath, `${JSON.stringify({ ...receipt, path: destination, afterHash }, null, 2)}
69052
69563
  `, "utf8");
69053
69564
  rewritten += 1;
@@ -69784,7 +70295,7 @@ var init_ignore_policy = __esm(() => {
69784
70295
 
69785
70296
  // ../cli/dist/public/operations/node-ports.js
69786
70297
  import { execFile as execFile2, execFileSync as execFileSync8 } from "node:child_process";
69787
- import { createHash as createHash35 } from "node:crypto";
70298
+ import { createHash as createHash36 } from "node:crypto";
69788
70299
  import * as fsSync from "node:fs";
69789
70300
  import * as fs9 from "node:fs/promises";
69790
70301
  import path72 from "node:path";
@@ -69814,7 +70325,7 @@ async function workspaceSnapshot(workspaceRoot) {
69814
70325
  "-z"
69815
70326
  ], { encoding: "buffer", maxBuffer: 64 * 1024 * 1024 });
69816
70327
  const paths = listed.toString("utf8").split("\x00").filter(Boolean).filter(includedSnapshotPath).map((raw) => ({ raw, key: raw.normalize("NFC") })).sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0);
69817
- const digest = createHash35("sha256");
70328
+ const digest = createHash36("sha256");
69818
70329
  digest.update("kibi.workspace-snapshot.v2\x00");
69819
70330
  for (const { raw, key: relativePath } of paths) {
69820
70331
  digest.update(relativePath);
@@ -70205,7 +70716,7 @@ var init_cli_runtime = __esm(() => {
70205
70716
  });
70206
70717
 
70207
70718
  // ../cli/dist/public/operations/legacy-migration-plan.js
70208
- import { createHash as createHash36 } from "node:crypto";
70719
+ import { createHash as createHash37 } from "node:crypto";
70209
70720
  function isRecord20(value) {
70210
70721
  return value !== null && typeof value === "object" && !Array.isArray(value);
70211
70722
  }
@@ -70240,7 +70751,7 @@ function stableStringify(value) {
70240
70751
  return JSON.stringify(stableValue3(value));
70241
70752
  }
70242
70753
  function hash(value) {
70243
- return createHash36("sha256").update(stableStringify(value)).digest("hex");
70754
+ return createHash37("sha256").update(stableStringify(value)).digest("hex");
70244
70755
  }
70245
70756
  function migrationRepairBatches(plan) {
70246
70757
  const seen = new Set;
@@ -70562,7 +71073,7 @@ var init_legacy_migration_plan = __esm(() => {
70562
71073
  });
70563
71074
 
70564
71075
  // ../cli/dist/public/operations/repair-plan.js
70565
- import { createHash as createHash37 } from "node:crypto";
71076
+ import { createHash as createHash38 } from "node:crypto";
70566
71077
  function isRecord21(value) {
70567
71078
  return value !== null && typeof value === "object" && !Array.isArray(value);
70568
71079
  }
@@ -70705,7 +71216,7 @@ function buildRepairPlan(payload, input, codeSnapshot) {
70705
71216
  },
70706
71217
  batches
70707
71218
  };
70708
- const planId = `repair-plan-${createHash37("sha256").update(stableStringify2(planCore)).digest("hex").slice(0, 24)}`;
71219
+ const planId = `repair-plan-${createHash38("sha256").update(stableStringify2(planCore)).digest("hex").slice(0, 24)}`;
70709
71220
  return {
70710
71221
  version: REPAIR_PLAN_VERSION,
70711
71222
  planId,
@@ -73807,7 +74318,7 @@ var init_migrate = __esm(() => {
73807
74318
  });
73808
74319
 
73809
74320
  // ../cli/dist/operations/planning/compile-intent.js
73810
- import { createHash as createHash38 } from "node:crypto";
74321
+ import { createHash as createHash39 } from "node:crypto";
73811
74322
  import path77 from "node:path";
73812
74323
  function compilePlanHash(plan) {
73813
74324
  const { planHash: _ignored, ...body } = plan;
@@ -73817,7 +74328,7 @@ function text7(value) {
73817
74328
  return typeof value === "string" ? value.trim() : "";
73818
74329
  }
73819
74330
  function hash2(value) {
73820
- return createHash38("sha256").update(canonicalize(value)).digest("hex");
74331
+ return createHash39("sha256").update(canonicalize(value)).digest("hex");
73821
74332
  }
73822
74333
  function shortHash2(value, length = 8) {
73823
74334
  return hash2(value).slice(0, length);
@@ -73964,7 +74475,7 @@ async function sourceHashes(context, locations) {
73964
74475
  for (const relative of sourceFilesFor(locations)) {
73965
74476
  try {
73966
74477
  const contents = context.fs ? await context.fs.readFile(path77.join(context.workspaceRoot, relative)) : null;
73967
- hashes[relative] = contents === null ? null : createHash38("sha256").update(contents).digest("hex");
74478
+ hashes[relative] = contents === null ? null : createHash39("sha256").update(contents).digest("hex");
73968
74479
  } catch {
73969
74480
  hashes[relative] = null;
73970
74481
  }
@@ -74549,7 +75060,7 @@ var init_compile_intent = __esm(() => {
74549
75060
  });
74550
75061
 
74551
75062
  // ../cli/dist/operations/planning/apply-plan.js
74552
- import { createHash as createHash39, randomUUID as randomUUID6 } from "node:crypto";
75063
+ import { createHash as createHash40, randomUUID as randomUUID6 } from "node:crypto";
74553
75064
  import path78 from "node:path";
74554
75065
  function text8(value) {
74555
75066
  return typeof value === "string" ? value.trim() : "";
@@ -74557,8 +75068,8 @@ function text8(value) {
74557
75068
  function isRecord23(value) {
74558
75069
  return value !== null && typeof value === "object" && !Array.isArray(value);
74559
75070
  }
74560
- function digest3(value) {
74561
- return createHash39("sha256").update(value).digest("hex");
75071
+ function digest4(value) {
75072
+ return createHash40("sha256").update(value).digest("hex");
74562
75073
  }
74563
75074
  function relationships(step) {
74564
75075
  if (!Array.isArray(step.relationships))
@@ -74722,7 +75233,7 @@ function validateEntityDeletionPlan(args) {
74722
75233
  throw new Error("Entity deletion apply failed: approvedPlanHash does not match planHash");
74723
75234
  }
74724
75235
  const { planHash: _ignored, ...body } = args.plan;
74725
- if (createHash39("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
75236
+ if (createHash40("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
74726
75237
  throw new Error("Entity deletion apply failed: planHash does not match the canonical plan body");
74727
75238
  }
74728
75239
  if (!Array.isArray(args.plan.entityIds) || args.plan.entityIds.length === 0) {
@@ -74801,7 +75312,7 @@ async function validateSources(context, sourceHashes) {
74801
75312
  throw new Error("Apply plan failed: source hashes require a filesystem-capable runtime");
74802
75313
  let actual;
74803
75314
  try {
74804
- actual = digest3(await context.fs.readFile(path78.join(context.workspaceRoot, relative)));
75315
+ actual = digest4(await context.fs.readFile(path78.join(context.workspaceRoot, relative)));
74805
75316
  } catch {
74806
75317
  actual = null;
74807
75318
  }
@@ -74830,12 +75341,12 @@ async function resolveSourceWriteTarget(workspaceRoot, fsPort, write) {
74830
75341
  const existing = await fsPort.readFile(absolute).catch(() => {
74831
75342
  return;
74832
75343
  });
74833
- const beforeHash = existing === undefined ? null : digest3(existing);
75344
+ const beforeHash = existing === undefined ? null : digest4(existing);
74834
75345
  if (beforeHash !== write.beforeHash) {
74835
75346
  throw new Error(`Apply plan failed: source hash changed for ${write.path}`);
74836
75347
  }
74837
75348
  const mode = write.mode ?? "write";
74838
- if (mode === "write" && (write.body === undefined || write.afterHash === null || digest3(write.body) !== write.afterHash)) {
75349
+ if (mode === "write" && (write.body === undefined || write.afterHash === null || digest4(write.body) !== write.afterHash)) {
74839
75350
  throw new Error(`Apply plan failed: afterHash does not match staged body for ${write.path}`);
74840
75351
  }
74841
75352
  if (mode === "delete" && write.afterHash !== null) {
@@ -74879,7 +75390,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
74879
75390
  for (const entry of prior.entries) {
74880
75391
  try {
74881
75392
  const current = await context.fs.readFile(path78.resolve(context.workspaceRoot, entry.path));
74882
- if (entry.afterHash === null || digest3(current) !== entry.afterHash) {
75393
+ if (entry.afterHash === null || digest4(current) !== entry.afterHash) {
74883
75394
  allAfter = false;
74884
75395
  }
74885
75396
  } catch {
@@ -74907,7 +75418,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
74907
75418
  } catch {
74908
75419
  current = undefined;
74909
75420
  }
74910
- const currentHash = current === undefined ? null : digest3(current);
75421
+ const currentHash = current === undefined ? null : digest4(current);
74911
75422
  if (currentHash !== entry.beforeHash && currentHash !== entry.afterHash) {
74912
75423
  throw new Error(`Apply plan recovery refused: ${entry.path} changed outside its journal`);
74913
75424
  }
@@ -75103,15 +75614,7 @@ async function executeSourceRecovery(args, context, onCommitted = () => {
75103
75614
  }
75104
75615
  };
75105
75616
  }
75106
- async function executeBootstrapPlan(args, context, recovery = false, remainingActions, priorResults = [], onCommitted = () => {
75107
- return;
75108
- }, carry = {}) {
75109
- const actions = recovery ? [...remainingActions ?? args.plan.actions] : validateBootstrapPlanShape(args);
75110
- preflightBootstrapActions(actions, context);
75111
- const prolog = context.prolog ?? await context.ensureProlog?.();
75112
- if (!prolog)
75113
- throw new Error("Bootstrap apply requires a Prolog runtime");
75114
- const operationContext = { ...context, prolog, sourceFirst: true };
75617
+ async function assertBootstrapPlanCurrent(args, operationContext) {
75115
75618
  const statusResult = await executeStatus({}, operationContext);
75116
75619
  const status = statusResult.structuredContent;
75117
75620
  if (!status)
@@ -75122,15 +75625,56 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
75122
75625
  workspaceSnapshot: workspace.snapshot.hash,
75123
75626
  sourceHashes: args.plan.expected.sourceHashes
75124
75627
  }) : status.snapshotId;
75125
- if (!recovery) {
75126
- if (args.plan.expected.branch !== "unknown" && status.branch !== args.plan.expected.branch)
75127
- throw new Error("Bootstrap apply failed: branch changed since planning");
75128
- if (boundLiveKbSnapshot !== args.plan.expected.kbSnapshotId)
75129
- throw new Error("Bootstrap apply failed: KB snapshot changed since planning");
75130
- if (args.plan.expected.workspaceSnapshot !== "unknown" && (!workspace.available || workspace.snapshot.hash !== args.plan.expected.workspaceSnapshot))
75131
- throw new Error("Bootstrap apply failed: workspace snapshot changed since planning");
75132
- await validateSources(operationContext, args.plan.expected.sourceHashes);
75628
+ if (args.plan.expected.branch !== "unknown" && status.branch !== args.plan.expected.branch)
75629
+ throw new Error("Bootstrap apply failed: branch changed since planning");
75630
+ if (boundLiveKbSnapshot !== args.plan.expected.kbSnapshotId)
75631
+ throw new Error("Bootstrap apply failed: KB snapshot changed since planning");
75632
+ if (args.plan.expected.workspaceSnapshot !== "unknown" && (!workspace.available || workspace.snapshot.hash !== args.plan.expected.workspaceSnapshot))
75633
+ throw new Error("Bootstrap apply failed: workspace snapshot changed since planning");
75634
+ await validateSources(operationContext, args.plan.expected.sourceHashes);
75635
+ return status;
75636
+ }
75637
+ async function preflightApplyPlan(args, context) {
75638
+ if (!("plan" in args))
75639
+ return;
75640
+ if (!isRecord23(args.plan))
75641
+ throw new Error("Apply plan failed: plan must be an object");
75642
+ if (typeof args.approvedPlanHash !== "string" || !/^[a-f0-9]{64}$/i.test(args.approvedPlanHash))
75643
+ throw new Error("Apply plan failed: approvedPlanHash must be the SHA-256 planHash returned with the plan");
75644
+ if (isBootstrapApplyArgs(args)) {
75645
+ preflightBootstrapActions(validateBootstrapPlanShape(args), context);
75646
+ const prolog = context.prolog ?? await context.ensureProlog?.();
75647
+ if (!prolog)
75648
+ throw new Error("Bootstrap apply requires a Prolog runtime");
75649
+ await assertBootstrapPlanCurrent(args, {
75650
+ ...context,
75651
+ prolog,
75652
+ sourceFirst: true
75653
+ });
75654
+ return;
75133
75655
  }
75656
+ if (isMigrationApplyArgs(args)) {
75657
+ validateMigrationPlanShape(args);
75658
+ return;
75659
+ }
75660
+ if (isEntityDeletionApplyArgs(args)) {
75661
+ validateEntityDeletionPlan(args);
75662
+ return;
75663
+ }
75664
+ validateCompilePlanShape(args);
75665
+ }
75666
+ async function executeBootstrapPlan(args, context, recovery = false, remainingActions, priorResults = [], onCommitted = () => {
75667
+ return;
75668
+ }, carry = {}) {
75669
+ const actions = recovery ? [...remainingActions ?? args.plan.actions] : validateBootstrapPlanShape(args);
75670
+ preflightBootstrapActions(actions, context);
75671
+ const prolog = context.prolog ?? await context.ensureProlog?.();
75672
+ if (!prolog)
75673
+ throw new Error("Bootstrap apply requires a Prolog runtime");
75674
+ const operationContext = { ...context, prolog, sourceFirst: true };
75675
+ const status = recovery ? (await executeStatus({}, operationContext)).structuredContent : await assertBootstrapPlanCurrent(args, operationContext);
75676
+ if (!status)
75677
+ throw new Error("Bootstrap apply failed: status returned no payload");
75134
75678
  const journalId = `bootstrap-${args.plan.planHash.slice(0, 16)}`;
75135
75679
  const journalPath = path78.join(context.workspaceRoot, ".kb", "recovery", `${journalId}.json`);
75136
75680
  if (!recovery && context.fs) {
@@ -76454,7 +76998,8 @@ __export(exports_planning, {
76454
76998
  applyPlanSpec: () => applyPlanSpec,
76455
76999
  compileIntentSpec: () => compileIntentSpec,
76456
77000
  executeApplyPlan: () => executeApplyPlan,
76457
- executeCompileIntent: () => executeCompileIntent
77001
+ executeCompileIntent: () => executeCompileIntent,
77002
+ preflightApplyPlan: () => preflightApplyPlan
76458
77003
  });
76459
77004
  var compileIntentSpec, applyPlanSpec;
76460
77005
  var init_planning = __esm(() => {
@@ -77765,7 +78310,7 @@ init_generate();
77765
78310
  init_presentation();
77766
78311
  init_ir();
77767
78312
  // src/skill-operations.ts
77768
- import { createHash as createHash40 } from "node:crypto";
78313
+ import { createHash as createHash41 } from "node:crypto";
77769
78314
 
77770
78315
  // src/skill-system/loader.ts
77771
78316
  import { existsSync as existsSync44, readFileSync as readFileSync47, readdirSync as readdirSync13, realpathSync as realpathSync10 } from "node:fs";
@@ -78037,7 +78582,7 @@ async function executeSkillsLoad2(input, _context) {
78037
78582
  assertNonEmptyString2(id, "id");
78038
78583
  const bundle = loadBundledSkill2(id);
78039
78584
  const resources = bundle.manifest.resources ?? [];
78040
- const contentHash = createHash40("sha256").update(bundle.body, "utf8").digest("hex");
78585
+ const contentHash = createHash41("sha256").update(bundle.body, "utf8").digest("hex");
78041
78586
  const resourceList = resources.length === 0 ? "none" : resources.join(", ");
78042
78587
  return {
78043
78588
  content: [
@@ -78511,6 +79056,7 @@ export {
78511
79056
  paginateResults,
78512
79057
  parseEnvContent,
78513
79058
  planBootstrapSpec,
79059
+ preflightApplyPlan,
78514
79060
  prepareImpactReviewSpec,
78515
79061
  presentBootstrap,
78516
79062
  queryLimitExceededOf,