scene-capability-engine 3.6.52 → 3.6.53

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,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.6.53] - 2026-03-16
11
+
12
+ ### Added
13
+ - Added a tracked project-shared high-value errorbook projection at `.sce/knowledge/errorbook/project-shared-registry.json`, seeded by default in template/takeover baselines so same-project work on another computer can recover curated `verified` and `promoted` knowledge through Git without committing raw `.sce/errorbook/**`.
14
+
15
+ ### Changed
16
+ - Errorbook mutation commands now auto-refresh the tracked project-shared projection, and registry-inclusive lookup can search that local file in `remote` mode without requiring an index.
17
+ - Co-work governance auditing now treats the project-shared errorbook projection as a required closure artifact: config must stay enabled, the project-shared source must exist, the projection file must parse, and it must be Git-tracked.
18
+
10
19
  ## [3.6.52] - 2026-03-16
11
20
 
12
21
  ### Added
package/README.md CHANGED
@@ -218,5 +218,5 @@ MIT. See [LICENSE](LICENSE).
218
218
 
219
219
  ---
220
220
 
221
- **Version**: 3.6.52
221
+ **Version**: 3.6.53
222
222
  **Last Updated**: 2026-03-16
package/README.zh.md CHANGED
@@ -223,5 +223,5 @@ MIT,见 [LICENSE](LICENSE)。
223
223
 
224
224
  ---
225
225
 
226
- **版本**:3.6.52
226
+ **版本**:3.6.53
227
227
  **最后更新**:2026-03-16
@@ -135,7 +135,7 @@ Timeline policy:
135
135
  - default enabled with local retention under `.sce/timeline/snapshots/`
136
136
  - stage/key-event checkpoints are automatically captured for `studio` and `session` commands
137
137
  - interval auto-checkpoints are integrated in the same flow via timeline checkpoint capture
138
- - `timeline push` now blocks before snapshot/push when collaboration governance drifts, so tracked runtime state, missing co-work ignore rules, missing shared `errorbook` registry baseline, invalid multi-agent config, legacy `.kiro*` references, or steering boundary drift cannot pass through managed push flow
138
+ - `timeline push` now blocks before snapshot/push when collaboration governance drifts, so tracked runtime state, missing co-work ignore rules, missing shared `errorbook` registry baseline, missing tracked project-shared errorbook projection, invalid multi-agent config, legacy `.kiro*` references, or steering boundary drift cannot pass through managed push flow
139
139
 
140
140
  ### Value Metrics
141
141
 
@@ -348,7 +348,7 @@ sce workspace legacy-migrate --dry-run --json
348
348
  sce workspace tracking-audit
349
349
  sce workspace tracking-audit --json
350
350
 
351
- # Audit collaboration governance boundaries and legacy naming drift
351
+ # Audit collaboration governance boundaries, shared errorbook closure, and legacy naming drift
352
352
  sce workspace collab-governance-audit
353
353
  sce workspace collab-governance-audit --json
354
354
  sce workspace collab-governance-audit --strict
@@ -638,8 +638,10 @@ Curated quality policy (`宁缺毋滥,优胜略汰`) defaults:
638
638
  - missing exit/cleanup/deadline metadata
639
639
  - expired mitigation deadline
640
640
  - `export` outputs a machine-readable registry bundle from curated local entries (recommended default: `promoted`, `quality>=75`).
641
+ - managed same-project sharing now uses a tracked projection file at `.sce/knowledge/errorbook/project-shared-registry.json`, refreshed automatically after `record`, `promote`, `deprecate`, and `requalify`
641
642
  - `sync-registry` pulls external registry JSON into local cache (`.sce/errorbook/registry-cache.json`) for unified `find` retrieval.
642
643
  - `find --include-registry --registry-mode remote` supports direct remote query for large registries (no full local sync required).
644
+ - registry-inclusive lookup can also search the tracked project-shared local file in `remote` mode without a shard index.
643
645
  - Recommended for large registries: maintain a remote index file (`registry/errorbook-registry.index.json`) and shard files, then provide `index_url` in registry config.
644
646
  - Since `v3.3.23`, `sce init` / `sce adopt` default baseline includes enabled central registry config in `.sce/config/errorbook-registry.json`.
645
647
  - `health-registry` validates config readability, source/index accessibility, and index-to-shard resolution before release.
@@ -72,12 +72,23 @@ Create `.sce/config/errorbook-registry.json`:
72
72
  "enabled": true,
73
73
  "search_mode": "remote",
74
74
  "cache_file": ".sce/errorbook/registry-cache.json",
75
+ "project_shared_projection": {
76
+ "enabled": true,
77
+ "file": ".sce/knowledge/errorbook/project-shared-registry.json",
78
+ "statuses": ["verified", "promoted"],
79
+ "min_quality": 75
80
+ },
75
81
  "sources": [
76
82
  {
77
83
  "name": "central",
78
84
  "enabled": true,
79
85
  "url": "https://raw.githubusercontent.com/heguangyong/sce-errorbook-registry/main/registry/errorbook-registry.json",
80
86
  "index_url": "https://raw.githubusercontent.com/heguangyong/sce-errorbook-registry/main/registry/errorbook-registry.index.json"
87
+ },
88
+ {
89
+ "name": "project-shared",
90
+ "enabled": true,
91
+ "file": ".sce/knowledge/errorbook/project-shared-registry.json"
81
92
  }
82
93
  ]
