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.
- package/README.md +38 -23
- package/dist/cli/context.js +110 -0
- package/dist/cli/help.js +6 -7
- package/dist/cli/init.js +56 -58
- package/dist/cli/main.js +12 -0
- package/dist/cli/session.js +37 -0
- package/dist/cli/task.js +52 -0
- package/dist/context/code-graph-extractor.js +123 -0
- package/dist/context/code-index.js +1154 -0
- package/dist/context/context-cache-store.js +925 -0
- package/dist/context/context-graph-builder.js +341 -0
- package/dist/context/context-graph.js +42 -0
- package/dist/context/context-pack.js +457 -0
- package/dist/context/context-slice-boundary.js +37 -0
- package/dist/context/context-slice.js +487 -0
- package/dist/context/document-extractors.js +343 -0
- package/dist/context/evidence-extractors.js +179 -0
- package/dist/context/extractor-contract.js +166 -0
- package/dist/context/registry-extractors.js +177 -0
- package/dist/context/release-extractors.js +175 -0
- package/dist/context/session-start.js +297 -0
- package/dist/context/source-manifest.js +566 -0
- package/dist/context/state-projection.js +196 -0
- package/dist/context/task-extractors.js +168 -0
- package/dist/core/schema.js +26 -0
- package/dist/harness/validate.js +7 -8
- package/dist/schemas/code-index.schema.json +173 -0
- package/dist/schemas/context-cache-record.schema.json +56 -0
- package/dist/schemas/context-cache-status.schema.json +167 -0
- package/dist/schemas/context-cache-warm.schema.json +222 -0
- package/dist/schemas/context-graph.schema.json +286 -0
- package/dist/schemas/context-pack.schema.json +246 -0
- package/dist/schemas/context-slice.schema.json +94 -0
- package/dist/schemas/context-source-manifest.schema.json +147 -0
- package/dist/schemas/schema-index.json +91 -0
- package/dist/schemas/session-start.schema.json +158 -0
- package/dist/schemas/task-close-repair-plan.schema.json +67 -0
- package/dist/schemas/task-context.schema.json +125 -0
- package/dist/schemas/task-finalize.schema.json +98 -0
- package/dist/schemas/task-lifecycle.schema.json +84 -0
- package/dist/services/capability-registry.js +266 -9
- package/dist/services/lifecycle-guide.js +23 -29
- package/dist/services/protocol-consistency.js +6 -8
- package/dist/task/acceptance.js +171 -0
- package/dist/task/task-close-repair-plan.js +190 -0
- package/dist/task/task-close.js +34 -35
- package/dist/task/task-finalize.js +377 -0
- package/dist/task/task-lifecycle.js +210 -0
- package/dist/task/task-next.js +10 -1
- package/dist/task/task-ready.js +4 -0
- 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
|
+
}
|