kibi-cli 2.21.2 → 2.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/cli-register-foundation.d.ts.map +1 -1
  2. package/dist/cli-register-foundation.js +1 -0
  3. package/dist/commands/check.d.ts +0 -1
  4. package/dist/commands/check.d.ts.map +1 -1
  5. package/dist/commands/check.js +47 -111
  6. package/dist/commands/doctor.d.ts.map +1 -1
  7. package/dist/commands/doctor.js +108 -6
  8. package/dist/commands/init-helpers.d.ts +16 -2
  9. package/dist/commands/init-helpers.d.ts.map +1 -1
  10. package/dist/commands/init-helpers.js +52 -10
  11. package/dist/commands/init.d.ts +1 -0
  12. package/dist/commands/init.d.ts.map +1 -1
  13. package/dist/commands/init.js +12 -1
  14. package/dist/commands/legacy-storage-migration.d.ts +46 -1
  15. package/dist/commands/legacy-storage-migration.d.ts.map +1 -1
  16. package/dist/commands/legacy-storage-migration.js +219 -9
  17. package/dist/commands/migrate.d.ts +37 -0
  18. package/dist/commands/migrate.d.ts.map +1 -1
  19. package/dist/commands/migrate.js +72 -188
  20. package/dist/commands/sync/manifest.d.ts.map +1 -1
  21. package/dist/commands/sync/manifest.js +9 -0
  22. package/dist/extractors/symbols-coordinator.js +1 -0
  23. package/dist/intent-search.d.ts.map +1 -1
  24. package/dist/intent-search.js +28 -6
  25. package/dist/operations/migration/inventory-rederive.d.ts +40 -1
  26. package/dist/operations/migration/inventory-rederive.d.ts.map +1 -1
  27. package/dist/operations/migration/inventory-rederive.js +99 -8
  28. package/dist/operations/migration/requirement-status.d.ts +25 -0
  29. package/dist/operations/migration/requirement-status.d.ts.map +1 -0
  30. package/dist/operations/migration/requirement-status.js +105 -0
  31. package/dist/operations/migration/schema6.d.ts +25 -0
  32. package/dist/operations/migration/schema6.d.ts.map +1 -1
  33. package/dist/operations/migration/schema6.js +265 -7
  34. package/dist/operations/migration/symbol-legacy-links.d.ts +51 -0
  35. package/dist/operations/migration/symbol-legacy-links.d.ts.map +1 -0
  36. package/dist/operations/migration/symbol-legacy-links.js +319 -0
  37. package/dist/operations/mutation/context-warning.d.ts +12 -2
  38. package/dist/operations/mutation/context-warning.d.ts.map +1 -1
  39. package/dist/operations/mutation/context-warning.js +6 -6
  40. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  41. package/dist/operations/mutation/source-authoring.js +130 -35
  42. package/dist/operations/mutation/types.d.ts +2 -1
  43. package/dist/operations/mutation/types.d.ts.map +1 -1
  44. package/dist/operations/mutation/upsert.d.ts +17 -2
  45. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  46. package/dist/operations/mutation/upsert.js +28 -11
  47. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  48. package/dist/operations/mutation/validate-upsert.js +40 -8
  49. package/dist/operations/mutation/validation.d.ts.map +1 -1
  50. package/dist/operations/mutation/validation.js +43 -1
  51. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  52. package/dist/operations/planning/apply-plan.js +34 -10
  53. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
  54. package/dist/operations/semantic-advisor/ingestion-boundary.js +25 -1
  55. package/dist/operations/semantic-advisor/stored-ledger-drift.d.ts +19 -0
  56. package/dist/operations/semantic-advisor/stored-ledger-drift.d.ts.map +1 -0
  57. package/dist/operations/semantic-advisor/stored-ledger-drift.js +78 -0
  58. package/dist/public/impact/diagnostics.d.ts.map +1 -1
  59. package/dist/public/impact/diagnostics.js +24 -14
  60. package/dist/public/operations/catalog.d.ts +3 -3
  61. package/dist/public/operations/check-executor.d.ts.map +1 -1
  62. package/dist/public/operations/check-executor.js +3 -0
  63. package/dist/public/operations/contracts.d.ts.map +1 -1
  64. package/dist/public/operations/contracts.js +6 -1
  65. package/dist/public/operations/decision-firmness.d.ts +3 -1
  66. package/dist/public/operations/decision-firmness.d.ts.map +1 -1
  67. package/dist/public/operations/decision-firmness.js +13 -3
  68. package/dist/public/operations/discovery-executors.d.ts +2 -0
  69. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  70. package/dist/public/operations/discovery-executors.js +24 -2
  71. package/dist/public/operations/migration-plan.d.ts +15 -0
  72. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  73. package/dist/public/operations/migration-plan.js +130 -17
  74. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  75. package/dist/public/operations/result-envelope.js +27 -1
  76. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  77. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  78. package/dist/public/operations/specs/check-rules.generated.js +2 -1
  79. package/dist/public/operations/specs/check.d.ts +1 -1
  80. package/dist/public/operations/specs/planning.d.ts +2 -2
  81. package/dist/public/operations/specs/planning.js +2 -2
  82. package/dist/public/operations/specs/semantic.d.ts.map +1 -1
  83. package/dist/public/operations/specs/semantic.js +26 -1
  84. package/dist/public/operations/symbol-granularity-check.d.ts +18 -0
  85. package/dist/public/operations/symbol-granularity-check.d.ts.map +1 -0
  86. package/dist/public/operations/symbol-granularity-check.js +175 -0
  87. package/dist/public/skills/kibi-usage/SKILL.md +8 -3
  88. package/dist/public/symbol-granularity.d.ts +17 -0
  89. package/dist/public/symbol-granularity.d.ts.map +1 -1
  90. package/dist/public/symbol-granularity.js +21 -0
  91. package/dist/traceability/evidence-model.d.ts +7 -3
  92. package/dist/traceability/evidence-model.d.ts.map +1 -1
  93. package/dist/traceability/staged-symbols-manifest.js +6 -5
  94. package/dist/traceability/validate.d.ts +14 -1
  95. package/dist/traceability/validate.d.ts.map +1 -1
  96. package/dist/traceability/validate.js +41 -0
  97. package/dist/utils/rule-registry.generated.d.ts +5 -0
  98. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  99. package/dist/utils/rule-registry.generated.js +6 -0
  100. package/package.json +3 -3
  101. package/src/public/impact/diagnostics.ts +29 -17
  102. package/src/public/operations/check-executor.ts +7 -0
  103. package/src/public/operations/contracts.ts +6 -1
  104. package/src/public/operations/decision-firmness.ts +18 -2
  105. package/src/public/operations/discovery-executors.ts +28 -2
  106. package/src/public/operations/migration-plan.ts +147 -20
  107. package/src/public/operations/result-envelope.ts +34 -1
  108. package/src/public/operations/specs/check-rules.generated.ts +2 -1
  109. package/src/public/operations/specs/planning.ts +2 -2
  110. package/src/public/operations/specs/semantic.ts +37 -2
  111. package/src/public/operations/symbol-granularity-check.ts +233 -0
  112. package/src/public/skills/kibi-usage/SKILL.md +8 -3
  113. package/src/public/symbol-granularity.ts +37 -0
