okstra 0.127.0 → 0.129.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/docs/architecture/storage-model.md +23 -3
  2. package/docs/architecture.md +28 -1
  3. package/docs/cli.md +9 -0
  4. package/docs/project-structure-overview.md +17 -7
  5. package/package.json +1 -1
  6. package/runtime/BUILD.json +2 -2
  7. package/runtime/agents/workers/antigravity-worker.md +3 -3
  8. package/runtime/agents/workers/claude-worker.md +4 -4
  9. package/runtime/agents/workers/codex-worker.md +3 -3
  10. package/runtime/agents/workers/report-writer-worker.md +5 -5
  11. package/runtime/prompts/lead/adapters/claude-code.md +2 -1
  12. package/runtime/prompts/lead/adapters/codex.md +1 -0
  13. package/runtime/prompts/lead/adapters/external.md +1 -0
  14. package/runtime/prompts/lead/convergence.md +45 -83
  15. package/runtime/prompts/lead/okstra-lead-contract.md +12 -8
  16. package/runtime/prompts/lead/report-writer.md +3 -2
  17. package/runtime/prompts/lead/team-contract.md +19 -9
  18. package/runtime/prompts/profiles/_common-contract.md +1 -1
  19. package/runtime/prompts/profiles/error-analysis.md +1 -1
  20. package/runtime/prompts/profiles/final-verification.md +10 -6
  21. package/runtime/prompts/profiles/implementation-planning.md +5 -0
  22. package/runtime/prompts/profiles/improvement-discovery.md +11 -1
  23. package/runtime/prompts/profiles/requirements-discovery.md +8 -1
  24. package/runtime/python/okstra_ctl/analysis_packet.py +14 -15
  25. package/runtime/python/okstra_ctl/codex_dispatch.py +51 -80
  26. package/runtime/python/okstra_ctl/context_cost.py +82 -7
  27. package/runtime/python/okstra_ctl/convergence.py +223 -0
  28. package/runtime/python/okstra_ctl/convergence_engine.py +1152 -0
  29. package/runtime/python/okstra_ctl/convergence_migration.py +184 -0
  30. package/runtime/python/okstra_ctl/convergence_store.py +85 -0
  31. package/runtime/python/okstra_ctl/dispatch_core.py +60 -1
  32. package/runtime/python/okstra_ctl/improvement_assignment.py +61 -0
  33. package/runtime/python/okstra_ctl/log_report.py +44 -5
  34. package/runtime/python/okstra_ctl/path_hints.py +28 -1
  35. package/runtime/python/okstra_ctl/paths.py +10 -1
  36. package/runtime/python/okstra_ctl/render.py +78 -11
  37. package/runtime/python/okstra_ctl/run.py +66 -2
  38. package/runtime/python/okstra_ctl/wizard.py +15 -4
  39. package/runtime/python/okstra_ctl/work_categories.py +37 -0
  40. package/runtime/python/okstra_ctl/worker_prompt_body.py +107 -0
  41. package/runtime/python/okstra_ctl/worker_prompt_contract.py +316 -0
  42. package/runtime/python/okstra_ctl/worker_prompt_headers.py +109 -10
  43. package/runtime/python/okstra_ctl/worker_prompt_policy.py +158 -0
  44. package/runtime/templates/implementation-worker-preamble.md +51 -0
  45. package/runtime/templates/operating-standard.md +4 -4
  46. package/runtime/templates/report-writer-prompt-preamble.md +37 -0
  47. package/runtime/templates/worker-error-contract.md +46 -0
  48. package/runtime/templates/worker-prompt-preamble.md +28 -227
  49. package/runtime/validators/lib/fixtures.sh +27 -12
  50. package/runtime/validators/validate-run.py +228 -45
  51. package/src/cli-registry.mjs +7 -0
  52. package/src/commands/execute/convergence.mjs +34 -0
  53. package/src/commands/inspect/log-report.mjs +5 -3
@@ -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
@@ -74,92 +76,48 @@ Read the worker result files generated in Phase 4/5 and extract individual findi
74
76
  - Items tagged `unknown` keep the literal `unknown` as their ticket key.
75
77
  2. For each finding, record the summary, evidence (file path, line number, basis), the discovering worker, **the worker-internal item ID that worker assigned** (e.g. `F-001`, `1.1`, `F-3` — see `prompts/profiles/_common-contract.md` "Cross-worker traceability" SSOT), and the parsed ticket set. Persist the item ID as `findings[].discoveredBy.<worker>.itemId` and each cross-worker confirmation as `findings[].sourceItems[]` (one entry per contributing `<worker>:<item-id>` pair). The final-report `## 6.1 Consensus` / `## 6.2 Differences` / `## 2.1 Primary Evidence` tables read this verbatim into their `Source items` columns; without it the synthesised `C-NNN` row loses its link back to the original worker wording.
76
78
  3. The lead groups findings based on semantic similarity AND ticket-set equality:
77
- - Same semantics + same ticket set across 2+ workers → immediately reach `full consensus`.
78
- - Same semantics but disjoint ticket sets → keep as separate groups (do NOT over-merge across tickets).
79
- - Only one worker confirms a finding → `unique`, enter the verification queue.
80
- 4. When grouping is ambiguous, prefer splitting over merging (avoid over-merging).
81
- 5. Persist each finding's ticket set in the convergence state artifact under a `ticketIds` field on the finding record. Re-verification rounds carry the same field forward.
82
- 6. After grouping, the verification queue contains EXACTLY the `unique`-marked findings (Step 3 case "Only one worker confirms"). `full-consensus` findings reached in Step 3 are recorded immediately in the convergence state with `classification: "full-consensus"` and DO NOT enter the queue.
79
+ - Same semantics + same ticket set across 2+ workers → one multi-source group.
80
+ - Same semantics but disjoint ticket sets → separate groups (do NOT over-merge across tickets).
81
+ - Only one worker confirms a finding → one single-source group.
82
+ 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.
84
+ 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.
83
85
 
84
86
  ### Round 1-N: Re-verification Loop (queue-pruned)
85
87
 
