kld-sdd 2.6.7 → 2.6.9

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.
Files changed (39) hide show
  1. package/bin/kld-sdd-init.js +39 -3
  2. package/lib/init.js +320 -45
  3. package/lib/workspace-layout.js +2 -0
  4. package/package.json +2 -2
  5. package/skywalk-sdd/context-client.cjs +59 -5
  6. package/skywalk-sdd/ontology/active-changes.cjs +297 -0
  7. package/skywalk-sdd/ontology/change-key.cjs +241 -0
  8. package/skywalk-sdd/ontology/cli.cjs +135 -0
  9. package/skywalk-sdd/ontology/list-changes.cjs +110 -0
  10. package/skywalk-sdd/ontology/modules.cjs +167 -0
  11. package/skywalk-sdd/ontology/naming-diagnose.cjs +594 -0
  12. package/skywalk-sdd/ontology/sdd-config.cjs +335 -0
  13. package/skywalk-sdd/ontology/workspace-layout.cjs +194 -0
  14. package/templates/dot-sdd.yaml +8 -0
  15. package/templates/git-hooks/commit-msg-sdd-trailer.cjs +224 -0
  16. package/templates/modules.yaml +13 -0
  17. package/templates/openspec/proposal.md +7 -1
  18. package/templates/sdd.config.yaml +12 -0
  19. package/templates/skills/kld-sdd/openspec-sync-specs/SKILL.md +148 -0
  20. package/templates/skills/kld-sdd/openspec-update-change/SKILL.md +86 -0
  21. package/templates/skills/kld-sdd/opsx-apply/SKILL.md +3 -3
  22. package/templates/skills/kld-sdd/opsx-apply/checklist.md +1 -1
  23. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +11 -1
  24. package/templates/skills/kld-sdd/opsx-check/SKILL.md +73 -3
  25. package/templates/skills/kld-sdd/opsx-design/SKILL.md +9 -0
  26. package/templates/skills/kld-sdd/opsx-explore/SKILL.md +37 -17
  27. package/templates/skills/kld-sdd/opsx-kb-ingest/SKILL.md +9 -14
  28. package/templates/skills/kld-sdd/opsx-ontology-query/SKILL.md +83 -109
  29. package/templates/skills/kld-sdd/opsx-ontology-query/phase-1-prechange.md +276 -0
  30. package/templates/skills/kld-sdd/opsx-ontology-query/phase-2-during.md +354 -0
  31. package/templates/skills/kld-sdd/opsx-ontology-query/phase-3-postchange.md +223 -0
  32. package/templates/skills/kld-sdd/opsx-ontology-query/phase-4-explore.md +240 -0
  33. package/templates/skills/kld-sdd/opsx-ontology-query/phase-5-governance.md +232 -0
  34. package/templates/skills/kld-sdd/opsx-ontology-query/reference.md +92 -4
  35. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +87 -16
  36. package/templates/skills/kld-sdd/opsx-propose/checklist.md +1 -0
  37. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +33 -3
  38. package/templates/skills/kld-sdd/opsx-task/SKILL.md +10 -0
  39. package/templates/skills/kld-sdd/opsx-tdd-core/checklist.md +1 -1
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * commit-msg hook,两种布局:
6
+ *
7
+ * - 多仓(代码仓有 .sdd.yaml,spec 是另一个 clone):
8
+ * 写 Spec-Revision(spec 干净 HEAD 的完整 SHA)+ Spec-Change(活动 change,可多条)。
9
+ * - 单仓(openspec 与代码同仓):
10
+ * spec 与代码在同一次 commit 里,指针自指无意义 → 只写 Spec-Change,不写 Spec-Revision,
11
+ * 且不做「spec 工作区干净」检查(提交进行中必然是脏的)。
12
+ *
13
+ * Spec-Change 来源:KLD_SDD_CHANGE(显式覆盖)> sdd.config.yaml active_changes。
14
+ * Not from branch name, ever.
15
+ *
16
+ * Authority: 10-最小化多仓库关联与变更命名方案.md
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ function resolveOntologyDir() {
23
+ const deployed = path.join(__dirname, '..', 'ontology');
24
+ if (fs.existsSync(path.join(deployed, 'change-key.cjs'))) return deployed;
25
+ const fromPackageTemplates = path.join(__dirname, '..', '..', 'skywalk-sdd', 'ontology');
26
+ if (fs.existsSync(path.join(fromPackageTemplates, 'change-key.cjs'))) return fromPackageTemplates;
27
+ throw new Error('无法定位 skywalk-sdd/ontology');
28
+ }
29
+
30
+ const ontologyDir = resolveOntologyDir();
31
+ const changeKey = require(path.join(ontologyDir, 'change-key.cjs'));
32
+ const sddConfig = require(path.join(ontologyDir, 'sdd-config.cjs'));
33
+ const activeChanges = require(path.join(ontologyDir, 'active-changes.cjs'));
34
+ const { parseFrontmatter } = require(path.join(ontologyDir, 'artifact-parser.cjs'));
35
+
36
+ function projectRootFromHook() {
37
+ const explicit = process.argv.find((arg) => arg.startsWith('--project='));
38
+ if (explicit) return path.resolve(explicit.slice('--project='.length));
39
+ return path.resolve(__dirname, '..', '..');
40
+ }
41
+
42
+ function fail(message) {
43
+ console.error(`[kld-sdd commit-msg] ${message}`);
44
+ process.exit(1);
45
+ }
46
+
47
+ function isSddLinkedCodeRepo(codeRepoRoot) {
48
+ return fs.existsSync(path.join(codeRepoRoot, '.sdd.yaml'));
49
+ }
50
+
51
+ /**
52
+ * 决定本次提交按哪种布局处理。布局来自 .sdd.yaml 显式声明,不做启发式推断
53
+ * (否则单独诊断的 spec 仓 clone 会被误判成单仓)。
54
+ *
55
+ * 单仓优先于 specPath:即使用户把 sdd.specPath 指回本仓,也走单仓路径,
56
+ * 否则会因为「spec 工作区不干净」永久阻断提交。
57
+ */
58
+ function resolveLayout(repoRoot, env = process.env) {
59
+ if (!isSddLinkedCodeRepo(repoRoot)) {
60
+ return { mode: 'unlinked' };
61
+ }
62
+ if (sddConfig.isMonoLayout(repoRoot)) {
63
+ return { mode: 'mono', specPath: repoRoot };
64
+ }
65
+
66
+ const context = sddConfig.resolveCodeRepoContext(repoRoot, env);
67
+ if (!context.specPath) {
68
+ fail(context.message || '无法解析 spec clone 路径。请执行 kld-sdd link-spec --path=<spec>');
69
+ }
70
+ if (context.code === sddConfig.CODES.SPEC_PATH_INVALID) {
71
+ fail(context.message);
72
+ }
73
+ // specPath 指回本仓:等同单仓,不能按外部 spec 校验干净度
74
+ if (path.resolve(context.specPath) === path.resolve(repoRoot)) {
75
+ return { mode: 'mono', specPath: repoRoot, context };
76
+ }
77
+ return { mode: 'multi', specPath: context.specPath, context };
78
+ }
79
+
80
+ /**
81
+ * Explicit override: KLD_SDD_CHANGE, comma/space separated for several changes.
82
+ * Not from branch name, not from repo-wide git config.
83
+ */
84
+ function parseEnvChangeKeys(env = process.env) {
85
+ const raw = env.KLD_SDD_CHANGE && String(env.KLD_SDD_CHANGE).trim();
86
+ if (!raw) return null;
87
+ const keys = [];
88
+ for (const token of raw.split(/[,\s]+/).filter(Boolean)) {
89
+ const validated = changeKey.validate(token);
90
+ if (!validated.ok) {
91
+ fail(`KLD_SDD_CHANGE 无效: ${validated.message}`);
92
+ }
93
+ if (!keys.includes(validated.normalized)) keys.push(validated.normalized);
94
+ }
95
+ return keys.length ? keys : null;
96
+ }
97
+
98
+ /**
99
+ * Spec-Change values: explicit env wins, otherwise every active change
100
+ * registered in the spec repo's sdd.config.yaml.
101
+ */
102
+ function resolveChangeKeys(specRoot, env = process.env) {
103
+ const fromEnv = parseEnvChangeKeys(env);
104
+ if (fromEnv) {
105
+ for (const key of fromEnv) {
106
+ requireChangeDir(specRoot, key, { strict: true });
107
+ }
108
+ return { keys: fromEnv, source: 'env' };
109
+ }
110
+ const registered = activeChanges.activeChangeKeys(specRoot);
111
+ const keys = registered.filter((key) => requireChangeDir(specRoot, key, { strict: false }));
112
+ return { keys, source: 'sdd.config.yaml' };
113
+ }
114
+
115
+ /**
116
+ * @param {{ strict: boolean }} options strict=true 阻断提交;false 仅警告并跳过该条
117
+ */
118
+ function requireChangeDir(specRoot, key, options = { strict: true }) {
119
+ const proposalPath = path.join(specRoot, 'openspec', 'changes', key, 'proposal.md');
120
+ if (!fs.existsSync(proposalPath)) {
121
+ const message = `spec 中不存在 change: openspec/changes/${key}/proposal.md`;
122
+ if (options.strict) fail(message);
123
+ console.error(`[kld-sdd commit-msg] 警告: ${message}(已跳过该 Spec-Change)`);
124
+ return false;
125
+ }
126
+ const lines = fs.readFileSync(proposalPath, 'utf8').split(/\r?\n/);
127
+ const fm = parseFrontmatter(lines);
128
+ const declared = (fm['change-key'] || '').toLowerCase();
129
+ if (declared && declared !== key) {
130
+ const message = `proposal change-key (${declared}) 与目录 (${key}) 不一致`;
131
+ if (options.strict) fail(message);
132
+ console.error(`[kld-sdd commit-msg] 警告: ${message}(已跳过该 Spec-Change)`);
133
+ return false;
134
+ }
135
+ return true;
136
+ }
137
+
138
+ /**
139
+ * 重写 Trailer 区块:同名旧行先全部摘除,再作为连续区块追加到末尾。
140
+ *
141
+ * @param {Record<string, string|string[]>} trailers 数组值写成多条同名 Trailer
142
+ */
143
+ function upsertTrailers(message, trailers) {
144
+ let body = String(message || '').replace(/\s+$/g, '');
145
+ const block = [];
146
+
147
+ for (const [name, value] of Object.entries(trailers)) {
148
+ const linePattern = new RegExp(`^${name}:[^\\n]*\\n?`, 'gim');
149
+ body = body.replace(linePattern, '');
150
+ const values = (Array.isArray(value) ? value : [value]).filter(
151
+ (item) => item != null && String(item).length > 0,
152
+ );
153
+ for (const item of values) {
154
+ block.push(`${name}: ${item}`);
155
+ }
156
+ }
157
+
158
+ body = body.replace(/\s+$/g, '');
159
+ if (block.length) {
160
+ body += `\n\n${block.join('\n')}`;
161
+ }
162
+ body = body.replace(/\n{3,}/g, '\n\n');
163
+ if (!body.endsWith('\n')) body += '\n';
164
+ return body;
165
+ }
166
+
167
+ function main(env = process.env) {
168
+ const msgFile = process.argv[2];
169
+ if (!msgFile) {
170
+ fail('缺少 commit message 文件参数');
171
+ }
172
+
173
+ const repoRoot = projectRootFromHook();
174
+ const layout = resolveLayout(repoRoot, env);
175
+
176
+ // 未接入 SDD 的普通代码仓:不写 Trailer
177
+ if (layout.mode === 'unlinked') {
178
+ process.exit(0);
179
+ }
180
+
181
+ const trailers = {
182
+ // 多个活动 change 时写多条 Spec-Change
183
+ 'Spec-Change': resolveChangeKeys(layout.specPath, env).keys,
184
+ };
185
+
186
+ if (layout.mode === 'multi') {
187
+ const context = layout.context;
188
+ // remote mismatch:仍允许写本地 Revision(溯源本地快照);仅警告
189
+ if (context.code === sddConfig.CODES.REMOTE_MISMATCH) {
190
+ console.error(`[kld-sdd commit-msg] 警告: ${context.message}`);
191
+ }
192
+
193
+ if (!sddConfig.gitWorkingTreeClean(layout.specPath)) {
194
+ fail('spec 工作区不干净。请先提交或暂存 spec 变更后再提交代码。');
195
+ }
196
+
197
+ const revision = sddConfig.gitHeadSha(layout.specPath);
198
+ if (!revision || revision.length < 40) {
199
+ fail(`无法读取完整 Spec-Revision(需要完整 SHA): ${revision}`);
200
+ }
201
+ trailers['Spec-Revision'] = revision;
202
+ }
203
+
204
+ const original = fs.readFileSync(msgFile, 'utf8');
205
+ const updated = upsertTrailers(original, trailers);
206
+ fs.writeFileSync(msgFile, updated, 'utf8');
207
+ }
208
+
209
+ if (require.main === module) {
210
+ try {
211
+ main();
212
+ } catch (error) {
213
+ fail(error.message || String(error));
214
+ }
215
+ }
216
+
217
+ module.exports = {
218
+ upsertTrailers,
219
+ parseEnvChangeKeys,
220
+ resolveChangeKeys,
221
+ isSddLinkedCodeRepo,
222
+ resolveLayout,
223
+ main,
224
+ };
@@ -0,0 +1,13 @@
1
+ # 模块代号注册表(spec 仓库根目录)
2
+ # 只登记模块代号与中文名,不包含代码仓库、路径或 CI 配置。
3
+ version: 1
4
+
5
+ modules:
6
+ fi:
7
+ name: 财务核算
8
+ mm:
9
+ name: 物料管理
10
+ sd:
11
+ name: 销售与分销
12
+ cross:
13
+ name: 跨模块
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  # 【用户选择配置 - 由 /opsx:propose 引导填写】
3
- change-id: "CHG-<CHANGE-SLUG>" # 创建时生成,后续不得修改
3
+ change-id: "CHG-<MODULE>-<YYMMDD>-<SLUG>" # 创建时由 change-key 派生一次,后续不得修改
4
+ change-key: "<module>-<yymmdd>-<slug>" # spec 仓目录名;可选写入代码 commit 的 Spec-Change(需 KLD_SDD_CHANGE)
5
+ title: "<中文标题>" # 人读名称,opsx-explore / 知识平台展示
6
+ module: "<module>" # modules.yaml 中的模块代号;跨模块用 cross
7
+ # affected-modules: # 仅 module=cross 时必填,至少两个已注册模块
8
+ # - fi
9
+ # - mm
4
10
  entity-id: "<8位十六进制ID>" # Change 的全局逻辑实体 ID,added 时生成
