universal-dev-standards 5.15.1 → 5.16.0
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/ai/standards/acceptance-criteria-traceability.ai.yaml +31 -0
- package/bundled/ai/standards/forward-derivation-standards.ai.yaml +23 -0
- package/bundled/ai/standards/knowledge-graph-memory.ai.yaml +1 -1
- package/bundled/core/acceptance-criteria-traceability.md +46 -0
- package/bundled/core/forward-derivation-standards.md +19 -0
- package/bundled/core/knowledge-graph-memory.md +2 -2
- package/bundled/locales/zh-CN/CHANGELOG.md +13 -3
- package/bundled/locales/zh-CN/README.md +1 -1
- package/bundled/locales/zh-CN/core/acceptance-criteria-traceability.md +46 -0
- package/bundled/locales/zh-CN/core/forward-derivation-standards.md +19 -0
- package/bundled/locales/zh-CN/skills/ac-coverage/SKILL.md +194 -0
- package/bundled/locales/zh-CN/skills/adr-assistant/SKILL.md +135 -40
- package/bundled/locales/zh-CN/skills/brainstorm-assistant/SKILL.md +217 -63
- package/bundled/locales/zh-CN/skills/brainstorm-assistant/guide.md +599 -0
- package/bundled/locales/zh-CN/skills/commands/brainstorm.md +92 -25
- package/bundled/locales/zh-CN/skills/commit-standards/SKILL.md +78 -16
- package/bundled/locales/zh-CN/skills/contract-test-assistant/SKILL.md +85 -26
- package/bundled/locales/zh-CN/skills/deploy-assistant/SKILL.md +189 -0
- package/bundled/locales/zh-CN/skills/dev-methodology/SKILL.md +110 -0
- package/bundled/locales/zh-CN/skills/dev-methodology/guide.md +255 -0
- package/bundled/locales/zh-CN/skills/dev-workflow-guide/SKILL.md +70 -11
- package/bundled/locales/zh-CN/skills/journey-test-assistant/SKILL.md +209 -0
- package/bundled/locales/zh-CN/skills/knowledge-graph/SKILL.md +58 -0
- package/bundled/locales/zh-CN/skills/knowledge-graph/guide.md +74 -0
- package/bundled/locales/zh-CN/skills/migration-assistant/SKILL.md +125 -8
- package/bundled/locales/zh-CN/skills/observability-assistant/guide.md +188 -0
- package/bundled/locales/zh-CN/skills/orchestrate/SKILL.md +173 -0
- package/bundled/locales/zh-CN/skills/plan/SKILL.md +240 -0
- package/bundled/locales/zh-CN/skills/push/SKILL.md +242 -0
- package/bundled/locales/zh-CN/skills/retrospective-assistant/SKILL.md +104 -36
- package/bundled/locales/zh-CN/skills/reverse-engineer/SKILL.md +88 -32
- package/bundled/locales/zh-CN/skills/runbook-assistant/guide.md +216 -0
- package/bundled/locales/zh-CN/skills/skill-builder/SKILL.md +149 -0
- package/bundled/locales/zh-CN/skills/slo-assistant/guide.md +188 -0
- package/bundled/locales/zh-CN/skills/spec-derivation/SKILL.md +86 -0
- package/bundled/locales/zh-CN/skills/spec-derivation/guide.md +476 -0
- package/bundled/locales/zh-CN/skills/spec-driven-dev/SKILL.md +155 -81
- package/bundled/locales/zh-CN/skills/sweep/SKILL.md +151 -0
- package/bundled/locales/zh-CN/skills/testing-guide/SKILL.md +207 -110
- package/bundled/locales/zh-TW/CHANGELOG.md +13 -3
- package/bundled/locales/zh-TW/README.md +1 -1
- package/bundled/locales/zh-TW/core/acceptance-criteria-traceability.md +46 -0
- package/bundled/locales/zh-TW/core/browser-compatibility-standards.md +222 -5
- package/bundled/locales/zh-TW/core/contract-testing-standards.md +184 -5
- package/bundled/locales/zh-TW/core/cross-flow-regression.md +192 -5
- package/bundled/locales/zh-TW/core/forward-derivation-standards.md +19 -0
- package/bundled/locales/zh-TW/core/knowledge-graph-memory.md +2 -2
- package/bundled/locales/zh-TW/core/release-readiness-gate.md +186 -5
- package/bundled/locales/zh-TW/skills/adr-assistant/SKILL.md +21 -42
- package/bundled/locales/zh-TW/skills/brainstorm-assistant/SKILL.md +212 -59
- package/bundled/locales/zh-TW/skills/brainstorm-assistant/guide.md +266 -579
- package/bundled/locales/zh-TW/skills/commands/brainstorm.md +91 -26
- package/bundled/locales/zh-TW/skills/commit-standards/SKILL.md +77 -15
- package/bundled/locales/zh-TW/skills/contract-test-assistant/SKILL.md +75 -16
- package/bundled/locales/zh-TW/skills/dev-methodology/guide.md +255 -0
- package/bundled/locales/zh-TW/skills/dev-workflow-guide/SKILL.md +125 -64
- package/bundled/locales/zh-TW/skills/knowledge-graph/SKILL.md +5 -5
- package/bundled/locales/zh-TW/skills/knowledge-graph/guide.md +74 -0
- package/bundled/locales/zh-TW/skills/migration-assistant/SKILL.md +128 -11
- package/bundled/locales/zh-TW/skills/observability-assistant/guide.md +188 -0
- package/bundled/locales/zh-TW/skills/orchestrate/SKILL.md +3 -2
- package/bundled/locales/zh-TW/skills/plan/SKILL.md +3 -2
- package/bundled/locales/zh-TW/skills/push/SKILL.md +3 -2
- package/bundled/locales/zh-TW/skills/retrospective-assistant/SKILL.md +94 -28
- package/bundled/locales/zh-TW/skills/reverse-engineer/SKILL.md +84 -28
- package/bundled/locales/zh-TW/skills/runbook-assistant/guide.md +216 -0
- package/bundled/locales/zh-TW/skills/slo-assistant/guide.md +188 -0
- package/bundled/locales/zh-TW/skills/spec-derivation/guide.md +476 -0
- package/bundled/locales/zh-TW/skills/spec-driven-dev/SKILL.md +148 -77
- package/bundled/locales/zh-TW/skills/testing-guide/SKILL.md +141 -44
- package/bundled/skills/brainstorm-assistant/SKILL.md +142 -106
- package/bundled/skills/brainstorm-assistant/guide.md +256 -661
- package/bundled/skills/commands/brainstorm.md +51 -30
- package/bundled/skills/knowledge-graph/SKILL.md +5 -5
- package/bundled/skills/knowledge-graph/guide.md +4 -4
- package/package.json +2 -2
- package/src/commands/check.js +11 -2
- package/src/lint/i18n.js +109 -23
- package/standards-registry.json +4 -4
- package/bundled/locales/zh-TW/docs/SKILL-FALLBACK-GUIDE.md +0 -407
|
@@ -6,9 +6,9 @@ argument-hint: "[problem or feature idea | 問題或功能構想]"
|
|
|
6
6
|
|
|
7
7
|
# Brainstorm Assistant | 腦力激盪助手
|
|
8
8
|
|
|
9
|
-
Structured ideation before specification writing. Transform vague ideas into actionable feature proposals through
|
|
9
|
+
Structured ideation before specification writing. Transform vague ideas into actionable feature proposals through a persona-ensemble brainstorm.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
在撰寫規格前進行結構化發想。透過 persona 集成式腦力激盪,將模糊構想轉化為可執行的功能提案。
|
|
12
12
|
|
|
13
13
|
## Usage | 用法
|
|
14
14
|
|
|
@@ -19,33 +19,37 @@ Structured ideation before specification writing. Transform vague ideas into act
|
|
|
19
19
|
## Workflow | 工作流程
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
FRAME ──► DIVERGE
|
|
23
|
-
定義問題
|
|
22
|
+
PRE-FLIGHT ──► FRAME ──► DIVERGE ──────────► CONVERGE ─────────► OUTPUT
|
|
23
|
+
防錨定 定義問題 persona 集成+透鏡 多評審面板+硬角色反駁 輸出提案
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
| Phase | Goal | Key Techniques | 目標 |
|
|
27
|
-
|
|
26
|
+
| Phase | Goal | Key Techniques (v3) | 目標 |
|
|
27
|
+
|-------|------|---------------------|------|
|
|
28
|
+
| **PRE-FLIGHT** | Prevent AI anchoring | User writes 3 ideas first; no analogy seed | 防止錨定 |
|
|
28
29
|
| **FRAME** | Define problem clearly | 5 Whys, HMW, Stakeholder Map | 清楚定義問題 |
|
|
29
|
-
| **DIVERGE** |
|
|
30
|
-
| **CONVERGE** |
|
|
30
|
+
| **DIVERGE** | Force viewpoint diversity | Persona ensemble + diversity lenses | 逼出視角多樣性 |
|
|
31
|
+
| **CONVERGE** | Bias-checked selection | Multi-critic panel + Devil's Advocate/Steelman | 降偏誤選擇 |
|
|
31
32
|
| **OUTPUT** | Actionable report | Brainstorm Report template | 可執行的報告 |
|
|
32
33
|
|
|
33
34
|
## Techniques | 技法
|
|
34
35
|
|
|
35
36
|
| Technique | Best For | 適用場景 |
|
|
36
37
|
|-----------|----------|----------|
|
|
37
|
-
| **
|
|
38
|
-
| **
|
|
39
|
-
| **
|
|
40
|
-
| **
|
|
41
|
-
| **
|
|
38
|
+
| **Persona ensemble** | Forced viewpoint diversity (v3 core) | 強制視角多樣性(v3 核心) |
|
|
39
|
+
| **Diversity lenses** | Analogical / reversal / morphological | 突破顯而易見區 |
|
|
40
|
+
| **Multi-critic panel** | Bias-reduced scoring (v3 core) | 降偏誤評分(v3 核心) |
|
|
41
|
+
| **Devil's Advocate + Steelman** | Hard-role rebuttal | 硬角色反駁 |
|
|
42
|
+
| **5 Whys / HMW / SCAMPER / Six Hats** | Classic framing & divergence | 經典框定與發散 |
|
|
42
43
|
|
|
43
44
|
## Examples | 範例
|
|
44
45
|
|
|
45
46
|
```bash
|
|
46
|
-
/brainstorm
|
|
47
|
-
/brainstorm "user retention"
|
|
48
|
-
/brainstorm --
|
|
47
|
+
/brainstorm # Start interactive session
|
|
48
|
+
/brainstorm "user retention" # Brainstorm around a topic
|
|
49
|
+
/brainstorm --enhanced "user retention" # Parallel persona ensemble (if host supports it)
|
|
50
|
+
/brainstorm --personas "designer,economist,skeptic" # Custom personas
|
|
51
|
+
/brainstorm --lens analogical "onboarding" # Force the analogical lens
|
|
52
|
+
/brainstorm --quick "reduce checkout friction" # Fast 3-idea mode
|
|
49
53
|
```
|
|
50
54
|
|
|
51
55
|
## Output Format | 輸出格式
|
|
@@ -57,9 +61,10 @@ FRAME ──► DIVERGE ──► CONVERGE ──► OUTPUT
|
|
|
57
61
|
[Refined problem from FRAME phase]
|
|
58
62
|
|
|
59
63
|
## Top 3 Recommendations
|
|
60
|
-
1. **[Idea]** —
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
1. **[Idea]** — Agg. X.X ✓ Passed rebuttal — Persona: [..] — [Why recommended]
|
|
65
|
+
|
|
66
|
+
## Diversity Note
|
|
67
|
+
[How many distinct personas/lenses the surviving ideas span]
|
|
63
68
|
|
|
64
69
|
## Next Steps
|
|
65
70
|
- [ ] Proceed to `/requirement` with top idea
|
|
@@ -83,26 +88,36 @@ After brainstorming, the typical workflow is:
|
|
|
83
88
|
|
|
84
89
|
| Input | AI Action |
|
|
85
90
|
|-------|-----------|
|
|
86
|
-
| `/brainstorm` |
|
|
87
|
-
| `/brainstorm "topic"` |
|
|
88
|
-
| `/brainstorm --
|
|
91
|
+
| `/brainstorm` | 啟動 PRE-FLIGHT,請使用者先寫 問題+3 想法+反向排除 |
|
|
92
|
+
| `/brainstorm "topic"` | 以指定主題啟動 PRE-FLIGHT,再進入 FRAME |
|
|
93
|
+
| `/brainstorm --personas "a,b,c"` | 以自訂 persona 組進入 DIVERGE |
|
|
94
|
+
| `/brainstorm --lens <name>` | 以指定多樣性透鏡為主進入 DIVERGE |
|
|
95
|
+
| `/brainstorm --enhanced` | 若宿主支援子代理則平行跑 persona/評審,否則靜默退回 baseline |
|
|
89
96
|
|
|
90
97
|
### Interaction Script | 互動腳本
|
|
91
98
|
|
|
99
|
+
#### PRE-FLIGHT Phase
|
|
100
|
+
1. 請使用者先寫 問題(一句)+3 個初始想法+最不想要的解法類型
|
|
101
|
+
2. 拒絕「像 X 但給 Y」種子,改寫為底層問題
|
|
102
|
+
|
|
103
|
+
🛑 **STOP**: 收到三項輸入前不生成任何想法(`--skip-preflight` 例外,顯示錨定警告)
|
|
104
|
+
|
|
92
105
|
#### FRAME Phase
|
|
93
|
-
1.
|
|
94
|
-
2.
|
|
106
|
+
1. 釐清問題陳述(5 Whys 找根因)
|
|
107
|
+
2. 重構為 HMW 問題;識別利害關係人
|
|
95
108
|
|
|
96
109
|
🛑 **STOP**: 問題定義後等待使用者確認
|
|
97
110
|
|
|
98
111
|
#### DIVERGE Phase
|
|
99
|
-
1.
|
|
100
|
-
2.
|
|
101
|
-
3.
|
|
112
|
+
1. 逐一以預設 persona(領域專家/懷疑者/跨域類比者/成本約束者/使用者代言)思維鏈生成想法
|
|
113
|
+
2. **分支隔離**:生成各 persona 時不互相預覽,全部產完才一起呈現
|
|
114
|
+
3. 至少套用一個多樣性透鏡(類比/假設反轉/形態矩陣)
|
|
115
|
+
4. 以多樣性(覆蓋的 persona/透鏡數)而非數量為繼續門檻
|
|
102
116
|
|
|
103
117
|
#### CONVERGE Phase
|
|
104
|
-
1.
|
|
105
|
-
2.
|
|
118
|
+
1. 以 3 個獨立評審(工程可行性/使用者影響/策略一致)各自評分後聚合
|
|
119
|
+
2. 對前 3 名跑硬角色 Devil's Advocate(論證會失敗)+ Steelman;使用者以 (a)修改/(b)反駁/(c)移除 回應
|
|
120
|
+
3. 排序並標記通過反駁的想法
|
|
106
121
|
|
|
107
122
|
🛑 **STOP**: 展示 Brainstorm Report 後等待使用者決定下一步
|
|
108
123
|
|
|
@@ -110,7 +125,9 @@ After brainstorming, the typical workflow is:
|
|
|
110
125
|
|
|
111
126
|
| Stop Point | 等待內容 |
|
|
112
127
|
|-----------|---------|
|
|
128
|
+
| PRE-FLIGHT 提交前 | 收到使用者三項輸入 |
|
|
113
129
|
| 問題定義後 | 確認問題正確 |
|
|
130
|
+
| 反駁輪每個反對理由 | 使用者 (a)/(b)/(c) 回應 |
|
|
114
131
|
| 報告展示後 | 決定進入 `/requirement` 或 `/sdd` |
|
|
115
132
|
|
|
116
133
|
### Error Handling | 錯誤處理
|
|
@@ -118,10 +135,14 @@ After brainstorming, the typical workflow is:
|
|
|
118
135
|
| Error Condition | AI Action |
|
|
119
136
|
|-----------------|-----------|
|
|
120
137
|
| 主題太廣泛 | 引導縮小範圍 |
|
|
121
|
-
|
|
|
138
|
+
| 使用「像 X 但給 Y」種子 | 改寫為底層問題再繼續(反種子 guardrail) |
|
|
139
|
+
| 指定的 persona/透鏡不存在 | 列出可用選項供選擇 |
|
|
140
|
+
| `--enhanced` 但宿主不支援子代理 | 靜默退回 baseline,照常進行 |
|
|
141
|
+
| 前 3 名全來自同一 persona/透鏡 | 標示並在輸出前再跑一個透鏡 |
|
|
122
142
|
|
|
123
143
|
## References | 參考
|
|
124
144
|
|
|
125
145
|
* [Brainstorm Assistant Skill](../brainstorm-assistant/SKILL.md)
|
|
146
|
+
* [Brainstorm Assistant Guide](../brainstorm-assistant/guide.md)
|
|
126
147
|
* [Requirement Assistant](../requirement-assistant/SKILL.md)
|
|
127
148
|
* [Spec-Driven Development](../spec-driven-dev/SKILL.md)
|
|
@@ -14,7 +14,7 @@ Answer structural questions across specs, decisions, and code — *"what is the
|
|
|
14
14
|
|
|
15
15
|
回答橫跨規格、決策與程式碼的結構性問題——*「XSPEC-205 的完整影響鏈是什麼?」*——依據[知識圖記憶標準](../../core/knowledge-graph-memory.md)的關係 schema。有無圖引擎皆可運作。
|
|
16
16
|
|
|
17
|
-
> **Implements**: XSPEC-237 Phase 5 — knowledge-graph skill (
|
|
17
|
+
> **Implements**: XSPEC-237 Phase 5 — knowledge-graph skill (EngramGraph opt-in)
|
|
18
18
|
|
|
19
19
|
## Mode Selection | 模式選擇
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ Detect which mode to use **before** answering:
|
|
|
22
22
|
|
|
23
23
|
| Condition | Mode |
|
|
24
24
|
|-----------|------|
|
|
25
|
-
| `
|
|
25
|
+
| `ENGRAM_URL` set, or a local graph engine responds on `/health` | Service mode (engine) |
|
|
26
26
|
| Otherwise | Degraded mode (Markdown) |
|
|
27
27
|
|
|
28
28
|
## Workflow | 工作流程
|
|
@@ -31,7 +31,7 @@ Detect which mode to use **before** answering:
|
|
|
31
31
|
2. **Choose mode** — probe for a graph engine (service) else fall back (degraded).
|
|
32
32
|
3. **Service mode (AC-5b)** — issue a single multi-hop query and present the returned chain, including cross-domain links (code → spec → decision):
|
|
33
33
|
```bash
|
|
34
|
-
curl -s -X POST "$
|
|
34
|
+
curl -s -X POST "$ENGRAM_URL/graph/impact-analysis" \
|
|
35
35
|
-H 'content-type: application/json' \
|
|
36
36
|
-d '{"nodeId":"XSPEC-205","maxHops":3}'
|
|
37
37
|
```
|
|
@@ -47,12 +47,12 @@ See [knowledge-graph-memory](../../core/knowledge-graph-memory.md). Front-matter
|
|
|
47
47
|
|
|
48
48
|
## Next Steps Guidance | 下一步引導
|
|
49
49
|
|
|
50
|
-
- If degraded mode hit a reading-depth limit, tell the user a graph engine (e.g.
|
|
50
|
+
- If degraded mode hit a reading-depth limit, tell the user a graph engine (e.g. EngramGraph) would give a complete chain, and how to set `ENGRAM_URL`.
|
|
51
51
|
- If a referenced id was not found, surface it as a dangling reference to fix.
|
|
52
52
|
- Offer to add missing `impacts`/`impacted_by` front-matter to the documents you traversed.
|
|
53
53
|
|
|
54
54
|
## Reference | 參考
|
|
55
55
|
|
|
56
56
|
- Standard: [core/knowledge-graph-memory.md](../../core/knowledge-graph-memory.md)
|
|
57
|
-
- Engine (opt-in): [
|
|
57
|
+
- Engine (opt-in): [EngramGraph](https://github.com/AsiaOstrich/EngramGraph) — `engramgraph`
|
|
58
58
|
- Detailed guide: [guide.md](guide.md)
|
|
@@ -8,12 +8,12 @@ Companion to [SKILL.md](SKILL.md). Worked examples of both operating modes and t
|
|
|
8
8
|
|
|
9
9
|
## 1. Service Mode (graph engine) | 服務模式
|
|
10
10
|
|
|
11
|
-
When
|
|
11
|
+
When an EngramGraph-compatible engine is reachable, a single query returns the full chain.
|
|
12
12
|
|
|
13
13
|
### Impact analysis | 影響分析
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
curl -s -X POST "$
|
|
16
|
+
curl -s -X POST "$ENGRAM_URL/graph/impact-analysis" \
|
|
17
17
|
-H 'content-type: application/json' \
|
|
18
18
|
-d '{"nodeId":"XSPEC-205","maxHops":3}'
|
|
19
19
|
# => { "nodeId": "XSPEC-205",
|
|
@@ -26,7 +26,7 @@ curl -s -X POST "$CODESAGE_URL/graph/impact-analysis" \
|
|
|
26
26
|
### Confidence feedback (SAGE) | 信心回饋
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
curl -s -X POST "$
|
|
29
|
+
curl -s -X POST "$ENGRAM_URL/graph/ingest" \
|
|
30
30
|
-H 'content-type: application/json' \
|
|
31
31
|
-d '{"type":"test_fail","functionId":"src/a.ts#execute"}'
|
|
32
32
|
```
|
|
@@ -66,4 +66,4 @@ Both modes produce the **same answer shape** (a list of connected Specs/Decision
|
|
|
66
66
|
## Reference | 參考
|
|
67
67
|
|
|
68
68
|
- [core/knowledge-graph-memory.md](../../core/knowledge-graph-memory.md)
|
|
69
|
-
- [
|
|
69
|
+
- [EngramGraph](https://github.com/AsiaOstrich/EngramGraph)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-dev-standards",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.16.0",
|
|
4
4
|
"description": "CLI tool for adopting Universal Development Standards",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"documentation",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@vitest/coverage-v8": "^4.1.5",
|
|
71
71
|
"ajv": "^8.20.0",
|
|
72
72
|
"ajv-formats": "^3.0.1",
|
|
73
|
-
"eslint": "10.4.
|
|
73
|
+
"eslint": "10.4.1",
|
|
74
74
|
"glob": "^13.0.1",
|
|
75
75
|
"globals": "17.6.0",
|
|
76
76
|
"husky": "^9.1.7",
|
package/src/commands/check.js
CHANGED
|
@@ -1861,13 +1861,14 @@ function displayWorkflowStatus(projectPath) {
|
|
|
1861
1861
|
*/
|
|
1862
1862
|
async function runI18nLint(projectPath, options = {}) {
|
|
1863
1863
|
const findings = lintI18nAll({ projectPath });
|
|
1864
|
-
const { errors, warnings } = partitionI18nFindings(findings);
|
|
1864
|
+
const { errors, warnings, infos } = partitionI18nFindings(findings);
|
|
1865
1865
|
|
|
1866
1866
|
if (options.json) {
|
|
1867
1867
|
console.log(JSON.stringify({
|
|
1868
1868
|
summary: {
|
|
1869
1869
|
errors: errors.length,
|
|
1870
1870
|
warnings: warnings.length,
|
|
1871
|
+
infos: infos.length,
|
|
1871
1872
|
},
|
|
1872
1873
|
findings,
|
|
1873
1874
|
}, null, 2));
|
|
@@ -1900,8 +1901,16 @@ async function runI18nLint(projectPath, options = {}) {
|
|
|
1900
1901
|
console.log();
|
|
1901
1902
|
}
|
|
1902
1903
|
|
|
1904
|
+
// Info findings are collapsed to a summary line to avoid flooding output
|
|
1905
|
+
// (e.g. many locale files still lack source_hash). They never fail the gate.
|
|
1906
|
+
if (infos.length > 0) {
|
|
1907
|
+
console.log(chalk.cyan(` ℹ ${infos.length} locale file(s) lack source_hash — silent content drift cannot be detected for them.`));
|
|
1908
|
+
console.log(chalk.gray(' Stamp source_hash (sha256[:12] of canonical) when a locale is verified in sync to enable detection.'));
|
|
1909
|
+
console.log();
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1903
1912
|
console.log(chalk.gray('─'.repeat(50)));
|
|
1904
|
-
console.log(` ${chalk.red('Errors:')} ${errors.length} ${chalk.yellow('Warnings:')} ${warnings.length}`);
|
|
1913
|
+
console.log(` ${chalk.red('Errors:')} ${errors.length} ${chalk.yellow('Warnings:')} ${warnings.length} ${chalk.cyan('Info:')} ${infos.length}`);
|
|
1905
1914
|
console.log();
|
|
1906
1915
|
|
|
1907
1916
|
if (errors.length > 0) {
|
package/src/lint/i18n.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* locale:description-must-match-language (error)
|
|
9
9
|
* locale:must-have-source-frontmatter (error)
|
|
10
10
|
* canonical:l3-language-consistency (warn)
|
|
11
|
-
* translation-drift-warn (warn)
|
|
11
|
+
* translation-drift-warn (warn) version-gap (needs a version on the canonical)
|
|
12
|
+
* translation-content-drift-warn (warn) source_hash mismatch — catches SILENT drift, version-independent (XSPEC-248)
|
|
13
|
+
* translation-hash-missing (info) blind-spot marker: no canonical version AND no source_hash → freshness unverifiable (XSPEC-248)
|
|
12
14
|
*
|
|
13
15
|
* `adopter-must-match-installed-locale` is deferred (needs project context,
|
|
14
16
|
* not source-tree level).
|
|
@@ -18,6 +20,7 @@
|
|
|
18
20
|
|
|
19
21
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
20
22
|
import { join, dirname, basename, resolve } from 'path';
|
|
23
|
+
import { createHash } from 'crypto';
|
|
21
24
|
|
|
22
25
|
// CJK Unified Ideographs ranges (excluding fullwidth punctuation, but
|
|
23
26
|
// including ranges Ext-A/B used by zh-TW/zh-CN). Hangul, Hiragana,
|
|
@@ -30,6 +33,11 @@ const CJK_REGEX = /[㐀-䶿一-鿿豈--ゟ゠-ヿ가-]/;
|
|
|
30
33
|
// eslint-disable-next-line no-control-regex
|
|
31
34
|
const NON_ASCII_REGEX = /[^\x09\x0A\x0D\x20-\x7E]/;
|
|
32
35
|
|
|
36
|
+
// Global variant of CJK_REGEX (same character ranges, derived to avoid
|
|
37
|
+
// re-typing the ranges) — used to *count* CJK characters for the
|
|
38
|
+
// l3-language-consistency ratio check (bilingual templates vs. real drift).
|
|
39
|
+
const CJK_GLOBAL_REGEX = new RegExp(CJK_REGEX.source, 'g');
|
|
40
|
+
|
|
33
41
|
/**
|
|
34
42
|
* Parse a minimal YAML front matter from markdown content.
|
|
35
43
|
* Returns { fm: {key→value}, fmEndLine: number, body: string }
|
|
@@ -112,6 +120,26 @@ function readSourceVersion(filePath) {
|
|
|
112
120
|
return null;
|
|
113
121
|
}
|
|
114
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Compute a stable content hash of a canonical file: sha256 of its content
|
|
125
|
+
* with CRLF normalized and trailing whitespace stripped, truncated to 12 hex.
|
|
126
|
+
*
|
|
127
|
+
* Used for `source_hash` drift detection (XSPEC-248), which — unlike the
|
|
128
|
+
* version-gap check — does NOT depend on the canonical carrying a `version:`
|
|
129
|
+
* field. This is what catches "silent drift": canonical content changed but
|
|
130
|
+
* nobody bumped the locale's version metadata.
|
|
131
|
+
*
|
|
132
|
+
* @param {string} filePath - Absolute path to canonical file
|
|
133
|
+
* @returns {string|null} 12-hex digest, or null if file is missing
|
|
134
|
+
*/
|
|
135
|
+
export function computeSourceHash(filePath) {
|
|
136
|
+
if (!existsSync(filePath)) return null;
|
|
137
|
+
const normalized = readFileSync(filePath, 'utf-8')
|
|
138
|
+
.replace(/\r\n/g, '\n')
|
|
139
|
+
.replace(/\s+$/, '');
|
|
140
|
+
return createHash('sha256').update(normalized).digest('hex').slice(0, 12);
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
/**
|
|
116
144
|
* Lint a canonical SKILL.md or core/*.md file for English-only frontmatter
|
|
117
145
|
* and L3 language consistency.
|
|
@@ -119,14 +147,20 @@ function readSourceVersion(filePath) {
|
|
|
119
147
|
* @param {string} skillMdPath - Absolute path to canonical file
|
|
120
148
|
* @returns {Array<{rule, severity, line, message, file}>}
|
|
121
149
|
*/
|
|
122
|
-
export function lintCanonical(skillMdPath) {
|
|
150
|
+
export function lintCanonical(skillMdPath, opts = {}) {
|
|
151
|
+
// opts.isGuide — canonical guide.md (XSPEC-248): guide descriptions
|
|
152
|
+
// legitimately carry CJK discoverability keywords (e.g. "可觀測性"), so the
|
|
153
|
+
// ASCII-description rule does NOT apply. The l3 body check still does (and
|
|
154
|
+
// runs regardless of description language, since there is no ASCII rule to
|
|
155
|
+
// double-report against).
|
|
156
|
+
const { isGuide = false } = opts;
|
|
123
157
|
const findings = [];
|
|
124
158
|
if (!existsSync(skillMdPath)) return findings;
|
|
125
159
|
const content = readFileSync(skillMdPath, 'utf-8');
|
|
126
160
|
const { fm, body } = parseFrontmatter(content);
|
|
127
161
|
|
|
128
|
-
// Rule: canonical:description-must-be-ascii
|
|
129
|
-
if (fm && fm.description) {
|
|
162
|
+
// Rule: canonical:description-must-be-ascii (SKILL.md / core only; not guides)
|
|
163
|
+
if (!isGuide && fm && fm.description) {
|
|
130
164
|
if (NON_ASCII_REGEX.test(fm.description.value)) {
|
|
131
165
|
findings.push({
|
|
132
166
|
rule: 'canonical:description-must-be-ascii',
|
|
@@ -140,9 +174,13 @@ export function lintCanonical(skillMdPath) {
|
|
|
140
174
|
|
|
141
175
|
// Rule: canonical:l3-language-consistency
|
|
142
176
|
// Heuristic: scan code-block-style output templates (```...```) for
|
|
143
|
-
// non-English example response text.
|
|
144
|
-
// (otherwise the description-must-be-ascii error already
|
|
145
|
-
|
|
177
|
+
// non-English example response text. For SKILL.md/core only run when the
|
|
178
|
+
// description is ASCII (otherwise the description-must-be-ascii error already
|
|
179
|
+
// covers it); for guides (no ASCII rule) always scan the body.
|
|
180
|
+
const runL3 = isGuide
|
|
181
|
+
? true
|
|
182
|
+
: Boolean(fm && fm.description && !NON_ASCII_REGEX.test(fm.description.value));
|
|
183
|
+
if (runL3) {
|
|
146
184
|
const fenceRegex = /```([a-zA-Z0-9_-]*)\n([\s\S]*?)```/g;
|
|
147
185
|
let match;
|
|
148
186
|
while ((match = fenceRegex.exec(body)) !== null) {
|
|
@@ -158,19 +196,28 @@ export function lintCanonical(skillMdPath) {
|
|
|
158
196
|
if (skipLangs.has(lang.toLowerCase())) continue;
|
|
159
197
|
|
|
160
198
|
// Likely an output template / example response (e.g., ```markdown,
|
|
161
|
-
// ```text, ```output, ```response).
|
|
199
|
+
// ```text, ```output, ```response). Only flag when the block is
|
|
200
|
+
// *predominantly* CJK — i.e. an untranslated example dumped into
|
|
201
|
+
// canonical. Deliberate bilingual `English | 中文` templates (house
|
|
202
|
+
// style) stay English-dominant, so they are allowed; a Chinese-only
|
|
203
|
+
// example response is CJK-dominant and still caught. (XSPEC-248 l3
|
|
204
|
+
// refinement — rule vs. bilingual-convention tension.)
|
|
162
205
|
if (CJK_REGEX.test(block)) {
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
206
|
+
const cjkCount = (block.match(CJK_GLOBAL_REGEX) || []).length;
|
|
207
|
+
const asciiLetterCount = (block.match(/[A-Za-z]/g) || []).length;
|
|
208
|
+
if (cjkCount > asciiLetterCount) {
|
|
209
|
+
// Compute approx. line number of the fence start
|
|
210
|
+
const lineNum = content.substring(0, match.index).split('\n').length;
|
|
211
|
+
findings.push({
|
|
212
|
+
rule: 'canonical:l3-language-consistency',
|
|
213
|
+
severity: 'warn',
|
|
214
|
+
line: lineNum,
|
|
215
|
+
file: skillMdPath,
|
|
216
|
+
message: `Canonical body contains a predominantly-CJK example response inside a non-code fenced block (lang=\`${lang || 'plain'}\`). Move the translated example to the locale variant (bilingual \`EN | 中文\` templates are allowed).`
|
|
217
|
+
});
|
|
218
|
+
// Only report first occurrence per file to keep output readable
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
174
221
|
}
|
|
175
222
|
}
|
|
176
223
|
}
|
|
@@ -233,12 +280,14 @@ export function lintLocale(skillMdPath, locale) {
|
|
|
233
280
|
}
|
|
234
281
|
}
|
|
235
282
|
|
|
236
|
-
//
|
|
283
|
+
// Rules: translation-drift-warn (version) + translation-content-drift-warn (hash) + translation-hash-missing
|
|
237
284
|
if (fm.source && fm.translation_version) {
|
|
238
285
|
// Resolve source path relative to this file's directory
|
|
239
286
|
const sourceRel = fm.source.value;
|
|
240
287
|
const sourcePath = resolve(dirname(skillMdPath), sourceRel);
|
|
241
288
|
const actualSourceVersion = readSourceVersion(sourcePath);
|
|
289
|
+
|
|
290
|
+
// (a) version-gap drift — only works when the canonical carries a version
|
|
242
291
|
if (actualSourceVersion) {
|
|
243
292
|
const gap = isMajorOrMinorGap(fm.translation_version.value, actualSourceVersion);
|
|
244
293
|
if (gap) {
|
|
@@ -254,14 +303,40 @@ export function lintLocale(skillMdPath, locale) {
|
|
|
254
303
|
});
|
|
255
304
|
}
|
|
256
305
|
}
|
|
306
|
+
|
|
307
|
+
// (b) content-hash drift — version-independent; catches SILENT drift (XSPEC-248)
|
|
308
|
+
if (fm.source_hash) {
|
|
309
|
+
const currentHash = computeSourceHash(sourcePath);
|
|
310
|
+
if (currentHash && currentHash !== fm.source_hash.value) {
|
|
311
|
+
findings.push({
|
|
312
|
+
rule: 'translation-content-drift-warn',
|
|
313
|
+
severity: 'warn',
|
|
314
|
+
line: fm.source_hash.line,
|
|
315
|
+
file: skillMdPath,
|
|
316
|
+
message: `Canonical content changed since last sync (source_hash \`${fm.source_hash.value}\` != current \`${currentHash}\`). Re-translate and update source_hash.`
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
} else if (!actualSourceVersion) {
|
|
320
|
+
// (c) blind-spot marker: neither a canonical version NOR a source_hash exists,
|
|
321
|
+
// so content freshness cannot be verified at all (how brainstorm rotted to v1.0).
|
|
322
|
+
findings.push({
|
|
323
|
+
rule: 'translation-hash-missing',
|
|
324
|
+
severity: 'info',
|
|
325
|
+
line: fm.source.line,
|
|
326
|
+
file: skillMdPath,
|
|
327
|
+
message: 'Cannot verify content freshness: canonical has no version field and locale has no source_hash. Stamp source_hash (sha256[:12] of canonical) to enable silent-drift detection.'
|
|
328
|
+
});
|
|
329
|
+
}
|
|
257
330
|
}
|
|
258
331
|
|
|
259
332
|
return findings;
|
|
260
333
|
}
|
|
261
334
|
|
|
262
335
|
/**
|
|
263
|
-
* Batch-lint a UDS project tree. Scans canonical `skills/`
|
|
264
|
-
* plus `locales/{locale}/skills/` and
|
|
336
|
+
* Batch-lint a UDS project tree. Scans canonical `skills/` (SKILL.md + guide.md)
|
|
337
|
+
* and `core/`, plus `locales/{locale}/skills/` (SKILL.md + guide.md) and
|
|
338
|
+
* `locales/{locale}/core/`. Canonical guide.md uses the guide ruleset
|
|
339
|
+
* (CJK discoverability keywords allowed in description; XSPEC-248).
|
|
265
340
|
*
|
|
266
341
|
* @param {object} opts
|
|
267
342
|
* @param {string} opts.projectPath - Project (or UDS) root path
|
|
@@ -276,7 +351,7 @@ export function lintAll({ projectPath, locales }) {
|
|
|
276
351
|
const coreDir = join(projectPath, 'core');
|
|
277
352
|
const localesDir = join(projectPath, 'locales');
|
|
278
353
|
|
|
279
|
-
// --- Canonical skills ---
|
|
354
|
+
// --- Canonical skills (SKILL.md + guide.md) ---
|
|
280
355
|
if (existsSync(skillsDir)) {
|
|
281
356
|
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
|
|
282
357
|
if (!entry.isDirectory()) continue;
|
|
@@ -284,6 +359,11 @@ export function lintAll({ projectPath, locales }) {
|
|
|
284
359
|
if (existsSync(skillFile)) {
|
|
285
360
|
findings.push(...lintCanonical(skillFile));
|
|
286
361
|
}
|
|
362
|
+
// XSPEC-248: guide.md uses the guide ruleset (CJK description allowed).
|
|
363
|
+
const guideFile = join(skillsDir, entry.name, 'guide.md');
|
|
364
|
+
if (existsSync(guideFile)) {
|
|
365
|
+
findings.push(...lintCanonical(guideFile, { isGuide: true }));
|
|
366
|
+
}
|
|
287
367
|
}
|
|
288
368
|
}
|
|
289
369
|
|
|
@@ -313,6 +393,11 @@ export function lintAll({ projectPath, locales }) {
|
|
|
313
393
|
if (existsSync(skillFile)) {
|
|
314
394
|
findings.push(...lintLocale(skillFile, locale));
|
|
315
395
|
}
|
|
396
|
+
// XSPEC-248: locale guide.md reuses the locale ruleset (freshness checks).
|
|
397
|
+
const guideFile = join(localeSkillsDir, entry.name, 'guide.md');
|
|
398
|
+
if (existsSync(guideFile)) {
|
|
399
|
+
findings.push(...lintLocale(guideFile, locale));
|
|
400
|
+
}
|
|
316
401
|
}
|
|
317
402
|
}
|
|
318
403
|
if (existsSync(localeCoreDir)) {
|
|
@@ -334,5 +419,6 @@ export function partitionFindings(findings) {
|
|
|
334
419
|
return {
|
|
335
420
|
errors: findings.filter(f => f.severity === 'error'),
|
|
336
421
|
warnings: findings.filter(f => f.severity === 'warn'),
|
|
422
|
+
infos: findings.filter(f => f.severity === 'info'),
|
|
337
423
|
};
|
|
338
424
|
}
|
package/standards-registry.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.16.0",
|
|
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": "5.
|
|
61
|
+
"version": "5.16.0"
|
|
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": "5.
|
|
68
|
+
"version": "5.16.0",
|
|
69
69
|
"note": "Skills are now included in the main repository under skills/"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
@@ -2350,7 +2350,7 @@
|
|
|
2350
2350
|
"id": "license-compliance",
|
|
2351
2351
|
"name": "License Compliance Standards",
|
|
2352
2352
|
"nameZh": "授權合規標準",
|
|
2353
|
-
"version": "5.
|
|
2353
|
+
"version": "5.16.0",
|
|
2354
2354
|
"source": {
|
|
2355
2355
|
"human": "core/license-compliance.md",
|
|
2356
2356
|
"ai": "ai/standards/license-compliance.ai.yaml"
|