okstra 0.149.0 → 0.151.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/docs/architecture.md +1 -1
  2. package/docs/cli.md +2 -2
  3. package/docs/project-structure-overview.md +1 -1
  4. package/package.json +3 -2
  5. package/runtime/BUILD.json +2 -2
  6. package/runtime/agents/workers/report-writer-worker.md +8 -0
  7. package/runtime/agents/workers/translator-worker.md +67 -0
  8. package/runtime/bin/okstra-render-final-report.py +0 -11
  9. package/runtime/bin/okstra-report-translate.py +191 -0
  10. package/runtime/prompts/lead/adapters/claude-code.md +1 -1
  11. package/runtime/prompts/lead/okstra-lead-contract.md +1 -0
  12. package/runtime/prompts/lead/plan-body-verification.md +1 -1
  13. package/runtime/prompts/lead/report-writer.md +16 -15
  14. package/runtime/prompts/lead/team-contract.md +2 -2
  15. package/runtime/prompts/wizard/prompts.ko.json +17 -1
  16. package/runtime/python/okstra_ctl/analysis_inputs.py +24 -9
  17. package/runtime/python/okstra_ctl/analysis_packet.py +23 -1
  18. package/runtime/python/okstra_ctl/clarification_items.py +241 -44
  19. package/runtime/python/okstra_ctl/codex_dispatch.py +2 -1
  20. package/runtime/python/okstra_ctl/convergence.py +15 -1
  21. package/runtime/python/okstra_ctl/dispatch_core.py +2 -2
  22. package/runtime/python/okstra_ctl/dispatch_state.py +12 -1
  23. package/runtime/python/okstra_ctl/final_report_paths.py +22 -1
  24. package/runtime/python/okstra_ctl/i18n.py +12 -7
  25. package/runtime/python/okstra_ctl/render_final_report.py +18 -17
  26. package/runtime/python/okstra_ctl/report_finalize.py +18 -0
  27. package/runtime/python/okstra_ctl/report_html/filters.py +15 -77
  28. package/runtime/python/okstra_ctl/report_html/render.py +44 -2
  29. package/runtime/python/okstra_ctl/report_translation.py +469 -0
  30. package/runtime/python/okstra_ctl/report_views.py +23 -9
  31. package/runtime/python/okstra_ctl/run.py +1 -1
  32. package/runtime/python/okstra_ctl/user_response.py +11 -6
  33. package/runtime/python/okstra_ctl/wizard.py +100 -25
  34. package/runtime/python/okstra_ctl/worker_liveness.py +130 -36
  35. package/runtime/templates/reports/html/base.template.html +12 -12
  36. package/runtime/templates/reports/html/i18n/en.json +395 -0
  37. package/runtime/templates/reports/html/i18n/ko.json +395 -0
  38. package/runtime/templates/reports/html/macros/forms.html +16 -16
  39. package/runtime/templates/reports/html/macros/visualizations.html +2 -2
  40. package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +17 -17
  41. package/runtime/templates/reports/html/tasks/error-analysis.template.html +12 -12
  42. package/runtime/templates/reports/html/tasks/feature-analysis.template.html +16 -16
  43. package/runtime/templates/reports/html/tasks/final-verification.template.html +12 -12
  44. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +37 -37
  45. package/runtime/templates/reports/html/tasks/implementation.template.html +18 -18
  46. package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +7 -7
  47. package/runtime/templates/reports/html/tasks/project-analysis.template.html +29 -29
  48. package/runtime/templates/reports/html/tasks/release-handoff.template.html +13 -13
  49. package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +14 -14
  50. package/runtime/templates/reports/report.js +8 -5
  51. package/runtime/validators/validate-report-views.py +1 -1
  52. package/runtime/validators/validate-run.py +59 -31
  53. package/src/cli-registry.mjs +11 -0
  54. package/src/commands/inspect/worker-liveness.mjs +9 -7
  55. package/src/commands/report/translate.mjs +31 -0
  56. package/src/lib/helper-scripts.mjs +1 -0
  57. package/runtime/templates/reports/i18n/ko.json +0 -273
