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.
Files changed (33) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +6 -1
  3. package/README.zh.md +6 -1
  4. package/bin/scene-capability-engine.js +2 -0
  5. package/docs/command-reference.md +29 -1
  6. package/docs/magicball-app-collection-phase-1.md +133 -0
  7. package/docs/magicball-cli-invocation-examples.md +40 -0
  8. package/docs/magicball-integration-doc-index.md +14 -6
  9. package/docs/magicball-integration-issue-tracker.md +42 -3
  10. package/docs/magicball-sce-adaptation-guide.md +36 -9
  11. package/docs/releases/README.md +2 -0
  12. package/docs/releases/v3.6.54.md +19 -0
  13. package/docs/releases/v3.6.55.md +18 -0
  14. package/docs/zh/releases/README.md +2 -0
  15. package/docs/zh/releases/v3.6.54.md +19 -0
  16. package/docs/zh/releases/v3.6.55.md +18 -0
  17. package/lib/app/collection-store.js +127 -0
  18. package/lib/app/install-apply-runner.js +192 -0
  19. package/lib/app/install-plan-service.js +410 -0
  20. package/lib/app/scene-workspace-store.js +132 -0
  21. package/lib/commands/app.js +281 -0
  22. package/lib/commands/device.js +194 -0
  23. package/lib/commands/scene.js +228 -0
  24. package/lib/device/current-device.js +158 -0
  25. package/lib/device/device-override-store.js +157 -0
  26. package/lib/problem/project-problem-projection.js +239 -0
  27. package/lib/workspace/collab-governance-audit.js +107 -0
  28. package/lib/workspace/collab-governance-gate.js +24 -4
  29. package/lib/workspace/takeover-baseline.js +76 -0
  30. package/package.json +1 -1
  31. package/template/.sce/README.md +1 -1
  32. package/template/.sce/config/problem-closure-policy.json +5 -0
  33. 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: audit.passed === true,
17
- reason: audit.reason || (audit.passed === true ? 'passed' : 'violations'),
35
+ passed,
36
+ reason: passed ? (audit.reason || 'passed') : 'violations',
18
37
  summary: audit.summary || {},
19
- warnings: Array.isArray(audit.warnings) ? audit.warnings : [],
20
- violations: Array.isArray(audit.violations) ? audit.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.53",
3
+ "version": "3.6.55",
4
4
  "description": "SCE (Scene Capability Engine) - A CLI tool and npm package for spec-driven development with AI coding assistants.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -243,6 +243,6 @@ A Spec is a complete feature definition with three parts:
243
243
  ---
244
244
 
245
245
  **Project Type**: Spec-driven development
246
- **sce Version**: 3.6.53
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
+ }