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 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
@@ -218,5 +218,5 @@ MIT. See [LICENSE](LICENSE).
218
218
 
219
219
  ---
220
220
 
221
- **Version**: 3.6.53
221
+ **Version**: 3.6.54
222
222
  **Last Updated**: 2026-03-16
package/README.zh.md CHANGED
@@ -223,5 +223,5 @@ MIT,见 [LICENSE](LICENSE)。
223
223
 
224
224
  ---
225
225
 
226
- **版本**:3.6.53
226
+ **版本**:3.6.54
227
227
  **最后更新**:2026-03-16
@@ -135,7 +135,7 @@ Timeline policy:
135
135
  - default enabled with local retention under `.sce/timeline/snapshots/`
136
136
  - stage/key-event checkpoints are automatically captured for `studio` and `session` commands
137
137
  - interval auto-checkpoints are integrated in the same flow via timeline checkpoint capture
138
- - `timeline push` now blocks before snapshot/push when collaboration governance drifts, so tracked runtime state, missing co-work ignore rules, missing shared `errorbook` registry baseline, missing tracked project-shared errorbook projection, invalid multi-agent config, legacy `.kiro*` references, or steering boundary drift cannot pass through managed push flow
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: 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.54",
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.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
+ }