okstra 0.186.7 → 0.187.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli-registry.d.mts +88 -1
- package/dist/cli-registry.mjs +68 -111
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +0 -1
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +8 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.mjs +1 -14
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/config.mjs +38 -40
- package/dist/commands/lifecycle/config.mjs.map +1 -1
- package/dist/commands/lifecycle/doctor.d.mts +22 -7
- package/dist/commands/lifecycle/doctor.mjs +77 -49
- package/dist/commands/lifecycle/doctor.mjs.map +1 -1
- package/dist/commands/lifecycle/install.d.mts +12 -10
- package/dist/commands/lifecycle/install.mjs +104 -39
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/lifecycle/paths.mjs +8 -3
- package/dist/commands/lifecycle/paths.mjs.map +1 -1
- package/dist/commands/lifecycle/preflight.mjs +2 -1
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/lifecycle/setup.mjs +22 -36
- package/dist/commands/lifecycle/setup.mjs.map +1 -1
- package/dist/commands/lifecycle/uninstall.d.mts +4 -2
- package/dist/commands/lifecycle/uninstall.mjs +59 -15
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
- package/dist/commands/memory/memory.mjs +7 -1
- package/dist/commands/memory/memory.mjs.map +1 -1
- package/dist/lib/helper-scripts.d.mts +1 -1
- package/dist/lib/helper-scripts.mjs +10 -18
- package/dist/lib/helper-scripts.mjs.map +1 -1
- package/dist/lib/host-config.d.mts +72 -0
- package/dist/lib/host-config.mjs +404 -0
- package/dist/lib/host-config.mjs.map +1 -0
- package/dist/lib/host-registry-client.d.mts +2 -2
- package/dist/lib/host-registry-client.mjs +0 -3
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/dist/lib/install-assets.d.mts +1 -0
- package/dist/lib/install-assets.mjs +4 -0
- package/dist/lib/install-assets.mjs.map +1 -1
- package/dist/lib/proc.d.mts +2 -0
- package/dist/lib/proc.mjs +12 -0
- package/dist/lib/proc.mjs.map +1 -1
- package/dist/lib/python-command.d.mts +2 -0
- package/dist/lib/python-command.mjs +52 -0
- package/dist/lib/python-command.mjs.map +1 -0
- package/dist/lib/python-helper.d.mts +2 -5
- package/dist/lib/python-helper.mjs +3 -52
- package/dist/lib/python-helper.mjs.map +1 -1
- package/dist/lib/runtime-payload.d.mts +23 -0
- package/dist/lib/runtime-payload.mjs +57 -0
- package/dist/lib/runtime-payload.mjs.map +1 -0
- package/dist/lib/types.d.mts +24 -13
- package/docs/architecture/storage-model.md +21 -5
- package/docs/architecture.md +37 -36
- package/docs/cli.md +55 -76
- package/docs/coding-rules.md +295 -0
- package/docs/container.md +10 -36
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/skills/okstra-code-review.md +0 -1
- package/docs/for-ai/skills/okstra-container-build.md +8 -41
- package/docs/for-ai/skills/okstra-inspect.md +4 -4
- package/docs/for-ai/skills/okstra-manager.md +2 -2
- package/docs/for-ai/skills/okstra-rollup.md +0 -1
- package/docs/for-ai/skills/okstra-run.md +3 -3
- package/docs/for-ai/skills/okstra-user-response.md +0 -1
- package/docs/performance-improvement-plan-v2.md +1 -1
- package/docs/project-structure-overview.md +62 -62
- package/docs/task-process/README.md +3 -3
- package/docs/task-process/common-flow.md +1 -1
- package/docs/task-process/error-analysis.md +3 -3
- package/docs/task-process/implementation-planning.md +3 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +6 -11
- package/runtime/agents/workers/report-writer-worker.md +1 -1
- package/runtime/bin/lib/okstra/cli.sh +4 -0
- package/runtime/bin/lib/okstra/globals.sh +2 -0
- package/runtime/bin/lib/okstra/interactive.sh +26 -173
- package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
- package/runtime/bin/lib/okstra/usage.sh +3 -3
- package/runtime/bin/okstra-compact-reminder.sh +1 -5
- package/runtime/bin/okstra-error-log.py +19 -2
- package/runtime/bin/okstra-import-check.py +26 -0
- package/runtime/bin/okstra-inject-report-index.py +5 -4
- package/runtime/bin/okstra-provider-exec.py +22 -17
- package/runtime/bin/okstra-render-final-report.py +16 -6
- package/runtime/bin/okstra-render-report-views.py +25 -18
- package/runtime/bin/okstra-report-translate.py +30 -18
- package/runtime/bin/okstra-spawn-followups.py +4 -0
- package/runtime/bin/okstra-token-usage.py +3 -0
- package/runtime/bin/okstra.sh +4 -2
- package/runtime/bin/okstra_bootstrap.py +58 -0
- package/runtime/prompts/coding-preflight/overview.md +1 -1
- package/runtime/prompts/duties/planning-worker.md +1 -1
- package/runtime/prompts/launch.template.md +35 -9
- package/runtime/prompts/lead/convergence.md +96 -87
- package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
- package/runtime/prompts/lead/plan-body-verification.md +65 -46
- package/runtime/prompts/lead/report-writer.md +34 -4
- package/runtime/prompts/lead/team-contract.md +4 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +3 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
- package/runtime/prompts/profiles/error-analysis.md +4 -3
- package/runtime/prompts/profiles/final-verification.md +2 -2
- package/runtime/prompts/profiles/implementation-planning.md +22 -17
- package/runtime/prompts/profiles/implementation.md +3 -2
- package/runtime/prompts/profiles/requirements-discovery.md +2 -1
- package/runtime/prompts/wizard/prompts.ko.json +29 -8
- package/runtime/python/okstra_ctl/__init__.py +6 -27
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +40 -22
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +14 -21
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
- package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
- package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
- package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +12 -1
- package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
- package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +215 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +408 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
- package/runtime/python/okstra_ctl/approval_decisions.py +260 -8
- package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
- package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
- package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
- package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
- package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
- package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
- package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
- package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
- package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
- package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
- package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
- package/runtime/python/okstra_ctl/cmux.py +38 -31
- package/runtime/python/okstra_ctl/code_review_target.py +163 -1
- package/runtime/python/okstra_ctl/conformance.py +23 -0
- package/runtime/python/okstra_ctl/container.py +38 -421
- package/runtime/python/okstra_ctl/context_cost.py +16 -2
- package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
- package/runtime/python/okstra_ctl/convergence.py +101 -15
- package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
- package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
- package/runtime/python/okstra_ctl/convergence_store.py +5 -29
- package/runtime/python/okstra_ctl/design_prep.py +108 -12
- package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
- package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
- package/runtime/python/okstra_ctl/dispatch_core.py +102 -25
- package/runtime/python/okstra_ctl/dispatch_state.py +268 -39
- package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -1
- package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
- package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
- package/runtime/python/okstra_ctl/error_log_core.py +1 -1
- package/runtime/python/okstra_ctl/error_report.py +14 -0
- package/runtime/python/okstra_ctl/error_zip.py +18 -2
- package/runtime/python/okstra_ctl/execution_identity.py +100 -7
- package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
- package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +90 -16
- package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
- package/runtime/python/okstra_ctl/handoff.py +24 -1
- package/runtime/python/okstra_ctl/ids.py +6 -16
- package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
- package/runtime/python/okstra_ctl/implementation_options.py +25 -11
- package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
- package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
- package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
- package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
- package/runtime/python/okstra_ctl/index.py +2 -2
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
- package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
- package/runtime/python/okstra_ctl/json_boundary.py +10 -0
- package/runtime/python/okstra_ctl/listing.py +0 -91
- package/runtime/python/okstra_ctl/locks.py +5 -19
- package/runtime/python/okstra_ctl/log_report.py +14 -0
- package/runtime/python/okstra_ctl/manager_cli.py +22 -3
- package/runtime/python/okstra_ctl/manager_launch.py +4 -8
- package/runtime/python/okstra_ctl/material.py +2 -2
- package/runtime/python/okstra_ctl/migrate.py +23 -1
- package/runtime/python/okstra_ctl/model_cli.py +19 -5
- package/runtime/python/okstra_ctl/model_discovery.py +46 -47
- package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
- package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
- package/runtime/python/okstra_ctl/model_io/references.py +370 -0
- package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
- package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
- package/runtime/python/okstra_ctl/model_pool.py +10 -6
- package/runtime/python/okstra_ctl/models.py +8 -8
- package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
- package/runtime/python/okstra_ctl/next_phase.py +121 -69
- package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
- package/runtime/python/okstra_ctl/pane_title.py +11 -8
- package/runtime/python/okstra_ctl/path_hints.py +36 -11
- package/runtime/python/okstra_ctl/paths.py +76 -8
- package/runtime/python/okstra_ctl/plan_items.py +132 -16
- package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
- package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
- package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
- package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
- package/runtime/python/okstra_ctl/prepare_error.py +20 -0
- package/runtime/python/okstra_ctl/prior_planning.py +157 -0
- package/runtime/python/okstra_ctl/profile_show.py +18 -1
- package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
- package/runtime/python/okstra_ctl/recap.py +22 -1
- package/runtime/python/okstra_ctl/reconcile.py +84 -0
- package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
- package/runtime/python/okstra_ctl/render.py +98 -32
- package/runtime/python/okstra_ctl/render_final_report.py +3 -7
- package/runtime/python/okstra_ctl/report_assembly.py +297 -7
- package/runtime/python/okstra_ctl/report_contract.py +30 -7
- package/runtime/python/okstra_ctl/report_finalize.py +206 -15
- package/runtime/python/okstra_ctl/report_html/common.py +2 -2
- package/runtime/python/okstra_ctl/report_html/render.py +8 -12
- package/runtime/python/okstra_ctl/report_language.py +8 -4
- package/runtime/python/okstra_ctl/report_narrative.py +90 -3
- package/runtime/python/okstra_ctl/report_projections.py +8 -2
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
- package/runtime/python/okstra_ctl/report_translation.py +2 -35
- package/runtime/python/okstra_ctl/report_views.py +4 -22
- package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
- package/runtime/python/okstra_ctl/rollup.py +13 -0
- package/runtime/python/okstra_ctl/run.py +639 -138
- package/runtime/python/okstra_ctl/run_audit.py +13 -0
- package/runtime/python/okstra_ctl/run_index_row.py +0 -12
- package/runtime/python/okstra_ctl/seeding.py +21 -11
- package/runtime/python/okstra_ctl/sequence.py +1 -1
- package/runtime/python/okstra_ctl/session.py +64 -14
- package/runtime/python/okstra_ctl/set_work_status.py +18 -0
- package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
- package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
- package/runtime/python/okstra_ctl/stage_map.py +57 -11
- package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
- package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
- package/runtime/python/okstra_ctl/stage_targets.py +3 -12
- package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
- package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
- package/runtime/python/okstra_ctl/task_target.py +4 -16
- package/runtime/python/okstra_ctl/team.py +49 -2
- package/runtime/python/okstra_ctl/time_report.py +17 -2
- package/runtime/python/okstra_ctl/usage_report.py +11 -0
- package/runtime/python/okstra_ctl/user_response.py +55 -218
- package/runtime/python/okstra_ctl/user_response_values.py +242 -0
- package/runtime/python/okstra_ctl/validation_contract.py +3 -0
- package/runtime/python/okstra_ctl/verification_target.py +68 -0
- package/runtime/python/okstra_ctl/wizard.py +457 -92
- package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
- package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
- package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
- package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
- package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
- package/runtime/python/okstra_ctl/worker_request.py +2 -1
- package/runtime/python/okstra_ctl/worker_runner.py +16 -9
- package/runtime/python/okstra_ctl/worker_state.py +16 -1
- package/runtime/python/okstra_ctl/workflow.py +18 -1
- package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
- package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
- package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
- package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
- package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
- package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
- package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
- package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
- package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
- package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
- package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
- package/runtime/python/okstra_ctl/write_policy.py +90 -213
- package/runtime/python/okstra_project/__init__.py +0 -4
- package/runtime/python/okstra_project/dirs.py +2 -2
- package/runtime/python/okstra_project/phase_pointer.py +84 -0
- package/runtime/python/okstra_project/slug.py +24 -0
- package/runtime/python/okstra_project/state.py +22 -230
- package/runtime/python/okstra_token_usage/claude.py +9 -1
- package/runtime/python/okstra_token_usage/cli.py +3 -1
- package/runtime/python/okstra_token_usage/collect.py +71 -10
- package/runtime/python/okstra_token_usage/cursor.py +2 -0
- package/runtime/python/okstra_token_usage/grok.py +1 -5
- package/runtime/python/okstra_token_usage/paths.py +26 -0
- package/runtime/python/okstra_token_usage/pricing.py +19 -7
- package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
- package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
- package/runtime/schemas/final-report-v2.0.schema.json +9 -26
- package/runtime/schemas/final-report-v3.0.schema.json +173 -41
- package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
- package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
- package/runtime/skills/okstra-container-build/SKILL.md +16 -47
- package/runtime/skills/okstra-inspect/facets/history.md +1 -1
- package/runtime/skills/okstra-inspect/facets/status.md +7 -6
- package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +7 -5
- package/runtime/templates/report-writer-prompt-preamble.md +1 -1
- package/runtime/templates/reports/brief.template.md +6 -2
- package/runtime/templates/reports/error-analysis-input.template.md +2 -0
- package/runtime/templates/reports/final-verification-input.template.md +2 -0
- package/runtime/templates/reports/implementation-input.template.md +7 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
- package/runtime/templates/reports/quick-input.template.md +2 -0
- package/runtime/templates/reports/release-handoff-input.template.md +6 -1
- package/runtime/templates/reports/report.js +20 -3
- package/runtime/templates/reports/schedule.template.md +2 -0
- package/runtime/templates/reports/settings.template.json +0 -10
- package/runtime/templates/reports/task-brief.template.md +3 -0
- package/runtime/templates/reports/user-response.template.md +7 -3
- package/runtime/validators/checks/fixtures-01.py +57 -0
- package/runtime/validators/checks/fixtures-02.py +565 -0
- package/runtime/validators/checks/runners-01.py +108 -0
- package/runtime/validators/checks/validate-assets-01.py +60 -0
- package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
- package/runtime/validators/checks/validate-tasks-01.py +46 -0
- package/runtime/validators/checks/validate-tasks-02.py +85 -0
- package/runtime/validators/checks/validate-tasks-03.py +61 -0
- package/runtime/validators/checks/validate-tasks-04.py +118 -0
- package/runtime/validators/forbidden_actions.py +73 -3
- package/runtime/validators/lib/common.sh +5 -0
- package/runtime/validators/lib/fixtures.sh +7 -591
- package/runtime/validators/lib/paths.sh +13 -0
- package/runtime/validators/lib/runners.sh +6 -104
- package/runtime/validators/lib/summary.sh +1 -1
- package/runtime/validators/lib/validate-assets.sh +6 -56
- package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
- package/runtime/validators/lib/validate-tasks.sh +9 -294
- package/runtime/validators/validate-implementation-plan-stages.py +4 -4
- package/runtime/validators/validate-run.py +1369 -2654
- package/runtime/validators/validate-workflow.sh +56 -16
- package/runtime/validators/validate_improvement_report.py +2 -1
- package/runtime/validators/validate_session_conformance.py +295 -49
- package/dist/commands/execute/agent-prompt.d.mts +0 -1
- package/dist/commands/execute/agent-prompt.mjs +0 -24
- package/dist/commands/execute/agent-prompt.mjs.map +0 -1
- package/dist/commands/execute/codex-dispatch.d.mts +0 -3
- package/dist/commands/execute/codex-dispatch.mjs +0 -6
- package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
- package/dist/commands/execute/codex-run.d.mts +0 -3
- package/dist/commands/execute/codex-run.mjs +0 -62
- package/dist/commands/execute/codex-run.mjs.map +0 -1
- package/dist/commands/execute/convergence.d.mts +0 -1
- package/dist/commands/execute/convergence.mjs +0 -38
- package/dist/commands/execute/convergence.mjs.map +0 -1
- package/dist/commands/execute/error-log.d.mts +0 -1
- package/dist/commands/execute/error-log.mjs +0 -18
- package/dist/commands/execute/error-log.mjs.map +0 -1
- package/dist/commands/execute/git-reconcile.d.mts +0 -1
- package/dist/commands/execute/git-reconcile.mjs +0 -30
- package/dist/commands/execute/git-reconcile.mjs.map +0 -1
- package/dist/commands/execute/handoff.d.mts +0 -1
- package/dist/commands/execute/handoff.mjs +0 -31
- package/dist/commands/execute/handoff.mjs.map +0 -1
- package/dist/commands/execute/incremental-carry.d.mts +0 -1
- package/dist/commands/execute/incremental-carry.mjs +0 -20
- package/dist/commands/execute/incremental-carry.mjs.map +0 -1
- package/dist/commands/execute/incremental-scope.d.mts +0 -1
- package/dist/commands/execute/incremental-scope.mjs +0 -29
- package/dist/commands/execute/incremental-scope.mjs.map +0 -1
- package/dist/commands/execute/integrate-stages.d.mts +0 -1
- package/dist/commands/execute/integrate-stages.mjs +0 -24
- package/dist/commands/execute/integrate-stages.mjs.map +0 -1
- package/dist/commands/execute/pane-title.d.mts +0 -1
- package/dist/commands/execute/pane-title.mjs +0 -20
- package/dist/commands/execute/pane-title.mjs.map +0 -1
- package/dist/commands/execute/plan-items.d.mts +0 -1
- package/dist/commands/execute/plan-items.mjs +0 -9
- package/dist/commands/execute/plan-items.mjs.map +0 -1
- package/dist/commands/execute/plan-validate.d.mts +0 -1
- package/dist/commands/execute/plan-validate.mjs +0 -68
- package/dist/commands/execute/plan-validate.mjs.map +0 -1
- package/dist/commands/execute/plan-verify.d.mts +0 -1
- package/dist/commands/execute/plan-verify.mjs +0 -43
- package/dist/commands/execute/plan-verify.mjs.map +0 -1
- package/dist/commands/execute/spawn-followups.d.mts +0 -1
- package/dist/commands/execute/spawn-followups.mjs +0 -22
- package/dist/commands/execute/spawn-followups.mjs.map +0 -1
- package/dist/commands/execute/team.d.mts +0 -3
- package/dist/commands/execute/team.mjs +0 -66
- package/dist/commands/execute/team.mjs.map +0 -1
- package/dist/commands/execute/token-usage.d.mts +0 -1
- package/dist/commands/execute/token-usage.mjs +0 -19
- package/dist/commands/execute/token-usage.mjs.map +0 -1
- package/dist/commands/execute/worker-audit-check.d.mts +0 -1
- package/dist/commands/execute/worker-audit-check.mjs +0 -34
- package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
- package/dist/commands/execute/worker-dispatch.d.mts +0 -7
- package/dist/commands/execute/worker-dispatch.mjs +0 -64
- package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
- package/dist/commands/execute/worker-state.d.mts +0 -1
- package/dist/commands/execute/worker-state.mjs +0 -28
- package/dist/commands/execute/worker-state.mjs.map +0 -1
- package/dist/commands/execute/worktree-lookup.d.mts +0 -1
- package/dist/commands/execute/worktree-lookup.mjs +0 -92
- package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
- package/dist/commands/execute/worktree-status.d.mts +0 -1
- package/dist/commands/execute/worktree-status.mjs +0 -121
- package/dist/commands/execute/worktree-status.mjs.map +0 -1
- package/dist/commands/inspect/code-review.d.mts +0 -1
- package/dist/commands/inspect/code-review.mjs +0 -32
- package/dist/commands/inspect/code-review.mjs.map +0 -1
- package/dist/commands/inspect/container.d.mts +0 -1
- package/dist/commands/inspect/container.mjs +0 -25
- package/dist/commands/inspect/container.mjs.map +0 -1
- package/dist/commands/inspect/context-cost.d.mts +0 -1
- package/dist/commands/inspect/context-cost.mjs +0 -25
- package/dist/commands/inspect/context-cost.mjs.map +0 -1
- package/dist/commands/inspect/design-prep.d.mts +0 -1
- package/dist/commands/inspect/design-prep.mjs +0 -22
- package/dist/commands/inspect/design-prep.mjs.map +0 -1
- package/dist/commands/inspect/error-report.d.mts +0 -1
- package/dist/commands/inspect/error-report.mjs +0 -25
- package/dist/commands/inspect/error-report.mjs.map +0 -1
- package/dist/commands/inspect/error-zip.d.mts +0 -1
- package/dist/commands/inspect/error-zip.mjs +0 -24
- package/dist/commands/inspect/error-zip.mjs.map +0 -1
- package/dist/commands/inspect/log-report.d.mts +0 -1
- package/dist/commands/inspect/log-report.mjs +0 -26
- package/dist/commands/inspect/log-report.mjs.map +0 -1
- package/dist/commands/inspect/model-io.d.mts +0 -1
- package/dist/commands/inspect/model-io.mjs +0 -25
- package/dist/commands/inspect/model-io.mjs.map +0 -1
- package/dist/commands/inspect/profile-show.d.mts +0 -1
- package/dist/commands/inspect/profile-show.mjs +0 -28
- package/dist/commands/inspect/profile-show.mjs.map +0 -1
- package/dist/commands/inspect/recap.d.mts +0 -1
- package/dist/commands/inspect/recap.mjs +0 -30
- package/dist/commands/inspect/recap.mjs.map +0 -1
- package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
- package/dist/commands/inspect/resolve-task-key.mjs +0 -24
- package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
- package/dist/commands/inspect/rollup.d.mts +0 -1
- package/dist/commands/inspect/rollup.mjs +0 -25
- package/dist/commands/inspect/rollup.mjs.map +0 -1
- package/dist/commands/inspect/run-audit.d.mts +0 -1
- package/dist/commands/inspect/run-audit.mjs +0 -25
- package/dist/commands/inspect/run-audit.mjs.map +0 -1
- package/dist/commands/inspect/set-work-status.d.mts +0 -1
- package/dist/commands/inspect/set-work-status.mjs +0 -29
- package/dist/commands/inspect/set-work-status.mjs.map +0 -1
- package/dist/commands/inspect/stage-map.d.mts +0 -1
- package/dist/commands/inspect/stage-map.mjs +0 -131
- package/dist/commands/inspect/stage-map.mjs.map +0 -1
- package/dist/commands/inspect/task-list.d.mts +0 -1
- package/dist/commands/inspect/task-list.mjs +0 -149
- package/dist/commands/inspect/task-list.mjs.map +0 -1
- package/dist/commands/inspect/task-show.d.mts +0 -1
- package/dist/commands/inspect/task-show.mjs +0 -108
- package/dist/commands/inspect/task-show.mjs.map +0 -1
- package/dist/commands/inspect/time-report.d.mts +0 -1
- package/dist/commands/inspect/time-report.mjs +0 -24
- package/dist/commands/inspect/time-report.mjs.map +0 -1
- package/dist/commands/inspect/usage-report.d.mts +0 -1
- package/dist/commands/inspect/usage-report.mjs +0 -23
- package/dist/commands/inspect/usage-report.mjs.map +0 -1
- package/dist/commands/inspect/user-response.d.mts +0 -1
- package/dist/commands/inspect/user-response.mjs +0 -35
- package/dist/commands/inspect/user-response.mjs.map +0 -1
- package/dist/commands/inspect/worker-liveness.d.mts +0 -1
- package/dist/commands/inspect/worker-liveness.mjs +0 -45
- package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
- package/dist/commands/lifecycle/contract-check.d.mts +0 -1
- package/dist/commands/lifecycle/contract-check.mjs +0 -18
- package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
- package/dist/commands/lifecycle/migrate.d.mts +0 -1
- package/dist/commands/lifecycle/migrate.mjs +0 -30
- package/dist/commands/lifecycle/migrate.mjs.map +0 -1
- package/dist/commands/lifecycle/model.d.mts +0 -1
- package/dist/commands/lifecycle/model.mjs +0 -22
- package/dist/commands/lifecycle/model.mjs.map +0 -1
- package/dist/commands/manager.d.mts +0 -3
- package/dist/commands/manager.mjs +0 -50
- package/dist/commands/manager.mjs.map +0 -1
- package/dist/commands/report/agent-activity.d.mts +0 -1
- package/dist/commands/report/agent-activity.mjs +0 -20
- package/dist/commands/report/agent-activity.mjs.map +0 -1
- package/dist/commands/report/approval-decision.d.mts +0 -1
- package/dist/commands/report/approval-decision.mjs +0 -21
- package/dist/commands/report/approval-decision.mjs.map +0 -1
- package/dist/commands/report/design-snapshot.d.mts +0 -1
- package/dist/commands/report/design-snapshot.mjs +0 -19
- package/dist/commands/report/design-snapshot.mjs.map +0 -1
- package/dist/commands/report/finalize.d.mts +0 -4
- package/dist/commands/report/finalize.mjs +0 -64
- package/dist/commands/report/finalize.mjs.map +0 -1
- package/dist/commands/report/inject-report-index.d.mts +0 -1
- package/dist/commands/report/inject-report-index.mjs +0 -21
- package/dist/commands/report/inject-report-index.mjs.map +0 -1
- package/dist/commands/report/render-final-report.d.mts +0 -1
- package/dist/commands/report/render-final-report.mjs +0 -23
- package/dist/commands/report/render-final-report.mjs.map +0 -1
- package/dist/commands/report/render-views.d.mts +0 -1
- package/dist/commands/report/render-views.mjs +0 -27
- package/dist/commands/report/render-views.mjs.map +0 -1
- package/dist/commands/report/translate.d.mts +0 -1
- package/dist/commands/report/translate.mjs +0 -33
- package/dist/commands/report/translate.mjs.map +0 -1
- package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
- package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
- package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
- package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
- package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
- package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
- package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
- package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
- package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
- package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
- package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
- package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
- package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
- package/runtime/bin/okstra-central.sh +0 -152
- package/runtime/bin/okstra-incremental-carry.py +0 -10
- package/runtime/bin/okstra-incremental-scope.py +0 -10
- package/runtime/bin/okstra-team-reconcile.sh +0 -36
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
- package/runtime/python/okstra_ctl/batch.py +0 -60
- package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
- package/runtime/python/okstra_ctl/container_registry.py +0 -72
- package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
- package/runtime/python/okstra_ctl/resolver.py +0 -54
- package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
- package/runtime/python/okstra_ctl/tmux.py +0 -134
- package/runtime/python/okstra_ctl/worktree.py +0 -1099
|
@@ -24,13 +24,15 @@
|
|
|
24
24
|
|
|
25
25
|
This contract governs **Phase 5.5 (Convergence loop)** — a *lead operating phase* inside a single okstra run, not a task-type lifecycle phase. It leaves the 7 task-type lifecycle phases (`requirements-discovery` → `error-analysis` → `implementation-option-selection` → `implementation-planning` → `implementation` → `final-verification` → `release-handoff`, see [okstra-lead-contract](./okstra-lead-contract.md) "Lifecycle Phase Boundaries") unchanged; the lead operating phases (Phase 1 Intake → Phase 7 Persist, see [okstra-lead-contract](./okstra-lead-contract.md) "Quick Reference") drive a *single* task-type run.
|
|
26
26
|
|
|
27
|
-
**`contested` is a
|
|
27
|
+
**`contested` is a terminal classification, never an intermediate queue label.** The verification queue carries findings that are *unique to a single worker* (entered in Round 0) or *mixed/unresolved after a re-verification round* (carried forward). A finding is labelled `contested` in two places, both of which remove it from the queue: at the round where an adversarial `counter-evidence` refute lands (§"Adversarial Verification Mode"), and when the **last executed round** completes with the queue still non-empty. A `contested` finding is never re-dispatched.
|
|
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
|
+
**Enforced:** `_validate_resolved_findings_leave_the_queue` in `validators/validate-run.py` fails a resolved finding whose `rounds[]` ledger is not a contiguous `1..N` — a gap is the finding re-entering the queue after classification.
|
|
32
|
+
|
|
31
33
|
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
34
|
|
|
33
|
-
The end-to-end artifact lifecycle is worker results → Round 0 grouping → reducer-owned queue → analyser-instance re-verification → optional `okstra convergence apply-critic-gaps` transition → validated terminal state (newly finalized v1.
|
|
35
|
+
The end-to-end artifact lifecycle is worker results → Round 0 grouping → reducer-owned queue → analyser-instance re-verification → optional `okstra convergence apply-critic-gaps` transition → validated terminal state (newly finalized v1.4, or unchanged historical v1.0–v1.3 from `reuse-final`) → report-writer narrative → deterministic report assembly. Cross-verification is queue-scoped: it does not mean that one worker reviews another worker's complete result. The reducer asks independent analyser instances to vote only on non-consensus findings selected by the persisted plan; the report writer never votes.
|
|
34
36
|
|
|
35
37
|
Initial and reverify worker prompts carry `**Audit sidecar path:**`. Initial workers write their reading confirmation there; reverify workers use their own canonical sidecar for the reverify session without reopening the initial worker's full reading packet.
|
|
36
38
|
|
|
@@ -58,12 +60,15 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
|
|
|
58
60
|
|------|------|------------|
|
|
59
61
|
| `full-consensus` | All participating workers agree | Required |
|
|
60
62
|
| `partial-consensus` | Majority of workers agree; dissenting opinions are recorded | Required |
|
|
61
|
-
| `contested` |
|
|
63
|
+
| `contested` | Terminal classification. Assigned to a finding that remains in the verification queue after the **last executed round** completes (round index = `effectiveMaxRounds`), and — in adversarial mode only — to a finding refuted with `counter-evidence` at the round that refute lands. Each worker's position across all executed rounds is recorded. Either way the finding leaves the queue and is never re-dispatched. | Required |
|
|
64
|
+
| `unverified` | Final classification only. Assigned to a finding that reached the last executed round with **every recorded vote** `verification-error` — a terminal non-result dispatch, or no analyser available to vote. Nobody inspected it, so `contested` would state a dispute that never happened. The gap ledger already applies the same rule (§'Gap verification'). | Required |
|
|
62
65
|
| `worker-unique` | Only the discoverer confirms and ALL other non-error votes are `DISAGREE`. `verification-error` votes are excluded from the tally per §"Worker failure handling in reverify"; a finding where every non-discoverer vote is `verification-error` is carried forward, never classified `worker-unique`. | Required |
|
|
63
66
|
|
|
64
67
|
## Convergence Algorithm
|
|
65
68
|
|
|
66
|
-
**Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. This rule applies identically to the plan-body verification round ([plan-body-verification](./plan-body-verification.md)) where the same verdict tokens are reused.
|
|
69
|
+
**Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. In adversarial mode a tie whose DISAGREE carries `counter-evidence` does not carry forward — it is classified `contested` in that round (§"Adversarial Verification Mode"). This rule applies identically to the plan-body verification round ([plan-body-verification](./plan-body-verification.md)) where the same verdict tokens are reused.
|
|
70
|
+
|
|
71
|
+
**Enforced:** the engine owns the classifier and replays it — `scripts/okstra_ctl/convergence_engine.py` `validate_final_state` recomputes each finding's expected final classification from its recorded votes and rejects the state when the persisted value differs, and `finalize` runs that same check before it writes, so a tie scored as a consensus never reaches the artifact. `okstra convergence validate` is the same call on demand. Nothing re-derives the majority rule outside the engine; a second implementation would only drift from it.
|
|
67
72
|
|
|
68
73
|
### Round 0: Parse worker results
|
|
69
74
|
|
|
@@ -87,10 +92,7 @@ Read the worker result files generated in Phase 4/5 and extract individual findi
|
|
|
87
92
|
5. Author the fixed grouping Markdown accepted by `okstra convergence prepare-groups --run-manifest <run-manifest> --input <grouping.md>`, then run that command. Python owns the artifact identifier, target path, schema version, task identity, run-manifest reference, and every participant reference. Each Markdown group records ticket IDs, origin worker and evidence, discovering workers, source worker item IDs, and optional captured evidence. An analysis sidetrack with no ticket uses an empty `Tickets:` value, never a placeholder. Use the ordered functional roster: finding workers have the `analysis` audience, the report author has `report-writer`, and the lead uses `lead`. A lead source never votes. Never infer live evidence or functional scope from wording, provider, model, or execution label.
|
|
88
93
|
|
|
89
94
|
The command sets each worker's paired `participantRef` and `sourceRoleExecutionRef` from the run manifest's canonical role state. It sets `sourceRoleExecutionRef` to the selected source `RoleExecution` row's `roleExecutionRef`, not that row's `sourceRoleExecutionRef` field.
|
|
90
|
-
6. Do not write a queue or classification in this grouped-input artifact. `okstra convergence seed` classifies Round 0 by
|
|
91
|
-
- Collaborative mode: multi-source groups become `full-consensus` immediately; only single-source groups enter the working queue.
|
|
92
|
-
- Adversarial mode: every finding enters the working queue regardless of source count. Semantic grouping merges provenance only; it does not decide a finding is reliable.
|
|
93
|
-
Section 6 never enters the grouped input.
|
|
95
|
+
6. Do not write a queue or classification in this grouped-input artifact. `okstra convergence seed` classifies Round 0 the same way in both modes: a group whose sources are **two or more distinct role executions** becomes `full-consensus` immediately, and only single-source groups enter the working queue. Independent co-derivation is already cross-verification — the adversarial burden of proof targets single-source claims, not a finding two roles reached on their own. A source is counted once per analysis worker, and one analysis worker is exactly one `sourceRoleExecutionRef` — the same identity the reverify roster uses for independence — so two roles held by one provider count as two and no role can count twice. **Enforced:** `_parse_workers` rejects a duplicate `workerId` and `_validate_worker_execution_identity` rejects a duplicate `sourceRoleExecutionRef`, both in `scripts/okstra_ctl/convergence_engine.py`. Semantic grouping merges provenance only; it does not decide a single-source finding is reliable. Section 6 never enters the grouped input.
|
|
94
96
|
|
|
95
97
|
### Round 1-N: Re-verification Loop (queue-pruned)
|
|
96
98
|
|
|
@@ -111,12 +113,12 @@ Follow this protocol exactly:
|
|
|
111
113
|
0. Version-selected schemas describe what the reducer reads: `schemas/convergence-groups-v1.0.schema.json` accepts only legacy groups, `schemas/convergence-groups-v2.0.schema.json` accepts only explicit v2 execution identity, and `schemas/convergence-round-results-v1.0.schema.json` feeds step 4's `apply-round --results`. `schemas/convergence-critic-results-v1.0.schema.json` is a fourth shape but **not** a reducer input — it describes the critic worker's own result document. Step 6's `apply-critic-gaps --results` takes the coverage batch you assemble from those candidates plus each analyser's vote (`{schemaVersion, taskKey, mode, provider, modelExecutionValue, dispatches[], gaps[]}`, spelled out in §"Coverage critic pass" §"State"); feeding the critic document straight in is rejected, by design. `okstra convergence example --kind <groups|round-results|critic-results>` prints a deterministic valid v1 instance of each and writes only JSON to stdout.
|
|
112
114
|
1. Run `okstra convergence seed --groups <groups> --run-manifest <current-run-manifest> --work-state <work> --final-state <final> --migration-dir <state/migrations>` for a v2 worker roster. `--run-manifest` must be the exact current manifest named by the groups document's `runManifestPath`; a previous run from the same task is not interchangeable. Omit the flag for a legacy v1 roster. A `reuse-final` action means validate the existing final and continue to Phase 6. `create-work`, `resume-work`, and `restart-round0` continue with planning.
|
|
113
115
|
2. Run `okstra convergence plan-round --work-state <work> --plan <round-plan>`. This is read-only with respect to the working state.
|
|
114
|
-
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`.
|
|
116
|
+
3. Every plan carries `dispatchable`: `true` on an `action: "dispatch"` plan, `false` on an `action: "finalize"` plan. 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`.
|
|
115
117
|
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>`.
|
|
116
|
-
5. Repeat `plan-round` and `apply-round` until the plan action is `finalize`.
|
|
118
|
+
5. Repeat `plan-round` and `apply-round` until the plan action is `finalize`. A `finalize` plan is **never dispatched**: it carries `dispatchable: false`, an empty `dispatches[]`, and a `note` naming the next command. Its `round` is only the `<N>` in `convergence-round-<N>-plan-<task-type>-<seq>.json` — not a round to run — so do not open reverify workers for it. **Enforced:** `okstra convergence apply-round` refuses a non-dispatch plan and returns the gate's closing reason with the remedy (`scripts/okstra_ctl/convergence_engine.py` `apply_round_results`).
|
|
117
119
|
6. When the coverage critic is enabled, convert its verification batch to the canonical `dispatches[]` / `gaps[]` result shape and run `okstra convergence apply-critic-gaps --work-state <work> --results <critic-results>` exactly once. The reducer, not the lead, merges verified gaps.
|
|
118
120
|
7. Run `okstra convergence finalize --work-state <work> --output <final>`.
|
|
119
|
-
8. Run `okstra convergence validate --state <final> --kind final`. Newly finalized convergence output is schema v1.
|
|
121
|
+
8. Run `okstra convergence validate --state <final> --kind final`. Newly finalized convergence output is schema v1.4. Under `reuse-final`, a valid historical final schema v1.0, v1.1, v1.2, or v1.3 remains consumable by the report-writer without rewrite; do not finalize, upgrade, or otherwise rewrite that reused artifact. Deliver the validated terminal state to the report-writer, which does not vote.
|
|
120
122
|
|
|
121
123
|
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.
|
|
122
124
|
|
|
@@ -130,10 +132,14 @@ A valid historical final schema v1.0, v1.1, or v1.2 is reused unchanged under `r
|
|
|
130
132
|
|
|
131
133
|
`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.
|
|
132
134
|
|
|
135
|
+
A gate-closed plan states its own terminality in `dispatchable: false` and `note`; read those, not `round`. With `effectiveMaxRounds: 2` the closing plan reads `{"action": "finalize", "round": 3, "dispatchable": false, "reason": "max-rounds-reached"}` — `round: 3` names the artifact, and there is no round 3 to dispatch.
|
|
136
|
+
|
|
133
137
|
#### Worker failure handling in reverify (BLOCKING)
|
|
134
138
|
|
|
135
139
|
A reverify dispatch that returns a **terminal non-result** (`timeout`, `error`, no result file, or the wrapper records `cli-failure`) MUST NOT be aggregated as `DISAGREE`. Misclassifying a worker failure as DISAGREE biases the queue toward `contested`/`worker-unique` and produces meaningless final classifications.
|
|
136
140
|
|
|
141
|
+
**Enforced:** `_validate_worker_failure_is_not_a_disagree` in `validators/validate-run.py` matches each round's `dispatches[].status` against that round's `votes` and fails a `disagree` recorded for a worker whose dispatch status is `timeout` / `error` / `not-run`.
|
|
142
|
+
|
|
137
143
|
Rules:
|
|
138
144
|
|
|
139
145
|
1. For each failed dispatch, put its actual terminal status and duration in the round-results `dispatches[]`; do not invent a vote. `okstra convergence apply-round` appends `votes[W].verdict = "verification-error"` with the terminal reason for every affected finding. A completed dispatch may separately return `UNVERIFIABLE` for a particular finding; the input alias is persisted as `verification-error` with its required non-empty explanation while the dispatch remains `completed`.
|
|
@@ -174,7 +180,7 @@ If every required analysis worker produces a non-result, the run verdict is `blo
|
|
|
174
180
|
|
|
175
181
|
### Scoped full-reanalysis
|
|
176
182
|
|
|
177
|
-
Adversarial mode forces `verificationMode = "full-reanalysis"`, but the re-analysis is **scoped to the evidence the finding under attack cites** (the file paths / line ranges / log lines in its `originEvidence`), plus the immediately surrounding context. The verifier MUST NOT re-read the whole task brief, instruction-set, or `final-report-template.md`. This keeps the documented "single largest avoidable cost in requirements-discovery, error-analysis, and implementation-planning" (see §"Reverify prompt: required-reading suppression") bounded while making the refutation real rather than a text-only argument.
|
|
183
|
+
Adversarial mode forces `verificationMode = "full-reanalysis"`, but the re-analysis is **scoped to the evidence the finding under attack cites** (the file paths / line ranges / log lines in its `originEvidence`), plus the immediately surrounding context. The verifier MUST NOT re-read the whole task brief, instruction-set, or `final-report-template.md`. **Enforced (template clause only):** `_validate_full_reanalysis_prompt_omits_the_report_template` in `validators/validate-run.py` fails a reverify prompt that references `final-report-template.md`; the brief and instruction-set clauses have no fixed literal to match on. This keeps the documented "single largest avoidable cost in requirements-discovery, error-analysis, and implementation-planning" (see §"Reverify prompt: required-reading suppression") bounded while making the refutation real rather than a text-only argument.
|
|
178
184
|
|
|
179
185
|
### Adversarial verdict semantics
|
|
180
186
|
|
|
@@ -194,7 +200,7 @@ Each `disagree` vote records a new field `disagreeBasis`:
|
|
|
194
200
|
| `counter-evidence` | The verifier opened and inspected the cited evidence and found a contradiction (`file:line` / log line) recorded in `explanation`. A **hard refute**. |
|
|
195
201
|
| `burden-not-met` | The verifier opened and inspected the cited evidence, but its contents were insufficient to establish the claim. |
|
|
196
202
|
|
|
197
|
-
A `disagree` with `disagreeBasis == null` is a contract violation in adversarial mode — every refutation must state which of the two grounds it rests on. Bare "I disagree" without re-inspection is not allowed. If capability, credential, network, or service state prevents that inspection, the verdict is `UNVERIFIABLE`, persisted as `verification-error`; verifier failure is never converted to `DISAGREE` or `burden-not-met`. A live/external claim without `evidenceArtifacts` remains schema-valid, but a verifier that needs the missing artifact and cannot independently access the source MUST answer `UNVERIFIABLE` with a non-empty explanation.
|
|
203
|
+
A `disagree` with `disagreeBasis == null` is a contract violation in adversarial mode — every refutation must state which of the two grounds it rests on. **Enforced:** `_validate_adversarial_disagree_carries_a_basis` in `validators/validate-run.py` fails such a vote when `config.adversarial` is set; non-adversarial rounds are out of scope because their verdict semantics differ. Bare "I disagree" without re-inspection is not allowed. If capability, credential, network, or service state prevents that inspection, the verdict is `UNVERIFIABLE`, persisted as `verification-error`; verifier failure is never converted to `DISAGREE` or `burden-not-met`. A live/external claim without `evidenceArtifacts` remains schema-valid, but a verifier that needs the missing artifact and cannot independently access the source MUST answer `UNVERIFIABLE` with a non-empty explanation.
|
|
198
204
|
|
|
199
205
|
### Adversarial classification (replaces the §"Convergence Algorithm" per-round classifier when `adversarial == true`)
|
|
200
206
|
|
|
@@ -212,7 +218,7 @@ ELIF all_others_disagree:
|
|
|
212
218
|
resolve F as "worker-unique" # only the discoverer still holds it
|
|
213
219
|
ELIF len(hard_refutes) >= 1:
|
|
214
220
|
# an evidence-backed refute exists and the roster is split → the claim is disputed
|
|
215
|
-
|
|
221
|
+
resolve F as "contested" IN THIS ROUND; F leaves the queue
|
|
216
222
|
ELIF burden-not-met disagrees are a majority of non-error votes (per the Majority definition in the Convergence Algorithm section):
|
|
217
223
|
carry F forward; at the LAST executed round classify it "contested"
|
|
218
224
|
ELSE:
|
|
@@ -220,12 +226,14 @@ ELSE:
|
|
|
220
226
|
resolve F as "partial-consensus"
|
|
221
227
|
```
|
|
222
228
|
|
|
223
|
-
`contested`
|
|
229
|
+
`contested` stays terminal (per §"Scope and Terminology") and is reached by two routes. A finding split on `burden-not-met` doubt alone is carried forward through intermediate rounds and labelled `contested` at the last executed round; a finding carrying a `counter-evidence` hard refute is labelled `contested` in the round that refute lands and leaves the queue there. For `requirements-discovery` (`effectiveMaxRounds = 1`) the two routes coincide — the single round IS the last round. The final-classifier block of §"Convergence Algorithm" honours this: its first branch classifies an adversarially carried-forward finding `contested` regardless of the AGREE tally, so the two sections cannot assign the same finding different labels.
|
|
224
230
|
|
|
225
|
-
Design intent: one `counter-evidence` refute denies a claim consensus (it cannot rise above `contested` however many others AGREE)
|
|
231
|
+
Design intent: one `counter-evidence` refute denies a claim consensus (it cannot rise above `contested` however many others AGREE). Because that refute is permanent, re-dispatching the finding buys nothing — the verifier gets no new information and re-casts the same refute, so the round is spent by construction. The refute therefore settles the finding where it lands, and the finding is reported under `## 6.2 Differences` exactly as a last-round `contested` finding is. A lone `burden-not-met` doubt does not sink an otherwise-surviving claim — only a majority of them does; that doubt IS resolvable by another round, so it still carries forward. When every non-discoverer refutes (all_others_disagree) the finding is worker-unique regardless of refute basis — only the discoverer still holds it. A caveat is weighed the same way a weak doubt is: with zero disagrees, SUPPLEMENT lands partial-consensus only when caveats are a **majority** of the non-error votes. A single verifier's scope note does not by itself deny a claim the rest of the roster passed cleanly — the caveat is still recorded in the dissent log either way, so the majority rule changes the label, never the record. (The collaborative classifier is more permissive still: there SUPPLEMENT counts as full agreement at any count.)
|
|
226
232
|
|
|
227
233
|
## Re-verification Dispatch
|
|
228
234
|
|
|
235
|
+
Every finding re-verification, coverage critic, acceptance critic, and critic-verification instruction passes [okstra-lead-contract](./okstra-lead-contract.md) "Worker instruction quality gate" before materialization. The lead resolves exact finding IDs, evidence paths, requested verdict fields, allowed verdict literals, and completion conditions from the current convergence state and cited results; a worker is not asked to infer them from a round summary.
|
|
236
|
+
|
|
229
237
|
### Invocation materialization gate (BLOCKING)
|
|
230
238
|
|
|
231
239
|
For every finding reverify row and critic-gap verification row, first write a
|
|
@@ -273,7 +281,7 @@ call specification; it does not prove which bytes the host primitive delivered.
|
|
|
273
281
|
|
|
274
282
|
### Sponsorship Optimization
|
|
275
283
|
|
|
276
|
-
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.
|
|
284
|
+
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. **Enforced (membership only):** `_validate_reverify_prompt_matches_plan` in `validators/validate-run.py` replays `plan == prompt` as a set — it fails an added or dropped finding. Order is not machine-checked; the engine row is the order of record. This excludes Section 6, every resolved finding, and every finding owned by the receiving origin worker because none can appear in the engine row. **Ownership is compared by `sourceRoleExecutionRef`, not by `participantRef`** — a worker sharing the origin's provider and model in a *different* role is a different role contract, a different duty and a different session, so it stays in the panel (ADR-0017; the same doctrine §"Critic gaps" states for critics). **Enforced:** `_worker_is_independent_from_finding` in `scripts/okstra_ctl/convergence_engine.py`. 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.
|
|
277
285
|
|
|
278
286
|
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.
|
|
279
287
|
|
|
@@ -304,7 +312,7 @@ The two error-path anchors carry the same absolute values the lead forwarded in
|
|
|
304
312
|
|
|
305
313
|
Relative to the Phase 4 anchor set rendered by `okstra_ctl.worker_prompt_headers.worker_prompt_headers()`, a reverify prompt drops two anchors whose targets lightweight mode never reads: `**Worker Preamble Path:**` and `**Worker Error Contract Path:**`.
|
|
306
314
|
|
|
307
|
-
**Where the composer's sections go.** `okstra agent-prompt materialize` (§"Invocation materialization gate") writes the dispatched body itself, as: these anchors, then the model-assignment block
|
|
315
|
+
**Where the composer's sections go.** `okstra agent-prompt materialize` (§"Invocation materialization gate") writes the dispatched body itself, as: these anchors, then the model-assignment block (`**Provider:**`, `**Model execution value:**`, `**Runner:**`, `**Host runtime:**`, and `**Host model value:**` for a native host), then `## Duty Contract`, then `## Task Instructions` followed verbatim by the task-instructions file the lead wrote. The composed prompt carries exactly one `**Model:**` header. Materialization preserves a lead-authored `**Model:** <role>, <modelExecutionValue>` line and omits its own model line in that case; when the task instructions have no model line, materialization writes `**Model:** <modelExecutionValue>` in the assignment block. The lead counts the composed document rather than assuming that both forms may coexist.
|
|
308
316
|
|
|
309
317
|
For an `antigravity` assignment, append the exact `PLAIN_FILE_WRITE_HEADER`
|
|
310
318
|
value from `okstra_ctl.worker_prompt_headers` immediately after
|
|
@@ -327,6 +335,15 @@ The task-instructions file the lead writes MUST open with this block, before any
|
|
|
327
335
|
<active-run-context workflow.forbiddenActions, verbatim>
|
|
328
336
|
```
|
|
329
337
|
|
|
338
|
+
Nothing may sit between the forbidden-actions text and the file's first `##`
|
|
339
|
+
heading — not even the prompt's own opening sentence. The block's value is read
|
|
340
|
+
from the line after `**Forbidden actions:**` up to the next `## ` heading or
|
|
341
|
+
`**X:**` header, so any line placed in that gap is folded into the value and the
|
|
342
|
+
exact-match check fails while the text is plainly correct. This is why each
|
|
343
|
+
prompt-body example below opens at `## Instructions` and states its round line
|
|
344
|
+
inside that section. **Enforced:** `_section_values` in
|
|
345
|
+
`scripts/okstra_ctl/worker_prompt_contract.py`.
|
|
346
|
+
|
|
330
347
|
This is the same placement `okstra_ctl.worker_prompt_body` uses for an initial
|
|
331
348
|
Phase 4 prompt, and it is where the checks look: `validate_reverify_prompt()`
|
|
332
349
|
reads the region after `## Task Instructions`, so a `**Model:**` line left in
|
|
@@ -357,6 +374,8 @@ If none of the three is available, **abort the reverify dispatch for that role**
|
|
|
357
374
|
Every lightweight, adversarial, full-reanalysis, and plan-body reverify prompt
|
|
358
375
|
MUST append this block verbatim after its variant-specific response format:
|
|
359
376
|
|
|
377
|
+
**Enforced:** `okstra_ctl.worker_prompt_contract.validate_reverify_prompt` runs at dispatch (`dispatch_state.py`) and rejects a prompt without this block, or with the heading but a missing clause. It matches the three clauses by their distinctive tokens rather than byte-for-byte — a hand-copied block's whitespace must not decide whether a round runs.
|
|
378
|
+
|
|
360
379
|
```markdown
|
|
361
380
|
## Output Contract
|
|
362
381
|
|
|
@@ -369,6 +388,8 @@ MUST append this block verbatim after its variant-specific response format:
|
|
|
369
388
|
|
|
370
389
|
Reverify prompts MUST NOT inject the Phase 2 `[Required reading]` clause:
|
|
371
390
|
|
|
391
|
+
**Enforced:** `_validate_reverify_prompt_suppresses_required_reading` in `validators/validate-run.py` fails any `*-reverify-r*.md` prompt containing the clause.
|
|
392
|
+
|
|
372
393
|
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.
|
|
373
394
|
|
|
374
395
|
- **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.
|
|
@@ -379,10 +400,10 @@ This is the single largest avoidable cost in `requirements-discovery`, `error-an
|
|
|
379
400
|
### Lightweight Re-verification Prompt
|
|
380
401
|
|
|
381
402
|
```
|
|
382
|
-
Perform re-verification for <task-key> (round <N>).
|
|
383
|
-
|
|
384
403
|
## Instructions
|
|
385
404
|
|
|
405
|
+
Perform re-verification for <task-key> (round <N>).
|
|
406
|
+
|
|
386
407
|
Review the following findings discovered by other workers.
|
|
387
408
|
For EACH finding, respond with exactly one verdict:
|
|
388
409
|
|
|
@@ -421,10 +442,10 @@ For each finding, respond as:
|
|
|
421
442
|
Used instead of the lightweight/full-reanalysis prompt when `config.adversarial == true`. The required anchor headers (§"Required reverify-prompt anchor headers") are identical. The `[Required reading]` clause is suppressed; only the cited-evidence paths of the items under attack are injected (see §"Adversarial Verification Mode" → Scoped full-reanalysis).
|
|
422
443
|
|
|
423
444
|
```
|
|
424
|
-
Perform ADVERSARIAL re-verification for <task-key> (round <N>).
|
|
425
|
-
|
|
426
445
|
## Instructions
|
|
427
446
|
|
|
447
|
+
Perform ADVERSARIAL re-verification for <task-key> (round <N>).
|
|
448
|
+
|
|
428
449
|
Your job is to BREAK each finding below, not to confirm it. For EACH finding,
|
|
429
450
|
open the cited evidence directly and actively search for evidence that the claim
|
|
430
451
|
is wrong, overstated, or unproven. Then respond with exactly one verdict:
|
|
@@ -472,10 +493,10 @@ UNVERIFIABLE is **not** `verification-error`. A verifier that opened the evidenc
|
|
|
472
493
|
### Full Re-analysis Re-verification Prompt
|
|
473
494
|
|
|
474
495
|
```
|
|
475
|
-
Perform deep re-verification for <task-key> (round <N>).
|
|
476
|
-
|
|
477
496
|
## Instructions
|
|
478
497
|
|
|
498
|
+
Perform deep re-verification for <task-key> (round <N>).
|
|
499
|
+
|
|
479
500
|
Independently verify the following findings by examining the original materials.
|
|
480
501
|
Use each finding as a starting point, NOT as a confirmed conclusion.
|
|
481
502
|
If capability, credential, network, or service state prevents access to evidence
|
|
@@ -577,13 +598,13 @@ Save it to `runs/<task-type>/state/convergence-<task-type>-<seq>.json`.
|
|
|
577
598
|
|
|
578
599
|
Schema rules:
|
|
579
600
|
|
|
580
|
-
- `schemaVersion`: literal string `"1.
|
|
601
|
+
- `schemaVersion`: literal string `"1.4"` for all new runs — both adversarial and collaborative. Historical readers accept `"1.0"` / `"1.1"` / `"1.2"` / `"1.3"` unchanged and never rewrite those artifacts during validation. v1.3 added the strict coverage-critic ledger and the rejection of unknown top-level fields; v1.4 adds the `unverified` classification and its `finalClassificationCounts.unverified` key. Work-state remains v1.0.
|
|
581
602
|
- `config.adversarial`: boolean. `true` when this run used adversarial verification (default for `requirements-discovery` / `error-analysis` / `implementation-option-selection` / `implementation-planning` / `project-analysis` / `feature-analysis` / `change-impact-analysis`). When `true`, `config.verificationMode` is `"full-reanalysis"` (scoped) and every `disagree` vote carries a non-null `disagreeBasis`.
|
|
582
|
-
- `config.effectiveMaxRounds`: the integer the lead actually used after resolving the phase-aware default (`1` for `requirements-discovery`, `2` otherwise).
|
|
603
|
+
- `config.effectiveMaxRounds`: the integer the lead actually used after resolving the phase-aware default (`1` for `requirements-discovery`, `2` otherwise). It may be lower than `config.maxRounds` — a phase-aware default that resolves below the manifest ceiling is the normal case — but never higher. **Enforced:** `scripts/okstra_ctl/convergence_engine.py` `_parse_config` raises `ConvergenceContractError` on `effectiveMaxRounds > maxRounds`, so a state carrying that pair cannot be loaded, and `plan_next_round` finalizes with reason `max-rounds-reached` once the executed rounds reach the effective budget rather than dispatching another one. `totalRounds` is therefore bounded by construction, not by a later re-count.
|
|
583
604
|
- `findings[].ticketIds`: array of ticket keys from Phase 4 grouping (parsed per the Round 0 step 5 rule). It is empty when the phase does not require ticket tagging; `"unknown"` is not a ticket key and must not be synthesized.
|
|
584
605
|
- `findings[].rounds[].votes.<worker>.verdict`: enum, one of `agree | disagree | supplement | verification-error`. Lower-case tokens; map upper-case AGREE/DISAGREE/SUPPLEMENT verdicts emitted by workers to their lower-case form and map the input alias `unverifiable` to persisted `verification-error`. The latter represents either a terminal non-result dispatch or a completed dispatch that could not verify a particular finding (§"Worker failure handling in reverify"). Every vote has a non-empty `explanation`.
|
|
585
606
|
- `findings[].rounds[].votes.<worker>.disagreeBasis`: enum `counter-evidence | burden-not-met | null`. Non-null only when `verdict == "disagree"` AND `config.adversarial == true`; `null` (or absent, treated as null) otherwise. See §"Adversarial Verification Mode".
|
|
586
|
-
- `findings[].classification`: enum, one of `full-consensus | partial-consensus | worker-unique | contested`. No other value is permitted.
|
|
607
|
+
- `findings[].classification`: enum, one of `full-consensus | partial-consensus | worker-unique | contested | unverified`. No other value is permitted. `unverified` exists from final schema v1.4 onward; a historical v1.0-v1.3 artifact read under `reuse-final` uses the four-value vocabulary and MUST NOT be rewritten to add it.
|
|
587
608
|
- `roundHistory[].inputQueueSize`: queue size at the start of this round.
|
|
588
609
|
- `roundHistory[].resolvedCount`: number of findings that exited the queue this round (sum of full+partial+worker-unique classifications produced this round).
|
|
589
610
|
- `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.
|
|
@@ -596,17 +617,20 @@ Schema rules:
|
|
|
596
617
|
|
|
597
618
|
## Coverage critic pass
|
|
598
619
|
|
|
599
|
-
Runs when `convergence.critic.enabled == true`. Critic is
|
|
620
|
+
Runs when `convergence.critic.enabled == true`. Critic is opt-in on `requirements-discovery`, `error-analysis`, `implementation-planning`, and `final-verification` (role `min` 0, `recommended`/`max` 1; the user chooses whether to add the slot and picks the model). A run without a critic slot renders `enabled: false` and skips this pass. For `final-verification` the critic runs in a different mode — see §"Acceptance critic pass (final-verification)". This pass targets **scope in both directions** — findings that are missing (coverage) and work the findings propose that no requirement asked for (over-scope) — distinct from convergence, which targets **agreement quality** among the findings already raised. The pass keeps its `coverage` mode id and `gaps` vocabulary for both halves; the two are told apart by each candidate's `category`, so no schema or reducer distinguishes them. In `implementation-planning` the same critic slot also settles plan-body analyser 1-1 splits as `critic-worker`.
|
|
600
621
|
|
|
601
622
|
### When
|
|
602
623
|
|
|
603
|
-
The critic input is the Round 0 consolidated finding list. Reverify rounds only classify findings — they never add or remove them (in-round queue insertions are forbidden, see §"Convergence State Artifact" `carriedForwardCount`) — so the critic dispatch MUST NOT wait for classification to finish:
|
|
624
|
+
The critic input is the Round 0 consolidated finding list. Reverify rounds only classify findings — they never add or remove them (in-round queue insertions are forbidden, see §"Convergence State Artifact" `carriedForwardCount`) — **Enforced:** `_validate_no_in_round_queue_insertion` in `validators/validate-run.py` fails a finding whose earliest `rounds[].round` is greater than 1 — so the critic dispatch MUST NOT wait for classification to finish:
|
|
604
625
|
|
|
605
626
|
- **Dispatch**: immediately after Round 0 grouping, CONCURRENTLY with the first reverify round's dispatches. When the verification queue is empty after Round 0 (no reverify round runs), dispatch right after grouping. Concurrent dispatch to the same provider is safe — the critic result path (`<provider>-worker-critic-...`) never collides with a reverify result path.
|
|
606
627
|
- **Gap verification + merge**: only after BOTH the finding-convergence loop has exited AND the critic result is collected, and BEFORE the Phase 6 report-writer dispatch. If the loop exited `aborted-non-result`, do NOT dispatch a gap-verification round — record every gap in `unverifiedGaps[]` per §"Gap verification".
|
|
607
628
|
|
|
608
629
|
### Dispatch (fresh one-shot)
|
|
609
|
-
|
|
630
|
+
Render the critic-only task instructions with `okstra convergence critic-prompt
|
|
631
|
+
--run-manifest <run-manifest>` and write that output to the instructions file
|
|
632
|
+
verbatim — the same pattern as `okstra plan-items prompt` at round 1. Then run
|
|
633
|
+
`okstra agent-prompt
|
|
610
634
|
materialize` with `--audience scope-critic`, `--assignment-ref critic/scope`,
|
|
611
635
|
the critic worker ID, and `--dispatch-kind critic`. Verify the returned
|
|
612
636
|
`metadataPath` before dispatch and use its `promptPath` without modification.
|
|
@@ -619,33 +643,37 @@ persisted assignment or either model value required by its runner is absent,
|
|
|
619
643
|
record `critic-skipped: model-unresolved`; never resolve a replacement model.
|
|
620
644
|
Result path: `runs/<task-type>/worker-results/<provider>-worker-critic-<task-type>-<seq>.md`.
|
|
621
645
|
|
|
622
|
-
**What the critic task-instructions file
|
|
623
|
-
|
|
646
|
+
**What the generated critic task-instructions file contains.** A critic dispatch
|
|
647
|
+
is not a reverify dispatch: `dispatchKind = "critic"` keeps
|
|
624
648
|
`audience = "analysis"`, so `worker_prompt_contract.validate_initial_prompts`
|
|
625
649
|
judges it by the full initial-analysis contract. Two of those requirements are
|
|
626
650
|
satisfied by the generated body for a Phase 4 worker
|
|
627
|
-
(`okstra_ctl.worker_prompt_body`) and by nothing
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
651
|
+
(`okstra_ctl.worker_prompt_body`) and by nothing in the materializer's anchor
|
|
652
|
+
block for a critic — `critic-prompt` emits both itself: the
|
|
653
|
+
`**Prompt Delivery Mode:** eager-include` header, and under `## Inputs` exactly
|
|
654
|
+
one `Primary analysis packet` line whose backticked path ends in
|
|
655
|
+
`analysis-packet.md`, read from the run manifest's `analysisPacketPath` (the
|
|
656
|
+
command fails when that field is missing, rather than emitting a body the
|
|
657
|
+
dispatch will reject). The rest of the body is:
|
|
658
|
+
|
|
659
|
+
- the Round 0 consolidated finding list, from the run's published grouping;
|
|
660
|
+
- one line per Phase 4 analyser — worker id, its result path, and the finding
|
|
661
|
+
ids that worker sourced — so "open the named result" points at a real file;
|
|
662
|
+
- when the task already has an implementation-planning report on disk (a rerun),
|
|
663
|
+
an **already-covered** index from it: requirement-coverage row ids,
|
|
664
|
+
clarification row ids, and stage titles. Ids and titles only, never body text;
|
|
665
|
+
- the two mandates below, including the `duplicateOf` declaration rule.
|
|
666
|
+
|
|
667
|
+
The lead writes none of it and edits none of it. **Enforced:**
|
|
668
|
+
`tests/contract/test_critic_prompt_equality_exemption.py`
|
|
669
|
+
`test_generated_critic_seed_satisfies_the_real_dispatch_checks` runs the
|
|
670
|
+
generated body through the same two checks the dispatch runs. A hand-edited file
|
|
671
|
+
that drops either line fails `okstra team dispatch --dispatch-kind critic`
|
|
672
|
+
before any process starts, reported as `<task-type> prompt contract: <worker>:
|
|
673
|
+
exactly one Primary analysis packet path is required (found 0)` and `exactly one
|
|
674
|
+
non-empty **Prompt Delivery Mode:** header is required`. Re-render and
|
|
675
|
+
re-materialize with `--replace-undispatched` (§"Invocation materialization
|
|
676
|
+
gate") rather than editing the published prompt.
|
|
649
677
|
|
|
650
678
|
The `-worker-` token is load-bearing, not decoration: the critic prompt carries the same generated anchor headers as every other worker ([team-contract](./team-contract.md) §"Worker prompts"), and its `**Audit sidecar path:**` comes from passing that result path through `okstra_ctl.worker_artifact_paths.audit_sidecar_rel()`, which inserts `-audit-` after the token and raises without it. A `<provider>-critic-...` name leaves the lead choosing between breaking the contract and hand-inventing the sidecar name. Note that `originWorker` stays `"<provider>-critic"` — that is a worker id in the convergence state, not a filename, and the two do not have to match.
|
|
651
679
|
|
|
@@ -661,35 +689,16 @@ Required reading before proposing a gap or an over-scope candidate:
|
|
|
661
689
|
|
|
662
690
|
Operational guardrails are not task requirements. A gap must trace to a brief requirement, an analysis-packet scope item, a source path the packet authorizes, or an evidence claim in a worker result. Do NOT infer missing verification from a one-line summary; open the named result and audit sidecar first.
|
|
663
691
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
-
|
|
671
|
-
- claims raised but never verified.
|
|
672
|
-
For each, emit a NEW finding with evidence (file:line or the requirement quote).
|
|
673
|
-
|
|
674
|
-
(2) UNREQUESTED — name work these findings propose that no requirement asked for:
|
|
675
|
-
- a finding whose proposed change serves no requirement, scope item, or
|
|
676
|
-
acceptance point you can QUOTE from the analysis packet,
|
|
677
|
-
- an abstraction, configuration knob, or generalization proposed for a caller or
|
|
678
|
-
a case nobody has stated,
|
|
679
|
-
- a rewrite, migration, or cleanup of code the requirements never mention.
|
|
680
|
-
For each, emit a candidate with `category: "unrequested-scope"`, quote the
|
|
681
|
-
proposed work verbatim, and state which requirement you searched for and did not
|
|
682
|
-
find.
|
|
683
|
-
|
|
684
|
-
Do NOT restate an existing finding. Judge (2) against the analysis packet's
|
|
685
|
-
requirements and scope, never against your own preference for how the code should
|
|
686
|
-
look — "I would have done it differently" is not unrequested work, and neither is
|
|
687
|
-
work the packet authorizes but you consider unnecessary. If a half has nothing,
|
|
688
|
-
say so explicitly for that half; silence on one half is an incomplete result.
|
|
689
|
-
```
|
|
692
|
+
The two mandates and the `duplicateOf` rule live in the generator
|
|
693
|
+
(`okstra_ctl/convergence_critic_prompt.py` `_MANDATES`), not here. There is no
|
|
694
|
+
second copy to keep in step: `critic-prompt` renders that text under `## Mandate`
|
|
695
|
+
above the Round 0 list, and the lead pastes the output whole.
|
|
696
|
+
|
|
697
|
+
**A candidate that overlaps an existing finding is declared, not restated.** The
|
|
698
|
+
critic sets `duplicateOf` to that finding's id (`schemas/convergence-critic-results-v1.0.schema.json` `$defs.Candidate`). A restated finding costs a whole gap-verification round to reject; a declared duplicate costs none — it is recorded in the ledger, never dispatched, and counted in `config.critic.gapsDuplicate`. **Enforced:** `okstra convergence apply-critic-gaps` rejects a `duplicateOf` that names no finding the state carries, and rejects a duplicate row carrying votes.
|
|
690
699
|
|
|
691
700
|
### Gap verification (1 adversarial reverify round)
|
|
692
|
-
Each critic gap enters the verification queue as a finding with `originWorker = "<provider>-critic"` and `source = "critic"
|
|
701
|
+
Each critic gap enters the verification queue as a finding with `originWorker = "<provider>-critic"` and `source = "critic"`, except a gap the critic declared `duplicateOf` — that one is recorded and never dispatched. The lead runs ONE adversarial reverify round (§"Adversarial Verification Mode" classifier) in which **each gap is verified by exactly one Phase 4 analyser**: walk the analyser roster in `criticVerification.analyserRoster` order and assign gap *i* to `roster[i % len(roster)]`, then dispatch each assigned analyser once with its own gaps. Rejecting a gap costs the same as accepting one and this round is off the books (`rounds: []`, no `roundHistory` entry) on the serial path, so a batch of two gaps no longer wakes four analysers. **Enforced:** `okstra_ctl.convergence_engine._critic_gap_coverage_errors` accepts a dispatch set that is either the assignee set or the full roster, and rejects anything else — the full roster stays valid because a run finished before this rule cannot say which shape it used, the same dual acceptance `_validate_round_ledger_counts` gives the two round-counting arithmetics. Choosing a critic provider that is already in the analyser roster costs nothing: the critic is a different role contract, a different duty and a different session, so an analyser is not disqualified by sharing its provider name (ADR-0017 — provider and model are not role identity, and the same model assigned to two roles gets two independent workers). The critic cannot judge its own gaps because it is not an analyser: the voter roster is `workers[]` filtered to `audience == "analysis"`, and a critic is not even representable there (the allowed values are `analysis` / `lead` / `report-writer`). `okstra apply-critic-gaps` refuses a vote from anyone outside that roster (`critic voter must be a non-critic analyser`). Only gaps classified `full-consensus` / `partial-consensus` merge into the final report findings; `contested` / `worker-unique` gaps are treated as hallucinations and dropped (recorded in the convergence state, not promoted).
|
|
693
702
|
|
|
694
703
|
**A gap that received no verdict is NOT a rejected gap (BLOCKING).** Dropping applies only to gaps the voters actually judged. A gap can also end the round *unjudged* — the verification dispatch returned a terminal non-result (`timeout`, `error`, no result file), the returned result covered only some of the gaps, or no non-critic analyser was available to vote at all. Nobody inspected those, so classifying them as hallucinations is a fabricated verdict. Each one MUST be recorded as a `## 5. Missing Information and Risks` row (`missingInformation`, `source: "critic-unverified"`) whose `risk` names the gap and the reason verification did not complete, and counted in `config.critic.gapsUnverified`. They are **not** promoted to findings (unverified) and **not** raised as `clarification` items — an unverified gap needs an analyser to verify it on the next run, not a decision from the user. Silently losing them is a contract violation: the batch that times out is exactly the batch of gaps too expensive to check, so the highest-risk items are the ones that vanish.
|
|
695
704
|
|
|
@@ -704,13 +713,13 @@ The asymmetry is deliberate and runs the opposite way from the coverage half: a
|
|
|
704
713
|
### State
|
|
705
714
|
- `convergence.critic` manifest block: `{ enabled, provider, modelExecutionValue }`.
|
|
706
715
|
- Each candidate's `category` tells the two halves apart: literal `"unrequested-scope"` for the over-scope half, any other value for a coverage gap. `schemas/convergence-critic-results-v1.0.schema.json` leaves `category` a free string, so this needs no schema or reducer change — but it also means nothing machine-checks the spelling. A misspelled category is read as a coverage gap and silently takes the drop-on-contested path.
|
|
707
|
-
- The lead passes one canonical coverage batch with `{ schemaVersion, taskKey, mode, provider, modelExecutionValue, dispatches, gaps }`; each gap carries its candidate fields plus `gapId` and `votes
|
|
708
|
-
- Convergence state artifact: merged gaps appear in `findings[]` with `source: "critic"` and `rounds: []`. The separate `criticVerification.gaps[]` ledger retains each gap's `summary`, `category`, `ticketIds`, `originEvidence`, optional `evidenceArtifacts`, classification, merge link, and
|
|
709
|
-
- `config.critic` is `{ provider, modelExecutionValue, gapsProposed, gapsMerged, gapsRejected, gapsUnverified }`, with `gapsProposed = gapsMerged + gapsRejected + gapsUnverified`. `full-consensus` / `partial-consensus` gaps merge, `contested` / `worker-unique` gaps count as rejected,
|
|
716
|
+
- The lead passes one canonical coverage batch with `{ schemaVersion, taskKey, mode, provider, modelExecutionValue, dispatches, gaps }`; each gap carries its candidate fields plus `gapId` and `votes`, or `duplicateOf` and no votes. `dispatches[]` contains exactly one row per **assigned** analyser (§"Gap verification"), even when execution did not produce a result: persist `status: timeout | error | not-run` and the elapsed `durationMs` instead of omitting that analyser. A batch that dispatched every Phase 4 analyser instead is still accepted. `apply-critic-gaps` rejects a non-terminal main queue, a dispatch set matching neither shape, duplicate analysers, unknown workers, critic dispatches/votes, votes without a completed dispatch, a `duplicateOf` naming no existing finding, and a second batch.
|
|
717
|
+
- Convergence state artifact: merged gaps appear in `findings[]` with `source: "critic"` and `rounds: []`. The separate `criticVerification.gaps[]` ledger retains each gap's `summary`, `category`, `ticketIds`, `originEvidence`, optional `evidenceArtifacts`, classification, merge link, votes, and `duplicateOf` when the critic declared one. Strict validation deterministically replays each complete critic-origin finding from that ledger; a critic batch never increments `roundHistory` or `totalRounds` and never creates a fake main round.
|
|
718
|
+
- `config.critic` is `{ provider, modelExecutionValue, gapsProposed, gapsMerged, gapsRejected, gapsUnverified, gapsDuplicate }`, with `gapsProposed = gapsMerged + gapsRejected + gapsUnverified + gapsDuplicate`. `full-consensus` / `partial-consensus` gaps merge, `contested` / `worker-unique` gaps count as rejected, gaps with no usable analyser vote appear in both the ledger and final `unverifiedGaps[]`, and a declared duplicate takes classification `duplicate` — it makes no finding and joins no other counter. A state written before `gapsDuplicate` existed omits the key and stays valid; the validator reads its absence as 0.
|
|
710
719
|
|
|
711
720
|
## Acceptance critic pass (final-verification)
|
|
712
721
|
|
|
713
|
-
The `final-verification` phase uses the same fresh one-shot `redispatch_worker` pattern and the same dispatch timing as §"Coverage critic pass" §"When" (provider + `config.critic.modelExecutionValue` from the `convergence.critic` block; critic is
|
|
722
|
+
The `final-verification` phase uses the same fresh one-shot `redispatch_worker` pattern and the same dispatch timing as §"Coverage critic pass" §"When" (provider + `config.critic.modelExecutionValue` from the `convergence.critic` block; critic is opt-in — the pass is skipped when the run resolved no critic; same model-unresolved skip rule) — the delivered work the critic inspects is likewise fixed before the reverify round starts. Only the prompt, the verification semantics, and the output sink differ — final-verification's findings are defects/blockers, so the critic acts as an **acceptance devil's advocate** (find reasons NOT to accept), and its candidate blockers are NEVER dropped (that would suppress real defects).
|
|
714
723
|
|
|
715
724
|
Before that call, write the acceptance-only task instructions and run `okstra
|
|
716
725
|
agent-prompt materialize` with `--audience acceptance-critic`,
|
|
@@ -751,13 +760,13 @@ Promoted blockers enter `## 5.8 Acceptance Blockers`; since `accepted` requires
|
|
|
751
760
|
|
|
752
761
|
### State
|
|
753
762
|
|
|
754
|
-
Critic output lives in the run's `worker-results/` directory (`runs/final-verification/worker-results/` for whole-task verification, `runs/final-verification/stage-<N>/worker-results/` for single-stage), filename `<provider>-worker-critic-final-verification-<seq>.md` (same `-worker-` token rule as §"Coverage critic pass" — the audit sidecar is derived from it). The convergence state `config.critic` summary records `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual`; v1.
|
|
763
|
+
Critic output lives in the run's `worker-results/` directory (`runs/final-verification/worker-results/` for whole-task verification, `runs/final-verification/stage-<N>/worker-results/` for single-stage), filename `<provider>-worker-critic-final-verification-<seq>.md` (same `-worker-` token rule as §"Coverage critic pass" — the audit sidecar is derived from it). The convergence state `config.critic` summary records `mode: "acceptance-devils-advocate"`, `candidatesProposed`, `confirmedBlockers`, `downgradedToResidual`; v1.4 enforces `candidatesProposed = confirmedBlockers + downgradedToResidual`, so no candidate can be silently dropped.
|
|
755
764
|
|
|
756
765
|
## Output
|
|
757
766
|
|
|
758
767
|
Information to be passed to Phase 6 after completing this contract:
|
|
759
768
|
|
|
760
|
-
- Newly finalized convergence output is schema v1.
|
|
769
|
+
- Newly finalized convergence output is schema v1.4. Under `reuse-final`, a valid historical final schema v1.0, v1.1, v1.2, or v1.3 remains consumable by the report-writer without rewrite. Either validated terminal artifact contains the classification of every finding; the report-writer consumes it and does not vote
|
|
761
770
|
- Round history and votes per worker for each finding
|
|
762
771
|
- Path to the convergence state artifact
|
|
763
772
|
- Convergence summary (count per category)
|