okstra 0.148.1 → 0.150.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 (78) hide show
  1. package/docs/architecture.md +1 -1
  2. package/docs/cli.md +1 -1
  3. package/docs/project-structure-overview.md +1 -1
  4. package/package.json +1 -1
  5. package/runtime/BUILD.json +2 -2
  6. package/runtime/agents/workers/translator-worker.md +67 -0
  7. package/runtime/bin/okstra-render-final-report.py +0 -11
  8. package/runtime/bin/okstra-render-report-views.py +20 -0
  9. package/runtime/bin/okstra-report-translate.py +158 -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/report-writer.md +14 -13
  13. package/runtime/prompts/lead/team-contract.md +2 -2
  14. package/runtime/prompts/profiles/project-analysis.md +18 -0
  15. package/runtime/prompts/profiles/release-handoff.md +3 -0
  16. package/runtime/prompts/profiles/requirements-discovery.md +7 -0
  17. package/runtime/prompts/wizard/prompts.ko.json +17 -1
  18. package/runtime/python/okstra_ctl/analysis_inputs.py +24 -9
  19. package/runtime/python/okstra_ctl/analysis_packet.py +23 -1
  20. package/runtime/python/okstra_ctl/clarification_items.py +241 -44
  21. package/runtime/python/okstra_ctl/codex_dispatch.py +2 -1
  22. package/runtime/python/okstra_ctl/dispatch_core.py +2 -2
  23. package/runtime/python/okstra_ctl/dispatch_state.py +12 -1
  24. package/runtime/python/okstra_ctl/final_report_paths.py +22 -1
  25. package/runtime/python/okstra_ctl/i18n.py +12 -7
  26. package/runtime/python/okstra_ctl/render_final_report.py +18 -17
  27. package/runtime/python/okstra_ctl/report_html/common.py +77 -47
  28. package/runtime/python/okstra_ctl/report_html/filters.py +63 -30
  29. package/runtime/python/okstra_ctl/report_html/models.py +15 -0
  30. package/runtime/python/okstra_ctl/report_html/render.py +86 -5
  31. package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +14 -4
  32. package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +3 -3
  33. package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +5 -3
  34. package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -7
  35. package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +5 -9
  36. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -5
  37. package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +1 -2
  38. package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +90 -4
  39. package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +1 -8
  40. package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +6 -4
  41. package/runtime/python/okstra_ctl/report_html/visualizations.py +146 -11
  42. package/runtime/python/okstra_ctl/report_translation.py +440 -0
  43. package/runtime/python/okstra_ctl/report_view_artifacts.py +5 -0
  44. package/runtime/python/okstra_ctl/report_views.py +23 -9
  45. package/runtime/python/okstra_ctl/run.py +1 -1
  46. package/runtime/python/okstra_ctl/time_report.py +2 -2
  47. package/runtime/python/okstra_ctl/usage_report.py +2 -2
  48. package/runtime/python/okstra_ctl/user_response.py +11 -6
  49. package/runtime/python/okstra_ctl/wizard.py +100 -25
  50. package/runtime/python/okstra_ctl/worker_liveness.py +130 -36
  51. package/runtime/schemas/final-report-v2.0.schema.json +229 -1
  52. package/runtime/templates/reports/final-report.template.md +55 -0
  53. package/runtime/templates/reports/html/assets/base.css +59 -9
  54. package/runtime/templates/reports/html/base.template.html +23 -44
  55. package/runtime/templates/reports/html/i18n/en.json +395 -0
  56. package/runtime/templates/reports/html/i18n/ko.json +395 -0
  57. package/runtime/templates/reports/html/macros/forms.html +20 -20
  58. package/runtime/templates/reports/html/macros/layout.html +18 -5
  59. package/runtime/templates/reports/html/macros/visualizations.html +7 -5
  60. package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +30 -15
  61. package/runtime/templates/reports/html/tasks/error-analysis.template.html +22 -15
  62. package/runtime/templates/reports/html/tasks/feature-analysis.template.html +35 -15
  63. package/runtime/templates/reports/html/tasks/final-verification.template.html +21 -14
  64. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +78 -19
  65. package/runtime/templates/reports/html/tasks/implementation.template.html +34 -16
  66. package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +11 -11
  67. package/runtime/templates/reports/html/tasks/project-analysis.template.html +65 -25
  68. package/runtime/templates/reports/html/tasks/release-handoff.template.html +28 -14
  69. package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +35 -15
  70. package/runtime/templates/reports/report.js +8 -5
  71. package/runtime/validators/validate-report-views.py +1 -1
  72. package/runtime/validators/validate-run.py +28 -31
  73. package/runtime/validators/validate_analysis_report.py +36 -0
  74. package/src/cli-registry.mjs +11 -0
  75. package/src/commands/inspect/worker-liveness.mjs +9 -7
  76. package/src/commands/report/translate.mjs +31 -0
  77. package/src/lib/helper-scripts.mjs +1 -0
  78. 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,46 +1,46 @@