83
94
  }
@@ -87,23 +98,30 @@ Notes:
87
98
  - `url` must be a raw JSON URL (`raw.githubusercontent.com`) or use a local file path.
88
99
  - `search_mode` supports `cache|remote|hybrid` (recommended: `remote` for very large registries).
89
100
  - Local cache file is used by cache/hybrid mode.
90
- - Since `v3.3.23`, `sce init` / `sce adopt` template baselines include this config by default (central source enabled).
101
+ - `project_shared_projection` is the Git-tracked same-project sharing path used for cross-computer co-work continuity.
102
+ - Since `v3.3.23`, `sce init` / `sce adopt` template baselines include registry config by default; current baseline also seeds the tracked project-shared projection file.
91
103
 
92
104
  ## 4) Daily Workflow
93
105
 
94
- 1. Export curated local entries:
106
+ 1. Keep project-shared same-project knowledge current:
107
+ ```bash
108
+ # SCE refreshes this automatically after record/promote/deprecate/requalify
109
+ .sce/knowledge/errorbook/project-shared-registry.json
110
+ ```
111
+
112
+ 2. Export curated local entries for central cross-project publication:
95
113
  ```bash
96
114
  sce errorbook export --status promoted --min-quality 75 --out .sce/errorbook/exports/registry.json --json
97
115
  ```
98
116
 
99
- 2. Merge approved entries into central repo `registry/errorbook-registry.json`.
117
+ 3. Merge approved entries into central repo `registry/errorbook-registry.json`.
100
118
 
101
- 3. Sync central registry into local cache:
119
+ 4. Sync central registry into local cache:
102
120
  ```bash
103
121
  sce errorbook sync-registry --source https://raw.githubusercontent.com/heguangyong/sce-errorbook-registry/main/registry/errorbook-registry.json --json
104
122
  ```
105
123
 
