universal-dev-standards 6.7.1 → 6.7.2
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/bundled/locales/zh-CN/CHANGELOG.md +19 -3
- package/bundled/locales/zh-CN/README.md +1 -1
- package/bundled/locales/zh-CN/SECURITY.md +1 -1
- package/bundled/locales/zh-TW/CHANGELOG.md +19 -3
- package/bundled/locales/zh-TW/README.md +1 -1
- package/bundled/locales/zh-TW/SECURITY.md +1 -1
- package/package.json +1 -1
- package/src/commands/update.js +42 -2
- package/src/reconciler/diff-engine.js +46 -3
- package/standards-registry.json +7 -7
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.7.
|
|
4
|
-
translation_version: 6.7.
|
|
5
|
-
last_synced: 2026-08-
|
|
3
|
+
source_version: 6.7.2
|
|
4
|
+
translation_version: 6.7.2
|
|
5
|
+
last_synced: 2026-08-18
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -17,6 +17,22 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.7.2] - 2026-08-18
|
|
21
|
+
|
|
22
|
+
### 修正
|
|
23
|
+
|
|
24
|
+
- **`uds update --skills` 更新了全部内容,却永不推进版本标记。** 五个采用 repo 中有四个停在 6.6.0,而同一份 manifest 的 `skills.version` 已经是 6.7.0;唯一推进的,正是那个没装 skills 的。两次执行都 exit 0、都打印「57 succeeded」、都没打印任何失败。先前对此的判读——「有东西报告了失败而它没有浮上来」——**是错的**。探针测到 `results=57 failing=0`、registry 版本解为 `"6.7.1"`、准备写入的值也正确:**reconciler 每一步都做对了,是较晚的一次写入撤销了它。** `update.js` 在命令开头读一次 manifest,那是在 reconciler 执行之前;`updateSkillsOnly()` 随后把那份过期的内存对象写回去覆盖掉它。`updateCommandsOnly()` 有一模一样的缺陷,**它是遍历找出来的,不是撞到的**。两处现在各自重读 manifest,并且**只套用自己拥有的字段**——把整份对象复制回去,会让任何后续步骤新增的字段重蹈同一个缺陷。这件事之所以要紧,是因为该机制自己的注释写着:它存在的目的,就是让每周陈旧度侦察(读的正是这个字段)不再误报。
|
|
25
|
+
- **一份列出 57 项变更、其中 55 项是无条件的计划。** skill 没有内容比对(XSPEC-382 R1),于是每次升级都重印同样的 55 行、理由完全相同,把审阅者真正需要批准的那 2 行埋在底下。现在它们折叠成一行,**而那一行写出自己折了几个**,总数不变——一个不声明自己设限的上限,读起来就像「就这些了」。摘要那行同样改为 `Update: 57 (2 changed, 55 unconditional reinstall)`;单独的 `Update: 57` 是真的,而且什么都没回答,**而决定要不要批准一次升级时读的正是摘要**。
|
|
26
|
+
|
|
27
|
+
### 变更
|
|
28
|
+
|
|
29
|
+
- **无条件重装的理由字符串收敛为单一导出常量**,不再是产生端一份、渲染端一份。两份副本之间的漂移在这里是无声的:折叠会单纯地停止折叠,而计划看起来与它一直以来的样子一模一样。
|
|
30
|
+
|
|
31
|
+
### 测试
|
|
32
|
+
|
|
33
|
+
- **为版本标记补上行为层测试,与既有的形状测试并存。** 随修正加入的回归测试断言的是源代码文本——那两个函数含有 `readManifest(projectPath)`——若有人重构成「调用它然后丢掉结果」,它仍然全绿。而这里宣称的是行为,所以 `tests/e2e/update-version-advances.test.js` 会真的跑一次安装、种下探针版本、执行 `update --apply --yes --skills`,再断言标记真的动了。已双向验过:修正在场为绿,还原缺陷为红。
|
|
34
|
+
- 两份新测试都断言**正反两臂**。折叠测试会检查一般计划完全不受影响,因为只验「那 55 行不见了」的测试,对一个把所有 update 行都丢掉的渲染器也照样会过。
|
|
35
|
+
|
|
20
36
|
## [6.7.1] - 2026-08-18
|
|
21
37
|
|
|
22
38
|
### 修正
|
|
@@ -15,7 +15,7 @@ status: current
|
|
|
15
15
|
|
|
16
16
|
> **语言**: [English](../../README.md) | [繁體中文](../zh-TW/README.md) | 简体中文
|
|
17
17
|
|
|
18
|
-
**版本**: 6.7.
|
|
18
|
+
**版本**: 6.7.2 | **发布日期**: 2026-08-18 | **授权**: [双重授权](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
语言无关、框架无关的软件项目文档标准。通过 AI 原生工作流,确保不同技术栈之间的一致性、质量和可维护性。
|
|
21
21
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.7.
|
|
4
|
-
translation_version: 6.7.
|
|
5
|
-
last_synced: 2026-08-
|
|
3
|
+
source_version: 6.7.2
|
|
4
|
+
translation_version: 6.7.2
|
|
5
|
+
last_synced: 2026-08-18
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -17,6 +17,22 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.7.2] - 2026-08-18
|
|
21
|
+
|
|
22
|
+
### 修正
|
|
23
|
+
|
|
24
|
+
- **`uds update --skills` 更新了全部內容,卻永不前進版本標記。** 五個採用 repo 中有四個停在 6.6.0,而同一份 manifest 的 `skills.version` 已經是 6.7.0;唯一前進的,正是那個沒裝 skills 的。兩次執行都 exit 0、都印「57 succeeded」、都沒印任何失敗。先前對此的判讀——「有東西回報了失敗而它沒有浮上來」——**是錯的**。探針量到 `results=57 failing=0`、registry 版本解為 `"6.7.1"`、準備寫入的值也正確:**reconciler 每一步都做對了,是較晚的一次寫入撤銷了它。** `update.js` 在指令開頭讀一次 manifest,那是在 reconciler 執行之前;`updateSkillsOnly()` 隨後把那份過期的記憶體物件寫回去覆蓋掉它。`updateCommandsOnly()` 有一模一樣的缺陷,**它是走訪找出來的,不是撞到的**。兩處現在各自重讀 manifest,並且**只套用自己擁有的欄位**——把整份物件複製回去,會讓任何後續步驟新增的欄位重蹈同一個缺陷。這件事之所以要緊,是因為該機制自己的註解寫著:它存在的目的,就是讓每週陳舊度偵察(讀的正是這個欄位)不再誤報。
|
|
25
|
+
- **一份列出 57 項變更、其中 55 項是無條件的計畫。** skill 沒有內容比對(XSPEC-382 R1),於是每次升級都重印同樣的 55 列、理由完全相同,把審閱者真正需要核可的那 2 列埋在底下。現在它們摺疊成一行,**而那一行寫出自己摺了幾個**,總數不變——一個不聲明自己設限的上限,讀起來就像「就這些了」。摘要那行同樣改為 `Update: 57 (2 changed, 55 unconditional reinstall)`;單獨的 `Update: 57` 是真的,而且什麼都沒回答,**而決定要不要核可一次升級時讀的正是摘要**。
|
|
26
|
+
|
|
27
|
+
### 變更
|
|
28
|
+
|
|
29
|
+
- **無條件重裝的理由字串收斂為單一匯出常數**,不再是產生端一份、渲染端一份。兩份副本之間的漂移在這裡是無聲的:摺疊會單純地停止摺疊,而計畫看起來與它一直以來的樣子一模一樣。
|
|
30
|
+
|
|
31
|
+
### 測試
|
|
32
|
+
|
|
33
|
+
- **為版本標記補上行為層測試,與既有的形狀測試並存。** 隨修正加入的迴歸測試斷言的是原始碼文字——那兩個函式含有 `readManifest(projectPath)`——若有人重構成「呼叫它然後丟掉結果」,它仍然全綠。而這裡宣稱的是行為,所以 `tests/e2e/update-version-advances.test.js` 會真的跑一次安裝、種下探針版本、執行 `update --apply --yes --skills`,再斷言標記真的動了。已雙向驗過:修正在場為綠,還原缺陷為紅。
|
|
34
|
+
- 兩份新測試都斷言**正反兩臂**。摺疊測試會檢查一般計畫完全不受影響,因為只驗「那 55 列不見了」的測試,對一個把所有 update 列都丟掉的渲染器也照樣會過。
|
|
35
|
+
|
|
20
36
|
## [6.7.1] - 2026-08-18
|
|
21
37
|
|
|
22
38
|
### 修正
|
|
@@ -15,7 +15,7 @@ status: current
|
|
|
15
15
|
|
|
16
16
|
> **語言**: [English](../../README.md) | 繁體中文 | [简体中文](../zh-CN/README.md)
|
|
17
17
|
|
|
18
|
-
**版本**: 6.7.
|
|
18
|
+
**版本**: 6.7.2 | **發布日期**: 2026-08-18 | **授權**: [雙重授權](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
語言無關、框架無關的軟體專案文件標準。透過 AI 原生工作流,確保不同技術堆疊之間的一致性、品質和可維護性。
|
|
21
21
|
|
package/package.json
CHANGED
package/src/commands/update.js
CHANGED
|
@@ -1933,7 +1933,26 @@ async function updateSkillsOnly(projectPath, manifest, options) {
|
|
|
1933
1933
|
Object.assign(manifest.skillHashes, result.allFileHashes);
|
|
1934
1934
|
}
|
|
1935
1935
|
|
|
1936
|
-
|
|
1936
|
+
// 🔴 Re-read before writing. `manifest` was loaded at the top of the update
|
|
1937
|
+
// command, BEFORE the reconciler ran; the reconciler writes its own copy to
|
|
1938
|
+
// disk — including the advanced `upstream.version` — and writing this stale
|
|
1939
|
+
// object back silently reverts it.
|
|
1940
|
+
//
|
|
1941
|
+
// Measured 2026-08-18 with probes inside plan-executor: results=57 failing=0,
|
|
1942
|
+
// registry version "6.7.1", "about to write upstream = {version:'6.7.1'}" —
|
|
1943
|
+
// and the value on disk afterwards was still the old one. The reconciler did
|
|
1944
|
+
// everything right and this write undid it. Both runs exited 0 and printed
|
|
1945
|
+
// "57 succeeded", so a repo could be fully upgraded while its own manifest
|
|
1946
|
+
// said otherwise, and the weekly staleness scout — which reads exactly
|
|
1947
|
+
// `upstream.version` — kept reporting it as behind. (XSPEC-382 R5)
|
|
1948
|
+
//
|
|
1949
|
+
// Only the fields this function owns are carried over: copying the whole
|
|
1950
|
+
// object back would reintroduce the same overwrite for any field a later
|
|
1951
|
+
// step adds.
|
|
1952
|
+
const freshForSkills = readManifest(projectPath) || manifest;
|
|
1953
|
+
freshForSkills.skills = manifest.skills;
|
|
1954
|
+
freshForSkills.skillHashes = manifest.skillHashes;
|
|
1955
|
+
writeManifest(freshForSkills, projectPath);
|
|
1937
1956
|
|
|
1938
1957
|
console.log();
|
|
1939
1958
|
process.exit(0);
|
|
@@ -2040,7 +2059,28 @@ async function updateCommandsOnly(projectPath, manifest, options) {
|
|
|
2040
2059
|
replaceCommandHashesForUpdatedAgents(manifest.commandHashes, result.allFileHashes);
|
|
2041
2060
|
}
|
|
2042
2061
|
|
|
2043
|
-
|
|
2062
|
+
// 🔴 Re-read before writing. `manifest` was loaded at the top of the update
|
|
2063
|
+
// command, BEFORE the reconciler ran; the reconciler writes its own copy to
|
|
2064
|
+
// disk — including the advanced `upstream.version` — and writing this stale
|
|
2065
|
+
// object back silently reverts it.
|
|
2066
|
+
//
|
|
2067
|
+
// Measured 2026-08-18 with probes inside plan-executor: results=57 failing=0,
|
|
2068
|
+
// registry version "6.7.1", "about to write upstream = {version:'6.7.1'}" —
|
|
2069
|
+
// and the value on disk afterwards was still the old one. The reconciler did
|
|
2070
|
+
// everything right and this write undid it. Both runs exited 0 and printed
|
|
2071
|
+
// "57 succeeded", so a repo could be fully upgraded while its own manifest
|
|
2072
|
+
// said otherwise, and the weekly staleness scout — which reads exactly
|
|
2073
|
+
// `upstream.version` — kept reporting it as behind. (XSPEC-382 R5)
|
|
2074
|
+
//
|
|
2075
|
+
// Only the fields this function owns are carried over: copying the whole
|
|
2076
|
+
// object back would reintroduce the same overwrite for any field a later
|
|
2077
|
+
// step adds.
|
|
2078
|
+
// Found by traversal, not by hitting it: the same shape existed here, and
|
|
2079
|
+
// fixing only the instance I ran into would have left the class open.
|
|
2080
|
+
const freshForCommands = readManifest(projectPath) || manifest;
|
|
2081
|
+
freshForCommands.commands = manifest.commands;
|
|
2082
|
+
freshForCommands.commandHashes = manifest.commandHashes;
|
|
2083
|
+
writeManifest(freshForCommands, projectPath);
|
|
2044
2084
|
|
|
2045
2085
|
console.log();
|
|
2046
2086
|
process.exit(0);
|
|
@@ -39,6 +39,17 @@
|
|
|
39
39
|
* @param {boolean} [options.force=false] - Force update even if hashes match
|
|
40
40
|
* @returns {ReconciliationPlan}
|
|
41
41
|
*/
|
|
42
|
+
/**
|
|
43
|
+
* The reason attached to every skill/command action, because no content
|
|
44
|
+
* comparison is implemented for them (XSPEC-382 R1).
|
|
45
|
+
*
|
|
46
|
+
* Exported and referenced by `formatPlan` rather than spelled out twice: two
|
|
47
|
+
* copies of a string are two things that can drift, and the drift here is
|
|
48
|
+
* silent — the collapse would just stop collapsing and the plan would go back
|
|
49
|
+
* to listing all 57 rows, which is indistinguishable from "it always did that".
|
|
50
|
+
*/
|
|
51
|
+
export const UNCONDITIONAL_REINSTALL_REASON = 'no hash available for comparison, re-installing';
|
|
52
|
+
|
|
42
53
|
export function computeDiff(desired, actual, options = {}) {
|
|
43
54
|
const { force = false } = options;
|
|
44
55
|
const actions = [];
|
|
@@ -138,7 +149,7 @@ function diffCategory(desiredMap, actualMap, category, actions, warnings, summar
|
|
|
138
149
|
type: 'update',
|
|
139
150
|
category,
|
|
140
151
|
path: desiredEntry.relativePath,
|
|
141
|
-
reason:
|
|
152
|
+
reason: UNCONDITIONAL_REINSTALL_REASON,
|
|
142
153
|
details: {
|
|
143
154
|
sourcePath: desiredEntry.sourcePath,
|
|
144
155
|
metadata: desiredEntry.metadata
|
|
@@ -339,6 +350,14 @@ export function formatPlan(plan) {
|
|
|
339
350
|
(grouped[action.type] || []).push(action);
|
|
340
351
|
}
|
|
341
352
|
|
|
353
|
+
// XSPEC-382 R3/R4 — how many updates are unconditional reinstalls rather than
|
|
354
|
+
// real changes. Computed once here because both the Update section and the
|
|
355
|
+
// Summary need it, and a reader of the Summary alone must not be left with a
|
|
356
|
+
// number that answers nothing: `Update: 57` is true and useless.
|
|
357
|
+
const unconditionalCount = grouped.update.filter(
|
|
358
|
+
(a) => a.reason === UNCONDITIONAL_REINSTALL_REASON
|
|
359
|
+
).length;
|
|
360
|
+
|
|
342
361
|
if (grouped.create.length > 0) {
|
|
343
362
|
lines.push(`+ Create (${grouped.create.length}):`);
|
|
344
363
|
for (const a of grouped.create) {
|
|
@@ -348,10 +367,30 @@ export function formatPlan(plan) {
|
|
|
348
367
|
}
|
|
349
368
|
|
|
350
369
|
if (grouped.update.length > 0) {
|
|
370
|
+
// XSPEC-382 R3 — collapse the unconditional reinstalls.
|
|
371
|
+
//
|
|
372
|
+
// Every skill is always an `update` because no content comparison exists for
|
|
373
|
+
// them (see the `hash: null` on both sides of the diff, and R1). On a real
|
|
374
|
+
// upgrade that produced `Update (57)` where 55 rows carried one identical
|
|
375
|
+
// reason and only 2 were actual changes — the two a reviewer needs to see,
|
|
376
|
+
// buried under fifty-five that say nothing about this upgrade.
|
|
377
|
+
//
|
|
378
|
+
// Collapsed, NOT hidden: the count is printed. A capped list that does not
|
|
379
|
+
// say it was capped reads as "these are all of them", which is the failure
|
|
380
|
+
// this repo keeps finding. Printing the denominator alongside the excluded
|
|
381
|
+
// count is the `class-level-fix` rule.
|
|
382
|
+
const real = grouped.update.filter((a) => a.reason !== UNCONDITIONAL_REINSTALL_REASON);
|
|
383
|
+
const collapsed = unconditionalCount;
|
|
384
|
+
|
|
351
385
|
lines.push(`~ Update (${grouped.update.length}):`);
|
|
352
|
-
for (const a of
|
|
386
|
+
for (const a of real) {
|
|
353
387
|
lines.push(` ~ ${a.path} (${a.reason})`);
|
|
354
388
|
}
|
|
389
|
+
if (collapsed > 0) {
|
|
390
|
+
lines.push(
|
|
391
|
+
` i ${collapsed} UDS-managed skill/command director${collapsed === 1 ? 'y' : 'ies'} reinstalled unconditionally — no content comparison is implemented for them (XSPEC-382)`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
355
394
|
lines.push('');
|
|
356
395
|
}
|
|
357
396
|
|
|
@@ -373,7 +412,11 @@ export function formatPlan(plan) {
|
|
|
373
412
|
|
|
374
413
|
lines.push('Summary:');
|
|
375
414
|
lines.push(` Create: ${plan.summary.create}`);
|
|
376
|
-
lines.push(
|
|
415
|
+
lines.push(
|
|
416
|
+
unconditionalCount > 0
|
|
417
|
+
? ` Update: ${plan.summary.update} (${plan.summary.update - unconditionalCount} changed, ${unconditionalCount} unconditional reinstall)`
|
|
418
|
+
: ` Update: ${plan.summary.update}`
|
|
419
|
+
);
|
|
377
420
|
lines.push(` Migrate Block: ${plan.summary.migrate_block}`);
|
|
378
421
|
lines.push(` Delete: ${plan.summary.delete}`);
|
|
379
422
|
lines.push(` Unchanged: ${plan.summary.unchanged}`);
|
package/standards-registry.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "6.7.
|
|
3
|
+
"version": "6.7.2",
|
|
4
4
|
"lastUpdated": "2026-05-13",
|
|
5
5
|
"description": "Standards registry for universal-dev-standards with integrated skills and AI-optimized formats",
|
|
6
6
|
"formats": {
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
"standards": {
|
|
59
59
|
"name": "universal-dev-standards",
|
|
60
60
|
"url": "https://github.com/AsiaOstrich/universal-dev-standards",
|
|
61
|
-
"version": "6.7.
|
|
61
|
+
"version": "6.7.2"
|
|
62
62
|
},
|
|
63
63
|
"skills": {
|
|
64
64
|
"name": "universal-dev-standards",
|
|
65
65
|
"url": "https://github.com/AsiaOstrich/universal-dev-standards",
|
|
66
66
|
"localPath": "skills",
|
|
67
67
|
"rawUrl": "https://raw.githubusercontent.com/AsiaOstrich/universal-dev-standards/main/skills",
|
|
68
|
-
"version": "6.7.
|
|
68
|
+
"version": "6.7.2",
|
|
69
69
|
"note": "Skills are now included in the main repository under skills/"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
@@ -2260,7 +2260,7 @@
|
|
|
2260
2260
|
"id": "license-compliance",
|
|
2261
2261
|
"name": "License Compliance Standards",
|
|
2262
2262
|
"nameZh": "授權合規標準",
|
|
2263
|
-
"version": "6.7.
|
|
2263
|
+
"version": "6.7.2",
|
|
2264
2264
|
"source": {
|
|
2265
2265
|
"human": "core/license-compliance.md",
|
|
2266
2266
|
"ai": "ai/standards/license-compliance.ai.yaml"
|
|
@@ -2272,7 +2272,7 @@
|
|
|
2272
2272
|
"id": "verification-oracle",
|
|
2273
2273
|
"name": "Verification Oracle Standards",
|
|
2274
2274
|
"nameZh": "驗證 Oracle 標準",
|
|
2275
|
-
"version": "6.7.
|
|
2275
|
+
"version": "6.7.2",
|
|
2276
2276
|
"source": {
|
|
2277
2277
|
"human": "core/verification-oracle.md",
|
|
2278
2278
|
"ai": "ai/standards/verification-oracle.ai.yaml"
|
|
@@ -2284,7 +2284,7 @@
|
|
|
2284
2284
|
"id": "model-provenance",
|
|
2285
2285
|
"name": "Model Provenance Policy Standards",
|
|
2286
2286
|
"nameZh": "模型來源政策標準",
|
|
2287
|
-
"version": "6.7.
|
|
2287
|
+
"version": "6.7.2",
|
|
2288
2288
|
"source": {
|
|
2289
2289
|
"human": "core/model-provenance.md",
|
|
2290
2290
|
"ai": "ai/standards/model-provenance.ai.yaml"
|
|
@@ -2296,7 +2296,7 @@
|
|
|
2296
2296
|
"id": "resource-cost-boundary",
|
|
2297
2297
|
"name": "Resource / Cost Boundary Declaration Standards",
|
|
2298
2298
|
"nameZh": "資源/成本邊界宣告標準",
|
|
2299
|
-
"version": "6.7.
|
|
2299
|
+
"version": "6.7.2",
|
|
2300
2300
|
"source": {
|
|
2301
2301
|
"human": "core/resource-cost-boundary.md",
|
|
2302
2302
|
"ai": "ai/standards/resource-cost-boundary.ai.yaml"
|