okstra 0.186.7 → 0.188.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 +107 -40
- 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 +39 -38
- 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 +47 -22
- 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/adapters/cmux.md +3 -3
- package/runtime/prompts/lead/convergence.md +99 -88
- 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 +35 -5
- 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 +63 -22
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +20 -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} +16 -2
- 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 +228 -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 +605 -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 +287 -10
- 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 +32 -4
- package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
- package/runtime/python/okstra_ctl/convergence.py +107 -16
- 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 +236 -49
- package/runtime/python/okstra_ctl/dispatch_state.py +417 -41
- 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 +28 -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/exact_coverage.py +5 -0
- 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 +319 -17
- 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 +32 -13
- 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 +4 -8
- 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 +213 -16
- 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 +220 -34
- package/runtime/python/okstra_ctl/report_projections.py +8 -2
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +393 -1
- package/runtime/python/okstra_ctl/report_translation.py +2 -35
- package/runtime/python/okstra_ctl/report_view_artifacts.py +42 -2
- 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/verdict_blocks.py +28 -8
- 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 +58 -17
- 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/antigravity.py +50 -12
- 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 +171 -36
- 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 +23 -8
- 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 +3 -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 +1363 -2643
- 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
|
@@ -10,6 +10,7 @@ SIGTERM a healthy worker in the middle of a long reasoning stretch.
|
|
|
10
10
|
"""
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import dataclasses
|
|
13
14
|
import json
|
|
14
15
|
import os
|
|
15
16
|
import selectors
|
|
@@ -21,6 +22,7 @@ from typing import Any, Callable, Mapping
|
|
|
21
22
|
|
|
22
23
|
from .domain.provider import ServedModelAttestation, ServedModelNormalizer
|
|
23
24
|
from .domain.worker_exec import (
|
|
25
|
+
NO_RESULT_EXIT_CODE,
|
|
24
26
|
SERVED_MODEL_MISMATCH_EXIT_CODE,
|
|
25
27
|
ExecCommand,
|
|
26
28
|
ExecutionStrategy,
|
|
@@ -71,7 +73,7 @@ def run_worker(
|
|
|
71
73
|
|
|
72
74
|
try:
|
|
73
75
|
with guard:
|
|
74
|
-
exit_code, timed_out, idle_seconds, raw_model = _launch(
|
|
76
|
+
exit_code, timed_out, idle_seconds, raw_model, usage = _launch(
|
|
75
77
|
command,
|
|
76
78
|
log_path,
|
|
77
79
|
presentation=presentation,
|
|
@@ -92,6 +94,11 @@ def run_worker(
|
|
|
92
94
|
raise
|
|
93
95
|
|
|
94
96
|
status = _closed(status, started_monotonic)
|
|
97
|
+
if usage is not None:
|
|
98
|
+
# 스트림이 마지막으로 보고한 토큰 스냅샷, 공급자 어휘 그대로. 토큰
|
|
99
|
+
# 수집기(`okstra_token_usage`)가 홈 트랜스크립트가 없는 공급자의
|
|
100
|
+
# 사용량을 여기서 읽는다.
|
|
101
|
+
status["usage"] = dict(usage)
|
|
95
102
|
if served_model_normalizer is not None:
|
|
96
103
|
attestation = served_model_normalizer(raw_model)
|
|
97
104
|
status["servedModelAttestation"] = _attestation_payload(attestation)
|
|
@@ -170,11 +177,14 @@ def _launch(
|
|
|
170
177
|
presentation: str,
|
|
171
178
|
idle_timeout_seconds: int,
|
|
172
179
|
on_spawn: Callable[[subprocess.Popen[bytes]], None],
|
|
173
|
-
) -> tuple[int, bool, int, str | None]:
|
|
180
|
+
) -> tuple[int, bool, int, str | None, Mapping[str, Any] | None]:
|
|
174
181
|
live = presentation == LIVE
|
|
175
182
|
transcript = SessionTranscript(log_path, live=live)
|
|
176
183
|
observation = _ServedModelObservation()
|
|
177
|
-
|
|
184
|
+
usage_observation = _UsageObservation()
|
|
185
|
+
strategy = _with_stream_observations(
|
|
186
|
+
command.presentation, observation, usage_observation
|
|
187
|
+
)
|
|
178
188
|
try:
|
|
179
189
|
# The strategy decided where this provider runs — some CLIs work in the
|
|
180
190
|
# stage tree, others in the project root and reach the tree by flag.
|
|
@@ -203,34 +213,43 @@ def _launch(
|
|
|
203
213
|
idle_timeout_seconds=idle_timeout_seconds,
|
|
204
214
|
stdin_text=command.stdin_text,
|
|
205
215
|
)
|
|
206
|
-
return
|
|
216
|
+
return (
|
|
217
|
+
exit_code,
|
|
218
|
+
timed_out,
|
|
219
|
+
idle_seconds,
|
|
220
|
+
observation.raw_model,
|
|
221
|
+
usage_observation.usage,
|
|
222
|
+
)
|
|
207
223
|
finally:
|
|
208
224
|
transcript.close()
|
|
209
225
|
|
|
210
226
|
|
|
211
|
-
def
|
|
227
|
+
def _with_stream_observations(
|
|
212
228
|
presentation: Presentation,
|
|
213
229
|
observation: _ServedModelObservation,
|
|
230
|
+
usage_observation: _UsageObservation,
|
|
214
231
|
) -> Presentation:
|
|
215
|
-
"""JSON 경로의 서빙
|
|
232
|
+
"""JSON 경로의 서빙 모델·토큰 사용량 관측을 러너가 모아 둔다.
|
|
216
233
|
|
|
217
|
-
해석 전략은 이벤트를 보고 모델
|
|
218
|
-
적는 일은 러너의 것이라, 여기서 한 번 감싼다.
|
|
234
|
+
해석 전략은 이벤트를 보고 모델 문자열과 사용량 스냅샷만 돌려준다. 그 값을
|
|
235
|
+
사이드카에 적는 일은 러너의 것이라, 여기서 한 번 감싼다. 사용량은 어댑터가
|
|
236
|
+
읽는 법을 넘긴 어휘에서만 관측된다.
|
|
219
237
|
"""
|
|
220
238
|
if not isinstance(presentation, JsonEvents):
|
|
221
239
|
return presentation
|
|
222
240
|
original = presentation.observe
|
|
241
|
+
original_usage = presentation.observe_usage
|
|
223
242
|
|
|
224
243
|
def observe(event: Mapping[str, Any]) -> str | None:
|
|
225
244
|
observed = original(event)
|
|
226
245
|
observation.record(observed)
|
|
246
|
+
if original_usage is not None:
|
|
247
|
+
usage_observation.record(original_usage(event))
|
|
227
248
|
return observed
|
|
228
249
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
join_adjacent_text=presentation.join_adjacent_text,
|
|
233
|
-
)
|
|
250
|
+
# 필드를 손으로 옮기지 않는다 — 그렇게 하던 동안 어댑터가 선언한
|
|
251
|
+
# `is_conclusion` 이 여기서 떨어져 나가, grok 의 종결 판정이 무효였다.
|
|
252
|
+
return dataclasses.replace(presentation, observe=observe)
|
|
234
253
|
|
|
235
254
|
|
|
236
255
|
class _ServedModelObservation:
|
|
@@ -242,6 +261,21 @@ class _ServedModelObservation:
|
|
|
242
261
|
self.raw_model = raw_model
|
|
243
262
|
|
|
244
263
|
|
|
264
|
+
class _UsageObservation:
|
|
265
|
+
"""스트림이 보고한 마지막 사용량 스냅샷.
|
|
266
|
+
|
|
267
|
+
첫 값이 아니라 마지막 값이다 — 단계별 스냅샷을 내는 CLI 도 종결 이벤트에
|
|
268
|
+
합계를 싣고, 그 이벤트가 스트림의 끝에 온다.
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
def __init__(self) -> None:
|
|
272
|
+
self.usage: Mapping[str, Any] | None = None
|
|
273
|
+
|
|
274
|
+
def record(self, usage: Mapping[str, Any] | None) -> None:
|
|
275
|
+
if isinstance(usage, Mapping) and usage:
|
|
276
|
+
self.usage = usage
|
|
277
|
+
|
|
278
|
+
|
|
245
279
|
class _AbnormalExit:
|
|
246
280
|
"""Close the status sidecar for the exits that raise nothing at all.
|
|
247
281
|
|
|
@@ -393,11 +427,16 @@ def _pump(
|
|
|
393
427
|
strategy.flush(transcript)
|
|
394
428
|
|
|
395
429
|
exit_code = process.wait()
|
|
396
|
-
if
|
|
397
|
-
# JSON 스트림은
|
|
398
|
-
# 놓칠
|
|
430
|
+
if isinstance(strategy, JsonEvents) and not strategy.concluded():
|
|
431
|
+
# JSON 스트림은 그 어휘의 종결 이벤트로 끝나야 한다. 텍스트를 흘리는
|
|
432
|
+
# CLI 에는 놓칠 종결 이벤트가 없다. 무엇이 종결인지는 전략이 안다.
|
|
399
433
|
transcript.note("no result event in the CLI's output")
|
|
400
|
-
|
|
434
|
+
if exit_code == 0 and not timed_out:
|
|
435
|
+
# 실측(dev-10341, 한 CLI 4회): 종결 이벤트도 산출물도 없이 exit 0.
|
|
436
|
+
# 그대로 돌려주면 사이드카와 셸 소비자에게는 성공으로 읽힌다.
|
|
437
|
+
# CLI 자신의 비영 코드는 보존한다 — 그쪽이 더 구체적인 사실이다.
|
|
438
|
+
exit_code = NO_RESULT_EXIT_CODE
|
|
439
|
+
if closing_text is not None:
|
|
401
440
|
# Result 는 format_* 가 줄을 만들지 않는다. 아카이브 결론은 러너가 적는다.
|
|
402
441
|
# 상한 밖에 둔다 — 잘린 결론은 사후 분석 전체를 잃게 한다.
|
|
403
442
|
if isinstance(strategy, JsonEvents):
|
|
@@ -559,6 +598,8 @@ def _terminate(process: subprocess.Popen[bytes]) -> None:
|
|
|
559
598
|
try:
|
|
560
599
|
os.killpg(process.pid, signal.SIGKILL)
|
|
561
600
|
except ProcessLookupError:
|
|
601
|
+
# expected-miss: SIGTERM 유예 사이에 그룹이 스스로 끝난 경우다.
|
|
602
|
+
# 죽이려던 대상이 이미 없는 것은 성공이지 실패가 아니다.
|
|
562
603
|
pass
|
|
563
604
|
process.wait()
|
|
564
605
|
|
|
@@ -18,8 +18,23 @@ def _validated_team_state_path(value: str) -> Path:
|
|
|
18
18
|
return path
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
_CLI_EPILOG = r"""Usage:
|
|
22
|
+
okstra worker-state transition --team-state <path> --worker <worker-id> \
|
|
23
|
+
--status <in-progress|completed|timeout|error|not-run> \
|
|
24
|
+
[--reason <text>] [--model <execution-value>]
|
|
25
|
+
|
|
26
|
+
in-progress records startedAt and clears endedAt. completed, timeout, and error
|
|
27
|
+
record endedAt. timeout, error, and not-run require --reason.
|
|
28
|
+
"""
|
|
29
|
+
_CLI_DESCRIPTION = "Atomically transition persisted worker status."
|
|
30
|
+
|
|
31
|
+
|
|
21
32
|
def _parser() -> argparse.ArgumentParser:
|
|
22
|
-
parser = argparse.ArgumentParser(
|
|
33
|
+
parser = argparse.ArgumentParser(
|
|
34
|
+
description=_CLI_DESCRIPTION,
|
|
35
|
+
epilog=_CLI_EPILOG,
|
|
36
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
37
|
+
prog="okstra worker-state")
|
|
23
38
|
commands = parser.add_subparsers(dest="command", required=True)
|
|
24
39
|
transition = commands.add_parser(
|
|
25
40
|
"transition", help="atomically transition one worker status"
|
|
@@ -54,7 +54,7 @@ PHASE_RULES: dict[str, dict[str, str]] = {
|
|
|
54
54
|
" - top-N ranking (default 8, brief `candidate-cap` overrides within 1..12)\n"
|
|
55
55
|
" - per-candidate columns Lens / Scope / Severity / Effort / Consensus / Source workers / Recommended next-phase / Expected behavior after / Evidence (path:line)\n"
|
|
56
56
|
" - Phase 1.5 reflect-back grilling log at `runs/improvement-discovery/<seq>/state/phase-1.5-grilling.md`\n"
|
|
57
|
-
" - Phase 5.5 consensus classification (full / partial / contested / worker-unique)"
|
|
57
|
+
" - Phase 5.5 consensus classification (full / partial / contested / worker-unique / unverified)"
|
|
58
58
|
),
|
|
59
59
|
},
|
|
60
60
|
"project-analysis": {
|
|
@@ -173,6 +173,22 @@ def load_phase_forbidden(workspace_root: Path) -> dict[str, str]:
|
|
|
173
173
|
return {phase: render_forbidden(items) for phase, items in data.items()}
|
|
174
174
|
|
|
175
175
|
|
|
176
|
+
def phase_position(task_type: str) -> str:
|
|
177
|
+
"""이 phase 가 생애주기 몇 번째인지 — `"5 of 7"`.
|
|
178
|
+
|
|
179
|
+
리드는 자기가 어느 칸을 도는지만 알고 지도를 못 본다. 다음 phase 이름은
|
|
180
|
+
이 run 의 리포트가 정하므로 계속 감추지만(`WORKFLOW_NEXT_RECOMMENDED_PHASE`
|
|
181
|
+
가 빈 문자열인 이유), 위치를 감출 이유는 없다 — 위치를 모르면 중간 단계를
|
|
182
|
+
도는 run 이 자기가 마지막인 것처럼 결론을 낸다.
|
|
183
|
+
|
|
184
|
+
`PHASE_SEQUENCE` 밖의 사이드트랙 task-type(improvement-discovery 등)은
|
|
185
|
+
생애주기 칸이 없으므로 빈 문자열이다.
|
|
186
|
+
"""
|
|
187
|
+
if task_type not in PHASE_SEQUENCE:
|
|
188
|
+
return ""
|
|
189
|
+
return f"{PHASE_SEQUENCE.index(task_type) + 1} of {len(PHASE_SEQUENCE)}"
|
|
190
|
+
|
|
191
|
+
|
|
176
192
|
def compute_workflow_state(
|
|
177
193
|
*,
|
|
178
194
|
task_type: str,
|
|
@@ -219,6 +235,7 @@ def compute_workflow_state(
|
|
|
219
235
|
"WORKFLOW_WORK_CATEGORY": resolved_work_category,
|
|
220
236
|
"WORKFLOW_CURRENT_PHASE": task_type,
|
|
221
237
|
"WORKFLOW_CURRENT_PHASE_STATE": phase_state,
|
|
238
|
+
"WORKFLOW_PHASE_POSITION": phase_position(task_type),
|
|
222
239
|
# 값이 아니라 자리표시자다. 실제 포인터는 리포트 라우팅에서 투영돼
|
|
223
240
|
# 매니페스트의 `workflow.nextRecommendedPhase` 구조체로 저장된다.
|
|
224
241
|
# 이 ctx 값은 그 구조체로 대체되지 않는다 — 남은 소비자는
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Per-task git worktree provisioning.
|
|
2
|
+
|
|
3
|
+
Every okstra task — regardless of task-type/phase — runs inside an
|
|
4
|
+
isolated git worktree rooted at
|
|
5
|
+
`~/.okstra/worktrees/<project_id>/<task_group>/<task_id>/`. The same
|
|
6
|
+
worktree is reused across all phases of one task-key (requirements-
|
|
7
|
+
discovery → error-analysis → implementation-option-selection →
|
|
8
|
+
implementation-planning → implementation), so phase N picks up exactly
|
|
9
|
+
the working-tree state phase N-1 left behind.
|
|
10
|
+
|
|
11
|
+
A global registry (`worktree_registry.py`) maps task-keys to the
|
|
12
|
+
on-disk path + branch and serialises reservations, so two concurrent
|
|
13
|
+
okstra runs cannot collide on the same path or branch name.
|
|
14
|
+
|
|
15
|
+
Pre-conditions handled here:
|
|
16
|
+
- Skip when `project_root` is not a git repo (degrade gracefully).
|
|
17
|
+
- Skip when `project_root` itself is already a non-main worktree
|
|
18
|
+
(caller's tree is already an isolated workspace; reuse it).
|
|
19
|
+
- Re-entry of the same task-key returns the existing worktree.
|
|
20
|
+
- Branch / path collisions across task-keys raise PrepareError-like
|
|
21
|
+
RuntimeError.
|
|
22
|
+
|
|
23
|
+
Side effects:
|
|
24
|
+
- `git worktree add -b <branch> <path> <base_ref>` invoked in the
|
|
25
|
+
main worktree of `project_root` (NOT `project_root` itself when it
|
|
26
|
+
is also a worktree — base must be the main checkout).
|
|
27
|
+
- Per-task sync dirs (.project-docs, .scratch, graphify-out by
|
|
28
|
+
default) symlinked from the **main worktree** into the new
|
|
29
|
+
worktree so every task sees the same shared state, irrespective of
|
|
30
|
+
which worktree the caller invoked okstra from.
|
|
31
|
+
- The function does NOT chdir.
|
|
32
|
+
|
|
33
|
+
구현은 여섯 층으로 나뉜다. 바닥의 세 층 — `naming`(문자열 계산),
|
|
34
|
+
`sync_config`(무엇을 옮길지), `git_ops`(git 사실) — 은 서로를 모른다. 그 위에
|
|
35
|
+
`cleanliness`(무엇이 더러운가) · `linking`(실제로 링크 걸기) ·
|
|
36
|
+
`decisions`(무엇이 일어날지 미리 답하기)가 있고, 부수 효과를 내는 것은
|
|
37
|
+
`provision` 하나뿐이다. 이 파일은 밖으로 나가는 이름만 모아 둔다.
|
|
38
|
+
"""
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
from .cleanliness import (
|
|
42
|
+
dirty_entries_excluding_okstra,
|
|
43
|
+
is_dirty_excluding_okstra,
|
|
44
|
+
nested_worktree_excludes,
|
|
45
|
+
okstra_clean_gate_excludes,
|
|
46
|
+
)
|
|
47
|
+
from .decisions import (
|
|
48
|
+
StageWorktreeDecision,
|
|
49
|
+
WorktreeDecision,
|
|
50
|
+
preview_worktree_decision,
|
|
51
|
+
resolve_stage_worktree_decision,
|
|
52
|
+
)
|
|
53
|
+
from .git_ops import (
|
|
54
|
+
MergeError,
|
|
55
|
+
_git,
|
|
56
|
+
_resolve_commit_sha,
|
|
57
|
+
is_ancestor,
|
|
58
|
+
is_git_work_tree,
|
|
59
|
+
main_worktree_path,
|
|
60
|
+
merge_branch,
|
|
61
|
+
remove_worktree_force,
|
|
62
|
+
)
|
|
63
|
+
from .naming import compute_branch_name, compute_worktree_path
|
|
64
|
+
from .provision import (
|
|
65
|
+
WorktreeProvision,
|
|
66
|
+
provision_stage_worktree,
|
|
67
|
+
provision_task_worktree,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
__all__ = [
|
|
71
|
+
"MergeError",
|
|
72
|
+
"StageWorktreeDecision",
|
|
73
|
+
"WorktreeDecision",
|
|
74
|
+
"WorktreeProvision",
|
|
75
|
+
"_git",
|
|
76
|
+
"_resolve_commit_sha",
|
|
77
|
+
"compute_branch_name",
|
|
78
|
+
"compute_worktree_path",
|
|
79
|
+
"dirty_entries_excluding_okstra",
|
|
80
|
+
"is_ancestor",
|
|
81
|
+
"is_dirty_excluding_okstra",
|
|
82
|
+
"is_git_work_tree",
|
|
83
|
+
"main_worktree_path",
|
|
84
|
+
"merge_branch",
|
|
85
|
+
"nested_worktree_excludes",
|
|
86
|
+
"okstra_clean_gate_excludes",
|
|
87
|
+
"preview_worktree_decision",
|
|
88
|
+
"provision_stage_worktree",
|
|
89
|
+
"provision_task_worktree",
|
|
90
|
+
"remove_worktree_force",
|
|
91
|
+
"resolve_stage_worktree_decision",
|
|
92
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""okstra 기준으로 worktree 가 깨끗한지 판정한다.
|
|
2
|
+
|
|
3
|
+
`git status` 가 보는 더러움과 okstra 가 신경 쓰는 더러움은 다르다. okstra 는
|
|
4
|
+
자기 산출물(`.okstra/`), 자기가 걸어 둔 sync 심링크, 자기가 만든 중첩 worktree
|
|
5
|
+
를 제외하고 남는 변경만 사용자의 것으로 본다. 그 제외 목록을 만드는 곳이
|
|
6
|
+
여기이고, 그래서 `sync_config`(무엇을 걸었나)와 `git_ops`(지금 무엇이 바뀌었나)
|
|
7
|
+
양쪽을 쓴다.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from .git_ops import _git
|
|
15
|
+
from .sync_config import (
|
|
16
|
+
_resolve_snapshot_files,
|
|
17
|
+
_resolve_sync_dirs,
|
|
18
|
+
_resolve_sync_files,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def okstra_clean_gate_excludes(project_root: Optional[Path] = None) -> tuple[str, ...]:
|
|
23
|
+
"""Project-relative paths okstra owns and source clean gates should ignore."""
|
|
24
|
+
out: list[str] = []
|
|
25
|
+
seen: set[str] = set()
|
|
26
|
+
for rel in (
|
|
27
|
+
".okstra",
|
|
28
|
+
*_resolve_sync_dirs(project_root),
|
|
29
|
+
*_resolve_sync_files(project_root),
|
|
30
|
+
*_resolve_snapshot_files(project_root),
|
|
31
|
+
):
|
|
32
|
+
cleaned = rel.strip().removeprefix("./").rstrip("/")
|
|
33
|
+
if not cleaned or cleaned == "." or cleaned in seen:
|
|
34
|
+
continue
|
|
35
|
+
seen.add(cleaned)
|
|
36
|
+
out.append(cleaned)
|
|
37
|
+
return tuple(out)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def nested_worktree_excludes(worktree_path) -> tuple[str, ...]:
|
|
41
|
+
"""Relative paths of git worktrees nested under `worktree_path`.
|
|
42
|
+
|
|
43
|
+
implementation stage worktrees live inside the task worktree
|
|
44
|
+
(`<task-id>/stage-<N>/`), so the parent's `git status --short` reports
|
|
45
|
+
each as an untracked `?? stage-N/`. They are git-registered worktrees,
|
|
46
|
+
not user source changes, so clean gates must ignore them. Derived from
|
|
47
|
+
`git worktree list` rather than a hardcoded pattern so it tracks exactly
|
|
48
|
+
what git registered.
|
|
49
|
+
"""
|
|
50
|
+
root = Path(worktree_path).resolve()
|
|
51
|
+
r = _git(root, "worktree", "list", "--porcelain")
|
|
52
|
+
if r.returncode != 0:
|
|
53
|
+
return ()
|
|
54
|
+
out: list[str] = []
|
|
55
|
+
for line in r.stdout.splitlines():
|
|
56
|
+
if not line.startswith("worktree "):
|
|
57
|
+
continue
|
|
58
|
+
wt = Path(line[len("worktree "):].strip()).resolve()
|
|
59
|
+
if wt == root:
|
|
60
|
+
continue
|
|
61
|
+
try:
|
|
62
|
+
rel = wt.relative_to(root)
|
|
63
|
+
except ValueError:
|
|
64
|
+
continue
|
|
65
|
+
out.append(str(rel))
|
|
66
|
+
return tuple(out)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def dirty_entries_excluding_okstra(cwd) -> list[str]:
|
|
70
|
+
"""`git status --short` rows for changes outside okstra-owned paths.
|
|
71
|
+
|
|
72
|
+
okstra-owned = `okstra_clean_gate_excludes` (e.g. `.okstra`, synced dirs)
|
|
73
|
+
plus `nested_worktree_excludes` (stage worktrees nested under `cwd`).
|
|
74
|
+
|
|
75
|
+
This is the clean-worktree question every okstra gate asks, and the one a
|
|
76
|
+
plan step must ask through `okstra worktree-status` rather than through a
|
|
77
|
+
bare `git status --porcelain`: okstra provisions `.okstra` plus the synced
|
|
78
|
+
entries into every task worktree, so a bare status is never empty there and
|
|
79
|
+
an assertion built on it fails on okstra's own scaffolding.
|
|
80
|
+
"""
|
|
81
|
+
owned = (*okstra_clean_gate_excludes(Path(cwd)), *nested_worktree_excludes(cwd))
|
|
82
|
+
excludes = [f":(exclude){p}" for p in owned]
|
|
83
|
+
out = _git(Path(cwd), "status", "--short", "--", ".", *excludes).stdout
|
|
84
|
+
return [line for line in out.splitlines() if line.strip()]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def is_dirty_excluding_okstra(cwd) -> bool:
|
|
88
|
+
"""True iff the worktree has changes outside okstra-owned paths."""
|
|
89
|
+
return bool(dirty_entries_excluding_okstra(cwd))
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""provision 하기 전에 무엇이 일어날지 답한다.
|
|
2
|
+
|
|
3
|
+
위저드는 사용자에게 경로와 브랜치를 보여 준 뒤 확인을 받는다. 그 시점에 이미
|
|
4
|
+
worktree 를 만들어 버리면 안 되므로, 판단(재사용인가 신규인가, 어느 경로인가)만
|
|
5
|
+
떼어 여기에 둔다. 레지스트리를 읽기는 하지만 예약하지는 않는다 — 예약은
|
|
6
|
+
`provision` 의 몫이다.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from .. import worktree_registry
|
|
14
|
+
from .git_ops import _is_inside_non_main_worktree, is_git_work_tree
|
|
15
|
+
from .naming import _safe_segment, compute_branch_name, compute_worktree_path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class WorktreeDecision:
|
|
20
|
+
"""Side-effect-free preview of what `provision_task_worktree` would do.
|
|
21
|
+
|
|
22
|
+
status:
|
|
23
|
+
- "new": no active registry entry; a fresh worktree would be created
|
|
24
|
+
- "reused": registry already has this task-key; existing path/branch returned
|
|
25
|
+
- "skipped-in-worktree": project_root is itself a non-main worktree
|
|
26
|
+
- "skipped-not-git": project_root has no .git
|
|
27
|
+
"""
|
|
28
|
+
status: str
|
|
29
|
+
path: str # worktree path (new: prospective; reuse: existing; skip: project_root)
|
|
30
|
+
branch: str = "" # new: prospective branch; reused: existing branch
|
|
31
|
+
base_ref: str = "" # new: requested base_ref; reused: existing base
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def preview_worktree_decision(
|
|
35
|
+
*,
|
|
36
|
+
project_root,
|
|
37
|
+
project_id: str,
|
|
38
|
+
task_group_segment: str,
|
|
39
|
+
task_id_segment: str,
|
|
40
|
+
work_category: str,
|
|
41
|
+
base_ref: str = "",
|
|
42
|
+
) -> "WorktreeDecision":
|
|
43
|
+
"""Side-effect-free: what provision_task_worktree WOULD do, without touching disk.
|
|
44
|
+
|
|
45
|
+
Mirrors provision's decision branches exactly; reuses the same read-only
|
|
46
|
+
helpers so preview never diverges from the actual provisioning result.
|
|
47
|
+
"""
|
|
48
|
+
project_root = Path(project_root)
|
|
49
|
+
if not is_git_work_tree(project_root):
|
|
50
|
+
return WorktreeDecision(status="skipped-not-git", path=str(project_root))
|
|
51
|
+
if _is_inside_non_main_worktree(project_root):
|
|
52
|
+
return WorktreeDecision(status="skipped-in-worktree", path=str(project_root))
|
|
53
|
+
safe_project = _safe_segment(project_id)
|
|
54
|
+
safe_group = _safe_segment(task_group_segment)
|
|
55
|
+
safe_task = _safe_segment(task_id_segment)
|
|
56
|
+
existing = worktree_registry.lookup(safe_project, safe_group, safe_task)
|
|
57
|
+
if existing is not None and existing.status == "active":
|
|
58
|
+
return WorktreeDecision(
|
|
59
|
+
status="reused", path=existing.worktree_path,
|
|
60
|
+
branch=existing.branch, base_ref=existing.base_ref,
|
|
61
|
+
)
|
|
62
|
+
return WorktreeDecision(
|
|
63
|
+
status="new",
|
|
64
|
+
path=str(compute_worktree_path(
|
|
65
|
+
project_id=safe_project, task_group_segment=safe_group,
|
|
66
|
+
task_id_segment=safe_task)),
|
|
67
|
+
branch=compute_branch_name(work_category=work_category, task_id_segment=safe_task),
|
|
68
|
+
base_ref=base_ref,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class StageWorktreeDecision:
|
|
74
|
+
"""Side-effect-free decision for one concrete implementation stage."""
|
|
75
|
+
|
|
76
|
+
status: str
|
|
77
|
+
path: str
|
|
78
|
+
branch: str = ""
|
|
79
|
+
base_ref: str = ""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def resolve_stage_worktree_decision(
|
|
83
|
+
*,
|
|
84
|
+
project_id: str,
|
|
85
|
+
task_group_segment: str,
|
|
86
|
+
task_id_segment: str,
|
|
87
|
+
work_category: str,
|
|
88
|
+
stage_number: int,
|
|
89
|
+
) -> StageWorktreeDecision:
|
|
90
|
+
"""Resolve whether one concrete stage worktree is new or reusable."""
|
|
91
|
+
safe_project = _safe_segment(project_id)
|
|
92
|
+
safe_group = _safe_segment(task_group_segment)
|
|
93
|
+
safe_task = _safe_segment(task_id_segment)
|
|
94
|
+
existing = worktree_registry.lookup(
|
|
95
|
+
safe_project, safe_group, safe_task, stage_number=stage_number)
|
|
96
|
+
if existing is not None and _stage_entry_is_reusable(existing):
|
|
97
|
+
return StageWorktreeDecision(
|
|
98
|
+
status="reused",
|
|
99
|
+
path=existing.worktree_path,
|
|
100
|
+
branch=existing.branch,
|
|
101
|
+
base_ref=existing.base_ref,
|
|
102
|
+
)
|
|
103
|
+
return StageWorktreeDecision(
|
|
104
|
+
status="new",
|
|
105
|
+
path=str(compute_worktree_path(
|
|
106
|
+
project_id=safe_project, task_group_segment=safe_group,
|
|
107
|
+
task_id_segment=safe_task, stage_number=stage_number)),
|
|
108
|
+
branch=compute_branch_name(
|
|
109
|
+
work_category=work_category, task_id_segment=safe_task,
|
|
110
|
+
stage_number=stage_number),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _stage_entry_is_reusable(entry: worktree_registry.WorktreeEntry) -> bool:
|
|
115
|
+
"""Whether a registered stage worktree can be entered by this run.
|
|
116
|
+
|
|
117
|
+
`active` is the live-run case. `released` with the directory still on disk is
|
|
118
|
+
the fix-run case: a stage whose verifier returned FAIL records a `failed`
|
|
119
|
+
consumers row, which frees the occupancy but deliberately keeps the worktree
|
|
120
|
+
and its branch as the reviewable stack. Re-entry MUST reuse that tree —
|
|
121
|
+
provisioning anew refuses on the existing path and branch. After whole-task
|
|
122
|
+
final-verification removes the directory the entry stops being reusable, so
|
|
123
|
+
the stage provisions from scratch.
|
|
124
|
+
"""
|
|
125
|
+
if entry.status == "active":
|
|
126
|
+
return True
|
|
127
|
+
return entry.status == "released" and Path(entry.worktree_path).is_dir()
|