scene-capability-engine 3.6.63 → 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 +5 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/docs/releases/README.md +1 -0
- package/docs/releases/v3.6.64.md +35 -0
- package/docs/zh/releases/README.md +1 -0
- package/docs/zh/releases/v3.6.64.md +35 -0
- package/lib/problem/project-problem-projection.js +82 -9
- package/package.json +1 -1
- package/template/.sce/README.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ 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
|
+
|
|
10
15
|
## [3.6.63] - 2026-03-21
|
|
11
16
|
|
|
12
17
|
### Fixed
|
package/README.md
CHANGED
package/README.zh.md
CHANGED
package/docs/releases/README.md
CHANGED
|
@@ -9,6 +9,7 @@ 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)
|
|
12
13
|
- [v3.6.63 release notes](./v3.6.63.md)
|
|
13
14
|
- [v3.6.62 release notes](./v3.6.62.md)
|
|
14
15
|
- [v3.6.61 release notes](./v3.6.61.md)
|
|
@@ -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.
|
|
@@ -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
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
const
|
|
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:
|
|
277
|
+
project: projectIdentity,
|
|
206
278
|
stale_days: staleDays,
|
|
207
279
|
scope: projectionConfig.scope
|
|
208
280
|
},
|
|
209
281
|
summary: {
|
|
210
|
-
total_entries:
|
|
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
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.64
|
|
247
247
|
**Last Updated**: 2026-03-21
|
|
248
248
|
**Purpose**: Guide AI tools to work effectively with this project
|