@@ -1,6 +1,6 @@
1
1
  import type { OperationContext } from "../../public/operations/runtime-types.js";
2
2
  import type { OperationResult } from "../../public/operations/types.js";
3
- import { analyzeSemanticAdvisorInput } from "../semantic-advisor/analyze-prose.js";
3
+ import type { SemanticAdvisorReceipt } from "../semantic-advisor/types.js";
4
4
  import { type StoredEntityLookup } from "./stored-entity.js";
5
5
  import type { RelationshipInput, StagedUpsertState, UpsertInput, UpsertPayload, ValidatedUpsert } from "./types.js";
6
6
  export declare function assertRelationshipShardContained(workspaceRoot: string, shardPath: string): void;
@@ -15,11 +15,26 @@ export type UpsertValidationOptions = Readonly<{
15
15
  */
16
16
  readonly staged?: StagedUpsertState;
17
17
  }>;
18
+ /** The semantic advisor result an upsert reports; no receipt for a non-requirement. */
19
+ export type UpsertSemanticAnalysis = Readonly<{
20
+ readonly receipt: SemanticAdvisorReceipt | null;
21
+ readonly warnings: readonly string[];
22
+ }>;
23
+ /**
24
+ * The semantic advisor analyses requirement prose: its propositions, claim
25
+ * keys and inventory contract describe a requirement. Any other entity has no
26
+ * prose inventory, so analysing its title (the fallback prose) would report
27
+ * claim keys nothing declares, for example a fact whose claim_key derives
28
+ * from claim_text. Its upsert carries no receipt.
29
+ */
30
+ export declare function analyzeUpsertSemantics(payload: UpsertInput & {
31
+ readonly relationships: readonly RelationshipInput[];
32
+ }): UpsertSemanticAnalysis;
18
33
  export type UpsertValidation = Readonly<{
19
34
  readonly validated: ValidatedUpsert;
20
35
  /** The upsert's relationships merged with the entity's existing ones. */
21
36
  readonly relationships: readonly RelationshipInput[];
22
- readonly semantic: ReturnType<typeof analyzeSemanticAdvisorInput>;
37
+ readonly semantic: UpsertSemanticAnalysis;
23
38
  }>;
24
39
  /**
25
40
  * The complete validation an upsert passes before its first write: schema,
@@ -1 +1 @@
1
- {"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/upsert.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAUxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AA8BnF,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AA8DpB,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GAChB,IAAI,CAkBN;AAwCD,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,aAAa,EAAE,SAAS,iBAAiB,EAAE,EAC3C,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA8BvC;AAmCD,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACrC,CAAC,CAAC;AAGH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,aAAa,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;CACnE,CAAC,CAAC;AAGH;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,gBAAgB,CAAC,CAiF3B;AAgDD,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C;;;;;;OAMG;IAEH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC,CAAC,CAAC;AAyBH,wBAAsB,aAAa,CACjC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CA2hBzC"}
1
+ {"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/upsert.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAexE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAyB3E,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AAsEpB,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GAChB,IAAI,CAkBN;AAwCD,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,aAAa,EAAE,SAAS,iBAAiB,EAAE,EAC3C,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA8BvC;AAmCD,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACrC,CAAC,CAAC;AAGH,uFAAuF;AACvF,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAChD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,CAAC,CAAC;AAGH;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,GAAG;IACrB,QAAQ,CAAC,aAAa,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACtD,GACA,sBAAsB,CASxB;AAGD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,aAAa,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;CAC3C,CAAC,CAAC;AAGH;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,gBAAgB,CAAC,CA0E3B;AAgDD,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C;;;;;;OAMG;IAEH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC,CAAC,CAAC;AAyBH,wBAAsB,aAAa,CACjC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CA2hBzC"}
@@ -59,6 +59,11 @@ function storedClaimKey(entity) {
59
59
  ? { claimKey: entity.claim_key }
60
60
  : {};
61
61
  }
62
+ // implements REQ-mcp-semantic-advisor-preflight
63
+ /** The written result's semantic advisor receipt, omitted for a non-requirement. */
64
+ function advisorReceipt(receipt) {
65
+ return receipt === null ? {} : { semanticAdvisor: receipt };
66
+ }
62
67
  function fileHash(pathname) {
63
68
  if (!existsSync(pathname))
64
69
  return null;
@@ -194,6 +199,21 @@ function reextractCanonicalSymbolEntity(symbolId, authoredRelativePath, workspac
194
199
  : { sourceFile: result.sourceFile }),
195
200
  };
196
201
  }
202
+ // implements REQ-mcp-semantic-advisor-preflight
203
+ /**
204
+ * The semantic advisor analyses requirement prose: its propositions, claim
205
+ * keys and inventory contract describe a requirement. Any other entity has no
206
+ * prose inventory, so analysing its title (the fallback prose) would report
207
+ * claim keys nothing declares, for example a fact whose claim_key derives
208
+ * from claim_text. Its upsert carries no receipt.
209
+ */
210
+ export function analyzeUpsertSemantics(payload) {
211
+ if (payload.type !== "req")
212
+ return { receipt: null, warnings: [] };
213
+ const semantic = analyzeSemanticAdvisorInput({ payload });
214
+ assertSemanticInventoryBoundary(payload, payload.relationships, semantic.receipt);
215
+ return semantic;
216
+ }
197
217
  // implements REQ-kibi-truthful-consistency
