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 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:
@@ -43,7 +43,8 @@ class GitignoreDetector {
43
43
  '.sce/environments.json',
44
44
  '.sce/backups/',
45
45
  '.sce/logs/',
46
- '.sce/reports/'
46
+ '.sce/reports/',
47
+ '.sce/state/'
47
48
  ];
48
49
  }
49
50
 
@@ -25,6 +25,7 @@ const LAYERED_RULES_TEMPLATE = `# ========================================
25
25
  .sce/backups/
26
26
  .sce/logs/
27
27
  .sce/reports/
28
+ .sce/state/
28
29
 
29
30
  # Spec artifacts (COMMIT - but exclude temporary files)
30
31
  .sce/specs/**/SESSION-*.md
@@ -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 [agentRows, timelineRows, sessionRows, migrations] = await Promise.all([
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 [agentRows, timelineRows, sessionRows, migrations] = await Promise.all([
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.4",
3
+ "version": "3.6.5",
4
4
  "description": "SCE (Scene Capability Engine) - A CLI tool and npm package for spec-driven development with AI coding assistants.",
5
5
  "main": "index.js",
6
6
  "bin": {