kibi-cli 2.21.2 → 2.22.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 (96) 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 +96 -6
  8. package/dist/commands/init-helpers.d.ts +14 -1
  9. package/dist/commands/init-helpers.d.ts.map +1 -1
  10. package/dist/commands/init-helpers.js +49 -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.map +1 -1
  18. package/dist/commands/migrate.js +48 -182
  19. package/dist/commands/sync/manifest.d.ts.map +1 -1
  20. package/dist/commands/sync/manifest.js +9 -0
  21. package/dist/extractors/symbols-coordinator.js +1 -0
  22. package/dist/intent-search.d.ts.map +1 -1
  23. package/dist/intent-search.js +28 -6
  24. package/dist/operations/migration/inventory-rederive.d.ts +40 -1
  25. package/dist/operations/migration/inventory-rederive.d.ts.map +1 -1
  26. package/dist/operations/migration/inventory-rederive.js +99 -8
  27. package/dist/operations/migration/requirement-status.d.ts +25 -0
  28. package/dist/operations/migration/requirement-status.d.ts.map +1 -0
  29. package/dist/operations/migration/requirement-status.js +105 -0
  30. package/dist/operations/migration/schema6.d.ts +25 -0
  31. package/dist/operations/migration/schema6.d.ts.map +1 -1
  32. package/dist/operations/migration/schema6.js +265 -7
  33. package/dist/operations/migration/symbol-legacy-links.d.ts +51 -0
  34. package/dist/operations/migration/symbol-legacy-links.d.ts.map +1 -0
  35. package/dist/operations/migration/symbol-legacy-links.js +319 -0
  36. package/dist/operations/mutation/context-warning.d.ts +12 -2
  37. package/dist/operations/mutation/context-warning.d.ts.map +1 -1
  38. package/dist/operations/mutation/context-warning.js +6 -6
  39. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  40. package/dist/operations/mutation/source-authoring.js +130 -35
  41. package/dist/operations/mutation/types.d.ts +2 -1
  42. package/dist/operations/mutation/types.d.ts.map +1 -1
  43. package/dist/operations/mutation/upsert.d.ts +17 -2
  44. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  45. package/dist/operations/mutation/upsert.js +28 -11
  46. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  47. package/dist/operations/mutation/validate-upsert.js +40 -8
  48. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  49. package/dist/operations/planning/apply-plan.js +18 -3
  50. package/dist/public/impact/diagnostics.d.ts.map +1 -1
  51. package/dist/public/impact/diagnostics.js +24 -14
  52. package/dist/public/operations/catalog.d.ts +3 -3
  53. package/dist/public/operations/check-executor.d.ts.map +1 -1
  54. package/dist/public/operations/check-executor.js +3 -0
  55. package/dist/public/operations/contracts.d.ts.map +1 -1
  56. package/dist/public/operations/contracts.js +6 -1
  57. package/dist/public/operations/discovery-executors.d.ts +2 -0
  58. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  59. package/dist/public/operations/discovery-executors.js +9 -2
  60. package/dist/public/operations/migration-plan.d.ts +2 -0
  61. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  62. package/dist/public/operations/migration-plan.js +101 -16
  63. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  64. package/dist/public/operations/result-envelope.js +27 -1
  65. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  66. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  67. package/dist/public/operations/specs/check-rules.generated.js +2 -1
  68. package/dist/public/operations/specs/check.d.ts +1 -1
  69. package/dist/public/operations/specs/planning.d.ts +2 -2
  70. package/dist/public/operations/specs/planning.js +2 -2
  71. package/dist/public/operations/symbol-granularity-check.d.ts +18 -0
  72. package/dist/public/operations/symbol-granularity-check.d.ts.map +1 -0
  73. package/dist/public/operations/symbol-granularity-check.js +175 -0
  74. package/dist/public/symbol-granularity.d.ts +17 -0
  75. package/dist/public/symbol-granularity.d.ts.map +1 -1
  76. package/dist/public/symbol-granularity.js +21 -0
  77. package/dist/traceability/evidence-model.d.ts +7 -3
  78. package/dist/traceability/evidence-model.d.ts.map +1 -1
  79. package/dist/traceability/staged-symbols-manifest.js +6 -5
  80. package/dist/traceability/validate.d.ts +14 -1
  81. package/dist/traceability/validate.d.ts.map +1 -1
  82. package/dist/traceability/validate.js +41 -0
  83. package/dist/utils/rule-registry.generated.d.ts +5 -0
  84. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  85. package/dist/utils/rule-registry.generated.js +6 -0
  86. package/package.json +3 -3
  87. package/src/public/impact/diagnostics.ts +29 -17
  88. package/src/public/operations/check-executor.ts +7 -0
  89. package/src/public/operations/contracts.ts +6 -1
  90. package/src/public/operations/discovery-executors.ts +12 -2
  91. package/src/public/operations/migration-plan.ts +100 -19
  92. package/src/public/operations/result-envelope.ts +34 -1
  93. package/src/public/operations/specs/check-rules.generated.ts +2 -1
  94. package/src/public/operations/specs/planning.ts +2 -2
  95. package/src/public/operations/symbol-granularity-check.ts +233 -0
  96. package/src/public/symbol-granularity.ts +37 -0
@@ -15,13 +15,16 @@
15
15
  * You should have received a copy of the GNU Affero General Public License
