okstra 0.186.7 → 0.187.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli-registry.d.mts +88 -1
- package/dist/cli-registry.mjs +68 -111
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +0 -1
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +8 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.mjs +1 -14
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/config.mjs +38 -40
- package/dist/commands/lifecycle/config.mjs.map +1 -1
- package/dist/commands/lifecycle/doctor.d.mts +22 -7
- package/dist/commands/lifecycle/doctor.mjs +77 -49
- package/dist/commands/lifecycle/doctor.mjs.map +1 -1
- package/dist/commands/lifecycle/install.d.mts +12 -10
- package/dist/commands/lifecycle/install.mjs +104 -39
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/lifecycle/paths.mjs +8 -3
- package/dist/commands/lifecycle/paths.mjs.map +1 -1
- package/dist/commands/lifecycle/preflight.mjs +2 -1
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/lifecycle/setup.mjs +22 -36
- package/dist/commands/lifecycle/setup.mjs.map +1 -1
- package/dist/commands/lifecycle/uninstall.d.mts +4 -2
- package/dist/commands/lifecycle/uninstall.mjs +59 -15
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
- package/dist/commands/memory/memory.mjs +7 -1
- package/dist/commands/memory/memory.mjs.map +1 -1
- package/dist/lib/helper-scripts.d.mts +1 -1
- package/dist/lib/helper-scripts.mjs +10 -18
- package/dist/lib/helper-scripts.mjs.map +1 -1
- package/dist/lib/host-config.d.mts +72 -0
- package/dist/lib/host-config.mjs +404 -0
- package/dist/lib/host-config.mjs.map +1 -0
- package/dist/lib/host-registry-client.d.mts +2 -2
- package/dist/lib/host-registry-client.mjs +0 -3
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/dist/lib/install-assets.d.mts +1 -0
- package/dist/lib/install-assets.mjs +4 -0
- package/dist/lib/install-assets.mjs.map +1 -1
- package/dist/lib/proc.d.mts +2 -0
- package/dist/lib/proc.mjs +12 -0
- package/dist/lib/proc.mjs.map +1 -1
- package/dist/lib/python-command.d.mts +2 -0
- package/dist/lib/python-command.mjs +52 -0
- package/dist/lib/python-command.mjs.map +1 -0
- package/dist/lib/python-helper.d.mts +2 -5
- package/dist/lib/python-helper.mjs +3 -52
- package/dist/lib/python-helper.mjs.map +1 -1
- package/dist/lib/runtime-payload.d.mts +23 -0
- package/dist/lib/runtime-payload.mjs +57 -0
- package/dist/lib/runtime-payload.mjs.map +1 -0
- package/dist/lib/types.d.mts +24 -13
- package/docs/architecture/storage-model.md +21 -5
- package/docs/architecture.md +37 -36
- package/docs/cli.md +55 -76
- package/docs/coding-rules.md +295 -0
- package/docs/container.md +10 -36
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/skills/okstra-code-review.md +0 -1
- package/docs/for-ai/skills/okstra-container-build.md +8 -41
- package/docs/for-ai/skills/okstra-inspect.md +4 -4
- package/docs/for-ai/skills/okstra-manager.md +2 -2
- package/docs/for-ai/skills/okstra-rollup.md +0 -1
- package/docs/for-ai/skills/okstra-run.md +3 -3
- package/docs/for-ai/skills/okstra-user-response.md +0 -1
- package/docs/performance-improvement-plan-v2.md +1 -1
- package/docs/project-structure-overview.md +62 -62
- package/docs/task-process/README.md +3 -3
- package/docs/task-process/common-flow.md +1 -1
- package/docs/task-process/error-analysis.md +3 -3
- package/docs/task-process/implementation-planning.md +3 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +6 -11
- package/runtime/agents/workers/report-writer-worker.md +1 -1
- package/runtime/bin/lib/okstra/cli.sh +4 -0
- package/runtime/bin/lib/okstra/globals.sh +2 -0
- package/runtime/bin/lib/okstra/interactive.sh +26 -173
- package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
- package/runtime/bin/lib/okstra/usage.sh +3 -3
- package/runtime/bin/okstra-compact-reminder.sh +1 -5
- package/runtime/bin/okstra-error-log.py +19 -2
- package/runtime/bin/okstra-import-check.py +26 -0
- package/runtime/bin/okstra-inject-report-index.py +5 -4
- package/runtime/bin/okstra-provider-exec.py +22 -17
- package/runtime/bin/okstra-render-final-report.py +16 -6
- package/runtime/bin/okstra-render-report-views.py +25 -18
- package/runtime/bin/okstra-report-translate.py +30 -18
- package/runtime/bin/okstra-spawn-followups.py +4 -0
- package/runtime/bin/okstra-token-usage.py +3 -0
- package/runtime/bin/okstra.sh +4 -2
- package/runtime/bin/okstra_bootstrap.py +58 -0
- package/runtime/prompts/coding-preflight/overview.md +1 -1
- package/runtime/prompts/duties/planning-worker.md +1 -1
- package/runtime/prompts/launch.template.md +35 -9
- package/runtime/prompts/lead/convergence.md +96 -87
- package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
- package/runtime/prompts/lead/plan-body-verification.md +65 -46
- package/runtime/prompts/lead/report-writer.md +34 -4
- package/runtime/prompts/lead/team-contract.md +4 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +3 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
- package/runtime/prompts/profiles/error-analysis.md +4 -3
- package/runtime/prompts/profiles/final-verification.md +2 -2
- package/runtime/prompts/profiles/implementation-planning.md +22 -17
- package/runtime/prompts/profiles/implementation.md +3 -2
- package/runtime/prompts/profiles/requirements-discovery.md +2 -1
- package/runtime/prompts/wizard/prompts.ko.json +29 -8
- package/runtime/python/okstra_ctl/__init__.py +6 -27
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +40 -22
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +14 -21
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
- package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
- package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
- package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +12 -1
- package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
- package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +215 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +408 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
- package/runtime/python/okstra_ctl/approval_decisions.py +260 -8
- package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
- package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
- package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
- package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
- package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
- package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
- package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
- package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
- package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
- package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
- package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
- package/runtime/python/okstra_ctl/cmux.py +38 -31
- package/runtime/python/okstra_ctl/code_review_target.py +163 -1
- package/runtime/python/okstra_ctl/conformance.py +23 -0
- package/runtime/python/okstra_ctl/container.py +38 -421
- package/runtime/python/okstra_ctl/context_cost.py +16 -2
- package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
- package/runtime/python/okstra_ctl/convergence.py +101 -15
- package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
- package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
- package/runtime/python/okstra_ctl/convergence_store.py +5 -29
- package/runtime/python/okstra_ctl/design_prep.py +108 -12
- package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
- package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
- package/runtime/python/okstra_ctl/dispatch_core.py +102 -25
- package/runtime/python/okstra_ctl/dispatch_state.py +268 -39
- package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -1
- package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
- package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
- package/runtime/python/okstra_ctl/error_log_core.py +1 -1
- package/runtime/python/okstra_ctl/error_report.py +14 -0
- package/runtime/python/okstra_ctl/error_zip.py +18 -2
- package/runtime/python/okstra_ctl/execution_identity.py +100 -7
- package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
- package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +90 -16
- package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
- package/runtime/python/okstra_ctl/handoff.py +24 -1
- package/runtime/python/okstra_ctl/ids.py +6 -16
- package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
- package/runtime/python/okstra_ctl/implementation_options.py +25 -11
- package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
- package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
- package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
- package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
- package/runtime/python/okstra_ctl/index.py +2 -2
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
- package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
- package/runtime/python/okstra_ctl/json_boundary.py +10 -0
- package/runtime/python/okstra_ctl/listing.py +0 -91
- package/runtime/python/okstra_ctl/locks.py +5 -19
- package/runtime/python/okstra_ctl/log_report.py +14 -0
- package/runtime/python/okstra_ctl/manager_cli.py +22 -3
- package/runtime/python/okstra_ctl/manager_launch.py +4 -8
- package/runtime/python/okstra_ctl/material.py +2 -2
- package/runtime/python/okstra_ctl/migrate.py +23 -1
- package/runtime/python/okstra_ctl/model_cli.py +19 -5
- package/runtime/python/okstra_ctl/model_discovery.py +46 -47
- package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
- package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
- package/runtime/python/okstra_ctl/model_io/references.py +370 -0
- package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
- package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
- package/runtime/python/okstra_ctl/model_pool.py +10 -6
- package/runtime/python/okstra_ctl/models.py +8 -8
- package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
- package/runtime/python/okstra_ctl/next_phase.py +121 -69
- package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
- package/runtime/python/okstra_ctl/pane_title.py +11 -8
- package/runtime/python/okstra_ctl/path_hints.py +36 -11
- package/runtime/python/okstra_ctl/paths.py +76 -8
- package/runtime/python/okstra_ctl/plan_items.py +132 -16
- package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
- package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
- package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
- package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
- package/runtime/python/okstra_ctl/prepare_error.py +20 -0
- package/runtime/python/okstra_ctl/prior_planning.py +157 -0
- package/runtime/python/okstra_ctl/profile_show.py +18 -1
- package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
- package/runtime/python/okstra_ctl/recap.py +22 -1
- package/runtime/python/okstra_ctl/reconcile.py +84 -0
- package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
- package/runtime/python/okstra_ctl/render.py +98 -32
- package/runtime/python/okstra_ctl/render_final_report.py +3 -7
- package/runtime/python/okstra_ctl/report_assembly.py +297 -7
- package/runtime/python/okstra_ctl/report_contract.py +30 -7
- package/runtime/python/okstra_ctl/report_finalize.py +206 -15
- package/runtime/python/okstra_ctl/report_html/common.py +2 -2
- package/runtime/python/okstra_ctl/report_html/render.py +8 -12
- package/runtime/python/okstra_ctl/report_language.py +8 -4
- package/runtime/python/okstra_ctl/report_narrative.py +90 -3
- package/runtime/python/okstra_ctl/report_projections.py +8 -2
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
- package/runtime/python/okstra_ctl/report_translation.py +2 -35
- package/runtime/python/okstra_ctl/report_views.py +4 -22
- package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
- package/runtime/python/okstra_ctl/rollup.py +13 -0
- package/runtime/python/okstra_ctl/run.py +639 -138
- package/runtime/python/okstra_ctl/run_audit.py +13 -0
- package/runtime/python/okstra_ctl/run_index_row.py +0 -12
- package/runtime/python/okstra_ctl/seeding.py +21 -11
- package/runtime/python/okstra_ctl/sequence.py +1 -1
- package/runtime/python/okstra_ctl/session.py +64 -14
- package/runtime/python/okstra_ctl/set_work_status.py +18 -0
- package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
- package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
- package/runtime/python/okstra_ctl/stage_map.py +57 -11
- package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
- package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
- package/runtime/python/okstra_ctl/stage_targets.py +3 -12
- package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
- package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
- package/runtime/python/okstra_ctl/task_target.py +4 -16
- package/runtime/python/okstra_ctl/team.py +49 -2
- package/runtime/python/okstra_ctl/time_report.py +17 -2
- package/runtime/python/okstra_ctl/usage_report.py +11 -0
- package/runtime/python/okstra_ctl/user_response.py +55 -218
- package/runtime/python/okstra_ctl/user_response_values.py +242 -0
- package/runtime/python/okstra_ctl/validation_contract.py +3 -0
- package/runtime/python/okstra_ctl/verification_target.py +68 -0
- package/runtime/python/okstra_ctl/wizard.py +457 -92
- package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
- package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
- package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
- package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
- package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
- package/runtime/python/okstra_ctl/worker_request.py +2 -1
- package/runtime/python/okstra_ctl/worker_runner.py +16 -9
- package/runtime/python/okstra_ctl/worker_state.py +16 -1
- package/runtime/python/okstra_ctl/workflow.py +18 -1
- package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
- package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
- package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
- package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
- package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
- package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
- package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
- package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
- package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
- package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
- package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
- package/runtime/python/okstra_ctl/write_policy.py +90 -213
- package/runtime/python/okstra_project/__init__.py +0 -4
- package/runtime/python/okstra_project/dirs.py +2 -2
- package/runtime/python/okstra_project/phase_pointer.py +84 -0
- package/runtime/python/okstra_project/slug.py +24 -0
- package/runtime/python/okstra_project/state.py +22 -230
- package/runtime/python/okstra_token_usage/claude.py +9 -1
- package/runtime/python/okstra_token_usage/cli.py +3 -1
- package/runtime/python/okstra_token_usage/collect.py +71 -10
- package/runtime/python/okstra_token_usage/cursor.py +2 -0
- package/runtime/python/okstra_token_usage/grok.py +1 -5
- package/runtime/python/okstra_token_usage/paths.py +26 -0
- package/runtime/python/okstra_token_usage/pricing.py +19 -7
- package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
- package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
- package/runtime/schemas/final-report-v2.0.schema.json +9 -26
- package/runtime/schemas/final-report-v3.0.schema.json +173 -41
- package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
- package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
- package/runtime/skills/okstra-container-build/SKILL.md +16 -47
- package/runtime/skills/okstra-inspect/facets/history.md +1 -1
- package/runtime/skills/okstra-inspect/facets/status.md +7 -6
- package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +7 -5
- package/runtime/templates/report-writer-prompt-preamble.md +1 -1
- package/runtime/templates/reports/brief.template.md +6 -2
- package/runtime/templates/reports/error-analysis-input.template.md +2 -0
- package/runtime/templates/reports/final-verification-input.template.md +2 -0
- package/runtime/templates/reports/implementation-input.template.md +7 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
- package/runtime/templates/reports/quick-input.template.md +2 -0
- package/runtime/templates/reports/release-handoff-input.template.md +6 -1
- package/runtime/templates/reports/report.js +20 -3
- package/runtime/templates/reports/schedule.template.md +2 -0
- package/runtime/templates/reports/settings.template.json +0 -10
- package/runtime/templates/reports/task-brief.template.md +3 -0
- package/runtime/templates/reports/user-response.template.md +7 -3
- package/runtime/validators/checks/fixtures-01.py +57 -0
- package/runtime/validators/checks/fixtures-02.py +565 -0
- package/runtime/validators/checks/runners-01.py +108 -0
- package/runtime/validators/checks/validate-assets-01.py +60 -0
- package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
- package/runtime/validators/checks/validate-tasks-01.py +46 -0
- package/runtime/validators/checks/validate-tasks-02.py +85 -0
- package/runtime/validators/checks/validate-tasks-03.py +61 -0
- package/runtime/validators/checks/validate-tasks-04.py +118 -0
- package/runtime/validators/forbidden_actions.py +73 -3
- package/runtime/validators/lib/common.sh +5 -0
- package/runtime/validators/lib/fixtures.sh +7 -591
- package/runtime/validators/lib/paths.sh +13 -0
- package/runtime/validators/lib/runners.sh +6 -104
- package/runtime/validators/lib/summary.sh +1 -1
- package/runtime/validators/lib/validate-assets.sh +6 -56
- package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
- package/runtime/validators/lib/validate-tasks.sh +9 -294
- package/runtime/validators/validate-implementation-plan-stages.py +4 -4
- package/runtime/validators/validate-run.py +1369 -2654
- package/runtime/validators/validate-workflow.sh +56 -16
- package/runtime/validators/validate_improvement_report.py +2 -1
- package/runtime/validators/validate_session_conformance.py +295 -49
- package/dist/commands/execute/agent-prompt.d.mts +0 -1
- package/dist/commands/execute/agent-prompt.mjs +0 -24
- package/dist/commands/execute/agent-prompt.mjs.map +0 -1
- package/dist/commands/execute/codex-dispatch.d.mts +0 -3
- package/dist/commands/execute/codex-dispatch.mjs +0 -6
- package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
- package/dist/commands/execute/codex-run.d.mts +0 -3
- package/dist/commands/execute/codex-run.mjs +0 -62
- package/dist/commands/execute/codex-run.mjs.map +0 -1
- package/dist/commands/execute/convergence.d.mts +0 -1
- package/dist/commands/execute/convergence.mjs +0 -38
- package/dist/commands/execute/convergence.mjs.map +0 -1
- package/dist/commands/execute/error-log.d.mts +0 -1
- package/dist/commands/execute/error-log.mjs +0 -18
- package/dist/commands/execute/error-log.mjs.map +0 -1
- package/dist/commands/execute/git-reconcile.d.mts +0 -1
- package/dist/commands/execute/git-reconcile.mjs +0 -30
- package/dist/commands/execute/git-reconcile.mjs.map +0 -1
- package/dist/commands/execute/handoff.d.mts +0 -1
- package/dist/commands/execute/handoff.mjs +0 -31
- package/dist/commands/execute/handoff.mjs.map +0 -1
- package/dist/commands/execute/incremental-carry.d.mts +0 -1
- package/dist/commands/execute/incremental-carry.mjs +0 -20
- package/dist/commands/execute/incremental-carry.mjs.map +0 -1
- package/dist/commands/execute/incremental-scope.d.mts +0 -1
- package/dist/commands/execute/incremental-scope.mjs +0 -29
- package/dist/commands/execute/incremental-scope.mjs.map +0 -1
- package/dist/commands/execute/integrate-stages.d.mts +0 -1
- package/dist/commands/execute/integrate-stages.mjs +0 -24
- package/dist/commands/execute/integrate-stages.mjs.map +0 -1
- package/dist/commands/execute/pane-title.d.mts +0 -1
- package/dist/commands/execute/pane-title.mjs +0 -20
- package/dist/commands/execute/pane-title.mjs.map +0 -1
- package/dist/commands/execute/plan-items.d.mts +0 -1
- package/dist/commands/execute/plan-items.mjs +0 -9
- package/dist/commands/execute/plan-items.mjs.map +0 -1
- package/dist/commands/execute/plan-validate.d.mts +0 -1
- package/dist/commands/execute/plan-validate.mjs +0 -68
- package/dist/commands/execute/plan-validate.mjs.map +0 -1
- package/dist/commands/execute/plan-verify.d.mts +0 -1
- package/dist/commands/execute/plan-verify.mjs +0 -43
- package/dist/commands/execute/plan-verify.mjs.map +0 -1
- package/dist/commands/execute/spawn-followups.d.mts +0 -1
- package/dist/commands/execute/spawn-followups.mjs +0 -22
- package/dist/commands/execute/spawn-followups.mjs.map +0 -1
- package/dist/commands/execute/team.d.mts +0 -3
- package/dist/commands/execute/team.mjs +0 -66
- package/dist/commands/execute/team.mjs.map +0 -1
- package/dist/commands/execute/token-usage.d.mts +0 -1
- package/dist/commands/execute/token-usage.mjs +0 -19
- package/dist/commands/execute/token-usage.mjs.map +0 -1
- package/dist/commands/execute/worker-audit-check.d.mts +0 -1
- package/dist/commands/execute/worker-audit-check.mjs +0 -34
- package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
- package/dist/commands/execute/worker-dispatch.d.mts +0 -7
- package/dist/commands/execute/worker-dispatch.mjs +0 -64
- package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
- package/dist/commands/execute/worker-state.d.mts +0 -1
- package/dist/commands/execute/worker-state.mjs +0 -28
- package/dist/commands/execute/worker-state.mjs.map +0 -1
- package/dist/commands/execute/worktree-lookup.d.mts +0 -1
- package/dist/commands/execute/worktree-lookup.mjs +0 -92
- package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
- package/dist/commands/execute/worktree-status.d.mts +0 -1
- package/dist/commands/execute/worktree-status.mjs +0 -121
- package/dist/commands/execute/worktree-status.mjs.map +0 -1
- package/dist/commands/inspect/code-review.d.mts +0 -1
- package/dist/commands/inspect/code-review.mjs +0 -32
- package/dist/commands/inspect/code-review.mjs.map +0 -1
- package/dist/commands/inspect/container.d.mts +0 -1
- package/dist/commands/inspect/container.mjs +0 -25
- package/dist/commands/inspect/container.mjs.map +0 -1
- package/dist/commands/inspect/context-cost.d.mts +0 -1
- package/dist/commands/inspect/context-cost.mjs +0 -25
- package/dist/commands/inspect/context-cost.mjs.map +0 -1
- package/dist/commands/inspect/design-prep.d.mts +0 -1
- package/dist/commands/inspect/design-prep.mjs +0 -22
- package/dist/commands/inspect/design-prep.mjs.map +0 -1
- package/dist/commands/inspect/error-report.d.mts +0 -1
- package/dist/commands/inspect/error-report.mjs +0 -25
- package/dist/commands/inspect/error-report.mjs.map +0 -1
- package/dist/commands/inspect/error-zip.d.mts +0 -1
- package/dist/commands/inspect/error-zip.mjs +0 -24
- package/dist/commands/inspect/error-zip.mjs.map +0 -1
- package/dist/commands/inspect/log-report.d.mts +0 -1
- package/dist/commands/inspect/log-report.mjs +0 -26
- package/dist/commands/inspect/log-report.mjs.map +0 -1
- package/dist/commands/inspect/model-io.d.mts +0 -1
- package/dist/commands/inspect/model-io.mjs +0 -25
- package/dist/commands/inspect/model-io.mjs.map +0 -1
- package/dist/commands/inspect/profile-show.d.mts +0 -1
- package/dist/commands/inspect/profile-show.mjs +0 -28
- package/dist/commands/inspect/profile-show.mjs.map +0 -1
- package/dist/commands/inspect/recap.d.mts +0 -1
- package/dist/commands/inspect/recap.mjs +0 -30
- package/dist/commands/inspect/recap.mjs.map +0 -1
- package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
- package/dist/commands/inspect/resolve-task-key.mjs +0 -24
- package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
- package/dist/commands/inspect/rollup.d.mts +0 -1
- package/dist/commands/inspect/rollup.mjs +0 -25
- package/dist/commands/inspect/rollup.mjs.map +0 -1
- package/dist/commands/inspect/run-audit.d.mts +0 -1
- package/dist/commands/inspect/run-audit.mjs +0 -25
- package/dist/commands/inspect/run-audit.mjs.map +0 -1
- package/dist/commands/inspect/set-work-status.d.mts +0 -1
- package/dist/commands/inspect/set-work-status.mjs +0 -29
- package/dist/commands/inspect/set-work-status.mjs.map +0 -1
- package/dist/commands/inspect/stage-map.d.mts +0 -1
- package/dist/commands/inspect/stage-map.mjs +0 -131
- package/dist/commands/inspect/stage-map.mjs.map +0 -1
- package/dist/commands/inspect/task-list.d.mts +0 -1
- package/dist/commands/inspect/task-list.mjs +0 -149
- package/dist/commands/inspect/task-list.mjs.map +0 -1
- package/dist/commands/inspect/task-show.d.mts +0 -1
- package/dist/commands/inspect/task-show.mjs +0 -108
- package/dist/commands/inspect/task-show.mjs.map +0 -1
- package/dist/commands/inspect/time-report.d.mts +0 -1
- package/dist/commands/inspect/time-report.mjs +0 -24
- package/dist/commands/inspect/time-report.mjs.map +0 -1
- package/dist/commands/inspect/usage-report.d.mts +0 -1
- package/dist/commands/inspect/usage-report.mjs +0 -23
- package/dist/commands/inspect/usage-report.mjs.map +0 -1
- package/dist/commands/inspect/user-response.d.mts +0 -1
- package/dist/commands/inspect/user-response.mjs +0 -35
- package/dist/commands/inspect/user-response.mjs.map +0 -1
- package/dist/commands/inspect/worker-liveness.d.mts +0 -1
- package/dist/commands/inspect/worker-liveness.mjs +0 -45
- package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
- package/dist/commands/lifecycle/contract-check.d.mts +0 -1
- package/dist/commands/lifecycle/contract-check.mjs +0 -18
- package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
- package/dist/commands/lifecycle/migrate.d.mts +0 -1
- package/dist/commands/lifecycle/migrate.mjs +0 -30
- package/dist/commands/lifecycle/migrate.mjs.map +0 -1
- package/dist/commands/lifecycle/model.d.mts +0 -1
- package/dist/commands/lifecycle/model.mjs +0 -22
- package/dist/commands/lifecycle/model.mjs.map +0 -1
- package/dist/commands/manager.d.mts +0 -3
- package/dist/commands/manager.mjs +0 -50
- package/dist/commands/manager.mjs.map +0 -1
- package/dist/commands/report/agent-activity.d.mts +0 -1
- package/dist/commands/report/agent-activity.mjs +0 -20
- package/dist/commands/report/agent-activity.mjs.map +0 -1
- package/dist/commands/report/approval-decision.d.mts +0 -1
- package/dist/commands/report/approval-decision.mjs +0 -21
- package/dist/commands/report/approval-decision.mjs.map +0 -1
- package/dist/commands/report/design-snapshot.d.mts +0 -1
- package/dist/commands/report/design-snapshot.mjs +0 -19
- package/dist/commands/report/design-snapshot.mjs.map +0 -1
- package/dist/commands/report/finalize.d.mts +0 -4
- package/dist/commands/report/finalize.mjs +0 -64
- package/dist/commands/report/finalize.mjs.map +0 -1
- package/dist/commands/report/inject-report-index.d.mts +0 -1
- package/dist/commands/report/inject-report-index.mjs +0 -21
- package/dist/commands/report/inject-report-index.mjs.map +0 -1
- package/dist/commands/report/render-final-report.d.mts +0 -1
- package/dist/commands/report/render-final-report.mjs +0 -23
- package/dist/commands/report/render-final-report.mjs.map +0 -1
- package/dist/commands/report/render-views.d.mts +0 -1
- package/dist/commands/report/render-views.mjs +0 -27
- package/dist/commands/report/render-views.mjs.map +0 -1
- package/dist/commands/report/translate.d.mts +0 -1
- package/dist/commands/report/translate.mjs +0 -33
- package/dist/commands/report/translate.mjs.map +0 -1
- package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
- package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
- package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
- package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
- package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
- package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
- package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
- package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
- package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
- package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
- package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
- package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
- package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
- package/runtime/bin/okstra-central.sh +0 -152
- package/runtime/bin/okstra-incremental-carry.py +0 -10
- package/runtime/bin/okstra-incremental-scope.py +0 -10
- package/runtime/bin/okstra-team-reconcile.sh +0 -36
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
- package/runtime/python/okstra_ctl/batch.py +0 -60
- package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
- package/runtime/python/okstra_ctl/container_registry.py +0 -72
- package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
- package/runtime/python/okstra_ctl/resolver.py +0 -54
- package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
- package/runtime/python/okstra_ctl/tmux.py +0 -134
- package/runtime/python/okstra_ctl/worktree.py +0 -1099
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""게이트를 읽는다 — 아직 열려 있는 것이 무엇인가.
|
|
2
|
+
|
|
3
|
+
`scan_approval_gate` 와 `scan_open_user_input` 을 run-prep · 위저드 ·
|
|
4
|
+
user-response CLI 가 공유하므로 세 곳이 서로 다른 답을 낼 수 없다.
|
|
5
|
+
판정은 fail-closed 다: 확신을 갖고 읽지 못하면 `unreadable_reason` 을 남기고
|
|
6
|
+
호출자는 soft-pass 대신 승인을 거절해야 한다.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional
|
|
13
|
+
from .parsing import (
|
|
14
|
+
ClarificationItem,
|
|
15
|
+
_LOOSE_SECTION_1_RE,
|
|
16
|
+
_section_1_slice,
|
|
17
|
+
_walk_section_1_table,
|
|
18
|
+
)
|
|
19
|
+
from .dispositions import (
|
|
20
|
+
APPROVAL_BLOCKS,
|
|
21
|
+
PROCEEDING_DISPOSITIONS,
|
|
22
|
+
USER_INPUT_BLOCKS,
|
|
23
|
+
incorporated_clarification_ids,
|
|
24
|
+
row_blocks_progress,
|
|
25
|
+
)
|
|
26
|
+
from .rows import (
|
|
27
|
+
_read_report_text,
|
|
28
|
+
_structured_report_data,
|
|
29
|
+
_v2_row,
|
|
30
|
+
)
|
|
31
|
+
from .sidecars import (
|
|
32
|
+
sidecar_dispositions,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class ClarificationScan:
|
|
38
|
+
"""Fail-closed read of the clarification rows blocking on one `Blocks`
|
|
39
|
+
value set.
|
|
40
|
+
|
|
41
|
+
``unreadable_reason`` is ``None`` only when the scan is confident: the
|
|
42
|
+
rows parsed cleanly (or the report is the legitimate table-less
|
|
43
|
+
placeholder) and ``blockers`` is therefore authoritative. A non-None
|
|
44
|
+
reason means the caller must refuse to act — never soft-pass.
|
|
45
|
+
"""
|
|
46
|
+
blockers: list[ClarificationItem]
|
|
47
|
+
unreadable_reason: Optional[str]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def scan_approval_gate(report_path: Path) -> ClarificationScan:
|
|
51
|
+
"""Scan for ``Blocks=approval`` rows that still block progress.
|
|
52
|
+
|
|
53
|
+
A recorded user proceeding disposition (`accept-risk` / `select` /
|
|
54
|
+
`answer`), including one that lives only in the sidecar, does not block.
|
|
55
|
+
``request-revision`` / ``reject`` still block unless this report's
|
|
56
|
+
``supersessionLedger`` already incorporated that id. The scan refuses to
|
|
57
|
+
guess whenever the schema drifted.
|
|
58
|
+
"""
|
|
59
|
+
return scan_clarification_blockers(
|
|
60
|
+
report_path,
|
|
61
|
+
APPROVAL_BLOCKS,
|
|
62
|
+
honor_sidecar_answers=True,
|
|
63
|
+
sidecar_unblocks_proceeding_only=True,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def scan_open_user_input(report_path: Path) -> ClarificationScan:
|
|
68
|
+
"""Scan for every unresolved row that still owes the user an answer
|
|
69
|
+
(``Blocks`` in ``{approval, next-phase}``)."""
|
|
70
|
+
return scan_clarification_blockers(
|
|
71
|
+
report_path, USER_INPUT_BLOCKS, honor_sidecar_answers=True
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def scan_clarification_blockers(
|
|
76
|
+
report_path: Path,
|
|
77
|
+
blocking_values: frozenset[str],
|
|
78
|
+
*,
|
|
79
|
+
honor_sidecar_answers: bool,
|
|
80
|
+
sidecar_unblocks_proceeding_only: bool = False,
|
|
81
|
+
) -> ClarificationScan:
|
|
82
|
+
"""Shared fail-closed clarification walk for both gates above — schema-v2
|
|
83
|
+
reads its rows from the data sibling and schema-v1 from the §1 table.
|
|
84
|
+
``honor_sidecar_answers`` hides rows the sidecar already answered.
|
|
85
|
+
``sidecar_unblocks_proceeding_only`` keeps an unincorporated
|
|
86
|
+
``request-revision`` / ``reject`` as a blocker so a return choice cannot
|
|
87
|
+
start the next phase. A ledger entry for that id means this report already
|
|
88
|
+
absorbed the return.
|
|
89
|
+
"""
|
|
90
|
+
v2_scan = _scan_v2_blockers(report_path, blocking_values)
|
|
91
|
+
scan = (
|
|
92
|
+
v2_scan if v2_scan is not None
|
|
93
|
+
else scan_section_1_blockers(_read_report_text(report_path), blocking_values)
|
|
94
|
+
)
|
|
95
|
+
return (
|
|
96
|
+
_resolve_blockers_answered_by_user(
|
|
97
|
+
report_path,
|
|
98
|
+
scan,
|
|
99
|
+
proceeding_only=sidecar_unblocks_proceeding_only,
|
|
100
|
+
)
|
|
101
|
+
if honor_sidecar_answers
|
|
102
|
+
else scan
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _resolve_blockers_answered_by_user(
|
|
107
|
+
report_path: Path,
|
|
108
|
+
scan: ClarificationScan,
|
|
109
|
+
*,
|
|
110
|
+
proceeding_only: bool = False,
|
|
111
|
+
) -> ClarificationScan:
|
|
112
|
+
"""사용자가 사이드카로 답한 행을 blocker 에서 뺀 스캔.
|
|
113
|
+
|
|
114
|
+
답의 정본은 사용자의 `user-responses/` 사이드카다. 리포트의 `Status` 는 그
|
|
115
|
+
run 이 스스로 적어둔 값이고, 답이 사이드카로만 들어오는 경로(HTML 뷰의
|
|
116
|
+
`Export user response`, `okstra user-response write`)에서는 갱신되지 않는다.
|
|
117
|
+
사용자 입력 목록은 사이드카 답변이 있는 항목을 다시 묻지 않는다. 승인
|
|
118
|
+
게이트는 진행 처분만 차단에서 뺀다.
|
|
119
|
+
|
|
120
|
+
fail-closed 는 그대로다: 행 자체를 못 읽은 스캔(`unreadable_reason`)은
|
|
121
|
+
어떤 id 가 blocker 인지 모르는 상태이므로 사이드카로 덮지 않는다.
|
|
122
|
+
"""
|
|
123
|
+
if scan.unreadable_reason is not None or not scan.blockers:
|
|
124
|
+
return scan
|
|
125
|
+
dispositions = sidecar_dispositions(report_path)
|
|
126
|
+
if not dispositions:
|
|
127
|
+
return scan
|
|
128
|
+
answered_ids = {
|
|
129
|
+
row_id
|
|
130
|
+
for row_id, disposition in dispositions.items()
|
|
131
|
+
if (not proceeding_only) or disposition in PROCEEDING_DISPOSITIONS
|
|
132
|
+
}
|
|
133
|
+
if not answered_ids:
|
|
134
|
+
return scan
|
|
135
|
+
return ClarificationScan(
|
|
136
|
+
[b for b in scan.blockers if b.row_id not in answered_ids], None
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def scan_section_1_blockers(
|
|
141
|
+
report_text: str, blocking_values: frozenset[str]
|
|
142
|
+
) -> ClarificationScan:
|
|
143
|
+
"""Fail-closed §1 walk over any markdown carrying the table — a schema-v1
|
|
144
|
+
report, or the carry-in body a reconciliation just produced."""
|
|
145
|
+
section = _section_1_slice(report_text)
|
|
146
|
+
if section is None:
|
|
147
|
+
if _LOOSE_SECTION_1_RE.search(report_text):
|
|
148
|
+
reason = (
|
|
149
|
+
"`## 1. Clarification Items` heading exists but does not match "
|
|
150
|
+
"the schema heading format (anchor/format drift)"
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
expected = "/".join(sorted(blocking_values))
|
|
154
|
+
reason = (
|
|
155
|
+
"report has no `## 1. Clarification Items` section — the gate "
|
|
156
|
+
f"cannot confirm there are no unresolved `Blocks={expected}` rows"
|
|
157
|
+
)
|
|
158
|
+
return ClarificationScan([], reason)
|
|
159
|
+
table = _walk_section_1_table(section)
|
|
160
|
+
if table.items is None:
|
|
161
|
+
if table.has_pipe_lines:
|
|
162
|
+
return ClarificationScan([], (
|
|
163
|
+
"§1 contains a table but its header row is not the schema "
|
|
164
|
+
"header (`| ... | Statement | Expected form | User input |`)"
|
|
165
|
+
))
|
|
166
|
+
# Renderer's emptyState placeholder: heading is intact and no table
|
|
167
|
+
# was emitted — confidently "no blocking items".
|
|
168
|
+
return ClarificationScan([], None)
|
|
169
|
+
if table.unparsed_row_count:
|
|
170
|
+
return ClarificationScan([], (
|
|
171
|
+
f"§1 table has {table.unparsed_row_count} row(s) whose metadata "
|
|
172
|
+
"cell could not be parsed (Blocks/Status markers missing or "
|
|
173
|
+
"malformed)"
|
|
174
|
+
))
|
|
175
|
+
blockers = [
|
|
176
|
+
it for it in table.items
|
|
177
|
+
if it.blocks in blocking_values and row_blocks_progress(it.status)
|
|
178
|
+
]
|
|
179
|
+
return ClarificationScan(blockers, None)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _scan_v2_blockers(
|
|
183
|
+
report_path: Path, blocking_values: frozenset[str]
|
|
184
|
+
) -> Optional[ClarificationScan]:
|
|
185
|
+
"""schema-v2 data.json 기준 스캔, 이 리포트가 v2 가 아니면 ``None``.
|
|
186
|
+
필수 필드가 빠진 행은 §1 의 unparsed row 와 같이 fail-closed 로 다룬다."""
|
|
187
|
+
data = _structured_report_data(report_path)
|
|
188
|
+
if data is None:
|
|
189
|
+
return None
|
|
190
|
+
entries = data.get("clarificationItems")
|
|
191
|
+
if entries is None:
|
|
192
|
+
return ClarificationScan([], None)
|
|
193
|
+
if not isinstance(entries, list):
|
|
194
|
+
return ClarificationScan([], (
|
|
195
|
+
"schema-v2 data.json `clarificationItems` is not an array — the "
|
|
196
|
+
"gate cannot read the clarification rows"
|
|
197
|
+
))
|
|
198
|
+
rows = [_v2_row(e) if isinstance(e, dict) else None for e in entries]
|
|
199
|
+
unparsed = sum(1 for row in rows if row is None)
|
|
200
|
+
if unparsed:
|
|
201
|
+
return ClarificationScan([], (
|
|
202
|
+
f"schema-v2 data.json has {unparsed} `clarificationItems` row(s) "
|
|
203
|
+
"missing id/blocks/status"
|
|
204
|
+
))
|
|
205
|
+
incorporated = incorporated_clarification_ids(data)
|
|
206
|
+
blockers = [
|
|
207
|
+
row["item"] for row in rows
|
|
208
|
+
if row["item"].blocks in blocking_values
|
|
209
|
+
and row_blocks_progress(
|
|
210
|
+
row["item"].status,
|
|
211
|
+
str(row.get("disposition") or ""),
|
|
212
|
+
incorporated=row["item"].row_id in incorporated,
|
|
213
|
+
)
|
|
214
|
+
]
|
|
215
|
+
return ClarificationScan(blockers, None)
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""사용자 응답 사이드카를 찾아 읽고 리포트에 붙일 형태로 만든다.
|
|
2
|
+
|
|
3
|
+
사이드카는 사용자가 답한 내용을 담은 별도 파일이다. 어떤 것을 리포트에
|
|
4
|
+
첨부할지, 어떤 답이 어떤 id 에 붙는지를 여기서 정한다.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from ..user_response_values import (
|
|
9
|
+
UserResponseError,
|
|
10
|
+
parse_analysis_review,
|
|
11
|
+
parse_user_response_entries,
|
|
12
|
+
)
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import re
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
ANSWER_DISPOSITIONS = frozenset({
|
|
18
|
+
"answer",
|
|
19
|
+
"select",
|
|
20
|
+
"accept-risk",
|
|
21
|
+
"request-revision",
|
|
22
|
+
"reject",
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def user_response_sidecars(source: Path) -> list[Path]:
|
|
27
|
+
"""``source`` 형제 ``user-responses/`` 의 ``user-response-*.md`` 목록(이름 순).
|
|
28
|
+
|
|
29
|
+
``source`` 가 ``runs/<task-type>/reports/final-report-*.md`` 레이아웃일 때만
|
|
30
|
+
형제 ``user-responses/`` 디렉토리를 찾는다(HTML 뷰의 `Export user response`
|
|
31
|
+
가 내려준 파일을 사용자가 거기 저장). 그 외 경로·디렉토리 부재 시 빈 목록.
|
|
32
|
+
carry-in 첨부 본문(``clarification_response_with_sidecars``)과 위저드 안내
|
|
33
|
+
문구의 사이드카 카운트가 같은 한 곳을 보도록 하는 단일 참조점이다.
|
|
34
|
+
"""
|
|
35
|
+
responses_dir = source.parent.parent / "user-responses"
|
|
36
|
+
if source.parent.name != "reports" or not responses_dir.is_dir():
|
|
37
|
+
return []
|
|
38
|
+
return sorted(
|
|
39
|
+
p for p in responses_dir.glob("user-response-*.md") if p.is_file()
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
_ANALYSIS_REPORT_NAME_RE = re.compile(
|
|
44
|
+
r"^final-report-(?P<task_type>project-analysis|feature-analysis|"
|
|
45
|
+
r"change-impact-analysis)-(?P<seq>\d{3})\.md$"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_SIDECAR_FRONTMATTER_RE = re.compile(
|
|
50
|
+
r"\A---[ \t]*\r?\n(?P<body>.*?)(?:\r?\n)---[ \t]*(?:\r?\n|\Z)",
|
|
51
|
+
re.DOTALL,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_ANALYSIS_REVIEW_HEADING_RE = re.compile(
|
|
56
|
+
r"^## ANALYSIS REVIEW\s*$", re.MULTILINE
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _sidecar_frontmatter_value(text: str, key: str) -> str:
|
|
61
|
+
match = _SIDECAR_FRONTMATTER_RE.match(text)
|
|
62
|
+
if match is None:
|
|
63
|
+
return ""
|
|
64
|
+
value = re.search(
|
|
65
|
+
rf"^{re.escape(key)}:\s*(\S.*?)\s*$",
|
|
66
|
+
match.group("body"),
|
|
67
|
+
re.MULTILINE,
|
|
68
|
+
)
|
|
69
|
+
return value.group(1) if value else ""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _sidecars_for_attachment(source: Path) -> list[Path]:
|
|
73
|
+
sidecars = user_response_sidecars(source)
|
|
74
|
+
report_match = _ANALYSIS_REPORT_NAME_RE.fullmatch(source.name)
|
|
75
|
+
if report_match is None:
|
|
76
|
+
return sidecars
|
|
77
|
+
expected_source = (
|
|
78
|
+
f"runs/{report_match.group('task_type')}/reports/{source.name}"
|
|
79
|
+
)
|
|
80
|
+
ordinary: list[Path] = []
|
|
81
|
+
candidates: list[tuple[Path, str]] = []
|
|
82
|
+
for sidecar in sidecars:
|
|
83
|
+
try:
|
|
84
|
+
text = sidecar.read_text(encoding="utf-8")
|
|
85
|
+
except OSError:
|
|
86
|
+
ordinary.append(sidecar)
|
|
87
|
+
continue
|
|
88
|
+
if _ANALYSIS_REVIEW_HEADING_RE.search(text) is None:
|
|
89
|
+
ordinary.append(sidecar)
|
|
90
|
+
continue
|
|
91
|
+
source_report = _sidecar_frontmatter_value(text, "source-report")
|
|
92
|
+
seq = _sidecar_frontmatter_value(text, "seq")
|
|
93
|
+
if source_report and source_report != expected_source:
|
|
94
|
+
continue
|
|
95
|
+
if seq and seq != report_match.group("seq"):
|
|
96
|
+
continue
|
|
97
|
+
candidates.append((sidecar, text))
|
|
98
|
+
|
|
99
|
+
valid: list[Path] = []
|
|
100
|
+
malformed: list[Path] = []
|
|
101
|
+
for sidecar, text in candidates:
|
|
102
|
+
try:
|
|
103
|
+
review = parse_analysis_review(text)
|
|
104
|
+
except UserResponseError:
|
|
105
|
+
malformed.append(sidecar)
|
|
106
|
+
continue
|
|
107
|
+
if review is not None and (
|
|
108
|
+
review.source_report == expected_source
|
|
109
|
+
and review.seq == report_match.group("seq")
|
|
110
|
+
):
|
|
111
|
+
valid.append(sidecar)
|
|
112
|
+
else:
|
|
113
|
+
malformed.append(sidecar)
|
|
114
|
+
selected = valid if valid else malformed
|
|
115
|
+
return sorted([*ordinary, *selected])
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _sidecar_answer_records(source: Path) -> dict[str, tuple[str, str]]:
|
|
119
|
+
"""사이드카 답을 `{id: (value, disposition)}` 로 모은다.
|
|
120
|
+
|
|
121
|
+
`disposition` 이 `ANSWER_DISPOSITIONS` 에 속하는 항목만 답으로 센다.
|
|
122
|
+
`reframe` 은 답이 아니므로 집합에서 빠진다. 같은 id 는 이름순 마지막이
|
|
123
|
+
이긴다 — 최신이 reframe 이거나 값이 비면 앞선 답을 지운다.
|
|
124
|
+
"""
|
|
125
|
+
answers: dict[str, tuple[str, str]] = {}
|
|
126
|
+
for sidecar in user_response_sidecars(source):
|
|
127
|
+
for entry in parse_user_response_entries(
|
|
128
|
+
sidecar.read_text(encoding="utf-8")
|
|
129
|
+
):
|
|
130
|
+
if entry.value and entry.disposition in ANSWER_DISPOSITIONS:
|
|
131
|
+
answers[entry.response_id] = (entry.value, entry.disposition)
|
|
132
|
+
else:
|
|
133
|
+
answers.pop(entry.response_id, None)
|
|
134
|
+
return answers
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def sidecar_answers(source: Path) -> dict[str, str]:
|
|
138
|
+
"""`user-responses/` 사이드카들의 답변을 `{clarification-id: value}` 로 모은다.
|
|
139
|
+
|
|
140
|
+
사용자가 답한 항목이 무엇인지 아는 단일 참조점 — carry-in 병합도, 승인
|
|
141
|
+
게이트도, 스킬의 열린 항목 목록도 전부 이 한 곳을 본다.
|
|
142
|
+
"""
|
|
143
|
+
return {
|
|
144
|
+
row_id: value
|
|
145
|
+
for row_id, (value, _disposition) in _sidecar_answer_records(source).items()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def sidecar_dispositions(source: Path) -> dict[str, str]:
|
|
150
|
+
"""사이드카 답의 처분을 `{clarification-id: disposition}` 로 모은다."""
|
|
151
|
+
return {
|
|
152
|
+
row_id: disposition
|
|
153
|
+
for row_id, (_value, disposition) in _sidecar_answer_records(source).items()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
_ATTACHED_SECTION_RE = re.compile(
|
|
158
|
+
r"^## (?P<name>user-response-[^\n]*?\.md)\s*$", re.MULTILINE
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def attached_response_sections(text: str) -> list[tuple[str, str]]:
|
|
163
|
+
"""`# Attached User Responses` 본문을 `(사이드카 이름, 사이드바 본문)` 으로 되돌린다.
|
|
164
|
+
|
|
165
|
+
`attached_user_responses_section` 의 역방향이다. 묶음 파일은 태스크 수준으로
|
|
166
|
+
누적되므로 답이 어느 run 에서 나왔는지는 각 구간의 frontmatter 만 안다 —
|
|
167
|
+
구간을 나누지 않으면 그 출처가 사라진다. 구간 이름을 못 찾으면 빈 목록:
|
|
168
|
+
묶음이 아닌 파일을 통째로 한 구간처럼 다루면 출처를 지어내게 된다.
|
|
169
|
+
"""
|
|
170
|
+
matches = list(_ATTACHED_SECTION_RE.finditer(text))
|
|
171
|
+
sections: list[tuple[str, str]] = []
|
|
172
|
+
for index, match in enumerate(matches):
|
|
173
|
+
end = matches[index + 1].start() if index + 1 < len(matches) else len(text)
|
|
174
|
+
sections.append((match.group("name"), text[match.end():end].strip()))
|
|
175
|
+
return sections
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def response_source_report(section_body: str) -> str:
|
|
179
|
+
"""구간이 답한 대상 리포트의 데이터 기록 파일 이름.
|
|
180
|
+
|
|
181
|
+
`source-data` 가 있으면 그것이 정본이다. 초기 사이드카는 `source-report`
|
|
182
|
+
(읽기용 Markdown) 만 실었으므로 그 짝 이름으로 되돌린다.
|
|
183
|
+
"""
|
|
184
|
+
data = _sidecar_frontmatter_value(section_body, "source-data")
|
|
185
|
+
if data:
|
|
186
|
+
return data
|
|
187
|
+
report = _sidecar_frontmatter_value(section_body, "source-report")
|
|
188
|
+
if report.endswith(".md"):
|
|
189
|
+
return f"{report[:-len('.md')]}.data.json"
|
|
190
|
+
return ""
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def attached_user_responses_section(source: Path) -> str:
|
|
194
|
+
"""`source` 형제 `user-responses/` 사이드카만 모은 `# Attached User Responses`
|
|
195
|
+
섹션 본문. 사이드카 부재 시 빈 문자열.
|
|
196
|
+
|
|
197
|
+
plan 본문이 이미 별도 경로(`--approved-plan`)로 참조되는 implementation
|
|
198
|
+
carry-in 에서, 원문을 중복 복사하지 않고 사용자 답변만 instruction-set 에
|
|
199
|
+
첨부할 때 쓴다. `clarification_response_with_sidecars` 와 같은 직렬화 포맷을
|
|
200
|
+
한 곳에서 만들어 두 carry-in 경로가 갈라지지 않게 한다.
|
|
201
|
+
"""
|
|
202
|
+
sidecars = _sidecars_for_attachment(source)
|
|
203
|
+
if not sidecars:
|
|
204
|
+
return ""
|
|
205
|
+
parts = ["# Attached User Responses\n"]
|
|
206
|
+
for sidecar in sidecars:
|
|
207
|
+
parts.append(
|
|
208
|
+
f"\n## {sidecar.name}\n\n"
|
|
209
|
+
f"{sidecar.read_text(encoding='utf-8').strip()}\n"
|
|
210
|
+
)
|
|
211
|
+
return "".join(parts)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"""cmux command helpers for okstra-owned worker surfaces.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
workspace the user is already looking at.
|
|
3
|
+
cmux is a GUI app, so there is no detached-server mode here — every surface
|
|
4
|
+
okstra creates lands in the workspace the user is already looking at.
|
|
6
5
|
"""
|
|
7
6
|
from __future__ import annotations
|
|
8
7
|
|
|
@@ -109,7 +108,7 @@ def cmux_available() -> bool:
|
|
|
109
108
|
Being able to drive cmux is not sufficient. Workers attach to the lead's
|
|
110
109
|
workspace, so a run whose lead location is unresolvable would open panes on
|
|
111
110
|
a screen nobody is watching; degrading to the blocking wrapper is the better
|
|
112
|
-
failure.
|
|
111
|
+
failure. See `resolve_lead_workspace` for when that happens.
|
|
113
112
|
"""
|
|
114
113
|
if not cmux_cli_path():
|
|
115
114
|
return False
|
|
@@ -128,9 +127,9 @@ def resolve_lead_workspace() -> str:
|
|
|
128
127
|
|
|
129
128
|
The UUID is read from the environment rather than from `identify`, whose
|
|
130
129
|
refs are positional and shift as surfaces open and close. `identify` is
|
|
131
|
-
consulted only to prove the environment is current: a
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
consulted only to prove the environment is current: a process that inherits
|
|
131
|
+
a CMUX_* block keeps it verbatim, so those variables can outlive the surface
|
|
132
|
+
they name.
|
|
134
133
|
"""
|
|
135
134
|
workspace = _lead_workspace_env()
|
|
136
135
|
if not workspace:
|
|
@@ -391,8 +390,7 @@ def worker_command_line(
|
|
|
391
390
|
remainder to the shell as a command name.
|
|
392
391
|
|
|
393
392
|
The `cd` is part of the command because neither `new-split` nor
|
|
394
|
-
`respawn-pane`
|
|
395
|
-
does.
|
|
393
|
+
`respawn-pane` takes a working-directory flag.
|
|
396
394
|
|
|
397
395
|
`exec` is here for termination, not for the pane's life, and removing it
|
|
398
396
|
does not keep a finished worker's pane on screen. Measured 2026-08-21: a
|
|
@@ -485,28 +483,6 @@ def restore_lead_width() -> None:
|
|
|
485
483
|
_size_lead_pane(workspace, ())
|
|
486
484
|
|
|
487
485
|
|
|
488
|
-
def capture_surface(surface_uuid: str, *, last_lines: int = 200) -> str:
|
|
489
|
-
"""What the worker's screen shows — for the lead to look at, never to parse.
|
|
490
|
-
|
|
491
|
-
cmux hands back a rendered grid: wrapped to the pane's width with a finite
|
|
492
|
-
history, so a path or a count read off it can be silently truncated.
|
|
493
|
-
"""
|
|
494
|
-
try:
|
|
495
|
-
result = run_cmux(
|
|
496
|
-
[
|
|
497
|
-
"capture-pane",
|
|
498
|
-
"--surface",
|
|
499
|
-
surface_uuid,
|
|
500
|
-
"--scrollback",
|
|
501
|
-
"--lines",
|
|
502
|
-
str(last_lines),
|
|
503
|
-
]
|
|
504
|
-
)
|
|
505
|
-
except (OSError, subprocess.SubprocessError):
|
|
506
|
-
return ""
|
|
507
|
-
return result.stdout if result.returncode == 0 else ""
|
|
508
|
-
|
|
509
|
-
|
|
510
486
|
def socket_reachability() -> str:
|
|
511
487
|
"""Why this process can or cannot reach cmux, decided at syscall level.
|
|
512
488
|
|
|
@@ -551,6 +527,37 @@ def unreachable_reason() -> str:
|
|
|
551
527
|
return LOST_NOTHING if resolve_lead_workspace() else LOST_GONE
|
|
552
528
|
|
|
553
529
|
|
|
530
|
+
def unreachable_advice(reason: str) -> tuple[str, str]:
|
|
531
|
+
"""(관측, 처방) — 사유별로 처방이 갈린다.
|
|
532
|
+
|
|
533
|
+
소켓 거부는 샌드박스가 AF_UNIX connect 를 막은 것이라 소켓 한 줄만 열면
|
|
534
|
+
풀리고 파일시스템 경계는 그대로 둬도 된다. 환경 소실은 CMUX_* 변수 자체가
|
|
535
|
+
없는 것이라 그 한 줄로는 못 고치고 리드를 다시 띄워야 한다.
|
|
536
|
+
|
|
537
|
+
두 소비자가 같은 문구를 써야 한다: 워커를 띄우다 막힌 dispatch
|
|
538
|
+
(`adapters/runtime/cmux.py`) 와, 같은 사유로 표면이 바뀌려는 것을 막는
|
|
539
|
+
prepare (`run.py`). 문구가 한쪽에만 있으면 다른 쪽이 낡은 처방을 안내한다.
|
|
540
|
+
"""
|
|
541
|
+
if reason == LOST_ENVIRONMENT:
|
|
542
|
+
return (
|
|
543
|
+
"this process has no cmux environment left — a sandbox sanitized it",
|
|
544
|
+
"Relaunch the lead from a cmux surface so its CMUX_* variables "
|
|
545
|
+
"reach this process.",
|
|
546
|
+
)
|
|
547
|
+
if reason == LOST_DENIED:
|
|
548
|
+
return (
|
|
549
|
+
"cmux is running but this process may not connect to its socket",
|
|
550
|
+
"Let the lead's sandbox reach that socket. Under codex that is one "
|
|
551
|
+
"key — set `sandbox_workspace_write.network_access = true` in "
|
|
552
|
+
"~/.codex/config.toml, or pass `-c "
|
|
553
|
+
"sandbox_workspace_write.network_access=true` when the lead "
|
|
554
|
+
"launches. The filesystem boundary can stay as it is. A lead "
|
|
555
|
+
"already running keeps the sandbox it started with, so relaunch "
|
|
556
|
+
"it after the change.",
|
|
557
|
+
)
|
|
558
|
+
return ("cmux is no longer reachable from this process", "")
|
|
559
|
+
|
|
560
|
+
|
|
554
561
|
def sidebar_log(workspace: str, message: str, *, level: str = "info") -> None:
|
|
555
562
|
"""Append one line to the workspace sidebar's log."""
|
|
556
563
|
_sidebar_call(
|