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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Validated JSON loading and atomic persistence for convergence artifacts."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import hashlib
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Any, Mapping
|
|
6
7
|
|
|
@@ -13,8 +14,6 @@ from .execution_manifest import (
|
|
|
13
14
|
)
|
|
14
15
|
from .final_report_schema import load_named_schema
|
|
15
16
|
from .json_boundary import JsonBoundaryError, load_owned_object, write_owned_object_atomic
|
|
16
|
-
from .worker_request import verifier_extra_dirs
|
|
17
|
-
from .write_policy import build_invocation_write_contract
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
DYNAMIC_VERIFIER_SOURCE_ROLES = frozenset({
|
|
@@ -107,19 +106,13 @@ def reserve_dynamic_verifier(
|
|
|
107
106
|
*,
|
|
108
107
|
input_digest: str,
|
|
109
108
|
invocation_ref: str | None = None,
|
|
110
|
-
artifact_paths: tuple[Path, ...],
|
|
111
|
-
worktree: Path | None = None,
|
|
112
109
|
) -> tuple[RoleExecution, Invocation]:
|
|
113
110
|
"""Reserve one provider-neutral verifier identity for a logical round.
|
|
114
111
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`
|
|
119
|
-
워크트리가 되어 writePolicyDigest 가 갈리고, 같은 invocationRef 가
|
|
120
|
-
`invocationRef drift` 로 거부된다 — 워크트리를 쓰는 런(implementation stage)
|
|
121
|
-
에서만 나타나고 워크트리가 없는 런(implementation-planning)에서는 안 나타나
|
|
122
|
-
버전 문제로 보이기 쉽다.
|
|
112
|
+
예약은 정체성만 잡는다. 쓰기 계약은 디스패치가 attempt 를 열 때 한 번만
|
|
113
|
+
계산해 그 attempt 행에 적는다 — 예약이 같은 값을 두 번째로 계산하던 동안,
|
|
114
|
+
두 계산의 입력(worktree)이 갈리면 같은 invocationRef 가 `invocationRef
|
|
115
|
+
drift` 로 거부됐다.
|
|
123
116
|
"""
|
|
124
117
|
if round_number < 1:
|
|
125
118
|
raise ExecutionManifestError("dynamic verifier round must be positive")
|
|
@@ -156,20 +149,6 @@ def reserve_dynamic_verifier(
|
|
|
156
149
|
raise ExecutionManifestError(
|
|
157
150
|
"dynamic verifier has no runner write capability"
|
|
158
151
|
)
|
|
159
|
-
try:
|
|
160
|
-
policy, enforcement = build_invocation_write_contract(
|
|
161
|
-
role="verifier",
|
|
162
|
-
project_root=project_root,
|
|
163
|
-
worktree=worktree,
|
|
164
|
-
artifact_paths=artifact_paths,
|
|
165
|
-
maximum_precision=capability.max_boundary_precision,
|
|
166
|
-
auxiliary_roots=verifier_extra_dirs("verifier"),
|
|
167
|
-
validated_auxiliary_roots=verifier_extra_dirs("verifier"),
|
|
168
|
-
)
|
|
169
|
-
except (OSError, ValueError) as exc:
|
|
170
|
-
raise ExecutionManifestError(
|
|
171
|
-
f"dynamic verifier write policy is invalid: {exc}"
|
|
172
|
-
) from exc
|
|
173
152
|
return Invocation(
|
|
174
153
|
invocation_ref=selected_ref,
|
|
175
154
|
participant_ref=verifier.participant_ref,
|
|
@@ -180,9 +159,6 @@ def reserve_dynamic_verifier(
|
|
|
180
159
|
dispatch_kind=f"reverify-r{round_number}",
|
|
181
160
|
round=round_number,
|
|
182
161
|
input_digest=input_digest,
|
|
183
|
-
write_policy=policy.to_payload(),
|
|
184
|
-
write_policy_digest=policy.digest,
|
|
185
|
-
write_enforcement=enforcement.to_payload(),
|
|
186
162
|
)
|
|
187
163
|
|
|
188
164
|
return reserve_derived_role_invocation(
|
|
@@ -53,6 +53,9 @@ _PLANNING_DATA_RE = re.compile(
|
|
|
53
53
|
)
|
|
54
54
|
_REQUEST_STATUSES = {"provisional", "blocked"}
|
|
55
55
|
_INPUT_DECISIONS = {"accept-draft", "modify-draft", "reject-draft", "defer"}
|
|
56
|
+
_RECORDED_REQUEST_RE = re.compile(
|
|
57
|
+
r"^design-prep-requests/design-prep-request-(?P<token>[0-9]+)-(?P<item>.+)\.md$"
|
|
58
|
+
)
|
|
56
59
|
_INPUT_FILENAME_RE = re.compile(
|
|
57
60
|
r"^design-prep-input-(?P<seq>\d+)-(?P<item>PREP-\d{3})-"
|
|
58
61
|
r"r(?P<revision>\d{3,})-(?P<input_id>[0-9a-f-]{36})\.md$"
|
|
@@ -322,12 +325,34 @@ def _request_target(
|
|
|
322
325
|
"design-prep-requests/"
|
|
323
326
|
f"design-prep-request-{planning_seq}-{item_id}.md"
|
|
324
327
|
)
|
|
325
|
-
|
|
328
|
+
request_dir = data_path.parent.parent / "design-prep-requests"
|
|
329
|
+
recorded = item.get("requestPath")
|
|
330
|
+
if recorded == expected_relative:
|
|
331
|
+
target = (data_path.parent.parent / expected_relative).resolve()
|
|
332
|
+
elif isinstance(recorded, str) and recorded.startswith("design-prep-requests/"):
|
|
333
|
+
target = (data_path.parent.parent / recorded).resolve()
|
|
334
|
+
match = _RECORDED_REQUEST_RE.fullmatch(recorded)
|
|
335
|
+
same_item = bool(match and match.group("item") == item_id)
|
|
336
|
+
stages = {
|
|
337
|
+
stage for stage in (item.get("stageRefs") or []) if isinstance(stage, int)
|
|
338
|
+
}
|
|
339
|
+
token_ok = bool(match) and int(match.group("token")) in stages
|
|
340
|
+
if target.is_file() and same_item:
|
|
341
|
+
pass
|
|
342
|
+
elif same_item and token_ok:
|
|
343
|
+
# 스냅샷은 stage 번호를 파일명에 쓴다. 파일이 아직 없어도 그 경로가
|
|
344
|
+
# 정본이다.
|
|
345
|
+
pass
|
|
346
|
+
else:
|
|
347
|
+
raise DesignPrepError(
|
|
348
|
+
f"item {item_id} must use canonical requestPath {expected_relative}"
|
|
349
|
+
)
|
|
350
|
+
else:
|
|
326
351
|
raise DesignPrepError(
|
|
327
352
|
f"item {item_id} must use canonical requestPath {expected_relative}"
|
|
328
353
|
)
|
|
329
|
-
request_dir
|
|
330
|
-
|
|
354
|
+
# resolve 하지 않은 request_dir 과 비교한다. 디렉터리가 바깥을 가리키는
|
|
355
|
+
# 심링크면 target.parent 가 바깥이 되어 탈출을 잡는다.
|
|
331
356
|
if target.parent != request_dir:
|
|
332
357
|
raise DesignPrepError(f"requestPath escapes design-prep-requests: {target}")
|
|
333
358
|
return target
|
|
@@ -358,13 +383,54 @@ def _render_request(
|
|
|
358
383
|
return target, _complete_request(body, assessment, request_content).encode("utf-8")
|
|
359
384
|
|
|
360
385
|
|
|
361
|
-
|
|
386
|
+
# 요청의 내용이 아니라 "어느 리포트가 이 요청을 실었는가" 에 딸린 프론트매터 줄.
|
|
387
|
+
# `request-content-fingerprint` 가 여기 드는 이유는 그 해시의 입력
|
|
388
|
+
# (`_render_request_body_without_fingerprints`) 이 `source-report` 줄을 포함하기
|
|
389
|
+
# 때문이다 — 리포트가 바뀌면 지문도 같이 흔들린다.
|
|
390
|
+
_REPORT_BOUND_REQUEST_FIELDS = ("source-report:", "request-content-fingerprint:")
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def _request_identity(raw: bytes) -> bytes:
|
|
394
|
+
"""발행 리포트에 딸린 줄을 뺀 요청 신원.
|
|
395
|
+
|
|
396
|
+
Stage Ledger 규칙은 `done` stage 의 본문을 다음 계획 run 으로 그대로
|
|
397
|
+
이월하도록 요구하고, 그러면 같은 PREP 항목이 매 run 마다 다시 탐지된다.
|
|
398
|
+
렌더된 요청은 `source-report` 한 줄만 달라지므로, 발행 리포트까지 신원에
|
|
399
|
+
넣으면 한 태스크의 **두 번째 계획 run 부터 조립이 항상 막힌다** — 요청
|
|
400
|
+
파일은 태스크 단위 디렉터리에 누적되고 이월은 선택이 아니기 때문이다.
|
|
401
|
+
|
|
402
|
+
내용의 동일성은 남는 것들이 그대로 덮는다: `assessment-fingerprint` 는
|
|
403
|
+
항목의 의미 필드만 해시하고(`assessment_fingerprint`), 본문 10개 절은 통째로
|
|
404
|
+
비교된다. 즉 요청이 실제로 달라지면 여기서 여전히 걸린다.
|
|
405
|
+
"""
|
|
406
|
+
try:
|
|
407
|
+
text = raw.decode("utf-8")
|
|
408
|
+
except UnicodeError:
|
|
409
|
+
# 신원을 계산할 수 없는 파일이다. 원본 바이트로 비교하게 두어 다르다고
|
|
410
|
+
# 판정되게 한다 — 읽을 수 없는 파일을 같다고 볼 근거가 없다.
|
|
411
|
+
return raw
|
|
412
|
+
return "\n".join(
|
|
413
|
+
line
|
|
414
|
+
for line in text.splitlines()
|
|
415
|
+
if not line.startswith(_REPORT_BOUND_REQUEST_FIELDS)
|
|
416
|
+
).encode("utf-8")
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _request_conflicts(target: Path, expected: bytes, conflict: str) -> str | None:
|
|
420
|
+
"""Return a one-line conflict description, or ``None`` when the file matches."""
|
|
362
421
|
try:
|
|
363
422
|
existing = target.read_bytes()
|
|
364
423
|
except OSError as exc:
|
|
365
424
|
raise DesignPrepError(f"cannot read existing request {target}: {exc}") from exc
|
|
366
|
-
if existing != expected:
|
|
367
|
-
|
|
425
|
+
if _request_identity(existing) != _request_identity(expected):
|
|
426
|
+
return f"refuses to overwrite {conflict}: {target}"
|
|
427
|
+
return None
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _require_matching_request(target: Path, expected: bytes, conflict: str) -> None:
|
|
431
|
+
reason = _request_conflicts(target, expected, conflict)
|
|
432
|
+
if reason is not None:
|
|
433
|
+
raise DesignPrepError(reason)
|
|
368
434
|
|
|
369
435
|
|
|
370
436
|
def _write_request_no_clobber(target: Path, encoded: bytes) -> None:
|
|
@@ -397,16 +463,32 @@ def materialize_design_prep_requests(plan_data_path: Path) -> list[Path]:
|
|
|
397
463
|
planning_seq = _planning_seq(data_path)
|
|
398
464
|
report_language = _report_language(data)
|
|
399
465
|
items = _design_preparation_items(data)
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
466
|
+
# 충돌은 한 항목씩 알리지 않는다. 요청 12개가 모두 옛 리포트의 파일과 부딪히면
|
|
467
|
+
# 첫 건에서 멈추는 구현은 사용자에게 12회 왕복을 시키고, 그때마다 이미 쓴
|
|
468
|
+
# 앞쪽 파일이 남아 상태가 절반만 진행된다. 전부 렌더해 전부 검사한 뒤 쓴다.
|
|
469
|
+
rendered = [
|
|
470
|
+
_render_request(
|
|
405
471
|
data_path=data_path,
|
|
406
472
|
planning_seq=planning_seq,
|
|
407
473
|
report_language=report_language,
|
|
408
474
|
item=item,
|
|
409
475
|
)
|
|
476
|
+
for item in items
|
|
477
|
+
if item.get("status") in _REQUEST_STATUSES
|
|
478
|
+
]
|
|
479
|
+
conflicts = [
|
|
480
|
+
reason
|
|
481
|
+
for target, encoded in rendered
|
|
482
|
+
if target.exists()
|
|
483
|
+
and (reason := _request_conflicts(target, encoded, "different request"))
|
|
484
|
+
]
|
|
485
|
+
if conflicts:
|
|
486
|
+
raise DesignPrepError(
|
|
487
|
+
f"{len(conflicts)} design-prep request(s) conflict with existing files: "
|
|
488
|
+
+ "; ".join(conflicts)
|
|
489
|
+
)
|
|
490
|
+
written: list[Path] = []
|
|
491
|
+
for target, encoded in rendered:
|
|
410
492
|
_write_request_no_clobber(target, encoded)
|
|
411
493
|
written.append(target)
|
|
412
494
|
return written
|
|
@@ -1367,8 +1449,22 @@ def _discover_planning_reports(project_root: Path) -> list[Path]:
|
|
|
1367
1449
|
return sorted(project_root.glob(pattern))
|
|
1368
1450
|
|
|
1369
1451
|
|
|
1452
|
+
_CLI_EPILOG = r"""Usage:
|
|
1453
|
+
okstra design-prep list [--project-root <path>] [--report <path>]
|
|
1454
|
+
okstra design-prep show --report <path> [--item PREP-001]
|
|
1455
|
+
okstra design-prep write --report <path> --item PREP-001 \
|
|
1456
|
+
--decision <accept-draft|modify-draft|reject-draft|defer> \
|
|
1457
|
+
[--overrides <json-object>] [--notes <text>] --confirmed
|
|
1458
|
+
"""
|
|
1459
|
+
_CLI_DESCRIPTION = "Review AI-prepared implementation design inputs."
|
|
1460
|
+
|
|
1461
|
+
|
|
1370
1462
|
def _build_parser() -> argparse.ArgumentParser:
|
|
1371
|
-
parser = argparse.ArgumentParser(
|
|
1463
|
+
parser = argparse.ArgumentParser(
|
|
1464
|
+
description=_CLI_DESCRIPTION,
|
|
1465
|
+
epilog=_CLI_EPILOG,
|
|
1466
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
1467
|
+
prog="okstra design-prep")
|
|
1372
1468
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
1373
1469
|
list_parser = subparsers.add_parser("list")
|
|
1374
1470
|
list_parser.add_argument("--project-root", default="")
|
|
@@ -114,8 +114,18 @@ def write_snapshot(narrative_path: Path, output_path: Path) -> dict[str, Any]:
|
|
|
114
114
|
return snapshot
|
|
115
115
|
|
|
116
116
|
|
|
117
|
+
_CLI_EPILOG = r"""Usage:
|
|
118
|
+
okstra design-snapshot --narrative <report-narrative.md> --output <design-preparation.json>
|
|
119
|
+
"""
|
|
120
|
+
_CLI_DESCRIPTION = "Write the detector-owned planning snapshot."
|
|
121
|
+
|
|
122
|
+
|
|
117
123
|
def main(argv: list[str] | None = None) -> int:
|
|
118
|
-
parser = argparse.ArgumentParser(
|
|
124
|
+
parser = argparse.ArgumentParser(
|
|
125
|
+
description=_CLI_DESCRIPTION,
|
|
126
|
+
epilog=_CLI_EPILOG,
|
|
127
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
128
|
+
prog="okstra design-snapshot")
|
|
119
129
|
parser.add_argument("--narrative", type=Path, required=True)
|
|
120
130
|
parser.add_argument("--output", type=Path, required=True)
|
|
121
131
|
args = parser.parse_args(argv)
|
|
@@ -110,6 +110,34 @@ def _phrase_matches(text: str, phrase: str) -> bool:
|
|
|
110
110
|
return re.search(rf"(?<![\w-]){re.escape(phrase)}(?![\w-])", text) is not None
|
|
111
111
|
|
|
112
112
|
|
|
113
|
+
SELECTED_DIRECTION_CONTRACT = "selected-direction"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _planned_file_structure(planning: Mapping[str, Any]) -> list[Any]:
|
|
117
|
+
"""설계 표면 탐지가 읽는 "이 계획이 건드리는 파일" 목록.
|
|
118
|
+
|
|
119
|
+
계획서에는 계약이 둘 있고 그 목록의 자리가 다르다. 선택 방향 계약은
|
|
120
|
+
`directionRealization.fileStructure`, 레거시 후보비교 계약은 추천 후보의
|
|
121
|
+
`optionCandidates[].fileStructure` 다.
|
|
122
|
+
|
|
123
|
+
분기가 이 함수 안이 아니라 호출부 한 곳(`plan_items._extract_prep_items`)에만
|
|
124
|
+
있던 동안, 같은 탐지기를 부르는 나머지 세 호출부(`design_snapshot`,
|
|
125
|
+
`report_projections`, `validate-run`)는 선택 방향 계획을 레거시 모양으로 읽고
|
|
126
|
+
"recommended option '' is missing or ambiguous" 로 실패했다. 계획은 멀쩡한데
|
|
127
|
+
최종 검증만 막혔다.
|
|
128
|
+
"""
|
|
129
|
+
if planning.get("planningContract") == SELECTED_DIRECTION_CONTRACT:
|
|
130
|
+
realization = planning.get("directionRealization")
|
|
131
|
+
if not isinstance(realization, Mapping):
|
|
132
|
+
raise DesignSurfaceError(
|
|
133
|
+
"selected-direction plan has no directionRealization object"
|
|
134
|
+
)
|
|
135
|
+
rows = realization.get("fileStructure")
|
|
136
|
+
return list(rows) if isinstance(rows, list) else []
|
|
137
|
+
rows = _selected_candidate(planning).get("fileStructure")
|
|
138
|
+
return list(rows) if isinstance(rows, list) else []
|
|
139
|
+
|
|
140
|
+
|
|
113
141
|
def _selected_candidate(planning: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
114
142
|
recommended = _normalise((planning.get("recommendedOption") or {}).get("name"))
|
|
115
143
|
candidates = [
|
|
@@ -321,8 +349,7 @@ def detect_design_surfaces(
|
|
|
321
349
|
value=step.get(field),
|
|
322
350
|
):
|
|
323
351
|
grouped.setdefault((stage_number, kind), []).append(evidence)
|
|
324
|
-
|
|
325
|
-
for row in selected.get("fileStructure") or []:
|
|
352
|
+
for row in _planned_file_structure(planning):
|
|
326
353
|
if not isinstance(row, Mapping):
|
|
327
354
|
continue
|
|
328
355
|
path = str(row.get("path") or "")
|
|
@@ -27,6 +27,7 @@ from .dispatch_state import (
|
|
|
27
27
|
load_json_object as _load_json_object,
|
|
28
28
|
link_agent_dispatch_result as _link_agent_dispatch_result,
|
|
29
29
|
missing_completion_paths as _missing_completion_paths,
|
|
30
|
+
_plan_verify_result_aliases,
|
|
30
31
|
mutate_team_state as _mutate_team_state,
|
|
31
32
|
require_string as _require_string,
|
|
32
33
|
resolve_project_path as _resolve_project_path,
|
|
@@ -54,7 +55,7 @@ from .assignment_resolver import (
|
|
|
54
55
|
from .application.open_worker import open_worker
|
|
55
56
|
from .domain.host import HostSessionContext
|
|
56
57
|
from .domain.provider import HostModelBinding, ServedModelAttestation
|
|
57
|
-
from .domain.worker_exec import SERVED_MODEL_MISMATCH_EXIT_CODE
|
|
58
|
+
from .domain.worker_exec import NO_RESULT_EXIT_CODE, SERVED_MODEL_MISMATCH_EXIT_CODE
|
|
58
59
|
from .domain.worker_runtime import (
|
|
59
60
|
SURFACE_CMUX_PANE,
|
|
60
61
|
EnvironmentBlocked,
|
|
@@ -90,7 +91,7 @@ from .initial_prompt_materialization import (
|
|
|
90
91
|
PromptDeliveryMode,
|
|
91
92
|
materialize_initial_prompts,
|
|
92
93
|
)
|
|
93
|
-
from .
|
|
94
|
+
from .agent.invocation import AgentInvocationError, materialize_retry_invocation
|
|
94
95
|
from .path_hints import hydrate_active_run_context
|
|
95
96
|
from .schema_excerpt import (
|
|
96
97
|
bundle_excerpt_path,
|
|
@@ -102,6 +103,7 @@ from .worker_audit_ledger import (
|
|
|
102
103
|
check_worker_results_audit,
|
|
103
104
|
parse_worker_result_name,
|
|
104
105
|
)
|
|
106
|
+
from .worker_artifacts import worker_provider_id
|
|
105
107
|
from .worker_prompt_body import REPORT_WRITER_WORKER_ID
|
|
106
108
|
from .worker_prompt_headers import (
|
|
107
109
|
WorkerPromptHeaderError,
|
|
@@ -121,6 +123,7 @@ from .write_policy import (
|
|
|
121
123
|
WritePolicy,
|
|
122
124
|
build_invocation_write_contract,
|
|
123
125
|
planned_paths_from_run_manifest,
|
|
126
|
+
task_root_from_run_manifest,
|
|
124
127
|
write_enforcement_from_payload,
|
|
125
128
|
write_policy_from_payload,
|
|
126
129
|
)
|
|
@@ -914,24 +917,32 @@ def _select_workers(
|
|
|
914
917
|
options,
|
|
915
918
|
)
|
|
916
919
|
supported = set(options.supported_worker_wrappers)
|
|
920
|
+
state = team_state or {}
|
|
921
|
+
|
|
922
|
+
def _wrapper_supported(worker_id: str) -> bool:
|
|
923
|
+
return _worker_provider(state, worker_id, options) in supported
|
|
924
|
+
|
|
917
925
|
selected = (
|
|
918
926
|
list(requested_workers)
|
|
919
927
|
if requested_workers
|
|
920
928
|
else [
|
|
921
929
|
worker
|
|
922
930
|
for worker in recommended
|
|
923
|
-
if worker
|
|
931
|
+
if _wrapper_supported(worker) and worker != REPORT_WRITER_WORKER_ID
|
|
924
932
|
]
|
|
925
933
|
)
|
|
926
934
|
unknown = [worker for worker in selected if worker not in recommended]
|
|
927
935
|
if unknown:
|
|
928
936
|
raise DispatchError("requested worker(s) are not in this run roster: " + ", ".join(unknown))
|
|
929
|
-
unsupported = [worker for worker in selected if
|
|
937
|
+
unsupported = [worker for worker in selected if not _wrapper_supported(worker)]
|
|
930
938
|
if unsupported:
|
|
931
939
|
allowed = ", ".join(sorted(supported))
|
|
932
940
|
raise DispatchError(
|
|
933
941
|
f"unsupported {options.unsupported_worker_label} worker(s): "
|
|
934
|
-
+ ", ".join(
|
|
942
|
+
+ ", ".join(
|
|
943
|
+
f"{worker} (provider {_worker_provider(state, worker, options)})"
|
|
944
|
+
for worker in unsupported
|
|
945
|
+
)
|
|
935
946
|
+ f" (dispatcher supports: {allowed})"
|
|
936
947
|
)
|
|
937
948
|
return selected
|
|
@@ -1162,13 +1173,11 @@ def _is_cli_wrapper_assignment(
|
|
|
1162
1173
|
options: _BuildOptions,
|
|
1163
1174
|
) -> bool:
|
|
1164
1175
|
state = _worker_state(team_state, worker_id)
|
|
1165
|
-
provider = _string_value(state.get("provider")) or _provider_for_worker(
|
|
1166
|
-
worker_id,
|
|
1167
|
-
options,
|
|
1168
|
-
)
|
|
1169
1176
|
return (
|
|
1170
1177
|
_string_value(state.get("runner")) or BACKEND_CLI_WRAPPER
|
|
1171
|
-
) == BACKEND_CLI_WRAPPER and
|
|
1178
|
+
) == BACKEND_CLI_WRAPPER and _worker_provider(
|
|
1179
|
+
team_state, worker_id, options
|
|
1180
|
+
) in options.supported_worker_wrappers
|
|
1172
1181
|
|
|
1173
1182
|
|
|
1174
1183
|
def _runner_for_worker(team_state: Mapping[str, Any], worker_id: str) -> str:
|
|
@@ -1203,6 +1212,7 @@ def _job_from_roster_worker(
|
|
|
1203
1212
|
completion_paths=_completion_paths(
|
|
1204
1213
|
fact.worker_id, result_path, manifest, project_root
|
|
1205
1214
|
),
|
|
1215
|
+
result_aliases=_plan_verify_result_aliases(result_path, manifest),
|
|
1206
1216
|
worktree_path=_worktree_path(manifest, active_context),
|
|
1207
1217
|
role=fact.role,
|
|
1208
1218
|
idle_timeout_seconds=options.idle_timeout_seconds,
|
|
@@ -1484,26 +1494,28 @@ def _persisted_write_contract(
|
|
|
1484
1494
|
if not job.has_execution_identity:
|
|
1485
1495
|
return None
|
|
1486
1496
|
manifest = read_execution_manifest(plan.manifest_path)
|
|
1487
|
-
|
|
1497
|
+
# 계약은 attempt 소유다 — 재시도는 attempt 마다 다른 프롬프트 파생 파일을
|
|
1498
|
+
# 쓰므로, invocation 으로 찾으면 1회차의 허용 경로를 2회차에 물린다.
|
|
1499
|
+
attempt = next(
|
|
1488
1500
|
(
|
|
1489
|
-
row for row in manifest.
|
|
1490
|
-
if row.invocation_ref == job.invocation_ref
|
|
1501
|
+
row for row in manifest.attempts
|
|
1502
|
+
if row.invocation_ref == job.invocation_ref and row.attempt == job.attempt
|
|
1491
1503
|
),
|
|
1492
1504
|
None,
|
|
1493
1505
|
)
|
|
1494
|
-
if
|
|
1495
|
-
raise DispatchError("worker
|
|
1506
|
+
if attempt is None:
|
|
1507
|
+
raise DispatchError("worker attempt write policy was not persisted")
|
|
1496
1508
|
try:
|
|
1497
1509
|
enforcement = write_enforcement_from_payload(
|
|
1498
|
-
|
|
1510
|
+
attempt.write_enforcement
|
|
1499
1511
|
)
|
|
1500
1512
|
policy = replace(
|
|
1501
|
-
write_policy_from_payload(
|
|
1513
|
+
write_policy_from_payload(attempt.write_policy),
|
|
1502
1514
|
maximum_boundary_precision=enforcement.boundary_precision,
|
|
1503
1515
|
)
|
|
1504
1516
|
except (TypeError, ValueError) as exc:
|
|
1505
1517
|
raise DispatchError(f"persisted worker write policy is invalid: {exc}") from exc
|
|
1506
|
-
if policy.digest !=
|
|
1518
|
+
if policy.digest != attempt.write_policy_digest:
|
|
1507
1519
|
raise DispatchError("persisted worker write policy digest changed")
|
|
1508
1520
|
return policy, enforcement
|
|
1509
1521
|
|
|
@@ -1555,9 +1567,6 @@ def _record_execution_attempt(plan: DispatchPlan, job: WorkerJob) -> None:
|
|
|
1555
1567
|
dispatch_kind=job.dispatch_kind,
|
|
1556
1568
|
round=_dispatch_round(job.dispatch_kind),
|
|
1557
1569
|
input_digest=job.prompt_digest,
|
|
1558
|
-
write_policy=write_policy.to_payload(),
|
|
1559
|
-
write_policy_digest=write_policy.digest,
|
|
1560
|
-
write_enforcement=write_enforcement.to_payload(),
|
|
1561
1570
|
),
|
|
1562
1571
|
Attempt(
|
|
1563
1572
|
invocation_ref=job.invocation_ref,
|
|
@@ -1569,6 +1578,9 @@ def _record_execution_attempt(plan: DispatchPlan, job: WorkerJob) -> None:
|
|
|
1569
1578
|
error_path=None,
|
|
1570
1579
|
change_summary={},
|
|
1571
1580
|
evidence_seal_ref=None,
|
|
1581
|
+
write_policy=write_policy.to_payload(),
|
|
1582
|
+
write_policy_digest=write_policy.digest,
|
|
1583
|
+
write_enforcement=write_enforcement.to_payload(),
|
|
1572
1584
|
),
|
|
1573
1585
|
task_key=task_key,
|
|
1574
1586
|
)
|
|
@@ -1603,6 +1615,9 @@ def _canonical_write_contract(
|
|
|
1603
1615
|
project_root=plan.project_root,
|
|
1604
1616
|
worktree=worktree,
|
|
1605
1617
|
artifact_paths=artifacts,
|
|
1618
|
+
task_root=task_root_from_run_manifest(
|
|
1619
|
+
plan.project_root, plan.manifest
|
|
1620
|
+
),
|
|
1606
1621
|
maximum_precision=capability.max_boundary_precision,
|
|
1607
1622
|
planned_paths=planned[0],
|
|
1608
1623
|
planned_paths_declared=planned[1],
|
|
@@ -2573,6 +2588,7 @@ def _dispatch_record(
|
|
|
2573
2588
|
"modelExecutionValue": job.model_execution_value,
|
|
2574
2589
|
"wrapperPath": str(job.wrapper_path),
|
|
2575
2590
|
"completionPaths": [str(path) for path in job.completion_paths],
|
|
2591
|
+
"resultAliases": [str(path) for path in job.result_aliases],
|
|
2576
2592
|
"worktreePath": job.worktree_path,
|
|
2577
2593
|
}
|
|
2578
2594
|
if job.invocation_id:
|
|
@@ -2713,12 +2729,14 @@ def _skip_reasons(
|
|
|
2713
2729
|
if worker_id not in set(selected)
|
|
2714
2730
|
}
|
|
2715
2731
|
supported = set(options.supported_worker_wrappers)
|
|
2732
|
+
state = team_state or {}
|
|
2716
2733
|
return {
|
|
2717
2734
|
worker_id: (
|
|
2718
2735
|
"skipped by worker dispatch default: worker is not supported by this dispatcher"
|
|
2719
2736
|
)
|
|
2720
2737
|
for worker_id in recommended
|
|
2721
|
-
if worker_id not in set(selected)
|
|
2738
|
+
if worker_id not in set(selected)
|
|
2739
|
+
and _worker_provider(state, worker_id, options) not in supported
|
|
2722
2740
|
}
|
|
2723
2741
|
|
|
2724
2742
|
|
|
@@ -2857,7 +2875,7 @@ def _retry_details(job: WorkerJob, attempt: int, outcome: WorkerOutcome) -> dict
|
|
|
2857
2875
|
def _outcome_from_status(record: Mapping[str, Any], status) -> WorkerOutcome:
|
|
2858
2876
|
return WorkerOutcome(
|
|
2859
2877
|
returncode=status.exit_code if status.exit_code is not None else 1,
|
|
2860
|
-
missing_completion_paths=
|
|
2878
|
+
missing_completion_paths=_record_missing_completion_paths(record),
|
|
2861
2879
|
pane_id=_string_value(record.get("paneId")),
|
|
2862
2880
|
status_sidecar_path=status.path,
|
|
2863
2881
|
timeout=status.timeout,
|
|
@@ -2925,8 +2943,43 @@ def _record_completion_paths(record: Mapping[str, Any]) -> list[Path]:
|
|
|
2925
2943
|
return [Path(path) for path in _string_list(record.get("completionPaths"))]
|
|
2926
2944
|
|
|
2927
2945
|
|
|
2946
|
+
def _record_missing_completion_paths(record: Mapping[str, Any]) -> tuple[Path, ...]:
|
|
2947
|
+
aliases = {Path(path) for path in _string_list(record.get("resultAliases"))}
|
|
2948
|
+
result_path = Path(_string_value(record.get("resultPath")))
|
|
2949
|
+
worker_result = Path(_string_value(record.get("workerResultPath")))
|
|
2950
|
+
missing: list[Path] = []
|
|
2951
|
+
for path in _record_completion_paths(record):
|
|
2952
|
+
if path.is_file():
|
|
2953
|
+
continue
|
|
2954
|
+
if path in {result_path, worker_result} and any(alias.is_file() for alias in aliases):
|
|
2955
|
+
continue
|
|
2956
|
+
missing.append(path)
|
|
2957
|
+
return tuple(missing)
|
|
2958
|
+
|
|
2959
|
+
|
|
2928
2960
|
def _should_retry(outcome: WorkerOutcome, attempt: int) -> bool:
|
|
2929
|
-
|
|
2961
|
+
"""결과가 없으면 재시도한다 — 기준은 종료 코드가 아니라 산출물이다.
|
|
2962
|
+
|
|
2963
|
+
종전에는 `returncode == 0` 을 요구했다. 그래서 시작하자마자 죽은 CLI 는
|
|
2964
|
+
재시도를 **한 번도** 못 받았고, 150초를 태우고 0 으로 끝난 CLI 만 받았다.
|
|
2965
|
+
비용 순서가 거꾸로였다.
|
|
2966
|
+
|
|
2967
|
+
실측(fontsninja-v3-site `dev-10341`, grok 워커 13회 디스패치): 세션 생성이
|
|
2968
|
+
거부돼 1.6~4.7초 만에 코드 1 로 죽은 경우가 4회다. 그중
|
|
2969
|
+
`requirements-discovery-002` 는 그것이 1차 시도였는데 재시도가 없어 분석자
|
|
2970
|
+
한 명이 그대로 사라졌고, 바로 다음 디스패치(`-003`)는 222초를 정상 수행했다.
|
|
2971
|
+
같은 명령을 지금 돌리면 동시 3회까지 전부 성공한다 — 조건은 분 단위로
|
|
2972
|
+
풀리는 일시적인 것이고, 재시도 비용은 2초다.
|
|
2973
|
+
|
|
2974
|
+
산출물이 이미 나왔으면 종료 코드가 무엇이든 재시도하지 않는다. 일이 끝났기
|
|
2975
|
+
때문이다. 타임아웃도 재시도하지 않는다 — 그것은 예산을 다 쓴 실패라
|
|
2976
|
+
재시도가 같은 예산을 한 번 더 쓴다.
|
|
2977
|
+
"""
|
|
2978
|
+
if outcome.timeout:
|
|
2979
|
+
return False
|
|
2980
|
+
if attempt >= MAX_WORKER_ATTEMPTS:
|
|
2981
|
+
return False
|
|
2982
|
+
return bool(outcome.missing_completion_paths)
|
|
2930
2983
|
|
|
2931
2984
|
|
|
2932
2985
|
def _job_for_next_attempt(job: WorkerJob) -> WorkerJob:
|
|
@@ -2946,6 +2999,8 @@ def _job_for_next_attempt(job: WorkerJob) -> WorkerJob:
|
|
|
2946
2999
|
def _failure_reason(outcome: WorkerOutcome) -> str:
|
|
2947
3000
|
if outcome.timeout:
|
|
2948
3001
|
return "worker wrapper timed out"
|
|
3002
|
+
if outcome.returncode == NO_RESULT_EXIT_CODE:
|
|
3003
|
+
return "CLI ended cleanly without emitting a result event"
|
|
2949
3004
|
if outcome.returncode != 0:
|
|
2950
3005
|
return f"wrapper exited with code {outcome.returncode}"
|
|
2951
3006
|
if outcome.missing_completion_paths:
|
|
@@ -2982,6 +3037,28 @@ def _resolve_wrapper(provider: str, workspace_root: Path, options: _BuildOptions
|
|
|
2982
3037
|
raise DispatchError(f"{script} not found (searched: {', '.join(str(c) for c in candidates)})")
|
|
2983
3038
|
|
|
2984
3039
|
|
|
3040
|
+
def _worker_provider(
|
|
3041
|
+
team_state: Mapping[str, Any],
|
|
3042
|
+
worker_id: str,
|
|
3043
|
+
options: _BuildOptions | None = None,
|
|
3044
|
+
) -> str:
|
|
3045
|
+
"""이 슬롯이 실제로 실행할 provider.
|
|
3046
|
+
|
|
3047
|
+
로스터의 worker_id 는 provider 이름이 아니다. 한 provider 가 두 역할을 맡으면
|
|
3048
|
+
`grok-implementer` · `grok-verifier` 처럼 역할 접미사가 붙기 때문이다. 지원 여부는
|
|
3049
|
+
항상 이 해석을 거친 provider 로 판정한다 — id 를 provider 집합과 직접 비교하면
|
|
3050
|
+
역할 접미사가 붙은 슬롯이 전부 미지원으로 떨어진다.
|
|
3051
|
+
"""
|
|
3052
|
+
workers = team_state.get("workers")
|
|
3053
|
+
recorded = ""
|
|
3054
|
+
if isinstance(workers, list):
|
|
3055
|
+
for worker in workers:
|
|
3056
|
+
if isinstance(worker, Mapping) and worker.get("workerId") == worker_id:
|
|
3057
|
+
recorded = _string_value(worker.get("provider"))
|
|
3058
|
+
break
|
|
3059
|
+
return recorded or _provider_for_worker(worker_id, options)
|
|
3060
|
+
|
|
3061
|
+
|
|
2985
3062
|
def _provider_for_worker(
|
|
2986
3063
|
worker_id: str,
|
|
2987
3064
|
options: _BuildOptions | None = None,
|
|
@@ -2992,7 +3069,7 @@ def _provider_for_worker(
|
|
|
2992
3069
|
return configured
|
|
2993
3070
|
if worker_id == REPORT_WRITER_WORKER_ID:
|
|
2994
3071
|
return "claude"
|
|
2995
|
-
return worker_id
|
|
3072
|
+
return worker_provider_id(worker_id)
|
|
2996
3073
|
|
|
2997
3074
|
|
|
2998
3075
|
|