okstra 0.101.1 → 0.102.1

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 (40) hide show
  1. package/README.kr.md +5 -5
  2. package/README.md +5 -5
  3. package/docs/for-ai/README.md +2 -3
  4. package/docs/for-ai/skills/{okstra-container.md → okstra-container-build.md} +3 -4
  5. package/docs/for-ai/skills/okstra-inspect.md +1 -2
  6. package/docs/kr/architecture/storage-model.md +1 -2
  7. package/docs/kr/architecture.md +6 -6
  8. package/docs/kr/cli.md +3 -3
  9. package/docs/kr/container.md +1 -1
  10. package/docs/project-structure-overview.md +5 -5
  11. package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +1 -1
  12. package/docs/superpowers/plans/2026-06-24-cross-project-precondition.md +542 -0
  13. package/docs/superpowers/plans/2026-06-24-decision-drafts-render-gap.md +568 -0
  14. package/docs/superpowers/plans/2026-06-24-manual-user-test-section.md +203 -0
  15. package/docs/superpowers/plans/2026-06-24-okstra-container-build-rename.md +525 -0
  16. package/docs/superpowers/specs/2026-06-24-cross-project-precondition-design.md +129 -0
  17. package/docs/superpowers/specs/2026-06-24-decision-drafts-render-gap-design.md +185 -0
  18. package/docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md +121 -0
  19. package/docs/superpowers/specs/2026-06-24-okstra-container-build-rename-design.md +110 -0
  20. package/package.json +1 -1
  21. package/runtime/BUILD.json +2 -2
  22. package/runtime/agents/workers/report-writer-worker.md +1 -0
  23. package/runtime/prompts/lead/report-writer.md +1 -1
  24. package/runtime/prompts/lead/team-contract.md +2 -2
  25. package/runtime/prompts/profiles/_implementation-deliverable.md +2 -0
  26. package/runtime/prompts/profiles/final-verification.md +4 -3
  27. package/runtime/prompts/profiles/implementation-planning.md +12 -9
  28. package/runtime/python/okstra_ctl/run.py +0 -1
  29. package/runtime/python/okstra_token_usage/paths.py +1 -2
  30. package/runtime/schemas/final-report-v1.0.schema.json +106 -3
  31. package/runtime/skills/{okstra-container → okstra-container-build}/SKILL.md +3 -3
  32. package/runtime/templates/reports/final-report.template.md +63 -2
  33. package/runtime/templates/reports/final-verification-input.template.md +3 -3
  34. package/runtime/templates/reports/i18n/en.json +28 -1
  35. package/runtime/templates/reports/i18n/ko.json +28 -1
  36. package/runtime/templates/worker-prompt-preamble.md +1 -1
  37. package/runtime/validators/validate-run.py +70 -1
  38. package/src/commands/lifecycle/install.mjs +17 -18
  39. package/src/commands/lifecycle/uninstall.mjs +8 -6
  40. package/src/lib/skill-catalog.mjs +10 -9
