okstra 0.151.0 → 0.152.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 (31) hide show
  1. package/docs/cli.md +1 -1
  2. package/docs/project-structure-overview.md +1 -1
  3. package/package.json +1 -1
  4. package/runtime/BUILD.json +2 -2
  5. package/runtime/agents/workers/antigravity-worker.md +2 -1
  6. package/runtime/agents/workers/claude-worker.md +2 -1
  7. package/runtime/agents/workers/codex-worker.md +2 -1
  8. package/runtime/agents/workers/grok-worker.md +2 -1
  9. package/runtime/agents/workers/kimi-worker.md +2 -1
  10. package/runtime/agents/workers/report-writer-worker.md +1 -1
  11. package/runtime/bin/okstra-report-translate.py +32 -11
  12. package/runtime/prompts/launch.template.md +1 -1
  13. package/runtime/prompts/lead/convergence.md +16 -4
  14. package/runtime/prompts/lead/report-writer.md +26 -14
  15. package/runtime/prompts/lead/team-contract.md +2 -1
  16. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  17. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  18. package/runtime/prompts/profiles/final-verification.md +1 -1
  19. package/runtime/prompts/profiles/implementation-planning.md +12 -7
  20. package/runtime/python/okstra_ctl/analysis_packet.py +1 -0
  21. package/runtime/python/okstra_ctl/dispatch_state.py +5 -1
  22. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +7 -0
  23. package/runtime/python/okstra_ctl/report_finalize.py +9 -4
  24. package/runtime/python/okstra_ctl/worker_prompt_body.py +4 -2
  25. package/runtime/python/okstra_ctl/worker_prompt_contract.py +41 -1
  26. package/runtime/python/okstra_ctl/worker_prompt_headers.py +3 -0
  27. package/runtime/templates/implementation-worker-preamble.md +12 -3
  28. package/runtime/templates/report-writer-prompt-preamble.md +5 -1
  29. package/runtime/templates/worker-prompt-preamble.md +12 -3
  30. package/runtime/validators/validate-implementation-plan-stages.py +170 -28
  31. package/runtime/validators/validate-run.py +137 -0
package/docs/cli.md CHANGED
@@ -401,7 +401,7 @@ worker roster contains Claude, Codex, or Antigravity.
401
401
 
402
402
  For a Codex lead dry run, use `okstra codex-run <args...>`. It adds `--render-only --lead-runtime codex` itself and prints the prepared task bundle and lead prompt without dispatching workers.
403
403
  The generated team-state, run manifest, and task manifest point `leadEventsPath` to `runs/<task-type>/state/lead-events-<task-type>-<seq>.jsonl`; rendering records a `bundle-prepared` event.
404
- Then `okstra codex-dispatch --project-root <dir> --run-manifest <run-manifest> [--workers <csv>]` reads each persisted assignment. `runner=native-session` rows stay with the current Codex host; `runner=cli-wrapper` rows run through their registered Claude, Antigravity, Grok, Kimi, or report-writer wrapper. The report-writer provider and model come from the manifest without a Codex-only opt-in flag; on success, postprocessing runs token-usage substitution → render-views → spawn-followups → validate-run in order.
404
+ Then `okstra codex-dispatch --project-root <dir> --run-manifest <run-manifest> [--workers <csv>]` reads each persisted assignment. `runner=native-session` rows stay with the current Codex host; `runner=cli-wrapper` rows run through their registered Claude, Antigravity, Grok, Kimi, or report-writer wrapper. The report-writer provider and model come from the manifest without a Codex-only opt-in flag; on success, postprocessing runs check-source → token-usage substitution → render-views → spawn-followups → validate-run in order.
405
405
 
406
406
  The Codex worker (`--workers codex`, `--codex-model`) and Codex lead runtime are separate. The former creates a worker assignment whose runner depends on the host; the latter selects Codex as the native lead boundary. On Claude Code the Codex worker uses a CLI wrapper, while on Codex it uses the host-native worker/session primitive.
407
407
 
@@ -312,7 +312,7 @@ Important modules:
312
312
  | `plan_items.py`, `plan_items_cli.py` | deterministic extraction of the report-writer data.json `P-*` plan-item queue plus the `okstra plan-items extract` / `validate` adapter |
313
313
  | `scope_provenance.py` | single source of truth for the scope-provenance grammar every phase-emitted requirement must declare, shared by `validators/validate-run.py` and `validators/validate_fanout.py` so the planning report and fan-out packets cannot drift |
314
314
  | `worker_artifact_paths.py` | canonical worker artifact path derivation (e.g. `audit_sidecar_rel` inserts `-audit-` after the first `-worker-` token), so dispatch and validation agree on non-canonical-path rejection |
315
- | `report_finalize.py` | Phase 7 post-report sequence **SSOT** — runs `token-usage` → `render-views` → `spawn-followups` → `validate-run` in that load-bearing order, stops at the first non-zero exit and names the failing step. Both lead paths converge here: the Codex adapter calls it in-process (`codex_dispatch`), a Claude-led run reaches it through `okstra report-finalize`. Neither reimplements the sequence |
315
+ | `report_finalize.py` | Phase 7 post-report sequence **SSOT** — runs `check-source` → `token-usage` → `render-views` → `spawn-followups` → `validate-run` in that load-bearing order, stops at the first non-zero exit and names the failing step. Both lead paths converge here: the Codex adapter calls it in-process (`codex_dispatch`), a Claude-led run reaches it through `okstra report-finalize`. Neither reimplements the sequence |
316
316
  | `wrapper_status.py` | worker wrapper status sidecar reader — the host-side reader of the sidecar written by `okstra-wrapper-status.py` (the heartbeat writer) |
317
317
  | `task_target.py` | shared helper resolving `task-key → (task_root, project_root)` (`resolve_task_root`) |
318
318
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.151.0",
3
+ "version": "0.152.0",
4
4
  "description": "Host-aware multi-provider cross-verification orchestrator runtime and agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.151.0",
3
- "builtAt": "2026-08-05T06:06:04.770Z",
2
+ "package": "0.152.0",
3
+ "builtAt": "2026-08-05T09:28:14.647Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -128,7 +128,7 @@ The Antigravity CLI's own exit terminates the underlying analysis; this wrapper
128
128
 
129
129
  ## MCP Scope
130
130
 
131
- This wrapper does NOT invoke MCP tools directly. MCP availability inside the Antigravity CLI is governed by the underlying CLI's own configuration. The `## Available MCP Servers` block from the lead prompt is forwarded verbatim into the dispatched prompt for record-keeping and so the Antigravity CLI's own logic can decide what to call this wrapper does not gate or filter it.
131
+ This wrapper does NOT invoke MCP tools directly. The analysis packet's `Available MCP Servers` section is the canonical server list. If the section is absent or says none, MCP is unavailable for this run; never infer tools from host configuration. The Antigravity CLI can use a packet-listed server only when its own configuration exposes that server; otherwise it records `MCP not available in this CLI`. This wrapper does not gate, filter, or invoke those tools.
132
132
 
133
133
  ## Prompt Composition
134
134
 
@@ -151,6 +151,7 @@ Before invoking the Antigravity CLI, you MUST:
151
151
 
152
152
  1. Extract `**Worker Preamble Path:**` and `**Worker Error Contract Path:**` and verify the CLI run will Read both selected files end-to-end. The first owns audience procedure; the second owns sidecar schema and write rules. Persist and forward both anchors unchanged; never substitute the analysis preamble for an implementation audience. **Exception — `-reverify-r<N>-` dispatches**: a Phase 5.5 re-verification prompt deliberately omits both reading contracts and uses its lightweight prompt contract; do NOT return a sentinel for those two omitted reading anchors. The `**Errors log path:**` / `**Errors sidecar path:**` gate still applies.
153
153
  2. Verify the lead's prompt body lists the per-run primary input files under `## Inputs` (normally `analysis-packet.md` for analysis workers). The source files named inside that packet are fallback/evidence paths to open when needed. Analysis workers do NOT read `final-report-template.md` — that file is for the report writer only.
