scene-capability-engine 3.6.53 → 3.6.55
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 +23 -0
- package/README.md +6 -1
- package/README.zh.md +6 -1
- package/bin/scene-capability-engine.js +2 -0
- package/docs/command-reference.md +29 -1
- package/docs/magicball-app-collection-phase-1.md +133 -0
- package/docs/magicball-cli-invocation-examples.md +40 -0
- package/docs/magicball-integration-doc-index.md +14 -6
- package/docs/magicball-integration-issue-tracker.md +42 -3
- package/docs/magicball-sce-adaptation-guide.md +36 -9
- package/docs/releases/README.md +2 -0
- package/docs/releases/v3.6.54.md +19 -0
- package/docs/releases/v3.6.55.md +18 -0
- package/docs/zh/releases/README.md +2 -0
- package/docs/zh/releases/v3.6.54.md +19 -0
- package/docs/zh/releases/v3.6.55.md +18 -0
- package/lib/app/collection-store.js +127 -0
- package/lib/app/install-apply-runner.js +192 -0
- package/lib/app/install-plan-service.js +410 -0
- package/lib/app/scene-workspace-store.js +132 -0
- package/lib/commands/app.js +281 -0
- package/lib/commands/device.js +194 -0
- package/lib/commands/scene.js +228 -0
- package/lib/device/current-device.js +158 -0
- package/lib/device/device-override-store.js +157 -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
|
@@ -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.55
|
|
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
|
+
}
|