okstra 0.130.2 → 0.130.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/docs/architecture.md +7 -1
  2. package/docs/cli.md +10 -2
  3. package/docs/project-structure-overview.md +16 -9
  4. package/docs/task-process/implementation-planning.md +11 -5
  5. package/package.json +1 -1
  6. package/runtime/BUILD.json +2 -2
  7. package/runtime/agents/workers/antigravity-worker.md +3 -1
  8. package/runtime/agents/workers/claude-worker.md +1 -1
  9. package/runtime/agents/workers/codex-worker.md +3 -1
  10. package/runtime/agents/workers/report-writer-worker.md +8 -4
  11. package/runtime/prompts/lead/adapters/claude-code.md +2 -2
  12. package/runtime/prompts/lead/convergence.md +44 -21
  13. package/runtime/prompts/lead/okstra-lead-contract.md +9 -2
  14. package/runtime/prompts/lead/plan-body-verification.md +55 -12
  15. package/runtime/prompts/lead/report-writer.md +16 -15
  16. package/runtime/prompts/lead/team-contract.md +8 -6
  17. package/runtime/prompts/profiles/implementation-planning.md +10 -2
  18. package/runtime/python/okstra_ctl/codex_dispatch.py +5 -1
  19. package/runtime/python/okstra_ctl/convergence.py +121 -1
  20. package/runtime/python/okstra_ctl/convergence_engine.py +903 -13
  21. package/runtime/python/okstra_ctl/convergence_migration.py +9 -1
  22. package/runtime/python/okstra_ctl/dispatch_core.py +13 -0
  23. package/runtime/python/okstra_ctl/plan_items.py +203 -0
  24. package/runtime/python/okstra_ctl/plan_items_cli.py +81 -0
  25. package/runtime/python/okstra_ctl/worker_artifact_paths.py +23 -0
  26. package/runtime/python/okstra_ctl/worker_liveness.py +4 -4
  27. package/runtime/python/okstra_ctl/worker_prompt_body.py +1 -1
  28. package/runtime/python/okstra_ctl/worker_prompt_contract.py +2 -1
  29. package/runtime/python/okstra_ctl/worker_prompt_headers.py +14 -0
  30. package/runtime/schemas/convergence-critic-results-v1.0.schema.json +57 -0
  31. package/runtime/schemas/convergence-groups-v1.0.schema.json +109 -0
  32. package/runtime/schemas/convergence-round-results-v1.0.schema.json +55 -0
  33. package/runtime/schemas/final-report-v1.0.schema.json +21 -2
  34. package/runtime/templates/report-writer-prompt-preamble.md +5 -3
  35. package/runtime/templates/reports/final-report.template.md +1 -1
  36. package/runtime/templates/worker-prompt-preamble.md +8 -7
  37. package/runtime/validators/validate-run.py +154 -118
  38. package/runtime/validators/validate_session_conformance.py +66 -23
  39. package/src/cli-registry.mjs +7 -0
  40. package/src/commands/execute/convergence.mjs +6 -1
  41. package/src/commands/execute/plan-items.mjs +9 -0
  42. package/src/commands/inspect/worker-liveness.mjs +2 -2
@@ -324,10 +324,16 @@ All selected initial prompt bodies must normalize to the same semantic content.
324
324
 
325
325
  `ConvergenceEngine` owns deterministic state transitions after Round 0 grouping: queue membership, roster-aware dispatch plans, vote reduction, classification, round history and limits, skip reasons, final state, and classification counts. The lead retains semantic grouping and evidence interpretation because those operations require judgment. Runtime adapters are transport-only: they dispatch the persisted batch and return structured terminal outcomes without recalculating engine state.
326
326
 
327
- The lead writes the grouped input, then advances it through `okstra convergence seed`, `plan-round`, `apply-round`, `finalize`, and `validate`. For worker W, each generated dispatch excludes findings originating from W; resolved findings leave the queue permanently. Lightweight reverify receives only its current persisted batch and embedded evidence, not the original analysis packet, profile, brief, or instruction set. Terminal worker non-results become `verification-error`; the engine never fabricates a `DISAGREE` vote. The report writer does not vote and may consume only a validated terminal v1.2 final state.
327
+ The complete artifact lifecycle is: worker results Round 0 grouping reducer queues only non-consensus findings analyser-instance re-verification optional critic gap reducer transition report-writer data.json deterministic plan-item extraction plan-body verifier round final render/validation. Initial workers write their findings to the assigned Result Path and their reading confirmation to the generated `Audit sidecar path`; the sidecar is evidence of input reading, not a source of findings.
328
+
329
+ Cross-verification does not mean that worker A reviews worker B's entire result. Round 0 records multi-source agreement immediately, and the reducer asks independent analyser instances to vote only on single-source or still-unresolved findings selected in the persisted queue. The report writer is never a voter. It organizes the validated result, while the later plan-body round verifies the consolidated `P-*` plan items rather than reopening the `F-*` finding queue.
330
+
331
+ The lead writes the grouped input, then advances it through the internal admin CLI operations `okstra convergence seed`, `plan-round`, `apply-round`, optional `apply-critic-gaps`, `finalize`, and `validate`. For worker W, each generated dispatch excludes findings originating from W; resolved findings leave the queue permanently. Lightweight reverify receives only its current persisted batch and embedded evidence, not the original analysis packet, profile, brief, or instruction set. Terminal worker non-results and completed per-finding `UNVERIFIABLE` responses become `verification-error`; the engine never fabricates a `DISAGREE` vote. The report writer does not vote and consumes only a validated terminal state. Newly finalized convergence output is schema v1.3; under the compatibility path, valid historical final schema versions v1.0, v1.1, or v1.2 are reused and consumed without rewrite.
328
332
 
329
333
  Coverage critic and plan-body verification remain separate from finding convergence. The critic audits the integrated Round 0 analysis, while implementation-planning's plan-body gate validates the later report draft through its own `P-*` queue and state file. Neither path changes the engine's `F-*` queue.
330
334
 
335
+ `okstra convergence` and `okstra plan-items` are lead-contract admin interfaces, not public skill entry points. The former `okstra-convergence` skill stays obsolete; these commands do not expand the installed public skill roster.
336
+
331
337
  Seeding provides the migration boundary for in-progress runs. A valid terminal legacy final is reused unchanged. A matching valid working state resumes. A malformed or partial legacy state is archived byte-for-byte under `state/migrations/` before an explicit Round 0 restart; an invalid new-engine working state fails closed until `--restart-from-round0` is supplied. Replacement of an existing final is allowed only while its recorded archive still matches the original bytes.
332
338
 
333
339
  ## Stable task identity
package/docs/cli.md CHANGED
@@ -639,8 +639,12 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
639
639
  | `okstra convergence seed --groups <path> --work-state <path> --final-state <path> --migration-dir <dir> [--restart-from-round0]` | Create, resume, reuse, or explicitly recover deterministic convergence state |
640
640
  | `okstra convergence plan-round --work-state <path> --plan <path>` | Persist the next roster-aware dispatch plan without mutating working state |
641
641
  | `okstra convergence apply-round --work-state <path> --plan <path> --results <path>` | Validate one complete structured result set and atomically reduce it into working state |
642
- | `okstra convergence finalize --work-state <path> --output <path>` | Materialize the terminal schema v1.2 convergence state |
642
+ | `okstra convergence apply-critic-gaps --work-state <path> --results <path>` | Apply one verified coverage-critic batch after the main queue reaches a terminal state |
643
+ | `okstra convergence finalize --work-state <path> --output <path>` | Materialize the terminal schema v1.3 convergence state |
643
644
  | `okstra convergence validate --state <path> --kind <working\|final>` | Validate replayable working state or a terminal final state |