16
16
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
  */
18
+ import { execFileSync } from "node:child_process";
18
19
  import { createHash } from "node:crypto";
19
20
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
20
21
  import * as path from "node:path";
21
22
  import fg from "fast-glob";
23
+ import { load as parseYaml } from "js-yaml";
24
+ import { writePendingSourceReceipt } from "../operations/mutation/source-authoring.js";
22
25
  import { readLegacyKbConfig, resolveLegacyEntityPaths, } from "../utils/config.js";
23
26
  import { defaultKbManifest, readKbManifest, writeKbManifest, } from "../utils/kb-manifest.js";
24
- import { CANONICAL_ENTITY_PATHS, ENTITY_LANES, KB_PATHS, } from "../utils/kb-paths.js";
27
+ import { CANONICAL_ENTITY_PATHS, ENTITY_LANES, KB_PATHS, LEGACY_DEFAULT_ENTITY_PATHS, } from "../utils/kb-paths.js";
25
28
  import { LATEST_KB_SCHEMA_VERSION } from "../utils/schema-version.js";
26
29
  import { updateGitIgnore } from "./init-helpers.js";
27
30
  const LANE_BY_CONFIG_KEY = {
@@ -57,6 +60,8 @@ export function planLegacyStorageMigration(cwd) {
57
60
  legacyConfigError: legacyConfigResult.error,
58
61
  schemaVersion: null,
59
62
  semanticAdvisorBackfill: null,
63
+ removals: [],
64
+ conflicts: [],
60
65
  blockers: [
61
66
  `Legacy .kb/config.json is malformed (${legacyConfigResult.error}). Repair the JSON or provide an explicit recovery/migration override; Kibi will not infer default legacy paths.`,
62
67
  ],
@@ -96,7 +101,11 @@ export function planLegacyStorageMigration(cwd) {
96
101
  }
97
102
  }
98
103
  // Symbols manifest + coordinates.
99
- const legacySymbols = legacyPaths.symbols;
104
+ const conflicts = [];
105
+ const symbolsSource = resolveLegacySymbolsSource(cwd, config, legacyPaths.symbols);
106
+ if (symbolsSource.conflict !== undefined)
107
+ conflicts.push(symbolsSource.conflict);
108
+ const legacySymbols = symbolsSource.path;
100
109
  if (typeof legacySymbols === "string" && legacySymbols.trim().length > 0) {
101
110
  const legacyManifest = path.resolve(cwd, legacySymbols);
102
111
  if (existsSync(legacyManifest) &&
@@ -122,17 +131,33 @@ export function planLegacyStorageMigration(cwd) {
122
131
  });
123
132
  }
124
133
  }
125
- // Destination-conflict detection: a move whose destination already exists
126
- // with different content requires an operator decision.
134
+ // Destination classification: an identical legacy copy is removed, an
135
+ // empty seeded symbols manifest is replaced, and anything else is an
136
+ // operator decision with the resolution Kibi can tell.
137
+ const removals = [];
138
+ const pending = [];
127
139
  for (const move of moves) {
128
140
  const destinationAbsolute = path.resolve(cwd, move.to);
129
- if (!existsSync(destinationAbsolute))
141
+ if (!existsSync(destinationAbsolute)) {
142
+ pending.push(move);
130
143
  continue;
144
+ }
131
145
  // 'kibi init' seeds an empty symbols manifest; the legacy one replaces it.
132
- if (move.lane === "symbols" && isEmptySymbolsStub(destinationAbsolute))
146
+ if (move.lane === "symbols" && isEmptySymbolsStub(destinationAbsolute)) {
147
+ pending.push(move);
148
+ continue;
149
+ }
150
+ const conflict = classifyExistingDestination(cwd, move);
151
+ if (conflict === null) {
152
+ removals.push({ path: move.from, duplicateOf: move.to });
133
153
  continue;
134
- blockers.push(`Destination ${move.to} already exists; resolve the conflict manually before migrating ${move.from}.`);
154
+ }
155
+ pending.push(move);
156
+ conflicts.push(conflict);
135
157
  }
158
+ moves.splice(0, moves.length, ...pending);
159
+ for (const conflict of conflicts)
160
+ blockers.push(conflict.resolution);
136
161
  // Duplicate destinations (two legacy sources collapsing onto one target).
137
162
  const destinations = new Map();
138
163
  for (const move of moves) {
@@ -148,9 +173,126 @@ export function planLegacyStorageMigration(cwd) {
148
173
  legacyConfig: legacyConfigResult.kind === "none" ? "absent" : "present",
149
174
  schemaVersion: typeof config?.schemaVersion === "number" ? config.schemaVersion : null,
150
175
  semanticAdvisorBackfill: config?.semanticAdvisorBackfill ?? null,
176
+ removals,
177
+ conflicts,
151
178
  blockers,
152
179
  };
153
180
  }
