okstra 0.125.6 → 0.128.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/architecture/storage-model.md +10 -3
- package/docs/architecture.md +14 -2
- package/docs/cli.md +1 -0
- package/docs/project-structure-overview.md +9 -6
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +1 -1
- package/runtime/bin/okstra-antigravity-exec.sh +8 -2
- package/runtime/bin/okstra-claude-exec.sh +8 -2
- package/runtime/bin/okstra-codex-exec.sh +65 -8
- package/runtime/prompts/lead/adapters/claude-code.md +1 -0
- package/runtime/prompts/lead/convergence.md +8 -1
- package/runtime/prompts/lead/okstra-lead-contract.md +26 -1
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/lead/team-contract.md +14 -3
- package/runtime/prompts/profiles/final-verification.md +10 -6
- package/runtime/python/okstra_ctl/analysis_packet.py +7 -2
- package/runtime/python/okstra_ctl/codex_dispatch.py +27 -1
- package/runtime/python/okstra_ctl/design_surfaces.py +30 -14
- package/runtime/python/okstra_ctl/dispatch_core.py +20 -0
- package/runtime/python/okstra_ctl/log_report.py +44 -5
- package/runtime/python/okstra_ctl/path_hints.py +5 -0
- package/runtime/python/okstra_ctl/render.py +22 -0
- package/runtime/python/okstra_ctl/render_final_report.py +71 -5
- package/runtime/python/okstra_ctl/run.py +69 -3
- package/runtime/python/okstra_ctl/schema_excerpt.py +17 -1
- package/runtime/python/okstra_ctl/wizard.py +15 -4
- package/runtime/python/okstra_ctl/work_categories.py +37 -0
- package/runtime/python/okstra_ctl/worker_heartbeat.py +50 -0
- package/runtime/python/okstra_ctl/worker_liveness.py +140 -0
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +223 -0
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +51 -2
- package/runtime/templates/reports/final-report.template.md +9 -9
- package/runtime/templates/worker-prompt-preamble.md +13 -5
- package/runtime/validators/forbidden_actions.py +43 -4
- package/runtime/validators/validate-implementation-plan-stages.py +6 -4
- package/runtime/validators/validate-report-views.py +4 -4
- package/runtime/validators/validate-run.py +61 -6
- package/runtime/validators/validate-schedule.py +6 -4
- package/runtime/validators/validate_fanout.py +4 -3
- package/runtime/validators/validate_improvement_report.py +6 -4
- package/runtime/validators/validate_session_conformance.py +16 -7
- package/src/cli-registry.mjs +7 -0
- package/src/commands/inspect/log-report.mjs +5 -3
- package/src/commands/inspect/worker-liveness.mjs +31 -0
- package/src/commands/lifecycle/preflight.mjs +25 -2
- package/src/lib/helper-scripts.mjs +23 -0
- package/src/lib/python-helper.mjs +1 -1
|
@@ -21,6 +21,7 @@ The task manifest, task index, instruction set, runs, and history are collected
|
|
|
21
21
|
│ ├── analysis-material.md
|
|
22
22
|
│ ├── reference-expectations.md
|
|
23
23
|
│ ├── task-brief.md
|
|
24
|
+
│ ├── verification-target.md # final-verification only; canonical target snapshot
|
|
24
25
|
│ ├── directive.txt # optional (mirrors --directive)
|
|
25
26
|
│ ├── final-report-schema.json
|
|
26
27
|
│ ├── final-report-template.md
|
|
@@ -106,6 +107,8 @@ The resolved run directory collects execution history. It divides its contents i
|
|
|
106
107
|
Worker prompt history is retained not under `/tmp`, but always as a canonical artifact under `prompts/` for the current run.
|
|
107
108
|
Unlike before, `analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, skill copies, and `final-report-template.md` are not duplicated for every run.
|
|
108
109
|
These materials retain canonical copies in `instruction-set/` under the stable task root.
|
|
110
|
+
For `final-verification`, `verification-target.md` is also stable-task instruction-set state. Prepare owns the snapshot and stores its project-relative path plus SHA-256 digest in task/run manifests and active-run context. Initial analysis prompts carry the compact worktree/scope/base/head/path/digest identity and read this sidecar on demand instead of copying its diff stat.
|
|
111
|
+
Each wrapper `<prompt>.log` beside a persisted `<prompt>.md` is a growing execution transcript, not another prompt snapshot. `okstra log-report` pairs the siblings and reports prompt/transcript bytes separately while keeping the legacy log-oriented `sizeBytes` and `totalBytes` fields.
|
|
109
112
|
When the same non-stage task type runs again, it reuses the same `runs/<task-type>/` run directory. Reruns of `implementation` and single-stage `final-verification` reuse their resolved `runs/<task-type>/stage-<N>/` run directories. In both cases, existing artifacts are not overwritten because run-level filenames are separated by `-<task-type>-<seq>` suffixes within type-specific subdirectories. Because `<seq>` is scanned independently for each category directory (`manifests/`, `prompts/`, `reports/`, `status/`, `state/`, `sessions/`, `worker-results/`) beneath the resolved run directory, values may differ by category even within the same run.
|
|
110
113
|
If flat legacy artifacts remain at the top level of a task-type run directory, the next execution automatically moves them into the corresponding type-specific subdirectories.
|
|
111
114
|
|
|
@@ -262,6 +265,7 @@ Main contents:
|
|
|
262
265
|
- expected report relative path
|
|
263
266
|
- expected status relative path
|
|
264
267
|
- prompt snapshot relative path
|
|
268
|
+
- final-verification target snapshot relative path and digest (empty for other task types)
|
|
265
269
|
- `worker prompt directory relative path`
|
|
266
270
|
- `worker prompt relative path by worker id`
|
|
267
271
|
- current run status
|
|
@@ -299,14 +303,17 @@ After `okstra` runs, Claude should follow this default sequence when reading the
|
|
|
299
303
|
3. Read `task-manifest.json`.
|
|
300
304
|
4. If the current `state/active-run-context-<task-type>-<seq>.json` exists, read it as the primary input for lead Phase 1. This file is a compact intake that reconstructs run artifact paths from `identity` + `pathHints` clues. If it does not exist, fall back to the current `manifests/run-manifest-<task-type>-<seq>.json` and `team-state`.
|
|
301
305
|
5. Read `instruction-set/analysis-profile.md` and `instruction-set/analysis-packet.md`.
|
|
306
|
+
For final-verification, use the compact target identity from active context and open `instruction-set/verification-target.md` only when its full mapping or diff stat is needed.
|
|
302
307
|
6. Read `task-index.md` only when a quick summary is needed.
|
|
303
308
|
7. Lazily read `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, and `final-report-template.md` when the packet is insufficient or when source citations/report writing require them.
|
|
304
309
|
8. Consult `history/timeline.json` and previous run results if necessary.
|
|
305
310
|
9. As `Claude lead`, organize roles according to the current run's worker roster (`Claude worker`, `Codex worker`, and `Report writer worker` by default; `Antigravity worker` only when explicitly included).
|
|
306
311
|
10. Save each selected worker prompt under the current run's `prompts/` directory at the assigned worker prompt history path before dispatching the worker.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
312
|
+
Every selected final-verification initial analysis worker receives the same full-core semantic body. Dispatch and Phase 7 validate compactness and normalized equality; report-writer and `-reverify-r<N>-` prompts are separate audiences.
|
|
313
|
+
11. In convergence Round 0, classify matching semantic/ticket-set findings as full consensus and queue only single-worker findings. Reverify each queued finding through other workers only; lightweight reverify does not reload the original bundle.
|
|
314
|
+
12. Collect a result or terminal status for each required worker.
|
|
315
|
+
13. Unless the brief requires a more specific format, write the final Markdown report using the `final-report-template.md` structure.
|
|
316
|
+
14. Save the result directly to `reports/final-report-<task-type>-<seq>.md` for the current run and, if necessary, update `status/final-<task-type>-<seq>.status`, `manifests/run-manifest-<task-type>-<seq>.json`, `task-manifest.json`, and `task-index.md` to match the current state.
|
|
310
317
|
|
|
311
318
|
Recommended worker status values:
|
|
312
319
|
|
package/docs/architecture.md
CHANGED
|
@@ -84,7 +84,7 @@ okstra's prepare responsibilities are consolidated in a single Python entry poin
|
|
|
84
84
|
- Resolves task-brief / clarification-response paths (cwd first → PROJECT_ROOT fallback)
|
|
85
85
|
- Computes the stable task root and all paths/sequences inside a per-task mutex (`~/.okstra/.locks/<task-key>.lock`), then persists them to `<run-dir>/manifests/run-context-<seq>.json`
|
|
86
86
|
- Persists user input to `<run-dir>/manifests/run-inputs-<seq>.json`
|
|
87
|
-
- Renders the instruction set (`analysis-profile.md`, `analysis-packet.md`, `analysis-material.md`, `task-brief.md`, `reference-expectations.md`, `final-report-template.md`, `final-report-schema.json`, optional `clarification-response.md`, optional `directive.txt`, `claude-execution-prompt.md`) and writes a run prompt snapshot
|
|
87
|
+
- Renders the instruction set (`analysis-profile.md`, `analysis-packet.md`, `analysis-material.md`, `task-brief.md`, `reference-expectations.md`, `final-report-template.md`, `final-report-schema.json`, optional `clarification-response.md`, optional `directive.txt`, final-verification-only `verification-target.md`, `claude-execution-prompt.md`) and writes a run prompt snapshot
|
|
88
88
|
- Updates `task-manifest.json`, `task-index.md`, `run-manifest-*.json`, `history/timeline.json`, and `discovery/{latest-task,task-catalog}.json`
|
|
89
89
|
- Writes a preassigned Claude session ID and `sessions/claude-resume-*.sh` (unless `--render-only` is used)
|
|
90
90
|
- Records record_start in the central indexes (`~/.okstra/{active,recent}.jsonl`, `projects/<id>/{index.jsonl, meta.json}`)
|
|
@@ -304,6 +304,16 @@ The standard `okstra` workflow applies the following team contract consistently
|
|
|
304
304
|
- Every attempted worker (`completed`, `timeout`, `error`) must have an assigned worker prompt history file under the current run's `prompts/` directory.
|
|
305
305
|
- An unnamed generic parallel worker is not accepted as a substitute for a required role.
|
|
306
306
|
|
|
307
|
+
### Final-verification prompt and re-verification boundaries
|
|
308
|
+
|
|
309
|
+
Final-verification uses one shared full-core responsibility for every selected initial analysis worker. Provider/model diversity supplies independent observations of the same requirements; it does not split acceptance criteria into disjoint worker scopes. `analysis-packet.md` carries the worker-facing verification procedure, while report-only deliverable and self-review guidance stays with the report writer.
|
|
310
|
+
|
|
311
|
+
At final-verification entry, `prepare_task_bundle()` snapshots the resolved worktree, verification scope, base ref, head ref, implementation/stage report mapping, and diff stat into `instruction-set/verification-target.md`. It records the sidecar's project-relative path and `sha256:<hex>` digest in the task/run manifest and active-run context. Initial prompts carry only six compact target identity headers plus one `analysis-packet.md` path; workers open the target sidecar on demand. The coding-preflight anchor remains implementation-only, regardless of whether an initial pane is displayed with the role `verifier`.
|
|
312
|
+
|
|
313
|
+
All selected initial prompt bodies must normalize to the same semantic content. One optional shared `## Run-specific directive` is bounded to 40 nonblank lines, and larger material is placed in the instruction set. `dispatch_core.py` and `codex_dispatch.py` enforce this before process launch; Phase 7 `validators/validate-run.py` applies the same `worker_prompt_contract.py` rules to the persisted initial prompt paths. Report-writer and `-reverify-r<N>-` prompts are outside this initial-prompt gate.
|
|
314
|
+
|
|
315
|
+
The convergence algorithm is unchanged: Round 0 immediately classifies findings with matching semantics and ticket sets from two or more workers as `full-consensus`; only single-worker findings enter the verification queue. For worker W, `items_for_W` excludes findings originating from W, and resolved findings are removed permanently. Lightweight reverify receives only that current batch and its embedded evidence, not the original analysis packet/profile/brief/instruction set. The report writer does not vote, and terminal worker non-results are recorded as `verification-error`, never fabricated as `DISAGREE`.
|
|
316
|
+
|
|
307
317
|
## Stable task identity
|
|
308
318
|
|
|
309
319
|
`okstra` uses the following combination as its basic identifier:
|
|
@@ -732,7 +742,9 @@ Errors that occur while workers (Claude/Codex/Antigravity worker, Report writer,
|
|
|
732
742
|
|
|
733
743
|
### Live-log mirror (codex / antigravity wrapper)
|
|
734
744
|
|
|
735
|
-
- On every dispatch, `scripts/okstra-codex-exec.sh` and `scripts/okstra-antigravity-exec.sh` create a `<prompt>.log` sidecar next to the prompt path and mirror stdout there (
|
|
745
|
+
- On every dispatch, `scripts/okstra-codex-exec.sh` and `scripts/okstra-antigravity-exec.sh` create a `<prompt>.log` sidecar next to the prompt path and mirror stdout there through a named FIFO (which keeps the CLI a single addressable PID for the idle watchdog). stderr is appended to the same file, preserving the subagent stderr-capture contract, and the file is truncated on each dispatch. This solves the problem where a calling subagent polls `BashOutput` every 60 seconds, leaving users unable to detect a stalled state during long-running work such as large-codebase scans in analysis or cargo / pytest in implementation.
|
|
746
|
+
- The `.log` is an execution transcript containing wrapper/CLI output, not the original prompt. `okstra log-report` pairs it with the sibling persisted `.md` prompt and reports `transcriptBytes`, `promptBytes`, and their ratio separately. The compatibility fields `path`, `sizeBytes`, and `totalBytes` continue to mean transcript data, so existing consumers and transcript-size ordering remain stable.
|
|
747
|
+
- **Per-block cap on the codex log copy** (`okstra_log_mirror` in `scripts/okstra-codex-exec.sh`): workers read their required inputs end-to-end per the Worker Preamble's *Reading rules*, so a single report read can dump 170KB+ into the log and observed sidecars reach 8MB. The mirror keeps the first `log_block_line_cap` (120) lines of each output block and replaces the remainder with a `[okstra log-mirror] N line(s) elided` marker, draining every 500 elided lines so the idle watchdog keeps seeing writes. **Only the log copy is capped** — the stdout passthrough stays byte-identical, so the dispatching subagent's `BashOutput` and Phase 5 synthesis are unaffected (`tests-js/codex-log-mirror.test.mjs` asserts that byte-identity). The marker set is codex-specific; the claude wrapper emits `--output-format=stream-json` and does not share this filter.
|
|
736
748
|
- When tmux is reachable in the lead environment, the wrapper automatically splits a sibling pane and runs `tail -F <log-path>`. The trace-pane title appends `-tail` to the caller (worker) pane title: `<cli>-<role>-<pid>-tail` (for example, `codex-worker-93421-tail`). At the same time, the caller (worker) pane title is set to `<cli>-<role>-<pid>`. `<pid>` is the wrapper's own PID, so multiple workers with the same role spawned concurrently remain distinguishable, and operators can visually map `<caller> ↔ <caller>-tail`. **Caller-pane resolution**—because the Claude Code Bash tool now removes both `$TMUX` and `$TMUX_PANE` from the environment, the wrapper does not depend on environment variables. It (1) derives `<RUN_DIR>` as `dirname(dirname(prompt_path))` from the prompt path (paths.py SSOT), and (2) reads `<RUN_DIR>/state/lead-pane.id`, written once by the lead in its foreground pane, as the split anchor. This remains reliable for background dispatches, unlike active-pane guessing, even if the user changes panes. If the file is absent or the pane is stale, it falls back to `tmux display-message -p '#{pane_id}'` (the active pane). The trace split explicitly anchors to that caller pane with `-t`. The role is the wrapper's fifth optional positional argument and defaults to `worker`. The caller pane title is captured and restored by an EXIT trap, preventing stale titles across dispatches. Focus returns to the caller pane, and the trace pane remains after CLI exit so its scrollback is available. All paths silently degrade when tmux is unreachable, splitting fails, or tmux is outdated.
|
|
737
749
|
- **Run-scoped tagging for cleanup**: A trace pane's `tail -F` is a child of the tmux shell and survives Claude's exit. The wrapper tags each spawned pane with `tmux set-option -p @okstra_trace_run=<RUN_DIR>`, and `okstra-trace-cleanup.sh` discovers panes server-wide from that tag via `tmux list-panes -a` and runs `tmux kill-pane`. It requires neither tmux environment variables nor a pane-ID registry. Because the tag is run-scoped, it does not kill trace panes from other simultaneous okstra runs. Cleanup has two entry forms: the lead invokes it with `--run-dir <RUN_DIR>` to clean traces and worker-agent panes for that run, or the `hooks.SessionEnd` entry in `templates/reports/settings.template.json` invokes it with `--reap` to clean all trace panes tagged below `$CLAUDE_PROJECT_DIR/.okstra/` when no single run directory exists at session end. Missing tmux and stale pane IDs silently degrade.
|
|
738
750
|
- **Automatic cleanup on phase transitions, including worker-agent panes**: `okstra-trace-cleanup.sh --run-dir <RUN_DIR>` closes not only tagged trace panes but also worker-agent panes occupied by dispatched subagents. These harness-owned panes cannot be tagged, so the script identifies them within the lead session (`tmux list-panes -s -t <lead-pane>`) through a title allowlist: `claude-worker` / `codex-worker` / `antigravity-worker` / `report-writer-worker`. Implementation role titles such as `claude-executor` / `codex-verifier` / `agy-executor-tail`, and FleetView teammate prefixes `✳ ` / `⠂ `, are also treated as okstra panes. Session scoping and exclusion of the lead's own pane are determined by `<RUN_DIR>/state/lead-pane.id`; the lead pane is never killed even if its title matches. Immediately before dispatching workers for a new phase (before the `PROGRESS: phase-5.5-convergence` / `phase-6-synthesis` marker), the lead calls this script with `--run-dir` to clean previous-phase panes without prompting.
|
package/docs/cli.md
CHANGED
|
@@ -639,6 +639,7 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
|
|
|
639
639
|
| `okstra manager <init\|discover-projects\|new\|task>` | Public CLI for grouping cross-project okstra tasks into manager-owned context. `new project`, `new task-group`, and `new task` create manager plans; `task assign`, `task note`, `task sync`, `task status`, and `task run` manage per-project assignments and snapshots. `new project --project-root` accepts only existing directories and performs setup-equivalent registration only if `.okstra/project.json` is absent. Public documentation uses the full `project-id:task-group:task-id` child task key; when child task IDs differ within the same manager task, select the exact child with `--child-task-id`. `task run` does not execute the child lead directly; it returns `prepared` launch metadata/event and a child launch-context packet as JSON |
|
|
640
640
|
| `okstra rollup [--task-group <group>] [--project-root <dir>] [--cwd <dir>]` | Read-only backend for the okstra-rollup skill. For every catalog task, or one task group, it emits JSON with per-task run counts, raw duration in ms, error counts, latest report paths, group totals, and status/category/phase distributions. Omitting `--task-group` targets the whole project catalog. The caller skill formats raw ms as HH:MM:SS and synthesizes report prose. Use the `okstra inspect` family for a single-task drill-down |
|
|
641
641
|
| `okstra usage-report [--days <positive-int>] [--project-root <dir>] [--cwd <dir>] [--json]` | Read-only backend for the okstra-usage skill. Defaults to the whole current project's last 30 days and emits task-type run coverage, raw/billable tokens, known USD cost, CPU-sum milliseconds, wall-clock milliseconds, unavailable reason counts, and unmatched pricing models |
|
|
642
|
+
| `okstra log-report [--project-root <dir>] [--cwd <dir>] [--top <N>] [--json]` | Read-only inventory of wrapper transcript `.log` files and their sibling prompt `.md` files. Each ranked entry preserves `path` / `sizeBytes` for compatibility and also reports `transcriptPath`, `transcriptBytes`, `promptPath`, `promptBytes`, and `transcriptToPromptRatio`; totals distinguish prompt bytes from transcript bytes and count paired files. Ranking remains transcript-size descending |
|
|
642
643
|
| `okstra recap <assemble\|record\|note> <task-root\|task-key> …` | Backend for the okstra-inspect `recap` facet. `assemble` is read-only and prints a JSON summary of phase transitions across a task's runs. `record --kind <summary\|qa> --mode <artifact\|code> --answer <text> [--question <text>] [--citation <path:line> …]` appends one line to `<task-root>/recap/recap-log.jsonl` and never mutates other artifacts. `note --kind <verification-evidence\|decision-draft\|analysis-note> --slug <topic> --purpose <text> --scope-note <text> (--body <markdown>\|--body-file <path>)` writes an agent-authored note to `<task-root>/notes/` and prints its path plus the `--clarification-response` argument for feeding it into a later run |
|
|
643
644
|
| `okstra user-response <list\|show\|write> …` | Backend for the `/okstra-user-response` skill: answer a task's open clarification questions in-session and write the response sidecar. `list --home <dir> --project <id> [--limit <n>]` finds reports with open questions; `show --report <md>` reads one report's questions; `write --report <md> --answers <json> [--approval <json>] [--task-key <key>]` writes the sidecar. Each answer carries a `disposition` of `answer` or `reframe`; a `reframe` is carried into the next run as a re-scoped brief. JSON output; exit 0 ok / 1 error |
|
|
644
645
|
| `okstra pr <template\|branches\|gen> …` | Backend for the okstra-pr-gen skill. Git-only—no project registration required. `template list\|show <name\|default>\|add --name <name> (--content <text>\|--file <path>)\|path` manages PR body templates under `~/.okstra/template/pr/` (bundled fallback `src/commands/pr/default.md`); `branches` recommends a base branch; `gen --base <ref> [--template <name\|default>]` emits a JSON bundle of the template plus `<base>..HEAD` commits and `<base>...HEAD` diffstat |
|
|
@@ -169,6 +169,7 @@ Runtime/install asset changes follow this checklist:
|
|
|
169
169
|
| `resolve-task-key` | `src/commands/inspect/resolve-task-key.mjs` | Resolve a bare task-id to candidate task-keys from the project catalog |
|
|
170
170
|
| `set-work-status` | `src/commands/inspect/set-work-status.mjs` | Set a task's user-managed `workStatus` in task-manifest.json (Python: `okstra_ctl.set_work_status`) |
|
|
171
171
|
| `time-report`, `log-report`, `error-report`, `error-zip` | `src/commands/inspect/*.mjs` | Read-side task runtime, wrapper log, and error aggregation helpers |
|
|
172
|
+
| `worker-liveness` | `src/commands/inspect/worker-liveness.mjs` | Report whether pending workers are still alive, so the lead's poll ends a stalled wait early instead of paying the deadline (Python: `okstra_ctl.worker_liveness`) |
|
|
172
173
|
| `context-cost` | `src/commands/inspect/context-cost.mjs` | Estimate task bundle file/read context cost |
|
|
173
174
|
| `worktree-lookup` | `src/commands/execute/worktree-lookup.mjs` | Look up a task-key's registered worktree |
|
|
174
175
|
| `plan-validate` | `src/commands/execute/plan-validate.mjs` | Check approved-plan approval marker |
|
|
@@ -218,7 +219,7 @@ Important modules:
|
|
|
218
219
|
|
|
219
220
|
| Module | Role |
|
|
220
221
|
|---|---|
|
|
221
|
-
| `run.py` | `prepare_task_bundle()` single authority and CLI parser |
|
|
222
|
+
| `run.py` | `prepare_task_bundle()` single authority and CLI parser; for final-verification it also owns `verification-target.md` snapshot/digest materialization before manifests and prompts are rendered |
|
|
222
223
|
| `implementation_stage.py` | `implementation` single-stage run orchestration — read the Stage Lifecycle Snapshot → pick an available Stage Map entry → provision an isolated stage worktree → publish the selected stage as run context (extracted from `run.py`) |
|
|
223
224
|
| `stage_targets.py` | Stage readiness/verification policy SSOT — from the Stage Lifecycle Snapshot (`consumers.jsonl` ledger + carry sidecar backfill + active registry reservation) it decides which stage is runnable, which commit it branches from, and what final-verification checks. `order_stage_closure` topologically sorts (Kahn) the dependency closure of the wizard's multi-selected stage set to produce the unattended `chain-stages` chaining order |
|
|
224
225
|
| `stage_fix_carry.py` | fix-run carry derivation for a re-run on an `implementation` stage whose latest final-report data.json carries verifier `FAIL` verdicts — collects the previous report path, previous run HEAD, failed verifiers, carried blocking findings, and a routing recommendation, which `run.py` renders into the analysis profile through the `{{FIX_RUN_CONTEXT}}` token. A first run, or a re-run after `PASS`, yields no carry and renders the token empty |
|
|
@@ -249,7 +250,8 @@ Important modules:
|
|
|
249
250
|
| `index.py`, `jsonl.py`, `reconcile.py`, `listing.py`, `batch.py`, `backfill.py` | `~/.okstra` run index and history operations |
|
|
250
251
|
| `run_index_row.py` | single reference point for creating / slimming / hydrating a `~/.okstra` run-index row — runId SSOT, preserves projectId raw |
|
|
251
252
|
| `error_report.py`, `error_log_core.py`, `error_zip.py` | backend for the okstra-inspect errors/error-zip facets — `error_log_core` is the read-only core that globs/parses/aggregates `errors-*.jsonl`, `error_report` renders the errors facet, and `error_zip` collects cross-project run directories, allowlist-anonymizes, aggregates clusters, and produces a zip |
|
|
252
|
-
| `
|
|
253
|
+
| `worker_heartbeat.py`, `worker_liveness.py` | `worker_heartbeat` is the single definition of the `- PROGRESS:` heartbeat line shape and its 5-minute (+60s grace) cadence budget, shared by the Phase 7 audit (`validators/validate_session_conformance.py`) and the live probe; `worker_liveness` backs `okstra worker-liveness`, reporting a pending worker as `stalled` (heartbeat past the budget) or `did-not-launch` (no wrapper `.log`/`.status.json` past the launch grace) |
|
|
254
|
+
| `log_report.py`, `time_report.py` | read-side backend for the okstra-inspect logs/time facets (`okstra log-report` pairs each wrapper transcript `.log` with its sibling prompt `.md` and reports both byte counts without changing legacy transcript-size fields; `okstra time-report` is per-task time aggregation) |
|
|
253
255
|
| `rollup.py` | read-side backend for the okstra-rollup skill — fans the catalog out per task-group (or the whole project) and deterministically aggregates each task's run count, elapsed time (raw ms), error count, and latest report path, plus group-level totals/status, category, and phase distribution. Reuses the `time_report`/`error_log_core` functions and delegates report-body synthesis to the skill |
|
|
254
256
|
| `usage_report.py` | Read-only okstra-usage backend — scans the whole current project's recent run timelines, defaults to 30 days, and returns task-type coverage, raw/billable tokens, known USD cost, CPU-sum and wall-clock milliseconds, unavailable reason counts, and unmatched pricing models |
|
|
255
257
|
| `json_registry.py` | shared flock + atomic JSON persistence for small okstra registries (`registry_lock`/`load_registry_json`/`save_registry_json`) — shared by `container_registry` and `worktree_registry` |
|
|
@@ -266,9 +268,9 @@ Important modules:
|
|
|
266
268
|
| `manager_store.py` | Manager-owned state mutation — project membership, task planning, assignment, directives, event append |
|
|
267
269
|
| `manager_sync.py` | One-way child project `.okstra` snapshot reader; corrupt child state becomes row-level `error` so other children continue |
|
|
268
270
|
| `manager_launch.py` | Child launch packet and manager child context renderer; records `prepared` launch metadata/events without changing project-local task state |
|
|
269
|
-
| `dispatch_core.py` | Backend-neutral worker dispatch core — worker execution/collection logic shared by any lead runtime (Claude/Codex/external) |
|
|
270
|
-
| `codex_dispatch.py` | Codex lead CLI-worker dispatcher — the `okstra codex-dispatch` backend. Reads the run manifest to run the Codex-side supported worker subset and performs token-usage substitution, view render, follow-up, and validation |
|
|
271
|
-
| `analysis_packet.py` | assembles the compact analysis-worker input packet for a task run
|
|
271
|
+
| `dispatch_core.py` | Backend-neutral worker dispatch core — worker execution/collection logic shared by any lead runtime (Claude/Codex/external); gates selected final-verification initial prompts through the shared compact/equality contract before launch |
|
|
272
|
+
| `codex_dispatch.py` | Codex lead CLI-worker dispatcher — the `okstra codex-dispatch` backend. Reads the run manifest to run the Codex-side supported worker subset, applies the same final-verification initial-prompt gate, and performs token-usage substitution, view render, follow-up, and validation |
|
|
273
|
+
| `analysis_packet.py` | assembles the compact analysis-worker input packet for a task run; final-verification packets include worker verification procedure but omit report-only deliverable/self-review guidance |
|
|
272
274
|
| `context_cost.py` | read-side context-cost estimator for a prepared okstra task bundle (the `okstra context-cost` backend) |
|
|
273
275
|
| `schema_excerpt.py` | generates a task-type-scoped excerpt of the final-report schema — a schema reduction to inject into the worker/lead prompt |
|
|
274
276
|
| `work_categories.py` | requirements-discovery work-category (domain) **SSOT** (`is_valid_category`) — the work-category allowlist is defined only here |
|
|
@@ -276,7 +278,8 @@ Important modules:
|
|
|
276
278
|
| `lead_runtime.py` | lead runtime metadata shared by the render and prepare paths (`LeadRuntimeInfo`) |
|
|
277
279
|
| `lead_events.py` | structured JSONL events emitted by non-Claude lead runtimes |
|
|
278
280
|
| `team_reconcile.py` | stale team-member reconciliation at run-end teardown |
|
|
279
|
-
| `worker_prompt_headers.py` | shared rendering of
|
|
281
|
+
| `worker_prompt_headers.py` | shared rendering of phase-aware worker prompt anchors (`worker_prompt_headers`): coding-preflight only for implementation and compact target identity for final-verification |
|
|
282
|
+
| `worker_prompt_contract.py` | deterministic compact initial final-verification prompt validator and normalized cross-worker equality SSOT, reused by dispatch adapters and the Phase 7 persisted-artifact validator |
|
|
280
283
|
| `wrapper_status.py` | worker wrapper status sidecar reader — the host-side reader of the sidecar written by `okstra-wrapper-status.py` (the heartbeat writer) |
|
|
281
284
|
| `task_target.py` | shared helper resolving `task-key → (task_root, project_root)` (`resolve_task_root`) |
|
|
282
285
|
| `graphify_cmd.py` | scope wrapper for the okstra-graphify skill — `resolve_scope` + `assemble` + `okstra graphify` subcommand dispatch. Forces the corpus to `.okstra` and outputs to `.okstra/graph/` |
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -67,7 +67,7 @@ Before writing the data.json, you MUST:
|
|
|
67
67
|
|
|
68
68
|
For the report writer specifically, the `## Inputs` list always includes:
|
|
69
69
|
|
|
70
|
-
- `<instruction-set>/final-report-schema.json` — the **per-task-type excerpt** of the data.json schema (scoped to this run's task-type at prep time: other task-types' deliverable blocks and their unreachable `$defs` are stripped). This is the shape you must author. Read this, NOT the full `schemas/final-report-v1.0.schema.json` (it is not in the task bundle and its `schemas/...` path is not resolvable here). Validation still runs against the full schema post-hoc, so the excerpt never relaxes the contract.
|
|
70
|
+
- `<instruction-set>/final-report-schema.json` — the **per-task-type excerpt** of the data.json schema (scoped to this run's task-type at prep time: other task-types' deliverable blocks and their unreachable `$defs` are stripped). This is the shape you must author. Read this, NOT the full `schemas/final-report-v1.0.schema.json` (it is not in the task bundle and its `schemas/...` path is not resolvable here). Validation still runs against the full schema post-hoc, so the excerpt never relaxes the contract. The excerpt is frozen at prep time and carries the okstra version it was cut from (`x-okstraCutFromVersion`); if the renderer rejects a field the excerpt told you to write, the runtime upgraded mid-run — the renderer's error names the version skew, and the **installed schema wins**.
|
|
71
71
|
- `<instruction-set>/final-report-template.md` — the **phase-stripped** Jinja2 template the renderer uses (only this run's §4.x deliverable block remains). Read it to understand which data.json fields appear where in the rendered markdown; do NOT edit it, and do NOT pull the full `templates/reports/final-report.template.md` source.
|
|
72
72
|
- `templates/reports/i18n/en.json` and `templates/reports/i18n/ko.json`.
|
|
73
73
|
- Every analysis worker's result file under `worker-results/`.
|
|
@@ -283,8 +283,14 @@ if (( idle_timeout_secs > 0 )); then
|
|
|
283
283
|
# GNU `stat -c` 우선 + 숫자 검증. BSD-ism `stat -f %m` 은 Linux 에서 깨끗이
|
|
284
284
|
# 실패하지 않고 비숫자 파일시스템 출력으로 성공해 아래 산술을 set -u 에서
|
|
285
285
|
# 깨뜨린다 — file_mtime(lib/okstra/interactive.sh) 와 동일 가드.
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
#
|
|
287
|
+
# `|| true` 는 필수다. macOS 에는 GNU `stat -c` 가 없어 exit 1 이고,
|
|
288
|
+
# `2>/dev/null` 은 stderr 만 막지 종료코드는 막지 못한다. 이 워치독은 명시적
|
|
289
|
+
# `( ) &` 서브셸이라 위쪽 `set -e` 를 상속하므로, 실패한 할당 하나가 첫 폴링에서
|
|
290
|
+
# 워치독을 통째로 죽인다 — 조용히, 로그 한 줄 없이. file_mtime 이 같은 코드로도
|
|
291
|
+
# 멀쩡한 것은 항상 `$( )` 안에서 호출돼 errexit 을 상속하지 않기 때문이다.
|
|
292
|
+
last_mtime="$(stat -c %Y "$log_path" 2>/dev/null || true)"
|
|
293
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime="$(stat -f %m "$log_path" 2>/dev/null || true)"
|
|
288
294
|
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime=0
|
|
289
295
|
now=$(date +%s)
|
|
290
296
|
idle=$(( now - last_mtime ))
|
|
@@ -126,8 +126,14 @@ if (( idle_timeout_secs > 0 )); then
|
|
|
126
126
|
# GNU `stat -c` 우선 + 숫자 검증. BSD-ism `stat -f %m` 은 Linux 에서 깨끗이
|
|
127
127
|
# 실패하지 않고 비숫자 파일시스템 출력으로 성공해 아래 산술을 set -u 에서
|
|
128
128
|
# 깨뜨린다 — file_mtime(lib/okstra/interactive.sh) 와 동일 가드.
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
#
|
|
130
|
+
# `|| true` 는 필수다. macOS 에는 GNU `stat -c` 가 없어 exit 1 이고,
|
|
131
|
+
# `2>/dev/null` 은 stderr 만 막지 종료코드는 막지 못한다. 이 워치독은 명시적
|
|
132
|
+
# `( ) &` 서브셸이라 위쪽 `set -e` 를 상속하므로, 실패한 할당 하나가 첫 폴링에서
|
|
133
|
+
# 워치독을 통째로 죽인다 — 조용히, 로그 한 줄 없이. file_mtime 이 같은 코드로도
|
|
134
|
+
# 멀쩡한 것은 항상 `$( )` 안에서 호출돼 errexit 을 상속하지 않기 때문이다.
|
|
135
|
+
last_mtime="$(stat -c %Y "$log_path" 2>/dev/null || true)"
|
|
136
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime="$(stat -f %m "$log_path" 2>/dev/null || true)"
|
|
131
137
|
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime=0
|
|
132
138
|
now=$(date +%s)
|
|
133
139
|
idle=$(( now - last_mtime ))
|
|
@@ -167,6 +167,57 @@ log_path="${prompt_path%.md}.log"
|
|
|
167
167
|
[[ "$log_path" == "$prompt_path" ]] && log_path="${prompt_path}.log"
|
|
168
168
|
: > "$log_path"
|
|
169
169
|
|
|
170
|
+
# Per-block line cap applied to the LOG COPY of codex's stdout (see
|
|
171
|
+
# okstra_log_mirror below). Workers read their required inputs end-to-end per
|
|
172
|
+
# templates/worker-prompt-preamble.md "Reading rules", so one report read can
|
|
173
|
+
# dump 170KB+ into the log; observed sidecar logs reach 8MB and account for
|
|
174
|
+
# ~93% of a project's `.okstra/` bytes (`okstra log-report` inventories them).
|
|
175
|
+
# 120 lines keeps each block's command and the head of its output — enough to
|
|
176
|
+
# reconstruct what ran — while cutting the whole-file dumps that dominate.
|
|
177
|
+
log_block_line_cap=120
|
|
178
|
+
|
|
179
|
+
# Mirror stdin to stdout verbatim AND to <log-path> with a per-block cap.
|
|
180
|
+
#
|
|
181
|
+
# Only the log copy is capped. The stdout passthrough is never filtered, so the
|
|
182
|
+
# dispatching subagent's `BashOutput` still receives codex's output verbatim for
|
|
183
|
+
# Phase 5 synthesis, and the model's own context is unaffected — the log is a
|
|
184
|
+
# *copy* of output codex already produced, not an input to anything except a
|
|
185
|
+
# human post-mortem and the `tail -n 10` diagnostic in
|
|
186
|
+
# agents/workers/_cli-wrapper-template.md.
|
|
187
|
+
#
|
|
188
|
+
# A "block" starts at one of codex's own top-level markers. Misreading a marker
|
|
189
|
+
# only resets the counter (less truncation); it can never drop a line from
|
|
190
|
+
# stdout. The marker set is codex-specific — the claude wrapper emits
|
|
191
|
+
# `--output-format=stream-json` and must NOT reuse this filter as-is.
|
|
192
|
+
okstra_log_mirror() {
|
|
193
|
+
awk -v logf="$1" -v cap="$2" '
|
|
194
|
+
function drain() {
|
|
195
|
+
if (elided > 0) {
|
|
196
|
+
printf " [okstra log-mirror] %d line(s) elided from this block\n", elided >> logf
|
|
197
|
+
fflush(logf)
|
|
198
|
+
elided = 0
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/^(exec|codex|thinking|user)$/ { drain(); kept = 0 }
|
|
202
|
+
{
|
|
203
|
+
print
|
|
204
|
+
fflush()
|
|
205
|
+
kept++
|
|
206
|
+
if (kept <= cap) {
|
|
207
|
+
print >> logf
|
|
208
|
+
fflush(logf)
|
|
209
|
+
} else {
|
|
210
|
+
elided++
|
|
211
|
+
# Periodic drain keeps the log mtime moving: the idle watchdog below
|
|
212
|
+
# polls that mtime as its liveness proxy and would otherwise SIGTERM
|
|
213
|
+
# codex partway through a long elided block.
|
|
214
|
+
if (elided >= 500) drain()
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
END { drain(); close(logf) }
|
|
218
|
+
'
|
|
219
|
+
}
|
|
220
|
+
|
|
170
221
|
# stdout-mirror FIFO path (created at the dispatch block below). Declared here,
|
|
171
222
|
# before the EXIT trap is defined, so the trap's cleanup reference is always set
|
|
172
223
|
# under `set -u`.
|
|
@@ -293,11 +344,11 @@ fi
|
|
|
293
344
|
# stdin redirect, stderr capture, and pipeline mirroring are intentionally
|
|
294
345
|
# inside the wrapper — this is the entire reason this script exists.
|
|
295
346
|
#
|
|
296
|
-
# stdout:
|
|
297
|
-
#
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
# can SIGTERM from the watchdog.
|
|
347
|
+
# stdout: mirrored to both the live log (for `tail -f`, capped per block by
|
|
348
|
+
# okstra_log_mirror) AND the wrapper's own stdout (verbatim, so the
|
|
349
|
+
# subagent's `BashOutput` still captures the final text for Phase 5
|
|
350
|
+
# synthesis). Implemented via a FIFO so codex itself stays a single
|
|
351
|
+
# addressable PID we can SIGTERM from the watchdog.
|
|
301
352
|
# stderr: appended to the live log only — mirrors the prior `2>/dev/null`
|
|
302
353
|
# contract of keeping the wrapper's stderr stream clean.
|
|
303
354
|
# exit: codex's own exit code is preserved by `wait`.
|
|
@@ -325,7 +376,7 @@ fi
|
|
|
325
376
|
stdout_fifo="${log_path}.stdout.fifo"
|
|
326
377
|
rm -f "$stdout_fifo"
|
|
327
378
|
mkfifo "$stdout_fifo"
|
|
328
|
-
|
|
379
|
+
okstra_log_mirror "$log_path" "$log_block_line_cap" < "$stdout_fifo" &
|
|
329
380
|
stdout_tee_pid=$!
|
|
330
381
|
|
|
331
382
|
# Disable git's fsmonitor for every git command codex runs in this process
|
|
@@ -363,8 +414,14 @@ if (( idle_timeout_secs > 0 )); then
|
|
|
363
414
|
# GNU `stat -c` 우선 + 숫자 검증. BSD-ism `stat -f %m` 은 Linux 에서 깨끗이
|
|
364
415
|
# 실패하지 않고 비숫자 파일시스템 출력으로 성공해 아래 산술을 set -u 에서
|
|
365
416
|
# 깨뜨린다 — file_mtime(lib/okstra/interactive.sh) 와 동일 가드.
|
|
366
|
-
|
|
367
|
-
|
|
417
|
+
#
|
|
418
|
+
# `|| true` 는 필수다. macOS 에는 GNU `stat -c` 가 없어 exit 1 이고,
|
|
419
|
+
# `2>/dev/null` 은 stderr 만 막지 종료코드는 막지 못한다. 이 워치독은 명시적
|
|
420
|
+
# `( ) &` 서브셸이라 위쪽 `set -e` 를 상속하므로, 실패한 할당 하나가 첫 폴링에서
|
|
421
|
+
# 워치독을 통째로 죽인다 — 조용히, 로그 한 줄 없이. file_mtime 이 같은 코드로도
|
|
422
|
+
# 멀쩡한 것은 항상 `$( )` 안에서 호출돼 errexit 을 상속하지 않기 때문이다.
|
|
423
|
+
last_mtime="$(stat -c %Y "$log_path" 2>/dev/null || true)"
|
|
424
|
+
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime="$(stat -f %m "$log_path" 2>/dev/null || true)"
|
|
368
425
|
[[ "$last_mtime" =~ ^[0-9]+$ ]] || last_mtime=0
|
|
369
426
|
now=$(date +%s)
|
|
370
427
|
idle=$(( now - last_mtime ))
|
|
@@ -62,6 +62,7 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
|
|
|
62
62
|
- Follow the core Result Path + terminal-status completion contract. The Claude adapter's wake mechanism is one `Bash(run_in_background: true)` poll covering every pending Result Path, not foreground sleep or an idle-notification dependency. A spawn acknowledgement is never completion.
|
|
63
63
|
- The background poll uses a per-worker deadline of twice the expected duration: 20 minutes for `requirements-discovery`, 30 for `error-analysis`, 40 for `implementation-planning`, 40 for `implementation`, and 20 for `final-verification`. On timeout, record terminal status and apply the core's single shared retry budget.
|
|
64
64
|
- The Claude worker heartbeat audit sidecar must update at least every five minutes while its result is pending. A missing or stale heartbeat consumes the same one-retry budget; after the second silent hang, record `timeout`. The result file remains the authoritative completion signal.
|
|
65
|
+
- **The background poll checks liveness, not only Result Paths.** Result Paths change once, at the very end, so polling them alone pays the full deadline for a worker that died at minute three. Each poll iteration MUST also run, in the same background shell, one `okstra worker-liveness` call covering every pending worker — `--audit <audit-sidecar-path>` for each in-process Claude worker, `--prompt <prompt-history-path>` for each CLI-wrapper worker. It exits non-zero when a worker is `stalled` (heartbeat older than the cadence budget) or `did-not-launch`; either verdict ends the wait for that worker immediately and spends the core's one-retry budget, rather than waiting out the deadline. The command reports only — it never kills or re-dispatches. It shares its heartbeat budget with the Phase 7 audit (`okstra_ctl.worker_heartbeat`), so a worker the live probe passes cannot fail the post-hoc one for cadence.
|
|
65
66
|
- The Claude Code harness blocks long foreground sleeps and shorter-sleep circumvention loops. Keep the result poll in a single background shell and let wrapper agents use their documented `BashOutput` loop.
|
|
66
67
|
- On approved cleanup, reconcile the current live session roster before sending shutdown requests. Never target the lead session.
|
|
67
68
|
- Collect usage before teardown. Resume through the recorded Claude session id and keep all run artifacts authoritative.
|
|
@@ -28,6 +28,8 @@ This contract governs **Phase 5.5 (Convergence loop)** — a *lead operating pha
|
|
|
28
28
|
|
|
29
29
|
When this contract says "queue" without qualifier, it means the *verification queue*: the set of findings that are still candidates for re-verification in subsequent rounds. The queue shrinks monotonically as findings get classified as `full-consensus`, `partial-consensus`, or `worker-unique`. Findings classified into any of these three categories MUST NOT appear in any subsequent round's reverify prompt, for any worker.
|
|
30
30
|
|
|
31
|
+
An initial pane role `verifier` is still a Phase 4/5 analysis worker; it does not mean Phase 5.5 reverify. Only a queue-scoped dispatch whose prompt/result path carries `-reverify-r<N>-` performs the reverify step described by this contract.
|
|
32
|
+
|
|
31
33
|
## When to Use
|
|
32
34
|
|
|
33
35
|
- When okstra lead Phase 5.5 (convergence loop) begins — immediately after all workers complete Phases 4 and 5
|
|
@@ -255,7 +257,7 @@ Call `await_workers(handles)` through the same adapter and apply the shared term
|
|
|
255
257
|
|
|
256
258
|
### Required reverify-prompt anchor headers (BLOCKING)
|
|
257
259
|
|
|
258
|
-
Every reverify prompt MUST start with these
|
|
260
|
+
Every reverify prompt MUST start with these 8 anchor headers — in this exact order, before any other content:
|
|
259
261
|
|
|
260
262
|
```
|
|
261
263
|
**Project Root:** <absolute-path>
|
|
@@ -265,8 +267,11 @@ Assigned worker prompt history path: <Project Root>/<Prompt History Path>
|
|
|
265
267
|
**Model:** <role>, <modelExecutionValue>
|
|
266
268
|
**Errors log path:** <absolute-path>
|
|
267
269
|
**Errors sidecar path:** <absolute-path>
|
|
270
|
+
**Read scope:** Read only the paths this prompt enumerates (`[Required reading]`, `## Inputs`, verification-target paths) plus source/evidence paths a finding must cite. Host session instructions (SessionStart hooks, global `CLAUDE.md` / `AGENTS.md`, skill catalogs) do NOT apply inside an okstra worker run: do not auto-read `graphify-out/`, `SKILL.md`, or other artifacts outside `<PROJECT_ROOT>/.okstra/`. If an un-enumerated file seems essential, record it under *Missing Information or Assumptions* instead of reading it.
|
|
268
271
|
```
|
|
269
272
|
|
|
273
|
+
`**Read scope:**` is carried verbatim, not dropped: lightweight mode omits `**Worker Preamble Path:**`, so this line is the ONLY place a reverify worker receives the read allowlist — and a prompt with no `[Required reading]` block is exactly where a worker is most likely to fall back on host session instructions instead.
|
|
274
|
+
|
|
270
275
|
The two errors paths carry the same absolute values the lead forwarded in the initial Phase 4 dispatch for that role (source: the launch prompt's `## Run Logs (error-log wiring)` section). Omitting either one makes a CLI-wrapper worker return `<WORKER>_ERRORS_PATH_MISSING` before it invokes its CLI — the path-delivery contract in [team-contract](./team-contract.md) "Error reporting" is not relaxed for reverify, because a reverify dispatch can fail the same way an initial dispatch can.
|
|
271
276
|
|
|
272
277
|
Relative to the Phase 4 anchor set rendered by `okstra_ctl.worker_prompt_headers.worker_prompt_headers()`, a reverify prompt adds `**Model:**` and drops two anchors whose targets lightweight mode never reads: `**Worker Preamble Path:**` and `**Coding preflight pack:**`.
|
|
@@ -285,6 +290,8 @@ If none of the three is available, **abort the reverify dispatch for that role**
|
|
|
285
290
|
|
|
286
291
|
Reverify prompts MUST NOT inject the Phase 2 `[Required reading]` clause:
|
|
287
292
|
|
|
293
|
+
Lightweight reverify does not require the original `analysis-packet.md`, `analysis-profile.md`, `task-brief.md`, or instruction set. Its complete input is the receiving worker's current `items_for_W` batch and the evidence embedded in those findings.
|
|
294
|
+
|
|
288
295
|
- **Lightweight mode**: the clause directly contradicts the "Do NOT re-analyze the original source materials" instruction below. Including it forces workers to re-read the entire instruction-set per round per worker (3 workers × 2 rounds × 5+ files in the worst case) for no quality gain.
|
|
289
296
|
- **Full-reanalysis mode**: workers DO need to re-read source materials, but only the analysis-worker file list (no `final-report-template.md`). If lead chooses to inject a reading clause here, it MUST mirror the audience-scoped enumeration in [okstra-lead-contract](./okstra-lead-contract.md) Phase 2 (no template).
|
|
290
297
|
|
|
@@ -208,6 +208,8 @@ These phases are governed by [team-contract](./team-contract.md). It is the cano
|
|
|
208
208
|
- Worker output contract (sections 1–5 + optional Section 6; the Reading Confirmation block lives in the audit sidecar, never in the worker-results file — the preamble "Reading rules" section is canonical and the validator rejects violations), header standard, terminal statuses, errors-sidecar schema.
|
|
209
209
|
- Token-usage tracking conventions.
|
|
210
210
|
|
|
211
|
+
For `final-verification`, Lead persists initial analysis prompts with one shared semantic body. Any genuine run delta appears under exactly one `## Run-specific directive` heading and applies to every selected analysis worker; Lead never shards verification requirements by worker, provider, or model. If the shared directive would exceed 40 nonblank lines, Lead writes it into the instruction set and adds the same reference to `analysis-packet.md` before dispatch. Phase 7 validates the persisted initial analysis prompts through the shared prompt contract before the run can pass.
|
|
212
|
+
|
|
211
213
|
`Report writer worker` is NOT an analysis worker. Do not dispatch it in Phase 4/5 alongside analysis workers. It is invoked only in Phase 6 — see [report-writer](./report-writer.md).
|
|
212
214
|
|
|
213
215
|
### Phase 3 — Runtime adapter setup (BLOCKING)
|
|
@@ -244,7 +246,30 @@ okstra error-log append-from-worker \
|
|
|
244
246
|
--task-key <taskKey> --agent <agent> --agent-role <role> --model <model>
|
|
245
247
|
```
|
|
246
248
|
|
|
247
|
-
|
|
249
|
+
`--agent`, `--agent-role`, and `--error-type` are **closed enums**, not free-form labels — the role names used elsewhere in these contracts (`Codex worker`, `Claude worker`) are rejected. Use exactly:
|
|
250
|
+
|
|
251
|
+
- `--agent` — `claude-worker` | `codex-worker` | `antigravity-worker` | `report-writer`
|
|
252
|
+
- `--agent-role` — `lead` | `worker` | `report-writer`
|
|
253
|
+
- `--error-type` — `cli-failure` | `contract-violation` | `tool-failure`
|
|
254
|
+
|
|
255
|
+
For a lead-attributed event there is no value in the list above — the selected adapter names the lead identity to pass.
|
|
256
|
+
|
|
257
|
+
For Codex/Antigravity wrappers: if the CLI returns non-zero, times out, or hits a rate limit, immediately call `append-observed` with the captured exit code, duration, message, and stderr excerpt. `append-observed` additionally requires `--phase`, `--command`, and `--command-kind`, so copy this form rather than trimming the one above:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
okstra error-log append-observed \
|
|
261
|
+
--out <absolute-errors-log-path-from-launch-prompt> \
|
|
262
|
+
--task-key <taskKey> --phase 5.5 \
|
|
263
|
+
--agent codex-worker --agent-role worker --model <model> \
|
|
264
|
+
--error-type cli-failure \
|
|
265
|
+
--command "okstra-codex-exec.sh <project-root> <model> <prompt-path>" \
|
|
266
|
+
--command-kind wrapper \
|
|
267
|
+
--exit-code 124 --duration-ms 1800000 \
|
|
268
|
+
--message "reverify-r1 wrapper never launched" \
|
|
269
|
+
--stderr-excerpt "<last stderr lines, or use --stderr-excerpt-file>"
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The wrapper subagent records this through its selected adapter — Lead does NOT need to re-record. Token usage is not inferred from dispatch return values; call `collect_usage` at the start of Phase 7.
|
|
248
273
|
|
|
249
274
|
## Phase 5.5: Convergence loop
|
|
250
275
|
|
|
@@ -43,7 +43,7 @@ The prompt MUST include, in this order at the top:
|
|
|
43
43
|
- `**Errors sidecar path:** <absolute-path>` — this worker's per-run sidecar JSON (`worker-results/report-writer-worker-errors-<task-type>-<seq>.json`).
|
|
44
44
|
7. `**Model:** Report writer worker, <modelExecutionValue>` (resolved per Phase 5.5 anchor-header rules)
|
|
45
45
|
8. 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:
|
|
46
|
-
- `<instruction-set>/final-report-schema.json` — a task-type excerpt of the data.json schema (the other task-types' deliverable blocks and their unreachable `$defs` are stripped; ~38% of the full schema is `$defs` alone). This is your authoring
|
|
46
|
+
- `<instruction-set>/final-report-schema.json` — a task-type excerpt of the data.json schema (the other task-types' deliverable blocks and their unreachable `$defs` are stripped; ~38% of the full schema is `$defs` alone). This is your authoring aid for the data.json shape — the installed schema, not the excerpt, is what the run is judged against. Do **NOT** pull the full `schemas/final-report-v1.0.schema.json` — it carries all task-types and its `schemas/...` path is not part of the task bundle. (Validation still runs against the full schema post-hoc via the renderer, so the excerpt never relaxes the contract.)
|
|
47
47
|
- `<instruction-set>/final-report-template.md` — the **phase-stripped** template (every other task-type's §5.x deliverable block removed by `render.py`'s `_strip_phase_blocks`, leaving only your run's §5.x). Do **NOT** also pull the full `templates/reports/final-report.template.md` source (it re-adds ~330 lines of other phases' deliverables and is not in the task bundle).
|
|
48
48
|
9. A one-line MCP pointer instead of the verbatim block (redundant — the brief is already in the report-writer's Required reading, item 8): `**MCP servers:** follow the task brief's "## Available MCP Servers" section (already in your Required reading).`
|
|
49
49
|
10. The convergence classifications (Full/Partial/Contested/Worker-Unique), the round history data (`roundHistory[]`), the `round2SkippedReason` value, and pointers to all worker result files under `worker-results/`. The report-writer worker populates `crossVerification.roundHistory` in the 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.
|
|
@@ -27,6 +27,8 @@ Okstra tasks use one lead plus the exact worker assignments selected in the prep
|
|
|
27
27
|
|
|
28
28
|
**Dispatch-prompt invariant.** Lead's dispatch prompt body for Claude / Codex / Antigravity workers MUST be byte-identical except for the role label and any wrapper-specific path headers (e.g. `**Worktree:**`, `**Errors sidecar path:**`). Lead MUST NOT bias the brief by inserting per-worker emphasis sentences ("you focus on X") into the body. Bias-by-prompt reproduces the historical failure mode where Claude commented only on assumptions, Codex only on code paths, and Antigravity only on requirements — leaving convergence with nothing to converge on.
|
|
29
29
|
|
|
30
|
+
Disjoint initial scopes are invalid triangulation. Every selected analysis worker owns the same common verification requirements; provider diversity supplies independent observations, not separate coverage slices. Do not shard the common scope by worker, provider, or model. Worker-specific depth belongs only in the non-voting Specialization Lens after the shared analysis is complete.
|
|
31
|
+
|
|
30
32
|
### Model Assignment Rules
|
|
31
33
|
|
|
32
34
|
1. `resultContract.requiredWorkerRoles` in `task-manifest.json` (and the lead model metadata) is the canonical source. There is no role-level fallback — a missing assignment is a manifest defect, not a license to invent one.
|
|
@@ -63,7 +65,13 @@ Only workers selected from `recommendedWorkers` in `task-manifest.json` and `res
|
|
|
63
65
|
|
|
64
66
|
Every worker prompt MUST start with the anchor headers rendered by `okstra_ctl.worker_prompt_headers.worker_prompt_headers()` (the generating SSOT — never hand-author or reorder them). Their meaning and extraction rules for workers are documented in the worker preamble §"Anchor headers". Phase-specific extra headers (implementation worktree, final-verification target snapshot, improvement-discovery grilling log) are listed there too.
|
|
65
67
|
|
|
66
|
-
The
|
|
68
|
+
The `**Coding preflight pack:**` anchor is emitted only when `taskType == implementation`. A pane display role named `verifier` during `final-verification` is still an initial analysis worker; it neither receives implementation conventions nor becomes a Phase 5.5 reverify dispatch. Reverify is identified by the `-reverify-r<N>-` prompt/result path contract in [convergence](./convergence.md).
|
|
69
|
+
|
|
70
|
+
The body must include: role name, task type, task key, assigned model, output contract, evidence handling rules, and `analysis-packet.md` as the single primary analysis input. For `final-verification`, the compact target anchors and that packet path are sufficient; do not copy the full diff stat, source/fallback path inventory, profile sections, report-output sections, or lead self-review into the initial prompt.
|
|
71
|
+
|
|
72
|
+
A `final-verification` prompt may contain one optional `## Run-specific directive` section for a true run delta. It is limited to 40 nonblank lines; the nonblank prompt body excluding common/target anchors is limited to 96 lines. Larger shared material belongs in the instruction set and analysis packet. Before initial dispatch, the selected adapter validates each prompt and requires byte-identical normalized analysis bodies after removing only worker/model/role and worker-specific artifact-path metadata.
|
|
73
|
+
|
|
74
|
+
The Phase 7 run validator enforces the same rule against the persisted prompt paths recorded by the run manifest and team-state. `validators/validate-run.py` calls `_validate_final_verification_initial_prompts`, which delegates parsing and normalized equality to `okstra_ctl.worker_prompt_contract`; it ignores the report writer and `-reverify-r<N>-` prompts because they belong to different phases of the protocol. A dispatch-time bypass therefore remains a run-level contract violation.
|
|
67
75
|
|
|
68
76
|
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.
|
|
69
77
|
|
|
@@ -78,7 +86,7 @@ Send byte-identical dispatch prompts to every analysis worker per the "Dispatch-
|
|
|
78
86
|
The lead does NOT inline `[Required reading]` or `[Error reporting]` blocks into worker prompts. Both contracts live in a single canonical file at `~/.okstra/templates/worker-prompt-preamble.md` (source: `templates/worker-prompt-preamble.md`). The lead injects the path via the `**Worker Preamble Path:**` anchor header and each worker Reads that file end-to-end before producing output.
|
|
79
87
|
|
|
80
88
|
What the lead MUST still do per dispatch:
|
|
81
|
-
- 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 analysis workers, list `analysis-packet.md` as the
|
|
89
|
+
- 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.
|
|
82
90
|
- Inject the absolute `**Errors log path:**` and `**Errors sidecar path:**` anchor headers — workers cannot synthesize these paths.
|
|
83
91
|
- Omit the preamble pointer for reverify dispatches (Phase 5.5 lightweight mode) — see [convergence](./convergence.md) "Reverify prompt: required-reading suppression".
|
|
84
92
|
|
|
@@ -86,7 +94,7 @@ Audience-scoped file enumeration (performance optimization — mandatory):
|
|
|
86
94
|
|
|
87
95
|
| Recipient | Files the lead lists under `## Inputs` |
|
|
88
96
|
|---|---|
|
|
89
|
-
| Claude / Codex / Antigravity analysis workers | `analysis-packet.md` as primary input; source/fallback
|
|
97
|
+
| Claude / Codex / Antigravity analysis workers | `analysis-packet.md` as primary input; for `final-verification`, no source/fallback list is copied into the prompt |
|
|
90
98
|
| Report writer worker (Phase 6) | task-brief, analysis-profile, analysis-material, reference-expectations, clarification-response (if carry-in), **plus** the instruction-set-local `final-report-template.md` (phase-stripped) and `final-report-schema.json` (per-task-type excerpt) — NOT the full `templates/reports/...` / `schemas/...` sources |
|
|
91
99
|
| Reverify dispatches | none — the lead provides only the items to reverify |
|
|
92
100
|
|
|
@@ -118,6 +126,9 @@ After each worker subagent returns (regardless of role), Lead MUST verify the ca
|
|
|
118
126
|
- The wrapper subagent returned an explicit `*_RESULT_MISSING` sentinel (codex-worker / antigravity-worker step 8c — `CODEX_RESULT_MISSING` / `ANTIGRAVITY_RESULT_MISSING`).
|
|
119
127
|
- The result file is absent at the resolved absolute path even though the worker returned without a `*_RESULT_MISSING` sentinel — for example, claude-worker returned its final assistant message but never persisted the artifact, or the wrapper exited 0 and the codex/antigravity sub-agent forwarded raw stdout despite the contract.
|
|
120
128
|
- The result file exists but cannot be parsed (frontmatter unreadable, sections 1–5 entirely missing). A truncated file in the middle of section 5 is NOT covered here — it goes to the validator's regular `error` path, not the retry path.
|
|
129
|
+
- `okstra worker-liveness` reports the worker `did-not-launch` — neither `<prompt-path>.log` nor `<prompt-path>.status.json` exists past the launch grace (default 60s). The wrapper writes its status sidecar before invoking the CLI and hard-fails loudly with a distinct exit code on every argument check before that, so the absence of BOTH artifacts means the dispatch itself never reached the script. Without this trigger the only evidence was a lead noticing two missing files by eye, and the run paid the full polling cap for a worker that never started.
|
|
130
|
+
- `okstra worker-liveness` reports the worker `stalled` — its audit sidecar's newest `- PROGRESS:` heartbeat is older than the cadence budget, or the sidecar carries no heartbeat at all. This is the in-process equivalent of the CLI wrappers' idle watchdog: the wrapper reaps a silent CLI itself, but nothing reaped a silent `claude-worker` until its deadline.
|
|
131
|
+
- The result file exists but its audit sidecar does not, at `runs/<task-type>/worker-results/<worker>-audit-<task-type>-<seq>.md`. Workers write both in the same step, so a result without a sidecar means the Reading Confirmation block — the only evidence the worker read its inputs — was never produced. `validate-run.py` fails the run on this at Phase 7 either way (`validate_worker_results_audit`); checking it here spends the existing one-retry budget while the role can still be re-dispatched, instead of surfacing hours later when the worker session is gone.
|
|
121
132
|
|
|
122
133
|
**One-retry policy:**
|
|
123
134
|
|
|
@@ -29,18 +29,22 @@
|
|
|
29
29
|
- **whole-task is a mutating phase, not a read-only one.** On entry, whole-task mode auto-merges (with `--no-ff`) the done stages not yet merged into the task branch to create an integration commit, then removes the cleanupable stage worktrees, registry stage-keys, and stage branches. If a merge conflict occurs it reports the conflicting files and aborts (the user resolves them manually and retries). A stage worktree with uncommitted changes remaining is preserved. Therefore the "fully-merged, clean target" the entry gate above refers to is the state after this auto-integration step completes, and whole-task final-verification must be treated as a mutating phase that creates the integration commit.
|
|
30
30
|
- **single-stage scope** (`--stage N`): prep verified stage N is `status:done` and its isolated stage worktree exists and is clean. Other stages' state is irrelevant. A single-stage run is a partial verification: it MUST NOT recommend plain `release-handoff`, but MAY recommend `release-handoff(stage-group)` when the verdict is `accepted` — the stage becomes PR-eligible for a stage-group handoff.
|
|
31
31
|
- the lead still captures `git status --short` from the injected worktree to confirm the analysis ran against the delivered work-tree state; an unexpected divergence (dirty tree outside `.okstra/`, missing worktree) is a `tool-failure`, not a silent proceed.
|
|
32
|
+
- Worker verification procedure:
|
|
33
|
+
- **Target confirmation:** verify the inline worktree, scope, base/head refs, target path, and digest against `verification-target.md` before analysis. Use the sidecar's stage/report mapping and complete diff stat; a missing sidecar, digest mismatch, dirty worktree outside `.okstra/`, or wrong head is a `tool-failure`, never a silent target reselection.
|
|
34
|
+
- **Evidence:** attach file:line, exact command + exit code, log excerpt, or MCP SELECT evidence to every finding. Mark a requirement as covered only when the cited artifact demonstrates it.
|
|
35
|
+
- **Tier 1 and Tier 2 read-only validation:** Tier 1 is the originating brief/approved plan `validation` set; Tier 2 is `<PROJECT_ROOT>/.okstra/project.json` `qaCommands`. Do not auto-detect commands from package manifests. A missing tier is `qa-command not configured: <category>`. Before execution, reject commands containing source/lockfile mutation tokens such as `--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, or `npm install` without `ci`; record the exact denied token.
|
|
36
|
+
- **Tier 3 stage conformance:** read `<task_root>/qa/conformance-manifest.json`. In whole-task scope, run every non-exempt `entries[].runCommand` against the merged worktree; in single-stage scope, run only the matching `stageKey`. Use `qaEnv` only with a replica/test datastore, refresh `result-<stageKey>.json`, and interpret the exit code plus the last `QA-RESULT: PASS|FAIL` marker. Missing/non-PASS evidence is an acceptance-blocker recommendation. An exemption or user waiver is not executed; record its reason, and surface a waiver as a `conditional-accept` recommendation.
|
|
37
|
+
- **Manual user test:** read only the source implementation report's `implementation.manualUserTest`. Execute reproducible steps and record `pass`, `fail`, or `blocked` with observed evidence. Human-only or environment-unavailable steps remain `blocked` with the exact reason. Reaffirm an `applicable=false` exemption; do not execute planning `designPreparation` or manual-test PREP items directly.
|
|
38
|
+
- **Design-preparation carry-in:** read only source implementation report `missingInformation` rows whose `source` starts with `design-prep:`. Recommend `ifStillOpen: block` as an acceptance blocker and `ifStillOpen: follow-up` as residual risk. This phase does not read planning PREP sidecars directly or mutate planning snapshots.
|
|
39
|
+
- **Could-not-verify honesty:** use `not-configured`, `env-unavailable`, `rejected`, `gap`, or `blocked` as appropriate. Never convert unavailable evidence into an executed/pass claim.
|
|
40
|
+
- **Source-mutation prohibition:** verification may write only assigned okstra run artifacts. Do not edit source, schema, deployment, lockfile, or configuration files; route detected defects to a later phase.
|
|
32
41
|
- Required deliverable shape (final report, in addition to the standard sections):
|
|
33
|
-
- **Source Implementation Report(s)**: the `VERIFICATION_TARGET` snapshot verbatim — verification scope, worktree path, base
|
|
42
|
+
- **Source Implementation Report(s)**: the `VERIFICATION_TARGET` snapshot verbatim — verification scope, worktree path, base/head refs, the list of stages under verification, and one row per stage citing its originating implementation final-report (`report_path` from `consumers.jsonl`; render `(report_path unrecorded)` when absent). Every analyser prompt carries the same compact target identity (`**Verification scope:** / **Worktree:** / **Verification base ref:** / **Verification head ref:** / **Verification target path:** / **Verification target digest:**`) and reads the sidecar on demand for the complete diff stat. A worker that cannot confirm its analysis ran against that worktree's delivered diff MUST record a `tool-failure`.
|
|
34
43
|
- **Verdict vocabulary**: Section 7 (`Final Verdict`) MUST include a `Verdict Token` field whose value is exactly one of `accepted`, `conditional-accept`, or `blocked`. `conditional-accept` requires an explicit, exhaustive list of conditions; ambiguous verdicts ("looks good", "mostly ready") are not allowed. Each condition MUST be recorded as a row in the **Conditional Acceptance Conditions** deliverable (`id` `CA-NNN`, `condition`, `evidenceRequired`, `blocksReleaseHandoff`). The validator enforces verdict↔deliverable consistency: `accepted` ⇒ zero acceptance blockers, `blocked` ⇒ at least one, `conditional-accept` ⇒ at least one condition, and a `release-handoff` routing recommendation is allowed only when the verdict is `accepted`. **Any Acceptance Blocker therefore forces the verdict off `accepted` (to `conditional-accept` or `blocked`); the gates below cite this rule instead of restating the arithmetic.**
|
|
35
44
|
- **Acceptance Blockers block** (under section 4): one row per blocker with `id`, `severity` (`critical` / `major` / `minor`), evidence (file path, log excerpt, or test output), and the recommended follow-up phase (`error-analysis` or `implementation-planning`). Empty block is acceptable and preferred — render the single line `- No acceptance blockers found.`
|
|
36
45
|
- **Residual Risk block** (under section 4): risks that are not blockers but should be tracked, each with mitigation owner and a trigger that would escalate them to a blocker.
|
|
37
46
|
- **Validation Evidence**: for every requirement in the originating plan or task brief, cite the artifact (commit SHA, test output, log line, MCP SELECT result) that demonstrates coverage. Paraphrased "verified" claims without an artifact are rejected.
|
|
38
47
|
- **Read-only command log**: any pre-existing test/validation command touched during this run MUST be listed with its exact command line and one honest status — `executed` (ran; carries its exit code) / `env-unavailable` (should run but cannot in this environment — missing replica DB, container, or service; carries the reason, never a faked pass) / `not-configured` (no such qa-command tier) / `rejected` (a mutating/denied token — skipped, carries the denied token). A check that could not run locally is recorded as `env-unavailable` with the reason — never silently dropped and never reported as `executed` with an invented exit code. Mutating-command prohibition is the shared read-only boundary (see Non-goals); it is not restated per row.
|
|
39
|
-
- **Two-tier command lookup (shared with `implementation`):** when this phase performs its own independent re-validation, the command source is exactly the same two tiers `implementation` verifiers use — Tier 1 is the originating task brief / approved plan's `validation` set, Tier 2 is `<PROJECT_ROOT>/.okstra/project.json` under `qaCommands`. Auto-detecting tools from manifest files is forbidden; missing tiers are recorded as `qa-command not configured: <category>` rather than guessed. The `cmd` deny-list (`--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, `npm install` without `ci`, etc.) is enforced identically. NOTE: runtime fail-fast validation (`okstra_ctl.qa_commands.validate_qa_commands`) only fires at `--task-type implementation` run-prep, so this phase MUST self-check each `qaCommands` entry against the deny-list before executing it — if a denied token is present, skip the command and record it as a `Read-only command log` line `qa-command rejected (denied token: <token>): <label>`.
|
|
40
|
-
- **Tier 3 — stage conformance scripts (whole-task union):** because this phase verifies the **integrated, merged** state, it re-runs conformance against that state rather than per-stage. Read the task-level manifest `<task_root>/qa/conformance-manifest.json` (the directory is the `TASK_QA_PATH` token) and, in **whole-task scope**, run the `runCommand` of **every** `entries[]` item against the merged worktree, refreshing each `<task_root>/qa/result-<stageKey>.json` (`{ "stageKey", "overall": "PASS"|"FAIL"|"MISSING", "ranAt", "requirements" }`). In **single-stage scope**, run only the entry whose `stageKey` matches the verified stage. An entry carrying an `exemption` or user `waiver` is NOT executed — record the skip and reason; a `waiver` becomes a `conditional-accept` condition surfaced in the section 7 Verdict (conformance left unverified by user acknowledgement). Each `runCommand` runs in the worktree cwd with `qaEnv` env (replica DB DSN / app base URL / env file) — **replica / test environment only**, never shared / staging / prod, and the same source/lockfile mutation deny-list applies (a conformance script MAY mutate only its `qaEnv` replica datastore). Interpret each result from the exit code + stdout `QA-RESULT: PASS|FAIL` (last wins) and `REQ <id>: PASS|FAIL: <reason>` lines; no `QA-RESULT` marker → `MISSING`. Any entry whose result is not `PASS` (including `MISSING` or a never-run/missing sidecar) is an **Acceptance Blocker** (`major`+). This is the same gate the `validate-run.py` Tier 3 check enforces on the result sidecars.
|
|
41
|
-
- **Manual user test results**: take each item from the source implementation report's §5.7.9 Manual User Test (Draft), execute the ones reproducible in this environment (e.g. `/okstra-container-build`, which runs `okstra container up`, then the documented steps), and record `result` (`pass` / `fail` / `blocked`) + observed value in §5.8.7 (data field `finalVerification.manualUserTest`). Steps that need human-only interaction this run cannot perform are recorded as `blocked` with the reason (handed to the user), never silently skipped. A failed manual test is an Acceptance Blocker. If the draft was an exemption (`applicable=false`), reaffirm the reason in one line (`applicable=false` + `exemptionReaffirm`).
|
|
42
|
-
For manual testing, read only the source implementation report's `implementation.manualUserTest`. Do not execute planning `designPreparation` / `manual-user-test` PREP items directly. The implementation report has already reconciled the approved seed with the actual diff. Record reproducible results; human-only or unavailable environments become `result: blocked` with the exact reason.
|
|
43
|
-
- **Non-manual design-preparation handoff**: read only source implementation report `missingInformation` rows whose `source` starts with `design-prep:`. Record `ifStillOpen: block` as an acceptance blocker and `ifStillOpen: follow-up` as residual risk. This phase does not read planning PREP sidecars directly, merge their inputs, or mutate the planning snapshot.
|
|
44
48
|
- **Could-not-verify roll-up (§5.8.9)**: the template mechanically aggregates every not-confirmed check into one scannable list — `gap` requirement-coverage rows, `not-configured` / `env-unavailable` / `rejected` command rows, and `blocked` manual tests. You do not hand-author it, but you MUST give those rows their honest status so nothing unverified hides across sections: a check silently recorded as `executed`/`covered` will not surface in the roll-up. This is okstra's answer to "say what could not be verified this run."
|
|
45
49
|
- **Routing recommendation**: the next safe phase — one of `release-handoff`, `done`, `error-analysis`, `implementation-planning` — tied to the verdict and blocker list. `release-handoff` is allowed ONLY when the Verdict Token is `accepted`. `release-handoff` is additionally allowed ONLY when the verification scope (the `Verification scope:` line of the injected `VERIFICATION_TARGET` block, recorded as the report's `verificationScope` field) is `whole-task`; a `single-stage` accepted run routes to `release-handoff(stage-group)` (or `implementation` / `done`); plain `release-handoff` remains whole-task-only. Enforcement: `validators/validate-run.py` rejects a `single-stage` report whose routing cites plain `release-handoff`.
|
|
46
50
|
- **Verified-row recording** (single-stage scope only): when the Verdict Token is `accepted`, the lead MUST run `okstra handoff record-verified --plan-run-root <plan-run-root> --stage <N> --report-path <final-report.md path> --data-json <final-report data.json path>` and quote the command + exit code in the report. The helper re-validates taskType/scope/verdict from data.json, so a non-accepted or whole-task report is rejected at the tool layer.
|