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 +9 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/docs/command-reference.md +4 -2
- package/docs/errorbook-registry.md +24 -5
- package/docs/releases/v3.6.53.md +19 -0
- package/docs/zh/releases/v3.6.53.md +19 -0
- package/lib/commands/errorbook.js +104 -7
- package/lib/workspace/collab-governance-audit.js +99 -6
- package/lib/workspace/takeover-baseline.js +90 -3
- package/package.json +1 -1
- package/template/.sce/README.md +1 -1
- package/template/.sce/config/errorbook-registry.json +14 -0
- package/template/.sce/knowledge/errorbook/project-shared-registry.json +15 -0
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
package/README.zh.md
CHANGED
|
@@ -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
|
-
-
|
|
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.
|
|
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
|
-
|
|
117
|
+
3. Merge approved entries into central repo `registry/errorbook-registry.json`.
|
|
100
118
|
|
|
101
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
411
|
-
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
package/template/.sce/README.md
CHANGED
|
@@ -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.
|
|
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
|
+
}
|