198
218
  /**
199
219
  * The complete validation an upsert passes before its first write: schema,
@@ -223,10 +243,7 @@ export async function validateUpsertForCommit(payload, context, options = {}) {
223
243
  await validateStrictLanePairing(prolog, validated.relationships, staged);
224
244
  await validateLiveRelationshipTargets(prolog, validated.entity, validated.relationships, staged);
225
245
  await validateSupersedesSourceHistory(prolog, validated.entity, validated.relationships, context.workspaceRoot, undefined, staged);
226
- const semantic = analyzeSemanticAdvisorInput({
227
- payload: { ...input, relationships },
228
- });
229
- assertSemanticInventoryBoundary({ ...input, relationships }, relationships, semantic.receipt);
246
+ const semantic = analyzeUpsertSemantics({ ...input, relationships });
230
247
  await assertLogicalGroundingClaimKeys(prolog, { ...input, relationships }, relationships, staged);
231
248
  await assertPredicateArgumentVocabulary(prolog, validated.entity, staged);
232
249
  // The origin the write records is added last: none of the checks above
@@ -321,7 +338,7 @@ export async function executeUpsert(input, context, options = {}) {
321
338
  let commitEntity;
322
339
  let relationshipCount = 0;
323
340
  let changeKind = null;
324
- let semanticAdvisor;
341
+ let semanticAdvisor = null;
325
342
  let compilerLock;
326
343
  let sourceMutationLock;
327
344
  let operationFailure;
@@ -510,7 +527,7 @@ export async function executeUpsert(input, context, options = {}) {
510
527
  updated: 0,
511
528
  relationships_created: validated.relationships.length,
512
529
  warnings: [...planRecoveryWarnings, ...semantic.warnings],
513
- semanticAdvisor: semantic.receipt,
530
+ ...advisorReceipt(semantic.receipt),
514
531
  ...storedClaimKey(commitEntity ?? validated.entity),
515
532
  deferredCommit,
516
533
  ...(sourceWrite ? { sourceWrites: [sourceWrite.receipt] } : {}),
@@ -587,10 +604,10 @@ export async function executeUpsert(input, context, options = {}) {
587
604
  ...semantic.warnings,
588
605
  ...coverage,
589
606
  ...idStyleWarnings,
590
- ...entityContextWarnings(input, commitEntity ?? validated.entity, context.workspaceRoot, sourceWrite?.receipt.path),
607
+ ...entityContextWarnings(input, commitEntity ?? validated.entity, context.workspaceRoot, { writtenPath: sourceWrite?.receipt.path }),
591
608
  ...shardWarnings,
592
609
  ],
593
- semanticAdvisor: semantic.receipt,
610
+ ...advisorReceipt(semantic.receipt),
594
611
  ...storedClaimKey(commitEntity ?? validated.entity),
595
612
  ...(shardWarnings.length > 0
596
613
  ? {
@@ -625,9 +642,9 @@ export async function executeUpsert(input, context, options = {}) {
625
642
  ? "skipped"
626
643
  : "no-conflict",
627
644
  checked_req_id: input.id,
628
- strict_readiness: semantic.receipt.logic_readiness === "modeled"
645
+ strict_readiness: semantic.receipt?.logic_readiness === "modeled"
629
646
  ? "modeled"
630
- : semantic.receipt.candidate_lane,
647
+ : (semantic.receipt?.candidate_lane ?? "none"),
631
648
  },
632
649
  }
633
650
  : {}),
@@ -662,7 +679,7 @@ export async function executeUpsert(input, context, options = {}) {
662
679
  updated: changeKind === "updated" ? 1 : 0,
663
680
  relationships_created: relationshipCount,
664
681
  warnings: [...planRecoveryWarnings, detail],
665
- semanticAdvisor: semanticAdvisor,
682
+ ...advisorReceipt(semanticAdvisor),
666
683
  status: "committed_with_repairs",
667
684
  effectFailures: [
668
685
  {
@@ -1 +1 @@
1
- {"version":3,"file":"validate-upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/validate-upsert.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAYxE,OAAO,KAAK,EAIV,WAAW,EACX,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA2MpB;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAC,CAgHjD"}
1
+ {"version":3,"file":"validate-upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/validate-upsert.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAWxE,OAAO,KAAK,EAIV,WAAW,EACX,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAiPpB;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAC,CAkHjD"}
@@ -1,13 +1,13 @@
1
1
  // implements REQ-kibi-operation-interface-parity
2
2
  import { escapeAtom } from "../../prolog/codec.js";
3
+ import { loadEntities } from "../../public/operations/discovery-entities.js";
3
4
  import { entityIdStyleWarnings } from "../../utils/entity-id-style.js";
4
- import { analyzeSemanticAdvisorInput } from "../semantic-advisor/analyze-prose.js";
5
- import { assertSemanticInventoryBoundary } from "../semantic-advisor/ingestion-boundary.js";
6
5
  import { entityContextWarnings } from "./context-warning.js";
7
6
  import { buildUpsertContradictionPreviewGoal, buildWhatIfAnalysisGoal, formatUpsertError, } from "./contradictions.js";
8
7
  import { validateRelationshipSources } from "./relationships.js";
8
+ import { canonicalSourcePath } from "./source-authoring.js";
9
9
  import { validateSymbolGranularity } from "./symbol-granularity.js";
10
- import { validateAppendOnlyProofReceipts, validateUpsertForCommit, } from "./upsert.js";
10
+ import { analyzeUpsertSemantics, validateAppendOnlyProofReceipts, validateUpsertForCommit, } from "./upsert.js";
11
11
  import { validateUpsertInput } from "./validation.js";
12
12
  import { symbolCoverageWarnings } from "./warnings.js";
13
13
  import { isBlockingWitness, parseWhatIfAnalysis } from "./what-if.js";
@@ -16,6 +16,37 @@ async function entityExists(prolog, id) {
16
16
  const result = await prolog.query(`once(kb_entity('${escapeAtom(id)}', _Type, _Props))`);
17
17
  return result.success;
18
18
  }
19
+ // implements REQ-kibi-operation-interface-parity, REQ-kb-entity-body-context
20
+ /**
21
+ * The authored document a real kb_upsert would update, resolved with the
22
+ * write's own rules (stored authored source, else the canonical lane path).
23
+ * The preview entity's `source` is the runtime marker, so without this the
24
+ * context warning would judge the statement instead of the existing body the
25
+ * write preserves. Undefined when the write would not touch a document.
26
+ */
27
+ async function previewDocumentPath(input, entity, context) {
28
+ if (context.fs === undefined || context.sourceFirst === false) {
29
+ return undefined;
30
+ }
31
+ try {
32
+ const existing = context.prolog === undefined
33
+ ? undefined
34
+ : (await loadEntities(context.prolog, {
35
+ id: input.id,
36
+ type: input.type,
37
+ }))[0];
38
+ const existingSource = typeof existing?.source === "string" ? existing.source : undefined;
39
+ if (input.document === undefined &&
40
+ existing !== undefined &&
41
+ !existingSource?.match(/\.(?:md|mdx|ya?ml|json)$/i)) {
42
+ return undefined;
43
+ }
44
+ return canonicalSourcePath(context, input, entity, existing);
45
+ }
46
+ catch {
47
+ return undefined;
48
+ }
49
+ }
19
50
  // implements REQ-kibi-operation-interface-parity, REQ-kibi-truthful-consistency
