okstra 0.82.1 → 0.84.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 (77) 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/prompts/launch.template.md +5 -3
  31. package/runtime/{skills/okstra-context-loader/SKILL.md → prompts/lead/context-loader.md} +7 -14
  32. package/runtime/{skills/okstra-convergence/SKILL.md → prompts/lead/convergence.md} +12 -19
  33. package/runtime/{agents/SKILL.md → prompts/lead/okstra-lead-contract.md} +53 -59
  34. package/runtime/{skills/okstra-report-writer/SKILL.md → prompts/lead/report-writer.md} +12 -19
  35. package/runtime/{skills/okstra-team-contract/SKILL.md → prompts/lead/team-contract.md} +13 -19
  36. package/runtime/{skills → prompts}/okstra-coding-preflight/languages/python.md +2 -2
  37. package/runtime/{skills → prompts}/okstra-coding-preflight/languages/rust.md +1 -1
  38. package/runtime/{skills/okstra-coding-preflight/SKILL.md → prompts/okstra-coding-preflight/overview.md} +27 -38
  39. package/runtime/prompts/okstra-coding-preflight/scripts/preedit-check.sh +79 -0
  40. package/runtime/prompts/profiles/_coding-conventions-preflight.md +2 -2
  41. package/runtime/prompts/profiles/_common-contract.md +2 -2
  42. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  43. package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
  44. package/runtime/prompts/profiles/error-analysis.md +2 -2
  45. package/runtime/prompts/profiles/final-verification.md +1 -1
  46. package/runtime/prompts/profiles/implementation-planning.md +4 -4
  47. package/runtime/prompts/profiles/requirements-discovery.md +2 -2
  48. package/runtime/python/okstra_ctl/codex_dispatch.py +12 -61
  49. package/runtime/python/okstra_ctl/context_cost.py +14 -11
  50. package/runtime/python/okstra_ctl/dispatch_core.py +36 -13
  51. package/runtime/python/okstra_ctl/paths.py +27 -1
  52. package/runtime/python/okstra_ctl/render.py +62 -8
  53. package/runtime/python/okstra_ctl/run.py +8 -6
  54. package/runtime/python/okstra_ctl/worker_prompt_headers.py +126 -0
  55. package/runtime/python/okstra_token_usage/claude.py +1 -1
  56. package/runtime/python/okstra_token_usage/collect.py +1 -1
  57. package/runtime/skills/okstra-brief/SKILL.md +65 -11
  58. package/runtime/skills/okstra-inspect/SKILL.md +1 -1
  59. package/runtime/skills/okstra-schedule/SKILL.md +4 -4
  60. package/runtime/skills/okstra-setup/SKILL.md +2 -2
  61. package/runtime/templates/reports/settings.template.json +14 -123
  62. package/runtime/templates/reports/task-brief.template.md +2 -2
  63. package/runtime/templates/worker-prompt-preamble.md +2 -2
  64. package/runtime/validators/lib/validate-assets.sh +12 -4
  65. package/runtime/validators/validate-brief.py +99 -16
  66. package/runtime/validators/validate-run.py +3 -3
  67. package/runtime/validators/validate_session_conformance.py +11 -11
  68. package/src/install.mjs +95 -81
  69. package/src/skill-catalog.mjs +35 -0
  70. package/src/uninstall.mjs +5 -0
  71. /package/runtime/{skills/okstra-coding-preflight/architecture → prompts/okstra-coding-preflight/architectures}/hexagonal.md +0 -0
  72. /package/runtime/{skills → prompts}/okstra-coding-preflight/clean-code.md +0 -0
  73. /package/runtime/{skills/okstra-coding-preflight/languages/nodejs.md → prompts/okstra-coding-preflight/frameworks/node-server.md} +0 -0
  74. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/java.md +0 -0
  75. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/javascript-typescript.md +0 -0
  76. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/kotlin.md +0 -0
  77. /package/runtime/{skills → prompts}/okstra-coding-preflight/languages/sql.md +0 -0
