universal-dev-standards 6.3.5 → 6.3.7
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 +16 -2
- package/bundled/locales/zh-CN/README.md +1 -1
- package/bundled/locales/zh-CN/ai/options/git-workflow/gitflow.ai.yaml +4 -4
- package/bundled/locales/zh-CN/ai/options/git-workflow/github-flow.ai.yaml +1 -1
- package/bundled/locales/zh-CN/ai/options/git-workflow/merge-commit.ai.yaml +3 -3
- package/bundled/locales/zh-CN/ai/options/git-workflow/rebase-ff.ai.yaml +2 -2
- package/bundled/locales/zh-CN/ai/options/git-workflow/squash-merge.ai.yaml +4 -4
- package/bundled/locales/zh-CN/ai/options/git-workflow/trunk-based.ai.yaml +2 -2
- package/bundled/locales/zh-CN/ai/options/testing/security-testing.ai.yaml +1 -1
- package/bundled/locales/zh-TW/CHANGELOG.md +16 -2
- package/bundled/locales/zh-TW/README.md +1 -1
- package/bundled/locales/zh-TW/ai/options/git-workflow/gitflow.ai.yaml +4 -4
- package/bundled/locales/zh-TW/ai/options/git-workflow/github-flow.ai.yaml +1 -1
- package/bundled/locales/zh-TW/ai/options/git-workflow/merge-commit.ai.yaml +3 -3
- package/bundled/locales/zh-TW/ai/options/git-workflow/rebase-ff.ai.yaml +2 -2
- package/bundled/locales/zh-TW/ai/options/git-workflow/squash-merge.ai.yaml +4 -4
- package/bundled/locales/zh-TW/ai/options/git-workflow/trunk-based.ai.yaml +2 -2
- package/bundled/locales/zh-TW/ai/options/testing/security-testing.ai.yaml +1 -1
- package/bundled/skills/ai/standards/checkin.ai.yaml +9 -9
- package/bundled/skills/ai/standards/commit.ai.yaml +7 -7
- package/bundled/skills/ai/standards/refactoring.ai.yaml +15 -15
- package/bundled/skills/ai/standards/testing.ai.yaml +11 -11
- package/package.json +1 -1
- package/src/commands/deps.js +13 -1
- package/src/utils/dependency-resolution.js +94 -6
- package/standards-registry.json +7 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.3.
|
|
4
|
-
translation_version: 6.3.
|
|
3
|
+
source_version: 6.3.7
|
|
4
|
+
translation_version: 6.3.7
|
|
5
5
|
last_synced: 2026-08-07
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
@@ -17,6 +17,20 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.3.7] - 2026-08-07
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **`uds deps` 只读 root 的 manifest,于是 monorepo 得到一个关于自己一部分的干净答案。** npm workspaces 会把声明放在不只一份 `package.json`,而这个命令只看了其中一份。实测于一个真实项目:回报 34 个依赖,实际声明 47 个——**在 workspace 里的那 13 个是隐形的,而其中一个带着 high 级别的公告**。**一个没有带着自己范围的计数,与一个完整的计数无从分辨**,而那正是这个命令存在要回报的失效。现在会从 `workspaces` 字段展开、检查每一份 manifest,并在报告中打印纳入了哪些 workspace,让分母自己带着范围。每一行漂移都标明它来自哪个 workspace——否则读者知道某个包漂移了,却不知道该去改哪一份 `package.json`。
|
|
25
|
+
- 三个细节决定了「有覆盖 workspaces」与「看起来有覆盖」的差别。lockfile 的条目可能被 hoist 到 root,**也可能**嵌套在 workspace 底下,所以两处都查;只查一处会把另一处回报成「not present in package-lock.json」,而**一个被捏造出来的未知读起来像一个发现**。workspace 依赖于另一个 workspace 时是文件链接而非已发布包,因此跳过而不查询——问 npm 会得到 404 并被记成 unverifiable。以及,比「最后一段结尾一个 `*`」更复杂的 `workspaces` 模式现在会**大声失败**而非匹配一个子集:**静静地覆盖得比作者本意少,是同一个缺陷换个地方发生**。
|
|
26
|
+
|
|
27
|
+
## [6.3.6] - 2026-08-07
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **6.3.5 说出货的标准全部可解析。它数的是 287 份里的 141 份。** 6.3.5 加的那道闸门**明文列举三个目录且不递归**,于是 `ai/options/`、`locales/`、`skills/`——三者皆由 `prepack` 打包进 tarball——都落在它的分母之外。6.3.5 发布后,那些目录里仍有 **10 份无法解析**,散在两个中文语系与 `skills/`。**一道自行列举范围的闸门,在下一次有人新增目录时就过期了**,因此它已改为走访整个 repo、检查构建与 vendor 路径之外的每一份 `.ai.yaml`——本 repo 为 759 份,而它先前宣称「完整」的是 423 份。
|
|
32
|
+
- **另有 8 份能解析、但解析出来是错的——这是任何「能不能解析」的检查都看不见的。** `{UT:70%,IT:20%}` 不是映射:冒号后没有空格时,YAML 读到的是一个纯量键 `UT:70%`、其值为 null。未加引号的 `- git commit -m "feat: add model"` 会变成 `{'git commit -m "feat': 'add model"'}`。**它们通过每一项语法检查,同时交给 agent 一堆胡言。** 闸门现在也会拒绝「键含引号字符」或「冒号后无空格」——那是纯量被静默读成映射的指纹——两个分支皆以对照组实测而非假设。
|
|
33
|
+
|
|
20
34
|
## [6.3.5] - 2026-08-07
|
|
21
35
|
|
|
22
36
|
### Fixed
|
|
@@ -15,7 +15,7 @@ status: current
|
|
|
15
15
|
|
|
16
16
|
> **语言**: [English](../../README.md) | [繁體中文](../zh-TW/README.md) | 简体中文
|
|
17
17
|
|
|
18
|
-
**版本**: 6.3.
|
|
18
|
+
**版本**: 6.3.7 | **发布日期**: 2026-07-31 | **授权**: [双重授权](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
语言无关、框架无关的软件项目文档标准。通过 AI 原生工作流,确保不同技术栈之间的一致性、质量和可维护性。
|
|
21
21
|
|
|
@@ -76,7 +76,7 @@ workflow:
|
|
|
76
76
|
commands:
|
|
77
77
|
- "# 进行变更..."
|
|
78
78
|
- git add .
|
|
79
|
-
- git commit -m "feat: {描述}"
|
|
79
|
+
- 'git commit -m "feat: {描述}"'
|
|
80
80
|
|
|
81
81
|
- step: 合併到 develop
|
|
82
82
|
commands:
|
|
@@ -114,7 +114,7 @@ workflow:
|
|
|
114
114
|
- step: 修正并完成
|
|
115
115
|
commands:
|
|
116
116
|
- "# 修正問題"
|
|
117
|
-
- git commit -m "fix: {描述}"
|
|
117
|
+
- 'git commit -m "fix: {描述}"'
|
|
118
118
|
- git checkout main
|
|
119
119
|
- git merge --no-ff hotfix/{問題}
|
|
120
120
|
- git tag -a v{版本}
|
|
@@ -126,8 +126,8 @@ quick_reference:
|
|
|
126
126
|
branches:
|
|
127
127
|
columns: [分支, 用途, 来源, 合併到]
|
|
128
128
|
rows:
|
|
129
|
-
- [main, 正式發布,
|
|
130
|
-
- [develop, 集成, main, -]
|
|
129
|
+
- [main, 正式發布, "-", "-"]
|
|
130
|
+
- [develop, 集成, main, "-"]
|
|
131
131
|
- [feature/*, 新功能, develop, develop]
|
|
132
132
|
- [release/*, 發布准备, develop, "main, develop"]
|
|
133
133
|
- [hotfix/*, 緊急修正, main, "main, develop"]
|
|
@@ -44,9 +44,9 @@ workflow:
|
|
|
44
44
|
|
|
45
45
|
- step: 进行多次有意義的提交
|
|
46
46
|
commands:
|
|
47
|
-
- git commit -m "feat: add user model"
|
|
48
|
-
- git commit -m "feat: add user service"
|
|
49
|
-
- git commit -m "test: add user tests"
|
|
47
|
+
- 'git commit -m "feat: add user model"'
|
|
48
|
+
- 'git commit -m "feat: add user service"'
|
|
49
|
+
- 'git commit -m "test: add user tests"'
|
|
50
50
|
|
|
51
51
|
- step: 使用合併提交合併
|
|
52
52
|
commands:
|
|
@@ -49,8 +49,8 @@ workflow:
|
|
|
49
49
|
|
|
50
50
|
- step: 进行提交
|
|
51
51
|
commands:
|
|
52
|
-
- git commit -m "feat: implement feature part 1"
|
|
53
|
-
- git commit -m "feat: implement feature part 2"
|
|
52
|
+
- 'git commit -m "feat: implement feature part 1"'
|
|
53
|
+
- 'git commit -m "feat: implement feature part 2"'
|
|
54
54
|
|
|
55
55
|
- step: 变基到最新 main
|
|
56
56
|
commands:
|
|
@@ -43,9 +43,9 @@ workflow:
|
|
|
43
43
|
|
|
44
44
|
- step: 进行多次提交
|
|
45
45
|
commands:
|
|
46
|
-
- git commit -m "wip: initial implementation"
|
|
47
|
-
- git commit -m "wip: add tests"
|
|
48
|
-
- git commit -m "wip: fix lint errors"
|
|
46
|
+
- 'git commit -m "wip: initial implementation"'
|
|
47
|
+
- 'git commit -m "wip: add tests"'
|
|
48
|
+
- 'git commit -m "wip: fix lint errors"'
|
|
49
49
|
|
|
50
50
|
- step: 壓縮合併(通過 GitHub/GitLab)
|
|
51
51
|
commands:
|
|
@@ -56,7 +56,7 @@ workflow:
|
|
|
56
56
|
commands:
|
|
57
57
|
- git checkout main
|
|
58
58
|
- git merge --squash feature/{功能名称}
|
|
59
|
-
- git commit -m "feat(scope): 總結消息 (#PR号)"
|
|
59
|
+
- 'git commit -m "feat(scope): 總結消息 (#PR号)"'
|
|
60
60
|
|
|
61
61
|
example:
|
|
62
62
|
before: |
|
|
@@ -61,7 +61,7 @@ workflow:
|
|
|
61
61
|
commands:
|
|
62
62
|
- "# 进行小而聚焦的变更"
|
|
63
63
|
- git add .
|
|
64
|
-
- git commit -m "feat: {描述}"
|
|
64
|
+
- 'git commit -m "feat: {描述}"'
|
|
65
65
|
|
|
66
66
|
- step: 推送到 trunk
|
|
67
67
|
commands:
|
|
@@ -79,7 +79,7 @@ workflow:
|
|
|
79
79
|
commands:
|
|
80
80
|
- "# 进行变更..."
|
|
81
81
|
- git add .
|
|
82
|
-
- git commit -m "feat: {描述}"
|
|
82
|
+
- 'git commit -m "feat: {描述}"'
|
|
83
83
|
|
|
84
84
|
- step: 快速合併
|
|
85
85
|
commands:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
source: ../../CHANGELOG.md
|
|
3
|
-
source_version: 6.3.
|
|
4
|
-
translation_version: 6.3.
|
|
3
|
+
source_version: 6.3.7
|
|
4
|
+
translation_version: 6.3.7
|
|
5
5
|
last_synced: 2026-08-07
|
|
6
6
|
status: current
|
|
7
7
|
---
|
|
@@ -17,6 +17,20 @@ status: current
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
+
## [6.3.7] - 2026-08-07
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **`uds deps` 只讀 root 的 manifest,於是 monorepo 得到一個關於自己一部分的乾淨答案。** npm workspaces 會把宣告放在不只一份 `package.json`,而這個指令只看了其中一份。實測於一個真實專案:回報 34 個相依,實際宣告 47 個——**在 workspace 裡的那 13 個是隱形的,而其中一個帶著 high 等級的公告**。**一個沒有帶著自己範圍的計數,與一個完整的計數無從分辨**,而那正是這個指令存在要回報的失效。現在會從 `workspaces` 欄位展開、檢查每一份 manifest,並在報告中印出納入了哪些 workspace,讓分母自己帶著範圍。每一列漂移都標明它來自哪個 workspace——否則讀者知道某個套件漂移了,卻不知道該去改哪一份 `package.json`。
|
|
25
|
+
- 三個細節決定了「有涵蓋 workspaces」與「看起來有涵蓋」的差別。lockfile 的條目可能被 hoist 到 root,**也可能**巢狀在 workspace 底下,所以兩處都查;只查一處會把另一處回報成「not present in package-lock.json」,而**一個被捏造出來的未知讀起來像一個發現**。workspace 相依於另一個 workspace 時是檔案連結而非已發布套件,因此跳過而不查詢——問 npm 會得到 404 並被記成 unverifiable。以及,比「最後一段結尾一個 `*`」更複雜的 `workspaces` 模式現在會**大聲失敗**而非匹配一個子集:**靜靜地涵蓋得比作者本意少,是同一個缺陷換個地方發生**。
|
|
26
|
+
|
|
27
|
+
## [6.3.6] - 2026-08-07
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **6.3.5 說出貨的標準全部可解析。它數的是 287 份裡的 141 份。** 6.3.5 加的那道閘門**明文列舉三個目錄且不遞迴**,於是 `ai/options/`、`locales/`、`skills/`——三者皆由 `prepack` 打包進 tarball——都落在它的分母之外。6.3.5 發布後,那些目錄裡仍有 **10 份無法解析**,散在兩個中文語系與 `skills/`。**一道自行列舉範圍的閘門,在下一次有人新增目錄時就過期了**,因此它已改為走訪整個 repo、檢查建置與 vendor 路徑之外的每一份 `.ai.yaml`——本 repo 為 759 份,而它先前宣稱「完整」的是 423 份。
|
|
32
|
+
- **另有 8 份能解析、但解析出來是錯的——這是任何「能不能解析」的檢查都看不見的。** `{UT:70%,IT:20%}` 不是對映:冒號後沒有空格時,YAML 讀到的是一個純量鍵 `UT:70%`、其值為 null。未加引號的 `- git commit -m "feat: add model"` 會變成 `{'git commit -m "feat': 'add model"'}`。**它們通過每一項語法檢查,同時交給 agent 一堆胡言。** 閘門現在也會拒絕「鍵含引號字元」或「冒號後無空格」——那是純量被靜默讀成對映的指紋——兩個分支皆以對照組實測而非假設。
|
|
33
|
+
|
|
20
34
|
## [6.3.5] - 2026-08-07
|
|
21
35
|
|
|
22
36
|
### Fixed
|
|
@@ -15,7 +15,7 @@ status: current
|
|
|
15
15
|
|
|
16
16
|
> **語言**: [English](../../README.md) | 繁體中文 | [简体中文](../zh-CN/README.md)
|
|
17
17
|
|
|
18
|
-
**版本**: 6.3.
|
|
18
|
+
**版本**: 6.3.7 | **發布日期**: 2026-07-31 | **授權**: [雙重授權](../../LICENSE) (CC BY 4.0 + MIT)
|
|
19
19
|
|
|
20
20
|
語言無關、框架無關的軟體專案文件標準。透過 AI 原生工作流,確保不同技術堆疊之間的一致性、品質和可維護性。
|
|
21
21
|
|
|
@@ -76,7 +76,7 @@ workflow:
|
|
|
76
76
|
commands:
|
|
77
77
|
- "# 進行變更..."
|
|
78
78
|
- git add .
|
|
79
|
-
- git commit -m "feat: {描述}"
|
|
79
|
+
- 'git commit -m "feat: {描述}"'
|
|
80
80
|
|
|
81
81
|
- step: 合併到 develop
|
|
82
82
|
commands:
|
|
@@ -114,7 +114,7 @@ workflow:
|
|
|
114
114
|
- step: 修正並完成
|
|
115
115
|
commands:
|
|
116
116
|
- "# 修正問題"
|
|
117
|
-
- git commit -m "fix: {描述}"
|
|
117
|
+
- 'git commit -m "fix: {描述}"'
|
|
118
118
|
- git checkout main
|
|
119
119
|
- git merge --no-ff hotfix/{問題}
|
|
120
120
|
- git tag -a v{版本}
|
|
@@ -126,8 +126,8 @@ quick_reference:
|
|
|
126
126
|
branches:
|
|
127
127
|
columns: [分支, 用途, 來源, 合併到]
|
|
128
128
|
rows:
|
|
129
|
-
- [main, 正式發布,
|
|
130
|
-
- [develop, 整合, main, -]
|
|
129
|
+
- [main, 正式發布, "-", "-"]
|
|
130
|
+
- [develop, 整合, main, "-"]
|
|
131
131
|
- [feature/*, 新功能, develop, develop]
|
|
132
132
|
- [release/*, 發布準備, develop, "main, develop"]
|
|
133
133
|
- [hotfix/*, 緊急修正, main, "main, develop"]
|
|
@@ -44,9 +44,9 @@ workflow:
|
|
|
44
44
|
|
|
45
45
|
- step: 進行多次有意義的提交
|
|
46
46
|
commands:
|
|
47
|
-
- git commit -m "feat: add user model"
|
|
48
|
-
- git commit -m "feat: add user service"
|
|
49
|
-
- git commit -m "test: add user tests"
|
|
47
|
+
- 'git commit -m "feat: add user model"'
|
|
48
|
+
- 'git commit -m "feat: add user service"'
|
|
49
|
+
- 'git commit -m "test: add user tests"'
|
|
50
50
|
|
|
51
51
|
- step: 使用合併提交合併
|
|
52
52
|
commands:
|
|
@@ -49,8 +49,8 @@ workflow:
|
|
|
49
49
|
|
|
50
50
|
- step: 進行提交
|
|
51
51
|
commands:
|
|
52
|
-
- git commit -m "feat: implement feature part 1"
|
|
53
|
-
- git commit -m "feat: implement feature part 2"
|
|
52
|
+
- 'git commit -m "feat: implement feature part 1"'
|
|
53
|
+
- 'git commit -m "feat: implement feature part 2"'
|
|
54
54
|
|
|
55
55
|
- step: 變基到最新 main
|
|
56
56
|
commands:
|
|
@@ -43,9 +43,9 @@ workflow:
|
|
|
43
43
|
|
|
44
44
|
- step: 進行多次提交
|
|
45
45
|
commands:
|
|
46
|
-
- git commit -m "wip: initial implementation"
|
|
47
|
-
- git commit -m "wip: add tests"
|
|
48
|
-
- git commit -m "wip: fix lint errors"
|
|
46
|
+
- 'git commit -m "wip: initial implementation"'
|
|
47
|
+
- 'git commit -m "wip: add tests"'
|
|
48
|
+
- 'git commit -m "wip: fix lint errors"'
|
|
49
49
|
|
|
50
50
|
- step: 壓縮合併(通過 GitHub/GitLab)
|
|
51
51
|
commands:
|
|
@@ -56,7 +56,7 @@ workflow:
|
|
|
56
56
|
commands:
|
|
57
57
|
- git checkout main
|
|
58
58
|
- git merge --squash feature/{功能名稱}
|
|
59
|
-
- git commit -m "feat(scope): 總結訊息 (#PR號)"
|
|
59
|
+
- 'git commit -m "feat(scope): 總結訊息 (#PR號)"'
|
|
60
60
|
|
|
61
61
|
example:
|
|
62
62
|
before: |
|
|
@@ -61,7 +61,7 @@ workflow:
|
|
|
61
61
|
commands:
|
|
62
62
|
- "# 進行小而聚焦的變更"
|
|
63
63
|
- git add .
|
|
64
|
-
- git commit -m "feat: {描述}"
|
|
64
|
+
- 'git commit -m "feat: {描述}"'
|
|
65
65
|
|
|
66
66
|
- step: 推送到 trunk
|
|
67
67
|
commands:
|
|
@@ -79,7 +79,7 @@ workflow:
|
|
|
79
79
|
commands:
|
|
80
80
|
- "# 進行變更..."
|
|
81
81
|
- git add .
|
|
82
|
-
- git commit -m "feat: {描述}"
|
|
82
|
+
- 'git commit -m "feat: {描述}"'
|
|
83
83
|
|
|
84
84
|
- step: 快速合併
|
|
85
85
|
commands:
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
v: 1.3.0
|
|
3
3
|
philosophy: [complete_unit,working_state,reversible,has_tests,understandable]
|
|
4
4
|
checklist:
|
|
5
|
-
build: {compile:"0 errors/warnings",deps:"installed,locked"}
|
|
6
|
-
test: {pass:"100%",new_tested:true,"cov":"not_decreased"}
|
|
7
|
-
quality: {standards:true,smells:"methods≤50,nest≤3,complex≤10,no_dup",security:"no_secrets,no_sqli,no_xss"}
|
|
8
|
-
docs: {api:true,readme:"if_needed",changelog:"user_facing→[Unreleased]"}
|
|
9
|
-
workflow: {branch:"feature/|fix/|docs/|chore/",commit:conventional,synced:true}
|
|
5
|
+
build: {compile: "0 errors/warnings",deps: "installed,locked"}
|
|
6
|
+
test: {pass: "100%",new_tested: true,"cov": "not_decreased"}
|
|
7
|
+
quality: {standards: true,smells: "methods≤50,nest≤3,complex≤10,no_dup",security: "no_secrets,no_sqli,no_xss"}
|
|
8
|
+
docs: {api: true,readme: "if_needed",changelog: "user_facing→[Unreleased]"}
|
|
9
|
+
workflow: {branch: "feature/|fix/|docs/|chore/",commit: conventional,synced: true}
|
|
10
10
|
never_commit: [build_error,test_fail,incomplete_feature,WIP_critical,debug_code,commented_code]
|
|
11
11
|
timing:
|
|
12
12
|
good: [completed_unit,bug_fixed_with_test,independent_refactor,runnable]
|
|
13
13
|
bad: [build_fail,test_fail,incomplete,experimental_TODO]
|
|
14
14
|
granularity:
|
|
15
|
-
size: {files:"1-10",lines:"50-300",scope:single_concern}
|
|
15
|
+
size: {files: "1-10",lines: "50-300",scope: single_concern}
|
|
16
16
|
combine: [feature+tests,tightly_related]
|
|
17
17
|
separate: [A+B,refactor+feature,fix+incidental]
|
|
18
18
|
special:
|
|
19
|
-
emergency: {stash:"git stash save WIP:desc",wip_branch:"git checkout -b wip/name"}
|
|
20
|
-
experimental: {branch:"experiment/name",free_commits:true,success:"squash→merge",fail:"doc_lessons→delete"}
|
|
21
|
-
hotfix: {from:main,minimal:true,mark:"[URGENT]"}
|
|
19
|
+
emergency: {stash: "git stash save WIP:desc",wip_branch: "git checkout -b wip/name"}
|
|
20
|
+
experimental: {branch: "experiment/name",free_commits: true,success: "squash→merge",fail: "doc_lessons→delete"}
|
|
21
|
+
hotfix: {from: main,minimal: true,mark: "[URGENT]"}
|
|
@@ -3,17 +3,17 @@ v: 1.0.0
|
|
|
3
3
|
format: "<type>(<scope>): <subject>\n\n<body>\n\n<footer>"
|
|
4
4
|
types: [feat,fix,refactor,docs,style,test,perf,build,ci,chore,revert,security]
|
|
5
5
|
types_zh: [功能,修復,重構,文件,樣式,測試,效能,建置,CI,雜項,回退,安全]
|
|
6
|
-
subject: {len:"≤72(50ideal)",tense:imperative,cap:first,period:no}
|
|
6
|
+
subject: {len: "≤72(50ideal)",tense: imperative,cap: first,period: no}
|
|
7
7
|
lang_decision:
|
|
8
8
|
tree: [open_source?→EN, intl_team?→EN, single_lang_team?→team_pref|EN, mixed→bilingual]
|
|
9
9
|
matrix:
|
|
10
|
-
open_source: {l:EN,r:"intl contributors"}
|
|
11
|
-
intl_team: {l:EN,r:"common lang"}
|
|
12
|
-
local_EN: {l:EN,r:"tools,mobility"}
|
|
13
|
-
local_ZH: {l:ZH,r:"clarity,preference"}
|
|
14
|
-
mixed: {l:bilingual,r:"best of both"}
|
|
10
|
+
open_source: {l: EN,r: "intl contributors"}
|
|
11
|
+
intl_team: {l: EN,r: "common lang"}
|
|
12
|
+
local_EN: {l: EN,r: "tools,mobility"}
|
|
13
|
+
local_ZH: {l: ZH,r: "clarity,preference"}
|
|
14
|
+
mixed: {l: bilingual,r: "best of both"}
|
|
15
15
|
bilingual_format: "type(scope): EN subject. 中文 subject.\n\nEN body\n\n中文 body\n\nfooter"
|
|
16
|
-
refs: {closes:"#N→auto_close",fixes:"#N→auto_close",refs:"#N→link_only"}
|
|
16
|
+
refs: {closes: "#N→auto_close",fixes: "#N→auto_close",refs: "#N→link_only"}
|
|
17
17
|
breaking: "BREAKING CHANGE: description\n\nMigration guide:\n1. step"
|
|
18
18
|
examples:
|
|
19
19
|
good: ["feat(auth): Add OAuth2 Google login","fix(api): Resolve memory leak"]
|
|
@@ -2,33 +2,33 @@
|
|
|
2
2
|
# Token-optimized for AI assistants
|
|
3
3
|
v: 1.0.0
|
|
4
4
|
decision:
|
|
5
|
-
tree: [in_prod?→n:rewrite|y:next, understand?→n:char_tests|y:next, "cov>60%?"→n:add_tests|y:next, arch_ok?→n:strangler|y:incr_refactor]
|
|
5
|
+
tree: [in_prod?→n:rewrite|y:next, understand?→n:char_tests|y:next, '"cov>60%?"→n:add_tests|y:next', arch_ok?→n:strangler|y:incr_refactor]
|
|
6
6
|
refactor: [large_code,good_tests,critical_biz,team_knows,sound_arch,tight_deadline,low_risk]
|
|
7
7
|
rewrite: [small,no_tests,downtime_ok,no_knowledge,flawed_arch,flex_time,high_risk]
|
|
8
8
|
warn: "Second-System Effect: Don't add features, over-abstract, ignore lessons"
|
|
9
9
|
scales:
|
|
10
|
-
S: {t:"5-15m",s:method,tech:[extract,rename,inline,const]}
|
|
11
|
-
M: {t:"hrs-days",s:module,req:"cov>80%"}
|
|
12
|
-
L: {t:"wks-mos",s:system,patterns:[strangler,branch_abstract,expand_contract]}
|
|
10
|
+
S: {t: "5-15m",s: method,tech: [extract,rename,inline,const]}
|
|
11
|
+
M: {t: "hrs-days",s: module,req: "cov>80%"}
|
|
12
|
+
L: {t: "wks-mos",s: system,patterns: [strangler,branch_abstract,expand_contract]}
|
|
13
13
|
patterns:
|
|
14
|
-
strangler: {1:intercept,2:migrate_partial,3:complete_decommission}
|
|
15
|
-
branch_abstract: {1:"client→iface→old",2:"→[old,new_toggled]",3:"→new_only"}
|
|
16
|
-
expand_contract: {expand:"add_both",migrate:"switch_clients",contract:"remove_old"}
|
|
14
|
+
strangler: {1: intercept,2: migrate_partial,3: complete_decommission}
|
|
15
|
+
branch_abstract: {1: "client→iface→old",2: "→[old,new_toggled]",3: "→new_only"}
|
|
16
|
+
expand_contract: {expand: "add_both",migrate: "switch_clients",contract: "remove_old"}
|
|
17
17
|
legacy:
|
|
18
18
|
def: "code without tests"
|
|
19
19
|
char_test: [call,expect_fail,run,update_assert,repeat]
|
|
20
|
-
seams: {obj:polymorphism,preproc:macros,link:DI}
|
|
21
|
-
sprout: {method:"new_fn→call_from_old",class:"new_class→ref_from_old"}
|
|
22
|
-
wrap: {method:"rename→wrapper",class:decorator}
|
|
20
|
+
seams: {obj: polymorphism,preproc: macros,link: DI}
|
|
21
|
+
sprout: {method: "new_fn→call_from_old",class: "new_class→ref_from_old"}
|
|
22
|
+
wrap: {method: "rename→wrapper",class: decorator}
|
|
23
23
|
db:
|
|
24
|
-
expand_contract: {1:"add_new,write_both",2:"copy,verify,read_new",3:"drop_old"}
|
|
24
|
+
expand_contract: {1: "add_new,write_both",2: "copy,verify,read_new",3: "drop_old"}
|
|
25
25
|
workflow:
|
|
26
26
|
before: [success_criteria,"cov>80%",clean_dir,branch,communicate]
|
|
27
27
|
during: [one_change,test_every,revert_if_fail,commit_often,no_new_func]
|
|
28
28
|
after: [tests_pass,measurably_better,docs,reviewed]
|
|
29
29
|
metrics:
|
|
30
|
-
code: {complexity:"<10",dup:"<3%"}
|
|
31
|
-
test: {cov:"≥80%",speed:faster,flaky:decrease}
|
|
30
|
+
code: {complexity: "<10",dup: "<3%"}
|
|
31
|
+
test: {cov: "≥80%",speed: faster,flaky: decrease}
|
|
32
32
|
debt:
|
|
33
|
-
quadrant: {prudent_delib:"know_debt",reckless_delib:"no_time",prudent_inadv:"learned_now",reckless_inadv:"what_layering?"}
|
|
34
|
-
priority: {H:"blocks→immediate",M:"slows→next_sprint",L:"minor→opportunistic"}
|
|
33
|
+
quadrant: {prudent_delib: "know_debt",reckless_delib: "no_time",prudent_inadv: "learned_now",reckless_inadv: "what_layering?"}
|
|
34
|
+
priority: {H: "blocks→immediate",M: "slows→next_sprint",L: "minor→opportunistic"}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
# Testing Standards - Ultra Compressed
|
|
2
2
|
v: 1.1.0
|
|
3
|
-
pyramid: {UT:70%,IT:20%,E2E:10%}
|
|
3
|
+
pyramid: {UT: 70%,IT: 20%,E2E: 10%}
|
|
4
4
|
levels:
|
|
5
|
-
UT: {scope:fn/class,speed:"<100ms",deps:mocked}
|
|
6
|
-
IT: {scope:components,speed:"1-10s",deps:"real_db(container)"}
|
|
7
|
-
ST: {scope:full_system,speed:min,deps:prod_like}
|
|
8
|
-
E2E: {scope:user_journey,speed:"30s+",deps:all_real}
|
|
9
|
-
coverage: {line:"70→85%",branch:"60→80%",fn:"80→90%"}
|
|
5
|
+
UT: {scope: fn/class,speed: "<100ms",deps: mocked}
|
|
6
|
+
IT: {scope: components,speed: "1-10s",deps: "real_db(container)"}
|
|
7
|
+
ST: {scope: full_system,speed: min,deps: prod_like}
|
|
8
|
+
E2E: {scope: user_journey,speed: "30s+",deps: all_real}
|
|
9
|
+
coverage: {line: "70→85%",branch: "60→80%",fn: "80→90%"}
|
|
10
10
|
theory:
|
|
11
11
|
chain: "error(human)→defect(code)→failure(runtime)"
|
|
12
12
|
oracle: [spec,reference,consistency,heuristic]
|
|
13
|
-
static: {def:"no_exec",tech:[review,analysis],tools:[ESLint,SonarQube]}
|
|
14
|
-
dynamic: {def:"execute",tech:[unit,integration,system]}
|
|
13
|
+
static: {def: "no_exec",tech: [review,analysis],tools: [ESLint,SonarQube]}
|
|
14
|
+
dynamic: {def: "execute",tech: [unit,integration,system]}
|
|
15
15
|
design:
|
|
16
16
|
black_box:
|
|
17
17
|
equiv_partition: "divide inputs→classes"
|
|
18
18
|
boundary: "test at edges"
|
|
19
19
|
decision_table: "conditions→actions"
|
|
20
20
|
state_trans: "valid state sequences"
|
|
21
|
-
white_box: {stmt:"every_stmt",branch:"every_decision",cond:"T/F",path:"all_paths"}
|
|
22
|
-
risk: {formula:"likelihood×impact",HH:extensive,HL:good,LH:good,LL:basic}
|
|
21
|
+
white_box: {stmt: "every_stmt",branch: "every_decision",cond: "T/F",path: "all_paths"}
|
|
22
|
+
risk: {formula: "likelihood×impact",HH: extensive,HL: good,LH: good,LL: basic}
|
|
23
23
|
doubles:
|
|
24
24
|
stub: "predefined returns"
|
|
25
25
|
mock: "verify interactions"
|
|
26
26
|
fake: "simplified impl"
|
|
27
27
|
spy: "record+delegate"
|
|
28
|
-
use: {UT:mock_all,IT:"fake_db,stub_api",ST:"real,fake_external",E2E:all_real}
|
|
28
|
+
use: {UT: mock_all,IT: "fake_db,stub_api",ST: "real,fake_external",E2E: all_real}
|
|
29
29
|
FIRST: [Fast,Independent,Repeatable,Self_validating,Timely]
|
|
30
30
|
AAA: [Arrange,Act,Assert]
|
|
31
31
|
antipatterns: [interdependence,flaky,impl_details,over_mock,no_assert,magic_values]
|
package/package.json
CHANGED
package/src/commands/deps.js
CHANGED
|
@@ -37,6 +37,15 @@ export function render(result) {
|
|
|
37
37
|
lines.push('');
|
|
38
38
|
lines.push(`${label} — ${result.examined} runtime dependenc${result.examined === 1 ? 'y' : 'ies'} checked`);
|
|
39
39
|
|
|
40
|
+
// The scope of the count, printed whenever it is more than the root. A
|
|
41
|
+
// denominator without its scope is what let this command report "34
|
|
42
|
+
// dependencies checked" for a repository that declared 47 across two
|
|
43
|
+
// manifests — the reader has no way to tell a complete answer from a subset.
|
|
44
|
+
const wsCount = result.workspaces?.length ?? 0;
|
|
45
|
+
if (wsCount > 0) {
|
|
46
|
+
lines.push(chalk.dim(` across the root and ${wsCount} workspace${wsCount === 1 ? '' : 's'}: ${result.workspaces.join(', ')}`));
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
if (!result.hasLockfile) {
|
|
41
50
|
lines.push(chalk.yellow(' no package-lock.json — nothing to compare the registry against'));
|
|
42
51
|
}
|
|
@@ -52,9 +61,12 @@ export function render(result) {
|
|
|
52
61
|
// underneath it" shape that R4 rewrote the supply-chain standard to stop.
|
|
53
62
|
lines.push(chalk.yellow(` ${result.drifted.length} tested ≠ resolves:`));
|
|
54
63
|
for (const d of result.drifted) {
|
|
64
|
+
// The workspace is part of the finding, not decoration: without it the
|
|
65
|
+
// reader knows a package drifted but not which package.json to edit.
|
|
66
|
+
const where = d.workspaceDir ? chalk.dim(` [${d.workspaceDir}]`) : '';
|
|
55
67
|
lines.push(
|
|
56
68
|
` ${chalk.bold(d.name)} ${chalk.dim(d.range)}` +
|
|
57
|
-
` tested=${chalk.cyan(d.locked)} resolves=${chalk.yellow(d.resolved)}`
|
|
69
|
+
` tested=${chalk.cyan(d.locked)} resolves=${chalk.yellow(d.resolved)}${where}`
|
|
58
70
|
);
|
|
59
71
|
}
|
|
60
72
|
lines.push('');
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
*/
|
|
67
67
|
|
|
68
68
|
import { spawn } from 'node:child_process';
|
|
69
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
69
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
70
70
|
import { join } from 'node:path';
|
|
71
71
|
import semver from 'semver';
|
|
72
72
|
|
|
@@ -200,6 +200,66 @@ function spawnNpm(cwd) {
|
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Expand npm's `workspaces` field into directories that contain a
|
|
205
|
+
* package.json. // implements XSPEC-366 R1 (workspaces)
|
|
206
|
+
*
|
|
207
|
+
* Accepts both spellings — an array, or `{ packages: [...] }` — and supports a
|
|
208
|
+
* trailing `*` in the last segment, which is what `packages/*` needs and is the
|
|
209
|
+
* shape npm's own docs use. A pattern that matches nothing is returned as
|
|
210
|
+
* nothing rather than as an error: an empty `packages/*` is a normal state for
|
|
211
|
+
* a repository that has not added one yet.
|
|
212
|
+
*/
|
|
213
|
+
function expandWorkspaces(root, field) {
|
|
214
|
+
const patterns = Array.isArray(field) ? field : Array.isArray(field?.packages) ? field.packages : [];
|
|
215
|
+
const dirs = [];
|
|
216
|
+
for (const pattern of patterns) {
|
|
217
|
+
if (typeof pattern !== 'string' || pattern.length === 0) continue;
|
|
218
|
+
const star = pattern.indexOf('*');
|
|
219
|
+
if (star === -1) {
|
|
220
|
+
if (existsSync(join(root, pattern, 'package.json'))) dirs.push(pattern);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// Only a trailing `*` in the final segment is supported. Anything more
|
|
224
|
+
// exotic is rejected loudly rather than silently matching less than the
|
|
225
|
+
// author meant — a workspace quietly outside the denominator is the defect
|
|
226
|
+
// this module exists to report.
|
|
227
|
+
const prefix = pattern.slice(0, star);
|
|
228
|
+
if (!pattern.endsWith('*') || prefix.includes('*') || (prefix.length > 0 && !prefix.endsWith('/'))) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`unsupported workspaces pattern ${JSON.stringify(pattern)} — only a trailing "*" in the last segment is understood`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
const parent = join(root, prefix);
|
|
234
|
+
if (!existsSync(parent)) continue;
|
|
235
|
+
for (const entry of readdirSync(parent, { withFileTypes: true })) {
|
|
236
|
+
if (!entry.isDirectory()) continue;
|
|
237
|
+
const rel = `${prefix}${entry.name}`;
|
|
238
|
+
if (existsSync(join(root, rel, 'package.json'))) dirs.push(rel);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return dirs;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Where the lockfile records this dependency.
|
|
246
|
+
*
|
|
247
|
+
* npm hoists what it can to the root `node_modules` and nests the rest under
|
|
248
|
+
* the workspace, so both have to be tried; checking only one under-reports and
|
|
249
|
+
* calls the miss "unverifiable", which is noise that trains people to ignore
|
|
250
|
+
* the report.
|
|
251
|
+
*/
|
|
252
|
+
function lockedVersionFor(lock, workspaceDir, name) {
|
|
253
|
+
const candidates = workspaceDir
|
|
254
|
+
? [`${workspaceDir}/node_modules/${name}`, `node_modules/${name}`]
|
|
255
|
+
: [`node_modules/${name}`];
|
|
256
|
+
for (const key of candidates) {
|
|
257
|
+
const entry = lock?.packages?.[key];
|
|
258
|
+
if (entry?.version) return entry.version;
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
203
263
|
/** Map over `items` with a bounded number of concurrent workers. */
|
|
204
264
|
async function mapWithConcurrency(items, limit, worker) {
|
|
205
265
|
const results = new Array(items.length);
|
|
@@ -238,16 +298,37 @@ export async function measureResolutionDrift(root, options = {}) {
|
|
|
238
298
|
const lockPath = join(root, 'package-lock.json');
|
|
239
299
|
const lock = existsSync(lockPath) ? JSON.parse(readFileSync(lockPath, 'utf8')) : null;
|
|
240
300
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
301
|
+
// Workspaces are examined too. A monorepo that declares its shipped
|
|
302
|
+
// front-end in a workspace and its server at the root has two manifests, and
|
|
303
|
+
// reading only the root reports a clean subset as if it were the whole — the
|
|
304
|
+
// failure this module exists to catch, performed by the tool itself.
|
|
305
|
+
const workspaceDirs = expandWorkspaces(root, pkg.workspaces);
|
|
306
|
+
const manifests = [{ dir: null, name: pkg.name ?? null, pkg }];
|
|
307
|
+
for (const dir of workspaceDirs) {
|
|
308
|
+
const wsPkg = JSON.parse(readFileSync(join(root, dir, 'package.json'), 'utf8'));
|
|
309
|
+
manifests.push({ dir, name: wsPkg.name ?? dir, pkg: wsPkg });
|
|
310
|
+
}
|
|
311
|
+
// A workspace depending on a sibling workspace is a file link, not a
|
|
312
|
+
// registry package. Querying npm for it returns 404, which would be recorded
|
|
313
|
+
// as unverifiable — a fabricated unknown, which is worse than a missing one
|
|
314
|
+
// because it looks like a finding.
|
|
315
|
+
const localNames = new Set(manifests.map((m) => m.pkg.name).filter(Boolean));
|
|
316
|
+
|
|
317
|
+
const declared = [];
|
|
318
|
+
for (const { dir, name: workspace, pkg: manifest } of manifests) {
|
|
319
|
+
for (const kind of ['dependencies', 'optionalDependencies']) {
|
|
320
|
+
for (const [name, range] of Object.entries(manifest[kind] ?? {})) {
|
|
321
|
+
if (localNames.has(name)) continue;
|
|
322
|
+
declared.push({ name, range, kind, workspace, workspaceDir: dir });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
245
326
|
|
|
246
327
|
const run = options.run ?? spawnNpm(root);
|
|
247
328
|
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
248
329
|
|
|
249
330
|
const rows = await mapWithConcurrency(declared, concurrency, async (dep) => {
|
|
250
|
-
const locked = lock
|
|
331
|
+
const locked = lockedVersionFor(lock, dep.workspaceDir, dep.name);
|
|
251
332
|
let resolved;
|
|
252
333
|
try {
|
|
253
334
|
resolved = await resolveViaNpm(dep.name, dep.range, run);
|
|
@@ -282,6 +363,13 @@ export async function measureResolutionDrift(root, options = {}) {
|
|
|
282
363
|
root,
|
|
283
364
|
packageName: pkg.name ?? null,
|
|
284
365
|
hasLockfile: lock !== null,
|
|
366
|
+
/**
|
|
367
|
+
* Workspace directories examined alongside the root, so a reader can tell
|
|
368
|
+
* "no workspaces here" from "workspaces were not looked at". The count
|
|
369
|
+
* printed by the report is the denominator, and a denominator without its
|
|
370
|
+
* scope is the shape this module was written to refuse.
|
|
371
|
+
*/
|
|
372
|
+
workspaces: workspaceDirs,
|
|
285
373
|
examined: rows.length,
|
|
286
374
|
consistent,
|
|
287
375
|
drifted,
|
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.3.
|
|
3
|
+
"version": "6.3.7",
|
|
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.3.
|
|
61
|
+
"version": "6.3.7"
|
|
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.3.
|
|
68
|
+
"version": "6.3.7",
|
|
69
69
|
"note": "Skills are now included in the main repository under skills/"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
@@ -2236,7 +2236,7 @@
|
|
|
2236
2236
|
"id": "license-compliance",
|
|
2237
2237
|
"name": "License Compliance Standards",
|
|
2238
2238
|
"nameZh": "授權合規標準",
|
|
2239
|
-
"version": "6.3.
|
|
2239
|
+
"version": "6.3.7",
|
|
2240
2240
|
"source": {
|
|
2241
2241
|
"human": "core/license-compliance.md",
|
|
2242
2242
|
"ai": "ai/standards/license-compliance.ai.yaml"
|
|
@@ -2248,7 +2248,7 @@
|
|
|
2248
2248
|
"id": "verification-oracle",
|
|
2249
2249
|
"name": "Verification Oracle Standards",
|
|
2250
2250
|
"nameZh": "驗證 Oracle 標準",
|
|
2251
|
-
"version": "6.3.
|
|
2251
|
+
"version": "6.3.7",
|
|
2252
2252
|
"source": {
|
|
2253
2253
|
"human": "core/verification-oracle.md",
|
|
2254
2254
|
"ai": "ai/standards/verification-oracle.ai.yaml"
|
|
@@ -2260,7 +2260,7 @@
|
|
|
2260
2260
|
"id": "model-provenance",
|
|
2261
2261
|
"name": "Model Provenance Policy Standards",
|
|
2262
2262
|
"nameZh": "模型來源政策標準",
|
|
2263
|
-
"version": "6.3.
|
|
2263
|
+
"version": "6.3.7",
|
|
2264
2264
|
"source": {
|
|
2265
2265
|
"human": "core/model-provenance.md",
|
|
2266
2266
|
"ai": "ai/standards/model-provenance.ai.yaml"
|
|
@@ -2272,7 +2272,7 @@
|
|
|
2272
2272
|
"id": "resource-cost-boundary",
|
|
2273
2273
|
"name": "Resource / Cost Boundary Declaration Standards",
|
|
2274
2274
|
"nameZh": "資源/成本邊界宣告標準",
|
|
2275
|
-
"version": "6.3.
|
|
2275
|
+
"version": "6.3.7",
|
|
2276
2276
|
"source": {
|
|
2277
2277
|
"human": "core/resource-cost-boundary.md",
|
|
2278
2278
|
"ai": "ai/standards/resource-cost-boundary.ai.yaml"
|