kibi-cli 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/dist/cli-json-command.d.ts +1 -2
  2. package/dist/cli-json-command.d.ts.map +1 -1
  3. package/dist/cli-json-command.js +88 -44
  4. package/dist/cli-operation-loader.d.ts +3 -0
  5. package/dist/cli-operation-loader.d.ts.map +1 -0
  6. package/dist/cli-operation-loader.js +58 -0
  7. package/dist/cli-operation-metadata.d.ts +82 -0
  8. package/dist/cli-operation-metadata.d.ts.map +1 -0
  9. package/dist/cli-operation-metadata.js +106 -0
  10. package/dist/cli-protocol.d.ts +1 -1
  11. package/dist/cli-protocol.d.ts.map +1 -1
  12. package/dist/cli-protocol.js +4 -3
  13. package/dist/cli-register-foundation.d.ts.map +1 -1
  14. package/dist/cli-register-foundation.js +14 -17
  15. package/dist/cli-register-json.d.ts +3 -0
  16. package/dist/cli-register-json.d.ts.map +1 -0
  17. package/dist/cli-register-json.js +35 -0
  18. package/dist/cli-register-maintenance.d.ts.map +1 -1
  19. package/dist/cli-register-maintenance.js +41 -8
  20. package/dist/cli-register-reporting.d.ts.map +1 -1
  21. package/dist/cli-register-reporting.js +18 -14
  22. package/dist/cli-register-skills.d.ts.map +1 -1
  23. package/dist/cli-register-skills.js +8 -6
  24. package/dist/cli.d.ts.map +1 -1
  25. package/dist/cli.js +3 -2
  26. package/dist/commands/aggregated-checks.d.ts.map +1 -1
  27. package/dist/commands/aggregated-checks.js +1 -0
  28. package/dist/commands/check.d.ts.map +1 -1
  29. package/dist/commands/check.js +30 -12
  30. package/dist/commands/coverage.d.ts +5 -0
  31. package/dist/commands/coverage.d.ts.map +1 -1
  32. package/dist/commands/coverage.js +6 -1
  33. package/dist/commands/discovery-shared.d.ts +2 -1
  34. package/dist/commands/discovery-shared.d.ts.map +1 -1
  35. package/dist/commands/discovery-shared.js +35 -13
  36. package/dist/commands/discovery-table.d.ts.map +1 -1
  37. package/dist/commands/discovery-table.js +96 -5
  38. package/dist/commands/engine.d.ts +8 -0
  39. package/dist/commands/engine.d.ts.map +1 -0
  40. package/dist/commands/engine.js +100 -0
  41. package/dist/commands/gaps.js +1 -1
  42. package/dist/commands/graph.js +1 -1
  43. package/dist/commands/query.d.ts.map +1 -1
  44. package/dist/commands/query.js +3 -1
  45. package/dist/commands/search.d.ts.map +1 -1
  46. package/dist/commands/search.js +3 -1
  47. package/dist/commands/status.d.ts.map +1 -1
  48. package/dist/commands/status.js +2 -1
  49. package/dist/commands/sync/cache.d.ts +12 -0
  50. package/dist/commands/sync/cache.d.ts.map +1 -1
  51. package/dist/commands/sync/cache.js +38 -1
  52. package/dist/commands/sync/persistence.d.ts +10 -1
  53. package/dist/commands/sync/persistence.d.ts.map +1 -1
  54. package/dist/commands/sync/persistence.js +276 -43
  55. package/dist/commands/sync/staging.d.ts +10 -1
  56. package/dist/commands/sync/staging.d.ts.map +1 -1
  57. package/dist/commands/sync/staging.js +84 -1
  58. package/dist/commands/sync.d.ts.map +1 -1
  59. package/dist/commands/sync.js +521 -27
  60. package/dist/commands/usage-metrics.d.ts +66 -2
  61. package/dist/commands/usage-metrics.d.ts.map +1 -1
  62. package/dist/commands/usage-metrics.js +34 -24
  63. package/dist/commands/usage-remediation.d.ts +9 -0
  64. package/dist/commands/usage-remediation.d.ts.map +1 -0
  65. package/dist/commands/usage-remediation.js +52 -0
  66. package/dist/engine-daemon.d.ts +3 -0
  67. package/dist/engine-daemon.d.ts.map +1 -0
  68. package/dist/engine-daemon.js +34 -0
  69. package/dist/engine.d.ts +63 -0
  70. package/dist/engine.d.ts.map +1 -0
  71. package/dist/engine.js +1256 -0
  72. package/dist/extractors/markdown.d.ts +8 -0
  73. package/dist/extractors/markdown.d.ts.map +1 -1
  74. package/dist/extractors/markdown.js +69 -2
  75. package/dist/extractors/relationships.d.ts +2 -0
  76. package/dist/extractors/relationships.d.ts.map +1 -1
  77. package/dist/extractors/relationships.js +15 -17
  78. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  79. package/dist/operations/modeling/logic-modeling.js +14 -5
  80. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  81. package/dist/operations/modeling/model-requirement.js +27 -2
  82. package/dist/operations/modeling/predicate-applyplan.d.ts +1 -1
  83. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  84. package/dist/operations/modeling/predicate-applyplan.js +18 -6
  85. package/dist/operations/modeling/predicate-inference.d.ts.map +1 -1
  86. package/dist/operations/modeling/predicate-inference.js +5 -17
  87. package/dist/operations/modeling/predicate-loader.js +1 -1
  88. package/dist/operations/modeling/predicate-types.d.ts +9 -1
  89. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  90. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  91. package/dist/operations/modeling/suggest-predicates.js +35 -11
  92. package/dist/operations/mutation/audit.js +1 -1
  93. package/dist/operations/mutation/contradictions.d.ts +1 -1
  94. package/dist/operations/mutation/contradictions.d.ts.map +1 -1
  95. package/dist/operations/mutation/contradictions.js +23 -18
  96. package/dist/operations/mutation/serialization.d.ts.map +1 -1
  97. package/dist/operations/mutation/serialization.js +6 -3
  98. package/dist/operations/mutation/upsert.d.ts +3 -1
  99. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  100. package/dist/operations/mutation/upsert.js +51 -12
  101. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  102. package/dist/operations/mutation/validate-upsert.js +18 -2
  103. package/dist/operations/mutation/validation.d.ts.map +1 -1
  104. package/dist/operations/mutation/validation.js +8 -0
  105. package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
  106. package/dist/operations/semantic-advisor/analysis-receipt.js +7 -1
  107. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  108. package/dist/operations/semantic-advisor/analyze-prose.js +76 -61
  109. package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
  110. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  111. package/dist/operations/semantic-advisor/clauses.js +7 -7
  112. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +18 -0
  113. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -0
  114. package/dist/operations/semantic-advisor/ingestion-boundary.js +167 -0
  115. package/dist/operations/semantic-advisor/shared.d.ts +8 -0
  116. package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
  117. package/dist/operations/semantic-advisor/shared.js +37 -3
  118. package/dist/operations/semantic-advisor/types.d.ts +5 -0
  119. package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
  120. package/dist/prolog/codec.d.ts.map +1 -1
  121. package/dist/prolog/codec.js +9 -2
  122. package/dist/prolog.d.ts +14 -0
  123. package/dist/prolog.d.ts.map +1 -1
  124. package/dist/prolog.js +279 -69
  125. package/dist/public/diagnostic-usage.d.ts +17 -0
  126. package/dist/public/diagnostic-usage.d.ts.map +1 -0
  127. package/dist/public/diagnostic-usage.js +152 -0
  128. package/dist/public/distribution-parity.d.ts +90 -0
  129. package/dist/public/distribution-parity.d.ts.map +1 -0
  130. package/dist/public/distribution-parity.js +367 -0
  131. package/dist/public/impact/full-kb-quality.d.ts +2 -0
  132. package/dist/public/impact/full-kb-quality.d.ts.map +1 -1
  133. package/dist/public/impact/full-kb-quality.js +53 -1
  134. package/dist/public/impact-diagnostics.d.ts +2 -0
  135. package/dist/public/impact-diagnostics.d.ts.map +1 -1
  136. package/dist/public/impact-diagnostics.js +1 -0
  137. package/dist/public/operations/catalog.d.ts +151 -6
  138. package/dist/public/operations/catalog.d.ts.map +1 -1
  139. package/dist/public/operations/check-executor.d.ts.map +1 -1
  140. package/dist/public/operations/check-executor.js +4 -0
  141. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  142. package/dist/public/operations/discovery-entities.js +3 -2
  143. package/dist/public/operations/discovery-executors.d.ts +6 -0
  144. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  145. package/dist/public/operations/discovery-executors.js +58 -5
  146. package/dist/public/operations/index.d.ts +4 -0
  147. package/dist/public/operations/index.d.ts.map +1 -1
  148. package/dist/public/operations/index.js +4 -0
  149. package/dist/public/operations/legacy-migration-plan.d.ts +113 -0
  150. package/dist/public/operations/legacy-migration-plan.d.ts.map +1 -0
  151. package/dist/public/operations/legacy-migration-plan.js +409 -0
  152. package/dist/public/operations/node-ports.d.ts.map +1 -1
  153. package/dist/public/operations/node-ports.js +90 -0
  154. package/dist/public/operations/prolog-json.d.ts.map +1 -1
  155. package/dist/public/operations/prolog-json.js +14 -4
  156. package/dist/public/operations/repair-plan.d.ts +184 -0
  157. package/dist/public/operations/repair-plan.d.ts.map +1 -0
  158. package/dist/public/operations/repair-plan.js +397 -0
  159. package/dist/public/operations/runtime-types.d.ts +38 -0
  160. package/dist/public/operations/runtime-types.d.ts.map +1 -1
  161. package/dist/public/operations/specs/check.d.ts +2 -2
  162. package/dist/public/operations/specs/check.js +2 -2
  163. package/dist/public/operations/specs/discovery.d.ts +1 -1
  164. package/dist/public/operations/specs/discovery.js +1 -1
  165. package/dist/public/operations/specs/modeling.d.ts +19 -1
  166. package/dist/public/operations/specs/modeling.d.ts.map +1 -1
  167. package/dist/public/operations/specs/modeling.js +16 -1
  168. package/dist/public/operations/specs/mutation-schemas.d.ts +96 -1
  169. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  170. package/dist/public/operations/specs/mutation-schemas.js +40 -1
  171. package/dist/public/operations/specs/reporting.d.ts +42 -1
  172. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  173. package/dist/public/operations/specs/reporting.js +70 -6
  174. package/dist/public/operations/specs/semantic.js +1 -1
  175. package/dist/public/operations/specs/upsert.d.ts +96 -1
  176. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  177. package/dist/public/operations/workspace-snapshot.d.ts +10 -0
  178. package/dist/public/operations/workspace-snapshot.d.ts.map +1 -0
  179. package/dist/public/operations/workspace-snapshot.js +20 -0
  180. package/dist/public/schemas/entity.d.ts.map +1 -1
  181. package/dist/public/schemas/entity.js +43 -1
  182. package/dist/public/skills/init-kibi/SKILL.md +2 -2
  183. package/dist/public/skills/kibi-freshness/SKILL.md +2 -2
  184. package/dist/public/skills/kibi-traceability/SKILL.md +8 -2
  185. package/dist/public/skills/kibi-usage/SKILL.md +12 -6
  186. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  187. package/dist/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  188. package/dist/public/skills/kibi-usage/resources/operation-access.md +1 -1
  189. package/dist/public/skills/kibi-usage/resources/workflows.md +19 -2
  190. package/dist/public/telemetry-acceptance.d.ts +89 -0
  191. package/dist/public/telemetry-acceptance.d.ts.map +1 -0
  192. package/dist/public/telemetry-acceptance.js +597 -0
  193. package/dist/public/telemetry-remediation.d.ts +33 -0
  194. package/dist/public/telemetry-remediation.d.ts.map +1 -0
  195. package/dist/public/telemetry-remediation.js +257 -0
  196. package/dist/public/verification-receipt.d.ts +75 -0
  197. package/dist/public/verification-receipt.d.ts.map +1 -0
  198. package/dist/public/verification-receipt.js +129 -0
  199. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  200. package/dist/runtime/cli-runtime.js +33 -37
  201. package/dist/schemas/entity.schema.json +96 -2
  202. package/dist/traceability/temp-kb.d.ts.map +1 -1
  203. package/dist/traceability/temp-kb.js +36 -3
  204. package/dist/types/entities.d.ts +1 -0
  205. package/dist/types/entities.d.ts.map +1 -1
  206. package/dist/utils/rule-registry.d.ts +2 -0
  207. package/dist/utils/rule-registry.d.ts.map +1 -1
  208. package/package.json +36 -4
  209. package/src/public/diagnostic-usage.ts +206 -0
  210. package/src/public/distribution-parity.ts +580 -0
  211. package/src/public/impact/full-kb-quality.ts +74 -1
  212. package/src/public/impact-diagnostics.ts +16 -0
  213. package/src/public/operations/check-executor.ts +4 -0
  214. package/src/public/operations/discovery-entities.ts +3 -2
  215. package/src/public/operations/discovery-executors.ts +68 -5
  216. package/src/public/operations/index.ts +4 -0
  217. package/src/public/operations/legacy-migration-plan.ts +688 -0
  218. package/src/public/operations/node-ports.ts +109 -0
  219. package/src/public/operations/prolog-json.ts +15 -3
  220. package/src/public/operations/repair-plan.ts +562 -0
  221. package/src/public/operations/runtime-types.ts +47 -0
  222. package/src/public/operations/specs/check.ts +2 -2
  223. package/src/public/operations/specs/discovery.ts +1 -1
  224. package/src/public/operations/specs/modeling.ts +19 -1
  225. package/src/public/operations/specs/mutation-schemas.ts +47 -1
  226. package/src/public/operations/specs/reporting.ts +91 -6
  227. package/src/public/operations/specs/semantic.ts +1 -1
  228. package/src/public/operations/workspace-snapshot.ts +28 -0
  229. package/src/public/schemas/entity.ts +44 -1
  230. package/src/public/skills/init-kibi/SKILL.md +2 -2
  231. package/src/public/skills/kibi-freshness/SKILL.md +2 -2
  232. package/src/public/skills/kibi-traceability/SKILL.md +8 -2
  233. package/src/public/skills/kibi-usage/SKILL.md +12 -6
  234. package/src/public/skills/kibi-usage/resources/fact-lanes.md +1 -1
  235. package/src/public/skills/kibi-usage/resources/logic-ir.md +1 -1
  236. package/src/public/skills/kibi-usage/resources/operation-access.md +1 -1
  237. package/src/public/skills/kibi-usage/resources/workflows.md +19 -2
  238. package/src/public/telemetry-acceptance.ts +859 -0
  239. package/src/public/telemetry-remediation.ts +462 -0
  240. package/src/public/verification-receipt.ts +170 -0
  241. package/src/schemas/entity.schema.json +96 -2