@@ -0,0 +1,185 @@
1
+ # Decision Drafts — implementation-planning 잠복 렌더 갭 해소 설계
2
+
3
+ - 작성일: 2026-06-24
4
+ - 대상 phase: `implementation-planning`
5
+ - 상태: 설계 승인 완료 (구현 계획 작성 대기)
6
+ - 선행 참조: [2026-06-24-cross-project-precondition-design.md](2026-06-24-cross-project-precondition-design.md) — 동일 종류 갭을 같은 패턴으로 해소한 직전 사례
7
+
8
+ ## 1. 배경과 문제
9
+
10
+ `implementation-planning` 보고서는 `data.json`(SSOT) → Jinja 템플릿([final-report.template.md](../../../templates/reports/final-report.template.md))으로 **결정론적 렌더**된다. report-writer 는 마크다운을 직접 쓰지 못한다([report-writer-worker.md:27](../../../agents/workers/report-writer-worker.md:27)).
11
+
12
+ 프로파일의 Decision-record evaluation 블록([implementation-planning.md:111-116](../../../prompts/profiles/implementation-planning.md:111))은 3기준(① 되돌리기 어려움 ② 맥락 없으면 의아함 ③ 실제 trade-off) 충족 시 보고서 부록 섹션 `Decision Drafts` 를 **의무화**한다:
13
+
14
+ - 각 draft 는 `## Context / ## Decision / ## Consequences / ## Alternatives Considered` 형태, `## Status: Proposed` 로 시작.
15
+ - 평가했으나 기준 미달인 후보는 `skipped adr-candidate: <topic> — reason: <criterion>` 한 줄로 같은 섹션 아래 기록.
16
+
17
+ 그러나 이 부록은 **실제 보고서에 출력될 수 없다**:
18
+
19
+ 1. 스키마([final-report-v1.0.schema.json](../../../schemas/final-report-v1.0.schema.json))에 `decisionDraft` 류 필드가 없다(`grep decisionDraft` → 0건).
20
+ 2. 템플릿에 `Decision Drafts` 렌더 블록이 없다.
21
+ 3. i18n·validator 어디에도 `Decision Drafts` 가 없다.
22
+
23
+ 즉 cross-project(`## Cross-Repo Carry`)와 **동일 종류의 잠복 렌더 갭**이다.
24
+
25
+ ### 부록이 load-bearing 인 이유
26
+
27
+ 이 부록은 단순 장식이 아니다. [implementation-planning.md:116](../../../prompts/profiles/implementation-planning.md:116)은 승인된 계획의 stepwise execution 에 다음 step 을 의무화한다:
28
+
29
+ > `Create <PROJECT_ROOT>/.okstra/decisions/<NNNN>-<slug>.md from the decision draft in section X`
30
+
31
+ 즉 `implementation` run 이 ADR 파일을 만들 때 **"보고서의 섹션 X 에 있는 드래프트"를 출처로 가리킨다**. 부록이 렌더되지 못하면 그 step 은 존재하지 않는 섹션을 가리키게 된다.
32
+
33
+ 보고서가 staging 위치로 설계된 이유:
34
+ - planning 은 계획 문서만 산출하고 source edit 이 금지된다([:48](../../../prompts/profiles/implementation-planning.md:48)).
35
+ - ADR 은 계획이 승인(frontmatter `approved: true`)되기 전에 "Accepted" 로 커밋되면 안 된다. 보고서에 `Proposed` draft 로 staging 했다가 승인+implementation 시점에 파일로 materialize 하는 것이 **승인 게이트를 지키는 흐름**이다.
36
+
37
+ 하류 소비처 확인: `.okstra/decisions/<NNNN>-<slug>.md` 경로는 [_common-contract.md:87](../../../prompts/profiles/_common-contract.md:87)·[requirements-discovery.md:60](../../../prompts/profiles/requirements-discovery.md:60)·okstra-brief 가 참조하나, "Decision Drafts **보고서 섹션**"을 소비하는 것은 오직 line 116 자기참조뿐이다.
38
+
39
+ ## 2. 목표 / 비목표
40
+
41
+ ### 목표
42
+ - Decision Drafts 를 **1급·렌더되는 구조 필드**로 승격해 잠복 렌더 갭을 해소한다.
43
+ - draft(생성 대상)와 skipped 후보(평가 후 의도적 드롭)를 **한 섹션 아래** 구조화한다.
44
+ - draft 필드를 빈말 못 하도록 스키마 레벨에서 강제한다.
45
+ - draft 가 선언되면 line 116 의 materialization step 이 실제 존재하도록 **검증기 게이트**를 둔다.
46
+
47
+ ### 비목표
48
+ - **런타임 게이트 없음.** `implementation` run 진입 시 ADR 파일 존재를 자동 확인하지 않는다.
49
+ - **승인 차단 없음.** 계획 frontmatter `approved` 를 막지 않는다.
50
+ - planning phase 가 `.okstra/decisions/*.md` 파일을 직접 쓰지 않는다 — 기존 "drafts are NOT written by this phase" 규칙 유지(implementation 이 materialize).
51
+ - brief 의 모든 `adr-candidate:*` 평가 완료를 watertight 하게 강제하지 않는다(§7 한계 참조).
52
+
53
+ ## 3. 설계
54
+
55
+ ### 3.1 데이터 모델 (schema)
56
+
57
+ `implementationPlanning` 객체에 배열 두 개를 추가한다(둘 다 기본 `[]` 허용 — 평가 결과가 없는 계획은 빈 배열). 둘 다 `required` 에 넣어 report-writer 가 항상 emit 하게 한다.
58
+
59
+ 새 `$defs/DecisionDraft` (cross-project 의 `CrossProjectDependency` 와 동수인 7 필드, 전부 `required`, `additionalProperties: false`):
60
+
61
+ | 필드 | 타입/제약 | 의미 |
62
+ |---|---|---|
63
+ | `number` | `^\d{4,}$` | 파일명 `<NNNN>-<slug>.md` 의 NNNN. `(max existing in .okstra/decisions/ + 1)` zero-pad ≥4 자리 |
64
+ | `slug` | `^[a-z0-9]+(?:-[a-z0-9]+)*$` | 파일명 slug. 보고서 서브섹션 헤딩 `#### <number>-<slug>` 에도 사용 |
65
+ | `status` | const `"Proposed"` | planning 시점은 항상 Proposed |
66
+ | `context` | string, minLength 1 | ADR Context |
67
+ | `decision` | string, minLength 1 | ADR Decision |
68
+ | `consequences` | string, minLength 1 | ADR Consequences |
69
+ | `alternativesConsidered` | string, minLength 1 | 기각된 대안과 사유 |
70
+
71
+ 새 `$defs/SkippedAdrCandidate` (`additionalProperties: false`, 둘 다 `required`):
72
+
73
+ | 필드 | 타입/제약 | 의미 |
74
+ |---|---|---|
75
+ | `topic` | string, minLength 1 | 평가한 adr-candidate 주제 |
76
+ | `reason` | string, minLength 1 | 미달한 기준 (예: "되돌리기 어렵지 않음") |
77
+
78
+ `implementationPlanning.properties` 추가:
79
+ - `decisionDrafts`: `array of $defs/DecisionDraft`
80
+ - `skippedAdrCandidates`: `array of $defs/SkippedAdrCandidate`
81
+
82
+ ### 3.2 렌더 (template + i18n)
83
+
84
+ `templates/reports/final-report.template.md` §5.4 에서 `### Requirement Coverage`([:252](../../../templates/reports/final-report.template.md:252)) **뒤**, `## 5.5 Stage Map`([:260](../../../templates/reports/final-report.template.md:260)) **앞**에 `### Decision Drafts` 서브섹션을 추가한다(cross-project 와 같은 §5.4 내 위치).
85
+
86
+ ```
87
+ ### Decision Drafts{aside}
88
+
89
+ {% if drafts empty and skipped empty %}
90
+ {{ t("emptyState.decisionDrafts") }}
91
+ {% else %}
92
+ {% for d in decisionDrafts %}
93
+ #### {{ d.number }}-{{ d.slug }}
94
+
95
+ - **{{ t("...status") }}:** {{ d.status }}
96
+ - **{{ t("...context") }}:** {{ d.context }}
97
+ - **{{ t("...decision") }}:** {{ d.decision }}
98
+ - **{{ t("...consequences") }}:** {{ d.consequences }}
99
+ - **{{ t("...alternativesConsidered") }}:** {{ d.alternativesConsidered }}
100
+ {% endfor %}
101
+ {% for s in skippedAdrCandidates %}
102
+ - skipped adr-candidate: {{ s.topic }} — reason: {{ s.reason }}
103
+ {% endfor %}
104
+ {% endif %}
105
+ ```
106
+
107
+ 설계 결정:
108
+ - **헤딩 레벨**: §5.4 서브섹션은 `###` 이므로 `### Decision Drafts`, 각 draft 는 `#### <number>-<slug>`. 프로파일이 말하는 draft 의 `## Context/...`(H2)는 **파일(`.okstra/decisions/*.md`)의 형태**이고, 보고서에서는 구조 필드를 라벨 불릿으로 렌더한다. implementation step 이 이 필드를 읽어 파일을 `## Status/Context/Decision/Consequences/Alternatives Considered` 형태로 materialize 한다. 보고서에 H2 를 박으면 보고서 헤딩 계층·TOC 앵커가 깨지므로 구조-필드 렌더가 정합적이다.
109
+ - **skipped 라인은 고정 English 토큰** (`skipped adr-candidate:` / `reason:`) — `RED:`/`GREEN:`/`Slice value:` 처럼 greppable contract token 이고 프로파일 verbatim 이라 i18n 하지 않는다(`topic`/`reason` 값은 보고서 언어를 따른다).
110
+ - 무조건 렌더(empty-state 포함) → 헤딩 substring 이 항상 존재 → §3.3(c) 헤딩 contract 통과.
111
+ - empty-state 가 기본값: 대부분의 계획은 draft·skipped 둘 다 빈 배열이라 empty-state 한 줄만 보인다.
112
+
113
+ i18n: `templates/reports/i18n/{ko,en}.json` 에 추가
114
+ - `sectionAside.decisionDrafts`
115
+ - `emptyState.decisionDrafts`
116
+ - `implementationPlanning.decisionDraftFields.{status, context, decision, consequences, alternativesConsidered}`
117
+
118
+ ### 3.3 강제 (validator) — 세 겹
119
+
120
+ **(a) 모양 규칙 — JSON 스키마가 강제**
121
+ `decisionDrafts` 가 비어있지 않으면 모든 draft 의 7 필드가 `required`+`minLength 1`+`status` const 를 만족해야 한다. `skippedAdrCandidates` 행도 `topic`/`reason` 비-빈. → "구체 내용"이 빈말이 될 수 없다. `validate-run.py` 의 data.json 스키마 검증 경로에서 자동 적용.
122
+
123
+ **(b) 자기완결 trigger — `validate-run.py` 교차 필드 검사 (신설)**
124
+ - 새 함수 `_validate_implementation_planning_decision_drafts(data, failures)`:
125
+ - `implementationPlanning.decisionDrafts` 가 비어있지 않으면, **어떤 stage 의 `stepwiseExecution` step 의 `files` 또는 `action` 이 `.okstra/decisions/` 를 참조**해야 한다.
126
+ - 위반 시 `failures.append(...)` → `contract-violated`.
127
+ - 이것이 line 116 의 "approved plan's stepwise execution MUST include `Create .okstra/decisions/<NNNN>-<slug>.md ...`" 를 **실제 강제 지점으로 승격**한다. cross-project 의 trigger(planner 가 DM `cross-project` 를 수동으로 찍어야 발동)와 달리, 이 검사는 draft 존재 자체가 trigger 이므로 **self-contained·watertight** 하다.
128
+ - CLAUDE.md 의 "MUST/BLOCKING 은 강제 지점을 명시해야 한다" 규칙을 충족한다.
129
+
130
+ **(c) 섹션 헤딩 contract**
131
+ `validate-run.py` 의 `PLANNING_REQUIRED_SECTIONS`([:1132](../../../validators/validate-run.py:1132))에 `"Decision Drafts"` 를 추가. 템플릿이 이 헤딩을 무조건 렌더하므로(empty-state 포함) substring 검사를 통과한다.
132
+
133
+ ### 3.4 프로파일 prose (`prompts/profiles/implementation-planning.md`)
134
+
135
+ - Decision-record evaluation 블록(111–116)의 "attach a decision draft as a report appendix section titled `Decision Drafts`" 와 "record `skipped adr-candidate: ...` one line under `Decision Drafts`" 를 **구조 필드 참조로 재작성**한다:
136
+ - draft 는 `decisionDrafts` 배열(필드: number/slug/status/context/decision/consequences/alternativesConsidered)로 기록 → §5.4 `### Decision Drafts` 에 렌더.
137
+ - skipped 후보는 `skippedAdrCandidates` 배열(topic/reason)로 기록.
138
+ - line 116 의 materialization step 의무는 유지하되, "from the decision draft in section X" 가 §5.4 `### Decision Drafts` 의 `#### <number>-<slug>` 서브섹션을 가리킴을 명시. 파일은 그 구조 필드를 `## Status/Context/Decision/Consequences/Alternatives Considered` 형태로 materialize.
139
+ - 섹션 헤딩 contract(line 57) 필수 substring 목록에 `Decision Drafts` 추가.
140
+ - self-review 패스([:123](../../../prompts/profiles/implementation-planning.md:123))에 점검 추가: "`decisionDrafts` 가 비어있지 않으면 매칭 materialization step(`.okstra/decisions/` 생성)이 어느 stage 의 stepwise 에 있는가 — validator (b) 가 잡지만 reviewer 도 확인".
141
+
142
+ ### 3.5 report-writer 정합 (`agents/workers/report-writer-worker.md`)
143
+
144
+ - `implementation-planning` authoring 안내에 한 줄: `decisionDrafts`/`skippedAdrCandidates` 를 populate(없으면 `[]`). shape 는 스키마 excerpt 가 주도하므로 최소 안내.
145
+
146
+ ### 3.6 스키마 excerpt / 빌드 흐름
147
+
148
+ - per-task-type 스키마 excerpt 빌더(`scripts/okstra_ctl/schema_excerpt.py`, `final_report_schema.py`)가 `implementationPlanning` 블록과 그 `$defs` 를 자동 포함하는지 확인한다. 새 `$defs/DecisionDraft`·`SkippedAdrCandidate` 가 `$ref` 로 도달 가능하므로 excerpt 에 자동 포함되어야 한다(`tests/report/test_schema_excerpt.py` 로 회귀 확인).
149
+ - `runtime/` 은 빌드 출력이므로 직접 수정 금지 — 소스 수정 후 `npm run build` 로 동기화.
150
+
151
+ ## 4. 변경 파일 목록
152
+
153
+ | 파일 | 변경 |
154
+ |---|---|
155
+ | `schemas/final-report-v1.0.schema.json` | `$defs/DecisionDraft`·`$defs/SkippedAdrCandidate` 신설; `implementationPlanning` 에 `decisionDrafts`·`skippedAdrCandidates` 추가(properties + required) |
156
+ | `templates/reports/final-report.template.md` | §5.4 에 `### Decision Drafts` 블록(draft 서브섹션 + skipped 라인 + empty-state) |
157
+ | `templates/reports/i18n/ko.json`, `en.json` | 섹션 aside·필드 라벨·empty-state 문자열 |
158
+ | `prompts/profiles/implementation-planning.md` | Decision-record evaluation 재작성(구조 필드 참조); heading contract·self-review 갱신 |
159
+ | `agents/workers/report-writer-worker.md` | decisionDrafts/skippedAdrCandidates populate 안내 1줄 |
160
+ | `validators/validate-run.py` | `PLANNING_REQUIRED_SECTIONS` 에 `Decision Drafts` 추가; `_validate_implementation_planning_decision_drafts` 신설 + 호출 배선 |
161
+ | `tests/` + `tests/fixtures/` | 아래 §6 검증 계획 |
162
+
163
+ ## 5. 강제 지점 매핑 (MUST → 어디서 강제되나)
164
+
165
+ | 규칙 | 강제 지점 |
166
+ |---|---|
167
+ | draft 7 필드 비-빈 + status const | JSON 스키마 `required`+`minLength`+`const` → `validate-run.py` 스키마 검증 |
168
+ | skipped 행 topic/reason 비-빈 | JSON 스키마 `required`+`minLength` |
169
+ | draft 있으면 `.okstra/decisions/` materialization step 존재 | `validate-run.py` `_validate_implementation_planning_decision_drafts` (신설, self-contained) |
170
+ | 섹션 헤딩 `Decision Drafts` 존재 | `validate-run.py` `PLANNING_REQUIRED_SECTIONS` substring + 템플릿 무조건 렌더 |
171
+ | report-writer 가 올바른 shape authoring | implementation-planning 스키마 excerpt 에 포함 |
172
+
173
+ ## 6. 검증 계획 (추가할 테스트)
174
+
175
+ - `tests/report/test_render_final_report.py`: `implementation-planning` 픽스처에 `decisionDrafts`(1행)·`skippedAdrCandidates`(1행) 추가 → 렌더 결과에 `#### <number>-<slug>` 서브섹션, Status/Context/Decision/Consequences/Alternatives Considered 라벨, `skipped adr-candidate: ... — reason: ...` 라인이 나오는지; 빈 배열 둘 → empty-state 라인이 나오는지.
176
+ - `tests/contract/` (또는 validate-run 테스트): `decisionDrafts` 가 비어있지 않은데 어느 stage 의 stepwise 도 `.okstra/decisions/` 를 참조하지 않으면 `contract-violated` 인지; draft 의 `context` 빈 값이면 스키마 검증 실패인지; materialization step 이 있으면 통과인지.
177
+ - `tests/report/test_schema_excerpt.py`: implementation-planning excerpt 에 새 `$defs` 가 포함되는지.
178
+ - **모든 implementation-planning 픽스처**(`tests/fixtures/final-report-data/implementation-planning-001.data.json` + 테스트 내 인라인 픽스처)에 `decisionDrafts`·`skippedAdrCandidates`(최소 `[]`) 추가 — `required` 승격으로 기존 테스트가 깨지지 않게.
179
+ - 완료 게이트: `npm run check` 가 그대로 exit 0.
180
+
181
+ ## 7. 한계 / 미해결
182
+
183
+ - **완전성(모든 adr-candidate 평가) 강제 불가**: "brief 의 모든 `adr-candidate:*` 를 draft 또는 skipped 로 처리했는가"는 brief 외부 입력과의 대조가 필요해 data.json 자기완결 검사로 watertight 하게 잡을 수 없다. 프로파일 self-review + §5.5.9 adversarial verification 휴리스틱으로만 보강한다(강제 아님 — cross-project spec §7 과 동일 종류 한계).
184
+ - **materialization step 의 정확성은 검사 안 함**: validator (b) 는 `.okstra/decisions/` 참조 step 의 *존재*만 본다. 그 step 이 올바른 `<NNNN>-<slug>` 를 가리키는지·draft 개수와 step 개수가 1:1 인지까지는 강제하지 않는다(self-review 책임).
185
+ - **planning 이 파일을 직접 쓰지 않음 유지**: 승인 게이트 보존을 위해 draft 는 보고서 staging 에만 두고, 실제 `.okstra/decisions/*.md` 생성은 implementation run 이 한다.
@@ -0,0 +1,121 @@
1
+ # Manual User Test 섹션 설계 (implementation → final-verification 연계)
2
+
3
+ - 작성일: 2026-06-24
4
+ - 상태: 설계 합의 완료, 구현 계획 대기
5
+ - 범위: okstra 산출물(implementation / final-verification)에 "사람이 직접 손으로 확인하는 테스트 절차"를 담는 섹션 신설
6
+
7
+ ## 1. 배경 — 실재하는 공백과 단절
8
+
9
+ 현재 okstra 산출물에는 "사람이 환경을 띄운 뒤 직접 클릭·확인하는 절차"를 적을 자리가 없다.
10
+
11
+ - implementation 딜리버러블의 테스트 섹션은 [final-report.template.md:458](../../../templates/reports/final-report.template.md) §5.7.6 Validation Evidence 하나뿐이며, 이는 `Phase | Command | Exit code | Output tail | TDD evidence` 구조의 **자동 검증 증거 전용**이다. 딜리버러블 명세 [_implementation-deliverable.md:19-20](../../../prompts/profiles/_implementation-deliverable.md) 의 Validation evidence / TDD evidence 도 전부 실행 명령 기반이다.
12
+ - planning 의 stage 별 `Acceptance:` 도 "RED 스텝을 PASS 로 뒤집는 같은 테스트 명령"으로 정의된다([implementation-planning.md:73](../../../prompts/profiles/implementation-planning.md)). 사람 절차가 아니다.
13
+ - final-verification 에는 사람 검증에 가까운 입력란이 있으나([final-verification-input.template.md:58](../../../templates/reports/final-verification-input.template.md) `Manual verification notes:`, `:70` `## Acceptance Criteria`), 이는 **검증 단계 입력**일 뿐 implementation 산출물로 흘러드는 "구현 직후의 테스트 절차"가 아니다.
14
+ - 결정적으로 [okstra-container-build/SKILL.md](../../../skills/okstra-container-build/SKILL.md) 는 사람이 만져볼 환경을 docker 로 띄워주지만(`:9` "so a human can poke at the running build"), `prompts/`·`templates/` 어디에서도 참조되지 않는 고립 스킬이다. **환경은 띄워주는데, 띄운 다음 무엇을 어떻게 확인할지를 적어둘 자리가 리포트에 없는 단절** 상태다.
15
+
16
+ ## 2. 목표 / 비목표
17
+
18
+ ### 목표
19
+ - implementation 단계가 "Manual User Test" 절차 **초안**을 산출하고, final-verification 단계가 그 초안을 **실제로 수행해 결과를 확정**한다.
20
+ - 관찰 가능한 변화가 있는 task 에 한해 절차를 요구한다(내부 전용 변경은 면제).
21
+ - `okstra-container-build` 와 리포트를 조건부로 연결해 단절을 해소한다.
22
+
23
+ ### 비목표 (YAGNI)
24
+ - 자동 E2E/통합 테스트 프레임워크 도입 — 기존 자동 검증(§5.7.6 / verifier)은 그대로 둔다.
25
+ - 모든 task 에 일괄 강제 — 관찰 불가 변경까지 N/A 스텁을 양산하지 않는다.
26
+ - docker-compose 가 없는 프로젝트에 컨테이너 사용 강요.
27
+
28
+ ## 3. 설계 결정 (확정)
29
+
30
+ | 결정 | 선택 | 근거 |
31
+ |---|---|---|
32
+ | 위치 | implementation(초안) ↔ final-verification(실행·확정) **양쪽 연계** | 절차 작성 시점(구현 직후)과 실행 시점(검증)이 다름. 단일 소유자 분리: 초안=implementation, 결과=final-verification |
33
+ | 범위 | 사람이 **관찰 가능한 변화**가 있을 때만 절차, 내부 전용이면 `사용자 테스트 불필요 — <사유>` 한 줄 | allowlist 방식으로 빈 섹션 노이즈 최소화 |
34
+ | container 연계 | **조건부** — `docker-compose.yml` 있으면 `/okstra-container-build` (`okstra container up <task-id>`), 없으면 일반 실행 명령 | 단절 해소 + compose 없는 프로젝트 과임 방지 |
35
+ | 강제 | **validator 구조 강제** — 헤딩 존재 + (절차 또는 면제사유) 중 하나, 빈 스텁 거부 | "관찰 가능 여부"는 의미 판단이라 validator 자동 판정 불가 → 구조만 강제, 적절성은 교차검증 워커가 판단. CLAUDE.md "MUST 는 강제 지점 명시" 충족 |
36
+
37
+ ## 4. 섹션 포맷 명세
38
+
39
+ ### 4.1 implementation 초안 — `## 5.7.x Manual User Test (Draft)`
40
+ 관찰 가능한 변화가 있으면 항목별로:
41
+
42
+ ```
43
+ - 대상: <어떤 사용자-facing 동작/기능>
44
+ - 환경 기동:
45
+ - (docker-compose.yml 있음) `/okstra-container-build` (`okstra container up <task-id>`) → published port 접속
46
+ - (없음) <프로젝트 실행 명령, 예: npm start / 해당 바이너리 실행>
47
+ - 확인 스텝:
48
+ 1. <사람이 하는 동작>
49
+ 2. ...
50
+ - 기대 결과: <관찰되어야 하는 것>
51
+ ```
52
+
53
+ 관찰 불가(내부 전용)면 항목 대신 한 줄:
54
+ ```
55
+ 사용자 테스트 불필요 — <사유, 예: 내부 리포지토리 메서드 리팩토링, 외부 관찰 동작 변화 없음>
56
+ ```
57
+
58
+ 절차 내용은 planning 의 `Acceptance:` 와 implementation 의 실제 diff 를 근거로 작성한다.
59
+
60
+ ### 4.2 final-verification 실행 결과 — `## 5.8.x Manual User Test Results`
61
+ implementation 초안의 각 항목을 수행하고:
62
+
63
+ ```
64
+ - 대상: <초안에서 인용>
65
+ - 수행: <실제로 한 동작 / 띄운 환경>
66
+ - 결과: pass | fail | blocked
67
+ - 관찰값: <실제로 본 것 — 초안의 기대 결과와 대조>
68
+ ```
69
+
70
+ 초안이 "사용자 테스트 불필요"였으면 그 사유의 타당성만 재확인(한 줄)하고 넘어간다.
71
+
72
+ ## 5. 데이터 흐름
73
+
74
+ ```
75
+ implementation-planning(Acceptance:)
76
+
77
+
78
+ implementation ──→ §5.7.x Manual User Test (Draft) [초안 단일 소유자]
79
+ │ │
80
+ │ (instruction-set 로 carry)
81
+ ▼ ▼
82
+ final-verification ──→ §5.8.x Manual User Test Results [결과 단일 소유자]
83
+
84
+ └── final-verification-input.template.md 의 Manual verification notes 입력란을 이 흐름과 통합
85
+ ```
86
+
87
+ ## 6. 영향 파일별 변경
88
+
89
+ | 파일 | 변경 |
90
+ |---|---|
91
+ | [prompts/profiles/_implementation-deliverable.md](../../../prompts/profiles/_implementation-deliverable.md) | 딜리버러블 항목에 "Manual user test draft" 추가 — §4.1 포맷, 범위 규칙(관찰 가능 시에만), 조건부 container 안내 지시 |
92
+ | [prompts/profiles/final-verification.md](../../../prompts/profiles/final-verification.md) | 초안을 수행·확정하라는 지시 추가 — §4.2 포맷 |
93
+ | [templates/reports/final-report.template.md](../../../templates/reports/final-report.template.md) | §5.7 에 `Manual User Test (Draft)` 하위섹션 신설(Routing Recommendation 직전 배치, 이후 번호 +1), §5.8 에 `Manual User Test Results` 하위섹션 신설(Routing 직전) |
94
+ | [templates/reports/final-verification-input.template.md](../../../templates/reports/final-verification-input.template.md) | `Manual verification notes:`(:58) 를 implementation 초안 인용 흐름과 통합 |
95
+ | [validators/validate-run.py](../../../validators/validate-run.py) | 기존 §5.7/§5.8 deliverable 섹션 scan 리스트(:1145-1163)에 신설 섹션명 추가 + 빈 스텁/면제사유 거부 체크 |
96
+ | [skills/okstra-container-build/SKILL.md](../../../skills/okstra-container-build/SKILL.md) | 리포트↔container 양방향 포인터 한 줄(이 절차 섹션에서 호출됨을 명시) |
97
+
98
+ > 구현 후 `npm run build` 로 `runtime/` 동기화 필요. 엔드유저 반영은 `okstra install` 재실행.
99
+
100
+ ## 7. enforcement (validator 체크 명세)
101
+
102
+ `validators/validate-run.py` 에 다음을 추가한다. 의미 판단(관찰 가능 여부)은 하지 않고 **구조만** 본다. 구현은 기존 메커니즘 위에 얹는다 — §5.7/§5.8 deliverable 섹션 제목 substring scan 리스트([validate-run.py:1145-1163](../../../validators/validate-run.py))에 신설 섹션명을 추가하면 헤딩 누락이 기존 "missing required §5.7" 에러([validate-run.py:1688](../../../validators/validate-run.py))로 잡힌다. 빈 스텁/면제사유 누락 거부는 deprecated stub 거부([validate-run.py:759](../../../validators/validate-run.py))와 동형의 체크를 추가한다.
103
+
104
+ - **implementation 리포트**: `## 5.7.x Manual User Test (Draft)` 헤딩이 존재해야 한다(MUST). 본문은 다음 중 하나여야 하며, 빈 스텁/플레이스홀더는 거부한다:
105
+ - 최소 1개의 절차 항목(대상 + 확인 스텝 + 기대 결과), 또는
106
+ - `사용자 테스트 불필요 — ` 로 시작하는 면제 한 줄(사유 비어 있으면 거부).
107
+ - **final-verification 리포트**: `## 5.8.x Manual User Test Results` 헤딩 존재(MUST). 본문은 결과 항목(결과=pass/fail/blocked) 또는 면제 재확인 한 줄.
108
+ - 절차의 **적절성·정확성**(스텝이 실제 변경을 커버하는지)은 validator 가 아니라 교차검증 워커/컨버전스가 판단한다 — validator 는 구조 게이트일 뿐임을 주석으로 명시.
109
+
110
+ 이로써 "Manual User Test 섹션은 implementation/final-verification 리포트에 필수" 라는 MUST 가 [validate-run.py](../../../validators/validate-run.py) 라는 구체적 강제 지점을 갖는다.
111
+
112
+ ## 8. 테스트 계획
113
+
114
+ - `tests/contract/` 또는 해당 validator 테스트: (a) 절차 항목 있는 리포트 통과, (b) 헤딩 누락 거부, (c) 빈 스텁 거부, (d) 면제 한 줄(사유 있음) 통과, (e) 면제 사유 비면 거부.
115
+ - `tests/report/` 또는 template 렌더 테스트: 신설 §5.7.x / §5.8.x 헤딩이 implementation / final-verification 렌더에 나타나고 다른 taskType 에는 안 나타나는지.
116
+ - `bash validators/validate-workflow.sh` 통과.
117
+ - 완료 게이트: `npm run check` exit 0.
118
+
119
+ ## 9. 미해결 사항
120
+
121
+ 없음. (섹션 정확 번호는 구현 시 §5.7/§5.8 재배치로 확정 — Routing Recommendation 을 항상 마지막으로 유지)
@@ -0,0 +1,110 @@
1
+ # okstra-container-build 스킬 rename 설계
2
+
3
+ - 작성일: 2026-06-24
4
+ - 상태: 설계 합의 완료, 구현 계획 대기
5
+ - 범위: 공개 agent skill 이름을 `okstra-container` 에서 `okstra-container-build` 로 변경
6
+
7
+ ## 1. 배경
8
+
9
+ 현재 컨테이너 기능은 세 표면이 분리되어 있다.
10
+
11
+ - 공개 스킬 이름과 설치 대상은 `src/lib/skill-catalog.mjs` 의 `USER_SKILL_NAMES` 가 정본이다([skill-catalog.mjs:7](../../../src/lib/skill-catalog.mjs)).
12
+ - 플러그인 보조 채널은 `.claude-plugin/plugin.json` 의 `skills` 배열을 사용한다([plugin.json:4](../../../.claude-plugin/plugin.json)).
13
+ - 실제 CLI 명령은 `okstra container ...` 이며, Node shim 이 Python `okstra_ctl.container` 로 위임한다([container.mjs:3](../../../src/commands/inspect/container.mjs), [container.py:927](../../../scripts/okstra_ctl/container.py)).
14
+
15
+ 사용자 요청은 스킬 이름을 `okstra-container-build` 로 바꾸는 것이다. 따라서 slash-command / skill discovery 표면은 새 이름으로 바꾸되, 안정된 CLI 명령과 런타임 리소스 이름은 유지한다.
16
+
17
+ ## 2. 목표 / 비목표
18
+
19
+ ### 목표
20
+
21
+ - 설치되는 공개 스킬과 slash command 를 `/okstra-container-build` 로 바꾼다.
22
+ - 신규 설치와 업그레이드 모두에서 옛 `/okstra-container` 가 남지 않게 한다.
23
+ - README, architecture, AI manual, phase prompt 의 사용자-facing 안내를 새 스킬명으로 맞춘다.
24
+ - `okstra container` CLI 와 Python entrypoint 는 유지해 기존 자동화와 테스트 표면을 흔들지 않는다.
25
+
26
+ ### 비목표
27
+
28
+ - `okstra container` CLI command rename.
29
+ - `okstra-container-<slug>` tmux session prefix rename.
30
+ - docker compose 생성 기능 추가. `build` 는 스킬 이름의 suffix 일 뿐, 기존처럼 프로젝트의 `docker-compose.yml` 을 재사용한다.
31
+ - 과거 changelog / 과거 구현 계획 문서의 역사적 문구 전면 수정.
32
+
33
+ ## 3. 검토한 접근
34
+
35
+ | 접근 | 판단 | 이유 |
36
+ |---|---|---|
37
+ | A. 공개 스킬명만 rename, CLI/runtime 이름 유지 | 선택 | 사용자 요청을 만족하면서 `okstra container` 자동화, Python argparse, tmux session 추적을 보존한다. |
38
+ | B. CLI 도 `okstra container-build` 로 rename | 제외 | `src/commands/inspect/container.mjs` 와 `scripts/okstra_ctl/container.py` 의 명령 계약까지 깨져 범위가 커진다. |
39
+ | C. 새 스킬을 추가하고 옛 `okstra-container` alias 를 유지 | 제외 | 설치 후 slash command 가 두 개 보인다. 사용자 요청이 rename 이므로 옛 이름은 upgrade 시 prune 해야 한다. |
40
+
41
+ ## 4. 설계 결정
42
+
43
+ | 결정 | 선택 | 근거 |
44
+ |---|---|---|
45
+ | 스킬 정본 | `USER_SKILL_NAMES` 의 `okstra-container` 를 `okstra-container-build` 로 교체 | installer 와 plugin sync 테스트가 이 목록을 기준으로 맞물린다([install.mjs:631](../../../src/commands/lifecycle/install.mjs), [skill-catalog.test.mjs:14](../../../tests-js/skill-catalog.test.mjs)). |
46
+ | 원천 디렉터리 | `skills/okstra-container/` 를 `skills/okstra-container-build/` 로 이동 | install copy/link mode 모두 `<name>/SKILL.md` 디렉터리명을 사용한다([install.mjs:631](../../../src/commands/lifecycle/install.mjs), [install.mjs:653](../../../src/commands/lifecycle/install.mjs)). |
47
+ | frontmatter | `name: okstra-container-build` | agent skill discovery 는 frontmatter 이름을 사용자 표면으로 노출한다. |
48
+ | CLI | `okstra container <up|status|logs|stop-watcher|down>` 유지 | CLI shim 과 Python parser 는 컨테이너 런타임 API 이름이며 스킬명과 독립적이다([container.mjs:3](../../../src/commands/inspect/container.mjs), [container.py:927](../../../scripts/okstra_ctl/container.py)). |
49
+ | runtime session prefix | `okstra-container-<slug>` 유지 | tmux/docker trace 리소스 이름이며 테스트가 고정한다([ids.py:93](../../../scripts/okstra_ctl/ids.py), [test_container_session_name.py:14](../../../tests/container/test_container_session_name.py)). |
50
+ | upgrade cleanup | 옛 `okstra-container` 를 exact-name prune 대상에 추가 | 설치는 새 스킬을 복사하지만 기존 skill home 의 옛 디렉터리를 자동 삭제하지 않는다([install.mjs:669](../../../src/commands/lifecycle/install.mjs)). |
51
+
52
+ ## 5. 영향 파일
53
+
54
+ ### 소스 / 설치 계약
55
+
56
+ | 파일 | 변경 |
57
+ |---|---|
58
+ | `skills/okstra-container/SKILL.md` | 디렉터리명을 `skills/okstra-container-build/` 로 변경하고 frontmatter `name` 갱신. 본문에서 slash-command 명칭만 새 이름으로 맞추고 `okstra container ...` CLI 예시는 유지. |
59
+ | `src/lib/skill-catalog.mjs` | `USER_SKILL_NAMES` 에서 `okstra-container-build` 로 교체. `OBSOLETE_INTERNAL_SKILL_NAMES` 는 `OBSOLETE_SKILL_NAMES` 로 재명명하고, 옛 공개 스킬 `okstra-container` 를 같은 exact-name prune 목록에 추가한다. |
60
+ | `.claude-plugin/plugin.json` | `./skills/okstra-container-build` 로 교체. |
61
+ | `src/commands/lifecycle/uninstall.mjs` | fallback skill names 에 `okstra-container-build` 를 포함하고, 옛 `okstra-container` 도 fallback cleanup 대상에 유지한다. |
62
+ | `tools/build.mjs` | 코드 변경 없음. `skills/` sync 로 새 디렉터리가 `runtime/skills/okstra-container-build` 로 반영되는지 build 로 검증. |
63
+
64
+ ### 문서 / 프롬프트
65
+
66
+ | 파일군 | 변경 |
67
+ |---|---|
68
+ | `README.md`, `README.kr.md` | slash command 표를 `/okstra-container-build` 로 갱신. |
69
+ | `docs/kr/architecture.md`, `docs/project-structure-overview.md` | 공개 스킬 7종 목록과 skill role 표를 새 이름으로 갱신. CLI `container` 설명은 유지하되 "for the okstra-container-build skill" 로 조정. |
70
+ | `docs/for-ai/README.md`, `docs/for-ai/skills/okstra-container.md` | AI manual 파일명을 `okstra-container-build.md` 로 바꾸고 라우팅 표를 새 이름으로 갱신. |
71
+ | `docs/for-ai/skills/okstra-inspect.md` | "container 는 별도 스킬" 링크를 새 manual 로 갱신. |
72
+ | `prompts/profiles/_implementation-deliverable.md`, `prompts/profiles/final-verification.md` | 사람이 실행할 스킬명은 `/okstra-container-build`, 실제 CLI 명령 예시는 `okstra container up ...` 로 표현해 혼동을 줄인다. |
73
+ | `docs/kr/container.md` | CLI 레퍼런스는 제목과 명령을 유지하고, 단일 진입점 bullet 의 스킬명만 `/okstra-container-build` 로 갱신. |
74
+
75
+ ### 그대로 둘 파일군
76
+
77
+ - `CHANGES.md`, `CHANGELOG.md`: 과거 릴리스 기록이므로 기존 `okstra-container` 항목을 역사로 보존한다. 이번 변경은 새 항목만 추가한다.
78
+ - `docs/superpowers/plans/2026-06-21-okstra-container-local-user-test.md`, `docs/superpowers/specs/2026-06-21-okstra-container-local-user-test-design.md`: 과거 기능 도입 당시 설계/계획 문서라 전체 rewrite 하지 않는다. 현재 문서로 rename 결정을 보완한다.
79
+ - `tests/container/*` 의 `okstra-container-<slug>` 기대값: 런타임 session prefix 검증이므로 유지한다.
80
+
81
+ ## 6. 업그레이드 동작
82
+
83
+ 문제: 현재 install 은 `USER_SKILL_NAMES` 를 설치한 뒤 manifest 를 새 목록으로 쓴다. 하지만 이미 `~/.claude/skills/okstra-container` 또는 `~/.agent/skills/okstra-container` 가 있으면 새 install 만으로 옛 디렉터리가 삭제되지 않는다.
84
+
85
+ 설계:
86
+
87
+ 1. old public skill 이름 `okstra-container` 를 exact-name prune 대상에 넣는다.
88
+ 2. `installSkillsCopy()` / `installSkillsLink()` 의 prune 단계에서 old dir 을 제거한다.
89
+ 3. wildcard `okstra-*` 삭제는 금지한다. 기존 obsolete cleanup 도 exact-name 방식이다([install.mjs:666](../../../src/commands/lifecycle/install.mjs)).
90
+ 4. uninstall fallback 은 manifest 가 없는 오래된 설치도 정리할 수 있게 old/new 이름을 모두 포함한다.
91
+
92
+ ## 7. 테스트 계획
93
+
94
+ - `node --test tests-js/skill-catalog.test.mjs`: plugin manifest 와 `USER_SKILL_NAMES` 순서 동기화.
95
+ - `node --test tests-js/install-runtime.test.mjs`: 새 스킬이 Claude/Codex skill home 에 설치되고 manifest 에 기록되는지 확인. 추가 회귀 테스트로 old `okstra-container` 디렉터리를 seed 한 뒤 install 이 prune 하는지 검증.
96
+ - `node --test tests-js/doctor-runtime.test.mjs`: 비필수 container skill 이 `REQUIRED_SKILLS` 에 들어가지 않는다는 테스트명을 새 이름으로 갱신.
97
+ - `python3 -m pytest tests/contract/test_docs_runtime_contract.py`: plugin manifest 계약과 docs runtime 계약 갱신.
98
+ - `npm run build`: `runtime/skills/okstra-container-build/SKILL.md` 생성과 `runtime/skills/okstra-container/` 부재 확인.
99
+ - 최종 게이트: `npm run check`.
100
+
101
+ ## 8. 사용자 영향
102
+
103
+ - 새 설치와 재설치 후 스킬 호출명은 `/okstra-container-build` 가 된다.
104
+ - 기존 `okstra container up/status/logs/stop-watcher/down` CLI 는 그대로 동작한다.
105
+ - 기존 tmux watcher/session 이름과 docker trace 라벨은 유지된다.
106
+ - 이전 `/okstra-container` 는 upgrade install 과정에서 제거된다.
107
+
108
+ ## 9. 미해결 사항
109
+
110
+ 없음.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.101.1",
3
+ "version": "0.102.1",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.101.1",
3
- "builtAt": "2026-06-24T05:11:23.276Z",
2
+ "package": "0.102.1",
3
+ "builtAt": "2026-06-24T14:07:45.289Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -101,6 +101,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
101
101
  - Cite file paths and line numbers in every `evidence.primary[].source` / `consensus[].evidence` cell.