@@ -14,140 +14,31 @@
14
14
  "WebFetch",
15
15
  "WebSearch",
16
16
  "TodoWrite",
17
-
18
- "Bash(pwd)",
19
- "Bash(cd)",
20
- "Bash(cd:*)",
21
-
22
- "Bash(ls)",
23
- "Bash(ls:*)",
24
- "Bash(cat:*)",
25
- "Bash(head:*)",
26
- "Bash(tail:*)",
27
- "Bash(wc:*)",
28
-
29
- "Bash(git)",
30
- "Bash(git:*)",
31
- "Bash(gh)",
32
- "Bash(gh:*)",
33
-
34
- "Bash(rg:*)",
35
- "Bash(grep:*)",
36
- "Bash(find:*)",
37
- "Bash(diff:*)",
38
-
39
- "Bash(jq:*)",
40
- "Bash(yq:*)",
41
- "Bash(awk:*)",
42
- "Bash(sed:*)",
43
- "Bash(tr:*)",
44
- "Bash(sort:*)",
45
- "Bash(uniq:*)",
46
- "Bash(xargs:*)",
47
-
48
- "Bash(mkdir:*)",
49
- "Bash(cp:*)",
50
- "Bash(mv:*)",
51
- "Bash(rm:*)",
52
- "Bash(chmod:*)",
53
-
54
- "Bash(printf:*)",
55
- "Bash(echo)",
56
- "Bash(echo:*)",
57
- "Bash(export:*)",
58
- "Bash(env)",
59
- "Bash(env:*)",
60
- "Bash(test:*)",
61
- "Bash(true)",
62
- "Bash(true:*)",
63
- "Bash(false)",
64
- "Bash(false:*)",
65
-
66
- "Bash(bash:*)",
67
- "Bash(sh:*)",
68
- "Bash(eval:*)",
69
-
70
- "Bash(python)",
71
- "Bash(python:*)",
72
- "Bash(python3)",
73
- "Bash(python3:*)",
74
- "Bash(pip)",
75
- "Bash(pip:*)",
76
- "Bash(pip3)",
77
- "Bash(pip3:*)",
78
- "Bash(uv:*)",
79
- "Bash(poetry:*)",
80
- "Bash(pytest:*)",
81
-
82
- "Bash(node)",
83
- "Bash(node:*)",
84
- "Bash(npm)",
85
- "Bash(npm:*)",
86
- "Bash(npx)",
87
- "Bash(npx:*)",
88
- "Bash(pnpm)",
89
- "Bash(pnpm:*)",
90
- "Bash(yarn)",
91
- "Bash(yarn:*)",
92
- "Bash(tsc)",
93
- "Bash(tsc:*)",
94
-
95
- "Bash(cargo)",
96
- "Bash(cargo:*)",
97
- "Bash(rustc)",
98
- "Bash(rustc:*)",
99
-
100
- "Bash(go)",
101
- "Bash(go:*)",
102
- "Bash(java)",
103
- "Bash(java:*)",
104
- "Bash(javac)",
105
- "Bash(javac:*)",
106
- "Bash(mvn)",
107
- "Bash(mvn:*)",
108
- "Bash(gradle)",
109
- "Bash(gradle:*)",
110
- "Bash(./gradlew)",
111
- "Bash(./gradlew:*)",
112
- "Bash(make)",
113
- "Bash(make:*)",
114
-
115
- "Bash(docker)",
116
- "Bash(docker:*)",
117
- "Bash(docker compose)",
118
- "Bash(docker compose:*)",
119
- "Bash(docker-compose)",
120
- "Bash(docker-compose:*)",
121
- "Bash(kubectl)",
122
- "Bash(kubectl:*)",
123
- "Bash(helm)",
124
- "Bash(helm:*)",
125
-
126
- "Bash(curl:*)",
127
- "Bash(wget:*)",
128
-
129
- "Bash(codex)",
130
- "Bash(codex:*)",
131
- "Bash(codex exec:*)",
132
17
  "Bash(okstra)",
