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
@@ -9,7 +9,10 @@ import { type EntityLane } from "../utils/kb-paths.js";
9
9
  * - legacy `.kb/config.json` is read (never trusted blindly);
10
10
  * - configured or default legacy paths are discovered on disk;
11
11
  * - files move into `.kb/<lane>/` without content changes;
12
- * - destination conflicts abort before any file moves;
12
+ * - a legacy copy identical to its destination is removed instead of moved;
13
+ * - other destination conflicts abort before any file moves, each with the
14
+ * resolution Kibi can tell (a stale subset of the symbols manifest, a
15
+ * generated coordinates file, or a real conflict);
13
16
  * - partial migrations resume safely (already-moved files are no-ops);
14
17
  * - on success the legacy config is retired.
15
18
  */
@@ -22,9 +25,34 @@ export interface StorageMigrationPlan {
22
25
  /** Lifecycle state preserved into the canonical manifest. */
23
26
  schemaVersion: number | null;
24
27
  semanticAdvisorBackfill: SemanticAdvisorBackfill | null;
28
+ /**
29
+ * Legacy copies byte-identical to their `.kb/` destination: the migration
30
+ * removes them instead of moving them.
31
+ */
32
+ removals: StorageMigrationRemoval[];
33
+ /** Existing destinations that block a move, with how to resolve each. */
34
+ conflicts: StorageMigrationConflict[];
25
35
  /** Why the migration cannot proceed automatically, if blocked. */
26
36
  blockers: string[];
27
37
  }