102
102
  - Preserve every analysis worker's ticket tagging — every row's `ticketId` field carries the ticket key or the task-fallback. For single-ticket runs, set `ticketCoverage` to `{"singleTicket": "<ticket>"}`. For runs that do not require ticket tagging (`release-handoff`, `final-verification`), set `ticketCoverage` to `{"omit": true}`.
103
103
  - For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage.
104
+ - For `implementation-planning`, also populate `implementationPlanning.decisionDrafts` (one row per decision meeting all three decision-record criteria; `[]` otherwise) and `implementationPlanning.skippedAdrCandidates` (evaluated-but-dropped adr-candidates; `[]` otherwise). The schema excerpt enumerates the row shape; the renderer emits §5.4 `### Decision Drafts`. When `decisionDrafts` is non-empty, the plan's stages MUST carry a stepwise step that creates `.okstra/decisions/<NNNN>-<slug>.md` (validate-run gates this).
104
105
  - When the `Task Type` is `improvement-discovery`, populate `## 5.9 Improvement Candidates` with the 10-column schema enforced by `validators/validate_improvement_report.py`. Source the row IDs (`I-NNN`), lens whitelist, and Source workers patterns from `scripts/okstra_ctl/improvement_lenses.py` — do NOT introduce new lens names or worker prefixes. `improvement-discovery` is NOT in the data.json schema enum, so author its markdown directly (not via `okstra-render-final-report.py`). Immediately after writing the markdown, run (`Bash`): `okstra inject-report-index <markdown path> --report-language <en|ko>`. That adds the top-of-report Index plus `I-NNN` / `C-NNN` scroll anchors; the run validator fails the report when the Index anchor is absent.
