okstra 0.82.1 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.kr.md +7 -6
  2. package/README.md +6 -6
  3. package/docs/kr/architecture.md +8 -7
  4. package/docs/kr/cli.md +2 -2
  5. package/docs/kr/performance-improvement-plan-v2.md +14 -14
  6. package/docs/project-structure-overview.md +7 -8
  7. package/docs/superpowers/plans/2026-06-15-coding-preflight-pack-dispatch-path.md +504 -0
  8. package/docs/superpowers/plans/2026-06-15-internal-skill-migration-final-fixups.md +342 -0
  9. package/docs/superpowers/plans/2026-06-15-internal-skill-migration-fixups.md +258 -0
  10. package/docs/superpowers/plans/2026-06-15-internal-skill-migration-remaining-fixups.md +387 -0
  11. package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +749 -0
  12. package/docs/superpowers/plans/2026-06-15-worker-prompt-anchor-final-fixups.md +828 -0
  13. package/docs/superpowers/plans/2026-06-15-worker-prompt-header-error-contract.md +490 -0
  14. package/docs/task-process/README.md +1 -1
  15. package/docs/task-process/error-analysis.md +1 -1
  16. package/docs/task-process/final-verification.md +1 -1
  17. package/docs/task-process/implementation-planning.md +1 -1
  18. package/docs/task-process/implementation.md +1 -1
  19. package/docs/task-process/release-handoff.md +1 -1
  20. package/docs/task-process/requirements-discovery.md +2 -2
  21. package/package.json +1 -1
  22. package/runtime/BUILD.json +2 -2
  23. package/runtime/agents/TODO.md +2 -0
  24. package/runtime/agents/workers/claude-worker.md +8 -8
  25. package/runtime/agents/workers/codex-worker.md +8 -8
  26. package/runtime/agents/workers/gemini-worker.md +8 -8
  27. package/runtime/agents/workers/report-writer-worker.md +2 -2
  28. package/runtime/bin/lib/okstra/globals.sh +0 -1
  29. package/runtime/bin/okstra-wrapper-status.py +1 -1
  30. package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/python.md +2 -2
  31. package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/rust.md +1 -1
  32. package/runtime/{skills/okstra-coding-preflight/SKILL.md → prompts/coding-preflight/overview.md} +27 -38
  33. package/runtime/prompts/launch.template.md +5 -3
  34. package/runtime/{skills/okstra-context-loader/SKILL.md → prompts/lead/context-loader.md} +7 -14
  35. package/runtime/{skills/okstra-convergence/SKILL.md → prompts/lead/convergence.md} +12 -19
  36. package/runtime/{agents/SKILL.md → prompts/lead/okstra-lead-contract.md} +53 -59
  37. package/runtime/{skills/okstra-report-writer/SKILL.md → prompts/lead/report-writer.md} +12 -19
  38. package/runtime/{skills/okstra-team-contract/SKILL.md → prompts/lead/team-contract.md} +13 -19
  39. package/runtime/prompts/profiles/_coding-conventions-preflight.md +2 -2
  40. package/runtime/prompts/profiles/_common-contract.md +2 -2
  41. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  42. package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
  43. package/runtime/prompts/profiles/error-analysis.md +2 -2
  44. package/runtime/prompts/profiles/final-verification.md +1 -1
  45. package/runtime/prompts/profiles/implementation-planning.md +4 -4
  46. package/runtime/prompts/profiles/requirements-discovery.md +2 -2
  47. package/runtime/python/okstra_ctl/codex_dispatch.py +12 -61
  48. package/runtime/python/okstra_ctl/context_cost.py +14 -11
  49. package/runtime/python/okstra_ctl/dispatch_core.py +36 -13
  50. package/runtime/python/okstra_ctl/paths.py +27 -1
  51. package/runtime/python/okstra_ctl/render.py +62 -8
  52. package/runtime/python/okstra_ctl/run.py +5 -5
  53. package/runtime/python/okstra_ctl/worker_prompt_headers.py +126 -0
  54. package/runtime/python/okstra_token_usage/claude.py +1 -1
  55. package/runtime/python/okstra_token_usage/collect.py +1 -1
  56. package/runtime/templates/reports/task-brief.template.md +2 -2
  57. package/runtime/templates/worker-prompt-preamble.md +2 -2
  58. package/runtime/validators/lib/validate-assets.sh +12 -4
  59. package/runtime/validators/validate-run.py +3 -3
  60. package/runtime/validators/validate_session_conformance.py +11 -11
  61. package/src/install.mjs +95 -81
  62. package/src/skill-catalog.mjs +35 -0
  63. package/src/uninstall.mjs +5 -0
  64. /package/runtime/{skills/okstra-coding-preflight/architecture → prompts/coding-preflight/architectures}/hexagonal.md +0 -0
  65. /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/clean-code.md +0 -0
  66. /package/runtime/{skills/okstra-coding-preflight/languages/nodejs.md → prompts/coding-preflight/frameworks/node-server.md} +0 -0
  67. /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/java.md +0 -0
  68. /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/javascript-typescript.md +0 -0
  69. /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/kotlin.md +0 -0
  70. /package/runtime/{skills/okstra-coding-preflight → prompts/coding-preflight}/languages/sql.md +0 -0
