scene-capability-engine 3.6.62 → 3.6.64

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,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.6.64] - 2026-03-21
11
+
12
+ ### Fixed
13
+ - Stabilized the tracked project-shared problem projection for co-work/publish flows by deriving `source.project` from a stable project identity and reusing prior entry timestamps when only clone-local mtimes drift.
14
+
15
+ ## [3.6.63] - 2026-03-21
16
+
17
+ ### Fixed
18
+ - Relaxed `audit:steering` so a clean-room checkout no longer hard-fails release validation just because the gitignored personal file `.sce/steering/CURRENT_CONTEXT.md` is absent.
19
+
10
20
  ## [3.6.62] - 2026-03-21
11
21
 
12
22
  ### Removed
package/README.md CHANGED
@@ -230,5 +230,5 @@ MIT. See [LICENSE](LICENSE).
230
230
 
231
231
  ---
232
232
 
233
- **Version**: 3.6.62
233
+ **Version**: 3.6.64
234
234
  **Last Updated**: 2026-03-21
package/README.zh.md CHANGED
@@ -235,5 +235,5 @@ MIT,见 [LICENSE](LICENSE)。
235
235
 
236
236
  ---
237
237
 
238
- **版本**:3.6.62
238
+ **版本**:3.6.64
239
239
  **最后更新**:2026-03-21
@@ -9,6 +9,8 @@ This directory stores release-facing documents:
9
9
  ## Archived Versions
10
10
 
11
11
  - [Release checklist](../release-checklist.md)
12
+ - [v3.6.64 release notes](./v3.6.64.md)
13
+ - [v3.6.63 release notes](./v3.6.63.md)
12
14
  - [v3.6.62 release notes](./v3.6.62.md)
13
15
  - [v3.6.61 release notes](./v3.6.61.md)
14
16
  - [v3.6.60 release notes](./v3.6.60.md)
@@ -0,0 +1,34 @@
1
+ # v3.6.63 Release Notes
2
+
3
+ Release date: 2026-03-21
4
+
5
+ ## Highlights
6
+
7
+ - Fixed a clean-room release blocker in `audit:steering`: the gitignored personal file `.sce/steering/CURRENT_CONTEXT.md` is now treated as optional when absent in a fresh checkout.
8
+ - Added unit coverage so steering governance still enforces stable layers while no longer requiring a local-only context file to exist in Git-tracked release environments.
9
+ - Kept the `v3.6.62` OpenHands cleanup intact; this patch only repairs the release path uncovered while publishing that cleanup.
10
+
11
+ ## Validation
12
+
13
+ - `npx jest tests/unit/scripts/steering-content-audit.test.js --runInBand`
14
+ - `npx jest tests/unit/commands/studio.test.js --runInBand`
15
+ - `npm run test:release`
16
+ - `npm run audit:release-docs`
17
+ - `npm run audit:steering`
18
+ - `npm run test:skip-audit`
19
+ - `npm run test:sce-tracking`
20
+ - `npm run gate:npm-runtime-assets`
21
+ - `npm run test:brand-consistency`
22
+ - `npm run audit:clarification-first`
23
+ - `npm run audit:magicball-engineering-contract`
24
+ - `npm run audit:magicball-project-contract`
25
+ - `npm run gate:collab-governance`
26
+ - `npm run gate:errorbook-registry-health`
27
+ - `npm run gate:errorbook-release`
28
+ - `npm run report:interactive-governance -- --fail-on-alert`
29
+ - `npm pack --dry-run`
30
+
31
+ ## Release Notes
32
+
33
+ - Use `v3.6.63` if you need the OpenHands cleanup from `v3.6.62` plus a clean-room-safe steering audit during publish.
34
+ - This fix preserves the co-work rule that `CURRENT_CONTEXT.md` remains local/personal state and should not be force-committed just to satisfy release automation.
@@ -0,0 +1,35 @@
1
+ # v3.6.64 Release Notes
2
+
3
+ Release date: 2026-03-21
4
+
5
+ ## Highlights
6
+
7
+ - Fixed the remaining publish blocker in the managed co-work path: `.sce/knowledge/problem/project-shared-problems.json` no longer churns just because a clean clone uses a different folder name or fresh filesystem mtimes.
8
+ - Reused prior projection timestamps when the logical problem entry is unchanged, so `gate:collab-governance` stays idempotent and no longer dirties the worktree right before `gate:git-managed`.
9
+ - Kept the `v3.6.62` OpenHands cleanup and the `v3.6.63` clean-room steering audit fix intact; this patch closes the last publish-path drift uncovered while releasing those changes.
10
+
11
+ ## Validation
12
+
13
+ - `npx jest tests/unit/problem/project-problem-projection.test.js --runInBand`
14
+ - `npx jest tests/unit/scripts/steering-content-audit.test.js --runInBand`
15
+ - `npx jest tests/unit/commands/studio.test.js --runInBand`
16
+ - `npm run test:release`
17
+ - `npm run audit:release-docs`
18
+ - `npm run audit:steering`
19
+ - `npm run test:skip-audit`
20
+ - `npm run test:sce-tracking`
21
+ - `npm run gate:npm-runtime-assets`
22
+ - `npm run test:brand-consistency`
23
+ - `npm run audit:clarification-first`
24
+ - `npm run audit:magicball-engineering-contract`
25
+ - `npm run audit:magicball-project-contract`
26
+ - `npm run gate:collab-governance`
27
+ - `npm run gate:errorbook-registry-health`
28
+ - `npm run gate:errorbook-release`
29
+ - `npm run report:interactive-governance -- --fail-on-alert`
30
+ - `npm pack --dry-run`
31
+
32
+ ## Release Notes
33
+
34
+ - Use `v3.6.64` if you need the OpenHands cleanup, clean-room steering audit, and a truly idempotent co-work publish path in one published package.
35
+ - This fix keeps shared problem knowledge tracked while removing clone-path and clone-mtime noise from the release gate.
@@ -9,6 +9,8 @@
9
9
  ## 历史版本归档