105
106
 
106
107
  Write the data.json (and the audit sidecar `.md`) with your `Write` tool — that is the canonical authoring path, and okstra ships no hook that blocks `.md` writes (its only settings hook is the `SessionEnd` trace-cleanup; the coding-preflight hook emits reminders but never blocks). A Bash heredoc is acceptable ONLY when a specific `Write` call is genuinely rejected by the host environment, and it MUST produce byte-identical content — do not reach for it pre-emptively. Then invoke the renderer (`Bash`): `okstra render-final-report <data.json path>`. Confirm both files exist and respond with a short status line prefixed by your model identity, copied verbatim from the `**Model:** Report writer worker, <modelExecutionValue>` line in your dispatch prompt (per Worker Preamble → "Return message to the lead"):
@@ -281,7 +281,7 @@ Every field MUST anchor its claim with at least one evidence reference — a `pa
281
281
  3. **Evidence and Detailed Analysis** — primary evidence rows (file path, line, snippet); secondary evidence / alternate interpretations. If `reference-expectations.md` lists explicit expected values, record match/gap per row.
282
282
  4. **Missing Information and Risks** — uncertain / "I don't know" items. `implementation-planning` adds §5.5 (see heading contract below); `release-handoff` adds §5.6.
283
283
  5. **Clarification Items** — single unified `C-*` table; column schema (4 columns with the short fields stacked in one record-meta cell), ID convention, and rerun behaviour are owned by `_common-contract.md §Clarification request policy` (SSOT). The deprecated `5.5.9 Open Questions` / `1.1 추가 자료 요청` / `1.2 사용자 확인 질문` sub-sections are removed; the validator fails reports that reintroduce them.
284
- 6. **Recommended Next Steps** — prioritized actions. After Phase 7's follow-up spawner runs, append a row per newly created task-key (see "Phase 6 → Phase 7 execution sequence" above). **Approval-gate consistency:** when §1 carries any `Blocks: approval` row with `Status` ∈ {open, answered}, the Verdict Card `Next Step` and the first recommended step MUST point to the clarification rerun (`resume-clarification` of the SAME task-type) — never to "frontmatter `approved: true` 플립 → `implementation` 직행". Run-prep enforces this gate (`run.py _validate_approved_plan` fail-closes on those rows and on a blocking data.json `gateResult`), so a direct-implementation next-step is an instruction the reader cannot actually follow.
284
+ 6. **Recommended Next Steps** — prioritized actions. After Phase 7's follow-up spawner runs, append a row per newly created task-key (see "Phase 6 → Phase 7 execution sequence" above). **Approval-gate consistency:** when §1 carries any `Blocks: approval` row with `Status` ∈ {open, answered}, the Verdict Card `Next Step` and the first recommended step MUST point to the clarification rerun (`resume-clarification` of the SAME task-type) — never to "frontmatter `approved: true` 플립 → `implementation` 직행". Run-prep enforces this gate (`run.py _validate_approved_plan` fail-closes on those rows and on a blocking data.json `gateResult`), so a direct-implementation next-step is an instruction the reader cannot actually follow. **Cross-project pointer rule:** cross-project 의존(타 repo / 다른 top-level 배포 모듈 / published 패키지)은 `crossProjectDependencies`(§5.4 Cross-Project Dependencies)가 authoritative — `recommendedNextSteps` 에는 그 substance(선행 작업·검증 신호·handoff)를 복제하지 말고 그 섹션을 가리키는 포인터 한 줄만 둔다(이중 기록 금지).
285
285
  7. **Follow-up Tasks** — auto-spawn-eligible table. Each row drives `okstra-spawn-followups.py`; see template §7 for the row schema.
286
286
 
287
287
  **§5.10 Fix History (data-presence gated).** When the run-manifest carries a `fixCycleId`, fill the data.json `fixCycle` block (`cycle` / `targetReport` / `symptom` / `runs`). Read the values from the task root's `history/fix-cycles.jsonl`: `cycle` MUST equal `fixCycleId`, `targetReport` / `symptom` come from that cycle's `opened` row, and `runs` lists its attached `run` rows (`taskType` / `runSeq` / `runManifest`). The validator (`validators/validate-run.py` → `_validate_fix_cycle`) fails the run when the block is missing or `fixCycle.cycle` does not match `fixCycleId`. When the run-manifest has no `fixCycleId`, OMIT the `fixCycle` block entirely — the renderer omits §5.10.
@@ -398,7 +398,7 @@ okstra token-usage /abs/path/to/run/state/team-state-<task-type>-<seq>.json --wr
398
398
 
399
399
  The script reads:
400
400
  - `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl` for the lead and every Claude-side worker (Claude worker, Report writer worker, plus the Claude wrappers around Codex/Antigravity workers). Sessions are discovered by the recorded team-state `teamName`, lead is identified by `lead.sessionId`, and other workers are identified by `agentName` (e.g. `claude-worker`, `codex-worker`, `antigravity-worker`, `report-writer`). **For this `agentName` match to work, Lead MUST set the Agent `name` arg to `<workerId>-worker` on every dispatch** (see [agents okstra-lead-contract.md Phase 4 — "Agent `name` on dispatch"](./okstra-lead-contract.md)); a worker dispatched without `name` carries no `agentName`, so the collector cannot attribute its session and records it `unavailable` (now surfaced as a `usageSummary.unattributedTeamSessions` entry rather than dropped silently).
401
- - `~/.codex/sessions/Y/M/D/rollout-*.jsonl` for the underlying Codex CLI session (matched by `cwd` and timestamp window of the wrapper subagent). Last `event_msg.token_count.total_token_usage.total_tokens` is the session total.
401
+ - `~/.agent/sessions/Y/M/D/rollout-*.jsonl` for the underlying Codex CLI session (matched by `cwd` and timestamp window of the wrapper subagent). Last `event_msg.token_count.total_token_usage.total_tokens` is the session total.
402
402
  - `~/.antigravity/tmp/<project>/chats/session-*.json` for the underlying Antigravity CLI session. Sum of per-message `tokens.total`.
403
403
 
404
404
  ### Resulting team-state shape
@@ -424,7 +424,7 @@ The script reads:
424
424
  "totalTokens": 2274011,
425
425
  "source": "claude-jsonl",
426
426
  "sessionId": "<wrapper-session-id>",
427
- "cliSessionPaths": ["/Users/.../.codex/sessions/.../rollout-*.jsonl"],
427
+ "cliSessionPaths": ["/Users/.../.agent/sessions/.../rollout-*.jsonl"],
428
428
  "cliTotalTokens": 5261833,
429
429
  "cliModel": "gpt-5.5"
430
430
  }
@@ -30,6 +30,7 @@ are collected and convergence finished. Phase 1-5 do not need it.
30
30
  - **Pure code changes** (no persisted state, no infra mutation): a reachable revert SHA is sufficient. Record the exact `git revert <SHA>` command that would undo the change, and confirm `git rev-parse <SHA>` resolves.
31
31
  - **Feature-flag-gated changes**: confirm the off-switch path was exercised in this run's validation evidence (i.e. one of the validation commands ran with the flag off and succeeded). A plan that ships a flag without exercising the off-path does NOT satisfy this requirement.
32
32
  - **Schema migrations, config-format changes, or any change with persisted state**: a **dry-run of the rollback step is mandatory**, not preferred. Record the exact rollback command and its captured exit code / stdout. If the migration tool offers no dry-run mode (`--dry-run`, `--plan`, equivalent), the executor MUST refuse to claim rollback verification and instead end the run with a routing recommendation back to `implementation-planning` for a safer rollback strategy. Skipping this step on a stateful change is treated as a `contract-violated` outcome by `final-verification`.
33
+ - **Manual user test draft**: when this run produces a user-observable change (UI / API / CLI / artifact), write `target / environment / steps / expected result` per change into §5.7.9 (data field `implementation.manualUserTest`, `applicable=true` with `items`). Environment line: if the project has a `docker-compose.yml`, use `/okstra-container-build` (which runs `okstra container up <task-id>`) then connect to the published port; otherwise the project's run command (e.g. `npm start`). When there is no user-observable change, set `applicable=false` and give a one-line `exemptionReason` instead of items. Base the steps on the approved plan's `Acceptance:` and this run's actual diff — these are the steps a human (or `final-verification`) re-runs by hand, NOT the automated validation commands in `Validation evidence`.
33
34
  - **Routing recommendation for `final-verification`**: brief note on whether the changes are ready for final-verification phase or need a new error-analysis / planning loop first.
34
35
  - **Follow-up tasks (Section 4 of the final report)**: every item discovered during this run that was *not* delivered MUST appear in the final report's `## 4. Follow-up Tasks (후속 작업)` table with a concrete `Origin`, `New Task ID`, `Suggested task-type`, `Scope`, and `Reason / Why deferred`. Sources include: out-of-scope discoveries that the executor consciously chose not to fold into this run, verifier concerns the executor declined to fix in-place, scope-boundary items from the approved plan that turned out to need their own ticket, and any unresolved `## 1. Clarification Items` row carried over from the approved plan (`Status` ∈ `{open, answered}` at approval time). An empty section is acceptable but only when expressed as the single line `- 후속 작업 없음.` — silence is treated as a contract violation. Rows with `Auto-spawn? = yes` will be materialised by `scripts/okstra-spawn-followups.py` in Phase 7; rows with `Auto-spawn? = no` MUST also appear in `Section 3. Recommended Next Steps` so the user knows to act manually.