@@ -0,0 +1,490 @@
1
+ # Worker Prompt Header Error Contract 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:** Make worker prompt header rendering failures surface through dispatcher-native `DispatchError` boundaries instead of leaking helper-local exception types or tracebacks.
6
+
7
+ **Architecture:** Keep `worker_prompt_headers.py` as the single renderer for the eight team-contract anchor headers, but give it a domain-specific exception type. Each dispatcher boundary (`codex_dispatch.py`, `dispatch_core.py`) converts that shared exception into its own public `DispatchError`, preserving existing CLI/error contracts. No graphify/plugin refresh work is included; the user explicitly excluded that external-plugin failure.
8
+
9
+ **Tech Stack:** Python 3.11+, `pytest`, existing Okstra dispatcher modules under `scripts/okstra_ctl/`, npm build/check wrappers.
10
+
11
+ ---
12
+
13
+ ## Current Evidence
14
+
15
+ - `scripts/okstra_ctl/worker_prompt_headers.py` renders the worker anchor headers in one shared helper.
16
+ - The helper currently raises raw `ValueError` from `_run_seq()` and `_require_string()` when required manifest fields are missing.
17
+ - `scripts/okstra_ctl/codex_dispatch.py` catches only its local `DispatchError` in `main()`, so a malformed manifest can bypass the clean CLI error path.
18
+ - `scripts/okstra_ctl/dispatch_core.py` already uses local `DispatchError` for malformed dispatch inputs, but `_materialize_prompt_if_missing()` delegates directly to `worker_prompt_headers()` without wrapping helper failures.
19
+ - The only remaining issue in scope is this exception-boundary gap. The graphify update failure is intentionally out of scope.
20
+
21
+ ## File Structure
22
+
23
+ - Modify: `scripts/okstra_ctl/worker_prompt_headers.py`
24
+ - Responsibility: render deterministic worker prompt anchor headers.
25
+ - Add: `WorkerPromptHeaderError` for malformed manifest/context input.
26
+ - Change: `_run_seq()` and `_require_string()` raise `WorkerPromptHeaderError`.
27
+
28
+ - Modify: `scripts/okstra_ctl/codex_dispatch.py`
29
+ - Responsibility: Codex CLI-wrapper dispatch boundary.
30
+ - Change: import `WorkerPromptHeaderError` and convert it to Codex-local `DispatchError` in `_base_prompt_headers()`.
31
+
32
+ - Modify: `scripts/okstra_ctl/dispatch_core.py`
33
+ - Responsibility: backend-neutral deterministic worker dispatch.
34
+ - Change: import `WorkerPromptHeaderError` and convert it to dispatch-core-local `DispatchError` in `_materialize_prompt_if_missing()`.
35
+
36
+ - Modify: `tests/test_worker_prompt_headers.py`
37
+ - Responsibility: shared header renderer regression tests.
38
+ - Add: helper raises `WorkerPromptHeaderError` for malformed fallback manifest input.
39
+
40
+ - Modify: `tests/test_codex_dispatch.py`
41
+ - Responsibility: Codex dispatcher regression tests.
42
+ - Add: `_base_prompt_headers()` wraps shared header errors as Codex `DispatchError`.
43
+
44
+ - Modify: `tests/test_dispatch_core.py`
45
+ - Responsibility: backend-neutral dispatcher regression tests.
46
+ - Add: `_materialize_prompt_if_missing()` wraps shared header errors as dispatch-core `DispatchError` and does not create a prompt file on failure.
47
+
48
+ - Modify: `CHANGES.md`
49
+ - Responsibility: user-visible behavior log.
50
+ - Add: clean dispatcher error behavior entry under `2026-06-15` after the current dispatch entry.
51
+
52
+ ---
53
+
54
+ ### Task 1: Add Shared Header Error Type
55
+
56
+ **Files:**
57
+ - Modify: `scripts/okstra_ctl/worker_prompt_headers.py`
58
+ - Test: `tests/test_worker_prompt_headers.py`
59
+
60
+ - [ ] **Step 1: Write the failing helper regression test**
61
+
62
+ Add `pytest` to the test imports and import the new exception type:
63
+
64
+ ```python
65
+ import pytest
66
+
67
+ from okstra_ctl.worker_prompt_headers import ( # noqa: E402
68
+ WorkerPromptHeaderError,
69
+ worker_prompt_headers,
70
+ )
71
+ ```
72
+
73
+ Append this test to `tests/test_worker_prompt_headers.py`:
74
+
75
+ ```python
76
+ def test_worker_prompt_headers_raise_contract_error_for_missing_manifest_fields(
77
+ tmp_path: Path,
78
+ ) -> None:
79
+ with pytest.raises(WorkerPromptHeaderError, match="teamStatePath"):
80
+ worker_prompt_headers(
81
+ project_root=tmp_path / "project",
82
+ prompt_rel=".okstra/run/prompts/codex.md",
83
+ result_rel=".okstra/run/worker-results/codex.md",
84
+ worker_id="codex",
85
+ manifest={},
86
+ active_context={},
87
+ )
88
+ ```
89
+
90
+ - [ ] **Step 2: Run the helper test and verify it fails for the intended reason**
91
+
92
+ Run:
93
+
94
+ ```bash
95
+ python3 -m pytest tests/test_worker_prompt_headers.py::test_worker_prompt_headers_raise_contract_error_for_missing_manifest_fields -q
96
+ ```
97
+
98
+ Expected: FAIL because `WorkerPromptHeaderError` is not importable yet, or because the helper still raises `ValueError`.
99
+
100
+ - [ ] **Step 3: Implement `WorkerPromptHeaderError` in the shared helper**
101
+
102
+ In `scripts/okstra_ctl/worker_prompt_headers.py`, add this class after the imports:
103
+
104
+ ```python
105
+ class WorkerPromptHeaderError(Exception):
106
+ """Raised when worker prompt anchor headers cannot be rendered."""
107
+ ```
108
+
109
+ Replace `_run_seq()` with:
110
+
111
+ ```python
112
+ def _run_seq(manifest: Mapping[str, Any]) -> str:
113
+ seqs = manifest.get("runSequencesByCategory")
114
+ if not isinstance(seqs, Mapping):
115
+ raise WorkerPromptHeaderError("run manifest has no runSequencesByCategory object")
116
+ return _require_string(seqs, "manifests")
117
+ ```
118
+
119
+ Replace `_require_string()` with:
120
+
121
+ ```python
122
+ def _require_string(payload: Mapping[str, Any], key: str) -> str:
123
+ value = payload.get(key)
124
+ if not isinstance(value, str) or not value.strip():
125
+ raise WorkerPromptHeaderError(f"missing required string field: {key}")
126
+ return value
127
+ ```
128
+
129
+ - [ ] **Step 4: Run the helper regression test and verify it passes**
130
+
131
+ Run:
132
+
133
+ ```bash
134
+ python3 -m pytest tests/test_worker_prompt_headers.py::test_worker_prompt_headers_raise_contract_error_for_missing_manifest_fields -q
135
+ ```
136
+
137
+ Expected: PASS.
138
+
139
+ - [ ] **Step 5: Run all shared helper tests**
140
+
141
+ Run:
142
+
143
+ ```bash
144
+ python3 -m pytest tests/test_worker_prompt_headers.py -q
145
+ ```
146
+
147
+ Expected: PASS for all tests in `tests/test_worker_prompt_headers.py`.
148
+
149
+ ---
150
+
151
+ ### Task 2: Wrap Codex Dispatcher Header Errors
152
+
153
+ **Files:**
154
+ - Modify: `scripts/okstra_ctl/codex_dispatch.py`
155
+ - Test: `tests/test_codex_dispatch.py`
156
+
157
+ - [ ] **Step 1: Write the failing Codex boundary regression test**
158
+
159
+ Extend the `tests/test_codex_dispatch.py` import from `okstra_ctl.codex_dispatch` to include `_base_prompt_headers`:
160
+
161
+ ```python
162
+ from okstra_ctl.codex_dispatch import ( # noqa: E402
163
+ DispatchError,
164
+ _base_prompt_headers,
165
+ build_dispatch_plan,
166
+ dispatch_plan,
167
+ )
168
+ ```
169
+
170
+ Append this test near the other prompt-generation tests:
171
+
172
+ ```python
173
+ def test_base_prompt_headers_wraps_worker_header_errors() -> None:
174
+ with pytest.raises(DispatchError, match="teamStatePath"):
175
+ _base_prompt_headers(
176
+ project_root=Path("/project"),
177
+ manifest={},
178
+ active_context={},
179
+ worker_id="codex",
180
+ prompt_rel=".okstra/run/prompts/codex.md",
181
+ result_rel=".okstra/run/worker-results/codex.md",
182
+ )
183
+ ```
184
+
185
+ - [ ] **Step 2: Run the Codex regression test and verify it fails for the intended reason**
186
+
187
+ Run:
188
+
189
+ ```bash
190
+ python3 -m pytest tests/test_codex_dispatch.py::test_base_prompt_headers_wraps_worker_header_errors -q
191
+ ```
192
+
193
+ Expected: FAIL because `_base_prompt_headers()` raises `WorkerPromptHeaderError` instead of Codex `DispatchError`.
194
+
195
+ - [ ] **Step 3: Convert shared header errors to Codex `DispatchError`**
196
+
197
+ In `scripts/okstra_ctl/codex_dispatch.py`, replace the worker header import with:
198
+
199
+ ```python
200
+ from .worker_prompt_headers import WorkerPromptHeaderError, worker_prompt_headers
201
+ ```
202
+
203
+ Replace `_base_prompt_headers()` with:
204
+
205
+ ```python
206
+ def _base_prompt_headers(
207
+ *,
208
+ project_root: Path,
209
+ manifest: Mapping[str, Any],
210
+ active_context: Mapping[str, Any],
211
+ worker_id: str,
212
+ prompt_rel: str,
213
+ result_rel: str,
214
+ ) -> list[str]:
215
+ try:
216
+ return worker_prompt_headers(
217
+ project_root=project_root,
218
+ prompt_rel=prompt_rel,
219
+ result_rel=result_rel,
220
+ worker_id=worker_id,
221
+ manifest=manifest,
222
+ active_context=active_context,
223
+ )
224
+ except WorkerPromptHeaderError as exc:
225
+ raise DispatchError(str(exc)) from exc
226
+ ```
227
+
228
+ - [ ] **Step 4: Run the Codex regression test and verify it passes**
229
+
230
+ Run:
231
+
232
+ ```bash
233
+ python3 -m pytest tests/test_codex_dispatch.py::test_base_prompt_headers_wraps_worker_header_errors -q
234
+ ```
235
+
236
+ Expected: PASS.
237
+
238
+ - [ ] **Step 5: Smoke-test the Codex boundary exception type**
239
+
240
+ Run:
241
+
242
+ ```bash
243
+ python3 - <<'PY'
244
+ from pathlib import Path
245
+ import sys
246
+
247
+ sys.path.insert(0, "scripts")
248
+
249
+ from okstra_ctl.codex_dispatch import DispatchError, _base_prompt_headers
250
+
251
+ try:
252
+ _base_prompt_headers(
253
+ project_root=Path("/project"),
254
+ manifest={},
255
+ active_context={},
256
+ worker_id="codex",
257
+ prompt_rel=".okstra/run/prompts/codex.md",
258
+ result_rel=".okstra/run/worker-results/codex.md",
259
+ )
260
+ except DispatchError as exc:
261
+ print(type(exc).__name__, str(exc))
262
+ else:
263
+ raise SystemExit("expected DispatchError")
264
+ PY
265
+ ```
266
+
267
+ Expected output:
268
+
269
+ ```text
270
+ DispatchError missing required string field: teamStatePath
271
+ ```
272
+
273
+ ---
274
+
275
+ ### Task 3: Wrap Backend-Neutral Dispatcher Header Errors
276
+
277
+ **Files:**
278
+ - Modify: `scripts/okstra_ctl/dispatch_core.py`
279
+ - Test: `tests/test_dispatch_core.py`
280
+
281
+ - [ ] **Step 1: Write the failing dispatch-core regression test**
282
+
283
+ Add `pytest` to `tests/test_dispatch_core.py` imports:
284
+
285
+ ```python
286
+ import pytest
287
+ ```
288
+
289
+ Append this test to `tests/test_dispatch_core.py`:
290
+
291
+ ```python
292
+ def test_materialize_prompt_wraps_worker_header_errors(tmp_path: Path) -> None:
293
+ prompt_path = tmp_path / "project" / "prompts" / "claude-worker-prompt.md"
294
+
295
+ with pytest.raises(dispatch_core.DispatchError, match="teamStatePath"):
296
+ dispatch_core._materialize_prompt_if_missing(
297
+ prompt_path=prompt_path,
298
+ prompt_rel="prompts/claude-worker-prompt.md",
299
+ worker_id="claude",
300
+ result_rel="worker-results/claude.md",
301
+ project_root=tmp_path / "project",
302
+ manifest={},
303
+ active_context={},
304
+ )
305
+
306
+ assert not prompt_path.exists()
307
+ ```
308
+
309
+ - [ ] **Step 2: Run the dispatch-core regression test and verify it fails for the intended reason**
310
+
311
+ Run:
312
+
313
+ ```bash
314
+ python3 -m pytest tests/test_dispatch_core.py::test_materialize_prompt_wraps_worker_header_errors -q
315
+ ```
316
+
317
+ Expected: FAIL because `_materialize_prompt_if_missing()` raises `WorkerPromptHeaderError` instead of dispatch-core `DispatchError`.
318
+
319
+ - [ ] **Step 3: Convert shared header errors to dispatch-core `DispatchError`**
320
+
321
+ In `scripts/okstra_ctl/dispatch_core.py`, replace the worker header import with:
322
+
323
+ ```python
324
+ from .worker_prompt_headers import WorkerPromptHeaderError, worker_prompt_headers
325
+ ```
326
+
327
+ Replace `_materialize_prompt_if_missing()` with:
328
+
329
+ ```python
330
+ def _materialize_prompt_if_missing(
331
+ *,
332
+ prompt_path: Path,
333
+ prompt_rel: str,
334
+ worker_id: str,
335
+ result_rel: str,
336
+ project_root: Path,
337
+ manifest: Mapping[str, Any],
338
+ active_context: Mapping[str, Any],
339
+ ) -> None:
340
+ if prompt_path.is_file():
341
+ return
342
+ prompt_path.parent.mkdir(parents=True, exist_ok=True)
343
+ try:
344
+ headers = worker_prompt_headers(
345
+ project_root=project_root,
346
+ prompt_rel=prompt_rel,
347
+ result_rel=result_rel,
348
+ worker_id=worker_id,
349
+ manifest=manifest,
350
+ active_context=active_context,
351
+ )
352
+ except WorkerPromptHeaderError as exc:
353
+ raise DispatchError(str(exc)) from exc
354
+ prompt_path.write_text("\n".join([*headers, ""]), encoding="utf-8")
355
+ ```
356
+
357
+ - [ ] **Step 4: Run the dispatch-core regression test and verify it passes**
358
+
359
+ Run:
360
+
361
+ ```bash
362
+ python3 -m pytest tests/test_dispatch_core.py::test_materialize_prompt_wraps_worker_header_errors -q
363
+ ```
364
+
365
+ Expected: PASS.
366
+
367
+ - [ ] **Step 5: Run all backend-neutral dispatch prompt tests**
368
+
369
+ Run:
370
+
371
+ ```bash
372
+ python3 -m pytest tests/test_dispatch_core.py -q
373
+ ```
374
+
375
+ Expected: PASS for all tests in `tests/test_dispatch_core.py`.
376
+
377
+ ---
378
+
379
+ ### Task 4: Verify Integration and Update User-Facing Log
380
+
381
+ **Files:**
382
+ - Modify: `CHANGES.md`
383
+ - Verify: `tests/test_worker_prompt_headers.py`, `tests/test_codex_dispatch.py`, `tests/test_dispatch_core.py`, `tests/test_docs_runtime_contract.py`, generated `runtime/`
384
+
385
+ - [ ] **Step 1: Run the focused regression suite**
386
+
387
+ Run:
388
+
389
+ ```bash
390
+ python3 -m pytest tests/test_worker_prompt_headers.py tests/test_codex_dispatch.py::test_base_prompt_headers_wraps_worker_header_errors tests/test_dispatch_core.py::test_materialize_prompt_wraps_worker_header_errors -q
391
+ ```
392
+
393
+ Expected: PASS for the shared helper test file and both new dispatcher boundary tests.
394
+
395
+ - [ ] **Step 2: Run the prompt/dispatch contract suite**
396
+
397
+ Run:
398
+
399
+ ```bash
400
+ python3 -m pytest tests/test_worker_prompt_headers.py tests/test_codex_dispatch.py tests/test_dispatch_core.py tests/test_docs_runtime_contract.py -q
401
+ ```
402
+
403
+ Expected: PASS for all prompt, dispatch, and runtime-contract tests.
404
+
405
+ - [ ] **Step 3: Update `CHANGES.md` under `## 2026-06-15`**
406
+
407
+ Insert this entry after the current `fix(dispatch): worker 프롬프트가 coding-preflight 팩 경로를 앵커 헤더로 직접 수신` section:
408
+
409
+ ```markdown
410
+ ### fix(dispatch): worker prompt header 오류를 dispatcher 경계에서 정규화
411
+
412
+ - **배경**: worker prompt anchor header 렌더러를 공유 모듈로 추출한 뒤 malformed fallback manifest 가 renderer 내부 예외를 그대로 노출할 수 있었다. Codex CLI dispatcher 는 `DispatchError` 만 clean error 로 처리하므로 helper 예외가 그대로 올라오면 traceback 형태로 보일 위험이 있었다.
413
+ - **해결**: shared renderer 는 `WorkerPromptHeaderError` 를 던지고, Codex dispatcher 와 backend-neutral dispatch core 는 이를 각자의 `DispatchError` 로 변환한다. 정상 header 렌더링 경로와 기존 deterministic prompt materialization 은 그대로 유지한다.
414
+ - 사용자 영향: 잘못된 run manifest/context 로 fallback worker prompt 를 만들 수 없는 경우에도 내부 helper 예외나 traceback 대신 dispatcher 의 일관된 오류 메시지로 실패한다.
415
+ ```
416
+
417
+ - [ ] **Step 4: Refresh runtime build output**
418
+
419
+ Run:
420
+
421
+ ```bash
422
+ npm run build
423
+ ```
424
+
425
+ Expected: exit code 0 and build sync completion, including `scripts/`, `prompts/`, `skills/`, `agents/`, `templates/`, and `validators/` sources.
426
+
427
+ - [ ] **Step 5: Run the full project check**
428
+
429
+ Run:
430
+
431
+ ```bash
432
+ npm run check
433
+ ```
434
+
435
+ Expected: exit code 0. JavaScript tests, Python tests, and `validators/validate-workflow.sh` all pass.
436
+
437
+ - [ ] **Step 6: Smoke-test runtime import after build**
438
+
439
+ Run:
440
+
441
+ ```bash
442
+ python3 - <<'PY'
443
+ from pathlib import Path
444
+ import sys
445
+
446
+ sys.path.insert(0, "runtime/scripts")
447
+
448
+ from okstra_ctl.codex_dispatch import DispatchError, _base_prompt_headers
449
+
450
+ try:
451
+ _base_prompt_headers(
452
+ project_root=Path("/project"),
453
+ manifest={},
454
+ active_context={},
455
+ worker_id="codex",
456
+ prompt_rel=".okstra/run/prompts/codex.md",
457
+ result_rel=".okstra/run/worker-results/codex.md",
458
+ )
459
+ except DispatchError as exc:
460
+ print(type(exc).__name__, str(exc))
461
+ else:
462
+ raise SystemExit("expected DispatchError")
463
+ PY
464
+ ```
465
+
466
+ Expected output:
467
+
468
+ ```text
469
+ DispatchError missing required string field: teamStatePath
470
+ ```
471
+
472
+ - [ ] **Step 7: Commit the finished fix**
473
+
474
+ Run:
475
+
476
+ ```bash
477
+ git add scripts/okstra_ctl/worker_prompt_headers.py scripts/okstra_ctl/codex_dispatch.py scripts/okstra_ctl/dispatch_core.py tests/test_worker_prompt_headers.py tests/test_codex_dispatch.py tests/test_dispatch_core.py CHANGES.md
478
+ git commit -m "fix(dispatch): normalize worker prompt header errors"
479
+ ```
480
+
481
+ Expected: one conventional commit containing source changes, regression tests, and the user-facing behavior log. `runtime/` stays generated and is not hand-edited.
482
+ ---
483
+
484
+ ## Self-Review Checklist
485
+
486
+ - [ ] The plan covers the only remaining in-scope issue: shared header renderer exceptions leaking past dispatcher error boundaries.
487
+ - [ ] The graphify update failure is excluded because the user classified it as an external plugin issue.
488
+ - [ ] No compatibility shim or duplicate renderer is introduced; `worker_prompt_headers.py` remains the single header-rendering implementation.
489
+ - [ ] Tests assert observable behavior: public exception type at the dispatcher boundary and absence of a partial prompt file on dispatch-core failure.
490
+ - [ ] `CHANGES.md` states the user impact and the mechanism that enforces the cleaner failure mode.
@@ -57,7 +57,7 @@ flowchart TD
57
57
  | phase boundary | [`scripts/okstra_ctl/workflow.py`](../../scripts/okstra_ctl/workflow.py) |
