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
@@ -16,26 +16,103 @@
16
16
  along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
  */
18
18
  import { execSync } from "node:child_process";
19
- import { existsSync } from "node:fs";
19
+ import { existsSync, readdirSync } from "node:fs";
20
20
  import * as path from "node:path";
21
21
  import { branchErrorToDiagnostic, createDocsNotIndexedDiagnostic, createInvalidAuthoringDiagnostic, createKbMissingDiagnostic, formatSyncSummary, } from "../diagnostics.js";
22
+ import { EngineClient, engineSocketPath, ensureJournaledBranchStoreAsync, fsyncJournaledBranchStore, } from "../engine.js";
22
23
  import { isCliDebugEnabled } from "../env.js";
23
- import { extractFromRelationshipShards, flattenRelationships, validateRelationships, } from "../extractors/relationships.js";
24
+ import { extractRelationshipShard, validateRelationships, } from "../extractors/relationships.js";
25
+ import { analyzeSemanticAdvisorInput } from "../operations/semantic-advisor/analyze-prose.js";
26
+ import { validateSemanticInventoryBoundary } from "../operations/semantic-advisor/ingestion-boundary.js";
24
27
  import { PrologProcess } from "../prolog.js";
25
28
  import { resolveActiveBranch, } from "../utils/branch-resolver.js";
26
29
  import { loadSyncConfig } from "../utils/config.js";
27
- import { SYNC_CACHE_TTL_MS, SYNC_CACHE_VERSION, hashFile, readSyncCache, toCacheKey, writeSyncCache, } from "./sync/cache.js";
30
+ import { SYNC_CACHE_TTL_MS, SYNC_CACHE_VERSION, hashFile, hashNormalized, readSyncCache, toCacheKey, writeSyncCache, } from "./sync/cache.js";
28
31
  import { discoverSourceFiles, } from "./sync/discovery.js";
29
32
  import { processExtractions } from "./sync/extraction.js";
30
33
  import { refreshManifestCoordinates } from "./sync/manifest.js";
31
- import { persistEntities, persistRelationships } from "./sync/persistence.js";
32
- import { atomicPublish, cleanupStaging, createUniqueStagingPath, prepareStagingEnvironment, } from "./sync/staging.js";
34
+ import { persistEntities, persistRelationships, retractEntitiesById, retractEntitiesForSources, retractEntityRelationshipsById, retractRelationships, } from "./sync/persistence.js";
35
+ import { atomicPublish, atomicPublishGeneration, cleanupStaging, createUniqueStagingPath, prepareStagingEnvironment, } from "./sync/staging.js";
33
36
  export class SyncError extends Error {
34
37
  constructor(message) {
35
38
  super(message);
36
39
  this.name = "SyncError";
37
40
  }
38
41
  }