20
51
  /**
21
52
  * The commit-time contradiction check, run against a staged and rolled-back
@@ -130,11 +161,10 @@ async function validateWithoutStore(input, context) {
130
161
  await validateAppendOnlyProofReceipts(validated.entity, context);
131
162
  validateRelationshipSources(input.id, validated.relationships);
132
163
  await validateSymbolGranularity(validated.entity, validated.relationships, context);
133
- const relationships = validated.relationships;
134
- const semantic = analyzeSemanticAdvisorInput({
135
- payload: { ...input, relationships },
164
+ const semantic = analyzeUpsertSemantics({
165
+ ...input,
166
+ relationships: validated.relationships,
136
167
  });
137
- assertSemanticInventoryBoundary({ ...input, relationships }, relationships, semantic.receipt);
138
168
  return { entity: validated.entity, semantic };
139
169
  }
140
170
  // implements REQ-kibi-operation-interface-parity, REQ-kibi-truthful-consistency
@@ -194,7 +224,9 @@ export async function executeValidateUpsert(input, context) {
194
224
  ...semantic.warnings,
195
225
  ...coverageWarnings,
196
226
  ...idStyleWarnings,
197
- ...entityContextWarnings(input, entity, context.workspaceRoot),
227
+ ...entityContextWarnings(input, entity, context.workspaceRoot, {
228
+ targetPath: await previewDocumentPath(input, entity, context),
229
+ }),
198
230
  ],
199
231
  semanticAdvisor: semantic.receipt,
200
232
  normalizedPreview: entity,
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/validation.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,0BAA0B,EAC3B,MAAM,oCAAoC,CAAC;AAU5C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAqI/D,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,IAAI,GACR,eAAe,CA6DjB;AAED,OAAO,EAAE,0BAA0B,EAAE,CAAC"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/validation.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,0BAA0B,EAC3B,MAAM,oCAAoC,CAAC;AAU5C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA8L/D,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,IAAI,GACR,eAAe,CA6DjB;AAED,OAAO,EAAE,0BAA0B,EAAE,CAAC"}
@@ -64,9 +64,51 @@ function unknownEntityProperties(entity) {
64
64
  const known = upsertEntitySchema.properties;
65
65
  return Object.keys(entity).filter((key) => !Object.hasOwn(known, key));
66
66
  }
67
+ /**
68
+ * The fields a type-conditional `else: { not: ... }` branch of the entity
69
+ * schema forbids, and the types that may carry them; null for any other
70
+ * schema path.
71
+ */
72
+ function typeRestrictedFields(schemaPath) {
73
+ const match = /^#\/allOf\/(\d+)\/else\/not$/.exec(schemaPath);
74
+ if (match === null)
75
+ return null;
76
+ const allOf = upsertEntitySchema.allOf;
77
+ const branch = Array.isArray(allOf) ? allOf[Number(match[1])] : undefined;
78
+ if (branch === null || typeof branch !== "object")
79
+ return null;
80
+ const record = branch;
81
+ const typeRule = record.if?.properties?.type;
82
+ const types = Array.isArray(typeRule?.enum)
83
+ ? typeRule.enum.map(String)
84
+ : typeof typeRule?.const === "string"
85
+ ? [typeRule.const]
86
+ : [];
87
+ const not = record.else?.not;
88
+ const requiredLists = [
89
+ not?.required,
90
+ ...(Array.isArray(not?.anyOf)
91
+ ? not.anyOf.map((entry) => entry?.required)
92
+ : []),
93
+ ];
94
+ const fields = requiredLists.flatMap((list) => Array.isArray(list) ? list.map(String) : []);
95
+ return types.length > 0 && fields.length > 0 ? { fields, types } : null;
96
+ }
67
97
  // implements REQ-upsert-unknown-field-guidance