58
58
  | task worktree | [`scripts/okstra_ctl/worktree.py`](../../scripts/okstra_ctl/worktree.py) |
59
59
  | worker roster parser | [`scripts/okstra_ctl/workers.py`](../../scripts/okstra_ctl/workers.py) |
60
- | lead operating contract | [`agents/SKILL.md`](../../agents/SKILL.md) |
60
+ | lead operating contract | [`prompts/lead/okstra-lead-contract.md`](../../prompts/lead/okstra-lead-contract.md) |
61
61
  | phase profiles | [`prompts/profiles/`](../../prompts/profiles/) |
62
62
  | final report shape | [`templates/reports/final-report.template.md`](../../templates/reports/final-report.template.md) |
63
63
 
@@ -97,5 +97,5 @@ final report는 다음을 담아야 한다.
97
97
  - [`templates/reports/error-analysis-input.template.md`](../../templates/reports/error-analysis-input.template.md)
98
98
  - [`scripts/okstra_ctl/workflow.py`](../../scripts/okstra_ctl/workflow.py)
99
99
  - [`scripts/okstra_ctl/wizard.py`](../../scripts/okstra_ctl/wizard.py)
100
- - [`agents/SKILL.md`](../../agents/SKILL.md)
100
+ - [`prompts/lead/okstra-lead-contract.md`](../../prompts/lead/okstra-lead-contract.md)
101
101
 