42
+ function relationshipKey(relationship) {
43
+ return `${relationship.type}\0${relationship.from}\0${relationship.to}`;
44
+ }
45
+ function relationshipFromKey(key) {
46
+ const [type, from, to] = key.split("\0");
47
+ if (type === undefined || from === undefined || to === undefined) {
48
+ throw new SyncError(`Invalid cached relationship key: ${key}`);
49
+ }
50
+ return { type, from, to };
51
+ }
52
+ function normalizedEntityHash(result) {
53
+ const entity = { ...result.entity };
54
+ // Manifest defaults are generated at extraction time. They must not turn a
55
+ // one-record edit into 10,000 false-positive symbol updates.
56
+ entity.created_at = undefined;
57
+ entity.updated_at = undefined;
58
+ return hashNormalized({
59
+ entity,
60
+ sourceFile: result.sourceFile,
61
+ relationships: result.relationships
62
+ .map(relationshipKey)
63
+ .sort((left, right) => left.localeCompare(right)),
64
+ });
65
+ }
66
+ async function checkpointNoopSync(workspaceRoot, branch) {
67
+ const engine = new EngineClient({
68
+ workspaceRoot,
69
+ branch,
70
+ timeout: 120_000,
71
+ });
72
+ try {
73
+ await engine.start();
74
+ const checkpoint = await engine.checkpoint();
75
+ if (!checkpoint.success) {
76
+ throw new SyncError(`Failed to publish the no-op sync checkpoint: ${checkpoint.error ?? "Unknown error"}`);
77
+ }
78
+ }
79
+ finally {
80
+ await engine.terminate();
81
+ }
82
+ }
83
+ function compilerCacheHasEntityDelta(cache) {
84
+ return (cache.entityHashes !== undefined && cache.sourceEntityIds !== undefined);
85
+ }
86
+ function compilerCacheIsFresh(cache, sourceFiles, nowMs) {
87
+ const sourceKeys = sourceFiles.map(toCacheKey);
88
+ if (Object.keys(cache.hashes).some((key) => !sourceKeys.includes(key))) {
89
+ return false;
90
+ }
91
+ return sourceKeys.every((key) => {
92
+ const lastSeen = cache.seenAt[key];
93
+ const lastSeenMs = lastSeen ? Date.parse(lastSeen) : Number.NaN;
94
+ return (typeof cache.hashes[key] === "string" &&
95
+ lastSeen !== undefined &&
96
+ !Number.isNaN(lastSeenMs) &&
97
+ nowMs - lastSeenMs <= SYNC_CACHE_TTL_MS);
98
+ });
99
+ }
100
+ function compilerCacheFilesMatch(cache, sourceFiles, relationshipFiles) {
101
+ for (const file of sourceFiles) {
102
+ if (cache.hashes[toCacheKey(file)] !== hashFile(file))
103
+ return false;
104
+ }
105
+ const relationshipKeys = new Set(relationshipFiles.map(toCacheKey));
106
+ if (Object.keys(cache.relationshipHashes ?? {}).some((key) => !relationshipKeys.has(key))) {
107
+ return false;
108
+ }
109
+ for (const file of relationshipFiles) {
110
+ if (cache.relationshipHashes?.[toCacheKey(file)] !== hashFile(file)) {
111
+ return false;
112
+ }
113
+ }
114
+ return true;
115
+ }
39
116
  // implements REQ-003, REQ-007
