@haaaiawd/anws 2.1.0 → 2.1.1

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/bin/cli.js CHANGED
@@ -22,8 +22,7 @@ COMMANDS
22
22
  OPTIONS
23
23
  -v, --version Print version number
24
24
  -h, --help Show this help message
25
- --target Target AI IDE(s) for \`init\`, comma-separated (${TARGET_IDS.join(', ')})
26
- --check Preview grouped update diffs without writing files or rebuilding install-lock state
25
+ --target Target AI IDE(s) for init, comma-separated (${TARGET_IDS.join(', ')})
27
26
 
28
27
  SUPPORTED TARGETS
29
28
  windsurf workflows + skills
@@ -40,8 +39,7 @@ SUPPORTED TARGETS
40
39
  EXAMPLES
41
40
  anws init # Choose target IDEs and install their managed workflow projections
42
41
  anws init --target windsurf,codex,opencode
43
- anws update # Update all matched targets from install-lock, fallback scan, or drift repair
44
- anws update --check # Preview grouped changes per target without writing files
42
+ anws update # One-click update for all matched targets from install-lock, fallback scan, or drift repair
45
43
  `.trimStart();
46
44
 
47
45
  // ─── 参数解析 ─────────────────────────────────────────────────────────────────
@@ -90,7 +88,15 @@ async function main() {
90
88
  break;
91
89
 
92
90
  case 'update':
93
- await require('../lib/update')({ check: values.check });
91
+ if (values.target !== undefined) {
92
+ error('`anws update --target` has been removed. Use `anws update` to update all matched targets.');
93
+ process.exit(1);
94
+ }
95
+ if (values.check) {
96
+ error('`anws update --check` has been removed. Use `anws update` directly.');
97
+ process.exit(1);
98
+ }
99
+ await require('../lib/update')();
94
100
  break;
95
101
 
96
102
  default:
package/lib/changelog.js CHANGED
@@ -55,6 +55,61 @@ function compareSemver(a, b) {
55
55
  return 0;
56
56
  }
57
57
 
58
+ function normalizeText(text) {
59
+ return String(text || '').replace(/\r\n/g, '\n');
60
+ }
61
+
62
+ function buildMergedChangeKey(item) {
63
+ const canonicalPath = item.source || item.file;
64
+ const summaryKey = JSON.stringify(item.summary || []);
65
+ const contentKey = item.type === 'modified'
66
+ ? summaryKey
67
+ : `${normalizeText(item.oldContent)}\n<<<ANWS_CHANGE_SPLIT>>>\n${normalizeText(item.newContent)}`;
68
+
69
+ return `${item.type}::${canonicalPath}::${item.resourceId || canonicalPath}::${contentKey}`;
70
+ }
71
+
72
+ function mergeChanges(changes) {
73
+ const grouped = new Map();
74
+
75
+ for (const item of changes) {
76
+ const canonicalPath = item.source || item.file;
77
+ const key = buildMergedChangeKey(item);
78
+ const affectedFile = {
79
+ file: item.file,
80
+ targetId: item.targetId || null,
81
+ targetLabel: item.targetLabel || null
82
+ };
83
+
84
+ if (!grouped.has(key)) {
85
+ grouped.set(key, {
86
+ ...item,
87
+ canonicalPath,
88
+ affectedFiles: [affectedFile]
89
+ });
90
+ continue;
91
+ }
92
+
93
+ const current = grouped.get(key);
94
+ current.affectedFiles.push(affectedFile);
95
+ }
96
+
97
+ return Array.from(grouped.values()).map((item) => ({
98
+ ...item,
99
+ affectedFiles: item.affectedFiles.sort((left, right) => left.file.localeCompare(right.file))
100
+ }));
101
+ }
102
+
103
+ function formatAffectedTargetList(item) {
104
+ const targets = Array.from(new Set(
105
+ item.affectedFiles
106
+ .filter((entry) => entry.targetId && entry.targetLabel)
107
+ .map((entry) => `${entry.targetLabel} (${entry.targetId})`)
108
+ ));
109
+
110
+ return targets.length > 0 ? targets.join(', ') : '无';
111
+ }
112
+
58
113
  function formatFileList(title, items) {
59
114
  const lines = [`### ${title}`];
60
115
  if (items.length === 0) {
@@ -63,7 +118,10 @@ function formatFileList(title, items) {
63
118
  }
64
119
 
65
120
  for (const item of items) {
66
- lines.push(`- \`${item.file}\``);
121
+ lines.push(`- \`${item.canonicalPath}\` — 影响 Targets: ${formatAffectedTargetList(item)}`);
122
+ for (const affectedFile of item.affectedFiles) {
123
+ lines.push(` - \`${affectedFile.file}\``);
124
+ }
67
125
  }
68
126
 
69
127
  return lines.join('\n');
@@ -72,7 +130,10 @@ function formatFileList(title, items) {
72
130
  function formatDetail(item) {
73
131
  if (item.type === 'added') {
74
132
  return [
75
- `### \`${item.file}\``,
133
+ `### \`${item.canonicalPath}\``,
134
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
135
+ '- **影响文件**:',
136
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
76
137
  '- **新增文件**',
77
138
  '- **说明**: 该文件在旧版本中不存在,因此无前后逐行对比。'
78
139
  ].join('\n');
@@ -80,7 +141,10 @@ function formatDetail(item) {
80
141
 
81
142
  if (item.type === 'deleted') {
82
143
  return [
83
- `### \`${item.file}\``,
144
+ `### \`${item.canonicalPath}\``,
145
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
146
+ '- **影响文件**:',
147
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
84
148
  '- **删除文件**',
85
149
  '- **说明**: 该文件在新版本中不存在,因此无前后逐行对比。'
86
150
  ].join('\n');
@@ -88,13 +152,19 @@ function formatDetail(item) {
88
152
 
89
153
  if (item.summary.length === 0) {
90
154
  return [
91
- `### \`${item.file}\``,
155
+ `### \`${item.canonicalPath}\``,
156
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
157
+ '- **影响文件**:',
158
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
92
159
  '- **说明**: 检测到内容变更,但未能提取到摘要。'
93
160
  ].join('\n');
94
161
  }
95
162
 
96
163
  return [
97
- `### \`${item.file}\``,
164
+ `### \`${item.canonicalPath}\``,
165
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
166
+ '- **影响文件**:',
167
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
98
168
  '```diff',
99
169
  ...item.summary.flatMap((pair) => [
100
170
  `- [old:${pair.oldLineNumber === null ? '-' : pair.oldLineNumber}] ${pair.oldText}`,
@@ -106,7 +176,8 @@ function formatDetail(item) {
106
176
 
107
177
  async function generateChangelog({ cwd, version, changes, targetSummary = null }) {
108
178
  const changelogDir = await ensureChangelogDir(cwd);
109
- const grouped = groupChanges(changes);
179
+ const mergedChanges = mergeChanges(changes);
180
+ const grouped = groupChanges(mergedChanges);
110
181
  const now = new Date();
111
182
  const timestamp = now.toISOString().replace('T', ' ').slice(0, 19);
112
183
  const filePath = path.join(changelogDir, `v${version}.md`);
@@ -143,7 +214,7 @@ async function generateChangelog({ cwd, version, changes, targetSummary = null }
143
214
  '',
144
215
  '## 内容级变更详情',
145
216
  '',
146
- ...(changes.length > 0 ? changes.map(formatDetail).flatMap((section) => [section, '']) : ['- 无变更', ''])
217
+ ...(mergedChanges.length > 0 ? mergedChanges.map(formatDetail).flatMap((section) => [section, '']) : ['- 无变更', ''])
147
218
  ].join('\n');
148
219
 
149
220
  await fs.writeFile(filePath, content, 'utf8');
package/lib/diff.js CHANGED
@@ -137,6 +137,7 @@ async function collectManagedFileDiffs({
137
137
  ? managedFiles
138
138
  : projectionPlan.flatMap((item) => item.managedFiles || []);
139
139
  const projectionMap = new Map(normalizedProjectionEntries.map((item) => [item.outputPath, item]));
140
+ const fallbackTarget = projectionPlan[0] || null;
140
141
 
141
142
  for (const rel of normalizedManagedFiles) {
142
143
  if (rel === 'AGENTS.md' && !shouldWriteRootAgents) {
@@ -144,6 +145,19 @@ async function collectManagedFileDiffs({
144
145
  }
145
146
 
146
147
  const entry = projectionMap.get(rel);
148
+ const metadata = entry
149
+ ? {
150
+ source: entry.source,
151
+ resourceId: entry.id,
152
+ targetId: entry.targetId,
153
+ targetLabel: entry.targetLabel
154
+ }
155
+ : {
156
+ source: rel,
157
+ resourceId: rel === 'AGENTS.md' ? 'root-agents' : rel,
158
+ targetId: fallbackTarget?.targetId || null,
159
+ targetLabel: fallbackTarget?.targetLabel || null
160
+ };
147
161
  const srcPath = rel === 'AGENTS.md'
148
162
  ? srcAgents
149
163
  : path.join(path.join(__dirname, '..', 'templates'), entry.source);
@@ -160,6 +174,7 @@ async function collectManagedFileDiffs({
160
174
  if (srcExists && !destExists) {
161
175
  results.push({
162
176
  file: rel,
177
+ ...metadata,
163
178
  type: 'added',
164
179
  summary: [],
165
180
  oldContent: '',
@@ -171,6 +186,7 @@ async function collectManagedFileDiffs({
171
186
  if (!srcExists && destExists) {
172
187
  results.push({
173
188
  file: rel,
189
+ ...metadata,
174
190
  type: 'deleted',
175
191
  summary: [],
176
192
  oldContent: await readTextOrEmpty(destPath),
@@ -193,6 +209,7 @@ async function collectManagedFileDiffs({
193
209
 
194
210
  results.push({
195
211
  file: rel,
212
+ ...metadata,
196
213
  type: 'modified',
197
214
  summary: createLineDiff(oldContent, newContent),
198
215
  oldContent,
package/lib/update.js CHANGED
@@ -5,32 +5,33 @@ const path = require('node:path');
5
5
  const { buildProjectionPlan } = require('./manifest');
6
6
  const { getTarget } = require('./adapters');
7
7
  const { planAgentsUpdate, resolveAgentsInstall, printLegacyMigrationWarning, pathExists } = require('./agents');
8
- const { collectManagedFileDiffs, printPreview } = require('./diff');
8
+ const { collectManagedFileDiffs } = require('./diff');
9
9
  const { detectUpgrade, generateChangelog } = require('./changelog');
10
10
  const { writeTargetFiles } = require('./copy');
11
11
  const { createInstallLock, dedupeTargets, detectInstallState, summarizeTargetState, writeInstallLock } = require('./install-state');
12
12
  const { confirm } = require('./prompt');
13
13
  const { ROOT_AGENTS_FILE, resolveCanonicalSource } = require('./resources');
14
- const { success, warn, error, info, fileLine, skippedLine, blank, logo, section } = require('./output');
14
+ const { warn, error, info, fileLine, skippedLine, blank, logo, section } = require('./output');
15
15
 
16
- async function update(options = {}) {
16
+ async function update() {
17
17
  const cwd = process.cwd();
18
- const check = !!options.check;
19
18
  const legacyAgentDir = path.join(cwd, '.agent');
20
19
  const { version } = require(path.join(__dirname, '..', 'package.json'));
21
20
  const installState = await detectInstallState(cwd);
22
21
  const legacyAgentExists = await pathExists(legacyAgentDir);
23
22
  const isLegacyMigration = installState.selectedTargets.length === 0 && legacyAgentExists;
24
- const selectedTargetIds = isLegacyMigration ? ['antigravity'] : installState.selectedTargets;
25
- const targetPlans = buildProjectionPlan(selectedTargetIds);
23
+ const detectedTargetIds = isLegacyMigration ? ['antigravity'] : installState.selectedTargets;
26
24
 
27
- if (selectedTargetIds.length === 0 && !legacyAgentExists) {
25
+ if (detectedTargetIds.length === 0 && !legacyAgentExists) {
28
26
  logo();
29
27
  error('No supported Anws target layout found in current directory.');
30
28
  info('Run `anws init` first to set up the workflow system.');
31
29
  process.exit(1);
32
30
  }
33
31
 
32
+ const targetPlans = buildProjectionPlan(detectedTargetIds);
33
+ const detectedTargetPlans = buildProjectionPlan(detectedTargetIds);
34
+
34
35
  const srcAgents = ROOT_AGENTS_FILE;
35
36
 
36
37
  if (isLegacyMigration) {
@@ -100,41 +101,18 @@ async function update(options = {}) {
100
101
 
101
102
  const changes = targetContexts.flatMap((context) => context.changes);
102
103
 
103
- if (check) {
104
- if (!versionState.needUpgrade) {
105
- if (!isLegacyMigration) {
106
- logo();
107
- blank();
108
- }
109
- info(`Already up to date. Latest recorded version is v${versionState.latestVersion || version}.`);
110
- printTargetSelection(installState, targetContexts.map((context) => context.target));
111
- return;
112
- }
113
- if (!isLegacyMigration) {
114
- logo();
115
- blank();
116
- }
117
- printTargetSelection(installState, targetContexts.map((context) => context.target));
118
- printPreview({
119
- fromVersion: versionState.fromVersion,
120
- toVersion: versionState.toVersion,
121
- changes
122
- });
123
- return;
124
- }
125
-
126
104
  if (!versionState.needUpgrade) {
127
105
  if (!isLegacyMigration) {
128
106
  logo();
129
107
  blank();
130
108
  }
131
109
  printTargetSelection(installState, targetContexts.map((context) => context.target));
132
- if (!check && installState.canRebuildLock && selectedTargetIds.length > 0) {
110
+ if (installState.canRebuildLock && detectedTargetIds.length > 0) {
133
111
  const generatedAt = new Date().toISOString();
134
112
  await writeInstallLock(cwd, createInstallLock({
135
113
  cliVersion: version,
136
114
  generatedAt,
137
- targets: dedupeTargets(targetPlans.map((targetPlan) => summarizeTargetState(targetPlan, version))),
115
+ targets: dedupeTargets(detectedTargetPlans.map((targetPlan) => summarizeTargetState(targetPlan, version))),
138
116
  lastUpdateSummary: {
139
117
  successfulTargets: [],
140
118
  failedTargets: [],
@@ -152,15 +130,7 @@ async function update(options = {}) {
152
130
  blank();
153
131
  }
154
132
 
155
- const confirmed = await askUpdate({
156
- installState,
157
- targets: targetContexts.map((context) => context.target)
158
- });
159
- if (!confirmed) {
160
- blank();
161
- info('Aborted. No files were changed.');
162
- return;
163
- }
133
+ printTargetSelection(installState, targetContexts.map((context) => context.target));
164
134
 
165
135
  const updated = [];
166
136
  const skipped = [];
@@ -217,6 +187,12 @@ async function update(options = {}) {
217
187
  }
218
188
  });
219
189
  const generatedAt = new Date().toISOString();
190
+ const successfulTargetIdSet = new Set(successfulTargets.map((item) => item.targetId));
191
+ const retainedDetectedTargets = installState.canRebuildLock
192
+ ? detectedTargetPlans
193
+ .filter((targetPlan) => !successfulTargetIdSet.has(targetPlan.targetId))
194
+ .map((targetPlan) => summarizeTargetState(targetPlan, version))
195
+ : [];
220
196
  const existingLockTargets = installState.canRebuildLock
221
197
  ? []
222
198
  : (installState.lockResult.lock?.targets || []);
@@ -225,6 +201,7 @@ async function update(options = {}) {
225
201
  generatedAt,
226
202
  targets: dedupeTargets([
227
203
  ...existingLockTargets,
204
+ ...retainedDetectedTargets,
228
205
  ...successfulTargets
229
206
  ]),
230
207
  lastUpdateSummary: {
@@ -251,23 +228,6 @@ async function update(options = {}) {
251
228
  });
252
229
  }
253
230
 
254
- async function askUpdate({ installState, targets }) {
255
- if (global.__ANWS_FORCE_YES) return true;
256
-
257
- if (!process.stdin.isTTY) {
258
- warn('Non-TTY environment detected. Skipping update to avoid accidental overwrites.');
259
- return false;
260
- }
261
-
262
- return confirm({
263
- message: buildUpdateConfirmationMessage(targets),
264
- contextLines: buildUpdateConfirmationContextLines(installState, targets),
265
- confirmLabel: 'Continue',
266
- cancelLabel: 'Cancel',
267
- defaultValue: false
268
- });
269
- }
270
-
271
231
  async function askMigrate() {
272
232
  if (global.__ANWS_FORCE_YES) return true;
273
233
 
@@ -308,23 +268,8 @@ async function maybeDeleteLegacyDir(legacyAgentDir) {
308
268
  return true;
309
269
  }
310
270
 
311
- function buildUpdateConfirmationMessage(targets) {
312
- const labels = targets.map((target) => target.label).filter(Boolean);
313
- if (labels.length === 0) {
314
- return 'This will overwrite all managed files for the detected target layout.';
315
- }
316
- return `This will overwrite all managed files for: ${labels.join(', ')}.`;
317
- }
318
-
319
- function buildUpdateConfirmationContextLines(installState, targets) {
320
- const lines = [
321
- `Matched targets: ${targets.map((target) => `${target.label} (${target.id})`).join(', ') || 'none'}`,
322
- installState.needsFallback ? 'State source: directory scan fallback' : 'State source: install-lock + directory scan'
323
- ];
324
- if (installState.drift.hasDrift) {
325
- lines.push(`State drift detected. Missing on disk: ${installState.drift.missingOnDisk.join(', ') || 'none'}; untracked on disk: ${installState.drift.untrackedOnDisk.join(', ') || 'none'}.`);
326
- }
327
- return lines;
271
+ function buildSelectionModeLine() {
272
+ return 'Selection mode: detected target layout';
328
273
  }
329
274
 
330
275
  function printLegacyMigrationNotice() {
@@ -338,6 +283,7 @@ function printLegacyMigrationNotice() {
338
283
  function printTargetSelection(installState, targets) {
339
284
  blank();
340
285
  section('Target selection', [
286
+ buildSelectionModeLine(),
341
287
  `Matched targets: ${targets.map((target) => `${target.label} (${target.id})`).join(', ') || 'none'}`,
342
288
  installState.needsFallback ? 'State source: directory scan fallback' : 'State source: install-lock + directory scan',
343
289
  ...(installState.drift.hasDrift
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haaaiawd/anws",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Anws — A spec-driven workflow framework for AI-assisted development. Empowers prompt engineers to build production-ready software through structured PRD → Architecture → Task decomposition. Works with Claude Code, GitHub Copilot, Cursor, Windsurf, and any tool that reads AGENTS.md.",
5
5
  "keywords": [
6
6
  "anws",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: design-reviewer
3
- description: 使用三维框架(系统设计、运行模拟、工程实现)系统性审查架构和系统设计文档。产出按严重度分级的发现,关联到具体文档段落。
3
+ description: 使用三维框架(系统设计、运行模拟、工程实现)系统性审查架构和系统设计文档,作为 challenge 工作流中的规范契约设计证据层。产出按严重度分级的发现,关联到具体文档段落。
4
4
  ---
5
5
 
6
6
  # 设计审查大师手册
@@ -9,6 +9,8 @@ description: 使用三维框架(系统设计、运行模拟、工程实现)
9
9
  > 对设计严厉,代码才能优雅。"
10
10
 
11
11
  你是**设计审查大师**,负责系统性审查架构和系统设计文档。你的三维框架确保没有任何一类风险被遗漏。
12
+ 在 `/challenge` 工作流中,你的角色是:**为规范契约是否闭合提供设计侧证据**,而不是单独给出脱离上下文的最终裁决。
13
+ 你优先要证明的是:哪些契约在**系统边界、接口、状态、时序、错误路径**上没有闭合。
12
14
 
13
15
  ---
14
16
 
@@ -126,15 +128,28 @@ description: 使用三维框架(系统设计、运行模拟、工程实现)
126
128
  | 工程实现 | — | — | — | — | — |
127
129
  | **合计** | **—** | **—** | **—** | **—** | **—** |
128
130
 
131
+ **高信号结论**: [用 1-3 句概括最值得进入 challenge 主报告的问题]
132
+
129
133
  ---
130
134
 
131
- ### [维度] — [ID]: [标题]
135
+ ### 核心发现清单
132
136
 
133
- **严重度**: Critical / High / Medium / Low
134
- **文档位置**: [精确的文件和章节引用]
137
+ | ID | 维度 | 严重度 | 文档位置 | 发现 | 影响 | 建议 |
138
+ |----|------|--------|----------|------|------|------|
139
+ | DR-01 | 系统设计 | Critical | 02_ARCHITECTURE_OVERVIEW.md §X | 边界定义冲突,两个系统职责重叠 | 实现阶段职责漂移、返工风险高 | 重新划清系统边界并更新引用 |
140
+ | DR-02 | 运行模拟 | High | 04_SYSTEM_DESIGN/... §Y | 故障传播路径未定义 | 级联失败时无法收敛 | 增加超时/降级/重试策略 |
141
+ | DR-03 | 工程实现 | Medium | ADR-00X / System Design §Z | 可测试接缝不足 | 后续验证成本高 | 增加接口隔离或 mock 接缝 |
142
+
143
+ > 仅输出真正影响设计判断的问题。低价值措辞、重复担忧不要进入清单。
144
+
145
+ ---
146
+
147
+ ### Top Findings 详情(仅展开 Critical / High)
135
148
 
136
- **问题**:
137
- [详细描述,引用具体文档内容]
149
+ #### DR-01 [标题]
150
+
151
+ **严重度**: Critical
152
+ **文档位置**: [精确的文件和章节引用]
138
153
 
139
154
  **证据**:
140
155
  - 文档分析: [来自 PRD/Architecture/ADR 的具体内容]
@@ -145,7 +160,7 @@ description: 使用三维框架(系统设计、运行模拟、工程实现)
145
160
  - [不修复会发生什么]
146
161
 
147
162
  **建议**:
148
- [修复方式,可提供多个选项]
163
+ - [最小修复方向]
149
164
  ```
150
165
 
151
166
  ---
@@ -537,14 +537,31 @@ classDiagram
537
537
  - **RBAC (Role-Based Access Control)**: 基于角色的访问控制
538
538
  - **p95**: 95th percentile,95%的请求响应时间小于该值
539
539
 
540
- ### 14.2 References (参考资料)
540
+ ### 14.2 Optional Skills & Reference Resources (可选 Skills 与参考资源)
541
+ >
542
+ > 本节用于记录在设计过程中实际参考过的 skill、组件库、方法论或外部资料。
543
+ > 这些内容是辅助输入,不是系统事实来源;最终方案仍以本项目的 PRD、ADR、Architecture Overview 和本文档自身为准。
544
+ >
545
+ > **记录建议**:
546
+ > - 写明资源名称
547
+ > - 写明它帮助了哪个设计决策
548
+ > - 写明最终采纳了什么,舍弃了什么
549
+ >
550
+ > **示例(前端系统)**:
551
+ > - `vercel-react-best-practices`: 用于校验 React 组件边界、渲染策略、性能优化建议
552
+ > - `frontend-design`: 用于参考排版、配色、层级和动效方向
553
+ > - `shadcn/ui`: 用于基础组件模式参考
554
+ > - `Aceternity UI`: 用于展示型区块和交互动效灵感
555
+ > - `Magic UI`: 用于 Tailwind-first 的视觉与动画参考
556
+
557
+ ### 14.3 References (参考资料)
541
558
  - [FastAPI Documentation](https://fastapi.tiangolo.com/)
542
559
  - [PostgreSQL Best Practices](https://wiki.postgresql.org/wiki/Don%27t_Do_This)
543
560
  - [JWT Best Practices](https://tools.ietf.org/html/rfc8725)
544
561
  - [Architecture Overview](../02_ARCHITECTURE_OVERVIEW.md)
545
562
  - [ADR001: Tech Stack](../03_ADR/ADR001_TECH_STACK.md)
546
563
 
547
- ### 14.3 Change Log (变更日志)
564
+ ### 14.4 Change Log (变更日志)
548
565
 
549
566
  | Version | Date | Changes | Author |
550
567
  | ------- | ---------- | -------- | ------ |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: task-reviewer
3
- description: 系统性审查 05_TASKS.md 的质量与完备性。通过 6 大检测 Pass 在语义模型上运行,检测重复、歧义、欠详述、不一致、覆盖缺口和质量问题。
3
+ description: 系统性审查 05_TASKS.md 的质量与完备性,作为 challenge 工作流中的规范契约任务承接证据层。通过 6 大检测 Pass 在语义模型上运行,检测重复、歧义、欠详述、不一致、覆盖缺口和质量问题。
4
4
  ---
5
5
 
6
6
  # 任务审查大师手册
@@ -9,10 +9,11 @@ description: 系统性审查 05_TASKS.md 的质量与完备性。通过 6 大检
9
9
  > 在代码暴露问题之前,找到裂缝。"
10
10
 
11
11
  你是**任务审查大师**,负责对 `05_TASKS.md` 进行系统性审计——以 PRD、Architecture 和 ADR 文档为基准,运行 **6 大检测 Pass**。你的武器是**语义模型**,而非朴素的字符串匹配。
12
+ 在 `/challenge` 工作流中,你的角色是:**为规范契约是否被任务承接、覆盖和验证提供证据**,而不是单独替代 challenge 的总判断。
13
+ 你优先要证明的是:关键承诺是否有实现任务、验证任务、边界/失败路径任务,以及是否存在幽灵任务稀释主轴。
12
14
 
13
15
  ---
14
-
15
- ## ⚡ 任务目标
16
+ ## 任务目标
16
17
 
17
18
  1. **加载文档 (必须)**: 读取 `.anws/v{N}/05_TASKS.md`、`01_PRD.md`、`02_ARCHITECTURE_OVERVIEW.md` 以及所有 `03_ADR/*.md`。
18
19
  2. **构建语义模型**: 构建 3 个清单模型(见 §语义模型构建)。
@@ -21,7 +22,7 @@ description: 系统性审查 05_TASKS.md 的质量与完备性。通过 6 大检
21
22
  5. **生成报告**: 输出任务审查报告(见 §输出格式)。
22
23
  6. **展示摘要**: 向用户展示检测汇总表 + 前 10 条发现。
23
24
 
24
- ## 🛑 硬约束
25
+ ## 硬约束
25
26
 
26
27
  - **发现上限**: 最多 50 条。超出时按严重度排序 → 截断 → 追加溢出摘要。
27
28
  - **只报告不修复**: 本技能**仅输出报告**。修复由用户或其他工作流完成。
@@ -29,8 +30,7 @@ description: 系统性审查 05_TASKS.md 的质量与完备性。通过 6 大检
29
30
  - **客观性**: 仅标记客观可检测的问题。不要为了填满报告而捏造问题。
30
31
 
31
32
  ---
32
-
33
- ## 🧠 语义模型构建
33
+ ## 语义模型构建
34
34
 
35
35
  > 在执行任何 Pass 之前,先构建以下 3 个模型。所有 Pass 在模型上操作,而非原始文本。
36
36
 
@@ -206,6 +206,8 @@ T{X.Y.Z}: 标题
206
206
 
207
207
  **整体健康度**: 🟢 健康 / 🟡 需关注 / 🔴 阻塞
208
208
 
209
+ **高信号结论**: [用 1-3 句概括最值得进入 challenge 主报告的问题]
210
+
209
211
  ---
210
212
 
211
213
  ### REQ 覆盖率
@@ -248,14 +250,36 @@ graph LR
248
250
 
249
251
  ---
250
252
 
251
- ### 问题清单
253
+ ### 核心发现清单
254
+
255
+ | ID | 严重度 | Pass | 位置 | 发现 | 影响 | 建议 |
256
+ |----|:------:|:----:|------|------|------|------|
257
+ | TR-01 | CRITICAL | E1 | REQ-003 / 05_TASKS.md §X | P0 需求无对应任务 | 核心能力无法落地 | 在对应 Sprint 增加实现与验证任务 |
258
+ | TR-02 | HIGH | B1 | T4.1.3 | 验收标准使用“正确处理”等模糊措辞 | 任务不可验证 | 量化错误码、兜底行为和验证方式 |
259
+ | TR-03 | HIGH | D1 | PRD / Architecture / Tasks | 术语漂移导致任务引用不一致 | 实施与对齐成本上升 | 按 ADR 统一术语 |
252
260
 
253
- | ID | 严重度 | Pass | 描述 | 建议 |
254
- |----|:------:|:----:|------|------|
255
- | TR-01 | CRITICAL | E | REQ-003 无对应任务 | 在 S2 增加 T2.2.6 |
256
- | TR-02 | HIGH | B | T4.1.3 验收标准使用"正确处理" | 量化:指明具体错误码+兜底行为 |
257
- | TR-03 | HIGH | D | "game core" vs "核心引擎" 术语漂移 | 按 ADR 统一为 "Core Engine" |
258
- | ... | ... | ... | ... | ... |
261
+ > 仅输出真正影响执行和验收的问题。低价值措辞润色不要淹没核心发现。
262
+
263
+ ---
264
+
265
+ ### Top Findings 详情(仅展开 Critical / High)
266
+
267
+ #### TR-01 [标题]
268
+
269
+ **Pass**: E1
270
+ **严重度**: CRITICAL
271
+ **位置**: [REQ-ID / Task ID / 文档章节]
272
+
273
+ **证据**:
274
+ - 需求来源: [PRD 中的 REQ / US]
275
+ - 任务映射: [哪些任务缺失 / 不完整]
276
+ - 交叉验证: [与 Architecture / ADR 的不一致点,如适用]
277
+
278
+ **影响**:
279
+ - [不修复会导致什么执行或交付问题]
280
+
281
+ **建议**:
282
+ - [最小修复方向]
259
283
 
260
284
  ---
261
285
 
@@ -268,14 +292,17 @@ graph LR
268
292
 
269
293
  ## 🎚️ 严重度分级
270
294
 
271
- | 等级 | 判定标准 | 典型示例 |
295
+ | 等级 | 判定标准 | 所需行动 |
272
296
  |:----:|---------|---------|
273
- | **CRITICAL** | 阻塞执行或遗漏核心功能 | PRD 需求零覆盖;Sprint 依赖环;核心文档缺失 |
274
- | **HIGH** | 导致返工或产出不可验证 | 重复任务;模糊的安全/性能验收;不可测标准;技术栈冲突 |
275
- | **MEDIUM** | 影响质量但不阻塞 | 术语漂移;NFR 覆盖缺失;边界情况欠详细 |
276
- | **LOW** | 润色项,不影响执行 | 措辞改进;轻微冗余;仅供参考 |
297
+ | **Critical** 🔴 | 根本性矛盾或不可能实现。不解决无法继续。 | P0 必须在 blueprint/forge 之前修复 |
298
+ | **High** 🟠 | 大概率导致返工或失败的严重风险。 | P1 — 在 forge 之前修复 |
299
+ | **Medium** 🟡 | 有变通方案的质量隐患。 | P2 实现阶段修复 |
300
+ | **Low** 🟢 | 润色项或轻微不一致。 | P3 — 后续跟踪 |
301
+
302
+ **健康度规则**: Critical ≥ 1 → 整体健康度设为 🔴 阻塞。High ≥ 5 → 🟡 需关注。其余 → 🟢 健康。
277
303
 
278
- **升级规则**: CRITICAL ≥ 1 → 整体健康度设为 🔴 阻塞。HIGH ≥ 5 → 🟡 需关注。其余 → 🟢 健康。
304
+ > [!NOTE]
305
+ > 输出时优先保留 Critical / High。Medium / Low 仅在确实影响执行判断或有稳定改进价值时保留。
279
306
 
280
307
  ---
281
308