@@ -164,4 +164,4 @@ source edit, follow-up fix, scope expansion은 전부 금지다. 결함을 발
164
164
  - [`scripts/okstra_ctl/run.py`](../../scripts/okstra_ctl/run.py)
165
165
  - [`scripts/okstra_ctl/workflow.py`](../../scripts/okstra_ctl/workflow.py)
166
166
  - [`scripts/okstra_ctl/render.py`](../../scripts/okstra_ctl/render.py)
167
- - [`agents/SKILL.md`](../../agents/SKILL.md)
167
+ - [`prompts/lead/okstra-lead-contract.md`](../../prompts/lead/okstra-lead-contract.md)
@@ -124,5 +124,5 @@ flowchart TD
124
124
  - [`templates/reports/final-report.template.md`](../../templates/reports/final-report.template.md)
125
125
  - [`scripts/okstra_ctl/render.py`](../../scripts/okstra_ctl/render.py)
126
126
  - [`validators/validate-run.py`](../../validators/validate-run.py)
127
- - [`agents/SKILL.md`](../../agents/SKILL.md)
127
+ - [`prompts/lead/okstra-lead-contract.md`](../../prompts/lead/okstra-lead-contract.md)
128
128
 
@@ -145,5 +145,5 @@ flowchart TD
145
145
  - [`scripts/okstra_ctl/wizard.py`](../../scripts/okstra_ctl/wizard.py)