86
- The verification queue holds only findings that are not yet classified. Confirmed items are *removed* from the queue and never re-sent.
88
+ The `ConvergenceEngine` reducer owns the working queue, classification strategy, pruning, round arithmetic, gate precedence, skip reason, final state, and classification counts. The lead owns only semantic grouping plus translation of observed worker outcomes into the structured round-results schema. Runtime adapters transport persisted batches and terminal outcomes only.
89
+
90
+ Working artifacts are stored beside the final state:
87
91
 
88
92
  ```text
89
- roundIndex = 0
90
- WHILE roundIndex < effectiveMaxRounds AND queue is non-empty:
91
- roundIndex += 1
92
-
93
- # Round 2 gate (only evaluated when entering round 2 or higher)
94
- IF roundIndex > 1 AND NOT round_gate_open(queue, roundHistory[-1].dispatches):
95
- record round2SkippedReason in convergence state
96
- BREAK
97
-
98
- inputQueueSize = len(queue)
99
- dispatches = []
100
- skippedWorkers = []
101
-
102
- FOR each analysis worker W (excluding report-writer-worker):
103
- items_for_W = [f for f in queue if W != f.originWorker]
104
- IF items_for_W is empty:
105
- skippedWorkers.append({worker: W, reason: "no items to verify"})
106
- CONTINUE
107
- dispatch = send_reverify_request(W, items_for_W, roundIndex)
108
- dispatches.append(dispatch)
109
-
110
- IF len(dispatches) > 0 AND all dispatches in this round are terminal non-result (timeout/error/no-result-file):
111
- # Per "Worker failure handling in reverify" below — do NOT treat as DISAGREE.
112
- record verification-error evidence on each finding in the queue for this round
113
- record round2SkippedReason = "all-reverify-non-result" for any subsequent round
114
- BREAK
115
-
116
- resolvedCount = 0
117
- carriedForwardCount = 0
118
-
119
- FOR each finding F in queue (snapshot):
120
- votes = aggregate_votes(F, dispatches) # AGREE / DISAGREE / SUPPLEMENT / verification-error
121
- IF all non-error votes are AGREE or SUPPLEMENT:
122
- F.classification = "full-consensus"
123
- queue.remove(F); resolvedCount += 1
124
- ELIF majority non-error votes are AGREE or SUPPLEMENT:
125
- F.classification = "partial-consensus"
126
- queue.remove(F); resolvedCount += 1
127
- ELIF all non-error votes are DISAGREE:
128
- F.classification = "worker-unique"
129
- queue.remove(F); resolvedCount += 1
130
- ELSE:
131
- # mixed / insufficient non-error votes, or all-error votes → carry forward
132
- carriedForwardCount += 1
133
-
134
- record roundHistory entry { round: roundIndex, inputQueueSize, resolvedCount,
135
- carriedForwardCount, dispatches, skippedWorkers }
136
-
137
- # Final classification — runs after the WHILE loop exits (queue empty OR roundIndex == effectiveMaxRounds OR Round 2 gate closed)
138
- FOR each finding F still in queue:
139
- IF config.adversarial AND F was carried forward by the adversarial round
140
- resolution (a surviving `counter-evidence` refute, or a burden-not-met
141
- majority — see §"Adversarial Verification Mode"):
142
- F.classification = "contested" # one evidence-backed refute denies consensus, whatever the AGREE tally
143
- ELIF majority AGREE-or-SUPPLEMENT across all executed rounds:
144
- F.classification = "partial-consensus"
145
- ELSE:
146
- F.classification = "contested"
93
+ convergence-groups-<task-type>-<seq>.json
94
+ convergence-work-<task-type>-<seq>.json
95
+ convergence-round-<N>-plan-<task-type>-<seq>.json
96
+ convergence-round-<N>-results-<task-type>-<seq>.json
97
+ convergence-<task-type>-<seq>.json
147
98
  ```
148
99
 
149
- The lead MUST construct the per-worker reverify prompt body from `items_for_W` only — confirmed findings from earlier rounds MUST NOT appear in the prompt, even as background. The dispatch-prompt invariant (every worker gets the same prompt content modulo their own findings) continues to apply to the per-round prompt body.
100
+ Follow this protocol exactly:
150
101
 
151
- #### Round 2 gate (`round_gate_open` predicate)
102
+ 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
+ 2. Run `okstra convergence plan-round --work-state <work> --plan <round-plan>`. This is read-only with respect to the working state.
104
+ 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
+ 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
+ 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.
152
109
 
