@openprd/cli 0.1.11 → 0.1.19

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 (147) hide show
  1. package/.openprd/changes/adaptive-project-framing/.openprd.yaml +2 -0
  2. package/.openprd/changes/adaptive-project-framing/design.md +47 -0
  3. package/.openprd/changes/adaptive-project-framing/proposal.md +35 -0
  4. package/.openprd/changes/adaptive-project-framing/specs/agent-requirements/spec.md +16 -0
  5. package/.openprd/changes/adaptive-project-framing/task-events.jsonl +20 -0
  6. package/.openprd/changes/adaptive-project-framing/tasks.md +340 -0
  7. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/.openprd.yaml +2 -0
  8. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/design.md +34 -0
  9. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/proposal.md +23 -0
  10. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/specs/agent-requirements/spec.md +16 -0
  11. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/task-events.jsonl +14 -0
  12. package/.openprd/changes/docs-anti-pollution-and-design-direction-review/tasks.md +238 -0
  13. package/.openprd/changes/loop-isolated-worktree-commit/.openprd.yaml +2 -0
  14. package/.openprd/changes/loop-isolated-worktree-commit/design.md +53 -0
  15. package/.openprd/changes/loop-isolated-worktree-commit/proposal.md +38 -0
  16. package/.openprd/changes/loop-isolated-worktree-commit/specs/agent-requirements/spec.md +16 -0
  17. package/.openprd/changes/loop-isolated-worktree-commit/task-events.jsonl +22 -0
  18. package/.openprd/changes/loop-isolated-worktree-commit/tasks.md +357 -0
  19. package/.openprd/changes/openprd/.openprd.yaml +2 -0
  20. package/.openprd/changes/openprd/design.md +46 -0
  21. package/.openprd/changes/openprd/proposal.md +36 -0
  22. package/.openprd/changes/openprd/specs/agent-requirements/spec.md +16 -0
  23. package/.openprd/changes/openprd/task-events.jsonl +23 -0
  24. package/.openprd/changes/openprd/tasks.md +242 -0
  25. package/.openprd/changes/openprd-generated-change/.openprd.yaml +2 -0
  26. package/.openprd/changes/openprd-generated-change/design.md +50 -0
  27. package/.openprd/changes/openprd-generated-change/proposal.md +37 -0
  28. package/.openprd/changes/openprd-generated-change/specs/agent-requirements/spec.md +16 -0
  29. package/.openprd/changes/openprd-generated-change/task-events.jsonl +21 -0
  30. package/.openprd/changes/openprd-generated-change/tasks.md +357 -0
  31. package/.openprd/changes/pm-human-in-the-loop/.openprd.yaml +2 -0
  32. package/.openprd/changes/pm-human-in-the-loop/design.md +38 -0
  33. package/.openprd/changes/pm-human-in-the-loop/proposal.md +29 -0
  34. package/.openprd/changes/pm-human-in-the-loop/specs/agent-requirements/spec.md +16 -0
  35. package/.openprd/changes/pm-human-in-the-loop/task-events.jsonl +18 -0
  36. package/.openprd/changes/pm-human-in-the-loop/tasks.md +306 -0
  37. package/.openprd/changes/test-strategy-router/.openprd.yaml +2 -0
  38. package/.openprd/changes/test-strategy-router/design.md +65 -0
  39. package/.openprd/changes/test-strategy-router/proposal.md +45 -0
  40. package/.openprd/changes/test-strategy-router/specs/agent-requirements/spec.md +16 -0
  41. package/.openprd/changes/test-strategy-router/task-events.jsonl +1 -0
  42. package/.openprd/changes/test-strategy-router/tasks.md +132 -0
  43. package/.openprd/changes/verify-boundaries-and-historical-refresh/.openprd.yaml +2 -0
  44. package/.openprd/changes/verify-boundaries-and-historical-refresh/design.md +28 -0
  45. package/.openprd/changes/verify-boundaries-and-historical-refresh/proposal.md +23 -0
  46. package/.openprd/changes/verify-boundaries-and-historical-refresh/specs/agent-requirements/spec.md +16 -0
  47. package/.openprd/changes/verify-boundaries-and-historical-refresh/task-events.jsonl +11 -0
  48. package/.openprd/changes/verify-boundaries-and-historical-refresh/tasks.md +66 -0
  49. package/.openprd/changes/vibe-coding-dev-check/.openprd.yaml +2 -0
  50. package/.openprd/changes/vibe-coding-dev-check/design.md +37 -0
  51. package/.openprd/changes/vibe-coding-dev-check/proposal.md +28 -0
  52. package/.openprd/changes/vibe-coding-dev-check/specs/agent-requirements/spec.md +16 -0
  53. package/.openprd/changes/vibe-coding-dev-check/task-events.jsonl +18 -0
  54. package/.openprd/changes/vibe-coding-dev-check/tasks.md +306 -0
  55. package/.openprd/design/README.md +11 -6
  56. package/.openprd/design/README_EN.md +4 -1
  57. package/.openprd/design/active/asset-spec.md +6 -0
  58. package/.openprd/design/active/direction-plan.md +5 -5
  59. package/.openprd/design/active/selected-direction.md +2 -0
  60. package/.openprd/design/anti-slop.md +26 -1
  61. package/.openprd/design/assets/surface-presets.md +6 -0
  62. package/.openprd/design/checklists/ui-quality-gate.md +7 -0
  63. package/.openprd/design/lenses/frontend-lenses.md +28 -0
  64. package/.openprd/design/recipes/content-experience.md +8 -1
  65. package/.openprd/design/recipes/operational-tool.md +7 -0
  66. package/.openprd/design/recipes/product-launch.md +8 -1
  67. package/.openprd/design/templates/README.md +6 -3
  68. package/.openprd/design/templates/README_EN.md +6 -3
  69. package/.openprd/design/templates/ops-dashboard.html +8 -1
  70. package/.openprd/design/templates/product-launch.html +2 -1
  71. package/.openprd/design/themes/theme-catalog.json +23 -5
  72. package/.openprd/engagements/active/prd.md +137 -112
  73. package/.openprd/standards/config.json +7 -1
  74. package/AGENTS.md +13 -3
  75. package/README.md +127 -14
  76. package/README_EN.md +156 -13
  77. package/package.json +1 -1
  78. package/scripts/dev-check-wrapup-copy.mjs +46 -21
  79. package/skills/openprd-diagram-review/SKILL.md +27 -2
  80. package/skills/openprd-diagram-review/references/explanation-svg-patterns.md +131 -0
  81. package/skills/openprd-discovery-loop/SKILL.md +2 -2
  82. package/skills/openprd-frontend-design/SKILL.md +44 -20
  83. package/skills/openprd-frontend-design/references/design-asset-contract.md +6 -0
  84. package/skills/openprd-frontend-design/references/direction-engine.md +29 -4
  85. package/skills/openprd-harness/SKILL.md +22 -16
  86. package/skills/openprd-quality/SKILL.md +10 -8
  87. package/skills/openprd-requirement-intake/SKILL.md +2 -0
  88. package/skills/openprd-router/SKILL.md +2 -1
  89. package/skills/openprd-shared/SKILL.md +26 -16
  90. package/skills/openprd-standards/SKILL.md +4 -2
  91. package/src/agent-canonical-content.js +810 -0
  92. package/src/agent-integration.js +258 -777
  93. package/src/canvas-app-client-app.js +838 -0
  94. package/src/canvas-app-client-helpers.js +1138 -0
  95. package/src/canvas-app-styles.js +898 -0
  96. package/src/canvas-app.html.js +135 -0
  97. package/src/canvas-binding.js +414 -0
  98. package/src/canvas-codex-relay.js +430 -0
  99. package/src/canvas-i18n.js +282 -0
  100. package/src/canvas-scene.js +377 -0
  101. package/src/canvas-session-store.js +148 -0
  102. package/src/canvas-workspace.js +1264 -0
  103. package/src/cli/args.js +72 -4
  104. package/src/cli/basic-print.js +3 -0
  105. package/src/cli/canvas-print.js +106 -0
  106. package/src/cli/doctor-print.js +20 -0
  107. package/src/cli/print.js +2 -0
  108. package/src/cli/quality-commands.js +222 -0
  109. package/src/cli/quality-print.js +28 -2
  110. package/src/cli/run-print.js +5 -0
  111. package/src/cli/workflow-print.js +3 -0
  112. package/src/codex-app-server-relay.js +251 -0
  113. package/src/codex-hook-runner-template.mjs +978 -58
  114. package/src/design-starter-support.js +2 -2
  115. package/src/design-starter.js +39 -3
  116. package/src/dev-standards.js +125 -10
  117. package/src/diagram-core.js +6 -20
  118. package/src/discovery.js +2 -8
  119. package/src/docs-compact.js +125 -0
  120. package/src/execution-strategy.js +1 -1
  121. package/src/fleet.js +9 -3
  122. package/src/guide.js +227 -0
  123. package/src/html-artifacts.js +428 -7
  124. package/src/knowledge.js +45 -11
  125. package/src/language-policy.js +63 -3
  126. package/src/learning-review.js +1 -1
  127. package/src/loop.js +1 -1
  128. package/src/openprd.js +68 -131
  129. package/src/openspec/change-validate.js +2 -2
  130. package/src/openspec/constants.js +6 -7
  131. package/src/openspec/execute.js +2 -2
  132. package/src/openspec/generate.js +318 -102
  133. package/src/openspec/migration.js +168 -0
  134. package/src/openspec/paths.js +1 -22
  135. package/src/prd-core.js +1 -1
  136. package/src/quality-visual-review.js +257 -14
  137. package/src/quality.js +46 -8
  138. package/src/review-presentation.js +60 -0
  139. package/src/run-harness.js +41 -3
  140. package/src/session-evidence.js +363 -0
  141. package/src/standards.js +64 -0
  142. package/src/test-strategy.js +7 -4
  143. package/src/visual-compare-centering.js +622 -0
  144. package/src/visual-compare-core.js +1123 -0
  145. package/src/visual-compare.js +620 -698
  146. package/src/workspace-core.js +94 -4
  147. package/src/workspace-workflow.js +46 -1
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { preferSimplifiedChinese } from '../language-policy.js';
3
+ import { detectPrimaryLanguage, isChineseLocale, preferUserLanguage } from '../language-policy.js';
4
4
  import { needsBusinessGuardrails } from '../prd-core.js';