154
+ 3. When the prompt carries `**Evidence ledger:** required-v1`, verify the CLI follows the selected preamble's `Evidence read ledger` procedure for every claim-evidence file it opens. Do not define or infer another audit-row format in this wrapper.
154
155
 
155
156
  Extract `**Audit sidecar path:** <abs-path>` verbatim from the lead's dispatch prompt and verify that the value is absolute. If the header is absent or the value is not absolute, return `ANTIGRAVITY_AUDIT_PATH_MISSING: lead prompt did not include a valid absolute **Audit sidecar path:** header` without invoking the CLI. Do NOT synthesize the audit sidecar path from the task type, worker name, or sequence.
156
157
 
@@ -50,7 +50,7 @@ Unlike the Codex / Antigravity workers, you are an in-process Claude subagent
50
50
  - **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.
51
51
  - **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.
52
52
 
53
- 5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the lead prompt's `## Available MCP Servers` section (sourced from `.okstra/project.json`'s `mcpServers` array). When the task requires inspection of an external system covered by one of those servers, call the listed tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
53
+ 5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the analysis packet's `Available MCP Servers` section. If the section is absent or says none, treat MCP as unavailable for this run; never infer tools from host configuration. When the task requires inspection of an external system covered by a listed server, call the tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
54
54
 
55
55
  6. If your dispatch prompt carries a `**Phase 1.5 Grilling Log:** <abs-path>` anchor header (the lead injects it only on `improvement-discovery` runs), the file it points to is the authoritative scope and lens definition. Read it at the absolute path from the anchor — do NOT synthesize the path from `<RUN_DIR>`. Use its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/validate_improvement_report.py`.
56
56
 
@@ -60,6 +60,7 @@ Before producing any output, you MUST:
60
60
 
61
61
  1. Extract `**Worker Preamble Path:**` and `**Worker Error Contract Path:**` from the lead prompt and Read both selected files end-to-end with one full-file `Read` each. The preamble owns audience procedure; the error contract owns sidecar schema and write rules. Never replace the selected path with a hard-coded analysis preamble.
62
62
  2. Read every primary input file the lead enumerated under `## Inputs` (or equivalent heading) end-to-end, following the selected preamble. Analysis workers normally receive `analysis-packet.md`; implementation workers receive their role sidecar and approved deliverable inputs.
63
+ 3. When the prompt carries `**Evidence ledger:** required-v1`, follow the selected preamble's `Evidence read ledger` procedure for every claim-evidence file you open. Do not invent a separate audit-row format here.
63
64
 
64
65
  **Heartbeat — write the audit sidecar EARLY and APPEND per stage (BLOCKING).** This worker runs as an in-process Agent or a fresh-session tmux pane, so the lead has no `BashOutput`-style liveness signal while it waits for your return — the audit sidecar is the only signal that survives a silent hang.
65
66
 
@@ -128,7 +128,7 @@ The Codex CLI's own exit terminates the underlying analysis; this wrapper termin
128
128
 
129
129
  ## MCP Scope
130
130
 
131
- This wrapper does NOT invoke MCP tools directly. MCP availability inside the Codex CLI is governed by the underlying CLI's own configuration. The `## Available MCP Servers` block from the lead prompt is forwarded verbatim into the dispatched prompt for record-keeping and so the Codex CLI's own logic can decide what to call this wrapper does not gate or filter it.
131
+ This wrapper does NOT invoke MCP tools directly. The analysis packet's `Available MCP Servers` section is the canonical server list. If the section is absent or says none, MCP is unavailable for this run; never infer tools from host configuration. The Codex CLI can use a packet-listed server only when its own configuration exposes that server; otherwise it records `MCP not available in this CLI`. This wrapper does not gate, filter, or invoke those tools.
132
132
 
133
133
  ## Prompt Composition
134
134
 
@@ -151,6 +151,7 @@ Before invoking the Codex CLI, you MUST:
151
151
 
152
152
  1. Extract `**Worker Preamble Path:**` and `**Worker Error Contract Path:**` and verify the CLI run will Read both selected files end-to-end. The first owns audience procedure; the second owns sidecar schema and write rules. Persist and forward both anchors unchanged; never substitute the analysis preamble for an implementation audience. **Exception — `-reverify-r<N>-` dispatches**: a Phase 5.5 re-verification prompt deliberately omits both reading contracts and uses its lightweight prompt contract; do NOT return a sentinel for those two omitted reading anchors. The `**Errors log path:**` / `**Errors sidecar path:**` gate still applies.
153
153
  2. Verify the lead's prompt body lists the per-run primary input files under `## Inputs` (normally `analysis-packet.md` for analysis workers). The source files named inside that packet are fallback/evidence paths to open when needed. Analysis workers do NOT read `final-report-template.md` — that file is for the report writer only.
154
+ 3. When the prompt carries `**Evidence ledger:** required-v1`, verify the CLI follows the selected preamble's `Evidence read ledger` procedure for every claim-evidence file it opens. Do not define or infer another audit-row format in this wrapper.
154
155
 
155
156
  Extract `**Audit sidecar path:** <abs-path>` verbatim from the lead's dispatch prompt and verify that the value is absolute. If the header is absent or the value is not absolute, return `CODEX_AUDIT_PATH_MISSING: lead prompt did not include a valid absolute **Audit sidecar path:** header` without invoking the CLI. Do NOT synthesize the audit sidecar path from the task type, worker name, or sequence.
156
157
 
@@ -128,7 +128,7 @@ The Grok CLI's own exit terminates the underlying analysis; this wrapper termina
128
128
 
129
129
  ## MCP Scope
130
130
 
131
- This wrapper does NOT invoke MCP tools directly. MCP availability inside the Grok CLI is governed by the underlying CLI's own configuration. The `## Available MCP Servers` block from the lead prompt is forwarded verbatim into the dispatched prompt for record-keeping and so the Grok CLI's own logic can decide what to call this wrapper does not gate or filter it.
131
+ This wrapper does NOT invoke MCP tools directly. The analysis packet's `Available MCP Servers` section is the canonical server list. If the section is absent or says none, MCP is unavailable for this run; never infer tools from host configuration. The Grok CLI can use a packet-listed server only when its own configuration exposes that server; otherwise it records `MCP not available in this CLI`. This wrapper does not gate, filter, or invoke those tools.
132
132
 
133
133
  ## Prompt Composition
134
134
 
@@ -151,6 +151,7 @@ Before invoking the Grok CLI, you MUST:
151
151
 
152
152
  1. Extract `**Worker Preamble Path:**` and `**Worker Error Contract Path:**` and verify the CLI run will Read both selected files end-to-end. The first owns audience procedure; the second owns sidecar schema and write rules. Persist and forward both anchors unchanged; never substitute the analysis preamble for an implementation audience. **Exception — `-reverify-r<N>-` dispatches**: a Phase 5.5 re-verification prompt deliberately omits both reading contracts and uses its lightweight prompt contract; do NOT return a sentinel for those two omitted reading anchors. The `**Errors log path:**` / `**Errors sidecar path:**` gate still applies.
153
153
  2. Verify the lead's prompt body lists the per-run primary input files under `## Inputs` (normally `analysis-packet.md` for analysis workers). The source files named inside that packet are fallback/evidence paths to open when needed. Analysis workers do NOT read `final-report-template.md` — that file is for the report writer only.
154
+ 3. When the prompt carries `**Evidence ledger:** required-v1`, verify the CLI follows the selected preamble's `Evidence read ledger` procedure for every claim-evidence file it opens. Do not define or infer another audit-row format in this wrapper.
154
155
 