35
36
 
@@ -46,6 +47,7 @@ are collected and convergence finished. Phase 1-5 do not need it.
46
47
  ```
47
48
  Only newly-added lines (those starting with `+` and not part of the `+++` header) are inspected. If output is anything other than `clean`, the run MUST either remove the placeholders before finalising or record an explicit justification per occurrence in the final report.
48
49
  7. **Stage-foreign literal scrub** — when the report-writer modelled this stage's `data.json` on another stage's report (a common shortcut for structural consistency), stage-specific literals get copied verbatim and silently misattribute this run. Confirm every branch name, commit SHA, stageKey, and stage number in the report resolves to **this** run's stage `<N>` — its worktree branch is `<prefix>-<task-id>-s<N>`, its stageKey `<task-id>-stage-<N>`. Sweep the report for any `-s<M>` / `stage-<M>` / `Stage <M>` where `M ≠ N` and for SHAs not in this run's `Commit list`; each hit is a copy-from-other-stage defect to correct before finalising.
50
+ 8. **Manual user test coverage** — when this run changed user-observable behaviour, §5.7.9 must carry concrete steps (not the `applicable=false` exemption). A user-facing change shipped with an exemption line is a contract violation; an internal-only change with no observable surface is the only valid use of the exemption.
49
51
 
50
52
  ## Lead post-stage persistence (BLOCKING — runs after the Executor emits `### Stage Carry Evidence`)