146
146
  - [`validators/validate-implementation-plan-stages.py`](../../validators/validate-implementation-plan-stages.py)
147
147
  - [`scripts/okstra_ctl/qa_commands.py`](../../scripts/okstra_ctl/qa_commands.py)
148
- - [`agents/SKILL.md`](../../agents/SKILL.md)
148
+ - [`prompts/lead/okstra-lead-contract.md`](../../prompts/lead/okstra-lead-contract.md)
149
149
 
@@ -62,7 +62,7 @@ sequenceDiagram
62
62
  P-->>W: lead prompt for current session
63
63
  ```
64
64
 
65
- profile에 `Required workers:` block이 없고 `run.py`도 `release-handoff`에서는 worker override를 비운다. 따라서 `agents/SKILL.md`의 일반 TeamCreate / convergence / report-writer 흐름을 타지 않는 것이 의도된 동작이다.
65
+ profile에 `Required workers:` block이 없고 `run.py`도 `release-handoff`에서는 worker override를 비운다. 따라서 `prompts/lead/okstra-lead-contract.md`의 일반 TeamCreate / convergence / report-writer 흐름을 타지 않는 것이 의도된 동작이다.
66
66
 
67
67
  ## 4. entry gate
68
68
 
@@ -79,7 +79,7 @@ flowchart TD
79
79
  R --> P7[Phase 7 token usage, validate, persist]
80
80
  ```