645
+ | `okstra convergence example --kind <groups\|round-results\|critic-results>` | Print one deterministic valid input example as JSON |
646
+ | `okstra plan-items extract --data <data.json> --output <items.json>` | Deterministically extract the complete implementation-planning `P-*` queue from report-writer data.json |
647
+ | `okstra plan-items validate --data <data.json> --items <items.json>` | Require the persisted `P-*` queue to match a fresh deterministic extraction exactly |
644
648
  | `okstra config <get\|set\|unset\|show> [key] [value] [--scope project\|global\|all]` | Manage persistent settings such as `pr-template-path` with atomic JSON writes |
645
649
  | `okstra memory <add\|list\|search\|show\|archive>` | Manage global conversation memory in `~/.okstra/memory-book`, a user-home store separate from project `.okstra/` and the CLI basis of the `save this in okstra` natural-language skill |
646
650
  | `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 |
@@ -670,7 +674,11 @@ The `okstra` Node CLI (`bin/okstra`) provides both installer/admin commands and
670
674
  | `okstra wizard <init\|step\|render-args\|confirmation\|outcome> --state-file <path>` | Interactive input state machine for okstra-run, implemented by `okstra_ctl.wizard`. Seed a state file with `init`, then repeatedly call `step --answer <val>` to receive the next `Prompt` JSON. `--answer` is **required**; use `--no-submit` to peek at the next prompt without submitting a response. `render-args` returns the final `render-bundle` argument map, and `confirmation` returns the user echo block. On a completed wizard, `outcome` returns `renderArgs`, `persistActions`, and `confirmationText` together; project/global release-handoff PR-template persistence appears as `persistActions[].command == "config.set"`. For an `implementation` task type, `stage_pick` follows `approved_plan_pick` and selects the stage before `executor_pick`. The brief step appears only for entry task types—requirements-discovery, error-analysis, and improvement-discovery. Downstream phases automatically carry the manifest brief, with a three-option `brief_carry` fallback when none is registered; `release-handoff` has no brief and enters multi-select `handoff_stage_pick` for eligible stage groups or the whole task |
671
675
  | `okstra token-usage ...` | Wrap the installed `okstra-token-usage.py` to collect and substitute run token usage. Session JSONL is incrementally scanned by default through a byte-cursor cache at `$OKSTRA_HOME/cache/token-usage/`; `--no-cache` bypasses the cache and forces a full rescan as an accuracy fallback |
672
676
 
673
- `okstra convergence` is an internal admin CLI used by the lead protocol, not a user-facing skill. The former `okstra-convergence` skill remains obsolete; the installed `prompts/lead/convergence.md` contract tells the lead when to invoke these operations.
677
+ The convergence state lifecycle is `groups v1.0 work v1.0 final v1.3`; round-plan, round-results, and optional critic-results v1.0 artifacts provide the auditable transitions between those endpoints.
678
+
679
+ `okstra convergence apply-critic-gaps` is the only transition that may add verified coverage gaps to terminal main-queue state. `okstra plan-items extract` creates the complete plan queue, and `okstra plan-items validate` rejects any omission or drift before verifier dispatch.
680
+
681
+ `okstra convergence` and `okstra plan-items` are internal admin CLI families used by the lead protocol. Each is an internal admin CLI, not a user-facing skill, and their presence does not add a public skill. The former `okstra-convergence` skill remains obsolete; the installed `prompts/lead/convergence.md` and `prompts/lead/plan-body-verification.md` contracts tell the lead when to invoke these operations.
674
682
 
675
683
  > Every subcommand is wired to `PYTHONPATH` and `~/.okstra/lib/python` by the Python helper (`src/lib/python-helper.mjs`) spawned by `bin/okstra`. When invoking `python3 -m okstra_ctl.*` directly, you must configure `PYTHONPATH` yourself.
676
684
 
@@ -107,12 +107,14 @@ So `runtime/` is not the only npm-published content. It is the install payload c
107
107
  | `scripts/okstra_ctl` | `runtime/python/okstra_ctl` |
108
108
  | `scripts/okstra_token_usage` | `runtime/python/okstra_token_usage` |
109
109
  | `scripts/okstra_vendor` | `runtime/python/okstra_vendor` |
110
- | `scripts/lib` | `runtime/python/lib` |
110
+ | `scripts/lib` | `runtime/bin/lib` |
111
111
  | selected `scripts/okstra*.sh` / `*.py` entrypoints | `runtime/bin/` |
112
112
  | `agents`, `prompts`, `schemas`, `templates`, `validators`, `skills` | same relative tree under `runtime/` |
113
113
 
114
114
  `npm run build` and `prepack` both run this sync. Never hand-edit `runtime/`.
115
115
 
116
+ The public manuals under `docs/` are selected directly by `package.json` for npm publication; npm-published source documentation is not copied into `runtime/docs/`. The install payload receives the operating contracts it needs through the explicit `prompts/`, `templates/`, and `schemas/` sync rows above.
117
+
116
118
  Runtime/install asset changes follow this checklist:
117
119
 
118
120
  1. Add or edit the source asset under `scripts/`, `skills/`, `agents/`, `prompts/`, `schemas`, `templates`, or `validators`.
@@ -177,7 +179,8 @@ Runtime/install asset changes follow this checklist:
177
179
  | `run` | `src/commands/execute/run.mjs` | Host-aware execution front door (`auto` → Claude/Codex/external path selection) |
178
180
  | `codex-run`, `codex-dispatch` | `src/commands/execute/codex-*.mjs` | Codex lead dry-run bundle preparation and CLI-backed worker dispatch |
179
181
  | `team` | `src/commands/execute/team.mjs` | External lead tmux-pane worker dispatch / await / teardown |
180
- | `convergence` | `src/commands/execute/convergence.mjs` | Internal admin CLI for the deterministic Phase 5.5 convergence engine (`seed`/`plan-round`/`apply-round`/`finalize`/`validate`; Python: `okstra_ctl.convergence`) |
182
+ | `convergence` | `src/commands/execute/convergence.mjs` | Internal admin CLI for the deterministic Phase 5.5 convergence engine (`seed`/`plan-round`/`apply-round`/`apply-critic-gaps`/`finalize`/`validate`/`example`; Python: `okstra_ctl.convergence`) |
183
+ | `plan-items` | `src/commands/execute/plan-items.mjs` | Internal admin CLI for deterministic plan-body item extraction and exact-match validation (`extract`/`validate`; Python: `okstra_ctl.plan_items_cli`) |
181
184
  | `report-finalize` | `src/commands/report/finalize.mjs` | Run the whole Phase 7 post-report sequence in contractual order (Python: `okstra_ctl.report_finalize`) — the single reference point shared with the Codex lead adapter |
182
185
  | `render-views` | `src/commands/report/render-views.mjs` | Generate the self-contained HTML report view |
183
186
  | `render-final-report`, `inject-report-index` | `src/commands/report/*.mjs` | Render final-report Markdown from data.json and inject top-of-report index anchors |
@@ -287,9 +290,10 @@ Important modules:
287
290
  | `worker_prompt_body.py` | provider-neutral initial analysis body/input renderer shared by Codex and external/team dispatch paths |
288
291
  | `worker_prompt_policy.py` | `PromptPlan` generating SSOT — resolves functional audience, equality group, packet-only input, coding-preflight eligibility, required headers, and size limits without consulting provider/model identity |
289
292
  | `worker_prompt_contract.py` | deterministic cross-task initial-prompt validator and normalized cross-worker equality SSOT, reused by dispatch adapters and the Phase 7 persisted-artifact validator |
290
- | `convergence_engine.py` | pure `ConvergenceEngine` reducer — seeds Round 0 working state, plans roster-aware rounds, applies structured outcomes, finalizes schema v1.2, and validates replayable state without dispatch or filesystem ownership |
293
+ | `convergence_engine.py` | pure `ConvergenceEngine` reducer — seeds Round 0 working state, plans roster-aware rounds, applies structured outcomes and one critic-gap batch, finalizes schema v1.3, and validates replayable state without dispatch or filesystem ownership |
291
294
  | `convergence_store.py`, `convergence_migration.py` | atomic JSON persistence plus legacy/new-engine seed decisions; valid terminal finals are reused, while invalid state requires byte-preserving archival before restart |
292
- | `convergence.py` | `okstra convergence` internal CLI orchestration for `seed`, `plan-round`, `apply-round`, `finalize`, and `validate`; it composes the reducer, store, and migration policy without duplicating their decisions |
295
+ | `convergence.py` | `okstra convergence` internal CLI orchestration for `seed`, `plan-round`, `apply-round`, `apply-critic-gaps`, `finalize`, `validate`, and `example`; it composes the reducer, store, and migration policy without duplicating their decisions |
296
+ | `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 |
293
297
  | `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 |
294
298
  | `wrapper_status.py` | worker wrapper status sidecar reader — the host-side reader of the sidecar written by `okstra-wrapper-status.py` (the heartbeat writer) |
295
299
  | `task_target.py` | shared helper resolving `task-key → (task_root, project_root)` (`resolve_task_root`) |
@@ -341,6 +345,8 @@ Token/cost accounting:
341
345
 
342
346
  `schemas/final-report-v1.0.schema.json` is the final-report data.json contract. The report-writer worker writes `final-report-<task-type>-<seq>.data.json`; the renderer produces Markdown from that JSON.
343
347
 
348
+ The deterministic convergence inputs are `schemas/convergence-groups-v1.0.schema.json`, `schemas/convergence-round-results-v1.0.schema.json`, and `schemas/convergence-critic-results-v1.0.schema.json`. `tools/build.mjs` syncs the entire source `schemas/` directory to `runtime/schemas/`; these JSON Schema files are runtime contracts, not Markdown publication-inventory entries.
349
+
344
350
  Optional (v1.0 backward-compatible) top-level keys:
345
351
 
346
352
  - `readerSummary` — the summary block a human reads first. When present, all five fields (`decision`, `humanActionRequired`, `blockingItems`, `safeToSkip`, `recommendedCommand`) are required. Existing data.json without it still renders as-is (the HTML dashboard falls back to `verdictCard`).
@@ -444,12 +450,13 @@ Single-stage `final-verification --stage <N>` reuses the matching implementation
444
450
 
445
451
  Current report pipeline:
446
452
 
447
- 1. Analysis workers write worker result files.
448
- 2. The lead writes semantic groups; the convergence engine persists working state, per-round plans/results, and then a validated `state/convergence-<task-type>-<seq>.json` v1.2 terminal state.
453
+ 1. Analysis workers write worker result files and the separate audit sidecars named by `Audit sidecar path`.
454
+ 2. The lead writes semantic groups; the convergence engine persists working state, per-round plans/results, an optional critic transition, and then a validated `state/convergence-<task-type>-<seq>.json` terminal state: schema v1.3 when newly finalized, or an unchanged historical final schema v1.0, v1.1, or v1.2 returned by `reuse-final`.
449
455
  3. Report-writer worker writes `reports/final-report-<task-type>-<seq>.data.json`.
450
- 4. `scripts/okstra-render-final-report.py` renders Markdown.
451
- 5. Token usage substitution fills usage/cost cells.
452
- 6. `scripts/okstra-render-report-views.py` emits the self-contained `.html` view.
456
+ 4. For implementation-planning, `okstra plan-items extract` creates the complete `P-*` queue, `validate` proves it still matches data.json, and the analyser instances run the separate plan-body verification round.
457
+ 5. `scripts/okstra-render-final-report.py` renders Markdown.
458
+ 6. Token usage substitution fills usage/cost cells.
459
+ 7. `scripts/okstra-render-report-views.py` emits the self-contained `.html` view, and run validation checks the final artifacts.
453
460
 
454
461
  The Markdown is derived, not the authoring source. The schema is the contract.
455
462
 
@@ -64,17 +64,23 @@ flowchart TD
64
64
  P1[Phase 1 intake] --> P2[Phase 2 prompts]
65
65
  P2 --> P3[Phase 3 TeamCreate]
66
66
  P3 --> P4[Phase 4/5 analyser dispatch]
67
- P4 --> C[Phase 5.5 finding convergence]
68
- C --> RW[Phase 6 report-writer writes draft]
69
- RW --> PBV[Phase 6 sub-step<br/>Plan Body Verification]
67
+ P4 --> A[Worker results + Audit sidecar path]
68
+ A --> G[Round 0 grouping]
69
+ G --> C[Reducer queue + analyser-instance re-verification]
70
+ C --> Critic[Optional critic gap reducer transition]
71
+ Critic --> RW[Phase 6 report-writer data.json]
72
+ RW --> Extract[Deterministic plan-item extraction]
73
+ Extract --> PBV[Phase 6 sub-step<br/>Plan-body verifier round]
70
74
  PBV --> Gate{gate result}
71
75
  Gate -->|passed / passed-with-dissent| Approval[render top Approval checkbox]
72
76
  Gate -->|blocked-by-disagreement / aborted-non-result| NoApproval[render block without checkbox]
73
- Approval --> P7[Phase 7 persist]
77
+ Approval --> P7[Phase 7 persistence/finalization<br/>canonical Markdown render<br/>HTML render + validate-run<br/>via okstra report-finalize]
74
78
  NoApproval --> P7
75
79
  ```
76
80
 
77
- Plan-body verification uses a different queue from Phase 5.5. Phase 5.5 verifies worker findings, and the Phase 6 sub-step re-verifies the consolidated plan body produced by the report-writer at the `P-*` plan-item level.
81
+ The artifact sequence is worker results and `Audit sidecar path` Round 0 grouping reducer-owned finding queue → analyser-instance re-verification optional critic transition → report-writer data.json → deterministic plan-item extraction → plan-body verifier round Phase 7 persistence/finalization. Phase 7 calls `okstra report-finalize`; token substitution produces the canonical Markdown render, followed by HTML render, follow-up materialization, and `validate-run`. The reducer queues only non-consensus findings; it does not send every complete worker result to every other worker.
82
+
83
+ Plan-body verification uses a different queue from Phase 5.5. Phase 5.5 verifies worker findings, and the Phase 6 sub-step re-verifies the consolidated plan body produced by the report-writer at the `P-*` plan-item level. The lead must create that queue through `okstra plan-items extract` and prove it is complete with `okstra plan-items validate` before dispatch.
78
84
 
79
85
  ## 5. design preparation and final report gate
80
86
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.130.2",
3
+ "version": "0.130.3",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.130.2",
3
- "builtAt": "2026-07-21T14:37:40.375Z",
2
+ "package": "0.130.3",
3
+ "builtAt": "2026-07-21T14:59:13.137Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -152,7 +152,9 @@ Before invoking the Antigravity CLI, you MUST:
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
154
 
155
- The CLI writes a Reading Confirmation block to the **audit sidecar** at `runs/<task-type>/worker-results/antigravity-worker-audit-<task-type>-<seq>.md`. The sidecar's body begins with `# Antigravity Worker Audit <task-key>` followed by one short line per input file confirming end-to-end reading. Placement follows the selected audience preamble's `Required reading` section. If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
155
+ 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
+ The CLI writes a Reading Confirmation block to the absolute path extracted from the `**Audit sidecar path:**` header. The sidecar's body begins with `# Antigravity Worker Audit — <task-key>` followed by one short line per input file confirming end-to-end reading. Placement follows the selected audience preamble's `Required reading` section. If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
156
158
 
157
159
  ## Worker Output Structure
158
160
 
@@ -63,7 +63,7 @@ Before producing any output, you MUST:
63
63
 
64
64
  **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
65
 
66
- - **Where:** `runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md`.
66
+ - **Where:** the absolute path in `**Audit sidecar path:**`.
67
67
  - **Start (before the per-file reads):** immediately after extracting `Project Root` and the assigned paths, `Write` just the heading line (`# Claude Worker Audit — <task-key>`) plus one `- PROGRESS: started <ISO-8601-UTC>` line.
68
68
  - **Append per stage** (`Edit` or heredoc `>>`): `read-<filename>`, `analysis-start`, `findings-draft-start`, `findings-draft-complete`, `write-result-start`.
69
69
  - **Cadence MUST NOT exceed 5 minutes:** if a single analysis stage runs longer, emit a `- PROGRESS: in-stage:<stage> <ISO-8601-UTC>` line. A 5-minute-stale sidecar mtime is the canonical "this worker has hung" signal for the operator.
@@ -152,7 +152,9 @@ Before invoking the Codex CLI, you MUST:
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
154
 
155
- The CLI writes a Reading Confirmation block to the **audit sidecar** at `runs/<task-type>/worker-results/codex-worker-audit-<task-type>-<seq>.md`. The sidecar's body begins with `# Codex Worker Audit <task-key>` followed by one short line per input file confirming end-to-end reading. Placement follows the selected audience preamble's `Required reading` section. If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
155
+ 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
+ The CLI writes a Reading Confirmation block to the absolute path extracted from the `**Audit sidecar path:**` header. The sidecar's body begins with `# Codex Worker Audit — <task-key>` followed by one short line per input file confirming end-to-end reading. Placement follows the selected audience preamble's `Required reading` section. If any file was skipped, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
156
158
 
157
159
  ## Worker Output Structure
158
160
 
@@ -30,7 +30,7 @@ If you find yourself thinking "I'll just write the markdown directly" — stop.
30
30
 
31
31
  ## Worker Result File (MANDATORY)
32
32
 
33
- You also write an audit sidecar at the path the lead registers as `**Worker Result Path:**` (default: `runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md`). The validator checks this file exists whenever the role's terminal status is `completed`. Schema: short YAML frontmatter (`workerId: "report-writer"`, plus the canonical fields copied verbatim from `analysis-material.md` per `team-contract`) followed by:
33
+ Write the required worker-result record at the lead-registered `**Worker Result Path:**`. The validator checks this file exists whenever the role's terminal status is `completed`. Schema: short YAML frontmatter (`workerId: "report-writer"`, plus the canonical fields copied verbatim from `analysis-material.md` per `team-contract`) followed by:
34
34
 
35
35
  1. The canonical data.json path you wrote (project-relative).
36
36
  2. The rendered markdown path produced by the renderer (project-relative).
@@ -39,6 +39,10 @@ You also write an audit sidecar at the path the lead registers as `**Worker Resu
39
39
 
40
40
  Do NOT duplicate the data.json contents here — the data.json is the canonical artifact; this sidecar is the validator-required pointer / audit record.
41
41
 
42
+ ## Heartbeat (BLOCKING)
43
+
44
+ Write the audit sidecar at `**Audit sidecar path:**` before required reading, then append `- PROGRESS: <stage> <ISO-8601-UTC>` at a cadence no longer than five minutes. Use only: `started`, `required-reading-complete`, `synthesis-start`, `data-json-write-start`, `render-start`, and `write-result-start`. The Phase 7 validator enforces the first stage, timestamps, cadence, and this allowlist.
45
+
42
46
  ## Execution Rules
43
47
 
44
48
  1. Extract the absolute `Project Root` from the lead prompt (look for a line starting with `**Project Root:**` or `Project Root:`). If missing, return:
@@ -75,7 +79,7 @@ For the report writer specifically, the `## Inputs` list always includes:
75
79
 
76
80
  For the carry-in `clarification-response.md` (if present), walk every row of `## 1. Clarification Items` including rows whose `User input` cell is blank — a blank cell with `Status=open` is a signal you must surface in the conditional `## 0. Clarification Response Carried In From Previous Run` section (the template's `RENDER_IF` guard activates it when the carry-in path is non-empty). When no carry-in path was provided, OMIT the `## 0.` heading entirely — do NOT write an empty-state stub.
77
81
 
78
- Write a Reading Confirmation block to your **audit sidecar** at `runs/<task-type>/worker-results/report-writer-worker-audit-<task-type>-<seq>.md`, per the selected report-writer preamble's `Required reading` section (the main final-report and worker-results files carry no Section 0 heading). If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating the report.
82
+ Write a Reading Confirmation block to `**Audit sidecar path:**`, per the selected report-writer preamble's `Required reading` section (the main final-report and worker-results files carry no Section 0 heading). If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating the report.
79
83
 
80
84
  ## Authoring Contract
81
85
 
@@ -92,7 +96,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
92
96
  - **§7 phase-continuation row (mandatory for non-terminal task-types).** When `header.taskType` is one of `requirements-discovery` / `implementation-planning` / `error-analysis` / `implementation` / `final-verification`, `followUpTasks` MUST contain at least one row whose `origin` is `phase-continuation`, `suggestedTaskType` equals the next phase (byte-identical to `finalVerdict.nextStep`'s referenced phase), `newTaskId` reuses the current task-id, `autoSpawn` is `"no"`, and `priority` is `"P0"`. For `release-handoff` runs, omit the phase-continuation row. Schema `allOf` clause enforces this via `contains`.
93
97
  - **No deprecated sections.** The schema has no `4.5.8 User Approval Request` body field, no `4.5.9 Open Questions`, no `5.1 Additional Material Request`, no `5.2 User Confirmation Questions` — clarifications go under the unified `clarificationItems[]` array.
94
98
  - **Optional Section 0.** Include `clarificationCarryIn` ONLY when the lead's prompt provides a non-empty carry-in path. Omit the key entirely otherwise (do NOT set it to `null` or an empty object).
95
- - **Reading Confirmation** goes in the audit sidecar per the selected report-writer preamble's `Required reading` section — never in the data.json or the main worker-results file.
99
+ - **Reading Confirmation** goes at `**Audit sidecar path:**` per the selected report-writer preamble's `Required reading` section — never in the data.json or the main worker-results file.
96
100
  - Include all four convergence categories. The schema's `crossVerification.consensus` / `.differences` arrays carry full / partial / contested / worker-unique items; do not omit any.
97
101
  - Convergence round history goes in `crossVerification.roundHistory.rounds[]` with `round2SkippedReason`. When convergence is disabled, set `crossVerification.roundHistory` to `{"disabled": true}`. Values come verbatim from `state/convergence-<task-type>-<seq>.json` — do not recompute.
98
102
  - `verification-error` votes are their own verdict (`planItems[].verdicts[].verdict` enum); they are NOT folded into AGREE / DISAGREE counts.
@@ -101,7 +105,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
101
105
  - If evidence is missing, write `"I don't know"` in the relevant statement field rather than fabricating confidence.
102
106
  - Cite file paths and line numbers in every `evidence.primary[].source` / `consensus[].evidence` cell.
103
107
  - Preserve every analysis worker's ticket tagging — every row's `ticketId` field carries the ticket key or the task-fallback. For single-ticket runs, set `ticketCoverage` to `{"singleTicket": "<ticket>"}`. For runs that do not require ticket tagging (`release-handoff`, `final-verification`), set `ticketCoverage` to `{"omit": true}`.
104
- - For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage.
108
+ - For `implementation-planning`, populate `implementationPlanning.requirementCoverage` with one row per concrete requirement from the brief / packet, using IDs `R-001`, `R-002`, ... in source order. A `covered` row's `coveredBy` MUST name the specific Option Candidate plus Stage/Step that satisfies the requirement. Use `status: "covered"` only when the report's plan actually covers it; use `documented-deviation` only when `coveredBy` states the concrete alternative and the row records non-empty unique `decisionRefs` plus `approvalDisposition`. Each `C-NNN` ref must name a clarification in this report; each `D-NNNN` ref must name a `decisionDrafts[].number`. `approvalDisposition: "accepted"` requires a referenced clarification with `status: answered|resolved` and non-empty `userInput`; `approvalDisposition: "blocked C-NNN"` requires that same-report clarification to be `status: open, blocks: approval`. Otherwise use `gap` or `blocked C-NNN` and ensure the corresponding `Clarification Items` row blocks approval. Do not collapse this into `ticketCoverage`; ticket coverage is not requirement coverage. **Enforced:** `schemas/final-report-v1.0.schema.json` `$defs.ImplementationRequirementCoverageRow` and `validators/validate-run.py` `_validate_requirement_deviations`.
105
109
  - For `implementation-planning`, each `requirementCoverage` row's `source` is a graded cell, not prose — free text like `"carry-in from requirements-discovery C-001"` is rejected. Write exactly one of: `brief:<heading>`, where the heading literally exists in the task brief; `derived:R-NNN — <one-line reason>`, whose chain must terminate at a `brief:` or `contract:` row of the same table without cycling; or `contract:<rule>`, for artifacts okstra's own phase contract mandates, whose allowlist is exactly the two tokens `decision-record-step` (the §5.4 Decision Drafts materialization step) and `glossary-step` (the glossary proposal step) — any other rule name is rejected, so never invent one. (Maintainer SSOT for that allowlist: `scripts/okstra_ctl/scope_provenance.py` in the okstra repo.) A requirement you cannot source this way does not belong in the table: put it in `clarificationItems[]` with `Blocks=approval`. **Enforced:** `validators/validate-run.py` `_validate_requirement_provenance`. In the same table, anchor every stage number in `coveredBy` to a `Stage` / `Stages` word (`Stage 2`, `Stages 1-3`) — `_validate_stage_has_requirement` reads that cell as prose and fails the plan when a Stage Map stage is cited by no row.
106
110
  - For `implementation-planning`, also populate `implementationPlanning.decisionDrafts` (one row per decision meeting all three decision-record criteria; `[]` otherwise) and `implementationPlanning.skippedAdrCandidates` (evaluated-but-dropped adr-candidates; `[]` otherwise). The schema excerpt enumerates the row shape; the renderer emits §5.4 `### Decision Drafts`. When `decisionDrafts` is non-empty, the plan's stages MUST carry a stepwise step that creates `.okstra/decisions/<NNNN>-<slug>.md` (validate-run gates this).
107
111
  - When the `Task Type` is `improvement-discovery`, populate `## 5.9 Improvement Candidates` with the 10-column schema enforced by `validators/validate_improvement_report.py`. Source the row IDs (`I-NNN`), lens whitelist, and Source workers patterns from `scripts/okstra_ctl/improvement_lenses.py` — do NOT introduce new lens names or worker prefixes. `improvement-discovery` is NOT in the data.json schema enum, so author its markdown directly (not via `okstra-render-final-report.py`). Immediately after writing the markdown, run (`Bash`): `okstra inject-report-index <markdown path> --report-language <en|ko>`. That adds the top-of-report Index plus `I-NNN` / `C-NNN` scroll anchors; the run validator fails the report when the Index anchor is absent.
@@ -62,8 +62,8 @@ This adapter maps the neutral Okstra lead operations to Claude Code host primiti
62
62
 
63
63
  - 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.
64
64
  - 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.
65
- - 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.
66
- - **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
+ - Each in-process 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.
66
+ - **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 worker, `--prompt <prompt-history-path>` for each CLI-wrapper worker. The dispatch record's `livenessMode` selects the flag; never infer it from provider or filename. 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.
67
67
  - 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.
68
68
  - On approved cleanup, reconcile the current live session roster before sending shutdown requests. Never target the lead session.
69
69
  - Collect usage before teardown. Resume through the recorded Claude session id and keep all run artifacts authoritative.
@@ -30,6 +30,10 @@ When this contract says "queue" without qualifier, it means the *verification qu
30
30
 
31
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
32
 
33
+ The end-to-end artifact lifecycle is worker results → Round 0 grouping → reducer-owned queue → analyser-instance re-verification → optional `okstra convergence apply-critic-gaps` transition → validated terminal state (newly finalized v1.3, or unchanged historical v1.0–v1.2 from `reuse-final`) → report-writer data.json. Cross-verification is queue-scoped: it does not mean that one worker reviews another worker's complete result. The reducer asks independent analyser instances to vote only on non-consensus findings selected by the persisted plan; the report writer never votes.
34
+
35
+ Initial and reverify worker prompts carry `**Audit sidecar path:**`. Initial workers write their reading confirmation there; reverify workers use their own canonical sidecar for the reverify session without reopening the initial worker's full reading packet.
36
+
33
37
  ## When to Use
34
38
 
35
39
  - When okstra lead Phase 5.5 (convergence loop) begins — immediately after all workers complete Phases 4 and 5
@@ -80,7 +84,7 @@ Read the worker result files generated in Phase 4/5 and extract individual findi
80
84
  - Same semantics but disjoint ticket sets → separate groups (do NOT over-merge across tickets).
81
85
  - Only one worker confirms a finding → one single-source group.
82
86
  4. When grouping is ambiguous, prefer splitting over merging (avoid over-merging). Semantic matching, ticket-set equality, and evidence interpretation remain lead judgments; the engine does not perform fuzzy matching or decide whether evidence is credible.
83
- 5. Write `runs/<task-type>/state/convergence-groups-<task-type>-<seq>.json`. Each group carries its `ticketIds`, `originWorker`, `originEvidence`, `discoveredBy`, and every `<worker>:<item-id>` source in `sourceItems`. Include the resolved worker roster in order with functional `audience` values; do not derive scope from provider or model identity.
87
+ 5. Write `runs/<task-type>/state/convergence-groups-<task-type>-<seq>.json`. Each group carries its `ticketIds`, `originWorker`, `originEvidence`, `discoveredBy`, and every `<worker>:<item-id>` source in `sourceItems`. When a live command or external read produced reproducible evidence, also include `evidenceArtifacts[]` with its `.okstra/` path, SHA-256 digest, command, and environment. The field is optional because historical or inaccessible evidence may not have a captured artifact. The lead and verifier MUST NOT infer live or external evidence from wording or keyword matching; they use the finding's explicit claim, provenance, and supplied artifacts. Include the resolved worker roster in order with functional `audience` values; do not derive scope from provider or model identity.
84
88
  6. Do not write a queue or classification in this grouped-input artifact. `okstra convergence seed` deterministically marks multi-source groups `full-consensus` and puts only single-source groups in the working queue. Section 6 never enters the grouped input.
85
89
 
86
90
  ### Round 1-N: Re-verification Loop (queue-pruned)
@@ -99,13 +103,15 @@ convergence-<task-type>-<seq>.json
99
103
 
100
104
  Follow this protocol exactly:
101
105
 
106
+ 0. The machine-readable inputs are `schemas/convergence-groups-v1.0.schema.json`, `schemas/convergence-round-results-v1.0.schema.json`, and `schemas/convergence-critic-results-v1.0.schema.json`. Use `okstra convergence example --kind <groups|round-results|critic-results>` for deterministic valid examples; the command writes only JSON to stdout.
102
107
  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.
103
108
  2. Run `okstra convergence plan-round --work-state <work> --plan <round-plan>`. This is read-only with respect to the working state.
104
109
  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`.
105
110
  4. Convert parsed verdicts and every terminal dispatch outcome into `convergence-round-<N>-results-<task-type>-<seq>.json`; then run `okstra convergence apply-round --work-state <work> --plan <round-plan> --results <round-results>`.
106
111
  5. Repeat `plan-round` and `apply-round` until the plan action is `finalize`.
107
- 6. Run `okstra convergence finalize --work-state <work> --output <final>`.
108
- 7. Run `okstra convergence validate --state <final> --kind final`. Only this validated final schema-v1.2 artifact is delivered to the report-writer, which does not vote.
112
+ 6. When the coverage critic is enabled, convert its verification batch to the canonical `dispatches[]` / `gaps[]` result shape and run `okstra convergence apply-critic-gaps --work-state <work> --results <critic-results>` exactly once. The reducer, not the lead, merges verified gaps.
113
+ 7. Run `okstra convergence finalize --work-state <work> --output <final>`.
114
+ 8. Run `okstra convergence validate --state <final> --kind final`. Newly finalized convergence output is schema v1.3. Under `reuse-final`, a valid historical final schema v1.0, v1.1, or v1.2 remains consumable by the report-writer without rewrite; do not finalize, upgrade, or otherwise rewrite that reused artifact. Deliver the validated terminal state to the report-writer, which does not vote.
109
115
 
110
116
  The planner preserves roster and queue order, excludes that finding's origin worker, emits at most one batch per analysis worker per round, and ensures the report-writer never appears in `dispatches` or `skippedWorkers`. Queue pruning is monotonic: a finding absent from the current queue cannot reappear in a later plan.
111
117
 
@@ -113,7 +119,7 @@ After `seed`, the lead MUST NOT hand-edit queue IDs, classifications, `roundHist
113
119
 
114
120
  #### Resume and legacy-state recovery (BLOCKING)
115
121
 
116
- A valid terminal legacy final is reused unchanged. A malformed, unreadable, or partial legacy final is archived byte-for-byte under `state/migrations/` and restarted from Round 0 because its queue provenance cannot be reconstructed. A matching valid working state resumes. A malformed new-engine working state fails closed and names the explicit `--restart-from-round0` recovery flag; when supplied, every invalid existing state is archived before replacement. The final path can be replaced only when its recorded migration archive still matches the original bytes.
122
+ A valid historical final schema v1.0, v1.1, or v1.2 is reused unchanged under `reuse-final`; it remains read-only and is not upgraded in place. A malformed, unreadable, or partial legacy final is archived byte-for-byte under `state/migrations/` and restarted from Round 0 because its queue provenance cannot be reconstructed. A matching valid working state resumes. A malformed new-engine working state fails closed and names the explicit `--restart-from-round0` recovery flag; when supplied, every invalid existing state is archived before replacement. The final path can be replaced only when its recorded migration archive still matches the original bytes.
117
123
 
118
124
  #### Engine-owned Round 2 gate
119
125
 
@@ -125,13 +131,15 @@ A reverify dispatch that returns a **terminal non-result** (`timeout`, `error`,
125
131
 
126
132
  Rules:
127
133
 
128
- 1. For each failed dispatch, put its actual terminal status and duration in the round-results `dispatches[]`; do not invent a vote. `okstra convergence apply-round` appends `votes[W].verdict = "verification-error"` with the terminal reason for every affected finding.
134
+ 1. For each failed dispatch, put its actual terminal status and duration in the round-results `dispatches[]`; do not invent a vote. `okstra convergence apply-round` appends `votes[W].verdict = "verification-error"` with the terminal reason for every affected finding. A completed dispatch may separately return `UNVERIFIABLE` for a particular finding; the input alias is persisted as `verification-error` with its required non-empty explanation while the dispatch remains `completed`.
129
135
  2. Record one event per failed dispatch via `okstra error-log append-observed --error-type cli-failure --agent <worker> ...` (the worker wrapper does this for wrapper failures; for in-process worker timeouts the lead does it).
130
136
  3. `apply-round` adds the worker to the persisted round's `skippedWorkers[]` with `{worker: <W>, reason: "dispatch-non-result", terminalStatus: <timeout|error|not-run>}`.
131
137
  4. If at least one dispatch was issued and every dispatch terminates as non-result, `apply-round` records the `all-reverify-non-result` stop state. The next `plan-round` returns `action: "finalize"`; record one `contract-violation` event per non-result dispatch.
132
138
  5. Section 6 (Specialization Lens) of a worker output is OUT of convergence scope per "Convergence scope" above — its absence is NEVER a `verification-error`.
133
139
 
134
- The engine's classifiers treat `verification-error` as "no usable vote" — it counts neither toward AGREE nor toward DISAGREE.
140
+ The engine's classifiers treat `verification-error` as "no usable vote" — it counts neither toward AGREE nor toward DISAGREE and is excluded from both numerator and denominator.
141
+
142
+ The public shorthand is `UNVERIFIABLE → verification-error`: `UNVERIFIABLE` is the worker-facing input token, while `verification-error` is the persisted state token. This mapping records unavailable evidence honestly and never turns inability to verify into a negative vote.
135
143
 
136
144
  ### Convergence Test
137
145
 
@@ -164,15 +172,16 @@ The persisted `verdict` enum is unchanged (`agree | disagree | supplement | veri
164
172
  | SURVIVES | `agree` | Actively tried to refute and failed — the claim withstood the attack. |
165
173
  | SURVIVES-WITH-CAVEAT | `supplement` | Holds, but a scope limit / extra condition / precondition was found. |
166
174
  | REFUTED | `disagree` | The claim was broken (or failed to prove itself). MUST carry a `disagreeBasis`. |
175
+ | UNVERIFIABLE | `verification-error` | Capability, credential, network, or service state prevented verification of this finding. |
167
176
 
168
177
  Each `disagree` vote records a new field `disagreeBasis`:
169
178
 
170
179
  | `disagreeBasis` | Meaning |
171
180
  |---|---|
172
- | `counter-evidence` | The verifier cited contradicting evidence (`file:line` / log line) in `explanation`. A **hard refute**. |
173
- | `burden-not-met` | The verifier re-inspected the cited evidence and could neither confirm nor refute the claim failed to prove itself ("when uncertain, lean to rejection"). |
181
+ | `counter-evidence` | The verifier opened and inspected the cited evidence and found a contradiction (`file:line` / log line) recorded in `explanation`. A **hard refute**. |
182
+ | `burden-not-met` | The verifier opened and inspected the cited evidence, but its contents were insufficient to establish the claim. |
174
183
 
175
- A `disagree` with `disagreeBasis == null` is a contract violation in adversarial mode — every refutation must state which of the two grounds it rests on. Bare "I disagree" without re-inspection is not allowed.
184
+ A `disagree` with `disagreeBasis == null` is a contract violation in adversarial mode — every refutation must state which of the two grounds it rests on. Bare "I disagree" without re-inspection is not allowed. If capability, credential, network, or service state prevents that inspection, the verdict is `UNVERIFIABLE`, persisted as `verification-error`; verifier failure is never converted to `DISAGREE` or `burden-not-met`. A live/external claim without `evidenceArtifacts` remains schema-valid, but a verifier that needs the missing artifact and cannot independently access the source MUST answer `UNVERIFIABLE` with a non-empty explanation.
176
185
 
177
186
  ### Adversarial classification (replaces the §"Convergence Algorithm" per-round classifier when `adversarial == true`)
178
187
 
@@ -215,12 +224,13 @@ Call `await_workers(handles)` through the same adapter and apply the shared term
215
224
 
216
225
  ### Required reverify-prompt anchor headers (BLOCKING)
217
226
 
218
- Every reverify prompt MUST start with these 8 anchor headers — in this exact order, before any other content:
227
+ Every reverify prompt MUST start with these 9 anchor headers — in this exact order, before any other content:
219
228
 
220
229
  ```
221
230
  **Project Root:** <absolute-path>
222
231
  **Prompt History Path:** runs/<task-type>/prompts/<role-slug>-reverify-r<N>-<task-type>-<seq>.md
223
232
  **Result Path:** runs/<task-type>/worker-results/<role-slug>-reverify-r<N>-<task-type>-<seq>.md
233
+ **Audit sidecar path:** <absolute-path>
224
234
  Assigned worker prompt history path: <Project Root>/<Prompt History Path>
225
235
  **Model:** <role>, <modelExecutionValue>
226
236
  **Errors log path:** <absolute-path>
@@ -230,6 +240,8 @@ Assigned worker prompt history path: <Project Root>/<Prompt History Path>
230
240
 
231
241
  `**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.
232
242
 
243
+ Before dispatch, materialize `**Audit sidecar path:**` by passing the exact reverify `**Result Path:**` through `okstra_ctl.worker_artifact_paths.audit_sidecar_rel()` and resolving that project-relative result against `**Project Root:**`. Write the resulting absolute path into the header. The lead MUST NOT construct the audit filename from a role, task type, round, or sequence independently.
244
+
233
245
  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.
234
246
 
235
247
  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:**`.
@@ -268,6 +280,8 @@ For EACH finding, respond with exactly one verdict:
268
280
  - **AGREE**: The finding is valid based on the evidence presented
269
281
  - **DISAGREE**: The finding is incorrect or unsupported (explain briefly why)
270
282
  - **SUPPLEMENT**: The finding is valid AND you have additional supporting evidence or context
283
+ - **UNVERIFIABLE**: Capability, credential, network, or service state prevents you
284
+ from checking this finding. Explain the unavailable capability; do not substitute DISAGREE.
271
285
 
272
286
  Do NOT re-analyze the original source materials. Judge based on the evidence provided.
273
287
 
@@ -286,7 +300,7 @@ Do NOT re-analyze the original source materials. Judge based on the evidence pro
286
300
  For each finding, respond as:
287
301
 
288
302
  ### F-001
289
- **Verdict**: AGREE | DISAGREE | SUPPLEMENT
303
+ **Verdict**: AGREE | DISAGREE | SUPPLEMENT | UNVERIFIABLE
290
304
  **Explanation**: <2-3 sentences>
291
305
 
292
306
  ### F-002
@@ -313,6 +327,8 @@ is wrong, overstated, or unproven. Then respond with exactly one verdict:
313
327
  - **SURVIVES**: You actively tried to refute it and failed — the claim withstood the attack.
314
328
  - **SURVIVES-WITH-CAVEAT**: It holds, but a scope limit / extra condition / missing
315
329
  precondition exists (state it).
330
+ - **UNVERIFIABLE**: Capability, credential, network, or service state prevents you
331
+ from opening or reproducing the cited evidence. Do not use REFUTED as a substitute.
316
332
 
317
333
  The burden of proof is on the claim. If after inspecting the cited evidence you remain
318
334
  uncertain, your verdict is REFUTED with basis = burden-not-met.
@@ -332,7 +348,7 @@ re-read the task brief, instruction-set, or report template.
332
348
  ## Response format
333
349
 
334
350
  ### F-001
335
- **Verdict**: REFUTED | SURVIVES | SURVIVES-WITH-CAVEAT
351
+ **Verdict**: REFUTED | SURVIVES | SURVIVES-WITH-CAVEAT | UNVERIFIABLE
336
352
  **Basis** (only if REFUTED): counter-evidence | burden-not-met
337
353
  **Explanation**: <2-3 sentences; for counter-evidence include the file:line you found>
338
354
 
@@ -340,7 +356,7 @@ re-read the task brief, instruction-set, or report template.
340
356
  ...
341
357
  ```
342
358
 
343
- When persisting votes, map SURVIVES→`agree`, SURVIVES-WITH-CAVEAT→`supplement`, REFUTED→`disagree`, and copy the stated Basis into `votes.<worker>.disagreeBasis` (null for non-REFUTED verdicts).
359
+ When persisting votes, map SURVIVES→`agree`, SURVIVES-WITH-CAVEAT→`supplement`, REFUTED→`disagree`, and UNVERIFIABLE→`verification-error`; copy the stated Basis into `votes.<worker>.disagreeBasis` (null for non-REFUTED verdicts). Every vote requires a non-empty `explanation`.
344
360
 
345
361
  ### Full Re-analysis Re-verification Prompt
346
362
 
@@ -351,6 +367,10 @@ You are <worker-role> performing deep re-verification for <task-key> (round <N>)
351
367
 
352
368
  Independently verify the following findings by examining the original materials.
353
369
  Use each finding as a starting point, NOT as a confirmed conclusion.
370
+ If capability, credential, network, or service state prevents access to evidence
371
+ needed for a finding, you MUST answer UNVERIFIABLE with a non-empty explanation.
372
+ You MUST NOT answer DISAGREE merely because access or reproduction failed;
373
+ UNVERIFIABLE is persisted as `verification-error` and excluded from consensus.
354
374
 
355
375
  ## Task bundle paths
356
376
  - Instruction set: <instruction-set path>
@@ -373,7 +393,7 @@ Use each finding as a starting point, NOT as a confirmed conclusion.
373
393
  For each finding:
374
394
 
375
395
  ### F-001
376
- **Verdict**: AGREE | DISAGREE | SUPPLEMENT
396
+ **Verdict**: AGREE | DISAGREE | SUPPLEMENT | UNVERIFIABLE
377
397
  **Your evidence**: <your independent evidence trail with file paths and line numbers>
378
398
  **Explanation**: <detailed analysis>
379
399
  ```
@@ -384,7 +404,7 @@ Save it to `runs/<task-type>/state/convergence-<task-type>-<seq>.json`.
384
404
 
385
405
  ```json
386
406
  {
387
- "schemaVersion": "1.2",
407
+ "schemaVersion": "1.3",
388
408
  "taskKey": "<task-key>",
389
409
  "config": {
390
410
  "enabled": true,
@@ -446,11 +466,11 @@ Save it to `runs/<task-type>/state/convergence-<task-type>-<seq>.json`.
446
466
 
447
467
  Schema rules:
448
468
 
449
- - `schemaVersion`: literal string `"1.2"` for all new runs — both adversarial and collaborative. v1.2 adds `config.adversarial` and `votes.<worker>.disagreeBasis`, written as `false` / `null` respectively on collaborative runs. Readers MUST accept `"1.0"` / `"1.1"` / `"1.2"` for historical artifacts and treat any missing field as `null`.
469
+ - `schemaVersion`: literal string `"1.3"` for all new runs — both adversarial and collaborative. Historical readers accept `"1.0"` / `"1.1"` / `"1.2"` unchanged and never rewrite those artifacts during validation. v1.3 adds the strict coverage-critic ledger and rejects unknown top-level fields; work-state remains v1.0.
450
470
  - `config.adversarial`: boolean. `true` when this run used adversarial verification (default for `requirements-discovery` / `error-analysis` / `implementation-planning`). When `true`, `config.verificationMode` is `"full-reanalysis"` (scoped) and every `disagree` vote carries a non-null `disagreeBasis`.
451
471
  - `config.effectiveMaxRounds`: the integer the lead actually used after resolving the phase-aware default (`1` for `requirements-discovery`, `2` otherwise). MUST equal `config.maxRounds` when the manifest explicitly set it.
452
472
  - `findings[].ticketIds`: array of ticket keys from Phase 4 grouping (parsed per the Round 0 step 5 rule). MAY be empty when the discovering worker tagged the finding `unknown`.
453
- - `findings[].rounds[].votes.<worker>.verdict`: enum, one of `agree | disagree | supplement | verification-error`. Lower-case tokens; map upper-case AGREE/DISAGREE/SUPPLEMENT verdicts emitted by workers to their lower-case form before persisting. `verification-error` is reserved for terminal non-result dispatches (§"Worker failure handling in reverify").
473
+ - `findings[].rounds[].votes.<worker>.verdict`: enum, one of `agree | disagree | supplement | verification-error`. Lower-case tokens; map upper-case AGREE/DISAGREE/SUPPLEMENT verdicts emitted by workers to their lower-case form and map the input alias `unverifiable` to persisted `verification-error`. The latter represents either a terminal non-result dispatch or a completed dispatch that could not verify a particular finding (§"Worker failure handling in reverify"). Every vote has a non-empty `explanation`.
454
474
  - `findings[].rounds[].votes.<worker>.disagreeBasis`: enum `counter-evidence | burden-not-met | null`. Non-null only when `verdict == "disagree"` AND `config.adversarial == true`; `null` (or absent, treated as null) otherwise. See §"Adversarial Verification Mode".
455
475
  - `findings[].classification`: enum, one of `full-consensus | partial-consensus | worker-unique | contested`. No other value is permitted.
456
476
  - `roundHistory[].inputQueueSize`: queue size at the start of this round.
@@ -472,7 +492,7 @@ Runs only when `convergence.critic.enabled == true` (set by `--critic <provider>
472
492
  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:
473
493
 
474
494
  - **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.
475
- - **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 — surface the gaps as unverified `clarification` items per §"Gap verification" and record that fact.
495
+ - **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".
476
496
 
477
497
  ### Dispatch (fresh one-shot)
478
498
  Dispatch one fresh pass to `config.critic.provider` through `redispatch_worker`, with `model = config.critic.modelExecutionValue` and `dispatchKind = "critic"`. If the model value is empty, record `critic-skipped: model-unresolved`; never dispatch without a model. Result path: `runs/<task-type>/worker-results/<provider>-critic-<task-type>-<seq>.md`. The critic prompt seeds the consolidated findings and asks ONLY for coverage gaps:
@@ -494,7 +514,9 @@ Each critic gap enters the verification queue as a finding with `originWorker =
494
514
 
495
515
  ### State
496
516
  - `convergence.critic` manifest block: `{ enabled, provider, modelExecutionValue }`.
497
- - Convergence state artifact: critic gaps appear in `findings[]` with `source: "critic"`. Add a `config.critic` summary `{ provider, modelExecutionValue, gapsProposed, gapsMerged, gapsUnverified }`. `gapsProposed - gapsMerged - gapsUnverified` is the number the voters judged and rejected, so a gap that quietly disappeared shows up as an arithmetic mismatch rather than as nothing at all. `source` and `config.critic` are optional v1.2 fields (readers treat absence as null); no enum changes.
517
+ - The lead passes one canonical coverage batch with `{ schemaVersion, taskKey, mode, provider, modelExecutionValue, dispatches, gaps }`; each gap carries its candidate fields plus `gapId` and `votes`. `dispatches[]` contains exactly one row for every Phase 4 analyser except the critic, even when execution did not produce a result: persist `status: timeout | error | not-run` and the elapsed `durationMs` instead of omitting that analyser. `apply-critic-gaps` rejects a non-terminal main queue, duplicate or missing analysers, unknown workers, critic dispatches/votes, votes without a completed dispatch, and a second batch.
518
+ - Convergence state artifact: merged gaps appear in `findings[]` with `source: "critic"` and `rounds: []`. The separate `criticVerification.gaps[]` ledger retains each gap's `summary`, `category`, `ticketIds`, `originEvidence`, optional `evidenceArtifacts`, classification, merge link, and votes. Strict v1.3 validation deterministically replays each complete critic-origin finding from that ledger; a critic batch never increments `roundHistory` or `totalRounds` and never creates a fake main round.
519
+ - `config.critic` is `{ provider, modelExecutionValue, gapsProposed, gapsMerged, gapsRejected, gapsUnverified }`, with `gapsProposed = gapsMerged + gapsRejected + gapsUnverified`. `full-consensus` / `partial-consensus` gaps merge, `contested` / `worker-unique` gaps count as rejected, and gaps with no usable analyser vote appear in both the ledger and final `unverifiedGaps[]`.
498
520
 
499
521
  ## Acceptance critic pass (final-verification)
500
522
 
@@ -518,6 +540,7 @@ existing Acceptance Blocker. If you find none, say so explicitly.
518
540
  ### Verification — confirm-or-downgrade (BLOCKING)
519
541
 
520
542
  Each candidate blocker is verified by the Phase 4 analysers (excluding the critic). Do NOT use the adversarial finding classifier's "uncertain → reject" rule here.
543
+ - Do NOT run `apply-critic-gaps` for this mode. That reducer implements coverage merge/drop semantics and rejects `acceptance-devils-advocate` input.
521
544
  - **Confirmed** (an analyser reproduces it or cites supporting evidence) → promote to a `## 5.8 Acceptance Blockers` row (keep severity + recommended follow-up phase).
522
545
  - **Not confirmed** (cannot reproduce, or evidence is weak) → **downgrade to a Residual Risk row — never drop it.** Record the escalation trigger so the user can re-judge a high-severity-but-unconfirmed candidate.
523
546
 
@@ -527,13 +550,13 @@ Promoted blockers enter `## 5.8 Acceptance Blockers`; since `accepted` requires
527
550
 
528
551
  ### State
529
552
 
530
- Critic output lives in the run's `worker-results/` directory (`runs/final-verification/worker-results/` for whole-task verification, `runs/final-verification/stage-<N>/worker-results/` for single-stage), filename `<provider>-critic-final-verification-<seq>.md`. The convergence state `config.critic` summary (see §"Coverage critic pass") records `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual` (optional v1.2 fields; readers treat absence as null).
553
+ Critic output lives in the run's `worker-results/` directory (`runs/final-verification/worker-results/` for whole-task verification, `runs/final-verification/stage-<N>/worker-results/` for single-stage), filename `<provider>-critic-final-verification-<seq>.md`. The convergence state `config.critic` summary records `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual`; v1.3 enforces `candidatesProposed = confirmedBlockers + downgradedToResidual`, so no candidate can be silently dropped.
531
554
 
532
555
  ## Output
533
556
 
534
557
  Information to be passed to Phase 6 after completing this contract:
535
558
 
536
- - Validated final schema-v1.2 artifact containing the four-category classification of all findings; the report-writer consumes it and does not vote
559
+ - Newly finalized convergence output is schema v1.3. Under `reuse-final`, a valid historical final schema v1.0, v1.1, or v1.2 remains consumable by the report-writer without rewrite. Either validated terminal artifact contains the four-category classification of all findings; the report-writer consumes it and does not vote
537
560
  - Round history and votes per worker for each finding
538
561
  - Path to the convergence state artifact
539
562
  - Convergence summary (count per category)