scene-capability-engine 3.6.3 → 3.6.5
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/CHANGELOG.md +57 -0
- package/README.md +9 -0
- package/README.zh.md +9 -0
- package/bin/scene-capability-engine.js +31 -1
- package/docs/command-reference.md +56 -0
- package/lib/collab/agent-registry.js +38 -1
- package/lib/commands/session.js +60 -2
- package/lib/commands/state.js +210 -0
- package/lib/gitignore/gitignore-detector.js +2 -1
- package/lib/gitignore/layered-rules-template.js +1 -0
- package/lib/runtime/project-timeline.js +202 -17
- package/lib/runtime/session-store.js +167 -14
- package/lib/state/sce-state-store.js +1280 -0
- package/lib/state/state-migration-manager.js +850 -0
- package/lib/steering/compliance-error-reporter.js +6 -0
- package/lib/steering/steering-compliance-checker.js +43 -8
- package/package.json +2 -1
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const { getSceStateStore } = require('./sce-state-store');
|
|
4
|
+
const { ProjectTimelineStore } = require('../runtime/project-timeline');
|
|
5
|
+
const { SessionStore } = require('../runtime/session-store');
|
|
6
|
+
|
|
7
|
+
const COMPONENT_AGENT_REGISTRY = 'collab.agent-registry';
|
|
8
|
+
const COMPONENT_TIMELINE_INDEX = 'runtime.timeline-index';
|
|
9
|
+
const COMPONENT_SCENE_SESSION_INDEX = 'runtime.scene-session-index';
|
|
10
|
+
const COMPONENT_ERRORBOOK_ENTRY_INDEX = 'errorbook.entry-index';
|
|
11
|
+
const COMPONENT_ERRORBOOK_INCIDENT_INDEX = 'errorbook.incident-index';
|
|
12
|
+
const COMPONENT_SPEC_SCENE_OVERRIDES = 'governance.spec-scene-overrides';
|
|
13
|
+
const COMPONENT_SPEC_SCENE_INDEX = 'governance.scene-index';
|
|
14
|
+
const DEFAULT_STATE_EXPORT_PATH = '.sce/reports/state-migration/state-export.latest.json';
|
|
15
|
+
|
|
16
|
+
const COMPONENT_DEFINITIONS = Object.freeze([
|
|
17
|
+
{
|
|
18
|
+
id: COMPONENT_AGENT_REGISTRY,
|
|
19
|
+
source_path: '.sce/config/agent-registry.json'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: COMPONENT_TIMELINE_INDEX,
|
|
23
|
+
source_path: '.sce/timeline/index.json'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: COMPONENT_SCENE_SESSION_INDEX,
|
|
27
|
+
source_path: '.sce/session-governance/scene-index.json'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: COMPONENT_ERRORBOOK_ENTRY_INDEX,
|
|
31
|
+
source_path: '.sce/errorbook/index.json'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: COMPONENT_ERRORBOOK_INCIDENT_INDEX,
|
|
35
|
+
source_path: '.sce/errorbook/staging/index.json'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: COMPONENT_SPEC_SCENE_OVERRIDES,
|
|
39
|
+
source_path: '.sce/spec-governance/spec-scene-overrides.json'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: COMPONENT_SPEC_SCENE_INDEX,
|
|
43
|
+
source_path: '.sce/spec-governance/scene-index.json'
|
|
44
|
+
}
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
function normalizeString(value) {
|
|
48
|
+
if (typeof value !== 'string') {
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
return value.trim();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeBoolean(value, fallback = false) {
|
|
55
|
+
if (typeof value === 'boolean') {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
const normalized = normalizeString(`${value}`).toLowerCase();
|
|
59
|
+
if (!normalized) {
|
|
60
|
+
return fallback;
|
|
61
|
+
}
|
|
62
|
+
if (['1', 'true', 'yes', 'on'].includes(normalized)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (['0', 'false', 'no', 'off'].includes(normalized)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return fallback;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeInteger(value, fallback = 0) {
|
|
72
|
+
const parsed = Number.parseInt(`${value}`, 10);
|
|
73
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
74
|
+
return fallback;
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function nowIso() {
|
|
80
|
+
return new Date().toISOString();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function normalizeCount(value) {
|
|
84
|
+
if (!Number.isFinite(Number(value))) {
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
return Math.max(0, Number.parseInt(`${value}`, 10) || 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function parseComponentIds(value) {
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
return Array.from(new Set(value.map((item) => normalizeString(item)).filter(Boolean)));
|
|
93
|
+
}
|
|
94
|
+
const normalized = normalizeString(value);
|
|
95
|
+
if (!normalized) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
return Array.from(new Set(normalized.split(/[,\s]+/g).map((item) => normalizeString(item)).filter(Boolean)));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resolveDefinitionsById(componentIds = []) {
|
|
102
|
+
if (!Array.isArray(componentIds) || componentIds.length === 0) {
|
|
103
|
+
return [...COMPONENT_DEFINITIONS];
|
|
104
|
+
}
|
|
105
|
+
const selected = new Set(componentIds);
|
|
106
|
+
return COMPONENT_DEFINITIONS.filter((item) => selected.has(item.id));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function mapAgentRegistryPayload(payload = {}) {
|
|
110
|
+
if (!payload || typeof payload !== 'object' || !payload.agents || typeof payload.agents !== 'object') {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return Object.values(payload.agents)
|
|
114
|
+
.map((item) => ({
|
|
115
|
+
agent_id: normalizeString(item && item.agentId),
|
|
116
|
+
machine_id: normalizeString(item && item.machineId),
|
|
117
|
+
instance_index: normalizeInteger(item && item.instanceIndex, 0),
|
|
118
|
+
hostname: normalizeString(item && item.hostname),
|
|
119
|
+
registered_at: normalizeString(item && item.registeredAt),
|
|
120
|
+
last_heartbeat: normalizeString(item && item.lastHeartbeat),
|
|
121
|
+
status: normalizeString(item && item.status),
|
|
122
|
+
current_task: item && item.currentTask && typeof item.currentTask === 'object'
|
|
123
|
+
? item.currentTask
|
|
124
|
+
: null
|
|
125
|
+
}))
|
|
126
|
+
.filter((item) => item.agent_id);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function mapTimelineIndexPayload(payload = {}) {
|
|
130
|
+
if (!payload || typeof payload !== 'object' || !Array.isArray(payload.snapshots)) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
return payload.snapshots
|
|
134
|
+
.map((item) => ({
|
|
135
|
+
snapshot_id: normalizeString(item && item.snapshot_id),
|
|
136
|
+
created_at: normalizeString(item && item.created_at),
|
|
137
|
+
trigger: normalizeString(item && item.trigger),
|
|
138
|
+
event: normalizeString(item && item.event),
|
|
139
|
+
summary: normalizeString(item && item.summary),
|
|
140
|
+
scene_id: normalizeString(item && item.scene_id),
|
|
141
|
+
session_id: normalizeString(item && item.session_id),
|
|
142
|
+
command: normalizeString(item && item.command),
|
|
143
|
+
file_count: normalizeInteger(item && item.file_count, 0),
|
|
144
|
+
total_bytes: normalizeInteger(item && item.total_bytes, 0),
|
|
145
|
+
snapshot_path: normalizeString(item && (item.path || item.snapshot_path)),
|
|
146
|
+
git: item && item.git && typeof item.git === 'object' ? item.git : {}
|
|
147
|
+
}))
|
|
148
|
+
.filter((item) => item.snapshot_id);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function mapSceneSessionIndexPayload(payload = {}) {
|
|
152
|
+
if (!payload || typeof payload !== 'object' || !payload.scenes || typeof payload.scenes !== 'object') {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const records = [];
|
|
157
|
+
for (const [sceneId, sceneRecord] of Object.entries(payload.scenes)) {
|
|
158
|
+
const normalizedSceneId = normalizeString(sceneId);
|
|
159
|
+
if (!normalizedSceneId) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const cycles = Array.isArray(sceneRecord && sceneRecord.cycles) ? sceneRecord.cycles : [];
|
|
163
|
+
for (const cycle of cycles) {
|
|
164
|
+
const cycleNo = normalizeInteger(cycle && cycle.cycle, 0);
|
|
165
|
+
const sessionId = normalizeString(cycle && cycle.session_id);
|
|
166
|
+
if (!cycleNo || !sessionId) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
records.push({
|
|
170
|
+
scene_id: normalizedSceneId,
|
|
171
|
+
cycle: cycleNo,
|
|
172
|
+
session_id: sessionId,
|
|
173
|
+
status: normalizeString(cycle && cycle.status),
|
|
174
|
+
started_at: normalizeString(cycle && cycle.started_at),
|
|
175
|
+
completed_at: normalizeString(cycle && cycle.completed_at)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return records;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function mapErrorbookEntryIndexPayload(payload = {}) {
|
|
183
|
+
if (!payload || typeof payload !== 'object' || !Array.isArray(payload.entries)) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
return payload.entries
|
|
187
|
+
.map((item) => ({
|
|
188
|
+
entry_id: normalizeString(item && (item.id || item.entry_id)),
|
|
189
|
+
fingerprint: normalizeString(item && item.fingerprint),
|
|
190
|
+
title: normalizeString(item && item.title),
|
|
191
|
+
status: normalizeString(item && item.status),
|
|
192
|
+
quality_score: normalizeInteger(item && item.quality_score, 0),
|
|
193
|
+
tags: Array.isArray(item && item.tags)
|
|
194
|
+
? item.tags.map((token) => normalizeString(token)).filter(Boolean)
|
|
195
|
+
: [],
|
|
196
|
+
ontology_tags: Array.isArray(item && item.ontology_tags)
|
|
197
|
+
? item.ontology_tags.map((token) => normalizeString(token)).filter(Boolean)
|
|
198
|
+
: [],
|
|
199
|
+
temporary_mitigation_active: item && item.temporary_mitigation_active === true,
|
|
200
|
+
temporary_mitigation_deadline_at: normalizeString(item && item.temporary_mitigation_deadline_at),
|
|
201
|
+
occurrences: normalizeInteger(item && item.occurrences, 0),
|
|
202
|
+
created_at: normalizeString(item && item.created_at),
|
|
203
|
+
updated_at: normalizeString(item && item.updated_at)
|
|
204
|
+
}))
|
|
205
|
+
.filter((item) => item.entry_id);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function mapErrorbookIncidentIndexPayload(payload = {}) {
|
|
209
|
+
if (!payload || typeof payload !== 'object' || !Array.isArray(payload.incidents)) {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
return payload.incidents
|
|
213
|
+
.map((item) => ({
|
|
214
|
+
incident_id: normalizeString(item && (item.id || item.incident_id)),
|
|
215
|
+
fingerprint: normalizeString(item && item.fingerprint),
|
|
216
|
+
title: normalizeString(item && item.title),
|
|
217
|
+
symptom: normalizeString(item && item.symptom),
|
|
218
|
+
state: normalizeString(item && item.state),
|
|
219
|
+
attempt_count: normalizeInteger(item && item.attempt_count, 0),
|
|
220
|
+
created_at: normalizeString(item && item.created_at),
|
|
221
|
+
updated_at: normalizeString(item && item.updated_at),
|
|
222
|
+
last_attempt_at: normalizeString(item && item.last_attempt_at),
|
|
223
|
+
resolved_at: normalizeString(item && item.resolved_at),
|
|
224
|
+
linked_entry_id: normalizeString(item && item.linked_entry_id)
|
|
225
|
+
}))
|
|
226
|
+
.filter((item) => item.incident_id);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function mapSpecSceneOverridesPayload(payload = {}) {
|
|
230
|
+
if (!payload || typeof payload !== 'object' || !payload.mappings || typeof payload.mappings !== 'object') {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
return Object.entries(payload.mappings)
|
|
234
|
+
.map(([specId, mapping]) => {
|
|
235
|
+
if (typeof mapping === 'string') {
|
|
236
|
+
return {
|
|
237
|
+
spec_id: normalizeString(specId),
|
|
238
|
+
scene_id: normalizeString(mapping),
|
|
239
|
+
source: 'override',
|
|
240
|
+
rule_id: '',
|
|
241
|
+
updated_at: normalizeString(payload && payload.updated_at)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
spec_id: normalizeString(specId),
|
|
246
|
+
scene_id: normalizeString(mapping && mapping.scene_id),
|
|
247
|
+
source: normalizeString(mapping && mapping.source) || 'override',
|
|
248
|
+
rule_id: normalizeString(mapping && mapping.rule_id),
|
|
249
|
+
updated_at: normalizeString(mapping && mapping.updated_at) || normalizeString(payload && payload.updated_at)
|
|
250
|
+
};
|
|
251
|
+
})
|
|
252
|
+
.filter((item) => item.spec_id && item.scene_id);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function mapSpecSceneIndexPayload(payload = {}) {
|
|
256
|
+
if (!payload || typeof payload !== 'object' || !payload.scenes || typeof payload.scenes !== 'object') {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
const generatedAt = normalizeString(payload.generated_at) || normalizeString(payload.updated_at);
|
|
260
|
+
const sceneFilter = normalizeString(payload.scene_filter);
|
|
261
|
+
return Object.entries(payload.scenes)
|
|
262
|
+
.map(([sceneId, scene]) => ({
|
|
263
|
+
scene_id: normalizeString(sceneId),
|
|
264
|
+
total_specs: normalizeInteger(scene && scene.total_specs, 0),
|
|
265
|
+
active_specs: normalizeInteger(scene && scene.active_specs, 0),
|
|
266
|
+
completed_specs: normalizeInteger(scene && scene.completed_specs, 0),
|
|
267
|
+
stale_specs: normalizeInteger(scene && scene.stale_specs, 0),
|
|
268
|
+
spec_ids: Array.isArray(scene && scene.spec_ids)
|
|
269
|
+
? scene.spec_ids.map((token) => normalizeString(token)).filter(Boolean)
|
|
270
|
+
: [],
|
|
271
|
+
active_spec_ids: Array.isArray(scene && scene.active_spec_ids)
|
|
272
|
+
? scene.active_spec_ids.map((token) => normalizeString(token)).filter(Boolean)
|
|
273
|
+
: [],
|
|
274
|
+
stale_spec_ids: Array.isArray(scene && scene.stale_spec_ids)
|
|
275
|
+
? scene.stale_spec_ids.map((token) => normalizeString(token)).filter(Boolean)
|
|
276
|
+
: [],
|
|
277
|
+
generated_at: generatedAt,
|
|
278
|
+
scene_filter: sceneFilter
|
|
279
|
+
}))
|
|
280
|
+
.filter((item) => item.scene_id);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function readJsonSource(absolutePath, fileSystem = fs) {
|
|
284
|
+
if (!await fileSystem.pathExists(absolutePath)) {
|
|
285
|
+
return {
|
|
286
|
+
exists: false,
|
|
287
|
+
payload: null,
|
|
288
|
+
parse_error: null
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const payload = await fileSystem.readJson(absolutePath);
|
|
294
|
+
return {
|
|
295
|
+
exists: true,
|
|
296
|
+
payload,
|
|
297
|
+
parse_error: null
|
|
298
|
+
};
|
|
299
|
+
} catch (error) {
|
|
300
|
+
return {
|
|
301
|
+
exists: true,
|
|
302
|
+
payload: null,
|
|
303
|
+
parse_error: error.message
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function readComponentSnapshot(component = {}, projectPath = process.cwd(), fileSystem = fs) {
|
|
309
|
+
const absolutePath = path.join(projectPath, component.source_path);
|
|
310
|
+
const source = await readJsonSource(absolutePath, fileSystem);
|
|
311
|
+
|
|
312
|
+
let records = [];
|
|
313
|
+
if (!source.parse_error && source.payload) {
|
|
314
|
+
if (component.id === COMPONENT_AGENT_REGISTRY) {
|
|
315
|
+
records = mapAgentRegistryPayload(source.payload);
|
|
316
|
+
} else if (component.id === COMPONENT_TIMELINE_INDEX) {
|
|
317
|
+
records = mapTimelineIndexPayload(source.payload);
|
|
318
|
+
} else if (component.id === COMPONENT_SCENE_SESSION_INDEX) {
|
|
319
|
+
records = mapSceneSessionIndexPayload(source.payload);
|
|
320
|
+
} else if (component.id === COMPONENT_ERRORBOOK_ENTRY_INDEX) {
|
|
321
|
+
records = mapErrorbookEntryIndexPayload(source.payload);
|
|
322
|
+
} else if (component.id === COMPONENT_ERRORBOOK_INCIDENT_INDEX) {
|
|
323
|
+
records = mapErrorbookIncidentIndexPayload(source.payload);
|
|
324
|
+
} else if (component.id === COMPONENT_SPEC_SCENE_OVERRIDES) {
|
|
325
|
+
records = mapSpecSceneOverridesPayload(source.payload);
|
|
326
|
+
} else if (component.id === COMPONENT_SPEC_SCENE_INDEX) {
|
|
327
|
+
records = mapSpecSceneIndexPayload(source.payload);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
id: component.id,
|
|
333
|
+
source_path: component.source_path,
|
|
334
|
+
exists: source.exists,
|
|
335
|
+
parse_error: source.parse_error,
|
|
336
|
+
source_record_count: records.length,
|
|
337
|
+
records
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function buildComponentPlan(componentSnapshot = {}) {
|
|
342
|
+
const exists = componentSnapshot.exists === true;
|
|
343
|
+
const parseError = normalizeString(componentSnapshot.parse_error);
|
|
344
|
+
const sourceCount = normalizeInteger(componentSnapshot.source_record_count, 0);
|
|
345
|
+
let status = 'ready';
|
|
346
|
+
if (!exists) {
|
|
347
|
+
status = 'missing';
|
|
348
|
+
} else if (parseError) {
|
|
349
|
+
status = 'parse-error';
|
|
350
|
+
} else if (sourceCount <= 0) {
|
|
351
|
+
status = 'empty';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
id: componentSnapshot.id,
|
|
356
|
+
source_path: componentSnapshot.source_path,
|
|
357
|
+
exists,
|
|
358
|
+
parse_error: parseError || null,
|
|
359
|
+
source_record_count: sourceCount,
|
|
360
|
+
status
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function buildStateMigrationPlan(options = {}, dependencies = {}) {
|
|
365
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
366
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
367
|
+
const env = dependencies.env || process.env;
|
|
368
|
+
const componentIds = parseComponentIds(options.components || options.component || options.componentIds);
|
|
369
|
+
const definitions = resolveDefinitionsById(componentIds);
|
|
370
|
+
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, { fileSystem, env });
|
|
371
|
+
|
|
372
|
+
const snapshots = [];
|
|
373
|
+
for (const definition of definitions) {
|
|
374
|
+
const snapshot = await readComponentSnapshot(definition, projectPath, fileSystem);
|
|
375
|
+
snapshots.push(snapshot);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const components = snapshots.map((snapshot) => buildComponentPlan(snapshot));
|
|
379
|
+
const totalSourceRecords = components.reduce((sum, item) => sum + normalizeInteger(item.source_record_count, 0), 0);
|
|
380
|
+
const readyComponents = components.filter((item) => item.status === 'ready').length;
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
mode: 'state-plan',
|
|
384
|
+
success: true,
|
|
385
|
+
generated_at: nowIso(),
|
|
386
|
+
store_path: stateStore.getStoreRelativePath ? stateStore.getStoreRelativePath() : null,
|
|
387
|
+
sqlite: {
|
|
388
|
+
configured: stateStore.isSqliteConfigured ? stateStore.isSqliteConfigured() : false,
|
|
389
|
+
available: stateStore.isSqliteAvailable ? stateStore.isSqliteAvailable() : false
|
|
390
|
+
},
|
|
391
|
+
components,
|
|
392
|
+
summary: {
|
|
393
|
+
total_components: components.length,
|
|
394
|
+
ready_components: readyComponents,
|
|
395
|
+
total_source_records: totalSourceRecords
|
|
396
|
+
},
|
|
397
|
+
snapshots
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function writeComponentToStateStore(componentSnapshot = {}, stateStore, componentId = '') {
|
|
402
|
+
if (componentId === COMPONENT_AGENT_REGISTRY) {
|
|
403
|
+
return stateStore.upsertAgentRuntimeRecords(componentSnapshot.records, {
|
|
404
|
+
source: componentId
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if (componentId === COMPONENT_TIMELINE_INDEX) {
|
|
408
|
+
return stateStore.upsertTimelineSnapshotIndex(componentSnapshot.records, {
|
|
409
|
+
source: componentId
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (componentId === COMPONENT_SCENE_SESSION_INDEX) {
|
|
413
|
+
return stateStore.upsertSceneSessionCycles(componentSnapshot.records, {
|
|
414
|
+
source: componentId
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (componentId === COMPONENT_ERRORBOOK_ENTRY_INDEX) {
|
|
418
|
+
return stateStore.upsertErrorbookEntryIndexRecords(componentSnapshot.records, {
|
|
419
|
+
source: componentId
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
if (componentId === COMPONENT_ERRORBOOK_INCIDENT_INDEX) {
|
|
423
|
+
return stateStore.upsertErrorbookIncidentIndexRecords(componentSnapshot.records, {
|
|
424
|
+
source: componentId
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
if (componentId === COMPONENT_SPEC_SCENE_OVERRIDES) {
|
|
428
|
+
return stateStore.upsertGovernanceSpecSceneOverrideRecords(componentSnapshot.records, {
|
|
429
|
+
source: componentId
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
if (componentId === COMPONENT_SPEC_SCENE_INDEX) {
|
|
433
|
+
return stateStore.upsertGovernanceSceneIndexRecords(componentSnapshot.records, {
|
|
434
|
+
source: componentId
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async function runStateMigration(options = {}, dependencies = {}) {
|
|
441
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
442
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
443
|
+
const env = dependencies.env || process.env;
|
|
444
|
+
const apply = normalizeBoolean(options.apply, false);
|
|
445
|
+
const requestedIds = parseComponentIds(options.components || options.component || options.componentIds);
|
|
446
|
+
const migrateAll = options.all === true || requestedIds.length === 0;
|
|
447
|
+
const selectedIds = migrateAll
|
|
448
|
+
? COMPONENT_DEFINITIONS.map((item) => item.id)
|
|
449
|
+
: requestedIds;
|
|
450
|
+
|
|
451
|
+
const plan = await buildStateMigrationPlan({
|
|
452
|
+
componentIds: selectedIds
|
|
453
|
+
}, {
|
|
454
|
+
projectPath,
|
|
455
|
+
fileSystem,
|
|
456
|
+
env
|
|
457
|
+
});
|
|
458
|
+
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, { fileSystem, env });
|
|
459
|
+
|
|
460
|
+
const operations = [];
|
|
461
|
+
let migratedComponents = 0;
|
|
462
|
+
let migratedRecords = 0;
|
|
463
|
+
|
|
464
|
+
for (const snapshot of plan.snapshots) {
|
|
465
|
+
const componentPlan = buildComponentPlan(snapshot);
|
|
466
|
+
const op = {
|
|
467
|
+
component_id: snapshot.id,
|
|
468
|
+
source_path: snapshot.source_path,
|
|
469
|
+
source_record_count: snapshot.source_record_count,
|
|
470
|
+
status: componentPlan.status,
|
|
471
|
+
applied: false,
|
|
472
|
+
result: null
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
if (componentPlan.status !== 'ready') {
|
|
476
|
+
operations.push(op);
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (!apply) {
|
|
481
|
+
op.status = 'planned';
|
|
482
|
+
operations.push(op);
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const startedAt = nowIso();
|
|
487
|
+
const writeResult = await writeComponentToStateStore(snapshot, stateStore, snapshot.id);
|
|
488
|
+
if (!writeResult) {
|
|
489
|
+
op.status = 'failed';
|
|
490
|
+
op.error = 'SQLite state backend unavailable for migration write';
|
|
491
|
+
operations.push(op);
|
|
492
|
+
await stateStore.appendStateMigrationRecord({
|
|
493
|
+
component_id: snapshot.id,
|
|
494
|
+
source_path: snapshot.source_path,
|
|
495
|
+
mode: 'apply',
|
|
496
|
+
status: 'failed',
|
|
497
|
+
metrics: {
|
|
498
|
+
source_record_count: snapshot.source_record_count
|
|
499
|
+
},
|
|
500
|
+
detail: {
|
|
501
|
+
error: op.error
|
|
502
|
+
},
|
|
503
|
+
started_at: startedAt,
|
|
504
|
+
completed_at: nowIso()
|
|
505
|
+
});
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
op.applied = true;
|
|
510
|
+
op.status = 'migrated';
|
|
511
|
+
op.result = writeResult;
|
|
512
|
+
operations.push(op);
|
|
513
|
+
migratedComponents += 1;
|
|
514
|
+
migratedRecords += normalizeInteger(writeResult.written, 0);
|
|
515
|
+
|
|
516
|
+
await stateStore.appendStateMigrationRecord({
|
|
517
|
+
component_id: snapshot.id,
|
|
518
|
+
source_path: snapshot.source_path,
|
|
519
|
+
mode: 'apply',
|
|
520
|
+
status: 'completed',
|
|
521
|
+
metrics: {
|
|
522
|
+
source_record_count: snapshot.source_record_count,
|
|
523
|
+
written: normalizeInteger(writeResult.written, 0),
|
|
524
|
+
target_total: normalizeInteger(writeResult.total, 0)
|
|
525
|
+
},
|
|
526
|
+
detail: {
|
|
527
|
+
component_id: snapshot.id
|
|
528
|
+
},
|
|
529
|
+
started_at: startedAt,
|
|
530
|
+
completed_at: nowIso()
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
mode: 'state-migrate',
|
|
536
|
+
success: operations.every((item) => item.status !== 'failed'),
|
|
537
|
+
apply,
|
|
538
|
+
generated_at: nowIso(),
|
|
539
|
+
store_path: stateStore.getStoreRelativePath ? stateStore.getStoreRelativePath() : null,
|
|
540
|
+
operations,
|
|
541
|
+
summary: {
|
|
542
|
+
selected_components: selectedIds.length,
|
|
543
|
+
migrated_components: migratedComponents,
|
|
544
|
+
migrated_records: migratedRecords
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async function runStateDoctor(options = {}, dependencies = {}) {
|
|
550
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
551
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
552
|
+
const env = dependencies.env || process.env;
|
|
553
|
+
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, { fileSystem, env });
|
|
554
|
+
const plan = await buildStateMigrationPlan({}, { projectPath, fileSystem, env, stateStore });
|
|
555
|
+
|
|
556
|
+
const [
|
|
557
|
+
agentRows,
|
|
558
|
+
timelineRows,
|
|
559
|
+
sessionRows,
|
|
560
|
+
errorbookEntryRows,
|
|
561
|
+
errorbookIncidentRows,
|
|
562
|
+
governanceOverrideRows,
|
|
563
|
+
governanceSceneRows,
|
|
564
|
+
migrations
|
|
565
|
+
] = await Promise.all([
|
|
566
|
+
stateStore.listAgentRuntimeRecords({ limit: 0 }),
|
|
567
|
+
stateStore.listTimelineSnapshotIndex({ limit: 0 }),
|
|
568
|
+
stateStore.listSceneSessionCycles({ limit: 0 }),
|
|
569
|
+
stateStore.listErrorbookEntryIndexRecords({ limit: 0 }),
|
|
570
|
+
stateStore.listErrorbookIncidentIndexRecords({ limit: 0 }),
|
|
571
|
+
stateStore.listGovernanceSpecSceneOverrideRecords({ limit: 0 }),
|
|
572
|
+
stateStore.listGovernanceSceneIndexRecords({ limit: 0 }),
|
|
573
|
+
stateStore.listStateMigrations({ limit: 20 })
|
|
574
|
+
]);
|
|
575
|
+
|
|
576
|
+
const targetCounts = new Map([
|
|
577
|
+
[COMPONENT_AGENT_REGISTRY, Array.isArray(agentRows) ? agentRows.length : 0],
|
|
578
|
+
[COMPONENT_TIMELINE_INDEX, Array.isArray(timelineRows) ? timelineRows.length : 0],
|
|
579
|
+
[COMPONENT_SCENE_SESSION_INDEX, Array.isArray(sessionRows) ? sessionRows.length : 0],
|
|
580
|
+
[COMPONENT_ERRORBOOK_ENTRY_INDEX, Array.isArray(errorbookEntryRows) ? errorbookEntryRows.length : 0],
|
|
581
|
+
[COMPONENT_ERRORBOOK_INCIDENT_INDEX, Array.isArray(errorbookIncidentRows) ? errorbookIncidentRows.length : 0],
|
|
582
|
+
[COMPONENT_SPEC_SCENE_OVERRIDES, Array.isArray(governanceOverrideRows) ? governanceOverrideRows.length : 0],
|
|
583
|
+
[COMPONENT_SPEC_SCENE_INDEX, Array.isArray(governanceSceneRows) ? governanceSceneRows.length : 0]
|
|
584
|
+
]);
|
|
585
|
+
|
|
586
|
+
const checks = plan.components.map((component) => {
|
|
587
|
+
const sourceCount = normalizeInteger(component.source_record_count, 0);
|
|
588
|
+
const targetCount = normalizeInteger(targetCounts.get(component.id), 0);
|
|
589
|
+
let syncStatus = 'synced';
|
|
590
|
+
if (component.status === 'missing') {
|
|
591
|
+
syncStatus = targetCount > 0 ? 'sqlite-only' : 'missing-source';
|
|
592
|
+
} else if (component.status === 'parse-error') {
|
|
593
|
+
syncStatus = 'source-parse-error';
|
|
594
|
+
} else if (sourceCount === 0 && targetCount === 0) {
|
|
595
|
+
syncStatus = 'empty';
|
|
596
|
+
} else if (targetCount < sourceCount) {
|
|
597
|
+
syncStatus = 'pending-migration';
|
|
598
|
+
}
|
|
599
|
+
return {
|
|
600
|
+
id: component.id,
|
|
601
|
+
source_path: component.source_path,
|
|
602
|
+
source_record_count: sourceCount,
|
|
603
|
+
sqlite_record_count: targetCount,
|
|
604
|
+
source_status: component.status,
|
|
605
|
+
sync_status: syncStatus
|
|
606
|
+
};
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
const runtime = await collectRuntimeDiagnostics({
|
|
610
|
+
projectPath,
|
|
611
|
+
fileSystem,
|
|
612
|
+
env,
|
|
613
|
+
stateStore
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
const blocking = [];
|
|
617
|
+
if (!plan.sqlite.available) {
|
|
618
|
+
blocking.push('sqlite-unavailable');
|
|
619
|
+
}
|
|
620
|
+
if (checks.some((item) => item.sync_status === 'source-parse-error')) {
|
|
621
|
+
blocking.push('source-parse-error');
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const alerts = checks
|
|
625
|
+
.filter((item) => item.sync_status === 'pending-migration')
|
|
626
|
+
.map((item) => `pending migration: ${item.id}`);
|
|
627
|
+
|
|
628
|
+
if (runtime.timeline && runtime.timeline.consistency && runtime.timeline.consistency.status === 'pending-sync') {
|
|
629
|
+
alerts.push('runtime timeline index pending-sync');
|
|
630
|
+
}
|
|
631
|
+
if (runtime.timeline && runtime.timeline.consistency && runtime.timeline.consistency.status === 'sqlite-ahead') {
|
|
632
|
+
alerts.push('runtime timeline index sqlite-ahead');
|
|
633
|
+
}
|
|
634
|
+
if (runtime.scene_session && runtime.scene_session.consistency && runtime.scene_session.consistency.status === 'pending-sync') {
|
|
635
|
+
alerts.push('runtime scene-session index pending-sync');
|
|
636
|
+
}
|
|
637
|
+
if (runtime.scene_session && runtime.scene_session.consistency && runtime.scene_session.consistency.status === 'sqlite-ahead') {
|
|
638
|
+
alerts.push('runtime scene-session index sqlite-ahead');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const summary = summarizeDoctorChecks(checks, alerts, blocking);
|
|
642
|
+
|
|
643
|
+
return {
|
|
644
|
+
mode: 'state-doctor',
|
|
645
|
+
success: blocking.length === 0,
|
|
646
|
+
generated_at: nowIso(),
|
|
647
|
+
store_path: plan.store_path,
|
|
648
|
+
sqlite: plan.sqlite,
|
|
649
|
+
summary,
|
|
650
|
+
runtime,
|
|
651
|
+
checks,
|
|
652
|
+
migrations: Array.isArray(migrations) ? migrations : [],
|
|
653
|
+
blocking,
|
|
654
|
+
alerts
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function summarizeDoctorChecks(checks = [], alerts = [], blocking = []) {
|
|
659
|
+
const normalizedChecks = Array.isArray(checks) ? checks : [];
|
|
660
|
+
const sourceRecords = normalizedChecks.reduce((sum, item) => sum + normalizeCount(item.source_record_count), 0);
|
|
661
|
+
const sqliteRecords = normalizedChecks.reduce((sum, item) => sum + normalizeCount(item.sqlite_record_count), 0);
|
|
662
|
+
const pendingComponents = normalizedChecks.filter((item) => item.sync_status === 'pending-migration').length;
|
|
663
|
+
const syncedComponents = normalizedChecks.filter((item) => item.sync_status === 'synced').length;
|
|
664
|
+
const sqliteOnlyComponents = normalizedChecks.filter((item) => item.sync_status === 'sqlite-only').length;
|
|
665
|
+
const missingSourceComponents = normalizedChecks.filter((item) => item.sync_status === 'missing-source').length;
|
|
666
|
+
const driftRecords = normalizedChecks.reduce((sum, item) => {
|
|
667
|
+
const source = normalizeCount(item.source_record_count);
|
|
668
|
+
const target = normalizeCount(item.sqlite_record_count);
|
|
669
|
+
return sum + Math.abs(source - target);
|
|
670
|
+
}, 0);
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
total_components: normalizedChecks.length,
|
|
674
|
+
synced_components: syncedComponents,
|
|
675
|
+
pending_components: pendingComponents,
|
|
676
|
+
sqlite_only_components: sqliteOnlyComponents,
|
|
677
|
+
missing_source_components: missingSourceComponents,
|
|
678
|
+
total_source_records: sourceRecords,
|
|
679
|
+
total_sqlite_records: sqliteRecords,
|
|
680
|
+
total_record_drift: driftRecords,
|
|
681
|
+
blocking_count: Array.isArray(blocking) ? blocking.length : 0,
|
|
682
|
+
alert_count: Array.isArray(alerts) ? alerts.length : 0
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async function collectRuntimeDiagnostics(dependencies = {}) {
|
|
687
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
688
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
689
|
+
const env = dependencies.env || process.env;
|
|
690
|
+
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, {
|
|
691
|
+
fileSystem,
|
|
692
|
+
env
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
const runtime = {
|
|
696
|
+
timeline: {
|
|
697
|
+
read_source: 'unavailable',
|
|
698
|
+
consistency: {
|
|
699
|
+
status: 'unavailable',
|
|
700
|
+
file_index_count: 0,
|
|
701
|
+
sqlite_index_count: null
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
scene_session: {
|
|
705
|
+
read_preference: 'file',
|
|
706
|
+
consistency: {
|
|
707
|
+
status: 'unavailable',
|
|
708
|
+
file_index_count: 0,
|
|
709
|
+
sqlite_index_count: null
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
try {
|
|
715
|
+
const timelineStore = new ProjectTimelineStore(projectPath, fileSystem, {
|
|
716
|
+
env,
|
|
717
|
+
stateStore,
|
|
718
|
+
preferSqliteReads: true
|
|
719
|
+
});
|
|
720
|
+
const timelineView = await timelineStore.listSnapshots({ limit: 1 });
|
|
721
|
+
runtime.timeline = {
|
|
722
|
+
read_source: normalizeString(timelineView.read_source) || 'file',
|
|
723
|
+
consistency: {
|
|
724
|
+
status: normalizeString(timelineView && timelineView.consistency && timelineView.consistency.status) || 'unknown',
|
|
725
|
+
file_index_count: normalizeCount(timelineView && timelineView.consistency && timelineView.consistency.file_index_count),
|
|
726
|
+
sqlite_index_count: Number.isFinite(Number(timelineView && timelineView.consistency && timelineView.consistency.sqlite_index_count))
|
|
727
|
+
? Number.parseInt(`${timelineView.consistency.sqlite_index_count}`, 10)
|
|
728
|
+
: null
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
} catch (_error) {
|
|
732
|
+
runtime.timeline = {
|
|
733
|
+
read_source: 'unavailable',
|
|
734
|
+
consistency: {
|
|
735
|
+
status: 'unavailable',
|
|
736
|
+
file_index_count: 0,
|
|
737
|
+
sqlite_index_count: null
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
try {
|
|
743
|
+
const sessionStore = new SessionStore(projectPath, fileSystem, {
|
|
744
|
+
env,
|
|
745
|
+
stateStore,
|
|
746
|
+
preferSqliteSceneReads: true
|
|
747
|
+
});
|
|
748
|
+
const sceneIndex = await sessionStore.getSceneIndexDiagnostics();
|
|
749
|
+
runtime.scene_session = {
|
|
750
|
+
read_preference: normalizeString(sceneIndex.read_preference) || 'file',
|
|
751
|
+
consistency: {
|
|
752
|
+
status: normalizeString(sceneIndex.status) || 'unknown',
|
|
753
|
+
file_index_count: normalizeCount(sceneIndex.file_scene_count),
|
|
754
|
+
sqlite_index_count: Number.isFinite(Number(sceneIndex.sqlite_scene_count))
|
|
755
|
+
? Number.parseInt(`${sceneIndex.sqlite_scene_count}`, 10)
|
|
756
|
+
: null
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
} catch (_error) {
|
|
760
|
+
runtime.scene_session = {
|
|
761
|
+
read_preference: 'file',
|
|
762
|
+
consistency: {
|
|
763
|
+
status: 'unavailable',
|
|
764
|
+
file_index_count: 0,
|
|
765
|
+
sqlite_index_count: null
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
return runtime;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
async function runStateExport(options = {}, dependencies = {}) {
|
|
774
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
775
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
776
|
+
const env = dependencies.env || process.env;
|
|
777
|
+
const outPathRaw = normalizeString(options.out) || DEFAULT_STATE_EXPORT_PATH;
|
|
778
|
+
const outPath = path.isAbsolute(outPathRaw)
|
|
779
|
+
? outPathRaw
|
|
780
|
+
: path.join(projectPath, outPathRaw);
|
|
781
|
+
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, { fileSystem, env });
|
|
782
|
+
|
|
783
|
+
const [
|
|
784
|
+
agentRows,
|
|
785
|
+
timelineRows,
|
|
786
|
+
sessionRows,
|
|
787
|
+
errorbookEntryRows,
|
|
788
|
+
errorbookIncidentRows,
|
|
789
|
+
governanceOverrideRows,
|
|
790
|
+
governanceSceneRows,
|
|
791
|
+
migrations
|
|
792
|
+
] = await Promise.all([
|
|
793
|
+
stateStore.listAgentRuntimeRecords({ limit: 0 }),
|
|
794
|
+
stateStore.listTimelineSnapshotIndex({ limit: 0 }),
|
|
795
|
+
stateStore.listSceneSessionCycles({ limit: 0 }),
|
|
796
|
+
stateStore.listErrorbookEntryIndexRecords({ limit: 0 }),
|
|
797
|
+
stateStore.listErrorbookIncidentIndexRecords({ limit: 0 }),
|
|
798
|
+
stateStore.listGovernanceSpecSceneOverrideRecords({ limit: 0 }),
|
|
799
|
+
stateStore.listGovernanceSceneIndexRecords({ limit: 0 }),
|
|
800
|
+
stateStore.listStateMigrations({ limit: 0 })
|
|
801
|
+
]);
|
|
802
|
+
|
|
803
|
+
const payload = {
|
|
804
|
+
mode: 'state-export',
|
|
805
|
+
success: true,
|
|
806
|
+
exported_at: nowIso(),
|
|
807
|
+
store_path: stateStore.getStoreRelativePath ? stateStore.getStoreRelativePath() : null,
|
|
808
|
+
tables: {
|
|
809
|
+
agent_runtime_registry: Array.isArray(agentRows) ? agentRows : [],
|
|
810
|
+
timeline_snapshot_registry: Array.isArray(timelineRows) ? timelineRows : [],
|
|
811
|
+
scene_session_cycle_registry: Array.isArray(sessionRows) ? sessionRows : [],
|
|
812
|
+
errorbook_entry_index_registry: Array.isArray(errorbookEntryRows) ? errorbookEntryRows : [],
|
|
813
|
+
errorbook_incident_index_registry: Array.isArray(errorbookIncidentRows) ? errorbookIncidentRows : [],
|
|
814
|
+
governance_spec_scene_override_registry: Array.isArray(governanceOverrideRows) ? governanceOverrideRows : [],
|
|
815
|
+
governance_scene_index_registry: Array.isArray(governanceSceneRows) ? governanceSceneRows : [],
|
|
816
|
+
state_migration_registry: Array.isArray(migrations) ? migrations : []
|
|
817
|
+
},
|
|
818
|
+
summary: {
|
|
819
|
+
agent_runtime_registry: Array.isArray(agentRows) ? agentRows.length : 0,
|
|
820
|
+
timeline_snapshot_registry: Array.isArray(timelineRows) ? timelineRows.length : 0,
|
|
821
|
+
scene_session_cycle_registry: Array.isArray(sessionRows) ? sessionRows.length : 0,
|
|
822
|
+
errorbook_entry_index_registry: Array.isArray(errorbookEntryRows) ? errorbookEntryRows.length : 0,
|
|
823
|
+
errorbook_incident_index_registry: Array.isArray(errorbookIncidentRows) ? errorbookIncidentRows.length : 0,
|
|
824
|
+
governance_spec_scene_override_registry: Array.isArray(governanceOverrideRows) ? governanceOverrideRows.length : 0,
|
|
825
|
+
governance_scene_index_registry: Array.isArray(governanceSceneRows) ? governanceSceneRows.length : 0,
|
|
826
|
+
state_migration_registry: Array.isArray(migrations) ? migrations.length : 0
|
|
827
|
+
},
|
|
828
|
+
out_file: path.relative(projectPath, outPath).replace(/\\/g, '/')
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
await fileSystem.ensureDir(path.dirname(outPath));
|
|
832
|
+
await fileSystem.writeJson(outPath, payload, { spaces: 2 });
|
|
833
|
+
return payload;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
module.exports = {
|
|
837
|
+
COMPONENT_AGENT_REGISTRY,
|
|
838
|
+
COMPONENT_TIMELINE_INDEX,
|
|
839
|
+
COMPONENT_SCENE_SESSION_INDEX,
|
|
840
|
+
COMPONENT_ERRORBOOK_ENTRY_INDEX,
|
|
841
|
+
COMPONENT_ERRORBOOK_INCIDENT_INDEX,
|
|
842
|
+
COMPONENT_SPEC_SCENE_OVERRIDES,
|
|
843
|
+
COMPONENT_SPEC_SCENE_INDEX,
|
|
844
|
+
COMPONENT_DEFINITIONS,
|
|
845
|
+
DEFAULT_STATE_EXPORT_PATH,
|
|
846
|
+
buildStateMigrationPlan,
|
|
847
|
+
runStateMigration,
|
|
848
|
+
runStateDoctor,
|
|
849
|
+
runStateExport
|
|
850
|
+
};
|