51
53
 
@@ -24,13 +24,13 @@
24
24
  - documentation or rollout gaps
25
25
  - production-specific failure modes not caught by tests (env/config drift across stages, secrets & permission/auth changes, migration ordering & rollback executability, observability gaps)
26
26
  - Pre-verification entry gate (resolved & enforced by `okstra render-bundle` prep — the lead does NOT recompute it):
27
- - the verification target (scope / worktree / base / head / stages / source reports / diff stat) is injected as the `VERIFICATION_TARGET` block. The lead MUST treat it as authoritative and MUST NOT re-pick a target from the brief.
27
+ - the verification target (scope / worktree / base / stages / source reports / diff stat) is injected as the `VERIFICATION_TARGET` block. The lead MUST treat it as authoritative and MUST NOT re-pick a target from the brief.
28
28
  - **whole-task scope** (`--stage auto`, default): prep has already verified every Stage Map stage is `status:done` in `consumers.jsonl`, every done stage's `head_commit` is an ancestor of the task worktree HEAD (all stage branches merged), and the worktree is clean outside `.okstra/`. If any check failed the run never started (PrepareError); a started whole-task run is therefore a fully-merged, clean target.
29
29
  - **whole-task 은 read-only 가 아니라 mutating phase 다.** whole-task 모드는 진입 시 아직 task 브랜치에 머지되지 않은 done stage 들을 `--no-ff` 로 자동 머지해 통합 커밋을 생성하고, 이어서 정리 가능한 stage worktree·registry stage-key·stage 브랜치를 제거한다. 머지 충돌이 발생하면 충돌 파일을 보고하고 중단한다(사용자가 수동 해소 후 재시도). 미커밋 변경이 남은 stage worktree 는 보존한다. 따라서 위 entry gate 가 말하는 "fully-merged, clean target" 은 이 자동 통합 단계가 끝난 뒤의 상태이며, whole-task final-verification 은 통합 커밋을 만드는 mutating phase 로 취급해야 한다.