181
+ function hashFile(filePath) {
182
+ try {
183
+ return createHash("sha256").update(readFileSync(filePath)).digest("hex");
184
+ }
185
+ catch {
186
+ return null;
187
+ }
188
+ }
189
+ /** Symbol ids a manifest declares, or null when it does not parse. */
190
+ function manifestSymbolIds(filePath) {
191
+ try {
192
+ const parsed = parseYaml(readFileSync(filePath, "utf8"));
193
+ if (!Array.isArray(parsed?.symbols))
194
+ return null;
195
+ return new Set(parsed.symbols.flatMap((symbol) => symbol !== null &&
196
+ typeof symbol === "object" &&
197
+ typeof symbol.id === "string"
198
+ ? [symbol.id]
199
+ : []));
200
+ }
201
+ catch {
202
+ return null;
203
+ }
204
+ }
205
+ /**
206
+ * How a move whose destination already exists is resolved: null when the
207
+ * legacy copy is identical (it is removed), otherwise the conflict and the
208
+ * resolution Kibi can tell.
209
+ */
210
+ // implements REQ-cli-schema-migration
211
+ function classifyExistingDestination(cwd, move) {
212
+ const from = path.resolve(cwd, move.from);
213
+ const to = path.resolve(cwd, move.to);
214
+ const legacyHash = hashFile(from);
215
+ if (legacyHash !== null && legacyHash === hashFile(to))
216
+ return null;
217
+ if (move.to === CANONICAL_ENTITY_PATHS.symbols) {
218
+ const legacyIds = manifestSymbolIds(from);
219
+ const currentIds = manifestSymbolIds(to);
220
+ if (legacyIds !== null &&
221
+ currentIds !== null &&
222
+ [...legacyIds].every((id) => currentIds.has(id))) {
223
+ return {
224
+ from: move.from,
225
+ to: move.to,
226
+ kind: "stale_subset",
227
+ resolution: `The legacy copy ${move.from} is a stale subset of ${move.to}: every symbol it declares (${legacyIds.size}) is already there. Remove it (git rm ${move.from}); Kibi removes only a copy identical to its destination.`,
228
+ };
229
+ }
230
+ }
231
+ if (move.to === KB_PATHS.symbolCoordinates) {
232
+ return {
233
+ from: move.from,
234
+ to: move.to,
235
+ kind: "generated",
236
+ resolution: `${move.from} is a generated coordinates file and ${move.to} already exists. Keep ${move.to} and remove the legacy copy (git rm ${move.from}); kibi sync --refresh-symbol-coordinates regenerates coordinates.`,
237
+ };
238
+ }
239
+ return {
240
+ from: move.from,
241
+ to: move.to,
242
+ kind: "conflict",
243
+ resolution: `Destination ${move.to} already exists with different content than ${move.from}. Compare them and keep the current one: replace ${move.to} when the legacy copy is newer, or remove (git rm) ${move.from} when it is a stale duplicate.`,
244
+ };
245
+ }
246
+ /** The parent folders of the lane roots a legacy config names explicitly. */
247
+ function configuredLaneParents(config) {
248
+ const parents = new Set();
249
+ for (const key of Object.keys(LANE_BY_CONFIG_KEY)) {
250
+ const value = config?.paths?.[key];
251
+ if (typeof value !== "string" || value.trim().length === 0)
252
+ continue;
253
+ const { root } = normalizeLegacySource(value.trim());
254
+ const parent = path.posix.dirname(root.replace(/^\.\//, ""));
255
+ if (parent !== "." && parent !== "")
256
+ parents.add(parent);
257
+ }
258
+ return [...parents].sort();
259
+ }
260
+ /**
261
+ * The legacy symbols manifest to move. A configured path wins. Without one,
262
+ * the historical `documentation/symbols.yaml` default applies only when the
263
+ * config does not place its lanes under another folder: there the manifest
264
+ * sits beside the lanes, and a `documentation/` copy may be a stale leftover.
265
+ * When Kibi cannot tell which file is meant, it moves none and reports the
266
+ * ambiguity instead.
267
+ */
268
+ // implements REQ-cli-schema-migration
269
+ function resolveLegacySymbolsSource(cwd, config, resolved) {
270
+ const configured = config?.paths?.symbols ?? config?.symbolsManifest;
271
+ if (typeof configured === "string" && configured.trim().length > 0)
272
+ return { path: resolved };
273
+ const parents = configuredLaneParents(config).filter((parent) => parent !== path.posix.dirname(LEGACY_DEFAULT_ENTITY_PATHS.symbols));
274
+ if (parents.length === 0)
275
+ return { path: resolved };
276
+ const candidates = parents
277
+ .map((parent) => `${parent}/symbols.yaml`)
278
+ .filter((candidate) => existsSync(path.resolve(cwd, candidate)));
279
+ const fallback = LEGACY_DEFAULT_ENTITY_PATHS.symbols;
280
+ const fallbackExists = existsSync(path.resolve(cwd, fallback));
281
+ if (candidates.length === 1 && !fallbackExists)
282
+ return { path: candidates[0] ?? null };
283
+ if (candidates.length === 0 && !fallbackExists)
284
+ return { path: null };
285
+ const all = [...candidates, ...(fallbackExists ? [fallback] : [])];
286
+ return {
287
+ path: null,
288
+ conflict: {
289
+ from: all.join(", "),
290
+ to: CANONICAL_ENTITY_PATHS.symbols,
291
+ kind: "ambiguous",
292
+ resolution: `The legacy .kb/config.json places its lanes under ${parents.join(", ")} but names no symbols manifest, and ${all.length === 1 ? `${all[0]} exists outside that folder` : `${all.join(" and ")} all exist`}; Kibi will not guess which one is current. Set paths.symbols in .kb/config.json to the current manifest, or remove (git rm) the stale one, then run kibi migrate --yes.`,
293
+ },
294
+ };
295
+ }
154
296
  /**
155
297
  * Apply the legacy storage migration. Moves every planned file, writes the
156
298
  * canonical lifecycle manifest, retires the legacy config, and prunes empty
@@ -160,6 +302,18 @@ export function applyLegacyStorageMigration(cwd, plan) {
160
302
  if (plan.blockers.length > 0) {
161
303
  throw new Error(`Legacy storage migration is blocked: ${plan.blockers.join(" ")}`);
162
304
  }
305
+ // A legacy copy identical to its destination carries nothing the
306
+ // destination lacks; it is removed rather than left to block a later run.
307
+ const removedFiles = [];
308
+ for (const removal of plan.removals) {
309
+ const absolute = path.resolve(cwd, removal.path);
310
+ if (!existsSync(absolute))
311
+ continue;
312
+ if (hashFile(absolute) !== hashFile(path.resolve(cwd, removal.duplicateOf)))
313
+ throw new Error(`Legacy storage migration is blocked: ${removal.path} changed since planning and no longer matches ${removal.duplicateOf}.`);
314
+ rmSync(absolute);
315
+ removedFiles.push(removal.path);
316
+ }
163
317
  const movedFiles = [];
164
318
  for (const move of plan.moves) {
165
319
  const sourceAbsolute = path.resolve(cwd, move.from);
@@ -196,9 +350,12 @@ export function applyLegacyStorageMigration(cwd, plan) {
196
350
  }
197
351
  pruneEmptyLegacyDirs(cwd, plan);
198
352
  rewritePendingSourceReceiptPaths(cwd, plan.moves);
353
+ const untrackedMovedFiles = writeMovedSourceReceipts(cwd, plan.moves);
199
354
  updateGitIgnore(cwd);
200
355
  return {
201
356
  movedFiles,
357
+ removedFiles,
358
+ untrackedMovedFiles,
202
359
  retiredLegacyConfig,
203
360
  manifestPath: path.relative(cwd, manifestPath).replaceAll(path.sep, "/"),
204
361
  schemaVersion: manifest.schemaVersion,
@@ -207,8 +364,11 @@ export function applyLegacyStorageMigration(cwd, plan) {
207
364
  }
208
365
  function pruneEmptyLegacyDirs(cwd, plan) {
209
366
  const roots = new Set();
210
- for (const move of plan.moves) {
211
- const absolute = path.resolve(cwd, move.from);
367
+ for (const from of [
368
+ ...plan.moves.map((move) => move.from),
369
+ ...plan.removals.map((removal) => removal.path),
370
+ ]) {
371
+ const absolute = path.resolve(cwd, from);
212
372
  let dir = path.dirname(absolute);
213
373
  while (dir.startsWith(path.resolve(cwd)) &&
214
374
  dir !== path.resolve(cwd) &&
@@ -270,6 +430,54 @@ export function rewritePendingSourceReceiptPaths(cwd, moves) {
270
430
  }
271
431
  return rewritten;
272
432
  }
433
+ /** Workspace-relative paths Git tracks, or null outside a repository. */
434
+ function gitTrackedFiles(cwd) {
435
+ try {
436
+ return new Set(execFileSync("git", ["-C", cwd, "ls-files", "--cached", "-z"], {
437
+ encoding: "utf8",
438
+ stdio: ["ignore", "pipe", "ignore"],
439
+ maxBuffer: 64 * 1024 * 1024,
440
+ timeout: 30_000,
441
+ })
442
+ .split("\0")
443
+ .filter((file) => file.length > 0)
444
+ .map((file) => file.replaceAll("\\", "/")));
445
+ }
446
+ catch {
447
+ return null;
448
+ }
449
+ }
450
+ /**
451
+ * Bind every moved file Git does not track to its bytes with a
452
+ * pending-source receipt, the record kb_upsert writes for the files it
453
+ * creates. `kibi sync` compiles tracked sources plus receipted ones, so the
454
+ * moved knowledge compiles at once; Kibi never stages anything itself, and
455
+ * each receipt retires when Git starts tracking its file. Later migration
456
+ * rewrites rebind the receipt they find. Covers files a previous,
457
+ * interrupted run moved too. Returns the untracked destinations.
458
+ */
459
+ // implements REQ-cli-schema-migration, REQ-core-atomic-upsert-persistence
460
+ export function writeMovedSourceReceipts(cwd, moves) {
461
+ const tracked = gitTrackedFiles(cwd);
462
+ if (tracked === null)
463
+ return [];
464
+ const untracked = [];
465
+ for (const move of moves) {
466
+ if (tracked.has(move.to))
467
+ continue;
468
+ const absolute = path.resolve(cwd, move.to);
469
+ let bytes;
470
+ try {
471
+ bytes = readFileSync(absolute);
472
+ }
473
+ catch {
474
+ continue;
475
+ }
476
+ writePendingSourceReceipt(cwd, move.to, createHash("sha256").update(bytes).digest("hex"));
477
+ untracked.push(move.to);
478
+ }
479
+ return untracked;
480
+ }
273
481
  // implements REQ-cli-schema-migration
274
482
  function isEmptySymbolsStub(filePath) {
275
483
  try {
@@ -293,6 +501,8 @@ function sourceSchemaVersion(legacyVersion, existing) {
293
501
  export function needsLegacyStorageMigration(cwd) {
294
502
  const plan = planLegacyStorageMigration(cwd);
295
503
  return (plan.moves.length > 0 ||
504
+ plan.removals.length > 0 ||
505
+ plan.blockers.length > 0 ||
296
506
  plan.legacyConfig === "present" ||
297
507
  plan.legacyConfig === "malformed");
298
508
  }
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAkEA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,uFAAuF;IACvF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA8KD,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,OAAO,EACzB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,MAAM,CAUR;AAsXD,wBAAgB,+BAA+B,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAKtE;AAGD,wBAAsB,cAAc,CAClC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgZ/B"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAmEA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,uFAAuF;IACvF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA+JD,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,OAAO,EACzB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,MAAM,CAUR;AAsOD,wBAAgB,+BAA+B,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAKtE;AAGD,wBAAsB,cAAc,CAClC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2Y/B"}
@@ -15,15 +15,13 @@
15
15
  * You should have received a copy of the GNU Affero General Public License
16
16
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
  */
18
- import { execFileSync } from "node:child_process";
19
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
18
+ import { existsSync, mkdirSync, renameSync, writeFileSync, } from "node:fs";
20
19
  import * as path from "node:path";
21
- import { load as parseYAML } from "js-yaml";
22
20
  import { applySafeInventoryRederivations, planInventoryRederivation, } from "../operations/migration/inventory-rederive.js";
23
21
  import { applyOriginBackfill, planOriginBackfill, } from "../operations/migration/origin-backfill.js";
24
22
  import { applySourcePathRepairs, findSourceRepairs, } from "../operations/migration/source-paths.js";
25
23
  import { applySupersededClosures, planSupersededClosures, } from "../operations/migration/superseded-closure.js";
26
- import { extractSymbolsFromStagedFile } from "../traceability/symbol-extract.js";
24
+ import { applySymbolLegacyLinks, planSymbolLegacyLinks, } from "../operations/migration/symbol-legacy-links.js";
27
25
  import { resolveBranchAttachment } from "../utils/branch-resolver.js";
28
26
  import { defaultKbManifest, readKbManifest, readKbManifestStatus, writeKbManifest, } from "../utils/kb-manifest.js";
29
27
  import { CANONICAL_ENTITY_PATHS } from "../utils/kb-paths.js";
@@ -162,137 +160,25 @@ function normalizeSemanticAdvisorBackfill(value) {
162
160
  ? value
163
161
  : null;
164
162
  }
165
- const TRACEABILITY_RELATIONSHIP_TYPES = new Set([
166
- "implements",
167
- "covered_by",
168
- "executable_for",
169
- ]);
170
- function hasTraceabilityRelationship(symbol) {
171
- if (Array.isArray(symbol.links) && symbol.links.length > 0) {
172
- return true;
173
- }
174
- if (!Array.isArray(symbol.relationships)) {
175
- return false;
176
- }
177
- return symbol.relationships.some((relationship) => {
178
- if (relationship === null ||
179
- typeof relationship !== "object" ||
180
- Array.isArray(relationship)) {
181
- return false;
182
- }
183
- const type = relationship.type;
184
- return (typeof type === "string" && TRACEABILITY_RELATIONSHIP_TYPES.has(type));
185
- });
186
- }
187
- function getGranularNames(cwd, sourceFile) {
188
- const absolutePath = path.isAbsolute(sourceFile)
189
- ? sourceFile
190
- : path.join(cwd, sourceFile);
191
- if (!existsSync(absolutePath)) {
192
- return new Set();
193
- }
194
- const symbols = extractSymbolsFromStagedFile({
195
- path: sourceFile,
196
- status: "M",
197
- hunkRanges: [{ start: 1, end: Number.MAX_SAFE_INTEGER }],
198
- content: readFileSync(absolutePath, "utf8"),
199
- });
200
- return new Set(symbols.map((symbol) => symbol.name));
201
- }
202
- function addLegacyReasonsToManifestText(content, symbolIds) {
203
- if (symbolIds.size === 0)
204
- return content;
205
- const lines = content.split("\n");
206
- const output = [];
207
- let activeId = null;
208
- let activeHasReason = false;
209
- let activePropertyIndent = "";
210
- const flushLegacyReason = () => {
211
- if (activeId !== null &&
212
- symbolIds.has(activeId) &&
213
- !activeHasReason &&
214
- activePropertyIndent.length > 0) {
215
- output.push(`${activePropertyIndent}granularity_reason: legacy-link`);
216
- activeHasReason = true;
217
- }
218
- };
219
- for (const line of lines) {
220
- const idMatch = line.match(/^(\s*)-\s+id:\s+(.+)\s*$/);
221
- if (idMatch) {
222
- flushLegacyReason();
223
- activeId = idMatch[2]?.trim() ?? null;
224
- activeHasReason = false;
225
- activePropertyIndent = `${idMatch[1] ?? ""} `;
226
- }
227
- if (activeId !== null && /^\s+granularity_reason:\s+/.test(line)) {
228
- activeHasReason = true;
229
- }
230
- if (activeId !== null &&
231
- symbolIds.has(activeId) &&
232
- !activeHasReason &&
233
- /^\s+status:\s+/.test(line)) {
234
- const indent = line.match(/^\s*/)?.[0] ?? "";
235
- output.push(`${indent}granularity_reason: legacy-link`);
236
- activeHasReason = true;
237
- }
238
- output.push(line);
239
- }
240
- flushLegacyReason();
241
- return output.join("\n");
242
- }
243
- function migrateSymbolGranularity(options) {
244
- const manifestPath = options.manifestPath ??
245
- path.join(options.cwd, CANONICAL_ENTITY_PATHS.symbols);
246
- if (!existsSync(manifestPath)) {
247
- return { count: 0, manifestPath };
248
- }
249
- const manifestContent = readFileSync(manifestPath, "utf8");
250
- const parsed = parseYAML(manifestContent);
251
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
252
- return { count: 0, manifestPath };
253
- }
254
- const manifest = parsed;
255
- if (!Array.isArray(manifest.symbols)) {
256
- return { count: 0, manifestPath };
257
- }
258
- let count = 0;
259
- const migratedSymbolIds = new Set();
260
- const granularNamesByFile = new Map();
261
- for (const symbol of manifest.symbols) {
262
- if (symbol === null ||
263
- typeof symbol !== "object" ||
264
- Array.isArray(symbol)) {
265
- continue;
266
- }
267
- const record = symbol;
268
- if (typeof record.sourceFile !== "string")
269
- continue;
270
- if (typeof record.title !== "string")
271
- continue;
272
- if (typeof record.id !== "string")
273
- continue;
274
- if (record.granularity_reason !== undefined)
275
- continue;
276
- if (!hasTraceabilityRelationship(record))
277
- continue;
278
- let granularNames = granularNamesByFile.get(record.sourceFile);
279
- if (granularNames === undefined) {
280
- granularNames = getGranularNames(options.cwd, record.sourceFile);
281
- granularNamesByFile.set(record.sourceFile, granularNames);
282
- }
283
- if (granularNames.size === 0 || granularNames.has(record.title)) {
284
- continue;
285
- }
286
- count += 1;
287
- if (!options.dryRun) {
288
- record.granularity_reason = "legacy-link";
289
- migratedSymbolIds.add(record.id);
290
- }
291
- }
292
- if (count > 0 && !options.dryRun) {
293
- writeTextAtomically(manifestPath, addLegacyReasonsToManifestText(manifestContent, migratedSymbolIds));
294
- }
295
- return { count, manifestPath };
163
+ /**
164
+ * Mark the coarse symbol links the staged granularity gate would block as
165
+ * legacy-link. Runs at any schema version: the rule can call a link coarse
166
+ * that an older run (or a KB that skipped the schema 2 step) left unmarked.
167
+ */
168
+ // implements REQ-cli-schema-migration, REQ-symbol-behavioral-anchors
169
+ function markLegacySymbolLinks(cwd, dryRun, manifestPath) {
170
+ if (dryRun) {
171
+ const count = planSymbolLegacyLinks(cwd, manifestPath).targets.length;
172
+ if (count > 0)
173
+ console.log(`dry run: would mark ${count} coarse symbol link(s) as legacy-link (granularity_reason), so the staged check accepts them.`);
174
+ return count;
175
+ }
176
+ const { marked, unmarked } = applySymbolLegacyLinks(cwd);
177
+ if (marked.length > 0)
178
+ console.log(`Marked ${marked.length} existing coarse symbol link(s) as legacy-link.`);
179
+ if (unmarked.length > 0)
180
+ printWarning(`Could not mark ${unmarked.join(", ")} as legacy-link in place; add granularity_reason: legacy-link to them in ${CANONICAL_ENTITY_PATHS.symbols} by hand.`);
181
+ return marked.length;
296
182
  }
297
183
  function describeRederivation(plan) {
298
184
  const { keptModeled, roleChanges, newlyUnresolved, downgradedModeled } = plan.summary;
@@ -300,7 +186,7 @@ function describeRederivation(plan) {
300
186
  }
301
187
  function printManualRederivations(plans) {
302
188
  for (const plan of plans) {
303
- printWarning(`${plan.requirementId} (${plan.path}) no longer matches the current semantic advisor and needs a manual fix: ${plan.reasons.join("; ")}. Run 'kibi migrate --format json' for the exact repair steps.`);
189
+ printWarning(`${plan.requirementId} (${plan.path}) no longer matches the current semantic advisor and needs a manual fix: ${plan.reasons.join("; ")}. Run 'kibi migrate --format json' for the exact repair steps.${plan.rederivableByDroppingGrounding ? ` Or approve semantic-inventory-rederive-drop-grounding-${plan.requirementId} with --apply-safe --approved-action to drop the ${plan.orphanedGrounding.length} orphaned grounding link(s).` : ""}`);
304
190
  }
305
191
  }
306
192
  /**
@@ -456,7 +342,9 @@ export async function migrateCommand(options = {}) {
456
342
  const configStatus = getSchemaVersionStatus(normalizedVersion === null
457
343
  ? undefined
458
344
  : { schemaVersion: normalizedVersion });
459
- const needsStorageMigration = storagePlan.moves.length > 0 || storagePlan.legacyConfig !== "absent";
345
+ const needsStorageMigration = storagePlan.moves.length > 0 ||
346
+ storagePlan.removals.length > 0 ||
347
+ storagePlan.legacyConfig !== "absent";
460
348
  const needsSchemaUpgrade = configStatus.needsMigration || normalizedVersion === null;
461
349
  const migrationWarning = manifestStatus.state === "invalid" || manifestStatus.state === "future"
462
350
  ? manifestStatus.warning
@@ -489,7 +377,9 @@ export async function migrateCommand(options = {}) {
489
377
  // and lifecycle findings block kibi check at any version.
490
378
  const drift = rederiveDriftedInventories(cwd, options.dryRun === true);
491
379
  const lifecycle = repairLifecycleFindings(cwd, options.dryRun === true);
380
+ const legacyLinks = markLegacySymbolLinks(cwd, options.dryRun === true);
492
381
  const rewroteSources = polarityCount > 0 ||
382
+ legacyLinks > 0 ||
493
383
  drift.rederived.length > 0 ||
494
384
  lifecycle.closed > 0 ||
495
385
  lifecycle.rewritten > 0 ||
@@ -513,7 +403,6 @@ export async function migrateCommand(options = {}) {
513
403
  }
514
404
  const fromVersionLabel = formatSchemaVersion(currentVersion, normalizedVersion);
515
405
  const migrationSteps = migrationStepsFor(configStatus.currentVersion);
516
- const symbolGranularityStep = migrationSteps.some((step) => step.id === "symbol-granularity-v2");
517
406
  const semanticBackfillStep = migrationSteps.some((step) => step.id === "semantic-backfill-v4");
518
407
  const originBackfillStep = migrationSteps.some((step) => step.id === "entity-origin-v6");
519
408
  const { planPolarityValueBackfill, applyPolarityValueBackfill } = await import("../operations/migration/polarity-values.js");
@@ -548,21 +437,17 @@ export async function migrateCommand(options = {}) {
548
437
  console.log(` ${move.from} -> ${move.to}`);
549
438
  }
550
439
  }
440
+ if (storagePlan.removals.length > 0) {
441
+ console.log(`dry run: would remove ${storagePlan.removals.length} legacy cop(ies) identical to their .kb/ file:`);
442
+ for (const removal of storagePlan.removals) {
443
+ console.log(` ${removal.path} (same as ${removal.duplicateOf})`);
444
+ }
445
+ }
551
446
  if (storagePlan.legacyConfig !== "absent") {
552
447
  console.log("dry run: would retire legacy .kb/config.json after moving knowledge into .kb/.");
553
448
  }
554
449
  console.log(`dry run: would set schemaVersion from ${fromVersionLabel} to ${LATEST_KB_SCHEMA_VERSION} in .kb/manifest.json.`);
555
- if (symbolGranularityStep) {
556
- const symbolsSource = storagePlan.moves.find((move) => move.to === CANONICAL_ENTITY_PATHS.symbols)?.from ?? CANONICAL_ENTITY_PATHS.symbols;
557
- const preview = migrateSymbolGranularity({
558
- cwd,
559
- dryRun: true,
560
- manifestPath: path.join(cwd, symbolsSource),
561
- });
562
- if (preview.count > 0) {
563
- console.log(`dry run: would mark ${preview.count} legacy coarse symbol link(s) as legacy-link.`);
564
- }
565
- }
450
+ markLegacySymbolLinks(cwd, true, storagePlan.moves.find((move) => move.to === CANONICAL_ENTITY_PATHS.symbols)?.from);
566
451
  if (originBackfillStep) {
567
452
  const backfill = planOriginBackfill(cwd);
568
453
  if (backfill.targets.length > 0) {
@@ -581,20 +466,18 @@ export async function migrateCommand(options = {}) {
581
466
  const storageResult = applyLegacyStorageMigration(cwd, storagePlan);
582
467
  if (storageResult.movedFiles.length > 0) {
583
468
  console.log(`Moved ${storageResult.movedFiles.length} knowledge file(s) into the canonical .kb/ layout.`);
584
- const untracked = untrackedFiles(cwd, storageResult.movedFiles);
469
+ const untracked = storageResult.untrackedMovedFiles.length;
585
470
  if (untracked > 0)
586
- printWarning(`${untracked} moved file(s) are not tracked by Git yet, and kibi sync compiles only tracked sources: until you stage them (git add .kb, plus the removals under the legacy folders) the KB compiles without them and plans report them as missing.`);
471
+ printWarning(`${untracked} moved file(s) are not tracked by Git yet. Kibi recorded a pending-source receipt for each, so kibi sync compiles them now; stage them before you commit (git add .kb, plus the removals under the legacy folders). Each receipt retires once Git tracks its file.`);
472
+ }
473
+ if (storageResult.removedFiles.length > 0) {
474
+ console.log(`Removed ${storageResult.removedFiles.length} legacy cop(ies) identical to their .kb/ file: ${storageResult.removedFiles.join(", ")}.`);
587
475
  }
588
476
  if (storageResult.retiredLegacyConfig) {
589
477
  console.log("Retired legacy .kb/config.json.");
590
478
  }
591
479
  }
592
- const symbolGranularityMigration = symbolGranularityStep
593
- ? migrateSymbolGranularity({
594
- cwd,
595
- dryRun: options.yes !== true,
596
- })
597
- : { count: 0 };
480
+ const legacyLinksMarked = markLegacySymbolLinks(cwd, false);
598
481
  const semanticAdvisorBackfill = normalizeSemanticAdvisorBackfill(readKbManifest(cwd)?.semanticAdvisorBackfill) ??
599
482
  normalizeSemanticAdvisorBackfill(storagePlan.semanticAdvisorBackfill) ??
600
483
  (semanticBackfillStep || needsStorageMigration
@@ -657,7 +540,7 @@ export async function migrateCommand(options = {}) {
657
540
  fromVersion: configStatus.currentVersion,
658
541
  migratedAt,
659
542
  semanticAdvisorBackfill,
660
- symbolGranularityLegacyLinks: symbolGranularityMigration.count,
543
+ symbolGranularityLegacyLinks: legacyLinksMarked,
661
544
  entityOriginBackfill: originBackfill.written.length + originBackfill.previouslyStamped,
662
545
  supersededRequirementsClosed: lifecycle.closed,
663
546
  sourcePathsRewritten: lifecycle.rewritten,
@@ -666,9 +549,6 @@ export async function migrateCommand(options = {}) {
666
549
  steps: migrationSteps.map((step) => step.id),
667
550
  }));
668
551
  console.log(`Migrated the KB to schemaVersion ${LATEST_KB_SCHEMA_VERSION} on the canonical .kb/ layout (was ${fromVersionLabel}).`);
669
- if (symbolGranularityMigration.count > 0) {
670
- console.log(`Marked ${symbolGranularityMigration.count} existing coarse symbol link(s) as legacy-link.`);
671
- }
672
552
  if (semanticAdvisorBackfill === "pending") {
673
553
  console.log(`Marked semantic advisor backfill as pending in ${KB_MANIFEST_RELATIVE}.`);
674
554
  }
@@ -750,9 +630,13 @@ async function buildWorkspaceMigrationPlan(workspaceRoot = process.cwd()) {
750
630
  if (checkable && storeSynced) {
751
631
  const checkResult = await executeOperation(runtime, checkSpec, {});
752
632
  if (checkResult.structuredContent?.migrationPlan !== undefined) {
753
- // Lifecycle repairs and their sync are planned above from authored
754
- // sources, which may be newer than the compiled store.
755
- plans.push(withoutActionCodes(checkResult.structuredContent.migrationPlan, SOURCE_PLANNED_LIFECYCLE_CODES));
633
+ // Lifecycle repairs, their sync and requirement status mappings are
634
+ // planned above from authored sources, which may be newer than the
635
+ // compiled store.
636
+ plans.push(withoutActionCodes(checkResult.structuredContent.migrationPlan, new Set([
637
+ ...SOURCE_PLANNED_LIFECYCLE_CODES,
638
+ "check_req-status-vocabulary",
639
+ ])));
756
640
  }
757
641
  const coverageInputs = [
758
642
  { by: "req", limit: 10_000, offset: 0 },
@@ -858,24 +742,6 @@ function approvedActionIdsOf(options) {
858
742
  .filter((id) => id.length > 0);
859
743
  return [...new Set(ids)];
860
744
  }
861
- /** How many of `files` Git does not track; 0 outside a repository. */
862
- // implements REQ-cli-schema-migration
863
- function untrackedFiles(cwd, files) {
864
- let tracked;
865
- try {
866
- tracked = new Set(execFileSync("git", ["-C", cwd, "ls-files", "-z", "--", ".kb"], {
867
- encoding: "utf8",
868
- stdio: ["ignore", "pipe", "ignore"],
869
- timeout: 10_000,
870
- })
871
- .split("\0")
872
- .filter((file) => file.length > 0));
873
- }
874
- catch {
875
- return 0;
876
- }
877
- return files.filter((file) => !tracked.has(file)).length;
878
- }
879
745
  /** The table rendering of an apply result; --format json prints it whole. */
880
746
  // implements REQ-cli-schema-migration
881
747
  function printApplySummary(content) {
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/manifest.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,EAML,uBAAuB,EACxB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,0BAA0B,EAG3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAG5E,UAAU,YAAY;IACpB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;CACjE;AAkBD,eAAO,MAAM,8BAA8B,QAc1C,CAAC;AAaF;;;;;;;;;GASG;AACH,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAG7E;AAoHD,wBAAsB,0BAA0B,CAE9C,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACA,OAAO,CAAC,IAAI,CAAC,CA0Mf;AAED;;;;;;;;GAQG;AAEH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,MAAM,GAAG,IAAI,CAWf;AAED,wBAAgB,0BAA0B,CAExC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAOT;AAED,wBAAgB,8BAA8B,CAE5C,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,OAAO,CAUT"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/manifest.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,EAML,uBAAuB,EACxB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,0BAA0B,EAG3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAG5E,UAAU,YAAY;IACpB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,2BAA2B,EAAE,OAAO,2BAA2B,CAAC;CACjE;AAkBD,eAAO,MAAM,8BAA8B,QAc1C,CAAC;AAeF;;;;;;;;;GASG;AACH,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAG7E;AA2HD,wBAAsB,0BAA0B,CAE9C,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACA,OAAO,CAAC,IAAI,CAAC,CA0Mf;AAED;;;;;;;;GAQG;AAEH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,MAAM,GAAG,IAAI,CAWf;AAED,wBAAgB,0BAA0B,CAExC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAOT;AAED,wBAAgB,8BAA8B,CAE5C,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAC3B,OAAO,CAUT"}