5
5
  import { EXECUTION_STRATEGY_METADATA_KEYS, formatTaskExecutionStrategyMetadata } from '../execution-strategy.js';
6
6
  import { TEST_STRATEGY_METADATA_KEYS, formatTaskTestStrategyMetadata } from '../test-strategy.js';
@@ -46,6 +46,198 @@ function scalar(value, fallback = '待补充') {
46
46
  return text || fallback;
47
47
  }
48
48
 
49
+ const OPENSPEC_COPY = {
50
+ zh: {
51
+ tbd: '待补充',
52
+ proposalReason: '## 背景与原因',
53
+ proposalChanges: '## 变更内容',
54
+ proposalCapability: '## 能力范围',
55
+ proposalImpact: '## 影响范围',
56
+ designTitle: '# 设计',
57
+ background: '## 背景',
58
+ goals: '## 目标',
59
+ scope: '## 范围',
60
+ constraints: '## 约束',
61
+ businessGuardrails: '## 业务护栏',
62
+ risksAndQuestions: '## 风险与开放问题',
63
+ generatedReason: '需要把当前产品需求转化为可执行的规格变更。',
64
+ generatedChange: '根据当前 PRD 生成 OpenPrd 管理的规格增量。',
65
+ productBehavior: '产品行为',
66
+ capabilitySuffix: '需求。',
67
+ primaryUserPrefix: '主要用户',
68
+ costDriverPrefix: '成本来源',
69
+ usageLimitPrefix: '额度限制',
70
+ dependencyPrefix: '依赖',
71
+ riskPrefix: '风险',
72
+ agentCanContinue: 'Agent 可以通过 OpenPrd 从 PRD 继续推进到 specs、tasks、validation 和 execution。',
73
+ generatedFromSnapshot: '根据最新 OpenPrd 快照生成。',
74
+ abusePreventionPrefix: '滥用防护',
75
+ monitoringSignalPrefix: '监控信号',
76
+ alertThresholdPrefix: '报警阈值',
77
+ stopLossPrefix: '止损动作',
78
+ assumptionPrefix: '假设',
79
+ questionPrefix: '问题',
80
+ currentBehavior: '当前 PRD 描述的产品行为',
81
+ generatedBehavior: '生成的能力应保持最新 OpenPrd 快照描述的行为。',
82
+ mainFlow: '主要用户完成主流程',
83
+ acceptanceSatisfied: '预期产品结果得到满足',
84
+ edgeCase: '出现边界情况',
85
+ failureMode: '出现失败模式',
86
+ requirementsTitle: '## 新增需求',
87
+ requirementPrefix: '### 需求',
88
+ scenarioSuccess: '#### 场景:主流程成功',
89
+ scenarioEdge: '#### 场景:边界情况保持可见',
90
+ scenarioFailure: '#### 场景:失败模式得到处理',
91
+ when: '当',
92
+ then: '则',
93
+ edgeThen: '产品应保持该情况明确可见,以支持实现和验证',
94
+ failureThen: '产品应提供有边界且可评审的结果',
95
+ implementationFallback: '补齐当前需求实现',
96
+ implementationPrefix: '实现',
97
+ verificationFallback: '验证当前需求闭环',
98
+ verificationPrefix: '验证',
99
+ currentRequirementLoop: '当前需求闭环',
100
+ donePrefix: '已完成:',
101
+ verifiedPrefix: '已验证:',
102
+ involvedPrefix: '涉及',
103
+ specReviewTitle: '评审生成的 spec 覆盖',
104
+ specReviewDone: (capability) => `生成的 ${capability} spec 符合 PRD 意图`,
105
+ docsTitle: '维护 docs/basic 项目基础文档',
106
+ docsDone: '已检查 docs/basic 是否缺失或因本次需求、流程、结构、依赖、产品行为变化而过期;若涉及后端、脚本、Agent 或工具链变更,已同步评估 CLI 与 API 接入面,并在 backend-structure.md 中记录事实或不适用原因;需要更新的基础文档已同步',
107
+ manualsTitle: '更新文件说明书和文件夹 README',
108
+ manualsDone: '本次变更涉及的文件说明书和文件夹 README 已检查;缺失的已补齐,过期的已更新',
109
+ finalValidateTitle: '运行 OpenPrd spec 校验',
110
+ finalValidateDone: '生成的 change 通过 OpenPrd 校验',
111
+ tasksTitle: '# 任务',
112
+ continueWith: (fileName) => `Continue with \`${fileName}\` after completing this file.`,
113
+ nonFunctionalPrefix: '回归非功能约束',
114
+ nonFunctionalDone: '非功能约束已经回归确认。',
115
+ edgePrefix: '边界情况',
116
+ failurePrefix: '失败处理',
117
+ edgeFailurePrefix: '回归边界条件与失败处理',
118
+ edgeFailureDone: '边界条件与失败处理已经回归确认。',
119
+ flowIntegrationPrefix: '打通主流程闭环',
120
+ flowIntegrationDone: '主流程关键节点已经打通,用户可以按预期从入口走到结果收尾。',
121
+ guardrailUsageTitle: '验证成本与额度护栏',
122
+ guardrailUsageDone: '已验证免费、试用或低权限用户不能绕过额度、并发、频率或总量限制',
123
+ guardrailAbuseTitle: '验证滥用与越权路径',
124
+ guardrailAbuseDone: '已覆盖重复请求、并发请求、越权身份和异常恢复等负向场景',
125
+ guardrailMonitoringTitle: '验证成本监控、报警和止损',
126
+ guardrailMonitoringDone: '已确认用量或成本信号、报警阈值和人工/自动止损动作可执行',
127
+ architecture: {
128
+ shared: ['对齐共享契约与 Host API 调用边界', '共享契约、Host API 和上下游调用边界已经对齐,后续功能可以直接接线。'],
129
+ domain: ['补齐领域服务、数据读取与状态同步', '领域服务、数据读取和状态同步已经接通,界面不会停留在假可见状态。'],
130
+ main: ['补齐主进程、窗口与后台接线', '主进程事件、窗口入口或后台能力的接线已经补齐,不会因为运行时边界遗漏而失效。'],
131
+ renderer: ['接通界面入口、导航与页面挂载', '用户可以从正确入口进入对应界面,页面挂载与状态收尾已经接通。'],
132
+ diagnostics: ['补齐日志、诊断与可观测性信号', '关键阶段日志和诊断信号已经保留,后续排查可以直接定位到断点。'],
133
+ },
134
+ },
135
+ en: {
136
+ tbd: 'TBD',
137
+ proposalReason: '## Background And Rationale',
138
+ proposalChanges: '## Change Scope',
139
+ proposalCapability: '## Capability Scope',
140
+ proposalImpact: '## Impact',
141
+ designTitle: '# Design',
142
+ background: '## Background',
143
+ goals: '## Goals',
144
+ scope: '## Scope',
145
+ constraints: '## Constraints',
146
+ businessGuardrails: '## Business Guardrails',
147
+ risksAndQuestions: '## Risks And Open Questions',
148
+ generatedReason: 'Convert the current product requirement into an executable spec change.',
149
+ generatedChange: 'Generate an OpenPrd-managed spec increment from the current PRD.',
150
+ productBehavior: 'product behavior',
151
+ capabilitySuffix: 'requirement.',
152
+ primaryUserPrefix: 'Primary user',
153
+ costDriverPrefix: 'Cost driver',
154
+ usageLimitPrefix: 'Usage limit',
155
+ dependencyPrefix: 'Dependency',
156
+ riskPrefix: 'Risk',
157
+ agentCanContinue: 'The agent can use OpenPrd to continue from PRD into specs, tasks, validation, and execution.',
158
+ generatedFromSnapshot: 'Generated from the latest OpenPrd snapshot.',
159
+ abusePreventionPrefix: 'Abuse prevention',
160
+ monitoringSignalPrefix: 'Monitoring signal',
161
+ alertThresholdPrefix: 'Alert threshold',
162
+ stopLossPrefix: 'Stop-loss action',
163
+ assumptionPrefix: 'Assumption',
164
+ questionPrefix: 'Question',
165
+ currentBehavior: 'the product behavior described by the current PRD',
166
+ generatedBehavior: 'The generated capability should preserve the behavior described by the latest OpenPrd snapshot.',
167
+ mainFlow: 'the primary user completes the main flow',
168
+ acceptanceSatisfied: 'the expected product outcome is satisfied',
169
+ edgeCase: 'an edge case occurs',
170
+ failureMode: 'a failure mode occurs',
171
+ requirementsTitle: '## New Requirements',
172
+ requirementPrefix: '### Requirement',
173
+ scenarioSuccess: '#### Scenario: Main Flow Succeeds',
174
+ scenarioEdge: '#### Scenario: Edge Case Stays Visible',
175
+ scenarioFailure: '#### Scenario: Failure Mode Is Handled',
176
+ when: 'When',
177
+ then: 'Then',
178
+ edgeThen: 'the product should keep the condition visible so implementation and verification can review it',
179
+ failureThen: 'the product should provide a bounded and reviewable result',
180
+ implementationFallback: 'Complete the current requirement implementation',
181
+ implementationPrefix: 'Implement',
182
+ verificationFallback: 'Verify the current requirement loop',
183
+ verificationPrefix: 'Verify',
184
+ currentRequirementLoop: 'current requirement loop',
185
+ donePrefix: 'Done: ',
186
+ verifiedPrefix: 'Verified: ',
187
+ involvedPrefix: 'Involves',
188
+ specReviewTitle: 'Review Generated Spec Coverage',
189
+ specReviewDone: (capability) => `The generated ${capability} spec matches the PRD intent`,
190
+ docsTitle: 'Maintain docs/basic Project Docs',
191
+ docsDone: 'docs/basic has been checked for missing or stale content caused by this requirement, flow, structure, dependency, or product behavior change; backend, script, agent, or toolchain changes have been reflected in backend-structure.md or marked not applicable.',
192
+ manualsTitle: 'Update File Manuals And Folder READMEs',
193
+ manualsDone: 'Affected file manuals and folder READMEs have been checked; missing docs were added and stale docs were updated',
194
+ finalValidateTitle: 'Run OpenPrd Spec Validation',
195
+ finalValidateDone: 'The generated change passes OpenPrd validation',
196
+ tasksTitle: '# Tasks',
197
+ continueWith: (fileName) => `Continue with \`${fileName}\` after completing this file.`,
198
+ nonFunctionalPrefix: 'Regress Non-Functional Constraints',
199
+ nonFunctionalDone: 'Non-functional constraints have been regression-checked.',
200
+ edgePrefix: 'Edge case',
201
+ failurePrefix: 'Failure handling',
202
+ edgeFailurePrefix: 'Regress Edge Cases And Failure Handling',
203
+ edgeFailureDone: 'Edge cases and failure handling have been regression-checked.',
204
+ flowIntegrationPrefix: 'Wire Main Flow End To End',
205
+ flowIntegrationDone: 'Main flow checkpoints have been wired so users can reach the expected result.',
206
+ guardrailUsageTitle: 'Verify Cost And Quota Guardrails',
207
+ guardrailUsageDone: 'Free, trial, or low-privilege users cannot bypass quota, concurrency, frequency, or total-usage limits',
208
+ guardrailAbuseTitle: 'Verify Abuse And Privilege Escalation Paths',
209
+ guardrailAbuseDone: 'Duplicate requests, concurrent requests, privilege escalation, and abnormal recovery paths are covered',
210
+ guardrailMonitoringTitle: 'Verify Cost Monitoring, Alerts, And Stop-Loss',
211
+ guardrailMonitoringDone: 'Usage or cost signals, alert thresholds, and manual or automatic stop-loss actions are executable',
212
+ architecture: {
213
+ shared: ['Align Shared Contracts And Host API Boundaries', 'Shared contracts, Host API calls, and upstream/downstream boundaries are aligned so follow-on work can wire directly.'],
214
+ domain: ['Complete Domain Services, Data Reads, And State Sync', 'Domain services, data reads, and state sync are wired so the UI does not stay in a fake-visible state.'],
215
+ main: ['Wire Main Process, Windows, And Background Runtime', 'Main-process events, window entry points, or background capabilities are wired without runtime-boundary gaps.'],
216
+ renderer: ['Wire UI Entry Points, Navigation, And Page Mounting', 'Users can enter the correct UI and page mounting plus state closeout are wired.'],
217
+ diagnostics: ['Add Logs, Diagnostics, And Observability Signals', 'Key-stage logs and diagnostics signals are retained so future debugging can locate the breakpoints.'],
218
+ },
219
+ },
220
+ };
221
+
222
+ function copyForLocale(locale) {
223
+ return isChineseLocale(locale) ? OPENSPEC_COPY.zh : OPENSPEC_COPY.en;
224
+ }
225
+
226
+ function resolveSnapshotLocale(snapshot) {
227
+ const sections = snapshot.sections ?? {};
228
+ return detectPrimaryLanguage([
229
+ snapshot.locale,
230
+ snapshot.language,
231
+ snapshot.title,
232
+ sections.problem?.problemStatement,
233
+ sections.problem?.whyNow,
234
+ ...(arrayValue(sections.goals?.goals)),
235
+ ...(arrayValue(sections.scope?.inScope)),
236
+ ...(arrayValue(sections.requirements?.functional)),
237
+ ...(arrayValue(sections.scenarios?.primaryFlows)),
238
+ ], snapshot.locale ?? snapshot.language);
239
+ }
240
+
49
241
  function arrayValue(value) {
50
242
  if (Array.isArray(value)) {
51
243
  return value.map((item) => scalar(item, '')).filter(Boolean);
@@ -82,10 +274,11 @@ function normalizeTaskSemanticKey(value) {
82
274
  .trim();
83
275
  }
84
276
 
85
- function summarizeTaskItems(items, limit = 2, maxLength = 56) {
277
+ function summarizeTaskItems(items, limit = 2, maxLength = 56, locale = 'zh-CN') {
278
+ const copy = copyForLocale(locale);
86
279
  const values = arrayValue(items).map((item) => trimTaskText(item)).filter(Boolean);
87
280
  if (values.length === 0) {
88
- return '当前需求闭环';
281
+ return copy.currentRequirementLoop;
89
282
  }
90
283
  const shown = [];
91
284
  for (const item of values) {
@@ -105,7 +298,8 @@ function buildDoneText(prefix, item) {
105
298
  return `${prefix}${trimTaskText(item)}`;
106
299
  }
107
300
 
108
- function cleanImplementationTitle(item) {
301
+ function cleanImplementationTitle(item, locale = 'zh-CN') {
302
+ const copy = copyForLocale(locale);
109
303
  const text = trimTaskText(item)
110
304
  .replace(/^系统需要/g, '')
111
305
  .replace(/^系统应/g, '')
@@ -115,7 +309,7 @@ function cleanImplementationTitle(item) {
115
309
  .replace(/^当前主工作树中/g, '')
116
310
  .trim();
117
311
  if (!text) {
118
- return '补齐当前需求实现';
312
+ return copy.implementationFallback;
119
313
  }
120
314
  if (/^(在|提供|保留|展示|支持|完成|补齐|同步|避免|接入|接通|收口|更新|修复|统一)/.test(text)) {
121
315
  return text;
@@ -123,18 +317,19 @@ function cleanImplementationTitle(item) {
123
317
  if (/^(Add|Apply|Build|Collect|Create|Deny|Display|Expose|Fetch|Handle|Implement|Keep|Load|Manage|Persist|Port|Prepare|Prevent|Protect|Record|Refresh|Render|Reuse|Route|Return|Save|Show|Submit|Support|Sync|Track|Update|Validate|Wire)/i.test(text)) {
124
318
  return text;
125
319
  }
126
- return `实现${text}`;
320
+ return isChineseLocale(locale) ? `${copy.implementationPrefix}${text}` : `${copy.implementationPrefix} ${text}`;
127
321
  }
128
322
 
129
- function buildVerificationTitle(item) {
323
+ function buildVerificationTitle(item, locale = 'zh-CN') {
324
+ const copy = copyForLocale(locale);
130
325
  const text = trimTaskText(item);
131
326
  if (!text) {
132
- return '验证当前需求闭环';
327
+ return copy.verificationFallback;
133
328
  }
134
329
  if (/^(验证|回归|确认|检查|Compare|Verify|Test|Review)/i.test(text)) {
135
330
  return text;
136
331
  }
137
- return `验证${text}`;
332
+ return isChineseLocale(locale) ? `${copy.verificationPrefix}${text}` : `${copy.verificationPrefix} ${text}`;
138
333
  }
139
334
 
140
335
  function chunkItems(items, maxItemsPerChunk = 2) {
@@ -155,30 +350,35 @@ function defaultTaskVerifyCommand(changeId, task) {
155
350
  const ARCHITECTURE_TASK_DEFINITIONS = [
156
351
  {
157
352
  key: 'shared-contracts',
353
+ copyKey: 'shared',
158
354
  pattern: /(shared contracts?|host methods?|host api|ipc|preload|schema|contract|entitlement|接口约定|共享契约|契约|协议|类型)/i,
159
355
  title: '对齐共享契约与 Host API 调用边界',
160
356
  done: '共享契约、Host API 和上下游调用边界已经对齐,后续功能可以直接接线。',
161
357
  },
162
358
  {
163
359
  key: 'domain-data',
360
+ copyKey: 'domain',
164
361
  pattern: /(domain|service|gateway|adapter|repository|storage|snapshot|backend|billing|orders?|entitlement|sync|cache|状态快照|领域服务|网关|适配器|仓储|后端|订单|会员|额度|数据)/i,
165
362
  title: '补齐领域服务、数据读取与状态同步',
166
363
  done: '领域服务、数据读取和状态同步已经接通,界面不会停留在假可见状态。',
167
364
  },
168
365
  {
169
366
  key: 'main-runtime',
367
+ copyKey: 'main',
170
368
  pattern: /(main process|electron main|main\b|window|daemon|tray|主进程|窗口|守护|后台)/i,
171
369
  title: '补齐主进程、窗口与后台接线',
172
370
  done: '主进程事件、窗口入口或后台能力的接线已经补齐,不会因为运行时边界遗漏而失效。',
173
371
  },
174
372
  {
175
373
  key: 'renderer-surface',
374
+ copyKey: 'renderer',
176
375
  pattern: /(renderer|view|ui|route|navigation|page|dialog|modal|surface|sidebar|entry|renderer 入口|界面|页面|弹窗|弹层|入口|导航|菜单|路由|侧边栏)/i,
177
376
  title: '接通界面入口、导航与页面挂载',
178
377
  done: '用户可以从正确入口进入对应界面,页面挂载与状态收尾已经接通。',
179
378
  },
180
379
  {
181
380
  key: 'diagnostics',
381
+ copyKey: 'diagnostics',
182
382
  pattern: /(diagnostic|log|logging|trace|telemetry|monitor|诊断|日志|追踪|监控|埋点)/i,
183
383
  title: '补齐日志、诊断与可观测性信号',
184
384
  done: '关键阶段日志和诊断信号已经保留,后续排查可以直接定位到断点。',
@@ -191,57 +391,61 @@ function resolveCapability(snapshot) {
191
391
  }
192
392
 
193
393
  function buildProposal({ changeId, capability, snapshot }) {
394
+ const locale = resolveSnapshotLocale(snapshot);
395
+ const copy = copyForLocale(locale);
194
396
  const sections = snapshot.sections ?? {};
195
397
  return [
196
398
  `# ${scalar(snapshot.title, changeId)}`,
197
399
  '',
198
- '## 背景与原因',
400
+ copy.proposalReason,
199
401
  '',
200
- scalar(sections.problem?.problemStatement, '需要把当前产品需求转化为可执行的规格变更。'),
402
+ scalar(sections.problem?.problemStatement, copy.generatedReason),
201
403
  '',
202
- '## 变更内容',
404
+ copy.proposalChanges,
203
405
  '',
204
406
  bullets([
205
407
  ...arrayValue(sections.scope?.inScope),
206
408
  ...arrayValue(sections.requirements?.functional),
207
409
  ...arrayValue(sections.goals?.acceptanceGoals),
208
- ], ['根据当前 PRD 生成 OpenPrd 管理的规格增量。']),
410
+ ], [copy.generatedChange]),
209
411
  '',
210
- '## 能力范围',
412
+ copy.proposalCapability,
211
413
  '',
212
- `- \`${capability}\`: ${scalar(snapshot.title, '产品行为')} 需求。`,
414
+ `- \`${capability}\`: ${scalar(snapshot.title, copy.productBehavior)} ${copy.capabilitySuffix}`,
213
415
  '',
214
- '## 影响范围',
416
+ copy.proposalImpact,
215
417
  '',
216
418
  bullets([
217
- ...arrayValue(sections.users?.primaryUsers).map((item) => `主要用户: ${item}`),
218
- ...arrayValue(sections.businessGuardrails?.costDrivers).map((item) => `成本来源: ${item}`),
219
- ...arrayValue(sections.businessGuardrails?.usageLimits).map((item) => `额度限制: ${item}`),
220
- ...arrayValue(sections.constraints?.dependencies).map((item) => `依赖: ${item}`),
221
- ...arrayValue(sections.risks?.risks).map((item) => `风险: ${item}`),
222
- ], ['Agent 可以通过 OpenPrd 从 PRD 继续推进到 specs、tasks、validation 和 execution。']),
419
+ ...arrayValue(sections.users?.primaryUsers).map((item) => `${copy.primaryUserPrefix}: ${item}`),
420
+ ...arrayValue(sections.businessGuardrails?.costDrivers).map((item) => `${copy.costDriverPrefix}: ${item}`),
421
+ ...arrayValue(sections.businessGuardrails?.usageLimits).map((item) => `${copy.usageLimitPrefix}: ${item}`),
422
+ ...arrayValue(sections.constraints?.dependencies).map((item) => `${copy.dependencyPrefix}: ${item}`),
423
+ ...arrayValue(sections.risks?.risks).map((item) => `${copy.riskPrefix}: ${item}`),
424
+ ], [copy.agentCanContinue]),
223
425
  '',
224
426
  ].join('\n');
225
427
  }
226
428
 
227
429
  function buildDesign({ snapshot }) {
430
+ const locale = resolveSnapshotLocale(snapshot);
431
+ const copy = copyForLocale(locale);
228
432
  const sections = snapshot.sections ?? {};
229
433
  return [
230
- '# 设计',
434
+ copy.designTitle,
231
435
  '',
232
- '## 背景',
436
+ copy.background,
233
437
  '',
234
- scalar(sections.problem?.whyNow, '根据最新 OpenPrd 快照生成。'),
438
+ scalar(sections.problem?.whyNow, copy.generatedFromSnapshot),
235
439
  '',
236
- '## 目标',
440
+ copy.goals,
237
441
  '',
238
442
  bullets(sections.goals?.goals),
239
443
  '',
240
- '## 范围',
444
+ copy.scope,
241
445
  '',
242
446
  bullets(sections.scope?.inScope),
243
447
  '',
244
- '## 约束',
448
+ copy.constraints,
245
449
  '',
246
450
  bullets([
247
451
  ...arrayValue(sections.constraints?.technical),
@@ -249,63 +453,67 @@ function buildDesign({ snapshot }) {
249
453
  ...arrayValue(sections.constraints?.dependencies),
250
454
  ]),
251
455
  '',
252
- '## 业务护栏',
456
+ copy.businessGuardrails,
253
457
  '',
254
458
  bullets([
255
- ...arrayValue(sections.businessGuardrails?.costDrivers).map((item) => `成本来源: ${item}`),
256
- ...arrayValue(sections.businessGuardrails?.usageLimits).map((item) => `额度限制: ${item}`),
257
- ...arrayValue(sections.businessGuardrails?.abusePrevention).map((item) => `滥用防护: ${item}`),
258
- ...arrayValue(sections.businessGuardrails?.monitoringSignals).map((item) => `监控信号: ${item}`),
259
- ...arrayValue(sections.businessGuardrails?.alertThresholds).map((item) => `报警阈值: ${item}`),
260
- ...arrayValue(sections.businessGuardrails?.stopLossActions).map((item) => `止损动作: ${item}`),
459
+ ...arrayValue(sections.businessGuardrails?.costDrivers).map((item) => `${copy.costDriverPrefix}: ${item}`),
460
+ ...arrayValue(sections.businessGuardrails?.usageLimits).map((item) => `${copy.usageLimitPrefix}: ${item}`),
461
+ ...arrayValue(sections.businessGuardrails?.abusePrevention).map((item) => `${copy.abusePreventionPrefix}: ${item}`),
462
+ ...arrayValue(sections.businessGuardrails?.monitoringSignals).map((item) => `${copy.monitoringSignalPrefix}: ${item}`),
463
+ ...arrayValue(sections.businessGuardrails?.alertThresholds).map((item) => `${copy.alertThresholdPrefix}: ${item}`),
464
+ ...arrayValue(sections.businessGuardrails?.stopLossActions).map((item) => `${copy.stopLossPrefix}: ${item}`),
261
465
  ]),
262
466
  '',
263
- '## 风险与开放问题',
467
+ copy.risksAndQuestions,
264
468
  '',
265
469
  bullets([
266
- ...arrayValue(sections.risks?.assumptions).map((item) => `假设: ${item}`),
267
- ...arrayValue(sections.risks?.risks).map((item) => `风险: ${item}`),
268
- ...arrayValue(sections.risks?.openQuestions).map((item) => `问题: ${item}`),
470
+ ...arrayValue(sections.risks?.assumptions).map((item) => `${copy.assumptionPrefix}: ${item}`),
471
+ ...arrayValue(sections.risks?.risks).map((item) => `${copy.riskPrefix}: ${item}`),
472
+ ...arrayValue(sections.risks?.openQuestions).map((item) => `${copy.questionPrefix}: ${item}`),
269
473
  ]),
270
474
  '',
271
475
  ].join('\n');
272
476
  }
273
477
 
274
478
  export function buildSpec({ snapshot }) {
479
+ const locale = resolveSnapshotLocale(snapshot);
480
+ const copy = copyForLocale(locale);
275
481
  const sections = snapshot.sections ?? {};
276
482
  const title = safeRequirementTitle(
277
- preferSimplifiedChinese(snapshot.title, '当前 PRD 描述的产品行为'),
278
- '当前 PRD 描述的产品行为',
483
+ preferUserLanguage(snapshot.title, locale, { zh: copy.currentBehavior, en: copy.currentBehavior }),
484
+ copy.currentBehavior,
279
485
  );
280
486
  const acceptanceSource = arrayValue(sections.goals?.acceptanceGoals)[0]
281
487
  ?? arrayValue(sections.requirements?.functional)[0];
282
- const flow = preferSimplifiedChinese(arrayValue(sections.scenarios?.primaryFlows)[0], '主要用户完成主流程');
283
- const acceptance = preferSimplifiedChinese(acceptanceSource, '预期产品结果得到满足');
284
- const edgeCase = preferSimplifiedChinese(arrayValue(sections.scenarios?.edgeCases)[0], '出现边界情况');
285
- const failureMode = preferSimplifiedChinese(arrayValue(sections.scenarios?.failureModes)[0], '出现失败模式');
488
+ const flow = preferUserLanguage(arrayValue(sections.scenarios?.primaryFlows)[0], locale, { zh: copy.mainFlow, en: copy.mainFlow });
489
+ const acceptance = preferUserLanguage(acceptanceSource, locale, { zh: copy.acceptanceSatisfied, en: copy.acceptanceSatisfied });
490
+ const edgeCase = preferUserLanguage(arrayValue(sections.scenarios?.edgeCases)[0], locale, { zh: copy.edgeCase, en: copy.edgeCase });
491
+ const failureMode = preferUserLanguage(arrayValue(sections.scenarios?.failureModes)[0], locale, { zh: copy.failureMode, en: copy.failureMode });
492
+ const headingSeparator = isChineseLocale(locale) ? ':' : ': ';
286
493
 
287
494
  return [
288
- '## 新增需求',
495
+ copy.requirementsTitle,
289
496
  '',
290
- `### 需求:${title}`,
291
- preferSimplifiedChinese(sections.problem?.problemStatement, '生成的能力应保持最新 OpenPrd 快照描述的行为。'),
497
+ `${copy.requirementPrefix}${headingSeparator}${title}`,
498
+ preferUserLanguage(sections.problem?.problemStatement, locale, { zh: copy.generatedBehavior, en: copy.generatedBehavior }),
292
499
  '',
293
- '#### 场景:主流程成功',
294
- `- **当** ${flow}`,
295
- `- **则** ${acceptance}`,
500
+ copy.scenarioSuccess,
501
+ `- **${copy.when}** ${flow}`,
502
+ `- **${copy.then}** ${acceptance}`,
296
503
  '',
297
- '#### 场景:边界情况保持可见',
298
- `- **当** ${edgeCase}`,
299
- '- **则** 产品应保持该情况明确可见,以支持实现和验证',
504
+ copy.scenarioEdge,
505
+ `- **${copy.when}** ${edgeCase}`,
506
+ `- **${copy.then}** ${copy.edgeThen}`,
300
507
  '',
301
- '#### 场景:失败模式得到处理',
302
- `- **当** ${failureMode}`,
303
- '- **则** 产品应提供有边界且可评审的结果',
508
+ copy.scenarioFailure,
509
+ `- **${copy.when}** ${failureMode}`,
510
+ `- **${copy.then}** ${copy.failureThen}`,
304
511
  '',
305
512
  ].join('\n');
306
513
  }
307
514
 
308
- function inferArchitectureTasks(snapshot) {
515
+ function inferArchitectureTasks(snapshot, locale = resolveSnapshotLocale(snapshot)) {
516
+ const copy = copyForLocale(locale);
309
517
  const sections = snapshot.sections ?? {};
310
518
  const lines = [
311
519
  ...arrayValue(sections.constraints?.technical),
@@ -326,13 +534,14 @@ function inferArchitectureTasks(snapshot) {
326
534
  .map(({ definition, matches }) => ({
327
535
  key: definition.key,
328
536
  type: 'implementation',
329
- title: definition.title,
330
- done: `${definition.done} 涉及: ${summarizeTaskItems(matches, 2, 72)}。`,
537
+ title: copy.architecture[definition.copyKey]?.[0] ?? definition.title,
538
+ done: `${copy.architecture[definition.copyKey]?.[1] ?? definition.done} ${copy.involvedPrefix}: ${summarizeTaskItems(matches, 2, 72, locale)}。`,
331
539
  phase: 'architecture',
332
540
  }));
333
541
  }
334
542
 
335
- function buildRequirementImplementationTasks(snapshot) {
543
+ function buildRequirementImplementationTasks(snapshot, locale = resolveSnapshotLocale(snapshot)) {
544
+ const copy = copyForLocale(locale);
336
545
  const sections = snapshot.sections ?? {};
337
546
  const sourceItems = arrayValue(sections.requirements?.functional);
338
547
  const fallbackScopeItems = arrayValue(sections.scope?.inScope);
@@ -349,8 +558,8 @@ function buildRequirementImplementationTasks(snapshot) {
349
558
  tasks.push({
350
559
  key,
351
560
  type: 'implementation',
352
- title: cleanImplementationTitle(item),
353
- done: buildDoneText('已完成:', item),
561
+ title: cleanImplementationTitle(item, locale),
562
+ done: buildDoneText(copy.donePrefix, item),
354
563
  phase: 'implementation',
355
564
  });
356
565
  }
@@ -358,51 +567,54 @@ function buildRequirementImplementationTasks(snapshot) {
358
567
  return tasks;
359
568
  }
360
569
 
361
- function buildFlowIntegrationTasks(snapshot) {
570
+ function buildFlowIntegrationTasks(snapshot, locale = resolveSnapshotLocale(snapshot)) {
571
+ const copy = copyForLocale(locale);
362
572
  const sections = snapshot.sections ?? {};
363
573
  const flows = arrayValue(sections.scenarios?.primaryFlows);
364
574
  if (flows.length === 0) {
365
575
  return [];
366
576
  }
367
577
  return [{
368
- key: `integration:${summarizeTaskItems(flows, 2, 48)}`,
578
+ key: `integration:${summarizeTaskItems(flows, 2, 48, locale)}`,
369
579
  type: 'implementation',
370
- title: `打通主流程闭环:${summarizeTaskItems(flows, 2, 56)}`,
371
- done: `主流程关键节点已经打通,用户可以按预期从入口走到结果收尾。涉及: ${summarizeTaskItems(flows, 2, 72)}。`,
580
+ title: `${copy.flowIntegrationPrefix}${isChineseLocale(locale) ? ':' : ': '}${summarizeTaskItems(flows, 2, 56, locale)}`,
581
+ done: `${copy.flowIntegrationDone} ${copy.involvedPrefix}: ${summarizeTaskItems(flows, 2, 72, locale)}。`,
372
582
  phase: 'integration',
373
583
  }];
374
584
  }
375
585
 
376
- function buildAcceptanceVerificationTasks(snapshot) {
586
+ function buildAcceptanceVerificationTasks(snapshot, locale = resolveSnapshotLocale(snapshot)) {
587
+ const copy = copyForLocale(locale);
377
588
  const sections = snapshot.sections ?? {};
378
589
  return arrayValue(sections.goals?.acceptanceGoals).map((item) => ({
379
590
  key: `acceptance:${normalizeTaskSemanticKey(item)}`,
380
591
  type: 'verification',
381
- title: buildVerificationTitle(item),
382
- done: buildDoneText('已验证:', item),
592
+ title: buildVerificationTitle(item, locale),
593
+ done: buildDoneText(copy.verifiedPrefix, item),
383
594
  phase: 'verification',
384
595
  }));
385
596
  }
386
597
 
387
- function buildNonFunctionalVerificationTasks(snapshot) {
598
+ function buildNonFunctionalVerificationTasks(snapshot, locale = resolveSnapshotLocale(snapshot)) {
599
+ const copy = copyForLocale(locale);
388
600
  const sections = snapshot.sections ?? {};
389
601
  const nonFunctionalChunks = chunkItems(arrayValue(sections.requirements?.nonFunctional), 2).map((items, index) => ({
390
- key: `non-functional:${index}:${summarizeTaskItems(items, 2, 36)}`,
602
+ key: `non-functional:${index}:${summarizeTaskItems(items, 2, 36, locale)}`,
391
603
  type: 'verification',
392
- title: `回归非功能约束:${summarizeTaskItems(items, 2, 56)}`,
393
- done: `非功能约束已经回归确认。涉及: ${summarizeTaskItems(items, 2, 72)}。`,
604
+ title: `${copy.nonFunctionalPrefix}${isChineseLocale(locale) ? ':' : ': '}${summarizeTaskItems(items, 2, 56, locale)}`,
605
+ done: `${copy.nonFunctionalDone} ${copy.involvedPrefix}: ${summarizeTaskItems(items, 2, 72, locale)}。`,
394
606
  phase: 'verification',
395
607
  }));
396
608
  const edgeAndFailure = [
397
- ...arrayValue(sections.scenarios?.edgeCases).map((item) => `边界情况:${trimTaskText(item)}`),
398
- ...arrayValue(sections.scenarios?.failureModes).map((item) => `失败处理:${trimTaskText(item)}`),
609
+ ...arrayValue(sections.scenarios?.edgeCases).map((item) => `${copy.edgePrefix}${isChineseLocale(locale) ? ':' : ': '}${trimTaskText(item)}`),
610
+ ...arrayValue(sections.scenarios?.failureModes).map((item) => `${copy.failurePrefix}${isChineseLocale(locale) ? ':' : ': '}${trimTaskText(item)}`),
399
611
  ];
400
612
  const edgeTasks = edgeAndFailure.length > 0
401
613
  ? [{
402
- key: `edge:${summarizeTaskItems(edgeAndFailure, 2, 36)}`,
614
+ key: `edge:${summarizeTaskItems(edgeAndFailure, 2, 36, locale)}`,
403
615
  type: 'verification',
404
- title: `回归边界条件与失败处理:${summarizeTaskItems(edgeAndFailure, 2, 56)}`,
405
- done: `边界条件与失败处理已经回归确认。涉及: ${summarizeTaskItems(edgeAndFailure, 2, 72)}。`,
616
+ title: `${copy.edgeFailurePrefix}${isChineseLocale(locale) ? ':' : ': '}${summarizeTaskItems(edgeAndFailure, 2, 56, locale)}`,
617
+ done: `${copy.edgeFailureDone} ${copy.involvedPrefix}: ${summarizeTaskItems(edgeAndFailure, 2, 72, locale)}。`,
406
618
  phase: 'verification',
407
619
  }]
408
620
  : [];
@@ -410,41 +622,43 @@ function buildNonFunctionalVerificationTasks(snapshot) {
410
622
  }
411
623
 
412
624
  function buildTaskItems({ changeId, snapshot, capability }) {
625
+ const locale = resolveSnapshotLocale(snapshot);
626
+ const copy = copyForLocale(locale);
413
627
  const candidates = [
414
628
  {
415
629
  key: 'governance:spec-review',
416
630
  type: 'governance',
417
- title: '评审生成的 spec 覆盖',
418
- done: `生成的 ${capability} spec 符合 PRD 意图`,
631
+ title: copy.specReviewTitle,
632
+ done: copy.specReviewDone(capability),
419
633
  verify: `openprd change . --validate --change ${changeId}`,
420
634
  phase: 'governance-start',
421
635
  },
422
- ...inferArchitectureTasks(snapshot),
423
- ...buildRequirementImplementationTasks(snapshot),
424
- ...buildFlowIntegrationTasks(snapshot),
425
- ...buildAcceptanceVerificationTasks(snapshot),
426
- ...buildNonFunctionalVerificationTasks(snapshot),
636
+ ...inferArchitectureTasks(snapshot, locale),
637
+ ...buildRequirementImplementationTasks(snapshot, locale),
638
+ ...buildFlowIntegrationTasks(snapshot, locale),
639
+ ...buildAcceptanceVerificationTasks(snapshot, locale),
640
+ ...buildNonFunctionalVerificationTasks(snapshot, locale),
427
641
  ...(needsBusinessGuardrails(snapshot)
428
642
  ? [
429
643
  {
430
644
  key: 'verification:guardrail-usage',
431
645
  type: 'verification',
432
- title: '验证成本与额度护栏',
433
- done: '已验证免费、试用或低权限用户不能绕过额度、并发、频率或总量限制',
646
+ title: copy.guardrailUsageTitle,
647
+ done: copy.guardrailUsageDone,
434
648
  phase: 'verification',
435
649
  },
436
650
  {
437
651
  key: 'verification:guardrail-abuse',
438
652
  type: 'verification',
439
- title: '验证滥用与越权路径',
440
- done: '已覆盖重复请求、并发请求、越权身份和异常恢复等负向场景',
653
+ title: copy.guardrailAbuseTitle,
654
+ done: copy.guardrailAbuseDone,
441
655
  phase: 'verification',
442
656
  },
443
657
  {
444
658
  key: 'verification:guardrail-monitoring',
445
659
  type: 'verification',
446
- title: '验证成本监控、报警和止损',
447
- done: '已确认用量或成本信号、报警阈值和人工/自动止损动作可执行',
660
+ title: copy.guardrailMonitoringTitle,
661
+ done: copy.guardrailMonitoringDone,
448
662
  phase: 'verification',
449
663
  },
450
664
  ]
@@ -452,24 +666,24 @@ function buildTaskItems({ changeId, snapshot, capability }) {
452
666
  {
453
667
  key: 'documentation:docs-basic',
454
668
  type: 'documentation',
455
- title: '维护 docs/basic 项目基础文档',
456
- done: '已检查 docs/basic 是否缺失或因本次需求、流程、结构、依赖、产品行为变化而过期;若涉及后端、脚本、Agent 或工具链变更,已同步评估 CLI 与 API 接入面,并在 backend-structure.md 中记录事实或不适用原因;需要更新的基础文档已同步',
669
+ title: copy.docsTitle,
670
+ done: copy.docsDone,
457
671
  verify: 'openprd standards . --verify',
458
672
  phase: 'documentation',
459
673
  },
460
674
  {
461
675
  key: 'documentation:manuals',
462
676
  type: 'documentation',
463
- title: '更新文件说明书和文件夹 README',
464
- done: '本次变更涉及的文件说明书和文件夹 README 已检查;缺失的已补齐,过期的已更新',
677
+ title: copy.manualsTitle,
678
+ done: copy.manualsDone,
465
679
  verify: 'openprd standards . --verify',
466
680
  phase: 'documentation',
467
681
  },
468
682
  {
469
683
  key: 'governance:final-validate',
470
684
  type: 'governance',
471
- title: '运行 OpenPrd spec 校验',
472
- done: '生成的 change 通过 OpenPrd 校验',
685
+ title: copy.finalValidateTitle,
686
+ done: copy.finalValidateDone,
473
687
  verify: `openprd change . --validate --change ${changeId}`,
474
688
  phase: 'governance-end',
475
689
  },
@@ -562,7 +776,8 @@ function taskFileName(index) {
562
776
  return index === 0 ? 'tasks.md' : `tasks-${String(index + 1).padStart(3, '0')}.md`;
563
777
  }
564
778
 
565
- function renderTaskFiles(tasks, maxItemsPerFile) {
779
+ function renderTaskFiles(tasks, maxItemsPerFile, locale = 'zh-CN') {
780
+ const copy = copyForLocale(locale);
566
781
  const chunks = [];
567
782
  for (let index = 0; index < tasks.length; index += maxItemsPerFile) {
568
783
  chunks.push(tasks.slice(index, index + maxItemsPerFile));
@@ -571,7 +786,7 @@ function renderTaskFiles(tasks, maxItemsPerFile) {
571
786
  return chunks.map((chunk, chunkIndex) => {
572
787
  const nextFileName = chunkIndex < chunks.length - 1 ? taskFileName(chunkIndex + 1) : null;
573
788
  const lines = [
574
- '# 任务',
789
+ copy.tasksTitle,
575
790
  '',
576
791
  ];
577
792
 
@@ -593,7 +808,7 @@ function renderTaskFiles(tasks, maxItemsPerFile) {
593
808
  }
594
809
 
595
810
  if (nextFileName) {
596
- lines.push(`- [ ] Continue with \`${nextFileName}\` after completing this file.`);
811
+ lines.push(`- [ ] ${copy.continueWith(nextFileName)}`);
597
812
  lines.push('');
598
813
  }
599
814
 
@@ -644,6 +859,7 @@ export async function generateOpenSpecChangeWorkspace(projectRoot, options = {})
644
859
  }
645
860
 
646
861
  const changeId = slugify(options.change ?? snapshot.title, 'openprd-generated-change');
862
+ const locale = resolveSnapshotLocale(snapshot);
647
863
  const capability = resolveCapability(snapshot);
648
864
  const changeDir = cjoin(openPrdChangeRoot(projectRoot), changeId);
649
865
  const files = [
@@ -669,7 +885,7 @@ export async function generateOpenSpecChangeWorkspace(projectRoot, options = {})
669
885
  },
670
886
  ];
671
887
  const tasks = buildTaskItems({ changeId, snapshot, capability });
672
- const taskFiles = renderTaskFiles(tasks, await readTaskMax(projectRoot));
888
+ const taskFiles = renderTaskFiles(tasks, await readTaskMax(projectRoot), locale);
673
889
 
674
890
  for (const file of taskFiles) {
675
891
  files.push({