@@ -0,0 +1,395 @@
1
+ {
2
+ "_meta": {
3
+ "lang": "ko",
4
+ "note": "사람이 읽는 HTML 리포트의 고정 문자열. 키는 en.json 과 공유하며, 한쪽에만 있는 키는 계약 테스트가 잡는다. okstra 가 영어로 쓰는 용어(stage, worktree, commit, lint 등)는 그대로 둔다."
5
+ },
6
+ "enum": {
7
+ "coverage": {
8
+ "covered": "깨뜨리면 테스트가 실패",
9
+ "partial": "일부만 테스트됨",
10
+ "gap": "테스트가 없음",
11
+ "risk": "통과하지만 확인하는 게 없음"
12
+ },
13
+ "effort": {
14
+ "S": "작음",
15
+ "M": "보통",
16
+ "L": "큼",
17
+ "XL": "매우 큼"
18
+ },
19
+ "versionSource": {
20
+ "manifest-range": "매니페스트 범위",
21
+ "lockfile-pinned": "lockfile 고정",
22
+ "container-image": "컨테이너 이미지",
23
+ "runtime-config": "런타임 설정",
24
+ "declared-doc": "문서에 선언됨"
25
+ },
26
+ "interfaceKind": {
27
+ "port": "포트",
28
+ "adapter": "어댑터",
29
+ "service-api": "서비스 API",
30
+ "module-export": "모듈 export",
31
+ "event": "이벤트"
32
+ },
33
+ "toolKind": {
34
+ "packageManager": "패키지 매니저",
35
+ "build": "빌드",
36
+ "test": "테스트",
37
+ "lint": "lint",
38
+ "typecheck": "타입 체크",
39
+ "format": "포맷",
40
+ "ci": "CI",
41
+ "container": "컨테이너"
42
+ },
43
+ "lens": {
44
+ "candidate-found": "후보 찾음",
45
+ "no-candidate": "없음"
46
+ },
47
+ "rejectionSource": {
48
+ "brief-desired-outcome": "브리프 · 원하는 결과",
49
+ "brief-out-of-scope": "브리프 · 범위 밖",
50
+ "brief-source-material": "브리프 · 근거 자료",
51
+ "clarification-raised": "질문으로 올림",
52
+ "absent-not-material": "없음 · 판단이 달라지지 않음"
53
+ },
54
+ "termSource": {
55
+ "glossary": "용어집",
56
+ "decision-record": "결정 기록",
57
+ "brief": "브리프",
58
+ "clarification": "사용자 답변"
59
+ },
60
+ "handoffMode": {
61
+ "whole-task": "태스크 전체",
62
+ "stage-group": "선택한 stage"
63
+ }
64
+ },
65
+ "enumHint": {
66
+ "coverage": {
67
+ "covered": "이 영역을 깨뜨리면 테스트가 실패한다.",
68
+ "partial": "테스트가 있긴 하지만, 이 영역의 일부는 어떤 테스트도 지나가지 않는다.",
69
+ "gap": "이 영역을 지나가는 테스트가 하나도 없다.",
70
+ "risk": "테스트가 돌고 통과하지만, 건너뛰거나 아무것도 단언하지 않는다. 그래서 통과했다는 사실이 이 영역이 동작한다는 근거가 되지 못한다. 스위트가 성공이라고 보고하는 만큼 '테스트가 없음'보다 위험하다."
71
+ }
72
+ },
73
+ "base": {
74
+ "skip-to-report-content": "리포트 본문으로 건너뛰기",
75
+ "task": "태스크",
76
+ "task-key": "태스크 키",
77
+ "written": "작성",
78
+ "elapsed": "소요 시간",
79
+ "evidence-ledger": "근거 대장",
80
+ "source-confidence": "출처 · 확신도",
81
+ "export-my-answers": "내 답변 내보내기",
82
+ "copy": "복사",
83
+ "dismiss": "닫기",
84
+ "export-downloads": "내보내면 다음 파일이 받아집니다",
85
+ "drop-that-file-into": ". 그 파일을 여기에 두면",
86
+ "and-the-next-run-picks-your-answers-up-on-it": "다음 run 이 알아서 답변을 읽어 갑니다."
87
+ },
88
+ "macros": {
89
+ "forms": {
90
+ "review-this-analysis": "이 분석 검토하기",
91
+ "verdict": "판정",
92
+ "accept": "수용",
93
+ "request-changes": "수정 요청",
94
+ "reject": "반려",
95
+ "affected-ids": "해당 ID",
96
+ "why": "이유",
97
+ "further-evidence": "추가 근거",
98
+ "scope-change-requested": "범위 변경 요청",
99
+ "approve-this-plan": "이 계획 승인하기",
100
+ "implementation-option": "구현 방식 선택",
101
+ "i-approve-this-plan": "이 계획을 승인합니다.",
102
+ "approval-is-held": "승인이 보류 중입니다:",
103
+ "the-code-alone-could-not-settle-these-fill-i": "코드만으로는 판단할 수 없는 항목입니다. 답을 채운 뒤 페이지 맨 아래의",
104
+ "export-my-answers": "내 답변 내보내기",
105
+ "at-the-foot-of-the-page": "버튼을 누르세요.",
106
+ "answer-as": "답변 형식",
107
+ "questions-waiting-on-you": "답변을 기다리는 질문"
108
+ },
109
+ "visualizations": {
110
+ "component": "구성 요소",
111
+ "what-it-does": "하는 일",
112
+ "paths": "경로",
113
+ "from": "출발",
114
+ "to": "도착",
115
+ "relation": "관계"
116
+ }
117
+ },
118
+ "tasks": {
119
+ "change-impact-analysis": {
120
+ "change-overview": "변경 개요",
121
+ "what-the-change-touches-and-where-it-stops": "변경이 닿는 곳과 멈추는 곳",
122
+ "what-it-touches": "닿는 곳",
123
+ "where-the-change-stops": "변경이 멈추는 곳",
124
+ "blast-radius": "영향 범위",
125
+ "tests-operations-compatibility": "테스트 · 운영 · 호환성",
126
+ "compatibility": "호환성",
127
+ "migration": "마이그레이션",
128
+ "rollback": "롤백",
129
+ "security-and-performance": "보안과 성능",
130
+ "plan-readiness": "계획 준비 상태",
131
+ "behaviour-that-must-not-change": "바뀌면 안 되는 동작",
132
+ "body": "내용",
133
+ "evidence": "근거",
134
+ "no-behaviour-is-pinned": "고정된 동작이 없습니다.",
135
+ "what-this-does-to-the-tests": "테스트에 미치는 영향",
136
+ "scope": "범위",
137
+ "action-needed": "필요한 조치",
138
+ "tests-targeted": "대상 테스트",
139
+ "no-test-is-affected": "영향받는 테스트가 없습니다.",
140
+ "what-this-does-to-operations": "운영에 미치는 영향",
141
+ "area": "영역",
142
+ "impact": "영향",
143
+ "there-is-no-operational-impact": "운영에 미치는 영향이 없습니다."
144
+ },
145
+ "error-analysis": {
146
+ "what-you-saw": "관측된 증상",
147
+ "reproduction": "재현",
148
+ "cause-candidates": "원인 후보",
149
+ "no-cause-candidate-has-been-established-yet": "아직 확정된 원인 후보가 없습니다.",
150
+ "what-cannot-be-settled-yet": "아직 판단할 수 없는 것",
151
+ "confidence-now": "현재 확신도:",
152
+ "disproof-attempted": "시도한 반증:",
153
+ "how-to-disprove-this": "반증하는 방법:",
154
+ "the-one-diagnostic-to-run-next": "다음에 딱 하나 돌려볼 진단",
155
+ "signal-to-check": "확인할 신호:",
156
+ "signal-that-rejects-it": "기각하는 신호:",
157
+ "next-step": "다음 단계",
158
+ "recommended-task": "권장 태스크",
159
+ "the-cause-this-rests-on": "이 판단이 근거하는 원인"
160
+ },
161
+ "feature-analysis": {
162
+ "feature-overview": "기능 개요",
163
+ "subject": "대상:",
164
+ "how-a-user-moves-through-it": "사용자가 지나가는 흐름",
165
+ "rules-and-state-changes": "규칙과 상태 변화",
166
+ "domain-rules": "도메인 규칙",
167
+ "state-change": "상태 변화",
168
+ "external-interactions": "외부 연동",
169
+ "tests-and-gaps": "테스트와 빈 곳",
170
+ "precondition": "선행 조건",
171
+ "body": "내용",
172
+ "evidence": "근거",
173
+ "nothing-was-recorded": "기록된 것이 없습니다.",
174
+ "state-once-it-is-done": "끝난 뒤의 상태",
175
+ "permissions-and-flags": "권한과 플래그",
176
+ "non-functional-concerns": "비기능 요건"
177
+ },
178
+ "final-verification": {
179
+ "verification-verdict": "검증 판정",
180
+ "result-per-requirement": "요구사항별 결과",
181
+ "what-blocks-acceptance": "수용을 막는 것",
182
+ "nothing-is-holding-acceptance": "수용을 막는 것이 없습니다.",
183
+ "residual-risk": "남은 위험",
184
+ "owner": "담당:",
185
+ "escalation-condition": "에스컬레이션 조건:",
186
+ "no-residual-risk-was-recorded": "기록된 잔여 위험이 없습니다.",
187
+ "manual-user-test-results": "수동 사용자 테스트 결과",
188
+ "what-was-verified": "검증한 내용",
189
+ "implementation-report": "구현 리포트",
190
+ "worktree": "worktree",
191
+ "base-commit": "기준 commit",
192
+ "diff-summary": "diff 요약",
193
+ "uncommitted-state": "커밋되지 않은 상태",
194
+ "per-stage-reports": "stage 별 리포트",
195
+ "release-path": "릴리스 경로",
196
+ "the-current-verdict-does-not-allow-release-h": "현재 판정으로는 release-handoff 를 시작할 수 없습니다. 차단 항목이나 조건을 먼저 해소하세요."
197
+ },
198
+ "implementation-planning": {
199
+ "what-the-plan-is-for": "이 계획이 하려는 것",
200
+ "implementation-options-compared": "구현 방식 비교",
201
+ "interface": "인터페이스:",
202
+ "blast-radius": "영향 범위:",
203
+ "option": "방식",
204
+ "complexity": "복잡도",
205
+ "risk": "위험",
206
+ "reversibility": "되돌리기 쉬움",
207
+ "test-cost": "테스트 비용",
208
+ "rollout-cost": "배포 비용",
209
+ "recommended": "권장",
210
+ "options-not-taken": "선택하지 않은 방식:",
211
+ "stage-map": "stage 구성",
212
+ "exit-contract": "완료 조건:",
213
+ "verification": "검증:",
214
+ "design-preparation": "설계 준비",
215
+ "design-item": "설계 항목",
216
+ "what-it-needs": "필요한 것",
217
+ "open-questions": "미결 질문",
218
+ "where-implementations-diverge": "구현이 갈리는 지점",
219
+ "action": "조치",
220
+ "implementations": "구현체",
221
+ "extraction-call": "공통화 판단",
222
+ "evidence": "근거",
223
+ "step-by-step-execution": "단계별 실행",
224
+ "files": "파일",
225
+ "command-test": "명령 / 테스트",
226
+ "expected": "기대 결과",
227
+ "verification-and-rollback": "검증과 롤백",
228
+ "validation-checklist": "검증 체크리스트",
229
+ "check": "확인 항목",
230
+ "command": "명령",
231
+ "how-each-requirement-gets-met": "요구사항별 충족 방법",
232
+ "requirement": "요구사항",
233
+ "body": "내용",
234
+ "covered-by": "담당하는 곳",
235
+ "cross-project-dependencies": "프로젝트 간 의존성",
236
+ "dependency": "의존 대상",
237
+ "work-needed": "필요한 작업",
238
+ "signal-to-check": "확인할 신호",
239
+ "how-to-start": "시작하는 방법",
240
+ "dependency-and-migration-risk": "의존성·마이그레이션 위험",
241
+ "item": "항목",
242
+ "impact": "영향",
243
+ "mitigation": "완화 방안",
244
+ "rollback-strategy": "롤백 전략",
245
+ "plan-body-verification": "계획 본문 검증",
246
+ "verdict": "판정",
247
+ "what-it-blocked": "무엇을 막았는지",
248
+ "plan-item": "계획 항목",
249
+ "subject": "제목",
250
+ "dissent-left-standing": "합의되지 않고 남은 이견",
251
+ "what-your-answers-overturned": "답변으로 뒤집힌 것",
252
+ "answer": "답변",
253
+ "statement-overturned": "뒤집힌 진술",
254
+ "replaced-with": "대체된 내용",
255
+ "no-answer-overturned-anything": "답변으로 뒤집힌 것이 없습니다.",
256
+ "scope-restated": "다시 정리한 범위",
257
+ "stages-verified-again": "다시 검증한 stage",
258
+ "stages-carried-over-as-they-were": "그대로 이어받은 stage",
259
+ "decision-record-draft": "결정 기록 초안",
260
+ "context": "배경",
261
+ "decision": "결정",
262
+ "result": "결과",
263
+ "there-is-no-decision-draft-to-record": "기록할 결정 초안이 없습니다.",
264
+ "what-was-deliberately-not-recorded": "일부러 기록하지 않은 것",
265
+ "topic": "주제",
266
+ "why": "이유",
267
+ "open-decisions-before-approval": "승인 전에 남은 결정",
268
+ "no-open-decision-is-holding-approval": "승인을 막는 미결 결정이 없습니다."
269
+ },
270
+ "implementation": {
271
+ "what-was-delivered": "무엇을 만들었나",
272
+ "what-changed": "무엇이 바뀌었나",
273
+ "requirement-coverage": "요구사항 충족",
274
+ "requirement": "요구사항",
275
+ "body": "내용",
276
+ "implemented-by": "구현한 곳",
277
+ "verification-result": "검증 결과",
278
+ "independent-check": "독립 검증:",
279
+ "what-is-left": "남은 것",
280
+ "nothing-was-changed-outside-the-plan": "계획 밖에서 바뀐 것이 없습니다.",
281
+ "commits-delivered": "만들어진 commit",
282
+ "there-are-no-commits": "commit 이 없습니다.",
283
+ "commit": "commit",
284
+ "subject": "제목",
285
+ "files": "파일",
286
+ "approved-plan": "승인된 계획",
287
+ "approval-evidence": "승인 근거",
288
+ "worktree": "worktree",
289
+ "can-this-be-rolled-back": "되돌릴 수 있나",
290
+ "rollback-command": "롤백 명령",
291
+ "how-to-verify": "확인 방법",
292
+ "result": "결과",
293
+ "no-rollback-check-was-recorded": "롤백 확인 기록이 없습니다.",
294
+ "stage-carry-over": "stage 인계",
295
+ "manual-user-test": "수동 사용자 테스트",
296
+ "environment": "환경:",
297
+ "expected": "기대 결과:",
298
+ "next-step": "다음 단계"
299
+ },
300
+ "improvement-discovery": {
301
+ "improvements-found": "찾은 개선점",
302
+ "lens": "관점",
303
+ "detail": "설명",
304
+ "evidence": "근거",
305
+ "ranked-candidates": "우선순위 후보",
306
+ "next-step": "다음 단계:",
307
+ "there-is-no-improvement-candidate-to-pick": "고를 개선 후보가 없습니다.",
308
+ "impact-and-effort": "효과와 공수",
309
+ "how-to-choose": "고르는 방법",
310
+ "selection-cap": "선택 상한:"
311
+ },
312
+ "project-analysis": {
313
+ "project-overview": "프로젝트 개요",
314
+ "architecture-and-dependencies": "구조와 의존 관계",
315
+ "infrastructure": "인프라",
316
+ "tech-stack": "기술 스택",
317
+ "language": "언어",
318
+ "version": "버전",
319
+ "version-source": "버전 출처",
320
+ "files": "파일",
321
+ "lines": "줄 수",
322
+ "framework": "프레임워크",
323
+ "role": "역할",
324
+ "evidence": "근거",
325
+ "tool": "도구",
326
+ "command": "명령",
327
+ "internal-interfaces": "내부 인터페이스",
328
+ "interface": "인터페이스",
329
+ "signature": "시그니처",
330
+ "consumers": "사용하는 곳",
331
+ "workflows": "동작 흐름",
332
+ "starts-with": "시작",
333
+ "result": "결과",
334
+ "order": "순서",
335
+ "action": "조치",
336
+ "component": "구성 요소",
337
+ "external-system": "외부 시스템",
338
+ "system-boundaries": "시스템 경계",
339
+ "paths-read": "개 경로를 읽음 ·",
340
+ "entry-point": "진입점",
341
+ "path": "경로",
342
+ "data-stores": "데이터 저장소",
343
+ "store": "저장소",
344
+ "reads": "읽기",
345
+ "writes": "쓰기",
346
+ "hotspots-to-look-at-first": "먼저 볼 곳",
347
+ "quality-and-test-coverage": "품질과 테스트 커버리지",
348
+ "area": "영역",
349
+ "detail": "설명",
350
+ "feature-map": "기능 지도",
351
+ "what-to-do-next": "다음에 할 일",
352
+ "blockers": "막고 있는 것"
353
+ },
354
+ "release-handoff": {
355
+ "handoff-outcome": "인계 결과",
356
+ "handoff-scope": "인계 범위",
357
+ "the-behaviour-you-chose": "선택한 동작",
358
+ "handoff-mode": "인계 방식",
359
+ "conflict-check": "충돌 확인",
360
+ "pr-body": "PR 본문",
361
+ "branch-and-pr": "브랜치와 PR",
362
+ "branch": "브랜치:",
363
+ "pr-result": "PR 결과:",
364
+ "pull-request-open": "Pull request 열기",
365
+ "conflict-verdict": "충돌 판정",
366
+ "commits-delivered": "만들어진 commit",
367
+ "there-are-no-commits": "commit 이 없습니다.",
368
+ "commit": "commit",
369
+ "subject": "제목",
370
+ "files": "파일",
371
+ "the-verification-report-this-rests-on": "이 판단이 근거하는 검증 리포트",
372
+ "what-to-do-next": "다음에 할 일"
373
+ },
374
+ "requirements-discovery": {
375
+ "the-request-as-understood": "이해한 요청",
376
+ "your-request-verbatim": "요청 원문",
377
+ "how-the-system-reads-it": "시스템이 읽은 내용",
378
+ "what-would-prove-this-wrong": "이 판단을 뒤집을 조건",
379
+ "the-requester-left-no-rejection-criteria-eve": "요청자가 기각 조건을 남기지 않았습니다. 있었더라도 분류나 다음 단계는 달라지지 않습니다.",
380
+ "terms-settled": "정리된 용어",
381
+ "as-the-brief-put-it": "브리프의 표현",
382
+ "settled-term": "확정한 용어",
383
+ "settled-by": "확정 근거",
384
+ "evidence": "근거",
385
+ "no-term-was-ambiguous": "모호한 용어가 없었습니다.",
386
+ "settled-and-unsettled-requirements": "확정된 요구사항과 미결 요구사항",
387
+ "nothing-is-left-unsettled": "미결로 남은 것이 없습니다.",
388
+ "next-tasks-and-dependencies": "다음 태스크와 의존 관계",
389
+ "next-task": "다음 태스크:",
390
+ "this-requirements-task-stands-alone-nothing": "이 요구사항 태스크는 독립적입니다. 선행 태스크가 없습니다.",
391
+ "decisions-that-are-yours": "사용자가 결정할 것",
392
+ "no-further-decision-is-needed": "추가로 결정할 것이 없습니다."
393
+ }
394
+ }
395
+ }
@@ -1,45 +1,45 @@
1
1
  {% macro analysis_review(selector_ids) -%}