30
30
  - **single-stage scope** (`--stage N`): prep verified stage N is `status:done` and its isolated stage worktree exists and is clean. Other stages' state is irrelevant. A single-stage run is a partial verification: it MUST NOT recommend plain `release-handoff`, but MAY recommend `release-handoff(stage-group)` when the verdict is `accepted` — the stage becomes PR-eligible for a stage-group handoff.
31
- - the lead still captures `git rev-parse HEAD` / `git status --short` from the injected worktree to confirm the analysis ran against the injected head; a mismatch is a `tool-failure`, not a silent proceed.
31
+ - the lead still captures `git status --short` from the injected worktree to confirm the analysis ran against the delivered work-tree state; an unexpected divergence (dirty tree outside `.okstra/`, missing worktree) is a `tool-failure`, not a silent proceed.
32
32
  - Required deliverable shape (final report, in addition to the standard sections):
33
- - **Source Implementation Report(s)**: the `VERIFICATION_TARGET` snapshot verbatim — verification scope, worktree path, base/head SHAs, the list of stages under verification, and one row per stage citing its originating implementation final-report (`report_path` from `consumers.jsonl`; render `(report_path unrecorded)` when absent). The lead injects this same snapshot into every analyser prompt (`**Verification scope:** / **Worktree:** / **Verification base ref:** / **Verification head SHA:** / **Verification diff stat:**`); a worker that cannot confirm its analysis ran against that exact head MUST record a `tool-failure`.
33
+ - **Source Implementation Report(s)**: the `VERIFICATION_TARGET` snapshot verbatim — verification scope, worktree path, base ref, the list of stages under verification, and one row per stage citing its originating implementation final-report (`report_path` from `consumers.jsonl`; render `(report_path unrecorded)` when absent). The lead injects this same snapshot into every analyser prompt (`**Verification scope:** / **Worktree:** / **Verification base ref:** / **Verification diff stat:**`); a worker that cannot confirm its analysis ran against that worktree's delivered diff (against `base ref`) MUST record a `tool-failure`.
34
34
  - **Verdict vocabulary**: Section 7 (`Final Verdict`) MUST include a `Verdict Token` field whose value is exactly one of `accepted`, `conditional-accept`, or `blocked`. `conditional-accept` requires an explicit, exhaustive list of conditions; ambiguous verdicts ("looks good", "mostly ready") are not allowed. Each condition MUST be recorded as a row in the **Conditional Acceptance Conditions** deliverable (`id` `CA-NNN`, `condition`, `evidenceRequired`, `blocksReleaseHandoff`). The validator enforces verdict↔deliverable consistency: `accepted` ⇒ zero acceptance blockers, `blocked` ⇒ at least one, `conditional-accept` ⇒ at least one condition, and a `release-handoff` routing recommendation is allowed only when the verdict is `accepted`.