68
98
  function formatEntityErrors(entity, errors) {
69
- const messages = errors.map((error) => {
99
+ // A field only some entity types carry fails a type-conditional `not`
100
+ // branch; AJV then reports "must NOT be valid" plus the "else" it chose.
101
+ const restricted = new Set(errors.flatMap((error) => error.keyword === "not" && typeRestrictedFields(error.schemaPath)
102
+ ? [error.schemaPath.replace(/\/else\/not$/, "/if")]
103
+ : []));
104
+ const visible = errors.filter((error) => !(error.keyword === "if" && restricted.has(error.schemaPath)));
105
+ const messages = visible.map((error) => {
106
+ const restriction = error.keyword === "not" ? typeRestrictedFields(error.schemaPath) : null;
107
+ if (restriction !== null) {
108
+ const present = restriction.fields.filter((field) => Object.hasOwn(entity, field));
109
+ const named = present.length > 0 ? present : restriction.fields;
110
+ return `${named.join(", ")} ${named.length === 1 ? "is" : "are"} only valid on ${restriction.types.join(" and ")} entities; remove ${named.length === 1 ? "it" : "them"} from this ${String(entity.type ?? "entity")}`;
111
+ }
70
112
  const params = error.params;
71
113
  const path = error.instancePath || "root";
72
114
  if (error.keyword === "additionalProperties" && params.additionalProperty) {
@@ -1 +1 @@
1
- {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AASlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AAgE/B,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AA6G9E,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACnE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,IAAI,CAaN;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,SAAS,cAAoB,GAC5B,eAAe,EAAE,CA2CnB;AA8zBD;;;;;;GAMG;AAEH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAyqDD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB;AACzB;;;GAGG;AACH,WAAW,GAAE,MAAM,IAAsB,GACxC,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,eAAe,CAAC;CACpC,CAAC,CAmDD"}
1
+ {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AASlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AAiE/B,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AA6G9E,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACnE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,IAAI,CAaN;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,SAAS,cAAoB,GAC5B,eAAe,EAAE,CA2CnB;AAi1BD;;;;;;GAMG;AAEH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAyqDD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB;AACzB;;;GAGG;AACH,WAAW,GAAE,MAAM,IAAsB,GACxC,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,eAAe,CAAC;CACpC,CAAC,CAmDD"}
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import { OperationError } from "../../cli-errors.js";
4
4
  import { branchEnsureCommand, branchMigrateCommand, branchRecoverCommand, } from "../../commands/branch.js";
5
- import { migrateCommand } from "../../commands/migrate.js";
5
+ import { describeMigrationRun, migrateCommand, } from "../../commands/migrate.js";
6
6
  import { syncCommand } from "../../commands/sync.js";
7
7
  import { PROOF_INTEGRATION_CONFIGURE, applyProofIntegrationAction, } from "../../proof/integration-plan.js";
8
8
  import { loadEntities } from "../../public/operations/discovery-entities.js";
@@ -16,7 +16,7 @@ import { canonicalFilesystemPath } from "../../utils/canonical-path.js";
16
16
  import { isDerivedKbPath } from "../../utils/kb-paths.js";
17
17
  import { bootstrapEmptyKbSnapshotId, bootstrapPlanHash, } from "../bootstrap/types.js";
18
18
  import { validateBootstrapPayload } from "../bootstrap/validation.js";
19
- import { SCHEMA6_AUTOMATIC_CODES, applySchema6MigrationAction, } from "../migration/schema6.js";
19
+ import { SCHEMA6_APPROVABLE_REVIEW_CODES, SCHEMA6_AUTOMATIC_CODES, applySchema6MigrationAction, } from "../migration/schema6.js";
20
20
  import { buildUpsertBatchCommitGoal, formatUpsertError, } from "../mutation/contradictions.js";
21
21
  import { executeDelete } from "../mutation/delete.js";
22
22
  import { PLAN_APPLY_JOURNAL_VERSION, contentHash, createPlanApplyJournal, isPlanApplyJournalId, openPlanApplyJournal, openPlanApplyJournalById, planRecoveryNotes, publishJournaledFiles, reconcilePendingSourceReceipts, recordPlanApplyJournal, recoverPlanApplyJournal, restoreJournaledFiles, settlePendingPlanApplyJournals, storeFingerprint, withPlanRecoveryNotes, } from "../mutation/plan-apply-journal.js";
@@ -273,7 +273,8 @@ function validateMigrationPlanShape(args) {
273
273
  for (const action of actions) {
274
274
  if (action.state !== "ready")
275
275
  throw new Error(`Migration apply failed: action '${action.id}' is blocked`);
276
- if (action.safety !== "automatic" || action.autoApplicable !== true)
276
+ if (!(action.safety === "automatic" && action.autoApplicable === true) &&
277
+ !isApprovableReviewAction(action))
277
278
  throw new Error(`Migration apply failed: action '${action.id}' is not automatic`);
278
279
  for (const dependency of action.dependsOn) {
279
280
  if (!selected.has(dependency))
@@ -282,6 +283,19 @@ function validateMigrationPlanShape(args) {
282
283
  }
283
284
  return actions;
284
285
  }
286
+ /**
287
+ * A review action Kibi can execute once a person approves it by id. Callers
288
+ * pass the approved ids explicitly; `kibi migrate --apply-safe` without
289
+ * `--approved-action` selects only automatic actions, so these never run on
290
+ * their own.
291
+ */
292
+ // implements REQ-agent-guided-migration-orchestration, REQ-cli-schema-migration
293
+ function isApprovableReviewAction(action) {
294
+ return (action.safety === "review" &&
295
+ action.autoApplicable === false &&
296
+ action.invocation.kind === "cli" &&
297
+ SCHEMA6_APPROVABLE_REVIEW_CODES.has(action.code));
298
+ }
285
299
  function topologicalActions(actions) {
286
300
  const byId = new Map(actions.map((action) => [action.id, action]));
287
301
  const result = [];
@@ -2141,12 +2155,12 @@ async function applyMigrationPlan(args, context, onCommitted = () => undefined)
2141
2155
  continue;
2142
2156
  }
2143
2157
  try {
2144
- await applyMigrationAction(action, context);
2158
+ const detail = await applyMigrationAction(action, context);
2145
2159
  onCommitted();
2146
2160
  results.push({
2147
2161
  actionId: action.id,
2148
2162
  outcome: "applied",
2149
- detail: `Applied ${action.code}.`,
2163
+ detail: detail ?? `Applied ${action.code}.`,
2150
2164
  });
2151
2165
  }
2152
2166
  catch (error) {
@@ -2278,6 +2292,10 @@ async function applyMigrationPlan(args, context, onCommitted = () => undefined)
2278
2292
  structuredContent: payload,
2279
2293
  };
2280
2294
  }
2295
+ /**
2296
+ * Run one approved migration action. Returns a detail line for the action
2297
+ * result when the executor reports what it changed.
2298
+ */
2281
2299
  async function applyMigrationAction(action, context) {
2282
2300
  switch (action.code) {
2283
2301
  case "legacy_branch_storage":
@@ -2328,14 +2346,19 @@ async function applyMigrationAction(action, context) {
2328
2346
  return;
2329
2347
  case "schema_version_upgrade":
2330
2348
  case "invalid_schema_version":
2331
- case "legacy_storage_migration":
2332
- if ((await migrateCommand({
2349
+ case "legacy_storage_migration": {
2350
+ // implements REQ-agent-guided-migration-orchestration
2351
+ const run = await migrateCommand({
2333
2352
  yes: true,
2334
2353
  workspaceRoot: context.workspaceRoot,
2335
2354
  initializeMissingConfig: true,
2336
- })).exitCode !== 0)
2355
+ });
2356
+ if (run.exitCode !== 0)
2337
2357
  throw new Error("Schema migration did not complete successfully.");
2338
- return;
2358
+ return run.summary !== undefined
2359
+ ? describeMigrationRun(run.summary)
2360
+ : undefined;
2361
+ }
2339
2362
  case "symbol_refresh_coordinates":
2340
2363
  case "coverage_source_coordinates": {
2341
2364
  const result = await syncCommand({
@@ -2354,7 +2377,8 @@ async function applyMigrationAction(action, context) {
2354
2377
  applyProofIntegrationAction(action, context.workspaceRoot);
2355
2378
  return;
2356
2379
  default:
2357
- if (SCHEMA6_AUTOMATIC_CODES.has(action.code)) {
2380
+ if (SCHEMA6_AUTOMATIC_CODES.has(action.code) ||
2381
+ SCHEMA6_APPROVABLE_REVIEW_CODES.has(action.code)) {
2358
2382
  await applySchema6MigrationAction(action, context);
2359
2383
  return;
2360
2384
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ingestion-boundary.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/ingestion-boundary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAG3E,OAAO,EACL,KAAK,OAAO,EAOb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAuB,MAAM,YAAY,CAAC;AAgB9E,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,OAAO,CAAC;CACd,CAAC,CAAC;AAuEH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,+BAA+B,CA0IjC;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,IAAI,CAWN;AAoDD;;;;GAIG;AAEH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAC9D,MAAM,GAAG,IAAI,CAUf;AAGD,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,MAAM,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC,GACD,OAAO,CAAC,IAAI,CAAC,CAwBf"}
1
+ {"version":3,"file":"ingestion-boundary.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/ingestion-boundary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAG3E,OAAO,EACL,KAAK,OAAO,EAOb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAuB,MAAM,YAAY,CAAC;AAgB9E,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,OAAO,CAAC;CACd,CAAC,CAAC;AAuEH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,+BAA+B,CA0IjC;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,IAAI,CAWN;AAoFD;;;;GAIG;AAEH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAC9D,MAAM,GAAG,IAAI,CAUf;AAGD,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,MAAM,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC,GACD,OAAO,CAAC,IAAI,CAAC,CAwBf"}
@@ -192,9 +192,33 @@ function groundingClaimKeyError(modeledClaimKeys, grounded) {
192
192
  return `Proposition-complete ingestion failed: claim '${duplicate}' has more than one logical grounding relationship.`;
193
193
  if (groundedClaimKeys.length !== modeledClaimKeys.length ||
194
194
  modeledClaimKeys.some((claimKey) => !groundedClaimKeys.includes(claimKey)))
195
- return "Proposition-complete ingestion failed: modeled proposition claim_keys must match logical grounding target claim_keys exactly.";
195
+ return `Proposition-complete ingestion failed: modeled proposition claim_keys must match logical grounding target claim_keys exactly. ${claimKeyMismatchDetail(modeledClaimKeys, grounded)}`;
196
196
  return null;
197
197
  }
198
+ /**
199
+ * Why the mismatch names both sides: a claim_key is derived from the clause
200
+ * text, so a requirement whose prose was reworded (or analyzed from another
201
+ * field than the stored semantic_text) carries keys none of its grounding
202
+ * facts declare. Listing the unmatched modeled keys and the facts with the
203
+ * keys they still declare lets the agent see the drift and pick the remedy
204
+ * without diffing the KB by hand.
205
+ */
206
+ // implements REQ-kibi-proposition-complete-ingestion
207
+ function claimKeyMismatchDetail(modeledClaimKeys, grounded) {
208
+ const groundedClaimKeys = grounded.map(([, claimKey]) => claimKey);
209
+ const ungrounded = modeledClaimKeys.filter((claimKey) => !groundedClaimKeys.includes(claimKey));
210
+ const stale = grounded.filter(([, claimKey]) => !modeledClaimKeys.includes(claimKey));
211
+ const parts = [
212
+ ungrounded.length > 0
213
+ ? `Modeled claims with no grounding fact declaring their claim_key: ${ungrounded.map((claimKey) => `'${claimKey}'`).join(", ")}.`
214
+ : "",
215
+ stale.length > 0
216
+ ? `Grounding facts whose claim_key the inventory no longer models: ${stale.map(([target, claimKey]) => `${target} ('${claimKey}')`).join(", ")}.`
217
+ : "",
218
+ "A claim_key is derived from the clause text, so reworded prose (or prose analyzed from a field other than the stored semantic_text) yields new keys. Keep the stored semantic_text wording, or kb_upsert each grounding fact with the new claim_key and claim_text (same fact id) before this write; for a change of meaning, supersede the requirement instead.",
219
+ ];
220
+ return parts.filter(Boolean).join(" ");
221
+ }
198
222
  /**
199
223
  * The write-time claim_key check, run against planned writes only. Returns
200
224
  * null when it passes or when a target is outside the plan (the write-time
@@ -0,0 +1,19 @@
1
+ import type { PrologPort } from "../../public/operations/runtime-types.js";
2
+ import type { SemanticAdvisorReceipt } from "./types.js";
3
+ /**
4
+ * Why `kb_model mode: analyze` with the `id` of a stored requirement compares
5
+ * its ledger with the stored one: a claim_key is derived from the clause
6
+ * text, so analyzing reworded prose, or the title instead of the stored
7
+ * `semantic_text`, yields keys that none of the requirement's grounding
8
+ * facts declare. The write that follows such a receipt is refused by the
9
+ * proposition-complete check, long after the agent picked the wording. The
10
+ * receipt therefore warns at analysis time, naming the stored modeled
11
+ * clauses the analysis no longer states, the facts that ground them, and the
12
+ * clauses the analysis adds, so the agent can keep the stored wording or
13
+ * re-ground the facts before writing.
14
+ *
15
+ * Returns null when the requirement is not stored, carries no recorded
16
+ * ledger, or every stored modeled clause is still among the analyzed ones.
17
+ */
18
+ export declare function storedLedgerDriftWarning(prolog: Pick<PrologPort, "query">, id: string, text: string, receipt: SemanticAdvisorReceipt): Promise<string | null>;
19
+ //# sourceMappingURL=stored-ledger-drift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stored-ledger-drift.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/stored-ledger-drift.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAE3E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAwCzD;;;;;;;;;;;;;;GAcG;AAEH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkDxB"}
@@ -0,0 +1,78 @@
1
+ import { escapeAtom, parseListOfLists, parsePrologValue, } from "../../prolog/codec.js";
2
+ import { loadEntities } from "../../public/operations/discovery-entities.js";
3
+ import { SEMANTIC_INVENTORY_VERSION, isRecord, stringValue } from "./shared.js";
4
+ const LOGICAL_RELATIONSHIPS = [
5
+ "requires_property",
6
+ "requires_predicate",
7
+ "requires_rule",
8
+ ];
9
+ function atom(value) {
10
+ return stringValue(parsePrologValue(value ?? "")).replace(/^['"]|['"]$/g, "");
11
+ }
12
+ function quoted(text) {
13
+ return text ? ` ("${text}")` : "";
14
+ }
15
+ async function groundingFactsOf(prolog, id) {
16
+ const result = await prolog.query(`findall([Type,To,ClaimKey], (member(Type, [${LOGICAL_RELATIONSHIPS.join(",")}]), kb_relationship(Type, '${escapeAtom(id)}', To), ((kb_entity(To, fact, _DriftProps), memberchk(claim_key=_DriftRaw, _DriftProps), normalize_term_atom(_DriftRaw, ClaimKey)) -> true ; ClaimKey = '')), Rows)`);
17
+ if (!result.success)
18
+ return [];
19
+ return parseListOfLists(result.bindings.Rows ?? "[]").map(([type, target, claimKey]) => ({
20
+ type: atom(type),
21
+ target: atom(target),
22
+ claimKey: atom(claimKey),
23
+ }));
24
+ }
25
+ /**
26
+ * Why `kb_model mode: analyze` with the `id` of a stored requirement compares
27
+ * its ledger with the stored one: a claim_key is derived from the clause
28
+ * text, so analyzing reworded prose, or the title instead of the stored
29
+ * `semantic_text`, yields keys that none of the requirement's grounding
30
+ * facts declare. The write that follows such a receipt is refused by the
31
+ * proposition-complete check, long after the agent picked the wording. The
32
+ * receipt therefore warns at analysis time, naming the stored modeled
33
+ * clauses the analysis no longer states, the facts that ground them, and the
34
+ * clauses the analysis adds, so the agent can keep the stored wording or
35
+ * re-ground the facts before writing.
36
+ *
37
+ * Returns null when the requirement is not stored, carries no recorded
38
+ * ledger, or every stored modeled clause is still among the analyzed ones.
39
+ */
40
+ // implements REQ-kibi-proposition-complete-ingestion
41
+ export async function storedLedgerDriftWarning(prolog, id, text, receipt) {
42
+ const [stored] = await loadEntities(prolog, { id, type: "req" });
43
+ if (!stored ||
44
+ stored.semantic_inventory_version !== SEMANTIC_INVENTORY_VERSION ||
45
+ !Array.isArray(stored.semantic_inventory))
46
+ return null;
47
+ const storedInventory = stored.semantic_inventory.filter(isRecord);
48
+ const storedKeys = new Set(storedInventory.map((entry) => stringValue(entry.claim_key)));
49
+ const analyzedKeys = new Set(receipt.propositions.map(({ claim_key }) => claim_key));
50
+ const drifted = storedInventory
51
+ .filter((entry) => stringValue(entry.status) === "modeled" &&
52
+ !analyzedKeys.has(stringValue(entry.claim_key)))
53
+ .map((entry) => ({
54
+ claimKey: stringValue(entry.claim_key),
55
+ claimText: stringValue(entry.claim_text),
56
+ }));
57
+ if (drifted.length === 0)
58
+ return null;
59
+ const facts = await groundingFactsOf(prolog, id);
60
+ const groundedBy = (claimKey) => {
61
+ const matches = facts.filter((fact) => fact.claimKey === claimKey);
62
+ return matches.length > 0
63
+ ? `, grounded by ${matches.map((fact) => `${fact.target} via ${fact.type}`).join(" and ")}`
64
+ : "";
65
+ };
66
+ const missing = drifted
67
+ .map(({ claimKey, claimText }) => `'${claimKey}'${quoted(claimText)}${groundedBy(claimKey)}`)
68
+ .join("; ");
69
+ const added = receipt.propositions
70
+ .filter(({ claim_key }) => !storedKeys.has(claim_key))
71
+ .map(({ claim_key, claim_text }) => `'${claim_key}'${quoted(claim_text)}`)
72
+ .join("; ");
73
+ const storedText = stringValue(stored.semantic_text);
74
+ const cause = storedText && storedText !== text
75
+ ? `The analyzed prose differs from the stored semantic_text of ${id}.`
76
+ : "The stored clause keys were derived from different prose.";
77
+ return `Claim-key drift against the stored ledger of ${id}: stored modeled clause(s) not stated by this analysis: ${missing}.${added ? ` Clause(s) this analysis adds without a stored grounding: ${added}.` : ""} ${cause} A kb_upsert of ${id} with this ledger fails the proposition-complete claim_key check unless each affected grounding fact is first re-written with the new claim_key and claim_text (same fact id). Keep the stored semantic_text wording to keep the keys, or supersede the requirement for a change of meaning.`;
78
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/public/impact/diagnostics.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAYrF,OAAO,KAAK,EACV,iBAAiB,EACjB,gCAAgC,EAChC,mCAAmC,EACpC,MAAM,YAAY,CAAC;AA6FpB,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,mCAAmC,GAC3C,oBAAoB,EAAE,CA+CxB;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,oBAAoB,EAAE,CAsBxB;AAED,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAC3C,OAAO,CAET;AAED,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,SAAS,iBAAiB,EAAE,GACxC,OAAO,CAIT"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/public/impact/diagnostics.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAarF,OAAO,KAAK,EACV,iBAAiB,EACjB,gCAAgC,EAChC,mCAAmC,EACpC,MAAM,YAAY,CAAC;AAoFpB,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,mCAAmC,GAC3C,oBAAoB,EAAE,CAmExB;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,oBAAoB,EAAE,CAsBxB;AAED,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,SAAS,oBAAoB,EAAE,GAC3C,OAAO,CAET;AAED,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,SAAS,iBAAiB,EAAE,GACxC,OAAO,CAIT"}
@@ -1,14 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { extractSymbolsFromStagedFile, } from "../../traceability/symbol-extract.js";
4
- import { ALLOWED_GRANULARITY_REASONS_PROSE, getBehavioralSymbolNames, getNonBehavioralSymbolNames, isAllowedGranularityReason, isTraceabilityRelationshipType, } from "../symbol-granularity.js";
5
- function isNarrowerSymbol(manifestSymbolName, candidate, sourceSymbols) {
6
- if (candidate.name === manifestSymbolName)
7
- return false;
8
- if (candidate.name.startsWith(`${manifestSymbolName}.`))
9
- return true;
10
- return !sourceSymbols.some((symbol) => symbol.name === manifestSymbolName);
11
- }
4
+ import { ALLOWED_GRANULARITY_REASONS_PROSE, getBehavioralSymbolNames, getNonBehavioralSymbolNames, isAllowedGranularityReason, isTraceabilityRelationshipType, narrowerSymbols, } from "../symbol-granularity.js";
12
5
  function hasTraceabilityRelationship(result) {
13
6
  return result.relationships.some((relationship) => isTraceabilityRelationshipType(relationship.type));
14
7
  }
@@ -62,6 +55,7 @@ function isChangedBehavioralReviewCandidate(symbol) {
62
55
  return false;
63
56
  return symbol.hunkRanges.length > 0;
64
57
  }
58
+ // implements REQ-cli-staged-impact-enforcement, REQ-cli-check
65
59
  export function createSymbolGranularityDiagnostics(options) {
66
60
  const diagnostics = [];
67
61
  for (const result of options.manifestResults) {
@@ -76,25 +70,41 @@ export function createSymbolGranularityDiagnostics(options) {
76
70
  if (hasValidGranularityReason(result))
77
71
  continue;
78
72
  const { candidateSymbols, sourceSymbols } = getGranularSymbolsForSourceFile(result.sourceFile, options);
79
- const narrowerSymbols = candidateSymbols.filter((symbol) => isNarrowerSymbol(result.entity.title, symbol, sourceSymbols));
80
- if (narrowerSymbols.length === 0)
73
+ const narrower = narrowerSymbols(result.entity.title, candidateSymbols, sourceSymbols);
74
+ if (narrower.length === 0)
81
75
  continue;
82
- const behavioralNames = getBehavioralSymbolNames(narrowerSymbols);
76
+ const behavioralNames = getBehavioralSymbolNames(narrower);
83
77
  if (behavioralNames.length === 0)
84
78
  continue;
85
- const nonBehavioralNames = getNonBehavioralSymbolNames(narrowerSymbols);
79
+ const nonBehavioralNames = getNonBehavioralSymbolNames(narrower);
86
80
  const ignoredSymbolsSuggestion = nonBehavioralNames.length > 0
87
81
  ? ` Non-behavioral symbols ignored for this decision: ${nonBehavioralNames.join(", ")}.`
88
82
  : "";
83
+ // A title the extractor does not produce (a private class member, a
84
+ // renamed or removed declaration) can never become granular by refresh:
85
+ // say so instead of only calling the link coarse.
86
+ const titleExtracted = sourceSymbols.some((symbol) => symbol.name === result.entity.title);
89
87
  diagnostics.push({
90
88
  id: "symbol_granularity_violation",
91
89
  severity: "error",
92
90
  blocking: true,
93
91
  category: "symbol",
92
+ entityId: result.entity.id,
93
+ ...(result.entity.source ? { source: result.entity.source } : {}),
94
94
  files: [result.entity.source, result.sourceFile],
95
95
  docs: ["docs/symbol-traceability-taxonomy.md"],
96
- message: `Symbol ${result.entity.id} links ${result.sourceFile} coarsely while granular symbols are available (behavioral only): ${behavioralNames.join(", ")}`,
97
- suggestion: `Move ownership/coverage/test relationships to the narrow behavioral symbol, add a manifest behavioral anchor, or add granularity_reason with ${ALLOWED_GRANULARITY_REASONS_PROSE} when the coarse symbol is intentional.${ignoredSymbolsSuggestion}`,
96
+ message: titleExtracted
97
+ ? `Symbol ${result.entity.id} links ${result.sourceFile} coarsely while granular symbols are available (behavioral only): ${behavioralNames.join(", ")}`
98
+ : `Symbol ${result.entity.id} names '${result.entity.title}', which the source extractor does not produce for ${result.sourceFile} (private class members and removed or renamed declarations are not tracked), so it links ${result.sourceFile} coarsely while granular symbols are available (behavioral only): ${behavioralNames.join(", ")}`,
99
+ suggestion: titleExtracted
100
+ ? `Move ownership/coverage/test relationships to the narrow behavioral symbol, add a manifest behavioral anchor, or add granularity_reason with ${ALLOWED_GRANULARITY_REASONS_PROSE} when the coarse symbol is intentional.${ignoredSymbolsSuggestion}`
101
+ : `Re-point this symbol's relationships to the public declaration that owns the behavior (one of: ${behavioralNames.join(", ")}) or remove the symbol; add granularity_reason with ${ALLOWED_GRANULARITY_REASONS_PROSE} only when coarse ownership is intentional.${ignoredSymbolsSuggestion}`,
102
+ evidence: {
103
+ sourceFile: result.sourceFile,
104
+ title: result.entity.title,
105
+ titleExtracted,
106
+ granularSymbols: behavioralNames,
107
+ },
98
108
  });
99
109
  }
100
110
  return diagnostics;