hadara 0.3.2 → 0.3.3-rc.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 (51) hide show
  1. package/README.md +38 -23
  2. package/dist/cli/context.js +110 -0
  3. package/dist/cli/help.js +6 -7
  4. package/dist/cli/init.js +56 -58
  5. package/dist/cli/main.js +12 -0
  6. package/dist/cli/session.js +37 -0
  7. package/dist/cli/task.js +52 -0
  8. package/dist/context/code-graph-extractor.js +123 -0
  9. package/dist/context/code-index.js +1154 -0
  10. package/dist/context/context-cache-store.js +925 -0
  11. package/dist/context/context-graph-builder.js +341 -0
  12. package/dist/context/context-graph.js +42 -0
  13. package/dist/context/context-pack.js +457 -0
  14. package/dist/context/context-slice-boundary.js +37 -0
  15. package/dist/context/context-slice.js +487 -0
  16. package/dist/context/document-extractors.js +343 -0
  17. package/dist/context/evidence-extractors.js +179 -0
  18. package/dist/context/extractor-contract.js +166 -0
  19. package/dist/context/registry-extractors.js +177 -0
  20. package/dist/context/release-extractors.js +175 -0
  21. package/dist/context/session-start.js +297 -0
  22. package/dist/context/source-manifest.js +566 -0
  23. package/dist/context/state-projection.js +196 -0
  24. package/dist/context/task-extractors.js +168 -0
  25. package/dist/core/schema.js +26 -0
  26. package/dist/harness/validate.js +7 -8
  27. package/dist/schemas/code-index.schema.json +173 -0
  28. package/dist/schemas/context-cache-record.schema.json +56 -0
  29. package/dist/schemas/context-cache-status.schema.json +167 -0
  30. package/dist/schemas/context-cache-warm.schema.json +222 -0
  31. package/dist/schemas/context-graph.schema.json +286 -0
  32. package/dist/schemas/context-pack.schema.json +246 -0
  33. package/dist/schemas/context-slice.schema.json +94 -0
  34. package/dist/schemas/context-source-manifest.schema.json +147 -0
  35. package/dist/schemas/schema-index.json +91 -0
  36. package/dist/schemas/session-start.schema.json +158 -0
  37. package/dist/schemas/task-close-repair-plan.schema.json +67 -0
  38. package/dist/schemas/task-context.schema.json +125 -0
  39. package/dist/schemas/task-finalize.schema.json +98 -0
  40. package/dist/schemas/task-lifecycle.schema.json +84 -0
  41. package/dist/services/capability-registry.js +266 -9
  42. package/dist/services/lifecycle-guide.js +23 -29
  43. package/dist/services/protocol-consistency.js +6 -8
  44. package/dist/task/acceptance.js +171 -0
  45. package/dist/task/task-close-repair-plan.js +190 -0
  46. package/dist/task/task-close.js +34 -35
  47. package/dist/task/task-finalize.js +377 -0
  48. package/dist/task/task-lifecycle.js +210 -0
  49. package/dist/task/task-next.js +10 -1
  50. package/dist/task/task-ready.js +4 -0
  51. package/package.json +1 -1