10
10
 
11
11
  - [发布检查清单](../release-checklist.md)
12
+ - [v3.6.64 发布说明](./v3.6.64.md)
13
+ - [v3.6.63 发布说明](./v3.6.63.md)
12
14
  - [v3.6.62 发布说明](./v3.6.62.md)
13
15
  - [v3.6.61 发布说明](./v3.6.61.md)
14
16
  - [v3.6.60 发布说明](./v3.6.60.md)
@@ -0,0 +1,34 @@
1
+ # v3.6.63 发布说明
2
+
3
+ 发布日期:2026-03-21
4
+
5
+ ## 重点变化
6
+
7
+ - 修复了 `audit:steering` 的 clean-room 发布阻断问题:在全新克隆中,如果被 `.gitignore` 排除的个人态文件 `.sce/steering/CURRENT_CONTEXT.md` 不存在,不再直接把发布校验打成失败。
8
+ - 增加了对应单测,确保 steering 治理仍然会继续严格检查稳定层文件,但不再强迫 Git 跟踪环境必须带着本地个人上下文文件。
9
+ - `v3.6.62` 的 OpenHands 清理内容保持不变;本补丁只修复发布那条线在真实发布过程中暴露出的流程缺陷。
10
+
11
+ ## 验证
12
+
13
+ - `npx jest tests/unit/scripts/steering-content-audit.test.js --runInBand`
14
+ - `npx jest tests/unit/commands/studio.test.js --runInBand`
15
+ - `npm run test:release`
16
+ - `npm run audit:release-docs`
17
+ - `npm run audit:steering`
18
+ - `npm run test:skip-audit`
19
+ - `npm run test:sce-tracking`
20
+ - `npm run gate:npm-runtime-assets`
21
+ - `npm run test:brand-consistency`
22
+ - `npm run audit:clarification-first`
23
+ - `npm run audit:magicball-engineering-contract`
24
+ - `npm run audit:magicball-project-contract`
25
+ - `npm run gate:collab-governance`
26
+ - `npm run gate:errorbook-registry-health`
27
+ - `npm run gate:errorbook-release`
28
+ - `npm run report:interactive-governance -- --fail-on-alert`
29
+ - `npm pack --dry-run`
30
+
31
+ ## 发布说明
32
+
33
+ - 如果你既需要 `v3.6.62` 的 OpenHands 清理,又需要 clean-room 发布环境下可通过的 steering 审计,请使用 `v3.6.63`。
34
+ - 该修复保持 co-work 规则不变:`CURRENT_CONTEXT.md` 依然属于本地个人态文件,不应为了过发布门禁而被强制提交到 Git。
@@ -0,0 +1,35 @@
1
+ # v3.6.64 发布说明
2
+
3
+ 发布日期:2026-03-21
4
+
5
+ ## 重点变化
6
+
7
+ - 修复了受管 co-work 发布路径里剩余的阻断点:`.sce/knowledge/problem/project-shared-problems.json` 不再因为 clean clone 的目录名不同或文件系统时间戳刷新,就在发布前无意义改写。
8
+ - 当问题条目的逻辑内容没有变化时,投影现在会复用上一次的稳定时间字段,保证 `gate:collab-governance` 真正幂等,不再在 `gate:git-managed` 前把工作区弄脏。
9
+ - `v3.6.62` 的 OpenHands 清理和 `v3.6.63` 的 clean-room steering 审计修复都保持不变;本补丁把发布路径上最后一个实际暴露出来的漂移点补齐。
10
+
11
+ ## 验证
12
+
13
+ - `npx jest tests/unit/problem/project-problem-projection.test.js --runInBand`
14
+ - `npx jest tests/unit/scripts/steering-content-audit.test.js --runInBand`
15
+ - `npx jest tests/unit/commands/studio.test.js --runInBand`
16
+ - `npm run test:release`
17
+ - `npm run audit:release-docs`
18
+ - `npm run audit:steering`
19
+ - `npm run test:skip-audit`
20
+ - `npm run test:sce-tracking`
21
+ - `npm run gate:npm-runtime-assets`
22
+ - `npm run test:brand-consistency`
23
+ - `npm run audit:clarification-first`
24
+ - `npm run audit:magicball-engineering-contract`
25
+ - `npm run audit:magicball-project-contract`
26
+ - `npm run gate:collab-governance`
27
+ - `npm run gate:errorbook-registry-health`
28
+ - `npm run gate:errorbook-release`
29
+ - `npm run report:interactive-governance -- --fail-on-alert`
30
+ - `npm pack --dry-run`
31
+
32
+ ## 发布说明
33
+
34
+ - 如果你需要同时拿到 OpenHands 清理、clean-room steering 审计修复,以及真正幂等的 co-work 发布链路,请使用 `v3.6.64`。
35
+ - 该修复保留了项目共享问题库的 Git 跟踪能力,同时去掉了由克隆目录名和克隆时间戳引入的发布噪音。
@@ -115,6 +115,72 @@ function normalizeProjectionAgeDays(value) {
115
115
  return Math.max(0, Math.floor(numeric));
116
116
  }