2
2
  <section id="analysis-review" data-report-section="analysis-review">
3
- <h2>Review this analysis</h2>
4
- <fieldset><legend>Verdict</legend>
5
- <input id="analysis-review-accepted" type="radio" name="analysis-review-status" value="accepted"><label for="analysis-review-accepted">Accept</label>
6
- <input id="analysis-review-revision-requested" type="radio" name="analysis-review-status" value="revision-requested"><label for="analysis-review-revision-requested">Request changes</label>
7
- <input id="analysis-review-rejected" type="radio" name="analysis-review-status" value="rejected"><label for="analysis-review-rejected">Reject</label>
3
+ <h2>{{ t('macros.forms.review-this-analysis') }}</h2>
4
+ <fieldset><legend>{{ t('macros.forms.verdict') }}</legend>
5
+ <input id="analysis-review-accepted" type="radio" name="analysis-review-status" value="accepted"><label for="analysis-review-accepted">{{ t('macros.forms.accept') }}</label>
6
+ <input id="analysis-review-revision-requested" type="radio" name="analysis-review-status" value="revision-requested"><label for="analysis-review-revision-requested">{{ t('macros.forms.request-changes') }}</label>
7
+ <input id="analysis-review-rejected" type="radio" name="analysis-review-status" value="rejected"><label for="analysis-review-rejected">{{ t('macros.forms.reject') }}</label>
8
8
  </fieldset>