155
156
  Extract `**Audit sidecar path:** <abs-path>` verbatim from the lead's dispatch prompt and verify that the value is absolute. If the header is absent or the value is not absolute, return `GROK_AUDIT_PATH_MISSING: lead prompt did not include a valid absolute **Audit sidecar path:** header` without invoking the CLI. Do NOT synthesize the audit sidecar path from the task type, worker name, or sequence.
156
157
 
@@ -128,7 +128,7 @@ The Kimi CLI's own exit terminates the underlying analysis; this wrapper termina
128
128
 
129
129
  ## MCP Scope
130
130
 
131
- This wrapper does NOT invoke MCP tools directly. MCP availability inside the Kimi CLI is governed by the underlying CLI's own configuration. The `## Available MCP Servers` block from the lead prompt is forwarded verbatim into the dispatched prompt for record-keeping and so the Kimi CLI's own logic can decide what to call this wrapper does not gate or filter it.
131
+ This wrapper does NOT invoke MCP tools directly. The analysis packet's `Available MCP Servers` section is the canonical server list. If the section is absent or says none, MCP is unavailable for this run; never infer tools from host configuration. The Kimi CLI can use a packet-listed server only when its own configuration exposes that server; otherwise it records `MCP not available in this CLI`. This wrapper does not gate, filter, or invoke those tools.
132
132
 
133
133
  ## Prompt Composition
134
134
 
@@ -151,6 +151,7 @@ Before invoking the Kimi CLI, you MUST:
151
151
 
152
152
  1. Extract `**Worker Preamble Path:**` and `**Worker Error Contract Path:**` and verify the CLI run will Read both selected files end-to-end. The first owns audience procedure; the second owns sidecar schema and write rules. Persist and forward both anchors unchanged; never substitute the analysis preamble for an implementation audience. **Exception — `-reverify-r<N>-` dispatches**: a Phase 5.5 re-verification prompt deliberately omits both reading contracts and uses its lightweight prompt contract; do NOT return a sentinel for those two omitted reading anchors. The `**Errors log path:**` / `**Errors sidecar path:**` gate still applies.
153
153
  2. Verify the lead's prompt body lists the per-run primary input files under `## Inputs` (normally `analysis-packet.md` for analysis workers). The source files named inside that packet are fallback/evidence paths to open when needed. Analysis workers do NOT read `final-report-template.md` — that file is for the report writer only.
154
+ 3. When the prompt carries `**Evidence ledger:** required-v1`, verify the CLI follows the selected preamble's `Evidence read ledger` procedure for every claim-evidence file it opens. Do not define or infer another audit-row format in this wrapper.
154
155
 
155
156
  Extract `**Audit sidecar path:** <abs-path>` verbatim from the lead's dispatch prompt and verify that the value is absolute. If the header is absent or the value is not absolute, return `KIMI_AUDIT_PATH_MISSING: lead prompt did not include a valid absolute **Audit sidecar path:** header` without invoking the CLI. Do NOT synthesize the audit sidecar path from the task type, worker name, or sequence.
156
157
 
@@ -69,7 +69,7 @@ Write the audit sidecar at `**Audit sidecar path:**` before required reading, th
69
69
 
70
70
  5. Anchor all file operations to the absolute `Project Root`. Use absolute paths everywhere — do not rely on inherited cwd, do not `cd`.
71
71
 
72
- 6. **MCP usage**: If the lead prompt's `## Available MCP Servers` block lists tools, you may invoke them by name (e.g. `mcp__<server>__<tool>`) to verify evidence cited by analysis workers. Do not invent MCP tools that are not listed.
72
+ 6. **MCP usage**: The analysis packet's `Available MCP Servers` section is canonical. If the section is absent or says none, treat MCP as unavailable for this run; never infer tools from host configuration. You may invoke packet-listed tools by name (e.g. `mcp__<server>__<tool>`) to verify evidence cited by analysis workers. Do not invent MCP tools that are not listed.
73
73
 
74
74
  ## Required Reading Before Authoring
75
75
 
@@ -8,7 +8,10 @@ Usage:
8
8
  ``extract`` writes the translator's work list — every pointer the report holds
9
9
  a translatable string at, paired with the English text. The translator fills
10
10
  in the values rather than authoring pointers, so a sidecar cannot cite a path
11
- the document does not have.
11
+ the document does not have. It refuses a data.json that is itself over the
12
+ Korean-prose limit: the work list would pair every pointer with target-language
13
+ text, and the translator would spend a full pass translating a document into
14
+ the language it is already in.
12
15
 
13
16
  ``check`` is the translator's own gate before it returns: it resolves every
14
17
  pointer in the sidecar against the report and reports what is still English.
@@ -68,9 +71,31 @@ def _load(path: Path) -> dict:
68
71
  return payload
69
72
 
70
73
 
74
+ def _english_source_failure(data_path: Path, data: dict) -> str | None:
75
+ """The Korean-prose message for *data*, or None when it reads as English."""
76
+ share, _ = hangul_share(data)
77
+ if share < HANGUL_PROSE_LIMIT:
78
+ return None
79
+ return (
80
+ f"error: {data_path.name} was authored in Korean "
81
+ f"({share:.0%} of its prose, limit {HANGUL_PROSE_LIMIT:.0%}). "
82
+ "The data.json is the English SSOT every later phase reads; the "
83
+ "report language selects the human HTML's language and is served "
84
+ "by the Phase 7 translator, not by authoring the SSOT in it.\n"
85
+ )
86
+
87
+
71
88
  def cmd_extract(args: argparse.Namespace) -> int:
72
89
  data_path = Path(args.data).resolve()
73
90
  data = _load(data_path)
91
+ # Refuse before building the work list. Extracting from a Korean SSOT
92
+ # produces a translation from the target language into itself: a
93
+ # full-cost artifact whose English column is not English, discovered
94
+ # only later when `check-source` fails the finalize step.
95
+ failure = _english_source_failure(data_path, data)
96
+ if failure is not None:
97
+ sys.stderr.write(failure)
98
+ return 1
74
99
  strings = extract(data)
75
100
  out_path = translation_source_path(data_path)
76
101
  lang = str((data.get("meta") or {}).get("reportLanguage") or "")
@@ -139,22 +164,18 @@ def cmd_check(args: argparse.Namespace) -> int:
139
164
 
140
165
  def cmd_check_source(args: argparse.Namespace) -> int:
141
166
  data_path = Path(args.data).resolve()
142
- share, length = hangul_share(_load(data_path))
167
+ data = _load(data_path)
168
+ share, length = hangul_share(data)
169
+ failure = _english_source_failure(data_path, data)
143
170
  payload = {
144
- "ok": share < HANGUL_PROSE_LIMIT,
171
+ "ok": failure is None,
145
172
  "hangulShare": round(share, 4),
146
173
  "limit": HANGUL_PROSE_LIMIT,
147
174
  "proseChars": length,
148
175
  }
149
176
  print(json.dumps(payload, ensure_ascii=False))
150
- if not payload["ok"]:
151
- sys.stderr.write(
152
- f"error: {data_path.name} was authored in Korean "
153
- f"({share:.0%} of its prose, limit {HANGUL_PROSE_LIMIT:.0%}). "
154
- "The data.json is the English SSOT every later phase reads; the "
155
- "report language selects the human HTML's language and is served "
156
- "by the Phase 7 translator, not by authoring the SSOT in it.\n"
157
- )
177
+ if failure is not None:
178
+ sys.stderr.write(failure)
158
179
  return 1
159
180
  return 0
160
181
 
@@ -75,7 +75,7 @@ Emit one `PROGRESS: <phase-id> <verb-phrase>` line as plain user-facing text at
75
75
  ## Available MCP Servers
76
76
 
77
77
  {{AVAILABLE_MCP_SERVERS}}