@@ -0,0 +1,688 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { requirementSemanticText } from "../../extractors/markdown.js";
4
+ import { buildSuggestion } from "../../operations/modeling/predicate-applyplan.js";
5
+ import { BUILT_IN_PREDICATE_SCHEMAS } from "../../operations/modeling/predicate-catalog.js";
6
+ import { inferSubject } from "../../operations/modeling/predicate-inference.js";
7
+ import { loadExistingPredicateSchemas } from "../../operations/modeling/predicate-loader.js";
8
+ import { scoreSchema } from "../../operations/modeling/predicate-ranker.js";
9
+ import type { PredicateSchemaCandidate } from "../../operations/modeling/predicate-types.js";
10
+ import { analyzeSemanticAdvisorInput } from "../../operations/semantic-advisor/analyze-prose.js";
11
+ import { semanticSourceHash } from "../../operations/semantic-advisor/shared.js";
12
+ import type {
13
+ SemanticModelingSuggestion,
14
+ SemanticProposition,
15
+ } from "../../operations/semantic-advisor/types.js";
16
+ import { loadMarkdownBody } from "../../search-ranking.js";
17
+ import { loadEntities } from "./discovery-entities.js";
18
+ import type { RepairPlan, RepairPlanBatch } from "./repair-plan.js";
19
+ import type { OperationContext } from "./runtime-types.js";
20
+
21
+ export const LEGACY_MIGRATION_PLAN_VERSION =
22
+ "kibi.legacy-migration-plan.v1" as const;
23
+
24
+ export type LegacyMigrationPlanInput = {
25
+ readonly migrationLimit?: number;
26
+ readonly migrationOffset?: number;
27
+ readonly migrationPredicateLimit?: number;
28
+ readonly migrationPredicateMinScore?: number;
29
+ };
30
+
31
+ export type LegacyMigrationPredicateCandidate = {
32
+ readonly schemaId: string;
33
+ readonly origin: "project_local" | "built_in";
34
+ readonly predicateName: string;
35
+ readonly argumentNames: readonly string[];
36
+ readonly argumentTypes: readonly string[];
37
+ readonly score: number;
38
+ readonly bindingStatus: "complete" | "incomplete";
39
+ readonly unboundArguments: readonly string[];
40
+ readonly polarity: "assert" | "deny";
41
+ readonly canonicalKey: string;
42
+ readonly writeEligible: false;
43
+ };
44
+
45
+ export type LegacyMigrationProposition = {
46
+ readonly claimKey: string;
47
+ readonly claimText: string;
48
+ readonly role: string;
49
+ readonly inventoryStatus: string;
50
+ readonly span: { readonly start: number; readonly end: number };
51
+ readonly recommendedLane:
52
+ | "strict_property"
53
+ | "predicate"
54
+ | "rule"
55
+ | "unresolved"
56
+ | "nonlogical";
57
+ readonly disposition:
58
+ | "strict_property_candidate"
59
+ | "predicate_candidate"
60
+ | "rule_candidate"
61
+ | "unresolved_ambiguity"
62
+ | "unresolved_ontology_gap"
63
+ | "nonlogical";
64
+ readonly predicateCandidates: readonly LegacyMigrationPredicateCandidate[];
65
+ readonly reviewRequired: true;
66
+ };
67
+
68
+ export type LegacyMigrationBatch = {
69
+ readonly id: string;
70
+ readonly requirementId: string;
71
+ readonly repairBatchId: string;
72
+ readonly dependsOn: readonly string[];
73
+ readonly state: "ready_for_review" | "blocked";
74
+ readonly autoApplicable: false;
75
+ readonly sourceBinding: {
76
+ readonly status: "compatible" | "conflict" | "missing";
77
+ readonly sourceFile: string | null;
78
+ readonly sourceKind: "authored_markdown_body";
79
+ readonly sourceHash: string | null;
80
+ readonly sourceByteLength: number;
81
+ readonly persistedField: "semantic_text" | null;
82
+ readonly existingTextRef: string | null;
83
+ readonly existingSemanticText: string | null;
84
+ readonly reason: string;
85
+ };
86
+ readonly sourceText: string | null;
87
+ readonly claimDelta: {
88
+ readonly preserved: readonly string[];
89
+ readonly added: readonly string[];
90
+ readonly stale: readonly string[];
91
+ };
92
+ readonly propositions: readonly LegacyMigrationProposition[];
93
+ readonly requirementPropertyPatchPreview: Readonly<
94
+ Record<string, unknown>
95
+ > | null;
96
+ readonly diagnostics: readonly string[];
97
+ };
98
+
99
+ export type LegacyMigrationPlan = {
100
+ readonly version: typeof LEGACY_MIGRATION_PLAN_VERSION;
101
+ readonly planId: string;
102
+ readonly readOnly: true;
103
+ readonly status: "no_candidates" | "ready" | "blocked" | "partial";
104
+ readonly codeSnapshot: string;
105
+ readonly repairPlanId: string;
106
+ readonly predicateCatalogHash: string;
107
+ readonly scope: {
108
+ readonly repairPlanComplete: boolean;
109
+ readonly candidateRequirements: number;
110
+ readonly selectedRequirements: number;
111
+ readonly offset: number;
112
+ readonly limit: number;
113
+ readonly selectionComplete: boolean;
114
+ readonly nextOffset: number | null;
115
+ };
116
+ readonly applicationPolicy: {
117
+ readonly previewOnly: true;
118
+ readonly reviewEveryProposition: true;
119
+ readonly preserveExistingTextRefs: true;
120
+ readonly exactBindingsBeforeWrite: true;
121
+ readonly validateBeforeEachWrite: true;
122
+ readonly sequentialUpsertsOnly: true;
123
+ readonly recheckContradictionsAfterEachBatch: true;
124
+ };
125
+ readonly summary: {
126
+ readonly batchCount: number;
127
+ readonly readyBatchCount: number;
128
+ readonly blockedBatchCount: number;
129
+ readonly propositionCount: number;
130
+ readonly assertivePropositionCount: number;
131
+ readonly unresolvedPropositionCount: number;
132
+ readonly predicateCandidateCount: number;
133
+ };
134
+ readonly batches: readonly LegacyMigrationBatch[];
135
+ readonly diagnostics: readonly string[];
136
+ };
137
+
138
+ type LegacyMigrationPlanDependencies = {
139
+ readonly requirements: readonly Readonly<Record<string, unknown>>[];
140
+ readonly projectPredicateSchemas: readonly PredicateSchemaCandidate[];
141
+ readonly readSource: (
142
+ requirement: Readonly<Record<string, unknown>>,
143
+ ) => Promise<string | null>;
144
+ readonly diagnostics?: readonly string[];
145
+ };
146
+
147
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
148
+ return value !== null && typeof value === "object" && !Array.isArray(value);
149
+ }
150
+
151
+ function stringList(value: unknown): string[] {
152
+ if (typeof value === "string") return [stripKbEntityPrefix(value)];
153
+ if (!Array.isArray(value)) return [];
154
+ return value.flatMap((item) =>
155
+ typeof item === "string" ? [stripKbEntityPrefix(item)] : [],
156
+ );
157
+ }
158
+
159
+ function stripKbEntityPrefix(value: string): string {
160
+ return value.replace(/^kb:entity\//, "");
161
+ }
162
+
163
+ function clampInteger(
164
+ value: number | undefined,
165
+ fallback: number,
166
+ minimum: number,
167
+ maximum: number,
168
+ ): number {
169
+ if (!Number.isInteger(value)) return fallback;
170
+ return Math.min(maximum, Math.max(minimum, value ?? fallback));
171
+ }
172
+
173
+ function clampScore(value: number | undefined): number {
174
+ if (typeof value !== "number" || !Number.isFinite(value)) return 0.35;
175
+ return Math.min(1, Math.max(0, value));
176
+ }
177
+
178
+ function stableValue(value: unknown): unknown {
179
+ if (Array.isArray(value)) return value.map(stableValue);
180
+ if (!isRecord(value)) return value;
181
+ return Object.fromEntries(
182
+ Object.entries(value)
183
+ .sort(([left], [right]) => left.localeCompare(right))
184
+ .map(([key, item]) => [key, stableValue(item)]),
185
+ );
186
+ }
187
+
188
+ function stableStringify(value: unknown): string {
189
+ return JSON.stringify(stableValue(value));
190
+ }
191
+
192
+ function hash(value: unknown): string {
193
+ return createHash("sha256").update(stableStringify(value)).digest("hex");
194
+ }
195
+
196
+ function migrationRepairBatches(plan: RepairPlan): RepairPlanBatch[] {
197
+ const seen = new Set<string>();
198
+ return plan.batches
199
+ .filter(
200
+ (batch) =>
201
+ batch.phase === "semantic_inventory" && batch.state === "ready",
202
+ )
203
+ .filter((batch) => {
204
+ if (seen.has(batch.requirementId)) return false;
205
+ seen.add(batch.requirementId);
206
+ return true;
207
+ })
208
+ .sort((left, right) =>
209
+ left.requirementId.localeCompare(right.requirementId),
210
+ );
211
+ }
212
+
213
+ function schemaIdentity(schema: PredicateSchemaCandidate): string {
214
+ return `${schema.predicate_name}(${schema.argument_names.join(",")}):${schema.argument_types.join(",")}`;
215
+ }
216
+
217
+ function predicateCatalog(
218
+ projectSchemas: readonly PredicateSchemaCandidate[],
219
+ ): readonly {
220
+ readonly schema: PredicateSchemaCandidate;
221
+ readonly origin: "project_local" | "built_in";
222
+ }[] {
223
+ const result: Array<{
224
+ schema: PredicateSchemaCandidate;
225
+ origin: "project_local" | "built_in";
226
+ }> = [];
227
+ const seen = new Set<string>();
228
+ for (const [origin, schemas] of [
229
+ ["project_local", projectSchemas],
230
+ ["built_in", BUILT_IN_PREDICATE_SCHEMAS],
231
+ ] as const) {
232
+ for (const schema of schemas) {
233
+ const identity = schemaIdentity(schema);
234
+ if (seen.has(identity)) continue;
235
+ seen.add(identity);
236
+ result.push({ schema, origin });
237
+ }
238
+ }
239
+ return result;
240
+ }
241
+
242
+ function rankPredicateCandidates(
243
+ claimText: string,
244
+ catalog: ReturnType<typeof predicateCatalog>,
245
+ limit: number,
246
+ minimumScore: number,
247
+ ): LegacyMigrationPredicateCandidate[] {
248
+ const subject = inferSubject(claimText, undefined);
249
+ return catalog
250
+ .map(({ schema, origin }) => ({
251
+ schema,
252
+ origin,
253
+ score: scoreSchema(schema, claimText),
254
+ }))
255
+ .filter(({ score }) => score >= minimumScore)
256
+ .sort(
257
+ (left, right) =>
258
+ right.score - left.score ||
259
+ (left.origin === right.origin
260
+ ? 0
261
+ : left.origin === "project_local"
262
+ ? -1
263
+ : 1) ||
264
+ left.schema.predicate_name.localeCompare(right.schema.predicate_name) ||
265
+ left.schema.id.localeCompare(right.schema.id),
266
+ )
267
+ .slice(0, limit)
268
+ .map(({ schema, origin, score }) => {
269
+ const suggestion = buildSuggestion(schema, claimText, subject, score);
270
+ return {
271
+ schemaId: schema.id,
272
+ origin,
273
+ predicateName: schema.predicate_name,
274
+ argumentNames: [...schema.argument_names],
275
+ argumentTypes: [...schema.argument_types],
276
+ score,
277
+ bindingStatus: suggestion.binding_status,
278
+ unboundArguments: [...suggestion.unbound_arguments],
279
+ polarity: suggestion.polarity,
280
+ canonicalKey: suggestion.canonical_key,
281
+ writeEligible: false,
282
+ };
283
+ });
284
+ }
285
+
286
+ function propositionDisposition(
287
+ proposition: SemanticProposition,
288
+ suggestion: SemanticModelingSuggestion | undefined,
289
+ candidates: readonly LegacyMigrationPredicateCandidate[],
290
+ ): Pick<LegacyMigrationProposition, "recommendedLane" | "disposition"> {
291
+ if (proposition.status === "nonlogical") {
292
+ return { recommendedLane: "nonlogical", disposition: "nonlogical" };
293
+ }
294
+ if (suggestion?.kind === "strict_property") {
295
+ return {
296
+ recommendedLane: "strict_property",
297
+ disposition: "strict_property_candidate",
298
+ };
299
+ }
300
+ if (suggestion?.kind === "rule") {
301
+ return { recommendedLane: "rule", disposition: "rule_candidate" };
302
+ }
303
+ if (suggestion?.kind === "predicate" || candidates.length > 0) {
304
+ return {
305
+ recommendedLane: "predicate",
306
+ disposition: "predicate_candidate",
307
+ };
308
+ }
309
+ if (
310
+ proposition.status === "ambiguous" ||
311
+ suggestion?.kind === "ambiguity_observation"
312
+ ) {
313
+ return {
314
+ recommendedLane: "unresolved",
315
+ disposition: "unresolved_ambiguity",
316
+ };
317
+ }
318
+ return {
319
+ recommendedLane: "unresolved",
320
+ disposition: "unresolved_ontology_gap",
321
+ };
322
+ }
323
+
324
+ function claimDelta(
325
+ existing: readonly string[],
326
+ expected: readonly string[],
327
+ ): LegacyMigrationBatch["claimDelta"] {
328
+ const existingSet = new Set(existing);
329
+ const expectedSet = new Set(expected);
330
+ return {
331
+ preserved: expected.filter((claim) => existingSet.has(claim)).sort(),
332
+ added: expected.filter((claim) => !existingSet.has(claim)).sort(),
333
+ stale: existing.filter((claim) => !expectedSet.has(claim)).sort(),
334
+ };
335
+ }
336
+
337
+ async function buildBatch(
338
+ repairBatch: RepairPlanBatch,
339
+ requirement: Readonly<Record<string, unknown>> | undefined,
340
+ readSource: LegacyMigrationPlanDependencies["readSource"],
341
+ catalog: ReturnType<typeof predicateCatalog>,
342
+ predicateLimit: number,
343
+ predicateMinScore: number,
344
+ ): Promise<LegacyMigrationBatch> {
345
+ const requirementId = repairBatch.requirementId;
346
+ const sourceFile =
347
+ typeof requirement?.source === "string" ? requirement.source : null;
348
+ const sourceText = requirement ? await readSource(requirement) : null;
349
+ const existingTextRef =
350
+ typeof requirement?.text_ref === "string" && requirement.text_ref.trim()
351
+ ? requirement.text_ref.trim()
352
+ : null;
353
+ const existingSemanticText =
354
+ typeof requirement?.semantic_text === "string" &&
355
+ requirement.semantic_text.trim()
356
+ ? requirement.semantic_text.trim()
357
+ : null;
358
+ const sourceHash = sourceText ? semanticSourceHash(sourceText) : null;
359
+ const sourceMissing = requirement === undefined || !sourceText;
360
+ const sourceConflict =
361
+ !sourceMissing &&
362
+ existingSemanticText !== null &&
363
+ existingSemanticText !== sourceText;
364
+ const sourceStatus = sourceMissing
365
+ ? "missing"
366
+ : sourceConflict
367
+ ? "conflict"
368
+ : "compatible";
369
+ const diagnostics: string[] = [];
370
+ if (requirement === undefined) {
371
+ diagnostics.push(
372
+ `Requirement ${requirementId} is present in the repair plan but absent from exact KB query results.`,
373
+ );
374
+ } else if (!sourceText) {
375
+ diagnostics.push(
376
+ `Requirement ${requirementId} has no readable authored Markdown body inside the workspace.`,
377
+ );
378
+ } else if (sourceConflict) {
379
+ diagnostics.push(
380
+ `Requirement ${requirementId} has semantic_text that differs from its normalized authored Markdown body; review the source drift before applying this migration.`,
381
+ );
382
+ }
383
+
384
+ if (!requirement || !sourceText || !sourceHash) {
385
+ return {
386
+ id: `legacy-migration-${requirementId}`,
387
+ requirementId,
388
+ repairBatchId: repairBatch.id,
389
+ dependsOn: [...repairBatch.dependsOn],
390
+ state: "blocked",
391
+ autoApplicable: false,
392
+ sourceBinding: {
393
+ status: sourceStatus,
394
+ sourceFile,
395
+ sourceKind: "authored_markdown_body",
396
+ sourceHash,
397
+ sourceByteLength: sourceText
398
+ ? Buffer.byteLength(sourceText, "utf8")
399
+ : 0,
400
+ persistedField: null,
401
+ existingTextRef,
402
+ existingSemanticText,
403
+ reason: diagnostics[0] ?? "Authored requirement source is unavailable.",
404
+ },
405
+ sourceText,
406
+ claimDelta: { preserved: [], added: [], stale: [] },
407
+ propositions: [],
408
+ requirementPropertyPatchPreview: null,
409
+ diagnostics,
410
+ };
411
+ }
412
+
413
+ const existingLogicClaims = stringList(requirement.logic_claims);
414
+ const analysis = analyzeSemanticAdvisorInput({
415
+ payload: {
416
+ type: "req",
417
+ id: requirementId,
418
+ properties: {
419
+ title: requirement.title,
420
+ status: requirement.status,
421
+ source: sourceFile ?? undefined,
422
+ semantic_text: sourceText,
423
+ logic_claims: existingLogicClaims,
424
+ },
425
+ relationships: [],
426
+ },
427
+ });
428
+ const suggestions = new Map(
429
+ analysis.receipt.suggestions.map((suggestion) => [
430
+ suggestion.claim_key,
431
+ suggestion,
432
+ ]),
433
+ );
434
+ const propositions = analysis.receipt.propositions.map((proposition) => {
435
+ const predicateCandidates =
436
+ proposition.status === "nonlogical"
437
+ ? []
438
+ : rankPredicateCandidates(
439
+ proposition.claim_text,
440
+ catalog,
441
+ predicateLimit,
442
+ predicateMinScore,
443
+ );
444
+ const disposition = propositionDisposition(
445
+ proposition,
446
+ suggestions.get(proposition.claim_key),
447
+ predicateCandidates,
448
+ );
449
+ return {
450
+ claimKey: proposition.claim_key,
451
+ claimText: proposition.claim_text,
452
+ role: proposition.role,
453
+ inventoryStatus: proposition.status,
454
+ span: { ...proposition.span },
455
+ ...disposition,
456
+ predicateCandidates,
457
+ reviewRequired: true,
458
+ } satisfies LegacyMigrationProposition;
459
+ });
460
+ const expectedClaimKeys = [
461
+ ...analysis.receipt.logic_coverage.expected_claim_keys,
462
+ ];
463
+ const propertyPatch = sourceConflict
464
+ ? null
465
+ : {
466
+ semantic_text: sourceText,
467
+ logic_claims: expectedClaimKeys,
468
+ semantic_clauses: analysis.receipt.clauses.map((clause) => clause.text),
469
+ semantic_inventory_version: analysis.receipt.inventory_contract.version,
470
+ semantic_source_field: analysis.receipt.inventory_contract.source_field,
471
+ semantic_source_hash: analysis.receipt.inventory_contract.source_hash,
472
+ semantic_inventory: analysis.receipt.propositions.map(
473
+ (proposition) => ({
474
+ claim_key: proposition.claim_key,
475
+ claim_text: proposition.claim_text,
476
+ role: proposition.role,
477
+ status: proposition.status,
478
+ span: { ...proposition.span },
479
+ ...(proposition.reason ? { reason: proposition.reason } : {}),
480
+ }),
481
+ ),
482
+ };
483
+
484
+ return {
485
+ id: `legacy-migration-${requirementId}`,
486
+ requirementId,
487
+ repairBatchId: repairBatch.id,
488
+ dependsOn: [...repairBatch.dependsOn],
489
+ state: sourceConflict ? "blocked" : "ready_for_review",
490
+ autoApplicable: false,
491
+ sourceBinding: {
492
+ status: sourceStatus,
493
+ sourceFile,
494
+ sourceKind: "authored_markdown_body",
495
+ sourceHash,
496
+ sourceByteLength: Buffer.byteLength(sourceText, "utf8"),
497
+ persistedField: sourceConflict ? null : "semantic_text",
498
+ existingTextRef,
499
+ existingSemanticText,
500
+ reason: sourceConflict
501
+ ? (diagnostics[0] ?? "Existing semantic_text has drifted from source.")
502
+ : existingTextRef
503
+ ? "Authored Markdown can be reviewed in semantic_text while preserving the independent text_ref evidence."
504
+ : "Authored Markdown can be reviewed in the dedicated semantic_text field.",
505
+ },
506
+ sourceText,
507
+ claimDelta: claimDelta(existingLogicClaims, expectedClaimKeys),
508
+ propositions,
509
+ requirementPropertyPatchPreview: propertyPatch,
510
+ diagnostics: [...diagnostics, ...analysis.warnings],
511
+ };
512
+ }
513
+
514
+ // implements REQ-kibi-legacy-migration-preview-v2
515
+ export async function buildLegacyMigrationPlan(
516
+ repairPlan: RepairPlan,
517
+ input: LegacyMigrationPlanInput,
518
+ codeSnapshot: string,
519
+ dependencies: LegacyMigrationPlanDependencies,
520
+ ): Promise<LegacyMigrationPlan> {
521
+ const offset = clampInteger(
522
+ input.migrationOffset,
523
+ 0,
524
+ 0,
525
+ Number.MAX_SAFE_INTEGER,
526
+ );
527
+ const limit = clampInteger(input.migrationLimit, 1, 1, 10);
528
+ const predicateLimit = clampInteger(input.migrationPredicateLimit, 5, 1, 20);
529
+ const predicateMinScore = clampScore(input.migrationPredicateMinScore);
530
+ const candidateBatches = migrationRepairBatches(repairPlan);
531
+ const selected = candidateBatches.slice(offset, offset + limit);
532
+ const requirementById = new Map(
533
+ dependencies.requirements.flatMap((requirement) =>
534
+ typeof requirement.id === "string"
535
+ ? [[requirement.id, requirement] as const]
536
+ : [],
537
+ ),
538
+ );
539
+ const catalog = predicateCatalog(dependencies.projectPredicateSchemas);
540
+ const predicateCatalogHash = hash(
541
+ catalog.map(({ schema, origin }) => ({ origin, schema })),
542
+ );
543
+ const batches: LegacyMigrationBatch[] = [];
544
+ for (const repairBatch of selected) {
545
+ batches.push(
546
+ await buildBatch(
547
+ repairBatch,
548
+ requirementById.get(repairBatch.requirementId),
549
+ dependencies.readSource,
550
+ catalog,
551
+ predicateLimit,
552
+ predicateMinScore,
553
+ ),
554
+ );
555
+ }
556
+ const readyBatchCount = batches.filter(
557
+ (batch) => batch.state === "ready_for_review",
558
+ ).length;
559
+ const blockedBatchCount = batches.length - readyBatchCount;
560
+ const propositionCount = batches.reduce(
561
+ (count, batch) => count + batch.propositions.length,
562
+ 0,
563
+ );
564
+ const assertivePropositionCount = batches.reduce(
565
+ (count, batch) =>
566
+ count +
567
+ batch.propositions.filter(
568
+ (proposition) => proposition.disposition !== "nonlogical",
569
+ ).length,
570
+ 0,
571
+ );
572
+ const unresolvedPropositionCount = batches.reduce(
573
+ (count, batch) =>
574
+ count +
575
+ batch.propositions.filter((proposition) =>
576
+ proposition.disposition.startsWith("unresolved_"),
577
+ ).length,
578
+ 0,
579
+ );
580
+ const predicateCandidateCount = batches.reduce(
581
+ (count, batch) =>
582
+ count +
583
+ batch.propositions.reduce(
584
+ (subtotal, proposition) =>
585
+ subtotal + proposition.predicateCandidates.length,
586
+ 0,
587
+ ),
588
+ 0,
589
+ );
590
+ const nextOffset =
591
+ offset + selected.length < candidateBatches.length
592
+ ? offset + selected.length
593
+ : null;
594
+ const diagnostics = [...(dependencies.diagnostics ?? [])];
595
+ if (!repairPlan.scope.complete) {
596
+ diagnostics.push(
597
+ "Legacy migration scope is partial because requirement coverage omitted actionable requirements; rerun complete coverage before treating this as a project inventory.",
598
+ );
599
+ }
600
+ if (candidateBatches.length > 0 && selected.length === 0) {
601
+ diagnostics.push(
602
+ `Migration offset ${offset} is beyond ${candidateBatches.length} ready semantic-inventory requirement batch(es).`,
603
+ );
604
+ }
605
+ const planCore = {
606
+ version: LEGACY_MIGRATION_PLAN_VERSION,
607
+ codeSnapshot,
608
+ repairPlanId: repairPlan.planId,
609
+ predicateCatalogHash,
610
+ scope: {
611
+ repairPlanComplete: repairPlan.scope.complete,
612
+ candidateRequirements: candidateBatches.length,
613
+ selectedRequirements: selected.length,
614
+ offset,
615
+ limit,
616
+ selectionComplete:
617
+ offset === 0 && selected.length === candidateBatches.length,
618
+ nextOffset,
619
+ },
620
+ batches,
621
+ };
622
+ const planId = `legacy-migration-plan-${hash(planCore).slice(0, 24)}`;
623
+ const status = !repairPlan.scope.complete
624
+ ? "partial"
625
+ : candidateBatches.length === 0
626
+ ? "no_candidates"
627
+ : readyBatchCount === 0
628
+ ? "blocked"
629
+ : "ready";
630
+
631
+ return {
632
+ ...planCore,
633
+ planId,
634
+ readOnly: true,
635
+ status,
636
+ applicationPolicy: {
637
+ previewOnly: true,
638
+ reviewEveryProposition: true,
639
+ preserveExistingTextRefs: true,
640
+ exactBindingsBeforeWrite: true,
641
+ validateBeforeEachWrite: true,
642
+ sequentialUpsertsOnly: true,
643
+ recheckContradictionsAfterEachBatch: true,
644
+ },
645
+ summary: {
646
+ batchCount: batches.length,
647
+ readyBatchCount,
648
+ blockedBatchCount,
649
+ propositionCount,
650
+ assertivePropositionCount,
651
+ unresolvedPropositionCount,
652
+ predicateCandidateCount,
653
+ },
654
+ diagnostics,
655
+ };
656
+ }
657
+
658
+ // implements REQ-kibi-legacy-migration-preview-v2
659
+ export async function buildLegacyMigrationPlanFromContext(
660
+ repairPlan: RepairPlan,
661
+ input: LegacyMigrationPlanInput,
662
+ codeSnapshot: string,
663
+ context: OperationContext,
664
+ ): Promise<LegacyMigrationPlan> {
665
+ if (!context.prolog) {
666
+ throw new Error("Legacy migration planning requires a Prolog context");
667
+ }
668
+ const requirements = await loadEntities(context.prolog, { type: "req" });
669
+ const schemaDiagnostics: string[] = [];
670
+ const projectPredicateSchemas = await loadExistingPredicateSchemas(
671
+ context.prolog,
672
+ true,
673
+ schemaDiagnostics,
674
+ );
675
+ return buildLegacyMigrationPlan(repairPlan, input, codeSnapshot, {
676
+ requirements,
677
+ projectPredicateSchemas,
678
+ diagnostics: schemaDiagnostics,
679
+ readSource: async (requirement) => {
680
+ const source =
681
+ typeof requirement.source === "string" ? requirement.source : "";
682
+ const body = await loadMarkdownBody(source, context.workspaceRoot);
683
+ if (body === null) return null;
684
+ const semanticText = requirementSemanticText(body);
685
+ return semanticText || null;
686
+ },
687
+ });
688
+ }