9
- <label for="analysis-review-affected-ids">Affected ids</label>
9
+ <label for="analysis-review-affected-ids">{{ t('macros.forms.affected-ids') }}</label>
10
10
  <select id="analysis-review-affected-ids" multiple>
11
11
  {% for analysis_id in selector_ids %}<option value="{{ analysis_id }}">{{ analysis_id }}</option>{% endfor %}
12
12
  </select>
13
- <label for="analysis-review-reason">Why</label><textarea id="analysis-review-reason" rows="3"></textarea>
14
- <label for="analysis-review-evidence">Further evidence</label><textarea id="analysis-review-evidence" rows="3"></textarea>
15
- <label for="analysis-review-scope-change">Scope change requested</label><textarea id="analysis-review-scope-change" rows="3"></textarea>
13
+ <label for="analysis-review-reason">{{ t('macros.forms.why') }}</label><textarea id="analysis-review-reason" rows="3"></textarea>
14
+ <label for="analysis-review-evidence">{{ t('macros.forms.further-evidence') }}</label><textarea id="analysis-review-evidence" rows="3"></textarea>
15
+ <label for="analysis-review-scope-change">{{ t('macros.forms.scope-change-requested') }}</label><textarea id="analysis-review-scope-change" rows="3"></textarea>
16
16
  </section>
