okstra 0.48.0 → 0.50.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 (60) hide show
  1. package/docs/kr/architecture.md +8 -8
  2. package/docs/kr/cli.md +2 -2
  3. package/docs/project-structure-overview.md +3 -3
  4. package/docs/superpowers/plans/2026-06-05-compact-markdown-report-tables.md +323 -0
  5. package/docs/superpowers/plans/2026-06-05-wizard-batch-prompts.md +559 -0
  6. package/docs/superpowers/specs/2026-06-05-compact-markdown-report-tables-design.md +87 -0
  7. package/docs/superpowers/specs/2026-06-05-wizard-batch-prompts-design.md +121 -0
  8. package/docs/task-process/error-analysis.md +1 -1
  9. package/docs/task-process/final-verification.md +1 -1
  10. package/docs/task-process/release-handoff.md +1 -1
  11. package/docs/task-process/requirements-discovery.md +1 -1
  12. package/package.json +1 -1
  13. package/runtime/BUILD.json +2 -2
  14. package/runtime/agents/SKILL.md +3 -3
  15. package/runtime/agents/workers/claude-worker.md +1 -1
  16. package/runtime/agents/workers/codex-worker.md +1 -1
  17. package/runtime/agents/workers/gemini-worker.md +1 -1
  18. package/runtime/agents/workers/report-writer-worker.md +3 -3
  19. package/runtime/bin/lib/okstra/tmux-pane.sh +40 -0
  20. package/runtime/bin/okstra-codex-exec.sh +17 -21
  21. package/runtime/bin/okstra-gemini-exec.sh +12 -15
  22. package/runtime/bin/okstra-render-report-views.py +1 -1
  23. package/runtime/bin/okstra-trace-cleanup.sh +13 -1
  24. package/runtime/prompts/launch.template.md +1 -1
  25. package/runtime/prompts/profiles/_common-contract.md +15 -15
  26. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  27. package/runtime/prompts/profiles/_implementation-executor.md +1 -1
  28. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  29. package/runtime/prompts/profiles/error-analysis.md +1 -1
  30. package/runtime/prompts/profiles/final-verification.md +2 -2
  31. package/runtime/prompts/profiles/implementation-planning.md +9 -9
  32. package/runtime/prompts/profiles/improvement-discovery.md +5 -5
  33. package/runtime/prompts/profiles/release-handoff.md +2 -2
  34. package/runtime/prompts/profiles/requirements-discovery.md +2 -2
  35. package/runtime/python/okstra_ctl/clarification_items.py +11 -11
  36. package/runtime/python/okstra_ctl/render.py +1 -1
  37. package/runtime/python/okstra_ctl/render_final_report.py +1 -1
  38. package/runtime/python/okstra_ctl/report_views.py +26 -39
  39. package/runtime/python/okstra_ctl/run.py +3 -3
  40. package/runtime/python/okstra_ctl/wizard.py +90 -3
  41. package/runtime/python/okstra_ctl/workflow.py +1 -1
  42. package/runtime/skills/okstra-brief/SKILL.md +1 -1
  43. package/runtime/skills/okstra-convergence/SKILL.md +8 -8
  44. package/runtime/skills/okstra-report-writer/SKILL.md +22 -22
  45. package/runtime/skills/okstra-run/SKILL.md +2 -0
  46. package/runtime/skills/okstra-team-contract/SKILL.md +1 -1
  47. package/runtime/templates/project-docs/task-index.template.md +1 -8
  48. package/runtime/templates/reports/final-report.template.md +194 -198
  49. package/runtime/templates/reports/i18n/en.json +16 -17
  50. package/runtime/templates/reports/i18n/ko.json +16 -17
  51. package/runtime/templates/reports/implementation-planning-input.template.md +1 -1
  52. package/runtime/templates/reports/release-handoff-input.template.md +1 -1
  53. package/runtime/templates/reports/schedule.template.md +3 -7
  54. package/runtime/templates/reports/user-response.template.md +1 -1
  55. package/runtime/templates/worker-prompt-preamble.md +1 -1
  56. package/runtime/validators/lib/fixtures.sh +2 -2
  57. package/runtime/validators/validate-implementation-plan-stages.py +9 -9
  58. package/runtime/validators/validate-report-views.py +10 -10
  59. package/runtime/validators/validate-run.py +36 -36
  60. package/runtime/validators/validate_improvement_report.py +8 -8
@@ -343,7 +343,7 @@ okstra phase 는 PRD / issue file 을 직접 쓰지 않습니다. 동등한 결
343
343
  |---|---|---|---|---|
344
344
  | `requirements-discovery` | 요청을 bugfix/feature/refactor/ops/improvement 중 하나로 분류하고 안전한 다음 phase로 라우팅 | work category, routing decision, missing-input list, clarification requests | `pending-routing-decision` (사용자 답변 후 결정) | 금지 |
345
345
  | `error-analysis` | 보고된 에러/사고의 증상·원인·재현 갭을 증거 기반으로 분석 | symptom/trigger 정리, root-cause 가설, reproduction gap, validation 경로 | `implementation-planning` | 금지 |