5
11
  version-id: "<UUID>" # 本次 Change 版本 UUID
6
12
  delta-state: "added"
@@ -0,0 +1,12 @@
1
+ # 活动变更登记表(spec 仓库根目录)
2
+ # 由 opsx-propose 隐式写入、opsx-archive 隐式移除,供代码仓 AI 与 commit-msg Hook 读取。
3
+ # 不登记代码仓清单、路径 glob 或 CI 配置。
4
+ version: 1
5
+
6
+ active_changes:
7
+ # 示例(propose 会自动写入,无需手填):
8
+ # - change-key: fi-260727-account-doc-head-create
9
+ # change-id: CHG-FI-260727-ACCOUNT-DOC-HEAD-CREATE
10
+ # title: 会计凭证头创建
11
+ # module: fi
12
+ # summary: 支持凭证头创建与字段校验
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: openspec-sync-specs
3
+ description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
4
+ allowed-tools: Bash(openspec:*)
5
+ license: MIT
6
+ compatibility: Requires openspec CLI.
7
+ metadata:
8
+ author: openspec
9
+ version: "1.0"
10
+ generatedBy: "1.6.0"
11
+ ---
12
+
13
+ Sync delta specs from a change to main specs.
14
+
15
+ This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
16
+
17
+ **Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
18
+
19
+ **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
20
+
21
+ **Steps**
22
+
23
+ 1. **If no change name provided, prompt for selection**
24
+
25
+ Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
26
+
27
+ Show changes that have delta specs (under `specs/` directory).
28
+
29
+ **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
30
+
31
+ 2. **Resolve change context**
32
+
33
+ Run:
34
+ ```bash
35
+ openspec status --change "<name>" --json
36
+ ```
37
+
38
+ 3. **Find delta specs**
39
+
40
+ Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.
41
+
42
+ Each delta spec file contains sections like:
43
+ - `## ADDED Requirements` - New requirements to add
44
+ - `## MODIFIED Requirements` - Changes to existing requirements
45
+ - `## REMOVED Requirements` - Requirements to remove
46
+ - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
47
+
48
+ If no delta specs found, inform user and stop.
49
+
50
+ 4. **For each delta spec, apply changes to main specs**
51
+
52
+ For each repo-local capability delta spec path returned by the CLI:
53
+
54
+ a. **Read the delta spec** to understand the intended changes
55
+
56
+ b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
57
+
58
+ c. **Apply changes intelligently**:
59
+
60
+ **ADDED Requirements:**
61
+ - If requirement doesn't exist in main spec → add it
62
+ - If requirement already exists → update it to match (treat as implicit MODIFIED)
63
+
64
+ **MODIFIED Requirements:**
65
+ - Find the requirement in main spec
66
+ - Apply the changes - this can be:
67
+ - Adding new scenarios (don't need to copy existing ones)
68
+ - Modifying existing scenarios
69
+ - Changing the requirement description
70
+ - Preserve scenarios/content not mentioned in the delta
71
+
72
+ **REMOVED Requirements:**
73
+ - Remove the entire requirement block from main spec
74
+
75
+ **RENAMED Requirements:**
76
+ - Find the FROM requirement, rename to TO
77
+
78
+ d. **Create new main spec** if capability doesn't exist yet:
79
+ - Create `openspec/specs/<capability>/spec.md`
80
+ - Add Purpose section (can be brief, mark as TBD)
81
+ - Add Requirements section with the ADDED requirements
82
+
83
+ 5. **Show summary**
84
+
85
+ After applying all changes, summarize:
86
+ - Which capabilities were updated
87
+ - What changes were made (requirements added/modified/removed/renamed)
88
+
89
+ **Delta Spec Format Reference**
90
+
91
+ ```markdown
92
+ ## ADDED Requirements
93
+
94
+ ### Requirement: New Feature
95
+ The system SHALL do something new.
96
+
97
+ #### Scenario: Basic case
98
+ - **WHEN** user does X
99
+ - **THEN** system does Y
100
+
101
+ ## MODIFIED Requirements
102
+
103
+ ### Requirement: Existing Feature
104
+ #### Scenario: New scenario to add
105
+ - **WHEN** user does A
106
+ - **THEN** system does B
107
+
108
+ ## REMOVED Requirements
109
+
110
+ ### Requirement: Deprecated Feature
111
+
112
+ ## RENAMED Requirements
113
+
114
+ - FROM: `### Requirement: Old Name`
115
+ - TO: `### Requirement: New Name`
116
+ ```
117
+
118
+ **Key Principle: Intelligent Merging**
119
+
120
+ Unlike programmatic merging, you can apply **partial updates**:
121
+ - To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
122
+ - The delta represents *intent*, not a wholesale replacement
123
+ - Use your judgment to merge changes sensibly
124
+
125
+ **Output On Success**
126
+
127
+ ```
128
+ ## Specs Synced: <change-name>
129
+
130
+ Updated main specs:
131
+
132
+ **<capability-1>**:
133
+ - Added requirement: "New Feature"
134
+ - Modified requirement: "Existing Feature" (added 1 scenario)
135
+
136
+ **<capability-2>**:
137
+ - Created new spec file
138
+ - Added requirement: "Another Feature"
139
+
140
+ Main specs are now updated. The change remains active - archive when implementation is complete.
141
+ ```
142
+
143
+ **Guardrails**
144
+ - Read both delta and main specs before making changes
145
+ - Preserve existing content not mentioned in delta
146
+ - If something is unclear, ask for clarification
147
+ - Show what you're changing as you go
148
+ - The operation should be idempotent - running twice should give same result
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: openspec-update-change
3
+ description: Update an OpenSpec change by revising its existing planning artifacts and keeping them coherent with one another. Use when the user wants to revise a change's plan, fold new decisions into it, or reconcile its artifacts after an edit. Never edits code.
4
+ allowed-tools: Bash(openspec:*)
5
+ license: MIT
6
+ compatibility: Requires openspec CLI.
7
+ metadata:
8
+ author: openspec
9
+ version: "1.0"
10
+ generatedBy: "1.6.0"
11
+ ---
12
+
13
+ Revise a change's existing planning artifacts and keep them coherent. Never edit code.
14
+
15
+ **Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
16
+
17
+ **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
18
+
19
+ **Steps**
20
+
21
+ 1. **If no change name provided, prompt for selection**
22
+
23
+ Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.
24
+
25
+ Present the top 3-4 most recently modified changes as options, showing:
26
+ - Change name
27
+ - Schema (from `schema` field if present, otherwise "spec-driven")
28
+ - Status (e.g., "0/5 tasks", "complete", "no tasks")
29
+ - How recently it was modified (from `lastModified` field)
30
+
31
+ Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to update.
32
+
33
+ **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
34
+
35
+ 2. **Get the change's artifacts**
36
+ ```bash
37
+ openspec status --change "<name>" --json
38
+ ```
39
+ Parse the JSON to understand current state. The response includes:
40
+ - `schemaName`: The workflow schema being used (e.g., "spec-driven")
41
+ - `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
42
+ - `isComplete`: Boolean indicating if all artifacts are complete
43
+ - `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
44
+
45
+ The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.
46
+
47
+ The files to edit are `artifactPaths.<id>.existingOutputPaths` - the concrete files that exist on disk, already glob-expanded for glob artifacts (e.g. `specs/**/*.md`). Do NOT write to `resolvedOutputPath`: for a glob artifact it is still the glob pattern, not a real file.
48
+
49
+ 3. **Understand the request**
50
+ - If the user asked for a specific revision ("the design now uses X"), that is the starting edit.
51
+ - If they only said "update" / "make this coherent", treat it as a coherence review: read the existing artifacts and check them against each other for contradictions, gaps, and duplication.
52
+
53
+ 4. **Read and reconcile**
54
+ - Read the artifact(s) the request touches and the change's other existing artifacts.
55
+ - Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
56
+ - Note everything that is now inconsistent, missing, or contradictory.
57
+ - Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.
58
+ - If the change is already coherent, say so and make no edits.
59
+
60
+ 5. **Confirm and apply, one artifact at a time**
61
+ - Show each proposed revision and why. Write only after the user confirms.
62
+ - If the user rejects a revision, do not write it - leave that artifact unchanged.
63
+ - When a substantial rewrite is needed, get that artifact's rules and template first:
64
+ ```bash
65
+ openspec instructions <artifact-id> --change "<name>" --json
66
+ ```
67
+
68
+ 6. **Point to the next step (guidance only - NEVER act on it)**
69
+ - Artifacts still missing -> suggest `/opsx:continue` to create them.
70
+ - Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.
71
+ - Everything done and implemented -> suggest `/opsx:archive`.
72
+
73
+ **Output**
74
+
75
+ After each invocation, show:
76
+ - Which artifacts were revised (and which proposed revisions were rejected)
77
+ - Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)
78
+ - Where the change stands and the recommended next command
79
+
80
+ **Guardrails**
81
+ - Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.
82
+ - Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.
83
+ - Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
84
+ - Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.
85
+ - Confirm every edit with the user before writing.
86
+ - If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the "Update vs. Start Fresh" heuristic).
@@ -183,8 +183,8 @@ openspec list --json
183
183
 
184
184
  **执行每个任务**(a-g 步骤):
185
185
 
186
- a. **DAG 依赖检查 & 层级收集** — 收集依赖已满足的待执行任务;同层多个独立任务可并行派发子代理。
187
- b. **显示当前层级任务** — `📍 层级 [X]:准备派发 [K] 个子代理并行处理 [M] 个任务`。
186
+ a. **DAG 依赖检查 & 层级收集** — 收集依赖已满足的待执行任务;同层多个独立任务可并行派发子代理。**⛔ TDD 模式豁免**:当 `test-strategy=tdd` 时,同层的 RED→GREEN 对**不可并行**,必须逐对串行执行(见下方 S2)。仅同层的非 TDD 模块任务(UI/配置/SQL DDL)可并行。
187
+ b. **显示当前层级任务** — `📍 层级 [X]:准备派发 [K] 个子代理并行处理 [M] 个任务`。**TDD 模式下**:若本层含 RED→GREEN 对,显示 `📍 层级 [X]:[M] 个 RED→GREEN 对须逐对串行执行(TDD 串行约束),[K] 个非 TDD 任务可并行`。
188
188
  c. **🤖 派发子代理实现代码** — 使用 Agent 工具并行派发同层任务,⛔ 子代理阶段不创建 worktree/分支。**派发模板与状态处理见 `./reference.md`「§5c 子代理派发模板」+ `./implementer-prompt.md`**。
189
189
  d. **⛔ 编译检查门禁** — 每完成一个任务后必须编译通过;**详细见 `./checklist.md`「§5d 编译检查门禁」**。
190
190
  e. **⛔ 测试执行门禁** — 按 `proposal.md` 的 `test-strategy` 决定(tdd=强制, impl-first=警告, none=跳过);**详细见 `./checklist.md`「§5e 测试执行门禁」**。
@@ -285,7 +285,7 @@ g. **继续下一个层级** — 重新检查 DAG,找出依赖已满足的下
285
285
  - **⛔ 必须实时更新任务状态**:每完成一个任务立即改 tasks.md,两种格式同步。
286
286
  - **⛔ apply 结束前 checkbox 全量同步校验**:`stage_end` 前对比 telemetry `task_update` 记录数与 tasks.md `[x]` 数量,不一致则补齐(见 `./checklist.md` §5f.1)。
287
287
  - **⛔ task_update 后必须验证 checkbox 已更新**:执行 `check-task` 确认 tasks.md 对应行已变更;未更新则手动修改。
288
- - **⛔ TDD RED→GREEN 严格串行**:不适用同层并行派发;RED-N 确认失败后必须执行中断声明再进入 GREEN-N;GREEN 完成后必须通过 Scope 门禁再进入下一个 RED
288
+ - **⛔ TDD RED→GREEN 严格串行**:不适用同层并行派发;RED-N 确认失败后必须执行中断声明再进入 GREEN-N;GREEN 完成后必须通过 Scope 门禁再进入下一个 RED。同层存在多个 RED→GREEN 对时,必须逐对串行完成,禁止并行派发子代理处理多个 RED→GREEN 对。同层非 TDD 模块任务(UI/配置/SQL DDL)仍可并行。
289
289
  - **Git 只读策略**:禁止为了度量自动初始化 Git、创建分支或提交 commit;非 Git 项目用 `vcs_mode=no-git` 继续执行。
290
290
  - **⛔ Step 0.1 隔离校验必做**:建 worktree / 建议分支名前必须完成 proposal + 跨 cap spec 依赖校验并输出报告。
291
291
  - **Worktree 为加速手段,非必选项**:校验通过且解耦方可多 worktree;有依赖或共享修改面则串行。
@@ -130,7 +130,7 @@ description: opsx-apply 的阶段强制检查点与自检清单。仅在执行 a
130
130
  - [ ] ⛔ **必须实时更新任务状态**:每完成一个任务立即改 tasks.md,两种格式(`- [ ]`→`- [x]` 与 `**状态**: [ ]`→`[x]`)同步
131
131
  - [ ] ⛔ **apply 结束前 checkbox 全量同步校验**:见 §5f.1,`stage_end` 前对比 telemetry `task_update` 记录数与 tasks.md `[x]` 数量
132
132
  - [ ] ⛔ **task_update 后必须验证 checkbox 已更新**:执行 `check-task` 确认 tasks.md 对应行已变更;未更新则手动修改
133
- - [ ] ⛔ **TDD RED→GREEN 严格串行**:不适用同层并行派发;RED-N 确认失败后必须执行中断声明再进入 GREEN-N;GREEN 完成后必须通过 Scope 门禁再进入下一个 RED
133
+ - [ ] ⛔ **TDD RED→GREEN 严格串行**:不适用同层并行派发;RED-N 确认失败后必须执行中断声明再进入 GREEN-N;GREEN 完成后必须通过 Scope 门禁再进入下一个 RED。同层多个 RED→GREEN 对须逐对串行,禁止并行派发;同层非 TDD 模块任务仍可并行
134
134
  - [ ] ⛔ **TDD 节奏校验**:见 §5e.1,连续 RED-N/GREEN-N 的 `task_update` 时间戳须有可验证间距
135
135
  - [ ] **Git 只读策略**:禁止为了度量自动初始化 Git、创建分支或提交 commit;非 Git 项目用 `vcs_mode=no-git` 继续执行
136
136
  - [ ] ⛔ **Step 0.1 隔离校验必做**:建 worktree / 建议分支名前必须完成 proposal + 跨 cap spec 依赖校验并输出报告;未通过不得按 full 并行策略拆 `kld-sdd/<change>/<cap>`
@@ -17,7 +17,7 @@ allowed-tools:
17
17
 
18
18
  你是一个 SDD(Specification-Driven Development)变更归档专家。激活本技能后,你要安全地结束变更生命周期:真实归档文档、同步正式 specs、记录 archive telemetry,并生成最终中文度量报告。
19
19
 
20
- > **硬依赖(收尾入库)**:zip 生成后的上传依赖同级已部署的 **`opsx-kb-ingest`**。进入 §5.5 前必须先 `Read` 该技能的 `SKILL.md` 并完成其 Session 启动。缺失则提示用户重新 `kld-sdd-init`,**不要**自造另一套入库协议。
20
+ > **硬依赖(收尾入库)**:zip 生成后的上传依赖同级已部署的 **`opsx-kb-ingest`**。进入 §5.5 前必须先 `Read` 该技能的 `SKILL.md` 并完成其 Session 启动。入库成功后,`Read` `opsx-ontology-query/phase-3-postchange.md` 验证版本生效、AC 保留、关系完整性。缺失则提示用户重新 `kld-sdd-init`,**不要**自造另一套入库协议。
21
21
 
22
22
  > **跨平台执行规则**
23
23
  > - 先确认当前终端工作目录是项目根目录;若不是,先 `cd` 到项目根目录。
@@ -118,6 +118,16 @@ node skywalk-sdd/log.cjs archive-docs --project=. --change=<变更名称> --reas
118
118
  - 最终中文报告生成到 `openspec/changes/archive/<日期>-<name>/reports/<name>-report.md` 及同名 `<name>-report.html`(默认同时生成 .md 与 .html 双产物,默认归档后 archive 目录,可用 --report-output 自定义)。
119
119
  - 执行日志 `openspec/changes/archive/<日期>-<name>/logs/execution-log.md` 随归档整目录迁移(人读审计层)。
120
120
 
121
+ ### 5.4 隐式注销活动变更
122
+
123
+ 归档成功后立即执行(不询问用户):
124
+
125
+ ```bash
126
+ node skywalk-sdd/ontology/cli.cjs active-change --remove --change=<变更名称> --project=.
127
+ ```
128
+
129
+ 把该 change 从 spec 仓 `sdd.config.yaml` 的 `active_changes` 移除,避免已归档 change 继续被代码 commit 写成 `Spec-Change`。该操作幂等;条目本就不存在时不报错。
130
+
121
131
  ### 5.5 收尾入库(opsx-kb-ingest)
122
132
 
123
133
  1. 确认 `${AGENT_SKILL_DIR}/opsx-kb-ingest/SKILL.md` 存在并 Read;按该技能完成 API Key / targets。
@@ -1,9 +1,10 @@
1
- ---
1
+ ---
2
2
  name: opsx-check
3
3
  description: "质量检查技能 - 验证文档完整性、一致性、算法正确性及可执行性"
4
4
  argument-hint: "[change-name] [上下文文件...]"
5
5
  license: MIT
6
- compatibility: Requires openspec CLI.
6
+ compatibility: Requires openspec CLI; depends on opsx-ontology-query for external-key validation and baseline checks.
7
+ depends-on: opsx-ontology-query
7
8
  metadata:
8
9
  author: sdd-team
9
10
  version: "3.0"
@@ -25,6 +26,10 @@ allowed-tools:
25
26
  > 即使检查发现代码相关问题,也只记录在检查报告中,**不自动修复代码**。
26
27
  > 代码修复将在 `/opsx-apply` 阶段进行。
27
28
 
29
+ > **KB 上下文**:check 阶段需验证外部键格式(REQ/FEAT/SCN 文法)、SCN REQ 前缀一致性和历史覆盖率基线。进入相关步骤前先 `Read` `opsx-ontology-query/phase-2-during.md` §3-5,并按 `SKILL.md` → Session 启动准备 API Key + targets。
30
+ >
31
+ > **📡 KB 就绪检查**:§2.6 在进入 KB 相关检查前会检测 KB 配置状态。若未配置,会**主动询问**用户选择「配置」或「跳过」,KB 相关检查项降级为仅本地验证。
32
+
28
33
 
29
34
  > **🖥️ 跨平台执行规则**
30
35
  > - 先确认当前终端工作目录是项目根目录;若不是,先 `cd` 到项目根目录。
@@ -69,6 +74,71 @@ openspec list
69
74
  - `specs/<capability>/design.md`(实现方案)
70
75
  - `specs/<capability>/tasks.md`(或 task.md,兼容旧格式)(任务拆解)
71
76
 
77
+ ### 2.5 【多仓库关联配置诊断】(在五维检查之前)
78
+
79
+ 先执行轻量配置诊断,**不执行代码 diff**,只检查命名与引用配置:
80
+
81
+ ```bash
82
+ # 在 spec 仓库:
83
+ node skywalk-sdd/ontology/cli.cjs diagnose-naming --project=. --change=<change-key>
84
+
85
+ # 在代码仓库:
86
+ node skywalk-sdd/ontology/cli.cjs diagnose-naming --project=. --mode=code-repo
87
+ ```
88
+
89
+ 诊断结果三类:
90
+
91
+ | 状态 | 含义 |
92
+ |------|------|
93
+ | PASS | 配置完整 |
94
+ | FIXABLE | 修复方式唯一,用户确认后可自动修复(如安装 commit-msg Hook、设置 sdd.specPath) |
95
+ | NEEDS_INPUT | 多候选或团队级配置,必须询问用户 |
96
+
97
+ 规则:
98
+ - 只问缺失项,不重复询问已合法配置
99
+ - 本地 Git config / Hook 可在确认后修复
100
+ - `.sdd.yaml`、`modules.yaml`、proposal frontmatter 修改前必须展示变更摘要
101
+ - 已被代码 commit 引用的 change-id/change-key **禁止静默重命名**
102
+ - 用户拒绝修复时按严重程度记 warning/failure,然后继续后续文档检查
103
+ - spec 仓诊断跳过代码仓 Hook;代码仓诊断跳过 modules/change 命名(入口分离)
104
+ - 单仓(`.sdd.yaml` 声明 `layout: mono`,openspec 与代码同仓):`mode=mono-repo`,命名与 Hook 一并就地检查;该布局 commit 只写 `Spec-Change`,不写 `Spec-Revision`
105
+ - 若在个人工作目录发现未接入的新代码仓(缺 `.sdd.yaml` 或 commit-msg Hook):
106
+ 1. 向用户展示仓名列表;
107
+ 2. 用户确认后执行 `kld-sdd sync-repos`(只装 Hook/关联,不装 skills);
108
+ 3. 用户拒绝则记 warning,不阻断五维文档检查
109
+ - Trailer 协议:已接入仓 commit 必写 `Spec-Revision`;`Spec-Change` 来自 spec 仓 `sdd.config.yaml` 的 `active_changes`(多个则写多条),`KLD_SDD_CHANGE` 可显式覆盖,**不从分支名推断**
110
+ - 活动变更登记表诊断:`sdd.config.yaml` 是否存在且格式合法;当前 change 是否已登记(未登记则提示执行 `active-change --register`);登记项对应目录是否仍存在(已归档应执行 `active-change --remove`)
111
+
112
+ 最终检查报告必须包含「多仓库关联配置」章节(命令输出已按此格式打印)。
113
+
114
+ ### 2.6 【KB 就绪检查】检测并配置知识库连接
115
+
116
+ 在进入 KB 相关检查(外部键验证、coverage 基线)之前,检测 Engineering KB 的连接状态。
117
+
118
+ 1. 检查 proposal.md frontmatter 中 `kb-status` 字段:
119
+ - 若 `kb-status: degraded(by-user-choice)` → KB 已被用户明确跳过,本阶段 KB 相关检查降级为仅本地验证(external-key 格式校验 + 本地 semantic-check),跳过 KB API 调用。
120
+ - 若未标记 → 继续检查。
121
+ 2. 运行 KB 就绪检查(程序化检测,自动搜索多 IDE 目录,消除路径歧义):
122
+ ```bash
123
+ node skywalk-sdd/context-client.cjs --check-only
124
+ ```
125
+ - 输出 `"available": true` → KB 已配置
126
+ - 输出 `"available": false` → KB 未配置
127
+ 3. **若已配置** → 直接进入 §3,正常使用 KB 做 coverage 基线、predecessor 预检。
128
+ 4. **若未配置** → 使用 **AskUserQuestion** 询问:
129
+
130
+ > "📡 **Engineering KB 未配置**
131
+ >
132
+ > KB 可以提供覆盖率基线检查、predecessor 版本预检(防止入库冲突)。是否现在配置?
133
+ >
134
+ > - A. **配置 KB**
135
+ > - B. **跳过 KB**,仅执行本地检查(semantic-check + external-key 格式校验),入库前无 predecessor 预检
136
+ > - C. **取消操作**"
137
+
138
+ - **选 A** → 配置 → 进入 §3。
139
+ - **选 B** → KB 相关检查项降级:coverage 基线跳过、`current-version` 预检跳过。检查报告中标注 `kb: degraded(by-user-choice)`。
140
+ - **选 C** → 终止 check。
141
+
72
142
  ### 3. 【上下文加载】识别并读取用户提供的文件
73
143
 
74
144
  **自动识别上下文文件**:
@@ -117,7 +187,7 @@ openspec list
117
187
 
118
188
  #### 4.4a TDD 合规性检查(仅 test-strategy=tdd 时执行)
119
189
 
120
- ⛔ 执行 `opsx-tdd-core/checklist.md` §B(11 项)逐项检查。
190
+ ⛔ 执行 `opsx-tdd-core/checklist.md` §B(15 项)逐项检查。
121
191
 
122
192
  > 不在此内联复制,以 opsx-tdd-core/checklist.md §B 为唯一真相源。
123
193
  > 额外补充:还需检查 `opsx-tdd-rules/rules/exception-path-coverage.md`(异常路径覆盖门禁)。