81
81
 
82
- `requirements-discovery`는 convergence default가 1 round다. 이 예외는 `render._build_convergence_block()`와 `agents/SKILL.md` 모두에서 명시된다.
82
+ `requirements-discovery`는 convergence default가 1 round다. 이 예외는 `render._build_convergence_block()`와 `prompts/lead/okstra-lead-contract.md` 모두에서 명시된다.
83
83
 
84
84
  ## 5. 산출물과 routing
85
85
 
@@ -111,5 +111,5 @@ Non-goal은 source edit, plan authoring, build, deployment다.
111
111
  - [`scripts/okstra_ctl/run.py`](../../scripts/okstra_ctl/run.py)
112
112
  - [`scripts/okstra_ctl/workflow.py`](../../scripts/okstra_ctl/workflow.py)
113
113
  - [`prompts/profiles/requirements-discovery.md`](../../prompts/profiles/requirements-discovery.md)
114
- - [`agents/SKILL.md`](../../agents/SKILL.md)
114
+ - [`prompts/lead/okstra-lead-contract.md`](../../prompts/lead/okstra-lead-contract.md)
115
115
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.82.1",
3
+ "version": "0.83.0",
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.82.1",
3
- "builtAt": "2026-06-15T04:53:43.767Z",
2
+ "package": "0.83.0",
3
+ "builtAt": "2026-06-15T16:01:38.767Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -2,6 +2,8 @@
2
2
 
