scene-capability-engine 3.6.53 → 3.6.54
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 +6 -1
- package/docs/releases/v3.6.54.md +19 -0
- package/docs/zh/releases/v3.6.54.md +19 -0
- package/lib/problem/project-problem-projection.js +239 -0
- package/lib/workspace/collab-governance-audit.js +107 -0
- package/lib/workspace/collab-governance-gate.js +24 -4
- package/lib/workspace/takeover-baseline.js +76 -0
- package/package.json +1 -1
- package/template/.sce/README.md +1 -1
- package/template/.sce/config/problem-closure-policy.json +5 -0
- package/template/.sce/knowledge/problem/project-shared-problems.json +16 -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.54] - 2026-03-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Added a tracked project-shared problem projection at `.sce/knowledge/problem/project-shared-problems.json`, derived from existing spec problem artifacts and seeded by default through template/takeover baselines.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Managed co-work gate now refreshes the project-shared problem projection before collaboration governance audit, so another computer can recover active/stale project problem facts through normal Git sync without asking for a separate local problem-library sync step.
|
|
17
|
+
- Collaboration governance auditing now treats project-shared problem projection drift as a violation alongside errorbook and multi-agent baseline drift.
|
|
18
|
+
|
|
10
19
|
## [3.6.53] - 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, missing tracked project-shared errorbook
|
|
138
|
+
- `timeline push` now refreshes the tracked project-shared problem projection before governance audit, then 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/problem projections, 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
|
|
|
@@ -791,6 +791,11 @@ Studio gate execution defaults:
|
|
|
791
791
|
Problem closure gate (default policy):
|
|
792
792
|
- Script: `node scripts/problem-closure-gate.js`
|
|
793
793
|
- Policy file: `.sce/config/problem-closure-policy.json` (auto-provisioned by `init/adopt/takeover`)
|
|
794
|
+
- Shared problem projection:
|
|
795
|
+
- config field: `project_shared_projection`
|
|
796
|
+
- tracked file: `.sce/knowledge/problem/project-shared-problems.json`
|
|
797
|
+
- default scope: `non_completed`
|
|
798
|
+
- managed co-work gate refreshes this file automatically before governance audit/push/publish
|
|
794
799
|
- Checks:
|
|
795
800
|
- verify stage: `problem-contract` + spec domain validation + domain coverage
|
|
796
801
|
- release stage: verify checks + verify report pass signal + governance high-alert block (configurable)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# v3.6.54 Release Notes
|
|
2
|
+
|
|
3
|
+
Release date: 2026-03-16
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- Added a tracked project-shared problem projection at `.sce/knowledge/problem/project-shared-problems.json`, built from existing spec `problem-contract` and `problem-domain-chain` artifacts instead of introducing a parallel local problem library.
|
|
8
|
+
- Managed co-work gate now refreshes that shared problem projection automatically before governance audit, so cross-computer continuation does not depend on manual reminders to sync local problem context.
|
|
9
|
+
- Collaboration governance now blocks when the shared project problem projection is missing, invalid, disabled, or not Git-tracked.
|
|
10
|
+
|
|
11
|
+
## Validation
|
|
12
|
+
|
|
13
|
+
- `npx jest tests/unit/problem/project-problem-projection.test.js tests/unit/scripts/collab-governance-gate.test.js tests/unit/workspace/collab-governance-audit.test.js tests/unit/workspace/takeover-baseline.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 completes the same-project co-work continuity line for problem knowledge. Historical specs were already shared by Git, and high-value errorbook knowledge was closed in `3.6.53`; `3.6.54` closes the remaining gap by projecting active/stale spec problem facts into a tracked shared file instead of relying on a machine-local problem library.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# v3.6.54 发布说明
|
|
2
|
+
|
|
3
|
+
发布日期:2026-03-16
|
|
4
|
+
|
|
5
|
+
## 重点变化
|
|
6
|
+
|
|
7
|
+
- 新增 Git 跟踪的项目共享问题投影文件 `.sce/knowledge/problem/project-shared-problems.json`,直接从现有 spec 的 `problem-contract` 和 `problem-domain-chain` 生成,而不是再新造一套本地问题库机制。
|
|
8
|
+
- managed co-work gate 现在会在治理审计前自动刷新这份共享问题投影,换电脑继续项目时不再依赖手动提醒“先同步问题库”。
|
|
9
|
+
- 协作治理现在会阻断缺失、禁用、损坏或未纳入 Git 跟踪的项目共享问题投影。
|
|
10
|
+
|
|
11
|
+
## 验证
|
|
12
|
+
|
|
13
|
+
- `npx jest tests/unit/problem/project-problem-projection.test.js tests/unit/scripts/collab-governance-gate.test.js tests/unit/workspace/collab-governance-audit.test.js tests/unit/workspace/takeover-baseline.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 共享,高价值 errorbook 在 `3.6.53` 已闭环,而 `3.6.54` 则把仍在处理中的项目问题事实也投影到受控共享文件中,不再依赖机器本地问题库。
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
loadStudioIntakePolicy,
|
|
8
|
+
scanSpecPortfolio
|
|
9
|
+
} = require('../studio/spec-intake-governor');
|
|
10
|
+
|
|
11
|
+
const PROJECT_SHARED_PROBLEM_PROJECTION_API_VERSION = 'sce.project-problem-projection/v0.1';
|
|
12
|
+
const DEFAULT_PROBLEM_CLOSURE_POLICY_PATH = '.sce/config/problem-closure-policy.json';
|
|
13
|
+
const DEFAULT_PROJECT_SHARED_PROBLEM_FILE = '.sce/knowledge/problem/project-shared-problems.json';
|
|
14
|
+
const DEFAULT_PROJECT_SHARED_PROBLEM_SCOPE = 'non_completed';
|
|
15
|
+
|
|
16
|
+
function normalizeText(value) {
|
|
17
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeProblemProjectionScope(value, fallback = DEFAULT_PROJECT_SHARED_PROBLEM_SCOPE) {
|
|
21
|
+
const normalized = normalizeText(`${value || ''}`).toLowerCase();
|
|
22
|
+
if (!normalized) {
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
if (normalized === 'all' || normalized === 'non_completed' || normalized === 'active_only') {
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
throw new Error('project_shared_projection.scope must be one of: all, non_completed, active_only');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function normalizeProblemProjectionConfig(payload = {}) {
|
|
32
|
+
const candidate = payload && typeof payload === 'object' && !Array.isArray(payload)
|
|
33
|
+
? payload
|
|
34
|
+
: {};
|
|
35
|
+
return {
|
|
36
|
+
enabled: candidate.enabled !== false,
|
|
37
|
+
file: normalizeText(candidate.file || DEFAULT_PROJECT_SHARED_PROBLEM_FILE) || DEFAULT_PROJECT_SHARED_PROBLEM_FILE,
|
|
38
|
+
scope: normalizeProblemProjectionScope(candidate.scope, DEFAULT_PROJECT_SHARED_PROBLEM_SCOPE)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function readProblemClosurePolicy(projectPath = process.cwd(), fileSystem = fs, configPath = DEFAULT_PROBLEM_CLOSURE_POLICY_PATH) {
|
|
43
|
+
const absolutePath = path.isAbsolute(configPath)
|
|
44
|
+
? configPath
|
|
45
|
+
: path.join(projectPath, configPath);
|
|
46
|
+
if (!await fileSystem.pathExists(absolutePath)) {
|
|
47
|
+
return {
|
|
48
|
+
exists: false,
|
|
49
|
+
path: absolutePath,
|
|
50
|
+
payload: null,
|
|
51
|
+
project_shared_projection: normalizeProblemProjectionConfig({})
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const payload = await fileSystem.readJson(absolutePath);
|
|
56
|
+
return {
|
|
57
|
+
exists: true,
|
|
58
|
+
path: absolutePath,
|
|
59
|
+
payload,
|
|
60
|
+
project_shared_projection: normalizeProblemProjectionConfig(payload.project_shared_projection)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function readJsonSafe(filePath, fileSystem = fs) {
|
|
65
|
+
if (!await fileSystem.pathExists(filePath)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return fileSystem.readJson(filePath).catch(() => null);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function pickScopeRecords(records = [], scope = DEFAULT_PROJECT_SHARED_PROBLEM_SCOPE) {
|
|
72
|
+
if (scope === 'all') {
|
|
73
|
+
return records;
|
|
74
|
+
}
|
|
75
|
+
if (scope === 'active_only') {
|
|
76
|
+
return records.filter((item) => item.lifecycle_state === 'active');
|
|
77
|
+
}
|
|
78
|
+
return records.filter((item) => item.lifecycle_state !== 'completed');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toRelativePosix(projectPath, absolutePath) {
|
|
82
|
+
return path.relative(projectPath, absolutePath).replace(/\\/g, '/');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function buildProjectSharedProblemProjection(projectPath = process.cwd(), options = {}, dependencies = {}) {
|
|
86
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
87
|
+
const studioIntakePolicy = dependencies.studioIntakePolicy || await loadStudioIntakePolicy(projectPath, fileSystem);
|
|
88
|
+
const staleDays = Number(
|
|
89
|
+
options.staleDays
|
|
90
|
+
|| studioIntakePolicy?.governance?.stale_days
|
|
91
|
+
|| 14
|
|
92
|
+
);
|
|
93
|
+
const closurePolicy = dependencies.problemClosurePolicyBundle
|
|
94
|
+
|| await readProblemClosurePolicy(projectPath, fileSystem, options.policyPath || DEFAULT_PROBLEM_CLOSURE_POLICY_PATH);
|
|
95
|
+
const projectionConfig = normalizeProblemProjectionConfig(
|
|
96
|
+
options.projectSharedProjection || closurePolicy.project_shared_projection
|
|
97
|
+
);
|
|
98
|
+
const scanOptions = {
|
|
99
|
+
staleDays
|
|
100
|
+
};
|
|
101
|
+
const records = dependencies.specPortfolio
|
|
102
|
+
|| await scanSpecPortfolio(projectPath, scanOptions, { fileSystem });
|
|
103
|
+
const scopedRecords = pickScopeRecords(records, projectionConfig.scope);
|
|
104
|
+
const entries = [];
|
|
105
|
+
|
|
106
|
+
for (const record of scopedRecords) {
|
|
107
|
+
const specRoot = path.join(projectPath, '.sce', 'specs', record.spec_id);
|
|
108
|
+
const contractPath = path.join(specRoot, 'custom', 'problem-contract.json');
|
|
109
|
+
const domainChainPath = path.join(specRoot, 'custom', 'problem-domain-chain.json');
|
|
110
|
+
const contract = await readJsonSafe(contractPath, fileSystem);
|
|
111
|
+
const domainChain = await readJsonSafe(domainChainPath, fileSystem);
|
|
112
|
+
const problemStatement = normalizeText(
|
|
113
|
+
record.problem_statement
|
|
114
|
+
|| (contract && contract.issue_statement)
|
|
115
|
+
|| (domainChain && domainChain.problem && domainChain.problem.statement)
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (!problemStatement) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const summary = domainChain && domainChain.summary && typeof domainChain.summary === 'object'
|
|
123
|
+
? domainChain.summary
|
|
124
|
+
: {};
|
|
125
|
+
const ontologyCounts = summary.ontology_counts && typeof summary.ontology_counts === 'object'
|
|
126
|
+
? summary.ontology_counts
|
|
127
|
+
: {};
|
|
128
|
+
|
|
129
|
+
entries.push({
|
|
130
|
+
spec_id: record.spec_id,
|
|
131
|
+
scene_id: record.scene_id || null,
|
|
132
|
+
lifecycle_state: record.lifecycle_state || null,
|
|
133
|
+
updated_at: record.updated_at || null,
|
|
134
|
+
age_days: Number.isFinite(Number(record.age_days)) ? Number(record.age_days) : null,
|
|
135
|
+
tasks_total: Number.isFinite(Number(record.tasks_total)) ? Number(record.tasks_total) : 0,
|
|
136
|
+
tasks_done: Number.isFinite(Number(record.tasks_done)) ? Number(record.tasks_done) : 0,
|
|
137
|
+
tasks_progress: Number.isFinite(Number(record.tasks_progress)) ? Number(record.tasks_progress) : 0,
|
|
138
|
+
problem_statement: problemStatement,
|
|
139
|
+
expected_outcome: normalizeText(contract && contract.expected_outcome),
|
|
140
|
+
impact_scope: normalizeText(contract && contract.impact_scope),
|
|
141
|
+
reproduction_steps: Array.isArray(contract && contract.reproduction_steps) ? contract.reproduction_steps : [],
|
|
142
|
+
forbidden_workarounds: Array.isArray(contract && contract.forbidden_workarounds)
|
|
143
|
+
? contract.forbidden_workarounds
|
|
144
|
+
: [],
|
|
145
|
+
verification_gates: Array.isArray(summary.verification_gates) ? summary.verification_gates : [],
|
|
146
|
+
ontology_counts: {
|
|
147
|
+
entity: Number(ontologyCounts.entity || 0),
|
|
148
|
+
relation: Number(ontologyCounts.relation || 0),
|
|
149
|
+
business_rule: Number(ontologyCounts.business_rule || 0),
|
|
150
|
+
decision_policy: Number(ontologyCounts.decision_policy || 0),
|
|
151
|
+
execution_flow: Number(ontologyCounts.execution_flow || 0)
|
|
152
|
+
},
|
|
153
|
+
evidence_binding_count: Number(summary.evidence_binding_count || 0),
|
|
154
|
+
hypothesis_count: Number(summary.hypothesis_count || 0),
|
|
155
|
+
risk_count: Number(summary.risk_count || 0),
|
|
156
|
+
paths: {
|
|
157
|
+
problem_contract: await fileSystem.pathExists(contractPath) ? toRelativePosix(projectPath, contractPath) : null,
|
|
158
|
+
problem_domain_chain: await fileSystem.pathExists(domainChainPath) ? toRelativePosix(projectPath, domainChainPath) : null
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
entries.sort((left, right) => String(right.updated_at || '').localeCompare(String(left.updated_at || '')));
|
|
164
|
+
const activeCount = entries.filter((item) => item.lifecycle_state === 'active').length;
|
|
165
|
+
const staleCount = entries.filter((item) => item.lifecycle_state === 'stale').length;
|
|
166
|
+
const completedCount = entries.filter((item) => item.lifecycle_state === 'completed').length;
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
api_version: PROJECT_SHARED_PROBLEM_PROJECTION_API_VERSION,
|
|
170
|
+
generated_at: new Date().toISOString(),
|
|
171
|
+
source: {
|
|
172
|
+
project: path.basename(projectPath),
|
|
173
|
+
stale_days: staleDays,
|
|
174
|
+
scope: projectionConfig.scope
|
|
175
|
+
},
|
|
176
|
+
summary: {
|
|
177
|
+
total_entries: entries.length,
|
|
178
|
+
active_entries: activeCount,
|
|
179
|
+
stale_entries: staleCount,
|
|
180
|
+
completed_entries: completedCount
|
|
181
|
+
},
|
|
182
|
+
entries
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function syncProjectSharedProblemProjection(projectPath = process.cwd(), options = {}, dependencies = {}) {
|
|
187
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
188
|
+
const closurePolicy = dependencies.problemClosurePolicyBundle
|
|
189
|
+
|| await readProblemClosurePolicy(projectPath, fileSystem, options.policyPath || DEFAULT_PROBLEM_CLOSURE_POLICY_PATH);
|
|
190
|
+
const projectionConfig = normalizeProblemProjectionConfig(
|
|
191
|
+
options.projectSharedProjection || closurePolicy.project_shared_projection
|
|
192
|
+
);
|
|
193
|
+
const absolutePath = path.isAbsolute(projectionConfig.file)
|
|
194
|
+
? projectionConfig.file
|
|
195
|
+
: path.join(projectPath, projectionConfig.file);
|
|
196
|
+
|
|
197
|
+
if (projectionConfig.enabled !== true) {
|
|
198
|
+
return {
|
|
199
|
+
mode: 'project-problem-projection-sync',
|
|
200
|
+
enabled: false,
|
|
201
|
+
file: absolutePath,
|
|
202
|
+
scope: projectionConfig.scope,
|
|
203
|
+
total_entries: 0,
|
|
204
|
+
refreshed: false
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const payload = await buildProjectSharedProblemProjection(projectPath, {
|
|
209
|
+
...options,
|
|
210
|
+
projectSharedProjection: projectionConfig
|
|
211
|
+
}, {
|
|
212
|
+
...dependencies,
|
|
213
|
+
problemClosurePolicyBundle: closurePolicy
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await fileSystem.ensureDir(path.dirname(absolutePath));
|
|
217
|
+
await fileSystem.writeJson(absolutePath, payload, { spaces: 2 });
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
mode: 'project-problem-projection-sync',
|
|
221
|
+
enabled: true,
|
|
222
|
+
file: absolutePath,
|
|
223
|
+
scope: projectionConfig.scope,
|
|
224
|
+
total_entries: Number(payload.summary.total_entries || payload.entries.length || 0),
|
|
225
|
+
refreshed: true
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module.exports = {
|
|
230
|
+
PROJECT_SHARED_PROBLEM_PROJECTION_API_VERSION,
|
|
231
|
+
DEFAULT_PROBLEM_CLOSURE_POLICY_PATH,
|
|
232
|
+
DEFAULT_PROJECT_SHARED_PROBLEM_FILE,
|
|
233
|
+
DEFAULT_PROJECT_SHARED_PROBLEM_SCOPE,
|
|
234
|
+
normalizeProblemProjectionScope,
|
|
235
|
+
normalizeProblemProjectionConfig,
|
|
236
|
+
readProblemClosurePolicy,
|
|
237
|
+
buildProjectSharedProblemProjection,
|
|
238
|
+
syncProjectSharedProblemProjection
|
|
239
|
+
};
|
|
@@ -5,6 +5,11 @@ const path = require('path');
|
|
|
5
5
|
const { minimatch } = require('minimatch');
|
|
6
6
|
const { loadGitSnapshot } = require('./spec-delivery-audit');
|
|
7
7
|
const SteeringComplianceChecker = require('../steering/steering-compliance-checker');
|
|
8
|
+
const {
|
|
9
|
+
normalizeProblemProjectionConfig,
|
|
10
|
+
DEFAULT_PROBLEM_CLOSURE_POLICY_PATH,
|
|
11
|
+
DEFAULT_PROJECT_SHARED_PROBLEM_FILE
|
|
12
|
+
} = require('../problem/project-problem-projection');
|
|
8
13
|
|
|
9
14
|
const MAX_SCAN_BYTES = 256 * 1024;
|
|
10
15
|
const ACTIVE_TEXT_EXTENSIONS = new Set([
|
|
@@ -53,6 +58,8 @@ const MULTI_AGENT_CONFIG_REFERENCE = '.sce/config/multi-agent.json';
|
|
|
53
58
|
const ERRORBOOK_REGISTRY_CONFIG_PATH = '.sce/config/errorbook-registry.json';
|
|
54
59
|
const PROJECT_SHARED_ERRORBOOK_REGISTRY_PATH = '.sce/knowledge/errorbook/project-shared-registry.json';
|
|
55
60
|
const PROJECT_SHARED_ERRORBOOK_SOURCE_NAME = 'project-shared';
|
|
61
|
+
const PROBLEM_CLOSURE_POLICY_PATH = DEFAULT_PROBLEM_CLOSURE_POLICY_PATH;
|
|
62
|
+
const PROJECT_SHARED_PROBLEM_FILE_PATH = DEFAULT_PROJECT_SHARED_PROBLEM_FILE;
|
|
56
63
|
const ADOPTION_CONFIG_PATH = '.sce/adoption-config.json';
|
|
57
64
|
|
|
58
65
|
const REQUIRED_GITIGNORE_RULES = Object.freeze([
|
|
@@ -625,6 +632,101 @@ async function inspectErrorbookConvergence(projectRoot, options = {}, dependenci
|
|
|
625
632
|
return report;
|
|
626
633
|
}
|
|
627
634
|
|
|
635
|
+
async function inspectProjectProblemProjection(projectRoot, gitSnapshot, options = {}, dependencies = {}) {
|
|
636
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
637
|
+
const policyPath = path.join(projectRoot, PROBLEM_CLOSURE_POLICY_PATH);
|
|
638
|
+
const exists = await fileSystem.pathExists(policyPath);
|
|
639
|
+
const report = {
|
|
640
|
+
file: PROBLEM_CLOSURE_POLICY_PATH,
|
|
641
|
+
exists,
|
|
642
|
+
valid: false,
|
|
643
|
+
project_shared_projection_file: PROJECT_SHARED_PROBLEM_FILE_PATH,
|
|
644
|
+
project_shared_projection_exists: false,
|
|
645
|
+
project_shared_projection_tracked: null,
|
|
646
|
+
warnings: [],
|
|
647
|
+
violations: [],
|
|
648
|
+
passed: true,
|
|
649
|
+
reason: 'passed'
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
if (!exists) {
|
|
653
|
+
report.passed = false;
|
|
654
|
+
report.reason = 'missing-config';
|
|
655
|
+
report.violations.push('problem closure policy is missing');
|
|
656
|
+
return report;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
let payload;
|
|
660
|
+
try {
|
|
661
|
+
payload = await fileSystem.readJson(policyPath);
|
|
662
|
+
} catch (error) {
|
|
663
|
+
report.passed = false;
|
|
664
|
+
report.reason = 'invalid-json';
|
|
665
|
+
report.violations.push(`invalid problem closure policy: ${error.message}`);
|
|
666
|
+
return report;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
670
|
+
report.passed = false;
|
|
671
|
+
report.reason = 'invalid-config';
|
|
672
|
+
report.violations.push('problem closure policy must be a JSON object');
|
|
673
|
+
return report;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
let projection;
|
|
677
|
+
try {
|
|
678
|
+
projection = normalizeProblemProjectionConfig(payload.project_shared_projection);
|
|
679
|
+
} catch (error) {
|
|
680
|
+
report.passed = false;
|
|
681
|
+
report.reason = 'invalid-config';
|
|
682
|
+
report.violations.push(error.message);
|
|
683
|
+
return report;
|
|
684
|
+
}
|
|
685
|
+
report.valid = true;
|
|
686
|
+
report.project_shared_projection_file = normalizeRelativePath(projectRoot, projection.file) || projection.file;
|
|
687
|
+
|
|
688
|
+
if (projection.enabled !== true) {
|
|
689
|
+
report.violations.push('problem closure policy must keep project_shared_projection.enabled=true under co-work baseline');
|
|
690
|
+
}
|
|
691
|
+
if (!projection.file) {
|
|
692
|
+
report.violations.push('problem closure policy must declare non-empty project_shared_projection.file');
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const projectionFile = report.project_shared_projection_file;
|
|
696
|
+
const projectionAbsolutePath = path.isAbsolute(projectionFile)
|
|
697
|
+
? projectionFile
|
|
698
|
+
: path.join(projectRoot, projectionFile);
|
|
699
|
+
report.project_shared_projection_exists = await fileSystem.pathExists(projectionAbsolutePath);
|
|
700
|
+
if (!report.project_shared_projection_exists) {
|
|
701
|
+
report.violations.push(`shared project problem projection file is missing: ${projectionFile}`);
|
|
702
|
+
} else {
|
|
703
|
+
try {
|
|
704
|
+
const projectionPayload = await fileSystem.readJson(projectionAbsolutePath);
|
|
705
|
+
if (!projectionPayload || typeof projectionPayload !== 'object' || Array.isArray(projectionPayload)) {
|
|
706
|
+
report.violations.push(`shared project problem projection must be a JSON object: ${projectionFile}`);
|
|
707
|
+
} else if (!Array.isArray(projectionPayload.entries)) {
|
|
708
|
+
report.violations.push(`shared project problem projection must declare entries[]: ${projectionFile}`);
|
|
709
|
+
}
|
|
710
|
+
} catch (error) {
|
|
711
|
+
report.violations.push(`invalid shared project problem projection: ${error.message}`);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (gitSnapshot && gitSnapshot.available === true) {
|
|
716
|
+
report.project_shared_projection_tracked = gitSnapshot.tracked_files.has(projectionFile);
|
|
717
|
+
if (report.project_shared_projection_tracked !== true) {
|
|
718
|
+
report.violations.push(`shared project problem projection must be tracked by git: ${projectionFile}`);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (report.violations.length > 0) {
|
|
723
|
+
report.passed = false;
|
|
724
|
+
report.reason = 'projection-drift';
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return report;
|
|
728
|
+
}
|
|
729
|
+
|
|
628
730
|
async function inspectMultiAgentConfig(projectRoot, scanResult, options = {}, dependencies = {}) {
|
|
629
731
|
const fileSystem = dependencies.fileSystem || fs;
|
|
630
732
|
const configPath = path.join(projectRoot, '.sce', 'config', 'multi-agent.json');
|
|
@@ -747,6 +849,7 @@ async function auditCollabGovernance(projectRoot = process.cwd(), options = {},
|
|
|
747
849
|
const multiAgent = await inspectMultiAgentConfig(projectRoot, scanResult, options, dependencies);
|
|
748
850
|
const errorbookRegistry = await inspectErrorbookRegistry(projectRoot, gitSnapshot, options, dependencies);
|
|
749
851
|
const errorbookConvergence = await inspectErrorbookConvergence(projectRoot, options, dependencies);
|
|
852
|
+
const projectProblemProjection = await inspectProjectProblemProjection(projectRoot, gitSnapshot, options, dependencies);
|
|
750
853
|
const steeringBoundary = inspectSteeringBoundary(projectRoot);
|
|
751
854
|
|
|
752
855
|
const legacyReferences = {
|
|
@@ -773,6 +876,7 @@ async function auditCollabGovernance(projectRoot = process.cwd(), options = {},
|
|
|
773
876
|
multi_agent: multiAgent,
|
|
774
877
|
errorbook_registry: errorbookRegistry,
|
|
775
878
|
errorbook_convergence: errorbookConvergence,
|
|
879
|
+
problem_projection: projectProblemProjection,
|
|
776
880
|
legacy_references: legacyReferences,
|
|
777
881
|
steering_boundary: steeringBoundary,
|
|
778
882
|
summary: {
|
|
@@ -782,6 +886,7 @@ async function auditCollabGovernance(projectRoot = process.cwd(), options = {},
|
|
|
782
886
|
multi_agent_violations: multiAgent.violations.length,
|
|
783
887
|
errorbook_registry_violations: errorbookRegistry.violations.length,
|
|
784
888
|
errorbook_convergence_violations: errorbookConvergence.violations.length,
|
|
889
|
+
problem_projection_violations: projectProblemProjection.violations.length,
|
|
785
890
|
legacy_reference_count: legacyReferences.matches.length,
|
|
786
891
|
steering_boundary_violations: steeringBoundary.violations.length
|
|
787
892
|
},
|
|
@@ -797,6 +902,7 @@ async function auditCollabGovernance(projectRoot = process.cwd(), options = {},
|
|
|
797
902
|
report.warnings.push(...multiAgent.warnings);
|
|
798
903
|
report.warnings.push(...errorbookRegistry.warnings);
|
|
799
904
|
report.warnings.push(...errorbookConvergence.warnings);
|
|
905
|
+
report.warnings.push(...projectProblemProjection.warnings);
|
|
800
906
|
report.warnings.push(...legacyReferences.warnings);
|
|
801
907
|
report.warnings.push(...steeringBoundary.warnings);
|
|
802
908
|
|
|
@@ -805,6 +911,7 @@ async function auditCollabGovernance(projectRoot = process.cwd(), options = {},
|
|
|
805
911
|
report.violations.push(...multiAgent.violations);
|
|
806
912
|
report.violations.push(...errorbookRegistry.violations);
|
|
807
913
|
report.violations.push(...errorbookConvergence.violations);
|
|
914
|
+
report.violations.push(...projectProblemProjection.violations);
|
|
808
915
|
report.violations.push(...legacyReferences.violations);
|
|
809
916
|
report.violations.push(
|
|
810
917
|
...steeringBoundary.violations.map((item) => `steering boundary violation: ${item.path || item.name}`)
|
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { auditCollabGovernance } = require('./collab-governance-audit');
|
|
4
|
+
const { syncProjectSharedProblemProjection } = require('../problem/project-problem-projection');
|
|
4
5
|
|
|
5
6
|
async function evaluateCollabGovernanceGate(options = {}, dependencies = {}) {
|
|
6
7
|
const projectPath = options.projectPath || dependencies.projectPath || process.cwd();
|
|
8
|
+
const warnings = [];
|
|
9
|
+
const preflightViolations = [];
|
|
10
|
+
let projectProblemProjection = null;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
projectProblemProjection = await (
|
|
14
|
+
dependencies.syncProjectSharedProblemProjection || syncProjectSharedProblemProjection
|
|
15
|
+
)(projectPath, options, dependencies);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
preflightViolations.push(`project-shared problem projection sync failed: ${error.message}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
const audit = await (dependencies.auditCollabGovernance || auditCollabGovernance)(
|
|
8
21
|
projectPath,
|
|
9
22
|
options,
|
|
10
23
|
dependencies
|
|
11
24
|
);
|
|
25
|
+
warnings.push(...(Array.isArray(audit.warnings) ? audit.warnings : []));
|
|
26
|
+
const violations = [
|
|
27
|
+
...preflightViolations,
|
|
28
|
+
...(Array.isArray(audit.violations) ? audit.violations : [])
|
|
29
|
+
];
|
|
30
|
+
const passed = audit.passed === true && preflightViolations.length === 0;
|
|
12
31
|
|
|
13
32
|
return {
|
|
14
33
|
mode: 'collab-governance-gate',
|
|
15
34
|
project_path: projectPath,
|
|
16
|
-
passed
|
|
17
|
-
reason:
|
|
35
|
+
passed,
|
|
36
|
+
reason: passed ? (audit.reason || 'passed') : 'violations',
|
|
18
37
|
summary: audit.summary || {},
|
|
19
|
-
warnings
|
|
20
|
-
violations
|
|
38
|
+
warnings,
|
|
39
|
+
violations,
|
|
40
|
+
project_problem_projection_sync: projectProblemProjection,
|
|
21
41
|
audit
|
|
22
42
|
};
|
|
23
43
|
}
|
|
@@ -235,6 +235,11 @@ const PROBLEM_CLOSURE_POLICY_DEFAULTS = Object.freeze({
|
|
|
235
235
|
schema_version: '1.0',
|
|
236
236
|
enabled: true,
|
|
237
237
|
governance_report_path: '.sce/reports/interactive-governance-report.json',
|
|
238
|
+
project_shared_projection: {
|
|
239
|
+
enabled: true,
|
|
240
|
+
file: '.sce/knowledge/problem/project-shared-problems.json',
|
|
241
|
+
scope: 'non_completed'
|
|
242
|
+
},
|
|
238
243
|
verify: {
|
|
239
244
|
require_problem_contract: true,
|
|
240
245
|
require_domain_validation: true,
|
|
@@ -335,6 +340,11 @@ const TAKEOVER_DEFAULTS = Object.freeze({
|
|
|
335
340
|
problem_closure: {
|
|
336
341
|
enabled: true,
|
|
337
342
|
governance_report_path: '.sce/reports/interactive-governance-report.json',
|
|
343
|
+
project_shared_projection: {
|
|
344
|
+
enabled: true,
|
|
345
|
+
file: '.sce/knowledge/problem/project-shared-problems.json',
|
|
346
|
+
scope: 'non_completed'
|
|
347
|
+
},
|
|
338
348
|
verify: {
|
|
339
349
|
require_problem_contract: true,
|
|
340
350
|
require_domain_validation: true,
|
|
@@ -577,6 +587,53 @@ function _buildProjectSharedErrorbookRegistry(existing, projectPath, nowIso, con
|
|
|
577
587
|
};
|
|
578
588
|
}
|
|
579
589
|
|
|
590
|
+
function _buildProjectSharedProblemProjection(existing, projectPath, config = {}) {
|
|
591
|
+
const projection = _isObject(config.project_shared_projection)
|
|
592
|
+
? config.project_shared_projection
|
|
593
|
+
: _clone(PROBLEM_CLOSURE_POLICY_DEFAULTS.project_shared_projection);
|
|
594
|
+
const fallback = {
|
|
595
|
+
api_version: 'sce.project-problem-projection/v0.1',
|
|
596
|
+
generated_at: new Date().toISOString(),
|
|
597
|
+
source: {
|
|
598
|
+
project: path.basename(projectPath),
|
|
599
|
+
scope: typeof projection.scope === 'string' && projection.scope.trim()
|
|
600
|
+
? projection.scope.trim()
|
|
601
|
+
: 'non_completed',
|
|
602
|
+
stale_days: 14
|
|
603
|
+
},
|
|
604
|
+
summary: {
|
|
605
|
+
total_entries: 0,
|
|
606
|
+
active_entries: 0,
|
|
607
|
+
stale_entries: 0,
|
|
608
|
+
completed_entries: 0
|
|
609
|
+
},
|
|
610
|
+
entries: []
|
|
611
|
+
};
|
|
612
|
+
if (!_isObject(existing) || !Array.isArray(existing.entries)) {
|
|
613
|
+
return fallback;
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
...fallback,
|
|
617
|
+
...existing,
|
|
618
|
+
source: _isObject(existing.source)
|
|
619
|
+
? {
|
|
620
|
+
...fallback.source,
|
|
621
|
+
...existing.source
|
|
622
|
+
}
|
|
623
|
+
: fallback.source,
|
|
624
|
+
summary: _isObject(existing.summary)
|
|
625
|
+
? {
|
|
626
|
+
...fallback.summary,
|
|
627
|
+
...existing.summary,
|
|
628
|
+
total_entries: Array.isArray(existing.entries) ? existing.entries.length : Number(existing.summary.total_entries || 0)
|
|
629
|
+
}
|
|
630
|
+
: {
|
|
631
|
+
...fallback.summary,
|
|
632
|
+
total_entries: Array.isArray(existing.entries) ? existing.entries.length : 0
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
580
637
|
function _normalizeRelativePath(value) {
|
|
581
638
|
return `${value || ''}`.replace(/\\/g, '/');
|
|
582
639
|
}
|
|
@@ -925,6 +982,20 @@ async function applyTakeoverBaseline(projectPath = process.cwd(), options = {})
|
|
|
925
982
|
const desiredSpecDomainPolicy = _deepMerge(existingSpecDomainPolicy || {}, SPEC_DOMAIN_POLICY_DEFAULTS);
|
|
926
983
|
const desiredProblemEvalPolicy = _deepMerge(existingProblemEvalPolicy || {}, PROBLEM_EVAL_POLICY_DEFAULTS);
|
|
927
984
|
const desiredProblemClosurePolicy = _deepMerge(existingProblemClosurePolicy || {}, PROBLEM_CLOSURE_POLICY_DEFAULTS);
|
|
985
|
+
const projectSharedProblemRelativePath = _isObject(desiredProblemClosurePolicy.project_shared_projection)
|
|
986
|
+
&& typeof desiredProblemClosurePolicy.project_shared_projection.file === 'string'
|
|
987
|
+
&& desiredProblemClosurePolicy.project_shared_projection.file.trim()
|
|
988
|
+
? desiredProblemClosurePolicy.project_shared_projection.file.trim()
|
|
989
|
+
: PROBLEM_CLOSURE_POLICY_DEFAULTS.project_shared_projection.file;
|
|
990
|
+
const projectSharedProblemPath = path.isAbsolute(projectSharedProblemRelativePath)
|
|
991
|
+
? projectSharedProblemRelativePath
|
|
992
|
+
: path.join(projectPath, projectSharedProblemRelativePath);
|
|
993
|
+
const existingProjectSharedProblemProjection = await _readJsonSafe(projectSharedProblemPath, fileSystem);
|
|
994
|
+
const desiredProjectSharedProblemProjection = _buildProjectSharedProblemProjection(
|
|
995
|
+
existingProjectSharedProblemProjection,
|
|
996
|
+
projectPath,
|
|
997
|
+
desiredProblemClosurePolicy
|
|
998
|
+
);
|
|
928
999
|
const desiredStudioIntakePolicy = _deepMerge(existingStudioIntakePolicy || {}, STUDIO_INTAKE_POLICY_DEFAULTS);
|
|
929
1000
|
const desiredStateStoragePolicy = _deepMerge(existingStateStoragePolicy || {}, cloneStateStoragePolicyDefaults());
|
|
930
1001
|
const customErrorbookFindings = await _scanProjectDefinedErrorbookMechanisms(projectPath, fileSystem);
|
|
@@ -981,6 +1052,11 @@ async function applyTakeoverBaseline(projectPath = process.cwd(), options = {})
|
|
|
981
1052
|
apply,
|
|
982
1053
|
fileSystem
|
|
983
1054
|
}));
|
|
1055
|
+
fileResults.push(await _reconcileJsonFile(projectSharedProblemPath, desiredProjectSharedProblemProjection, {
|
|
1056
|
+
projectPath,
|
|
1057
|
+
apply,
|
|
1058
|
+
fileSystem
|
|
1059
|
+
}));
|
|
984
1060
|
fileResults.push(await _reconcileJsonFile(studioIntakePolicyPath, desiredStudioIntakePolicy, {
|
|
985
1061
|
projectPath,
|
|
986
1062
|
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.54
|
|
247
247
|
**Last Updated**: 2026-03-16
|
|
248
248
|
**Purpose**: Guide AI tools to work effectively with this project
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
"schema_version": "1.0",
|
|
3
3
|
"enabled": true,
|
|
4
4
|
"governance_report_path": ".sce/reports/interactive-governance-report.json",
|
|
5
|
+
"project_shared_projection": {
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"file": ".sce/knowledge/problem/project-shared-problems.json",
|
|
8
|
+
"scope": "non_completed"
|
|
9
|
+
},
|
|
5
10
|
"verify": {
|
|
6
11
|
"require_problem_contract": true,
|
|
7
12
|
"require_domain_validation": true,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"api_version": "sce.project-problem-projection/v0.1",
|
|
3
|
+
"generated_at": "2026-03-16T00:00:00.000Z",
|
|
4
|
+
"source": {
|
|
5
|
+
"project": "template",
|
|
6
|
+
"scope": "non_completed",
|
|
7
|
+
"stale_days": 14
|
|
8
|
+
},
|
|
9
|
+
"summary": {
|
|
10
|
+
"total_entries": 0,
|
|
11
|
+
"active_entries": 0,
|
|
12
|
+
"stale_entries": 0,
|
|
13
|
+
"completed_entries": 0
|
|
14
|
+
},
|
|
15
|
+
"entries": []
|
|
16
|
+
}
|