okstra 0.186.6 → 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 +487 -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 +32 -7
- package/runtime/python/okstra_ctl/report_finalize.py +206 -15
- package/runtime/python/okstra_ctl/report_html/common.py +4 -9
- package/runtime/python/okstra_ctl/report_html/render.py +11 -12
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -5
- 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 +13 -26
- package/runtime/schemas/final-report-v3.0.schema.json +177 -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/html/base.template.html +13 -0
- package/runtime/templates/reports/html/i18n/en.json +19 -0
- package/runtime/templates/reports/html/i18n/ko.json +19 -0
- package/runtime/templates/reports/html/tasks/final-verification.template.html +2 -2
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +19 -9
- package/runtime/templates/reports/html/tasks/implementation.template.html +1 -1
- 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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""`okstra plan-validate` 의 프로젝션 — 승인 계획 문서의 구조 검증.
|
|
2
|
+
|
|
3
|
+
원래 `src/commands/execute/plan-validate.mts` 의 문자열 리터럴 안에 있던 코드다
|
|
4
|
+
(docs/coding-rules.md R1 위반). 로직은 그대로 옮겼다.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_CLI_EPILOG = r"""Usage:
|
|
13
|
+
okstra plan-validate <plan-path>
|
|
14
|
+
|
|
15
|
+
Output: JSON { ok: true, planPath } on success.
|
|
16
|
+
On failure: { ok: false, reason } with non-zero exit code.
|
|
17
|
+
|
|
18
|
+
Use this in flows that need to confirm a plan was approved without invoking
|
|
19
|
+
the full prepare_task_bundle pipeline.
|
|
20
|
+
"""
|
|
21
|
+
_CLI_DESCRIPTION = "Verify an approved-plan file has a recognised approval marker."
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main(argv: list[str] | None = None) -> int:
|
|
25
|
+
parser = argparse.ArgumentParser(
|
|
26
|
+
description=_CLI_DESCRIPTION,
|
|
27
|
+
epilog=_CLI_EPILOG,
|
|
28
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
29
|
+
prog="okstra plan-validate")
|
|
30
|
+
parser.add_argument("plan_path", metavar="plan-path")
|
|
31
|
+
args = parser.parse_args(argv)
|
|
32
|
+
|
|
33
|
+
# run.py 는 무거운 모듈이라 import 시점을 이 함수 안으로 미룬다 —
|
|
34
|
+
# `--help` 만 부르는 호출에 prepare 전체를 적재하지 않기 위해서다.
|
|
35
|
+
from .run import PrepareError, _validate_approved_plan
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
_validate_approved_plan(args.plan_path)
|
|
39
|
+
except PrepareError as exc:
|
|
40
|
+
print(json.dumps({
|
|
41
|
+
"ok": False, "stage": "validation",
|
|
42
|
+
"reason": str(exc), "planPath": args.plan_path,
|
|
43
|
+
}))
|
|
44
|
+
return 1
|
|
45
|
+
|
|
46
|
+
print(json.dumps({"ok": True, "planPath": args.plan_path}))
|
|
47
|
+
return 0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""`okstra plan-verify` 의 진입점.
|
|
2
|
+
|
|
3
|
+
판정 로직은 `validators/validate-run.py --section plan-body` 가 전부 갖고 있다.
|
|
4
|
+
이 모듈이 하는 일은 둘뿐이다 — 그 `--section` 값을 이 명령이 정하는 것으로
|
|
5
|
+
못박고, 자기 이름의 도움말을 갖는 것. 검증기의 파서를 그대로 보여 주면 prog 가
|
|
6
|
+
`validate-run.py` 로 나오고, 여기서 금지된 `--section` 이 선택 가능한 플래그로
|
|
7
|
+
보인다.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .report_finalize import resolve_workspace_validator
|
|
17
|
+
|
|
18
|
+
_CLI_EPILOG = r"""Usage:
|
|
19
|
+
okstra plan-verify --narrative <report-writer-narrative.md> --state <plan-body-verification.json>
|
|
20
|
+
okstra plan-verify --report <final-report-implementation-planning-<seq>.data.json>
|
|
21
|
+
|
|
22
|
+
Recomputes the gate from the convergence-owned state's `planItems[].verdicts`
|
|
23
|
+
before publication. The `--report` form reads historical v2 reports. It runs
|
|
24
|
+
every plan-body contract check that a round can be judged on its own — verdict
|
|
25
|
+
provenance, fixability, subject substance, self-fix grouping, round recording,
|
|
26
|
+
clarification matching, and the state-file round history.
|
|
27
|
+
|
|
28
|
+
Emits one JSON object:
|
|
29
|
+
|
|
30
|
+
gate.recomputed the gate value this round's votes actually support
|
|
31
|
+
gate.blockedBy the `gateBlockedBy` causes, recomputed
|
|
32
|
+
gate.blockingItems the plan items classified `majority-disagree`
|
|
33
|
+
gate.items[] per-item classification
|
|
34
|
+
failures[] blocking contract violations — exit code 2 when non-empty
|
|
35
|
+
advisories[] contract findings that are recorded, not round-blocking
|
|
36
|
+
warnings[] advisory signals (self-fix recurrence, uniform verifier)
|
|
37
|
+
|
|
38
|
+
Exit code 0 means no blocking failure, not "no finding". What blocks is the
|
|
39
|
+
allowlist in `scripts/okstra_ctl/blocking_checks.py` — the same set the finished
|
|
40
|
+
run's validation blocks on, so a finding the completed run would let pass cannot
|
|
41
|
+
hold a round open mid-loop. Everything else lands in `advisories[]` and on
|
|
42
|
+
stderr as `validate-run: advisory — ...`; record it, then continue the round.
|
|
43
|
+
|
|
44
|
+
Run this at every round boundary and record what it returns. The single-vote
|
|
45
|
+
blocking kinds, the advisory-only kinds and the P-Var / P-Rb exemptions live in
|
|
46
|
+
this one implementation — a lead that tallies the verdicts by hand instead is
|
|
47
|
+
re-deriving them per round, and a round scored wrong spends its self-fix budget
|
|
48
|
+
on the wrong items.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main(argv: list[str] | None = None) -> int:
|
|
53
|
+
parser = argparse.ArgumentParser(
|
|
54
|
+
prog="okstra plan-verify",
|
|
55
|
+
description="Score the §5.5.9 plan-body gate for one self-fix round.",
|
|
56
|
+
epilog=_CLI_EPILOG,
|
|
57
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument("--workspace-root", required=True,
|
|
60
|
+
help=argparse.SUPPRESS)
|
|
61
|
+
parser.add_argument("--narrative", default="",
|
|
62
|
+
help="the report-writer narrative this round scored")
|
|
63
|
+
parser.add_argument("--state", default="",
|
|
64
|
+
help="the convergence-owned plan-body-verification.json")
|
|
65
|
+
parser.add_argument("--report", default="",
|
|
66
|
+
help="a historical v2 final-report .data.json")
|
|
67
|
+
args = parser.parse_args(argv)
|
|
68
|
+
|
|
69
|
+
forwarded: list[str] = ["--section", "plan-body"]
|
|
70
|
+
for flag in ("narrative", "state", "report"):
|
|
71
|
+
value = getattr(args, flag)
|
|
72
|
+
if value:
|
|
73
|
+
forwarded += [f"--{flag}", value]
|
|
74
|
+
|
|
75
|
+
validator = resolve_workspace_validator(Path(args.workspace_root), "validate-run.py")
|
|
76
|
+
return subprocess.run([sys.executable, str(validator), *forwarded], check=False).returncode
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""task bundle prepare 실패 타입.
|
|
2
|
+
|
|
3
|
+
`stage_targets` 안에 있던 것을 leaf 로 뽑았다. 그 모듈의 도크스트링은 이 예외가
|
|
4
|
+
"저수준 모듈에 정의"돼 있다고 적었지만, 실제로는 944줄에 `stage_integrate` 를
|
|
5
|
+
끌어오는 모듈이었다. 그래서 예외 타입 하나가 필요한 `plan_run_root` 가
|
|
6
|
+
`stage_targets` 전체를 끌어오게 되고, 반대로 `stage_targets` 는
|
|
7
|
+
`plan_run_root` 를 함수 본문에서 지연 import 해야 했다.
|
|
8
|
+
|
|
9
|
+
`stage_targets` 와 `run` 이 이 이름을 재노출하므로 기존 호출부는 바뀌지 않는다.
|
|
10
|
+
이 모듈은 아무것도 import 하지 않는다.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PrepareError(Exception):
|
|
16
|
+
"""surface to caller — task bundle prepare failed.
|
|
17
|
+
|
|
18
|
+
run.py(final-verification) 와 container.py 가 whole-task 통합 헬퍼의 실패를
|
|
19
|
+
같은 타입으로 받는다.
|
|
20
|
+
"""
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""직전 implementation-planning run 이 남긴 계획 맥락을 packet 용으로 좁힌다.
|
|
2
|
+
|
|
3
|
+
계획 run 이 실패한 뒤 `--selected-direction` 으로 다시 도는 경우, 새 run 은 직전
|
|
4
|
+
run 의 산출물을 하나도 받지 못했다. analysis packet 에 그것을 실을 자리가 없었고
|
|
5
|
+
(`analysis_packet.build_analysis_packet` 에 직전 finding·판정·리포트를 받는 인자가
|
|
6
|
+
없었다), 판정 이월은 답변된 clarification 이 있는 경로에만 있다
|
|
7
|
+
(`incremental_scope.decide_scope` 는 답변된 `C-NNN` 이 없으면 `full` 을 되돌린다).
|
|
8
|
+
그래서 planner 들이 brief 에서 전부 다시 유도했고, 같은 이견이 새 `P-*` 번호로
|
|
9
|
+
다시 열렸다.
|
|
10
|
+
|
|
11
|
+
이 모듈은 그 재유도를 줄일 만큼만 모은다 — clarification 행, Stage Map 한 줄씩,
|
|
12
|
+
직전 수렴이 못 가른 finding, 그리고 나머지를 위한 리포트 경로. 리포트 전문은
|
|
13
|
+
싣지 않는다: 그 중복이 실측 283K 리포트로 892K 중복 읽기를 만들어 report-writer 를
|
|
14
|
+
timeout 시킨 적이 있다(`clarification_items.carry` 참고).
|
|
15
|
+
|
|
16
|
+
판정은 소유하지 않는다. 이 블록은 사실 기록이고, 승인되지 않은 계획을 그대로
|
|
17
|
+
복사하라는 지시가 아니다 — 그 문장은 packet 블록 머리글이 직접 말한다
|
|
18
|
+
(`analysis_packet._prior_planning_block`).
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Mapping
|
|
25
|
+
|
|
26
|
+
from .clarification_items import carried_clarification_rows
|
|
27
|
+
from .final_report_paths import final_report_data_path
|
|
28
|
+
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
29
|
+
from .paths import RunRef, infer_project_root, project_rel
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
PLANNING_TASK_TYPE = "implementation-planning"
|
|
33
|
+
# 직전 run 이 끝내지 못한 두 상태. `convergence_engine._CLASSIFICATION_COUNT_KEYS`
|
|
34
|
+
# 의 어휘를 그대로 쓴다 — 새 어휘를 만들면 같은 finding 이 run 마다 다르게 읽힌다.
|
|
35
|
+
UNSETTLED_CLASSIFICATIONS = ("contested", "worker-unique")
|
|
36
|
+
_SUMMARY_LIMIT = 200
|
|
37
|
+
_WHITESPACE_RE = re.compile(r"\s+")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def build_prior_planning_summary(task_root: Path) -> str:
|
|
41
|
+
"""이 task 의 직전 계획 run 요약. 직전 run 이 없으면 빈 문자열.
|
|
42
|
+
|
|
43
|
+
직전 리포트 해소는 wizard / interactive 진입점이 쓰는 것과 같다
|
|
44
|
+
(`RunRef.latest_under`: mtime 최신, 동률이면 basename, `.data.json` 우선).
|
|
45
|
+
같은 task-type 으로 좁히므로 다른 phase 의 리포트를 계획 맥락으로 집지 않는다.
|
|
46
|
+
"""
|
|
47
|
+
task_root = Path(task_root)
|
|
48
|
+
ref = RunRef.latest_under(task_root, (PLANNING_TASK_TYPE,))
|
|
49
|
+
if ref is None:
|
|
50
|
+
return ""
|
|
51
|
+
report = ref.report
|
|
52
|
+
project_root = infer_project_root(task_root)
|
|
53
|
+
sections = [f"- Prior planning report: `{project_rel(report, project_root)}`"]
|
|
54
|
+
rows = carried_clarification_rows(report)
|
|
55
|
+
if rows:
|
|
56
|
+
sections.append(f"### Prior Clarification Items\n\n{rows}")
|
|
57
|
+
stage_map = _stage_map_lines(report)
|
|
58
|
+
if stage_map:
|
|
59
|
+
sections.append(
|
|
60
|
+
"### Prior Stage Map\n\n"
|
|
61
|
+
"`stage | title | depends-on`, as the prior plan wrote it.\n\n"
|
|
62
|
+
+ "\n".join(stage_map)
|
|
63
|
+
)
|
|
64
|
+
unsettled = _unsettled_finding_lines(ref)
|
|
65
|
+
if unsettled:
|
|
66
|
+
sections.append(
|
|
67
|
+
"### Findings the prior run left unsettled\n\n"
|
|
68
|
+
+ "\n".join(unsettled)
|
|
69
|
+
)
|
|
70
|
+
return "\n\n".join(sections)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _stage_map_lines(report: Path) -> list[str]:
|
|
74
|
+
"""직전 계획의 Stage Map 을 스테이지당 한 줄로.
|
|
75
|
+
|
|
76
|
+
`stageMap` 이 정본이고 `stages` 는 그것이 없는 리포트를 위한 폴백이다 —
|
|
77
|
+
`plan_items._planning_stage_rows` 가 쓰는 것과 같은 우선순위다.
|
|
78
|
+
"""
|
|
79
|
+
planning = _planning(report)
|
|
80
|
+
rows = planning.get("stageMap")
|
|
81
|
+
if not isinstance(rows, list) or not rows:
|
|
82
|
+
rows = planning.get("stages")
|
|
83
|
+
if not isinstance(rows, list):
|
|
84
|
+
return []
|
|
85
|
+
lines = []
|
|
86
|
+
for row in rows:
|
|
87
|
+
if not isinstance(row, Mapping):
|
|
88
|
+
continue
|
|
89
|
+
stage = row.get("stage")
|
|
90
|
+
if not isinstance(stage, int) or isinstance(stage, bool):
|
|
91
|
+
continue
|
|
92
|
+
title = _one_line(row.get("title")) or "(untitled)"
|
|
93
|
+
depends = _one_line(row.get("dependsOn")) or "(unrecorded)"
|
|
94
|
+
lines.append(f"- {stage} | {title} | depends-on: {depends}")
|
|
95
|
+
return lines
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _unsettled_finding_lines(ref: RunRef) -> list[str]:
|
|
99
|
+
"""직전 수렴이 `contested` / `worker-unique` 로 닫은 finding 한 줄씩.
|
|
100
|
+
|
|
101
|
+
상태 파일이 없거나 못 읽으면 빈 목록이다. 없는 것이 정상 분기다 — 수렴을
|
|
102
|
+
돌기 전에 죽은 run, 그리고 디렉터리별 seq 가 갈린 run 이 둘 다 여기로 온다
|
|
103
|
+
(`RunRef.convergence_state` 참고).
|
|
104
|
+
|
|
105
|
+
seq 를 못 읽은 ref 도 같다. 옛 타임스탬프 이름의 리포트가 그 경우이고, 그
|
|
106
|
+
이름에서는 상태 파일 이름을 재구성할 수 없다.
|
|
107
|
+
"""
|
|
108
|
+
if ref.seq is None:
|
|
109
|
+
return []
|
|
110
|
+
state = _load_object(ref.convergence_state)
|
|
111
|
+
findings = state.get("findings")
|
|
112
|
+
if not isinstance(findings, list):
|
|
113
|
+
return []
|
|
114
|
+
lines = []
|
|
115
|
+
for finding in findings:
|
|
116
|
+
if not isinstance(finding, Mapping):
|
|
117
|
+
continue
|
|
118
|
+
classification = finding.get("classification")
|
|
119
|
+
if classification not in UNSETTLED_CLASSIFICATIONS:
|
|
120
|
+
continue
|
|
121
|
+
finding_id = _one_line(finding.get("findingId"))
|
|
122
|
+
if not finding_id:
|
|
123
|
+
continue
|
|
124
|
+
summary = _one_line(finding.get("summary")) or "(no summary recorded)"
|
|
125
|
+
lines.append(f"- `{finding_id}` ({classification}) — {summary}")
|
|
126
|
+
return lines
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _planning(report: Path) -> Mapping[str, Any]:
|
|
130
|
+
data = _load_object(final_report_data_path(report))
|
|
131
|
+
planning = data.get("implementationPlanning")
|
|
132
|
+
return planning if isinstance(planning, Mapping) else {}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _load_object(path: Path) -> Mapping[str, Any]:
|
|
136
|
+
"""소유한 JSON 객체, 못 읽으면 빈 매핑.
|
|
137
|
+
|
|
138
|
+
이 블록은 전부 최선 노력이다. 직전 run 의 산출물이 깨졌다고 새 run 의 준비를
|
|
139
|
+
막으면, 다시 도는 이유였던 그 실패가 재실행 자체를 못 하게 만든다.
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
data = load_owned_object(path, artifact="prior planning artifact")
|
|
143
|
+
except (JsonBoundaryError, OSError):
|
|
144
|
+
# expected-miss: 직전 run 이 남기지 못했거나 깨뜨린 파일. 이 블록은
|
|
145
|
+
# 맥락일 뿐이라 없는 상태가 정상 분기고, 소비처는 해당 하위 블록을
|
|
146
|
+
# 생략한다.
|
|
147
|
+
return {}
|
|
148
|
+
return data if isinstance(data, Mapping) else {}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _one_line(value: object) -> str:
|
|
152
|
+
if not isinstance(value, str):
|
|
153
|
+
return ""
|
|
154
|
+
collapsed = _WHITESPACE_RE.sub(" ", value).strip()
|
|
155
|
+
if len(collapsed) <= _SUMMARY_LIMIT:
|
|
156
|
+
return collapsed
|
|
157
|
+
return collapsed[: _SUMMARY_LIMIT - 1].rstrip() + "…"
|
|
@@ -107,9 +107,26 @@ def _write_stdout(text: str) -> None:
|
|
|
107
107
|
os.close(devnull)
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
_CLI_EPILOG = r"""Usage:
|
|
111
|
+
okstra profile show <task-type> [--resolved]
|
|
112
|
+
|
|
113
|
+
--resolved expand {{INCLUDE:}} targets and append the lazy-read sidecars
|
|
114
|
+
|
|
115
|
+
A profile is assembled from the top-level body, its {{INCLUDE:}} targets, and
|
|
116
|
+
the sidecars its own table names — so grepping the top-level file and finding
|
|
117
|
+
nothing does not mean the rule is absent. This prints all of it, so one grep
|
|
118
|
+
answers "does this task-type cover X".
|
|
119
|
+
|
|
120
|
+
Read-only: writes no manifest and registers no run. That is what separates it
|
|
121
|
+
from 'okstra render-bundle', which answers the same question with side effects.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
|
|
110
125
|
def main(argv: list[str] | None = None) -> int:
|
|
111
126
|
parser = argparse.ArgumentParser(
|
|
112
|
-
|
|
127
|
+
epilog=_CLI_EPILOG,
|
|
128
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
129
|
+
prog="okstra profile",
|
|
113
130
|
description="Print a phase profile, optionally fully resolved (read-only).",
|
|
114
131
|
)
|
|
115
132
|
parser.add_argument("command", choices=("show",))
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""프로젝트 루트 해석·project.json upsert·PR 템플릿 해석의 CLI 경계.
|
|
2
|
+
|
|
3
|
+
이 네 갈래는 원래 `config.mts` · `setup.mts` · `check-project.mts` 안에
|
|
4
|
+
파이썬 소스 배열로 각각 복사돼 있었다(docs/coding-rules.md R1 위반, 그리고
|
|
5
|
+
같은 해석을 네 벌 유지하는 중복). 로직은 그대로 옮겼고 출력 형식도 그대로다 —
|
|
6
|
+
호출부의 파서를 건드리지 않기 위해서다.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from okstra_project import (
|
|
16
|
+
ResolverError,
|
|
17
|
+
project_json_path,
|
|
18
|
+
resolve_project_root,
|
|
19
|
+
upsert_project_json,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _resolve(args: argparse.Namespace) -> int:
|
|
26
|
+
"""`PROJECT_ROOT` / `PROJECT_JSON` 또는 `RESOLVER_ERROR` 태그 줄."""
|
|
27
|
+
try:
|
|
28
|
+
pr = resolve_project_root(explicit_root=args.explicit_root, cwd=args.cwd)
|
|
29
|
+
except ResolverError as exc:
|
|
30
|
+
print("RESOLVER_ERROR", exc)
|
|
31
|
+
return 0
|
|
32
|
+
print("PROJECT_ROOT", pr)
|
|
33
|
+
print("PROJECT_JSON", project_json_path(pr))
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _resolve_root(args: argparse.Namespace) -> int:
|
|
38
|
+
"""루트 경로만 stdout 으로.
|
|
39
|
+
|
|
40
|
+
`--on-error` 로 실패 취급이 갈린다. `fail` 은 stderr + 비0(엄격한 호출부),
|
|
41
|
+
`empty` 는 아무것도 내지 않고 0(autofill 처럼 실패해도 진행하는 호출부).
|
|
42
|
+
두 갈래를 한 구현 안에 두는 이유는, 예전에 셸과 TS 가 같은 해석을 각자
|
|
43
|
+
베껴 두어 갈라질 수 있었기 때문이다.
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
root = resolve_project_root(explicit_root=args.explicit_root, cwd=args.cwd)
|
|
47
|
+
except ResolverError as exc:
|
|
48
|
+
if args.on_error == "fail":
|
|
49
|
+
sys.stderr.write(f"{args.error_prefix}{exc}\n")
|
|
50
|
+
return args.error_code
|
|
51
|
+
return 0
|
|
52
|
+
print(root)
|
|
53
|
+
return 0
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _upsert(args: argparse.Namespace) -> int:
|
|
57
|
+
"""`--format tagged` 는 `OK <json>` / `ERROR <msg>`, `strict` 는 stderr + 비0."""
|
|
58
|
+
try:
|
|
59
|
+
result = upsert_project_json(Path(args.project_root), args.project_id)
|
|
60
|
+
except ResolverError as exc:
|
|
61
|
+
if args.format == "strict":
|
|
62
|
+
sys.stderr.write(f"project.json upsert failed: {exc}\n")
|
|
63
|
+
return 1
|
|
64
|
+
print("ERROR", exc)
|
|
65
|
+
return 0
|
|
66
|
+
if args.format == "tagged":
|
|
67
|
+
print("OK", json.dumps(result))
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _project_json(args: argparse.Namespace) -> int:
|
|
72
|
+
"""`<projectId>\t<projectRoot>` 한 줄. 해석·파일 어느 쪽이든 없으면 아무것도 내지 않는다."""
|
|
73
|
+
try:
|
|
74
|
+
root = resolve_project_root(explicit_root=args.explicit_root or "")
|
|
75
|
+
except ResolverError:
|
|
76
|
+
return 0
|
|
77
|
+
path = project_json_path(Path(root))
|
|
78
|
+
if not path.is_file():
|
|
79
|
+
return 0
|
|
80
|
+
try:
|
|
81
|
+
data = load_owned_object(path, artifact="project.json")
|
|
82
|
+
except JsonBoundaryError:
|
|
83
|
+
# 깨진 project.json 은 autofill 을 끄는 신호다 — 이 경로의 호출부는
|
|
84
|
+
# 값을 못 얻으면 사용자에게 직접 묻는다. 여기서 죽이면 물어볼 기회도 없다.
|
|
85
|
+
return 0
|
|
86
|
+
print(f"{data.get('projectId', '')}\t{root}")
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _pr_template(args: argparse.Namespace) -> int:
|
|
91
|
+
from .pr_template import PrTemplateError, resolve_pr_template_path
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
resolved = resolve_pr_template_path(Path(args.project_root))
|
|
95
|
+
except PrTemplateError as exc:
|
|
96
|
+
print(json.dumps({"ok": False, "reason": str(exc)}))
|
|
97
|
+
return 0
|
|
98
|
+
print(json.dumps({
|
|
99
|
+
"ok": True, "path": str(resolved.path), "source": resolved.source,
|
|
100
|
+
}))
|
|
101
|
+
return 0
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def main(argv: list[str] | None = None) -> int:
|
|
105
|
+
parser = argparse.ArgumentParser(prog="okstra project-setup")
|
|
106
|
+
sub = parser.add_subparsers(dest="op", required=True)
|
|
107
|
+
|
|
108
|
+
resolve = sub.add_parser("resolve")
|
|
109
|
+
resolve.add_argument("--explicit-root", default="")
|
|
110
|
+
resolve.add_argument("--cwd", required=True)
|
|
111
|
+
resolve.set_defaults(handler=_resolve)
|
|
112
|
+
|
|
113
|
+
resolve_root = sub.add_parser("resolve-root")
|
|
114
|
+
resolve_root.add_argument("--explicit-root", default="")
|
|
115
|
+
resolve_root.add_argument("--cwd", default="")
|
|
116
|
+
resolve_root.add_argument("--on-error", choices=("fail", "empty"), default="fail")
|
|
117
|
+
resolve_root.add_argument("--error-prefix", default="")
|
|
118
|
+
resolve_root.add_argument("--error-code", type=int, default=2)
|
|
119
|
+
resolve_root.set_defaults(handler=_resolve_root)
|
|
120
|
+
|
|
121
|
+
upsert = sub.add_parser("upsert")
|
|
122
|
+
upsert.add_argument("--project-root", required=True)
|
|
123
|
+
upsert.add_argument("--project-id", required=True)
|
|
124
|
+
upsert.add_argument("--format", choices=("tagged", "strict"), default="tagged")
|
|
125
|
+
upsert.set_defaults(handler=_upsert)
|
|
126
|
+
|
|
127
|
+
project_json = sub.add_parser("project-json")
|
|
128
|
+
project_json.add_argument("--explicit-root", default="")
|
|
129
|
+
project_json.set_defaults(handler=_project_json)
|
|
130
|
+
|
|
131
|
+
pr_template = sub.add_parser("pr-template")
|
|
132
|
+
pr_template.add_argument("--project-root", required=True)
|
|
133
|
+
pr_template.set_defaults(handler=_pr_template)
|
|
134
|
+
|
|
135
|
+
args = parser.parse_args(argv)
|
|
136
|
+
return args.handler(args)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
raise SystemExit(main())
|
|
@@ -213,8 +213,29 @@ def _add_common(sp) -> None:
|
|
|
213
213
|
sp.add_argument("--cwd", default=".")
|
|
214
214
|
|
|
215
215
|
|
|
216
|
+
_CLI_EPILOG = r"""Usage:
|
|
217
|
+
okstra recap assemble <task-root|task-key> [--project-root <dir>] [--cwd <dir>]
|
|
218
|
+
okstra recap record <task-root|task-key> --kind <summary|qa> --mode <artifact|code> \
|
|
219
|
+
[--question <text>] --answer <text> [--citation <path:line> ...]
|
|
220
|
+
okstra recap note <task-root|task-key> \
|
|
221
|
+
--kind <verification-evidence|decision-draft|analysis-note> --slug <topic> \
|
|
222
|
+
--purpose <text> --scope-note <text> (--body <markdown> | --body-file <path>)
|
|
223
|
+
|
|
224
|
+
`assemble` is read-only and prints a JSON summary of phase transitions across runs.
|
|
225
|
+
`record` appends one line to <task-root>/recap/recap-log.jsonl; it never mutates
|
|
226
|
+
other task artifacts.
|
|
227
|
+
`note` writes an agent-authored markdown note to <task-root>/notes/ and prints its
|
|
228
|
+
path plus the `--clarification-response` argument for feeding it into a later run.
|
|
229
|
+
"""
|
|
230
|
+
_CLI_DESCRIPTION = "Assemble a task's run-to-run phase recap, append a recap log entry, or write an agent note."
|
|
231
|
+
|
|
232
|
+
|
|
216
233
|
def main(argv: list[str] | None = None) -> int:
|
|
217
|
-
parser = argparse.ArgumentParser(
|
|
234
|
+
parser = argparse.ArgumentParser(
|
|
235
|
+
description=_CLI_DESCRIPTION,
|
|
236
|
+
epilog=_CLI_EPILOG,
|
|
237
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
238
|
+
prog="okstra recap")
|
|
218
239
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
219
240
|
|
|
220
241
|
p_asm = sub.add_parser("assemble", help="assemble run-to-run phase transitions")
|
|
@@ -227,3 +227,87 @@ def _reconcile_recent_locked(home: Path, *, project: Optional[str] = None) -> di
|
|
|
227
227
|
for pid, urows in updated_by_project.items():
|
|
228
228
|
_sync_project_index(home, pid, urows)
|
|
229
229
|
return summary
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def reconcile_home(home: Path, *, project: Optional[str] = None) -> dict:
|
|
233
|
+
"""active 승격과 recent in-place 갱신을 한 번에 수행하는 단일 호출점.
|
|
234
|
+
|
|
235
|
+
두 함수는 서로 다른 파일을 보지만 같은 사실(디스크 run-manifest 의
|
|
236
|
+
validation)을 근거로 삼는 한 쌍이다. active 만 돌리면 `--render-only` 로
|
|
237
|
+
recent 에 prepared 로 박힌 행이 영영 갱신되지 않고, recent 만 돌리면 끝난
|
|
238
|
+
run 이 active 에 running 으로 남는다. 호출자가 한쪽만 부를 수 있는 표면을
|
|
239
|
+
남기지 않기 위해 여기서 묶는다.
|
|
240
|
+
|
|
241
|
+
project 를 주면 해당 projectId 행만 대상으로 삼는다. 두 함수 각각이 중앙
|
|
242
|
+
락을 잡았다 놓으므로, 이 함수를 central_lock 안에서 부르면 self-deadlock
|
|
243
|
+
이다(flock 은 재진입 불가).
|
|
244
|
+
"""
|
|
245
|
+
return {
|
|
246
|
+
"active": reconcile_active(home, project=project),
|
|
247
|
+
"recent": reconcile_recent(home, project=project),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def settle_run_row(home: Path, *, project_id: str, execution_manifest_rel: str,
|
|
252
|
+
validation_status: str) -> bool:
|
|
253
|
+
"""검증기가 방금 확정한 판정을 그 run 의 인덱스 행에 그대로 기록한다.
|
|
254
|
+
|
|
255
|
+
`reconcile_*` 는 디스크에서 상태를 **추론**한다 — 어느 run 이 끝났는지 모르는
|
|
256
|
+
호출자를 위한 경로다. 검증기는 자기가 방금 무엇을 썼는지 알고 있으므로 추론할
|
|
257
|
+
것이 없고, 추론에 맡기면 실제로 틀린다: `reconcile_recent` 는 이미 종결 상태인
|
|
258
|
+
행을 갱신 대상에서 빼므로, 한 번 `failed` 로 굳은 뒤 고쳐서 다시 통과시킨 run
|
|
259
|
+
이 인덱스에는 영영 `failed` 로 남는다(검증 재실행은 흔한 흐름이다).
|
|
260
|
+
|
|
261
|
+
행은 `executionManifestPath` 로 찾는다 — `runDirRel` 은 같은 task-type 의 여러
|
|
262
|
+
run-seq 가 공유해 유일하지 않다. 그 필드가 없는 옛 행은 못 찾고 False 를
|
|
263
|
+
돌려주며, 그런 행은 `reconcile_home` 의 일반 스윕이 맡는다.
|
|
264
|
+
|
|
265
|
+
반환값: 행을 찾아 기록했으면 True.
|
|
266
|
+
"""
|
|
267
|
+
with central_lock(home):
|
|
268
|
+
return _settle_run_row_locked(
|
|
269
|
+
home, project_id=project_id,
|
|
270
|
+
execution_manifest_rel=execution_manifest_rel,
|
|
271
|
+
validation_status=validation_status)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _matches_run(row: dict, project_id: str, execution_manifest_rel: str) -> bool:
|
|
275
|
+
return (row.get("projectId") == project_id
|
|
276
|
+
and str(row.get("executionManifestPath") or "") == execution_manifest_rel)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _settle_run_row_locked(home: Path, *, project_id: str,
|
|
280
|
+
execution_manifest_rel: str,
|
|
281
|
+
validation_status: str) -> bool:
|
|
282
|
+
# 빈 키는 따로 막지 않는다 — `_matches_run` 의 문자열 동일성 비교가 어떤 행도
|
|
283
|
+
# 고르지 않으므로 결과가 같다(`test_settle_run_row_needs_both_...` 이 고정).
|
|
284
|
+
status = normalize_reconciled_report_status(validation_status)
|
|
285
|
+
finished_at = _now_iso()
|
|
286
|
+
|
|
287
|
+
active = home / "active.jsonl"
|
|
288
|
+
rows = read_run_index(active)
|
|
289
|
+
survivors = [r for r in rows
|
|
290
|
+
if not _matches_run(r, project_id, execution_manifest_rel)]
|
|
291
|
+
promoted = [r for r in rows
|
|
292
|
+
if _matches_run(r, project_id, execution_manifest_rel)]
|
|
293
|
+
if promoted:
|
|
294
|
+
for row in promoted:
|
|
295
|
+
row["status"] = status
|
|
296
|
+
row["finishedAt"] = finished_at
|
|
297
|
+
row["validation"] = validation_status
|
|
298
|
+
_publish_promoted(home, active, survivors, promoted)
|
|
299
|
+
return True
|
|
300
|
+
|
|
301
|
+
recent = home / "recent.jsonl"
|
|
302
|
+
rows = read_run_index(recent)
|
|
303
|
+
updated = [r for r in rows
|
|
304
|
+
if _matches_run(r, project_id, execution_manifest_rel)]
|
|
305
|
+
if not updated:
|
|
306
|
+
return False
|
|
307
|
+
for row in updated:
|
|
308
|
+
row["status"] = status
|
|
309
|
+
row["finishedAt"] = finished_at
|
|
310
|
+
row["validation"] = validation_status
|
|
311
|
+
rewrite_jsonl(recent, [slim_run_row(row) for row in rows])
|
|
312
|
+
_sync_project_index(home, project_id, updated)
|
|
313
|
+
return True
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import inspect
|
|
5
5
|
from collections.abc import Iterable, Mapping
|
|
6
|
+
from functools import lru_cache
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
from okstra_project.dirs import okstra_home
|
|
@@ -158,7 +159,23 @@ def default_host_registry(
|
|
|
158
159
|
bundled_root = Path(__file__).resolve().parents[1] / "adapters" / "hosts"
|
|
159
160
|
user_root = okstra_home() / "adapters" / "hosts"
|
|
160
161
|
providers = provider_registry or default_provider_registry()
|
|
161
|
-
return
|
|
162
|
+
return _host_registry_for_roots(bundled_root, user_root, providers)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@lru_cache(maxsize=None)
|
|
166
|
+
def _host_registry_for_roots(
|
|
167
|
+
bundled_root: Path,
|
|
168
|
+
user_root: Path,
|
|
169
|
+
provider_registry: ProviderRegistry,
|
|
170
|
+
) -> HostAdapterRegistry:
|
|
171
|
+
"""discovery root 조합마다 레지스트리를 한 번만 조립한다.
|
|
172
|
+
|
|
173
|
+
`from_roots` 는 매니페스트를 디스크에서 훑고 어댑터 팩토리 모듈을 importlib
|
|
174
|
+
로 적재하므로 호출당 비용이 크다. 호출부(위저드의 역할/모델 프롬프트 등)는
|
|
175
|
+
같은 root 로 이걸 루프 안에서 반복 호출한다. `default_provider_registry` 의
|
|
176
|
+
`_registry_for_roots` 와 같은 캐시 형태를 쓴다.
|
|
177
|
+
"""
|
|
178
|
+
return HostAdapterRegistry.from_roots((bundled_root, user_root), provider_registry)
|
|
162
179
|
|
|
163
180
|
|
|
164
181
|
def _load_entries(
|