17
17
  {%- endmacro %}
18
18
 
19
19
  {% macro plan_approval(state) -%}
20
20
  <section id="plan-approval" data-report-section="plan-approval">
21
- <h2>Approve this plan</h2>
22
- <label for="approval-option">Implementation option</label>
21
+ <h2>{{ t('macros.forms.approve-this-plan') }}</h2>
22
+ <label for="approval-option">{{ t('macros.forms.implementation-option') }}</label>
23
23
  <select id="approval-option"{% if state.disabled_reason %} disabled{% endif %}>
24
24
  {% for name in state.option_names %}
25
25
  {% if name == state.recommended_option %}<option data-recommended="true" selected value="{{ name }}">{{ name | inline_code }} (recommended)</option>{% else %}<option value="{{ name }}">{{ name | inline_code }}</option>{% endif %}
26
26
  {% endfor %}
27
27
  </select>
28
- <input type="checkbox" id="approval-checkbox"{% if state.disabled_reason %} disabled{% endif %}><label for="approval-checkbox">I approve this plan.</label>
29
- {% if state.disabled_reason %}<div class="approval-disabled-reason"><p>Approval is held: <strong>{{ state.disabled_reason }}</strong>.</p><p>Answer {{ state.blocker_ids | join(', ') }}, export your answers, then regenerate the report with the clarification resume command.</p></div>{% endif %}
28
+ <input type="checkbox" id="approval-checkbox"{% if state.disabled_reason %} disabled{% endif %}><label for="approval-checkbox">{{ t('macros.forms.i-approve-this-plan') }}</label>
29
+ {% if state.disabled_reason %}<div class="approval-disabled-reason"><p>{{ t('macros.forms.approval-is-held') }} <strong>{{ state.disabled_reason }}</strong>.</p><p>Answer {{ state.blocker_ids | join(', ') }}, export your answers, then regenerate the report with the clarification resume command.</p></div>{% endif %}
30
30
  </section>
31
31
  {%- endmacro %}
32
32
 
33
33
  {% macro clarification_responses(items) -%}
34
34
  {% if items %}
35
- <section class="clarification-responses" aria-label="Questions waiting on you">
35
+ <section class="clarification-responses" aria-label="{{ t('macros.forms.questions-waiting-on-you') }}">
36
36
  <h2>{{ items | length }} questions waiting on you</h2>
37
- <p class="clarification-lede">The code alone could not settle these. Fill in your answers, then press <strong>Export my answers</strong> at the foot of the page.</p>
37
+ <p class="clarification-lede">{{ t('macros.forms.the-code-alone-could-not-settle-these-fill-i') }} <strong>{{ t('macros.forms.export-my-answers') }}</strong> {{ t('macros.forms.at-the-foot-of-the-page') }}</p>
38
38
  {% for row in items %}
39
39
  <article class="clarification-item" id="id-{{ row.id }}" data-response-id="{{ row.id }}" data-kind="{{ row.kind }}">
40
40
  <p class="eyebrow">{{ row.id }} · {{ row.kind }}</p>
41
41
  {{ row.statement | paragraphs }}