1
1
  {% macro analysis_review(selector_ids) -%}
2
2
  <section id="analysis-review" data-report-section="analysis-review">
3
- <h2>분석 검토</h2>
4
- <fieldset><legend>판정</legend>
5
- <input id="analysis-review-accepted" type="radio" name="analysis-review-status" value="accepted"><label for="analysis-review-accepted">수락</label>
6
- <input id="analysis-review-revision-requested" type="radio" name="analysis-review-status" value="revision-requested"><label for="analysis-review-revision-requested">수정 요청</label>
7
- <input id="analysis-review-rejected" type="radio" name="analysis-review-status" value="rejected"><label for="analysis-review-rejected">거절</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">영향받는 ID</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">이유</label><textarea id="analysis-review-reason" rows="3"></textarea>
14
- <label for="analysis-review-evidence">추가 근거</label><textarea id="analysis-review-evidence" rows="3"></textarea>
15
- <label for="analysis-review-scope-change">요청 범위 변경</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>계획 승인</h2>
22
- <label for="approval-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
- {% if name == state.recommended_option %}<option data-recommended="true" selected value="{{ name }}">{{ name | inline_code }} (권장)</option>{% else %}<option value="{{ name }}">{{ name | inline_code }}</option>{% endif %}
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">이 계획을 승인합니다.</label>
29
- {% if state.disabled_reason %}<div class="approval-disabled-reason"><p><strong>{{ state.disabled_reason }}</strong>라 아직 승인할 수 없습니다.</p><p>{{ state.blocker_ids | join(', ') }}에 답하고 사용자 응답을 내보낸 뒤 clarification 재개 명령으로 보고서를 다시 생성하세요.</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="답변이 필요한 질문">
36
- <h2>답변이 필요한 질문 {{ items | length }}개</h2>
37
- <p class="clarification-lede">코드만 봐서는 정할 수 없어 남겨 둔 항목입니다. 답을 채운 뒤 맨 아래 <strong>사용자 응답 내보내기</strong>를 누르세요.</p>
35
+ <section class="clarification-responses" aria-label="{{ t('macros.forms.questions-waiting-on-you') }}">
36
+ <h2>{{ items | length }} questions waiting on you</h2>
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
- <article class="clarification-item" data-response-id="{{ row.id }}" data-kind="{{ row.kind }}">
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
- <p class="clarification-expected"><strong>답변 형식</strong> — {{ row.expectedForm | inline_code }}</p>
43
- <label for="response-{{ row.id }}">{{ row.id }} 답변</label>
42
+ <dl class="clarification-expected"><dt>{{ t('macros.forms.answer-as') }}</dt><dd>{{ row.expectedForm | inline_code }}</dd></dl>
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>
46
46
  {% endfor %}
