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
|
@@ -7,8 +7,10 @@ does not depend on a parser the lead re-writes each time.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import argparse
|
|
10
|
+
import copy
|
|
10
11
|
import importlib.util
|
|
11
12
|
import json
|
|
13
|
+
import re
|
|
12
14
|
from collections.abc import Mapping, Sequence
|
|
13
15
|
from datetime import datetime, timezone
|
|
14
16
|
from pathlib import Path
|
|
@@ -21,6 +23,7 @@ from .convergence import (
|
|
|
21
23
|
validated_run_authority,
|
|
22
24
|
)
|
|
23
25
|
from .convergence_store import write_json_atomic
|
|
26
|
+
from .incremental_carry import sync_prepared_dispatch_queue
|
|
24
27
|
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
25
28
|
from .report_finalize import task_manifest_path
|
|
26
29
|
from .plan_derivations import extract_tokens, find_derivations
|
|
@@ -30,13 +33,17 @@ from .plan_items import (
|
|
|
30
33
|
advisory_plan_body_gating,
|
|
31
34
|
content_hash,
|
|
32
35
|
correction_prompt_text,
|
|
36
|
+
critic_is_rostered,
|
|
33
37
|
critic_tie_prompt_text,
|
|
34
38
|
dispatch_item_ids,
|
|
35
39
|
extract_plan_items,
|
|
36
40
|
next_dispatch,
|
|
37
41
|
planning_stage_ledger,
|
|
38
42
|
reverify_item_ids,
|
|
43
|
+
reverify_prompt_text,
|
|
39
44
|
tie_vote_item_ids,
|
|
45
|
+
voting_analyser_keys,
|
|
46
|
+
with_response_format,
|
|
40
47
|
)
|
|
41
48
|
from .paths import RunRef
|
|
42
49
|
from .stage_ledger import build_stage_ledger
|
|
@@ -151,7 +158,9 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
151
158
|
prepare.add_argument(
|
|
152
159
|
"--state", type=Path,
|
|
153
160
|
help="previous plan-body state; when it carries verifiedContentHash "
|
|
154
|
-
"rows the dispatch queue shrinks to the self-fix reverify set"
|
|
161
|
+
"rows the dispatch queue shrinks to the self-fix reverify set, and "
|
|
162
|
+
"each queued item's recorded votes and selfFixNote are carried "
|
|
163
|
+
"into the prompt as its `**Prior round dissent**` block",
|
|
155
164
|
)
|
|
156
165
|
prepare.add_argument(
|
|
157
166
|
"--tie-vote", action="store_true",
|
|
@@ -164,7 +173,8 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
164
173
|
validate_prepared.add_argument("--run-manifest", type=Path, required=True)
|
|
165
174
|
validate_prepared.add_argument(
|
|
166
175
|
"--state", type=Path,
|
|
167
|
-
help="same previous state prepare used to shrink the dispatch queue"
|
|
176
|
+
help="same previous state prepare used to shrink the dispatch queue "
|
|
177
|
+
"and to build the prior-dissent carry; both are re-derived here",
|
|
168
178
|
)
|
|
169
179
|
validate_prepared.add_argument(
|
|
170
180
|
"--tie-vote", action="store_true",
|
|
@@ -177,7 +187,14 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
177
187
|
collect.add_argument("--result", action="append", default=[], required=True,
|
|
178
188
|
metavar="<worker-id>=<path>",
|
|
179
189
|
help="one worker's plan-verify result file (repeatable)")
|
|
180
|
-
collect.add_argument(
|
|
190
|
+
collect.add_argument(
|
|
191
|
+
"--items", type=Path, required=True,
|
|
192
|
+
metavar="<plan-items artifact>",
|
|
193
|
+
help="the plan-items artifact this round dispatched (not a list of "
|
|
194
|
+
"ids). Its `dispatchQueue` is the id set the results must answer, "
|
|
195
|
+
"so a tie round takes the `--tie-vote` artifact, not the full "
|
|
196
|
+
"round's one",
|
|
197
|
+
)
|
|
181
198
|
collect.add_argument("--output", type=Path, required=True)
|
|
182
199
|
derivations = commands.add_parser(
|
|
183
200
|
"derivations",
|
|
@@ -199,6 +216,15 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
199
216
|
help="record the stage ledger so the gate can scope itself to the "
|
|
200
217
|
"stage about to start; without it the gate does not narrow",
|
|
201
218
|
)
|
|
219
|
+
seed.add_argument(
|
|
220
|
+
"--prior-state", type=Path,
|
|
221
|
+
help="the previous run's plan-body-verification-<task-type>-<seq>.json. "
|
|
222
|
+
"A newly seeded item whose contentHash still equals that run's "
|
|
223
|
+
"verifiedContentHash inherits its verdicts and is tagged "
|
|
224
|
+
"carriedForwardFromSeq, so round 1 does not re-judge text nobody "
|
|
225
|
+
"changed. Requires --state, refuses a state file belonging to "
|
|
226
|
+
"another task, and never carries on a matching id alone",
|
|
227
|
+
)
|
|
202
228
|
apply_verdicts = commands.add_parser(
|
|
203
229
|
"apply-verdicts",
|
|
204
230
|
help="overwrite planBodyVerification.planItems[].verdicts in owned state",
|
|
@@ -239,8 +265,18 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
239
265
|
complete.add_argument("--run-manifest", type=Path, required=True)
|
|
240
266
|
complete.add_argument("--round", type=int, required=True, dest="round_number")
|
|
241
267
|
complete.add_argument("--self-fix-note", action="append", default=[], metavar="<item-id>=<markdown-file>")
|
|
242
|
-
complete.add_argument(
|
|
243
|
-
|
|
268
|
+
complete.add_argument(
|
|
269
|
+
"--self-fix-group", action="append", default=[],
|
|
270
|
+
metavar="<cause-file>=<item-id>[,<item-id>...]",
|
|
271
|
+
help="this round's cause groups; requires --self-fix-stop-reason",
|
|
272
|
+
)
|
|
273
|
+
complete.add_argument(
|
|
274
|
+
"--self-fix-stop-reason",
|
|
275
|
+
choices=("all-resolved", "no-progress", "max-rounds-reached"),
|
|
276
|
+
help="why the self-fix loop stops. Required with --self-fix-group, and "
|
|
277
|
+
"valid on its own to record a stop the round did not rewrite for; "
|
|
278
|
+
"on its own it leaves selfFixRoundsApplied and selfFixGroups alone",
|
|
279
|
+
)
|
|
244
280
|
_add_dispatch_commands(commands)
|
|
245
281
|
return parser
|
|
246
282
|
|
|
@@ -354,6 +390,8 @@ def _prepare(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
354
390
|
envelope["tieSplits"] = _tie_splits_from_state(
|
|
355
391
|
getattr(args, "state", None), envelope["dispatchQueue"],
|
|
356
392
|
)
|
|
393
|
+
else:
|
|
394
|
+
envelope.update(_reverify_carry(args, envelope["dispatchQueue"]))
|
|
357
395
|
gating = not advisory_plan_body_gating(_planning(source), envelope["items"])
|
|
358
396
|
_sync_task_manifest_gating(args.run_manifest, gating)
|
|
359
397
|
write_json_atomic(output, envelope)
|
|
@@ -506,6 +544,70 @@ def _render_payload(item_id: str, payload: object) -> list[str]:
|
|
|
506
544
|
return rows
|
|
507
545
|
|
|
508
546
|
|
|
547
|
+
# 검증 시점의 서사에는 조립 소유 필드가 없다는 계약을 검증자에게 직접 알린다.
|
|
548
|
+
# run 003 실측: critic 이 P-Prep 항목에 `designSurfaceCoverage` 처분을 요구하며
|
|
549
|
+
# DISAGREE(b) — writer 가 그 필드를 쓰면 계약 위반이라 self-fix 로도 풀 수 없는
|
|
550
|
+
# 차단이었다.
|
|
551
|
+
_PREP_OWNERSHIP_NOTE = (
|
|
552
|
+
"\nOwnership boundary: `designPreparation` / `designSurfaceCoverage` rows "
|
|
553
|
+
"are produced by report assembly after this verification, so the narrative "
|
|
554
|
+
"under review carries neither — their absence is the contract, not a "
|
|
555
|
+
"defect. Judge the trigger evidence above; do not DISAGREE because the "
|
|
556
|
+
"writer did not produce those fields.\n"
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def _step_coordinate(payload: object, field: str) -> int | None:
|
|
561
|
+
if not isinstance(payload, Mapping):
|
|
562
|
+
return None
|
|
563
|
+
value = payload.get(field)
|
|
564
|
+
if isinstance(value, int) and not isinstance(value, bool) and value >= 1:
|
|
565
|
+
return value
|
|
566
|
+
return None
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _render_prior_steps(item: Mapping[str, Any], all_items: object) -> str:
|
|
570
|
+
"""같은 stage 의 선행 step 요약. run 003 실측: step 1.1 이 만드는 스크립트를
|
|
571
|
+
1.2 검증자가 '없다'고 판정했다 — 검증 단위는 step 인데 payload 가 자기 행뿐이라
|
|
572
|
+
계획이 앞 step 에서 만드는 것을 볼 수 없었다. dispatchQueue 가 좁혀져도 선행
|
|
573
|
+
맥락은 봉투의 전체 items 에서 온다."""
|
|
574
|
+
stage = _step_coordinate(item.get("payload"), "stage")
|
|
575
|
+
step = _step_coordinate(item.get("payload"), "step")
|
|
576
|
+
if stage is None or step is None or step < 2:
|
|
577
|
+
return ""
|
|
578
|
+
priors: list[tuple[int, str]] = []
|
|
579
|
+
for other in all_items if isinstance(all_items, list) else []:
|
|
580
|
+
if not isinstance(other, Mapping):
|
|
581
|
+
continue
|
|
582
|
+
other_step = _step_coordinate(other.get("payload"), "step")
|
|
583
|
+
if (
|
|
584
|
+
other_step is None
|
|
585
|
+
or other_step >= step
|
|
586
|
+
or _step_coordinate(other.get("payload"), "stage") != stage
|
|
587
|
+
):
|
|
588
|
+
continue
|
|
589
|
+
payload = other.get("payload")
|
|
590
|
+
action = str(payload.get("action") or other.get("subject") or "").strip()
|
|
591
|
+
files = payload.get("files")
|
|
592
|
+
if isinstance(files, str) and files.strip():
|
|
593
|
+
files = [files]
|
|
594
|
+
suffix = (
|
|
595
|
+
" — files: " + ", ".join(f"`{scalar(entry)}`" for entry in files)
|
|
596
|
+
if isinstance(files, list) and files
|
|
597
|
+
else ""
|
|
598
|
+
)
|
|
599
|
+
priors.append((other_step, f"- step {other_step}: {action}{suffix}\n"))
|
|
600
|
+
if not priors:
|
|
601
|
+
return ""
|
|
602
|
+
priors.sort()
|
|
603
|
+
return (
|
|
604
|
+
"\nPrior steps in this stage (already applied when this step runs):\n"
|
|
605
|
+
+ "".join(text for _, text in priors)
|
|
606
|
+
+ "Judge this step against the state after them — a script, file, or "
|
|
607
|
+
"path a prior step creates is not missing.\n"
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
|
|
509
611
|
def _render_analyser_split(verdicts: object) -> str:
|
|
510
612
|
"""동수 항목에 이미 찍힌 분석자 표를 critic 프롬프트에 붙인다."""
|
|
511
613
|
if not isinstance(verdicts, list):
|
|
@@ -544,11 +646,147 @@ def _tie_splits_from_state(
|
|
|
544
646
|
return splits
|
|
545
647
|
|
|
546
648
|
|
|
649
|
+
_PRIOR_DISSENT_PROMPT_ANCHOR = "**Prior round dissent**"
|
|
650
|
+
_NO_SELF_FIX_NOTE = (
|
|
651
|
+
"no correction — this item's text shifted under a neighbouring rewrite"
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def _prior_vote(row: Mapping[str, Any]) -> dict[str, Any]:
|
|
656
|
+
"""직전 라운드의 한 표를 프롬프트가 렌더할 만큼만 옮긴다.
|
|
657
|
+
|
|
658
|
+
`basis` 는 워커가 적은 근거다. `explanation` 이 계약상 필수 줄이고 `note` 는
|
|
659
|
+
반증 후보라, 설명이 비어 있을 때만 후자로 내려간다. 둘 다 없으면 근거 없는
|
|
660
|
+
표이므로 빈 문자열을 남긴다 — 없는 근거를 지어내지 않는다.
|
|
661
|
+
"""
|
|
662
|
+
vote: dict[str, Any] = {
|
|
663
|
+
"worker": str(row.get("worker") or ""),
|
|
664
|
+
"verdict": str(row.get("verdict") or ""),
|
|
665
|
+
}
|
|
666
|
+
kind = str(row.get("breakageKind") or "").strip()
|
|
667
|
+
if kind:
|
|
668
|
+
vote["breakageKind"] = kind
|
|
669
|
+
fixability = str(row.get("fixability") or "").strip()
|
|
670
|
+
if fixability:
|
|
671
|
+
vote["fixability"] = fixability
|
|
672
|
+
basis = (
|
|
673
|
+
str(row.get("explanation") or "").strip()
|
|
674
|
+
or str(row.get("note") or "").strip()
|
|
675
|
+
)
|
|
676
|
+
if basis:
|
|
677
|
+
vote["basis"] = basis
|
|
678
|
+
return vote
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
def _prior_rounds_from_state(
|
|
682
|
+
state_path: Path | None, queue: Sequence[str],
|
|
683
|
+
) -> dict[str, dict[str, Any]]:
|
|
684
|
+
"""큐에 오른 항목별로 직전 라운드의 표와 자가수정 노트를 모은다.
|
|
685
|
+
|
|
686
|
+
`planBodyVerification.planItems[].verdicts[]` 는 라운드마다 덮어써지므로 지금
|
|
687
|
+
거기 남아 있는 것이 직전 라운드의 판정이다. 자가수정 노트는 감사용
|
|
688
|
+
`planItems[]` 행의 `selfFixNote` 에 본문 그대로 들어 있다.
|
|
689
|
+
|
|
690
|
+
라운드 번호가 없는 표는 어느 라운드의 것인지 알 수 없어 싣지 않는다 —
|
|
691
|
+
`apply-verdicts --round` 가 필수라 계약을 지킨 행에는 항상 번호가 있다.
|
|
692
|
+
큐 순서를 그대로 따라 담아 같은 입력이 같은 바이트를 내도록 한다.
|
|
693
|
+
"""
|
|
694
|
+
if state_path is None or not state_path.is_file():
|
|
695
|
+
return {}
|
|
696
|
+
state = _load_json_object(state_path)
|
|
697
|
+
audit = state.get("planItems")
|
|
698
|
+
notes = {
|
|
699
|
+
str(row["id"]): row["selfFixNote"].strip()
|
|
700
|
+
for row in (audit if isinstance(audit, list) else [])
|
|
701
|
+
if isinstance(row, Mapping)
|
|
702
|
+
and isinstance(row.get("id"), str)
|
|
703
|
+
and isinstance(row.get("selfFixNote"), str)
|
|
704
|
+
and row["selfFixNote"].strip()
|
|
705
|
+
}
|
|
706
|
+
voted = {
|
|
707
|
+
str(row["id"]): row
|
|
708
|
+
for row in _state_plan_body_items(state, state_path)
|
|
709
|
+
if isinstance(row, Mapping) and isinstance(row.get("id"), str)
|
|
710
|
+
}
|
|
711
|
+
prior: dict[str, dict[str, Any]] = {}
|
|
712
|
+
for item_id in queue:
|
|
713
|
+
row = voted.get(item_id)
|
|
714
|
+
if row is None:
|
|
715
|
+
continue
|
|
716
|
+
verdicts = [
|
|
717
|
+
vote for vote in (row.get("verdicts") or [])
|
|
718
|
+
if isinstance(vote, Mapping) and isinstance(vote.get("round"), int)
|
|
719
|
+
]
|
|
720
|
+
if not verdicts:
|
|
721
|
+
continue
|
|
722
|
+
last = max(int(vote["round"]) for vote in verdicts)
|
|
723
|
+
entry: dict[str, Any] = {
|
|
724
|
+
"round": last,
|
|
725
|
+
"votes": [
|
|
726
|
+
_prior_vote(vote) for vote in verdicts if int(vote["round"]) == last
|
|
727
|
+
],
|
|
728
|
+
}
|
|
729
|
+
if item_id in notes:
|
|
730
|
+
entry["selfFixNote"] = notes[item_id]
|
|
731
|
+
prior[item_id] = entry
|
|
732
|
+
return prior
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def _reverify_carry(
|
|
736
|
+
args: argparse.Namespace, queue: Sequence[str],
|
|
737
|
+
) -> dict[str, Any]:
|
|
738
|
+
"""라운드 2+ 봉투에 실을 직전 라운드 맥락. 실을 것이 없으면 빈 dict.
|
|
739
|
+
|
|
740
|
+
라운드 1 은 `--state` 없이 준비되고, `--state` 가 있어도 판정이 하나도
|
|
741
|
+
기록돼 있지 않으면 옮길 반대 의견이 없다. 두 경우 모두 봉투는 라운드 1 의
|
|
742
|
+
모양 그대로다.
|
|
743
|
+
"""
|
|
744
|
+
prior = _prior_rounds_from_state(getattr(args, "state", None), queue)
|
|
745
|
+
if not prior:
|
|
746
|
+
return {}
|
|
747
|
+
return {"dispatchKind": "reverify", "priorRounds": prior}
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def _render_prior_round(entry: object) -> str:
|
|
751
|
+
"""직전 라운드의 반대 의견을 이 항목의 블록으로 렌더한다.
|
|
752
|
+
|
|
753
|
+
앵커는 `**Prior round dissent**` 이고, 워커가 답에 적는 줄의 앵커
|
|
754
|
+
(`**Prior dissent**`, `verdict_blocks.py` 가 읽는다) 와 다른 문자열이다.
|
|
755
|
+
두 표기는 서로 다른 산출물의 서로 다른 앵커다.
|
|
756
|
+
"""
|
|
757
|
+
if not isinstance(entry, Mapping):
|
|
758
|
+
return ""
|
|
759
|
+
votes = entry.get("votes")
|
|
760
|
+
if not isinstance(votes, list) or not votes:
|
|
761
|
+
return ""
|
|
762
|
+
rows = [
|
|
763
|
+
f"{_PRIOR_DISSENT_PROMPT_ANCHOR} (round {scalar(entry.get('round'))}):\n"
|
|
764
|
+
]
|
|
765
|
+
for vote in votes:
|
|
766
|
+
if not isinstance(vote, Mapping):
|
|
767
|
+
continue
|
|
768
|
+
kind = str(vote.get("breakageKind") or "")
|
|
769
|
+
token = str(vote.get("verdict") or "")
|
|
770
|
+
if kind:
|
|
771
|
+
token = f"{token}({kind})"
|
|
772
|
+
rows.append(
|
|
773
|
+
f"- `{scalar(vote.get('worker'))}`: `{scalar(token)}` — "
|
|
774
|
+
f"`{scalar(vote.get('basis'))}`\n"
|
|
775
|
+
)
|
|
776
|
+
if vote.get("fixability"):
|
|
777
|
+
rows.append(" " + line("Fixability", vote.get("fixability")))
|
|
778
|
+
rows.append(
|
|
779
|
+
line("What the planner changed", entry.get("selfFixNote") or _NO_SELF_FIX_NOTE)
|
|
780
|
+
)
|
|
781
|
+
return "".join(rows)
|
|
782
|
+
|
|
783
|
+
|
|
547
784
|
def _prompt(args: argparse.Namespace) -> str:
|
|
548
785
|
envelope = _load_json_object(
|
|
549
786
|
_prepared_items_path(args.run_manifest, require_regular=True)
|
|
550
787
|
)
|
|
551
|
-
|
|
788
|
+
all_items = envelope.get("items") if isinstance(envelope.get("items"), list) else []
|
|
789
|
+
items = all_items
|
|
552
790
|
queue = envelope.get("dispatchQueue")
|
|
553
791
|
if isinstance(queue, list):
|
|
554
792
|
allowed = {item_id for item_id in queue if isinstance(item_id, str)}
|
|
@@ -556,6 +794,10 @@ def _prompt(args: argparse.Namespace) -> str:
|
|
|
556
794
|
item for item in items
|
|
557
795
|
if isinstance(item, Mapping) and item.get("id") in allowed
|
|
558
796
|
]
|
|
797
|
+
prior_rounds = (
|
|
798
|
+
envelope.get("priorRounds")
|
|
799
|
+
if isinstance(envelope.get("priorRounds"), Mapping) else {}
|
|
800
|
+
)
|
|
559
801
|
rows = ["# Plan verification queue\n", line("Item count", len(items))]
|
|
560
802
|
for index, item in enumerate(items, 1):
|
|
561
803
|
if not isinstance(item, Mapping):
|
|
@@ -563,7 +805,13 @@ def _prompt(args: argparse.Namespace) -> str:
|
|
|
563
805
|
item_id = str(item.get("id") or "")
|
|
564
806
|
rows.extend((f"\n## Plan item {index}\n", line("Item ID", item_id),
|
|
565
807
|
line("Subject", item.get("subject"))))
|
|
808
|
+
# 직전 반대 의견이 현재 본문보다 앞이다 — 계약이 요구하는 판단 순서가
|
|
809
|
+
# "반대 의견과 정정을 읽고 나서 지금 본문을 본다" 이기 때문이다.
|
|
810
|
+
rows.append(_render_prior_round(prior_rounds.get(item_id)))
|
|
566
811
|
rows.extend(_render_payload(item_id, item.get("payload")))
|
|
812
|
+
rows.append(_render_prior_steps(item, all_items))
|
|
813
|
+
if item_id.startswith("P-Prep-"):
|
|
814
|
+
rows.append(_PREP_OWNERSHIP_NOTE)
|
|
567
815
|
split = _render_analyser_split(
|
|
568
816
|
(envelope.get("tieSplits") or {}).get(item_id) if isinstance(
|
|
569
817
|
envelope.get("tieSplits"), Mapping
|
|
@@ -571,9 +819,11 @@ def _prompt(args: argparse.Namespace) -> str:
|
|
|
571
819
|
)
|
|
572
820
|
if split:
|
|
573
821
|
rows.append(split)
|
|
574
|
-
body = "".join(rows)
|
|
822
|
+
body = with_response_format("".join(rows))
|
|
575
823
|
if envelope.get("dispatchKind") == "critic-tie":
|
|
576
824
|
return critic_tie_prompt_text(body)
|
|
825
|
+
if envelope.get("dispatchKind") == "reverify":
|
|
826
|
+
return reverify_prompt_text(body)
|
|
577
827
|
return body
|
|
578
828
|
|
|
579
829
|
|
|
@@ -594,6 +844,17 @@ def _validate_prepared(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
594
844
|
)
|
|
595
845
|
if actual.get("dispatchQueue") != expected_queue:
|
|
596
846
|
raise PlanItemContractError("prepared dispatch queue does not match")
|
|
847
|
+
if not getattr(args, "tie_vote", False):
|
|
848
|
+
# 라운드 2+ 의 반대 의견 반입은 여기서만 검사된다. 봉투에서 빠지면
|
|
849
|
+
# 프롬프트에도 빠지고, 반대한 워커는 자기 판정을 그대로 다시 적는다.
|
|
850
|
+
expected_carry = _reverify_carry(args, expected_queue)
|
|
851
|
+
if actual.get("priorRounds") != expected_carry.get("priorRounds"):
|
|
852
|
+
raise PlanItemContractError(
|
|
853
|
+
"prepared plan items do not carry the previous round's dissent "
|
|
854
|
+
"— re-run `okstra plan-items prepare` with the same --state so "
|
|
855
|
+
"each re-dispatched item carries its `**Prior round dissent**` "
|
|
856
|
+
"block"
|
|
857
|
+
)
|
|
597
858
|
return {"ok": True, "operation": "validate-prepared", "path": str(path)}
|
|
598
859
|
|
|
599
860
|
|
|
@@ -834,6 +1095,104 @@ def _stage_ledger_snapshot(run_manifest: Path | None) -> dict[str, str] | None:
|
|
|
834
1095
|
return snapshot or None
|
|
835
1096
|
|
|
836
1097
|
|
|
1098
|
+
# `plan-body-verification-<task-type-segment>-<seq>.json`. seq 는 파일 이름에만
|
|
1099
|
+
# 있다 — 상태 본문은 자기 run 의 순번을 담지 않는다.
|
|
1100
|
+
_PRIOR_STATE_NAME_RE = re.compile(
|
|
1101
|
+
r"^plan-body-verification-.+-(?P<seq>\d{3,})\.json$"
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
class _PriorRunCarry:
|
|
1106
|
+
"""직전 run 에서 실제로 이월된 것. 이월을 요청하지 않았으면 만들지 않는다."""
|
|
1107
|
+
|
|
1108
|
+
def __init__(self, prev_seq: str, count: int) -> None:
|
|
1109
|
+
self.prev_seq = prev_seq
|
|
1110
|
+
self.count = count
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
def _state_task_root(path: Path, *, option: str) -> Path:
|
|
1114
|
+
"""``<task_root>/runs/<task-type>/state/<file>`` 의 task_root.
|
|
1115
|
+
|
|
1116
|
+
plan-body 상태 파일은 자기 안에 task 신원을 담지 않는다(`_new_v3_state` 는
|
|
1117
|
+
`schemaVersion` 과 `owner` 만 쓴다). 남은 신원은 경로뿐이므로, 이월 양쪽이
|
|
1118
|
+
같은 task 의 기록인지는 여기서만 판정할 수 있다. 정규 run 경로가 아니면
|
|
1119
|
+
판정할 근거가 없다는 뜻이고, 그때는 이월을 거절한다 — 판정 못 하는 신원을
|
|
1120
|
+
통과시키면 남의 task 판정이 이 run 의 게이트를 결정한다.
|
|
1121
|
+
"""
|
|
1122
|
+
try:
|
|
1123
|
+
return RunRef.from_run_dir(path.parent.parent).task_root
|
|
1124
|
+
except ValueError as exc:
|
|
1125
|
+
raise PlanItemContractError(
|
|
1126
|
+
f"{option} must be a plan-body state under "
|
|
1127
|
+
f"<task-root>/runs/<task-type>/state/: {path}"
|
|
1128
|
+
) from exc
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
def _carry_prior_run_verdicts(
|
|
1132
|
+
args: argparse.Namespace,
|
|
1133
|
+
added: list[dict[str, Any]],
|
|
1134
|
+
hashes: Mapping[str, str],
|
|
1135
|
+
) -> _PriorRunCarry | None:
|
|
1136
|
+
"""직전 run 이 같은 본문에 내린 판정을 새로 시드된 행에 옮긴다.
|
|
1137
|
+
|
|
1138
|
+
실패한 계획 run 을 다시 돌면 판정이 하나도 넘어오지 않았다. 이월은 답변된
|
|
1139
|
+
clarification 이 있는 경로에만 있고(`incremental_scope.decide_scope` 는
|
|
1140
|
+
답변된 `C-NNN` 이 없으면 `full` 을 되돌려 `incremental_carry` 를 도달 불가로
|
|
1141
|
+
만든다), 그래서 아무도 고치지 않은 문장이 매 재실행마다 다시 채점됐다.
|
|
1142
|
+
|
|
1143
|
+
행 모양은 `incremental_carry._stamp_carried` 와 같다 — `verdicts` 를 그대로
|
|
1144
|
+
옮기고, `carriedForwardFromSeq` 에 직전 seq 를 찍고, `verifiedContentHash` 를
|
|
1145
|
+
이번 추출 해시로 맞춘다. 그 해시가 시드의 `dispatchQueue` 계산에 그대로
|
|
1146
|
+
들어가므로(`_queue_for` → `reverify_item_ids`), 이월된 행은 라운드 1 큐에서
|
|
1147
|
+
빠진다. 큐를 따로 줄이지 않는 이유가 이것이다: incremental 경로가
|
|
1148
|
+
`incremental_carry._recompute_dispatch_queue` 에서 쓰는 것과 같은 장치다.
|
|
1149
|
+
|
|
1150
|
+
id 만 같은 행은 절대 이월하지 않는다. `P-*` id 는 위치에서 나오므로 계획이
|
|
1151
|
+
한 줄만 밀려도 같은 번호가 다른 문장을 가리킨다. 해시가 같아야 그 문장이
|
|
1152
|
+
같은 문장이다.
|
|
1153
|
+
"""
|
|
1154
|
+
prior = getattr(args, "prior_state", None)
|
|
1155
|
+
if prior is None:
|
|
1156
|
+
return None
|
|
1157
|
+
if args.state is None:
|
|
1158
|
+
raise PlanItemContractError("--prior-state requires --state")
|
|
1159
|
+
matched = _PRIOR_STATE_NAME_RE.match(prior.name)
|
|
1160
|
+
if matched is None:
|
|
1161
|
+
raise PlanItemContractError(
|
|
1162
|
+
"--prior-state must name a plan-body-verification-<task-type>-<seq>"
|
|
1163
|
+
f".json file; the seq is what a carried row is tagged with: {prior}"
|
|
1164
|
+
)
|
|
1165
|
+
if _state_task_root(prior, option="--prior-state") != _state_task_root(
|
|
1166
|
+
args.state, option="--state"
|
|
1167
|
+
):
|
|
1168
|
+
raise PlanItemContractError(
|
|
1169
|
+
f"--prior-state belongs to another task than --state: {prior}"
|
|
1170
|
+
)
|
|
1171
|
+
prev_seq = matched.group("seq")
|
|
1172
|
+
prior_rows = {
|
|
1173
|
+
str(row["id"]): row
|
|
1174
|
+
for row in _state_plan_body_items(_load_json_object(prior), prior)
|
|
1175
|
+
if isinstance(row, Mapping) and isinstance(row.get("id"), str)
|
|
1176
|
+
}
|
|
1177
|
+
count = 0
|
|
1178
|
+
for row in added:
|
|
1179
|
+
item_id = row.get("id")
|
|
1180
|
+
previous = prior_rows.get(item_id)
|
|
1181
|
+
if previous is None:
|
|
1182
|
+
continue
|
|
1183
|
+
verdicts = previous.get("verdicts")
|
|
1184
|
+
if not isinstance(verdicts, list) or not verdicts:
|
|
1185
|
+
continue
|
|
1186
|
+
current_hash = hashes.get(item_id)
|
|
1187
|
+
if not current_hash or previous.get("verifiedContentHash") != current_hash:
|
|
1188
|
+
continue
|
|
1189
|
+
row["verdicts"] = copy.deepcopy(verdicts)
|
|
1190
|
+
row["carriedForwardFromSeq"] = prev_seq
|
|
1191
|
+
row["verifiedContentHash"] = current_hash
|
|
1192
|
+
count += 1
|
|
1193
|
+
return _PriorRunCarry(prev_seq, count)
|
|
1194
|
+
|
|
1195
|
+
|
|
837
1196
|
def _seed(args: argparse.Namespace) -> dict[str, Any]:
|
|
838
1197
|
"""Create the `planBodyVerification.planItems[]` rows a round lands in.
|
|
839
1198
|
|
|
@@ -847,6 +1206,10 @@ def _seed(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
847
1206
|
Idempotent by id. An existing row keeps everything it carries — verdicts
|
|
848
1207
|
already applied, `carriedForwardFromSeq`, `selfFixNote` — because a re-seed
|
|
849
1208
|
between rounds must not erase the round before it.
|
|
1209
|
+
|
|
1210
|
+
`--prior-state` extends that to the previous *run*: a newly seeded item
|
|
1211
|
+
whose text the previous run already judged inherits its verdicts instead of
|
|
1212
|
+
entering round 1 again (`_carry_prior_run_verdicts`).
|
|
850
1213
|
"""
|
|
851
1214
|
source = _plan_source(args)
|
|
852
1215
|
extracted = _envelope(source)["items"]
|
|
@@ -887,6 +1250,7 @@ def _seed(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
887
1250
|
for item in extracted
|
|
888
1251
|
if item["id"] not in known
|
|
889
1252
|
]
|
|
1253
|
+
carried = _carry_prior_run_verdicts(args, added, hashes)
|
|
890
1254
|
recorded.extend(added)
|
|
891
1255
|
for row in recorded:
|
|
892
1256
|
item_id = row.get("id") if isinstance(row, Mapping) else None
|
|
@@ -932,13 +1296,22 @@ def _seed(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
932
1296
|
_planning(source), extracted,
|
|
933
1297
|
)
|
|
934
1298
|
write_json_atomic(target, data)
|
|
935
|
-
|
|
1299
|
+
result = {
|
|
936
1300
|
"ok": True,
|
|
937
1301
|
"operation": "seed",
|
|
938
1302
|
"path": str(target),
|
|
939
1303
|
"seeded": len(added),
|
|
940
1304
|
"existing": len(known),
|
|
941
1305
|
}
|
|
1306
|
+
if carried is not None:
|
|
1307
|
+
result["carried"] = carried.count
|
|
1308
|
+
result["carriedForwardFromSeq"] = carried.prev_seq
|
|
1309
|
+
if carried.count:
|
|
1310
|
+
# 시드는 prepare **뒤에** 돈다(리드 계약 step 1). 이월이 상태의
|
|
1311
|
+
# 큐만 줄이면 `plan-items prompt` 가 읽는 형제 파일은 여전히 전량
|
|
1312
|
+
# 이라, 워커는 직전 run 이 이미 판정한 행을 다시 받는다.
|
|
1313
|
+
sync_prepared_dispatch_queue(target, data)
|
|
1314
|
+
return result
|
|
942
1315
|
|
|
943
1316
|
|
|
944
1317
|
def _judged_row(row: Mapping[str, Any], project_root: Path | None) -> dict[str, Any]:
|
|
@@ -1061,7 +1434,14 @@ def _append_item_verdicts(
|
|
|
1061
1434
|
|
|
1062
1435
|
|
|
1063
1436
|
def _gate_module() -> Any:
|
|
1064
|
-
|
|
1437
|
+
# 부모 개수를 세면 체크아웃에서만 맞는다 — 설치본은 패키지가
|
|
1438
|
+
# `~/.okstra/lib/python/` 이라 validators 가 `~/.okstra/lib/` 아래다.
|
|
1439
|
+
from .paths import RUN_VALIDATOR_RELATIVE, find_asset_root
|
|
1440
|
+
|
|
1441
|
+
root = find_asset_root(RUN_VALIDATOR_RELATIVE)
|
|
1442
|
+
if root is None:
|
|
1443
|
+
raise PlanItemContractError("cannot locate plan-body gate authority")
|
|
1444
|
+
path = root.joinpath(*RUN_VALIDATOR_RELATIVE)
|
|
1065
1445
|
spec = importlib.util.spec_from_file_location("okstra_plan_gate", path)
|
|
1066
1446
|
if spec is None or spec.loader is None:
|
|
1067
1447
|
raise PlanItemContractError("cannot load plan-body gate authority")
|
|
@@ -1088,12 +1468,7 @@ def _round_snapshot(item: Mapping[str, Any], round_number: int) -> dict[str, Any
|
|
|
1088
1468
|
|
|
1089
1469
|
|
|
1090
1470
|
def _plan_gate_summary(verification: Mapping[str, Any]) -> dict[str, Any]:
|
|
1091
|
-
|
|
1092
|
-
spec = importlib.util.spec_from_file_location("okstra_plan_gate", path)
|
|
1093
|
-
if spec is None or spec.loader is None:
|
|
1094
|
-
raise PlanItemContractError("cannot load plan-body gate authority")
|
|
1095
|
-
module = importlib.util.module_from_spec(spec)
|
|
1096
|
-
spec.loader.exec_module(module)
|
|
1471
|
+
module = _gate_module()
|
|
1097
1472
|
summary = module.plan_body_gate_summary({
|
|
1098
1473
|
"implementationPlanning": {"planBodyVerification": dict(verification)}
|
|
1099
1474
|
})
|
|
@@ -1105,8 +1480,13 @@ def _plan_gate_summary(verification: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
1105
1480
|
def _record_self_fixes(args: argparse.Namespace, audit: list[object], verification: dict[str, Any]) -> None:
|
|
1106
1481
|
notes: dict[str, str] = {}
|
|
1107
1482
|
known = {item.get("id") for item in audit if isinstance(item, Mapping)}
|
|
1108
|
-
if args.
|
|
1109
|
-
raise PlanItemContractError(
|
|
1483
|
+
if args.self_fix_group and not args.self_fix_stop_reason:
|
|
1484
|
+
raise PlanItemContractError(
|
|
1485
|
+
"--self-fix-group requires --self-fix-stop-reason — a self-fix "
|
|
1486
|
+
"round has to say why the loop stops, and a defaulted "
|
|
1487
|
+
"`all-resolved` is the one value that later forbids promoting the "
|
|
1488
|
+
"items it left unresolved"
|
|
1489
|
+
)
|
|
1110
1490
|
for raw in args.self_fix_note:
|
|
1111
1491
|
item_id, separator, filename = raw.partition("=")
|
|
1112
1492
|
if not separator or not item_id or not filename:
|
|
@@ -1137,7 +1517,12 @@ def _record_self_fixes(args: argparse.Namespace, audit: list[object], verificati
|
|
|
1137
1517
|
if groups:
|
|
1138
1518
|
verification["selfFixGroups"] = groups
|
|
1139
1519
|
verification["selfFixRoundsApplied"] = args.round_number
|
|
1140
|
-
|
|
1520
|
+
if args.self_fix_stop_reason:
|
|
1521
|
+
# 원인 그룹 없이 중단 사유만 기록하는 라운드는 `selfFixRoundsApplied` 를
|
|
1522
|
+
# 건드리지 않는다. `validate-run.py` `_validate_self_fix_grouping` 이
|
|
1523
|
+
# `max(selfFixGroups[].round) == selfFixRoundsApplied` 를 요구하므로,
|
|
1524
|
+
# 그룹 없이 올린 숫자는 통과할 수 있는 값이 없는 상태를 만든다.
|
|
1525
|
+
verification["selfFixStopReason"] = args.self_fix_stop_reason
|
|
1141
1526
|
|
|
1142
1527
|
|
|
1143
1528
|
def _round_inputs(args: argparse.Namespace) -> tuple[dict[str, Any], list[dict[str, Any]], list[object], list[object]]:
|
|
@@ -1177,14 +1562,22 @@ def _record_audit_round(audit: list[object], snapshots: Mapping[str, dict[str, A
|
|
|
1177
1562
|
item["rounds"].append(snapshots[str(item["id"])])
|
|
1178
1563
|
|
|
1179
1564
|
|
|
1180
|
-
def _participant_counts(
|
|
1565
|
+
def _participant_counts(
|
|
1566
|
+
manifest: Mapping[str, Any],
|
|
1567
|
+
snapshots: Mapping[str, dict[str, Any]],
|
|
1568
|
+
items: Sequence[Mapping[str, Any]],
|
|
1569
|
+
) -> tuple[set[str], int, int]:
|
|
1570
|
+
"""이번 라운드의 투표자, run 전체의 분석자 수, 로스터 크기.
|
|
1571
|
+
|
|
1572
|
+
`workers` 는 uniformVerifiers 를 뽑기 위한 이번 라운드 큐의 투표자다.
|
|
1573
|
+
`voting` 은 게이트 산술의 분모이므로 라운드 큐가 아니라 run 전체를 세고,
|
|
1574
|
+
검증기와 같은 함수(`voting_analyser_keys`)를 쓴다 — 종전에는 이쪽만 라운드
|
|
1575
|
+
큐를 세서, critic 이 동수만 가른 라운드에서 기록값 0 과 검증기 재계산값이
|
|
1576
|
+
갈렸다.
|
|
1577
|
+
"""
|
|
1181
1578
|
workers = {worker for row in snapshots.values() for worker in row["votes"]}
|
|
1182
|
-
voting =
|
|
1183
|
-
|
|
1184
|
-
for worker, verdict in row["votes"].items()
|
|
1185
|
-
if verdict != "verification-error"
|
|
1186
|
-
}
|
|
1187
|
-
assignments = _load_json_object(args.run_manifest).get("workerAssignments")
|
|
1579
|
+
voting = voting_analyser_keys(items)
|
|
1580
|
+
assignments = manifest.get("workerAssignments")
|
|
1188
1581
|
if not isinstance(assignments, list):
|
|
1189
1582
|
raise PlanItemContractError("run manifest workerAssignments must be an array")
|
|
1190
1583
|
rostered = sum(1 for row in assignments if isinstance(row, Mapping)
|
|
@@ -1192,6 +1585,34 @@ def _participant_counts(args: argparse.Namespace, snapshots: Mapping[str, dict[s
|
|
|
1192
1585
|
return workers, len(voting), rostered
|
|
1193
1586
|
|
|
1194
1587
|
|
|
1588
|
+
def _reject_round_gap(history: list[Any], round_number: int) -> None:
|
|
1589
|
+
"""앞 라운드의 결과가 기록되기 전에 다음 라운드를 닫지 못하게 한다.
|
|
1590
|
+
|
|
1591
|
+
`roundCount` 는 이 인자의 최대값이라 건너뛴 라운드까지 세지만, 이력에는
|
|
1592
|
+
행이 남지 않는다. 그 불일치는 phase 끝에서 활동 건수 검증이 잡아내는데
|
|
1593
|
+
(`recorded=3` vs `rounds=4`), 그때는 라운드를 다시 닫을 방법이 없어 실행
|
|
1594
|
+
전체가 막힌다. 빠진 라운드의 판정도 그대로 사라진다 — 다음 라운드는 그것을
|
|
1595
|
+
settle 하러 존재하므로 근거 없는 표결이 된다.
|
|
1596
|
+
|
|
1597
|
+
실측(2026-08-27, `fontsninja-v3-site` `dev-10341`): 이력이 1·2·4 이고
|
|
1598
|
+
`roundCount` 가 4 였다.
|
|
1599
|
+
"""
|
|
1600
|
+
recorded = {
|
|
1601
|
+
row.get("round")
|
|
1602
|
+
for row in history
|
|
1603
|
+
if isinstance(row, Mapping) and isinstance(row.get("round"), int)
|
|
1604
|
+
}
|
|
1605
|
+
missing = [
|
|
1606
|
+
number for number in range(1, round_number) if number not in recorded
|
|
1607
|
+
]
|
|
1608
|
+
if missing:
|
|
1609
|
+
raise PlanItemContractError(
|
|
1610
|
+
f"round {round_number} cannot be completed while round(s) "
|
|
1611
|
+
f"{', '.join(str(number) for number in missing)} have no history "
|
|
1612
|
+
"entry — complete them in order so each round's verdict is recorded"
|
|
1613
|
+
)
|
|
1614
|
+
|
|
1615
|
+
|
|
1195
1616
|
def _complete_round(args: argparse.Namespace) -> dict[str, Any]:
|
|
1196
1617
|
if args.round_number < 1:
|
|
1197
1618
|
raise PlanItemContractError("--round must be 1 or greater")
|
|
@@ -1202,20 +1623,31 @@ def _complete_round(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
1202
1623
|
raise PlanItemContractError(
|
|
1203
1624
|
"advisory plan-body gating allows one verification round"
|
|
1204
1625
|
)
|
|
1205
|
-
if args.self_fix_group or args.self_fix_note:
|
|
1626
|
+
if args.self_fix_group or args.self_fix_note or args.self_fix_stop_reason:
|
|
1206
1627
|
raise PlanItemContractError(
|
|
1207
1628
|
"advisory plan-body gating forbids the self-fix loop"
|
|
1208
1629
|
)
|
|
1630
|
+
_reject_round_gap(history, args.round_number)
|
|
1209
1631
|
snapshots, summary = _round_snapshots(current, verification, args.round_number)
|
|
1210
1632
|
_record_audit_round(audit, snapshots, args.round_number)
|
|
1211
1633
|
gate = str(summary["recomputed"])
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1634
|
+
manifest = _load_json_object(args.run_manifest)
|
|
1635
|
+
workers, voting, rostered = _participant_counts(manifest, snapshots, current)
|
|
1636
|
+
# critic-tie 는 큐 일부에만 표를 남긴다. 합집합 워커를 모든 행에
|
|
1637
|
+
# 강제하면 표 없는 행에서 KeyError 가 난다. 모든 행에 같은 판정이
|
|
1638
|
+
# 있을 때만 uniform 이다.
|
|
1639
|
+
uniform = []
|
|
1640
|
+
for worker in workers:
|
|
1641
|
+
values = [
|
|
1642
|
+
row["votes"][worker]
|
|
1643
|
+
for row in snapshots.values()
|
|
1644
|
+
if worker in row["votes"]
|
|
1645
|
+
]
|
|
1646
|
+
if len(values) != len(snapshots) or len(set(values)) != 1:
|
|
1647
|
+
continue
|
|
1648
|
+
uniform.append(
|
|
1649
|
+
{"worker": worker, "verdict": values[0], "itemCount": len(values)}
|
|
1650
|
+
)
|
|
1219
1651
|
verification.update({"roundCount": max(int(verification.get("roundCount", 0)), args.round_number),
|
|
1220
1652
|
"gateResult": gate, "gateBlockedBy": summary["blockedBy"],
|
|
1221
1653
|
# 무엇이 막았는지는 `gateBlockedBy` 가, 무엇이 막지 않았고
|
|
@@ -1235,7 +1667,9 @@ def _complete_round(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
1235
1667
|
"operation": "complete-round",
|
|
1236
1668
|
"path": str(args.state),
|
|
1237
1669
|
"gateResult": gate,
|
|
1238
|
-
"nextDispatch": next_dispatch(
|
|
1670
|
+
"nextDispatch": next_dispatch(
|
|
1671
|
+
current, payloads, critic_rostered=critic_is_rostered(manifest),
|
|
1672
|
+
).as_dict(),
|
|
1239
1673
|
}
|
|
1240
1674
|
|
|
1241
1675
|
|
|
@@ -1258,10 +1692,26 @@ def _prepared_payloads(run_manifest: Path | None) -> dict[str, dict[str, Any]]:
|
|
|
1258
1692
|
}
|
|
1259
1693
|
|
|
1260
1694
|
|
|
1695
|
+
def _rostered_critic(run_manifest: Path | None) -> bool:
|
|
1696
|
+
"""매니페스트 없이 부르면 종전대로 critic 이 있다고 본다.
|
|
1697
|
+
|
|
1698
|
+
없다고 단정하면 critic 을 둔 run 이 가를 수 있는 동수까지 사용자에게
|
|
1699
|
+
넘긴다 — 그쪽이 더 나쁜 오답이다. 리드 계약은 `next-dispatch` 를
|
|
1700
|
+
`--run-manifest` 와 함께 부르도록 못박고 있다
|
|
1701
|
+
(`prompts/lead/plan-body-verification.md` §"Round protocol" step 6).
|
|
1702
|
+
"""
|
|
1703
|
+
if run_manifest is None:
|
|
1704
|
+
return True
|
|
1705
|
+
return critic_is_rostered(_load_json_object(run_manifest))
|
|
1706
|
+
|
|
1707
|
+
|
|
1261
1708
|
def _state_next_dispatch(args: argparse.Namespace) -> NextDispatch:
|
|
1262
1709
|
_data, current, _audit, _history = _round_inputs(args)
|
|
1263
|
-
|
|
1264
|
-
|
|
1710
|
+
run_manifest = getattr(args, "run_manifest", None)
|
|
1711
|
+
payloads = _prepared_payloads(run_manifest)
|
|
1712
|
+
return next_dispatch(
|
|
1713
|
+
current, payloads, critic_rostered=_rostered_critic(run_manifest),
|
|
1714
|
+
)
|
|
1265
1715
|
|
|
1266
1716
|
|
|
1267
1717
|
def _next_dispatch(args: argparse.Namespace) -> dict[str, Any]:
|