346
- | `implementation-planning` | 코딩 시작 전 안전한 구현 방향과 옵션을 평가 | 최소 2개 구현 옵션, 영향 파일 목록, trade-off, 단계별 실행 순서, validation/rollback, **User Approval Request** 블록, **§4.5.9 Plan Body Verification** (Phase 6 워커 사후 검증 라운드 — 합성된 plan 의 내적 일관성을 워커가 `AGREE` / `DISAGREE(a-e)` / `SUPPLEMENT` 로 cross-verify; gate 결과가 `passed` / `passed-with-dissent` 일 때만 Approval 마커 렌더, `blocked-by-disagreement` / `aborted-non-result` 일 때는 majority DISAGREE 항목이 `## 5. Clarification Items` 의 `Blocks=approval` row 로 변환됨). **산출 구조**: 항상 `## 4.5 Stage Map` + N 개의 `## 4.5.<i> Stage <i>` 섹션. 각 stage 의 effective step ≤ 6. `depends-on (none)` 인 stage 들은 별도 `implementation` run 으로 병렬 실행 가능 | `implementation` (사용자 승인 후) | 금지 |
346
+ | `implementation-planning` | 코딩 시작 전 안전한 구현 방향과 옵션을 평가 | 최소 2개 구현 옵션, 영향 파일 목록, trade-off, 단계별 실행 순서, validation/rollback, **User Approval Request** 블록, **§5.5.9 Plan Body Verification** (Phase 6 워커 사후 검증 라운드 — 합성된 plan 의 내적 일관성을 워커가 `AGREE` / `DISAGREE(a-e)` / `SUPPLEMENT` 로 cross-verify; gate 결과가 `passed` / `passed-with-dissent` 일 때만 Approval 마커 렌더, `blocked-by-disagreement` / `aborted-non-result` 일 때는 majority DISAGREE 항목이 `## 1. Clarification Items` 의 `Blocks=approval` row 로 변환됨). **산출 구조**: 항상 `## 5.5 Stage Map` + N 개의 `## 5.5.<i> Stage <i>` 섹션. 각 stage 의 effective step ≤ 6. `depends-on (none)` 인 stage 들은 별도 `implementation` run 으로 병렬 실행 가능 | `implementation` (사용자 승인 후) | 금지 |
347
347
  | `implementation` | 승인된 `implementation-planning` final report의 단계대로 소스 코드를 수정. **한 run 에 한 stage 만 실행** (`--stage <auto\|N>` 인수로 stage 선택) | commit list, diff summary, out-of-plan edits 블록, validation/TDD evidence, rollback 검증, verifier 결과(Gemini/Codex/Claude), `carry/stage-<N>.json` evidence sidecar | `final-verification` | 허용 (승인된 plan의 파일 목록 한정, `git push`/publish/deploy/실제 migration 금지) |
348
348
  | `final-verification` | 완료된 작업의 잔존 결함·회귀 위험을 점검하고 release 판단 | acceptance verdict, residual risk, follow-up 라우팅(`error-analysis`/`implementation-planning`/`release-handoff`) | `pending-release-handoff` (verdict 가 `accepted` 일 때만 `release-handoff` 로 진입; 그 외에는 `error-analysis` 또는 `implementation-planning` 으로 리라우팅) | 금지 (read-only 테스트만 허용) |
349
349
  | `release-handoff` | `accepted` 받은 변경을 사용자가 선택한 방식대로 커밋·푸시·PR 로 전달 | 사용자 메뉴 응답(H1 action / H2 PR base / H3 message handling) 기록, 실행한 git/gh 명령 로그, commit SHA 목록, PR URL | `done-or-follow-up` | 허용 — 단 **사용자가 메뉴로 선택한 mutating 명령만** 실행. `git push --force*`, base 브랜치 직접 push, `--no-verify`, `gh release`, publish/deploy 는 금지. source code 자체는 수정 금지(이전 `implementation` 의 diff 를 그대로 패키징). |
@@ -370,12 +370,12 @@ okstra phase 는 PRD / issue file 을 직접 쓰지 않습니다. 동등한 결
370
370
  [brief: scope=codebase + priority-lenses]
371
371
  ↓ okstra-run --task-type improvement-discovery
372
372
  [improvement-discovery]
