okstra 0.27.0 → 0.29.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.
- package/bin/okstra +1 -0
- package/docs/superpowers/plans/2026-05-17-dual-format-final-report.md +167 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +6 -5
- package/runtime/agents/workers/codex-worker.md +5 -4
- package/runtime/agents/workers/gemini-worker.md +5 -4
- package/runtime/agents/workers/report-writer-worker.md +10 -3
- package/runtime/bin/okstra-render-report-views.py +129 -0
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +12 -4
- package/runtime/prompts/profiles/implementation-planning.md +1 -1
- package/runtime/python/okstra_ctl/report_views.py +701 -0
- package/runtime/python/okstra_token_usage/cli.py +9 -2
- package/runtime/python/okstra_token_usage/report.py +32 -3
- package/runtime/skills/okstra-convergence/SKILL.md +2 -2
- package/runtime/skills/okstra-report-writer/SKILL.md +25 -8
- package/runtime/skills/okstra-team-contract/SKILL.md +16 -15
- package/runtime/templates/reports/final-report.template.md +398 -211
- package/runtime/templates/reports/report.css +151 -0
- package/runtime/templates/reports/report.js +163 -0
- package/runtime/templates/reports/user-response.template.md +69 -0
- package/runtime/validators/lib/fixtures.sh +76 -2
- package/runtime/validators/validate-report-views.py +283 -0
- package/runtime/validators/validate-run.py +564 -4
- package/runtime/validators/validate-workflow.sh +4 -0
- package/src/install.mjs +1 -0
- package/src/render-views.mjs +67 -0
package/bin/okstra
CHANGED
|
@@ -15,6 +15,7 @@ const COMMANDS = new Map([
|
|
|
15
15
|
["worktree-lookup", () => import("../src/worktree-lookup.mjs").then((m) => m.run)],
|
|
16
16
|
["plan-validate", () => import("../src/plan-validate.mjs").then((m) => m.run)],
|
|
17
17
|
["render-bundle", () => import("../src/render-bundle.mjs").then((m) => m.run)],
|
|
18
|
+
["render-views", () => import("../src/render-views.mjs").then((m) => m.run)],
|
|
18
19
|
["wizard", () => import("../src/wizard.mjs").then((m) => m.run)],
|
|
19
20
|
]);
|
|
20
21
|
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Dual-Format Final Report (AI MD slim + Human HTML) Implementation Plan
|
|
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:** Phase 6 의 final-report 산출물에 두 가지 파생 view(AI 용 토큰-절약형 `*.slim.md`, 사람 용 self-contained `*.html`)를 추가한다. HTML 안에서 사용자가 의사결정 입력을 채우면 “Export to MD” 버튼이 그 입력을 AI 가 다음 phase 입력으로 바로 쓸 수 있는 user-response markdown 으로 직렬화한다. 원본 final-report MD 는 단일 source-of-truth 로 유지되며 사용자가 손으로 머지하지 않는다.
|
|
6
|
+
|
|
7
|
+
**Architecture:**
|
|
8
|
+
- **Source of truth:** 기존 `runs/<task-type>/reports/final-report-<task-type>-<seq>.md`. 변경 없음. report-writer-worker 의 작성 계약은 그대로.
|
|
9
|
+
- **파생 산출물:**
|
|
10
|
+
- `final-report-<task-type>-<seq>.slim.md` — 결정론적 stripper 가 원본 MD 에서 생성. 토큰 부담이 큰 섹션(장식 헤더, sentinel/placeholder 셀, verdict-detail 중복, 캡션, 구분선)을 규칙 기반으로 제거.
|
|
11
|
+
- `final-report-<task-type>-<seq>.html` — 결정론적 renderer 가 원본 MD 에서 생성. 단일 파일, 인라인 CSS, vanilla JS 만 사용(외부 CDN 금지). Section 5(Clarification Items) / Section 6(Recommended Next Steps) / Section 7(Follow-up Tasks) 의 “user-actionable” 행에 자동으로 `<form>` 컨트롤이 부착된다.
|
|
12
|
+
- **사용자 응답 경로:** HTML 안의 “Export user response” 버튼은 폼 값을 `.md` 텍스트(고정 스키마)로 직렬화하여 페이지 하단 `<pre>` 에 출력하고 클립보드 복사 버튼을 제공한다. 사용자는 그 텍스트를 `runs/<task-type>/user-responses/user-response-<task-type>-<seq>.md` 경로에 붙여넣어 저장한다(Phase 7 후속 단계 또는 다음 phase 의 lead 가 그 파일을 입력으로 소비). **원본 final-report MD 는 절대 수정하지 않는다.**
|
|
13
|
+
- **용어 충돌 회피:** 2026-05-17 머지된 변경에서 worker-results audit 파일은 "audit sidecar"로, Section 0 Reading Confirmation 은 그 audit sidecar 전용으로 단일화되었다. 따라서 본 plan 의 "decision sidecar" 용어는 **사용 금지**. 사용자 입력 산출물은 `user response` / `user-response-*.md` 로 일관되게 부른다. 경로도 `decisions/` 가 아닌 `user-responses/`.
|
|
14
|
+
- **호출 경로 single reference:** 두 변환은 모두 새 Python 모듈 `scripts/okstra_ctl/report_views.py` 한 곳에서 처리한다. 다음 세 entrypoint 가 같은 함수를 호출:
|
|
15
|
+
1. `scripts/okstra-render-report-views.py` (CLI, Phase 7 step 0 에서 호출)
|
|
16
|
+
2. `bin/okstra render-views <task-key>` (node CLI 래퍼)
|
|
17
|
+
3. report-writer-worker SKILL 의 Persistence Checklist 항목 (실패 시 lead fallback)
|
|
18
|
+
|
|
19
|
+
**Tech Stack:** Python 3 (markdown stripper + HTML renderer, 표준 라이브러리만 — `markdown` 의존성 추가 금지), pytest fixtures, HTML5 + vanilla JS, JSON Schema(hand-rolled) for decision sidecar.
|
|
20
|
+
|
|
21
|
+
> 참고: 본 plan 은 **Phase 6 → Phase 7 실행 순서(BLOCKING order)** 의 step 2(token substitution) 직후, step 3(follow-up spawner) 전에 “step 2.5 — render report views” 를 삽입한다. token placeholder 가 치환된 MD 가 두 view 의 입력이어야 슬림 MD/HTML 에도 실제 토큰 숫자가 들어간다. 이 순서는 BLOCKING.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## File Structure
|
|
26
|
+
|
|
27
|
+
| 경로 | 책임 | 변경 종류 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `scripts/okstra_ctl/report_views.py` | `slim_markdown(src_md) -> str`, `render_html(src_md, *, run_meta) -> str`, `serialize_user_response(form_payload) -> str` 의 단일 reference 구현 | Create |
|
|
30
|
+
| `scripts/okstra-render-report-views.py` | 위 모듈을 호출하는 CLI entrypoint. 입력: 원본 final-report 경로. 출력: `*.slim.md`, `*.html` | Create |
|
|
31
|
+
| `bin/okstra` (또는 `src/commands/render-views.mjs`) | node CLI `okstra render-views` 서브커맨드 — 단순 위임 | Modify (or Create one file) |
|
|
32
|
+
| `skills/okstra-report-writer/SKILL.md` | Phase 6→7 sequence 에 step 2.5 추가, Persistence Checklist 에 두 산출물 항목 추가 | Modify |
|
|
33
|
+
| `agents/workers/report-writer-worker.md` | required-reading 에 본 plan / 결정 sidecar 스키마 링크 추가 | Modify |
|
|
34
|
+
| `templates/reports/final-report.template.md` | 변경 없음 (source of truth 유지). 단 “user-actionable 행” 인식 마커(예: 행 ID `D-*` for decision) 가 이미 존재하는지 확인하고 없으면 Section 5 / 6 / 7 의 row 스키마에 `Response ID` 컬럼만 옵션으로 추가 | Modify (좁은 범위) |
|
|
35
|
+
| `templates/reports/user-response.template.md` | 사용자 응답 직렬화 포맷의 single reference (예: 각 Response ID 별 한 블록, frontmatter 포함) | Create |
|
|
36
|
+
| `templates/reports/report.css` | HTML view 의 인라인 임포트 대상 (build 시 인라인 됨). 외부 CDN 없음 | Create |
|
|
37
|
+
| `templates/reports/report.js` | HTML view 의 인라인 임포트 대상. form → MD 직렬화 + 복사 로직 | Create |
|
|
38
|
+
| `validators/validate-report-views.py` | 원본 MD 의 결정 행 수와 HTML/slim/sidecar 스키마 일관성 검사. Phase 7 의 validator 체인에 추가 | Create |
|
|
39
|
+
| `tests/fixtures/reports/golden-original.md` | 결정/비결정 행을 모두 포함한 합성 final-report | Create |
|
|
40
|
+
| `tests/fixtures/reports/golden-original.slim.md` | 위 입력의 기대 slim 산출물 | Create |
|
|
41
|
+
| `tests/fixtures/reports/golden-original.html` | 위 입력의 기대 HTML 산출물(테스트는 normalize 후 diff) | Create |
|
|
42
|
+
| `tests/test_report_views.py` | golden fixture round-trip + sidecar 직렬화 단위 테스트 | Create |
|
|
43
|
+
| `CHANGES.md` | 사용자 영향 한 줄 추가 | Modify |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Convention Notes (모든 task 공통)
|
|
48
|
+
|
|
49
|
+
- 모든 변경은 **end-user seed 경로**(repo 안 `scripts/`, `skills/`, `agents/`, `templates/`, `validators/`)에 들어가며, 빌드 후 `runtime/` 에 동기화된다. 개인 `~/.claude/` 직접 편집 금지(MEMORY: `feedback_okstra_fixes_target_end_users.md`).
|
|
50
|
+
- HTML 은 반드시 **단일 `.html` 파일**. `<script src=>` / `<link href=>` 의 외부 URL 금지. 차트는 인라인 SVG 로 직접 그린다(필요 시 추후 plan 에서 작은 sparkline 모듈 추가). 이 제약은 self-contained 정책의 BLOCKING 요건.
|
|
51
|
+
- Slim MD 가 제거하는 항목은 **결정론적**이어야 한다. “LLM 이 보기에 안 중요해 보이는 문장”을 제거하지 않는다. 제거 규칙은 module docstring + `templates/reports/user-response.template.md` 헤더에 명시.
|
|
52
|
+
- 사용자가 만든 user-response 파일은 `runs/<task-type>/user-responses/` 아래에 둔다. 이 경로는 새로 추가. validator 는 파일이 없어도 OK(사용자 의사결정이 필요 없는 run 일 수 있다); 있을 때만 스키마 검사.
|
|
53
|
+
- **Slim/HTML 양쪽 모두에 대한 “absolutely preserve” invariant** (validator hard-fail 방지 목적, BLOCKING):
|
|
54
|
+
- `## Verdict Card` 블록 전체 (셀 byte-identical 유지 — whitespace 정규화 금지)
|
|
55
|
+
- `## 2. Final Verdict` 표의 `Verdict Token` / `Direction` / `Next Step` 행
|
|
56
|
+
- `## 0. Clarification Response Carried In` (존재 시)
|
|
57
|
+
- `implementation-planning` 의 9개 substring (`Option Candidates` / `Trade-off` / `Recommended Option` / `Stepwise Execution Order` / `Dependency` / `Validation Checklist` / `Rollback` / `User Approval Request` / `Plan Body Verification` + `Gate result:`)
|
|
58
|
+
- `implementation` 의 §4.7 8개 sub-section substring (Approved Plan Reference / Commit List / Diff Summary / Out-of-plan Edits / Validation Evidence / Verifier Results / Rollback Verification / Routing Recommendation)
|
|
59
|
+
- `final-verification` 의 §4.8 6개 sub-section substring (Source Implementation Report / Acceptance Blockers / Residual Risk / Validation Evidence / Read-only Command Log / Routing Recommendation)
|
|
60
|
+
- `release-handoff` 의 §4.6 모든 sub-section (4.6.1–4.6.7) **포함 §4.6.6 Merge Conflict Probe**
|
|
61
|
+
Slim stripper 는 이 substring/블록이 포함된 행 또는 그 행에서 직접 참조하는 표/리스트 cell 을 제거하지 않는다. HTML renderer 도 같다(다만 `<details>` 접힘은 허용).
|
|
62
|
+
- Pre-1.0 정책(MEMORY: `feedback_pre_v1_no_compat.md`): 기존 final-report 와 호환되지 않는 행 스키마 변경이 필요하면 즉시 도입한다. compat shim 만들지 않는다.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Tasks
|
|
67
|
+
|
|
68
|
+
### Task 1 — Stripper + Renderer 모듈 (`report_views.py`) 작성
|
|
69
|
+
|
|
70
|
+
- [ ] 1.1 입력/출력 spec 을 module docstring 에 fix. slim 의 제거 규칙(아래)을 enum 으로 명시:
|
|
71
|
+
- `STRIP_TOKEN_PLACEHOLDER_ZEROS` (`$0.00` 행 — `Codex/Gemini CLI 추가 비용` 만 예외로 keep)
|
|
72
|
+
- `STRIP_DECORATIVE_HR` (3개 이상 `---` 연속 구분선)
|
|
73
|
+
- `STRIP_VERDICT_DETAIL` (`§4.5.9 Verdict details` 의 wide matrix — `Verdict summary` 카드만 keep)
|
|
74
|
+
- `STRIP_INTRO_RECIPE` (“> **읽는 법**:” 인용 단락 — 정의 라인은 keep)
|
|
75
|
+
- `COLLAPSE_EMPTY_SECTIONS` (`- 후속 작업 없음.` / `- 합의 미달 항목 없음.` 류 한 줄 placeholder 는 keep, 그러나 그 위 빈 줄 2개 이상은 1개로 정규화)
|
|
76
|
+
- [ ] 1.2 `slim_markdown` 구현 — line-based scanner. 정규식은 module top-level 에 컴파일.
|
|
77
|
+
- [ ] 1.3 `render_html` 구현 — 원본 MD 를 자체 mini-parser(테이블 / 헤딩 / 코드블록 / 인용 / 리스트 / 인라인 코드 / link 만 지원)로 토큰화 후 HTML emit. 외부 markdown 라이브러리 import 금지(제로 의존성). 헤딩 ID 는 slugify. `<a class="toc">` 자동 생성.
|
|
78
|
+
- [ ] 1.4 “User-response row” 검출 — Section 5/6/7 의 표 안에서 `Response ID` 컬럼이 비어 있지 않은 행을 만나면, 그 셀들을 `<form data-response-id="...">` 안에 wrap. 각 행 타입별 컨트롤 매핑:
|
|
79
|
+
- 자유 텍스트 응답 → `<textarea>`
|
|
80
|
+
- 단일 선택(예: accept / reject / hold) → `<select>` 또는 `<input type=radio>`
|
|
81
|
+
- 다중 선택 → `<input type=checkbox>` 그룹
|
|
82
|
+
- 승인 게이트 → `<input type=checkbox> I approve`
|
|
83
|
+
컨트롤 종류는 행의 `Response Type` 옵션 컬럼(또는 `templates/reports/user-response.template.md` 의 enum)으로 결정. 알 수 없으면 textarea fallback.
|
|
84
|
+
**제외 대상(폼 부착 금지):** §4.6 sub-sections(release-handoff deliverables — H1/H2/H3 메뉴로 이미 처리됨, §4.6.6 Merge Conflict Probe 포함), §4.7 sub-sections(implementation deliverables — read-only 결과 보고), §4.8 sub-sections(final-verification deliverables — 동일). 이들 섹션은 Response ID 가 있더라도 무시하고 raw 텍스트로 렌더한다.
|
|
85
|
+
- [ ] 1.5 `serialize_user_response(form_payload)` 구현 — 입력은 JS 측에서 보낼 dict. 출력은 `user-response.template.md` 포맷의 문자열. Python 측에서도 호출 가능(server-side fallback 용)하지만 1차 구현은 JS 가 클라이언트에서 직접 같은 포맷을 만든다 — Python 의 이 함수는 **그 직렬화 결과가 스키마에 맞는지 검증**하는 reference (validator 가 호출).
|
|
86
|
+
|
|
87
|
+
### Task 2 — `templates/reports/user-response.template.md` 작성
|
|
88
|
+
|
|
89
|
+
- [ ] 2.1 frontmatter 스키마 정의: `task-key`, `task-type`, `seq`, `source-report`, `decisions[].id`, `decisions[].type`, `decisions[].value`, `decisions[].rationale?`, `created-by: user`, `created-at: <ISO>`.
|
|
90
|
+
- [ ] 2.2 본문 포맷: Response ID 별로 `### D-001 — <title>` 헤딩 + `Type: <select|radio|checkbox|textarea|approval>` 라인 + `Value:` 블록 + 선택적 `Rationale:`.
|
|
91
|
+
- [ ] 2.3 “Why” 주석 한 줄(`<!-- single source of truth: scripts/okstra_ctl/report_views.py serialize_user_response -->`).
|
|
92
|
+
|
|
93
|
+
### Task 3 — HTML 자산 (`templates/reports/report.css`, `report.js`)
|
|
94
|
+
|
|
95
|
+
- [ ] 3.1 `report.css` — 읽기 폭 80ch 최대, 테이블 sticky header, `details` 토글, 인쇄 대응(`@media print`). 색은 시스템 색(`Canvas` / `CanvasText`) 기반으로 다크모드 자동.
|
|
96
|
+
- [ ] 3.2 `report.js` —
|
|
97
|
+
- `collectFormValues()` 함수: 모든 `[data-decision-id]` form 의 현재 값을 수집.
|
|
98
|
+
- `buildSidecarMarkdown(values)` 함수: `templates/reports/user-response.template.md` 스키마와 **byte-identical** 출력을 만들도록 작성. (Task 4 의 golden fixture 가 Python ↔ JS 결과를 동시 검증.)
|
|
99
|
+
- 상단/하단에 각각 `[Export decision sidecar]` 버튼, 출력 `<pre id="sidecar-output">`, `[Copy]` 버튼. Copy 는 `navigator.clipboard.writeText`, 실패 시 selectRange fallback.
|
|
100
|
+
- [ ] 3.3 HTML renderer 는 빌드 시 두 파일을 `<style>` / `<script>` 로 인라인. 외부 참조 0.
|
|
101
|
+
|
|
102
|
+
### Task 4 — Golden fixtures + 테스트
|
|
103
|
+
|
|
104
|
+
- [ ] 4.1 `tests/fixtures/reports/golden-original.md` 작성 — 다음을 모두 포함: token 치환 후의 비용 행(`$26.55` 등), §4.5.9 Verdict details, §0 Clarification Response Carried In, §5 Clarification Items 표(Response ID 2개), §6 Recommended Next Steps(Response ID 1개 — approval), §7 Follow-up Tasks(자동 생성 1행). **추가로 phase-별 fixture 3개**: `golden-implementation.md`(§4.7 8 sub-section 모두 포함), `golden-final-verification.md`(§4.8 6 sub-section + Verdict Token=`accepted|conditional-accept|blocked` 한 케이스씩), `golden-release-handoff.md`(§4.6.1–4.6.7 모두 포함, §4.6.6 Merge Conflict Probe 포함). 각 fixture 의 slim/html 기대 산출물을 동봉.
|
|
105
|
+
- [ ] 4.2 기대 산출물 작성 — `.slim.md`, `.html`. HTML 비교는 (a) DOCTYPE + 인라인 자산 strip, (b) whitespace normalize 후 byte diff.
|
|
106
|
+
- [ ] 4.3 `tests/test_report_views.py` — slim/HTML round-trip + `serialize_user_response` 가 example payload 로 스키마 검증 통과 + JS 직렬화 결과(Node 로 `templates/reports/report.js` 의 `buildSidecarMarkdown` 호출)와 Python 결과가 동일한지 확인하는 cross-check.
|
|
107
|
+
|
|
108
|
+
### Task 5 — Phase 6→7 sequence 통합
|
|
109
|
+
|
|
110
|
+
- [ ] 5.1 `skills/okstra-report-writer/SKILL.md` — “Phase 6 → Phase 7 execution sequence (BLOCKING order)” 섹션에 **step 2.5 — Render report views (BLOCKING)** 를 추가. 명령:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
python3 scripts/okstra-render-report-views.py \
|
|
114
|
+
<runDirectoryPath>/reports/final-report-<task-type>-<seq>.md
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
step 2 이후 step 3 이전. 실패 시 lead 가 자체 호출 후 fallback. 두 산출물(`*.slim.md`, `*.html`) 부재 시 validator 가 run 을 실패시킨다(아래 Task 6).
|
|
118
|
+
- [ ] 5.2 Persistence Checklist 에 두 항목 추가:
|
|
119
|
+
- `[ ] Slim AI report: runs/<task-type>/reports/final-report-<task-type>-<seq>.slim.md`
|
|
120
|
+
- `[ ] Human HTML report: runs/<task-type>/reports/final-report-<task-type>-<seq>.html`
|
|
121
|
+
- [ ] 5.3 `agents/workers/report-writer-worker.md` 의 required-reading 에 본 plan + `user-response.template.md` 추가. 단, **report-writer worker 자체는 HTML/slim 을 만들지 않는다** — 그건 phase 7 의 결정론적 스크립트의 책임. worker 는 원본 MD 만 쓴다(현재와 동일).
|
|
122
|
+
- [ ] 5.4 `skills/okstra-report-writer/SKILL.md` 의 작성자 표기는 2026-05-17 머지의 **Report Owner: Claude lead / Report Author: `<Report writer worker | Claude lead (release-handoff or fallback)>`** 용어를 그대로 따른다(별도 신규 용어 도입 금지).
|
|
123
|
+
- [ ] 5.5 Analysis worker(`claude/codex/gemini-worker.md`) 의 required-reading 은 **건드리지 않는다** — 2026-05-17 정정에서 final-report template 을 의도적으로 제외했다. slim/HTML renderer 는 그 worker 들의 입력이 아니다.
|
|
124
|
+
|
|
125
|
+
### Task 6 — Validator
|
|
126
|
+
|
|
127
|
+
- [ ] 6.1 `validators/validate-report-views.py` 작성 — 원본 MD 가 있는 run 폴더에서:
|
|
128
|
+
- 두 파생 파일이 존재하는가
|
|
129
|
+
- slim 의 헤딩 집합이 원본의 헤딩 집합의 부분집합인가
|
|
130
|
+
- **slim 이 `validators/validate-run.py` 가 검사하는 모든 substring(Verdict Card 셀, §2 Verdict Token, implementation-planning 9개, implementation §4.7 8개, final-verification §4.8 6개, release-handoff §4.6 + §4.6.6)을 원본과 byte-identical 로 보존하는가** — 신규 strip 규칙이 future validator 와 충돌하는 회귀를 차단.
|
|
131
|
+
- HTML 에서 검출된 Response ID 집합이 원본 MD 의 Response ID 집합과 동일한가
|
|
132
|
+
- HTML 의 §4.6 / §4.7 / §4.8 안에 `<form>` 또는 `<input>` 가 **없는가** (deliverable section 에 폼 부착 금지 규칙)
|
|
133
|
+
- 외부 URL(`http(s)://`) 이 HTML 의 `<script src>` / `<link href>` / `<img src>` 에 등장하지 않는가 (self-contained 보장)
|
|
134
|
+
- [ ] 6.2 `validators/validate-run.py` 의 기존 phase-별 substring 검사 직후에 위 호출을 hook. 실패 시 `exit 1`. 2026-05-17 머지된 Verdict Card byte-match / §4.6.6 / verdict vocabulary / §4.7 / §4.8 검사와 같은 단계에서 실행되도록 한다.
|
|
135
|
+
|
|
136
|
+
### Task 7 — Node CLI 위임 + 문서
|
|
137
|
+
|
|
138
|
+
- [ ] 7.1 `src/commands/render-views.mjs` (또는 기존 command 디렉토리 규약에 맞춰) 추가. 단순히 `~/.okstra/lib/python/.../okstra-render-report-views.py` 를 spawn. 인자 파싱만 JS.
|
|
139
|
+
- [ ] 7.2 `bin/okstra` 의 서브커맨드 라우팅 테이블에 등록.
|
|
140
|
+
- [ ] 7.3 `node bin/okstra doctor` 가 새 스크립트의 존재 확인 항목을 출력하는지 확인(있으면 추가, 없으면 보류).
|
|
141
|
+
- [ ] 7.4 `CHANGES.md` 한 줄 추가:
|
|
142
|
+
- `feat(report-views): Phase 7 이 final-report 외에 *.slim.md(AI용 토큰절약) + *.html(self-contained, 결정폼 포함) 두 view 를 자동 생성. 사용자 결정은 HTML 의 [Export decision sidecar] 버튼으로 markdown 직렬화 후 runs/<task-type>/decisions/ 에 저장. 사용자 영향: 보고서 디렉토리에 두 파일 추가, 원본 MD 형식 변경 없음.`
|
|
143
|
+
|
|
144
|
+
### Task 8 — End-to-end smoke
|
|
145
|
+
|
|
146
|
+
- [ ] 8.1 `tests-e2e/` 아래에 새 시나리오 추가(또는 기존 scenario-01 에 후속 step 추가): 작은 fake run 의 phase 6 산출물에 대해 render-views 를 호출하고, 두 파일이 생성되며 validator 가 통과하는지.
|
|
147
|
+
- [ ] 8.2 실제 브라우저에서 HTML 을 열고 (a) form 선택, (b) Export 버튼 클릭, (c) `<pre>` 출력이 `serialize_user_response` Python 출력과 byte-match 하는지 수동 확인. (CLAUDE.md: UI 변경은 브라우저에서 직접 확인.)
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Out of scope (본 plan 에서 하지 않음)
|
|
152
|
+
|
|
153
|
+
- 사용자가 만든 decision sidecar 를 **다음 phase 의 lead 가 어떻게 소비하는지** 의 계약 변경. 그건 별도 plan 으로 다룬다 (phase profile 의 required-reading 에 sidecar 경로 추가, lead 가 sidecar 의 Response ID 별 응답을 prompt 에 어떻게 인용하는지 등).
|
|
154
|
+
- 차트 라이브러리 도입. 현재는 인라인 SVG 의 sparkline 정도만. 본격적인 시각화는 후속 plan.
|
|
155
|
+
- 원본 final-report 의 한국어 본문 자체를 줄이는 prompt-diet 작업(이건 v2 plan 의 P2 범위).
|
|
156
|
+
- `release-handoff` 단독 lead 분기에서의 동작 차이. 일단 같은 스크립트를 호출하되, release-handoff 의 Section 4.6 은 사용자 결정 행이 이미 H1/H2/H3 메뉴로 처리되므로 form 부착 대상에서 제외하는 special-case 만 Task 1.4 단계에서 추가.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Open questions for approval
|
|
161
|
+
|
|
162
|
+
다음 항목에 대해 사용자 응답을 기다린다 (구현 시작 전 BLOCKING):
|
|
163
|
+
|
|
164
|
+
1. Response ID 컬럼을 Section 5/6/7 의 표 스키마에 **신설**해도 되는가? 또는 §5 의 기존 `C-*`(Clarification) ID 를 그대로 user-response 트리거로 재사용해야 하는가? — 후자라면 `templates/reports/final-report.template.md` 수정이 거의 0 이고, 8-column SSOT(`_common-contract.md §Clarification request policy`)와도 충돌하지 않는다. **권장: 재사용**.
|
|
165
|
+
2. user-response 파일 위치를 `runs/<task-type>/user-responses/` 로 신설할까, 아니면 audit sidecar 와 같은 `runs/<task-type>/worker-results/` 디렉토리에 다른 prefix(`user-response-*.md`)로 둘까? 후자는 디렉토리 신설 없이 끝나지만 author 가 사람이라 audit 파일과 섞이는 부담이 있다.
|
|
166
|
+
3. HTML 의 차트 표현은 1차에서는 “표 그대로 + 작은 인라인 SVG 진행바” 정도로 충분한가? (본격 차트는 후속 plan)
|
|
167
|
+
4. 2026-05-17 머지로 추가된 §4.7 / §4.8 / §4.6.6 / Verdict Card 검사는 모두 substring 기반이다. slim stripper 가 “보존 invariant” 를 지키는지 검증하는 방법으로 **substring 검사를 그대로 재실행**하는 게 가장 안전한가, 아니면 별도 “preserved heading” 화이트리스트를 두는 게 맞나? **권장: 전자 — `validate-run.py` 의 substring 목록을 import 하여 동일 함수로 검사**하면 단일 reference point 가 유지된다.
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -54,11 +54,11 @@ Unlike the Codex / Gemini workers, you are an in-process Claude subagent — you
|
|
|
54
54
|
|
|
55
55
|
## Required Reading Before Any Analysis
|
|
56
56
|
|
|
57
|
-
Before producing any output, you MUST read every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character.
|
|
57
|
+
Before producing any output, you MUST read every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. For analysis workers this includes the task brief, analysis profile, analysis material (if present), reference expectations, and the carry-in clarification response (if present). Analysis workers do NOT read `final-report-template.md` — that file is for the Report writer worker only (see `okstra-team-contract` "Audience-scoped enumeration"). Producing findings without the template is the intended contract; the report writer in Phase 6 owns final-report structure.
|
|
58
58
|
|
|
59
59
|
- Use a single `Read` call per file with no `offset` and no `limit`. If a file is genuinely too large for one read, page through it with explicit `offset` / `limit` calls that together cover the entire file, and record the page boundaries in your Findings.
|
|
60
|
-
- For the carry-in clarification response, walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface, not skip. Skimming these rows is the most common failure mode here; the fact that the file you will eventually contribute to has a structurally similar section 5 is NOT a license to skim.
|
|
61
|
-
- Before listing any Findings,
|
|
60
|
+
- For the carry-in clarification response, walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface, not skip. Skimming these rows is the most common failure mode here; the fact that the file you will eventually contribute to has a structurally similar section 5 is NOT a license to skim.
|
|
61
|
+
- Before listing any Findings, write a Reading Confirmation block to your **audit sidecar** at `runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md` (sibling to your main worker-results file — substitute `claude-worker-<task-type>-<seq>.md` → `claude-worker-audit-<task-type>-<seq>.md`). The sidecar's body begins with `# Claude Worker Audit — <task-key>` followed by one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). Do NOT include a `## 0. Reading Confirmation` heading in the main worker-results file — the validator now fails worker-results that contain one. If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
|
|
62
62
|
- Do not skip a file because its name suggests its content is already familiar from a prior run. Each file is canonical for the current run only.
|
|
63
63
|
|
|
64
64
|
## Worker Output Structure
|
|
@@ -67,7 +67,6 @@ When returning results, start the file with a YAML frontmatter block, then organ
|
|
|
67
67
|
|
|
68
68
|
**Frontmatter (mandatory)** — set `workerId: "claude"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the input files (`analysis-material.md` is canonical; if it lacks any field, record a `tool-failure` and stop). Full schema and a concrete example live in the `okstra-team-contract` skill's "Result Frontmatter" subsection.
|
|
69
69
|
|
|
70
|
-
0. **Reading Confirmation** - one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). If any file was skipped, record a `tool-failure` and do NOT produce sections 1–5.
|
|
71
70
|
1. **Findings** - what you identified
|
|
72
71
|
2. **Missing Information or Assumptions** - gaps in the analysis
|
|
73
72
|
3. **Safe or Reasonable Areas** - parts that look correct
|
|
@@ -76,6 +75,8 @@ When returning results, start the file with a YAML frontmatter block, then organ
|
|
|
76
75
|
|
|
77
76
|
Include file paths and line numbers when discussing code evidence.
|
|
78
77
|
|
|
78
|
+
**Item IDs (mandatory).** Every row in sections 1–5 (and any optional section 6) MUST carry a worker-internal item ID unique within this file. Use the leading column for table-form items (`F-001`, `M-001`, `S-001`, `U-001`, `R-001` per section) or a `[<ID>]` prefix for bullet/numbered items. The ID shape is your choice but it MUST appear — the lead's §1.1 / §1.2 / §3.1 synthesis preserves these IDs in its `Source items (worker:item)` column to keep cross-worker traceability intact. See `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT.
|
|
79
|
+
|
|
79
80
|
**Ticket tagging:** For runs whose task type is `requirements-discovery`, `error-analysis`, `implementation-planning`, or `implementation`, every item in sections 1–5 MUST carry a ticket identifier. Use the `Ticket ID` column in table-form items and the `[TICKETID: <id>]` prefix in bullet/numbered items. Fill priority: `Issue / Ticket` from the input → `Task ID` (no prefix, e.g. `8852`) → `unknown`. Multiple tickets are comma-separated. Full rules live in the `okstra-team-contract` skill's Ticket Tagging section.
|
|
80
81
|
|
|
81
82
|
This contract mirrors the `okstra-team-contract` skill's Worker Output Contract — that skill is the authoritative source if the two ever diverge.
|
|
@@ -87,7 +88,7 @@ You are an in-process Claude subagent — Lead's `Agent()` call blocks until you
|
|
|
87
88
|
After your `Write` to the assigned worker-results file (path provided by Lead as `**Worker Result Path:**` or derived under `runs/<task-type>/worker-results/claude-worker-<task-type>-<seq>.md`) succeeds:
|
|
88
89
|
|
|
89
90
|
1. Return your final assistant message **immediately**, in this format:
|
|
90
|
-
`Worker results written to <abs path>. Sections
|
|
91
|
+
`Worker results written to <abs path>. Sections 1–5 complete. Findings: <n>.`
|
|
91
92
|
2. Do NOT perform additional `Read`, `Grep`, `Glob`, MCP, or self-review tool calls after the file is written.
|
|
92
93
|
3. Do NOT rewrite the worker-results file with `Write` more than once. If a correction is genuinely required, perform a single `Edit` and then return immediately.
|
|
93
94
|
4. The only exception is recording a `tool-failure` in the errors sidecar when a post-Write failure is itself the failure being reported — return immediately after that single sidecar append.
|
|
@@ -122,11 +122,11 @@ This wrapper does NOT invoke MCP tools directly. MCP availability inside the Cod
|
|
|
122
122
|
|
|
123
123
|
## Required Reading Before Any Analysis
|
|
124
124
|
|
|
125
|
-
Before producing any output, you MUST ensure the underlying Codex CLI run reads every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character.
|
|
125
|
+
Before producing any output, you MUST ensure the underlying Codex CLI run reads every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. For analysis workers this includes the task brief, analysis profile, analysis material (if present), reference expectations, and the carry-in clarification response (if present). Analysis workers do NOT read `final-report-template.md` — that file is for the Report writer worker only (see `okstra-team-contract` "Audience-scoped enumeration"). Producing findings without the template is the intended contract; the report writer in Phase 6 owns final-report structure.
|
|
126
126
|
|
|
127
127
|
- The lead's prompt body, which you persist verbatim and feed into Codex via stdin, already contains the explicit list of files and the end-to-end reading rule. Do not strip or summarize that block before passing it to the CLI.
|
|
128
|
-
- For the carry-in clarification response, the CLI must walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface. The fact that the prior run's final report and the upcoming output share section 5 structure is NOT a license to skim.
|
|
129
|
-
- The
|
|
128
|
+
- For the carry-in clarification response, the CLI must walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface. The fact that the prior run's final report and the upcoming output share section 5 structure is NOT a license to skim.
|
|
129
|
+
- The wrapper writes a Reading Confirmation block to the **audit sidecar** at `runs/<task-type>/worker-results/codex-worker-audit-<task-type>-<seq>.md` (sibling to the main worker-results file). The sidecar's body begins with `# Codex Worker Audit — <task-key>` followed by one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). The main Codex output MUST NOT contain a `## 0. Reading Confirmation` heading — the validator now fails worker-results that contain one. If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
|
|
130
130
|
|
|
131
131
|
## Worker Output Structure
|
|
132
132
|
|
|
@@ -134,7 +134,6 @@ When returning results, start the file with a YAML frontmatter block, then organ
|
|
|
134
134
|
|
|
135
135
|
**Frontmatter (mandatory)** — set `workerId: "codex"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the input files (`analysis-material.md` is canonical; if it lacks any field, record a `tool-failure` and stop). Full schema and a concrete example live in the `okstra-team-contract` skill's "Result Frontmatter" subsection.
|
|
136
136
|
|
|
137
|
-
0. **Reading Confirmation** - one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). If any file was skipped, record a `tool-failure` and do NOT produce sections 1–5.
|
|
138
137
|
1. **Findings** - what Codex identified
|
|
139
138
|
2. **Missing Information or Assumptions** - gaps in the analysis
|
|
140
139
|
3. **Safe or Reasonable Areas** - parts that look correct
|
|
@@ -143,6 +142,8 @@ When returning results, start the file with a YAML frontmatter block, then organ
|
|
|
143
142
|
|
|
144
143
|
Include file paths and line numbers when discussing code evidence.
|
|
145
144
|
|
|
145
|
+
**Item IDs (mandatory).** Every row in sections 1–5 (and any optional section 6) MUST carry a worker-internal item ID unique within this file. Codex tends to use hierarchical numbering (`1.1`, `1.2`, `1.3`, ...); that shape is fine — keep what's natural. What matters is that each item is addressable. The lead's §1.1 / §1.2 / §3.1 synthesis preserves these IDs as `codex:<your-id>` entries in its `Source items (worker:item)` column. See `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT.
|
|
146
|
+
|
|
146
147
|
**Ticket tagging:** For runs whose task type is `requirements-discovery`, `error-analysis`, `implementation-planning`, or `implementation`, every item in sections 1–5 MUST carry a ticket identifier. Use the `Ticket ID` column in table-form items and the `[TICKETID: <id>]` prefix in bullet/numbered items. Fill priority: `Issue / Ticket` from the input → `Task ID` (no prefix, e.g. `8852`) → `unknown`. Multiple tickets are comma-separated. Full rules live in the `okstra-team-contract` skill's Ticket Tagging section.
|
|
147
148
|
|
|
148
149
|
This contract mirrors the `okstra-team-contract` skill's Worker Output Contract — that skill is the authoritative source if the two ever diverge.
|
|
@@ -122,11 +122,11 @@ This wrapper does NOT invoke MCP tools directly. MCP availability inside the Gem
|
|
|
122
122
|
|
|
123
123
|
## Required Reading Before Any Analysis
|
|
124
124
|
|
|
125
|
-
Before producing any output, you MUST ensure the underlying Gemini CLI run reads every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character.
|
|
125
|
+
Before producing any output, you MUST ensure the underlying Gemini CLI run reads every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. For analysis workers this includes the task brief, analysis profile, analysis material (if present), reference expectations, and the carry-in clarification response (if present). Analysis workers do NOT read `final-report-template.md` — that file is for the Report writer worker only (see `okstra-team-contract` "Audience-scoped enumeration"). Producing findings without the template is the intended contract; the report writer in Phase 6 owns final-report structure.
|
|
126
126
|
|
|
127
127
|
- The lead's prompt body, which you persist verbatim and feed into Gemini via stdin, already contains the explicit list of files and the end-to-end reading rule. Do not strip or summarize that block before passing it to the CLI.
|
|
128
|
-
- For the carry-in clarification response, the CLI must walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface. The structural similarity between the prior final report and the upcoming output is the most common reason this step gets skipped — do not repeat that.
|
|
129
|
-
- The
|
|
128
|
+
- For the carry-in clarification response, the CLI must walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface. The structural similarity between the prior final report and the upcoming output is the most common reason this step gets skipped — do not repeat that.
|
|
129
|
+
- The wrapper writes a Reading Confirmation block to the **audit sidecar** at `runs/<task-type>/worker-results/gemini-worker-audit-<task-type>-<seq>.md` (sibling to the main worker-results file). The sidecar's body begins with `# Gemini Worker Audit — <task-key>` followed by one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). The main Gemini output MUST NOT contain a `## 0. Reading Confirmation` heading — the validator now fails worker-results that contain one. If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
|
|
130
130
|
|
|
131
131
|
## Worker Output Structure
|
|
132
132
|
|
|
@@ -134,7 +134,6 @@ When returning results, start the file with a YAML frontmatter block, then organ
|
|
|
134
134
|
|
|
135
135
|
**Frontmatter (mandatory)** — set `workerId: "gemini"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the input files (`analysis-material.md` is canonical; if it lacks any field, record a `tool-failure` and stop). Full schema and a concrete example live in the `okstra-team-contract` skill's "Result Frontmatter" subsection.
|
|
136
136
|
|
|
137
|
-
0. **Reading Confirmation** - one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). If any file was skipped, record a `tool-failure` and do NOT produce sections 1–5.
|
|
138
137
|
1. **Findings** - what Gemini identified
|
|
139
138
|
2. **Missing Information or Assumptions** - gaps in the analysis
|
|
140
139
|
3. **Safe or Reasonable Areas** - parts that look correct
|
|
@@ -143,6 +142,8 @@ When returning results, start the file with a YAML frontmatter block, then organ
|
|
|
143
142
|
|
|
144
143
|
Include file paths and line numbers when discussing code evidence.
|
|
145
144
|
|
|
145
|
+
**Item IDs (mandatory).** Every row in sections 1–5 (and any optional section 6) MUST carry a worker-internal item ID unique within this file. Gemini may use `F-1`, `F-2`, ... or numbered hierarchical IDs — either is fine. What matters is that each item is addressable. The lead's §1.1 / §1.2 / §3.1 synthesis preserves these IDs as `gemini:<your-id>` entries in its `Source items (worker:item)` column. See `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT.
|
|
146
|
+
|
|
146
147
|
**Ticket tagging:** For runs whose task type is `requirements-discovery`, `error-analysis`, `implementation-planning`, or `implementation`, every item in sections 1–5 MUST carry a ticket identifier. Use the `Ticket ID` column in table-form items and the `[TICKETID: <id>]` prefix in bullet/numbered items. Fill priority: `Issue / Ticket` from the input → `Task ID` (no prefix, e.g. `8852`) → `unknown`. Multiple tickets are comma-separated. Full rules live in the `okstra-team-contract` skill's Ticket Tagging section.
|
|
147
148
|
|
|
148
149
|
This contract mirrors the `okstra-team-contract` skill's Worker Output Contract — that skill is the authoritative source if the two ever diverge.
|
|
@@ -46,18 +46,25 @@ If you find yourself thinking "I'll just return the report inline and let lead s
|
|
|
46
46
|
Before writing the final report, you MUST read every input file enumerated in the `[Required reading]` block of the lead's prompt from the very first character to the very last character. This always includes `final-report-template.md` and every analysis worker's result file under `worker-results/`, plus the convergence output under `state/convergence-<task-type>-<seq>.json` (if present).
|
|
47
47
|
|
|
48
48
|
- Use a single `Read` call per file with no `offset` and no `limit`. If a file is too large for one read, page through it with explicit `offset` / `limit` calls covering the full file.
|
|
49
|
-
- For the carry-in `clarification-response.md` (if present), walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) including rows whose `User input` cell is blank — a blank cell with `Status=open` is itself a signal you must surface in
|
|
49
|
+
- For the carry-in `clarification-response.md` (if present), walk every row of `## 5. Clarification Items` (`C-001`, `C-002`, ...) including rows whose `User input` cell is blank — a blank cell with `Status=open` is itself a signal you must surface in the conditional `## 0. Clarification Response Carried In From Previous Run` section (the template's `RENDER_IF` guard activates it when the carry-in path is non-empty). The fact that the file you write has a structurally similar section 5 is NOT an excuse to skim. When no carry-in path was provided, OMIT the `## 0.` heading entirely — do NOT write an empty-state stub.
|
|
50
50
|
- Open every analysis-worker result file under `worker-results/` end-to-end. Do not summarize them from convergence output alone — convergence captures classifications, not full evidence.
|
|
51
|
-
-
|
|
51
|
+
- Write a Reading Confirmation block to your **audit sidecar** at `runs/<task-type>/worker-results/report-writer-worker-audit-<task-type>-<seq>.md` (sibling to the main worker-results file). The sidecar's body begins with `# Report Writer Worker Audit — <task-key>` followed by one short line per input file confirming end-to-end reading. The main final-report and the main worker-results file MUST NOT contain a `## 0. Reading Confirmation` heading — the validator now fails reports that contain one. If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating the report.
|
|
52
52
|
- When the convergence-state file is present, read it fully and reproduce the `roundHistory[]` array, `round2SkippedReason`, and `finalClassificationCounts` in the final report's Section 1 Round History sub-table. Do not derive these values from worker results alone — they live in `state/convergence-<task-type>-<seq>.json`.
|
|
53
53
|
|
|
54
54
|
## Authoring Contract
|
|
55
55
|
|
|
56
56
|
The final-report file MUST follow `instruction-set/final-report-template.md` if present; otherwise the structure defined in the `okstra-report-writer` skill.
|
|
57
57
|
|
|
58
|
+
You author the final-report **markdown only**. The slim AI copy (`*.slim.md`) and the self-contained human HTML view (`*.html`) are produced deterministically by Phase 7 step 1.5 (`scripts/okstra-render-report-views.py`) from the markdown you wrote — do NOT generate or paste HTML/slim content yourself. The original final-report MD is the single source of truth; user input collected via the HTML form goes into a separate `runs/<task-type>/user-responses/user-response-<task-type>-<seq>.md` sidecar (schema in [`templates/reports/user-response.template.md`](../../templates/reports/user-response.template.md)) and never overwrites your report.
|
|
59
|
+
|
|
58
60
|
Hard rules:
|
|
59
61
|
|
|
60
|
-
- The file's `Author:` header line is `Report writer worker` (your role)
|
|
62
|
+
- The file's `Report Author:` header line is `Report writer worker` (your role); `Report Owner:` remains `Claude lead`. Do NOT set `Report Author:` to `Claude lead` unless this run is `release-handoff` (which is single-lead by design) or a recorded report-writer dispatch failure forced the fallback.
|
|
63
|
+
- **Source items (worker:item) preservation.** When synthesising `## 1.1 Consensus` / `## 1.2 Differences` / `## 3.1 Primary Evidence` rows from worker outputs, the `Source items` / `Supporting workers` / `Workers (position)` / `Source` column MUST list each contributing worker's item ID as `worker:item-id` (e.g. `claude:F-001, codex:1.1, gemini:F-3`). Bare worker-name lists (e.g. `claude, codex, gemini`) are deprecated — they break traceability back to the original worker-results files. See `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT.
|
|
64
|
+
- **Verdict Card (top)** is mandatory in every final-report. Its `Verdict Token` / `Direction` / `Next Step` cells MUST byte-match the corresponding cells in `## 2. Final Verdict` and the first item of `## 6. Recommended Next Steps`. The validator treats the card as a non-authoritative index — divergence is `contract-violated`.
|
|
65
|
+
- **No deprecated sections.** Do NOT emit `4.5.8 User Approval Request` (the body stub is deleted; the top-of-report Approval block is the only one), `4.5.9 Open Questions`, `5.1 추가 자료 요청`, or `5.2 사용자 확인 질문`. The validator fails reports that contain any of these headings.
|
|
66
|
+
- **Conditional Section 0.** Render `## 0. Clarification Response Carried In From Previous Run` ONLY when the carry-in path is non-empty. Never write an empty-state stub (`"No prior clarification response was provided."`). The validator fails empty Section 0.
|
|
67
|
+
- **Reading Confirmation** lives in the audit sidecar (`runs/<task-type>/worker-results/report-writer-worker-audit-<task-type>-<seq>.md`), never in the final-report or main worker-results file.
|
|
61
68
|
- Include all four convergence categories (Full Consensus, Partial Consensus, Contested, Worker-Unique). Do not omit Contested or Worker-Unique findings.
|
|
62
69
|
- Include a Round History sub-table in Section 1 (one row per executed round) and a `round2SkippedReason` line below it. When convergence is disabled, omit both. The values are quoted verbatim from `state/convergence-<task-type>-<seq>.json` — do not recompute.
|
|
63
70
|
- Treat `verification-error` votes as their own verdict. They are listed in vote summaries as `verification-error`, not folded into AGREE/DISAGREE counts.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""CLI entrypoint for Phase 7 step 1.5 — render two derived views of
|
|
3
|
+
an okstra final-report markdown.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
okstra-render-report-views.py <path-to-final-report.md>
|
|
7
|
+
[--task-key <task-group/task-id>]
|
|
8
|
+
[--task-type <profile>]
|
|
9
|
+
[--seq <NNN>]
|
|
10
|
+
[--source-report <relative-path>]
|
|
11
|
+
|
|
12
|
+
When the optional flags are omitted, the script infers what it can from
|
|
13
|
+
the report path (``runs/<task-type>/reports/final-report-<task-type>-<seq>.md``)
|
|
14
|
+
and the report's frontmatter / ``- Task Key:`` / ``- Task Type:`` lines.
|
|
15
|
+
|
|
16
|
+
Outputs (idempotent — overwrites):
|
|
17
|
+
- <stem>.slim.md — token-saving copy for the next-phase lead prompt
|
|
18
|
+
- <stem>.html — single-file self-contained HTML view
|
|
19
|
+
|
|
20
|
+
This script is the canonical single-reference-point. The Node CLI
|
|
21
|
+
(``bin/okstra render-views``) is a thin wrapper that spawns it.
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
import os
|
|
27
|
+
import re
|
|
28
|
+
import sys
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
32
|
+
SCRIPTS_DIR = REPO_ROOT / "scripts"
|
|
33
|
+
HOME_LIB = Path(os.environ.get("OKSTRA_HOME", str(Path.home() / ".okstra"))) / "lib" / "python"
|
|
34
|
+
|
|
35
|
+
# Prefer dev sources when present, fall back to install. Without this
|
|
36
|
+
# order, a stale install (~/.okstra/lib/python/okstra_ctl) without the
|
|
37
|
+
# report_views module shadows the in-repo copy during development.
|
|
38
|
+
if (SCRIPTS_DIR / "okstra_ctl" / "report_views.py").is_file():
|
|
39
|
+
if str(SCRIPTS_DIR) in sys.path:
|
|
40
|
+
sys.path.remove(str(SCRIPTS_DIR))
|
|
41
|
+
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
42
|
+
if HOME_LIB.is_dir() and str(HOME_LIB) not in sys.path:
|
|
43
|
+
sys.path.append(str(HOME_LIB))
|
|
44
|
+
elif HOME_LIB.is_dir() and str(HOME_LIB) not in sys.path:
|
|
45
|
+
sys.path.insert(0, str(HOME_LIB))
|
|
46
|
+
|
|
47
|
+
from okstra_ctl.report_views import RunMeta, render_both_views # noqa: E402
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
_TEMPLATES_DIRS = (
|
|
51
|
+
REPO_ROOT / "templates" / "reports",
|
|
52
|
+
Path.home() / ".okstra" / "lib" / "templates" / "reports",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# task-type itself can contain hyphens (``implementation-planning``,
|
|
56
|
+
# ``final-verification``, ``release-handoff``), so the filename segment
|
|
57
|
+
# between ``final-report-`` and ``-<seq>.md`` is matched greedily; the
|
|
58
|
+
# trailing ``-(?P<seq>\d+)\.md$`` anchor pins seq to the last numeric tail.
|
|
59
|
+
_PATH_RE = re.compile(
|
|
60
|
+
r"runs/(?P<task_type>[^/]+)/reports/final-report-.+-(?P<seq>\d+)\.md$"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _load_assets() -> tuple[str, str]:
|
|
65
|
+
css_text: str | None = None
|
|
66
|
+
js_text: str | None = None
|
|
67
|
+
for d in _TEMPLATES_DIRS:
|
|
68
|
+
css = d / "report.css"
|
|
69
|
+
js = d / "report.js"
|
|
70
|
+
if css.is_file() and js.is_file():
|
|
71
|
+
css_text = css.read_text(encoding="utf-8")
|
|
72
|
+
js_text = js.read_text(encoding="utf-8")
|
|
73
|
+
break
|
|
74
|
+
if css_text is None or js_text is None:
|
|
75
|
+
raise SystemExit(
|
|
76
|
+
"report.css / report.js not found. Looked under: "
|
|
77
|
+
+ ", ".join(str(d) for d in _TEMPLATES_DIRS)
|
|
78
|
+
)
|
|
79
|
+
return css_text, js_text
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _infer_from_path(path: Path) -> dict[str, str]:
|
|
83
|
+
posix = path.as_posix()
|
|
84
|
+
m = _PATH_RE.search(posix)
|
|
85
|
+
if m:
|
|
86
|
+
return {"task_type": m.group("task_type"), "seq": m.group("seq")}
|
|
87
|
+
return {}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _infer_from_body(text: str) -> dict[str, str]:
|
|
91
|
+
found: dict[str, str] = {}
|
|
92
|
+
for label, key in (("Task Key", "task_key"), ("Task Type", "task_type")):
|
|
93
|
+
m = re.search(rf"^- {label}:\s*(\S.*?)\s*$", text, re.MULTILINE)
|
|
94
|
+
if m:
|
|
95
|
+
found[key] = m.group(1)
|
|
96
|
+
return found
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def main(argv: list[str] | None = None) -> int:
|
|
100
|
+
parser = argparse.ArgumentParser(
|
|
101
|
+
description="Render slim AI + self-contained HTML views of an okstra final-report."
|
|
102
|
+
)
|
|
103
|
+
parser.add_argument("report_path", type=Path)
|
|
104
|
+
parser.add_argument("--task-key", default=None)
|
|
105
|
+
parser.add_argument("--task-type", default=None)
|
|
106
|
+
parser.add_argument("--seq", default=None)
|
|
107
|
+
parser.add_argument("--source-report", default=None)
|
|
108
|
+
args = parser.parse_args(argv)
|
|
109
|
+
|
|
110
|
+
report_path: Path = args.report_path.resolve()
|
|
111
|
+
if not report_path.is_file():
|
|
112
|
+
parser.error(f"final-report not found: {report_path}")
|
|
113
|
+
|
|
114
|
+
inferred = {**_infer_from_path(report_path), **_infer_from_body(report_path.read_text(encoding="utf-8"))}
|
|
115
|
+
task_key = args.task_key or inferred.get("task_key") or "unknown"
|
|
116
|
+
task_type = args.task_type or inferred.get("task_type") or "unknown"
|
|
117
|
+
seq = args.seq or inferred.get("seq") or "000"
|
|
118
|
+
source_report = args.source_report or report_path.name
|
|
119
|
+
|
|
120
|
+
css, js = _load_assets()
|
|
121
|
+
meta = RunMeta(task_key=task_key, task_type=task_type, seq=seq, source_report=source_report)
|
|
122
|
+
slim_path, html_path = render_both_views(report_path, run_meta=meta, css=css, js=js)
|
|
123
|
+
print(f"slim: {slim_path}")
|
|
124
|
+
print(f"html: {html_path}")
|
|
125
|
+
return 0
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
sys.exit(main())
|
|
@@ -85,4 +85,4 @@ Invoke the `okstra` skill now. Read the manifests below for all task metadata, p
|
|
|
85
85
|
|
|
86
86
|
- Source path: `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}`
|
|
87
87
|
- If the source path above is empty, no prior clarification response was attached to this run.
|
|
88
|
-
- If the source path is set, a copy is staged at `{{INSTRUCTION_SET_RELATIVE_PATH}}/clarification-response.md`. Read it before running workers; reconcile each `C-*` row in section 5 (`## 5. Clarification Items`) of the prior report against new evidence and record the outcome in
|
|
88
|
+
- If the source path is set, a copy is staged at `{{INSTRUCTION_SET_RELATIVE_PATH}}/clarification-response.md`. Read it before running workers; reconcile each `C-*` row in section 5 (`## 5. Clarification Items`) of the prior report against new evidence and record the outcome in the conditional `## 0. Clarification Response Carried In From Previous Run` section of this run's final report (render that heading only when carry-in is non-empty — the validator fails empty Section 0 stubs).
|
|
@@ -43,7 +43,7 @@ profile document.
|
|
|
43
43
|
- `complete` → proceed normally.
|
|
44
44
|
- `partial` → proceed; treat still-unmarked `intent-check:` / `conversion-block:` rows as the `skipped` branch.
|
|
45
45
|
- `skipped` → do NOT silently infer the missing answers. Promote each unmarked `intent-check:` / `conversion-block:` row into this run's `## 5. Clarification Items` as `Kind=decision`. Use `Blocks=approval` in `implementation-planning`, where the row gates the User Approval Request; otherwise use `Blocks=next-phase`. The recommended answer is drawn from the brief's matching content and clearly labelled `보고자 직접 확인 권장`.
|
|
46
|
-
- `pending` (or field missing) → ABORT analysis; write
|
|
46
|
+
- `pending` (or field missing) → ABORT analysis; render the Verdict Card with `Verdict Token = blocked` + `Direction = hold` and write a single `## Reporter Confirmation Required` block (no leading number) summarising which rows are pending. The `## 5. Clarification Items` table carries one row per pending item with `Blocks=approval` in `implementation-planning`, otherwise `Blocks=next-phase`. The operator must rerun `okstra-brief` Step 6.5. Do NOT emit `## 0.` for this case — Section 0 is reserved for clarification-response carry-in only.
|
|
47
47
|
`[CONFIRMED <YYYY-MM-DD> → RC-N]` markers on `Open Questions` rows are the per-row signal that the reporter has answered; their answers live verbatim under `## Reporter Confirmations` in the brief.
|
|
48
48
|
- `Source Material` is reporter-verbatim. Do NOT paraphrase, summarize, reorder, or restructure it. Quote it directly when needed.
|
|
49
49
|
- `Augmentation` entries carry one of four labels — `evidence-link`, `format-conversion`, `terminology-mapping`, `intent-inference`. Treat them as follows:
|
|
@@ -62,10 +62,18 @@ profile document.
|
|
|
62
62
|
- **Canonical column schema (SSOT — must match `templates/reports/final-report.template.md` §5.1 exactly):** every `## 5. Clarification Items` table has exactly these 8 columns, in this order:
|
|
63
63
|
`| ID | Ticket ID | Kind | Statement | Expected form | Blocks | Status | User input |`.
|
|
64
64
|
Profile-specific addenda may tighten cell content but MUST NOT add, remove, rename, or reorder columns. The `ID` cell uses `C-NNN` (3-digit zero-padded), the `Status` cell ∈ `{open, answered, resolved, obsolete}`, and the `Kind` / `Blocks` legal values are listed below.
|
|
65
|
-
- section 5 is a **single unified table** per `final-report-template.md`. Every clarification item — whether the user must attach a file, choose between options, or supply a single number/path — is one row of that table. Do not split it into sub-sections, do not create a parallel table elsewhere in the report, and do not duplicate the same item into
|
|
65
|
+
- section 5 is a **single unified table** per `final-report-template.md`. Every clarification item — whether the user must attach a file, choose between options, or supply a single number/path — is one row of that table. Do not split it into sub-sections (`5.1 추가 자료 요청` / `5.2 사용자 확인 질문` / `4.5.9 Open Questions` are removed and the validator fails reports that reintroduce them), do not create a parallel table elsewhere in the report, and do not duplicate the same item into the top-of-report `User Approval Request (사용자 승인 게이트)` block or any other section.
|
|
66
66
|
- each row's `Kind` column picks one of `{material, decision, data-point}`: `material` for files / snapshots / logs / screenshots the user must attach (the `User input` cell will hold a path or URL); `decision` for choices and yes/no confirmations only the user can make; `data-point` for a single number, ID, date, or short string the user can answer inline. Items that mix "yes/no + file path if yes" are one row of `Kind=material` with the combined expectation written into `Expected form`.
|
|
67
67
|
- each row's `Blocks` column picks one of `{approval, next-phase, none}`. `approval` is reserved for items that gate an approval action, especially the `implementation-planning` User Approval Request; outside `implementation-planning`, unresolved brief reporter-confirmation rows use `next-phase` instead. `next-phase` blocks the next run from starting cleanly. `none` is informational/audit-only.
|
|
68
68
|
- write every entry in full, descriptive sentences that a non-developer can act on without further context. Avoid abbreviations and internal jargon. The `Statement` cell must state *what* is needed, *why* the answer / attachment changes the next step, and (for `material`) *where* the user can find it and *where* to place it. The `Expected form` cell must state the shape of the answer (예/아니오, 보기 중 하나, 숫자/날짜, 파일 경로, 짧은 서술 등); supply concrete option choices when applicable.
|
|
69
69
|
- the same `final-report.md` file is the canonical artifact carried into the next run; the user appends answers inline before rerunning. The preferred turn-around is `scripts/okstra.sh --resume-clarification --task-key <project-id>:<task-group>:<task-id>` (opens the latest report in `$EDITOR`, then auto-reruns the same phase with `--clarification-response` carry-in). The lower-level form `--clarification-response <path>` remains available for scripted runs.
|
|
70
|
-
- if a clarification response was carried in for this run, walk every `C-*` row of the prior report's `## 5. Clarification Items` table
|
|
71
|
-
|
|
70
|
+
- if a clarification response was carried in for this run, render the conditional `## 0. Clarification Response Carried In From Previous Run` section (the template's `RENDER_IF` guard activates it), walk every `C-*` row of the prior report's `## 5. Clarification Items` table, reconcile each one against new evidence, and update its `Status` to `resolved` or `obsolete` before issuing the next decision/verdict. When no carry-in path was provided, omit the `## 0.` heading entirely — the validator fails reports that emit an empty Section 0 stub (e.g. "No prior clarification response was provided for this run.").
|
|
71
|
+
- Verdict Card (shared — applies to every final-report regardless of profile):
|
|
72
|
+
- The top-of-report `## Verdict Card` block is mandatory in every final-report. Its `Verdict Token`, `Direction`, and `Next Step` cells MUST byte-match the corresponding cells in `## 2. Final Verdict` and the first item of `## 6. Recommended Next Steps`. The validator treats the card as a non-authoritative index — when card values diverge from the authoritative sections, the run is `contract-violated`.
|
|
73
|
+
- Cross-worker traceability (shared — applies to every analysis worker output and to the lead's `## 1.` / `## 3.` tables in the final-report):
|
|
74
|
+
- **Worker-side item IDs (free-form but unique within the worker).** Every row item in sections 1–5 (and any optional section 6) of an analysis worker's output MUST carry an item ID that is unique within that one worker's result file. The ID convention is the worker's choice — `F-001` / `F-002` per the suggested schema, `1.1` / `1.2` / `1.3` as Codex tends to use, or any other shape — but it MUST appear as the leading column of the row (for table-form items) or as a `[<ID>]` prefix (for bullet/numbered items). Workers that emit findings without IDs make cross-worker reconciliation impossible.
|
|
75
|
+
- **Lead-side ID assignment + source preservation.** When the lead (or `report-writer-worker`) synthesises `## 1.1 Consensus` / `## 1.2 Differences` / `## 3.1 Primary Evidence` rows from worker outputs, the lead assigns a fresh `C-NNN` / `D-NNN` / `E-NNN` row ID. The `Source items` column (or, where the template still calls it `Supporting workers` / `Workers (position)` / `Source`, that same column) MUST list every contributing worker:item pair (e.g. `claude:F-001, codex:1.1, gemini:F-3`) so a reviewer can trace the synthesised row back to each worker's original wording without re-reading every worker-results file. Bare worker names without item IDs (e.g. `claude, codex, gemini`) are deprecated for these tables; the validator does not yet fail on them but the readability pass treats it as a contract violation.
|
|
76
|
+
- **Why this matters.** A real run had `claude=F-1..F-11`, `codex=1.1..1.8`, `gemini=F-3..F-9` — three incompatible ID schemes. When the lead synthesised `C-1..C-8`, the link from `C-3` back to "which sentence in which worker file" was lost. Source-item preservation restores that link without forcing every worker to adopt a single ID prefix, which would over-constrain worker output style.
|
|
77
|
+
- Audit sidecar (shared — applies to every analysis-worker output and every final-report):
|
|
78
|
+
- Reading Confirmation lines (one short line per input file confirming end-to-end reading) live in the **worker audit sidecar** at `runs/<task-type>/worker-results/<worker>-audit-<task-type>-<seq>.md`, NOT in the worker's main worker-results file. The worker-results body starts at section 1 (Findings). The validator fails worker-results files that contain a `## 0. Reading Confirmation` heading.
|
|
79
|
+
- The audit sidecar carries any other meta the worker wants to log (tool-call counts, MCP query summaries, timing notes). The lead's final-report does NOT duplicate this content — it is consumed by the validator and by post-run audit tooling, not by end-user readers.
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
- dependency / migration risk assessment (ordering constraints, data backfills, feature-flag prerequisites, repo-internal sequencing)
|
|
70
70
|
- validation checklist (pre / mid / post) — each item is an exact command or observable outcome
|
|
71
71
|
- rollback strategy — exact revert path (commits, flags, migrations) and the signal that triggers rollback
|
|
72
|
-
- explicit `User Approval Request (사용자 승인 게이트)` block placed at the **top of the report** with a single canonical checkbox marker `- [ ] Approved` (user toggles to `- [x] Approved` to authorise the next `implementation` run).
|
|
72
|
+
- explicit `User Approval Request (사용자 승인 게이트)` block placed at the **top of the report** with a single canonical checkbox marker `- [ ] Approved` (user toggles to `- [x] Approved` to authorise the next `implementation` run). The `User Approval Request` validator key-substring is satisfied by this top block — do NOT recreate a `### 4.5.8 User Approval Request` body stub (the validator now fails reports that contain one, see `validators/validate-run.py` and `templates/reports/final-report.template.md` §4.5.8).
|
|
73
73
|
- **the marker line is rendered only when the plan-body verification gate (§4.5.9) returns `passed` or `passed-with-dissent`.** When the gate returns `blocked-by-disagreement` or `aborted-non-result`, the top-of-report Approval block is rendered **without** the canonical `- [ ] Approved` bullet (the rest of the block — title, summary, audit lines — stays). The `validators/validate-run.py` `validate_phase_boundary` function enforces this exact correspondence between gate result and marker line presence.
|
|
74
74
|
- every ambiguity flagged during pre-planning that the user must resolve before approval registered as a `Blocks=approval` row in the `## 5. Clarification Items` table (do NOT create a separate `Open Questions` block under `4.5.x` — the unified table is the single home)
|
|
75
75
|
- **§4.5.9 Plan Body Verification (BLOCKING).** After report-writer finishes the draft, the lead MUST run a worker peer-review round on the consolidated plan body (sections 4.5.1 – 4.5.7) and populate `### 4.5.9 Plan Body Verification` in the final report. The round protocol, plan-item ID scheme (`P-Opt-*` / `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*`), verdict semantics, gate-result classification, and dissent log format are defined in `skills/okstra-convergence/SKILL.md` "Plan-body verification mode". The four gate-result values are `passed`, `passed-with-dissent`, `blocked-by-disagreement`, `aborted-non-result`. When the gate would have been `blocked-by-disagreement` or `aborted-non-result`, the lead MUST NOT silently flip it to one of the passing values to "unblock" the run — that is a contract violation.
|