@@ -1,7 +1,7 @@
1
- {% macro narrative(value, field_name, evidence_index={}) -%}
1
+ {% macro narrative(value, field_name) -%}
2
2
  <div class="narrative" data-report-field="{{ field_name }}">
3
3
  {{ value.text | paragraphs }}
4
- <p class="evidence-refs">근거: {{ value.evidenceRefs | evidence_refs(evidence_index) }}</p>
4
+ <p class="evidence-refs">Evidence: {{ value.evidenceRefs | evidence_refs }}</p>
5
5
  </div>
6
6
  {%- endmacro %}
7
7
 
@@ -11,9 +11,22 @@
11
11
  </ol>
12
12
  {%- endmacro %}
13
13
 
14
- {% macro summary_card(title, body, tone='neutral') -%}
15
- <article class="summary-card tone-{{ tone }}">
16
- <h3>{{ title }}</h3>
14
+ {# The values that identify a row — id, name, kind, owner, status — as one
15
+ cell, one labelled value per line. Given a column each they took roughly
16
+ half the table's width to carry a handful of characters, and the prose
17
+ columns beside them were squeezed to the point of stacking. Merging them
18
+ costs the column headings, so every value carries its own name here:
19
+ `pairs` is a list of (name, value), and the status keeps its pill. #}
20
+ {% macro row_key(pairs=[], status_name='', status_raw='', status_text='') -%}
21
+ <td class="row-key"><dl>
22
+ {%- for name, value in pairs %}{% if value %}<div><dt>{{ name }}</dt><dd>{{ value | inline_code }}</dd></div>{% endif %}{% endfor -%}
23
+ {%- if status_text %}<div><dt>{{ status_name }}</dt><dd><span class="status status-{{ status_raw }}">{{ status_text }}</span></dd></div>{% endif -%}
24
+ </dl></td>
25
+ {%- endmacro %}
26
+
27
+ {% macro summary_card(title, body, tone='neutral', anchor='') -%}
28
+ <article class="summary-card tone-{{ tone }}"{% if anchor %} id="id-{{ anchor }}"{% endif %}>
29
+ {% if title %}<h3>{{ title }}</h3>{% endif %}
17
30
  <p>{{ body | inline_code }}</p>
18
31
  </article>
19
32
  {%- endmacro %}
@@ -1,24 +1,26 @@
1
- {% macro figure(model) -%}
1
+ {% from "html/macros/layout.html" import row_key %}
2
+ {% macro figure(model, anchor_nodes=false) -%}
2
3
  <figure data-figure-kind="{{ model.kind }}" aria-labelledby="{{ model.figure_id }}-title" aria-describedby="{{ model.figure_id }}-summary">
3
4
  <figcaption>
4
5
  <strong id="{{ model.figure_id }}-title">{{ model.title }}</strong>
5
6
  <span id="{{ model.figure_id }}-summary">{{ model.summary }}</span>
6
7
  </figcaption>
7
8
  <div class="visualization" aria-hidden="true">{{ model.svg | safe }}</div>
9
+ {% set show_paths = model.nodes | selectattr("paths") | first is defined %}
8
10
  <table class="visualization-fallback">
9
- <thead><tr><th>ID</th><th>이름</th><th>그룹</th><th>상태</th><th>설명</th></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>
10
12
  <tbody>
11
13
  {% for node in model.nodes %}
12
- <tr data-fallback-id="{{ node.id }}"><td>{{ node.id }}</td><td>{{ node.label | inline_code }}</td><td>{{ node.group | inline_code }}</td><td>{{ node.status }}</td><td>{{ node.detail | inline_code }}</td></tr>
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>
13
15
  {% endfor %}
14
16
  </tbody>
15
17
  </table>
16
18
  {% if model.edges %}
17
19
  <table class="visualization-fallback visualization-edges">
18
- <thead><tr><th>출발</th><th>도착</th><th>관계</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>
19
21
  <tbody>
20
22
  {% for edge in model.edges %}
21
- <tr data-fallback-edge="{{ edge.source }}-{{ edge.target }}"><td>{{ edge.source }}</td><td>{{ edge.target }}</td><td>{{ edge.label | inline_code }}</td></tr>
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>
22
24
  {% endfor %}
23
25
  </tbody>
24
26
  </table>
@@ -5,35 +5,50 @@
5
5
 
6
6
  {% block human_content %}
7
7
  <section data-report-section="change-overview">
8
- <h2>변경 개요</h2>
9
- {{ render_narrative(narrative.changeExplanation, "changeImpactAnalysis.userNarrative.changeExplanation", evidenceIndex) }}
8
+ <h2>{{ t('tasks.change-impact-analysis.change-overview') }}</h2>
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>영향받는 영역과 영향 없는 경계</h2>
15
- {{ render_narrative(narrative.impactExplanation, "changeImpactAnalysis.userNarrative.impactExplanation", evidenceIndex) }}
16
- <div data-report-field="changeImpactAnalysis.impactItems"><h3>영향받는 영역</h3>{% for row in change.impactItems %}{{ summary_card(row.id ~ " · " ~ (row.target | inline_code), row.impactKind ~ " / 영향 " ~ row.level ~ " / 신뢰 " ~ row.confidence) }}{% endfor %}</div>
17
- <div data-report-field="changeImpactAnalysis.unaffectedBoundaries"><h3>영향 없는 경계</h3>{{ render_narrative(narrative.unaffectedExplanation, "changeImpactAnalysis.userNarrative.unaffectedExplanation", evidenceIndex) }}<ul>{% for row in change.unaffectedBoundaries %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
14
+ <h2>{{ t('tasks.change-impact-analysis.what-the-change-touches-and-where-it-stops') }}</h2>
15
+ {{ render_narrative(narrative.impactExplanation, "changeImpactAnalysis.userNarrative.impactExplanation") }}
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>파급 범위</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>테스트·운영·호환성</h2>
27
- <div data-report-field="changeImpactAnalysis.compatibilityImpact"><h3>호환성</h3><ul>{% for row in change.compatibilityImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
28
- <div data-report-field="changeImpactAnalysis.migrationImpact"><h3>마이그레이션</h3><ul>{% for row in change.migrationImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
29
- <div data-report-field="changeImpactAnalysis.rollbackImpact"><h3>롤백</h3><ul>{% for row in change.rollbackImpact %}<li>{{ row.summary | inline_code }}</li>{% endfor %}</ul></div>
30
- <div data-report-field="changeImpactAnalysis.securityAndPerformanceImpact"><h3>보안과 성능</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>계획 준비도</h2>
35
- {{ render_narrative(narrative.planningReadiness, "changeImpactAnalysis.userNarrative.planningReadiness", evidenceIndex) }}
36
- <ul>{% for row in change.planningInputs %}<li>{{ row.constraint | inline_code }}{% if row.unknown %} — 미확정{% endif %}</li>{% endfor %}</ul>
34
+ <h2>{{ t('tasks.change-impact-analysis.plan-readiness') }}</h2>
35
+ {{ render_narrative(narrative.planningReadiness, "changeImpactAnalysis.userNarrative.planningReadiness") }}
36
+ <ul>{% for row in change.planningInputs %}<li>{{ row.constraint | inline_code }}{% if row.unknown %} — Unsettled{% endif %}</li>{% endfor %}</ul>
37
+ </section>
38
+
39
+ <section data-report-section="preserved-behaviors" data-report-field="changeImpactAnalysis.preservedBehaviors">
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
+ </section>
43
+
44
+ <section data-report-section="test-impact" data-report-field="changeImpactAnalysis.testImpact">
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
+ </section>
48
+
49
+ <section data-report-section="operational-impact" data-report-field="changeImpactAnalysis.operationalImpact">
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>
37
52
  </section>
38
53
 
39
54
  {{ analysis_review(analysisReviewIds) }}