scene-capability-engine 3.6.4 → 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 +24 -0
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/docs/command-reference.md +8 -0
- package/lib/gitignore/gitignore-detector.js +2 -1
- package/lib/gitignore/layered-rules-template.js +1 -0
- package/lib/state/sce-state-store.js +651 -0
- package/lib/state/state-migration-manager.js +194 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.6.5] - 2026-03-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Stage-2 SQLite migration components for Errorbook indexes:
|
|
14
|
+
- `errorbook.entry-index` (`.sce/errorbook/index.json`)
|
|
15
|
+
- `errorbook.incident-index` (`.sce/errorbook/staging/index.json`)
|
|
16
|
+
- Stage-3 SQLite migration components for spec governance indexes:
|
|
17
|
+
- `governance.spec-scene-overrides` (`.sce/spec-governance/spec-scene-overrides.json`)
|
|
18
|
+
- `governance.scene-index` (`.sce/spec-governance/scene-index.json`)
|
|
19
|
+
- New SQLite index tables in state store:
|
|
20
|
+
- `errorbook_entry_index_registry`
|
|
21
|
+
- `errorbook_incident_index_registry`
|
|
22
|
+
- `governance_spec_scene_override_registry`
|
|
23
|
+
- `governance_scene_index_registry`
|
|
24
|
+
- New `SceStateStore` APIs:
|
|
25
|
+
- `upsert/listErrorbookEntryIndexRecords`
|
|
26
|
+
- `upsert/listErrorbookIncidentIndexRecords`
|
|
27
|
+
- `upsert/listGovernanceSpecSceneOverrideRecords`
|
|
28
|
+
- `upsert/listGovernanceSceneIndexRecords`
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- `sce state plan/doctor/migrate/export` now include spec-governance index components in migration scope and parity summaries.
|
|
32
|
+
- State export payload now includes spec-governance index tables for audit/debug snapshots.
|
|
33
|
+
|
|
10
34
|
## [3.6.4] - 2026-03-05
|
|
11
35
|
|
|
12
36
|
### Added
|
package/README.md
CHANGED
|
@@ -153,6 +153,7 @@ Studio task-stream output contract (default):
|
|
|
153
153
|
- reconciliation gate: `npm run gate:state-migration-reconciliation`
|
|
154
154
|
- runtime reads now prefer sqlite indexes for timeline/scene-session views when indexed data exists
|
|
155
155
|
- `state doctor` now emits `summary` and runtime diagnostics (`runtime.timeline`, `runtime.scene_session`) with read-source and consistency status
|
|
156
|
+
- migratable components now include errorbook + spec-governance indexes (`errorbook.entry-index`, `errorbook.incident-index`, `governance.spec-scene-overrides`, `governance.scene-index`) in addition to runtime registries
|
|
156
157
|
- Write authorization lease model (SQLite-backed):
|
|
157
158
|
- policy file: `.sce/config/authorization-policy.json`
|
|
158
159
|
- grant lease: `sce auth grant --scope studio:* --reason "<reason>" --auth-password <password> --json`
|
package/README.zh.md
CHANGED
|
@@ -153,6 +153,7 @@ Studio 任务流输出契约(默认):
|
|
|
153
153
|
- 对账门禁:`npm run gate:state-migration-reconciliation`
|
|
154
154
|
- 运行时读取在存在索引数据时优先使用 SQLite(timeline/scene-session 视图)
|
|
155
155
|
- `state doctor` 新增 `summary` 与运行时诊断(`runtime.timeline`、`runtime.scene_session`),可直接读取读源与一致性状态
|
|
156
|
+
- 可迁移组件扩展到 errorbook + spec-governance 索引(`errorbook.entry-index`、`errorbook.incident-index`、`governance.spec-scene-overrides`、`governance.scene-index`)
|
|
156
157
|
- 写入授权租约模型(SQLite 持久化):
|
|
157
158
|
- 策略文件:`.sce/config/authorization-policy.json`
|
|
158
159
|
- 申请租约:`sce auth grant --scope studio:* --reason "<原因>" --auth-password <密码> --json`
|
|
@@ -734,11 +734,19 @@ Current migratable components:
|
|
|
734
734
|
- `collab.agent-registry` (`.sce/config/agent-registry.json`)
|
|
735
735
|
- `runtime.timeline-index` (`.sce/timeline/index.json`)
|
|
736
736
|
- `runtime.scene-session-index` (`.sce/session-governance/scene-index.json`)
|
|
737
|
+
- `errorbook.entry-index` (`.sce/errorbook/index.json`)
|
|
738
|
+
- `errorbook.incident-index` (`.sce/errorbook/staging/index.json`)
|
|
739
|
+
- `governance.spec-scene-overrides` (`.sce/spec-governance/spec-scene-overrides.json`)
|
|
740
|
+
- `governance.scene-index` (`.sce/spec-governance/scene-index.json`)
|
|
737
741
|
|
|
738
742
|
SQLite index tables introduced for gradual migration:
|
|
739
743
|
- `agent_runtime_registry`
|
|
740
744
|
- `timeline_snapshot_registry`
|
|
741
745
|
- `scene_session_cycle_registry`
|
|
746
|
+
- `errorbook_entry_index_registry`
|
|
747
|
+
- `errorbook_incident_index_registry`
|
|
748
|
+
- `governance_spec_scene_override_registry`
|
|
749
|
+
- `governance_scene_index_registry`
|
|
742
750
|
- `state_migration_registry`
|
|
743
751
|
|
|
744
752
|
Runtime read preference:
|
|
@@ -21,6 +21,23 @@ function normalizeInteger(value, fallback = 0) {
|
|
|
21
21
|
return parsed;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function normalizeBooleanValue(value, fallback = false) {
|
|
25
|
+
if (typeof value === 'boolean') {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
const normalized = normalizeString(`${value || ''}`).toLowerCase();
|
|
29
|
+
if (!normalized) {
|
|
30
|
+
return fallback;
|
|
31
|
+
}
|
|
32
|
+
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
function normalizeNonNegativeInteger(value, fallback = 0) {
|
|
25
42
|
const parsed = Number.parseInt(`${value}`, 10);
|
|
26
43
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
@@ -116,6 +133,10 @@ class SceStateStore {
|
|
|
116
133
|
timeline_snapshots: {},
|
|
117
134
|
scene_session_cycles: {},
|
|
118
135
|
agent_runtime: {},
|
|
136
|
+
errorbook_entry_index: {},
|
|
137
|
+
errorbook_incident_index: {},
|
|
138
|
+
governance_spec_scene_override: {},
|
|
139
|
+
governance_scene_index: {},
|
|
119
140
|
migration_records: {},
|
|
120
141
|
auth_leases: {},
|
|
121
142
|
auth_events: [],
|
|
@@ -317,6 +338,75 @@ class SceStateStore {
|
|
|
317
338
|
|
|
318
339
|
CREATE INDEX IF NOT EXISTS idx_state_migration_registry_component_started
|
|
319
340
|
ON state_migration_registry(component_id, started_at DESC);
|
|
341
|
+
|
|
342
|
+
CREATE TABLE IF NOT EXISTS errorbook_entry_index_registry (
|
|
343
|
+
entry_id TEXT PRIMARY KEY,
|
|
344
|
+
fingerprint TEXT,
|
|
345
|
+
title TEXT,
|
|
346
|
+
status TEXT,
|
|
347
|
+
quality_score INTEGER,
|
|
348
|
+
tags_json TEXT,
|
|
349
|
+
ontology_tags_json TEXT,
|
|
350
|
+
temporary_mitigation_active INTEGER,
|
|
351
|
+
temporary_mitigation_deadline_at TEXT,
|
|
352
|
+
occurrences INTEGER,
|
|
353
|
+
created_at TEXT,
|
|
354
|
+
updated_at TEXT,
|
|
355
|
+
source TEXT,
|
|
356
|
+
indexed_at TEXT NOT NULL
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
CREATE INDEX IF NOT EXISTS idx_errorbook_entry_index_registry_status_updated
|
|
360
|
+
ON errorbook_entry_index_registry(status, updated_at DESC);
|
|
361
|
+
|
|
362
|
+
CREATE TABLE IF NOT EXISTS errorbook_incident_index_registry (
|
|
363
|
+
incident_id TEXT PRIMARY KEY,
|
|
364
|
+
fingerprint TEXT,
|
|
365
|
+
title TEXT,
|
|
366
|
+
symptom TEXT,
|
|
367
|
+
state TEXT,
|
|
368
|
+
attempt_count INTEGER,
|
|
369
|
+
created_at TEXT,
|
|
370
|
+
updated_at TEXT,
|
|
371
|
+
last_attempt_at TEXT,
|
|
372
|
+
resolved_at TEXT,
|
|
373
|
+
linked_entry_id TEXT,
|
|
374
|
+
source TEXT,
|
|
375
|
+
indexed_at TEXT NOT NULL
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
CREATE INDEX IF NOT EXISTS idx_errorbook_incident_index_registry_state_updated
|
|
379
|
+
ON errorbook_incident_index_registry(state, updated_at DESC);
|
|
380
|
+
|
|
381
|
+
CREATE TABLE IF NOT EXISTS governance_spec_scene_override_registry (
|
|
382
|
+
spec_id TEXT PRIMARY KEY,
|
|
383
|
+
scene_id TEXT NOT NULL,
|
|
384
|
+
source TEXT,
|
|
385
|
+
rule_id TEXT,
|
|
386
|
+
updated_at TEXT,
|
|
387
|
+
indexed_at TEXT NOT NULL
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
CREATE INDEX IF NOT EXISTS idx_governance_spec_scene_override_registry_scene
|
|
391
|
+
ON governance_spec_scene_override_registry(scene_id, updated_at DESC);
|
|
392
|
+
|
|
393
|
+
CREATE TABLE IF NOT EXISTS governance_scene_index_registry (
|
|
394
|
+
scene_id TEXT PRIMARY KEY,
|
|
395
|
+
total_specs INTEGER,
|
|
396
|
+
active_specs INTEGER,
|
|
397
|
+
completed_specs INTEGER,
|
|
398
|
+
stale_specs INTEGER,
|
|
399
|
+
spec_ids_json TEXT,
|
|
400
|
+
active_spec_ids_json TEXT,
|
|
401
|
+
stale_spec_ids_json TEXT,
|
|
402
|
+
generated_at TEXT,
|
|
403
|
+
scene_filter TEXT,
|
|
404
|
+
source TEXT,
|
|
405
|
+
indexed_at TEXT NOT NULL
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
CREATE INDEX IF NOT EXISTS idx_governance_scene_index_registry_counts
|
|
409
|
+
ON governance_scene_index_registry(total_specs DESC, active_specs DESC);
|
|
320
410
|
`);
|
|
321
411
|
}
|
|
322
412
|
|
|
@@ -511,6 +601,83 @@ class SceStateStore {
|
|
|
511
601
|
};
|
|
512
602
|
}
|
|
513
603
|
|
|
604
|
+
_mapErrorbookEntryIndexRow(row) {
|
|
605
|
+
if (!row) {
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
entry_id: normalizeString(row.entry_id),
|
|
610
|
+
fingerprint: normalizeString(row.fingerprint) || null,
|
|
611
|
+
title: normalizeString(row.title) || null,
|
|
612
|
+
status: normalizeString(row.status) || null,
|
|
613
|
+
quality_score: normalizeNonNegativeInteger(row.quality_score, 0),
|
|
614
|
+
tags: normalizeStringArray(parseJsonSafe(row.tags_json, []), []),
|
|
615
|
+
ontology_tags: normalizeStringArray(parseJsonSafe(row.ontology_tags_json, []), []),
|
|
616
|
+
temporary_mitigation_active: Number(row.temporary_mitigation_active) === 1,
|
|
617
|
+
temporary_mitigation_deadline_at: normalizeIsoTimestamp(row.temporary_mitigation_deadline_at, '') || null,
|
|
618
|
+
occurrences: normalizeNonNegativeInteger(row.occurrences, 0),
|
|
619
|
+
created_at: normalizeIsoTimestamp(row.created_at, '') || null,
|
|
620
|
+
updated_at: normalizeIsoTimestamp(row.updated_at, '') || null,
|
|
621
|
+
source: normalizeString(row.source) || null,
|
|
622
|
+
indexed_at: normalizeIsoTimestamp(row.indexed_at, '') || null
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
_mapErrorbookIncidentIndexRow(row) {
|
|
627
|
+
if (!row) {
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
return {
|
|
631
|
+
incident_id: normalizeString(row.incident_id),
|
|
632
|
+
fingerprint: normalizeString(row.fingerprint) || null,
|
|
633
|
+
title: normalizeString(row.title) || null,
|
|
634
|
+
symptom: normalizeString(row.symptom) || null,
|
|
635
|
+
state: normalizeString(row.state) || null,
|
|
636
|
+
attempt_count: normalizeNonNegativeInteger(row.attempt_count, 0),
|
|
637
|
+
created_at: normalizeIsoTimestamp(row.created_at, '') || null,
|
|
638
|
+
updated_at: normalizeIsoTimestamp(row.updated_at, '') || null,
|
|
639
|
+
last_attempt_at: normalizeIsoTimestamp(row.last_attempt_at, '') || null,
|
|
640
|
+
resolved_at: normalizeIsoTimestamp(row.resolved_at, '') || null,
|
|
641
|
+
linked_entry_id: normalizeString(row.linked_entry_id) || null,
|
|
642
|
+
source: normalizeString(row.source) || null,
|
|
643
|
+
indexed_at: normalizeIsoTimestamp(row.indexed_at, '') || null
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
_mapGovernanceSpecSceneOverrideRow(row) {
|
|
648
|
+
if (!row) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
spec_id: normalizeString(row.spec_id),
|
|
653
|
+
scene_id: normalizeString(row.scene_id),
|
|
654
|
+
source: normalizeString(row.source) || null,
|
|
655
|
+
rule_id: normalizeString(row.rule_id) || null,
|
|
656
|
+
updated_at: normalizeIsoTimestamp(row.updated_at, '') || null,
|
|
657
|
+
indexed_at: normalizeIsoTimestamp(row.indexed_at, '') || null
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
_mapGovernanceSceneIndexRow(row) {
|
|
662
|
+
if (!row) {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
scene_id: normalizeString(row.scene_id),
|
|
667
|
+
total_specs: normalizeNonNegativeInteger(row.total_specs, 0),
|
|
668
|
+
active_specs: normalizeNonNegativeInteger(row.active_specs, 0),
|
|
669
|
+
completed_specs: normalizeNonNegativeInteger(row.completed_specs, 0),
|
|
670
|
+
stale_specs: normalizeNonNegativeInteger(row.stale_specs, 0),
|
|
671
|
+
spec_ids: normalizeStringArray(parseJsonSafe(row.spec_ids_json, []), []),
|
|
672
|
+
active_spec_ids: normalizeStringArray(parseJsonSafe(row.active_spec_ids_json, []), []),
|
|
673
|
+
stale_spec_ids: normalizeStringArray(parseJsonSafe(row.stale_spec_ids_json, []), []),
|
|
674
|
+
generated_at: normalizeIsoTimestamp(row.generated_at, '') || null,
|
|
675
|
+
scene_filter: normalizeString(row.scene_filter) || null,
|
|
676
|
+
source: normalizeString(row.source) || null,
|
|
677
|
+
indexed_at: normalizeIsoTimestamp(row.indexed_at, '') || null
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
514
681
|
async resolveOrCreateTaskRef(options = {}) {
|
|
515
682
|
const sceneId = normalizeString(options.sceneId);
|
|
516
683
|
const specId = normalizeString(options.specId);
|
|
@@ -1514,6 +1681,490 @@ class SceStateStore {
|
|
|
1514
1681
|
.filter(Boolean);
|
|
1515
1682
|
}
|
|
1516
1683
|
|
|
1684
|
+
async upsertErrorbookEntryIndexRecords(records = [], options = {}) {
|
|
1685
|
+
const source = normalizeString(options.source) || 'file.errorbook.index';
|
|
1686
|
+
const nowIso = this.now();
|
|
1687
|
+
const normalizedRecords = Array.isArray(records)
|
|
1688
|
+
? records.map((item) => ({
|
|
1689
|
+
entry_id: normalizeString(item && (item.entry_id || item.id)),
|
|
1690
|
+
fingerprint: normalizeString(item && item.fingerprint) || null,
|
|
1691
|
+
title: normalizeString(item && item.title) || null,
|
|
1692
|
+
status: normalizeString(item && item.status) || null,
|
|
1693
|
+
quality_score: normalizeNonNegativeInteger(item && item.quality_score, 0),
|
|
1694
|
+
tags: normalizeStringArray(item && item.tags, []),
|
|
1695
|
+
ontology_tags: normalizeStringArray(item && item.ontology_tags, []),
|
|
1696
|
+
temporary_mitigation_active: normalizeBooleanValue(item && item.temporary_mitigation_active, false),
|
|
1697
|
+
temporary_mitigation_deadline_at: normalizeIsoTimestamp(item && item.temporary_mitigation_deadline_at, '') || null,
|
|
1698
|
+
occurrences: normalizeNonNegativeInteger(item && item.occurrences, 0),
|
|
1699
|
+
created_at: normalizeIsoTimestamp(item && item.created_at, '') || null,
|
|
1700
|
+
updated_at: normalizeIsoTimestamp(item && item.updated_at, nowIso) || nowIso,
|
|
1701
|
+
source,
|
|
1702
|
+
indexed_at: nowIso
|
|
1703
|
+
}))
|
|
1704
|
+
.filter((item) => item.entry_id)
|
|
1705
|
+
: [];
|
|
1706
|
+
|
|
1707
|
+
if (this._useMemoryBackend()) {
|
|
1708
|
+
for (const item of normalizedRecords) {
|
|
1709
|
+
this._memory.errorbook_entry_index[item.entry_id] = { ...item };
|
|
1710
|
+
}
|
|
1711
|
+
return {
|
|
1712
|
+
success: true,
|
|
1713
|
+
written: normalizedRecords.length,
|
|
1714
|
+
total: Object.keys(this._memory.errorbook_entry_index || {}).length
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
if (!await this.ensureReady()) {
|
|
1719
|
+
return null;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
const statement = this._db.prepare(`
|
|
1723
|
+
INSERT OR REPLACE INTO errorbook_entry_index_registry(
|
|
1724
|
+
entry_id, fingerprint, title, status, quality_score, tags_json, ontology_tags_json,
|
|
1725
|
+
temporary_mitigation_active, temporary_mitigation_deadline_at, occurrences, created_at, updated_at, source, indexed_at
|
|
1726
|
+
)
|
|
1727
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1728
|
+
`);
|
|
1729
|
+
|
|
1730
|
+
this._withTransaction(() => {
|
|
1731
|
+
for (const item of normalizedRecords) {
|
|
1732
|
+
statement.run(
|
|
1733
|
+
item.entry_id,
|
|
1734
|
+
item.fingerprint,
|
|
1735
|
+
item.title,
|
|
1736
|
+
item.status,
|
|
1737
|
+
item.quality_score,
|
|
1738
|
+
JSON.stringify(item.tags || []),
|
|
1739
|
+
JSON.stringify(item.ontology_tags || []),
|
|
1740
|
+
item.temporary_mitigation_active ? 1 : 0,
|
|
1741
|
+
item.temporary_mitigation_deadline_at,
|
|
1742
|
+
item.occurrences,
|
|
1743
|
+
item.created_at,
|
|
1744
|
+
item.updated_at,
|
|
1745
|
+
item.source,
|
|
1746
|
+
item.indexed_at
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
const totalRow = this._db
|
|
1752
|
+
.prepare('SELECT COUNT(*) AS total FROM errorbook_entry_index_registry')
|
|
1753
|
+
.get();
|
|
1754
|
+
|
|
1755
|
+
return {
|
|
1756
|
+
success: true,
|
|
1757
|
+
written: normalizedRecords.length,
|
|
1758
|
+
total: normalizeNonNegativeInteger(totalRow && totalRow.total, 0)
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
async listErrorbookEntryIndexRecords(options = {}) {
|
|
1763
|
+
const limit = normalizeInteger(options.limit, 100);
|
|
1764
|
+
const status = normalizeString(options.status);
|
|
1765
|
+
|
|
1766
|
+
if (this._useMemoryBackend()) {
|
|
1767
|
+
let rows = Object.values(this._memory.errorbook_entry_index || {}).map((item) => ({ ...item }));
|
|
1768
|
+
if (status) {
|
|
1769
|
+
rows = rows.filter((item) => normalizeString(item.status) === status);
|
|
1770
|
+
}
|
|
1771
|
+
rows.sort((left, right) => (Date.parse(right.updated_at || '') || 0) - (Date.parse(left.updated_at || '') || 0));
|
|
1772
|
+
if (limit > 0) {
|
|
1773
|
+
rows = rows.slice(0, limit);
|
|
1774
|
+
}
|
|
1775
|
+
return rows.map((row) => this._mapErrorbookEntryIndexRow({
|
|
1776
|
+
...row,
|
|
1777
|
+
tags_json: JSON.stringify(row.tags || []),
|
|
1778
|
+
ontology_tags_json: JSON.stringify(row.ontology_tags || [])
|
|
1779
|
+
}));
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
if (!await this.ensureReady()) {
|
|
1783
|
+
return null;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
let query = `
|
|
1787
|
+
SELECT entry_id, fingerprint, title, status, quality_score, tags_json, ontology_tags_json,
|
|
1788
|
+
temporary_mitigation_active, temporary_mitigation_deadline_at, occurrences, created_at, updated_at, source, indexed_at
|
|
1789
|
+
FROM errorbook_entry_index_registry
|
|
1790
|
+
`;
|
|
1791
|
+
const params = [];
|
|
1792
|
+
if (status) {
|
|
1793
|
+
query += ' WHERE status = ?';
|
|
1794
|
+
params.push(status);
|
|
1795
|
+
}
|
|
1796
|
+
query += ' ORDER BY updated_at DESC';
|
|
1797
|
+
if (limit > 0) {
|
|
1798
|
+
query += ' LIMIT ?';
|
|
1799
|
+
params.push(limit);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
const rows = this._db.prepare(query).all(...params);
|
|
1803
|
+
return rows
|
|
1804
|
+
.map((row) => this._mapErrorbookEntryIndexRow(row))
|
|
1805
|
+
.filter(Boolean);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
async upsertErrorbookIncidentIndexRecords(records = [], options = {}) {
|
|
1809
|
+
const source = normalizeString(options.source) || 'file.errorbook.incident-index';
|
|
1810
|
+
const nowIso = this.now();
|
|
1811
|
+
const normalizedRecords = Array.isArray(records)
|
|
1812
|
+
? records.map((item) => ({
|
|
1813
|
+
incident_id: normalizeString(item && (item.incident_id || item.id)),
|
|
1814
|
+
fingerprint: normalizeString(item && item.fingerprint) || null,
|
|
1815
|
+
title: normalizeString(item && item.title) || null,
|
|
1816
|
+
symptom: normalizeString(item && item.symptom) || null,
|
|
1817
|
+
state: normalizeString(item && item.state) || null,
|
|
1818
|
+
attempt_count: normalizeNonNegativeInteger(item && item.attempt_count, 0),
|
|
1819
|
+
created_at: normalizeIsoTimestamp(item && item.created_at, '') || null,
|
|
1820
|
+
updated_at: normalizeIsoTimestamp(item && item.updated_at, nowIso) || nowIso,
|
|
1821
|
+
last_attempt_at: normalizeIsoTimestamp(item && item.last_attempt_at, '') || null,
|
|
1822
|
+
resolved_at: normalizeIsoTimestamp(item && item.resolved_at, '') || null,
|
|
1823
|
+
linked_entry_id: normalizeString(item && item.linked_entry_id) || null,
|
|
1824
|
+
source,
|
|
1825
|
+
indexed_at: nowIso
|
|
1826
|
+
}))
|
|
1827
|
+
.filter((item) => item.incident_id)
|
|
1828
|
+
: [];
|
|
1829
|
+
|
|
1830
|
+
if (this._useMemoryBackend()) {
|
|
1831
|
+
for (const item of normalizedRecords) {
|
|
1832
|
+
this._memory.errorbook_incident_index[item.incident_id] = { ...item };
|
|
1833
|
+
}
|
|
1834
|
+
return {
|
|
1835
|
+
success: true,
|
|
1836
|
+
written: normalizedRecords.length,
|
|
1837
|
+
total: Object.keys(this._memory.errorbook_incident_index || {}).length
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
if (!await this.ensureReady()) {
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
const statement = this._db.prepare(`
|
|
1846
|
+
INSERT OR REPLACE INTO errorbook_incident_index_registry(
|
|
1847
|
+
incident_id, fingerprint, title, symptom, state, attempt_count,
|
|
1848
|
+
created_at, updated_at, last_attempt_at, resolved_at, linked_entry_id, source, indexed_at
|
|
1849
|
+
)
|
|
1850
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1851
|
+
`);
|
|
1852
|
+
|
|
1853
|
+
this._withTransaction(() => {
|
|
1854
|
+
for (const item of normalizedRecords) {
|
|
1855
|
+
statement.run(
|
|
1856
|
+
item.incident_id,
|
|
1857
|
+
item.fingerprint,
|
|
1858
|
+
item.title,
|
|
1859
|
+
item.symptom,
|
|
1860
|
+
item.state,
|
|
1861
|
+
item.attempt_count,
|
|
1862
|
+
item.created_at,
|
|
1863
|
+
item.updated_at,
|
|
1864
|
+
item.last_attempt_at,
|
|
1865
|
+
item.resolved_at,
|
|
1866
|
+
item.linked_entry_id,
|
|
1867
|
+
item.source,
|
|
1868
|
+
item.indexed_at
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
});
|
|
1872
|
+
|
|
1873
|
+
const totalRow = this._db
|
|
1874
|
+
.prepare('SELECT COUNT(*) AS total FROM errorbook_incident_index_registry')
|
|
1875
|
+
.get();
|
|
1876
|
+
|
|
1877
|
+
return {
|
|
1878
|
+
success: true,
|
|
1879
|
+
written: normalizedRecords.length,
|
|
1880
|
+
total: normalizeNonNegativeInteger(totalRow && totalRow.total, 0)
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
async listErrorbookIncidentIndexRecords(options = {}) {
|
|
1885
|
+
const limit = normalizeInteger(options.limit, 100);
|
|
1886
|
+
const state = normalizeString(options.state);
|
|
1887
|
+
|
|
1888
|
+
if (this._useMemoryBackend()) {
|
|
1889
|
+
let rows = Object.values(this._memory.errorbook_incident_index || {}).map((item) => ({ ...item }));
|
|
1890
|
+
if (state) {
|
|
1891
|
+
rows = rows.filter((item) => normalizeString(item.state) === state);
|
|
1892
|
+
}
|
|
1893
|
+
rows.sort((left, right) => (Date.parse(right.updated_at || '') || 0) - (Date.parse(left.updated_at || '') || 0));
|
|
1894
|
+
if (limit > 0) {
|
|
1895
|
+
rows = rows.slice(0, limit);
|
|
1896
|
+
}
|
|
1897
|
+
return rows.map((row) => this._mapErrorbookIncidentIndexRow(row));
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
if (!await this.ensureReady()) {
|
|
1901
|
+
return null;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
let query = `
|
|
1905
|
+
SELECT incident_id, fingerprint, title, symptom, state, attempt_count,
|
|
1906
|
+
created_at, updated_at, last_attempt_at, resolved_at, linked_entry_id, source, indexed_at
|
|
1907
|
+
FROM errorbook_incident_index_registry
|
|
1908
|
+
`;
|
|
1909
|
+
const params = [];
|
|
1910
|
+
if (state) {
|
|
1911
|
+
query += ' WHERE state = ?';
|
|
1912
|
+
params.push(state);
|
|
1913
|
+
}
|
|
1914
|
+
query += ' ORDER BY updated_at DESC';
|
|
1915
|
+
if (limit > 0) {
|
|
1916
|
+
query += ' LIMIT ?';
|
|
1917
|
+
params.push(limit);
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
const rows = this._db.prepare(query).all(...params);
|
|
1921
|
+
return rows
|
|
1922
|
+
.map((row) => this._mapErrorbookIncidentIndexRow(row))
|
|
1923
|
+
.filter(Boolean);
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
async upsertGovernanceSpecSceneOverrideRecords(records = [], options = {}) {
|
|
1927
|
+
const source = normalizeString(options.source) || 'file.spec-governance.spec-scene-overrides';
|
|
1928
|
+
const nowIso = this.now();
|
|
1929
|
+
const normalizedRecords = Array.isArray(records)
|
|
1930
|
+
? records.map((item) => ({
|
|
1931
|
+
spec_id: normalizeString(item && item.spec_id),
|
|
1932
|
+
scene_id: normalizeString(item && item.scene_id),
|
|
1933
|
+
source: normalizeString(item && item.source) || source,
|
|
1934
|
+
rule_id: normalizeString(item && item.rule_id) || null,
|
|
1935
|
+
updated_at: normalizeIsoTimestamp(item && item.updated_at, nowIso) || nowIso,
|
|
1936
|
+
indexed_at: nowIso
|
|
1937
|
+
}))
|
|
1938
|
+
.filter((item) => item.spec_id && item.scene_id)
|
|
1939
|
+
: [];
|
|
1940
|
+
|
|
1941
|
+
if (this._useMemoryBackend()) {
|
|
1942
|
+
for (const item of normalizedRecords) {
|
|
1943
|
+
this._memory.governance_spec_scene_override[item.spec_id] = { ...item };
|
|
1944
|
+
}
|
|
1945
|
+
return {
|
|
1946
|
+
success: true,
|
|
1947
|
+
written: normalizedRecords.length,
|
|
1948
|
+
total: Object.keys(this._memory.governance_spec_scene_override || {}).length
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
if (!await this.ensureReady()) {
|
|
1953
|
+
return null;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
const statement = this._db.prepare(`
|
|
1957
|
+
INSERT OR REPLACE INTO governance_spec_scene_override_registry(
|
|
1958
|
+
spec_id, scene_id, source, rule_id, updated_at, indexed_at
|
|
1959
|
+
)
|
|
1960
|
+
VALUES(?, ?, ?, ?, ?, ?)
|
|
1961
|
+
`);
|
|
1962
|
+
|
|
1963
|
+
this._withTransaction(() => {
|
|
1964
|
+
for (const item of normalizedRecords) {
|
|
1965
|
+
statement.run(
|
|
1966
|
+
item.spec_id,
|
|
1967
|
+
item.scene_id,
|
|
1968
|
+
item.source,
|
|
1969
|
+
item.rule_id,
|
|
1970
|
+
item.updated_at,
|
|
1971
|
+
item.indexed_at
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
|
|
1976
|
+
const totalRow = this._db
|
|
1977
|
+
.prepare('SELECT COUNT(*) AS total FROM governance_spec_scene_override_registry')
|
|
1978
|
+
.get();
|
|
1979
|
+
|
|
1980
|
+
return {
|
|
1981
|
+
success: true,
|
|
1982
|
+
written: normalizedRecords.length,
|
|
1983
|
+
total: normalizeNonNegativeInteger(totalRow && totalRow.total, 0)
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
async listGovernanceSpecSceneOverrideRecords(options = {}) {
|
|
1988
|
+
const limit = normalizeInteger(options.limit, 100);
|
|
1989
|
+
const sceneId = normalizeString(options.sceneId);
|
|
1990
|
+
const specId = normalizeString(options.specId);
|
|
1991
|
+
|
|
1992
|
+
if (this._useMemoryBackend()) {
|
|
1993
|
+
let rows = Object.values(this._memory.governance_spec_scene_override || {}).map((item) => ({ ...item }));
|
|
1994
|
+
if (sceneId) {
|
|
1995
|
+
rows = rows.filter((item) => normalizeString(item.scene_id) === sceneId);
|
|
1996
|
+
}
|
|
1997
|
+
if (specId) {
|
|
1998
|
+
rows = rows.filter((item) => normalizeString(item.spec_id) === specId);
|
|
1999
|
+
}
|
|
2000
|
+
rows.sort((left, right) => (Date.parse(right.updated_at || '') || 0) - (Date.parse(left.updated_at || '') || 0));
|
|
2001
|
+
if (limit > 0) {
|
|
2002
|
+
rows = rows.slice(0, limit);
|
|
2003
|
+
}
|
|
2004
|
+
return rows.map((row) => this._mapGovernanceSpecSceneOverrideRow(row));
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
if (!await this.ensureReady()) {
|
|
2008
|
+
return null;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
let query = `
|
|
2012
|
+
SELECT spec_id, scene_id, source, rule_id, updated_at, indexed_at
|
|
2013
|
+
FROM governance_spec_scene_override_registry
|
|
2014
|
+
`;
|
|
2015
|
+
const clauses = [];
|
|
2016
|
+
const params = [];
|
|
2017
|
+
if (sceneId) {
|
|
2018
|
+
clauses.push('scene_id = ?');
|
|
2019
|
+
params.push(sceneId);
|
|
2020
|
+
}
|
|
2021
|
+
if (specId) {
|
|
2022
|
+
clauses.push('spec_id = ?');
|
|
2023
|
+
params.push(specId);
|
|
2024
|
+
}
|
|
2025
|
+
if (clauses.length > 0) {
|
|
2026
|
+
query += ` WHERE ${clauses.join(' AND ')}`;
|
|
2027
|
+
}
|
|
2028
|
+
query += ' ORDER BY updated_at DESC, spec_id ASC';
|
|
2029
|
+
if (limit > 0) {
|
|
2030
|
+
query += ' LIMIT ?';
|
|
2031
|
+
params.push(limit);
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
const rows = this._db.prepare(query).all(...params);
|
|
2035
|
+
return rows
|
|
2036
|
+
.map((row) => this._mapGovernanceSpecSceneOverrideRow(row))
|
|
2037
|
+
.filter(Boolean);
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
async upsertGovernanceSceneIndexRecords(records = [], options = {}) {
|
|
2041
|
+
const source = normalizeString(options.source) || 'file.spec-governance.scene-index';
|
|
2042
|
+
const nowIso = this.now();
|
|
2043
|
+
const normalizedRecords = Array.isArray(records)
|
|
2044
|
+
? records.map((item) => ({
|
|
2045
|
+
scene_id: normalizeString(item && item.scene_id),
|
|
2046
|
+
total_specs: normalizeNonNegativeInteger(item && item.total_specs, 0),
|
|
2047
|
+
active_specs: normalizeNonNegativeInteger(item && item.active_specs, 0),
|
|
2048
|
+
completed_specs: normalizeNonNegativeInteger(item && item.completed_specs, 0),
|
|
2049
|
+
stale_specs: normalizeNonNegativeInteger(item && item.stale_specs, 0),
|
|
2050
|
+
spec_ids: normalizeStringArray(item && item.spec_ids, []),
|
|
2051
|
+
active_spec_ids: normalizeStringArray(item && item.active_spec_ids, []),
|
|
2052
|
+
stale_spec_ids: normalizeStringArray(item && item.stale_spec_ids, []),
|
|
2053
|
+
generated_at: normalizeIsoTimestamp(item && item.generated_at, nowIso) || nowIso,
|
|
2054
|
+
scene_filter: normalizeString(item && item.scene_filter) || null,
|
|
2055
|
+
source: normalizeString(item && item.source) || source,
|
|
2056
|
+
indexed_at: nowIso
|
|
2057
|
+
}))
|
|
2058
|
+
.filter((item) => item.scene_id)
|
|
2059
|
+
: [];
|
|
2060
|
+
|
|
2061
|
+
if (this._useMemoryBackend()) {
|
|
2062
|
+
for (const item of normalizedRecords) {
|
|
2063
|
+
this._memory.governance_scene_index[item.scene_id] = { ...item };
|
|
2064
|
+
}
|
|
2065
|
+
return {
|
|
2066
|
+
success: true,
|
|
2067
|
+
written: normalizedRecords.length,
|
|
2068
|
+
total: Object.keys(this._memory.governance_scene_index || {}).length
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
if (!await this.ensureReady()) {
|
|
2073
|
+
return null;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
const statement = this._db.prepare(`
|
|
2077
|
+
INSERT OR REPLACE INTO governance_scene_index_registry(
|
|
2078
|
+
scene_id, total_specs, active_specs, completed_specs, stale_specs,
|
|
2079
|
+
spec_ids_json, active_spec_ids_json, stale_spec_ids_json, generated_at,
|
|
2080
|
+
scene_filter, source, indexed_at
|
|
2081
|
+
)
|
|
2082
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2083
|
+
`);
|
|
2084
|
+
|
|
2085
|
+
this._withTransaction(() => {
|
|
2086
|
+
for (const item of normalizedRecords) {
|
|
2087
|
+
statement.run(
|
|
2088
|
+
item.scene_id,
|
|
2089
|
+
item.total_specs,
|
|
2090
|
+
item.active_specs,
|
|
2091
|
+
item.completed_specs,
|
|
2092
|
+
item.stale_specs,
|
|
2093
|
+
JSON.stringify(item.spec_ids || []),
|
|
2094
|
+
JSON.stringify(item.active_spec_ids || []),
|
|
2095
|
+
JSON.stringify(item.stale_spec_ids || []),
|
|
2096
|
+
item.generated_at,
|
|
2097
|
+
item.scene_filter,
|
|
2098
|
+
item.source,
|
|
2099
|
+
item.indexed_at
|
|
2100
|
+
);
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
|
|
2104
|
+
const totalRow = this._db
|
|
2105
|
+
.prepare('SELECT COUNT(*) AS total FROM governance_scene_index_registry')
|
|
2106
|
+
.get();
|
|
2107
|
+
|
|
2108
|
+
return {
|
|
2109
|
+
success: true,
|
|
2110
|
+
written: normalizedRecords.length,
|
|
2111
|
+
total: normalizeNonNegativeInteger(totalRow && totalRow.total, 0)
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
async listGovernanceSceneIndexRecords(options = {}) {
|
|
2116
|
+
const limit = normalizeInteger(options.limit, 100);
|
|
2117
|
+
const sceneId = normalizeString(options.sceneId);
|
|
2118
|
+
|
|
2119
|
+
if (this._useMemoryBackend()) {
|
|
2120
|
+
let rows = Object.values(this._memory.governance_scene_index || {}).map((item) => ({ ...item }));
|
|
2121
|
+
if (sceneId) {
|
|
2122
|
+
rows = rows.filter((item) => normalizeString(item.scene_id) === sceneId);
|
|
2123
|
+
}
|
|
2124
|
+
rows.sort((left, right) => {
|
|
2125
|
+
if (right.total_specs !== left.total_specs) {
|
|
2126
|
+
return right.total_specs - left.total_specs;
|
|
2127
|
+
}
|
|
2128
|
+
return `${left.scene_id}`.localeCompare(`${right.scene_id}`);
|
|
2129
|
+
});
|
|
2130
|
+
if (limit > 0) {
|
|
2131
|
+
rows = rows.slice(0, limit);
|
|
2132
|
+
}
|
|
2133
|
+
return rows.map((row) => this._mapGovernanceSceneIndexRow({
|
|
2134
|
+
...row,
|
|
2135
|
+
spec_ids_json: JSON.stringify(row.spec_ids || []),
|
|
2136
|
+
active_spec_ids_json: JSON.stringify(row.active_spec_ids || []),
|
|
2137
|
+
stale_spec_ids_json: JSON.stringify(row.stale_spec_ids || [])
|
|
2138
|
+
}));
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
if (!await this.ensureReady()) {
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
let query = `
|
|
2146
|
+
SELECT scene_id, total_specs, active_specs, completed_specs, stale_specs,
|
|
2147
|
+
spec_ids_json, active_spec_ids_json, stale_spec_ids_json, generated_at,
|
|
2148
|
+
scene_filter, source, indexed_at
|
|
2149
|
+
FROM governance_scene_index_registry
|
|
2150
|
+
`;
|
|
2151
|
+
const params = [];
|
|
2152
|
+
if (sceneId) {
|
|
2153
|
+
query += ' WHERE scene_id = ?';
|
|
2154
|
+
params.push(sceneId);
|
|
2155
|
+
}
|
|
2156
|
+
query += ' ORDER BY total_specs DESC, active_specs DESC, scene_id ASC';
|
|
2157
|
+
if (limit > 0) {
|
|
2158
|
+
query += ' LIMIT ?';
|
|
2159
|
+
params.push(limit);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
const rows = this._db.prepare(query).all(...params);
|
|
2163
|
+
return rows
|
|
2164
|
+
.map((row) => this._mapGovernanceSceneIndexRow(row))
|
|
2165
|
+
.filter(Boolean);
|
|
2166
|
+
}
|
|
2167
|
+
|
|
1517
2168
|
_resolveOrCreateTaskRefInMemory(options = {}) {
|
|
1518
2169
|
const sceneId = normalizeString(options.sceneId);
|
|
1519
2170
|
const specId = normalizeString(options.specId);
|
|
@@ -7,6 +7,10 @@ const { SessionStore } = require('../runtime/session-store');
|
|
|
7
7
|
const COMPONENT_AGENT_REGISTRY = 'collab.agent-registry';
|
|
8
8
|
const COMPONENT_TIMELINE_INDEX = 'runtime.timeline-index';
|
|
9
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';
|
|
10
14
|
const DEFAULT_STATE_EXPORT_PATH = '.sce/reports/state-migration/state-export.latest.json';
|
|
11
15
|
|
|
12
16
|
const COMPONENT_DEFINITIONS = Object.freeze([
|
|
@@ -21,6 +25,22 @@ const COMPONENT_DEFINITIONS = Object.freeze([
|
|
|
21
25
|
{
|
|
22
26
|
id: COMPONENT_SCENE_SESSION_INDEX,
|
|
23
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'
|
|
24
44
|
}
|
|
25
45
|
]);
|
|
26
46
|
|
|
@@ -159,6 +179,107 @@ function mapSceneSessionIndexPayload(payload = {}) {
|
|
|
159
179
|
return records;
|
|
160
180
|
}
|
|
161
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
|
+
|
|
162
283
|
async function readJsonSource(absolutePath, fileSystem = fs) {
|
|
163
284
|
if (!await fileSystem.pathExists(absolutePath)) {
|
|
164
285
|
return {
|
|
@@ -196,6 +317,14 @@ async function readComponentSnapshot(component = {}, projectPath = process.cwd()
|
|
|
196
317
|
records = mapTimelineIndexPayload(source.payload);
|
|
197
318
|
} else if (component.id === COMPONENT_SCENE_SESSION_INDEX) {
|
|
198
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);
|
|
199
328
|
}
|
|
200
329
|
}
|
|
201
330
|
|
|
@@ -285,6 +414,26 @@ async function writeComponentToStateStore(componentSnapshot = {}, stateStore, co
|
|
|
285
414
|
source: componentId
|
|
286
415
|
});
|
|
287
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
|
+
}
|
|
288
437
|
return null;
|
|
289
438
|
}
|
|
290
439
|
|
|
@@ -404,17 +553,34 @@ async function runStateDoctor(options = {}, dependencies = {}) {
|
|
|
404
553
|
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, { fileSystem, env });
|
|
405
554
|
const plan = await buildStateMigrationPlan({}, { projectPath, fileSystem, env, stateStore });
|
|
406
555
|
|
|
407
|
-
const [
|
|
556
|
+
const [
|
|
557
|
+
agentRows,
|
|
558
|
+
timelineRows,
|
|
559
|
+
sessionRows,
|
|
560
|
+
errorbookEntryRows,
|
|
561
|
+
errorbookIncidentRows,
|
|
562
|
+
governanceOverrideRows,
|
|
563
|
+
governanceSceneRows,
|
|
564
|
+
migrations
|
|
565
|
+
] = await Promise.all([
|
|
408
566
|
stateStore.listAgentRuntimeRecords({ limit: 0 }),
|
|
409
567
|
stateStore.listTimelineSnapshotIndex({ limit: 0 }),
|
|
410
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 }),
|
|
411
573
|
stateStore.listStateMigrations({ limit: 20 })
|
|
412
574
|
]);
|
|
413
575
|
|
|
414
576
|
const targetCounts = new Map([
|
|
415
577
|
[COMPONENT_AGENT_REGISTRY, Array.isArray(agentRows) ? agentRows.length : 0],
|
|
416
578
|
[COMPONENT_TIMELINE_INDEX, Array.isArray(timelineRows) ? timelineRows.length : 0],
|
|
417
|
-
[COMPONENT_SCENE_SESSION_INDEX, Array.isArray(sessionRows) ? sessionRows.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]
|
|
418
584
|
]);
|
|
419
585
|
|
|
420
586
|
const checks = plan.components.map((component) => {
|
|
@@ -614,10 +780,23 @@ async function runStateExport(options = {}, dependencies = {}) {
|
|
|
614
780
|
: path.join(projectPath, outPathRaw);
|
|
615
781
|
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, { fileSystem, env });
|
|
616
782
|
|
|
617
|
-
const [
|
|
783
|
+
const [
|
|
784
|
+
agentRows,
|
|
785
|
+
timelineRows,
|
|
786
|
+
sessionRows,
|
|
787
|
+
errorbookEntryRows,
|
|
788
|
+
errorbookIncidentRows,
|
|
789
|
+
governanceOverrideRows,
|
|
790
|
+
governanceSceneRows,
|
|
791
|
+
migrations
|
|
792
|
+
] = await Promise.all([
|
|
618
793
|
stateStore.listAgentRuntimeRecords({ limit: 0 }),
|
|
619
794
|
stateStore.listTimelineSnapshotIndex({ limit: 0 }),
|
|
620
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 }),
|
|
621
800
|
stateStore.listStateMigrations({ limit: 0 })
|
|
622
801
|
]);
|
|
623
802
|
|
|
@@ -630,12 +809,20 @@ async function runStateExport(options = {}, dependencies = {}) {
|
|
|
630
809
|
agent_runtime_registry: Array.isArray(agentRows) ? agentRows : [],
|
|
631
810
|
timeline_snapshot_registry: Array.isArray(timelineRows) ? timelineRows : [],
|
|
632
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 : [],
|
|
633
816
|
state_migration_registry: Array.isArray(migrations) ? migrations : []
|
|
634
817
|
},
|
|
635
818
|
summary: {
|
|
636
819
|
agent_runtime_registry: Array.isArray(agentRows) ? agentRows.length : 0,
|
|
637
820
|
timeline_snapshot_registry: Array.isArray(timelineRows) ? timelineRows.length : 0,
|
|
638
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,
|
|
639
826
|
state_migration_registry: Array.isArray(migrations) ? migrations.length : 0
|
|
640
827
|
},
|
|
641
828
|
out_file: path.relative(projectPath, outPath).replace(/\\/g, '/')
|
|
@@ -650,6 +837,10 @@ module.exports = {
|
|
|
650
837
|
COMPONENT_AGENT_REGISTRY,
|
|
651
838
|
COMPONENT_TIMELINE_INDEX,
|
|
652
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,
|
|
653
844
|
COMPONENT_DEFINITIONS,
|
|
654
845
|
DEFAULT_STATE_EXPORT_PATH,
|
|
655
846
|
buildStateMigrationPlan,
|
package/package.json
CHANGED