3
3
  이 파일은 okstra 워크플로우의 워커 종료 / Phase 4 대기 시간 개선 작업, 그리고 워커 설정 일관성 감사(2026-05-03)에서 도출됐지만 단일 라운드에서 안전하게 처리할 수 없어 별도 라운드로 미룬 항목을 기록한다.
4
4
 
5
+ > Historical note: entries before 2026-06-15 may reference the former internal-skill layout; current lead contracts live under `prompts/lead/` and current coding-preflight resources live under `prompts/coding-preflight/`.
6
+
5
7
  ---
6
8
 
7
9
  ## 항목 C2 — Worker-results 경로 표준 결정 [해결됨, 2026-05-03]
@@ -45,7 +45,7 @@ Unlike the Codex / Gemini workers, you are an in-process Claude subagent — you
45
45
 
46
46
  4. Anchor all file operations to the absolute `Project Root` from the lead prompt. Use absolute paths — do NOT rely on inherited cwd. Never use `cd` to change directory.
47
47
  - **Executor exception (implementation phase only):** when this worker is dispatched as the `Executor` and the lead prompt provides an `EXECUTOR_WORKTREE_PATH` that differs from the session's inherited cwd, cwd-sensitive Bash commands (`cargo *`, `npm *`, `pnpm *`, `bun *`, `pytest`, `make *`, `go *`, language-toolchain test/build commands) MUST be prefixed with `cd <EXECUTOR_WORKTREE_PATH> && ` in the same Bash invocation — e.g. `cd /Users/.../worktrees/foo && cargo test -p bar`. Do NOT wrap the whole thing in `bash -lc "..."` or `bash -c "..."`; pass the chained command directly to the Bash tool so the leading `cd` token remains visible to the permission layer. The `cd` is scoped to the single Bash subshell and does not mutate the session's shell state, so this does not conflict with the "never use cd" rule above (which prevents the worker from drifting the session cwd across calls).
48
- - **Executor coding-conventions preflight (BLOCKING, before your first `Edit` / `Write`):** when dispatched as the `Executor`, you MUST run the coding-conventions preflight defined in the executor sidecar (`prompts/profiles/_implementation-executor.md` → "Pre-implementation context exploration") before writing any code detect each touched file's language and read okstra's installed coding-conventions files directly — `~/.claude/skills/okstra-coding-preflight/languages/<lang>.md` + `clean-code.md` + any `architecture/hexagonal.md` overlay (placed by `okstra install`), then state in one line which conventions apply. The skill is `user-invocable: false` and subagents do NOT auto-trigger skills, so read the files via the Read tool by absolute path rather than relying on Skill invocation; if those files are not readable in your runtime, degrade per that sidecar section (agnostic principles + project lint/convention files) — never skip the gate.
48
+ - **Executor coding-conventions preflight (BLOCKING, before your first `Edit` / `Write`):** when dispatched as the `Executor`, you MUST run the coding-conventions preflight defined in the executor sidecar (`prompts/profiles/_implementation-executor.md` → "Pre-implementation context exploration") before writing any code. Use this worker prompt's `**Coding preflight pack:**` anchor header; read that pack's `overview.md` and `clean-code.md`, then follow the routed pack's language framework → architecture stages, iterating every rule and loading every matching resource (for example `frameworks/node-server.md` and `architectures/hexagonal.md` when their conditions match). The preflight pack is a runtime resource, not an auto-invoked skill; read the files via the Read tool by absolute path.
49
49
  - **Verifier QA-gate exception:** verifier roles MAY use the same `cd <WORKTREE> && <cmd>` shape when executing project-declared `qaCommands` (lint / format / typecheck / test) from `project.json`, since those commands are cwd-sensitive by nature. Outside the QA gate, verifiers still read with absolute paths only — do NOT use `cd` for file inspection.
50
50
  - **No extra chaining beyond `cd && cmd`:** the permission matcher only allows the exact two-segment shape `cd <PATH> && <single-command>`. Do NOT append additional pipes, semicolons, redirects, or `&&` chains — e.g. `cd ... && cargo test ... 2>&1 | tail -20; echo "exit:$?"` will trigger a permission prompt every dispatch because the trailing `| tail`, `; echo`, and `2>&1` tokens disqualify the prefix match against `Bash(cargo:*)`. Let Claude Code capture the full stdout/stderr and exit code natively — do not post-process with `tail`, `head`, or `echo "exit:$?"`. If output truncation is genuinely needed, run the command first and read the result in a separate tool call.
51
51
 
@@ -68,7 +68,7 @@ Before producing any output, you MUST:
68
68
 
69
69
  When returning results, start the file with a YAML frontmatter block, then organize the body into the following sections in this exact order.
70
70
 