35
35
  - **Acceptance Blockers block** (under section 4): one row per blocker with `id`, `severity` (`critical` / `major` / `minor`), evidence (file path, log excerpt, or test output), and the recommended follow-up phase (`error-analysis` or `implementation-planning`). Empty block is acceptable and preferred — render the single line `- No acceptance blockers found.`
36
36
  - **Residual Risk block** (under section 4): risks that are not blockers but should be tracked, each with mitigation owner and a trigger that would escalate them to a blocker.
@@ -38,6 +38,7 @@
38
38
  - **Read-only command log**: any pre-existing test/validation command executed during this run MUST be listed with its exact command line and exit code. No mutating commands may appear here.
39
39
  - **Two-tier command lookup (shared with `implementation`):** when this phase performs its own independent re-validation, the command source is exactly the same two tiers `implementation` verifiers use — Tier 1 is the originating task brief / approved plan's `validation` set, Tier 2 is `<PROJECT_ROOT>/.okstra/project.json` under `qaCommands`. Auto-detecting tools from manifest files is forbidden; missing tiers are recorded as `qa-command not configured: <category>` rather than guessed. The `cmd` deny-list (`--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, `npm install` without `ci`, etc.) is enforced identically. NOTE: runtime fail-fast validation (`okstra_ctl.qa_commands.validate_qa_commands`) only fires at `--task-type implementation` run-prep, so this phase MUST self-check each `qaCommands` entry against the deny-list before executing it — if a denied token is present, skip the command and record it as a `Read-only command log` line `qa-command rejected (denied token: <token>): <label>`.
40
40
  - **Tier 3 — stage conformance scripts (whole-task union):** because this phase verifies the **integrated, merged** state, it re-runs conformance against that state rather than per-stage. Read the task-level manifest `<task_root>/qa/conformance-manifest.json` (the directory is the `TASK_QA_PATH` token) and, in **whole-task scope**, run the `runCommand` of **every** `entries[]` item against the merged worktree, refreshing each `<task_root>/qa/result-<stageKey>.json` (`{ "stageKey", "overall": "PASS"|"FAIL"|"MISSING", "ranAt", "requirements" }`). In **single-stage scope**, run only the entry whose `stageKey` matches the verified stage. An entry carrying an `exemption` or user `waiver` is NOT executed — record the skip and reason; a `waiver` becomes a `conditional-accept` condition surfaced in the section 7 Verdict (conformance left unverified by user acknowledgement). Each `runCommand` runs in the worktree cwd with `qaEnv` env (replica DB DSN / app base URL / env file) — **replica / test environment only**, never shared / staging / prod, and the same source/lockfile mutation deny-list applies (a conformance script MAY mutate only its `qaEnv` replica datastore). Interpret each result from the exit code + stdout `QA-RESULT: PASS|FAIL` (last wins) and `REQ <id>: PASS|FAIL: <reason>` lines; no `QA-RESULT` marker → `MISSING`. Any entry whose result is not `PASS` (including `MISSING` or a never-run/missing sidecar) is an **Acceptance Blocker** (`major`+), so the verdict becomes `conditional-accept` / `blocked`. This is the same gate the `validate-run.py` Tier 3 check enforces on the result sidecars.
41
+ - **Manual user test results**: take each item from the source implementation report's §5.7.9 Manual User Test (Draft), execute the ones reproducible in this environment (e.g. `/okstra-container-build`, which runs `okstra container up`, then the documented steps), and record `result` (`pass` / `fail` / `blocked`) + observed value in §5.8.7 (data field `finalVerification.manualUserTest`). Steps that need human-only interaction this run cannot perform are recorded as `blocked` with the reason (handed to the user), never silently skipped. A failed manual test is an Acceptance Blocker. If the draft was an exemption (`applicable=false`), reaffirm the reason in one line (`applicable=false` + `exemptionReaffirm`).
41
42
  - **Routing recommendation**: the next safe phase — one of `release-handoff`, `done`, `error-analysis`, `implementation-planning` — tied to the verdict and blocker list. `release-handoff` is allowed ONLY when the Verdict Token is `accepted`. `release-handoff` is additionally allowed ONLY when the verification scope (the `Verification scope:` line of the injected `VERIFICATION_TARGET` block, recorded as the report's `verificationScope` field) is `whole-task`; a `single-stage` accepted run routes to `release-handoff(stage-group)` (or `implementation` / `done`); plain `release-handoff` remains whole-task-only. Enforcement: `validators/validate-run.py` rejects a `single-stage` report whose routing cites plain `release-handoff`.
42
43
  - **Verified-row recording** (single-stage scope only): when the Verdict Token is `accepted`, the lead MUST run `okstra handoff record-verified --plan-run-root <plan-run-root> --stage <N> --report-path <final-report.md path> --data-json <final-report data.json path>` and quote the command + exit code in the report. The helper re-validates taskType/scope/verdict from data.json, so a non-accepted or whole-task report is rejected at the tool layer.
43
44
  - Clarification request policy (phase-specific addendum — shared policy is in `_common-contract.md`):