@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.
- package/.openprd/changes/adaptive-project-framing/.openprd.yaml +2 -0
- package/.openprd/changes/adaptive-project-framing/design.md +47 -0
- package/.openprd/changes/adaptive-project-framing/proposal.md +35 -0
- package/.openprd/changes/adaptive-project-framing/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/adaptive-project-framing/task-events.jsonl +20 -0
- package/.openprd/changes/adaptive-project-framing/tasks.md +340 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/.openprd.yaml +2 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/design.md +34 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/proposal.md +23 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/task-events.jsonl +14 -0
- package/.openprd/changes/docs-anti-pollution-and-design-direction-review/tasks.md +238 -0
- package/.openprd/changes/loop-isolated-worktree-commit/.openprd.yaml +2 -0
- package/.openprd/changes/loop-isolated-worktree-commit/design.md +53 -0
- package/.openprd/changes/loop-isolated-worktree-commit/proposal.md +38 -0
- package/.openprd/changes/loop-isolated-worktree-commit/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/loop-isolated-worktree-commit/task-events.jsonl +22 -0
- package/.openprd/changes/loop-isolated-worktree-commit/tasks.md +357 -0
- package/.openprd/changes/openprd/.openprd.yaml +2 -0
- package/.openprd/changes/openprd/design.md +46 -0
- package/.openprd/changes/openprd/proposal.md +36 -0
- package/.openprd/changes/openprd/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/openprd/task-events.jsonl +23 -0
- package/.openprd/changes/openprd/tasks.md +242 -0
- package/.openprd/changes/openprd-generated-change/.openprd.yaml +2 -0
- package/.openprd/changes/openprd-generated-change/design.md +50 -0
- package/.openprd/changes/openprd-generated-change/proposal.md +37 -0
- package/.openprd/changes/openprd-generated-change/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/openprd-generated-change/task-events.jsonl +21 -0
- package/.openprd/changes/openprd-generated-change/tasks.md +357 -0
- package/.openprd/changes/pm-human-in-the-loop/.openprd.yaml +2 -0
- package/.openprd/changes/pm-human-in-the-loop/design.md +38 -0
- package/.openprd/changes/pm-human-in-the-loop/proposal.md +29 -0
- package/.openprd/changes/pm-human-in-the-loop/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/pm-human-in-the-loop/task-events.jsonl +18 -0
- package/.openprd/changes/pm-human-in-the-loop/tasks.md +306 -0
- package/.openprd/changes/test-strategy-router/.openprd.yaml +2 -0
- package/.openprd/changes/test-strategy-router/design.md +65 -0
- package/.openprd/changes/test-strategy-router/proposal.md +45 -0
- package/.openprd/changes/test-strategy-router/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/test-strategy-router/task-events.jsonl +1 -0
- package/.openprd/changes/test-strategy-router/tasks.md +132 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/.openprd.yaml +2 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/design.md +28 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/proposal.md +23 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/task-events.jsonl +11 -0
- package/.openprd/changes/verify-boundaries-and-historical-refresh/tasks.md +66 -0
- package/.openprd/changes/vibe-coding-dev-check/.openprd.yaml +2 -0
- package/.openprd/changes/vibe-coding-dev-check/design.md +37 -0
- package/.openprd/changes/vibe-coding-dev-check/proposal.md +28 -0
- package/.openprd/changes/vibe-coding-dev-check/specs/agent-requirements/spec.md +16 -0
- package/.openprd/changes/vibe-coding-dev-check/task-events.jsonl +18 -0
- package/.openprd/changes/vibe-coding-dev-check/tasks.md +306 -0
- package/.openprd/design/README.md +11 -6
- package/.openprd/design/README_EN.md +4 -1
- package/.openprd/design/active/asset-spec.md +6 -0
- package/.openprd/design/active/direction-plan.md +5 -5
- package/.openprd/design/active/selected-direction.md +2 -0
- package/.openprd/design/anti-slop.md +26 -1
- package/.openprd/design/assets/surface-presets.md +6 -0
- package/.openprd/design/checklists/ui-quality-gate.md +7 -0
- package/.openprd/design/lenses/frontend-lenses.md +28 -0
- package/.openprd/design/recipes/content-experience.md +8 -1
- package/.openprd/design/recipes/operational-tool.md +7 -0
- package/.openprd/design/recipes/product-launch.md +8 -1
- package/.openprd/design/templates/README.md +6 -3
- package/.openprd/design/templates/README_EN.md +6 -3
- package/.openprd/design/templates/ops-dashboard.html +8 -1
- package/.openprd/design/templates/product-launch.html +2 -1
- package/.openprd/design/themes/theme-catalog.json +23 -5
- package/.openprd/engagements/active/prd.md +137 -112
- package/.openprd/standards/config.json +7 -1
- package/AGENTS.md +13 -3
- package/README.md +127 -14
- package/README_EN.md +156 -13
- package/package.json +1 -1
- package/scripts/dev-check-wrapup-copy.mjs +46 -21
- package/skills/openprd-diagram-review/SKILL.md +27 -2
- package/skills/openprd-diagram-review/references/explanation-svg-patterns.md +131 -0
- package/skills/openprd-discovery-loop/SKILL.md +2 -2
- package/skills/openprd-frontend-design/SKILL.md +44 -20
- package/skills/openprd-frontend-design/references/design-asset-contract.md +6 -0
- package/skills/openprd-frontend-design/references/direction-engine.md +29 -4
- package/skills/openprd-harness/SKILL.md +22 -16
- package/skills/openprd-quality/SKILL.md +10 -8
- package/skills/openprd-requirement-intake/SKILL.md +2 -0
- package/skills/openprd-router/SKILL.md +2 -1
- package/skills/openprd-shared/SKILL.md +26 -16
- package/skills/openprd-standards/SKILL.md +4 -2
- package/src/agent-canonical-content.js +810 -0
- package/src/agent-integration.js +258 -777
- package/src/canvas-app-client-app.js +838 -0
- package/src/canvas-app-client-helpers.js +1138 -0
- package/src/canvas-app-styles.js +898 -0
- package/src/canvas-app.html.js +135 -0
- package/src/canvas-binding.js +414 -0
- package/src/canvas-codex-relay.js +430 -0
- package/src/canvas-i18n.js +282 -0
- package/src/canvas-scene.js +377 -0
- package/src/canvas-session-store.js +148 -0
- package/src/canvas-workspace.js +1264 -0
- package/src/cli/args.js +72 -4
- package/src/cli/basic-print.js +3 -0
- package/src/cli/canvas-print.js +106 -0
- package/src/cli/doctor-print.js +20 -0
- package/src/cli/print.js +2 -0
- package/src/cli/quality-commands.js +222 -0
- package/src/cli/quality-print.js +28 -2
- package/src/cli/run-print.js +5 -0
- package/src/cli/workflow-print.js +3 -0
- package/src/codex-app-server-relay.js +251 -0
- package/src/codex-hook-runner-template.mjs +978 -58
- package/src/design-starter-support.js +2 -2
- package/src/design-starter.js +39 -3
- package/src/dev-standards.js +125 -10
- package/src/diagram-core.js +6 -20
- package/src/discovery.js +2 -8
- package/src/docs-compact.js +125 -0
- package/src/execution-strategy.js +1 -1
- package/src/fleet.js +9 -3
- package/src/guide.js +227 -0
- package/src/html-artifacts.js +428 -7
- package/src/knowledge.js +45 -11
- package/src/language-policy.js +63 -3
- package/src/learning-review.js +1 -1
- package/src/loop.js +1 -1
- package/src/openprd.js +68 -131
- package/src/openspec/change-validate.js +2 -2
- package/src/openspec/constants.js +6 -7
- package/src/openspec/execute.js +2 -2
- package/src/openspec/generate.js +318 -102
- package/src/openspec/migration.js +168 -0
- package/src/openspec/paths.js +1 -22
- package/src/prd-core.js +1 -1
- package/src/quality-visual-review.js +257 -14
- package/src/quality.js +46 -8
- package/src/review-presentation.js +60 -0
- package/src/run-harness.js +41 -3
- package/src/session-evidence.js +363 -0
- package/src/standards.js +64 -0
- package/src/test-strategy.js +7 -4
- package/src/visual-compare-centering.js +622 -0
- package/src/visual-compare-core.js +1123 -0
- package/src/visual-compare.js +620 -698
- package/src/workspace-core.js +94 -4
- package/src/workspace-workflow.js +46 -1
package/src/openspec/generate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
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
|
|
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
|
|
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
|
-
], [
|
|
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) =>
|
|
218
|
-
...arrayValue(sections.businessGuardrails?.costDrivers).map((item) =>
|
|
219
|
-
...arrayValue(sections.businessGuardrails?.usageLimits).map((item) =>
|
|
220
|
-
...arrayValue(sections.constraints?.dependencies).map((item) =>
|
|
221
|
-
...arrayValue(sections.risks?.risks).map((item) =>
|
|
222
|
-
], [
|
|
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,
|
|
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) =>
|
|
256
|
-
...arrayValue(sections.businessGuardrails?.usageLimits).map((item) =>
|
|
257
|
-
...arrayValue(sections.businessGuardrails?.abusePrevention).map((item) =>
|
|
258
|
-
...arrayValue(sections.businessGuardrails?.monitoringSignals).map((item) =>
|
|
259
|
-
...arrayValue(sections.businessGuardrails?.alertThresholds).map((item) =>
|
|
260
|
-
...arrayValue(sections.businessGuardrails?.stopLossActions).map((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) =>
|
|
267
|
-
...arrayValue(sections.risks?.risks).map((item) =>
|
|
268
|
-
...arrayValue(sections.risks?.openQuestions).map((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
|
-
|
|
278
|
-
|
|
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 =
|
|
283
|
-
const acceptance =
|
|
284
|
-
const edgeCase =
|
|
285
|
-
const failureMode =
|
|
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
|
-
|
|
291
|
-
|
|
497
|
+
`${copy.requirementPrefix}${headingSeparator}${title}`,
|
|
498
|
+
preferUserLanguage(sections.problem?.problemStatement, locale, { zh: copy.generatedBehavior, en: copy.generatedBehavior }),
|
|
292
499
|
'',
|
|
293
|
-
|
|
294
|
-
`-
|
|
295
|
-
`-
|
|
500
|
+
copy.scenarioSuccess,
|
|
501
|
+
`- **${copy.when}** ${flow}`,
|
|
502
|
+
`- **${copy.then}** ${acceptance}`,
|
|
296
503
|
'',
|
|
297
|
-
|
|
298
|
-
`-
|
|
299
|
-
|
|
504
|
+
copy.scenarioEdge,
|
|
505
|
+
`- **${copy.when}** ${edgeCase}`,
|
|
506
|
+
`- **${copy.then}** ${copy.edgeThen}`,
|
|
300
507
|
'',
|
|
301
|
-
|
|
302
|
-
`-
|
|
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}
|
|
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(
|
|
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:
|
|
371
|
-
done:
|
|
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(
|
|
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:
|
|
393
|
-
done:
|
|
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) =>
|
|
398
|
-
...arrayValue(sections.scenarios?.failureModes).map((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:
|
|
405
|
-
done:
|
|
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:
|
|
418
|
-
done:
|
|
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:
|
|
456
|
-
done:
|
|
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:
|
|
464
|
-
done:
|
|
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:
|
|
472
|
-
done:
|
|
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(`- [ ]
|
|
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({
|