40
117
  export async function syncCommand(options = {}, runtime = {}) {
41
118
  const validateOnly = options.validateOnly ?? false;
@@ -70,6 +147,24 @@ export async function syncCommand(options = {}, runtime = {}) {
70
147
  throw new SyncError(`Failed to resolve active branch: ${branchResult.error}`);
71
148
  }
72
149
  currentBranch = branchResult.branch;
150
+ // Cut over legacy branches before creating a staging copy. If another
151
+ // CLI/MCP operation has an engine attached, stop that single writer so the
152
+ // atomic publish below cannot race its RDF lock.
153
+ const livePathForEngine = path.join(process.cwd(), `.kb/branches/${currentBranch}`);
154
+ if (!validateOnly) {
155
+ await ensureJournaledBranchStoreAsync(livePathForEngine);
156
+ const existingEngine = new EngineClient({
157
+ workspaceRoot: process.cwd(),
158
+ branch: currentBranch,
159
+ timeout: 2_000,
160
+ });
161
+ const needsExclusiveGenerationPublish = rebuild || runtime.createProlog !== undefined;
162
+ if (needsExclusiveGenerationPublish &&
163
+ existsSync(engineSocketPath(process.cwd(), currentBranch))) {
164
+ await existingEngine.stop(false).catch(() => undefined);
165
+ await existingEngine.terminate();
166
+ }
167
+ }
73
168
  if (isCliDebugEnabled()) {
74
169
  // eslint-disable-next-line no-console
75
170
  console.log("[kibi-debug] currentBranch:", currentBranch);
@@ -88,16 +183,120 @@ export async function syncCommand(options = {}, runtime = {}) {
88
183
  const syncCache = readSyncCache(cachePath);
89
184
  const nowIso = new Date().toISOString();
90
185
  const nowMs = Date.now();
186
+ const currentSourceKeys = new Set(sourceFiles.map(toCacheKey));
187
+ const relationshipShardFiles = existsSync(relationshipsDir)
188
+ ? readdirSync(relationshipsDir)
189
+ .filter((file) => file.endsWith(".yaml") || file.endsWith(".yml"))
190
+ .map((file) => path.join(relationshipsDir, file))
191
+ .sort()
192
+ : [];
193
+ // No-op is a first-class compiler result. Hashing the configured inputs is
194
+ // sufficient when normalized entity/shard inventories are already fresh;
195
+ // avoid rebuilding maps and rediscovering RDF state for the common path.
196
+ if (!validateOnly &&
197
+ !rebuild &&
198
+ !options.refreshSymbolCoordinates &&
199
+ runtime.createProlog === undefined &&
200
+ compilerCacheHasEntityDelta(syncCache) &&
201
+ compilerCacheIsFresh(syncCache, sourceFiles, nowMs) &&
202
+ compilerCacheFilesMatch(syncCache, sourceFiles, relationshipShardFiles)) {
203
+ await checkpointNoopSync(process.cwd(), currentBranch);
204
+ console.log("✓ Imported 0 entities, 0 relationships (no changes)");
205
+ return withOptionalCommit({
206
+ branch: currentBranch,
207
+ timestamp: new Date().toISOString(),
208
+ entityCounts,
209
+ relationshipCount: 0,
210
+ success: true,
211
+ published: false,
212
+ failures: diagnostics,
213
+ durationMs: Date.now() - startTime,
214
+ exitCode: 0,
215
+ }, getCurrentCommit());
216
+ }
217
+ const changedSourceHashes = new Map();
218
+ for (const file of sourceFiles) {
219
+ const key = toCacheKey(file);
220
+ const hash = hashFile(file);
221
+ if (syncCache.hashes[key] !== hash)
222
+ changedSourceHashes.set(key, hash);
223
+ }
91
224
  const nextHashes = {};
92
225
  const nextSeenAt = {};
93
- const shardResults = extractFromRelationshipShards(relationshipsDir);
94
- const allRelationships = flattenRelationships(shardResults);
226
+ const nextSemanticHashes = {
227
+ ...syncCache.semanticHashes,
228
+ };
229
+ const nextSemanticContracts = {
230
+ ...syncCache.semanticContracts,
231
+ };
232
+ const nextRelationshipHashes = {};
233
+ const nextShardRelationships = {};
234
+ const addedShardRelationships = [];
235
+ const removedShardRelationships = [];
236
+ const initialSemanticBaseline = Object.keys(syncCache.hashes).length === 0;
95
237
  const changedMarkdownFiles = [];
96
238
  const changedManifestFiles = [];
239
+ const forceEntitySourceKeys = new Set();
240
+ // A removed source has no file left to extract, but its entities must be
241
+ // retracted from the branch. Keep this separate from failed extraction:
242
+ // malformed current files remain authoritative until a later successful
243
+ // sync rather than deleting their last known-good entities.
244
+ const deletedSourceFiles = Object.keys(syncCache.hashes)
245
+ .filter((key) => !currentSourceKeys.has(key))
246
+ .map((key) => path.resolve(process.cwd(), key));
247
+ const currentRelationshipKeys = new Set();
248
+ for (const shardPath of relationshipShardFiles) {
249
+ const key = toCacheKey(shardPath);
250
+ currentRelationshipKeys.add(key);
251
+ let hash;
252
+ try {
253
+ hash = hashFile(shardPath);
254
+ nextRelationshipHashes[key] = hash;
255
+ }
256
+ catch (error) {
257
+ const message = error instanceof Error ? error.message : String(error);
258
+ console.warn(`Warning: Failed to hash relationship shard ${shardPath}: ${message}`);
259
+ }
260
+ const previousKeys = syncCache.shardRelationships?.[key] ?? [];
261
+ const shardChanged = syncCache.shardRelationships === undefined ||
262
+ hash === undefined ||
263
+ syncCache.relationshipHashes?.[key] !== hash;
264
+ const currentKeys = shardChanged
265
+ ? extractRelationshipShard(shardPath)
266
+ .relationships.map(relationshipKey)
267
+ .sort((left, right) => left.localeCompare(right))
268
+ : previousKeys;
269
+ nextShardRelationships[key] = currentKeys;
270
+ if (shardChanged) {
271
+ const previous = new Set(previousKeys);
272
+ const current = new Set(currentKeys);
273
+ for (const edge of previous) {
274
+ if (!current.has(edge)) {
275
+ removedShardRelationships.push(relationshipFromKey(edge));
276
+ }
277
+ }
278
+ for (const edge of current) {
279
+ if (!previous.has(edge)) {
280
+ addedShardRelationships.push(relationshipFromKey(edge));
281
+ }
282
+ }
283
+ }
284
+ }
285
+ for (const [key, previousKeys] of Object.entries(syncCache.shardRelationships ?? {})) {
286
+ if (currentRelationshipKeys.has(key))
287
+ continue;
288
+ for (const edge of previousKeys) {
289
+ removedShardRelationships.push(relationshipFromKey(edge));
290
+ }
291
+ }
292
+ const relationshipChanged = addedShardRelationships.length > 0 ||
293
+ removedShardRelationships.length > 0;
97
294
  for (const file of sourceFiles) {
98
295
  try {
99
296
  const key = toCacheKey(file);
100
- const hash = hashFile(file);
297
+ const hash = changedSourceHashes.get(key) ?? syncCache.hashes[key];
298
+ if (hash === undefined)
299
+ continue;
101
300
  const lastSeen = syncCache.seenAt[key];
102
301
  const lastSeenMs = lastSeen ? Date.parse(lastSeen) : Number.NaN;
103
302
  const expired = !lastSeen ||
@@ -105,8 +304,10 @@ export async function syncCommand(options = {}, runtime = {}) {
105
304
  nowMs - lastSeenMs > SYNC_CACHE_TTL_MS;
106
305
  nextHashes[key] = hash;
107
306
  nextSeenAt[key] = nowIso;
108
- const isChanged = expired || syncCache.hashes[key] !== hash || validateOnly || rebuild;
307
+ const isChanged = expired || changedSourceHashes.has(key) || validateOnly || rebuild;
109
308
  if (isChanged) {
309
+ if (expired)
310
+ forceEntitySourceKeys.add(key);
110
311
  if (markdownFiles.includes(file)) {
111
312
  changedMarkdownFiles.push(file);
112
313
  }
@@ -120,9 +321,134 @@ export async function syncCommand(options = {}, runtime = {}) {
120
321
  console.warn(`Warning: Failed to hash ${file}: ${message}`);
121
322
  }
122
323
  }
324
+ // A v1 cache has only whole-file hashes. Perform one compiler-metadata
325
+ // backfill so subsequent edits can be reduced to normalized entity deltas.
326
+ if (syncCache.entityHashes === undefined ||
327
+ syncCache.sourceEntityIds === undefined) {
328
+ for (const file of markdownFiles) {
329
+ if (!changedMarkdownFiles.includes(file))
330
+ changedMarkdownFiles.push(file);
331
+ }
332
+ for (const file of manifestFiles) {
333
+ if (!changedManifestFiles.includes(file))
334
+ changedManifestFiles.push(file);
335
+ }
336
+ }
337
+ // Coordinate refresh must precede extraction so the manifest overlay that
338
+ // is persisted into RDF observes the newly generated coordinates. Force
339
+ // each refreshed manifest through extraction even when its authored YAML
340
+ // hash is unchanged; the generated coordinate artifact is an input too.
341
+ if (!validateOnly && options.refreshSymbolCoordinates) {
342
+ for (const file of manifestFiles) {
343
+ try {
344
+ await refreshManifestCoordinates(file, process.cwd(), {
345
+ refreshSymbolCoordinates: options.refreshSymbolCoordinates,
346
+ });
347
+ if (!changedManifestFiles.includes(file)) {
348
+ changedManifestFiles.push(file);
349
+ }
350
+ }
351
+ catch (error) {
352
+ const message = error instanceof Error ? error.message : String(error);
353
+ console.warn(`Warning: Failed to refresh symbol coordinates in ${file}: ${message}`);
354
+ }
355
+ }
356
+ }
123
357
  const performedFullReindex = changedMarkdownFiles.length === markdownFiles.length &&
124
358
  changedManifestFiles.length === manifestFiles.length;
125
- const { results, failedCacheKeys, errors } = await processExtractions(changedMarkdownFiles, changedManifestFiles, validateOnly);
359
+ const extraction = await processExtractions(changedMarkdownFiles, changedManifestFiles, validateOnly);
360
+ const { failedCacheKeys, errors } = extraction;
361
+ const extractedResults = extraction.results;
362
+ const nextEntityHashes = {
363
+ ...(syncCache.entityHashes ?? {}),
364
+ };
365
+ const nextSourceEntityIds = {
366
+ ...(syncCache.sourceEntityIds ?? {}),
367
+ };
368
+ const removedEntityIds = new Set();
369
+ const changedEntityIds = new Set();
370
+ const supportsEntityDelta = syncCache.entityHashes !== undefined &&
371
+ syncCache.sourceEntityIds !== undefined;
372
+ const useEntityDelta = supportsEntityDelta &&
373
+ !validateOnly &&
374
+ !rebuild &&
375
+ runtime.createProlog === undefined;
376
+ const materializeAllRelationships = !useEntityDelta || changedMarkdownFiles.length > 0;
377
+ const allRelationships = materializeAllRelationships
378
+ ? Object.values(nextShardRelationships).flat().map(relationshipFromKey)
379
+ : [...addedShardRelationships, ...removedShardRelationships];
380
+ for (const deletedSource of deletedSourceFiles) {
381
+ const sourceKey = toCacheKey(deletedSource);
382
+ for (const id of nextSourceEntityIds[sourceKey] ?? []) {
383
+ removedEntityIds.add(id);
384
+ delete nextEntityHashes[id];
385
+ }
386
+ delete nextSourceEntityIds[sourceKey];
387
+ }
388
+ const resultsBySource = new Map();
389
+ for (const result of extractedResults) {
390
+ const sourceKey = toCacheKey(result.entity.source);
391
+ const sourceResults = resultsBySource.get(sourceKey) ?? [];
392
+ sourceResults.push(result);
393
+ resultsBySource.set(sourceKey, sourceResults);
394
+ }
395
+ for (const changedSource of [
396
+ ...changedMarkdownFiles,
397
+ ...changedManifestFiles,
398
+ ]) {
399
+ const sourceKey = toCacheKey(changedSource);
400
+ if (failedCacheKeys.has(sourceKey))
401
+ continue;
402
+ const sourceResults = resultsBySource.get(sourceKey) ?? [];
403
+ const previousIds = new Set(nextSourceEntityIds[sourceKey] ?? []);
404
+ const currentIds = sourceResults.map(({ entity }) => entity.id).sort();
405
+ const currentIdSet = new Set(currentIds);
406
+ for (const previousId of previousIds) {
407
+ if (!currentIdSet.has(previousId)) {
408
+ removedEntityIds.add(previousId);
409
+ delete nextEntityHashes[previousId];
410
+ }
411
+ }
412
+ for (const result of sourceResults) {
413
+ const hash = normalizedEntityHash(result);
414
+ if (forceEntitySourceKeys.has(sourceKey) ||
415
+ nextEntityHashes[result.entity.id] !== hash) {
416
+ changedEntityIds.add(result.entity.id);
417
+ }
418
+ nextEntityHashes[result.entity.id] = hash;
419
+ }
420
+ nextSourceEntityIds[sourceKey] = currentIds;
421
+ }
422
+ const results = useEntityDelta
423
+ ? extractedResults.filter(({ entity }) => changedEntityIds.has(entity.id))
424
+ : extractedResults;
425
+ for (const result of extractedResults) {
426
+ if (result.entity.type !== "req")
427
+ continue;
428
+ const key = toCacheKey(result.entity.source);
429
+ const payload = {
430
+ type: result.entity.type,
431
+ id: result.entity.id,
432
+ properties: result.entity,
433
+ relationships: result.relationships,
434
+ };
435
+ const semantic = analyzeSemanticAdvisorInput({ payload });
436
+ const boundary = validateSemanticInventoryBoundary(payload, result.relationships, semantic.receipt);
437
+ const previousSemanticHash = syncCache.semanticHashes[key];
438
+ const advertisedContract = result.entity.semantic_inventory_version !== undefined ||
439
+ result.entity.semantic_source_hash !== undefined ||
440
+ result.entity.semantic_inventory !== undefined;
441
+ const enforceBoundary = (!initialSemanticBaseline && syncCache.hashes[key] === undefined) ||
442
+ syncCache.semanticContracts[key] === true ||
443
+ (previousSemanticHash !== undefined &&
444
+ previousSemanticHash !== boundary.sourceHash) ||
445
+ advertisedContract;
446
+ if (enforceBoundary && boundary.errors.length > 0) {
447
+ throw new SyncError(`${key}: proposition-complete ingestion failed: ${boundary.errors.join("; ")}. Run kb_semantic_advisor with the complete requirement prose and preserve its inventory contract.`);
448
+ }
449
+ nextSemanticHashes[key] = boundary.sourceHash;
450
+ nextSemanticContracts[key] = advertisedContract;
451
+ }
126
452
  // Collect INVALID_AUTHORING diagnostics
127
453
  for (const err of errors) {
128
454
  const error = new Error(err.message);
@@ -139,21 +465,6 @@ export async function syncCommand(options = {}, runtime = {}) {
139
465
  diagnostics.push(createInvalidAuthoringDiagnostic(err.file, embeddedTypes));
140
466
  }
141
467
  }
142
- if (!validateOnly) {
143
- if (options.refreshSymbolCoordinates) {
144
- for (const file of manifestFiles) {
145
- try {
146
- await refreshManifestCoordinates(file, process.cwd(), {
147
- refreshSymbolCoordinates: options.refreshSymbolCoordinates,
148
- });
149
- }
150
- catch (error) {
151
- const message = error instanceof Error ? error.message : String(error);
152
- console.warn(`Warning: Failed to refresh symbol coordinates in ${file}: ${message}`);
153
- }
154
- }
155
- }
156
- }
157
468
  if (validateOnly && errors.length > 0 && results.length === 0) {
158
469
  for (const err of errors) {
159
470
  console.error(`${err.file}: ${err.message}`);
@@ -171,21 +482,42 @@ export async function syncCommand(options = {}, runtime = {}) {
171
482
  exitCode: 1,
172
483
  }, getCurrentCommit());
173
484
  }
174
- if (results.length === 0 && allRelationships.length === 0 && !rebuild) {
485
+ if (results.length === 0 &&
486
+ removedEntityIds.size === 0 &&
487
+ !relationshipChanged &&
488
+ (useEntityDelta || changedMarkdownFiles.length === 0) &&
489
+ (useEntityDelta || changedManifestFiles.length === 0) &&
490
+ deletedSourceFiles.length === 0 &&
491
+ !rebuild) {
175
492
  const evictedHashes = {};
176
493
  const evictedSeenAt = {};
494
+ const evictedSemanticHashes = {};
495
+ const evictedSemanticContracts = {};
177
496
  for (const [key, hash] of Object.entries(nextHashes)) {
178
497
  if (failedCacheKeys.has(key)) {
179
498
  continue;
180
499
  }
181
500
  evictedHashes[key] = hash;
182
501
  evictedSeenAt[key] = nextSeenAt[key] ?? nowIso;
502
+ if (nextSemanticHashes[key] !== undefined) {
503
+ evictedSemanticHashes[key] = nextSemanticHashes[key];
504
+ evictedSemanticContracts[key] = nextSemanticContracts[key] === true;
505
+ }
183
506
  }
184
507
  writeSyncCache(cachePath, {
185
508
  version: SYNC_CACHE_VERSION,
186
509
  hashes: evictedHashes,
510
+ relationshipHashes: nextRelationshipHashes,
511
+ entityHashes: nextEntityHashes,
512
+ sourceEntityIds: nextSourceEntityIds,
513
+ shardRelationships: nextShardRelationships,
187
514
  seenAt: evictedSeenAt,
515
+ semanticHashes: evictedSemanticHashes,
516
+ semanticContracts: evictedSemanticContracts,
188
517
  });
518
+ if (runtime.createProlog === undefined) {
519
+ await checkpointNoopSync(process.cwd(), currentBranch);
520
+ }
189
521
  console.log("✓ Imported 0 entities, 0 relationships (no changes)");
190
522
  return withOptionalCommit({
191
523
  branch: currentBranch,
@@ -204,6 +536,135 @@ export async function syncCommand(options = {}, runtime = {}) {
204
536
  if (!kbExists && !rebuild) {
205
537
  diagnostics.push(createKbMissingDiagnostic(currentBranch, livePath));
206
538
  }
539
+ // implements REQ-core-journaled-engine-delta-sync
540
+ // Normal syncs are compiled directly into the long-lived single-writer
541
+ // engine. Rebuilds retain generation replacement semantics, and injected
542
+ // Prolog runtimes keep the staging path used by the contract fixtures.
543
+ // This is the cutover that avoids copying and rewriting a complete branch
544
+ // for a one-symbol or relationship-only change.
545
+ if (!validateOnly && !rebuild && runtime.createProlog === undefined) {
546
+ const engine = new EngineClient({
547
+ workspaceRoot: process.cwd(),
548
+ branch: currentBranch,
549
+ timeout: 120_000,
550
+ });
551
+ const engineProlog = engine;
552
+ try {
553
+ await engine.start();
554
+ const successfulChangedSources = [
555
+ ...changedMarkdownFiles,
556
+ ...changedManifestFiles,
557
+ ].filter((file) => !failedCacheKeys.has(toCacheKey(file)));
558
+ const removedCount = useEntityDelta
559
+ ? await retractEntitiesById(engineProlog, [...removedEntityIds])
560
+ : await retractEntitiesForSources(engineProlog, [
561
+ ...successfulChangedSources,
562
+ ...deletedSourceFiles,
563
+ ]);
564
+ if (useEntityDelta) {
565
+ await retractEntityRelationshipsById(engineProlog, [
566
+ ...changedEntityIds,
567
+ ]);
568
+ const exactRemovedEdges = removedShardRelationships.filter((relationship) => !removedEntityIds.has(relationship.from) &&
569
+ !removedEntityIds.has(relationship.to) &&
570
+ !changedEntityIds.has(relationship.from));
571
+ await retractRelationships(engineProlog, exactRemovedEdges);
572
+ }
573
+ else if (relationshipChanged) {
574
+ const cleared = await engine.query("kb_retract_all_relationships");
575
+ if (!cleared.success) {
576
+ throw new SyncError(`Failed to clear changed relationship shards: ${cleared.error || "Unknown error"}`);
577
+ }
578
+ }
579
+ const entityIds = new Set();
580
+ for (const { entity } of results) {
581
+ entityCounts[entity.type] = (entityCounts[entity.type] || 0) + 1;
582
+ }
583
+ const { entityCount, kbModified: entitiesModified } = await persistEntities(engineProlog, results, entityIds, {
584
+ loadExistingEntityIds: !useEntityDelta,
585
+ });
586
+ const validationErrors = useEntityDelta
587
+ ? []
588
+ : validateRelationships(allRelationships, entityIds);
589
+ if (validationErrors.length > 0) {
590
+ console.warn(`Warning: ${validationErrors.length} dangling relationship(s) found`);
591
+ }
592
+ const danglingKeys = new Set(validationErrors.map(({ relationship }) => `${relationship.type}|${relationship.from}|${relationship.to}`));
593
+ const shardDeltaByKey = new Map();
594
+ const shardCandidates = useEntityDelta
595
+ ? [
596
+ ...addedShardRelationships,
597
+ ...allRelationships.filter((relationship) => changedEntityIds.has(relationship.from)),
598
+ ]
599
+ : allRelationships;
600
+ for (const relationship of shardCandidates) {
601
+ if (!danglingKeys.has(`${relationship.type}|${relationship.from}|${relationship.to}`)) {
602
+ shardDeltaByKey.set(relationshipKey(relationship), relationship);
603
+ }
604
+ }
605
+ const { relationshipCount, kbModified: relationshipsModified } = await persistRelationships(engineProlog, results, [
606
+ ...shardDeltaByKey.values(),
607
+ ]);
608
+ const kbModified = removedCount > 0 ||
609
+ relationshipChanged ||
610
+ entitiesModified ||
611
+ relationshipsModified;
612
+ if (kbModified)
613
+ engine.invalidateCache();
614
+ const saveResult = await engine.save();
615
+ if (!saveResult.success) {
616
+ throw new SyncError(`Failed to save journaled KB: ${saveResult.error || "Unknown error"}`);
617
+ }
618
+ const evictedHashes = {};
619
+ const evictedSeenAt = {};
620
+ const evictedSemanticHashes = {};
621
+ const evictedSemanticContracts = {};
622
+ for (const [key, hash] of Object.entries(nextHashes)) {
623
+ if (failedCacheKeys.has(key))
624
+ continue;
625
+ evictedHashes[key] = hash;
626
+ evictedSeenAt[key] = nextSeenAt[key] ?? nowIso;
627
+ if (nextSemanticHashes[key] !== undefined) {
628
+ evictedSemanticHashes[key] = nextSemanticHashes[key];
629
+ evictedSemanticContracts[key] = nextSemanticContracts[key] === true;
630
+ }
631
+ }
632
+ writeSyncCache(path.join(livePath, "sync-cache.json"), {
633
+ version: SYNC_CACHE_VERSION,
634
+ hashes: evictedHashes,
635
+ relationshipHashes: nextRelationshipHashes,
636
+ entityHashes: nextEntityHashes,
637
+ sourceEntityIds: nextSourceEntityIds,
638
+ shardRelationships: nextShardRelationships,
639
+ seenAt: evictedSeenAt,
640
+ semanticHashes: evictedSemanticHashes,
641
+ semanticContracts: evictedSemanticContracts,
642
+ });
643
+ published = true;
644
+ if (performedFullReindex &&
645
+ markdownFiles.length > 0 &&
646
+ entityCount < markdownFiles.length) {
647
+ diagnostics.push(createDocsNotIndexedDiagnostic(markdownFiles.length, entityCount));
648
+ }
649
+ console.log(`✓ Imported ${entityCount} entities, ${relationshipCount} relationships (removed ${removedCount} entities)`);
650
+ const commit = getCurrentCommit();
651
+ const summary = withOptionalCommit({
652
+ branch: currentBranch,
653
+ timestamp: new Date().toISOString(),
654
+ entityCounts,
655
+ relationshipCount,
656
+ success: true,
657
+ published,
658
+ failures: diagnostics,
659
+ durationMs: Date.now() - startTime,
660
+ }, commit);
661
+ console.log(formatSyncSummary(summary));
662
+ return { ...summary, exitCode: 0 };
663
+ }
664
+ finally {
665
+ await engine.terminate();
666
+ }
667
+ }
207
668
  stagingPath = createUniqueStagingPath(currentBranch, process.cwd());
208
669
  const runtimeContext = {
209
670
  currentBranch,
@@ -227,6 +688,20 @@ export async function syncCommand(options = {}, runtime = {}) {
227
688
  for (const { entity } of results) {
228
689
  entityCounts[entity.type] = (entityCounts[entity.type] || 0) + 1;
229
690
  }
691
+ // A copied generation retains entities from old source contents. Remove
692
+ // only successfully extracted/deleted sources before applying the new
693
+ // entities; failed extraction deliberately leaves the previous data.
694
+ await retractEntitiesForSources(prolog, [
695
+ ...changedMarkdownFiles,
696
+ ...changedManifestFiles,
697
+ ...deletedSourceFiles,
698
+ ].filter((file) => !failedCacheKeys.has(toCacheKey(file))));
699
+ if (relationshipChanged) {
700
+ const clearRelationships = await prolog.query("kb_retract_all_relationships");
701
+ if (!clearRelationships.success) {
702
+ throw new SyncError(`Failed to clear changed relationship shards: ${clearRelationships.error || "Unknown error"}`);
703
+ }
704
+ }
230
705
  const { entityCount, kbModified: entitiesModified } = await persistEntities(prolog, results, entityIds);
231
706
  const validationErrors = validateRelationships(allRelationships, entityIds);
232
707
  if (validationErrors.length > 0) {
@@ -284,22 +759,41 @@ export async function syncCommand(options = {}, runtime = {}) {
284
759
  }
285
760
  await prolog.query("kb_detach");
286
761
  await prolog.terminate();
287
- atomicPublish(stagingPath, livePath);
762
+ const journaledLive = existsSync(path.join(livePath, "storage.json"));
763
+ if (rebuild && journaledLive) {
764
+ atomicPublishGeneration(stagingPath, livePath);
765
+ }
766
+ else {
767
+ atomicPublish(stagingPath, livePath);
768
+ }
769
+ fsyncJournaledBranchStore(livePath);
288
770
  cleanupStaging(stagingPath);
289
771
  const evictedHashes = {};
290
772
  const evictedSeenAt = {};
773
+ const evictedSemanticHashes = {};
774
+ const evictedSemanticContracts = {};
291
775
  for (const [key, hash] of Object.entries(nextHashes)) {
292
776
  if (failedCacheKeys.has(key)) {
293
777
  continue;
294
778
  }
295
779
  evictedHashes[key] = hash;
296
780
  evictedSeenAt[key] = nextSeenAt[key] ?? nowIso;
781
+ if (nextSemanticHashes[key] !== undefined) {
782
+ evictedSemanticHashes[key] = nextSemanticHashes[key];
783
+ evictedSemanticContracts[key] = nextSemanticContracts[key] === true;
784
+ }
297
785
  }
298
786
  const liveCachePath = path.join(livePath, "sync-cache.json");
299
787
  writeSyncCache(liveCachePath, {
300
788
  version: SYNC_CACHE_VERSION,
301
789
  hashes: evictedHashes,
790
+ relationshipHashes: nextRelationshipHashes,
791
+ entityHashes: nextEntityHashes,
792
+ sourceEntityIds: nextSourceEntityIds,
793
+ shardRelationships: nextShardRelationships,
302
794
  seenAt: evictedSeenAt,
795
+ semanticHashes: evictedSemanticHashes,
796
+ semanticContracts: evictedSemanticContracts,
303
797
  });
304
798
  published = true;
305
799
  if (performedFullReindex &&