71
- **Frontmatter (mandatory)** — set `workerId: "claude"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the primary input (`analysis-packet.md`; fall back to `analysis-material.md` only if the packet is missing a field). Full schema and a concrete example live in the `okstra-team-contract` skill's "Result Frontmatter" subsection.
71
+ **Frontmatter (mandatory)** — set `workerId: "claude"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the primary input (`analysis-packet.md`; fall back to `analysis-material.md` only if the packet is missing a field). Full schema and a concrete example live in the team-contract resource (`prompts/lead/team-contract.md`) "Result Frontmatter" subsection.
72
72
 
73
73
  1. **Findings** - what you identified
74
74
  2. **Missing Information or Assumptions** - gaps in the analysis
@@ -80,15 +80,15 @@ Include file paths and line numbers when discussing code evidence.
80
80
 
81
81
  **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 §6.1 / §6.2 / §2.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.
82
82
 
83
- **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.
83
+ **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 team-contract resource (`prompts/lead/team-contract.md`) Ticket Tagging section.
84
84
 
85
- This contract mirrors the `okstra-team-contract` skill's Worker Output Contract — that skill is the authoritative source if the two ever diverge.
85
+ This contract mirrors the team-contract resource (`prompts/lead/team-contract.md`) Worker Output Contract — that resource is the authoritative source if the two ever diverge.
86
86
 
87
87
  ## Stop Condition (BLOCKING)
88
88
 
89
- When Lead dispatches you with `team_name` (Teams mode), its `Agent()` call returns `Spawned successfully` **immediately** and does NOT block on your completion — Lead detects your completion by self-scheduled polling of your worker-results file (see `okstra-team-contract` "Worker-completion detection (self-scheduled polling)"). Therefore you MUST write your worker-results file at the canonical Result Path before returning: that file's appearance is the ONLY completion signal Lead uses. Lingering after your worker-results file is on disk extends Phase 4 wall-clock time for the entire run and delays convergence. Be deliberate about stopping.
89
+ When Lead dispatches you with `team_name` (Teams mode), its `Agent()` call returns `Spawned successfully` **immediately** and does NOT block on your completion — Lead detects your completion by self-scheduled polling of your worker-results file (see `team-contract` "Worker-completion detection (self-scheduled polling)"). Therefore you MUST write your worker-results file at the canonical Result Path before returning: that file's appearance is the ONLY completion signal Lead uses. Lingering after your worker-results file is on disk extends Phase 4 wall-clock time for the entire run and delays convergence. Be deliberate about stopping.
90
90
 
91
- After your `Write` to the assigned worker-results file (path provided by Lead as `**Result Path:**` — the canonical anchor header defined in `okstra-team-contract` "Worker Prompt Composition" — or derived under `runs/<task-type>/worker-results/claude-worker-<task-type>-<seq>.md`) succeeds:
91
+ After your `Write` to the assigned worker-results file (path provided by Lead as `**Result Path:**` — the canonical anchor header defined in `team-contract` "Worker Prompt Composition" — or derived under `runs/<task-type>/worker-results/claude-worker-<task-type>-<seq>.md`) succeeds:
92
92
 
93
93
  1. Return your final assistant message **immediately**. Begin every return with your model identity, copied verbatim from the `**Model:** Claude worker, <modelExecutionValue>` line in your dispatch prompt (per Worker Preamble → "Return message to the lead"), then the status line — for an analysis dispatch:
94
94
  ```
@@ -114,7 +114,7 @@ If the header line is absent from the dispatch prompt, return `CLAUDE_WORKER_ERR
114
114
 
115
115
  **Tool failure (worker-reported)** — if `Write` of the prompt history file, `mkdir`, any MCP call, or any other pre-/in-analysis tool call fails (non-zero exit, exception, or empty result where data was required), append a `tool-failure` entry to the worker errors sidecar at the absolute path extracted from the `**Errors sidecar path:**` header. If the file does not exist, create it with `{"schemaVersion": 1, "errors": []}` then append.
116
116
 
117
- The sidecar follows the schema in `skills/okstra-team-contract/SKILL.md` (Optional errors sidecar). Lead will dump it to the run error log after this subagent terminates.
117
+ The sidecar follows the schema in `prompts/lead/team-contract.md` (Optional errors sidecar). Lead will dump it to the run error log after this subagent terminates.
118
118
 
119
119
  There is NO `cli-failure` category for this worker — Claude worker has no external CLI to invoke. Treat MCP errors and Bash errors uniformly as `tool-failure`.
120
120
 
@@ -122,7 +122,7 @@ There is NO `cli-failure` category for this worker — Claude worker has no exte
122
122
 
123
123
  - Return error messages as-is on failure.
124
124
  - Do not summarize or modify your own analysis output beyond the structured sections above.
125
- - Sections 1–5 are the common core — same dimensions for every analysis worker. Your specialization (broad reasoning depth, hidden-assumption surfacing, execution-risk decomposition) only enters Section 6 if you have additive content beyond the core. See `skills/okstra-team-contract/SKILL.md` "Worker Output Contract" for the authoritative split.
125
+ - Sections 1–5 are the common core — same dimensions for every analysis worker. Your specialization (broad reasoning depth, hidden-assumption surfacing, execution-risk decomposition) only enters Section 6 if you have additive content beyond the core. See `prompts/lead/team-contract.md` "Worker Output Contract" for the authoritative split.
126
126
 
127
127
  ## Stage evidence emission (BLOCKING, implementation task only)
128
128