117
117
 
118
+ function toComparableEntry(entry) {
119
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
120
+ return null;
121
+ }
122
+ const clone = {
123
+ ...entry
124
+ };
125
+ delete clone.updated_at;
126
+ delete clone.age_days;
127
+ return sortKeysDeep(clone);
128
+ }
129
+
130
+ function stabilizeEntriesWithExistingProjection(entries = [], existingProjection = null) {
131
+ const existingEntries = Array.isArray(existingProjection && existingProjection.entries)
132
+ ? existingProjection.entries
133
+ : [];
134
+ const existingBySpecId = new Map(
135
+ existingEntries
136
+ .filter((item) => item && typeof item === 'object' && !Array.isArray(item) && normalizeText(item.spec_id))
137
+ .map((item) => [normalizeText(item.spec_id), item])
138
+ );
139
+
140
+ return entries.map((entry) => {
141
+ const previous = existingBySpecId.get(normalizeText(entry.spec_id));
142
+ if (!previous) {
143
+ return entry;
144
+ }
145
+ const previousComparable = toComparableEntry(previous);
146
+ const nextComparable = toComparableEntry(entry);
147
+ if (
148
+ previousComparable
149
+ && nextComparable
150
+ && JSON.stringify(previousComparable) === JSON.stringify(nextComparable)
151
+ ) {
152
+ return {
153
+ ...entry,
154
+ updated_at: previous.updated_at || entry.updated_at || null,
155
+ age_days: normalizeProjectionAgeDays(previous.age_days)
156
+ };
157
+ }
158
+ return entry;
159
+ });
160
+ }
161
+
162
+ async function resolveStableProjectIdentity(projectPath = process.cwd(), fileSystem = fs, fallbackValue = '') {
163
+ const packageJsonPath = path.join(projectPath, 'package.json');
164
+ if (await fileSystem.pathExists(packageJsonPath)) {
165
+ try {
166
+ const packageJson = await fileSystem.readJson(packageJsonPath);
167
+ const packageName = normalizeText(packageJson && packageJson.name);
168
+ if (packageName) {
169
+ return packageName;
170
+ }
171
+ } catch (_error) {
172
+ // Fall through to the remaining stable identity candidates.
173
+ }
174
+ }
175
+
176
+ const fallback = normalizeText(fallbackValue);
177
+ if (fallback) {
178
+ return fallback;
179
+ }
180
+
181
+ return path.basename(projectPath);
182
+ }
183
+
118
184
  async function buildProjectSharedProblemProjection(projectPath = process.cwd(), options = {}, dependencies = {}) {
119
185
  const fileSystem = dependencies.fileSystem || fs;
120
186
  const studioIntakePolicy = dependencies.studioIntakePolicy || await loadStudioIntakePolicy(projectPath, fileSystem);
@@ -128,6 +194,11 @@ async function buildProjectSharedProblemProjection(projectPath = process.cwd(),
128
194
  const projectionConfig = normalizeProblemProjectionConfig(
129
195
  options.projectSharedProjection || closurePolicy.project_shared_projection
130
196
  );
197
+ const projectIdentity = await resolveStableProjectIdentity(
198
+ projectPath,
199
+ fileSystem,
200
+ options.projectIdentity || dependencies.existingProjection?.source?.project
201
+ );
131
202
  const scanOptions = {
132
203
  staleDays
133
204
  };
@@ -193,26 +264,27 @@ async function buildProjectSharedProblemProjection(projectPath = process.cwd(),
193
264
  });
194
265
  }
