kibi-runtime 2.13.0 → 2.13.2

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
@@ -6757,7 +6757,7 @@ function manifestVersion(manifestPath, expectedName) {
6757
6757
  }
6758
6758
  function cliVersion() {
6759
6759
  if (true)
6760
- return "2.15.0";
6760
+ return "2.17.0";
6761
6761
  return manifestVersion(path2.join(moduleDirectory, "..", "package.json"), "kibi-cli") ?? "unknown";
6762
6762
  }
6763
6763
  function coreVersion() {
@@ -12218,7 +12218,7 @@ var init_search_answer = __esm(() => {
12218
12218
  });
12219
12219
 
12220
12220
  // ../cli/dist/utils/branch-store.js
12221
- import { existsSync as existsSync10, readFileSync as readFileSync9, statSync as statSync6 } from "node:fs";
12221
+ import { existsSync as existsSync10, readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync6 } from "node:fs";
12222
12222
  import * as path15 from "node:path";
12223
12223
  function inspectBranchStore(workspaceRoot, branch) {
12224
12224
  const storePath = branchStorePath(workspaceRoot, branch);
@@ -12354,9 +12354,86 @@ function branchStoreReason(inspection) {
12354
12354
  remediation: inspection.recoveryRequired ? { command_argv: ["kibi", "branch", "recover"], applyRequired: true } : { command_argv: ["kibi", "branch", "ensure"], applyRequired: false }
12355
12355
  };
12356
12356
  }
12357
+ function countFiles(directory, extension) {
12358
+ let count = 0;
12359
+ let entries;
12360
+ try {
12361
+ entries = readdirSync4(directory, { withFileTypes: true });
12362
+ } catch {
12363
+ return 0;
12364
+ }
12365
+ for (const entry of entries) {
12366
+ if (entry.isDirectory()) {
12367
+ count += countFiles(path15.join(directory, entry.name), extension);
12368
+ } else if (entry.isFile() && entry.name.endsWith(extension)) {
12369
+ count += 1;
12370
+ }
12371
+ }
12372
+ return count;
12373
+ }
12374
+ function countAuthoredKbSources(workspaceRoot) {
12375
+ const kbRoot = path15.join(workspaceRoot, KB_ROOT);
12376
+ return AUTHORED_SOURCE_DIRECTORIES.reduce((total, [directory, extension]) => total + countFiles(path15.join(kbRoot, directory), extension), 0);
12377
+ }
12378
+ function branchStoreCompilation(inspection) {
12379
+ if (inspection.state === "missing") {
12380
+ return { compiled: false, generation: null };
12381
+ }
12382
+ const currentPath = path15.join(inspection.path, "CURRENT");
12383
+ let current = "";
12384
+ try {
12385
+ current = existsSync10(currentPath) ? readFileSync9(currentPath, "utf8").trim() : "";
12386
+ } catch {
12387
+ current = "";
12388
+ }
12389
+ if (current === "") {
12390
+ return { compiled: inspection.state === "healthy", generation: null };
12391
+ }
12392
+ const sequence = Number(current.split(":").at(-1));
12393
+ return {
12394
+ compiled: Number.isFinite(sequence) && sequence > 0 || journalHoldsGraphData(inspection.path),
12395
+ generation: current
12396
+ };
12397
+ }
12398
+ function journalHoldsGraphData(storePath) {
12399
+ try {
12400
+ return readdirSync4(path15.join(storePath, "rdf")).some((entry) => entry !== "lock" && !entry.startsWith("."));
12401
+ } catch {
12402
+ return false;
12403
+ }
12404
+ }
12405
+ function uncompiledBranchStoreReason(workspaceRoot, inspection, branch) {
12406
+ if (inspection.state !== "healthy" && inspection.state !== "missing") {
12407
+ return null;
12408
+ }
12409
+ const compilation = branchStoreCompilation(inspection);
12410
+ if (compilation.compiled)
12411
+ return null;
12412
+ const authoredSources = countAuthoredKbSources(workspaceRoot);
12413
+ if (authoredSources === 0)
12414
+ return null;
12415
+ const storeState = inspection.state === "missing" ? "does not exist yet" : `is empty (${compilation.generation ?? "no generation"}: nothing compiled)`;
12416
+ return {
12417
+ code: BRANCH_STORE_NOT_COMPILED,
12418
+ path: inspection.path,
12419
+ entityIds: [],
12420
+ detail: `The KB store for branch ${branch} ${storeState}, while .kb/ holds ${authoredSources} authored source file(s). Kibi compiles one store per branch and never copies another branch's, so every query and check reads an empty KB until this branch is compiled.`,
12421
+ remediation: { command_argv: ["kibi", "sync"], applyRequired: false },
12422
+ blocking: true,
12423
+ authoredSources,
12424
+ ...compilation.generation !== null ? { generation: compilation.generation } : {}
12425
+ };
12426
+ }
12427
+ var BRANCH_STORE_NOT_COMPILED = "branch_store_not_compiled", BRANCH_STORE_NOT_COMPILED_RULE = "branch-store-not-compiled", AUTHORED_SOURCE_DIRECTORIES;
12357
12428
  var init_branch_store = __esm(() => {
12358
12429
  init_store_lock();
12359
12430
  init_branch_store_locator();
12431
+ init_kb_paths();
12432
+ AUTHORED_SOURCE_DIRECTORIES = [
12433
+ ...ENTITY_LANES.map((lane) => [lane, ".md"]),
12434
+ ["adrs", ".md"],
12435
+ ["relationships", ".yaml"]
12436
+ ];
12360
12437
  });
12361
12438
 
12362
12439
  // ../cli/dist/utils/config.js
@@ -13381,8 +13458,27 @@ function buildActionsFromStatus(input) {
13381
13458
  autoApplicable: configStatus.status !== "newer"
13382
13459
  }));
13383
13460
  }