106
- 4. Search local + shared entries:
124
+ 5. Search local + shared entries:
107
125
  ```bash
108
126
  sce errorbook find --query "approve order timeout" --include-registry --json
109
127
  sce errorbook find --query "approve order timeout" --include-registry --registry-mode remote --json
@@ -116,6 +134,7 @@ SCE_REGISTRY_HEALTH_STRICT=1 node scripts/errorbook-registry-health-gate.js --js
116
134
 
117
135
  ## 5) Governance Rules
118
136
 
137
+ - Same-project co-work should rely on the tracked project-shared projection instead of committing raw `.sce/errorbook/**`.
119
138
  - Publish to central registry only curated entries (recommended: `status=promoted` and `quality>=75`).
120
139
  - Do not publish sensitive tenant/customer data.
121
140
  - Temporary mitigation entries must remain bounded and governed (exit criteria, cleanup task, deadline).
@@ -0,0 +1,19 @@
1
+ # v3.6.53 Release Notes
2
+
3
+ Release date: 2026-03-16
4
+
5
+ ## Highlights
6
+
7
+ - Closed the co-work loop for same-project high-value `errorbook` knowledge by adding a tracked shared projection at `.sce/knowledge/errorbook/project-shared-registry.json`.
8
+ - Baseline/template projects now seed the shared projection source by default, so another computer can recover historical specs and curated project errorbook knowledge after normal Git sync.
9
+ - Collaboration governance now blocks managed push/publish when the shared project errorbook projection is missing, disabled, invalid, or not Git-tracked.
10
+
11
+ ## Validation
12
+
13
+ - `npx jest tests/unit/commands/errorbook.test.js tests/unit/workspace/takeover-baseline.test.js tests/unit/workspace/collab-governance-audit.test.js tests/integration/takeover-baseline-cli.integration.test.js --runInBand`
14
+ - `node scripts/release-doc-version-audit.js --fail-on-error`
15
+ - `node scripts/collab-governance-gate.js --fail-on-violation --json`
16
+
17
+ ## Release Notes
18
+
19
+ - This patch turns “shared errorbook experience” from a config-level promise into a real same-project continuity path. Historical specs stay Git-shared as before, while high-value `verified/promoted` errorbook knowledge now has a dedicated tracked projection instead of leaking raw `.sce/errorbook/**` runtime state into version control.
@@ -0,0 +1,19 @@
1
+ # v3.6.53 发布说明
2
+
3
+ 发布日期:2026-03-16
4
+
5
+ ## 重点变化
6
+
7
+ - 补齐了同一项目 co-work 下高价值 `errorbook` 经验的闭环,新增 Git 跟踪投影文件 `.sce/knowledge/errorbook/project-shared-registry.json`。
8
+ - baseline / template 现在默认带上这个共享投影源,另一台电脑正常 `git pull` 后即可同时拿到历史 spec 和项目级高价值错题经验。
9
+ - 协作治理现在会阻断缺失、禁用、损坏或未纳入 Git 跟踪的项目共享错题投影,避免“能力存在但默认不生效”。
10
+
11
+ ## 验证
12
+
13
+ - `npx jest tests/unit/commands/errorbook.test.js tests/unit/workspace/takeover-baseline.test.js tests/unit/workspace/collab-governance-audit.test.js tests/integration/takeover-baseline-cli.integration.test.js --runInBand`
14
+ - `node scripts/release-doc-version-audit.js --fail-on-error`
15
+ - `node scripts/collab-governance-gate.js --fail-on-violation --json`
16
+
17
+ ## 发布说明
18
+
19
+ - 这个补丁版把“共享错题经验”从配置层能力补成了真正可落地的同项目跨电脑延续机制。历史 spec 仍按原方式走 Git 共享,而高价值 `verified/promoted` 错题经验则通过单独的受控投影文件同步,不再把 `.sce/errorbook/**` 运行态数据直接带入版本库。
@@ -18,6 +18,15 @@ const TEMPORARY_MITIGATION_TAG = 'temporary-mitigation';
18
18
  const DEFAULT_ERRORBOOK_REGISTRY_CONFIG = '.sce/config/errorbook-registry.json';
19
19
  const DEFAULT_ERRORBOOK_REGISTRY_CACHE = '.sce/errorbook/registry-cache.json';
20
20
  const DEFAULT_ERRORBOOK_REGISTRY_EXPORT = '.sce/errorbook/exports/errorbook-registry-export.json';
21
+ const DEFAULT_PROJECT_SHARED_ERRORBOOK_REGISTRY = '.sce/knowledge/errorbook/project-shared-registry.json';
22
+ const DEFAULT_PROJECT_SHARED_ERRORBOOK_STATUSES = Object.freeze(['verified', 'promoted']);
23
+ const DEFAULT_PROJECT_SHARED_ERRORBOOK_MIN_QUALITY = 75;
24
+ const DEFAULT_PROJECT_SHARED_ERRORBOOK_PROJECTION = Object.freeze({
25
+ enabled: true,
26
+ file: DEFAULT_PROJECT_SHARED_ERRORBOOK_REGISTRY,
27
+ statuses: [...DEFAULT_PROJECT_SHARED_ERRORBOOK_STATUSES],
28
+ min_quality: DEFAULT_PROJECT_SHARED_ERRORBOOK_MIN_QUALITY
29
+ });
21
30
  const STATUS_RANK = Object.freeze({
22
31
  deprecated: 0,
23
32
  candidate: 1,
@@ -107,6 +116,10 @@ function nowIso() {
107
116
  return new Date().toISOString();
108
117
  }
109
118
 
119
+ function cloneJson(value) {
120
+ return JSON.parse(JSON.stringify(value));
121
+ }
122
+
110
123
  function normalizeText(value) {
111
124
  if (typeof value !== 'string') {
112
125
  return '';
@@ -1036,6 +1049,26 @@ function normalizeStatusList(values = [], fallback = ['promoted']) {
1036
1049
  return unique;
1037
1050
  }
1038
1051
 
1052
+ function normalizeProjectSharedProjection(input = {}) {
1053
+ const candidate = input && typeof input === 'object' && !Array.isArray(input)
1054
+ ? input
1055
+ : {};
1056
+ const minQualityCandidate = candidate.min_quality ?? candidate.minQuality;
1057
+ const minQuality = Number.isFinite(Number(minQualityCandidate))
1058
+ ? Math.max(0, Math.min(100, Number(minQualityCandidate)))
1059
+ : DEFAULT_PROJECT_SHARED_ERRORBOOK_MIN_QUALITY;
1060
+
1061
+ return {
1062
+ enabled: normalizeBoolean(candidate.enabled, true),
1063
+ file: normalizeText(candidate.file || candidate.out || candidate.path || DEFAULT_PROJECT_SHARED_ERRORBOOK_REGISTRY),
1064
+ statuses: normalizeStatusList(
1065
+ candidate.statuses || candidate.status || DEFAULT_PROJECT_SHARED_ERRORBOOK_STATUSES,
1066
+ DEFAULT_PROJECT_SHARED_ERRORBOOK_STATUSES
1067
+ ),
1068
+ min_quality: minQuality
1069
+ };
1070
+ }
1071
+
1039
1072
  function normalizeRegistrySource(input = {}) {
1040
1073
  const candidate = input || {};
1041
1074
  const name = normalizeText(candidate.name) || 'default';
@@ -1067,7 +1100,8 @@ async function readErrorbookRegistryConfig(paths, fileSystem = fs) {
1067
1100
  enabled: false,
1068
1101
  search_mode: 'cache',
1069
1102
  cache_file: DEFAULT_ERRORBOOK_REGISTRY_CACHE,
1070
- sources: []
1103
+ sources: [],
1104
+ project_shared_projection: cloneJson(DEFAULT_PROJECT_SHARED_ERRORBOOK_PROJECTION)
1071
1105
  };
1072
1106
  if (!await fileSystem.pathExists(paths.configFile)) {
1073
1107
  return fallback;
@@ -1083,7 +1117,55 @@ async function readErrorbookRegistryConfig(paths, fileSystem = fs) {
1083
1117
  enabled: normalizeBoolean(payload.enabled, true),
1084
1118
  search_mode: normalizeRegistryMode(payload.search_mode || payload.searchMode, 'cache'),
1085
1119
  cache_file: normalizeText(payload.cache_file || payload.cacheFile || DEFAULT_ERRORBOOK_REGISTRY_CACHE),
1086
- sources
1120
+ sources,
1121
+ project_shared_projection: normalizeProjectSharedProjection(
1122
+ payload.project_shared_projection || payload.projectSharedProjection
1123
+ )
1124
+ };
1125
+ }
1126
+
1127
+ async function refreshProjectSharedErrorbookProjection(projectPath, fileSystem = fs, options = {}) {
1128
+ const registryPaths = resolveErrorbookRegistryPaths(projectPath, {
1129
+ configPath: options.config
1130
+ });
1131
+ const registryConfig = await readErrorbookRegistryConfig(registryPaths, fileSystem);
1132
+ const projection = normalizeProjectSharedProjection(
1133
+ options.projectSharedProjection || registryConfig.project_shared_projection
1134
+ );
1135
+ const projectionFile = resolveProjectPath(
1136
+ projectPath,
1137
+ projection.file,
1138
+ DEFAULT_PROJECT_SHARED_ERRORBOOK_REGISTRY
1139
+ );
1140
+
1141
+ if (projection.enabled !== true) {
1142
+ return {
1143
+ enabled: false,
1144
+ refreshed: false,
1145
+ file: projectionFile,
1146
+ statuses: projection.statuses,
1147
+ min_quality: projection.min_quality,
1148
+ total_entries: 0
1149
+ };
1150
+ }
1151
+
1152
+ const exportResult = await runErrorbookExportCommand({
1153
+ out: projection.file,
1154
+ status: projection.statuses.join(','),
1155
+ minQuality: projection.min_quality,
1156
+ silent: true
1157
+ }, {
1158
+ projectPath,
1159
+ fileSystem
1160
+ });
1161
+
1162
+ return {
1163
+ enabled: true,
1164
+ refreshed: true,
1165
+ file: exportResult.out_file,
1166
+ statuses: exportResult.statuses,
1167
+ min_quality: exportResult.min_quality,
1168
+ total_entries: exportResult.total_entries
1087
1169
  };
1088
1170
  }
1089
1171
 
@@ -1322,6 +1404,7 @@ async function searchRegistryRemote(options = {}, dependencies = {}) {
1322
1404
 
1323
1405
  const warnings = [];
1324
1406
  let shardSources = [];
1407
+ const localFileSource = !isHttpSource(source.source);
1325
1408
  if (source.index_url) {
1326
1409
  try {
1327
1410
  const indexPayload = await loadRegistryPayload(projectPath, source.index_url, fileSystem);
@@ -1332,7 +1415,10 @@ async function searchRegistryRemote(options = {}, dependencies = {}) {
1332
1415
  }
1333
1416
 
1334
1417
  if (shardSources.length === 0) {
1335
- if (allowRemoteFullscan) {
1418
+ if (localFileSource) {
1419
+ shardSources = [source.source];
1420
+ warnings.push('local registry file source scanned directly without index');
1421
+ } else if (allowRemoteFullscan) {
1336
1422
  shardSources = [source.source];
1337
1423
  warnings.push('remote index unavailable; fallback to full-source scan');
1338
1424
  } else {
@@ -1769,13 +1855,15 @@ async function runErrorbookRecordCommand(options = {}, dependencies = {}) {
1769
1855
  const incidentLoop = await syncIncidentLoopForRecord(paths, normalized, entry, {
1770
1856
  nowIso: entry.updated_at
1771
1857
  }, fileSystem);
1858
+ const projectSharedProjection = await refreshProjectSharedErrorbookProjection(projectPath, fileSystem, options);
1772
1859
 
1773
1860
  const result = {
1774
1861
  mode: 'errorbook-record',
1775
1862
  created,
1776
1863
  deduplicated,
1777
1864
  entry,
1778
- incident_loop: incidentLoop
1865
+ incident_loop: incidentLoop,
1866
+ project_shared_projection: projectSharedProjection
1779
1867
  };
1780
1868
 
1781
1869
  if (options.json) {
@@ -2575,11 +2663,13 @@ async function runErrorbookPromoteCommand(options = {}, dependencies = {}) {
2575
2663
  }
2576
2664
  index.entries.sort((left, right) => `${right.updated_at}`.localeCompare(`${left.updated_at}`));
2577
2665
  await writeErrorbookIndex(paths, index, fileSystem);
2666
+ const projectSharedProjection = await refreshProjectSharedErrorbookProjection(projectPath, fileSystem, options);
2578
2667
 
2579
2668
  const result = {
2580
2669
  mode: 'errorbook-promote',
2581
2670
  promoted: true,
2582
- entry
2671
+ entry,
2672
+ project_shared_projection: projectSharedProjection
2583
2673
  };
2584
2674
 
2585
2675
  if (options.json) {
@@ -2672,11 +2762,13 @@ async function runErrorbookDeprecateCommand(options = {}, dependencies = {}) {
2672
2762
  }
2673
2763
  index.entries.sort((left, right) => `${right.updated_at}`.localeCompare(`${left.updated_at}`));
2674
2764
  await writeErrorbookIndex(paths, index, fileSystem);
2765
+ const projectSharedProjection = await refreshProjectSharedErrorbookProjection(projectPath, fileSystem, options);
2675
2766
 
2676
2767
  const result = {
2677
2768
  mode: 'errorbook-deprecate',
2678
2769
  deprecated: true,
2679
- entry
2770
+ entry,
2771
+ project_shared_projection: projectSharedProjection
2680
2772
  };
2681
2773
 
2682
2774
  if (options.json) {
@@ -2744,11 +2836,13 @@ async function runErrorbookRequalifyCommand(options = {}, dependencies = {}) {
2744
2836
  }
2745
2837
  index.entries.sort((left, right) => `${right.updated_at}`.localeCompare(`${left.updated_at}`));
2746
2838
  await writeErrorbookIndex(paths, index, fileSystem);
2839
+ const projectSharedProjection = await refreshProjectSharedErrorbookProjection(projectPath, fileSystem, options);
2747
2840
 
2748
2841
  const result = {
2749
2842
  mode: 'errorbook-requalify',
2750
2843
  requalified: true,
2751
- entry
2844
+ entry,
2845
+ project_shared_projection: projectSharedProjection
2752
2846
  };
2753
2847
 
2754
2848
  if (options.json) {
@@ -3009,6 +3103,8 @@ module.exports = {
3009
3103
  DEFAULT_ERRORBOOK_REGISTRY_CONFIG,
3010
3104
  DEFAULT_ERRORBOOK_REGISTRY_CACHE,
3011
3105
  DEFAULT_ERRORBOOK_REGISTRY_EXPORT,
3106
+ DEFAULT_PROJECT_SHARED_ERRORBOOK_REGISTRY,
3107
+ DEFAULT_PROJECT_SHARED_ERRORBOOK_PROJECTION,
3012
3108
  HIGH_RISK_SIGNAL_TAGS,
3013
3109
  DEBUG_EVIDENCE_TAGS,
3014
3110
  DEFAULT_PROMOTE_MIN_QUALITY,
@@ -3035,5 +3131,6 @@ module.exports = {
3035
3131
  runErrorbookReleaseGateCommand,
3036
3132
  runErrorbookDeprecateCommand,
3037
3133
  runErrorbookRequalifyCommand,
3134
+ refreshProjectSharedErrorbookProjection,
3038
3135
  registerErrorbookCommands
3039
3136
  };
@@ -51,6 +51,8 @@ const ACTIVE_TEXT_SCAN_EXCLUDES = Object.freeze([
51
51
  const LEGACY_REFERENCE_REGEX = /\.kiro(?:[\\/]|-workspaces\b)/;
52
52
  const MULTI_AGENT_CONFIG_REFERENCE = '.sce/config/multi-agent.json';
53
53
  const ERRORBOOK_REGISTRY_CONFIG_PATH = '.sce/config/errorbook-registry.json';
54
+ const PROJECT_SHARED_ERRORBOOK_REGISTRY_PATH = '.sce/knowledge/errorbook/project-shared-registry.json';
55
+ const PROJECT_SHARED_ERRORBOOK_SOURCE_NAME = 'project-shared';
54
56
  const ADOPTION_CONFIG_PATH = '.sce/adoption-config.json';
55
57
 
56
58
  const REQUIRED_GITIGNORE_RULES = Object.freeze([
@@ -396,6 +398,24 @@ function validateErrorbookRegistryConfig(payload) {
396
398
  violations.push('errorbook registry config must declare non-empty field "cache_file"');
397
399
  }
398
400
 
401
+ const projection = payload.project_shared_projection;
402
+ if (!projection || typeof projection !== 'object' || Array.isArray(projection)) {
403
+ violations.push('errorbook registry config must declare object field "project_shared_projection"');
404
+ } else {
405
+ if (projection.enabled !== true) {
406
+ violations.push('errorbook registry config must keep project_shared_projection.enabled=true under co-work baseline');
407
+ }
408
+ if (typeof projection.file !== 'string' || !projection.file.trim()) {
409
+ violations.push('errorbook registry config must declare non-empty project_shared_projection.file');
410
+ }
411
+ if (!Array.isArray(projection.statuses) || projection.statuses.length === 0) {
412
+ violations.push('errorbook registry config must declare non-empty project_shared_projection.statuses');
413
+ }
414
+ if (!Number.isFinite(Number(projection.min_quality))) {
415
+ violations.push('errorbook registry config must declare numeric project_shared_projection.min_quality');
416
+ }
417
+ }
418
+
399
419
  const sources = Array.isArray(payload.sources) ? payload.sources : [];
400
420
  if (sources.length === 0) {
401
421
  violations.push('errorbook registry config must declare at least one registry source');
@@ -407,18 +427,38 @@ function validateErrorbookRegistryConfig(payload) {
407
427
  return false;
408
428
  }
409
429
  const enabled = item.enabled !== false;
410
- const url = typeof item.url === 'string' ? item.url.trim() : '';
411
- return enabled && Boolean(url);
430
+ const source = typeof item.url === 'string' && item.url.trim()
431
+ ? item.url.trim()
432
+ : typeof item.file === 'string' && item.file.trim()
433
+ ? item.file.trim()
434
+ : typeof item.path === 'string' && item.path.trim()
435
+ ? item.path.trim()
436
+ : '';
437
+ return enabled && Boolean(source);
412
438
  });
413
439
 
414
440
  if (enabledSources.length === 0) {
415
- violations.push('errorbook registry config must keep at least one enabled source with a non-empty "url"');
441
+ violations.push('errorbook registry config must keep at least one enabled source with a non-empty url/file');
442
+ }
443
+
444
+ if (projection && typeof projection === 'object' && !Array.isArray(projection)) {
445
+ const hasProjectSharedSource = enabledSources.some((item) => {
446
+ const name = typeof item.name === 'string' ? item.name.trim() : '';
447
+ const file = typeof item.file === 'string' ? item.file.trim() : '';
448
+ const sourcePath = typeof item.path === 'string' ? item.path.trim() : '';
449
+ return name === PROJECT_SHARED_ERRORBOOK_SOURCE_NAME
450
+ || file === projection.file
451
+ || sourcePath === projection.file;
452
+ });
453
+ if (!hasProjectSharedSource) {
454
+ violations.push('errorbook registry config must keep an enabled project-shared source aligned with project_shared_projection.file');
455
+ }
416
456
  }
417
457
 
418
458
  return violations;
419
459
  }
420
460
 
421
- async function inspectErrorbookRegistry(projectRoot, options = {}, dependencies = {}) {
461
+ async function inspectErrorbookRegistry(projectRoot, gitSnapshot, options = {}, dependencies = {}) {
422
462
  const fileSystem = dependencies.fileSystem || fs;
423
463
  const configPath = path.join(projectRoot, '.sce', 'config', 'errorbook-registry.json');
424
464
  const exists = await fileSystem.pathExists(configPath);
@@ -428,6 +468,9 @@ async function inspectErrorbookRegistry(projectRoot, options = {}, dependencies
428
468
  valid: false,
429
469
  enabled: null,
430
470
  enabled_source_count: 0,
471
+ project_shared_projection_file: PROJECT_SHARED_ERRORBOOK_REGISTRY_PATH,
472
+ project_shared_projection_exists: false,
473
+ project_shared_projection_tracked: null,
431
474
  warnings: [],
432
475
  violations: [],
433
476
  passed: true,
@@ -454,8 +497,23 @@ async function inspectErrorbookRegistry(projectRoot, options = {}, dependencies
454
497
  const validationErrors = validateErrorbookRegistryConfig(payload);
455
498
  report.valid = validationErrors.length === 0;
456
499
  report.enabled = typeof payload.enabled === 'boolean' ? payload.enabled : null;
500
+ report.project_shared_projection_file = payload
501
+ && payload.project_shared_projection
502
+ && typeof payload.project_shared_projection.file === 'string'
503
+ && payload.project_shared_projection.file.trim()
504
+ ? normalizeRelativePath(projectRoot, payload.project_shared_projection.file.trim()) || payload.project_shared_projection.file.trim()
505
+ : PROJECT_SHARED_ERRORBOOK_REGISTRY_PATH;
457
506
  report.enabled_source_count = Array.isArray(payload.sources)
458
- ? payload.sources.filter((item) => item && typeof item === 'object' && item.enabled !== false && typeof item.url === 'string' && item.url.trim()).length
507
+ ? payload.sources.filter((item) => {
508
+ if (!item || typeof item !== 'object' || Array.isArray(item) || item.enabled === false) {
509
+ return false;
510
+ }
511
+ return (
512
+ (typeof item.url === 'string' && item.url.trim())
513
+ || (typeof item.file === 'string' && item.file.trim())
514
+ || (typeof item.path === 'string' && item.path.trim())
515
+ );
516
+ }).length
459
517
  : 0;
460
518
 
461
519
  if (validationErrors.length > 0) {
@@ -465,6 +523,41 @@ async function inspectErrorbookRegistry(projectRoot, options = {}, dependencies
465
523
  return report;
466
524
  }
467
525
 
526
+ const projectionFile = report.project_shared_projection_file;
527
+ const projectionAbsolutePath = path.isAbsolute(projectionFile)
528
+ ? projectionFile
529
+ : path.join(projectRoot, projectionFile);
530
+ report.project_shared_projection_exists = await fileSystem.pathExists(projectionAbsolutePath);
531
+ if (!report.project_shared_projection_exists) {
532
+ report.passed = false;
533
+ report.reason = 'missing-project-shared-projection';
534
+ report.violations.push(`shared project errorbook projection file is missing: ${projectionFile}`);
535
+ return report;
536
+ }
537
+
538
+ try {
539
+ const projectionPayload = await fileSystem.readJson(projectionAbsolutePath);
540
+ if (!projectionPayload || typeof projectionPayload !== 'object' || Array.isArray(projectionPayload)) {
541
+ report.violations.push(`shared project errorbook projection must be a JSON object: ${projectionFile}`);
542
+ } else if (!Array.isArray(projectionPayload.entries)) {
543
+ report.violations.push(`shared project errorbook projection must declare entries[]: ${projectionFile}`);
544
+ }
545
+ } catch (error) {
546
+ report.violations.push(`invalid shared project errorbook projection: ${error.message}`);
547
+ }
548
+
549
+ if (gitSnapshot && gitSnapshot.available === true) {
550
+ report.project_shared_projection_tracked = gitSnapshot.tracked_files.has(projectionFile);
551
+ if (report.project_shared_projection_tracked !== true) {
552
+ report.violations.push(`shared project errorbook projection must be tracked by git: ${projectionFile}`);
553
+ }
554
+ }
555
+
556
+ if (report.violations.length > 0) {
557
+ report.passed = false;
558
+ report.reason = 'invalid-config';
559
+ }
560
+
468
561
  return report;
469
562
  }
470
563
 
@@ -652,7 +745,7 @@ async function auditCollabGovernance(projectRoot = process.cwd(), options = {},
652
745
  const runtimeTracking = inspectRuntimeTracking(gitSnapshot);
653
746
  const scanResult = await scanActiveTextReferences(projectRoot, gitSnapshot.tracked_files, options, dependencies);
654
747
  const multiAgent = await inspectMultiAgentConfig(projectRoot, scanResult, options, dependencies);
655
- const errorbookRegistry = await inspectErrorbookRegistry(projectRoot, options, dependencies);
748
+ const errorbookRegistry = await inspectErrorbookRegistry(projectRoot, gitSnapshot, options, dependencies);
656
749
  const errorbookConvergence = await inspectErrorbookConvergence(projectRoot, options, dependencies);
657
750
  const steeringBoundary = inspectSteeringBoundary(projectRoot);
658
751
 
@@ -101,12 +101,23 @@ const ERRORBOOK_REGISTRY_DEFAULTS = Object.freeze({
101
101
  enabled: true,
102
102
  search_mode: 'remote',
103
103
  cache_file: '.sce/errorbook/registry-cache.json',
104
+ project_shared_projection: {
105
+ enabled: true,
106
+ file: '.sce/knowledge/errorbook/project-shared-registry.json',
107
+ statuses: ['verified', 'promoted'],
108
+ min_quality: 75
109
+ },
104
110
  sources: [
105
111
  {
106
112
  name: 'central',
107
113
  enabled: true,
108
114
  url: 'https://raw.githubusercontent.com/heguangyong/sce-errorbook-registry/main/registry/errorbook-registry.json',
109
115
  index_url: 'https://raw.githubusercontent.com/heguangyong/sce-errorbook-registry/main/registry/errorbook-registry.index.json'
116
+ },
117
+ {
118
+ name: 'project-shared',
119
+ enabled: true,
120
+ file: '.sce/knowledge/errorbook/project-shared-registry.json'
110
121
  }
111
122
  ]
112
123
  });
@@ -501,12 +512,68 @@ function _buildTakeoverBaselineConfig(existing, sceVersion) {
501
512
 
502
513
  function _buildErrorbookRegistryConfig(existing) {
503
514
  const base = _isObject(existing) ? _clone(existing) : {};
515
+ const projectSharedProjection = _deepMerge(
516
+ _clone(ERRORBOOK_REGISTRY_DEFAULTS.project_shared_projection),
517
+ _isObject(base.project_shared_projection) ? base.project_shared_projection : {}
518
+ );
519
+ const existingSources = Array.isArray(base.sources) && base.sources.length > 0
520
+ ? _clone(base.sources)
521
+ : _clone(ERRORBOOK_REGISTRY_DEFAULTS.sources);
522
+ const hasProjectSharedSource = existingSources.some((item) => {
523
+ if (!_isObject(item)) {
524
+ return false;
525
+ }
526
+ const name = typeof item.name === 'string' ? item.name.trim() : '';
527
+ const file = typeof item.file === 'string' ? item.file.trim() : '';
528
+ const sourcePath = typeof item.path === 'string' ? item.path.trim() : '';
529
+ return name === 'project-shared'
530
+ || file === projectSharedProjection.file
531
+ || sourcePath === projectSharedProjection.file;
532
+ });
533
+ if (!hasProjectSharedSource) {
534
+ existingSources.push({
535
+ name: 'project-shared',
536
+ enabled: true,
537
+ file: projectSharedProjection.file
538
+ });
539
+ }
504
540
  return {
505
541
  ..._clone(ERRORBOOK_REGISTRY_DEFAULTS),
506
542
  ...base,
507
- sources: Array.isArray(base.sources) && base.sources.length > 0
508
- ? _clone(base.sources)
509
- : _clone(ERRORBOOK_REGISTRY_DEFAULTS.sources)
543
+ project_shared_projection: projectSharedProjection,
544
+ sources: existingSources
545
+ };
546
+ }
547
+
548
+ function _buildProjectSharedErrorbookRegistry(existing, projectPath, nowIso, config = {}) {
549
+ const projection = _isObject(config.project_shared_projection)
550
+ ? config.project_shared_projection
551
+ : _clone(ERRORBOOK_REGISTRY_DEFAULTS.project_shared_projection);
552
+ const fallback = {
553
+ api_version: 'sce.errorbook.registry/v0.1',
554
+ generated_at: nowIso,
555
+ source: {
556
+ project: path.basename(projectPath),
557
+ scope: 'project-shared',
558
+ statuses: Array.isArray(projection.statuses) ? projection.statuses : ['verified', 'promoted'],
559
+ min_quality: Number.isFinite(Number(projection.min_quality)) ? Number(projection.min_quality) : 75
560
+ },
561
+ total_entries: 0,
562
+ entries: []
563
+ };
564
+ if (!_isObject(existing) || !Array.isArray(existing.entries)) {
565
+ return fallback;
566
+ }
567
+ return {
568
+ ...fallback,
569
+ ...existing,
570
+ source: _isObject(existing.source)
571
+ ? {
572
+ ...fallback.source,
573
+ ...existing.source
574
+ }
575
+ : fallback.source,
576
+ total_entries: Array.isArray(existing.entries) ? existing.entries.length : fallback.total_entries
510
577
  };
511
578
  }
512
579
 
@@ -838,6 +905,21 @@ async function applyTakeoverBaseline(projectPath = process.cwd(), options = {})
838
905
  const desiredAutoConfig = _buildAutoConfig(existingAuto);
839
906
  const desiredTakeover = _buildTakeoverBaselineConfig(existingTakeover, sceVersion);
840
907
  const desiredErrorbookRegistry = _buildErrorbookRegistryConfig(existingErrorbookRegistry);
908
+ const projectSharedRegistryRelativePath = _isObject(desiredErrorbookRegistry.project_shared_projection)
909
+ && typeof desiredErrorbookRegistry.project_shared_projection.file === 'string'
910
+ && desiredErrorbookRegistry.project_shared_projection.file.trim()
911
+ ? desiredErrorbookRegistry.project_shared_projection.file.trim()
912
+ : ERRORBOOK_REGISTRY_DEFAULTS.project_shared_projection.file;
913
+ const projectSharedErrorbookRegistryPath = path.isAbsolute(projectSharedRegistryRelativePath)
914
+ ? projectSharedRegistryRelativePath
915
+ : path.join(projectPath, projectSharedRegistryRelativePath);
916
+ const existingProjectSharedErrorbookRegistry = await _readJsonSafe(projectSharedErrorbookRegistryPath, fileSystem);
917
+ const desiredProjectSharedErrorbookRegistry = _buildProjectSharedErrorbookRegistry(
918
+ existingProjectSharedErrorbookRegistry,
919
+ projectPath,
920
+ nowIso,
921
+ desiredErrorbookRegistry
922
+ );
841
923
  const desiredMultiAgentConfig = _buildMultiAgentConfig(existingMultiAgentConfig);
842
924
  const desiredSessionGovernance = _deepMerge(existingSessionGovernance || {}, SESSION_GOVERNANCE_DEFAULTS);
843
925
  const desiredSpecDomainPolicy = _deepMerge(existingSpecDomainPolicy || {}, SPEC_DOMAIN_POLICY_DEFAULTS);
@@ -869,6 +951,11 @@ async function applyTakeoverBaseline(projectPath = process.cwd(), options = {})
869
951
  apply,
870
952
  fileSystem
871
953
  }));
954
+ fileResults.push(await _reconcileJsonFile(projectSharedErrorbookRegistryPath, desiredProjectSharedErrorbookRegistry, {
955
+ projectPath,
956
+ apply,
957
+ fileSystem
958
+ }));
872
959
  fileResults.push(await _reconcileJsonFile(multiAgentConfigPath, desiredMultiAgentConfig, {
873
960
  projectPath,
874
961
  apply,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.52",
3
+ "version": "3.6.53",
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": {
@@ -243,6 +243,6 @@ A Spec is a complete feature definition with three parts:
243
243
  ---
244
244
 
245
245
  **Project Type**: Spec-driven development
246
- **sce Version**: 3.6.52
246
+ **sce Version**: 3.6.53
247
247
  **Last Updated**: 2026-03-16
248
248
  **Purpose**: Guide AI tools to work effectively with this project
@@ -2,12 +2,26 @@
2
2
  "enabled": true,
3
3
  "search_mode": "remote",
4
4
  "cache_file": ".sce/errorbook/registry-cache.json",
5
+ "project_shared_projection": {
6
+ "enabled": true,
7
+ "file": ".sce/knowledge/errorbook/project-shared-registry.json",
8
+ "statuses": [
9
+ "verified",
10
+ "promoted"
11
+ ],
12
+ "min_quality": 75
13
+ },
5
14
  "sources": [
6
15
  {
7
16
  "name": "central",
8
17
  "enabled": true,
9
18
  "url": "https://raw.githubusercontent.com/heguangyong/sce-errorbook-registry/main/registry/errorbook-registry.json",
10
19
  "index_url": "https://raw.githubusercontent.com/heguangyong/sce-errorbook-registry/main/registry/errorbook-registry.index.json"
20
+ },
21
+ {
22
+ "name": "project-shared",
23
+ "enabled": true,
24
+ "file": ".sce/knowledge/errorbook/project-shared-registry.json"
11
25
  }
12
26
  ]
13
27
  }
@@ -0,0 +1,15 @@
1
+ {
2
+ "api_version": "sce.errorbook.registry/v0.1",
3
+ "generated_at": "2026-03-16T00:00:00.000Z",
4
+ "source": {
5
+ "project": "template",
6
+ "scope": "project-shared",
7
+ "statuses": [
8
+ "verified",
9
+ "promoted"
10
+ ],
11
+ "min_quality": 75
12
+ },
13
+ "total_entries": 0,
14
+ "entries": []
15
+ }