okstra 0.151.0 → 0.151.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/cli.md +1 -1
- package/docs/project-structure-overview.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -1
- package/runtime/agents/workers/claude-worker.md +2 -1
- package/runtime/agents/workers/codex-worker.md +2 -1
- package/runtime/agents/workers/grok-worker.md +2 -1
- package/runtime/agents/workers/kimi-worker.md +2 -1
- package/runtime/agents/workers/report-writer-worker.md +1 -1
- package/runtime/bin/okstra-report-translate.py +32 -11
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/lead/convergence.md +16 -4
- package/runtime/prompts/lead/report-writer.md +26 -14
- package/runtime/prompts/lead/team-contract.md +2 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +8 -7
- package/runtime/python/okstra_ctl/analysis_packet.py +1 -0
- package/runtime/python/okstra_ctl/dispatch_state.py +5 -1
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +7 -0
- package/runtime/python/okstra_ctl/report_finalize.py +9 -4
- package/runtime/python/okstra_ctl/worker_prompt_body.py +4 -2
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +41 -1
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +3 -0
- package/runtime/templates/implementation-worker-preamble.md +12 -3
- package/runtime/templates/report-writer-prompt-preamble.md +5 -1
- package/runtime/templates/worker-prompt-preamble.md +12 -3
- package/runtime/validators/validate-run.py +111 -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
package/runtime/BUILD.json
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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**:
|
|
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
|
-
|
|
167
|
+
data = _load(data_path)
|
|
168
|
+
share, length = hangul_share(data)
|
|
169
|
+
failure = _english_source_failure(data_path, data)
|
|
143
170
|
payload = {
|
|
144
|
-
"ok":
|
|
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
|
|
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
|
|
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.
|
|
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`.
|
|
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
|
|
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.
|
|
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.
|
|
58
|
-
15.
|
|
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
|
-
|
|
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
|
|
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. **`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:**
|
|
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
|
|
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
|
|
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
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
- **Follow established patterns**: in existing codebases, conform to current conventions. Targeted cleanup of a file you are already modifying is acceptable; unrelated refactors are not.
|
|
43
43
|
- **Variation-point extraction (OCP)**: when the same behavior is served by two or more resources / implementations — stated in the brief, or foreseeable from a sibling task or the code you inspected — the plan MUST record it in `variationPointAnalysis` and include an option that extracts the variation point behind an interface (a port, or a strategy the next implementation plugs into), scored against the non-extracted option in the trade-off matrix. Penalize an option that branches on resource identity inside a service (one `if` / `switch` arm per implementation): adding the next implementation then means editing that same call site again, which is the closed-for-extension shape this principle exists to catch. This does not contradict YAGNI below: YAGNI drops *speculative* variation (a second implementation nobody named), while a behavior with two implementations already on the table is a present fact, not a forecast. **Enforced:** the `variationPointAnalysis` bullet under `Required deliverable shape` names the schema / validator / `P-Var-*` enforcement points.
|
|
44
44
|
- **YAGNI ruthlessly**: drop features, abstractions, and configuration knobs that do not serve the stated requirement.
|
|
45
|
-
- **Project review-rule preflight**:
|
|
45
|
+
- **Project review-rule preflight**: 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. Do not run the PR-review workflow here; extract only the rules. For Fonts Ninja-style TS/NestJS review packs, this means planning away known review findings before code exists: shared transforms instead of duplicate helper stacks, behavioral tests instead of collaborator-tautology assertions, domain rules in domain modules rather than repositories/adapters, domain objects under `domain/`, plain-English functions, truthful/specific names, and no dead APIs introduced by the plan.
|
|
46
46
|
- Expected output emphasis:
|
|
47
47
|
- feasible plan options
|
|
48
48
|
- dependency and risk visibility
|
|
@@ -80,11 +80,12 @@
|
|
|
80
80
|
- admissible — the answer selects between behaviours the code must implement, fixes a requirement the plan would otherwise satisfy incorrectly, or resolves a safety/data-integrity question.
|
|
81
81
|
- NOT admissible → use `Blocks=none` — QA-harness or tooling scope, report notation and wording, numbering or citation-range cleanup, anything inside an option the plan does not recommend, and anything the codebase answers (which the codebase-first rule already forbids raising at all). These belong in `## 5. Missing Information and Risks` or a Working Assumption; they are recorded, not gating.
|
|
82
82
|
- A row you would answer with "the plan would still produce the same code either way" is by construction `Blocks=none`.
|
|
83
|
-
-
|
|
84
|
-
- The
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
83
|
+
- Deliverable completeness contract (BLOCKING — the schema checks data keys, not heading strings):
|
|
84
|
+
- The plan lives in `data.json` under `implementationPlanning`, and `schemas/final-report-v2.0.schema.json` requires every one of these keys: `optionCandidates`, `tradeoffMatrix`, `recommendedOption`, `stageMap`, `stages`, `dependencyMigrationRisk`, `validationChecklist`, `rollbackStrategy`, `requirementCoverage`, `planBodyVerification`, `crossProjectDependencies`, `decisionDrafts`, `skippedAdrCandidates`, `variationPointAnalysis`, `userNarrative`. A missing block fails schema validation; there is nothing to satisfy by naming a heading. (Approval is not a body section — it is the YAML frontmatter `approved` field.)
|
|
85
|
+
- Each `stages[]` entry requires `stage`, `title`, `sliceValue`, `acceptance`, `carryIn`, `stepwiseExecution` (1–6 rows), `exitContract`, and `stageValidation`. Each `stageMap[]` row requires `stage`, `title`, `dependsOn`, `stepCount`, `exitContractSummary`.
|
|
86
|
+
- Beyond the schema, `validators/validate-run.py` reads the same data.json for `_validate_planning_conformance_declared`, `_validate_end_state_coverage`, `_validate_requirement_provenance`, `_validate_stage_has_requirement`, and `_validate_plan_body_state_file`. These run for every planning report regardless of schema version.
|
|
87
|
+
- **Do not chase English heading substrings.** `PLANNING_REQUIRED_SECTIONS` and the structural Stage Map scan (`validators/validate-implementation-plan-stages.py`) live inside `validate_phase_boundary`, which returns immediately when `schemaVersion == "2.0"` — they gate historical v1 Markdown only. The v2 AI-handoff template renders nine headings and serialises the plan as JSON beneath them, so those substrings cannot appear, and a report is not defective for lacking them.
|
|
88
|
+
- *Guideline, not gated:* write each `stepwiseExecution` row's `action` with a `RED:` / `GREEN:` prefix (or state a TDD exemption in `stageValidation`), and name the success / boundary / failure cases in `acceptance`. The v1 scan checked those tokens literally; v2 requires only that the fields be non-empty, so this is now review-enforced rather than validator-enforced.
|
|
88
89
|
- Required deliverable shape (final report, in addition to the standard sections):
|
|
89
90
|
- at least two implementation options. **Each option must include**:
|
|
90
91
|
- **File Structure**: an explicit list of files to create / modify / delete with each file's responsibility (one-line each). Use the form `Create: path — responsibility` / `Modify: path:line-range — change summary` / `Delete: path — reason`. Write every `path` in full and `<PROJECT_ROOT>`-relative — never ellipsis-abbreviated (`…` / `...` / a trailing `/…`); an abbreviated path does not resolve and is rejected by plan-body verification as a kind-b path mismatch.
|
|
@@ -188,7 +189,7 @@
|
|
|
188
189
|
3. **Internal consistency** — option file lists, trade-off matrix, and recommended step list must agree on file paths, names, and signatures. A symbol called `clearLayers()` in the matrix and `clearFullLayers()` in the steps is a bug.
|
|
189
190
|
4. **Ambiguity check** — any requirement that could be read two ways must be made explicit or moved to the `## 1. Clarification Items` table as a `Blocks=approval` row.
|
|
190
191
|
5. **Scope check** — if the recommended plan now spans multiple independent subsystems, recommend splitting into separate planning runs rather than shipping an oversized plan. Then walk the plan in the expansion direction: for every stage, name the Requirement Coverage row that demanded it, and for every requirement row, read its `Source` cell as a skeptic — does the cited brief heading actually exist, and does a `derived:` rationale state a real technical consequence rather than a preference? Move anything that fails to a `Blocks=approval` clarification row.
|
|
191
|
-
6. **Review-rule preflight check** — if a project review rule pack
|
|
192
|
+
6. **Review-rule preflight check** — if the task brief cites a project review rule pack under `Source Material` or `Reporter Confirmations`, map each relevant rule to the recommended option. Reject the draft if it knowingly creates a violation that the later PR reviewer would flag, unless the plan records a specific rationale and follow-up. In particular, scan for repeated helper stacks across planned files, tests that assert delegation to the same calculator/helper they exercise, public names that hide side effects, domain rules placed in repositories/adapters, and APIs made dead by this change.
|
|
192
193
|
7. **Plan-body verification reconciliation (BLOCKING for implementation-planning).** For every §5.5.9 `planItems[]` entry whose verdicts make it `majority-disagree`, set that item's `clarificationId` to a `C-<N>` row that MUST exist in `## 1. Clarification Items` with `Kind` chosen per the standard policy and `Blocks=approval`. **Enforced:** `validators/validate-run.py` `_validate_plan_body_clarification_matching` recomputes each item's class and fails when a majority-disagree item has no `clarificationId`, or its `clarificationId` is dangling / points at a non-`approval` row. For `partial-consensus` and `dissent-isolated` plan-items, the dissenting opinion lives in §5.5.9 `Dissent log` and is NOT promoted to §5.
|
|
193
194
|
8. **Stage Map self-check** — for every stage, count the effective rows of its `Stepwise Execution Order` table by hand; reject the draft if any stage exceeds 8. Confirm each stage declares a non-empty `Slice value:` and `Acceptance:` line, the three `Test case (success|boundary|failure):` lines (or carries a `TDD exemption:` line), and that its first step `action` starts with `RED:` with a later `GREEN:` — this is what validator S10 enforces, including S10d on the test-case lines. Read each stage's three test-case lines as a reviewer: reject any that restates the happy path in all three slots, leaves `boundary` blank, or writes `N/A` where a real edge input exists. Walk the `depends-on` graph and confirm it is a DAG (no cycle, no self-reference). For each `depends-on` link, confirm it encodes a real data/contract dependency — do NOT add links to serialise unrelated work, and do NOT split a stage merely to create more parallel stages. **Parallel-safety:** for every pair of `depends-on (none)` stages, confirm their `Stage Exit Contract` predicted file sets are disjoint; if they share a file, merge them or add a `depends-on` link (validator S9 rejects overlap). **Project-boundary:** confirm no stage mixes edits from two projects (different repo/`PROJECT_ROOT` or different top-level deployable module); if any stage does, split it per project. For multi-project plans, confirm each stage's `title` carries its `[<project>]` tag and the `Cross-project parallelism:` line under the table records the parallel-vs-sequenced determination (with the forcing dependency) for every project pair; for cross-repo work, confirm it is split into separate per-repo runs (required — one run structurally cannot touch another repo) rather than crammed into one task's stages.
|
|
194
195
|
9. **Cross-project dependency check** — confirm you have not missed a dependency on another repo / another top-level deployable module / a published package. If `dependencyMigrationRisk` has a `kind: cross-project` row, confirm a matching `direction: upstream-precondition` `XP-NNN` row exists in `crossProjectDependencies`, and re-read as a reviewer whether its `requiredWork` is the concrete work the other side must actually build rather than an abstract phrase ("other side's work done") — validator S only checks existence, so concreteness is the self-review's responsibility. Confirm cross-repo work is split into a separate run + XP row instead of being crammed into one task's stages, and that the cross-project substance is not duplicated in `§3 Recommended Next Steps` but lives only in `§5.4 Cross-Project Dependencies`.
|
|
@@ -275,7 +275,11 @@ def validate_initial_prompts(
|
|
|
275
275
|
PromptRecord(job.worker_id, job.dispatch_kind, job.prompt_path)
|
|
276
276
|
for job in jobs
|
|
277
277
|
]
|
|
278
|
-
errors = validate_initial_prompt_records(
|
|
278
|
+
errors = validate_initial_prompt_records(
|
|
279
|
+
manifest=manifest,
|
|
280
|
+
records=records,
|
|
281
|
+
require_evidence_ledger=True,
|
|
282
|
+
)
|
|
279
283
|
if errors:
|
|
280
284
|
task_type = require_string(manifest, "taskType")
|
|
281
285
|
raise DispatchError(f"{task_type} prompt contract: " + "; ".join(errors))
|
|
@@ -521,6 +521,7 @@ def _validate_prepublication_set(
|
|
|
521
521
|
errors = validate_initial_prompt_records(
|
|
522
522
|
manifest=context.manifest,
|
|
523
523
|
records=[record],
|
|
524
|
+
require_evidence_ledger=True,
|
|
524
525
|
)
|
|
525
526
|
if errors:
|
|
526
527
|
raise InitialPromptMaterializationError(
|
|
@@ -530,6 +531,7 @@ def _validate_prepublication_set(
|
|
|
530
531
|
errors = validate_initial_prompt_records(
|
|
531
532
|
manifest=context.manifest,
|
|
532
533
|
records=records,
|
|
534
|
+
require_evidence_ledger=True,
|
|
533
535
|
)
|
|
534
536
|
if errors:
|
|
535
537
|
raise InitialPromptMaterializationError(
|
|
@@ -577,6 +579,7 @@ def _validate_existing_prompt(
|
|
|
577
579
|
basic_errors = validate_initial_prompt_records(
|
|
578
580
|
manifest=context.manifest,
|
|
579
581
|
records=[basic_record],
|
|
582
|
+
require_evidence_ledger=True,
|
|
580
583
|
)
|
|
581
584
|
if basic_errors:
|
|
582
585
|
raise InitialPromptMaterializationError(
|
|
@@ -593,6 +596,7 @@ def _validate_existing_prompt(
|
|
|
593
596
|
expected_errors = validate_initial_prompt_records(
|
|
594
597
|
manifest=context.manifest,
|
|
595
598
|
records=[expected_record],
|
|
599
|
+
require_evidence_ledger=True,
|
|
596
600
|
)
|
|
597
601
|
if expected_errors:
|
|
598
602
|
reason = _existing_metadata_reason(expected_errors)
|
|
@@ -765,6 +769,7 @@ def _validate_published_set(
|
|
|
765
769
|
errors = validate_initial_prompt_records(
|
|
766
770
|
manifest=context.manifest,
|
|
767
771
|
records=[record],
|
|
772
|
+
require_evidence_ledger=True,
|
|
768
773
|
)
|
|
769
774
|
if errors:
|
|
770
775
|
raise InitialPromptMaterializationError(
|
|
@@ -774,6 +779,7 @@ def _validate_published_set(
|
|
|
774
779
|
errors = validate_initial_prompt_records(
|
|
775
780
|
manifest=context.manifest,
|
|
776
781
|
records=records,
|
|
782
|
+
require_evidence_ledger=True,
|
|
777
783
|
)
|
|
778
784
|
if errors:
|
|
779
785
|
raise InitialPromptMaterializationError(
|
|
@@ -983,6 +989,7 @@ def _validate_existing_record_subset(
|
|
|
983
989
|
errors = validate_initial_prompt_records(
|
|
984
990
|
manifest=manifest,
|
|
985
991
|
records=records,
|
|
992
|
+
require_evidence_ledger=True,
|
|
986
993
|
)
|
|
987
994
|
if errors:
|
|
988
995
|
raise InitialPromptMaterializationError(
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"""Phase 7 report post-processing — the single reference point.
|
|
2
2
|
|
|
3
3
|
Phase 7 turns a Phase 6 final-report data.json into shippable artifacts through
|
|
4
|
-
|
|
5
|
-
spawning, and run validation. The order is
|
|
6
|
-
substitution ships `--` token cells, and
|
|
7
|
-
report-views contract.
|
|
4
|
+
five ordered steps: English-SSOT verification, usage substitution, html view
|
|
5
|
+
rendering, follow-up task spawning, and run validation. The order is
|
|
6
|
+
load-bearing — rendering before substitution ships `--` token cells, and
|
|
7
|
+
validating before rendering trips the report-views contract.
|
|
8
|
+
|
|
9
|
+
The translation sidecar is NOT one of these steps. `render-views` overlays it,
|
|
10
|
+
so a non-English run dispatches the translator before this sequence starts —
|
|
11
|
+
after verifying the data.json is English, which is why `check-source` is also
|
|
12
|
+
available as a standalone command.
|
|
8
13
|
|
|
9
14
|
Every lead adapter drives Phase 7 through this module: the Codex adapter calls
|
|
10
15
|
it in-process (``codex_dispatch``), and a Claude-led run reaches the same code
|
|
@@ -128,6 +128,7 @@ def report_writer_input_lines(
|
|
|
128
128
|
team_state: Mapping[str, Any],
|
|
129
129
|
) -> list[str]:
|
|
130
130
|
inputs = [
|
|
131
|
+
("Analysis packet", instruction_path(manifest, active_context, "analysisPacketPath")),
|
|
131
132
|
("Task brief", instruction_path(manifest, active_context, "taskBriefPath")),
|
|
132
133
|
("Analysis profile", instruction_path(manifest, active_context, "analysisProfilePath")),
|
|
133
134
|
("Analysis material", instruction_path(manifest, active_context, "analysisMaterialPath")),
|
|
@@ -193,8 +194,9 @@ def run_path(
|
|
|
193
194
|
|
|
194
195
|
def mcp_pointer_line() -> str:
|
|
195
196
|
return (
|
|
196
|
-
'**MCP servers:** follow the
|
|
197
|
-
"section
|
|
197
|
+
'**MCP servers:** follow the analysis packet\'s "Available MCP Servers" '
|
|
198
|
+
"section. If the section is absent or says none, treat MCP as unavailable "
|
|
199
|
+
"for this run; never infer tools from host configuration."
|
|
198
200
|
)
|
|
199
201
|
|
|
200
202
|
|
|
@@ -11,6 +11,7 @@ from .worker_prompt_policy import (
|
|
|
11
11
|
PromptPlan,
|
|
12
12
|
resolve_prompt_plan_for_manifest,
|
|
13
13
|
)
|
|
14
|
+
from .worker_prompt_headers import EVIDENCE_LEDGER_HEADER
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
MAX_FINAL_VERIFICATION_DIRECTIVE_LINES = 40
|
|
@@ -24,6 +25,7 @@ FORBIDDEN_ACTIONS_HEADER = "**Forbidden actions:**"
|
|
|
24
25
|
|
|
25
26
|
_DIRECTIVE_HEADING = "## Run-specific directive"
|
|
26
27
|
_WORKER_ERROR_CONTRACT_HEADER = "**Worker Error Contract Path:**"
|
|
28
|
+
_EVIDENCE_LEDGER_HEADER_PREFIX = "**Evidence ledger:**"
|
|
27
29
|
_PRIMARY_PACKET_RE = re.compile(
|
|
28
30
|
r"(?im)^-\s+Primary analysis packet:\s+`[^`\n]*analysis-packet\.md`\s*$"
|
|
29
31
|
)
|
|
@@ -45,6 +47,7 @@ _NON_BODY_PREFIXES = (
|
|
|
45
47
|
"**Audit sidecar path:**",
|
|
46
48
|
"Assigned worker prompt history path:",
|
|
47
49
|
"**Worker Preamble Path:**",
|
|
50
|
+
_EVIDENCE_LEDGER_HEADER_PREFIX,
|
|
48
51
|
*ERRORS_PATH_HEADERS,
|
|
49
52
|
"**Read scope:**",
|
|
50
53
|
"**File write mode:**",
|
|
@@ -239,8 +242,13 @@ def validate_initial_prompt_records(
|
|
|
239
242
|
*,
|
|
240
243
|
manifest: Mapping[str, Any],
|
|
241
244
|
records: Sequence[PromptRecord],
|
|
245
|
+
require_evidence_ledger: bool = False,
|
|
242
246
|
) -> list[str]:
|
|
243
|
-
"""Validate prompt audiences and compare their normalized equality groups.
|
|
247
|
+
"""Validate prompt audiences and compare their normalized equality groups.
|
|
248
|
+
|
|
249
|
+
Newly published prompts opt into the evidence-ledger requirement. Persisted
|
|
250
|
+
historical prompts still validate under the contract they were written with.
|
|
251
|
+
"""
|
|
244
252
|
errors: list[str] = []
|
|
245
253
|
equality_groups: dict[str, dict[str, str]] = {}
|
|
246
254
|
for record in records:
|
|
@@ -262,6 +270,15 @@ def validate_initial_prompt_records(
|
|
|
262
270
|
f"{record.worker_id}: {error}"
|
|
263
271
|
for error in _validate_record_metadata(text, record)
|
|
264
272
|
)
|
|
273
|
+
errors.extend(
|
|
274
|
+
f"{record.worker_id}: {error}"
|
|
275
|
+
for error in _validate_evidence_ledger_header(
|
|
276
|
+
text,
|
|
277
|
+
plan,
|
|
278
|
+
record.dispatch_kind,
|
|
279
|
+
required=require_evidence_ledger,
|
|
280
|
+
)
|
|
281
|
+
)
|
|
265
282
|
if plan.equality_group:
|
|
266
283
|
group = equality_groups.setdefault(plan.equality_group, {})
|
|
267
284
|
group[record.worker_id] = text
|
|
@@ -289,6 +306,29 @@ def _validate_record_metadata(text: str, record: PromptRecord) -> list[str]:
|
|
|
289
306
|
return errors
|
|
290
307
|
|
|
291
308
|
|
|
309
|
+
def _validate_evidence_ledger_header(
|
|
310
|
+
text: str,
|
|
311
|
+
plan: PromptPlan,
|
|
312
|
+
dispatch_kind: str,
|
|
313
|
+
*,
|
|
314
|
+
required: bool,
|
|
315
|
+
) -> list[str]:
|
|
316
|
+
values = _header_values(text, _EVIDENCE_LEDGER_HEADER_PREFIX)
|
|
317
|
+
if dispatch_kind != "initial":
|
|
318
|
+
return []
|
|
319
|
+
if plan.audience == "report-writer":
|
|
320
|
+
if values:
|
|
321
|
+
return ["Evidence ledger header is forbidden for report-writer"]
|
|
322
|
+
return []
|
|
323
|
+
if not values:
|
|
324
|
+
if required:
|
|
325
|
+
return [f"exactly one `{EVIDENCE_LEDGER_HEADER}` header is required"]
|
|
326
|
+
return []
|
|
327
|
+
if values != ["required-v1"]:
|
|
328
|
+
return [f"exactly one `{EVIDENCE_LEDGER_HEADER}` header is required"]
|
|
329
|
+
return []
|
|
330
|
+
|
|
331
|
+
|
|
292
332
|
def _validate_delivery_mode(
|
|
293
333
|
values: list[str],
|
|
294
334
|
expected: str | None,
|
|
@@ -36,6 +36,7 @@ READ_SCOPE_HEADER = (
|
|
|
36
36
|
"un-enumerated file seems essential, record it under *Missing Information "
|
|
37
37
|
"or Assumptions* instead of reading it."
|
|
38
38
|
)
|
|
39
|
+
EVIDENCE_LEDGER_HEADER = "**Evidence ledger:** required-v1"
|
|
39
40
|
|
|
40
41
|
# `agy`'s write tool validates the target against the Gemini artifact store
|
|
41
42
|
# whenever the model attaches ArtifactMetadata, and rejects every path outside
|
|
@@ -101,6 +102,8 @@ def worker_prompt_headers(
|
|
|
101
102
|
headers.append(
|
|
102
103
|
f"**Coding preflight pack:** {_coding_preflight_pack_path(active_context)}"
|
|
103
104
|
)
|
|
105
|
+
if dispatch_kind == "initial" and plan.audience != "report-writer":
|
|
106
|
+
headers.append(EVIDENCE_LEDGER_HEADER)
|
|
104
107
|
headers.extend([
|
|
105
108
|
f"**Errors log path:** {errors_log_path}",
|
|
106
109
|
f"**Errors sidecar path:** {errors_sidecar_path}",
|
|
@@ -25,6 +25,14 @@ Work like a senior engineer who owns this result, not a commentator on it.
|
|
|
25
25
|
- Only the executor may mutate source files. Verifiers remain read-only except for okstra result/audit artifacts and project-declared QA commands.
|
|
26
26
|
- Execute the role sidecar's pre-write, post-write, QA, and return gates without substituting analysis Sections 1–6 or report-authoring instructions.
|
|
27
27
|
|
|
28
|
+
## Evidence read ledger
|
|
29
|
+
|
|
30
|
+
When `**Evidence ledger:** required-v1` is present, append one canonical row to the audit sidecar immediately after opening every file used as claim evidence:
|
|
31
|
+
|
|
32
|
+
- Evidence read: `<project-relative path without a line suffix>`
|
|
33
|
+
|
|
34
|
+
Every file citation in the result MUST use backticks and a line suffix, for example `src/config/env.ts:1-22`. A cited path without a matching ledger row fails Phase 7 in `validators/validate-run.py` `validate_worker_results_audit()`. Do not add a row for a file you did not open.
|
|
35
|
+
|
|
28
36
|
## Anchor headers
|
|
29
37
|
|
|
30
38
|
Every initial implementation prompt begins with these generated common anchors in this exact order, before its implementation-specific body:
|
|
@@ -36,9 +44,10 @@ Every initial implementation prompt begins with these generated common anchors i
|
|
|
36
44
|
5. `**Worker Preamble Path:** <absolute-path>`
|
|
37
45
|
6. `**Worker Error Contract Path:** <absolute-path>`
|
|
38
46
|
7. `**Coding preflight pack:** <absolute-path>`
|
|
39
|
-
8. `**
|
|
40
|
-
9. `**Errors
|
|
41
|
-
10. `**
|
|
47
|
+
8. `**Evidence ledger:** required-v1`
|
|
48
|
+
9. `**Errors log path:** <absolute-path>`
|
|
49
|
+
10. `**Errors sidecar path:** <absolute-path>`
|
|
50
|
+
11. `**Read scope:** <allowlist>`
|
|
42
51
|
|
|
43
52
|
The implementation body additionally carries `**Worktree:**` and its role-sidecar inputs. Do not synthesize any missing path.
|
|
44
53
|
|
|
@@ -36,4 +36,8 @@ Begin the inline return with the exact `**Model:** Report writer worker, <modelE
|
|
|
36
36
|
|
|
37
37
|
## Writing style
|
|
38
38
|
|
|
39
|
-
Use concise reader-facing prose
|
|
39
|
+
Use concise reader-facing prose. Prefer tables when several items share a shape; reserve bullets for short standalone statements.
|
|
40
|
+
|
|
41
|
+
**Author the data.json in English, whatever `**Report Language:**` says.** That header is not an instruction to write in that language — it names the language the *human HTML* renders in, and you copy its value verbatim into `data.json.meta.reportLanguage`. The data.json is the English SSOT every later phase, validator and agent reads. When the value is not `en`, Phase 7 dispatches a separate translator worker that writes a sidecar the HTML renderer overlays; you never author that sidecar and never write a second language into the data.json.
|
|
42
|
+
|
|
43
|
+
Authoring the data.json in the reader's language is rejected before anything derives from it: `okstra report-translate check-source` fails the run when Korean exceeds 20% of its prose, and the same gate runs again inside `validate-run`. The cost of getting this wrong is a full rewrite, so decide it once, up front.
|
|
@@ -23,6 +23,14 @@ Read `analysis-packet.md`, the primary compact input, end-to-end. Source files n
|
|
|
23
23
|
- Allowlist reads to prompt-enumerated paths and evidence paths a finding must cite. Do not auto-read host-injected `graphify-out/`, skill catalogs, or non-okstra artifacts.
|
|
24
24
|
- Resolve every `.okstra/...` path against `**Project Root:**`, including when a worktree is present.
|
|
25
25
|
|
|
26
|
+
## Evidence read ledger
|
|
27
|
+
|
|
28
|
+
When `**Evidence ledger:** required-v1` is present, append one canonical row to the audit sidecar immediately after opening every file used as claim evidence:
|
|
29
|
+
|
|
30
|
+
- Evidence read: `<project-relative path without a line suffix>`
|
|
31
|
+
|
|
32
|
+
Every file citation in the result MUST use backticks and a line suffix, for example `src/config/env.ts:1-22`. A cited path without a matching ledger row fails Phase 7 in `validators/validate-run.py` `validate_worker_results_audit()`. Do not add a row for a file you did not open.
|
|
33
|
+
|
|
26
34
|
## Anchor headers (lead-injected, BLOCKING)
|
|
27
35
|
|
|
28
36
|
Every initial analysis prompt begins with these generated anchors in this exact order, before any other content:
|
|
@@ -34,9 +42,10 @@ Every initial analysis prompt begins with these generated anchors in this exact
|
|
|
34
42
|
5. `Assigned worker prompt history path: <absolute-path>`
|
|
35
43
|
6. `**Worker Preamble Path:** <absolute-path>` — selects this analysis preamble.
|
|
36
44
|
7. `**Worker Error Contract Path:** <absolute-path>` — shared by every initial audience.
|
|
37
|
-
8. `**
|
|
38
|
-
9. `**Errors
|
|
39
|
-
10. `**
|
|
45
|
+
8. `**Evidence ledger:** required-v1`
|
|
46
|
+
9. `**Errors log path:** <absolute-path>`
|
|
47
|
+
10. `**Errors sidecar path:** <absolute-path>`
|
|
48
|
+
11. `**Read scope:** <allowlist>`
|
|
40
49
|
|
|
41
50
|
`final-verification` additionally carries its six verification-target anchors. `improvement-discovery` carries `**Phase 1.5 Grilling Log:**`. Reverify prompts are lightweight and do not use this preamble.
|
|
42
51
|
|
|
@@ -103,6 +103,7 @@ from okstra_ctl.worker_prompt_contract import ( # noqa: E402
|
|
|
103
103
|
PromptRecord,
|
|
104
104
|
validate_initial_prompt_records,
|
|
105
105
|
)
|
|
106
|
+
from okstra_ctl.worker_prompt_headers import EVIDENCE_LEDGER_HEADER # noqa: E402
|
|
106
107
|
from validate_analysis_report import validate_analysis_report # noqa: E402
|
|
107
108
|
from okstra_ctl.convergence_engine import ( # noqa: E402
|
|
108
109
|
grouped_input_digest,
|
|
@@ -2378,6 +2379,16 @@ def validate_report(
|
|
|
2378
2379
|
_WORKER_RESULT_BASENAME_RE = re.compile(
|
|
2379
2380
|
r"^(?P<worker>[a-z][a-z0-9-]*-worker)-(?P<task_type>[a-z][a-z-]*?)-(?P<seq>\d{3})\.md$"
|
|
2380
2381
|
)
|
|
2382
|
+
_EVIDENCE_READ_RE = re.compile(
|
|
2383
|
+
r"^- Evidence read: `(?P<path>[^`\n]+)`\s*$",
|
|
2384
|
+
re.MULTILINE,
|
|
2385
|
+
)
|
|
2386
|
+
_FILE_LINE_CITATION_RE = re.compile(
|
|
2387
|
+
r"`(?P<path>(?!https?://)[^`\n]+?):(?P<line>\d+(?:-\d+)?)`"
|
|
2388
|
+
)
|
|
2389
|
+
_EXTENSIONLESS_SOURCE_FILENAMES = frozenset(
|
|
2390
|
+
{"Dockerfile", "Justfile", "Makefile", "Procfile", "Rakefile"}
|
|
2391
|
+
)
|
|
2381
2392
|
|
|
2382
2393
|
_REPORT_BASENAME_SEQ_RE = re.compile(r"-(?P<seq>\d{3})(?:\.data)?\.(?:md|json)$")
|
|
2383
2394
|
|
|
@@ -2390,6 +2401,90 @@ def _report_run_seq(report_path: Path) -> str | None:
|
|
|
2390
2401
|
return match.group("seq") if match else None
|
|
2391
2402
|
|
|
2392
2403
|
|
|
2404
|
+
def _cited_file_paths(content: str) -> set[str]:
|
|
2405
|
+
paths: set[str] = set()
|
|
2406
|
+
for match in _FILE_LINE_CITATION_RE.finditer(content):
|
|
2407
|
+
path = match.group("path")
|
|
2408
|
+
if _looks_like_file_path(path):
|
|
2409
|
+
paths.add(path)
|
|
2410
|
+
return paths
|
|
2411
|
+
|
|
2412
|
+
|
|
2413
|
+
def _looks_like_file_path(path: str) -> bool:
|
|
2414
|
+
if (
|
|
2415
|
+
not path
|
|
2416
|
+
or path.startswith(("-", "$"))
|
|
2417
|
+
or any(char.isspace() for char in path)
|
|
2418
|
+
):
|
|
2419
|
+
return False
|
|
2420
|
+
if re.fullmatch(r"[0-9a-fA-F]{7,64}", path):
|
|
2421
|
+
return False
|
|
2422
|
+
return (
|
|
2423
|
+
"/" in path
|
|
2424
|
+
or "." in Path(path).name
|
|
2425
|
+
or Path(path).name in _EXTENSIONLESS_SOURCE_FILENAMES
|
|
2426
|
+
)
|
|
2427
|
+
|
|
2428
|
+
|
|
2429
|
+
def _audit_evidence_read_paths(content: str) -> set[str]:
|
|
2430
|
+
return {
|
|
2431
|
+
match.group("path")
|
|
2432
|
+
for match in _EVIDENCE_READ_RE.finditer(content)
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
|
|
2436
|
+
def _worker_prompt_path(
|
|
2437
|
+
report_path: Path,
|
|
2438
|
+
worker_role: str,
|
|
2439
|
+
task_type: str,
|
|
2440
|
+
seq: str,
|
|
2441
|
+
) -> Path:
|
|
2442
|
+
return (
|
|
2443
|
+
report_path.parent.parent
|
|
2444
|
+
/ "prompts"
|
|
2445
|
+
/ f"{worker_role}-prompt-{task_type}-{seq}.md"
|
|
2446
|
+
)
|
|
2447
|
+
|
|
2448
|
+
|
|
2449
|
+
def _validate_worker_evidence_read_ledger(
|
|
2450
|
+
*,
|
|
2451
|
+
report_path: Path,
|
|
2452
|
+
worker_role: str,
|
|
2453
|
+
task_type: str,
|
|
2454
|
+
seq: str,
|
|
2455
|
+
result_name: str,
|
|
2456
|
+
result_content: str,
|
|
2457
|
+
audit_path: Path,
|
|
2458
|
+
failures: list[str],
|
|
2459
|
+
) -> None:
|
|
2460
|
+
if worker_role == "report-writer-worker":
|
|
2461
|
+
return
|
|
2462
|
+
prompt_path = _worker_prompt_path(report_path, worker_role, task_type, seq)
|
|
2463
|
+
try:
|
|
2464
|
+
prompt_content = prompt_path.read_text(encoding="utf-8")
|
|
2465
|
+
except OSError:
|
|
2466
|
+
return
|
|
2467
|
+
if EVIDENCE_LEDGER_HEADER not in prompt_content.splitlines():
|
|
2468
|
+
return
|
|
2469
|
+
try:
|
|
2470
|
+
audit_content = audit_path.read_text(encoding="utf-8")
|
|
2471
|
+
except OSError as exc:
|
|
2472
|
+
failures.append(
|
|
2473
|
+
f"worker audit sidecar unreadable: {audit_path.name} ({exc})"
|
|
2474
|
+
)
|
|
2475
|
+
return
|
|
2476
|
+
|
|
2477
|
+
missing_paths = sorted(
|
|
2478
|
+
_cited_file_paths(result_content) - _audit_evidence_read_paths(audit_content)
|
|
2479
|
+
)
|
|
2480
|
+
for missing_path in missing_paths:
|
|
2481
|
+
failures.append(
|
|
2482
|
+
f"worker `{worker_role}` result `{result_name}` cites "
|
|
2483
|
+
f"`{missing_path}:line` without an Evidence read row for "
|
|
2484
|
+
f"`{missing_path}` in `{audit_path.name}`"
|
|
2485
|
+
)
|
|
2486
|
+
|
|
2487
|
+
|
|
2393
2488
|
def validate_worker_results_audit(
|
|
2394
2489
|
report_path: Path, task_type: str, failures: list[str]
|
|
2395
2490
|
) -> None:
|
|
@@ -2404,6 +2499,10 @@ def validate_worker_results_audit(
|
|
|
2404
2499
|
2. The matching audit sidecar exists at
|
|
2405
2500
|
`<worker>-audit-<task-type>-<seq>.md`. Missing sidecar means the
|
|
2406
2501
|
worker silently skipped the reading-confirmation step.
|
|
2502
|
+
3. For new prompts carrying the required-v1 marker, every canonical
|
|
2503
|
+
backticked `path:line` citation has a matching Evidence read row in
|
|
2504
|
+
that audit sidecar. Historical prompts without the marker retain the
|
|
2505
|
+
existence-only contract.
|
|
2407
2506
|
|
|
2408
2507
|
Scoped to this run's seq. `worker-results/` accumulates every run's
|
|
2409
2508
|
artifacts, so scanning the whole directory judged a run by files it did
|
|
@@ -2465,6 +2564,18 @@ def validate_worker_results_audit(
|
|
|
2465
2564
|
f"Confirmation block (one short line per input file). Workers "
|
|
2466
2565
|
f"write this in the same step as the main worker-results file."
|
|
2467
2566
|
)
|
|
2567
|
+
continue
|
|
2568
|
+
|
|
2569
|
+
_validate_worker_evidence_read_ledger(
|
|
2570
|
+
report_path=report_path,
|
|
2571
|
+
worker_role=worker_role,
|
|
2572
|
+
task_type=task_type,
|
|
2573
|
+
seq=seq,
|
|
2574
|
+
result_name=rel,
|
|
2575
|
+
result_content=content,
|
|
2576
|
+
audit_path=audit_path,
|
|
2577
|
+
failures=failures,
|
|
2578
|
+
)
|
|
2468
2579
|
|
|
2469
2580
|
|
|
2470
2581
|
def validate_team_state_usage(team_state: dict, failures: list[str]) -> None:
|