@@ -0,0 +1,925 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CONTEXT_CACHE_WARM_COMMAND = exports.CONTEXT_CACHE_STATUS_COMMAND = exports.CONTEXT_CACHE_RECORD_VERSION = exports.CONTEXT_CACHE_WARM_SCHEMA_ID = exports.CONTEXT_CACHE_STATUS_SCHEMA_ID = exports.CONTEXT_CACHE_RECORD_SCHEMA_ID = void 0;
7
+ exports.buildContextCacheRecord = buildContextCacheRecord;
8
+ exports.readContextCacheRecord = readContextCacheRecord;
9
+ exports.writeContextCacheRecord = writeContextCacheRecord;
10
+ exports.readContextSourceManifestCache = readContextSourceManifestCache;
11
+ exports.writeContextSourceManifestCache = writeContextSourceManifestCache;
12
+ exports.listContextGraphExtractorShardKeys = listContextGraphExtractorShardKeys;
13
+ exports.contextGraphExtractorShardCachePath = contextGraphExtractorShardCachePath;
14
+ exports.contextGraphCoreShardCachePath = contextGraphCoreShardCachePath;
15
+ exports.contextCodeIndexShardCachePath = contextCodeIndexShardCachePath;
16
+ exports.buildContextGraphExtractorShardRecord = buildContextGraphExtractorShardRecord;
17
+ exports.writeContextGraphExtractorShard = writeContextGraphExtractorShard;
18
+ exports.readContextGraphExtractorShard = readContextGraphExtractorShard;
19
+ exports.collectContextGraphExtractorShards = collectContextGraphExtractorShards;
20
+ exports.buildContextGraphCoreExtractionResult = buildContextGraphCoreExtractionResult;
21
+ exports.buildContextGraphCoreShardRecord = buildContextGraphCoreShardRecord;
22
+ exports.writeContextGraphCoreShard = writeContextGraphCoreShard;
23
+ exports.readContextGraphCoreShard = readContextGraphCoreShard;
24
+ exports.buildContextCodeIndexShardRecord = buildContextCodeIndexShardRecord;
25
+ exports.writeContextCodeIndexShard = writeContextCodeIndexShard;
26
+ exports.readContextCodeIndexShard = readContextCodeIndexShard;
27
+ exports.createContextCacheStatusReport = createContextCacheStatusReport;
28
+ exports.createContextCacheWarmReport = createContextCacheWarmReport;
29
+ exports.createSourceManifestCacheAnalysis = createSourceManifestCacheAnalysis;
30
+ const node_fs_1 = __importDefault(require("node:fs"));
31
+ const node_path_1 = __importDefault(require("node:path"));
32
+ const fs_1 = require("../core/fs");
33
+ const schema_1 = require("../core/schema");
34
+ const source_manifest_1 = require("./source-manifest");
35
+ const code_index_1 = require("./code-index");
36
+ const extractor_contract_1 = require("./extractor-contract");
37
+ const document_extractors_1 = require("./document-extractors");
38
+ const evidence_extractors_1 = require("./evidence-extractors");
39
+ const registry_extractors_1 = require("./registry-extractors");
40
+ const release_extractors_1 = require("./release-extractors");
41
+ const task_extractors_1 = require("./task-extractors");
42
+ exports.CONTEXT_CACHE_RECORD_SCHEMA_ID = 'hadara.context.cacheRecord.v1';
43
+ exports.CONTEXT_CACHE_STATUS_SCHEMA_ID = 'hadara.context.cacheStatus.v1';
44
+ exports.CONTEXT_CACHE_WARM_SCHEMA_ID = 'hadara.context.cacheWarm.v1';
45
+ exports.CONTEXT_CACHE_RECORD_VERSION = 'c6.2-cache-record-v1';
46
+ exports.CONTEXT_CACHE_STATUS_COMMAND = 'context.cache.status';
47
+ exports.CONTEXT_CACHE_WARM_COMMAND = 'context.cache.warm';
48
+ const CONTEXT_GRAPH_EXTRACTOR_SHARD_PROJECTION_PREFIX = 'context.graph.extractor';
49
+ const CONTEXT_GRAPH_CORE_PROJECTION = 'context.graph.core';
50
+ const CONTEXT_CODE_INDEX_PROJECTION = 'context.codeIndex';
51
+ const CONTEXT_GRAPH_EXTRACTOR_SHARD_SCHEMA_VERSION = 'hadara.contextGraph.v1';
52
+ const CONTEXT_GRAPH_CORE_CACHE_PATH = `${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT}/graph-core.json`;
53
+ const CONTEXT_CODE_INDEX_CACHE_PATH = `${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT}/code-index.json`;
54
+ const CONTEXT_GRAPH_EXTRACTOR_SHARD_KEYS = [
55
+ 'extractTaskBoard',
56
+ 'extractDocsRegistry',
57
+ 'extractCommandRegistry'
58
+ ];
59
+ const CONTEXT_GRAPH_EXTRACTOR_SHARD_CACHE_PATHS = {
60
+ extractTaskBoard: `${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT}/extractors/task-board.json`,
61
+ extractDocsRegistry: `${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT}/extractors/docs-registry.json`,
62
+ extractCommandRegistry: `${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT}/extractors/command-registry.json`
63
+ };
64
+ const CONTEXT_GRAPH_EXTRACTOR_SHARD_EXTRACTORS = {
65
+ extractTaskBoard: task_extractors_1.extractTaskBoard,
66
+ extractDocsRegistry: registry_extractors_1.extractDocsRegistry,
67
+ extractCommandRegistry: registry_extractors_1.extractCommandRegistry
68
+ };
69
+ const CONTEXT_GRAPH_CORE_EXTRACTOR_KEYS = [
70
+ 'extractTaskCapsules',
71
+ 'extractTaskBoard',
72
+ 'extractDocsRegistry',
73
+ 'extractCommandRegistry',
74
+ 'extractManagedSections',
75
+ 'extractProjectState',
76
+ 'extractDecisions',
77
+ 'extractAgentHandoff',
78
+ 'extractEvidence',
79
+ 'extractReleaseReadiness'
80
+ ];
81
+ function buildContextCacheRecord(options) {
82
+ const extractorKeys = options.extractorKeys?.length ? [...options.extractorKeys].sort() : undefined;
83
+ const sourceSubsetHash = extractorKeys
84
+ ? (0, extractor_contract_1.hashContextGraphJson)(extractorKeys.map((extractorKey) => ({
85
+ extractorKey,
86
+ subsetHash: (0, source_manifest_1.createContextSourceSubsetHash)(options.manifest, { extractorKey })
87
+ })))
88
+ : (0, source_manifest_1.createContextSourceSubsetHash)(options.manifest);
89
+ const extractorVersions = {
90
+ ...Object.fromEntries(Object.entries(options.manifest.extractorVersions)
91
+ .filter(([key]) => !extractorKeys || extractorKeys.includes(key))),
92
+ ...(options.extractorVersions ?? {})
93
+ };
94
+ const createdAt = options.createdAt ?? new Date().toISOString();
95
+ const cacheKey = (0, extractor_contract_1.hashContextGraphJson)({
96
+ projection: options.projection,
97
+ projectionSchemaVersion: options.projectionSchemaVersion,
98
+ manifestHash: options.manifest.manifestHash,
99
+ sourceSubsetHash,
100
+ extractorVersions
101
+ });
102
+ return {
103
+ schemaVersion: exports.CONTEXT_CACHE_RECORD_SCHEMA_ID,
104
+ cacheRecordVersion: exports.CONTEXT_CACHE_RECORD_VERSION,
105
+ cacheKey,
106
+ projection: options.projection,
107
+ projectionSchemaVersion: options.projectionSchemaVersion,
108
+ createdAt,
109
+ manifestHash: options.manifest.manifestHash,
110
+ sourceSubsetHash,
111
+ extractorVersions,
112
+ degraded: options.degraded ?? false,
113
+ issues: options.issues ?? [],
114
+ payload: options.payload
115
+ };
116
+ }
117
+ function readContextCacheRecord(projectRoot, cachePath) {
118
+ const relativePath = normalizeContextCachePath(cachePath);
119
+ const absolutePath = node_path_1.default.join(projectRoot, relativePath);
120
+ if (!node_fs_1.default.existsSync(absolutePath)) {
121
+ return {
122
+ ok: false,
123
+ status: 'missing',
124
+ path: relativePath,
125
+ issues: [contextCacheIssue('info', 'CONTEXT_CACHE_MISS', `Context cache record is missing at ${relativePath}.`, relativePath)]
126
+ };
127
+ }
128
+ let parsed;
129
+ try {
130
+ parsed = JSON.parse(node_fs_1.default.readFileSync(absolutePath, 'utf8'));
131
+ }
132
+ catch (error) {
133
+ return {
134
+ ok: false,
135
+ status: 'corrupt',
136
+ path: relativePath,
137
+ issues: [contextCacheIssue('warning', 'CONTEXT_CACHE_CORRUPT', `Context cache record at ${relativePath} could not be parsed: ${error instanceof Error ? error.message : String(error)}.`, relativePath)]
138
+ };
139
+ }
140
+ const validation = (0, schema_1.validateSchema)(exports.CONTEXT_CACHE_RECORD_SCHEMA_ID, parsed);
141
+ if (!validation.ok) {
142
+ return {
143
+ ok: false,
144
+ status: 'schema-mismatch',
145
+ path: relativePath,
146
+ issues: [contextCacheIssue('warning', 'CONTEXT_CACHE_SCHEMA_MISMATCH', `Context cache record at ${relativePath} does not match ${exports.CONTEXT_CACHE_RECORD_SCHEMA_ID}.`, relativePath)]
147
+ };
148
+ }
149
+ return {
150
+ ok: true,
151
+ status: 'valid',
152
+ path: relativePath,
153
+ record: parsed,
154
+ issues: []
155
+ };
156
+ }
157
+ function writeContextCacheRecord(projectRoot, cachePath, record) {
158
+ const relativePath = normalizeContextCachePath(cachePath);
159
+ const validation = (0, schema_1.validateSchema)(exports.CONTEXT_CACHE_RECORD_SCHEMA_ID, record);
160
+ if (!validation.ok) {
161
+ throw new Error(`Context cache record does not match ${exports.CONTEXT_CACHE_RECORD_SCHEMA_ID}: ${validation.issues[0]?.path ?? '$'}`);
162
+ }
163
+ (0, fs_1.atomicWriteTextFile)(projectRoot, relativePath, `${JSON.stringify(record, null, 2)}\n`);
164
+ }
165
+ function readContextSourceManifestCache(projectRoot) {
166
+ const absolutePath = node_path_1.default.join(projectRoot, source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH);
167
+ if (!node_fs_1.default.existsSync(absolutePath)) {
168
+ return {
169
+ ok: false,
170
+ status: 'missing',
171
+ path: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH,
172
+ issues: [contextCacheIssue('info', 'CONTEXT_CACHE_MISS', `Source manifest cache is missing at ${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH}.`, source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH)]
173
+ };
174
+ }
175
+ let parsed;
176
+ try {
177
+ parsed = JSON.parse(node_fs_1.default.readFileSync(absolutePath, 'utf8'));
178
+ }
179
+ catch (error) {
180
+ return {
181
+ ok: false,
182
+ status: 'corrupt',
183
+ path: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH,
184
+ issues: [contextCacheIssue('warning', 'CONTEXT_CACHE_CORRUPT', `Source manifest cache could not be parsed: ${error instanceof Error ? error.message : String(error)}.`, source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH)]
185
+ };
186
+ }
187
+ const validation = (0, schema_1.validateSchema)(source_manifest_1.CONTEXT_SOURCE_MANIFEST_SCHEMA_ID, parsed);
188
+ if (!validation.ok) {
189
+ return {
190
+ ok: false,
191
+ status: 'schema-mismatch',
192
+ path: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH,
193
+ issues: [contextCacheIssue('warning', 'CONTEXT_CACHE_SCHEMA_MISMATCH', `Source manifest cache does not match ${source_manifest_1.CONTEXT_SOURCE_MANIFEST_SCHEMA_ID}.`, source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH)]
194
+ };
195
+ }
196
+ return {
197
+ ok: true,
198
+ status: 'valid',
199
+ path: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH,
200
+ manifest: parsed,
201
+ issues: []
202
+ };
203
+ }
204
+ function writeContextSourceManifestCache(projectRoot, manifest) {
205
+ const validation = (0, schema_1.validateSchema)(source_manifest_1.CONTEXT_SOURCE_MANIFEST_SCHEMA_ID, manifest);
206
+ if (!validation.ok) {
207
+ throw new Error(`Context source manifest does not match ${source_manifest_1.CONTEXT_SOURCE_MANIFEST_SCHEMA_ID}: ${validation.issues[0]?.path ?? '$'}`);
208
+ }
209
+ (0, fs_1.atomicWriteTextFile)(projectRoot, source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH, `${JSON.stringify(manifest, null, 2)}\n`);
210
+ }
211
+ function listContextGraphExtractorShardKeys() {
212
+ return [...CONTEXT_GRAPH_EXTRACTOR_SHARD_KEYS];
213
+ }
214
+ function contextGraphExtractorShardCachePath(extractorKey) {
215
+ return CONTEXT_GRAPH_EXTRACTOR_SHARD_CACHE_PATHS[extractorKey];
216
+ }
217
+ function contextGraphCoreShardCachePath() {
218
+ return CONTEXT_GRAPH_CORE_CACHE_PATH;
219
+ }
220
+ function contextCodeIndexShardCachePath() {
221
+ return CONTEXT_CODE_INDEX_CACHE_PATH;
222
+ }
223
+ function buildContextGraphExtractorShardRecord(input) {
224
+ return buildContextCacheRecord({
225
+ projection: contextGraphExtractorShardProjection(input.extractorKey),
226
+ projectionSchemaVersion: CONTEXT_GRAPH_EXTRACTOR_SHARD_SCHEMA_VERSION,
227
+ manifest: input.manifest,
228
+ extractorKeys: [input.extractorKey],
229
+ payload: input.result,
230
+ createdAt: input.createdAt,
231
+ degraded: input.result.issues.some((issue) => issue.severity === 'warning' || issue.severity === 'error')
232
+ });
233
+ }
234
+ function writeContextGraphExtractorShard(input) {
235
+ const record = buildContextGraphExtractorShardRecord(input);
236
+ writeContextCacheRecord(input.projectRoot, contextGraphExtractorShardCachePath(input.extractorKey), record);
237
+ return record;
238
+ }
239
+ function readContextGraphExtractorShard(input) {
240
+ const cachePath = contextGraphExtractorShardCachePath(input.extractorKey);
241
+ const read = readContextCacheRecord(input.projectRoot, cachePath);
242
+ if (!read.ok || !read.record) {
243
+ return {
244
+ ok: false,
245
+ hit: false,
246
+ status: read.status === 'valid' ? 'schema-mismatch' : read.status,
247
+ extractorKey: input.extractorKey,
248
+ path: read.path,
249
+ issues: read.issues
250
+ };
251
+ }
252
+ const expected = buildContextCacheRecord({
253
+ projection: contextGraphExtractorShardProjection(input.extractorKey),
254
+ projectionSchemaVersion: CONTEXT_GRAPH_EXTRACTOR_SHARD_SCHEMA_VERSION,
255
+ manifest: input.manifest,
256
+ extractorKeys: [input.extractorKey],
257
+ payload: read.record.payload,
258
+ createdAt: read.record.createdAt
259
+ });
260
+ const shapeIssue = validateGraphExtractionPayload(read.record.payload, input.extractorKey, read.path);
261
+ if (shapeIssue) {
262
+ return {
263
+ ok: false,
264
+ hit: false,
265
+ status: 'schema-mismatch',
266
+ extractorKey: input.extractorKey,
267
+ path: read.path,
268
+ record: read.record,
269
+ issues: [shapeIssue]
270
+ };
271
+ }
272
+ if (read.record.projection !== expected.projection
273
+ || read.record.projectionSchemaVersion !== expected.projectionSchemaVersion
274
+ || read.record.sourceSubsetHash !== expected.sourceSubsetHash
275
+ || !sameRecord(read.record.extractorVersions, expected.extractorVersions)) {
276
+ return {
277
+ ok: false,
278
+ hit: false,
279
+ status: 'stale',
280
+ extractorKey: input.extractorKey,
281
+ path: read.path,
282
+ record: read.record,
283
+ issues: [contextCacheIssue('warning', 'CONTEXT_CACHE_STALE', `Context graph extractor shard ${input.extractorKey} is stale.`, read.path)]
284
+ };
285
+ }
286
+ return {
287
+ ok: true,
288
+ hit: true,
289
+ status: 'fresh',
290
+ extractorKey: input.extractorKey,
291
+ path: read.path,
292
+ record: read.record,
293
+ result: read.record.payload,
294
+ issues: []
295
+ };
296
+ }
297
+ function collectContextGraphExtractorShards(input) {
298
+ const results = {};
299
+ const reads = CONTEXT_GRAPH_EXTRACTOR_SHARD_KEYS.map((extractorKey) => readContextGraphExtractorShard({ projectRoot: input.projectRoot, manifest: input.manifest, extractorKey }));
300
+ for (const read of reads) {
301
+ if (read.hit && read.result)
302
+ results[read.extractorKey] = read.result;
303
+ }
304
+ const hitReads = reads.filter((read) => read.hit);
305
+ const staleReads = reads.filter((read) => read.status === 'stale');
306
+ const cache = {
307
+ used: hitReads.length > 0,
308
+ hit: hitReads.length > 0,
309
+ mode: 'extractor-shards',
310
+ manifestHash: input.manifest.manifestHash,
311
+ readShardCount: reads.length,
312
+ hitShardCount: hitReads.length,
313
+ missShardCount: reads.filter((read) => read.status === 'missing').length,
314
+ staleShardCount: staleReads.length,
315
+ corruptShardCount: reads.filter((read) => read.status === 'corrupt').length,
316
+ schemaMismatchShardCount: reads.filter((read) => read.status === 'schema-mismatch').length,
317
+ shardPaths: reads.map((read) => read.path).sort(),
318
+ staleExtractorKeys: staleReads.map((read) => read.extractorKey).sort(),
319
+ ...(hitReads[0]?.record ? { createdAt: hitReads[0].record.createdAt, cachePath: hitReads[0].path } : {})
320
+ };
321
+ return {
322
+ results,
323
+ cache,
324
+ issues: reads.flatMap((read) => read.issues)
325
+ };
326
+ }
327
+ function buildContextGraphCoreExtractionResult(projectRoot) {
328
+ return (0, extractor_contract_1.mergeGraphExtractionResults)([
329
+ (0, task_extractors_1.extractTaskCapsules)(projectRoot),
330
+ (0, task_extractors_1.extractTaskBoard)(projectRoot),
331
+ (0, registry_extractors_1.extractDocsRegistry)(projectRoot),
332
+ (0, registry_extractors_1.extractCommandRegistry)(projectRoot),
333
+ (0, document_extractors_1.extractManagedSections)(projectRoot),
334
+ (0, document_extractors_1.extractProjectState)(projectRoot),
335
+ (0, document_extractors_1.extractDecisions)(projectRoot),
336
+ (0, document_extractors_1.extractAgentHandoff)(projectRoot),
337
+ (0, evidence_extractors_1.extractEvidence)(projectRoot),
338
+ (0, release_extractors_1.extractReleaseReadiness)(projectRoot)
339
+ ]);
340
+ }
341
+ function buildContextGraphCoreShardRecord(input) {
342
+ return buildContextCacheRecord({
343
+ projection: CONTEXT_GRAPH_CORE_PROJECTION,
344
+ projectionSchemaVersion: CONTEXT_GRAPH_EXTRACTOR_SHARD_SCHEMA_VERSION,
345
+ manifest: input.manifest,
346
+ extractorKeys: [...CONTEXT_GRAPH_CORE_EXTRACTOR_KEYS],
347
+ payload: input.result,
348
+ createdAt: input.createdAt,
349
+ degraded: input.result.issues.some((issue) => issue.severity === 'warning' || issue.severity === 'error')
350
+ });
351
+ }
352
+ function writeContextGraphCoreShard(input) {
353
+ const record = buildContextGraphCoreShardRecord(input);
354
+ writeContextCacheRecord(input.projectRoot, CONTEXT_GRAPH_CORE_CACHE_PATH, record);
355
+ return record;
356
+ }
357
+ function readContextGraphCoreShard(input) {
358
+ const read = readContextCacheRecord(input.projectRoot, CONTEXT_GRAPH_CORE_CACHE_PATH);
359
+ if (!read.ok || !read.record) {
360
+ return {
361
+ ok: false,
362
+ hit: false,
363
+ status: read.status === 'valid' ? 'schema-mismatch' : read.status,
364
+ path: read.path,
365
+ issues: read.issues
366
+ };
367
+ }
368
+ const expected = buildContextCacheRecord({
369
+ projection: CONTEXT_GRAPH_CORE_PROJECTION,
370
+ projectionSchemaVersion: CONTEXT_GRAPH_EXTRACTOR_SHARD_SCHEMA_VERSION,
371
+ manifest: input.manifest,
372
+ extractorKeys: [...CONTEXT_GRAPH_CORE_EXTRACTOR_KEYS],
373
+ payload: read.record.payload,
374
+ createdAt: read.record.createdAt
375
+ });
376
+ const shapeIssue = validateGraphCorePayload(read.record.payload, read.path);
377
+ if (shapeIssue) {
378
+ return {
379
+ ok: false,
380
+ hit: false,
381
+ status: 'schema-mismatch',
382
+ path: read.path,
383
+ record: read.record,
384
+ issues: [shapeIssue]
385
+ };
386
+ }
387
+ if (read.record.projection !== expected.projection
388
+ || read.record.projectionSchemaVersion !== expected.projectionSchemaVersion
389
+ || read.record.sourceSubsetHash !== expected.sourceSubsetHash
390
+ || !sameRecord(read.record.extractorVersions, expected.extractorVersions)) {
391
+ return {
392
+ ok: false,
393
+ hit: false,
394
+ status: 'stale',
395
+ path: read.path,
396
+ record: read.record,
397
+ issues: [contextCacheIssue('warning', 'CONTEXT_CACHE_STALE', 'Context graph core shard is stale.', read.path)]
398
+ };
399
+ }
400
+ return {
401
+ ok: true,
402
+ hit: true,
403
+ status: 'fresh',
404
+ path: read.path,
405
+ record: read.record,
406
+ result: read.record.payload,
407
+ issues: []
408
+ };
409
+ }
410
+ function buildContextCodeIndexShardRecord(input) {
411
+ return buildContextCacheRecord({
412
+ projection: CONTEXT_CODE_INDEX_PROJECTION,
413
+ projectionSchemaVersion: code_index_1.CODE_INDEX_SCHEMA_ID,
414
+ manifest: input.manifest,
415
+ extractorKeys: ['codeIndex'],
416
+ payload: input.result,
417
+ createdAt: input.createdAt,
418
+ degraded: input.result.summary.degraded || input.result.issues.some((issue) => issue.severity === 'warning' || issue.severity === 'error')
419
+ });
420
+ }
421
+ function writeContextCodeIndexShard(input) {
422
+ const record = buildContextCodeIndexShardRecord(input);
423
+ writeContextCacheRecord(input.projectRoot, CONTEXT_CODE_INDEX_CACHE_PATH, record);
424
+ return record;
425
+ }
426
+ function readContextCodeIndexShard(input) {
427
+ const read = readContextCacheRecord(input.projectRoot, CONTEXT_CODE_INDEX_CACHE_PATH);
428
+ if (!read.ok || !read.record) {
429
+ return {
430
+ ok: false,
431
+ hit: false,
432
+ status: read.status === 'valid' ? 'schema-mismatch' : read.status,
433
+ path: read.path,
434
+ issues: read.issues
435
+ };
436
+ }
437
+ const expected = buildContextCacheRecord({
438
+ projection: CONTEXT_CODE_INDEX_PROJECTION,
439
+ projectionSchemaVersion: code_index_1.CODE_INDEX_SCHEMA_ID,
440
+ manifest: input.manifest,
441
+ extractorKeys: ['codeIndex'],
442
+ payload: read.record.payload,
443
+ createdAt: read.record.createdAt
444
+ });
445
+ const shapeIssue = validateCodeIndexPayload(read.record.payload, read.path);
446
+ if (shapeIssue) {
447
+ return {
448
+ ok: false,
449
+ hit: false,
450
+ status: 'schema-mismatch',
451
+ path: read.path,
452
+ record: read.record,
453
+ issues: [shapeIssue]
454
+ };
455
+ }
456
+ if (read.record.projection !== expected.projection
457
+ || read.record.projectionSchemaVersion !== expected.projectionSchemaVersion
458
+ || read.record.sourceSubsetHash !== expected.sourceSubsetHash
459
+ || !sameRecord(read.record.extractorVersions, expected.extractorVersions)) {
460
+ return {
461
+ ok: false,
462
+ hit: false,
463
+ status: 'stale',
464
+ path: read.path,
465
+ record: read.record,
466
+ issues: [contextCacheIssue('warning', 'CONTEXT_CACHE_STALE', 'Context code-index shard is stale.', read.path)]
467
+ };
468
+ }
469
+ return {
470
+ ok: true,
471
+ hit: true,
472
+ status: 'fresh',
473
+ path: read.path,
474
+ record: read.record,
475
+ result: withCodeIndexCacheHitMetadata(read.record.payload, read.record, read.path),
476
+ issues: []
477
+ };
478
+ }
479
+ function createContextCacheStatusReport(input) {
480
+ const analysis = createSourceManifestCacheAnalysis({
481
+ projectRoot: input.projectRoot,
482
+ generatedAt: input.generatedAt,
483
+ generatedByCommand: exports.CONTEXT_CACHE_STATUS_COMMAND
484
+ });
485
+ const shardItems = createContextGraphExtractorShardWarmItems({
486
+ projectRoot: input.projectRoot,
487
+ manifest: analysis.currentManifest,
488
+ execute: false,
489
+ generatedAt: analysis.generatedAt
490
+ });
491
+ return {
492
+ schemaVersion: exports.CONTEXT_CACHE_STATUS_SCHEMA_ID,
493
+ command: exports.CONTEXT_CACHE_STATUS_COMMAND,
494
+ ok: true,
495
+ generatedAt: analysis.generatedAt,
496
+ projectRoot: input.projectRoot,
497
+ cacheRoot: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT,
498
+ readOnly: true,
499
+ summary: {
500
+ mode: analysis.cacheFresh ? 'hit' : analysis.cached.status === 'missing' ? 'miss' : analysis.cached.status === 'valid' ? 'stale' : 'corrupt',
501
+ cachePresent: analysis.cached.status !== 'missing',
502
+ cacheFresh: analysis.cacheFresh,
503
+ fastPath: analysis.fastPath,
504
+ degraded: analysis.degraded,
505
+ staleExtractorKeys: analysis.staleExtractorKeys
506
+ },
507
+ manifest: createManifestReportSection(analysis),
508
+ diagnostics: createContextCacheDiagnostics({
509
+ projectRoot: input.projectRoot,
510
+ analysis,
511
+ shardItems
512
+ }),
513
+ issues: analysis.issues
514
+ };
515
+ }
516
+ function createContextCacheWarmReport(input) {
517
+ const execute = input.execute ?? false;
518
+ const analysis = createSourceManifestCacheAnalysis({
519
+ projectRoot: input.projectRoot,
520
+ generatedAt: input.generatedAt,
521
+ generatedByCommand: exports.CONTEXT_CACHE_WARM_COMMAND
522
+ });
523
+ const shardItems = createContextGraphExtractorShardWarmItems({
524
+ projectRoot: input.projectRoot,
525
+ manifest: analysis.currentManifest,
526
+ execute,
527
+ generatedAt: analysis.generatedAt
528
+ });
529
+ const shardWritePlanned = shardItems.some((item) => item.planned);
530
+ const shardWriteExecuted = shardItems.some((item) => item.executed);
531
+ const writePlanned = !analysis.cacheFresh;
532
+ let writeExecuted = false;
533
+ if (execute && writePlanned) {
534
+ writeContextSourceManifestCache(input.projectRoot, analysis.currentManifest);
535
+ writeExecuted = true;
536
+ }
537
+ return {
538
+ schemaVersion: exports.CONTEXT_CACHE_WARM_SCHEMA_ID,
539
+ command: exports.CONTEXT_CACHE_WARM_COMMAND,
540
+ ok: true,
541
+ generatedAt: analysis.generatedAt,
542
+ projectRoot: input.projectRoot,
543
+ cacheRoot: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT,
544
+ mode: execute ? 'execute' : 'dry-run',
545
+ summary: {
546
+ cacheMode: analysis.cacheFresh ? 'fresh' : analysis.cached.status === 'missing' ? 'miss' : analysis.cached.status === 'valid' ? 'stale' : 'corrupt',
547
+ cachePresent: analysis.cached.status !== 'missing',
548
+ cacheFresh: analysis.cacheFresh,
549
+ fastPath: analysis.fastPath,
550
+ writePlanned: writePlanned || shardWritePlanned,
551
+ writeExecuted: writeExecuted || shardWriteExecuted,
552
+ shardWritePlanned,
553
+ shardWriteExecuted,
554
+ shardHitCount: shardItems.filter((item) => item.beforeStatus === 'fresh').length,
555
+ shardMissCount: shardItems.filter((item) => item.beforeStatus === 'missing').length,
556
+ shardStaleCount: shardItems.filter((item) => item.beforeStatus === 'stale').length,
557
+ shardCorruptCount: shardItems.filter((item) => item.beforeStatus === 'corrupt').length,
558
+ shardSchemaMismatchCount: shardItems.filter((item) => item.beforeStatus === 'schema-mismatch').length,
559
+ degraded: analysis.degraded,
560
+ staleExtractorKeys: analysis.staleExtractorKeys
561
+ },
562
+ manifest: createManifestReportSection(analysis),
563
+ write: {
564
+ policy: execute ? 'execute' : 'dry-run',
565
+ planned: writePlanned,
566
+ executed: writeExecuted,
567
+ cachePath: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH,
568
+ beforeStatus: analysis.cached.status,
569
+ ...(analysis.cached.manifest ? { beforeManifestHash: analysis.cached.manifest.manifestHash } : {}),
570
+ afterManifestHash: analysis.currentManifest.manifestHash,
571
+ ...(!writePlanned ? { skippedReason: 'cache-fresh' } : {})
572
+ },
573
+ shards: {
574
+ planned: shardWritePlanned,
575
+ executed: shardWriteExecuted,
576
+ items: shardItems
577
+ },
578
+ diagnostics: createContextCacheDiagnostics({
579
+ projectRoot: input.projectRoot,
580
+ analysis,
581
+ shardItems
582
+ }),
583
+ issues: analysis.issues
584
+ };
585
+ }
586
+ function createContextCacheDiagnostics(input) {
587
+ const shardSummary = {
588
+ total: input.shardItems.length,
589
+ fresh: input.shardItems.filter((item) => item.beforeStatus === 'fresh').length,
590
+ missing: input.shardItems.filter((item) => item.beforeStatus === 'missing').length,
591
+ stale: input.shardItems.filter((item) => item.beforeStatus === 'stale').length,
592
+ corrupt: input.shardItems.filter((item) => item.beforeStatus === 'corrupt').length,
593
+ schemaMismatch: input.shardItems.filter((item) => item.beforeStatus === 'schema-mismatch').length,
594
+ planned: input.shardItems.filter((item) => item.planned).length,
595
+ plannedShardKeys: input.shardItems.filter((item) => item.planned).map((item) => item.extractorKey).sort()
596
+ };
597
+ const manifestCorrupt = input.analysis.cached.status === 'corrupt' || input.analysis.cached.status === 'schema-mismatch';
598
+ const hasPartialShards = input.analysis.cacheFresh && shardSummary.planned > 0;
599
+ const state = manifestCorrupt
600
+ ? 'corrupt'
601
+ : input.analysis.cached.status === 'missing'
602
+ ? 'missing'
603
+ : input.analysis.cacheFresh
604
+ ? hasPartialShards ? 'partial' : 'fresh'
605
+ : 'stale';
606
+ const needsWarm = state !== 'fresh';
607
+ return {
608
+ state,
609
+ operatorSummary: contextCacheOperatorSummary(state, shardSummary.planned),
610
+ ...(needsWarm ? {
611
+ recommendedCommand: 'hadara context cache warm --execute --json',
612
+ recommendedCommandArgs: ['context', 'cache', 'warm', '--execute', '--json']
613
+ } : {}),
614
+ slowPath: {
615
+ mountedWorkspace: input.projectRoot.startsWith('/mnt/'),
616
+ fullManifestBuilt: input.analysis.fastPath !== 'hit',
617
+ fastPath: input.analysis.fastPath,
618
+ ...(input.analysis.fastPathReason ? { reason: input.analysis.fastPathReason } : {}),
619
+ ...(input.analysis.fastPathStrategy ? { strategy: input.analysis.fastPathStrategy } : {})
620
+ },
621
+ manifestChanges: {
622
+ addedPathCount: input.analysis.comparison.addedPaths.length,
623
+ removedPathCount: input.analysis.comparison.removedPaths.length,
624
+ changedPathCount: input.analysis.comparison.changedPaths.length,
625
+ unchangedSourceCount: input.analysis.comparison.unchangedPaths.length,
626
+ staleExtractorKeys: input.analysis.staleExtractorKeys
627
+ },
628
+ shardSummary
629
+ };
630
+ }
631
+ function contextCacheOperatorSummary(state, plannedShardCount) {
632
+ if (state === 'fresh')
633
+ return 'Context cache is fresh and all warm shards are available.';
634
+ if (state === 'missing')
635
+ return 'Context cache is missing; run an explicit warm execute to populate source manifest and shards.';
636
+ if (state === 'corrupt')
637
+ return 'Context cache has corrupt or schema-mismatched records; run an explicit warm execute to repair it.';
638
+ if (state === 'partial')
639
+ return `Source manifest is fresh but ${plannedShardCount} warm shard(s) are missing, stale, corrupt, or schema-mismatched.`;
640
+ return 'Context cache is stale relative to current project source metadata; run an explicit warm execute to refresh it.';
641
+ }
642
+ function createContextGraphExtractorShardWarmItems(input) {
643
+ const extractorItems = CONTEXT_GRAPH_EXTRACTOR_SHARD_KEYS.map((extractorKey) => {
644
+ const read = readContextGraphExtractorShard({
645
+ projectRoot: input.projectRoot,
646
+ manifest: input.manifest,
647
+ extractorKey
648
+ });
649
+ const planned = !read.hit;
650
+ let afterCacheKey;
651
+ if (input.execute && planned) {
652
+ const result = CONTEXT_GRAPH_EXTRACTOR_SHARD_EXTRACTORS[extractorKey](input.projectRoot);
653
+ const record = writeContextGraphExtractorShard({
654
+ projectRoot: input.projectRoot,
655
+ manifest: input.manifest,
656
+ extractorKey,
657
+ result,
658
+ createdAt: input.generatedAt
659
+ });
660
+ afterCacheKey = record.cacheKey;
661
+ }
662
+ return {
663
+ extractorKey,
664
+ cachePath: read.path,
665
+ beforeStatus: read.status,
666
+ planned,
667
+ executed: input.execute && planned,
668
+ ...(read.record ? { beforeCacheKey: read.record.cacheKey } : {}),
669
+ ...(afterCacheKey ? { afterCacheKey } : {}),
670
+ ...(!planned ? { skippedReason: 'cache-fresh' } : {})
671
+ };
672
+ });
673
+ const graphCoreRead = readContextGraphCoreShard({
674
+ projectRoot: input.projectRoot,
675
+ manifest: input.manifest
676
+ });
677
+ const graphCorePlanned = !graphCoreRead.hit;
678
+ let graphCoreAfterCacheKey;
679
+ if (input.execute && graphCorePlanned) {
680
+ const result = buildContextGraphCoreExtractionResult(input.projectRoot);
681
+ const record = writeContextGraphCoreShard({
682
+ projectRoot: input.projectRoot,
683
+ manifest: input.manifest,
684
+ result,
685
+ createdAt: input.generatedAt
686
+ });
687
+ graphCoreAfterCacheKey = record.cacheKey;
688
+ }
689
+ return [
690
+ ...extractorItems,
691
+ {
692
+ extractorKey: 'graphCore',
693
+ cachePath: graphCoreRead.path,
694
+ beforeStatus: graphCoreRead.status,
695
+ planned: graphCorePlanned,
696
+ executed: input.execute && graphCorePlanned,
697
+ ...(graphCoreRead.record ? { beforeCacheKey: graphCoreRead.record.cacheKey } : {}),
698
+ ...(graphCoreAfterCacheKey ? { afterCacheKey: graphCoreAfterCacheKey } : {}),
699
+ ...(!graphCorePlanned ? { skippedReason: 'cache-fresh' } : {})
700
+ },
701
+ createContextCodeIndexShardWarmItem(input)
702
+ ];
703
+ }
704
+ function createContextCodeIndexShardWarmItem(input) {
705
+ const read = readContextCodeIndexShard({
706
+ projectRoot: input.projectRoot,
707
+ manifest: input.manifest
708
+ });
709
+ const planned = !read.hit;
710
+ let afterCacheKey;
711
+ let result;
712
+ if (input.execute && planned) {
713
+ result = (0, code_index_1.buildCodeIndexReport)({
714
+ projectRoot: input.projectRoot,
715
+ generatedAt: input.generatedAt,
716
+ sourceEntries: codeIndexSourceEntries(input.manifest),
717
+ fileSummaryCache: {
718
+ mode: 'read-write',
719
+ createdAt: input.generatedAt,
720
+ extractorVersion: input.manifest.extractorVersions.codeIndex
721
+ }
722
+ });
723
+ const record = writeContextCodeIndexShard({
724
+ projectRoot: input.projectRoot,
725
+ manifest: input.manifest,
726
+ result,
727
+ createdAt: input.generatedAt
728
+ });
729
+ afterCacheKey = record.cacheKey;
730
+ }
731
+ return {
732
+ extractorKey: 'codeIndex',
733
+ cachePath: read.path,
734
+ beforeStatus: read.status,
735
+ planned,
736
+ executed: input.execute && planned,
737
+ ...(read.record ? { beforeCacheKey: read.record.cacheKey } : {}),
738
+ ...(afterCacheKey ? { afterCacheKey } : {}),
739
+ ...(result?.cache?.readFileSummaryCount === undefined ? {} : {
740
+ readFileSummaryCount: result.cache.readFileSummaryCount,
741
+ reusedFileSummaryCount: result.cache.reusedFileSummaryCount,
742
+ recomputedFileSummaryCount: result.cache.recomputedFileSummaryCount,
743
+ missingFileSummaryCount: result.cache.missingFileSummaryCount,
744
+ staleFileSummaryCount: result.cache.staleFileSummaryCount,
745
+ corruptFileSummaryCount: result.cache.corruptFileSummaryCount,
746
+ schemaMismatchFileSummaryCount: result.cache.schemaMismatchFileSummaryCount
747
+ }),
748
+ ...(!planned ? { skippedReason: 'cache-fresh' } : {})
749
+ };
750
+ }
751
+ function codeIndexSourceEntries(manifest) {
752
+ return manifest.sources
753
+ .filter((source) => source.extractorKeys.includes('codeIndex'))
754
+ .map((source) => ({
755
+ path: source.path,
756
+ sizeBytes: source.sizeBytes,
757
+ ...(source.mtimeMs === undefined ? {} : { mtimeMs: source.mtimeMs }),
758
+ ...(source.contentHash ? { contentHash: source.contentHash } : {}),
759
+ metadataHash: source.metadataHash,
760
+ extractorKeys: source.extractorKeys
761
+ }));
762
+ }
763
+ function withCodeIndexCacheHitMetadata(report, record, cachePath) {
764
+ return {
765
+ ...report,
766
+ cache: {
767
+ used: true,
768
+ hit: true,
769
+ mode: 'code-index',
770
+ manifestHash: record.manifestHash,
771
+ createdAt: record.createdAt,
772
+ cachePath
773
+ }
774
+ };
775
+ }
776
+ function createSourceManifestCacheAnalysis(input) {
777
+ const generatedAt = input.generatedAt ?? new Date().toISOString();
778
+ const cached = readContextSourceManifestCache(input.projectRoot);
779
+ const fastFreshness = cached.status === 'valid' && cached.manifest
780
+ ? (0, source_manifest_1.checkContextSourceManifestFastFreshness)(input.projectRoot, cached.manifest)
781
+ : undefined;
782
+ if (cached.status === 'valid' && cached.manifest) {
783
+ if (fastFreshness?.ok) {
784
+ return {
785
+ generatedAt,
786
+ cached,
787
+ currentManifest: cached.manifest,
788
+ comparison: {
789
+ addedPaths: [],
790
+ removedPaths: [],
791
+ changedPaths: [],
792
+ unchangedPaths: cached.manifest.sources.map((source) => source.path).sort(),
793
+ staleExtractorKeys: []
794
+ },
795
+ staleExtractorKeys: [],
796
+ cacheFresh: true,
797
+ fastPath: 'hit',
798
+ fastPathReason: fastFreshness.reason,
799
+ fastPathStrategy: fastFreshness.strategy,
800
+ degraded: cached.manifest.issues.some((issue) => issue.severity === 'warning' || issue.severity === 'error'),
801
+ issues: [...cached.issues]
802
+ };
803
+ }
804
+ }
805
+ const currentManifest = (0, source_manifest_1.buildContextSourceManifest)({
806
+ projectRoot: input.projectRoot,
807
+ generatedAt,
808
+ generatedByCommand: input.generatedByCommand,
809
+ ...(cached.status === 'valid' && cached.manifest ? { previousManifest: cached.manifest } : {})
810
+ });
811
+ const comparison = cached.manifest
812
+ ? (0, source_manifest_1.compareContextSourceManifests)(cached.manifest, currentManifest)
813
+ : { addedPaths: [], removedPaths: [], changedPaths: [], unchangedPaths: [], staleExtractorKeys: [] };
814
+ const staleExtractorKeys = comparison.staleExtractorKeys;
815
+ const cacheFresh = cached.status === 'valid'
816
+ && cached.manifest?.manifestHash === currentManifest.manifestHash
817
+ && staleExtractorKeys.length === 0
818
+ && comparison.addedPaths.length === 0
819
+ && comparison.removedPaths.length === 0
820
+ && comparison.changedPaths.length === 0;
821
+ const degraded = currentManifest.issues.some((issue) => issue.severity === 'warning' || issue.severity === 'error');
822
+ const issues = [...cached.issues];
823
+ if (cached.status === 'valid' && !cacheFresh) {
824
+ issues.push(contextCacheIssue('warning', 'CONTEXT_CACHE_STALE', 'Source manifest cache is stale relative to current project source metadata.', source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH));
825
+ }
826
+ return {
827
+ generatedAt,
828
+ cached,
829
+ currentManifest,
830
+ comparison,
831
+ staleExtractorKeys,
832
+ cacheFresh,
833
+ fastPath: fastFreshness ? 'miss' : 'skipped',
834
+ ...(fastFreshness ? {
835
+ fastPathReason: fastFreshness.reason,
836
+ ...(fastFreshness.strategy ? { fastPathStrategy: fastFreshness.strategy } : {})
837
+ } : {}),
838
+ degraded,
839
+ issues
840
+ };
841
+ }
842
+ function createManifestReportSection(analysis) {
843
+ return {
844
+ cachePath: source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_PATH,
845
+ status: analysis.cacheFresh ? 'fresh' : analysis.cached.status === 'valid' ? 'stale' : analysis.cached.status,
846
+ currentManifestHash: analysis.currentManifest.manifestHash,
847
+ currentSourceCount: analysis.currentManifest.summary.sourceCount,
848
+ currentSkippedSourceCount: analysis.currentManifest.summary.skippedSourceCount,
849
+ ...(analysis.cached.manifest ? {
850
+ cachedManifestHash: analysis.cached.manifest.manifestHash,
851
+ cachedGeneratedAt: analysis.cached.manifest.generatedAt,
852
+ cachedSourceCount: analysis.cached.manifest.summary.sourceCount
853
+ } : {}),
854
+ addedPaths: analysis.comparison.addedPaths,
855
+ removedPaths: analysis.comparison.removedPaths,
856
+ changedPaths: analysis.comparison.changedPaths,
857
+ unchangedSourceCount: analysis.comparison.unchangedPaths.length,
858
+ staleExtractorKeys: analysis.staleExtractorKeys,
859
+ fastPath: analysis.fastPath,
860
+ ...(analysis.fastPathReason ? { fastPathReason: analysis.fastPathReason } : {}),
861
+ ...(analysis.fastPathStrategy ? { fastPathStrategy: analysis.fastPathStrategy } : {})
862
+ };
863
+ }
864
+ function normalizeContextCachePath(inputPath) {
865
+ const normalizedPath = (0, extractor_contract_1.normalizeContextGraphPath)(inputPath);
866
+ if (node_path_1.default.isAbsolute(inputPath) || normalizedPath.startsWith('../') || normalizedPath === '..') {
867
+ throw new Error(`Context cache path must be project-relative: ${inputPath}`);
868
+ }
869
+ if (normalizedPath !== source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT && !normalizedPath.startsWith(`${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT}/`)) {
870
+ throw new Error(`Context cache path must stay under ${source_manifest_1.CONTEXT_SOURCE_MANIFEST_CACHE_ROOT}: ${inputPath}`);
871
+ }
872
+ return normalizedPath;
873
+ }
874
+ function contextGraphExtractorShardProjection(extractorKey) {
875
+ return `${CONTEXT_GRAPH_EXTRACTOR_SHARD_PROJECTION_PREFIX}.${extractorKey}`;
876
+ }
877
+ function validateGraphExtractionPayload(payload, extractorKey, cachePath) {
878
+ if (!payload
879
+ || typeof payload !== 'object'
880
+ || !payload.source
881
+ || payload.source.extractor !== extractorKey
882
+ || !Array.isArray(payload.source.paths)
883
+ || typeof payload.source.sourceHash !== 'string'
884
+ || !Array.isArray(payload.nodes)
885
+ || !Array.isArray(payload.edges)
886
+ || !Array.isArray(payload.issues)) {
887
+ return contextCacheIssue('warning', 'CONTEXT_CACHE_SCHEMA_MISMATCH', `Context graph extractor shard ${extractorKey} payload is not a valid extraction result.`, cachePath);
888
+ }
889
+ return undefined;
890
+ }
891
+ function validateGraphCorePayload(payload, cachePath) {
892
+ if (!payload
893
+ || typeof payload !== 'object'
894
+ || !payload.source
895
+ || payload.source.extractor !== 'mergeGraphExtractionResults'
896
+ || !Array.isArray(payload.source.paths)
897
+ || typeof payload.source.sourceHash !== 'string'
898
+ || !Array.isArray(payload.nodes)
899
+ || !Array.isArray(payload.edges)
900
+ || !Array.isArray(payload.issues)) {
901
+ return contextCacheIssue('warning', 'CONTEXT_CACHE_SCHEMA_MISMATCH', 'Context graph core shard payload is not a valid merged extraction result.', cachePath);
902
+ }
903
+ return undefined;
904
+ }
905
+ function validateCodeIndexPayload(payload, cachePath) {
906
+ const validation = (0, schema_1.validateSchema)(code_index_1.CODE_INDEX_SCHEMA_ID, payload);
907
+ if (!validation.ok) {
908
+ return contextCacheIssue('warning', 'CONTEXT_CACHE_SCHEMA_MISMATCH', `Context code-index shard payload is not a valid ${code_index_1.CODE_INDEX_SCHEMA_ID} report.`, cachePath);
909
+ }
910
+ return undefined;
911
+ }
912
+ function sameRecord(left, right) {
913
+ const leftKeys = Object.keys(left).sort();
914
+ const rightKeys = Object.keys(right).sort();
915
+ return leftKeys.length === rightKeys.length
916
+ && leftKeys.every((key, index) => key === rightKeys[index] && left[key] === right[key]);
917
+ }
918
+ function contextCacheIssue(severity, code, message, issuePath) {
919
+ return {
920
+ severity,
921
+ code,
922
+ message,
923
+ ...(issuePath ? { path: issuePath } : {})
924
+ };
925
+ }