133
18
  "Bash(okstra:*)",
134
19
  "Bash(npx okstra@latest:*)",
135
20
  "Bash(npx -y okstra@latest:*)",
136
21
  "Bash($HOME/.okstra/bin/:*)",
137
- "Bash(STATE_FILE=:*)",
138
- "Bash(ROOT=:*)",
139
-
22
+ "Bash(codex)",
23
+ "Bash(codex:*)",
140
24
  "Bash(gemini)",
141
25
  "Bash(gemini:*)",
142
-
143
26
  "Bash(claude)",
144
- "Bash(claude:*)",
145
-
146
- "mcp__test-context7__resolve-library-id",
147
- "mcp__test-context7__query-docs"
27
+ "Bash(claude:*)"
148
28
  ]
149
29
  },
150
30
  "hooks": {
31
+ "PreToolUse": [
32
+ {
33
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit",
34
+ "hooks": [
35
+ {
36
+ "type": "command",
37
+ "command": "bash \"$HOME/.okstra/prompts/okstra-coding-preflight/scripts/preedit-check.sh\""
38
+ }
39
+ ]
40
+ }
41
+ ],
151
42
  "SessionEnd": [
152
43
  {
153
44
  "hooks": [
@@ -112,7 +112,7 @@ taskType: "{{FM_TASK_TYPE}}"
112
112
 
113
113
  ### Standing Ticket-Tagging Rule (always applied by every worker)
114
114
 
115
- - 모든 항목 및 표 행에 ticket을 명시한다. 표 형식은 `Ticket ID` 컬럼, bullet/번호목록·섹션 헤더는 `[TICKETID: <id>]` 태그를 사용한다. 채움 우선순위·폴백·다중 ticket 규칙은 `okstra-team-contract` SKILL의 Ticket Tagging 절을 따른다.
115
+ - 모든 항목 및 표 행에 ticket을 명시한다. 표 형식은 `Ticket ID` 컬럼, bullet/번호목록·섹션 헤더는 `[TICKETID: <id>]` 태그를 사용한다. 채움 우선순위·폴백·다중 ticket 규칙은 `team-contract` SKILL의 Ticket Tagging 절을 따른다.
116
116
 
117
117
  ### Standing Scope-Discipline Questions (always answered by every worker)
118
118
 
@@ -138,7 +138,7 @@ taskType: "{{FM_TASK_TYPE}}"
138
138
  ## Phase Boundary
139
139
 
140
140
  - This okstra run executes the lifecycle phase named in `Task Type` and stops there. Outputs that belong to any other phase MUST NOT be produced inside this run.
141
- - Allowed and forbidden actions for each task type are listed in `Lifecycle Phase Boundaries` of the okstra skill (`agents/SKILL.md`). The lead and every worker stay inside that boundary.
141
+ - Allowed and forbidden actions for each task type are listed in `Lifecycle Phase Boundaries` of the okstra skill (`prompts/lead/okstra-lead-contract.md`). The lead and every worker stay inside that boundary.
142
142
  - "다음 단계 진행해" or any equivalent user phrase is interpreted as "complete the remaining outputs of the current phase," never as "start the next lifecycle phase." The next phase begins only via a fresh okstra invocation with the new `--task-type`.
143
143
  - For `implementation-planning` specifically: produce a plan document with the sections listed in `okstra-implementation-planning-input.template.md` `## Required Plan Deliverable`. Do not edit project source code, run builds/migrations/deployments, or write artifacts outside the run's own directories.
144
144
  - For `implementation` specifically: edits are bounded by the approved plan's file list (the `--approved-plan` reference). The run MUST refuse to start if the approved plan path is missing or its frontmatter `approved` field is not `true`. `git push`, publish, deploy, real migrations, and any third-party write API call remain forbidden; only local `git add`/`git commit` are allowed. Verifier roles stay read-only — they record fix recommendations rather than applying edits — and acceptance verdicts belong to `final-verification`, not this phase.
@@ -18,7 +18,7 @@ Different recipients need different files. Do NOT include `final-report-template
18
18
  |---|---|
19
19
  | Claude / Codex / Gemini analysis workers | analysis-packet.md as the primary compact input; task-brief, analysis-profile, analysis-material, reference-expectations, and clarification-response remain source/fallback paths, not automatic first-read files |
20
20
  | Report writer worker (Phase 6) | all of the above **plus** the instruction-set-local `final-report-template.md` (phase-stripped) and `final-report-schema.json` (per-task-type excerpt) — NOT the full `templates/reports/...` / `schemas/...` sources |
21
- | Reverify dispatches (Phase 5.5, lightweight mode) | **do NOT inject `[Required reading]` at all** — see [okstra-convergence](../skills/okstra-convergence/SKILL.md) "Reverify prompt: required-reading suppression". |
21
+ | Reverify dispatches (Phase 5.5, lightweight mode) | **do NOT inject `[Required reading]` at all** — see [convergence](../prompts/lead/convergence.md) "Reverify prompt: required-reading suppression". |
22
22
 
23
23
  ### Reading rules
24
24
 
@@ -112,7 +112,7 @@ Every item in sections 1–5 (and any section 6) MUST carry a worker-internal it
112
112
 
113
113
  For task types `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.
114
114
 
115
- See `skills/okstra-team-contract/SKILL.md` "Worker Output Contract" for the full frontmatter schema and section ordering rules. This preamble is consistent with that contract; the team-contract document is authoritative if the two ever diverge.
115
+ See `prompts/lead/team-contract.md` "Worker Output Contract" for the full frontmatter schema and section ordering rules. This preamble is consistent with that contract; the team-contract document is authoritative if the two ever diverge.
116
116
 
117
117
  ## Return message to the lead (all workers)
118
118
 
@@ -57,10 +57,18 @@ else:
57
57
  continue
58
58
  check(source_path, workers_target / source_path.name)
59
59
 
60
- # 2. Lead agent SKILL.md: agents/SKILL.md -> runtime/agents/SKILL.md
61
- lead_source = agents_source_root / "SKILL.md"
62
- if lead_source.is_file():
63
- check(lead_source, runtime_root / "agents" / "SKILL.md")
60
+ # 2. Lead contract + internal lead resources: prompts/lead/* + prompts/coding-preflight/*
61
+ # -> runtime/prompts/*. These ship as runtime resources (okstra install copies
62
+ # runtime/prompts -> ~/.okstra/prompts), no longer as agent skills.
63
+ prompts_source_root = agents_source_root.parent / "prompts"
64
+ for sub in ("lead", "coding-preflight"):
65
+ sub_root = prompts_source_root / sub
66
+ if not sub_root.is_dir():
67
+ errors.append(f"missing prompts source directory: {sub_root}")
68
+ continue
69
+ for source_path in sorted(sub_root.rglob("*.md")):
70
+ relative = source_path.relative_to(prompts_source_root)
71
+ check(source_path, runtime_root / "prompts" / relative)
64
72
 
65
73
  # 3. Skill packages: skills/<name>/SKILL.md -> runtime/skills/<name>/SKILL.md
66
74
  if not skills_source_root.is_dir():
@@ -25,7 +25,13 @@ Checks performed per brief file:
25
25
  own `brief-id`.
26
26
  9. `reporter-confirmations` consistency: when `complete`, every
27
27
  `intent-check:` and `conversion-block:` row in Open Questions MUST
28
- carry a `[CONFIRMED YYYY-MM-DD → RC-N]` marker.
28
+ carry a `[CONFIRMED YYYY-MM-DD → RC-N]` marker; when `partial`, at
29
+ least one such row MUST carry the marker (use `skipped` when nothing
30
+ was answered).
31
+ 10. `scope` is one of {reporter-input, codebase} (absent ⇒ reporter-input).
32
+ 11. codebase-scan variant (`scope: codebase`): the Scan Scope section is
33
+ non-empty and Priority Lenses lists 1–4 values from the lens whitelist
34
+ (`scripts/okstra_ctl/improvement_lenses.py` SSOT).
29
35
 
30
36
  Exit code 0 on PASS, 1 on FAIL.
31
37
  """
@@ -38,6 +44,19 @@ import sys
38
44
  from pathlib import Path
39
45
  from typing import Iterable
40
46
 
47
+ # scripts/ (repo) or python/ (installed under ~/.okstra/lib) is not a package;
48
+ # insert whichever exists so okstra_ctl is importable for the lens whitelist.
49
+ _VALIDATORS_DIR = Path(__file__).resolve().parent
50
+ for _ssot_dir in (_VALIDATORS_DIR.parent / "scripts", _VALIDATORS_DIR.parent / "python"):
51
+ if _ssot_dir.is_dir() and str(_ssot_dir) not in sys.path:
52
+ sys.path.insert(0, str(_ssot_dir))
53
+
54
+ from okstra_ctl.improvement_lenses import (
55
+ LENSES,
56
+ MAX_PRIORITY_LENSES,
57
+ MIN_PRIORITY_LENSES,
58
+ )
59
+
41
60
  REQUIRED_FRONTMATTER_KEYS = {
42
61
  "type",
43
62
  "brief-id",
@@ -68,6 +87,8 @@ AUGMENTATION_LABELS = {
68
87
 
69
88
  REPORTER_CONFIRMATION_VALUES = {"complete", "partial", "pending", "skipped"}
70
89
 
90
+ SCOPE_VALUES = {"reporter-input", "codebase"}
91
+
71
92
 
72
93
  def parse_frontmatter(text: str) -> tuple[dict[str, str], int]:
73
94
  """Return (frontmatter dict, line after closing `---`)."""
@@ -173,6 +194,68 @@ def parse_augmentation_label(entry: str) -> tuple[str | None, str]:
173
194
  return None, stripped
174
195
 
175
196
 
197
+ def meaningful_bullets(text: str, heading: str) -> list[str]:
198
+ """Real `- ` bullets under a heading, excluding placeholders/template scaffold."""
199
+ body = section_body(text, heading)
200
+ out: list[str] = []
201
+ for line in body.splitlines():
202
+ stripped = line.strip()
203
+ if not stripped.startswith("- "):
204
+ continue
205
+ content = stripped[2:].strip()
206
+ if is_placeholder(content) or is_template_example(content):
207
+ continue
208
+ out.append(content.strip("`"))
209
+ return out
210
+
211
+
212
+ def priority_lens_values(text: str) -> list[str]:
213
+ """Lens tokens from the `## Priority Lenses` bullets (`- <lens>: <rationale>`)."""
214
+ return [
215
+ bullet.split(":", 1)[0].strip()
216
+ for bullet in meaningful_bullets(text, "Priority Lenses")
217
+ ]
218
+
219
+
220
+ def check_codebase_scope(text: str, errors: list[str]) -> None:
221
+ """codebase-scan variant: Scan Scope non-empty, Priority Lenses ⊆ whitelist."""
222
+ if not meaningful_bullets(text, "Scan Scope"):
223
+ errors.append("scope is 'codebase' but the Scan Scope section has no entries")
224
+ lenses = priority_lens_values(text)
225
+ if not (MIN_PRIORITY_LENSES <= len(lenses) <= MAX_PRIORITY_LENSES):
226
+ errors.append(
227
+ f"Priority Lenses must list {MIN_PRIORITY_LENSES}–{MAX_PRIORITY_LENSES} "
228
+ f"lens(es), found {len(lenses)}"
229
+ )
230
+ unknown = [lens for lens in lenses if lens not in LENSES]
231
+ if unknown:
232
+ errors.append(
233
+ f"Priority Lenses contains values outside the lens whitelist: {unknown} "
234
+ f"(allowed: {sorted(LENSES)})"
235
+ )
236
+
237
+
238
+ def check_reporter_confirmations(
239
+ rc_status: str | None, reporter_rows: list[str], errors: list[str]
240
+ ) -> None:
241
+ """`complete` ⇒ every reporter row confirmed; `partial` ⇒ at least one."""
242
+ confirmed = [row for row in reporter_rows if "[CONFIRMED" in row]
243
+ if rc_status == "complete":
244
+ unconfirmed = [row for row in reporter_rows if "[CONFIRMED" not in row]
245
+ if unconfirmed:
246
+ errors.append(
247
+ f"reporter-confirmations is 'complete' but {len(unconfirmed)} "
248
+ f"intent-check:/conversion-block: row(s) lack a [CONFIRMED …] "
249
+ f"marker (e.g. {unconfirmed[0]!r})"
250
+ )
251
+ elif rc_status == "partial" and reporter_rows and not confirmed:
252
+ errors.append(
253
+ "reporter-confirmations is 'partial' but no intent-check:/"
254
+ "conversion-block: row carries a [CONFIRMED …] marker "
255
+ "(use 'skipped' when nothing was answered)"
256
+ )
257
+
258
+
176
259
  def validate_brief(path: Path, briefs_root: Path) -> list[str]:
177
260
  text = path.read_text(encoding="utf-8")
178
261
  errors: list[str] = []
@@ -202,6 +285,15 @@ def validate_brief(path: Path, briefs_root: Path) -> list[str]:
202
285
  f"{fm.get('reporter-confirmations')!r}"
203
286
  )
204
287
 
288
+ scope = fm.get("scope", "reporter-input")
289
+ if scope not in SCOPE_VALUES:
290
+ errors.append(
291
+ f"frontmatter scope must be one of {sorted(SCOPE_VALUES)} "
292
+ f"(or absent ⇒ reporter-input), got {scope!r}"
293
+ )
294
+ if scope == "codebase":
295
+ check_codebase_scope(text, errors)
296
+
205
297
  # 2. brief-id matches filename stem
206
298
  stem = path.stem
207
299
  if fm.get("brief-id") and fm["brief-id"] != stem:
@@ -306,21 +398,12 @@ def validate_brief(path: Path, briefs_root: Path) -> list[str]:
306
398
  f"({brief_id!r})"
307
399
  )
308
400
 
309
- # 9. reporter-confirmations consistency
310
- rc_status = fm.get("reporter-confirmations")
311
- if rc_status == "complete":
312
- unconfirmed = [
313
- row
314
- for row in (intent_check_rows + conversion_block_rows)
315
- if "[CONFIRMED" not in row
316
- ]
317
- if unconfirmed:
318
- sample = unconfirmed[0]
319
- errors.append(
320
- f"reporter-confirmations is 'complete' but {len(unconfirmed)} "
321
- f"intent-check:/conversion-block: row(s) lack a [CONFIRMED …] "
322
- f"marker (e.g. {sample!r})"
323
- )
401
+ # 9. reporter-confirmations consistency (complete / partial)
402
+ check_reporter_confirmations(
403
+ fm.get("reporter-confirmations"),
404
+ intent_check_rows + conversion_block_rows,
405
+ errors,
406
+ )
324
407
 
325
408
  return errors
326
409
 
@@ -436,7 +436,7 @@ def validate_team_state(
436
436
  "team-state.teamCreate.attempted must be true once any worker has "
437
437
  "been dispatched (status in completed/timeout/error/in-progress). "
438
438
  "Phase 3 (TeamCreate) was skipped — workers ran in-process without "
439
- "the Teams split-pane surface. See agents/SKILL.md Phase 3. "
439
+ "the Teams split-pane surface. See prompts/lead/okstra-lead-contract.md Phase 3. "
440
440
  "(The no-team concurrent-run path is legal ONLY when the "
441
441
  "run-manifest carries prepare-recorded `concurrentRun.detected: "
442
442
  'true` AND team-state records `teamCreate: { attempted: false, '
@@ -1373,7 +1373,7 @@ def _validate_verdict_card_consistency(content: str, failures: list[str]) -> Non
1373
1373
  """Verdict Card is a non-authoritative index of §7. If both blocks
1374
1374
  carry a Verdict Token row, the values MUST byte-match (modulo case
1375
1375
  and surrounding whitespace) — divergence is a contract violation per
1376
- `okstra-report-writer` SKILL.md "Authoring Contract".
1376
+ `report-writer` SKILL.md "Authoring Contract".
1377
1377
  """
1378
1378
  card_value = _extract_verdict_card_token(content)
1379
1379
  final_value = _extract_final_verdict_token(content)
@@ -1828,7 +1828,7 @@ def _validate_session_conformance(
1828
1828
  claude_projects_dir: str | None,
1829
1829
  failures: list[str],
1830
1830
  ) -> None:
1831
- """agents/SKILL.md BLOCKING 계약 3종(PROGRESS 체크포인트 / claude-worker
1831
+ """prompts/lead/okstra-lead-contract.md BLOCKING 계약 3종(PROGRESS 체크포인트 / claude-worker
1832
1832
  heartbeat / implementation entry guard)의 post-hoc 검사를 위임하고 실패를
1833
1833
  ``session-conformance: `` 접두로 folding 한다. 설계:
1834
1834
  docs/superpowers/specs/2026-06-10-blocking-contract-posthoc-conformance-design.md
@@ -1,12 +1,12 @@
1
- """agents/SKILL.md BLOCKING 계약 3종의 post-hoc conformance 검사.
1
+ """prompts/lead/okstra-lead-contract.md BLOCKING 계약 3종의 post-hoc conformance 검사.
2
2
 
3
3
  설계: docs/superpowers/specs/2026-06-10-blocking-contract-posthoc-conformance-design.md
4
4
 
5
5
  | 검사 | 선언 위치 | 증거 |
6
6
  |------|----------|------|
7
- | 1. lead PROGRESS 체크포인트 라인 | agents/SKILL.md "Progress reporting (BLOCKING)" | lead 세션 jsonl 의 assistant text 블록 |
7
+ | 1. lead PROGRESS 체크포인트 라인 | prompts/lead/okstra-lead-contract.md "Progress reporting (BLOCKING)" | lead 세션 jsonl 의 assistant text 블록 |
8
8
  | 2. claude-worker 5분 heartbeat | agents/workers/claude-worker.md "Heartbeat" | audit 사이드카의 `- PROGRESS: <stage> <ISO>` 라인 |
9
- | 3. implementation sidecar entry guard | agents/SKILL.md "Entry guard (BLOCKING)" | lead 세션 jsonl 의 Read tool_use |
9
+ | 3. implementation sidecar entry guard | prompts/lead/okstra-lead-contract.md "Entry guard (BLOCKING)" | lead 세션 jsonl 의 Read tool_use |
10
10
 
11
11
  스캔 규칙 (false-pass 방지가 핵심):
12
12
  - `type == "assistant"` 레코드만 본다 — Skill 호출 시 SKILL.md 본문(체크포인트
@@ -336,7 +336,7 @@ def _check_worker_checkpoint_lines(
336
336
  errors.append(
337
337
  f"PROGRESS checkpoint missing: no `phase-4-dispatch worker=<role>` "
338
338
  f"line names worker `{role}` — one line per dispatched worker, "
339
- "agents/SKILL.md 'Progress reporting (BLOCKING)'."
339
+ "prompts/lead/okstra-lead-contract.md 'Progress reporting (BLOCKING)'."
340
340
  )
341
341
  if status == "completed" and not _phase_mentions_worker(
342
342
  by_phase.get("phase-5-collect", []), needles
@@ -344,7 +344,7 @@ def _check_worker_checkpoint_lines(
344
344
  errors.append(
345
345
  f"PROGRESS checkpoint missing: no `phase-5-collect worker=<role>` "
346
346
  f"line names completed worker `{role}` — one line per collected "
347
- "result, agents/SKILL.md 'Progress reporting (BLOCKING)'."
347
+ "result, prompts/lead/okstra-lead-contract.md 'Progress reporting (BLOCKING)'."
348
348
  )
349
349
 
350
350
 
@@ -363,19 +363,19 @@ def _check_progress_checkpoints(
363
363
  if condition and phase not in by_phase:
364
364
  errors.append(
365
365
  f"PROGRESS checkpoint missing: `{phase}` ({detail}) — "
366
- "agents/SKILL.md 'Progress reporting (BLOCKING)'."
366
+ "prompts/lead/okstra-lead-contract.md 'Progress reporting (BLOCKING)'."
367
367
  )
368
368
 
369
369
  intake = by_phase.get("phase-1-intake", [])
370
370
  if not any("complete" not in line.lower() for _ts, line in intake):
371
371
  errors.append(
372
372
  "PROGRESS checkpoint missing: `phase-1-intake reading task bundle` "
373
- "(start-of-Phase-1 line) — agents/SKILL.md 'Progress reporting (BLOCKING)'."
373
+ "(start-of-Phase-1 line) — prompts/lead/okstra-lead-contract.md 'Progress reporting (BLOCKING)'."
374
374
  )
375
375
  if not any("complete" in line.lower() for _ts, line in intake):
376
376
  errors.append(
377
377
  "PROGRESS checkpoint missing: `phase-1-intake complete` "
378
- "(after all intake reads) — agents/SKILL.md 'Progress reporting (BLOCKING)'."
378
+ "(after all intake reads) — prompts/lead/okstra-lead-contract.md 'Progress reporting (BLOCKING)'."
379
379
  )
380
380
 
381
381
  workers = [w for w in (team_state.get("workers") or []) if isinstance(w, dict)]
@@ -427,7 +427,7 @@ def _check_heartbeat_sidecar(path: Path, errors: list[str]) -> None:
427
427
  f"`{rel}` has no `- PROGRESS: <stage> <ISO-8601-UTC>` heartbeat lines — "
428
428
  "the claude-worker MUST write `started` immediately and append one "
429
429
  "line per stage at <= 5-minute cadence (agents/workers/claude-worker.md "
430
- "'Heartbeat', agents/SKILL.md Common Mistakes)."
430
+ "'Heartbeat', prompts/lead/okstra-lead-contract.md Common Mistakes)."
431
431
  )
432
432
  return
433
433
  if entries[0][0] != "started":
@@ -498,7 +498,7 @@ def _check_implementation_sidecar_reads(evidence: _LeadEvidence, errors: list[st
498
498
  f"implementation entry guard: no `Read` of `{basename}` found in "
499
499
  f"the lead session jsonl within this run's window — the sidecar "
500
500
  f"MUST be read fresh at {read_at} every implementation run "
501
- "(agents/SKILL.md 'Entry guard (BLOCKING)')."
501
+ "(prompts/lead/okstra-lead-contract.md 'Entry guard (BLOCKING)')."
502
502
  )
503
503
  continue
504
504
  anchor_ts = anchors.get(anchor_phase)
@@ -507,7 +507,7 @@ def _check_implementation_sidecar_reads(evidence: _LeadEvidence, errors: list[st
507
507
  f"implementation entry guard: `{basename}` was first Read at "
508
508
  f"{min(ts_list)}, not before the first `PROGRESS: {anchor_phase}` "
509
509
  f"line ({anchor_ts}) — it must be loaded at {read_at}, before "
510
- "that checkpoint (agents/SKILL.md 'Entry guard (BLOCKING)')."
510
+ "that checkpoint (prompts/lead/okstra-lead-contract.md 'Entry guard (BLOCKING)')."
511
511
  )
512
512
 
513
513