38
+ export interface StorageMigrationRemoval {
39
+ /** The legacy copy to remove. */
40
+ path: string;
41
+ /** The `.kb/` file it duplicates. */
42
+ duplicateOf: string;
43
+ }
44
+ export interface StorageMigrationConflict {
45
+ from: string;
46
+ to: string;
47
+ /**
48
+ * `stale_subset`: every symbol of the legacy manifest is already in the
49
+ * `.kb/` one. `generated`: a symbol-coordinates file, which sync
50
+ * regenerates. `ambiguous`: Kibi cannot tell which legacy file is meant.
51
+ * `conflict`: different content Kibi cannot classify.
52
+ */
53
+ kind: "stale_subset" | "generated" | "ambiguous" | "conflict";
54
+ resolution: string;
55
+ }
28
56
  export interface StorageMigrationMove {
29
57
  from: string;
30
58
  to: string;
@@ -34,6 +62,13 @@ export interface StorageMigrationMove {
34
62
  export declare function planLegacyStorageMigration(cwd: string): StorageMigrationPlan;
35
63
  export interface StorageMigrationResult {
36
64
  movedFiles: string[];
65
+ /** Legacy copies removed because they were identical to their destination. */
66
+ removedFiles: string[];
67
+ /**
68
+ * Moved files Git does not track yet. Each got a pending-source receipt, so
69
+ * `kibi sync` compiles them before they are staged; empty outside Git.
70
+ */
71
+ untrackedMovedFiles: string[];
37
72
  retiredLegacyConfig: boolean;
38
73
  manifestPath: string;
39
74
  schemaVersion: number;
@@ -47,6 +82,16 @@ export interface StorageMigrationResult {
47
82
  export declare function applyLegacyStorageMigration(cwd: string, plan: StorageMigrationPlan): StorageMigrationResult;
48
83
  /** Rewrite recovery pending-source receipts after a storage cutover. */
49
84
  export declare function rewritePendingSourceReceiptPaths(cwd: string, moves: readonly StorageMigrationMove[]): number;
85
+ /**
86
+ * Bind every moved file Git does not track to its bytes with a
87
+ * pending-source receipt, the record kb_upsert writes for the files it
88
+ * creates. `kibi sync` compiles tracked sources plus receipted ones, so the
89
+ * moved knowledge compiles at once; Kibi never stages anything itself, and
90
+ * each receipt retires when Git starts tracking its file. Later migration
91
+ * rewrites rebind the receipt they find. Covers files a previous,
92
+ * interrupted run moved too. Returns the untracked destinations.
93
+ */
94
+ export declare function writeMovedSourceReceipts(cwd: string, moves: readonly StorageMigrationMove[]): string[];
50
95
  /** True when the workspace still needs the legacy storage migration. */
51
96
  export declare function needsLegacyStorageMigration(cwd: string): boolean;
52
97
  /** Entity lanes that must exist for the canonical layout to be complete. */
@@ -1 +1 @@
1
- {"version":3,"file":"legacy-storage-migration.d.ts","sourceRoot":"","sources":["../../src/commands/legacy-storage-migration.ts"],"names":[],"mappings":"AAoCA,OAAO,EACL,KAAK,uBAAuB,EAI7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,sBAAsB,CAAC;AAI9B;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxD,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B;AA2BD,6DAA6D;AAC7D,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAwH5E;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,uBAAuB,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,oBAAoB,GACzB,sBAAsB,CAwDxB;AA+BD,wEAAwE;AACxE,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,CAwCR;AA8BD,wEAAwE;AACxE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOhE;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAI/D"}
1
+ {"version":3,"file":"legacy-storage-migration.d.ts","sourceRoot":"","sources":["../../src/commands/legacy-storage-migration.ts"],"names":[],"mappings":"AAwCA,OAAO,EACL,KAAK,uBAAuB,EAI7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,sBAAsB,CAAC;AAI9B;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxD;;;OAGG;IACH,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,yEAAyE;IACzE,SAAS,EAAE,wBAAwB,EAAE,CAAC;IACtC,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,IAAI,EAAE,cAAc,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B;AA2BD,6DAA6D;AAC7D,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAkJ5E;AAoID,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,uBAAuB,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,oBAAoB,GACzB,sBAAsB,CAyExB;AAkCD,wEAAwE;AACxE,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,CAwCR;AAqBD;;;;;;;;GAQG;AAEH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,EAAE,CAqBV;AA8BD,wEAAwE;AACxE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAShE;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAI/D"}
@@ -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
  }
@@ -11,12 +11,49 @@ interface MigrateOptions {
11
11
  /** Internal migration-plan executor: create only the canonical baseline manifest. */
12
12
  initializeMissingConfig?: boolean;
13
13
  }
14
+ export interface MigrationAuditRecord {
15
+ auditVersion: number;
16
+ branch: string;
17
+ manifestPath: string;
18
+ fromVersion: number | null;
19
+ migratedAt: string;
20
+ semanticAdvisorBackfill: "pending" | "completed" | "not_applicable" | null;
21
+ status: "applied";
22
+ symbolGranularityLegacyLinks: number;
23
+ /** Entities carrying origin {kind: migration, ref: 'kibi migrate v5->v6'}. */
24
+ entityOriginBackfill: number;
25
+ /** Superseded requirements this run set to status: closed. */
26
+ supersededRequirementsClosed: number;
27
+ /** Pre-canonical source paths this run pointed at their .kb/ file. */
28
+ sourcePathsRewritten: number;
29
+ /** Authored source values this run removed: they named the entity's own file or nothing. */
30
+ sourcePathsRemoved: number;
31
+ toVersion: number;
32
+ /** The schema warning the KB showed before this run migrated it. */
33
+ preMigrationWarning: string | null;
34
+ steps: readonly string[];
35
+ }
36
+ /** What one applied `kibi migrate --yes` run changed, for plan apply results. */
37
+ export interface MigrationRunSummary {
38
+ /** Workspace-relative path of the audit record this run wrote. */
39
+ auditPath: string;
40
+ audit: MigrationAuditRecord;
41
+ /** Whether this run added the canonical derived entries to .gitignore. */
42
+ gitignoreUpdated: boolean;
43
+ }
14
44
  export declare function formatSchemaVersion(rawSchemaVersion: unknown, normalized: number | null): string;
15
45
  export declare function warnMigrationRequiredWithoutYes(): {
16
46
  exitCode: number;
17
47
  };
18
48
  export declare function migrateCommand(options?: MigrateOptions): Promise<{
19
49
  exitCode: number;
50
+ summary?: MigrationRunSummary;
20
51
  }>;
52
+ /**
53
+ * One line naming what an applied migration run changed, read from the audit
54
+ * record it wrote: a count of zero shows a pass that had nothing left to do
55
+ * (for example legacy links an earlier plan action already marked).
56
+ */
57
+ export declare function describeMigrationRun(summary: MigrationRunSummary): string;
21
58
  export {};
22
59
  //# sourceMappingURL=migrate.d.ts.map
@@ -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;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB,EAAE,SAAS,GAAG,WAAW,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC3E,MAAM,EAAE,SAAS,CAAC;IAClB,4BAA4B,EAAE,MAAM,CAAC;IACrC,8EAA8E;IAC9E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,4BAA4B,EAAE,MAAM,CAAC;IACrC,sEAAsE;IACtE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,4FAA4F;IAC5F,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,0EAA0E;IAC1E,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAyID,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,CAAC;IAAC,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAgZ9D;AAED;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAGzE"}