78
- - The full usage policy and per-phase rules live in the analysis packet's `Available MCP Servers` extract. Inject only the one-line pointer below into each analysis-worker prompt: `**MCP servers:** follow the analysis packet's "Available MCP Servers" section (already in your Required reading).`
78
+ - The full usage policy and per-phase rules live in the analysis packet's `Available MCP Servers` extract. Inject only the one-line pointer below into each analysis-worker prompt: `**MCP servers:** follow the analysis packet's "Available MCP Servers" section. If the section is absent or says none, treat MCP as unavailable for this run; never infer tools from host configuration.`
79
79
  - **Invocation rule (forward to every worker prompt)**: MCP tools are addressed through the executing worker provider's native tool interface — never by running the tool name as a shell command. In-process workers call the tool directly; CLI workers use that CLI's configured MCP transport. A worker without the configured MCP server records `MCP not available in this CLI` instead of guessing.
80
80
  - Codex worker and Antigravity worker run external CLIs; they can only use these MCP servers if their own CLI configs mirror them. If not, instruct the worker to record `MCP not available in this CLI` in its `Missing Information or Assumptions` block rather than guessing or shell-falling-back.
81
81
  - MCP queries are evidence-grade. Cite server, table, and the SELECT used in worker output. MCP must NOT be used as a write path in any phase, including `implementation`.
@@ -106,7 +106,7 @@ convergence-<task-type>-<seq>.json
106
106
 
107
107
  Follow this protocol exactly:
108
108
 
109
- 0. The machine-readable inputs are `schemas/convergence-groups-v1.0.schema.json`, `schemas/convergence-round-results-v1.0.schema.json`, and `schemas/convergence-critic-results-v1.0.schema.json`. Use `okstra convergence example --kind <groups|round-results|critic-results>` for deterministic valid examples; the command writes only JSON to stdout.
109
+ 0. Two schemas describe what the reducer reads: `schemas/convergence-groups-v1.0.schema.json` feeds step 1's `seed --groups`, and `schemas/convergence-round-results-v1.0.schema.json` feeds step 4's `apply-round --results`. `schemas/convergence-critic-results-v1.0.schema.json` is a third shape but **not** a reducer input — it describes the critic worker's own result document. Step 6's `apply-critic-gaps --results` takes the coverage batch you assemble from those candidates plus each analyser's vote (`{schemaVersion, taskKey, mode, provider, modelExecutionValue, dispatches[], gaps[]}`, spelled out in §"Coverage critic pass" §"State"); feeding the critic document straight in is rejected, by design. `okstra convergence example --kind <groups|round-results|critic-results>` prints a deterministic valid instance of each and writes only JSON to stdout.
110
110
  1. Run `okstra convergence seed --groups <groups> --work-state <work> --final-state <final> --migration-dir <state/migrations>`. A `reuse-final` action means validate the existing final and continue to Phase 6. `create-work`, `resume-work`, and `restart-round0` continue with planning.
111
111
  2. Run `okstra convergence plan-round --work-state <work> --plan <round-plan>`. This is read-only with respect to the working state.
112
112
  3. When the plan action is `dispatch`, create exactly one reverify prompt for each `dispatches[]` row and dispatch it through the selected runtime adapter. Its findings are exactly that row's `findingIds`.
