okstra 0.180.0 → 0.183.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-registry.mjs +16 -2
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.d.mts +4 -2
- package/dist/commands/execute/render-bundle.mjs +46 -5
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +11 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/inspect/model-io.d.mts +1 -0
- package/dist/commands/inspect/model-io.mjs +25 -0
- package/dist/commands/inspect/model-io.mjs.map +1 -0
- package/dist/commands/inspect/stage-map.mjs +29 -8
- package/dist/commands/inspect/stage-map.mjs.map +1 -1
- package/dist/commands/inspect/task-list.mjs +52 -6
- package/dist/commands/inspect/task-list.mjs.map +1 -1
- package/dist/commands/inspect/user-response.mjs +14 -4
- package/dist/commands/inspect/user-response.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.d.mts +1 -0
- package/dist/commands/lifecycle/check-project.mjs +69 -50
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/contract-check.d.mts +1 -0
- package/dist/commands/lifecycle/contract-check.mjs +18 -0
- package/dist/commands/lifecycle/contract-check.mjs.map +1 -0
- package/dist/commands/lifecycle/preflight.mjs +154 -51
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/pr/pr.d.mts +1 -0
- package/dist/commands/pr/pr.mjs +19 -1
- package/dist/commands/pr/pr.mjs.map +1 -1
- package/dist/commands/report/agent-activity.mjs +2 -2
- package/dist/commands/report/translate.mjs +3 -0
- package/dist/commands/report/translate.mjs.map +1 -1
- package/dist/lib/host-registry-client.mjs +13 -9
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/docs/architecture.md +11 -0
- package/docs/cli.md +26 -13
- package/docs/container.md +6 -4
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-brief-gen.md +5 -3
- package/docs/for-ai/skills/okstra-code-review.md +4 -4
- package/docs/for-ai/skills/okstra-container-build.md +20 -17
- package/docs/for-ai/skills/okstra-inspect.md +20 -23
- package/docs/for-ai/skills/okstra-manager.md +19 -18
- package/docs/for-ai/skills/okstra-memory.md +2 -2
- package/docs/for-ai/skills/okstra-pr-gen.md +3 -3
- package/docs/for-ai/skills/okstra-rollup.md +14 -13
- package/docs/for-ai/skills/okstra-run.md +7 -3
- package/docs/for-ai/skills/okstra-schedule-gen.md +15 -18
- package/docs/for-ai/skills/okstra-setup.md +7 -7
- package/docs/for-ai/skills/okstra-usage.md +5 -4
- package/docs/for-ai/skills/okstra-user-response.md +50 -32
- package/docs/project-structure-overview.md +29 -26
- package/docs/task-process/implementation.md +7 -4
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +4 -4
- package/runtime/agents/workers/report-writer-worker.md +3 -3
- package/runtime/agents/workers/translator-worker.md +5 -13
- package/runtime/bin/okstra-error-log.py +51 -11
- package/runtime/bin/okstra-report-translate.py +210 -23
- package/runtime/prompts/host-orchestration/implementation.md +1 -1
- package/runtime/prompts/launch.template.md +3 -7
- package/runtime/prompts/lead/context-loader.md +41 -141
- package/runtime/prompts/lead/convergence.md +8 -6
- package/runtime/prompts/lead/okstra-lead-contract.md +25 -35
- package/runtime/prompts/lead/plan-body-verification.md +187 -14
- package/runtime/prompts/lead/report-writer.md +20 -1
- package/runtime/prompts/lead/team-contract.md +8 -53
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +3 -2
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +8 -2
- package/runtime/prompts/profiles/release-handoff.md +0 -1
- package/runtime/prompts/wizard/prompts.ko.json +0 -1
- package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +69 -17
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +13 -4
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +6 -1
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +50 -5
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +66 -5
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +70 -2
- package/runtime/python/okstra_ctl/agent_activity.py +118 -35
- package/runtime/python/okstra_ctl/agent_invocation.py +19 -6
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +65 -18
- package/runtime/python/okstra_ctl/analysis_inputs.py +5 -4
- package/runtime/python/okstra_ctl/analysis_packet.py +81 -1
- package/runtime/python/okstra_ctl/approval_decisions.py +3 -2
- package/runtime/python/okstra_ctl/attempt_evidence.py +2 -2
- package/runtime/python/okstra_ctl/backfill.py +13 -10
- package/runtime/python/okstra_ctl/batch.py +2 -4
- package/runtime/python/okstra_ctl/build_tools.py +6 -3
- package/runtime/python/okstra_ctl/claim_reproduction.py +101 -0
- package/runtime/python/okstra_ctl/clarification_items.py +27 -13
- package/runtime/python/okstra_ctl/cmux.py +130 -52
- package/runtime/python/okstra_ctl/code_review_target.py +34 -8
- package/runtime/python/okstra_ctl/consumers.py +5 -4
- package/runtime/python/okstra_ctl/container.py +103 -8
- package/runtime/python/okstra_ctl/context_cost.py +2 -1
- package/runtime/python/okstra_ctl/contract_graph.py +497 -0
- package/runtime/python/okstra_ctl/contract_graph_cli.py +62 -0
- package/runtime/python/okstra_ctl/convergence.py +338 -17
- package/runtime/python/okstra_ctl/convergence_engine.py +10 -18
- package/runtime/python/okstra_ctl/convergence_provenance.py +58 -8
- package/runtime/python/okstra_ctl/convergence_store.py +55 -34
- package/runtime/python/okstra_ctl/design_prep.py +7 -4
- package/runtime/python/okstra_ctl/dispatch_core.py +35 -65
- package/runtime/python/okstra_ctl/dispatch_state.py +134 -59
- package/runtime/python/okstra_ctl/doctor.py +6 -3
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +70 -9
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +16 -30
- package/runtime/python/okstra_ctl/error_log_write.py +35 -30
- package/runtime/python/okstra_ctl/error_report.py +26 -1
- package/runtime/python/okstra_ctl/error_zip.py +27 -5
- package/runtime/python/okstra_ctl/execution_identity.py +3 -2
- package/runtime/python/okstra_ctl/execution_manifest.py +7 -4
- package/runtime/python/okstra_ctl/final_report_schema.py +2 -2
- package/runtime/python/okstra_ctl/fix_cycles.py +2 -2
- package/runtime/python/okstra_ctl/fixed_text.py +39 -0
- package/runtime/python/okstra_ctl/git_reconcile.py +41 -9
- package/runtime/python/okstra_ctl/handoff.py +5 -4
- package/runtime/python/okstra_ctl/i18n.py +4 -2
- package/runtime/python/okstra_ctl/implementation_direction.py +22 -14
- package/runtime/python/okstra_ctl/implementation_outcome.py +4 -7
- package/runtime/python/okstra_ctl/incremental_carry.py +2 -1
- package/runtime/python/okstra_ctl/incremental_scope.py +8 -3
- package/runtime/python/okstra_ctl/index.py +8 -11
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +79 -7
- package/runtime/python/okstra_ctl/invocation.py +3 -6
- package/runtime/python/okstra_ctl/json_boundary.py +366 -0
- package/runtime/python/okstra_ctl/json_registry.py +10 -12
- package/runtime/python/okstra_ctl/jsonl.py +19 -2
- package/runtime/python/okstra_ctl/lead_events.py +33 -1
- package/runtime/python/okstra_ctl/listing.py +3 -3
- package/runtime/python/okstra_ctl/log_report.py +24 -2
- package/runtime/python/okstra_ctl/manager_cli.py +92 -7
- package/runtime/python/okstra_ctl/manager_store.py +12 -10
- package/runtime/python/okstra_ctl/material.py +5 -1
- package/runtime/python/okstra_ctl/migrate.py +29 -25
- package/runtime/python/okstra_ctl/model_cli.py +3 -15
- package/runtime/python/okstra_ctl/model_io_cli.py +1051 -0
- package/runtime/python/okstra_ctl/mutation_probe.py +13 -4
- package/runtime/python/okstra_ctl/pane_reclaim.py +3 -2
- package/runtime/python/okstra_ctl/paths.py +9 -0
- package/runtime/python/okstra_ctl/plan_items.py +115 -4
- package/runtime/python/okstra_ctl/plan_items_cli.py +513 -18
- package/runtime/python/okstra_ctl/pr_template.py +3 -2
- package/runtime/python/okstra_ctl/project_meta.py +5 -7
- package/runtime/python/okstra_ctl/recap.py +5 -4
- package/runtime/python/okstra_ctl/reconcile.py +21 -27
- package/runtime/python/okstra_ctl/registry/host_discovery.py +3 -2
- package/runtime/python/okstra_ctl/registry/provider_registry.py +3 -2
- package/runtime/python/okstra_ctl/render.py +26 -15
- package/runtime/python/okstra_ctl/render_final_report.py +3 -2
- package/runtime/python/okstra_ctl/report_assembly.py +172 -17
- package/runtime/python/okstra_ctl/report_finalize.py +7 -10
- package/runtime/python/okstra_ctl/report_html/render.py +3 -2
- package/runtime/python/okstra_ctl/report_language.py +3 -2
- package/runtime/python/okstra_ctl/report_markdown.py +13 -1
- package/runtime/python/okstra_ctl/report_narrative.py +40 -8
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +518 -0
- package/runtime/python/okstra_ctl/report_views.py +3 -2
- package/runtime/python/okstra_ctl/rollup.py +65 -4
- package/runtime/python/okstra_ctl/run.py +159 -56
- package/runtime/python/okstra_ctl/run_audit.py +3 -2
- package/runtime/python/okstra_ctl/run_context.py +6 -9
- package/runtime/python/okstra_ctl/run_index_row.py +2 -8
- package/runtime/python/okstra_ctl/schedule_semantics.py +5 -2
- package/runtime/python/okstra_ctl/schema_excerpt.py +4 -2
- package/runtime/python/okstra_ctl/session_transcript.py +27 -1
- package/runtime/python/okstra_ctl/set_work_status.py +64 -38
- package/runtime/python/okstra_ctl/stage_fix_carry.py +4 -2
- package/runtime/python/okstra_ctl/stage_map.py +26 -6
- package/runtime/python/okstra_ctl/stage_targets.py +3 -4
- package/runtime/python/okstra_ctl/team.py +2 -1
- package/runtime/python/okstra_ctl/team_reconcile.py +11 -2
- package/runtime/python/okstra_ctl/time_report.py +51 -4
- package/runtime/python/okstra_ctl/usage_identity.py +2 -1
- package/runtime/python/okstra_ctl/usage_report.py +58 -4
- package/runtime/python/okstra_ctl/user_response.py +1431 -66
- package/runtime/python/okstra_ctl/wizard.py +11 -44
- package/runtime/python/okstra_ctl/work_categories.py +3 -2
- package/runtime/python/okstra_ctl/worker_prompt_body.py +18 -7
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +3 -2
- package/runtime/python/okstra_ctl/worker_runner.py +14 -12
- package/runtime/python/okstra_ctl/workflow.py +2 -1
- package/runtime/python/okstra_ctl/worktree.py +3 -2
- package/runtime/python/okstra_ctl/wrapper_status.py +4 -2
- package/runtime/python/okstra_ctl/write_policy.py +4 -2
- package/runtime/python/okstra_token_usage/antigravity.py +39 -12
- package/runtime/python/okstra_token_usage/collect.py +90 -38
- package/runtime/python/okstra_token_usage/grok.py +127 -0
- package/runtime/schemas/final-report-v2.0.schema.json +9 -0
- package/runtime/schemas/final-report-v3.0.schema.json +9 -0
- package/runtime/schemas/report-synthesis-packet-v1.0.schema.json +140 -0
- package/runtime/skills/okstra-brief-gen/SKILL.md +9 -7
- package/runtime/skills/okstra-code-review/SKILL.md +21 -11
- package/runtime/skills/okstra-container-build/SKILL.md +18 -18
- package/runtime/skills/okstra-inspect/SKILL.md +12 -11
- package/runtime/skills/okstra-inspect/facets/error-zip.md +8 -8
- package/runtime/skills/okstra-inspect/facets/errors.md +2 -2
- package/runtime/skills/okstra-inspect/facets/history.md +9 -14
- package/runtime/skills/okstra-inspect/facets/logs.md +2 -2
- package/runtime/skills/okstra-inspect/facets/recap.md +5 -5
- package/runtime/skills/okstra-inspect/facets/report.md +6 -10
- package/runtime/skills/okstra-inspect/facets/status.md +9 -8
- package/runtime/skills/okstra-inspect/facets/time.md +3 -3
- package/runtime/skills/okstra-manager/SKILL.md +16 -14
- package/runtime/skills/okstra-memory/SKILL.md +3 -3
- package/runtime/skills/okstra-pr-gen/SKILL.md +5 -4
- package/runtime/skills/okstra-rollup/SKILL.md +6 -16
- package/runtime/skills/okstra-run/SKILL.md +7 -7
- package/runtime/skills/okstra-schedule-gen/SKILL.md +21 -17
- package/runtime/skills/okstra-setup/SKILL.md +21 -13
- package/runtime/skills/okstra-setup/references/project-config.md +2 -2
- package/runtime/skills/okstra-usage/SKILL.md +10 -10
- package/runtime/skills/okstra-user-response/SKILL.md +78 -107
- package/runtime/templates/report-writer-prompt-preamble.md +17 -1
- package/runtime/templates/reports/schedule.template.md +4 -4
- package/runtime/templates/worker-error-contract.md +17 -29
- package/runtime/validators/validate-run.py +379 -63
- package/runtime/validators/validate_session_conformance.py +43 -7
|
@@ -87,7 +87,7 @@ User-utterance interpretation rule:
|
|
|
87
87
|
|
|
88
88
|
A single okstra run frequently spans 30–120 minutes with multi-minute silent windows while workers run; without progress signals the user cannot distinguish "still working" from "hung". Lead MUST emit a single short progress line at each checkpoint below — plain user-facing text in a separate brief message (not buried inside a tool call), one line per checkpoint, format: `PROGRESS: <phase-id> <verb-phrase>`. Emit the line raw — the literal `PROGRESS:` token must begin the line. Do NOT wrap it in inline-code backticks (`` `PROGRESS: ...` ``) or a ```` ``` ```` code fence; markdown wrapping is what the post-hoc conformance validator scrapes around, and raw emit keeps the signal unambiguous.
|
|
89
89
|
|
|
90
|
-
For an `implementation-planning` run whose run manifest declares `activityContractVersion: 1`, record every required activity boundary with `okstra agent-activity append` against the manifest-provided `leadEventsPath`. The ordering is fixed: the structured append succeeds first, the matching `PROGRESS:` line is emitted second, and the immediately following `ACTIVITY:` line projects the same structured fields into the conversation language. Do not reconstruct structured activity from conversation text. If the append fails, do not present that activity boundary as completed.
|
|
90
|
+
For an `implementation-planning` run whose run manifest declares `activityContractVersion: 1`, record every required activity boundary with `okstra agent-activity append` against the manifest-provided `leadEventsPath`. Model-facing calls pass prose through `--summary-file <md>` and a command through `--command`, `--command-cwd`, `--command-exit-code`, and `--command-output-file <md>`; do not construct `--command-record` JSON. The ordering is fixed: the structured append succeeds first, the matching `PROGRESS:` line is emitted second, and the immediately following `ACTIVITY:` line projects the same structured fields into the conversation language. Do not reconstruct structured activity from conversation text. If the append fails, do not present that activity boundary as completed.
|
|
91
91
|
|
|
92
92
|
The live projection follows this shape:
|
|
93
93
|
|
|
@@ -161,7 +161,7 @@ When a terminal row preserves a pre-correction dissent classification, keep supe
|
|
|
161
161
|
|
|
162
162
|
## Model assignments
|
|
163
163
|
|
|
164
|
-
**The lead never invents a model.** Every role's model
|
|
164
|
+
**The lead never invents a model.** Every role's model comes from the `Worker Roster` section of `okstra model-io run-input`. A missing assignment is a run-input defect, not a license to fall back — see [team-contract](./team-contract.md) "Model Assignment Rules". Run preparation seeds the assignment values from `OKSTRA_DEFAULT_*_MODEL` (`scripts/okstra_ctl/run.py`).
|
|
165
165
|
|
|
166
166
|
**Reading an assignment is not enough — the selected adapter must apply it at dispatch.** `dispatch_worker` receives the complete manifest assignment. The selected runtime adapter passes `hostModelValue` to a `runner=native-session` host primitive or `modelExecutionValue` to a `runner=cli-wrapper` provider process without changing provider, role, or model. A missing or unsupported runner-specific mapping is a pre-dispatch contract failure, never a silent fallback.
|
|
167
167
|
|
|
@@ -208,16 +208,15 @@ Executor is chosen at run-prep time via `--executor <claude|codex|antigravity>`
|
|
|
208
208
|
|
|
209
209
|
**REQUIRED RESOURCE:** Read [context-loader](./context-loader.md) first to discover task bundle paths.
|
|
210
210
|
|
|
211
|
-
Treat cross verify input as a task bundle, not as a single file. If the user did not specify an explicit task key or task path, use
|
|
211
|
+
Treat cross verify input as a task bundle, not as a single file. If the user did not specify an explicit task key or task path, use context-loader's current-task pointer. For task browsing, task-id disambiguation, or project-level task inventory, use context-loader's rendered discovery result rather than reading discovery JSON directly.
|
|
212
212
|
|
|
213
213
|
After context-loader completes, read **only the compact intake files below** in a single parallel-Read message at the start of Phase 1. The other instruction-set files are loaded lazily at the phase that actually needs them — see "Lazy reading discipline" below. This split exists because re-absorbing the full instruction-set baseline at every phase entry is the dominant source of lead-token bloat — most of it is files only one downstream phase uses.
|
|
214
214
|
|
|
215
215
|
**Mandatory at Phase 1 start (parallel Read, one message):**
|
|
216
216
|
|
|
217
|
-
1. `
|
|
218
|
-
2. `
|
|
219
|
-
3. `instruction-set/analysis-
|
|
220
|
-
4. `instruction-set/analysis-packet.md` — primary compact input for analysis worker dispatch
|
|
217
|
+
1. `okstra model-io run-input --run-manifest <run-manifest-path found by context-loader>` — fixed Markdown run identity and scope input
|
|
218
|
+
2. `instruction-set/analysis-profile.md` — needed to pick the right `Required workers:` block and phase rules
|
|
219
|
+
3. `instruction-set/analysis-packet.md` — primary compact input for analysis worker dispatch
|
|
221
220
|
|
|
222
221
|
**Lazy reading discipline (do NOT read at Phase 1):**
|
|
223
222
|
|
|
@@ -226,7 +225,8 @@ After context-loader completes, read **only the compact intake files below** in
|
|
|
226
225
|
- `instruction-set/analysis-material.md` — read only if the packet is insufficient or a source citation needs verification. Many task bundles have no meaningful material file beyond a duplicate brief wrapper.
|
|
227
226
|
- `instruction-set/reference-expectations.md` — read at Phase 6 synthesis (or whenever the report-writer worker is dispatched) — it informs the match/gap assessment. Analysis workers use the packet excerpt unless they need source verification.
|
|
228
227
|
- `instruction-set/final-report-template.md` — never read by Lead. The Report writer worker reads it as part of its own [Required reading]; Lead only references its path when dispatching.
|
|
229
|
-
-
|
|
228
|
+
- Run history timeline JSON — do not read or parse it. For carry-in or resume resolution, use the workflow snapshot, artifact paths, final status path, and resume command in `okstra model-io run-input`; report insufficient information instead of opening timeline JSON.
|
|
229
|
+
- Owned lifecycle artifacts are projected only through the purpose-specific `okstra model-io run-input` and `active-context-input` views.
|
|
230
230
|
|
|
231
231
|
**Implementation profile lazy reading discipline (BLOCKING — applies only when `task_type == "implementation"`):**
|
|
232
232
|
|
|
@@ -272,32 +272,22 @@ For `improvement-discovery`, Lead records `## Primary Pass Assignments` in the P
|
|
|
272
272
|
4. Persist the setup outcome in team-state using the existing fields required by that backend.
|
|
273
273
|
5. Emit the canonical `PROGRESS: phase-3-team-create <adapter-specific-status>` checkpoint. The phase id remains stable for artifact compatibility; only the adapter-owned verb phrase varies.
|
|
274
274
|
|
|
275
|
-
### Phase 4 / Phase 5 — Dispatch, await, and error-log
|
|
275
|
+
### Phase 4 / Phase 5 — Dispatch, await, and error-log recording
|
|
276
276
|
|
|
277
|
-
For each selected worker assignment, persist the exact prompt history, emit the per-worker Phase 4 checkpoint, and call `dispatch_worker(assignment, prompt)` through the selected adapter. Then call `await_workers(handles)`. A dispatch acknowledgement or process/pane creation is never completion: verify the terminal status, Result Path, worker-results audit path
|
|
277
|
+
For each selected worker assignment, persist the exact prompt history, emit the per-worker Phase 4 checkpoint, and call `dispatch_worker(assignment, prompt)` through the selected adapter. Then call `await_workers(handles)`. A dispatch acknowledgement or process/pane creation is never completion: verify the terminal status, Result Path, and worker-results audit path required by `team-contract` before emitting the Phase 5 collection checkpoint.
|
|
278
278
|
|
|
279
279
|
Retries and convergence re-verification always call `redispatch_worker` to create a fresh one-shot session. Never reuse a worker conversation or switch adapters/providers to hide a failed assignment.
|
|
280
280
|
|
|
281
281
|
### Errors log path wiring (BLOCKING)
|
|
282
282
|
|
|
283
|
-
The launch prompt's `## Run Logs (error-log wiring)` section gives Lead the resolved absolute
|
|
283
|
+
The launch prompt's `## Run Logs (error-log wiring)` section gives Lead the resolved absolute path for the run-level errors log. When Lead constructs each worker's dispatch prompt body, Lead MUST inject this header line verbatim:
|
|
284
284
|
|
|
285
285
|
- `**Errors log path:** <absolute run-level errors log path from launch prompt>`
|
|
286
|
-
- `**Errors sidecar path:** <absolute per-worker sidecar path matching the dispatched worker>`
|
|
287
286
|
|
|
288
|
-
Workers are contractually required to extract
|
|
287
|
+
Workers are contractually required to extract this line and abort with `<WORKER>_ERRORS_PATH_MISSING` if it is absent (see each worker definition's "Path extraction (BLOCKING)" block). A worker records its tool failure through the typed `okstra error-log append-observed` form in that contract; it does not write an intermediate JSON file.
|
|
289
288
|
|
|
290
289
|
After each worker terminates, BEFORE classifying its terminal status, verify the canonical result file exists at the absolute path resolved from the `**Result Path:**` header. If it is absent — or the deterministic provider process returned `CODEX_RESULT_MISSING` / `ANTIGRAVITY_RESULT_MISSING` — re-dispatch the SAME worker once with the byte-identical prompt. Only after the second attempt also misses may the role be classified `error` with `--message "result-missing after 1 retry"`. Full rules: [team-contract](./team-contract.md) "Lead Redispatch Policy on Result-Missing".
|
|
291
290
|
|
|
292
|
-
After each worker terminates (any terminal status), if its errors sidecar exists, dump it to the run error log using the same resolved paths from the launch prompt:
|
|
293
|
-
|
|
294
|
-
```bash
|
|
295
|
-
okstra error-log append-from-worker \
|
|
296
|
-
--sidecar <absolute-sidecar-path-from-launch-prompt> \
|
|
297
|
-
--out <absolute-errors-log-path-from-launch-prompt> \
|
|
298
|
-
--task-key <taskKey> --agent <agent> --agent-role <role> --model <model>
|
|
299
|
-
```
|
|
300
|
-
|
|
301
291
|
`--agent`, `--agent-role`, and `--error-type` are **closed enums**, not free-form labels — the role names used elsewhere in these contracts (`Codex worker`, `Claude worker`) are rejected. Use exactly:
|
|
302
292
|
|
|
303
293
|
- `--agent` — `claude-worker` | `codex-worker` | `antigravity-worker` | `grok-worker` | `kimi-worker` | `report-writer`
|
|
@@ -318,10 +308,13 @@ okstra error-log append-observed \
|
|
|
318
308
|
--command-kind wrapper \
|
|
319
309
|
--exit-code 124 --duration-ms 1800000 \
|
|
320
310
|
--message "reverify-r1 wrapper never launched" \
|
|
321
|
-
--stderr-excerpt "<last stderr lines, or use --stderr-excerpt-file>"
|
|
311
|
+
--stderr-excerpt "<last stderr lines, or use --stderr-excerpt-file>" \
|
|
312
|
+
--cause sandbox-denied \
|
|
313
|
+
--evidence targetProbe=wrapper-write-.okstra-state-denied \
|
|
314
|
+
--evidence controlProbe=wrapper-write-tmp-succeeds
|
|
322
315
|
```
|
|
323
316
|
|
|
324
|
-
Keep `--message` to the error actually observed — asserting that a sandbox or permission boundary blocked the call requires `--
|
|
317
|
+
Keep `--message` to the error actually observed — asserting that a sandbox or permission boundary blocked the call requires `--cause sandbox-denied` plus both `--evidence targetProbe=<value>` and `--evidence controlProbe=<value>` probes, and an unevidenced block claim in `--message` is rejected.
|
|
325
318
|
|
|
326
319
|
The deterministic dispatcher records this through its selected adapter — Lead does NOT need to re-record. Token usage is not inferred from dispatch return values; call `collect_usage` at the start of Phase 7.
|
|
327
320
|
|
|
@@ -356,14 +349,11 @@ If `Report writer worker` is in the selected roster (`recommendedWorkers` / `res
|
|
|
356
349
|
|
|
357
350
|
Before constructing the dispatch prompt, the lead MUST:
|
|
358
351
|
|
|
359
|
-
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
`**Report Language:**` in the report-writer dispatch prompt, and ensure
|
|
365
|
-
report assembly writes the same value into `data.json.meta.reportLanguage`
|
|
366
|
-
from the immutable run manifest.
|
|
352
|
+
- Preserve the `**Report Language:**` value already materialized in the
|
|
353
|
+
report-writer dispatch prompt. The dispatcher resolves project/global
|
|
354
|
+
configuration and brief-language inference before the model receives the
|
|
355
|
+
prompt; report assembly copies the immutable run-manifest value into the
|
|
356
|
+
final record.
|
|
367
357
|
|
|
368
358
|
The convergence output provides four finding categories:
|
|
369
359
|
|
|
@@ -394,7 +384,7 @@ Lead's responsibilities in this sub-step (in order):
|
|
|
394
384
|
|
|
395
385
|
For a new `implementation-planning` run, the fixed order is initial verification → one planner self-fix → targeted re-verification → user gate. The initial verification is round 1 and the targeted re-verification is round 2. A second automatic self-fix is a contract violation.
|
|
396
386
|
|
|
397
|
-
1. Build the queue with `okstra plan-items
|
|
387
|
+
1. Build the queue with `okstra plan-items prepare --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`, place the output of `okstra plan-items prompt --run-manifest <run-manifest>` verbatim in every verifier prompt, then run `okstra plan-items validate-prepared --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`. Python resolves the one convergence-owned state path from that run identity. The lead MUST NOT summarise, select, omit, reorder, or renumber the queue. Each prompt uses the compact subject plus the lossless payload, and asks every item:
|
|
398
388
|
|
|
399
389
|
```text
|
|
400
390
|
What concrete false-positive input, failure ordering, or omitted dependency
|
|
@@ -403,8 +393,8 @@ For a new `implementation-planning` run, the fixed order is initial verification
|
|
|
403
393
|
|
|
404
394
|
An `AGREE` response records the considered counterexample and exclusion reason in its note; unverified external material is `verification-error`, not `DISAGREE`.
|
|
405
395
|
2. Dispatch a single plan-body reverify round to every analyser worker in the roster (`claude`, `codex`, and `antigravity` when opted in). `Report writer worker` is NOT a participant in this round.
|
|
406
|
-
3.
|
|
407
|
-
4.
|
|
396
|
+
3. Record each verifier Markdown result through `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker-id>=<result.md> --round <N>`. Python validates every submitted `P-*` identifier against the current convergence state and overwrites only that round's verdicts. Then resolve the gate result to one of `passed` / `passed-with-dissent` / `blocked-by-disagreement` / `aborted-non-result`.
|
|
397
|
+
4. After `okstra plan-verify` succeeds, run `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N>`. Python reads the current worker assignments, atomically appends the convergence-owned history, and updates its nested final projection. This state is the only plan-verification input report assembly reads.
|
|
408
398
|
5. Record every surviving `majority-disagree` decision through `okstra approval-decision`; record its plan and clarification links only on activities. Do not append `clarificationItems[]` directly.
|
|
409
399
|
6. Run report assembly after the final plan-body state, approval ledger, design snapshot, activity ledger, and team state are complete. Assembly writes `implementationPlanning.planBodyVerification` and derived clarification rows while publishing `data.json` once.
|
|
410
400
|
7. Publish the report record `frontmatter.approved` field as `false`. There is no in-body `- [ ] Approved` marker line — approval lives only in the record (see [plan-body-verification](./plan-body-verification.md) §"Round protocol" step 9). The user may set it to `true` (via `--approve` or the in-session wizard) only when the gate is `passed` or `passed-with-dissent`. **Enforced:** `validators/validate-run.py` `validate_phase_boundary` fails a report shipping `approved: true` under `blocked-by-disagreement` / `aborted-non-result`, and run-prep (`scripts/okstra_ctl/run.py` `_validate_approved_plan`) fail-closes the same case. Manually flipping a blocked gate to passing is a contract violation.
|
|
@@ -16,7 +16,7 @@ Plan-body verification runs **after** finding convergence and **after** the repo
|
|
|
16
16
|
Phase 4 workers produce independent analyses (Findings F-001…)
|
|
17
17
|
→ Phase 5.5 FINDING convergence ([convergence](./convergence.md), sections "Convergence Algorithm" through "Convergence State Artifact")
|
|
18
18
|
→ Phase 6 report-writer authors report-writer-narrative Markdown (consolidated Option Candidates / Stepwise Execution Order / Dependency / Validation Checklist / Rollback)
|
|
19
|
-
→ okstra plan-items
|
|
19
|
+
→ okstra plan-items prepare + prompt + validate-prepared creates and projects the deterministic P-* queue
|
|
20
20
|
→ PLAN-BODY VERIFICATION ROUND ← this contract
|
|
21
21
|
→ final render/validation
|
|
22
22
|
→ User Approval gate (the frontmatter `approved:` flip is honoured by run-prep only when this round's Gate result is `passed` or `passed-with-dissent`)
|
|
@@ -56,9 +56,9 @@ The shared Majority definition and the auto-disable rule (fewer than 2 analyser
|
|
|
56
56
|
From the report-writer's draft of `## 5.4 Implementation Plan Deliverables`, the lead creates the verification queue only through this sequence (see also `templates/reports/final-report-v2.template.md` §5.5.9):
|
|
57
57
|
|
|
58
58
|
```text
|
|
59
|
-
okstra plan-items
|
|
60
|
-
→ place
|
|
61
|
-
→ okstra plan-items validate --narrative <report-writer-narrative.md> --
|
|
59
|
+
okstra plan-items prepare --narrative <report-writer-narrative.md> --run-manifest <run-manifest>
|
|
60
|
+
→ place `okstra plan-items prompt --run-manifest <run-manifest>` output verbatim in every verifier prompt
|
|
61
|
+
→ okstra plan-items validate-prepared --narrative <report-writer-narrative.md> --run-manifest <run-manifest>
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
The persisted `items[]` are the sole queue. The lead MUST NOT freely summarise,
|
|
@@ -103,6 +103,130 @@ For every detector-produced `(stage, kind)`, extract exactly one plan item named
|
|
|
103
103
|
|
|
104
104
|
When extracting each item, lead also captures a **`subject`** — a plain one-line label (≤12 words) describing *what that item is* in the reader's terms, e.g. `P-Opt-1` → "Option A: split upload v2 into a new module", `P-Step-1.1` → "Stage 1 Step 2: regression-check with `npm run test:v2`". This is a label-capture, not new analysis. The `subject` is what §5.5.9 renders as the per-item heading so the reader knows *what* each AGREE/DISAGREE is about without cross-referencing §4.5; a bare `P-*` ID with no subject is a contract violation. **Enforced:** `validators/validate-run.py` `_validate_plan_item_subject_substance` fails a subject that is a placeholder — under 3 chars, equal to the item id, or shaped like a bare `P-*` id.
|
|
105
105
|
|
|
106
|
+
## Fact and judgement (BLOCKING)
|
|
107
|
+
|
|
108
|
+
A single-vote block has a reason behind it: two spelled-out references
|
|
109
|
+
contradicting each other is a fact one verifier can settle by looking, and a
|
|
110
|
+
fact must not be outvoted. What was missing is that the fact was never checked —
|
|
111
|
+
writing "this path does not exist" was enough to block.
|
|
112
|
+
|
|
113
|
+
So a DISAGREE on a single-vote kind declares what sort of claim it is:
|
|
114
|
+
|
|
115
|
+
- **`claimKind: fact`** — okstra can reproduce it. Carry a `reproduction` probe:
|
|
116
|
+
`path-exists` / `path-absent`, `literal-present` / `literal-absent`, or
|
|
117
|
+
`citations-differ`. **okstra runs it and writes `reproductionResult`; do not
|
|
118
|
+
write that field yourself.** A verifier that reports its own result puts the
|
|
119
|
+
gate on a self-report, and the same claim then settles differently depending
|
|
120
|
+
on who raised it.
|
|
121
|
+
- **`claimKind: judgement`** — no mechanical check exists. It takes a quorum,
|
|
122
|
+
exactly like `b` / `c` / `e`.
|
|
123
|
+
|
|
124
|
+
A claim that cannot be written as one of those three probes is a `judgement`.
|
|
125
|
+
That is a classification, not a demotion: giving a single-vote block to something
|
|
126
|
+
no machine can confirm is what was wrong in the first place.
|
|
127
|
+
|
|
128
|
+
| Declared | Reproduced | Effect |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `fact` | `reproduced` | blocks on one vote |
|
|
131
|
+
| `fact` | `not-reproduced` | quorum, and the claim is recorded as unfounded |
|
|
132
|
+
| `fact` | `not-runnable` | quorum — okstra could not judge it, which refutes nothing |
|
|
133
|
+
| `judgement` | — | quorum |
|
|
134
|
+
| nothing declared | — | blocks on one vote, as before |
|
|
135
|
+
|
|
136
|
+
The last row is deliberate. Verdicts recorded before this field existed are not
|
|
137
|
+
re-judged in hindsight, so adoption only ever relaxes: a claim earns the quorum
|
|
138
|
+
route by declaring itself, never loses a block by staying silent.
|
|
139
|
+
|
|
140
|
+
**Enforced:** `validators/validate-run.py` `_single_vote_block_survives`, with
|
|
141
|
+
the probes in `scripts/okstra_ctl/claim_reproduction.py`.
|
|
142
|
+
|
|
143
|
+
## What the gate asks (BLOCKING)
|
|
144
|
+
|
|
145
|
+
The gate does not ask whether the plan is free of defects. Under adversarial
|
|
146
|
+
reading a plan of any size yields findings every round, so a bar of zero is not
|
|
147
|
+
reachable and a run that aims at it does not end — one task spent five runs and
|
|
148
|
+
its last two went entirely into the plan's account of itself.
|
|
149
|
+
|
|
150
|
+
It asks two things instead, and both must hold:
|
|
151
|
+
|
|
152
|
+
1. **The stage about to start is executable as written.** Its steps, commands,
|
|
153
|
+
exit contract, rollback target and validation signals are settled — §"Gate
|
|
154
|
+
scope" decides which items that covers.
|
|
155
|
+
2. **What was set aside is written down.** Every defect the gate stopped
|
|
156
|
+
blocking on appears in `planBodyVerification.setAside` with its reason —
|
|
157
|
+
`observed` (a frozen stage), `deferred` (a stage not yet reached), or
|
|
158
|
+
`record` (the plan's account of itself).
|
|
159
|
+
|
|
160
|
+
The second condition is what makes the first safe to relax. Knowing a defect is
|
|
161
|
+
the acceptance condition, not removing it; without the register a deferred
|
|
162
|
+
defect and one nobody raised read the same in the report.
|
|
163
|
+
|
|
164
|
+
`okstra plan-items complete-round` writes the register from the same computation
|
|
165
|
+
that produced the gate value. Do not hand-write it — a hand-written register
|
|
166
|
+
drifts from the verdicts it claims to summarise, and nothing else reads it, so
|
|
167
|
+
the drift stays invisible.
|
|
168
|
+
|
|
169
|
+
**Enforced:** `validators/validate-run.py` `_validate_set_aside_register` fails
|
|
170
|
+
a scored round whose declared register does not match the one recomputed from
|
|
171
|
+
the verdicts.
|
|
172
|
+
|
|
173
|
+
## Two blocks — execution and record (BLOCKING)
|
|
174
|
+
|
|
175
|
+
A plan carries two kinds of content, and they answer to different gates.
|
|
176
|
+
|
|
177
|
+
| Block | Items | A defect there |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| `execution` | steps, options, dependencies, validations, rollbacks, design prep, variation points, the selected direction | blocks the start |
|
|
180
|
+
| `record` | requirement coverage rows, and the approval dispositions and decision references they carry | **does not block.** It is recorded and becomes the next run's input |
|
|
181
|
+
|
|
182
|
+
The record is the plan's account of itself: which brief line each stage answers,
|
|
183
|
+
what the user decided, which clarification a deviation rests on. It has to be
|
|
184
|
+
accurate and it is still verified — but a wrong sentence in it does not make the
|
|
185
|
+
next stage unsafe to start, and treating it as though it did is what kept a plan
|
|
186
|
+
whose executable content had already settled from ever being approved.
|
|
187
|
+
|
|
188
|
+
**A requirement that nothing builds is not a record defect.** A coverage row with
|
|
189
|
+
`status: gap` or a `blocked C-NNN` blocks through `gateBlockedBy: coverage-gap`,
|
|
190
|
+
which reads the rows directly and is untouched by this split. What stops blocking
|
|
191
|
+
is a verdict about the row's *accuracy* — a citation that resolves to the wrong
|
|
192
|
+
stage, a disposition recorded without its confirmation.
|
|
193
|
+
|
|
194
|
+
**Enforced:** `scripts/okstra_ctl/plan_items.py` `_item_block` assigns the block
|
|
195
|
+
at extraction, `okstra plan-items seed` carries it onto the row, and
|
|
196
|
+
`validators/validate-run.py` `_plan_item_gate_class` downgrades a `record`
|
|
197
|
+
blocker to `has-dissent`. `_independent_coverage_blockers` is the separate
|
|
198
|
+
channel that keeps a genuine gap blocking.
|
|
199
|
+
|
|
200
|
+
## Gate scope — the stage about to start (BLOCKING)
|
|
201
|
+
|
|
202
|
+
The plan covers every stage; implementation runs one at a time. An item blocks
|
|
203
|
+
approval only when it has standing over the stage that is about to start:
|
|
204
|
+
|
|
205
|
+
- **`in-scope`** — one of the item's stages is `ready` or `active`, or the item
|
|
206
|
+
belongs to no stage at all. It blocks.
|
|
207
|
+
- **`observed`** — the item's stages are all `done`. It does not block. A frozen
|
|
208
|
+
stage's defect cannot be fixed by planning at all: the Stage Ledger forbids
|
|
209
|
+
editing its commands, so an item that blocks on one blocks forever.
|
|
210
|
+
- **`deferred`** — the item's stages are all still `blocked`. It does not block;
|
|
211
|
+
the stage it judges has not been reached.
|
|
212
|
+
|
|
213
|
+
An item with no `stageScope` is `in-scope` on purpose. `P-Opt-*`, `P-Var-*`,
|
|
214
|
+
`P-Dep-*`, `P-Rb-*` and `P-Dir-1` judge the plan as a whole, and scoping them out
|
|
215
|
+
would stop an unrequested-work verdict from blocking a start. So does a coverage
|
|
216
|
+
or validation row whose plan has not filled `stageRefs` yet — an absent scope is
|
|
217
|
+
read as every stage, which is the safe direction.
|
|
218
|
+
|
|
219
|
+
**Nothing is dropped.** An out-of-scope blocker becomes `has-dissent`, so the
|
|
220
|
+
gate reads `passed-with-dissent` rather than `passed` and the reader can see that
|
|
221
|
+
something is outstanding. `gate.items[]` carries the bucket for each item; a
|
|
222
|
+
scoped-out defect and a real consensus would otherwise look identical in the
|
|
223
|
+
record.
|
|
224
|
+
|
|
225
|
+
**Enforced:** `validators/validate-run.py` `_stage_scope_bucket` and
|
|
226
|
+
`_plan_item_gate_class`, which `_recompute_plan_body_gate` and
|
|
227
|
+
`_gate_summary_item` both call — the scoping cannot apply to the gate value and
|
|
228
|
+
not to the summary the round protocol records.
|
|
229
|
+
|
|
106
230
|
## Plan-body verdict semantics
|
|
107
231
|
|
|
108
232
|
The verdict tokens `AGREE` / `DISAGREE` / `SUPPLEMENT` are reused, but their meaning is plan-specific:
|
|
@@ -222,9 +346,11 @@ CLI-wrapper calls follow the planned execution surface after
|
|
|
222
346
|
consume only `modelExecutionValue`. A missing or invalid invocation contract blocks the
|
|
223
347
|
round before any host or provider process starts.
|
|
224
348
|
|
|
225
|
-
1. Lead runs `okstra plan-items
|
|
349
|
+
1. Lead runs `okstra plan-items prepare --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`, places the fixed output of `okstra plan-items prompt --run-manifest <run-manifest>` verbatim in every verifier prompt, then runs `okstra plan-items validate-prepared --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`. Python resolves the one convergence-owned state path from that run identity. Dispatch only after that exact-match validation succeeds.
|
|
350
|
+
|
|
351
|
+
**Then seed the landing table (BLOCKING):** `okstra plan-items seed --narrative <report-writer-narrative.md> --state <plan-body-verification.json> --run-manifest <run-manifest.json>`. `apply-verdicts` in step 8 refuses a verdict whose item has no `planBodyVerification.planItems[]` row. The report writer never owns that state, so the deterministic seed is the only creator of its rows. The seed is idempotent by id and never touches an existing row, so it is safe to re-run between rounds and after a self-fix re-extraction. Skipping it makes step 8 fail with `plan-body state has no row for [...]`.
|
|
226
352
|
|
|
227
|
-
|
|
353
|
+
**`--run-manifest` is what scopes the gate to the stage you are starting.** Seed uses it to record `planBodyVerification.stageLedger` — a `{stage: status}` snapshot in the Stage Ledger's own `done` / `active` / `ready` / `blocked` vocabulary. Without it the block carries no ledger, and the gate falls back to judging every stage at once, which is the behaviour described in the paragraph below and the reason this flag exists.
|
|
228
354
|
2. For each analyser worker in the roster (`claude`, `codex`, and `antigravity` if opted in), lead constructs a reverify prompt using the template in §"Plan-body reverify prompt" below.
|
|
229
355
|
3. Dispatch uses the same wrapper infrastructure as finding convergence, so the `--role-slug` is the same canonical `<role>-worker` that convergence uses — not a round-specific slug. Result file path: `runs/<task-type>/worker-results/<role>-worker-plan-verify-r<N>-implementation-planning-<seq>.md` (e.g. `codex-worker-plan-verify-r1-implementation-planning-003.md`). **`<seq>` is the report's sequence** — the one in this run's `final-report-<task-type>-<seq>` filename, NOT the `workerResults` sequence the initial analysis results carry. The two are equal in most runs and diverge in some (`reports: 004` alongside `workerResults: 005` is a real case), and provenance globs on the report's. Picking the other one makes `_validate_plan_body_verdict_provenance` report that no result file exists while the file is sitting in the directory. The `-worker-` token is load-bearing twice over: §"Plan-body reverify prompt" requires the same anchor headers as convergence, whose `**Audit sidecar path:**` is derived by `okstra_ctl.worker_artifact_paths.audit_sidecar_rel()` inserting `-audit-` after that token — a slug without it makes the header underivable and the helper raises. Record each `planItems[].verdicts[].worker` as the same `<role>-worker` string, because provenance compares it to this filename's prefix. **Enforced:** `tests/contract/test_reverify_dispatch_anchors.py` derives the sidecar from the documented name and re-extracts the prefix the provenance resolver uses.
|
|
230
356
|
**Verdict provenance (BLOCKING).** Every verdict recorded in `planItems[].verdicts[]` MUST trace back to a dispatch that actually returned a result file at the path above. The whole gate — classification, self-fix eligibility, promotion, `gateBlockedBy` — is computed from these votes, so an unbacked vote lets the round be skipped while the gate still reads `passed`. **Enforced:** `validators/validate-run.py` `_validate_plan_body_verdict_provenance` fails any `verdicts[].worker` with no matching `<worker>-plan-verify-r<N>-<task-type>-<seq>.md` result file. Recording a `verification-error` for a dispatch that produced no result is the correct way to represent a failed worker — inventing an `AGREE` is a contract violation.
|
|
@@ -260,7 +386,7 @@ round before any host or provider process starts.
|
|
|
260
386
|
**Record the cause, not just the outcome.** The gate value names the outcome; `planBodyVerification.gateBlockedBy` (array) names every input that blocked it — `majority-disagree`, `coverage-gap`, `non-result`. Two independent inputs can block: a `majority-disagree` plan item, and a Requirement Coverage `gap` / `blocked C-NNN` row (`prompts/profiles/implementation-planning.md` §"Requirement Coverage"). A coverage-only block still renders as `blocked-by-disagreement` because that is the only blocking non-abort value, so **without `gateBlockedBy` the report asserts a worker disagreement that never happened** and the reader hunts for a dissent that does not exist. Leave the array empty for a passing gate. **Enforced:** `validators/validate-run.py` `_validate_gate_blocked_by` cross-checks the declared causes against the recorded verdicts and coverage rows, and fails a passing gate that has a blocking coverage row — the coverage rule was prose-only before.
|
|
261
387
|
|
|
262
388
|
**A coverage row citing this run's own `C-NNN` is not an independent blocker.** When a coverage row's `blocked C-NNN` points at a clarification that step 8 below promoted from a `majority-disagree` item in *this same run*, that blocker is already counted once as the plan item. Counting it again as a coverage gap makes the run block on a clarification it just authored, and the row carries into the next run as a fresh blocker — the Requirement Coverage ↔ Clarification cycle. Such rows are excluded from `coverage-gap`. **Enforced:** `validators/validate-run.py` `_independent_coverage_blockers`.
|
|
263
|
-
6.
|
|
389
|
+
6. `okstra plan-items complete-round --run-manifest <current-run-manifest.json>` derives `planBodyVerification.participatingAnalysers` from the current assigned roster and persisted votes, then atomically records the completed round. The gate arithmetic is unchanged, but a shrunken roster changes what the round can settle: with two participating analysers a 1-AGREE / 1-DISAGREE split is a tie, so it reaches neither consensus nor `majority-disagree` and the item has to go back for a round (see `needs-reverify` above). **Enforced:** `validators/validate-run.py` `_validate_participating_analysers` recomputes `voting` from the recorded verdicts and fails a declared figure the table denies. `validators/validate-run.py` `_detect_uniform_verifier` remains advisory; do not copy its JSON output into state.
|
|
264
390
|
|
|
265
391
|
**Check each verifier's verdict distribution before the next round.** Read the `okstra plan-verify` warnings alongside the gate value. Two shapes mean the roster was narrower than it looks: a verifier whose every vote was one token, and a verifier that returned no vote for items it was assigned. Both are contract violations of the adversarial posture, not stylistic preferences — the verifier is told to open the cited evidence and judge it.
|
|
266
392
|
|
|
@@ -268,19 +394,19 @@ round before any host or provider process starts.
|
|
|
268
394
|
|
|
269
395
|
**How the corrective round is recorded.** The first prompt was dispatched, so it is immutable — `--replace-undispatched` refuses it, correctly. Materialize the correction under a NEW `--invocation-id` and a new prompt path. Before linking its result, retire the first attempt's link: `okstra agent-prompt reject-result --run-manifest <path> --dispatch-id <first dispatch id> --superseded-by <corrective dispatch id> --reason "<what was wrong with the returned result>"`. Without that step the corrective `link-result` fails with `agent result is already linked to another dispatch`, which is how a worker that ran for twenty minutes and wrote a good result ends up unrecordable. Nothing is deleted: the rejected link stays in `agentResultLinks` carrying `supersededBy` and `rejectionReason`, so the ledger shows both attempts and why the second exists.
|
|
270
396
|
|
|
271
|
-
Then
|
|
397
|
+
Then run `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N>`. Python appends one immutable round history entry, records each verified item's votes, derives the current projection from the actual assigned roster, and stamps `completedAt` after the preceding verification command succeeds. The file accumulates across rounds; it is never truncated to the latest one. Report assembly later projects the completed nested `planBodyVerification` into the final record.
|
|
272
398
|
7. **Self-fix loop (one rewrite, targeting planner-fixable defects).** After round 1, lead may run one report-writer rewrite when at least one `majority-disagree` item has a majority of its `DISAGREE` verdicts at `fixability == planner-fixable`. The targeted re-verification after that rewrite is round 2. After round 2, stop automatic self-fix regardless of outcome. Classify every remaining item as `user-decision`, `noncritical-dissent`, or `correctness-critical`. A second automatic self-fix is a contract violation. The fixed order is initial verification → one planner self-fix → targeted re-verification → user gate.
|
|
273
|
-
- **Group the targets by cause before instructing (BLOCKING).** Blocked items are usually several derivatives of one defect
|
|
399
|
+
- **Group the targets by cause before instructing (BLOCKING).** Blocked items are usually several derivatives of one defect. Lead partitions this round's targets into cause groups and instructs each group as **"remove this cause"**, naming the derivatives it accounts for. The convergence command owns the persisted group and correction fields; the lead does not edit JSON state.
|
|
274
400
|
- lead instructs report-writer to rewrite the items in each cause group (NOT a full draft regeneration; procedure in [report-writer](./report-writer.md) §"Self-fix rewrite").
|
|
275
401
|
- missing or weak `P-Prep-*` contracts are repaired by adding kind-specific inline detail or an AI-prepared PREP item with a concrete proposal. Facts that require user or external authority remain `blocked` and keep their request material; never invent those facts during self-fix.
|
|
276
402
|
- **Drop plan items whose element the round deleted.** A self-fix rewrite may remove a plan element (a validation check, a rollback row). `P-*` ids are positional, so a deletion shifts every later row and silently re-points surviving verdicts at their neighbours — and a verdict recorded against a removed element keeps blocking a gate while being unfindable in the plan, so reading the plan never reveals the cause. After each round, re-extract plan items with `okstra plan-items extract` and re-verify any item whose `subject` no longer matches; never carry the old vote forward across a shift. **Enforced:** `validators/validate-run.py` `_validate_verdicts_match_current_subjects` (re-pointing) and `_validate_plan_item_extraction_completeness` (dangling ids).
|
|
277
403
|
- **Classify each cause group before instructing it (BLOCKING).** A group is either an *authoring* defect — the plan says something wrong, incomplete, or self-contradictory, which self-fix owns — or a *citation* defect, where the plan points at an analysis artifact incorrectly. Only the first is self-fix work. For the second the finding already exists and already went through convergence, so the fix is to re-cite the converged artifact; instructing report-writer to re-derive the fact means the author reads the source material and produces a **finding that never went through convergence**, which the plan then carries as if it had. That is the role boundary the lead contract draws ("keep analysis, execution, verification, and report authoring responsibilities distinct; return defects to the role that owns them"), and report-writer is authoring-only by its own contract. `P-Req-*` items with breakage kind `f` are where this goes wrong most often: the question is usually whether a coverage row points correctly at something already measured, not whether the measurement is right. State the classification in the group's instruction so the author knows which of the two it is being asked to do.
|
|
278
404
|
- **A verdict older than the last self-fix is not a verdict (BLOCKING).** A verdict cast in round 1 judged the text before the only automatic rewrite. Once that rewrite runs, the judgement is about a plan that no longer exists. `--round <N>` on `apply-verdicts` stamps each row, and `validators/validate-run.py` `_validate_verdict_rounds_outlive_self_fix` fails any non-carried item whose verdict round is at or before `selfFixRoundsApplied`. Before declaring the gate, every item still holding a pre-self-fix verdict MUST be re-verified in round 2.
|
|
279
|
-
-
|
|
280
|
-
-
|
|
405
|
+
- Lead re-runs plan-body verification, then records each worker Markdown result through `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker>=<result.md> --round <N>`. Score the result with `okstra plan-verify --narrative <report-writer-narrative.md> --state <plan-body-verification.json>`, then call `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N>`. These commands fail on an assigned item the worker left unanswered, on a verdict for an item outside the queue, and on a duplicate worker result.
|
|
406
|
+
- For a self-fix, record the correction through the typed convergence command rather than writing `selfFixNote` or `selfFixGroups` JSON. A resolved item does not create a clarification.
|
|
281
407
|
- **Each round is a worker batch.** Before dispatching round N ≥ 2, reclaim the previous round's completed verifiers exactly as at any other batch boundary ([okstra-lead-contract](./okstra-lead-contract.md) "Run-scoped worker-resource lifecycle") and emit `PROGRESS: phase-batch-cleanup panes=<n>`, then announce the round with `PROGRESS: phase-5.5.9-plan-verify round=<N> items=<count>`. Saying a round will "reuse" the previous verifiers and then dispatching under fresh names leaves every prior round holding its panes — five rounds of that is what exhausts the pane budget and blocks the next dispatch. **Enforced:** `validators/validate_session_conformance.py` `_check_plan_verify_cleanup_checkpoints` requires both lines once the state file records two or more rounds.
|
|
282
|
-
- **Round completion.** A round is complete only after
|
|
283
|
-
- **Loop termination.**
|
|
408
|
+
- **Round completion.** A round is complete only after `okstra plan-verify` exits 0 and `okstra plan-items complete-round` succeeds. A round left with a non-zero exit carries its defect into the next round's inputs. Report assembly and rendering occur only after the convergence state is terminal. **Enforced:** `validators/validate-run.py` `_validate_plan_body_state_rounds` requires one stored round per round number and a corresponding item vote.
|
|
409
|
+
- **Loop termination.** The convergence command owns the round count and stop reason; the lead does not write either JSON field. A user-directed correction does not consume the automatic self-fix limit, and a verification failure after that correction does not restart the automatic loop:
|
|
284
410
|
- `all-resolved` — no planner-fixable `majority-disagree` item remains. Exit.
|
|
285
411
|
- `no-progress` — the round resolved **zero** planner-fixable items relative to the previous round. Exit even with budget left: the same rewrite would repeat. Newly *introduced* defects count against progress, so a rewrite that trades one defect for another stops the loop rather than churning. A round that re-targets only what the previous round left unresolved is this same conclusion reached one dispatch earlier — exit on it under this reason rather than paying for the round that proves it. **Enforced (advisory):** `validators/validate-run.py` `_detect_self_fix_recurrence` warns on that shape and names this stop reason.
|
|
286
412
|
- `max-rounds-reached` — `selfFixRoundsApplied == selfFixMaxRounds`. Exit.
|
|
@@ -293,7 +419,7 @@ round before any host or provider process starts.
|
|
|
293
419
|
- `Statement` summarising the disagreement and the worker breakage `<kind>`
|
|
294
420
|
- `Kind` chosen per the standard policy (usually `decision` for option-level conflicts, `data-point` for path/symbol mismatches)
|
|
295
421
|
- `Blocks=approval`
|
|
296
|
-
- the
|
|
422
|
+
- the report record's `planItems[].clarificationRefs[]` reaching that `C-<N>`. Under contract v3 the report record carries the **plural** field and the v3.0 schema forbids `clarificationId` on a plan item; report assembly derives the refs from the activity ledger's `clarificationRefs[]` + `planItemIds[]`, so record the decision through `okstra approval-decision` rather than writing the link by hand. The lead-owned state file keeps the singular `clarificationId`. `validators/validate-run.py` `_validate_plan_body_clarification_matching` recomputes each item's class and fails when a majority-disagree item reaches no clarification, or reaches one that is missing or not `blocks: approval`.
|
|
297
423
|
- set `approvalContext.classification` to `user-decision` for a majority `needs-user-input` item, `correctness-critical` for `DISAGREE(a)`, `DISAGREE(f)` on `P-Req-*`, or an independent Requirement Coverage blocker, and `noncritical-dissent` for another surviving majority disagreement.
|
|
298
424
|
- record the decision through `okstra approval-decision open`. Each option carries `disposition`, exactly one `reach`, and optional `scopeEffects`. The activity ledger carries affected `planItemIds` and `clarificationRefs`; the approval row never copies those backtrace IDs. `select` is allowed only for `user-decision`, `accept-risk` only for `noncritical-dissent`, and `request-revision` / `reject` for any classification. `correctness-critical` never offers or records `accept-risk`. **Enforced:** `scripts/okstra_ctl/approval_decisions.py` and report assembly.
|
|
299
425
|
- **Self-fix exhaustion is not risk acceptance.** A `noncritical-dissent` item remains blocking until the user explicitly selects `accept-risk`. Record the user's non-empty original text and existing activity check references through `okstra approval-decision resolve`; report assembly derives the report `resolution`.
|
|
@@ -310,6 +436,18 @@ round before any host or provider process starts.
|
|
|
310
436
|
|
|
311
437
|
## `plan-body-verification-<task-type>-<seq>.json` schema
|
|
312
438
|
|
|
439
|
+
**Take the path from the launch prompt, never from this filename (BLOCKING).**
|
|
440
|
+
The run's `## Run Paths` block renders `Plan-body verification state:` with the
|
|
441
|
+
exact path, and `scripts/okstra_ctl/paths.py` is what computed it. Do not build
|
|
442
|
+
the name from the pattern in this heading: a run carries **two seq families** —
|
|
443
|
+
`state` (team-state, convergence, lead-events, this file) and `reports` (the
|
|
444
|
+
final report and its siblings) — and they diverge whenever the two advance at
|
|
445
|
+
different rates. Writing this file under the report's seq puts it where nothing
|
|
446
|
+
looks: `validators/validate_session_conformance.py` resolves it from the
|
|
447
|
+
team-state name, so the round count reads as 0 and
|
|
448
|
+
`verification-round-completed count must match automatic plan-body rounds=0`
|
|
449
|
+
fails a run whose rounds all ran.
|
|
450
|
+
|
|
313
451
|
**Which file is authoritative for what.** Contract v3 keeps both views in one convergence-owned state file before publication:
|
|
314
452
|
|
|
315
453
|
| | records | what a self-fix round does to it |
|
|
@@ -428,9 +566,35 @@ Required prompt anchor headers are identical to finding convergence (see [conver
|
|
|
428
566
|
The [convergence](./convergence.md) §"Required reverify output contract"
|
|
429
567
|
applies unchanged: append it verbatim after the response format below.
|
|
430
568
|
|
|
569
|
+
The prompt-body contract check runs on every `analysis`-audience prompt, not
|
|
570
|
+
just the critic's, so this round needs the same two lines the critic's
|
|
571
|
+
instructions need ([convergence](./convergence.md) §"What the critic
|
|
572
|
+
task-instructions file MUST contain"): a `**Prompt Delivery Mode:**` header and,
|
|
573
|
+
under `## Inputs`, exactly one `- Primary analysis packet:` line whose path ends
|
|
574
|
+
in `analysis-packet.md`. The literal label and the backticks are what the check
|
|
575
|
+
matches — a bare path or a reworded label counts as zero. They are in the
|
|
576
|
+
template below; keep them when you fill it in.
|
|
577
|
+
|
|
578
|
+
Carrying the packet does not license re-analysis. It is there so the verifier
|
|
579
|
+
can resolve a plan item back to the requirement it claims to satisfy; the
|
|
580
|
+
posture in §"Adversarial plan-body posture" still applies, and this round does
|
|
581
|
+
not revisit the requirements themselves.
|
|
582
|
+
|
|
583
|
+
Omitting either line fails `okstra team dispatch --dispatch-kind
|
|
584
|
+
reverify-planbody-r<n>` before any process starts, reported as `<task-type>
|
|
585
|
+
prompt contract: <worker>: exactly one Primary analysis packet path is required
|
|
586
|
+
(found 0)`. Fix the instructions file and re-materialize with
|
|
587
|
+
`--replace-undispatched` rather than editing the published prompt.
|
|
588
|
+
|
|
431
589
|
````
|
|
432
590
|
Perform plan-body verification for <task-key> (round 1).
|
|
433
591
|
|
|
592
|
+
**Prompt Delivery Mode:** eager-include
|
|
593
|
+
|
|
594
|
+
## Inputs
|
|
595
|
+
|
|
596
|
+
- Primary analysis packet: `<path ending in analysis-packet.md>`
|
|
597
|
+
|
|
434
598
|
## Instructions
|
|
435
599
|
|
|
436
600
|
Review the following items extracted from the consolidated implementation plan
|
|
@@ -447,6 +611,13 @@ verdict:
|
|
|
447
611
|
(e) item contradicts the trade-off matrix — **including a `P-Opt-*` option carrying an abstraction (helper module, strategy / factory, indirection layer, interface), a configuration knob every planned call site passes identically, or an optional parameter no planned call site supplies, when no `P-Req-*` requirement-coverage item in this same queue maps to it**: the matrix priced a complexity the option does not buy. Decide this from the queue alone — the requirement-coverage rows are in it, so this is plan-internal consistency, not a re-analysis of the brief. A behavior that already has two implementations is the opposite defect and belongs to `P-Var-*`; do not raise both on one behavior,
|
|
448
612
|
(f) requirement coverage row does not map the stated requirement to a concrete satisfying option / stage / step — citing an existing option counts as concrete even if that option's paths are abbreviated (that is (b) on the option's item, not (f)).
|
|
449
613
|
When you give a DISAGREE, also answer **Fixability** — `planner-fixable` if this defect can be fixed using only the code + this plan draft + the brief, `needs-user-input` if an open user clarification / external information is required.
|
|
614
|
+
|
|
615
|
+
On a `DISAGREE(a)`, or a `DISAGREE(f)` on a `P-Req-*` item, also answer **Claim** — those are the kinds that can block on your vote alone, so state what sort of claim it is and give okstra something to check.
|
|
616
|
+
|
|
617
|
+
- **`fact`** — write a **Probe** okstra can run, as one line of JSON: `{"kind": "path-absent", "path": "src/missing.ts"}`. The kinds are `path-exists`, `path-absent`, `literal-present`, `literal-absent` (a literal inside a file), and `citations-differ` (two spelled-out references that contradict each other, as `left` / `right`). Paths are project-relative.
|
|
618
|
+
- **`judgement`** — no probe. It takes a quorum, like `b` / `c` / `e`.
|
|
619
|
+
|
|
620
|
+
**Do not write a reproduction result.** okstra runs the probe and records the outcome; a result you write is discarded. If your claim cannot be written as one of those probes, it is a `judgement` — that is a classification, not a demotion, and a claim no machine can confirm should never have blocked on one vote.
|
|
450
621
|
- **SUPPLEMENT**: The item is sound but a dependency / edge case / precondition
|
|
451
622
|
is missing.
|
|
452
623
|
- **UNVERIFIABLE**: Capability, credential, network, or service state prevents
|
|
@@ -528,6 +699,8 @@ never recorded, and the round is scored on the items that remain.
|
|
|
528
699
|
### P-Step-3
|
|
529
700
|
**Verdict**: AGREE | DISAGREE(<a|b|c|d|e|f>) | SUPPLEMENT | UNVERIFIABLE
|
|
530
701
|
**Fixability** (only when DISAGREE): planner-fixable | needs-user-input — "planner-fixable if it can be fixed using only the code + this plan + the brief". Fixability is not applicable for `UNVERIFIABLE`.
|
|
702
|
+
**Claim** (only on `DISAGREE(a)`, or `DISAGREE(f)` on a P-Req item): fact | judgement
|
|
703
|
+
**Probe** (only when Claim is fact): <one line of JSON, e.g. {"kind": "path-absent", "path": "src/missing.ts"}>
|
|
531
704
|
**Note**: <the falsification candidate considered and why it is excluded; required even for AGREE>
|
|
532
705
|
**Explanation**: <2-3 sentences>
|
|
533
706
|
|
|
@@ -30,6 +30,25 @@ Resolution `checkRefs` name existing `A-NNN` activity rows. Those activity rows
|
|
|
30
30
|
|
|
31
31
|
## Report-writer dispatch
|
|
32
32
|
|
|
33
|
+
For report contract 3.0, prompt materialization first freezes one report synthesis
|
|
34
|
+
packet. The packet contains the task brief, analysis packet, report template,
|
|
35
|
+
report schema, convergence state, every successful settled worker result recorded
|
|
36
|
+
by the run manifest, accumulated `user-responses/` sidecars, and the current
|
|
37
|
+
session/token/cost accounting snapshot. Each file-backed source carries its owner,
|
|
38
|
+
project-relative path, SHA-256 digest, and value. The report writer receives the
|
|
39
|
+
packet's Markdown reading projection as the only task input instead of an
|
|
40
|
+
independently assembled list of raw paths.
|
|
41
|
+
|
|
42
|
+
The packet's authoring contract names the result path, narrative format, writing
|
|
43
|
+
instructions, runtime-owned content, and validation rules. Missing configured
|
|
44
|
+
paths and missing files are collected across the full source set before dispatch
|
|
45
|
+
and reported together with their owners. Report assembly compares every frozen
|
|
46
|
+
source digest again and reports every changed or missing source in one result.
|
|
47
|
+
This is enforced by
|
|
48
|
+
`initial_prompt_materialization._materialize_report_writer_packet()`,
|
|
49
|
+
`report_synthesis_packet.build_report_synthesis_packet()`, and
|
|
50
|
+
`report_assembly.assemble_report()`.
|
|
51
|
+
|
|
33
52
|
Materialize the duty prompt with `okstra agent-prompt materialize --audience report-writer`. The prompt starts with these anchors in order:
|
|
34
53
|
|
|
35
54
|
1. `**Project Root:**`
|
|
@@ -41,7 +60,7 @@ Materialize the duty prompt with `okstra agent-prompt materialize --audience rep
|
|
|
41
60
|
7. `**Errors log path:**`
|
|
42
61
|
8. `**Errors sidecar path:**`
|
|
43
62
|
|
|
44
|
-
The
|
|
63
|
+
The errors sidecar anchor reserves the runtime-owned write-artifact path used by dispatch validation. No model-authored error JSON file is part of report-writer dispatch; failures use the typed error-log command from the worker error contract.
|
|
45
64
|
|
|
46
65
|
Register the dispatch with `okstra agent-prompt record-dispatch`. When `terminalBackend` is `cmux-pane`, run `okstra team dispatch`; for `runner: cli-wrapper`, run `okstra worker-dispatch --audience report-writer`. Attach the result through `okstra agent-prompt link-result`. The result path is the report narrative Markdown, not the final record.
|
|
47
66
|
|