373
- ↓ final-report (## 4.9 Improvement Candidates 후보 N개)
373
+ ↓ final-report (## 5.9 Improvement Candidates 후보 N개)
374
374
  ↓ (사용자가 후보 K개 선택, 각각 새 brief 작성)
375
375
  [requirements-discovery | implementation-planning | error-analysis] (선택된 후보별로 새 task-id 로)
376
376
  ````
377
377
 
378
- `PHASE_SEQUENCE` 의 정식 멤버에 들어가지 않는 sidetrack entry-point. 단방향 라이프사이클을 깨지 않으면서 코드베이스 발견 시나리오를 흡수한다. lens 화이트리스트와 candidate-cap 은 `scripts/okstra_ctl/improvement_lenses.py` SSOT 1개에서 통일된다. final-report 의 `## 4.9 Improvement Candidates` 표 (10 column) 는 `validators/validate_improvement_report.py` 의 11항목 contract 가 강제한다. 양방향 grilling 두 지점 (`okstra-brief` Step 4 강화 budget 8 + lead 의 Phase 1.5 reflect-back budget 12) 으로 사용자와 AI 의 이해도를 일치시킨다.
378
+ `PHASE_SEQUENCE` 의 정식 멤버에 들어가지 않는 sidetrack entry-point. 단방향 라이프사이클을 깨지 않으면서 코드베이스 발견 시나리오를 흡수한다. lens 화이트리스트와 candidate-cap 은 `scripts/okstra_ctl/improvement_lenses.py` SSOT 1개에서 통일된다. final-report 의 `## 5.9 Improvement Candidates` 표 (10 column) 는 `validators/validate_improvement_report.py` 의 11항목 contract 가 강제한다. 양방향 grilling 두 지점 (`okstra-brief` Step 4 강화 budget 8 + lead 의 Phase 1.5 reflect-back budget 12) 으로 사용자와 AI 의 이해도를 일치시킨다.
379
379
 
380
380
  ### requirements-discovery fan-out
381
381
 
@@ -860,11 +860,11 @@ Claude가 작성하는 최종 보고서는 아래 구조를 우선 사용합니
860
860
 
861
861
  - `## Verdict Card` — **최상단 의무 섹션**. Final Conclusion / Verdict Token / Direction / Approval Required? / Next Step 5 행. Verdict Token / Direction / Next Step 셀은 본문 §2 (실행 현황) 와 §6 (다음 단계) 의 권위 셀과 byte-match 해야 합니다.
862
862
  - (선택) `## 0. Clarification Response Carried In From Previous Run` — 직전 run 에서 응답이 carry-in 된 경우에만 렌더링. 빈 carry-in 일 때는 헤딩 자체를 출력하지 않습니다.
863
- - `## 1. 문제 또는 검증 대상 요약` — §1.1 Consensus / §1.2 Differences 표 각각 `Source items (worker:item)` 컬럼 보존 (cross-worker traceability).
863
+ - `## 1. 문제 또는 검증 대상 요약` — §6.1 Consensus / §6.2 Differences 표 각각 `Source items (worker:item)` 컬럼 보존 (cross-worker traceability).
864
864
  - `## 2. 에이전트별 실행 현황`
865
- - `## 3. Cross Verification 결과` — §3.1 Primary Evidence 에 `Source items (worker:item)` + `Source (path:line / log)` 컬럼.
866
- - `## 4. 최종 판단` — `implementation-planning` 의 §4.5.9 Plan Body Verification 은 `Verdict details` 표 (5-열, plan item × worker) 하나로 emit.
867
- - `## 5. Clarification Items` — 통합 8-열 표 한 곳. 기존 §5.1 / §5.2 / §4.5.8 / §4.5.9 Open Questions 는 deprecated 되어 validator 가 등장 시 fail.
865
+ - `## 3. Cross Verification 결과` — §2.1 Primary Evidence 에 `Source items (worker:item)` + `Source (path:line / log)` 컬럼.
866
+ - `## 4. 최종 판단` — `implementation-planning` 의 §5.5.9 Plan Body Verification 은 `Verdict details` 표 (5-열, plan item × worker) 하나로 emit.
867
+ - `## 1. Clarification Items` — 통합 8-열 표 한 곳. 기존 §6.1 / §6.2 / §5.5.8 / §5.5.9 Open Questions 는 deprecated 되어 validator 가 등장 시 fail.
868
868
  - `## 6. 권장 다음 단계`
869
869
  - `## Token Usage Summary` — sentinel (`pending` / `N/A` / `--` / `?` / 빈 셀) 또는 zero (`0` / `$0.00`) 박제 시 validator 가 출고를 차단합니다. `Codex/Gemini CLI 추가 비용` 행만 "CLI 미사용" 의미로 `$0.00` 허용.
870
870
 
@@ -940,7 +940,7 @@ Phase 7 step 1.5 가 final-report MD 한 본을 입력으로 두 view 를 결정
940
940
  phase 산출물의 출고 가능 여부를 강제하는 진입점:
941
941
 
942
942
  - `validators/validate-workflow.sh` — phase contract 통합 검증.
943
- - `validators/validate-run.py` — run-level final-report 본문 contract (Verdict Card 존재, deprecated §5.1/§5.2/§4.5.8/§4.5.9 Open Questions 부재, Plan Body Verification gate × Approval 마커 cross-check, Token Usage sentinel/zero 차단, 워커-결과 audit 사이드카 존재).
943
+ - `validators/validate-run.py` — run-level final-report 본문 contract (Verdict Card 존재, deprecated §6.1/§6.2/§5.5.8/§5.5.9 Open Questions 부재, Plan Body Verification gate × Approval 마커 cross-check, Token Usage sentinel/zero 차단, 워커-결과 audit 사이드카 존재).
944
944
  - `validators/validate-report-views.py` — slim MD / HTML view 의 phase substring 보존 및 form-control 영역 검사.
945
945
  - `validators/validate-brief.py` — brief schema (front-matter, `Reporter Confirmations` 섹션 존재, root parent-id self 규칙, slug 컨벤션 등) 강제. `bash validators/validate-brief.sh <brief.md>` 가 thin wrapper.
946
946
 
package/docs/kr/cli.md CHANGED
@@ -118,7 +118,7 @@ interactive terminal에서 실행하면 다음 규칙이 추가로 적용됩니
118
118
  - `scan-scope`: 1개 이상의 경로.
119
119
  - `out-of-scope`: 선택.
120
120
  - `candidate-cap`: 1–12, 기본 8.
121
- - 출력: `## 4.9 Improvement Candidates` 표 (10 column: Cand ID / Lens / Title / Scope / Severity / Effort / Consensus / Source workers / Recommended next-phase / Evidence).
121
+ - 출력: `## 5.9 Improvement Candidates` 표 (10 column: Cand ID / Lens / Title / Scope / Severity / Effort / Consensus / Source workers / Recommended next-phase / Evidence).
122
122
  - Verdict Token: `candidates-ready` / `no-candidates` / `blocked`.
123
123
  - 라우팅: 자동 spin-off 없음. 사용자가 후보를 골라 새 task-id 로 `requirements-discovery` / `implementation-planning` / `error-analysis` 진입.
124
124
  - 워커: claude + codex + gemini + report-writer 모두 필수.
@@ -475,7 +475,7 @@ scripts/okstra.sh --task-type error-analysis --related-tasks scanner-regression,
475
475
 
476
476
  `implementation-planning` task-type 의 Phase 6 plan-body verification 라운드를 끕니다. 기본값은 활성. 다른 task-type 에서는 무시됩니다.
477
477
 
478
- - **활성 (default)**: Phase 6 에서 Report writer worker 가 final-report draft 를 작성한 직후, lead 가 합성된 plan 의 §4.5 본문 (Option Candidates / Stepwise Execution Order / Dependency / Validation Checklist / Rollback) 을 `P-*` plan-item 단위로 쪼개 모든 analyser 워커 (`claude`, `codex`, 그리고 옵트인된 `gemini`) 에게 reverify dispatch 합니다. 워커의 평결 (`AGREE` / `DISAGREE(a-e)` / `SUPPLEMENT`) 을 집계해 4 가지 gate result (`passed` / `passed-with-dissent` / `blocked-by-disagreement` / `aborted-non-result`) 중 하나를 산출하고, `passed` / `passed-with-dissent` 일 때만 final-report 상단의 `- [ ] Approved` 마커가 렌더됩니다. majority DISAGREE 항목은 `## 5. Clarification Items` 의 `Blocks=approval` row 로 변환됩니다 (자동 revise 없음 — 사용자가 답변 후 같은 phase 를 resume 해야 함).
478
+ - **활성 (default)**: Phase 6 에서 Report writer worker 가 final-report draft 를 작성한 직후, lead 가 합성된 plan 의 §4.5 본문 (Option Candidates / Stepwise Execution Order / Dependency / Validation Checklist / Rollback) 을 `P-*` plan-item 단위로 쪼개 모든 analyser 워커 (`claude`, `codex`, 그리고 옵트인된 `gemini`) 에게 reverify dispatch 합니다. 워커의 평결 (`AGREE` / `DISAGREE(a-e)` / `SUPPLEMENT`) 을 집계해 4 가지 gate result (`passed` / `passed-with-dissent` / `blocked-by-disagreement` / `aborted-non-result`) 중 하나를 산출하고, `passed` / `passed-with-dissent` 일 때만 final-report 상단의 `- [ ] Approved` 마커가 렌더됩니다. majority DISAGREE 항목은 `## 1. Clarification Items` 의 `Blocks=approval` row 로 변환됩니다 (자동 revise 없음 — 사용자가 답변 후 같은 phase 를 resume 해야 함).
479
479
  - **비활성 (`--no-plan-verification` 전달 시)**: Phase 6 sub-step 전체가 skip 되고 final-report 상단의 Approval 마커가 무조건 렌더됩니다 (legacy 동작). 빠른 반복용 opt-out — handoff-ready plan 에는 권장하지 않습니다.
480
480
  - 본 flag 는 manifest 의 `convergence.planBodyVerification.enabled` 를 `false` 로 기록합니다. resume 명령에서도 같은 flag 를 명시해야 같은 동작이 유지됩니다 (`_canonical_argv` 가 resume fidelity emit 을 보장).
481
481
  - 자세한 라운드 프로토콜 / verdict semantics / state 파일 스키마는 `skills/okstra-convergence/SKILL.md` 의 "Plan-body verification mode (implementation-planning only)" 섹션 참고.
@@ -237,7 +237,7 @@ Token/cost accounting:
237
237
  | `validate-brief.py`, `validate-brief.sh` | Brief frontmatter/body contract validation |
238
238
  | `validate-report-views.py` | Slim/HTML view validation |
239
239
  | `validate-schedule.py` | Schedule section/order/code validation |
240
- | `validate-implementation-plan-stages.py` | Stage Map 구조 강제 — S1–S8 규칙 검사 (`## 4.5 Stage Map` + `## 4.5.<i> Stage <i>` 섹션, stage 당 step ≤ 6 등) |
240
+ | `validate-implementation-plan-stages.py` | Stage Map 구조 강제 — S1–S8 규칙 검사 (`## 5.5 Stage Map` + `## 5.5.<i> Stage <i>` 섹션, stage 당 step ≤ 6 등) |
241
241
  | `validate_improvement_report.py` | improvement-discovery final-report 의 11항목 contract 강제. `validate-run.py` 가 `task_type == "improvement-discovery"` 일 때 자동 호출 |
242
242
  | `validate-workflow.sh` | End-to-end fixture workflow validation |
243
243
  | `lib/*.sh` | Shared shell validator helpers and fixtures |
@@ -392,7 +392,7 @@ Project-local `<PROJECT_ROOT>/.claude/settings.local.json` is provisioned as a s
392
392
  |---|---|---|
393
393
  | `requirements-discovery` | Classify and route work | `error-analysis` or `implementation-planning` |
394
394
  | `error-analysis` | Reproduce and explain failure | `implementation-planning` |
395
- | `implementation-planning` | Compare options, produce approval-ready plan; 산출은 항상 `## 4.5 Stage Map` + N 개의 `## 4.5.<i> Stage <i>` 섹션 구조. `implementation` 은 stage 단위로 분할 실행 가능 | `implementation` after approval |
395
+ | `implementation-planning` | Compare options, produce approval-ready plan; 산출은 항상 `## 5.5 Stage Map` + N 개의 `## 5.5.<i> Stage <i>` 섹션 구조. `implementation` 은 stage 단위로 분할 실행 가능 | `implementation` after approval |
396
396
  | `implementation` | Executor changes code, verifiers check independently | `final-verification` |
397
397
  | `final-verification` | Read-only acceptance verification | `release-handoff` if accepted |
398
398
  | `release-handoff` | User-selected commit/PR handoff | done or follow-up |
@@ -436,7 +436,7 @@ When changing code, keep these docs in sync:
436
436
  | `Verdict Token` | `accepted`, `conditional-accept`, `blocked`, `not-applicable` |
437
437
  | `Direction` | `continue-investigation`, `begin-implementation`, `approve`, `reject`, `hold` |
438
438
 
439
- Clarifications now live in the unified `## 5. Clarification Items` table. Deprecated `5.1` / `5.2` split sections are no longer part of the schema.
439
+ Clarifications now live in the unified `## 1. Clarification Items` table. Deprecated `5.1` / `5.2` split sections are no longer part of the schema.
440
440
 
441
441
  ---
442
442
 
@@ -0,0 +1,323 @@
1
+ # Compact markdown final-report tables (option X) 구현 계획
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** 정본 final-report `.md` 의 narrative 표를 `meta(<br> stack) + prose` 형태로 재구성해 markdown 에디터에서도 핵심 본문이 넓게 읽히게 한다. §5 Clarification 은 평면 유지.
6
+
7
+ **Architecture:** 변경은 (1) jinja 템플릿 레이아웃 + i18n 라벨 1개, (2) `report_views._inline` 의 `<br>` 보존 + 이제 불필요한 grouping 분기 제거(§5 만 유지)에 한정. `data.json`·report-writer 계약 불변.
8
+
9
+ **Tech Stack:** jinja2 템플릿, Python 3 (report_views, pytest), JSON i18n. 빌드 `npm run build`.
10
+
11
+ **설계 근거:** [`docs/superpowers/specs/2026-06-05-compact-markdown-report-tables-design.md`](../specs/2026-06-05-compact-markdown-report-tables-design.md)
12
+
13
+ **범위 메모:** spec 표 목록(§1, §1.1, §1.2, §3.1, §3.2, §4, Execution Status)에 더해 **§7 Follow-up Tasks** 도 동일 안전 md-merge 대상에 포함한다(코드가 컬럼 파싱하지 않음 — §5 와 달리 안전, 일관성 확보). §5 만 평면 예외.
14
+
15
+ ---
16
+
17
+ ## 파일 구조
18
+
19
+ | 파일 | 책임 | 작업 |
20
+ |---|---|---|
21
+ | [`templates/reports/final-report.template.md`](../../../templates/reports/final-report.template.md) | §1/§1.1/§1.2/§3.1/§3.2/§4/§7/Exec 표를 meta+prose 로 | Modify |
22
+ | [`templates/reports/i18n/ko.json`](../../../templates/reports/i18n/ko.json) · [`en.json`](../../../templates/reports/i18n/en.json) | `columns.recordMeta` 키 추가 | Modify |
23
+ | [`scripts/okstra_ctl/report_views.py`](../../../scripts/okstra_ctl/report_views.py) | `_inline` `<br>` 보존; generic/Exec/§7 grouping 분기 제거(§5 유지) | Modify |
24
+ | [`tests/test_report_views.py`](../../../tests/test_report_views.py) | `<br>` 보존 + §5 유지 + §1/§3/§4 plain 테스트 | Modify |
25
+ | [`CHANGES.md`](../../../CHANGES.md) | 사용자 영향 항목 | Modify |
26
+
27
+ 작업 순서: 템플릿+i18n(.md 구조) → report_views(`<br>` 보존 + 분기 정리) → CHANGES + 전체 검증 + 실제 재렌더.
28
+
29
+ ---
30
+
31
+ ### Task 1: 템플릿 + i18n — narrative 표를 meta(`<br>`)+prose 로
32
+
33
+ **Files:**
34
+ - Modify: `templates/reports/i18n/ko.json`, `templates/reports/i18n/en.json`
35
+ - Modify: `templates/reports/final-report.template.md`
36
+
37
+ - [ ] **Step 1: i18n 에 meta 헤더 키 추가**
38
+
39
+ `templates/reports/i18n/ko.json` 의 `"columns"` 객체에 추가: `"recordMeta": "항목"`.
40
+ `templates/reports/i18n/en.json` 의 `"columns"` 객체에 추가: `"recordMeta": "Record"`.
41
+
42
+ - [ ] **Step 2: §1 Summary 표 교체**
43
+
44
+ `templates/reports/final-report.template.md` 의 §1 표 블록
45
+ ```
46
+ | ID | Ticket ID | {{ t("columns.summary") }} | {{ t("columns.source") }} |
47
+ |----|-----------|------------|----------------------------|
48
+ {% for row in summary -%}
49
+ | {{ row.id }} | `{{ row.ticketId }}` | {{ row.summary }} | {{ row.source }} |
50
+ {% endfor %}
51
+ ```
52
+ 를 다음으로 교체:
53
+ ```
54
+ | {{ t("columns.recordMeta") }} | {{ t("columns.summary") }} |
55
+ |--------|------------|
56
+ {% for row in summary -%}
57
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>{{ t("columns.source") }}: {{ row.source }} | {{ row.summary }} |
58
+ {% endfor %}
59
+ ```
60
+
61
+ - [ ] **Step 3: §1.1 Consensus 표 교체**
62
+
63
+ ```
64
+ | ID | Ticket ID | Statement | Source items (worker:item) | Evidence (path:line / log / worker report) |
65
+ |----|-----------|-----------|----------------------------|---------------------------------------------|
66
+ {% for row in crossVerification.consensus -%}
67
+ | {{ row.id }} | `{{ row.ticketId }}` | {{ row.statement }} | {{ row.sourceItems | join(', ') }} | {{ row.evidence }} |
68
+ {% endfor %}
69
+ ```
70
+ 를:
71
+ ```
72
+ | {{ t("columns.recordMeta") }} | Statement | Evidence (path:line / log / worker report) |
73
+ |--------|-----------|---------------------------------------------|
74
+ {% for row in crossVerification.consensus -%}
75
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Source items: {{ row.sourceItems | join(', ') }} | {{ row.statement }} | {{ row.evidence }} |
76
+ {% endfor %}
77
+ ```
78
+
79
+ - [ ] **Step 4: §1.2 Differences 표 교체**
80
+
81
+ ```
82
+ | ID | Ticket ID | Disagreement | Workers (position + item) | Evidence |
83
+ |----|-----------|--------------|---------------------------|----------|
84
+ {% for row in crossVerification.differences -%}
85
+ | {{ row.id }} | `{{ row.ticketId }}` | {{ row.disagreement }} | {% for w in row.workersPosition %}{{ w.worker }}:{{ w.itemId }} ({{ w.position }}){% if not loop.last %} / {% endif %}{% endfor %} | {{ row.evidence }} |
86
+ {% endfor %}
87
+ ```
88
+ 를:
89
+ ```
90
+ | {{ t("columns.recordMeta") }} | Disagreement | Evidence |
91
+ |--------|--------------|----------|
92
+ {% for row in crossVerification.differences -%}
93
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Workers: {% for w in row.workersPosition %}{{ w.worker }}:{{ w.itemId }} ({{ w.position }}){% if not loop.last %} / {% endif %}{% endfor %} | {{ row.disagreement }} | {{ row.evidence }} |
94
+ {% endfor %}
95
+ ```
96
+
97
+ - [ ] **Step 5: §3.1 Primary Evidence 표 교체**
98
+
99
+ ```
100
+ | ID | Ticket ID | Evidence | Source items (worker:item) | Source (path:line / log) |
101
+ |----|-----------|----------|----------------------------|---------------------------|
102
+ {% for row in evidence.primary -%}
103
+ | {{ row.id }} | `{{ row.ticketId }}` | {{ row.evidence }} | {{ row.sourceItems | join(', ') }} | {{ row.source }} |
104
+ {% endfor %}
105
+ ```
106
+ 를:
107
+ ```
108
+ | {{ t("columns.recordMeta") }} | Evidence |
109
+ |--------|----------|
110
+ {% for row in evidence.primary -%}
111
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}`<br>Source items: {{ row.sourceItems | join(', ') }}<br>Source: {{ row.source }} | {{ row.evidence }} |
112
+ {% endfor %}
113
+ ```
114
+
115
+ - [ ] **Step 6: §3.2 Secondary 표 교체**
116
+
117
+ ```
118
+ | ID | Ticket ID | Hypothesis or supporting evidence | Source / confidence |
119
+ |----|-----------|-----------------------------------|---------------------|
120
+ {% for row in evidence.secondary -%}
121
+ | {{ row.id }} | `{{ row.ticketId }}` | {{ row.hypothesis }} | {{ row.confidence }} |
122
+ {% endfor %}
123
+ ```
124
+ 를:
125
+ ```
126
+ | {{ t("columns.recordMeta") }} | Hypothesis or supporting evidence | Source / confidence |
127
+ |--------|-----------------------------------|---------------------|
128
+ {% for row in evidence.secondary -%}
129
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}` | {{ row.hypothesis }} | {{ row.confidence }} |
130
+ {% endfor %}
131
+ ```
132
+
133
+ - [ ] **Step 7: §4 Risks 표 교체**
134
+
135
+ ```
136
+ | ID | Ticket ID | Item | Risk if ignored | Mitigation Owner |
137
+ |----|-----------|------|-----------------|------------------|
138
+ {% for row in missingInformation -%}
139
+ | {{ row.id }} | `{{ row.ticketId }}` | {{ row.item }} | {{ row.risk }} | {{ row.owner }} |
140
+ ```
141
+ 의 헤더/구분/row 3줄을:
142
+ ```
143
+ | {{ t("columns.recordMeta") }} | Item | Risk if ignored | Mitigation Owner |
144
+ |--------|------|-----------------|------------------|
145
+ {% for row in missingInformation -%}
146
+ | **{{ row.id }}**<br>Ticket: `{{ row.ticketId }}` | {{ row.item }} | {{ row.risk }} | {{ row.owner }} |
147
+ ```
148
+ (`{% endfor %}` 이후는 그대로.)
149
+
150
+ - [ ] **Step 8: Execution Status 표 교체**
151
+
152
+ ```
153
+ | Agent | Role | Model | Status | {{ t("columns.rawTokens") }} | {{ t("columns.billableTokens") }} | {{ t("columns.cost") }} | Duration | Summary of Key Findings |
154
+ |-------|------|-------|--------|-----------|-----------|------------|----------|-------------------------|
155
+ {% for row in executionStatus -%}
156
+ | {{ row.agent }} | {{ row.role }} | {{ row.model }} | {{ row.status }} | {{ row.totalTokens | format_int }}{% if row.cliTotalTokens %} (CLI: {{ row.cliTotalTokens | format_int }}){% endif %} | {{ row.billableTokens | format_int }} | {{ row.costUsd | format_usd }}{% if row.cliCostUsd %} (+ CLI {{ row.cliCostUsd | format_usd }}){% endif %} | {{ row.durationMs | format_duration_ms }} | {{ row.summary }} |
157
+ {% endfor %}
158
+ ```
159
+ 를:
160
+ ```
161
+ | {{ t("columns.recordMeta") }} | Summary of Key Findings |
162
+ |--------|-------------------------|
163
+ {% for row in executionStatus -%}
164
+ | **{{ row.agent }}**<br>Role: {{ row.role }}<br>Model: {{ row.model }}<br>Status: {{ row.status }}<br>{{ t("columns.rawTokens") }}: {{ row.totalTokens | format_int }}{% if row.cliTotalTokens %} (CLI: {{ row.cliTotalTokens | format_int }}){% endif %}<br>{{ t("columns.billableTokens") }}: {{ row.billableTokens | format_int }}<br>{{ t("columns.cost") }}: {{ row.costUsd | format_usd }}{% if row.cliCostUsd %} (+ CLI {{ row.cliCostUsd | format_usd }}){% endif %}<br>Duration: {{ row.durationMs | format_duration_ms }} | {{ row.summary }} |
165
+ {% endfor %}
166
+ ```
167
+
168
+ - [ ] **Step 9: §7 Follow-up Tasks 표 교체**
169
+
170
+ §7 표를 찾아(헤더에 `Title` / `Scope` / `Reason`), 짧은 컬럼(ID, Ticket ID, Origin, New Task ID, Suggested task-type, Priority, Auto-spawn 등)을 meta 셀에 `<br>` stack 하고 Title/Scope/Reason 을 prose 컬럼으로 둔다. 헤더를 `| {{ t("columns.recordMeta") }} | Title | Scope (files/areas) | Reason / Why deferred |` 로, row 의 meta 셀은 `**{{ row.id }}**<br>Ticket: \`{{ row.ticketId }}\`<br>Origin: {{ row.origin }}<br>New Task ID: {{ row.newTaskId }}<br>Type: {{ row.suggestedTaskType }}<br>Priority: {{ row.priority }}<br>Auto-spawn: {{ row.autoSpawn }}` 로 구성(현 row 의 필드명을 그대로 사용 — 먼저 현 §7 row 의 jinja 필드명을 읽어 정확히 매핑할 것). 빈 상태 분기 유지.
171
+
172
+ - [ ] **Step 10: §5 는 건드리지 않음 (확인)**
173
+
174
+ §5 Clarification Items 의 8-컬럼 표(`| ID | Ticket ID | Kind | Statement | Expected form | Blocks | Status | User input |`)는 **변경하지 않는다**. grep 으로 §5 표가 8-컬럼 그대로인지 확인.
175
+
176
+ - [ ] **Step 11: 렌더 스모크 + 커밋**
177
+
178
+ 기존 렌더 테스트로 회귀 확인 후, 픽스처 data.json 으로 렌더해 §1/§1.1/§1.2/§3.1/§3.2/§4/§7/Exec 가 `recordMeta` 헤더 + `<br>` 를 포함하고 §5 가 8-컬럼인지 확인:
179
+ ```bash
180
+ python3 -m pytest tests/test_render_final_report.py tests/test_template_full_render_both_langs.py -q
181
+ ```
182
+ Expected: PASS (템플릿 문법 OK). 실패 시 jinja 문법/필드명 교정.
183
+
184
+ ```bash
185
+ git add templates/reports/final-report.template.md templates/reports/i18n/ko.json templates/reports/i18n/en.json
186
+ git commit -m "feat(report-template): compact narrative tables as meta(<br>)+prose; §5 stays flat"
187
+ ```
188
+
189
+ ---
190
+
191
+ ### Task 2: report_views — `<br>` 보존 + grouping 분기 정리
192
+
193
+ **Files:**
194
+ - Modify: `scripts/okstra_ctl/report_views.py`
195
+ - Modify: `tests/test_report_views.py`
196
+
197
+ - [ ] **Step 1: 실패 테스트 작성 (`<br>` 보존 + §1 plain)**
198
+
199
+ `tests/test_report_views.py` 끝에 추가:
200
+ ```python
201
+ def test_inline_preserves_br_tags():
202
+ from okstra_ctl.report_views import _inline # noqa: PLC0415
203
+ out = _inline("**C-1**<br>Ticket: `DEV-1`<br>Source items: claude:F-001")
204
+ assert "<br>" in out
205
+ assert "&lt;br&gt;" not in out
206
+ assert "<strong>C-1</strong>" in out
207
+ assert "<code>DEV-1</code>" in out
208
+
209
+
210
+ def test_summary_meta_table_renders_plain_with_br():
211
+ # §1 Summary is now pre-merged in the .md (meta col + Summary col); the
212
+ # grouped-table branch must NOT fire (no separate `Ticket ID` column) and
213
+ # the <br> in the meta cell survives.
214
+ html_out = _emit(
215
+ "| 항목 | 한 줄 요약 |",
216
+ "| **P-001**<br>Ticket: `DEV-9184`<br>출처: task-brief.md:19 | " + ("on-the-fly 계산 전환 핵심 변경 " * 4) + " |",
217
+ section="1. Summary of the Problem or Verification Target",
218
+ )
219
+ assert 'class="grouped-table"' not in html_out
220
+ assert "<br>" in html_out
221
+ ```
222
+
223
+ - [ ] **Step 2: 실패 확인**
224
+
225
+ Run: `python3 -m pytest tests/test_report_views.py -q -k "inline_preserves_br or summary_meta_table"`
226
+ Expected: FAIL — `_inline` escapes `<br>`; §1 still hits the generic grouping branch (grouped-table present).
227
+
228
+ - [ ] **Step 3: `_inline` 가 `<br>` 보존**
229
+
230
+ `scripts/okstra_ctl/report_views.py` `_inline` 의 `return out` 직전에 추가:
231
+ ```python
232
+ # Preserve explicit <br> line breaks used inside compact meta cells (the
233
+ # markdown source intentionally stacks short fields with <br>). They are
234
+ # escaped to &lt;br&gt; by html.escape above; restore the tag.
235
+ out = out.replace("&lt;br&gt;", "<br>").replace("&lt;br/&gt;", "<br>").replace("&lt;br /&gt;", "<br>")
236
+ ```
237
+
238
+ - [ ] **Step 4: generic/Exec/§7 grouping 분기 제거 (§5 유지)**
239
+
240
+ `_grouped_table_spec` 에서 다음을 **삭제**:
241
+ - "Execution Status by Agent" 분기 (`if len(norm) >= 3 and norm[0] == "Agent" ...`).
242
+ - "§7 Follow-up Tasks" 분기 (`if any("Follow-up Tasks" in h ...)`).
243
+ - generic "Ticket ID" 분기 (`if any(h == "Ticket ID" for h in norm): ...`) + 그 헬퍼 `_is_force_meta`, `_column_is_wide`, 상수 `_WIDE_PROSE_TOKENS`, `_FORCE_META_TOKENS`, `_WIDE_CONTENT_THRESHOLD`, `_FOLLOWUP_WIDE_PREFIXES` (이제 미사용).
244
+ - **유지:** §5 Clarification 분기(Expected form/Statement/User input wide) + signature 의 `rows` 파라미터(호출부 호환). `rows` 가 더는 분기 로직에 안 쓰이면, 호출부 `_grouped_table_spec(header_cells, rows, section_path)` 도 `_grouped_table_spec(header_cells, section_path)` 로 되돌리고 시그니처에서 `rows` 제거(미사용 인자 정리).
245
+
246
+ docstring 을 "§5 Clarification Items 만 grouped (interactive form). 나머지 narrative 표는 템플릿에서 이미 compact 하게 렌더되므로 여기서 grouping 하지 않는다." 로 갱신.
247
+
248
+ - [ ] **Step 5: 통과 확인 + 기존 grouped 테스트 정리**
249
+
250
+ Run: `python3 -m pytest tests/test_report_views.py -q`
251
+ Expected: 신규 2개 PASS. **단, Task 직전(2343e30)에 추가한 §1/§3/§4 grouped 테스트**(`test_summary_table_groups_short_cols_and_widens_prose`, `test_risks_table_widens_item_risk_mitigation`)는 이제 의도가 바뀌었으므로 제거하거나 plain+`<br>` 기대로 갱신한다. `test_clarification_expected_form_is_wide_not_meta` 는 **유지**(§5 grouping 살아있음). 모든 report_views 테스트 PASS 확인.
252
+
253
+ - [ ] **Step 6: 빌드 + 커밋**
254
+
255
+ ```bash
256
+ npm run build && bash validators/validate-workflow.sh
257
+ git add scripts/okstra_ctl/report_views.py tests/test_report_views.py runtime/
258
+ git commit -m "refactor(report-views): preserve <br>; group only §5 (narrative tables compact in md)"
259
+ ```
260
+
261
+ ---
262
+
263
+ ### Task 3: CHANGES + 전체 검증 + 실제 재렌더
264
+
265
+ **Files:**
266
+ - Modify: `CHANGES.md`
267
+
268
+ - [ ] **Step 1: CHANGES 항목 추가**
269
+
270
+ `## 2026-06-05` 아래(직전 report-views 항목 근처)에 삽입:
271
+ ```markdown
272
+ ### feat(report-template): 정본 final-report `.md` 표를 compact 하게 (meta + prose)
273
+
274
+ - markdown 표는 컬럼 병합이 안 돼 ID·Ticket·Source 같은 짧은 코드 컬럼이 칸을 차지하면 요약·근거·이견·위험 같은 긴 본문이 좁아져 뭉개졌다(에디터에서 한 글자/줄). 이제 §1 Summary·§1.1 Consensus·§1.2 Differences·§3.1/§3.2 Evidence·§4 Risks·§7 Follow-up·Execution Status 표를 **짧은 코드 필드는 `<br>` 로 한 meta 셀에 stack + 긴 본문은 별도 컬럼**으로 렌더한다. §5 Clarification 은 carry-in 파서·validator 8-컬럼 계약 때문에 평면 유지(§5 compact 는 HTML view grouping 담당). `data.json`·report-writer 계약은 불변(템플릿 레이아웃만). HTML self-contained view 도 `_inline` 이 `<br>` 를 보존해 동일하게 compact 하게 보인다.
275
+ - 사용자 영향: 다음 release + `npx -y okstra@latest install` 후 적용. 이제 final-report 를 어떤 markdown 에디터로 열어도 핵심 본문이 넓게 읽힌다. `.md`↔HTML 레이아웃이 일관된다.
276
+ ```
277
+
278
+ - [ ] **Step 2: 전체 검증**
279
+
280
+ ```bash
281
+ npm run build
282
+ python3 -m pytest tests/ -q
283
+ bash validators/validate-workflow.sh
284
+ node bin/okstra --version
285
+ ```
286
+ ALL must pass (worktree 격리 flake 예외는 단독 재실행 확인). 그 외 실패 → STOP/BLOCKED.
287
+
288
+ - [ ] **Step 3: 실제 재렌더 검증 (BLOCKING — 육안)**
289
+
290
+ 기존 사용자 리포트를 /tmp 복사본으로 재렌더하고 구조 확인:
291
+ ```bash
292
+ SRC="/Volumes/Workspaces/workspace/projects/FontsNinja/app/fontradar-v2-api/.okstra/tasks/calcule-des-prix-1-1/dev-9184/runs/requirements-discovery/reports/final-report-requirements-discovery-001.data.json"
293
+ ```
294
+ NOTE: HTML view 는 `.md` 에서 파생되므로, **새 템플릿으로 `.md` 를 다시 렌더**해야 한다. data.json → md 렌더는 `scripts/okstra-render-final-report.py`(또는 report-writer 경로) 사용. 해당 CLI 의 인자를 `--help` 로 확인 후, /tmp 에 새 `.md` 를 렌더하고:
295
+ - 새 `.md` 의 §1/§1.1/§1.2/§3.1/§3.2/§4/§7/Exec 가 `**ID**<br>Ticket: …` meta 셀 + prose 컬럼인지,
296
+ - §5 가 여전히 `| ID | Ticket ID | Kind | Statement | Expected form | Blocks | Status | User input |` 8-컬럼인지,
297
+ - 그 `.md` 로 `okstra-render-report-views.py` 를 돌려 HTML 의 meta 셀이 `<br>` 줄바꿈으로 보이는지(literal `&lt;br&gt;` 아님),
298
+ - `python3 -c "import sys; sys.path.insert(0,'scripts'); from okstra_ctl.clarification_items import parse_clarification_items; print(len(parse_clarification_items(open('<new-md>').read()) or []))"` 로 §5 carry-in 파서가 여전히 행을 파싱하는지
299
+ 확인. /tmp 산출물은 정리(사용자 프로젝트 파일은 건드리지 않음).
300
+
301
+ - [ ] **Step 4: 최종 커밋**
302
+
303
+ ```bash
304
+ git add CHANGES.md
305
+ git commit -m "docs(changes): log compact markdown final-report tables"
306
+ ```
307
+
308
+ ---
309
+
310
+ ## Self-Review (작성자 체크리스트)
311
+
312
+ **1. Spec coverage**
313
+ - §2.1 대상 표 7종 + §7 → Task 1 Step 2–9.
314
+ - §2.1 §5 평면 유지 → Task 1 Step 10 + 미변경.
315
+ - §2.2 meta `<br>` 포맷 + i18n → Task 1 Step 1–9.
316
+ - §2.3 `_inline` `<br>` 보존 → Task 2 Step 3.
317
+ - §2.3 generic/Exec 분기 제거, §5 유지 → Task 2 Step 4(§7 도 제거).
318
+ - §2.4 계약 불변 → Task 1(템플릿만), §5 미변경.
319
+ - §4 검증(실제 재렌더 + §5 파서) → Task 3 Step 2–3.
320
+
321
+ **2. Placeholder scan:** §7(Step 9)은 현 row 필드명을 "먼저 읽어 매핑"하라고 명시 — 구현자가 실제 필드명 확인 후 작성(추측 금지). 그 외 모든 jinja/코드 블록은 실제 내용. TBD 없음.
322
+
323
+ **3. 식별자 일관성:** `columns.recordMeta`(i18n, Task1) ↔ 템플릿 헤더 사용 동일. `_inline`(Task2) ↔ report_views. `_grouped_table_spec` 시그니처에서 `rows` 제거 시 호출부도 함께(Task2 Step4 명시).