153
- `round_gate_open(queue, previous_dispatches)` returns `true` iff ALL three conditions hold (here `previous_dispatches` is the most recent entry's `dispatches` array in `roundHistory`); otherwise the lead records `round2SkippedReason` and breaks out of the loop:
110
+ 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.
154
111
 
155
- | Condition | Required value | `round2SkippedReason` if not met |
156
- |---|---|---|
157
- | `effectiveMaxRounds >= 2` | true | `"max-rounds-1"` |
158
- | `len(queue) > 0` after round 1 | true | `"queue-empty"` |
112
+ After `seed`, the lead MUST NOT hand-edit queue IDs, classifications, `roundHistory` arithmetic, `round2SkippedReason`, `finalState`, `totalRounds`, or `finalClassificationCounts`. The persisted plan is the assignment audit record; adapters and leads may not recalculate it. `scripts/okstra_ctl/convergence_engine.py` and `okstra convergence validate` enforce these rules.
113
+
114
+ #### Resume and legacy-state recovery (BLOCKING)
159
115
 
160
- The third gate condition "all reverify dispatches terminated as non-result" is handled inline by the WHILE-loop body (see the `BREAK` on `all dispatches ... terminal non-result` and §"Worker failure handling in reverify" rule 4) which records `round2SkippedReason = "all-reverify-non-result"` and aborts before the predicate is re-evaluated. It is therefore not duplicated as a gate row here.
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.
161
117
 
162
- When all conditions hold the predicate returns `true` and `round2SkippedReason` is set to `"not-skipped"`. The field is mandatory on every convergence state artifact — write `"not-skipped"` rather than omitting the key.
118
+ #### Engine-owned Round 2 gate
119
+
120
+ `plan-round` applies gate precedence in one place: auto-disabled, all reverify non-result, effective maximum of one, empty queue, then maximum rounds reached. `finalize` maps that internal reason to the public `round2SkippedReason` and `finalState`. The lead and adapters never reproduce this predicate.
163
121
 
164
122
  #### Worker failure handling in reverify (BLOCKING)
165
123
 
@@ -167,17 +125,17 @@ A reverify dispatch that returns a **terminal non-result** (`timeout`, `error`,
167
125
 
168
126
  Rules:
169
127
 
170
- 1. For each affected finding, append a `votes[W].verdict = "verification-error"` entry instead of `disagree`, plus the wrapper's captured exit reason in `votes[W].explanation`.
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.
171
129
  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).
172
- 3. Add an entry to the round's `skippedWorkers[]` with `{worker: <W>, reason: "dispatch-non-result", terminalStatus: <timeout|error|not-run>}`.
173
- 4. If at least one dispatch was issued AND all reverify dispatches in a round terminate as non-result (mirroring the pseudocode's `len(dispatches) > 0` guard), the round is treated as gate-closed: write `round2SkippedReason: "all-reverify-non-result"` (even if the round in question is round 1 — i.e. round 2 never runs because round 1 produced no usable votes), record one `contract-violation` event per non-result dispatch, and exit the WHILE loop.
130
+ 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
+ 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.
174
132
  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`.
175
133
 
176
- The final classifier (`FOR each finding F still in queue` block) treats `verification-error` as "no usable vote" — it counts neither toward AGREE nor toward DISAGREE.
134
+ The engine's classifiers treat `verification-error` as "no usable vote" — it counts neither toward AGREE nor toward DISAGREE.
177
135
 
178
136
  ### Convergence Test
179
137
 
180
- The exit conditions and final-classification rules are defined by the §"Convergence Algorithm" pseudocode (the `WHILE` exit, the post-loop `FOR each finding F still in queue` block, and the `finalState` mapping in §"Convergence State Artifact"). This is the single source no separate prose copy is maintained here to prevent drift.
138
+ The executable source is `scripts/okstra_ctl/convergence_engine.py`; `okstra convergence validate --kind final` replays its persisted-state invariants. This document defines orchestration and semantic boundaries, not a second implementation of the reducer.
181
139
 
182
140
  ## Verification Mode
183
141
 
@@ -247,7 +205,9 @@ Design intent: one `counter-evidence` refute denies a claim consensus (it cannot
247
205
 
248
206
  ### Sponsorship Optimization
249
207
 
250
- For each analysis worker with a non-empty current round's `items_for_W` batch, build one reverify prompt and call `redispatch_worker(assignment, prompt, reason)` exactly once per worker through the selected runtime adapter. The batch contains only that worker's eligible findings from the current verification queue; final classification labels never select dispatch targets. The assignment, model, prompt path, Result Path, worker-results path, errors paths, and `dispatchKind` come from the current run artifacts. Every reverify is a fresh one-shot session.
208
+ For each persisted round plan, build exactly one prompt per `dispatches[]` row and call `redispatch_worker(assignment, prompt, reason)` once through the selected runtime adapter. The prompt contains exactly that row's `findingIds` in plan order and MUST NOT add, remove, or reorder findings. This excludes Section 6, every resolved finding, and every finding owned by the receiving origin worker because none can appear in the engine row. The assignment, model, prompt path, Result Path, worker-results path, errors paths, and `dispatchKind` come from the current run artifacts. Every reverify is a fresh one-shot session.
209
+
210
+ The persisted round plan is the audit record for batch membership. The lead and adapter do not branch on task type, provider, model identity, classification labels, or their own view of the queue. They dispatch only the engine-returned row through the selected runtime adapter.
251
211
 
252
212
  Call `await_workers(handles)` through the same adapter and apply the shared terminal-status/completion-path contract before counting a vote. The selected adapter owns native invocation spelling and any jobs-file/CLI fields. This contract owns only the reverify payload and verdict semantics.
253
213
 
@@ -288,6 +248,8 @@ If none of the three is available, **abort the reverify dispatch for that role**
288
248
 
289
249
  Reverify prompts MUST NOT inject the Phase 2 `[Required reading]` clause:
290
250
 
251
+ 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 engine-planned `findingIds` batch and the evidence embedded in those findings.
252
+
291
253
  - **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.
292
254
  - **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).
293
255
 
@@ -496,9 +458,9 @@ Schema rules:
496
458
  - `roundHistory[].carriedForwardCount`: queue size at the END of this round — the single definition. In-round insertions into the queue are forbidden, so this always equals `inputQueueSize - resolvedCount`. The pseudocode's per-item `carriedForwardCount += 1` accumulator is a counting convenience that lands on the same value; persist the post-round queue length, not the loop accumulator, if the two ever diverge.
497
459
  - `roundHistory[].dispatches[]`: one entry per worker that was actually dispatched in this round. Each entry is `{worker, status, durationMs}`. `status ∈ {completed, timeout, error, not-run}`. `durationMs` is integer milliseconds and is always present, even for terminal-non-result dispatches (use the elapsed time before the wrapper gave up).
498
460
  - `roundHistory[].skippedWorkers[]`: per-worker `{worker, reason}` for workers with no items to verify OR with a non-result dispatch.
499
- - `round2SkippedReason`: literal enum `queue-empty | max-rounds-1 | all-reverify-non-result | not-skipped | auto-disabled`. Always present. Precedence when more than one condition holds, highest first: `"auto-disabled"` when the auto-disable rule fired (see §"Configuration" — the loop never built a queue; `roundHistory` is empty); `"max-rounds-1"` when `effectiveMaxRounds == 1` (Round 2 was never attempted — this wins over `queue-empty` even when Round 1 drained the queue, so a clean `requirements-discovery` run records `max-rounds-1`); `"all-reverify-non-result"` when all Round 1 dispatches terminated as non-result; `"queue-empty"` when Round 1 fully drained the queue (`effectiveMaxRounds >= 2` only, by the precedence above); `"not-skipped"` when Round 2 actually ran.
461
+ - `round2SkippedReason`: literal enum `queue-empty | max-rounds-1 | all-reverify-non-result | not-skipped | auto-disabled`. Always present and derived by `finalize`; the engine owns precedence so the lead never writes it manually.
500
462
  - `finalClassificationCounts`: post-loop counts. Required field with keys `fullConsensus`, `partialConsensus`, `contested`, `workerUnique`.
501
- - `finalState ∈ {converged, max-rounds-reached, aborted-non-result}`. Assigned by the lead at WHILE-loop exit: `converged` when the queue is empty at the end of any round; `max-rounds-reached` when the loop exits because `roundIndex == effectiveMaxRounds` with the queue still non-empty; `aborted-non-result` when the loop exits via the Worker-failure BREAK (per the "Worker failure handling in reverify" section, rule 4). `aborted-non-result` is the new v1.1 value.
463
+ - `finalState ∈ {converged, max-rounds-reached, aborted-non-result}`. Derived by `finalize` from the validated queue, history, and stop reason; the lead does not assign it.
502
464
  - `totalRounds`: count of rounds actually executed (not `effectiveMaxRounds`). May be `0` when Round 0 produced no queue items (all findings reached consensus during grouping).
503
465
 
504
466
  ## Coverage critic pass
@@ -571,7 +533,7 @@ Critic output lives in the run's `worker-results/` directory (`runs/final-verifi
571
533
 
572
534
  Information to be passed to Phase 6 after completing this contract:
573
535
 
574
- - Final classification of all findings (4-category)
536
+ - Validated final schema-v1.2 artifact containing the four-category classification of all findings; the report-writer consumes it and does not vote
575
537
  - Round history and votes per worker for each finding
576
538
  - Path to the convergence state artifact
577
539
  - Convergence summary (count per category)
@@ -47,7 +47,7 @@ Read-side inspection (`/okstra-inspect`) and scheduling (`/okstra-schedule-gen`)
47
47
  | 3. Dispatch setup | Execute the selected adapter's pre-dispatch setup and record its state | selected runtime adapter + this contract |
48
48
  | 4. Execution | Call `dispatch_worker` once per selected assignment | selected runtime adapter + `team-contract` |
49
49
  | 5. Completion wait | Call `await_workers` and verify terminal state plus required artifacts | selected runtime adapter + `team-contract` |
50
- | 5.5 Convergence | Cross-verify findings across workers | `convergence` |
50
+ | 5.5 Convergence | Semantically group findings, then drive deterministic state transitions through `ConvergenceEngine` via `okstra convergence` | `convergence` |
51
51
  | 5.6 Critic pass | (opt-in) fresh one-shot critic pass through `redispatch_worker`: coverage gaps (discovery/error-analysis/impl-planning) or acceptance devil's-advocate (final-verification). The critic dispatch fires concurrently with the first 5.5 reverify round (its input is fixed at Round 0); gap/blocker verification (one round) completes here | `convergence` "Coverage critic pass" / "Acceptance critic pass" |
52
52
  | 6. Synthesis | Dispatch Report writer worker, review draft. **For `implementation-planning`: then run the Phase 6 plan-body verification sub-step (see Phase 6 section below).** | `report-writer` + `plan-body-verification` (sub-step) |
53
53
  | 7. Persist | Call `collect_usage`, update manifests, run the cleanup approval gate, then call `shutdown_workers` only on approval | selected runtime adapter + `report-writer` + this contract |
@@ -208,6 +208,10 @@ 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
+
213
+ For `improvement-discovery`, Lead records `## Primary Pass Assignments` in the Phase 1.5 grilling log before worker prompt generation. Enumerate selected analyser worker instances in run-manifest `requiredWorkerRoles` order and rotate them over the resolved lenses in log order; provider and model names never determine assignment position. Every analyser still inspects every resolved lens. Phase 7 recomputes this rotation from the persisted roster and fails a missing, extra, duplicate, out-of-order, or out-of-scope assignment.
214
+
211
215
  `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
216
 
213
217
  ### Phase 3 — Runtime adapter setup (BLOCKING)
@@ -280,15 +284,15 @@ Convergence is enabled by default. Configure via task-manifest.json:
280
284
  - `convergence.verificationMode`: `"lightweight"` | `"full-reanalysis"` (default: `"lightweight"`; the adversarial phases below force `"full-reanalysis"`)
281
285
  - `convergence.adversarial`: true/false — **phase-aware default**: `true` for `requirements-discovery` / `error-analysis` / `implementation-planning`, `false` otherwise. When `true`, Phase 5.5 runs in adversarial mode (verifiers refute findings; burden of proof on the claim). See [convergence](./convergence.md) "Adversarial Verification Mode".
282
286
 
283
- When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5, and record the resolved value in the convergence state artifact at `config.effectiveMaxRounds`.
287
+ When `task-manifest.json` does not set `convergence.maxRounds`, lead MUST resolve the effective value via the phase-aware default above before entering Phase 5.5 and put it in the grouped input at `config.effectiveMaxRounds`.
284
288
 
285
- **Round 2 is gated, not unconditional.** Even when `effectiveMaxRounds == 2`, Round 2 runs only when (a) the verification queue is non-empty after Round 1, AND (b) at least one Round 1 reverify dispatch terminated as `completed`. Otherwise lead writes `round2SkippedReason` to the convergence state and proceeds to final classification. See [convergence](./convergence.md) "Round 2 gate" for the predicate.
289
+ The lead judges semantic similarity, ticket-set equality, and evidence. After writing the grouped input, it drives `okstra convergence seed plan-round apply-round finalize validate` and MUST NOT calculate queue membership, classification, history arithmetic, skip reasons, final state, or counts itself. `ConvergenceEngine` owns all of those deterministic transitions.
286
290
 
287
- **Confirmed findings are pruned from the queue.** Findings classified as `full-consensus`, `partial-consensus`, or `worker-unique` MUST NOT appear in any subsequent round's reverify prompt for any worker. `contested` is a final classification assigned only when the last executed round completes and the queue is still non-empty it is NEVER an intermediate queue label.
291
+ For every dispatch plan, create only the worker batches returned by the engine and send them through the selected adapter. Confirmed findings cannot reappear because queue pruning is engine-owned and monotonic. Reverify terminal failures are structured outcomes for `apply-round`, never lead-authored `DISAGREE` votes.
288
292
 
289
293
  If any re-verification batch yields a `verification-error` terminal status, or a worker result fails the contract, Lead MUST record one event per violation via `okstra error-log append-observed --error-type contract-violation --agent <offending-agent> ...`. For an internally detected violation without a specific worker, use the selected adapter's lead-role event identity.
290
294
 
291
- If convergence is disabled, proceed directly to Phase 6 with the raw worker results.
295
+ If convergence is disabled, `seed`/`finalize` produce the auto-disabled final state and Phase 6 uses the raw worker results for synthesis.
292
296
 
293
297
  ## Phase 6: Final report assembly
294
298
 
@@ -401,10 +405,10 @@ After persistence, reply briefly in the resolved Report Language with: completio
401
405
  | Selecting a generic worker assignment for Report writer worker | Use the rostered `report-writer-worker` assignment; the selected adapter owns its native field mapping |
402
406
  | Including `final-report-template.md` in analysis worker `[Required reading]` | Template belongs only in the report-writer prompt — see [team-contract](./team-contract.md) audience-scoped enumeration |
403
407
  | Injecting `[Required reading]` into lightweight reverify prompts | Lightweight reverify forbids re-reading source materials — see [convergence](./convergence.md) "Reverify prompt: required-reading suppression" |
404
- | Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and record in convergence state artifact |
408
+ | Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and put it in the grouped input |
405
409
  | Issuing serial Read calls in Phase 1 | The intake files are independent — issue all Read calls in a single message (parallel) |
406
410
  | Flagging an adapter-shortened dispatch prompt as "incomplete" because it omits host-loaded material | The Worker Preamble pointer and core inputs remain mandatory; the selected adapter may omit duplicate host-loaded definitions |
407
411
  | Waiting silently after `dispatch_worker` returns without a completed worker artifact | A dispatch acknowledgement is not completion — call `await_workers` and enforce the selected adapter's liveness policy |
408
- | Re-sending confirmed findings (`full-consensus`/`partial-consensus`/`worker-unique`) to a worker in Round 2 | Queue pruning rule see [convergence](./convergence.md) "Round 1-N: Re-verification Loop (queue-pruned)" |
409
- | Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` | Worker failure handling record as `verification-error` and add to `skippedWorkers[]`. See [convergence](./convergence.md) "Worker failure handling in reverify" |
412
+ | Re-sending a finding absent from the persisted round plan | Dispatch exactly the engine-returned `findingIds`; see [convergence](./convergence.md) "Re-verification Dispatch" |
413
+ | Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` | Put the terminal outcome in round results; `apply-round` records `verification-error`. See [convergence](./convergence.md) "Worker failure handling in reverify" |
410
414
  | Skipping `--substitute-data` in the Phase 7 collector run | Always pass the flag — see [report-writer](./report-writer.md) "Phase 7 token-usage collector" |
@@ -37,8 +37,9 @@ The prompt MUST include, in this order at the top:
37
37
  3. `**Result Path:** runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json` — canonical JSON SSOT. The renderer produces the sibling `.md` automatically.
38
38
  4. `**Worker Result Path:** runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md` — mandatory validator-checked worker-results audit file
39
39
  5. `Assigned worker prompt history path: <absolute-path>`
40
- 6. The three BLOCKING dispatch anchor headers the lead MUST inject (per [team-contract](./team-contract.md) headers #5 / #7 / #8 — the worker cannot synthesize the two error paths and must not dead-end when they are absent):
41
- - `**Worker Preamble Path:** <absolute-path>` — the canonical Required Reading + Error Reporting SSOT at `~/.okstra/templates/worker-prompt-preamble.md`; the worker Reads it end-to-end before producing output.
40
+ 6. The four BLOCKING dispatch anchor headers generated from the report-writer audience (the worker cannot synthesize any of these paths):
41
+ - `**Worker Preamble Path:** <absolute-path>` — selects `templates/report-writer-prompt-preamble.md`.
42
+ - `**Worker Error Contract Path:** <absolute-path>` — selects `templates/worker-error-contract.md`.
42
43
  - `**Errors log path:** <absolute-path>` — run-level errors JSONL (`logs/errors-<task-type>-<seq>.jsonl`).
43
44
  - `**Errors sidecar path:** <absolute-path>` — this worker's per-run sidecar JSON (`worker-results/report-writer-worker-errors-<task-type>-<seq>.json`).
44
45
  7. `**Model:** Report writer worker, <modelExecutionValue>` (resolved per Phase 5.5 anchor-header rules)
@@ -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,15 @@ 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 body must include: role name, task type, task key, required bundle paths, assigned model, output contract, evidence handling rules, and any relevant config/deployment expectations from `reference-expectations.md`.
68
+ The `**Coding preflight pack:**` anchor is emitted only for the `implementation-executor` and `implementation-verifier` audiences. An implementation report-writer never receives it. 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
+ For `improvement-discovery`, `worker_prompt_headers()` resolves and validates the Phase 1.5 grilling log before dispatch and emits its absolute path as `**Phase 1.5 Grilling Log:**`. The lead does not hand-author or guess that path.
71
+
72
+ 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.
73
+
74
+ A `final-verification` prompt may contain exactly 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.
75
+
76
+ The Phase 7 run validator enforces the same cross-task rule against the persisted prompt paths recorded by the run manifest, dispatch records, and team-state. Both dispatch adapters and `validators/validate-run.py` call `okstra_ctl.worker_prompt_contract.validate_initial_prompt_records()`. The validator resolves each record through `PromptPlan`, applies audience-specific anchors, compares every equality group with at least two prompts, validates report-writer common anchors without adding it to an analysis group, and ignores `-reverify-r<N>-` prompts because they belong to the lightweight convergence protocol. A dispatch-time bypass therefore remains a run-level contract violation.
67
77
 
68
78
  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
79
 
@@ -73,20 +83,20 @@ Persist the exact worker prompt before dispatch per Operating Rule 6; never use
73
83
 
74
84
  Send byte-identical dispatch prompts to every analysis worker per the "Dispatch-prompt invariant" (Role Definitions). Specialization lives in Section 6 of the worker output, not in the dispatch prompt body.
75
85
 
76
- ### Required Reading + Error Reporting via Worker Preamble (SSOT)
86
+ ### Audience preambles + shared error contract (SSOT)
77
87
 
78
- 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.
88
+ The lead does not inline reading or error blocks. It resolves `PromptPlan.audience` and injects exactly one `**Worker Preamble Path:**`: analysis `templates/worker-prompt-preamble.md`, implementation executor/verifier → `templates/implementation-worker-preamble.md`, report-writer → `templates/report-writer-prompt-preamble.md`. Every initial worker also receives `**Worker Error Contract Path:**` pointing to `templates/worker-error-contract.md`, the sole owner of error schema and write rules. Reverify prompts use neither preamble.
79
89
 
80
90
  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 required primary input and list task-brief / analysis-profile / analysis-material / reference-expectations / clarification-response as source/fallback paths only when useful. The preamble describes the rules; the lead provides the specific paths for THIS run.
82
- - Inject the absolute `**Errors log path:**` and `**Errors sidecar path:**` anchor headers — workers cannot synthesize these paths.
91
+ - 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.
92
+ - Inject `**Worker Error Contract Path:**` plus the absolute `**Errors log path:**` and `**Errors sidecar path:**` headers — workers cannot synthesize these paths.
83
93
  - Omit the preamble pointer for reverify dispatches (Phase 5.5 lightweight mode) — see [convergence](./convergence.md) "Reverify prompt: required-reading suppression".
84
94
 
85
95
  Audience-scoped file enumeration (performance optimization — mandatory):
86
96
 
87
97
  | Recipient | Files the lead lists under `## Inputs` |
88
98
  |---|---|
89
- | Claude / Codex / Antigravity analysis workers | `analysis-packet.md` as primary input; source/fallback paths may be listed below it but are not automatic first-read files |
99
+ | Claude / Codex / Antigravity analysis workers | `analysis-packet.md` as primary input; for `final-verification`, no source/fallback list is copied into the prompt |
90
100
  | 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
101
  | Reverify dispatches | none — the lead provides only the items to reverify |
92
102
 
@@ -141,7 +151,7 @@ After each worker subagent returns (regardless of role), Lead MUST verify the ca
141
151
 
142
152
  ## Worker Output Contract
143
153
 
144
- The canonical worker output contract — result frontmatter schema, section ordering (1–5 + optional Section 6), item-ID rules, ticket tagging, and the worker result header standard — lives in the worker preamble (`templates/worker-prompt-preamble.md`), the file every worker reads before producing output.
154
+ The canonical analysis-worker output contract — frontmatter, sections 1–5 plus optional Section 6, item IDs, and ticket tagging — lives in `templates/worker-prompt-preamble.md`. Implementation output behavior remains in its executor/verifier sidecars; report authoring remains in the report-writer preamble and Phase 6 contract.
145
155
 
146
156
  Lead-facing duties that stay here:
147
157
 
@@ -215,8 +225,8 @@ without proceeding.
215
225
  2. Re-verification workers follow a constrained response format (verdict + brief explanation).
216
226
  3. Workers cannot vote on their own findings (only verify other workers’ work).
217
227
  4. The `report writer worker` does not participate in re-verification voting. It is responsible only for generating the final report.
218
- 5. Division of labor: the lead performs **finding-to-finding matching** (deciding which worker-A finding maps to which worker-B finding for cross-review) and mediates the round protocol; **workers cast the AGREE / DISAGREE / SUPPLEMENT votes** that determine consensus. The lead does NOT vote on substance and does NOT collapse worker disagreements by fiat disagreements flow into the `contested` / `partial-consensus` classifications defined in `prompts/lead/convergence.md`.
219
- 6. Batch processing is performed with one spawn per worker per round (not one spawn per finding).
228
+ 5. Division of labor: the lead performs **finding-to-finding matching** (semantic similarity plus ticket-set equality); workers cast AGREE / DISAGREE / SUPPLEMENT verdicts; `ConvergenceEngine` owns queue membership, classifications, round arithmetic, pruning, skip reasons, and final counts. The lead does not vote or hand-edit engine state.
229
+ 6. Batch processing is performed with one fresh spawn per persisted `plan-round` worker row (not one spawn per finding). The selected adapter transports that exact batch and terminal result without changing membership.
220
230
  7. These rules do not apply if Convergence is disabled.
221
231
 
222
232
  ## Re-verification Terminal Statuses
@@ -75,7 +75,7 @@ profile document.
75
75
  - **Worker-side item IDs (free-form but unique within the worker).** Every row item in sections 1–5 (and any optional section 6) of an analysis worker's output MUST carry an item ID that is unique within that one worker's result file. The ID convention is the worker's choice — `F-001` / `F-002` per the suggested schema, `1.1` / `1.2` / `1.3` as Codex tends to use, or any other shape — but it MUST appear as the leading column of the row (for table-form items) or as a `[<ID>]` prefix (for bullet/numbered items). Workers that emit findings without IDs make cross-worker reconciliation impossible.
76
76
  - **Lead-side ID assignment + source preservation.** When the lead (or `report-writer-worker`) synthesises `## 6.1 Consensus` / `## 6.2 Differences` / `## 2.1 Primary Evidence` rows from worker outputs, the lead assigns a fresh `C-NNN` / `D-NNN` / `E-NNN` row ID. The `Source items` column (or, where the template still calls it `Supporting workers` / `Workers (position)` / `Source`, that same column) MUST list every contributing worker:item pair (e.g. `claude:F-001, codex:1.1, antigravity:F-3`) so a reviewer can trace the synthesised row back to each worker's original wording without re-reading every worker-results file. Bare worker names without item IDs (e.g. `claude, codex, antigravity`) are deprecated for these tables; the validator does not yet fail on them but the readability pass treats it as a contract violation.
77
77
  - **Why this matters.** A real run had `claude=F-1..F-11`, `codex=1.1..1.8`, `antigravity=F-3..F-9` — three incompatible ID schemes. When the lead synthesised `C-1..C-8`, the link from `C-3` back to "which sentence in which worker file" was lost. Source-item preservation restores that link without forcing every worker to adopt a single ID prefix, which would over-constrain worker output style.
78
- - Audit sidecar (shared): Reading Confirmation placement and audit-sidecar rules are canonical in the worker preamble (`templates/worker-prompt-preamble.md` §"Reading rules"). Profiles do not restate them; the main worker-results body starts at section 1.
78
+ - Audit sidecar (shared): Reading Confirmation placement follows the audience-selected preamble named by `**Worker Preamble Path:**`. Profiles do not restate it; the main worker-results body starts at section 1.
79
79
 
80
80
  - Markdown authoring (shared — applies to markdown documents not already governed by an okstra template/schema):
81
81
  - ad-hoc markdown documents should begin with an `Index` section. Template-governed artifacts such as final-reports, worker-results, and briefs follow their own schema first.
@@ -14,7 +14,7 @@
14
14
  - read `Related Task Graph` before forming root-cause candidates. `depends-on`, `blocks` / `blocked-by`, parent/child, follow-up, and split edges define upstream/downstream boundaries for the symptom: identify whether this task's failure is caused by, blocks, or merely relates to another task before merging causes.
15
15
  - any `intent-inference` augmentation that re-characterises the symptom (e.g. classifying a vague reporter phrase like "it sometimes doesn't work" as "intermittent failure on a specific code path") is a **hypothesis**, not a confirmed symptom. If `[CONFIRMED …]` appears on the matching `intent-check:` row, treat that confirmation as the symptom. Otherwise follow the precondition's `skipped` branch above and keep the inference labelled as a hypothesis in the root-cause analysis.
16
16
  - `conversion-block:` rows mean the brief could not map a reporter statement to project vocabulary; never invent the missing mapping in this phase.
17
- - Diagnosis loop:
17
+ - Worker diagnosis procedure:
18
18
  - **Symptom lock:** state the reporter's symptom verbatim, then translate it into one observable failure condition. If no observable condition can be derived from the brief, record that gap as the first blocker instead of guessing.
19
19
  - **Reproduction status:** classify the run as `reproduced`, `not-reproduced`, or `blocked-before-repro`. Cite the command/log/file evidence used. If no command can be run safely in this phase, explain the read-only evidence path and the exact material needed next.
20
20
  - **Falsifiable cause candidates:** every root-cause candidate must include supporting evidence, the strongest falsifying evidence checked, confidence, and the next diagnostic action that would disprove it. A candidate that cannot be falsified is too vague for this phase.
@@ -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 ref, 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). The lead injects this same snapshot into every analyser prompt (`**Verification scope:** / **Worktree:** / **Verification base ref:** / **Verification diff stat:**`); a worker that cannot confirm its analysis ran against that worktree's delivered diff (against `base ref`) MUST record a `tool-failure`.
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.
@@ -13,6 +13,11 @@
13
13
  - Apply the shared reporter-confirmation precondition exactly as written. In this phase, unresolved `intent-check:` / `conversion-block:` rows carry `Blocks=approval`, so the approval frontmatter stays `approved: false` until they are resolved.
14
14
  - never plan around an unconfirmed `intent-inference` augmentation as if it were a settled requirement. Treat the inference as settled ONLY when a `[CONFIRMED …]` marker sits on the matching `intent-check:` row after the precondition runs; absent the marker it stays a `Blocks=approval` clarification item per the precondition's `skipped` branch.
15
15
  - `conversion-block:` rows are handled by the precondition; planning around an untranslated reporter phrase is forbidden until it is resolved.
16
+ - Worker planning procedure:
17
+ - identify requirement gaps and affected interfaces with file:line evidence, resolving codebase-answerable ambiguity before returning findings
18
+ - compare at least two feasible options unless the brief carries a confirmed decision; record concrete trade-offs and current-pattern evidence for every option
19
+ - propose stages with real dependency edges, validation signals, rollback order, and change-locality evidence; do not serialize independent work
20
+ - surface migration, deployment, cross-project, and approval risks without drafting final-report headings or schema rows
16
21
  - Pre-planning context exploration (mandatory before option drafting):
17
22
  - read the task brief, related-task briefs, and any cited spec / design doc end-to-end
18
23
  - inspect the current state of every file the task names (or the closest matching files if names are stale) — record current responsibilities, public interfaces, and known coupling points
@@ -21,8 +21,13 @@
21
21
  - per-candidate evidence (path:line) and scope mapping
22
22
  - per-candidate severity / effort / recommended-next-phase
23
23
  - convergence classification (full / partial / contested / worker-unique) across workers
24
+ - Worker candidate procedure:
25
+ - inspect every resolved priority lens inside the resolved scan scope; the assigned primary lens changes only the first pass, never total coverage
26
+ - for the primary pass, return an evidence-backed candidate or a no-candidate rationale citing the highest-signal path:line inspected
27
+ - for every candidate, record lens, scope, severity, effort, recommended next phase, evidence, and a worker-local source item ID
28
+ - identify overlap with other findings or linked tasks as duplicate, broader/narrower, conflicting, blocked-by, or follow-up instead of silently merging it
24
29
  - Worker diversity rule:
25
- - every analyser inspects every priority lens, but each starts with a different primary pass. Order lenses exactly as they appear in the brief; `claude` starts at lens 1, `codex` at lens 2, and `antigravity` at lens 3, wrapping around when fewer lenses are present.
30
+ - every analyser inspects every resolved priority lens. The Phase 1.5 grilling log assigns only the first pass: enumerate selected analyser worker instances in `requiredWorkerRoles` order and rotate them over resolved priority lenses in log order. Provider/model names never determine the position.
26
31
  - each worker, before broadening to the remaining lenses, must do one of: (a) produce at least one candidate from its primary pass, or (b) record a no-candidate rationale citing the highest-signal path:line evidence it inspected.
27
32
  - two workers' candidates are the same candidate only when they cite the same underlying design/code problem and the same remediation direction. Shared evidence paths alone are not enough to merge; keep distinct failure modes distinct.
28
33
  - when a candidate from one worker overlaps another worker's evidence, convergence must classify the relationship as one of: duplicate, broader/narrower, or conflicting. Do not collapse contested candidates just to meet the candidate cap.
@@ -33,10 +38,15 @@
33
38
  - For each open question Lead asks ONE `AskUserQuestion` with a `(Recommended)` answer drawn from a codebase-first inspection. Budget: at most 12 questions in this phase.
34
39
  - Stop conditions (OR): all questions resolved / budget exhausted / user signals proceed.
35
40
  - Lead persists the round at `<RUN_DIR>/state/phase-1.5-grilling.md` with one section per question (question / recommended / user answer) and a closing `Resolved scope` / `Resolved lenses` block. Worker prompts use this resolved block as the authoritative scope and lens definition.
41
+ - The same log includes `## Primary Pass Assignments` with a `Worker ID | Primary lens` table. It contains every selected analyser exactly once in `requiredWorkerRoles` order; the lead derives it from the resolved roster and lenses rather than provider catalog order.
36
42
  - After writing the log and before Phase 4 dispatch, the lead injects its **absolute path** into every analyser prompt as the `**Phase 1.5 Grilling Log:** <absolute-path>` anchor header (see `templates/worker-prompt-preamble.md` §"Anchor headers"). This is the improvement-discovery counterpart to the `**Worktree:**` / `**Verification …:**` anchors that implementation / final-verification inject: workers read the log from this explicit path rather than re-deriving `<RUN_DIR>`. The path is byte-identical across all analysers, so it does not break the dispatch-prompt invariant.
37
43
  - Decision-tree walk (bounded):
38
44
  - When candidates branch on a structural question (e.g. "is module X meant to own this responsibility?"), resolve via `Read` / `Grep` first. Only escalate to the user inside the Phase 1.5 budget.
39
45
  - Expected output emphasis:
46
+ - evidence-backed candidate or explicit no-candidate result for every resolved lens
47
+ - worker-local candidate IDs that convergence can trace back to their source worker
48
+ - uncertainty and overlap relationships kept explicit for downstream consensus classification
49
+ - Report assembly instructions:
40
50
  - the `## 5.9 Improvement Candidates` table populated with rows that obey the 10-column schema from `validators/validate_improvement_report.py` (Cand ID `I-NNN`, Lens from whitelist, Title, Scope ⊆ scan-scope, Severity, Effort, Consensus, Source workers `<worker>:<id>` from {claude, codex, antigravity}, Recommended next-phase ∈ {requirements-discovery, implementation-planning, error-analysis}, Evidence as path:line list)
41
51
  - `Consensus` cells in `## 5.9 Improvement Candidates` use the table enum exactly: `full`, `partial`, `contested`, `worker-unique`. Map convergence's `full-consensus` / `partial-consensus` labels to `full` / `partial` before writing the table.
42
52
  - `## 7. Final Verdict` Verdict Token ∈ {`candidates-ready`, `no-candidates`, `blocked`}; Direction `routing`; Next Step "ask the user to select K candidates (see the ## 5.9 table)"
@@ -14,6 +14,11 @@
14
14
  - before deciding whether to fan out, read `Related Task Graph`. Use `depends-on`, `blocks` / `blocked-by`, parent/child, follow-up, and split edges as seed ordering constraints. Do not flatten a directed graph into unrelated tasks.
15
15
  - `intent-inference` augmentations whose paired `intent-check:` row carries `[CONFIRMED …]` are treated as **confirmed**; trust the confirmation text in `## Reporter Confirmations` over the original inference if they differ. Unconfirmed `intent-inference` rows under `reporter-confirmations: skipped` follow the precondition's `skipped` branch above.
16
16
  - `conversion-block:` rows are explicit "translation failed" signals — never attempt to resolve them by inference here; the precondition above already handled them.
17
+ - Worker discovery procedure:
18
+ - classify the request and cite the evidence that determines both its work category and safest next phase
19
+ - identify independently startable decomposition candidates without publishing or rendering fan-out artifacts; preserve every directed dependency from `Related Task Graph`
20
+ - resolve codebase-answerable ambiguity by inspection and record file:line evidence; return only human-owned decisions as clarification candidates with the evidence already checked
21
+ - state the reporter's rejection criteria, missing routing inputs, and the evidence boundary behind each recommendation
17
22
  - Primary focus areas:
18
23
  - classify the work as bugfix, feature, improvement, refactor, or ops
19
24
  - determine whether `error-analysis` or `implementation-planning` is the next safe step. Direct `implementation` handoff is never a valid routing target — implementation requires an approved `implementation-planning` report
@@ -49,7 +54,9 @@
49
54
  - evidence-backed routing decision
50
55
  - uncertainty boundaries and missing inputs
51
56
  - next recommended phase and safe resume guidance
52
- - canonical-term resolution for every `terminology:*` brief item, written as a one-line `<term> = <definition>` line in a new `Domain Alignment` subsection of the final report; alongside each, propose whether `<PROJECT_ROOT>/.okstra/glossary.md` should be updated (proposal only — actual writes happen via `okstra-brief-gen` Step 4.5 on a subsequent run)
57
+ - canonical-term resolution for every `terminology:*` brief item as `<term> = <definition>`, plus whether `<PROJECT_ROOT>/.okstra/glossary.md` should be updated
58
+ - Report assembly instructions:
59
+ - write canonical-term resolutions in a new `Domain Alignment` subsection of the final report; actual glossary writes happen via `okstra-brief-gen` Step 4.5 on a subsequent run
53
60
  - Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
54
61
  - if any blocking input is missing at the time of writing the final report, populate `## 1. Clarification Items` in `final-report-template.md` (a single unified table; `Blocks=next-phase` for items the next run cannot start without)
55
62
  - prefer concrete questions whose answers map directly to a routing decision (`bugfix` vs `feature`, `error-analysis` vs `implementation-planning`, etc.). State each option in plain language with one sentence describing what choosing it would mean for the next phase.