195
266
 
196
- entries.sort((left, right) => String(right.updated_at || '').localeCompare(String(left.updated_at || '')));
197
- const activeCount = entries.filter((item) => item.lifecycle_state === 'active').length;
198
- const staleCount = entries.filter((item) => item.lifecycle_state === 'stale').length;
199
- const completedCount = entries.filter((item) => item.lifecycle_state === 'completed').length;
267
+ const stabilizedEntries = stabilizeEntriesWithExistingProjection(entries, dependencies.existingProjection);
268
+ stabilizedEntries.sort((left, right) => String(right.updated_at || '').localeCompare(String(left.updated_at || '')));
269
+ const activeCount = stabilizedEntries.filter((item) => item.lifecycle_state === 'active').length;
270
+ const staleCount = stabilizedEntries.filter((item) => item.lifecycle_state === 'stale').length;
271
+ const completedCount = stabilizedEntries.filter((item) => item.lifecycle_state === 'completed').length;
200
272
 
201
273
  return {
202
274
  api_version: PROJECT_SHARED_PROBLEM_PROJECTION_API_VERSION,
203
275
  generated_at: new Date().toISOString(),
204
276
  source: {
205
- project: path.basename(projectPath),
277
+ project: projectIdentity,
206
278
  stale_days: staleDays,
207
279
  scope: projectionConfig.scope
208
280
  },
209
281
  summary: {
210
- total_entries: entries.length,
282
+ total_entries: stabilizedEntries.length,
211
283
  active_entries: activeCount,
212
284
  stale_entries: staleCount,
213
285
  completed_entries: completedCount
214
286
  },
215
- entries
287
+ entries: stabilizedEntries
216
288
  };
217
289
  }
218
290
 
@@ -238,14 +310,15 @@ async function syncProjectSharedProblemProjection(projectPath = process.cwd(), o
238
310
  };
239
311
  }
240
312
 
313
+ const existing = await readJsonSafe(absolutePath, fileSystem);
241
314
  const payload = await buildProjectSharedProblemProjection(projectPath, {
242
315
  ...options,
243
316
  projectSharedProjection: projectionConfig
244
317
  }, {
245
318
  ...dependencies,
246
- problemClosurePolicyBundle: closurePolicy
319
+ problemClosurePolicyBundle: closurePolicy,
320
+ existingProjection: existing
247
321
  });
248
- const existing = await readJsonSafe(absolutePath, fileSystem);
249
322
  const existingComparable = toComparableProjection(existing);
250
323
  const nextComparable = toComparableProjection(payload);
251
324
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.62",
3
+ "version": "3.6.64",
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": {
@@ -186,6 +186,10 @@ function pushViolation(violations, severity, file, rule, message, suggestion) {
186
186
  });
187
187
  }
188
188
 
189
+ function isOptionalCurrentContextFile(fileName) {
190
+ return fileName === 'CURRENT_CONTEXT.md';
191
+ }
192
+
189
193
  function auditSteeringContent(options = {}) {
190
194
  const projectPath = path.resolve(options.projectPath || process.cwd());
191
195
  const packageJsonPath = path.join(projectPath, 'package.json');
@@ -206,6 +210,17 @@ function auditSteeringContent(options = {}) {
206
210
  for (const fileName of fileNames) {
207
211
  const absolutePath = path.join(steeringDir, fileName);
208
212
  if (!fs.existsSync(absolutePath)) {
213
+ if (isOptionalCurrentContextFile(fileName)) {
214
+ pushViolation(
215
+ violations,
216
+ 'warning',
217
+ fileName,
218
+ 'missing_optional_context',
219
+ `${fileName} is absent in this checkout; clean-room repositories may omit personal current context until active local work begins.`,
220
+ 'Create CURRENT_CONTEXT.md locally when active work starts, but keep it out of Git-tracked release state.'
221
+ );
222
+ continue;
223
+ }
209
224
  pushViolation(
210
225
  violations,
211
226
  'error',
@@ -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.62
246
+ **sce Version**: 3.6.64
247
247
  **Last Updated**: 2026-03-21
248
248
  **Purpose**: Guide AI tools to work effectively with this project