@@ -541,11 +541,23 @@ Runs only when `convergence.critic.enabled == true` (set by `--critic <provider>
541
541
 
542
542
  The critic input is the Round 0 consolidated finding list. Reverify rounds only classify findings — they never add or remove them (in-round queue insertions are forbidden, see §"Convergence State Artifact" `carriedForwardCount`) — so the critic dispatch MUST NOT wait for classification to finish:
543
543
 
544
- - **Dispatch**: immediately after Round 0 grouping, CONCURRENTLY with the first reverify round's dispatches. When the verification queue is empty after Round 0 (no reverify round runs), dispatch right after grouping. Concurrent dispatch to the same provider is safe — the critic result path (`<provider>-critic-...`) never collides with a reverify result path.
544
+ - **Dispatch**: immediately after Round 0 grouping, CONCURRENTLY with the first reverify round's dispatches. When the verification queue is empty after Round 0 (no reverify round runs), dispatch right after grouping. Concurrent dispatch to the same provider is safe — the critic result path (`<provider>-worker-critic-...`) never collides with a reverify result path.
545
545
  - **Gap verification + merge**: only after BOTH the finding-convergence loop has exited AND the critic result is collected, and BEFORE the Phase 6 report-writer dispatch. If the loop exited `aborted-non-result`, do NOT dispatch a gap-verification round — record every gap in `unverifiedGaps[]` per §"Gap verification".
546
546
 
547
547
  ### Dispatch (fresh one-shot)
548
- Dispatch one fresh pass to `config.critic.provider` through `redispatch_worker`, with `model = config.critic.modelExecutionValue` and `dispatchKind = "critic"`. If the model value is empty, record `critic-skipped: model-unresolved`; never dispatch without a model. Result path: `runs/<task-type>/worker-results/<provider>-critic-<task-type>-<seq>.md`. The critic prompt seeds the consolidated findings and asks ONLY for coverage gaps:
548
+ Dispatch one fresh pass to `config.critic.provider` through `redispatch_worker`, with `model = config.critic.modelExecutionValue` and `dispatchKind = "critic"`. If the model value is empty, record `critic-skipped: model-unresolved`; never dispatch without a model. Result path: `runs/<task-type>/worker-results/<provider>-worker-critic-<task-type>-<seq>.md`.
549
+
550
+ The `-worker-` token is load-bearing, not decoration: the critic prompt carries the same generated anchor headers as every other worker ([team-contract](./team-contract.md) §"Worker prompts"), and its `**Audit sidecar path:**` comes from passing that result path through `okstra_ctl.worker_artifact_paths.audit_sidecar_rel()`, which inserts `-audit-` after the token and raises without it. A `<provider>-critic-...` name leaves the lead choosing between breaking the contract and hand-inventing the sidecar name. Note that `originWorker` stays `"<provider>-critic"` — that is a worker id in the convergence state, not a filename, and the two do not have to match.
551
+
552
+ The critic prompt seeds the consolidated findings and asks ONLY for coverage gaps:
553
+
554
+ Required reading before proposing a gap:
555
+ - the current run's `analysis-packet.md` for requirements and phase scope;
556
+ - `convergence-groups-<task-type>-<seq>.json` for the complete Round 0 ledger;
557
+ - every initial analysis-worker result named by team-state;
558
+ - each matching audit sidecar, to distinguish an uninspected path from a claim that was inspected but summarized during grouping.
559
+
560
+ Operational guardrails are not task requirements. A gap must trace to a brief requirement, an analysis-packet scope item, a source path the packet authorizes, or an evidence claim in a worker result. Do NOT infer missing verification from a one-line summary; open the named result and audit sidecar first.
549
561
 
550
562
  ```
551
563
  You are the coverage critic for <task-key>. Below are the consolidated findings
@@ -600,7 +612,7 @@ Promoted blockers enter `## 5.8 Acceptance Blockers`; since `accepted` requires
600
612
 
601
613
  ### State
602
614
 
603
- Critic output lives in the run's `worker-results/` directory (`runs/final-verification/worker-results/` for whole-task verification, `runs/final-verification/stage-<N>/worker-results/` for single-stage), filename `<provider>-critic-final-verification-<seq>.md`. The convergence state `config.critic` summary records `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual`; v1.3 enforces `candidatesProposed = confirmedBlockers + downgradedToResidual`, so no candidate can be silently dropped.
615
+ Critic output lives in the run's `worker-results/` directory (`runs/final-verification/worker-results/` for whole-task verification, `runs/final-verification/stage-<N>/worker-results/` for single-stage), filename `<provider>-worker-critic-final-verification-<seq>.md` (same `-worker-` token rule as §"Coverage critic pass" — the audit sidecar is derived from it). The convergence state `config.critic` summary records `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual`; v1.3 enforces `candidatesProposed = confirmedBlockers + downgradedToResidual`, so no candidate can be silently dropped.
604
616
 
605
617
  ## Output
606
618
 
@@ -48,15 +48,14 @@ The prompt MUST include, in this order at the top:
48
48
  10. The full `[Required reading]` clause (see [team-contract](./team-contract.md)) — for Phase 6 it adds two **per-task-type, instruction-set-local** read-only files, both scoped to this run's task-type by `okstra-ctl` at prep time:
49
49
  - `<instruction-set>/final-report-schema.json` — a task-type excerpt of schema v2. This is the binding authoring shape; the installed full schema is what the run is judged against. Do **NOT** pull the full repository schema because it is outside the task bundle.
50
50
  - `<instruction-set>/final-report-template.md` — the AI handoff Markdown template. It shows the agent-facing ledger shape, not the human presentation. The task-specific HTML renderer reads data.json separately.
51
- 11. A one-line MCP pointer instead of the verbatim block (redundant — the brief is already in the report-writer's Required reading, item 10): `**MCP servers:** follow the task brief's "## Available MCP Servers" section (already in your Required reading).`
51
+ 11. The analysis packet path plus a one-line MCP pointer instead of copying the server block verbatim: `**MCP servers:** follow the analysis packet's "Available MCP Servers" section. If the section is absent or says none, treat MCP as unavailable for this run; never infer tools from host configuration.`
52
52
  12. `Convergence state: runs/<task-type>/state/convergence-<task-type>-<seq>.json`, followed by pointers to all analysis-worker result files under `worker-results/`. The convergence path is deterministic and is listed even before Phase 5.5 creates the file. Read its classifications (Full/Partial/Contested/Worker-Unique), `roundHistory[]`, `round2SkippedReason`, and `finalClassificationCounts`; populate `crossVerification.roundHistory` in data.json so Section 6 can show which rounds executed, queue sizes, and why Round 2 was (or was not) skipped. The renderer prints the full per-round table only when more than one round ran; single-round or zero-round histories are auto-collapsed to a one-line summary.
53
53
  13. `**Report Language:** <en|ko>` — must be either `en` or `ko`; `auto`
54
54
  has been resolved by the lead from project.json / global config
55
55
  before the dispatch is constructed. The worker copies this verbatim
56
56
  into `data.json.meta.reportLanguage`.
57
- 14. For implementation-planning runs: a literal block listing the 12 required English section headings `Option Candidates`, `Trade-off`, `Recommended Option`, `Stage Map`, `Stepwise Execution Order`, `Dependency`, `Validation Checklist`, `Rollback`, `Requirement Coverage`, `Plan Body Verification`, `Cross-Project Dependencies`, `Decision Drafts`. This list is `PLANNING_REQUIRED_SECTIONS` in `validators/validate-run.py`; that tuple is the SSOT and this block must match it exactly. The writer uses these exact substrings as section headings (Korean translation in parentheses is allowed), and the `Plan Body Verification` section carries its required `Gate result:` line.
58
- 15. An explicit instruction: `You are the author of THREE files: (a) the final-report data.json at <Result Path>, (b) its rendered Markdown sibling produced through "okstra render-final-report <Result Path>", and (c) the worker-result pointer at <Worker Result Path>. Maintain the separate heartbeat audit sidecar at <Audit sidecar path>. Do not return the report inline. The dispatch fails when any of the three completion artifacts is missing, and session conformance fails when the audit sidecar is missing or invalid.`
59
- 16. The prose budget (dedup contract): `verdictCard.finalConclusion` is the conclusion SSOT — at most 3 sentences. `rationale.*` fields stay within 2 sentences each; `humanSummary` entries stay concise; task `userNarrative` explains each user-facing section once with evidence references. Do not copy these narratives into the AI Markdown. `summary` stays at 3-5 rows unless the run covers multiple tickets. Generation time scales with output volume, so exceeding the budget is a cost bug, not extra diligence.
57
+ 14. An explicit instruction: `You are the author of THREE files: (a) the final-report data.json at <Result Path>, (b) its rendered Markdown sibling produced through "okstra render-final-report <Result Path>", and (c) the worker-result pointer at <Worker Result Path>. Maintain the separate heartbeat audit sidecar at <Audit sidecar path>. Do not return the report inline. The dispatch fails when any of the three completion artifacts is missing, and session conformance fails when the audit sidecar is missing or invalid.`
58
+ 15. The prose budget (dedup contract): `verdictCard.finalConclusion` is the conclusion SSOT at most 3 sentences. `rationale.*` fields stay within 2 sentences each; `humanSummary` entries stay concise; task `userNarrative` explains each user-facing section once with evidence references. Do not copy these narratives into the AI Markdown. `summary` stays at 3-5 rows unless the run covers multiple tickets. Generation time scales with output volume, so exceeding the budget is a cost bug, not extra diligence.
60
59
 
61
60
  **Fix-run incremental authoring (applies when the run's profile carries a "Fix-Run Carry" block).** Do not author the data.json from scratch. Start by copying the previous run's data.json (the `Previous report` path in the Fix-Run Carry block) to this run's Result Path, then update ONLY the blocks the fix run changed: `meta`/`header` (run seq, dates), `executionStatus`, `implementation.verifierResults`, `implementation.validationEvidence`, `implementation.commitList` / `diffSummary`, `crossVerification`, `verdictCard`, `finalVerdict`, and any `evidence` rows the fix touched. Deliverable prose for unchanged sections is carried forward verbatim — do not re-generate it. Then invoke the renderer exactly as in a full run. The schema validation and renderer contract are unchanged, so an incrementally-authored data.json passes the same post-hoc gates. The lead's dispatch prompt MUST include the previous data.json path when the carry block is present.
62
61
 
@@ -83,7 +82,17 @@ Phase 6 first produces the final-report data.json at `runs/<task-type>/reports/f
83
82
 
84
83
  For an implementation-planning run, the Report writer worker owns the Phase 6 design assessment snapshot: it writes `designPreparation` and every stage's `designSurfaceCoverage` into data.json from the detector output and consolidated plan. It does not create user inputs, consume a user answer as if it were part of that snapshot, or materialize `design-prep-requests/`; `schemas/final-report-v2.0.schema.json` and `validators/validate-run.py` `_validate_design_prep_contract` enforce the snapshot shape, detector coverage, and references.
85
84
 
86
- Phase 7 post-processing is **one command**. `okstra report-finalize` owns the ordered sequence it is the same code path the Codex lead adapter runs automatically, so a Claude-led run and a Codex-led run finalize identically:
85
+ ### Before `report-finalize`: the translation sidecar (BLOCKING order)
86
+
87
+ `report-finalize` step `render-views` overlays the translation sidecar, so a non-English run must produce that sidecar **before** the command runs. That leaves exactly one correct order, and it is not the intuitive one:
88
+
89
+ 1. **Verify the data.json is English first.** Run `okstra report-translate check-source <data.json>`. Do this even when **Report Language** is `en` — it is the cheapest gate in the phase and it protects every step after it.
90
+ 2. **Only when it passes and Report Language is not `en`**, dispatch the translator worker, which writes `final-report-<task-type>-<seq>.i18n.<lang>.json`.
91
+ 3. Then run `report-finalize`.
92
+
93
+ **Never dispatch the translator before step 1.** The data.json is the English SSOT; a report-writer that authored it in the reader's language produces a translation *from that language into itself* — a full-cost, entirely useless artifact, and the run still fails at `check-source` afterwards. **Enforced:** `okstra report-translate extract` refuses to build a work list from a data.json over the Korean-prose limit, so a mis-ordered dispatch fails at the translator's first command instead of after it. When it does fail, the fix is a report-writer rewrite in English — discard the sidecar and `translation-source.json` produced from the Korean draft rather than editing them, because their English column is not English.
94
+
95
+ Phase 7 post-processing is then **one command**. `okstra report-finalize` owns the ordered sequence — it is the same code path the Codex lead adapter runs automatically, so a Claude-led run and a Codex-led run finalize identically:
87
96
 
88
97
  ```bash
89
98
  okstra report-finalize \
@@ -92,24 +101,25 @@ okstra report-finalize \
92
101
  --report <runDirectoryPath>/reports/final-report-<task-type>-<seq>.md
93
102
  ```
94
103
 
95
- Do NOT run the four steps below by hand. Hand-running them is the recurring root cause of reports shipping with `--` token cells, a missing html sibling, Section 3 missing follow-up entries, or Section 4 rows never spawning — the order is load-bearing and a skipped step surfaces only later, as a validator `contract-violated`. Every step is idempotent, so after fixing a reported failure just re-run the same command.
104
+ Do NOT run the five steps below by hand. Hand-running them is the recurring root cause of reports shipping with `--` token cells, a missing html sibling, Section 3 missing follow-up entries, or Section 4 rows never spawning — the order is load-bearing and a skipped step surfaces only later, as a validator `contract-violated`. Every step is idempotent, so after fixing a reported failure just re-run the same command.
96
105
 
97
106
  The steps it executes, in this contractual order, and the contract each one carries:
98
107
 
99
- 1. **`token-usage` — collect usage.** Aggregates `leadUsage` / `workers[].usage` / `usageSummary` into team-state, populates `tokenUsage` and the execution-status usage fields in data.json, and re-invokes the renderer so the markdown carries real numbers.
108
+ 1. **`check-source` — verify the data.json is English.** The same gate as the pre-translator check above, run again here because everything after it derives from the data.json: rendering a Korean SSOT into English chrome, spawning follow-ups from it, and validating it all succeed on a record the next phase cannot read. A failure here means the report-writer authored in the reader's language; re-dispatch it with the English rule rather than editing the data.json by hand.
109
+ 2. **`token-usage` — collect usage.** Aggregates `leadUsage` / `workers[].usage` / `usageSummary` into team-state, populates `tokenUsage` and the execution-status usage fields in data.json, and re-invokes the renderer so the markdown carries real numbers.
100
110
 
101
111
  The data.json paths populated: `tokenUsage.lead.{totalTokens,billableTokens,costUsd}`, the `worker` / `grand` rows, `tokenUsage.cli.costUsd`, and each `executionStatus[].{totalTokens,billableTokens,costUsd,durationMs,cliTotalTokens,cliCostUsd}` for rows whose role matches a team-state worker. The data.json MUST already exist (Phase 6 output).
102
112
 
103
113
  For implementation-planning, this Phase 7 canonical render calls `materialize_design_prep_requests()` after token substitution and creates deterministic request files only for `provisional` / `blocked` items. Later answers are append-only user-input sidecars; request generation and user input never rewrite the assessment fields, so the source report remains immutable as the design-input snapshot after this render. `validators/validate-run.py` `_validate_design_prep_requests` enforces request existence, canonical path, content, and assessment fingerprint.
104
- 2. **`render-views` — render the human report artifact.** Runs against the substituted v2 data.json and its Markdown sibling.
114
+ 3. **`render-views` — render the human report artifact.** Runs against the substituted v2 data.json and its Markdown sibling.
105
115
 
106
116
  Output (idempotent — re-running overwrites):
107
117
  - `runs/<task-type>/reports/final-report-<task-type>-<seq>.html` — single-file self-contained human view, always generated for schema v2 from the dedicated template registered for that task type. Clarification rows with `Status` ∈ {`open`, `answered`} embed response controls and export a `user-response-<task-type>-<seq>.md` sidecar. The original data and Markdown artifacts are never mutated by user input.
108
118
  - the implementation-planning report renders a **Plan Approval** section at the end of the body (implementation-option `<select>` + an approval checkbox) — disabled while any §1 `Blocks: approval` row is unresolved. Checking approval and exporting embeds a `## APPROVAL` block in the sidecar body, and the implementation-start wizard's approve-confirm step detects it and, after user confirmation, applies it through the existing `--approve` / `--implementation-option` path.
109
119
  - Schema-v1 and quick compatibility reports retain the legacy conditional HTML path; this does not change the schema-v2 always-generated contract.
110
120
 
111
- It runs after usage collection so token placeholders are substituted in any rendered html, and before routing persistence so the html artifact, when generated, exists for the validator step that checks it.
112
- 3. **`spawn-followups` — routing and follow-up persistence.** Turns the report's `## 4. Follow-up Tasks` rows into `tasks/<task-group>/<new-task-id>/` stubs.
121
+ It runs after usage collection so token placeholders are substituted in any rendered html, and before routing persistence so the html artifact, when generated, exists for the validator step that checks it. It also overlays the translation sidecar, which is why a non-English run must dispatch the translator before this command — see the ordering rule above.
122
+ 4. **`spawn-followups` — routing and follow-up persistence.** Turns the report's `## 4. Follow-up Tasks` rows into `tasks/<task-group>/<new-task-id>/` stubs.
113
123
 
114
124
  Behaviour contract:
115
125
  - Idempotent: rows whose target dir exists are reported as `existing` and skipped. Reruns of the same parent task are safe.
@@ -124,7 +134,7 @@ The steps it executes, in this contractual order, and the contract each one carr
124
134
  ```
125
135
 
126
136
  The status file is written after routing and follow-up persistence completes.
127
- 4. **`validate-run` — validate the finished run.** Checks the completed artifact set, including the report-views contract that catches a missing or stale html sibling. A failure here names the specific contract; fix it and re-run `okstra report-finalize`.
137
+ 5. **`validate-run` — validate the finished run.** Checks the completed artifact set, including the report-views contract that catches a missing or stale html sibling. A failure here names the specific contract; fix it and re-run `okstra report-finalize`.
128
138
 
129
139
  After `okstra report-finalize` reports `"ok": true`, **execute the run-scoped cleanup gate.** Call `shutdown_workers` only after that success, all persistence work, and explicit user approval under [okstra-lead-contract](./okstra-lead-contract.md) "Run-scoped worker-resource lifecycle". If the user keeps resources, leave the selected adapter's resources intact and surface its manual cleanup guidance.
130
140
 
@@ -208,11 +218,13 @@ Token Summary Generation Rules:
208
218
  - If `lead` or any `worker.usage` records unavailable evidence, show `--` for that row and append a one-line note (`reason: <note>`).
209
219
  - If pricing for a model is unknown, the script omits `estimatedCostUsd` for that block — show `N/A` in that column and add a note like `pricing missing for model <model>`.
210
220
 
211
- ### Implementation-planning section heading contract (BLOCKING)
221
+ ### Implementation-planning section heading contract (schema v1 only)
222
+
223
+ **This does not apply to any run you will author.** New runs are schema v2 (`report_contract.CURRENT_REPORT_SCHEMA_VERSION`), and `validate_phase_boundary` returns before the substring scan when `schemaVersion == "2.0"` — the v2 deliverable is gated by the schema instead, whose `implementationPlanning` block requires every one of these contents as a named key. The v2 AI-handoff template carries nine headings and serialises the plan as JSON beneath them, so it cannot produce these strings and is not expected to.
212
224
 
213
- When the run's `task-type` is `implementation-planning`, the final report MUST contain section headings whose **lines include each of the 12 literal English substrings below**. The validator (`validators/validate-run.py`) does plain substring matching on the report text and validates the design-preparation data contract missing headings was a real, repeatedly observed failure mode caused by translating the headings to Korean.
225
+ Reading this section as a live instruction is a known and expensive mistake: the writer is sent to author headings the v2 template has no place for, and the run reads as structurally unpassable when nothing is wrong with it. It is retained only for rendering or diagnosing historical schema-v1 reports.
214
226
 
215
- The rows below mirror `PLANNING_REQUIRED_SECTIONS` in `validators/validate-run.py`, which is the SSOT. **Enforced:** `tests/contract/test_planning_required_sections_ssot.py` fails when this table and that tuple diverge a heading listed here but not required (or required but not listed) sends the writer to author a section the validator rejects, or to omit one it demands.
227
+ For those v1 reports, the final report must contain section headings whose **lines include each of the 12 literal English substrings below**. The rows mirror `PLANNING_REQUIRED_SECTIONS` in `validators/validate-run.py`, which is the SSOT. **Enforced:** `tests/contract/test_planning_required_sections_ssot.py` fails when this table and that tuple diverge, and pins the v1 scoping above so the section cannot silently become unconditional again.
216
228
 
217
229
  | # | Required substring | Recommended heading form |
218
230
  |---|--------------------|--------------------------|
@@ -79,7 +79,7 @@ The Phase 7 run validator enforces the same cross-task rule against the persiste
79
79
 
80
80
  When a worker reads any project-relative path from the prompt, it MUST resolve it against `Project Root` (e.g. `<Project Root>/<Result Path>`) — never use bare relative paths that depend on cwd.
81
81
 
82
- If the task brief contains an `## Available MCP Servers` section, inject only the one-line pointer into every analysis worker's prompt (and into the report-writer prompt when it is dispatched in Phase 6) the brief is already in every worker's [Required reading], so verbatim copy is redundant: `**MCP servers:** follow the task brief's "## Available MCP Servers" section (already in your Required reading).` Codex/Antigravity workers run external CLIs whose MCP availability is governed by their own CLI configs; they can record `MCP not available in this CLI` cleanly after reading that section in the brief.
82
+ Inject only the packet-scoped one-line pointer into every analysis worker's prompt and into the report-writer prompt when it is dispatched in Phase 6: `**MCP servers:** follow the analysis packet's "Available MCP Servers" section. If the section is absent or says none, treat MCP as unavailable for this run; never infer tools from host configuration.` Codex/Antigravity workers run external CLIs whose MCP availability is governed by their own CLI configs; they record `MCP not available in this CLI` when their CLI does not expose a server the packet names.
83
83
 
84
84
  Persist the exact worker prompt before dispatch per Operating Rule 6; never use `/tmp/*prompt*.txt` as the canonical artifact path.
85
85
 
@@ -91,6 +91,7 @@ The lead does not inline reading or error blocks. It resolves `PromptPlan.audien
91
91
 
92
92
  What the lead MUST still do per dispatch:
93
93
  - Inject the input file enumeration into the dispatch prompt body via an `## Inputs` section (or any heading the recipient agent expects), listing the actual project-relative primary inputs derived from the run's `instruction-set/`. For `final-verification` analysis workers, list only `analysis-packet.md` as the primary input; source files are reached on demand through that packet. Other phases may list source/fallback paths when useful. The preamble describes the rules; the lead provides the specific paths for THIS run.
94
+ - Inject `**Evidence ledger:** required-v1` into every initial non-report-writer prompt. The selected audience preamble owns the audit-row syntax; `validators/validate-run.py` `validate_worker_results_audit()` enforces that each backticked `path:line` result citation has a matching evidence-read row. Report-writer and reverify prompts do not carry this marker.
94
95
  - Inject `**Worker Error Contract Path:**` plus the absolute `**Errors log path:**` and `**Errors sidecar path:**` headers — workers cannot synthesize these paths.
95
96
  - Omit the preamble pointer for reverify dispatches (Phase 5.5 lightweight mode) — see [convergence](./convergence.md) "Reverify prompt: required-reading suppression".
96
97
 
@@ -17,6 +17,6 @@ Load the applicable coding conventions for every language the diff will touch, t
17
17
 
18
18
  - **Resource selection — read the routed pack, never inline it here.** Use this worker prompt's `**Coding preflight pack:**` anchor header as the absolute path to the installed routed pack. Detect each touched file's language and framework from its extension or project manifest (`package.json`, `Cargo.toml`, `pyproject.toml`, `pom.xml`, `build.gradle*`, `prisma/schema.prisma`), then read that pack's resources via the Read tool by absolute path. Always read `overview.md` (the router) + `clean-code.md`, then select per the router's three ordered stages — Stage 1 language → `languages/<lang>.md`, Stage 2 framework → `frameworks/<fw>.md` (e.g. `frameworks/node-server.md` for server-side Node), Stage 3 architecture → `architectures/<arch>.md` (e.g. `architectures/hexagonal.md` for ports-and-adapters / NestJS-hex). Each stage is a list of rules; include EVERY matching resource (a change set can touch multiple languages/frameworks/architectures) — do not stop at the first match. These files are runtime resources, not Skill-tool skills, so always read them by path.
19
19
  - **Declared architecture style — an authoritative Stage 3 input, and it binds.** Before selecting resources, read `<PROJECT_ROOT>/.okstra/project.json` and take `architecture.style`. A declared `hexagonal` selects `architectures/hexagonal.md` even when none of Stage 3's layout signals matched, so the declaration — not the directory shape — decides. A declared `layered` has no pack resource; its invariant applies from this line: dependencies run one direction only — an upper layer may import a lower one, never the reverse — and a variation point is extracted onto a layer boundary. A declared style makes this overlay binding rather than advisory, and which rule binds follows the style: under `hexagonal` the overlay's otherwise-advisory concrete-adapter item is blocking, so a service dependency you add or modify goes through a port instead of a concrete implementation and that placement violation is fixed before the write rather than recorded as a note; under `layered` what binds is the direction invariant just stated — your own judgement over the import list of every file the diff touches, plus extracting a variation point onto a layer boundary — while the concrete-adapter item stays advisory, since `layered` has no ports to route it through. An absent field, a `none` style, or an unreadable `project.json` changes nothing — Stage 3 stays detection-driven and its overlay stays advisory, leaving the language-agnostic principles below as the only always-binding layer. The verifier re-grades the same diff under the same declaration (`_implementation-verifier.md` → Static design & test-quality review), so a placement violation missed here returns as a verdict `FAIL`.
20
- - **Project review rule packs:** also look for project-local review skills in `<PROJECT_ROOT>/skills/*review*`, `<PROJECT_ROOT>/.claude/skills/*review*`, and up to two parent directories' `skills/*review*/SKILL.md`. Read the relevant `SKILL.md` plus referenced `references/*.md` files and apply their rules during implementation. This is a prevention pass, not a PR-comment generation workflow: do not dispatch reviewer subagents from the executor. For Fonts Ninja-style PR review packs, the executor must avoid newly introduced duplicate helper stacks, tautological tests that merely re-call the delegated helper, self-mocking, domain rules in adapters/ports, domain objects outside `domain/`, dead APIs, weak public names, and functions that fail the plain-English read.
20
+ - **Project review rule packs:** apply a project review rule pack only when the task brief's `Source Material` or `Reporter Confirmations` cites its exact `SKILL.md` path. Read only that cited file and the `references/*.md` files it directly names. Do not search parent directories or host skill catalogs. Apply those rules during implementation as a prevention pass, not a PR-comment generation workflow: do not dispatch reviewer subagents from the executor. For Fonts Ninja-style PR review packs, the executor must avoid newly introduced duplicate helper stacks, tautological tests that merely re-call the delegated helper, self-mocking, domain rules in adapters/ports, domain objects outside `domain/`, dead APIs, weak public names, and functions that fail the plain-English read.
21
21
  - **Language-agnostic principles that ALWAYS bind (the TDD loop MUST satisfy them):** (1) no self-mocking of the SUT — stub/spy only injected collaborators, never the subject's own methods; (2) behavioral assertions on outcomes (return value, state, persisted rows, events, boundary calls) — never `toHaveBeenCalled*` on an internal helper as the only/primary assertion; (3) truthful names — a `get*` / `find*` that writes/inserts, or a name encoding the caller's use-case (`*ForInit`) or hiding a domain rule (`findValid*`), is a defect; (4) single-purpose functions ≤50 effective lines, plain-English readability. Self-mocking (1) — Enforced by `validators/detect_self_mock.py` (static); absent `qa/self-mock-*.json` sidecar BLOCKS at `validate-run.py`.
22
22
  - **Graceful degradation (codex / antigravity executor runtimes, or any runtime where the resolved coding-preflight pack files are absent or unreadable):** do NOT skip the gate — apply the agnostic principles above plus the project's own `CLAUDE.md` / `CONTRIBUTING` / formatter+lint config, and record `coding-conventions: resource-unavailable → applied <project rules + agnostic principles>` in the final report. Never claim a resource read that did not happen.
@@ -156,7 +156,7 @@ Re-running commands proves the diff *builds and passes*; it does NOT prove the d
156
156
 
157
157
  - **Scope (no silent sampling).** Enumerate every changed source/test file via `git diff --name-only <base>...HEAD` and review each one. Skipping a changed file silently is a `contract-violated` outcome. If a file's language has no reference and is not covered by the agnostic checks below, record `design-review skipped: <file> (language=<x> no reference)` — never pass it silently.
158
158
  - **Load the same conventions the executor used via the routed pack.** Use this worker prompt's `**Coding preflight pack:**` anchor header as the absolute path to the installed routed pack. Read `overview.md` first, then `clean-code.md`, then apply the router's three ordered stages: language, framework, architecture. In each stage, iterate every rule, treat a rule as matched when any listed condition is true, and accumulate every matching resource — including `frameworks/node-server.md` for server-side Node work and `architectures/hexagonal.md` for ports-and-adapters / NestJS-hex layouts. Degrade to the agnostic checks below when the resolved pack is unreadable, and record either `coding-conventions: resources=<...>` or `coding-conventions: resource-unavailable → applied <project rules + agnostic principles>`. The verifier does NOT inline language rules — it loads the same situation-specific resources as the executor preflight.
159
- - **Load project review rule packs when present.** Search the project root, `.claude/skills`, and up to two parent `skills/` directories for `*review*/SKILL.md` rule packs. Read their referenced `references/*.md` files and apply them as an overlay on this static review. If a premium review skill exists, use its coverage philosophy (recall-first enumeration followed by verify-only confirmation) as the verifier's mental model, but do NOT dispatch extra reviewer agents unless the task explicitly configured them. Record `project-review-rules: <paths read>` or `project-review-rules: none found` in the worker result.
159
+ - **Load brief-cited project review rule packs.** Apply a project review rule pack only when the task brief's `Source Material` or `Reporter Confirmations` cites its exact `SKILL.md` path. Read only that cited file and the `references/*.md` files it directly names. Do not search parent directories or host skill catalogs. Apply the cited rules as an overlay on this static review, but do NOT dispatch extra reviewer agents unless the task explicitly configured them. Record `project-review-rules: <paths read>` or `project-review-rules: none cited` in the worker result.
160
160
  - **Declared architecture style promotes the placement overlay from advisory to binding.** Read `<PROJECT_ROOT>/.okstra/project.json` — the same file Tier 2's `qaCommands` comes from — take `architecture.style`, and record `architecture-style: <hexagonal|layered|none>` in the worker result next to the `coding-conventions:` line. A declared `hexagonal` counts the overlay as loaded even when none of the router's Stage 3 layout signals matched, so the **Hexagonal** blocking check below applies in full, and the concrete-adapter injection listed under Advisory findings is promoted to a blocking finding → verdict `FAIL`, not a `should-fix`. A declared `layered` has no pack resource; its binding invariant is direction — an upper layer may import a lower one, never the reverse — so a changed file whose import list reaches back up a layer, or around a layer boundary, is a blocking placement violation cited `path:line` from that import list. The `layered` half is worker judgement: no machine check reads layer names, so a missed reverse dependency is a missed finding, not a validator failure. A `none` style, an absent field, or an unreadable `project.json` leaves this section exactly as it is today — Stage 3 stays detection-driven and the placement items stay advisory. **Enforced:** `scripts/okstra_project/resolver.py` `resolve_architecture` reads this same field for the planning-side rule in `validators/validate-run.py` `_validate_variation_point_analysis`, and `_validate_verifier_fail_blocks_verdict` (cited under the DB gate below) keeps the resulting `FAIL` from being dropped during synthesis.
161
161
  - **Blocking checks (any hit → verdict `FAIL`, cited `path:line` + rule name, recommended fix recorded — the verifier does NOT apply it):**
162
162
  - **New duplication / DRY:** two or more newly added or meaningfully modified blocks implement the same helper stack, transform, or domain rule. Literal copy-paste is always blocking; semantically equivalent transforms across services are blocking unless the approved plan explicitly justified keeping them separate. Recommend the shared module location.
@@ -20,7 +20,7 @@
20
20
  - **External Tier 3 de-duplication exception.** A DB/IO/SQL surface covered by an in-scope Tier 3 entry whose `requires` include `db`, `http`, or `external` is governed by the External QA outcome policy. Its non-PASS or unavailable result MUST NOT generate a second legacy db-test-not-configured or mock-only blocker solely for that same Tier 3 non-PASS or unavailable result. Tier 1 or Tier 2 failures remain blocking, and DB surfaces without declared external Tier 3 coverage remain blocking.
21
21
  - no new defects introduced — the diff does not break previously-working behaviour and adds no new bug (logic/off-by-one, null/empty handling, resource leaks, broken error paths)
22
22
  - scope conformance — the delivered diff stays within the approved plan's scope; flag out-of-scope edits, unrelated file changes, leftover debug/commented-out code, and unintended deletions
23
- - project review-rule packs (when present) — search `<PROJECT_ROOT>/skills/*review*`, `<PROJECT_ROOT>/.claude/skills/*review*`, and up to two parent directories' `skills/*review*/SKILL.md`; read the matching `SKILL.md` + referenced `references/*.md` and apply their rules as an acceptance overlay (record `project-review-rules: <paths read>` or `project-review-rules: none found`). This is a static review pass, not a PR-comment workflow — do NOT dispatch reviewer subagents. Because this phase verifies the **whole-task merged diff**, it is the gate that catches **cross-stage findings a per-stage `implementation` verifier structurally cannot see** (each implementation run reviews only its own stage diff): most importantly two cross-stage conditions: (a) the same helper stack / transform / domain rule duplicated across stages or services — byte-identical duplication is always an Acceptance Blocker, and semantically-equivalent transforms across services are blockers unless the approved plan explicitly justified keeping them separate; (b) an API newly orphaned because its only caller was removed in a different stage. A confirmed cross-stage duplication of this kind is an Acceptance Blocker (`major`+) that cites every `path:line` location and names the shared-module location to converge on. (Single-stage scope sees only one stage, so it cannot raise cross-stage findings — note that limitation rather than implying coverage.)
23
+ - project review-rule packs (when brief-cited) — apply a project review rule pack only when the task brief's `Source Material` or `Reporter Confirmations` cites its exact `SKILL.md` path; read only that cited file and the `references/*.md` files it directly names. Do not search parent directories or host skill catalogs. Apply the cited rules as an acceptance overlay (record `project-review-rules: <paths read>` or `project-review-rules: none cited`). This is a static review pass, not a PR-comment workflow — do NOT dispatch reviewer subagents. Because this phase verifies the **whole-task merged diff**, it is the gate that catches **cross-stage findings a per-stage `implementation` verifier structurally cannot see** (each implementation run reviews only its own stage diff): most importantly two cross-stage conditions: (a) the same helper stack / transform / domain rule duplicated across stages or services — byte-identical duplication is always an Acceptance Blocker, and semantically-equivalent transforms across services are blockers unless the approved plan explicitly justified keeping them separate; (b) an API newly orphaned because its only caller was removed in a different stage. A confirmed cross-stage duplication of this kind is an Acceptance Blocker (`major`+) that cites every `path:line` location and names the shared-module location to converge on. (Single-stage scope sees only one stage, so it cannot raise cross-stage findings — note that limitation rather than implying coverage.)
24
24
  - Residual-tracked — note as Residual Risk unless severe enough to block:
25
25
  - unresolved edge cases
26
26
  - regression risk in adjacent code paths not directly changed