42
- <dl class="clarification-expected"><dt>Answer as</dt><dd>{{ row.expectedForm | inline_code }}</dd></dl>
42
+ <dl class="clarification-expected"><dt>{{ t('macros.forms.answer-as') }}</dt><dd>{{ row.expectedForm | inline_code }}</dd></dl>
43
43
  <label for="response-{{ row.id }}">Your answer to {{ row.id }}</label>
44
44
  <textarea id="response-{{ row.id }}" data-response-id="{{ row.id }}" rows="4">{{ row.userInput | default('') }}</textarea>
45
45
  </article>
@@ -8,7 +8,7 @@
8
8
  <div class="visualization" aria-hidden="true">{{ model.svg | safe }}</div>
9
9
  {% set show_paths = model.nodes | selectattr("paths") | first is defined %}
10
10
  <table class="visualization-fallback">
11
- <thead><tr><th>Component</th><th>What it does</th>{% if show_paths %}<th>Paths</th>{% endif %}</tr></thead>
11
+ <thead><tr><th>{{ t('macros.visualizations.component') }}</th><th>{{ t('macros.visualizations.what-it-does') }}</th>{% if show_paths %}<th>{{ t('macros.visualizations.paths') }}</th>{% endif %}</tr></thead>
12
12
  <tbody>
13
13
  {% for node in model.nodes %}
14
14
  <tr{% if anchor_nodes %} id="id-{{ node.id }}"{% endif %} data-fallback-id="{{ node.id }}">{{ row_key(pairs=[("ID", node.id), ("Name", node.label), ("Kind", node.note)]) }}<td>{{ node.detail | inline_code }}</td>{% if show_paths %}<td>{% for path in node.paths %}<code>{{ path }}</code>{% if not loop.last %} {% endif %}{% endfor %}</td>{% endif %}</tr>
@@ -17,7 +17,7 @@
17
17
  </table>
18
18
  {% if model.edges %}
19
19
  <table class="visualization-fallback visualization-edges">
20
- <thead><tr><th>From</th><th>To</th><th>Relation</th></tr></thead>
20
+ <thead><tr><th>{{ t('macros.visualizations.from') }}</th><th>{{ t('macros.visualizations.to') }}</th><th>{{ t('macros.visualizations.relation') }}</th></tr></thead>
21
21
  <tbody>
22
22
  {% for edge in model.edges %}
23
23
  <tr data-fallback-edge="{{ edge.source }}-{{ edge.target }}"><td>{{ edge.source | inline_code }}</td><td>{{ edge.target | inline_code }}</td><td>{{ edge.label | inline_code }}</td></tr>
@@ -5,50 +5,50 @@
5
5
 
6
6
  {% block human_content %}
7
7
  <section data-report-section="change-overview">
8
- <h2>Change overview</h2>
8
+ <h2>{{ t('tasks.change-impact-analysis.change-overview') }}</h2>
9
9
  {{ render_narrative(narrative.changeExplanation, "changeImpactAnalysis.userNarrative.changeExplanation") }}
10
10
  <p>{{ change.changeRequest.summary | inline_code }}</p>
11
11
  </section>
12
12
 
13
13
  <section data-report-section="affected-and-unaffected">
14
- <h2>What the change touches, and where it stops</h2>
14
+ <h2>{{ t('tasks.change-impact-analysis.what-the-change-touches-and-where-it-stops') }}</h2>
15
15
  {{ render_narrative(narrative.impactExplanation, "changeImpactAnalysis.userNarrative.impactExplanation") }}