13461
+ const uncompiled = (input.staleReasons ?? []).find((reason) => reason.code === BRANCH_STORE_NOT_COMPILED);
13462
+ if (uncompiled !== undefined) {
13463
+ actions.push(migrationAction({
13464
+ id: "branch-store-compile",
13465
+ code: BRANCH_STORE_NOT_COMPILED,
13466
+ category: "storage",
13467
+ safety: "automatic",
13468
+ invocation: { kind: "cli", command_argv: ["kibi", "sync"] },
13469
+ affectedFiles: [
13470
+ typeof store?.path === "string" ? store.path : ".kb/branches"
13471
+ ],
13472
+ evidence: { reason: uncompiled },
13473
+ dependsOn: actions.map((action) => action.id).filter((id) => id === "branch-store-ensure" || id === "schema-config-upgrade"),
13474
+ postconditions: [{ branchStore: "compiled" }],
13475
+ autoApplicable: true
13476
+ }));
13477
+ }
13384
13478
  for (const reason of input.staleReasons ?? []) {
13385
13479
  const code = typeof reason.code === "string" ? reason.code : "stale_source";
13480
+ if (code === BRANCH_STORE_NOT_COMPILED)
13481
+ continue;
13386
13482
  const file = typeof reason.path === "string" ? reason.path : "";
13387
13483
  actions.push(migrationAction({
13388
13484
  id: `freshness-${code}-${file}`,
@@ -13448,6 +13544,7 @@ function buildActionsFromStatus(input) {
13448
13544
  }
13449
13545
  var MIGRATION_PLAN_VERSION = "kibi.migration-plan.v2", PREDICATE_SCHEMA_ALIGNMENT_CODE = "predicate_schema_alignment";
13450
13546
  var init_migration_plan = __esm(() => {
13547
+ init_branch_store();
13451
13548
  init_config();
13452
13549
  init_kb_manifest();
13453
13550
  init_schema6_check_actions();
@@ -13659,7 +13756,8 @@ async function executeStatus(_input, context) {
13659
13756
  }
13660
13757
  const snapshotEvidence = await readWorkspaceSnapshot(context);
13661
13758
  const existingReasons = payload.staleReasons ?? [];
13662
- const storeReason = branchStoreReason(store);
13759
+ const storeReason = uncompiledBranchStoreReason(context.workspaceRoot, store, attachment.kbBranch) ?? branchStoreReason(store);
13760
+ const storeUncompiled = storeReason?.code === BRANCH_STORE_NOT_COMPILED;
13663
13761
  const lockReason = storeLockJournalReason(store.path);
13664
13762
  const compilerReason = compilerFingerprintReason(store.path);
13665
13763
  const engineReason = engineStatus ? {
@@ -13682,6 +13780,7 @@ async function executeStatus(_input, context) {
13682
13780
  const enrichedPayload = {
13683
13781
  ...payload,
13684
13782
  ...compilerReason ? { syncState: "stale", dirty: true } : {},
13783
+ ...storeUncompiled ? { syncState: "stale", dirty: true } : {},
13685
13784
  branchAttachment: attachment,
13686
13785
  branchStore: store,
13687
13786
  ...engineStatus ? { engineStatus } : {},
@@ -13786,7 +13885,7 @@ async function executeStatus(_input, context) {
13786
13885
  content: [
13787
13886
  {
13788
13887
  type: "text",
13789
- text: `Branch ${payload.branch} is ${payload.syncState} (snapshot ${payload.snapshotId}, dirty=${payload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})`
13888
+ text: `Branch ${payload.branch} is ${enrichedPayload.syncState} (snapshot ${payload.snapshotId}, dirty=${enrichedPayload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})${storeUncompiled ? `. Blocking: ${String(storeReason?.detail)} Run kibi sync.` : ""}`
13790
13889
  }
13791
13890
  ],
13792
13891
  structuredContent: statusWithPlan
@@ -13988,10 +14087,21 @@ function sourceOf(payload) {
13988
14087
  function normalizeKey(value) {
13989
14088
  return value.trim().toLowerCase().replace(/['’]/g, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
13990
14089
  }
13991
- function singularize(value) {
13992
- if (["status", "results"].includes(value))
14090
+ function singularizeToken(value, keep = []) {
14091
+ if (value.length <= 3 || keep.includes(value))
13993
14092
  return value;
13994
- return value.endsWith("s") && value.length > 3 ? value.slice(0, -1) : value;
14093
+ if (value.endsWith("ies") && value.length > 4) {
14094
+ return `${value.slice(0, -3)}y`;
14095
+ }
14096
+ if (ES_PLURAL_SUFFIXES.some((suffix) => value.endsWith(suffix))) {
14097
+ return value.slice(0, -2);
14098
+ }
14099
+ if (!value.endsWith("s") || /(?:ss|us|is)$/.test(value))
14100
+ return value;
14101
+ return value.slice(0, -1);
14102
+ }
14103
+ function singularize(value) {
14104
+ return singularizeToken(value, ["status", "results"]);
13995
14105
  }
13996
14106
  function normalizeSubjectKey(value) {
13997
14107
  return normalizeKey(value).split("_").map(singularize).join(".");
@@ -14121,9 +14231,10 @@ function predicateSuggestion(payload, evidence, name, args, rationale, polarity
14121
14231
  } : null
14122
14232
  };
14123
14233
  }
14124
- var SEMANTIC_INVENTORY_VERSION = "kibi.semantic-inventory.v1";
14234
+ var SEMANTIC_INVENTORY_VERSION = "kibi.semantic-inventory.v1", ES_PLURAL_SUFFIXES;
14125
14235
  var init_shared = __esm(() => {
14126
14236
  init_clauses();
14237
+ ES_PLURAL_SUFFIXES = ["ches", "shes", "sses", "xes"];
14127
14238
  });
14128
14239
 
14129
14240
  // ../cli/dist/operations/semantic-advisor/ingestion-boundary.js
@@ -23280,6 +23391,12 @@ var init_rule_registry_generated = __esm(() => {
23280
23391
  enforcementClass: "canonical",
23281
23392
  category: "integrity"
23282
23393
  },
23394
+ {
23395
+ name: "branch-store-not-compiled",
23396
+ description: "The current branch's KB store must be compiled from the authored .kb/ sources; a missing or empty store is reported once, naming kibi sync, instead of every store-backed rule reading an empty KB",
23397
+ enforcementClass: "canonical",
23398
+ category: "integrity"
23399
+ },
23283
23400
  {
23284
23401
  name: "source-path-dangling",
23285
23402
  description: "An authored entity's source field must name an existing workspace path (a #anchor suffix is ignored), an existing entity id, or an http(s) URL; a missing source is allowed",
@@ -23614,7 +23731,9 @@ function buildStableRequirementIds(claim) {
23614
23731
  function buildStrictWriteSet(input) {
23615
23732
  const statement = normalizeStatement(input.statement);
23616
23733
  const confidence = normalizeConfidence(input.claim.confidence);
23617
- const ids = buildStableRequirementIds(input.claim);
23734
+ const minted = buildStableRequirementIds(input.claim);
23735
+ const requirementId = input.requirementId?.trim();
23736
+ const ids = requirementId ? { ...minted, reqId: requirementId } : minted;
23618
23737
  const textRef = normalizeTextRef(input.claim.provenance, input.claim.source);
23619
23738
  const metadataTags = buildMetadataTags({
23620
23739
  confidence,
@@ -23904,6 +24023,9 @@ function normalizeEntityDirectory(directory) {
23904
24023
  function buildEntitySourcePath(directory, entityId) {
23905
24024
  return `${directory}/${entityId}.md`;
23906
24025
  }
24026
+ function factSourcePath(factId) {
24027
+ return buildEntitySourcePath(resolveEntitySourcePaths().facts, factId);
24028
+ }
23907
24029
  var STRICT_CONFIDENCE_THRESHOLD = 0.7, SUBJECT_KEY_CONVENTION, ORDERING_OPERATORS;
23908
24030
  var init_strict_modeling = __esm(() => {
23909
24031
  init_kb_paths();
@@ -27187,7 +27309,11 @@ function reuseExistingSubject(writeSet, existingFactId) {
27187
27309
  subjectFact: {
27188
27310
  ...writeSet.subjectFact,
27189
27311
  id: existingFactId,
27190
- properties: { ...writeSet.subjectFact.properties, id: existingFactId }
27312
+ properties: {
27313
+ ...writeSet.subjectFact.properties,
27314
+ id: existingFactId,
27315
+ source: factSourcePath(existingFactId)
27316
+ }
27191
27317
  },
27192
27318
  relationships: writeSet.relationships.map((relationship) => relationship.type === "constrains" ? { ...relationship, to: existingFactId } : relationship)
27193
27319
  }
@@ -27213,10 +27339,11 @@ function possibleDuplicateObservation(claimKey, statement, source, candidates) {
27213
27339
  }))
27214
27340
  };
27215
27341
  }
27216
- async function applyVocabularyAlignment(context, extracted, claimKey) {
27342
+ async function applyVocabularyAlignment(context, extracted, claimKey, requirementId) {
27217
27343
  const initial = buildStrictWriteSet({
27218
27344
  claim: extracted.claim,
27219
- statement: extracted.statement
27345
+ statement: extracted.statement,
27346
+ ...requirementId !== undefined ? { requirementId } : {}
27220
27347
  });
27221
27348
  const unchanged = {
27222
27349
  writeSet: initial,
@@ -27244,7 +27371,8 @@ async function applyVocabularyAlignment(context, extracted, claimKey) {
27244
27371
  if (followsVocabulary) {
27245
27372
  const rebuilt = buildStrictWriteSet({
27246
27373
  claim: { ...extracted.claim, subjectKey: subject.subjectKey },
27247
- statement: extracted.statement
27374
+ statement: extracted.statement,
27375
+ ...requirementId !== undefined ? { requirementId } : {}
27248
27376
  });
27249
27377
  ({ writeSet, replacedSubjectId } = reuseExistingSubject(rebuilt, subject.existingFactId));
27250
27378
  } else {
@@ -27534,7 +27662,7 @@ async function handleKbModelRequirement(_prolog, args, workspaceRoot, context) {
27534
27662
  if (conditional?.kind === "unparsed")
27535
27663
  return unresolvedConditionalResult(args, extracted, conditional.reason, workspaceRoot);
27536
27664
  const claimKey = semanticClaimKey(extracted.statement);
27537
- const aligned = await applyVocabularyAlignment(context, extracted, claimKey);
27665
+ const aligned = await applyVocabularyAlignment(context, extracted, claimKey, normalizeOptionalString(args.requirementId));
27538
27666
  const writeSet = aligned.writeSet;
27539
27667
  const logicClaims = Array.from(new Set([...args.existingLogicClaims ?? [], claimKey]));
27540
27668
  const plan = aligned.adjustPlan(strictWriteSetToApplyPlan(writeSet));
@@ -43258,13 +43386,34 @@ function coverageForRequirement(result, manifestResults, entities) {
43258
43386
  scenarioTests
43259
43387
  };
43260
43388
  }
43389
+ function supersededRequirementIds(manifestResults) {
43390
+ return new Set(manifestResults.flatMap((result) => result.entity.type === "req" ? result.relationships.flatMap((relationship) => relationship.type === "supersedes" ? [relationship.to] : []) : []));
43391
+ }
43392
+ function isReviewableRequirement(requirement, superseded) {
43393
+ if (superseded.has(requirement.id))
43394
+ return false;
43395
+ const status = requirement.status.trim().toLowerCase();
43396
+ if (REVIEWED_REQUIREMENT_STATUSES.has(status))
43397
+ return true;
43398
+ return status === "closed" && (requirement.tags ?? []).includes("implemented");
43399
+ }
43261
43400
  function createCoverageDepthQualityDiagnostics(manifestResults, proofByRequirement = new Map) {
43262
43401
  const entities = entitiesById(manifestResults);
43263
- return manifestResults.filter((result) => result.entity.type === "req").flatMap((result) => {
43402
+ const superseded = supersededRequirementIds(manifestResults);
43403
+ return manifestResults.filter((result) => result.entity.type === "req" && isReviewableRequirement(result.entity, superseded)).flatMap((result) => {
43264
43404
  const diagnostic = createDiagnostic(coverageForRequirement(result, manifestResults, entities), proofByRequirement.get(result.entity.id));
43265
43405
  return diagnostic === undefined ? [] : [diagnostic];
43266
43406
  }).toSorted((left, right) => (left.entityId ?? "").localeCompare(right.entityId ?? ""));
43267
43407
  }
43408
+ var REVIEWED_REQUIREMENT_STATUSES;
43409
+ var init_coverage_depth_quality = __esm(() => {
43410
+ REVIEWED_REQUIREMENT_STATUSES = new Set([
43411
+ "open",
43412
+ "in_progress",
43413
+ "active",
43414
+ "approved"
43415
+ ]);
43416
+ });
43268
43417
 
43269
43418
  // ../cli/dist/public/impact/requirement-quality.js
43270
43419
  function isRequirement2(result) {
@@ -43937,6 +44086,7 @@ var init_full_kb_quality = __esm(() => {
43937
44086
  init_proof_receipt();
43938
44087
  init_relationship();
43939
44088
  init_telemetry_acceptance();
44089
+ init_coverage_depth_quality();
43940
44090
  init_requirement_quality();
43941
44091
  init_symbol_quality();
43942
44092
  RELATIONSHIP_TYPES = relationship_default.properties.type.enum;
@@ -44852,13 +45002,13 @@ var init_check_policy_rules = __esm(() => {
44852
45002
  });
44853
45003
 
44854
45004
  // ../cli/dist/operations/migration/kb-sources.js
44855
- import { existsSync as existsSync21, mkdirSync as mkdirSync7, readFileSync as readFileSync24, readdirSync as readdirSync5, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "node:fs";
45005
+ import { existsSync as existsSync21, mkdirSync as mkdirSync7, readFileSync as readFileSync24, readdirSync as readdirSync6, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "node:fs";
44856
45006
  import path45 from "node:path";
44857
45007
  import { isDeepStrictEqual } from "node:util";
44858
45008
  function walkMarkdown(directory, files) {
44859
45009
  let entries;
44860
45010
  try {
44861
- entries = readdirSync5(directory, { withFileTypes: true, encoding: "utf8" });
45011
+ entries = readdirSync6(directory, { withFileTypes: true, encoding: "utf8" });
44862
45012
  } catch {
44863
45013
  return;
44864
45014
  }
@@ -53447,7 +53597,7 @@ var require_out4 = __commonJS(function(exports2, module2) {
53447
53597
  import { execFile } from "node:child_process";
53448
53598
  import { createHash as createHash24 } from "node:crypto";
53449
53599
  import * as fs7 from "node:fs";
53450
- import { existsSync as existsSync23, readFileSync as readFileSync27, readdirSync as readdirSync6, unlinkSync as unlinkSync4 } from "node:fs";
53600
+ import { existsSync as existsSync23, readFileSync as readFileSync27, readdirSync as readdirSync7, unlinkSync as unlinkSync4 } from "node:fs";
53451
53601
  import * as path49 from "node:path";
53452
53602
  import { promisify } from "node:util";
53453
53603
  async function gitTrackedPaths(cwd) {
@@ -53461,7 +53611,7 @@ function pendingSourcePaths(cwd, recoverMissing) {
53461
53611
  }
53462
53612
  const paths = new Map;
53463
53613
  const recoveredReceipts = [];
53464
- for (const name of readdirSync6(root)) {
53614
+ for (const name of readdirSync7(root)) {
53465
53615
  if (!name.endsWith(".json"))
53466
53616
  continue;
53467
53617
  const receiptPath = path49.join(root, name);
@@ -53614,7 +53764,7 @@ async function discoverSourceFiles(cwd, options2 = {}) {
53614
53764
  }
53615
53765
  const pendingRoot = path49.join(cwd, ".kb", "recovery", "pending-sources");
53616
53766
  if (options2.consumeTrackedPendingReceipts !== false && existsSync23(pendingRoot)) {
53617
- for (const name of readdirSync6(pendingRoot)) {
53767
+ for (const name of readdirSync7(pendingRoot)) {
53618
53768
  if (!name.endsWith(".json"))
53619
53769
  continue;
53620
53770
  try {
@@ -54176,7 +54326,8 @@ __export(exports_check_executor, {
54176
54326
  buildSummary: () => buildSummary,
54177
54327
  executeCheck: () => executeCheck,
54178
54328
  getEffectiveRules: () => getEffectiveRules,
54179
- resolveCheckRules: () => resolveCheckRules
54329
+ resolveCheckRules: () => resolveCheckRules,
54330
+ uncompiledBranchStoreViolation: () => uncompiledBranchStoreViolation
54180
54331
  });
54181
54332
  function requireProlog2(context) {
54182
54333
  if (context.prolog === undefined) {
@@ -54212,6 +54363,33 @@ async function readStatusMigrationPlan(context) {
54212
54363
  throw error;
54213
54364
  }
54214
54365
  }
54366
+ function uncompiledBranchStoreViolation(context, workspaceRoot) {
54367
+ let attachment;
54368
+ try {
54369
+ attachment = context.branchAttachment ?? resolveBranchAttachment(workspaceRoot);
54370
+ } catch {
54371
+ return null;
54372
+ }
54373
+ if ("error" in attachment)
54374
+ return null;
54375
+ const store = inspectBranchStore(workspaceRoot, attachment.kbBranch);
54376
+ const reason = uncompiledBranchStoreReason(workspaceRoot, store, attachment.kbBranch);
54377
+ if (reason === null)
54378
+ return null;
54379
+ return {
54380
+ rule: BRANCH_STORE_NOT_COMPILED_RULE,
54381
+ entityId: attachment.kbBranch,
54382
+ description: String(reason.detail),
54383
+ suggestion: "Run kibi sync to compile this branch's store from the authored .kb/ sources (or apply the branch-store-compile action of the returned migrationPlan with kb_apply_plan), then rerun kb_check. Run kibi init once to install the git hooks that compile a new branch on checkout.",
54384
+ source: store.path,
54385
+ evidence: {
54386
+ store: { state: store.state, path: store.path },
54387
+ authoredSources: reason.authoredSources,
54388
+ ...reason.generation !== undefined ? { generation: reason.generation } : {},
54389
+ remediation: reason.remediation
54390
+ }
54391
+ };
54392
+ }
54215
54393
  async function executeCheck(args, context, options2 = {}) {
54216
54394
  try {
54217
54395
  const workspaceRoot = args.workspaceRoot ?? context.workspaceRoot;
@@ -54227,6 +54405,39 @@ async function executeCheck(args, context, options2 = {}) {
54227
54405
  ...stagedEntityIdStyleDiagnostics(workspaceRoot, args)
54228
54406
  ];
54229
54407
  const maxDiagnosticsOption = args.maxDiagnostics !== undefined ? { maxDiagnostics: args.maxDiagnostics } : {};
54408
+ const uncompiled = context.branchAttachment !== undefined || options2.branchStoreAttached === true ? uncompiledBranchStoreViolation(context, workspaceRoot) : null;
54409
+ if (uncompiled !== null) {
54410
+ const violations = [uncompiled];
54411
+ const statusPlan = await readStatusMigrationPlan(context);
54412
+ const migrationPlan = await migrationPlanForCheck(context, violations, [], statusPlan);
54413
+ return {
54414
+ content: [
54415
+ {
54416
+ type: "text",
54417
+ text: buildSummary({
54418
+ violations,
54419
+ impactResult,
54420
+ qualityDiagnostics: []
54421
+ })
54422
+ }
54423
+ ],
54424
+ structuredContent: buildStructuredContent({
54425
+ violations,
54426
+ diagnostics: [
54427
+ {
54428
+ category: "SYNC_ERROR",
54429
+ severity: "error",
54430
+ message: uncompiled.description,
54431
+ ...uncompiled.source !== undefined ? { file: uncompiled.source } : {},
54432
+ ...uncompiled.suggestion !== undefined ? { suggestion: uncompiled.suggestion } : {}
54433
+ }
54434
+ ],
54435
+ qualityDiagnostics: [],
54436
+ impactResult,
54437
+ migrationPlan
54438
+ })
54439
+ };
54440
+ }
54230
54441
  if (rulesAllowlist.size === 0) {
54231
54442
  const qualityDiagnostics = hasExplicitRules && options2.collectFullQualityDiagnosticsForExplicitRules !== true ? [] : impactResult ? impactQualityDiagnostics : await collectFullKbQualityDiagnostics({
54232
54443
  prolog,
@@ -54327,6 +54538,8 @@ async function executeCheck(args, context, options2 = {}) {
54327
54538
  }
54328
54539
  var init_check_executor = __esm(() => {
54329
54540
  init_aggregated_checks();
54541
+ init_branch_resolver();
54542
+ init_branch_store();
54330
54543
  init_impact_diagnostics();
54331
54544
  init_check_helpers();
54332
54545
  init_check_policy_rules();
@@ -54340,7 +54553,7 @@ var init_check_executor = __esm(() => {
54340
54553
  });
54341
54554
 
54342
54555
  // ../cli/dist/public/operations/specs/check-rules.generated.js
54343
- var SELECTABLE_RULE_NAMES, SELECTABLE_RULES_ALLOWED = "must-priority-coverage, symbol-coverage, symbol-traceability, no-dangling-refs, source-relationship-parity, source-path-dangling, no-cycles, required-fields, deprecated-adr-no-successor, superseded-requirement-open, scenario-feasibility, scenario-feasibility-unknown, exception-claim-keys, numeric-string-value, rule-key-arguments-missing, domain-contradictions, logic-coverage, rule-safety, rule-verifiability, query-plan-safety, req-status-vocabulary, strict-fact-shape, strict-req-fact-pairing, predicate-verifiability, strict-readiness, semantic-completeness, related-requirement-unmodeled, proof-contract-symbols, entity-id-style, domain-redundancy, domain-implication, subject-key-identity, subject-key-shape, ontology-quality, exception-unapproved, exception-approval-self-attested, agent-requirement-unapproved, requirement-rationale-missing, symbol-owner-superseded, adr-unlinked, adr-proposed, predicate-schema-conformance, entity-context-missing, entity-context-acknowledged, policy-ownership, policy-markers";
54556
+ var SELECTABLE_RULE_NAMES, SELECTABLE_RULES_ALLOWED = "must-priority-coverage, symbol-coverage, symbol-traceability, no-dangling-refs, source-relationship-parity, branch-store-not-compiled, source-path-dangling, no-cycles, required-fields, deprecated-adr-no-successor, superseded-requirement-open, scenario-feasibility, scenario-feasibility-unknown, exception-claim-keys, numeric-string-value, rule-key-arguments-missing, domain-contradictions, logic-coverage, rule-safety, rule-verifiability, query-plan-safety, req-status-vocabulary, strict-fact-shape, strict-req-fact-pairing, predicate-verifiability, strict-readiness, semantic-completeness, related-requirement-unmodeled, proof-contract-symbols, entity-id-style, domain-redundancy, domain-implication, subject-key-identity, subject-key-shape, ontology-quality, exception-unapproved, exception-approval-self-attested, agent-requirement-unapproved, requirement-rationale-missing, symbol-owner-superseded, adr-unlinked, adr-proposed, predicate-schema-conformance, entity-context-missing, entity-context-acknowledged, policy-ownership, policy-markers";
54344
54557
  var init_check_rules_generated = __esm(() => {
54345
54558
  SELECTABLE_RULE_NAMES = [
54346
54559
  "must-priority-coverage",
@@ -54348,6 +54561,7 @@ var init_check_rules_generated = __esm(() => {
54348
54561
  "symbol-traceability",
54349
54562
  "no-dangling-refs",
54350
54563
  "source-relationship-parity",
54564
+ "branch-store-not-compiled",
54351
54565
  "source-path-dangling",
54352
54566
  "no-cycles",
54353
54567
  "required-fields",
@@ -55417,9 +55631,7 @@ function normalizePredicateToken(value) {
55417
55631
  return value.trim().replace(/\b(?:a|an|the)\b\s*/gi, "").replace(/['’]/g, "").replace(/\s+/g, "_").replace(/[^A-Za-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "");
55418
55632
  }
55419
55633
  function singularize2(value) {
55420
- if (["changes", "status", "results"].includes(value))
55421
- return value;
55422
- return value.endsWith("s") && value.length > 3 ? value.slice(0, -1) : value;
55634
+ return singularizeToken(value, ["changes", "status", "results"]);
55423
55635
  }
55424
55636
  function normalizeSubjectKey3(value) {
55425
55637
  return slug2(value).split("_").map(singularize2).join(".");
@@ -55491,7 +55703,9 @@ function inferEvent(text) {
55491
55703
  return eventName ?? "domain_event";
55492
55704
  }
55493
55705
  var DEFAULT_MIN_SCORE = 0.35;
55494
- var init_predicate_utils = () => {};
55706
+ var init_predicate_utils = __esm(() => {
55707
+ init_shared();
55708
+ });
55495
55709
 
55496
55710
  // ../cli/dist/operations/modeling/predicate-ranker.js
55497
55711
  function tokens3(value) {
@@ -55836,7 +56050,10 @@ var init_predicate_applicability = __esm(() => {
55836
56050
  permission_rule: {
55837
56051
  allOf: [
55838
56052
  [/only|may|can|allowed|denied|forbidden|must\s+not|cannot/i],
55839
- [/delete|export|access|read|write|update|remove|manage|permission/i]
56053
+ [
56054
+ /delete|export|access|read|write|update|remove|manage|permission/i,
56055
+ /\b(?:may|can|cannot|can't|must\s+(?:not|never)|never|(?:is|are)\s+(?:not\s+)?(?:allowed|permitted|forbidden)\s+to)\s+(?:only\s+)?(?:publish|unpublish|approve|assign|view|see|edit|modify|create|submit|share|invite|upload|download|comment|post|complete|archive|restore|grant|revoke|purchase|cancel|execute|invoke|configure|moderate|ban|join|book|reserve|transfer|close|reopen|merge)\b/i
56056
+ ]
55840
56057
  ]
55841
56058
  },
55842
56059
  resource_constraint: {
@@ -57028,8 +57245,9 @@ function buildSuggestion(schema, text, subject, score, argumentBindings = {}, po
57028
57245
  constrainedSubjects
57029
57246
  })
57030
57247
  ]));
57031
- const subjectKey = constrainedSubjects.length === 0 ? null : subjectArgument >= 0 ? predicateArgs[subjectArgument] ?? null : reviewedSubject ?? null;
57032
- const subjectPairing = constrainedSubjects.length === 0 ? "not_required" : subjectKey !== null && constrainedSubjects.includes(subjectKey) ? "paired" : "unpaired";
57248
+ const plannedSubjects = constrainedSubjects.length > 0 ? constrainedSubjects : explicitSubject ? [subject] : [];
57249
+ const subjectKey = plannedSubjects.length === 0 ? null : subjectArgument >= 0 ? predicateArgs[subjectArgument] ?? null : reviewedSubject ?? null;
57250
+ const subjectPairing = plannedSubjects.length === 0 ? "not_required" : subjectKey !== null && plannedSubjects.includes(subjectKey) ? "paired" : "unpaired";
57033
57251
  const bindingProvenance = aggregateBindingProvenance(Object.values(bindingProvenanceByArgument));
57034
57252
  const unboundArguments = schema.argument_names.filter((name) => undeclaredArguments.has(name) || !bindingCanBeApplied(bindingProvenanceByArgument[name] ?? "placeholder"));
57035
57253
  if (subjectPairing === "unpaired") {
@@ -57831,7 +58049,7 @@ var init_modeling = __esm(() => {
57831
58049
  },
57832
58050
  requirementId: {
57833
58051
  type: "string",
57834
- description: "Optional existing requirement ID to receive the requires_rule relationship."
58052
+ description: "Optional requirement ID. The strict write set updates this requirement instead of minting a REQ-AUTO id, and a typed logic rule attaches its requires_rule relationship to it."
57835
58053
  },
57836
58054
  claimKey: {
57837
58055
  type: "string",
@@ -61714,7 +61932,7 @@ var init_validation3 = __esm(() => {
61714
61932
  });
61715
61933
 
61716
61934
  // ../cli/dist/public/skill-system/loader.js
61717
- import { existsSync as existsSync25, readFileSync as readFileSync29, readdirSync as readdirSync7, realpathSync as realpathSync8 } from "node:fs";
61935
+ import { existsSync as existsSync25, readFileSync as readFileSync29, readdirSync as readdirSync8, realpathSync as realpathSync8 } from "node:fs";
61718
61936
  import { dirname as dirname11, join as join20, resolve as resolve16 } from "node:path";
61719
61937
  import { fileURLToPath as fileURLToPath4 } from "node:url";
61720
61938
  function listBundledSkills() {
@@ -61735,7 +61953,7 @@ function readBundledSkillResource(id, resourcePath) {
61735
61953
  function listFrom(dir) {
61736
61954
  if (!existsSync25(dir))
61737
61955
  return [];
61738
- return readdirSync7(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join20(dir, entry.name)).filter((root) => existsSync25(join20(root, SKILL_FILE_NAME))).map((root) => parseSkillBundle(root).manifest).sort((a, b) => a.id.localeCompare(b.id));
61956
+ return readdirSync8(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join20(dir, entry.name)).filter((root) => existsSync25(join20(root, SKILL_FILE_NAME))).map((root) => parseSkillBundle(root).manifest).sort((a, b) => a.id.localeCompare(b.id));
61739
61957
  }
61740
61958
  function findRoot(dir, id) {
61741
61959
  return listRoots(dir).find((root) => parseSkillBundle(root).manifest.id === id);
@@ -61743,7 +61961,7 @@ function findRoot(dir, id) {
61743
61961
  function listRoots(dir) {
61744
61962
  if (!existsSync25(dir))
61745
61963
  return [];
61746
- return readdirSync7(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join20(dir, entry.name)).filter((root) => existsSync25(join20(root, SKILL_FILE_NAME)));
61964
+ return readdirSync8(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join20(dir, entry.name)).filter((root) => existsSync25(join20(root, SKILL_FILE_NAME)));
61747
61965
  }
61748
61966
  function readBundle(bundle, id, resourcePath) {
61749
61967
  const resource = normalizeResourcePath(resourcePath);
@@ -61898,6 +62116,28 @@ function requiredFields(spec) {
61898
62116
  const required = spec.businessInputSchema.required;
61899
62117
  return Array.isArray(required) ? required : [];
61900
62118
  }
62119
+ function foreignParameterWarnings(selector, choice, routes, rest) {
62120
+ const route = routes[choice];
62121
+ if (route === undefined)
62122
+ return [];
62123
+ const own = new Set(Object.keys(properties2(route)));
62124
+ const equivalents = route.name === undefined ? {} : EQUIVALENT_PARAMETERS[route.name] ?? {};
62125
+ const warnings = [];
62126
+ for (const parameter of Object.keys(rest)) {
62127
+ if (own.has(parameter) || rest[parameter] === undefined)
62128
+ continue;
62129
+ const owners = Object.entries(routes).filter(([other, otherRoute]) => other !== choice && (parameter in properties2(otherRoute))).map(([other]) => `${selector}: ${other}`);
62130
+ if (owners.length === 0)
62131
+ continue;
62132
+ const equivalent = equivalents[parameter];
62133
+ warnings.push({
62134
+ parameter,
62135
+ message: `${parameter} is a ${owners.join(" / ")} argument; ${selector}: ${choice} ignored it.`,
62136
+ nextAction: equivalent !== undefined ? `Use ${equivalent} in ${selector}: ${choice}.` : `Drop ${parameter}, or switch to ${owners.join(" / ")} if that is what you meant.`
62137
+ });
62138
+ }
62139
+ return warnings;
62140
+ }
61901
62141
  async function dispatchComposite(selector, routes, input, context) {
61902
62142
  const choice = input[selector];
61903
62143
  const route = typeof choice === "string" ? routes[choice] : undefined;
@@ -61916,16 +62156,46 @@ async function dispatchComposite(selector, routes, input, context) {
61916
62156
  const structured = wrapped ? result.structuredContent : result;
61917
62157
  const payload = isRecord(structured) ? structured : {};
61918
62158
  const content = wrapped && Array.isArray(result.content) ? result.content : [{ type: "text", text: `${selector} ${String(choice)} completed` }];
62159
+ const ignored = foreignParameterWarnings(selector, String(choice), routes, rest);
62160
+ if (ignored.length === 0) {
62161
+ return {
62162
+ content,
62163
+ structuredContent: { [selector]: choice, ...payload }
62164
+ };
62165
+ }
62166
+ const asRecords = route.name !== undefined && RECORD_WARNING_OPERATIONS.has(route.name);
62167
+ const routedWarnings = Array.isArray(payload.warnings) ? {
62168
+ warnings: [
62169
+ ...payload.warnings,
62170
+ ...ignored.map((warning) => asRecords ? {
62171
+ kind: "parameter_ignored",
62172
+ message: warning.message,
62173
+ nextAction: warning.nextAction
62174
+ } : `${warning.message} ${warning.nextAction}`)
62175
+ ]
62176
+ } : {};
61919
62177
  return {
61920
- content,
61921
- structuredContent: { [selector]: choice, ...payload }
62178
+ content: [
62179
+ ...content,
62180
+ {
62181
+ type: "text",
62182
+ text: ignored.map((warning) => `${warning.message} ${warning.nextAction}`).join(`
62183
+ `)
62184
+ }
62185
+ ],
62186
+ structuredContent: { [selector]: choice, ...payload, ...routedWarnings }
61922
62187
  };
61923
62188
  }
61924
- var SKILL_ROUTES, MODEL_ROUTES, skillsSpec, modelSpec;
62189
+ var EQUIVALENT_PARAMETERS, RECORD_WARNING_OPERATIONS, SKILL_ROUTES, MODEL_ROUTES, skillsSpec, modelSpec;
61925
62190
  var init_composite = __esm(() => {
61926
62191
  init_modeling();
61927
62192
  init_semantic();
61928
62193
  init_skills2();
62194
+ EQUIVALENT_PARAMETERS = {
62195
+ kb_suggest_predicates: { subjectKey: "subjectHint" },
62196
+ kb_model_requirement: { subjectHint: "subjectKey" }
62197
+ };
62198
+ RECORD_WARNING_OPERATIONS = new Set(["kb_model_requirement"]);
61929
62199
  SKILL_ROUTES = {
61930
62200
  list: skillsListSpec,
61931
62201
  load: skillsLoadSpec,
@@ -64193,7 +64463,7 @@ var init_source_authoring = __esm(() => {
64193
64463
 
64194
64464
  // ../cli/dist/operations/mutation/plan-apply-journal.js
64195
64465
  import { createHash as createHash28, randomUUID as randomUUID3 } from "node:crypto";
64196
- import { closeSync as closeSync3, existsSync as existsSync26, fsyncSync as fsyncSync2, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync30, readdirSync as readdirSync8, renameSync as renameSync5, statSync as statSync10, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "node:fs";
64466
+ import { closeSync as closeSync3, existsSync as existsSync26, fsyncSync as fsyncSync2, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync30, readdirSync as readdirSync9, renameSync as renameSync5, statSync as statSync10, unlinkSync as unlinkSync5, writeSync as writeSync2 } from "node:fs";
64197
64467
  import path53 from "node:path";
64198
64468
  function planApplyJournalId(planHash) {
64199
64469
  return `plan-apply-${planHash.slice(0, 16).toLowerCase()}`;
@@ -64433,7 +64703,7 @@ function removeOrphanedStagedFiles(workspaceRoot, files) {
64433
64703
  const prefix = `${path53.basename(absolute)}.kibi-stage-`;
64434
64704
  let names;
64435
64705
  try {
64436
- names = readdirSync8(path53.dirname(absolute));
64706
+ names = readdirSync9(path53.dirname(absolute));
64437
64707
  } catch {
64438
64708
  continue;
64439
64709
  }
@@ -64572,7 +64842,7 @@ async function recoverPendingPlanApplyJournals(context, options2) {
64572
64842
  for (const directory of location.all) {
64573
64843
  let names;
64574
64844
  try {
64575
- names = readdirSync8(directory);
64845
+ names = readdirSync9(directory);
64576
64846
  } catch {
64577
64847
  continue;
64578
64848
  }
@@ -68895,7 +69165,7 @@ var init_staging = __esm(() => {
68895
69165
  // ../cli/dist/commands/sync.js
68896
69166
  import { execSync as execSync4 } from "node:child_process";
68897
69167
  import { createHash as createHash32 } from "node:crypto";
68898
- import { existsSync as existsSync34, mkdirSync as mkdirSync12, readFileSync as readFileSync40, readdirSync as readdirSync9, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "node:fs";
69168
+ import { existsSync as existsSync34, mkdirSync as mkdirSync12, readFileSync as readFileSync40, readdirSync as readdirSync10, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "node:fs";
68899
69169
  import * as path66 from "node:path";
68900
69170
  function relationshipKey2(relationship) {
68901
69171
  return `${relationship.type}\x00${relationship.from}\x00${relationship.to}`;
@@ -68940,7 +69210,7 @@ function trackedRelationshipFiles(workspaceRoot, relationshipsDir, recoverMissin
68940
69210
  const pending = new Set;
68941
69211
  const pendingRoot = path66.join(workspaceRoot, ".kb", "recovery", "pending-sources");
68942
69212
  if (existsSync34(pendingRoot)) {
68943
- for (const receiptName of readdirSync9(pendingRoot)) {
69213
+ for (const receiptName of readdirSync10(pendingRoot)) {
68944
69214
  if (!receiptName.endsWith(".json"))
68945
69215
  continue;
68946
69216
  let receipt;
@@ -68982,7 +69252,7 @@ function trackedRelationshipFiles(workspaceRoot, relationshipsDir, recoverMissin
68982
69252
  }
68983
69253
  }
68984
69254
  try {
68985
- return readdirSync9(relationshipsDir).filter((file) => file.endsWith(".yaml") || file.endsWith(".yml")).map((file) => path66.join(relationshipsDir, file)).filter((file) => {
69255
+ return readdirSync10(relationshipsDir).filter((file) => file.endsWith(".yaml") || file.endsWith(".yml")).map((file) => path66.join(relationshipsDir, file)).filter((file) => {
68986
69256
  const relative = path66.relative(workspaceRoot, file).replaceAll(path66.sep, "/");
68987
69257
  return tracked.has(relative) || pending.has(relative);
68988
69258
  }).sort();
@@ -70946,7 +71216,7 @@ ${KIBI_BIN_RESOLVER}
70946
71216
 
70947
71217
  // ../cli/dist/commands/legacy-storage-migration.js
70948
71218
  import { createHash as createHash35 } from "node:crypto";
70949
- import { existsSync as existsSync37, mkdirSync as mkdirSync15, readFileSync as readFileSync43, readdirSync as readdirSync11, renameSync as renameSync12, rmSync as rmSync5, statSync as statSync12, writeFileSync as writeFileSync18 } from "node:fs";
71219
+ import { existsSync as existsSync37, mkdirSync as mkdirSync15, readFileSync as readFileSync43, readdirSync as readdirSync12, renameSync as renameSync12, rmSync as rmSync5, statSync as statSync12, writeFileSync as writeFileSync18 } from "node:fs";
70950
71220
  import * as path72 from "node:path";
70951
71221
  function stripGlobToRoot(pattern) {
70952
71222
  const normalized = pattern.replaceAll("\\", "/").replace(/\/+$/, "");
@@ -71103,7 +71373,7 @@ function pruneEmptyLegacyDirs(cwd, plan) {
71103
71373
  const ordered = [...roots].sort((a, b) => b.length - a.length);
71104
71374
  for (const dir of ordered) {
71105
71375
  try {
71106
- if (statSync12(dir).isDirectory() && readdirSync11(dir).length === 0) {
71376
+ if (statSync12(dir).isDirectory() && readdirSync12(dir).length === 0) {
71107
71377
  rmSync5(dir, { recursive: true });
71108
71378
  }
71109
71379
  } catch {}
@@ -71115,7 +71385,7 @@ function rewritePendingSourceReceiptPaths(cwd, moves) {
71115
71385
  return 0;
71116
71386
  const moveByFrom = new Map(moves.map((move) => [move.from, move.to]));
71117
71387
  let rewritten = 0;
71118
- for (const name of readdirSync11(pendingRoot)) {
71388
+ for (const name of readdirSync12(pendingRoot)) {
71119
71389
  if (!name.endsWith(".json"))
71120
71390
  continue;
71121
71391
  const receiptPath = path72.join(pendingRoot, name);
@@ -71412,7 +71682,7 @@ var init_entity_body_context2 = __esm(() => {
71412
71682
  });
71413
71683
 
71414
71684
  // ../cli/dist/public/ignore-policy.js
71415
- import { existsSync as existsSync38, readFileSync as readFileSync44, readdirSync as readdirSync12 } from "node:fs";
71685
+ import { existsSync as existsSync38, readFileSync as readFileSync44, readdirSync as readdirSync13 } from "node:fs";
71416
71686
  import * as path73 from "node:path";
71417
71687
  function readIgnoreFileLines(filePath) {
71418
71688
  if (!existsSync38(filePath))
@@ -71437,7 +71707,7 @@ function createRepoIgnorePolicy(workspaceRoot) {
71437
71707
  function walk(dirAbs) {
71438
71708
  let entries;
71439
71709
  try {
71440
- entries = readdirSync12(dirAbs, { withFileTypes: true });
71710
+ entries = readdirSync13(dirAbs, { withFileTypes: true });
71441
71711
  } catch {
71442
71712
  return;
71443
71713
  }
@@ -74732,6 +75002,17 @@ var init_cli_operation_metadata = __esm(() => {
74732
75002
  // ../cli/dist/cli-validate.js
74733
75003
  var init_cli_validate = () => {};
74734
75004
 
75005
+ // ../cli/dist/operations/planning/migration-refusal.js
75006
+ var MigrationActionRefusedError, MIGRATION_PLAN_REFUSED = "MIGRATION_PLAN_REFUSED";
75007
+ var init_migration_refusal = __esm(() => {
75008
+ MigrationActionRefusedError = class MigrationActionRefusedError extends Error {
75009
+ constructor(message) {
75010
+ super(message);
75011
+ this.name = "MigrationActionRefusedError";
75012
+ }
75013
+ };
75014
+ });
75015
+
74735
75016
  // ../cli/dist/public/operations/result-envelope.js
74736
75017
  function record4(value) {
74737
75018
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -74823,23 +75104,33 @@ function extraFailedEffects(declared, data) {
74823
75104
  function resultVersion(spec) {
74824
75105
  return spec.resultVersion ?? `kibi.${spec.name}.v1`;
74825
75106
  }
75107
+ function refusalDetail(row) {
75108
+ const results = Array.isArray(row.actionResults) ? row.actionResults : [];
75109
+ const failure = results.find((entry) => record4(entry) && entry.outcome === "failed");
75110
+ return record4(failure) && typeof failure.detail === "string" ? failure.detail : "";
75111
+ }
74826
75112
  function toKibiResult(spec, data, options2 = {}) {
74827
75113
  const row = record4(data) ? data : undefined;
75114
+ const migrationRefused = row?.version === "kibi.migration-apply-result.v1" && row.outcome === "refused";
74828
75115
  let status = options2.status;
74829
75116
  if (!status) {
74830
75117
  status = "success";
74831
75118
  if (row?.status === "committed_with_repairs")
74832
75119
  status = "committed_with_repairs";
74833
- if (row?.status === "rejected")
75120
+ if (row?.status === "rejected" || migrationRefused)
74834
75121
  status = "error";
74835
75122
  }
74836
75123
  const terminalError = row?.status === "rejected" ? {
74837
75124
  code: "BOOTSTRAP_PLAN_REJECTED",
74838
75125
  message: "Bootstrap stopped at a deterministic failure. Inspect actionResults for committed actions and re-plan from the current state.",
74839
75126
  retryable: false
75127
+ } : migrationRefused ? {
75128
+ code: MIGRATION_PLAN_REFUSED,
75129
+ message: `Migration plan refused; nothing was changed. ${refusalDetail(row)}`.trim(),
75130
+ retryable: false
74840
75131
  } : undefined;
74841
75132
  const resultError = options2.error ?? terminalError;
74842
- const outcome = effectOutcome(status, options2);
75133
+ const outcome = effectOutcome(status, migrationRefused && options2.attempted === undefined ? { ...options2, attempted: false } : options2);
74843
75134
  return {
74844
75135
  kibiProtocol: KIBI_PROTOCOL_VERSION,
74845
75136
  operation: spec.name,
@@ -74888,6 +75179,9 @@ function appendPayloadCountField(fields, countKey, noun, payload) {
74888
75179
  fields.result_summary = count === null ? `${noun} count unavailable` : `${count} ${noun}`;
74889
75180
  }
74890
75181
  var KIBI_PROTOCOL_VERSION = 1;
75182
+ var init_result_envelope = __esm(() => {
75183
+ init_migration_refusal();
75184
+ });
74891
75185
 
74892
75186
  // ../cli/dist/cli-protocol.js
74893
75187
  var import__2020, outputValidator;
@@ -74897,6 +75191,7 @@ var init_cli_protocol = __esm(() => {
74897
75191
  init_cli_operation_metadata();
74898
75192
  init_cli_validate();
74899
75193
  init_engine_limits();
75194
+ init_result_envelope();
74900
75195
  init_detached_snapshot();
74901
75196
  import__2020 = __toESM(require__2020(), 1);
74902
75197
  outputValidator = new import__2020.default({ strict: false, allErrors: true });
@@ -74905,6 +75200,7 @@ var init_cli_protocol = __esm(() => {
74905
75200
  // ../cli/dist/public/diagnostic-usage.js
74906
75201
  var RECEIPT_GAP_CODES;
74907
75202
  var init_diagnostic_usage = __esm(() => {
75203
+ init_result_envelope();
74908
75204
  RECEIPT_GAP_CODES = new Set([
74909
75205
  "missing_proof_receipt",
74910
75206
  "stale_proof_receipt",
@@ -75632,9 +75928,221 @@ var init_migrate = __esm(() => {
75632
75928
  ]);
75633
75929
  });
75634
75930
 
75635
- // ../cli/dist/operations/planning/compile-intent.js
75636
- import { createHash as createHash39 } from "node:crypto";
75931
+ // ../cli/dist/proof/integrations.js
75932
+ import { existsSync as existsSync41, readFileSync as readFileSync48 } from "node:fs";
75637
75933
  import path79 from "node:path";
75934
+ function integrationsPath(workspaceRoot) {
75935
+ return path79.join(workspaceRoot, PROOF_INTEGRATIONS_PATH);
75936
+ }
75937
+ function proofIntegrationErrors(value) {
75938
+ const file = record5(value);
75939
+ if (!file)
75940
+ return [
75941
+ `${PROOF_INTEGRATIONS_PATH} must be an object with version and integrations`
75942
+ ];
75943
+ const errors = [];
75944
+ if (file.version !== PROOF_INTEGRATION_VERSION)
75945
+ errors.push(`version must be ${PROOF_INTEGRATION_VERSION}; received ${JSON.stringify(file.version ?? null)}`);
75946
+ if (!Array.isArray(file.integrations) || file.integrations.length === 0) {
75947
+ errors.push("integrations must be a non-empty array");
75948
+ return errors;
75949
+ }
75950
+ const seen = new Set;
75951
+ file.integrations.forEach((entry, index) => {
75952
+ const row = record5(entry);
75953
+ const label = `integrations[${index}]`;
75954
+ if (!row) {
75955
+ errors.push(`${label} must be an object`);
75956
+ return;
75957
+ }
75958
+ if (!nonEmpty(row.id))
75959
+ errors.push(`${label}.id must be a non-empty string`);
75960
+ else {
75961
+ if (seen.has(String(row.id)))
75962
+ errors.push(`${label}.id duplicates '${String(row.id)}'`);
75963
+ seen.add(String(row.id));
75964
+ }
75965
+ if (!nonEmpty(row.producer))
75966
+ errors.push(`${label}.producer must be one of: ${PRODUCER_KINDS.join(", ")} (or a custom producer id)`);
75967
+ if (!Array.isArray(row.command) || row.command.length === 0 || !row.command.every((argument) => nonEmpty(argument)))
75968
+ errors.push(`${label}.command must be a non-empty argv array`);
75969
+ if (nonEmpty(row.producer) && (row.producer === "junit" || row.producer === "tap") && !nonEmpty(row.artifact))
75970
+ errors.push(`${label}.artifact is required for producer '${String(row.producer)}' (path of the native report to convert)`);
75971
+ if (row.targets !== undefined) {
75972
+ if (!Array.isArray(row.targets) || row.targets.length === 0 || !row.targets.every((target) => nonEmpty(target)))
75973
+ errors.push(`${label}.targets must be a non-empty array of target names`);
75974
+ }
75975
+ if (row.options !== undefined && !record5(row.options))
75976
+ errors.push(`${label}.options must be an object when present`);
75977
+ if (row.description !== undefined && !nonEmpty(row.description))
75978
+ errors.push(`${label}.description must be a non-empty string when present`);
75979
+ });
75980
+ return errors;
75981
+ }
75982
+ function loadProofIntegrations(workspaceRoot) {
75983
+ const filePath = integrationsPath(workspaceRoot);
75984
+ if (!existsSync41(filePath)) {
75985
+ return {
75986
+ available: false,
75987
+ error: `No proof integration configuration at ${PROOF_INTEGRATIONS_PATH}. Run kibi proof inspect --json and apply its integrationPlan with kb_apply_plan to configure one.`
75988
+ };
75989
+ }
75990
+ let parsed;
75991
+ try {
75992
+ parsed = JSON.parse(readFileSync48(filePath, "utf8"));
75993
+ } catch (error) {
75994
+ return {
75995
+ available: false,
75996
+ error: `${PROOF_INTEGRATIONS_PATH} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
75997
+ };
75998
+ }
75999
+ const errors = proofIntegrationErrors(parsed);
76000
+ if (errors.length > 0)
76001
+ return {
76002
+ available: false,
76003
+ error: `Invalid ${PROOF_INTEGRATIONS_PATH}: ${errors.join("; ")}`
76004
+ };
76005
+ return { available: true, integrations: parsed };
76006
+ }
76007
+ function resolveIntegration(integrations, id) {
76008
+ return integrations.integrations.find((entry) => entry.id === id) ?? null;
76009
+ }
76010
+ function toExecution(integration) {
76011
+ return {
76012
+ id: integration.id,
76013
+ producer: integration.producer,
76014
+ ...integration.producer_version !== undefined ? { producer_version: integration.producer_version } : {},
76015
+ command: integration.command,
76016
+ ...integration.artifact !== undefined ? { artifact: integration.artifact } : {},
76017
+ ...integration.targets !== undefined ? { targets: integration.targets } : {},
76018
+ ...integration.options !== undefined ? { options: integration.options } : {}
76019
+ };
76020
+ }
76021
+ function record5(value) {
76022
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
76023
+ }
76024
+ function nonEmpty(value) {
76025
+ return typeof value === "string" && value.trim().length > 0;
76026
+ }
76027
+ var PROOF_INTEGRATIONS_PATH = ".kb/proof/integrations.json", PRODUCER_KINDS;
76028
+ var init_integrations = __esm(() => {
76029
+ init_proof_protocol();
76030
+ PRODUCER_KINDS = [
76031
+ "command",
76032
+ "playwright",
76033
+ "junit",
76034
+ "tap"
76035
+ ];
76036
+ });
76037
+
76038
+ // ../cli/dist/proof/integration-plan.js
76039
+ import { createHash as createHash39 } from "node:crypto";
76040
+ import { existsSync as existsSync42, mkdirSync as mkdirSync17, readFileSync as readFileSync49, renameSync as renameSync14, writeFileSync as writeFileSync20 } from "node:fs";
76041
+ import path80 from "node:path";
76042
+ function sha2563(bytes) {
76043
+ return createHash39("sha256").update(bytes).digest("hex");
76044
+ }
76045
+ function fileHash3(filePath) {
76046
+ try {
76047
+ return sha2563(readFileSync49(filePath));
76048
+ } catch {
76049
+ return null;
76050
+ }
76051
+ }
76052
+ function sourceHashes(root) {
76053
+ const hashes = {};
76054
+ for (const file of PROPOSAL_SOURCES) {
76055
+ const hash = fileHash3(path80.join(root, file));
76056
+ if (hash !== null)
76057
+ hashes[file] = hash;
76058
+ }
76059
+ return hashes;
76060
+ }
76061
+ function readIntegrationsFile(root) {
76062
+ try {
76063
+ const parsed = JSON.parse(readFileSync49(integrationsPath(root), "utf8"));
76064
+ return proofIntegrationErrors(parsed).length === 0 ? parsed : null;
76065
+ } catch {
76066
+ return null;
76067
+ }
76068
+ }
76069
+ function isRecord22(value) {
76070
+ return value !== null && typeof value === "object" && !Array.isArray(value);
76071
+ }
76072
+ function applyProofIntegrationAction(action, root) {
76073
+ const evidence = action.evidence;
76074
+ const integration = evidence.integration;
76075
+ if (evidence.path !== PROOF_INTEGRATIONS_PATH || evidence.mode !== "create" && evidence.mode !== "update" || !isRecord22(integration) || typeof integration.id !== "string")
76076
+ throw new MigrationActionRefusedError(`Proof integration action '${action.id}' is malformed; rerun kibi proof inspect for a new plan.`);
76077
+ const planned = isRecord22(evidence.sourceHashes) ? evidence.sourceHashes : {};
76078
+ const live = sourceHashes(root);
76079
+ const changed = [
76080
+ ...new Set([...Object.keys(planned), ...Object.keys(live)])
76081
+ ].filter((file) => planned[file] !== live[file]);
76082
+ if (changed.length > 0)
76083
+ throw new MigrationActionRefusedError(`Proof integration plan refused: ${changed.join(", ")} changed since planning; rerun kibi proof inspect and review the new plan.`);
76084
+ const filePath = integrationsPath(root);
76085
+ let integrations;
76086
+ if (evidence.mode === "create") {
76087
+ if (existsSync42(filePath))
76088
+ throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} already exists and this plan only creates it. Run kibi proof inspect --update <integration id> for a plan that adds or replaces one named integration.`);
76089
+ integrations = [integration];
76090
+ } else {
76091
+ if (fileHash3(filePath) !== evidence.previousFileHash)
76092
+ throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} changed since planning; rerun kibi proof inspect --update ${integration.id}.`);
76093
+ const current = readIntegrationsFile(root);
76094
+ if (current === null)
76095
+ throw new MigrationActionRefusedError(`Proof integration plan refused: ${PROOF_INTEGRATIONS_PATH} is not a valid ${PROOF_INTEGRATION_VERSION} file.`);
76096
+ integrations = current.integrations.some((entry) => entry.id === integration.id) ? current.integrations.map((entry) => entry.id === integration.id ? integration : entry) : [...current.integrations, integration];
76097
+ }
76098
+ const file = { version: PROOF_INTEGRATION_VERSION, integrations };
76099
+ const errors = proofIntegrationErrors(file);
76100
+ if (errors.length > 0)
76101
+ throw new MigrationActionRefusedError(`Proof integration plan refused: the result would be invalid (${errors.join("; ")}).`);
76102
+ mkdirSync17(path80.dirname(filePath), { recursive: true });
76103
+ const temporary = `${filePath}.${process.pid}.tmp`;
76104
+ writeFileSync20(temporary, `${JSON.stringify(file, null, 2)}
76105
+ `, "utf8");
76106
+ renameSync14(temporary, filePath);
76107
+ }
76108
+ var PROOF_INTEGRATION_CONFIGURE = "proof_integration_configure", PROPOSAL_SOURCES;
76109
+ var init_integration_plan = __esm(() => {
76110
+ init_migration_refusal();
76111
+ init_migration_plan();
76112
+ init_proof_protocol();
76113
+ init_integrations();
76114
+ PROPOSAL_SOURCES = [
76115
+ "package.json",
76116
+ "bun.lock",
76117
+ "bun.lockb",
76118
+ "pnpm-lock.yaml",
76119
+ "yarn.lock",
76120
+ "playwright.config.ts",
76121
+ "playwright.config.mjs",
76122
+ "playwright.config.js",
76123
+ "vitest.config.ts",
76124
+ "vitest.config.mjs",
76125
+ "vitest.config.js",
76126
+ "jest.config.ts",
76127
+ "jest.config.mjs",
76128
+ "jest.config.js",
76129
+ "pytest.ini",
76130
+ "pyproject.toml",
76131
+ "setup.cfg",
76132
+ "tox.ini",
76133
+ "go.mod",
76134
+ "Cargo.toml",
76135
+ "pom.xml",
76136
+ "build.gradle",
76137
+ "build.gradle.kts",
76138
+ "Gemfile",
76139
+ "Rakefile"
76140
+ ];
76141
+ });
76142
+
76143
+ // ../cli/dist/operations/planning/compile-intent.js
76144
+ import { createHash as createHash40 } from "node:crypto";
76145
+ import path81 from "node:path";
75638
76146
  function compilePlanHash(plan) {
75639
76147
  const { planHash: _ignored, ...body } = plan;
75640
76148
  return hash2(body);
@@ -75643,7 +76151,7 @@ function text7(value) {
75643
76151
  return typeof value === "string" ? value.trim() : "";
75644
76152
  }
75645
76153
  function hash2(value) {
75646
- return createHash39("sha256").update(canonicalize(value)).digest("hex");
76154
+ return createHash40("sha256").update(canonicalize(value)).digest("hex");
75647
76155
  }
75648
76156
  function shortHash2(value, length = 8) {
75649
76157
  return hash2(value).slice(0, length);
@@ -75671,7 +76179,7 @@ function requiredIntent(args) {
75671
76179
  return intent;
75672
76180
  }
75673
76181
  function validateLocation(location) {
75674
- if (!text7(location.path) || path79.isAbsolute(location.path) || location.path.split(/[\\/]/).includes("..")) {
76182
+ if (!text7(location.path) || path81.isAbsolute(location.path) || location.path.split(/[\\/]/).includes("..")) {
75675
76183
  throw new Error("Compile intent failed: sourceLocations.path must be workspace-relative");
75676
76184
  }
75677
76185
  }
@@ -75701,25 +76209,25 @@ function mergeSteps(steps) {
75701
76209
  merged.set(key, {
75702
76210
  ...step,
75703
76211
  ...Array.isArray(step.relationships) ? {
75704
- relationships: mergeRelationships(step.relationships.filter(isRecord22))
76212
+ relationships: mergeRelationships(step.relationships.filter(isRecord23))
75705
76213
  } : {}
75706
76214
  });
75707
76215
  continue;
75708
76216
  }
75709
76217
  const properties = {
75710
- ...isRecord22(previous.properties) ? previous.properties : {},
75711
- ...isRecord22(step.properties) ? step.properties : {}
76218
+ ...isRecord23(previous.properties) ? previous.properties : {},
76219
+ ...isRecord23(step.properties) ? step.properties : {}
75712
76220
  };
75713
76221
  const relationships = mergeRelationships([
75714
- ...Array.isArray(previous.relationships) ? previous.relationships.filter(isRecord22) : [],
75715
- ...Array.isArray(step.relationships) ? step.relationships.filter(isRecord22) : []
76222
+ ...Array.isArray(previous.relationships) ? previous.relationships.filter(isRecord23) : [],
76223
+ ...Array.isArray(step.relationships) ? step.relationships.filter(isRecord23) : []
75716
76224
  ]);
75717
76225
  merged.set(key, { ...previous, ...step, properties, relationships });
75718
76226
  }
75719
76227
  const rank = (step) => ({ test: 1, scenario: 2, req: 3 })[text7(step.type)] ?? 0;
75720
76228
  return [...merged.values()].sort((left, right) => rank(left) - rank(right) || text7(left.id).localeCompare(text7(right.id)));
75721
76229
  }
75722
- function isRecord22(value) {
76230
+ function isRecord23(value) {
75723
76231
  return value !== null && typeof value === "object" && !Array.isArray(value);
75724
76232
  }
75725
76233
  function withRequirementInventory(steps, requirementId, receipt, propositions) {
@@ -75740,7 +76248,7 @@ function withRequirementInventory(steps, requirementId, receipt, propositions) {
75740
76248
  return steps.map((step) => {
75741
76249
  if (text7(step.type) !== "req" || text7(step.id) !== requirementId)
75742
76250
  return step;
75743
- const properties = isRecord22(step.properties) ? step.properties : {};
76251
+ const properties = isRecord23(step.properties) ? step.properties : {};
75744
76252
  return {
75745
76253
  ...step,
75746
76254
  properties: {
@@ -75785,12 +76293,12 @@ function propositionDisposition2(proposition, suggestion) {
75785
76293
  function sourceFilesFor(locations) {
75786
76294
  return Array.from(new Set((locations ?? []).map((location) => location.path.trim()).filter(Boolean))).sort();
75787
76295
  }
75788
- async function sourceHashes(context, locations) {
76296
+ async function sourceHashes2(context, locations) {
75789
76297
  const hashes = {};
75790
76298
  for (const relative of sourceFilesFor(locations)) {
75791
76299
  try {
75792
- const contents = context.fs ? await context.fs.readFile(path79.join(context.workspaceRoot, relative)) : null;
75793
- hashes[relative] = contents === null ? null : createHash39("sha256").update(contents).digest("hex");
76300
+ const contents = context.fs ? await context.fs.readFile(path81.join(context.workspaceRoot, relative)) : null;
76301
+ hashes[relative] = contents === null ? null : createHash40("sha256").update(contents).digest("hex");
75794
76302
  } catch {
75795
76303
  hashes[relative] = null;
75796
76304
  }
@@ -75812,12 +76320,12 @@ async function withDocumentTargets(context, prolog, steps, requirement, now) {
75812
76320
  for (const step of steps) {
75813
76321
  const type = text7(step.type);
75814
76322
  const id = text7(step.id);
75815
- const properties = isRecord22(step.properties) ? step.properties : {};
76323
+ const properties = isRecord23(step.properties) ? step.properties : {};
75816
76324
  if (type === "symbol" || Object.keys(properties).length === 0) {
75817
76325
  targeted.push(step);
75818
76326
  continue;
75819
76327
  }
75820
- const stepDocument = isRecord22(step.document) ? step.document : {};
76328
+ const stepDocument = isRecord23(step.document) ? step.document : {};
75821
76329
  const document = type === "req" && id === requirement.id ? {
75822
76330
  ...requirement.body !== undefined ? { body: requirement.body } : {},
75823
76331
  ...requirement.path !== undefined ? { path: requirement.path } : {}
@@ -75827,7 +76335,7 @@ async function withDocumentTargets(context, prolog, steps, requirement, now) {
75827
76335
  type,
75828
76336
  id,
75829
76337
  properties,
75830
- relationships: Array.isArray(step.relationships) ? step.relationships.filter(isRecord22) : [],
76338
+ relationships: Array.isArray(step.relationships) ? step.relationships.filter(isRecord23) : [],
75831
76339
  document
75832
76340
  };
75833
76341
  const { entity } = validateUpsertInput(input, now);
@@ -75846,7 +76354,7 @@ async function existingRequirementBody(context, source) {
75846
76354
  if (!context.fs || !source.endsWith(".md"))
75847
76355
  return;
75848
76356
  try {
75849
- const raw = await context.fs.readFile(path79.join(context.workspaceRoot, source));
76357
+ const raw = await context.fs.readFile(path81.join(context.workspaceRoot, source));
75850
76358
  return readAuthoredEntity(String(raw), source)?.body ?? undefined;
75851
76359
  } catch {
75852
76360
  return;
@@ -75857,12 +76365,12 @@ function generatedRequirementId(intent) {
75857
76365
  }
75858
76366
  function planWhatIfGoal(steps, now) {
75859
76367
  return buildWhatIfAnalysisGoal(steps.map((step) => {
75860
- const relationships = (Array.isArray(step.relationships) ? step.relationships.filter(isRecord22) : []).map((relationship) => ({
76368
+ const relationships = (Array.isArray(step.relationships) ? step.relationships.filter(isRecord23) : []).map((relationship) => ({
75861
76369
  type: text7(relationship.type),
75862
76370
  from: text7(relationship.from),
75863
76371
  to: text7(relationship.to)
75864
76372
  }));
75865
- const properties = isRecord22(step.properties) ? step.properties : {};
76373
+ const properties = isRecord23(step.properties) ? step.properties : {};
75866
76374
  if (Object.keys(properties).length === 0) {
75867
76375
  return {
75868
76376
  entity: { id: text7(step.id), type: text7(step.type) },
@@ -75894,7 +76402,7 @@ function whatIfWitness(witness) {
75894
76402
  };
75895
76403
  }
75896
76404
  function whatIfWitnesses(value) {
75897
- return (Array.isArray(value) ? value.filter(isRecord22) : []).map(whatIfWitness);
76405
+ return (Array.isArray(value) ? value.filter(isRecord23) : []).map(whatIfWitness);
75898
76406
  }
75899
76407
  function parseWhatIfAnalysis(raw) {
75900
76408
  if (typeof raw !== "string")
@@ -75911,7 +76419,7 @@ function parseWhatIfAnalysis(raw) {
75911
76419
  const after = whatIfWitnesses(parsed);
75912
76420
  return { after, introduced: after, removed: [], unchanged: [] };
75913
76421
  }
75914
- if (!isRecord22(parsed))
76422
+ if (!isRecord23(parsed))
75915
76423
  return null;
75916
76424
  return {
75917
76425
  after: whatIfWitnesses(parsed.after),
@@ -75986,7 +76494,7 @@ function applyAcceptedProposals(steps, proposals) {
75986
76494
  if (!existing)
75987
76495
  continue;
75988
76496
  const relationships = mergeRelationships([
75989
- ...Array.isArray(existing.relationships) ? existing.relationships.filter(isRecord22) : [],
76497
+ ...Array.isArray(existing.relationships) ? existing.relationships.filter(isRecord23) : [],
75990
76498
  proposal.relationship
75991
76499
  ]);
75992
76500
  byId.set(targetId, { ...existing, relationships });
@@ -76294,7 +76802,7 @@ async function executeCompileIntent(args, context) {
76294
76802
  diagnostics.push("Change mode to update and supply requirementId to revise an existing requirement.");
76295
76803
  const unresolved = propositions.some((proposition) => proposition.status === "ambiguous" || proposition.status === "ontology_gap") || diagnostics.some((entry) => entry.includes("unresolved") || entry.includes("not found") || entry.includes("different content"));
76296
76804
  let statusValue = contradictions.outcome === "conflict" ? "blocked" : unresolved ? "needs_resolution" : "ready";
76297
- const sourceHashMap = await sourceHashes(context, args.sourceLocations);
76805
+ const sourceHashMap = await sourceHashes2(context, args.sourceLocations);
76298
76806
  let planSteps = stepsWithAcceptedProposals;
76299
76807
  const sourceInput = {
76300
76808
  excerpt: args.sourceExcerpt,
@@ -76375,21 +76883,21 @@ var init_compile_intent = __esm(() => {
76375
76883
  });
76376
76884
 
76377
76885
  // ../cli/dist/operations/planning/apply-plan.js
76378
- import { createHash as createHash40, randomUUID as randomUUID6 } from "node:crypto";
76379
- import path80 from "node:path";
76886
+ import { createHash as createHash41, randomUUID as randomUUID6 } from "node:crypto";
76887
+ import path82 from "node:path";
76380
76888
  function text8(value) {
76381
76889
  return typeof value === "string" ? value.trim() : "";
76382
76890
  }
76383
- function isRecord23(value) {
76891
+ function isRecord24(value) {
76384
76892
  return value !== null && typeof value === "object" && !Array.isArray(value);
76385
76893
  }
76386
76894
  function digest4(value) {
76387
- return createHash40("sha256").update(value).digest("hex");
76895
+ return createHash41("sha256").update(value).digest("hex");
76388
76896
  }
76389
76897
  function relationships(step) {
76390
76898
  if (!Array.isArray(step.relationships))
76391
76899
  return [];
76392
- return step.relationships.filter(isRecord23).map((relationship) => {
76900
+ return step.relationships.filter(isRecord24).map((relationship) => {
76393
76901
  const type = text8(relationship.type);
76394
76902
  const from = text8(relationship.from);
76395
76903
  const to = text8(relationship.to);
@@ -76405,8 +76913,8 @@ function asUpsert(step) {
76405
76913
  throw new Error(`Apply plan failed: unsupported step entity type '${type}'`);
76406
76914
  if (!id)
76407
76915
  throw new Error("Apply plan failed: every step needs an entity id");
76408
- const properties = isRecord23(step.properties) ? step.properties : {};
76409
- const document = isRecord23(step.document) ? {
76916
+ const properties = isRecord24(step.properties) ? step.properties : {};
76917
+ const document = isRecord24(step.document) ? {
76410
76918
  ...typeof step.document.path === "string" ? { path: step.document.path } : {},
76411
76919
  ...typeof step.document.body === "string" ? { body: step.document.body } : {}
76412
76920
  } : undefined;
@@ -76419,7 +76927,7 @@ function asUpsert(step) {
76419
76927
  };
76420
76928
  }
76421
76929
  function validateCompilePlanShape(args) {
76422
- if (!isRecord23(args.plan))
76930
+ if (!isRecord24(args.plan))
76423
76931
  throw new Error("Apply plan failed: plan must be an object");
76424
76932
  if (args.plan.version !== "kibi.compile-plan.v1")
76425
76933
  throw new Error("Apply plan failed: unsupported plan version");
@@ -76441,7 +76949,7 @@ function isBootstrapApplyArgs(args) {
76441
76949
  return "plan" in args && args.plan.version === "kibi.bootstrap-plan.v1";
76442
76950
  }
76443
76951
  function assertSourceWriteStaysInWorkspace(root, absolute, writePath) {
76444
- if (absolute !== root && !absolute.startsWith(`${root}${path80.sep}`)) {
76952
+ if (absolute !== root && !absolute.startsWith(`${root}${path82.sep}`)) {
76445
76953
  throw new Error(`Apply plan failed: sourceWrites.path escapes workspace: ${writePath}`);
76446
76954
  }
76447
76955
  }
@@ -76459,7 +76967,7 @@ function orderBootstrapActions(actions, completed = new Set) {
76459
76967
  for (const action of actions) {
76460
76968
  if (!action.id || byId.has(action.id))
76461
76969
  throw new Error("Bootstrap apply failed: action IDs must be unique");
76462
- if (action.kind !== "upsert" || !isRecord23(action.payload))
76970
+ if (action.kind !== "upsert" || !isRecord24(action.payload))
76463
76971
  throw new Error(`Bootstrap apply failed: action '${action.id}' is invalid`);
76464
76972
  byId.set(action.id, action);
76465
76973
  }
@@ -76548,7 +77056,7 @@ function validateEntityDeletionPlan(args) {
76548
77056
  throw new Error("Entity deletion apply failed: approvedPlanHash does not match planHash");
76549
77057
  }
76550
77058
  const { planHash: _ignored, ...body } = args.plan;
76551
- if (createHash40("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
77059
+ if (createHash41("sha256").update(JSON.stringify(body)).digest("hex") !== args.plan.planHash) {
76552
77060
  throw new Error("Entity deletion apply failed: planHash does not match the canonical plan body");
76553
77061
  }
76554
77062
  if (!Array.isArray(args.plan.entityIds) || args.plan.entityIds.length === 0) {
@@ -76559,7 +77067,7 @@ function validateEntityDeletionPlan(args) {
76559
77067
  }
76560
77068
  }
76561
77069
  function validateMigrationPlanShape(args) {
76562
- if (!isRecord23(args.plan))
77070
+ if (!isRecord24(args.plan))
76563
77071
  throw new Error("Migration apply failed: plan must be an object");
76564
77072
  if (args.plan.version !== "kibi.migration-plan.v2")
76565
77073
  throw new Error("Migration apply failed: unsupported plan version");
@@ -76621,13 +77129,13 @@ function topologicalActions(actions) {
76621
77129
  async function validateSources(context, sourceHashes) {
76622
77130
  let checked = 0;
76623
77131
  for (const [relative, expected] of Object.entries(sourceHashes)) {
76624
- if (!relative || path80.isAbsolute(relative) || relative.split(/[\\/]/).includes(".."))
77132
+ if (!relative || path82.isAbsolute(relative) || relative.split(/[\\/]/).includes(".."))
76625
77133
  throw new Error("Apply plan failed: source hash paths must be workspace-relative");
76626
77134
  if (!context.fs)
76627
77135
  throw new Error("Apply plan failed: source hashes require a filesystem-capable runtime");
76628
77136
  let actual;
76629
77137
  try {
76630
- actual = digest4(await context.fs.readFile(path80.join(context.workspaceRoot, relative)));
77138
+ actual = digest4(await context.fs.readFile(path82.join(context.workspaceRoot, relative)));
76631
77139
  } catch {
76632
77140
  actual = null;
76633
77141
  }
@@ -76638,19 +77146,19 @@ async function validateSources(context, sourceHashes) {
76638
77146
  return checked;
76639
77147
  }
76640
77148
  async function resolveSourceWriteTarget(workspaceRoot, fsPort, write) {
76641
- if (!write.path || path80.isAbsolute(write.path) || write.path.split(/[\\/]/).includes("..")) {
77149
+ if (!write.path || path82.isAbsolute(write.path) || write.path.split(/[\\/]/).includes("..")) {
76642
77150
  throw new Error(`Apply plan failed: sourceWrites.path must be workspace-relative: ${write.path}`);
76643
77151
  }
76644
- const absolute = path80.resolve(workspaceRoot, write.path);
76645
- const root = path80.resolve(workspaceRoot);
77152
+ const absolute = path82.resolve(workspaceRoot, write.path);
77153
+ const root = path82.resolve(workspaceRoot);
76646
77154
  assertSourceWriteStaysInWorkspace(root, absolute, write.path);
76647
- const relative = path80.relative(root, absolute).split(path80.sep).join("/");
77155
+ const relative = path82.relative(root, absolute).split(path82.sep).join("/");
76648
77156
  if (relative === ".kb" || isDerivedKbPath(relative)) {
76649
77157
  throw new Error("Apply plan failed: sourceWrites.path cannot target Kibi's derived .kb runtime trees");
76650
77158
  }
76651
77159
  const realRoot = canonicalFilesystemPath(root);
76652
77160
  const realExisting = canonicalFilesystemPath(absolute);
76653
- if (realExisting !== realRoot && !realExisting.startsWith(`${realRoot}${path80.sep}`)) {
77161
+ if (realExisting !== realRoot && !realExisting.startsWith(`${realRoot}${path82.sep}`)) {
76654
77162
  throw new Error(`Apply plan failed: sourceWrites.path follows a symlink outside the workspace: ${write.path}`);
76655
77163
  }
76656
77164
  const existing = await fsPort.readFile(absolute).catch(() => {
@@ -76681,7 +77189,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76681
77189
  throw new Error("Apply plan failed: sourceWrites require a filesystem-capable runtime");
76682
77190
  }
76683
77191
  const fsPort = context.fs;
76684
- const journalPath = path80.join(context.workspaceRoot, ".kb", "recovery", `source-writes-${planHash.slice(0, 16)}.json`);
77192
+ const journalPath = path82.join(context.workspaceRoot, ".kb", "recovery", `source-writes-${planHash.slice(0, 16)}.json`);
76685
77193
  const journalId = `source-writes-${planHash.slice(0, 16)}`;
76686
77194
  const sameEntries = (entries) => entries.length === writes.length && entries.every((entry) => writes.some((write) => write.path === entry.path && (write.mode ?? "write") === entry.mode && write.beforeHash === entry.beforeHash && write.afterHash === entry.afterHash));
76687
77195
  const readJournal = async () => {
@@ -76704,7 +77212,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76704
77212
  let allAfter = true;
76705
77213
  for (const entry of prior.entries) {
76706
77214
  try {
76707
- const current = await context.fs.readFile(path80.resolve(context.workspaceRoot, entry.path));
77215
+ const current = await context.fs.readFile(path82.resolve(context.workspaceRoot, entry.path));
76708
77216
  if (entry.afterHash === null || digest4(current) !== entry.afterHash) {
76709
77217
  allAfter = false;
76710
77218
  }
@@ -76726,7 +77234,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76726
77234
  }
76727
77235
  if (prior && (prior.state === "prepared" || prior.state === "publishing_sources")) {
76728
77236
  for (const entry of prior.entries) {
76729
- const absolute = path80.resolve(context.workspaceRoot, entry.path);
77237
+ const absolute = path82.resolve(context.workspaceRoot, entry.path);
76730
77238
  let current;
76731
77239
  try {
76732
77240
  current = await context.fs.readFile(absolute);
@@ -76759,7 +77267,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76759
77267
  const { absolute, existing, mode } = await resolveSourceWriteTarget(context.workspaceRoot, fsPort, write);
76760
77268
  originals.push({ absolute, body: existing });
76761
77269
  paths.push(write.path);
76762
- const stageBase = path80.join(context.workspaceRoot, ".kb", "recovery", `${journalId}-${entries.length}`);
77270
+ const stageBase = path82.join(context.workspaceRoot, ".kb", "recovery", `${journalId}-${entries.length}`);
76763
77271
  entries.push({
76764
77272
  path: write.path,
76765
77273
  mode,
@@ -76770,7 +77278,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76770
77278
  afterStage: `${stageBase}.after`
76771
77279
  });
76772
77280
  }
76773
- await context.fs.mkdir(path80.dirname(journalPath));
77281
+ await context.fs.mkdir(path82.dirname(journalPath));
76774
77282
  for (let index = 0;index < writes.length; index += 1) {
76775
77283
  const original = originals[index];
76776
77284
  const entry = entries[index];
@@ -76791,7 +77299,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76791
77299
  const write = writes[index];
76792
77300
  if (!absolute || !write)
76793
77301
  continue;
76794
- await context.fs.mkdir(path80.dirname(absolute));
77302
+ await context.fs.mkdir(path82.dirname(absolute));
76795
77303
  await context.fs.writeFile(journalPath, `${JSON.stringify({ version: 1, planHash, state: "publishing_sources", entries }, null, 2)}
76796
77304
  `);
76797
77305
  if ((write.mode ?? "write") === "delete") {
@@ -76843,7 +77351,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76843
77351
  } catch {}
76844
77352
  }
76845
77353
  try {
76846
- await context.fs.mkdir(path80.dirname(journalPath));
77354
+ await context.fs.mkdir(path82.dirname(journalPath));
76847
77355
  await context.fs.writeFile(journalPath, `${JSON.stringify({ version: 1, planHash, state: "rolled_back", entries }, null, 2)}
76848
77356
  `);
76849
77357
  } catch {}
@@ -76860,7 +77368,7 @@ async function applySourceWrites(context, writes, planHash, allowReplay = false,
76860
77368
  async function markSourceJournal(context, journalId, state) {
76861
77369
  if (!journalId || !context.fs)
76862
77370
  return;
76863
- const journalPath = path80.join(context.workspaceRoot, ".kb", "recovery", `${journalId}.json`);
77371
+ const journalPath = path82.join(context.workspaceRoot, ".kb", "recovery", `${journalId}.json`);
76864
77372
  try {
76865
77373
  const current = JSON.parse(await context.fs.readFile(journalPath));
76866
77374
  await context.fs.writeFile(journalPath, `${JSON.stringify({ ...current, state }, null, 2)}
@@ -76875,7 +77383,7 @@ async function executeSourceRecovery(args, context, onCommitted = () => {
76875
77383
  if (!/^[A-Za-z0-9._-]+$/.test(args.recoveryJournalId)) {
76876
77384
  throw new Error("Source recovery journal ID is invalid");
76877
77385
  }
76878
- const journalPath = path80.join(context.workspaceRoot, ".kb", "recovery", `${args.recoveryJournalId}.json`);
77386
+ const journalPath = path82.join(context.workspaceRoot, ".kb", "recovery", `${args.recoveryJournalId}.json`);
76879
77387
  const journal = JSON.parse(await context.fs.readFile(journalPath));
76880
77388
  if (journal.version !== 1 || typeof journal.planHash !== "string" || !Array.isArray(journal.entries) || !["sources_committed", "compiled_published", "repair_required"].includes(journal.state ?? "")) {
76881
77389
  throw new Error("Source recovery requires a committed or repair_required journal");
@@ -76952,7 +77460,7 @@ async function assertBootstrapPlanCurrent(args, operationContext) {
76952
77460
  async function preflightApplyPlan(args, context) {
76953
77461
  if (!("plan" in args))
76954
77462
  return;
76955
- if (!isRecord23(args.plan))
77463
+ if (!isRecord24(args.plan))
76956
77464
  throw new Error("Apply plan failed: plan must be an object");
76957
77465
  if (typeof args.approvedPlanHash !== "string" || !/^[a-f0-9]{64}$/i.test(args.approvedPlanHash))
76958
77466
  throw new Error("Apply plan failed: approvedPlanHash must be the SHA-256 planHash returned with the plan");
@@ -76991,7 +77499,7 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
76991
77499
  if (!status)
76992
77500
  throw new Error("Bootstrap apply failed: status returned no payload");
76993
77501
  const journalId = `bootstrap-${args.plan.planHash.slice(0, 16)}`;
76994
- const journalPath = path80.join(context.workspaceRoot, ".kb", "recovery", `${journalId}.json`);
77502
+ const journalPath = path82.join(context.workspaceRoot, ".kb", "recovery", `${journalId}.json`);
76995
77503
  if (!recovery && context.fs) {
76996
77504
  try {
76997
77505
  await context.fs.readFile(journalPath);
@@ -77024,7 +77532,7 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
77024
77532
  const writeJournal = async (state, activeActionId, currentCheckpoint = lastCheckpoint) => {
77025
77533
  if (!context.fs)
77026
77534
  return;
77027
- await context.fs.mkdir(path80.dirname(journalPath));
77535
+ await context.fs.mkdir(path82.dirname(journalPath));
77028
77536
  await context.fs.writeFile(journalPath, `${JSON.stringify({
77029
77537
  version: 2,
77030
77538
  kind: "bootstrap",
@@ -77294,7 +77802,7 @@ async function dispatchApplyPlan(args, context, onCommitted, recovered) {
77294
77802
  throw new Error("Bootstrap recovery requires a filesystem-capable runtime");
77295
77803
  if (!/^bootstrap-[a-f0-9]{16}$/.test(args.recoveryJournalId))
77296
77804
  throw new Error("Bootstrap recovery journal ID is invalid");
77297
- const journalPath = path80.join(context.workspaceRoot, ".kb", "recovery", `${args.recoveryJournalId}.json`);
77805
+ const journalPath = path82.join(context.workspaceRoot, ".kb", "recovery", `${args.recoveryJournalId}.json`);
77298
77806
  const journal = JSON.parse(await context.fs.readFile(journalPath));
77299
77807
  if (journal.version !== 2 || journal.kind !== "bootstrap" || !journal.plan)
77300
77808
  throw new Error("Bootstrap recovery journal is invalid");
@@ -77478,7 +77986,7 @@ async function planFileWrites(context, prolog, fsPort, sourceWrites, steps, now)
77478
77986
  const relative = canonicalSourcePath(context, step.input, step.entity, existing);
77479
77987
  let file = files.get(relative);
77480
77988
  if (file === undefined) {
77481
- const existingBytes = await fsPort.readFile(path80.join(workspaceRoot, relative)).catch(() => {
77989
+ const existingBytes = await fsPort.readFile(path82.join(workspaceRoot, relative)).catch(() => {
77482
77990
  return;
77483
77991
  });
77484
77992
  const before = existingBytes ?? null;
@@ -77503,8 +78011,8 @@ async function planFileWrites(context, prolog, fsPort, sourceWrites, steps, now)
77503
78011
  file.after = after;
77504
78012
  file.afterHash = contentHash(after);
77505
78013
  }
77506
- const root = path80.resolve(workspaceRoot);
77507
- const kbRoot = path80.join(workspaceRoot, ".kb");
78014
+ const root = path82.resolve(workspaceRoot);
78015
+ const kbRoot = path82.join(workspaceRoot, ".kb");
77508
78016
  const createdAt = now.toISOString();
77509
78017
  for (const step of steps) {
77510
78018
  for (const relationship of step.relationships) {
@@ -77515,7 +78023,7 @@ async function planFileWrites(context, prolog, fsPort, sourceWrites, steps, now)
77515
78023
  continue;
77516
78024
  const shardPath = computeShardPath(kbRoot, from);
77517
78025
  assertRelationshipShardContained(workspaceRoot, shardPath);
77518
- const relative = path80.relative(root, path80.resolve(shardPath)).split(path80.sep).join("/");
78026
+ const relative = path82.relative(root, path82.resolve(shardPath)).split(path82.sep).join("/");
77519
78027
  let file = files.get(relative);
77520
78028
  if (file === undefined) {
77521
78029
  const existing = await fsPort.readFile(shardPath).catch(() => null);
@@ -77967,7 +78475,7 @@ function withRecoveryReport(result, recovered) {
77967
78475
  action: recovery.action,
77968
78476
  restoredPaths: recovery.restoredPaths
77969
78477
  }));
77970
- if (isRecord23(structured.validationSummary)) {
78478
+ if (isRecord24(structured.validationSummary)) {
77971
78479
  const summary = structured.validationSummary;
77972
78480
  return {
77973
78481
  content,
@@ -78000,7 +78508,7 @@ async function applyingBootstrapJournal(args, context) {
78000
78508
  return;
78001
78509
  if (!/^bootstrap-[a-f0-9]{16}$/.test(args.recoveryJournalId))
78002
78510
  return;
78003
- const journalPath = path80.join(context.workspaceRoot, ".kb", "recovery", `${args.recoveryJournalId}.json`);
78511
+ const journalPath = path82.join(context.workspaceRoot, ".kb", "recovery", `${args.recoveryJournalId}.json`);
78004
78512
  try {
78005
78513
  const journal = JSON.parse(await context.fs.readFile(journalPath));
78006
78514
  return journal.kind === "bootstrap" && journal.state === "applying" ? journalPath : undefined;
@@ -78087,6 +78595,8 @@ async function applyMigrationPlan(args, context, onCommitted = () => {
78087
78595
  }
78088
78596
  const results = [];
78089
78597
  let failed = false;
78598
+ let refusals = 0;
78599
+ let failures = 0;
78090
78600
  for (const action of actions) {
78091
78601
  if (failed) {
78092
78602
  results.push({
@@ -78106,6 +78616,9 @@ async function applyMigrationPlan(args, context, onCommitted = () => {
78106
78616
  });
78107
78617
  } catch (error) {
78108
78618
  failed = true;
78619
+ failures += 1;
78620
+ if (error instanceof MigrationActionRefusedError)
78621
+ refusals += 1;
78109
78622
  results.push({
78110
78623
  actionId: action.id,
78111
78624
  outcome: "failed",
@@ -78152,7 +78665,7 @@ async function applyMigrationPlan(args, context, onCommitted = () => {
78152
78665
  });
78153
78666
  }
78154
78667
  }
78155
- const outcome = failed ? results.some((result) => result.outcome === "applied") ? "partially_applied" : "reconciliation_required" : "applied";
78668
+ const outcome = failed ? results.some((result) => result.outcome === "applied") ? "partially_applied" : failures > 0 && refusals === failures ? "refused" : "reconciliation_required" : "applied";
78156
78669
  const kbState = finalStatus.branchAttachment?.migrationRequired ? "legacy_compat" : finalStatus.syncState === "stale" ? "stale" : finalStatus.dirty ? "dirty" : finalStatus.syncState === "fresh" ? "clean_fresh" : "not_evaluated";
78157
78670
  const snapshotState = finalStatus.proofSnapshotAvailable === false ? "unavailable" : finalStatus.proofSnapshotDirty === true ? "dirty" : finalStatus.proofSnapshotDirty === false ? "fresh" : "not_evaluated";
78158
78671
  const proven = coverageSummary?.proofProven;
@@ -78173,7 +78686,7 @@ async function applyMigrationPlan(args, context, onCommitted = () => {
78173
78686
  ],
78174
78687
  ...remainingPlan !== undefined ? { remainingPlan } : {},
78175
78688
  closeout: {
78176
- taskOutcome: outcome === "applied" ? "complete" : outcome === "reconciliation_required" ? "blocked" : "interim",
78689
+ taskOutcome: outcome === "applied" ? "complete" : outcome === "reconciliation_required" || outcome === "refused" ? "blocked" : "interim",
78177
78690
  kbState,
78178
78691
  snapshotState,
78179
78692
  proofState,
@@ -78184,7 +78697,7 @@ async function applyMigrationPlan(args, context, onCommitted = () => {
78184
78697
  content: [
78185
78698
  {
78186
78699
  type: "text",
78187
- text: `${outcome === "applied" ? "Applied" : "Stopped after"} migration plan ${args.plan.planHash.slice(0, 12)}.`
78700
+ text: outcome === "applied" ? `Applied migration plan ${args.plan.planHash.slice(0, 12)}.` : outcome === "refused" ? `Refused migration plan ${args.plan.planHash.slice(0, 12)}; nothing was changed.` : `Stopped after migration plan ${args.plan.planHash.slice(0, 12)}.`
78188
78701
  }
78189
78702
  ],
78190
78703
  structuredContent: payload
@@ -78214,6 +78727,14 @@ async function applyMigrationAction(action, context) {
78214
78727
  case "missing_exact_branch_store":
78215
78728
  await branchEnsureCommand({ workspaceRoot: context.workspaceRoot });
78216
78729
  return;
78730
+ case BRANCH_STORE_NOT_COMPILED: {
78731
+ const result = await syncCommand({
78732
+ workspaceRoot: context.workspaceRoot
78733
+ });
78734
+ if (!result.success)
78735
+ throw new Error("Compiling the branch store (kibi sync) did not complete successfully.");
78736
+ return;
78737
+ }
78217
78738
  case "damaged_exact_branch_store":
78218
78739
  await branchRecoverCommand({
78219
78740
  apply: true,
@@ -78243,21 +78764,24 @@ async function applyMigrationAction(action, context) {
78243
78764
  case PREDICATE_SCHEMA_ALIGNMENT_CODE:
78244
78765
  await applyPredicateSchemaAlignment(action, context);
78245
78766
  return;
78767
+ case PROOF_INTEGRATION_CONFIGURE:
78768
+ applyProofIntegrationAction(action, context.workspaceRoot);
78769
+ return;
78246
78770
  default:
78247
78771
  if (SCHEMA6_AUTOMATIC_CODES.has(action.code)) {
78248
78772
  await applySchema6MigrationAction(action, context);
78249
78773
  return;
78250
78774
  }
78251
- throw new Error(`Migration action '${action.code}' has no automatic executor.`);
78775
+ throw new MigrationActionRefusedError(`Migration action '${action.code}' has no automatic executor.`);
78252
78776
  }
78253
78777
  }
78254
78778
  async function applyPredicateSchemaAlignment(action, context) {
78255
78779
  const invocation = action.invocation;
78256
78780
  if (invocation.kind !== "operation" || invocation.name !== "kb_upsert")
78257
- throw new Error("Predicate schema alignment requires its planned kb_upsert invocation.");
78781
+ throw new MigrationActionRefusedError("Predicate schema alignment requires its planned kb_upsert invocation.");
78258
78782
  const input = asUpsert(invocation.input);
78259
78783
  if (input.type !== "fact")
78260
- throw new Error("Predicate schema alignment only rewrites fact entities.");
78784
+ throw new MigrationActionRefusedError("Predicate schema alignment only rewrites fact entities.");
78261
78785
  const prolog = context.prolog ?? await context.ensureProlog?.();
78262
78786
  if (!prolog)
78263
78787
  throw new Error("Predicate schema alignment requires a Prolog runtime.");
@@ -78267,9 +78791,9 @@ async function applyPredicateSchemaAlignment(action, context) {
78267
78791
  const currentNamespace = typeof current?.predicate_namespace === "string" && current.predicate_namespace !== "" ? current.predicate_namespace : "default";
78268
78792
  const currentArgs = Array.isArray(current?.predicate_args) ? current.predicate_args : [];
78269
78793
  const rewrites = Array.isArray(evidence.rewrites) ? evidence.rewrites : [];
78270
- const unchanged = current !== undefined && current.fact_kind === "predicate" && currentNamespace === evidence.namespace && rewrites.every((rewrite) => isRecord23(rewrite) && typeof rewrite.index === "number" && currentArgs[rewrite.index] === rewrite.from);
78794
+ const unchanged = current !== undefined && current.fact_kind === "predicate" && currentNamespace === evidence.namespace && rewrites.every((rewrite) => isRecord24(rewrite) && typeof rewrite.index === "number" && currentArgs[rewrite.index] === rewrite.from);
78271
78795
  if (!unchanged)
78272
- throw new Error(`Predicate fact ${input.id} changed since planning; rerun kibi check and approve the new plan.`);
78796
+ throw new MigrationActionRefusedError(`Predicate fact ${input.id} changed since planning; rerun kibi check and approve the new plan.`);
78273
78797
  await executeUpsert(input, operationContext);
78274
78798
  }
78275
78799
  var PLAN_APPLY_RESULT_VERSION = "kibi.plan-apply-result.v1", ENTITY_TYPES4;
@@ -78278,11 +78802,13 @@ var init_apply_plan = __esm(() => {
78278
78802
  init_branch();
78279
78803
  init_migrate();
78280
78804
  init_sync();
78805
+ init_integration_plan();
78281
78806
  init_discovery_entities();
78282
78807
  init_discovery_executors();
78283
78808
  init_migration_plan();
78284
78809
  init_migration_plan();
78285
78810
  init_shards();
78811
+ init_branch_store();
78286
78812
  init_canonical_path();
78287
78813
  init_kb_paths();
78288
78814
  init_types();
@@ -78295,6 +78821,7 @@ var init_apply_plan = __esm(() => {
78295
78821
  init_upsert();
78296
78822
  init_workspace_mutation_lock();
78297
78823
  init_compile_intent();
78824
+ init_migration_refusal();
78298
78825
  ENTITY_TYPES4 = new Set([
78299
78826
  "req",
78300
78827
  "scenario",
@@ -78719,113 +79246,6 @@ function evaluateContractAgainstRun(artifact, contract) {
78719
79246
  }
78720
79247
  var MAX_ATTRIBUTED_MEMBERS = 5;
78721
79248
 
78722
- // ../cli/dist/proof/integrations.js
78723
- import { existsSync as existsSync41, readFileSync as readFileSync48 } from "node:fs";
78724
- import path81 from "node:path";
78725
- function integrationsPath(workspaceRoot) {
78726
- return path81.join(workspaceRoot, PROOF_INTEGRATIONS_PATH);
78727
- }
78728
- function proofIntegrationErrors(value) {
78729
- const file = record5(value);
78730
- if (!file)
78731
- return [
78732
- `${PROOF_INTEGRATIONS_PATH} must be an object with version and integrations`
78733
- ];
78734
- const errors = [];
78735
- if (file.version !== PROOF_INTEGRATION_VERSION)
78736
- errors.push(`version must be ${PROOF_INTEGRATION_VERSION}; received ${JSON.stringify(file.version ?? null)}`);
78737
- if (!Array.isArray(file.integrations) || file.integrations.length === 0) {
78738
- errors.push("integrations must be a non-empty array");
78739
- return errors;
78740
- }
78741
- const seen = new Set;
78742
- file.integrations.forEach((entry, index) => {
78743
- const row = record5(entry);
78744
- const label = `integrations[${index}]`;
78745
- if (!row) {
78746
- errors.push(`${label} must be an object`);
78747
- return;
78748
- }
78749
- if (!nonEmpty(row.id))
78750
- errors.push(`${label}.id must be a non-empty string`);
78751
- else {
78752
- if (seen.has(String(row.id)))
78753
- errors.push(`${label}.id duplicates '${String(row.id)}'`);
78754
- seen.add(String(row.id));
78755
- }
78756
- if (!nonEmpty(row.producer))
78757
- errors.push(`${label}.producer must be one of: ${PRODUCER_KINDS.join(", ")} (or a custom producer id)`);
78758
- if (!Array.isArray(row.command) || row.command.length === 0 || !row.command.every((argument) => nonEmpty(argument)))
78759
- errors.push(`${label}.command must be a non-empty argv array`);
78760
- if (nonEmpty(row.producer) && (row.producer === "junit" || row.producer === "tap") && !nonEmpty(row.artifact))
78761
- errors.push(`${label}.artifact is required for producer '${String(row.producer)}' (path of the native report to convert)`);
78762
- if (row.targets !== undefined) {
78763
- if (!Array.isArray(row.targets) || row.targets.length === 0 || !row.targets.every((target) => nonEmpty(target)))
78764
- errors.push(`${label}.targets must be a non-empty array of target names`);
78765
- }
78766
- if (row.options !== undefined && !record5(row.options))
78767
- errors.push(`${label}.options must be an object when present`);
78768
- if (row.description !== undefined && !nonEmpty(row.description))
78769
- errors.push(`${label}.description must be a non-empty string when present`);
78770
- });
78771
- return errors;
78772
- }
78773
- function loadProofIntegrations(workspaceRoot) {
78774
- const filePath = integrationsPath(workspaceRoot);
78775
- if (!existsSync41(filePath)) {
78776
- return {
78777
- available: false,
78778
- error: `No proof integration configuration at ${PROOF_INTEGRATIONS_PATH}. Run bootstrap for this repository to configure proof producers.`
78779
- };
78780
- }
78781
- let parsed;
78782
- try {
78783
- parsed = JSON.parse(readFileSync48(filePath, "utf8"));
78784
- } catch (error) {
78785
- return {
78786
- available: false,
78787
- error: `${PROOF_INTEGRATIONS_PATH} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
78788
- };
78789
- }
78790
- const errors = proofIntegrationErrors(parsed);
78791
- if (errors.length > 0)
78792
- return {
78793
- available: false,
78794
- error: `Invalid ${PROOF_INTEGRATIONS_PATH}: ${errors.join("; ")}`
78795
- };
78796
- return { available: true, integrations: parsed };
78797
- }
78798
- function resolveIntegration(integrations, id) {
78799
- return integrations.integrations.find((entry) => entry.id === id) ?? null;
78800
- }
78801
- function toExecution(integration) {
78802
- return {
78803
- id: integration.id,
78804
- producer: integration.producer,
78805
- ...integration.producer_version !== undefined ? { producer_version: integration.producer_version } : {},
78806
- command: integration.command,
78807
- ...integration.artifact !== undefined ? { artifact: integration.artifact } : {},
78808
- ...integration.targets !== undefined ? { targets: integration.targets } : {},
78809
- ...integration.options !== undefined ? { options: integration.options } : {}
78810
- };
78811
- }
78812
- function record5(value) {
78813
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
78814
- }
78815
- function nonEmpty(value) {
78816
- return typeof value === "string" && value.trim().length > 0;
78817
- }
78818
- var PROOF_INTEGRATIONS_PATH = ".kb/proof/integrations.json", PRODUCER_KINDS;
78819
- var init_integrations = __esm(() => {
78820
- init_proof_protocol();
78821
- PRODUCER_KINDS = [
78822
- "command",
78823
- "playwright",
78824
- "junit",
78825
- "tap"
78826
- ];
78827
- });
78828
-
78829
79249
  // ../cli/dist/operations/mutation/entity-projection.js
78830
79250
  function projectEntityProperties(entity) {
78831
79251
  const relationshipKeys = new Set(RELATIONSHIP_TYPES2);
@@ -79324,11 +79744,11 @@ function validateInput(input) {
79324
79744
  }
79325
79745
  return endpoint;
79326
79746
  }
79327
- function isRecord24(value) {
79747
+ function isRecord25(value) {
79328
79748
  return value !== null && typeof value === "object" && !Array.isArray(value);
79329
79749
  }
79330
79750
  function decodeRows(payload) {
79331
- if (!isRecord24(payload) || !isRecord24(payload.results)) {
79751
+ if (!isRecord25(payload) || !isRecord25(payload.results)) {
79332
79752
  throw new Error("SPARQL endpoint returned an invalid result document");
79333
79753
  }
79334
79754
  const bindings = payload.results.bindings;
@@ -79336,12 +79756,12 @@ function decodeRows(payload) {
79336
79756
  throw new Error("SPARQL endpoint returned an invalid bindings array");
79337
79757
  }
79338
79758
  const rows = bindings.map((binding) => {
79339
- if (!isRecord24(binding)) {
79759
+ if (!isRecord25(binding)) {
79340
79760
  throw new Error("SPARQL endpoint returned an invalid binding");
79341
79761
  }
79342
79762
  const row = {};
79343
79763
  for (const [variable, term] of Object.entries(binding)) {
79344
- if (!isRecord24(term) || typeof term.value !== "string") {
79764
+ if (!isRecord25(term) || typeof term.value !== "string") {
79345
79765
  throw new Error(`SPARQL endpoint returned an invalid ${variable} term`);
79346
79766
  }
79347
79767
  row[variable] = term.value;
@@ -79625,10 +80045,10 @@ init_generate();
79625
80045
  init_presentation();
79626
80046
  init_ir();
79627
80047
  // src/skill-operations.ts
79628
- import { createHash as createHash41 } from "node:crypto";
80048
+ import { createHash as createHash42 } from "node:crypto";
79629
80049
 
79630
80050
  // src/skill-system/loader.ts
79631
- import { existsSync as existsSync44, readFileSync as readFileSync50, readdirSync as readdirSync13, realpathSync as realpathSync10 } from "node:fs";
80051
+ import { existsSync as existsSync45, readFileSync as readFileSync51, readdirSync as readdirSync14, realpathSync as realpathSync10 } from "node:fs";
79632
80052
  import { dirname as dirname22, join as join34, resolve as resolve29 } from "node:path";
79633
80053
  import { fileURLToPath as fileURLToPath5 } from "node:url";
79634
80054
 
@@ -79675,7 +80095,7 @@ class SkillOversizeError2 extends Error {
79675
80095
  }
79676
80096
 
79677
80097
  // src/skill-system/paths.ts
79678
- import { existsSync as existsSync42, statSync as statSync13 } from "node:fs";
80098
+ import { existsSync as existsSync43, statSync as statSync13 } from "node:fs";
79679
80099
  import { isAbsolute as isAbsolute19, join as join33, normalize as normalize3, relative as relative20, resolve as resolve27 } from "node:path";
79680
80100
  var SKILL_FILE_NAME2 = "SKILL.md";
79681
80101
  function normalizeResourcePath2(pathLike) {
@@ -79691,7 +80111,7 @@ function isWithinRoot3(rootDir, candidatePath) {
79691
80111
  }
79692
80112
  function resolveSkillFilePath2(pathLike) {
79693
80113
  const resolved = resolve27(pathLike);
79694
- if (existsSync42(resolved) && statSync13(resolved).isDirectory())
80114
+ if (existsSync43(resolved) && statSync13(resolved).isDirectory())
79695
80115
  return join33(resolved, SKILL_FILE_NAME2);
79696
80116
  return resolved.endsWith(SKILL_FILE_NAME2) ? resolved : join33(resolved, SKILL_FILE_NAME2);
79697
80117
  }
@@ -79816,7 +80236,7 @@ function isStringArray2(value) {
79816
80236
  var moduleDir = dirname22(fileURLToPath5(import.meta.url));
79817
80237
  var overrideSkillsDir;
79818
80238
  function defaultBundledSkillsDir2() {
79819
- return existsSync44(resolve29(moduleDir, "skills")) ? resolve29(moduleDir, "skills") : resolve29(moduleDir, "../skills");
80239
+ return existsSync45(resolve29(moduleDir, "skills")) ? resolve29(moduleDir, "skills") : resolve29(moduleDir, "../skills");
79820
80240
  }
79821
80241
  function setBundledSkillsDir2(dir) {
79822
80242
  overrideSkillsDir = dir;
@@ -79846,17 +80266,17 @@ function readBundledSkillResourceFrom2(skillsDir, id, resourcePath) {
79846
80266
  return readBundle2(loadBundledSkillFrom2(skillsDir, id), id, resourcePath);
79847
80267
  }
79848
80268
  function listFrom2(dir) {
79849
- if (!existsSync44(dir))
80269
+ if (!existsSync45(dir))
79850
80270
  return [];
79851
- return readdirSync13(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join34(dir, entry.name)).filter((root) => existsSync44(join34(root, SKILL_FILE_NAME2))).map((root) => parseSkillBundle2(root).manifest).sort((a, b) => a.id.localeCompare(b.id));
80271
+ return readdirSync14(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join34(dir, entry.name)).filter((root) => existsSync45(join34(root, SKILL_FILE_NAME2))).map((root) => parseSkillBundle2(root).manifest).sort((a, b) => a.id.localeCompare(b.id));
79852
80272
  }
79853
80273
  function findRoot2(dir, id) {
79854
80274
  return listRoots2(dir).find((root) => parseSkillBundle2(root).manifest.id === id);
79855
80275
  }
79856
80276
  function listRoots2(dir) {
79857
- if (!existsSync44(dir))
80277
+ if (!existsSync45(dir))
79858
80278
  return [];
79859
- return readdirSync13(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join34(dir, entry.name)).filter((root) => existsSync44(join34(root, SKILL_FILE_NAME2)));
80279
+ return readdirSync14(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join34(dir, entry.name)).filter((root) => existsSync45(join34(root, SKILL_FILE_NAME2)));
79860
80280
  }
79861
80281
  function readBundle2(bundle, id, resourcePath) {
79862
80282
  const resource = normalizeResourcePath2(resourcePath);
@@ -79874,7 +80294,7 @@ function readBundle2(bundle, id, resourcePath) {
79874
80294
  throw new SkillResourceNotFoundError2(id, resourcePath);
79875
80295
  }
79876
80296
  assertMaxBytes2(candidate, RESOURCE_MAX_BYTES2);
79877
- return readFileSync50(candidate, "utf8");
80297
+ return readFileSync51(candidate, "utf8");
79878
80298
  }
79879
80299
  // src/skill-operations.ts
79880
80300
  function assertNonEmptyString2(value, field) {
@@ -79897,7 +80317,7 @@ async function executeSkillsLoad2(input, _context) {
79897
80317
  assertNonEmptyString2(id, "id");
79898
80318
  const bundle = loadBundledSkill2(id);
79899
80319
  const resources = bundle.manifest.resources ?? [];
79900
- const contentHash = createHash41("sha256").update(bundle.body, "utf8").digest("hex");
80320
+ const contentHash = createHash42("sha256").update(bundle.body, "utf8").digest("hex");
79901
80321
  const resourceList = resources.length === 0 ? "none" : resources.join(", ");
79902
80322
  return {
79903
80323
  content: [
@@ -79997,6 +80417,7 @@ init_branch_store_locator();
79997
80417
  init_detached_snapshot();
79998
80418
 
79999
80419
  // src/index.ts
80420
+ init_result_envelope();
80000
80421
  init_engine();
80001
80422
 
80002
80423
  // ../cli/dist/public/prolog/index.js
@@ -80031,7 +80452,7 @@ init_symbols_coordinator2();
80031
80452
  // ../cli/dist/env/bootstrap.js
80032
80453
  import fs13 from "node:fs";
80033
80454
  import os2 from "node:os";
80034
- import path82 from "node:path";
80455
+ import path83 from "node:path";
80035
80456
  var WORKSPACE_ENV_KEYS = [
80036
80457
  "KIBI_WORKSPACE",
80037
80458
  "KIBI_PROJECT_ROOT",
@@ -80057,13 +80478,13 @@ function readFirstEnv(env, keys) {
80057
80478
  return null;
80058
80479
  }
80059
80480
  function nextAncestorDirectory2(current) {
80060
- const parent = path82.dirname(current);
80481
+ const parent = path83.dirname(current);
80061
80482
  return parent === current ? undefined : parent;
80062
80483
  }
80063
80484
  function findUpwards(startDir, marker) {
80064
- let current = path82.resolve(startDir);
80485
+ let current = path83.resolve(startDir);
80065
80486
  while (current !== undefined) {
80066
- if (fs13.existsSync(path82.join(current, marker))) {
80487
+ if (fs13.existsSync(path83.join(current, marker))) {
80067
80488
  return current;
80068
80489
  }
80069
80490
  current = nextAncestorDirectory2(current);
@@ -80073,24 +80494,24 @@ function findUpwards(startDir, marker) {
80073
80494
  function resolveKibiWorkspaceRoot(startDir = process.cwd(), env = process.env) {
80074
80495
  const envRoot = readFirstEnv(env, WORKSPACE_ENV_KEYS);
80075
80496
  if (envRoot)
80076
- return path82.resolve(envRoot);
80497
+ return path83.resolve(envRoot);
80077
80498
  const kbRoot = findUpwards(startDir, ".kb");
80078
80499
  if (kbRoot)
80079
80500
  return kbRoot;
80080
80501
  const gitRoot = findUpwards(startDir, ".git");
80081
80502
  if (gitRoot)
80082
80503
  return gitRoot;
80083
- return path82.resolve(startDir);
80504
+ return path83.resolve(startDir);
80084
80505
  }
80085
80506
  function resolveEnvFilePath(envFileName, workspaceRoot) {
80086
- if (path82.isAbsolute(envFileName))
80507
+ if (path83.isAbsolute(envFileName))
80087
80508
  return envFileName;
80088
- return path82.resolve(workspaceRoot, envFileName);
80509
+ return path83.resolve(workspaceRoot, envFileName);
80089
80510
  }
80090
80511
  function resolveKibiUserEnvPath(options2 = {}) {
80091
80512
  const env = options2.env ?? process.env;
80092
- const xdg = options2.xdgConfigHome?.trim() || env.XDG_CONFIG_HOME?.trim() || path82.join(options2.homedir ?? os2.homedir(), ".config");
80093
- return path82.join(xdg, "kibi", "env");
80513
+ const xdg = options2.xdgConfigHome?.trim() || env.XDG_CONFIG_HOME?.trim() || path83.join(options2.homedir ?? os2.homedir(), ".config");
80514
+ return path83.join(xdg, "kibi", "env");
80094
80515
  }
80095
80516
  function resolveKibiProjectEnvPath(workspaceRoot, env = process.env) {
80096
80517
  const override = env.KIBI_ENV_FILE?.trim();