16
- <div data-report-field="changeImpactAnalysis.impactItems"><h3>What it touches</h3>{% for row in change.impactItems %}{{ summary_card(row.id ~ " · " ~ (row.target | inline_code), row.impactKind ~ " / Impact " ~ row.level ~ " / Confidence " ~ row.confidence, anchor=row.id) }}{% endfor %}</div>
17
- <div data-report-field="changeImpactAnalysis.unaffectedBoundaries"><h3>Where the change stops</h3>{{ render_narrative(narrative.unaffectedExplanation, "changeImpactAnalysis.userNarrative.unaffectedExplanation") }}<ul>{% for row in change.unaffectedBoundaries %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
16
+ <div data-report-field="changeImpactAnalysis.impactItems"><h3>{{ t('tasks.change-impact-analysis.what-it-touches') }}</h3>{% for row in change.impactItems %}{{ summary_card(row.id ~ " · " ~ (row.target | inline_code), row.impactKind ~ " / Impact " ~ row.level ~ " / Confidence " ~ row.confidence, anchor=row.id) }}{% endfor %}</div>
17
+ <div data-report-field="changeImpactAnalysis.unaffectedBoundaries"><h3>{{ t('tasks.change-impact-analysis.where-the-change-stops') }}</h3>{{ render_narrative(narrative.unaffectedExplanation, "changeImpactAnalysis.userNarrative.unaffectedExplanation") }}<ul>{% for row in change.unaffectedBoundaries %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
18
18
  </section>
19
19
 
20
20
  <section data-report-section="blast-radius" data-report-field="changeImpactAnalysis.dependencyBlastRadius">
21
- <h2>Blast radius</h2>
21
+ <h2>{{ t('tasks.change-impact-analysis.blast-radius') }}</h2>
22
22
  {{ figure(blastRadiusFigure) }}
23
23
  </section>
24
24
 
25
25
  <section data-report-section="test-and-operations">
26
- <h2>Tests, operations, compatibility</h2>
27
- <div data-report-field="changeImpactAnalysis.compatibilityImpact"><h3>Compatibility</h3><ul>{% for row in change.compatibilityImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
28
- <div data-report-field="changeImpactAnalysis.migrationImpact"><h3>Migration</h3><ul>{% for row in change.migrationImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
29
- <div data-report-field="changeImpactAnalysis.rollbackImpact"><h3>Rollback</h3><ul>{% for row in change.rollbackImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
30
- <div data-report-field="changeImpactAnalysis.securityAndPerformanceImpact"><h3>Security and performance</h3><ul>{% for row in change.securityAndPerformanceImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
26
+ <h2>{{ t('tasks.change-impact-analysis.tests-operations-compatibility') }}</h2>
27
+ <div data-report-field="changeImpactAnalysis.compatibilityImpact"><h3>{{ t('tasks.change-impact-analysis.compatibility') }}</h3><ul>{% for row in change.compatibilityImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
28
+ <div data-report-field="changeImpactAnalysis.migrationImpact"><h3>{{ t('tasks.change-impact-analysis.migration') }}</h3><ul>{% for row in change.migrationImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
29
+ <div data-report-field="changeImpactAnalysis.rollbackImpact"><h3>{{ t('tasks.change-impact-analysis.rollback') }}</h3><ul>{% for row in change.rollbackImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
30
+ <div data-report-field="changeImpactAnalysis.securityAndPerformanceImpact"><h3>{{ t('tasks.change-impact-analysis.security-and-performance') }}</h3><ul>{% for row in change.securityAndPerformanceImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
31
31
  </section>
32
32
 
33
33
  <section data-report-section="planning-readiness" data-report-field="changeImpactAnalysis.planningInputs">
34
- <h2>Plan readiness</h2>
34
+ <h2>{{ t('tasks.change-impact-analysis.plan-readiness') }}</h2>
35
35
  {{ render_narrative(narrative.planningReadiness, "changeImpactAnalysis.userNarrative.planningReadiness") }}
36
36
  <ul>{% for row in change.planningInputs %}<li>{{ row.constraint | inline_code }}{% if row.unknown %} — Unsettled{% endif %}</li>{% endfor %}</ul>
37
37
  </section>
38
38
 
39
39
  <section data-report-section="preserved-behaviors" data-report-field="changeImpactAnalysis.preservedBehaviors">
40
- <h2>Behaviour that must not change</h2>
41
- <table><thead><tr><th>Body</th><th>Evidence</th></tr></thead><tbody>{% for row in change.preservedBehaviors %}<tr><td>{{ row.statement | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="2">No behaviour is pinned.</td></tr>{% endfor %}</tbody></table>
40
+ <h2>{{ t('tasks.change-impact-analysis.behaviour-that-must-not-change') }}</h2>
41
+ <table><thead><tr><th>{{ t('tasks.change-impact-analysis.body') }}</th><th>{{ t('tasks.change-impact-analysis.evidence') }}</th></tr></thead><tbody>{% for row in change.preservedBehaviors %}<tr><td>{{ row.statement | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="2">{{ t('tasks.change-impact-analysis.no-behaviour-is-pinned') }}</td></tr>{% endfor %}</tbody></table>
42
42
  </section>
43
43
 
44
44
  <section data-report-section="test-impact" data-report-field="changeImpactAnalysis.testImpact">
45
- <h2>What this does to the tests</h2>
46
- <table><thead><tr><th>Scope</th><th>Action needed</th><th>Tests targeted</th><th>Evidence</th></tr></thead><tbody>{% for row in change.testImpact %}<tr><td>{{ row.scope | inline_code }}</td><td>{{ row.action | inline_code }}</td><td>{% for path in row.testPaths %}<code>{{ path }}</code>{% if not loop.last %} {% endif %}{% endfor %}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="4">No test is affected.</td></tr>{% endfor %}</tbody></table>
45
+ <h2>{{ t('tasks.change-impact-analysis.what-this-does-to-the-tests') }}</h2>
46
+ <table><thead><tr><th>{{ t('tasks.change-impact-analysis.scope') }}</th><th>{{ t('tasks.change-impact-analysis.action-needed') }}</th><th>{{ t('tasks.change-impact-analysis.tests-targeted') }}</th><th>{{ t('tasks.change-impact-analysis.evidence') }}</th></tr></thead><tbody>{% for row in change.testImpact %}<tr><td>{{ row.scope | inline_code }}</td><td>{{ row.action | inline_code }}</td><td>{% for path in row.testPaths %}<code>{{ path }}</code>{% if not loop.last %} {% endif %}{% endfor %}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="4">{{ t('tasks.change-impact-analysis.no-test-is-affected') }}</td></tr>{% endfor %}</tbody></table>
47
47
  </section>
48
48
 
49
49
  <section data-report-section="operational-impact" data-report-field="changeImpactAnalysis.operationalImpact">
50
- <h2>What this does to operations</h2>
51
- <table><thead><tr><th>Area</th><th>Impact</th><th>Evidence</th></tr></thead><tbody>{% for row in change.operationalImpact %}<tr><td>{{ row.area | inline_code }}</td><td>{{ row.impact | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="3">There is no operational impact.</td></tr>{% endfor %}</tbody></table>
50
+ <h2>{{ t('tasks.change-impact-analysis.what-this-does-to-operations') }}</h2>
51
+ <table><thead><tr><th>{{ t('tasks.change-impact-analysis.area') }}</th><th>{{ t('tasks.change-impact-analysis.impact') }}</th><th>{{ t('tasks.change-impact-analysis.evidence') }}</th></tr></thead><tbody>{% for row in change.operationalImpact %}<tr><td>{{ row.area | inline_code }}</td><td>{{ row.impact | inline_code }}</td><td>{{ row.currentCodeEvidence | code_evidence }}</td></tr>{% else %}<tr><td colspan="3">{{ t('tasks.change-impact-analysis.there-is-no-operational-impact') }}</td></tr>{% endfor %}</tbody></table>
52
52
  </section>
53
53
 
54
54
  {{ analysis_review(analysisReviewIds) }}
@@ -4,44 +4,44 @@
4
4
 
5
5
  {% block human_content %}
6
6
  <section data-report-section="symptom">
7
- <h2>What you saw</h2>
7
+ <h2>{{ t('tasks.error-analysis.what-you-saw') }}</h2>
8
8
  {{ render_narrative(narrative.symptomExplanation, "errorAnalysis.userNarrative.symptomExplanation") }}
9
9
  <blockquote data-report-field="errorAnalysis.symptomVerbatim">{{ error.symptomVerbatim | inline_code }}</blockquote>
10
10
  <p data-report-field="errorAnalysis.observableFailure">{{ error.observableFailure | inline_code }}</p>
11
11
  </section>
12
12
 
13
13
  <section data-report-section="reproduction">
14
- <h2>Reproduction</h2>
14
+ <h2>{{ t('tasks.error-analysis.reproduction') }}</h2>
15
15
  <p><span class="status status-{{ error.reproduction.status }}">{{ error.reproduction.status }}</span></p>
16
16
  <ul>{% for item in error.reproduction.evidence %}<li>{{ [item] | evidence_refs }}</li>{% endfor %}</ul>
17
17
  {% if error.reproduction.blockedReason %}<p>{{ error.reproduction.blockedReason | inline_code }}</p>{% endif %}
18
18
  </section>
19
19
 
20
20
  <section data-report-section="causes">
21
- <h2>Cause candidates</h2>
21
+ <h2>{{ t('tasks.error-analysis.cause-candidates') }}</h2>
22
22
  {{ render_narrative(narrative.causeExplanation, "errorAnalysis.userNarrative.causeExplanation") }}
23
23
  {{ figure(causeFigure) }}
24
- <div class="summary-grid">{% for row in error.causeCandidates %}<article class="summary-card tone-{{ row.confidence }}" id="id-{{ row.id }}"><p class="eyebrow">{{ row.id }} · Confidence {{ row.confidence }}</p><h3>{{ row.statement | inline_code }}</h3><p class="evidence-refs">Supporting evidence: {{ row.supportingEvidence | evidence_refs }}</p></article>{% else %}<p>No cause candidate has been established yet.</p>{% endfor %}</div>
24
+ <div class="summary-grid">{% for row in error.causeCandidates %}<article class="summary-card tone-{{ row.confidence }}" id="id-{{ row.id }}"><p class="eyebrow">{{ row.id }} · Confidence {{ row.confidence }}</p><h3>{{ row.statement | inline_code }}</h3><p class="evidence-refs">Supporting evidence: {{ row.supportingEvidence | evidence_refs }}</p></article>{% else %}<p>{{ t('tasks.error-analysis.no-cause-candidate-has-been-established-yet') }}</p>{% endfor %}</div>
25
25
  </section>
26
26
 
27
27
  <section data-report-section="uncertainty">
28
- <h2>What cannot be settled yet</h2>
28
+ <h2>{{ t('tasks.error-analysis.what-cannot-be-settled-yet') }}</h2>
29
29
  {{ render_narrative(narrative.uncertaintyExplanation, "errorAnalysis.userNarrative.uncertaintyExplanation") }}
30
- {% for row in error.causeCandidates %}<article class="summary-card" id="id-{{ row.id }}-detail"><h3>{{ row.id }}</h3><p data-report-field="errorAnalysis.causeCandidates.confidence"><strong>Confidence now:</strong> {{ row.confidence }}</p><p data-report-field="errorAnalysis.causeCandidates.falsifyingEvidenceChecked"><strong>Disproof attempted:</strong> {{ row.falsifyingEvidenceChecked | join(', ') | inline_code }}</p><p data-report-field="errorAnalysis.causeCandidates.disproveWith"><strong>How to disprove this:</strong> {{ row.disproveWith | inline_code }}</p></article>{% endfor %}
30
+ {% for row in error.causeCandidates %}<article class="summary-card" id="id-{{ row.id }}-detail"><h3>{{ row.id }}</h3><p data-report-field="errorAnalysis.causeCandidates.confidence"><strong>{{ t('tasks.error-analysis.confidence-now') }}</strong> {{ row.confidence }}</p><p data-report-field="errorAnalysis.causeCandidates.falsifyingEvidenceChecked"><strong>{{ t('tasks.error-analysis.disproof-attempted') }}</strong> {{ row.falsifyingEvidenceChecked | join(', ') | inline_code }}</p><p data-report-field="errorAnalysis.causeCandidates.disproveWith"><strong>{{ t('tasks.error-analysis.how-to-disprove-this') }}</strong> {{ row.disproveWith | inline_code }}</p></article>{% endfor %}
31
31
  </section>
32
32
 
33
33
  <section data-report-section="next-diagnostic">
34
- <h2>The one diagnostic to run next</h2>
34
+ <h2>{{ t('tasks.error-analysis.the-one-diagnostic-to-run-next') }}</h2>
35
35
  {{ render_narrative(narrative.diagnosticGuidance, "errorAnalysis.userNarrative.diagnosticGuidance") }}
36
36
  {{ summary_card("Run", error.nextDiagnostic.action, "important") }}
37
- <p><strong>Signal to check:</strong> {{ error.nextDiagnostic.confirmingSignal | inline_code }}</p>
38
- <p><strong>Signal that rejects it:</strong> {{ error.nextDiagnostic.rejectingSignal | inline_code }}</p>
37
+ <p><strong>{{ t('tasks.error-analysis.signal-to-check') }}</strong> {{ error.nextDiagnostic.confirmingSignal | inline_code }}</p>
38
+ <p><strong>{{ t('tasks.error-analysis.signal-that-rejects-it') }}</strong> {{ error.nextDiagnostic.rejectingSignal | inline_code }}</p>
39
39
  </section>
40
40
 
41
41
  <section data-report-section="routing" data-report-field="errorAnalysis.routing">
42
- <h2>Next step</h2>
43
- <p><strong>Recommended task</strong> — <code>{{ error.routing.nextTaskType }}</code></p>
44
- <p><strong>The cause this rests on</strong> — {{ error.routing.leadingCauseId | inline_code }}</p>
42
+ <h2>{{ t('tasks.error-analysis.next-step') }}</h2>
43
+ <p><strong>{{ t('tasks.error-analysis.recommended-task') }}</strong> — <code>{{ error.routing.nextTaskType }}</code></p>
44
+ <p><strong>{{ t('tasks.error-analysis.the-cause-this-rests-on') }}</strong> — {{ error.routing.leadingCauseId | inline_code }}</p>
45
45
  <p>{{ error.routing.rationale | inline_code }}</p>
46
46
  </section>
47
47
  {% endblock %}