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
|
@@ -4,7 +4,8 @@ This is a deliberately narrow JSON Schema implementation that supports
|
|
|
4
4
|
exactly the keywords used by ``schemas/final-report-v2.0.schema.json``:
|
|
5
5
|
|
|
6
6
|
type, required, properties, additionalProperties, enum, const, pattern,
|
|
7
|
-
minLength, minItems, maxItems, uniqueItems, items, minimum, maximum,
|
|
7
|
+
minLength, minItems, maxItems, uniqueItems, prefixItems, items, minimum, maximum,
|
|
8
|
+
boolean schemas, $ref ($defs),
|
|
8
9
|
oneOf, allOf, if/then/else, contains, minContains, maxContains, not
|
|
9
10
|
|
|
10
11
|
We do NOT depend on the ``jsonschema`` PyPI package because its
|
|
@@ -21,7 +22,7 @@ from __future__ import annotations
|
|
|
21
22
|
import json
|
|
22
23
|
import re
|
|
23
24
|
from pathlib import Path
|
|
24
|
-
from typing import Any
|
|
25
|
+
from typing import Any, Mapping
|
|
25
26
|
|
|
26
27
|
class SchemaError(ValueError):
|
|
27
28
|
"""Raised when a schema itself is malformed (e.g. a $ref points
|
|
@@ -102,12 +103,32 @@ def _resolve_ref(ref: str, root: dict) -> dict:
|
|
|
102
103
|
return node
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
_BRANCH_ROOT_PREFIX = f"{_format_path(())}: "
|
|
107
|
+
"""분기 probe 의 경로는 분기 루트 기준이라 `<root>:` 가 매 줄 반복된다.
|
|
108
|
+
바깥 오류가 이미 실제 경로를 싣고 있으므로 안쪽에서는 뗀다."""
|
|
109
|
+
|
|
110
|
+
_BRANCH_ERROR_LIMIT = 8
|
|
111
|
+
"""분기 하나당 실을 오류 수. 나머지는 개수만 적는다 — 열세 개 짜리 목록도
|
|
112
|
+
앞의 여덟이면 어느 블록을 안 썼는지 판정된다."""
|
|
113
|
+
|
|
114
|
+
|
|
105
115
|
class _Validator:
|
|
106
116
|
def __init__(self, root_schema: dict):
|
|
107
117
|
self.root = root_schema
|
|
108
118
|
self.errors: list[str] = []
|
|
109
119
|
|
|
110
|
-
def validate(
|
|
120
|
+
def validate(
|
|
121
|
+
self,
|
|
122
|
+
instance: Any,
|
|
123
|
+
schema: dict | bool,
|
|
124
|
+
path: tuple[str | int, ...],
|
|
125
|
+
) -> None:
|
|
126
|
+
if schema is True:
|
|
127
|
+
return
|
|
128
|
+
if schema is False:
|
|
129
|
+
self._err(path, "value is not allowed by the false schema")
|
|
130
|
+
return
|
|
131
|
+
|
|
111
132
|
# Handle $ref first — everything else is composed under the
|
|
112
133
|
# resolved schema.
|
|
113
134
|
if "$ref" in schema:
|
|
@@ -196,9 +217,14 @@ class _Validator:
|
|
|
196
217
|
for previous in instance[:index]
|
|
197
218
|
):
|
|
198
219
|
self._err(path, "array items are not unique")
|
|
220
|
+
prefix_items = schema.get("prefixItems") or []
|
|
221
|
+
for i, item_schema in enumerate(prefix_items[: len(instance)]):
|
|
222
|
+
self.validate(instance[i], item_schema, path + (i,))
|
|
223
|
+
|
|
199
224
|
items = schema.get("items")
|
|
200
225
|
if items is not None:
|
|
201
|
-
|
|
226
|
+
remaining_items = instance[len(prefix_items) :]
|
|
227
|
+
for i, value in enumerate(remaining_items, start=len(prefix_items)):
|
|
202
228
|
self.validate(value, items, path + (i,))
|
|
203
229
|
contains = schema.get("contains")
|
|
204
230
|
if contains is not None:
|
|
@@ -229,30 +255,78 @@ class _Validator:
|
|
|
229
255
|
if else_schema is not None:
|
|
230
256
|
self.validate(instance, else_schema, path)
|
|
231
257
|
|
|
232
|
-
def _validate_one_of(
|
|
233
|
-
|
|
234
|
-
|
|
258
|
+
def _validate_one_of(
|
|
259
|
+
self,
|
|
260
|
+
instance: Any,
|
|
261
|
+
branches: list[dict | bool],
|
|
262
|
+
path: tuple[str | int, ...],
|
|
263
|
+
) -> None:
|
|
264
|
+
per_branch = [self._branch_errors(instance, branch) for branch in branches]
|
|
265
|
+
matches = sum(1 for errors in per_branch if not errors)
|
|
266
|
+
if matches == 1:
|
|
267
|
+
return
|
|
268
|
+
if matches == 0:
|
|
235
269
|
self._err(
|
|
236
270
|
path,
|
|
237
|
-
|
|
238
|
-
|
|
271
|
+
"oneOf matched no branch (expected exactly 1); "
|
|
272
|
+
+ self._branch_diagnosis(per_branch),
|
|
239
273
|
)
|
|
274
|
+
return
|
|
275
|
+
self._err(
|
|
276
|
+
path,
|
|
277
|
+
f"oneOf matched {matches} branches (expected exactly 1); "
|
|
278
|
+
f"branches: {[self._summarise(b) for b in branches]}",
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def _branch_diagnosis(per_branch: list[list[str]]) -> str:
|
|
283
|
+
"""왜 각 분기가 안 맞았는지. 분기 스키마를 인라인하지 않는다.
|
|
240
284
|
|
|
241
|
-
|
|
285
|
+
종전에는 `oneOf` 실패가 분기 스키마 두 개를 통째로 찍었다. 1,500자
|
|
286
|
+
한 줄이면서 **어느 필드가 빠졌는지는 말하지 않아서**, 읽는 사람이
|
|
287
|
+
narrative 의 키 목록을 뽑아 스키마의 `required` 와 손으로 대조해야
|
|
288
|
+
했다. 분기가 실제로 낸 오류가 그 대조의 답이므로 그것을 싣는다.
|
|
289
|
+
"""
|
|
290
|
+
parts: list[str] = []
|
|
291
|
+
for index, errors in enumerate(per_branch, start=1):
|
|
292
|
+
shown = [
|
|
293
|
+
error[len(_BRANCH_ROOT_PREFIX):]
|
|
294
|
+
if error.startswith(_BRANCH_ROOT_PREFIX)
|
|
295
|
+
else error
|
|
296
|
+
for error in errors[:_BRANCH_ERROR_LIMIT]
|
|
297
|
+
]
|
|
298
|
+
remainder = len(errors) - len(shown)
|
|
299
|
+
if remainder > 0:
|
|
300
|
+
shown.append(f"(+{remainder} more)")
|
|
301
|
+
parts.append(f"branch {index}: " + "; ".join(shown))
|
|
302
|
+
return " | ".join(parts)
|
|
303
|
+
|
|
304
|
+
def _branch_errors(self, instance: Any, schema: dict | bool) -> list[str]:
|
|
305
|
+
probe = _Validator(self.root)
|
|
306
|
+
probe.validate(instance, schema, ())
|
|
307
|
+
return probe.errors
|
|
308
|
+
|
|
309
|
+
def _validate_not(
|
|
310
|
+
self,
|
|
311
|
+
instance: Any,
|
|
312
|
+
sub_schema: dict | bool,
|
|
313
|
+
path: tuple[str | int, ...],
|
|
314
|
+
) -> None:
|
|
242
315
|
if self._matches(instance, sub_schema):
|
|
243
316
|
self._err(path, f"value must NOT match {self._summarise(sub_schema)}")
|
|
244
317
|
|
|
245
|
-
def _matches(self, instance: Any, schema: dict) -> bool:
|
|
246
|
-
"""Cheap 'does this validate' probe used by if/
|
|
318
|
+
def _matches(self, instance: Any, schema: dict | bool) -> bool:
|
|
319
|
+
"""Cheap 'does this validate' probe used by if/contains.
|
|
247
320
|
Returns True iff the sub-schema produces zero errors. Does not
|
|
248
|
-
mutate ``self.errors``.
|
|
321
|
+
mutate ``self.errors``. `oneOf` keeps the errors themselves
|
|
322
|
+
(`_branch_errors`) because it has to say why every branch failed.
|
|
249
323
|
"""
|
|
250
|
-
|
|
251
|
-
probe.validate(instance, schema, ())
|
|
252
|
-
return not probe.errors
|
|
324
|
+
return not self._branch_errors(instance, schema)
|
|
253
325
|
|
|
254
326
|
@staticmethod
|
|
255
|
-
def _summarise(schema: dict) -> str:
|
|
327
|
+
def _summarise(schema: dict | bool) -> str:
|
|
328
|
+
if isinstance(schema, bool):
|
|
329
|
+
return str(schema).lower()
|
|
256
330
|
keys = sorted(k for k in schema if k not in ("description", "$comment"))
|
|
257
331
|
return "{" + ", ".join(f"{k}={schema[k]!r}" for k in keys[:3]) + "}"
|
|
258
332
|
|
|
@@ -260,6 +334,234 @@ class _Validator:
|
|
|
260
334
|
self.errors.append(f"{_format_path(path)}: {message}")
|
|
261
335
|
|
|
262
336
|
|
|
337
|
+
def verdict_token_rule(schema: Mapping[str, Any], task_type: str) -> tuple[str, ...]:
|
|
338
|
+
"""이 task type 의 `finalVerdict.verdictToken` 에 스키마가 허용하는 값.
|
|
339
|
+
|
|
340
|
+
스키마는 `allOf` 의 if/then 가지로 세 묶음을 나눈다(분석형 enum,
|
|
341
|
+
final-verification enum, 나머지 `not-applicable` const). 작성기는 그 가지를
|
|
342
|
+
읽지 못해 `analysis-complete` 를 error-analysis 에 썼고(2026-09-02 실측),
|
|
343
|
+
조립이 아니라 HTML 렌더에서야 거절됐다. 여기서 뽑아 저작 계약에 싣는다.
|
|
344
|
+
가지가 없으면 빈 튜플이다 — 제약이 없다는 뜻이지 실패가 아니다.
|
|
345
|
+
"""
|
|
346
|
+
def _walk(node: Any) -> tuple[str, ...]:
|
|
347
|
+
if isinstance(node, dict):
|
|
348
|
+
condition = node.get("if")
|
|
349
|
+
then = node.get("then")
|
|
350
|
+
if isinstance(condition, dict) and isinstance(then, dict):
|
|
351
|
+
header = (condition.get("properties") or {}).get("header") or {}
|
|
352
|
+
selector = (header.get("properties") or {}).get("taskType") or {}
|
|
353
|
+
allowed = selector.get("enum")
|
|
354
|
+
if allowed is None and "const" in selector:
|
|
355
|
+
allowed = [selector["const"]]
|
|
356
|
+
if isinstance(allowed, list) and task_type in allowed:
|
|
357
|
+
verdict = (then.get("properties") or {}).get("finalVerdict") or {}
|
|
358
|
+
token = (verdict.get("properties") or {}).get("verdictToken") or {}
|
|
359
|
+
if "const" in token:
|
|
360
|
+
return (str(token["const"]),)
|
|
361
|
+
if isinstance(token.get("enum"), list):
|
|
362
|
+
return tuple(str(value) for value in token["enum"])
|
|
363
|
+
for value in node.values():
|
|
364
|
+
found = _walk(value)
|
|
365
|
+
if found:
|
|
366
|
+
return found
|
|
367
|
+
elif isinstance(node, list):
|
|
368
|
+
for value in node:
|
|
369
|
+
found = _walk(value)
|
|
370
|
+
if found:
|
|
371
|
+
return found
|
|
372
|
+
return ()
|
|
373
|
+
|
|
374
|
+
return _walk(dict(schema))
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def task_block_rules(schema: Mapping[str, Any], block_key: str) -> tuple[str, ...]:
|
|
378
|
+
"""이 task type 의 데이터 블록(`properties[block_key]`)이 요구하는 모양을 한
|
|
379
|
+
객체당 한 줄의 저작 계약 문장으로 편다.
|
|
380
|
+
|
|
381
|
+
리드는 report-writer 지시문을 쓸 때 블록 안쪽의 필수 필드·식별자 패턴·
|
|
382
|
+
정수 필드·고정 길이 배열을 알 길이 프롬프트 경로에 없어 스키마 JSON 을
|
|
383
|
+
손으로 파싱했고, 그래도 놓친 만큼 같은 리포트를 다시 썼다(2026-09-03 실측,
|
|
384
|
+
dev-10626 implementation-option-selection: 네 회차). 여기서 뽑아 합성 묶음의
|
|
385
|
+
저작 계약에 싣는다. 블록이 없거나 객체가 아니면 빈 튜플이다.
|
|
386
|
+
|
|
387
|
+
경로는 스키마 키다(`rankedOptions[]` 는 `- Item N` 목록의 각 항목). 같은
|
|
388
|
+
정의(`$ref`)가 두 경로에 나오면 두 번째는 첫 경로를 가리킨다 — 랭킹 옵션과
|
|
389
|
+
감사 항목이 하위 구조 여섯 개를 공유한다.
|
|
390
|
+
"""
|
|
391
|
+
root = dict(schema)
|
|
392
|
+
block = (root.get("properties") or {}).get(block_key)
|
|
393
|
+
if not isinstance(block, dict):
|
|
394
|
+
return ()
|
|
395
|
+
lines: list[str] = []
|
|
396
|
+
described: dict[str, str] = {}
|
|
397
|
+
_describe_object(root, block, block_key, lines, described)
|
|
398
|
+
return tuple(lines)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _resolved(root: dict, node: Any) -> tuple[dict, str | None]:
|
|
402
|
+
"""`$ref` 를 풀고, allOf 가지의 properties/required 를 얕게 합친다.
|
|
403
|
+
|
|
404
|
+
반환값의 둘째는 이 노드가 가리킨 정의 이름 — 같은 정의를 두 경로에서
|
|
405
|
+
두 번 펴지 않기 위해 쓴다.
|
|
406
|
+
"""
|
|
407
|
+
if not isinstance(node, dict):
|
|
408
|
+
return {}, None
|
|
409
|
+
ref_name: str | None = None
|
|
410
|
+
if "$ref" in node:
|
|
411
|
+
ref_name = str(node["$ref"]).rsplit("/", 1)[-1]
|
|
412
|
+
node = _resolve_ref(node["$ref"], root)
|
|
413
|
+
merged = dict(node)
|
|
414
|
+
for branch in node.get("allOf") or []:
|
|
415
|
+
resolved, branch_ref = _resolved(root, branch)
|
|
416
|
+
if ref_name is None:
|
|
417
|
+
ref_name = branch_ref
|
|
418
|
+
properties = dict(merged.get("properties") or {})
|
|
419
|
+
for key, value in (resolved.get("properties") or {}).items():
|
|
420
|
+
existing = properties.get(key)
|
|
421
|
+
properties[key] = {**existing, **value} if isinstance(existing, dict) else value
|
|
422
|
+
merged["properties"] = properties
|
|
423
|
+
merged["required"] = list(
|
|
424
|
+
dict.fromkeys([*(merged.get("required") or []), *(resolved.get("required") or [])])
|
|
425
|
+
)
|
|
426
|
+
for keyword in ("type", "pattern", "const", "enum", "minItems", "maxItems", "items", "prefixItems"):
|
|
427
|
+
if keyword in resolved and keyword not in merged:
|
|
428
|
+
merged[keyword] = resolved[keyword]
|
|
429
|
+
return merged, ref_name
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _describe_object(
|
|
433
|
+
root: dict, node: Any, path: str, lines: list[str], described: dict[str, str],
|
|
434
|
+
) -> None:
|
|
435
|
+
schema, ref_name = _resolved(root, node)
|
|
436
|
+
if ref_name and ref_name in described:
|
|
437
|
+
lines.append(f"`{path}`: same shape as `{described[ref_name]}`")
|
|
438
|
+
return
|
|
439
|
+
if ref_name:
|
|
440
|
+
described[ref_name] = path
|
|
441
|
+
properties = schema.get("properties") or {}
|
|
442
|
+
required = [key for key in (schema.get("required") or []) if isinstance(key, str)]
|
|
443
|
+
optional = [key for key in properties if key not in required]
|
|
444
|
+
parts: list[str] = []
|
|
445
|
+
if required:
|
|
446
|
+
parts.append("required " + ", ".join(f"`{key}`" for key in required))
|
|
447
|
+
if optional:
|
|
448
|
+
parts.append("optional " + ", ".join(f"`{key}`" for key in optional))
|
|
449
|
+
nested: list[tuple[str, Any]] = []
|
|
450
|
+
for key, value in properties.items():
|
|
451
|
+
constraint = _describe_property(root, value, f"{path}.{key}", nested)
|
|
452
|
+
if constraint:
|
|
453
|
+
parts.append(f"`{key}` {constraint}")
|
|
454
|
+
lines.append(f"`{path}`: " + "; ".join(parts))
|
|
455
|
+
for child_path, child in nested:
|
|
456
|
+
_describe_object(root, child, child_path, lines, described)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _describe_property(
|
|
460
|
+
root: dict, node: Any, path: str, nested: list[tuple[str, Any]],
|
|
461
|
+
) -> str:
|
|
462
|
+
"""한 속성의 제약을 한 구절로. 객체 자식은 `nested` 에 넣어 자기 줄을 받는다."""
|
|
463
|
+
schema, _ref = _resolved(root, node)
|
|
464
|
+
branches = schema.get("oneOf") or schema.get("anyOf") or []
|
|
465
|
+
nullable = False
|
|
466
|
+
if branches:
|
|
467
|
+
resolved_branches = [_resolved(root, branch)[0] for branch in branches]
|
|
468
|
+
nullable = any(branch.get("type") == "null" for branch in resolved_branches)
|
|
469
|
+
others = [branch for branch in branches if _resolved(root, branch)[0].get("type") != "null"]
|
|
470
|
+
if len(others) == 1:
|
|
471
|
+
schema = _resolved(root, others[0])[0] | {"__branch": others[0]}
|
|
472
|
+
types = schema.get("type")
|
|
473
|
+
types = types if isinstance(types, list) else ([types] if types else [])
|
|
474
|
+
if "null" in types:
|
|
475
|
+
nullable = True
|
|
476
|
+
types = [item for item in types if item != "null"]
|
|
477
|
+
suffix = " or `_none_`" if nullable else ""
|
|
478
|
+
if "const" in schema:
|
|
479
|
+
return f"exactly `{schema['const']}`{suffix}"
|
|
480
|
+
if isinstance(schema.get("enum"), list):
|
|
481
|
+
return "one of " + ", ".join(f"`{value}`" for value in schema["enum"]) + suffix
|
|
482
|
+
if "properties" in schema or types == ["object"]:
|
|
483
|
+
nested.append((path, schema.get("__branch", node)))
|
|
484
|
+
return f"object (see `{path}`){suffix}"
|
|
485
|
+
if types == ["array"] or "items" in schema or "prefixItems" in schema:
|
|
486
|
+
return _describe_array(root, schema, path, nested) + suffix
|
|
487
|
+
if types == ["integer"]:
|
|
488
|
+
return "integer" + _range(schema) + suffix
|
|
489
|
+
if types == ["number"]:
|
|
490
|
+
return "number" + _range(schema) + suffix
|
|
491
|
+
if types == ["string"]:
|
|
492
|
+
pattern = schema.get("pattern")
|
|
493
|
+
return (f"matches `{pattern}`" if pattern else "string") + suffix
|
|
494
|
+
if types == ["boolean"]:
|
|
495
|
+
return "`true` or `false`" + suffix
|
|
496
|
+
return ""
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def _describe_array(
|
|
500
|
+
root: dict, schema: dict, path: str, nested: list[tuple[str, Any]],
|
|
501
|
+
) -> str:
|
|
502
|
+
minimum = schema.get("minItems")
|
|
503
|
+
maximum = schema.get("maxItems")
|
|
504
|
+
if maximum == 0:
|
|
505
|
+
return "must be empty (`> _none_`)"
|
|
506
|
+
if minimum is not None and minimum == maximum:
|
|
507
|
+
size = f"exactly {maximum} items"
|
|
508
|
+
else:
|
|
509
|
+
bounds = [
|
|
510
|
+
text
|
|
511
|
+
for text in (
|
|
512
|
+
f"at least {minimum}" if minimum else "",
|
|
513
|
+
f"at most {maximum}" if maximum is not None else "",
|
|
514
|
+
)
|
|
515
|
+
if text
|
|
516
|
+
]
|
|
517
|
+
size = "list" + (f" ({', '.join(bounds)})" if bounds else "")
|
|
518
|
+
prefix_items = schema.get("prefixItems")
|
|
519
|
+
if isinstance(prefix_items, list) and prefix_items:
|
|
520
|
+
order = _prefix_order(root, prefix_items)
|
|
521
|
+
first, _ = _resolved(root, prefix_items[0])
|
|
522
|
+
if "properties" in first:
|
|
523
|
+
nested.append((f"{path}[]", prefix_items[0]))
|
|
524
|
+
return f"{size} in this order: {order}; each item (see `{path}[]`)"
|
|
525
|
+
return f"{size} in this order: {order}"
|
|
526
|
+
items = schema.get("items")
|
|
527
|
+
item_schema, _ = _resolved(root, items) if items is not None else ({}, None)
|
|
528
|
+
if "properties" in item_schema:
|
|
529
|
+
nested.append((f"{path}[]", items))
|
|
530
|
+
return f"{size} of objects (see `{path}[]`)"
|
|
531
|
+
if "pattern" in item_schema:
|
|
532
|
+
return f"{size} of strings matching `{item_schema['pattern']}`"
|
|
533
|
+
if isinstance(item_schema.get("enum"), list):
|
|
534
|
+
return f"{size}, each one of " + ", ".join(f"`{value}`" for value in item_schema["enum"])
|
|
535
|
+
if item_schema.get("type"):
|
|
536
|
+
return f"{size} of {item_schema['type']}"
|
|
537
|
+
return size
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _prefix_order(root: dict, prefix_items: list) -> str:
|
|
541
|
+
"""고정 순서 배열의 각 자리가 못 박은 값 — `criterion` 처럼 상수인 속성."""
|
|
542
|
+
labels: list[str] = []
|
|
543
|
+
for item in prefix_items:
|
|
544
|
+
merged, _ = _resolved(root, item)
|
|
545
|
+
consts = [
|
|
546
|
+
f"`{key}`=`{value['const']}`"
|
|
547
|
+
for key, value in (merged.get("properties") or {}).items()
|
|
548
|
+
if isinstance(value, dict) and "const" in value
|
|
549
|
+
]
|
|
550
|
+
labels.append(", ".join(consts) if consts else "item")
|
|
551
|
+
return "; ".join(labels)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def _range(schema: dict) -> str:
|
|
555
|
+
low, high = schema.get("minimum"), schema.get("maximum")
|
|
556
|
+
if low is not None and high is not None:
|
|
557
|
+
return f" {low}..{high}"
|
|
558
|
+
if low is not None:
|
|
559
|
+
return f" >= {low}"
|
|
560
|
+
if high is not None:
|
|
561
|
+
return f" <= {high}"
|
|
562
|
+
return ""
|
|
563
|
+
|
|
564
|
+
|
|
263
565
|
def validate(data: Any, schema: dict) -> list[str]:
|
|
264
566
|
"""Validate ``data`` against ``schema``. Returns the list of human-
|
|
265
567
|
readable error messages (empty when the data is valid)."""
|
|
@@ -273,12 +273,32 @@ def _items_as_json(items: List[StaleItem]) -> list:
|
|
|
273
273
|
"suggested_commit": i.suggested_commit} for i in items]
|
|
274
274
|
|
|
275
275
|
|
|
276
|
+
_CLI_EPILOG = r"""기본은 검사:
|
|
277
|
+
stale 항목을 JSON 으로 출력하고, confirm 항목이 남으면 exit 2.
|
|
278
|
+
patch-id 로 내용 동일성이 증명되는 항목(auto)은 --apply 로 일괄 보정되고,
|
|
279
|
+
내용이 바뀐 항목(confirm)은 --stage/--use-ref 로만 보정된다.
|
|
280
|
+
|
|
281
|
+
Usage:
|
|
282
|
+
okstra git-reconcile --plan-run-root <dir> --project-id <id> \
|
|
283
|
+
--task-group <g> --task-id <t> --work-category <c> \
|
|
284
|
+
[--apply] [--stage <N> --use-ref <ref>] [--reset-anchor <ref>] [--json]
|
|
285
|
+
|
|
286
|
+
Exit codes:
|
|
287
|
+
0 stale 없음 또는 보정 완료
|
|
288
|
+
2 confirm 항목 잔존 (check: 확인 필요 / apply: 일부 미보정)
|
|
289
|
+
1 error (resolve 실패 등)
|
|
290
|
+
"""
|
|
291
|
+
_CLI_DESCRIPTION = "Okstra \ubc16 git \ud788\uc2a4\ud1a0\ub9ac \ubcc0\uacbd \ud6c4 \uae30\ub85d \ud654\ud574."
|
|
292
|
+
|
|
293
|
+
|
|
276
294
|
def _parse_args(argv: Optional[list]):
|
|
277
295
|
import argparse
|
|
278
296
|
|
|
279
297
|
p = argparse.ArgumentParser(
|
|
280
|
-
|
|
281
|
-
epilog=
|
|
298
|
+
description=_CLI_DESCRIPTION,
|
|
299
|
+
epilog=_CLI_EPILOG,
|
|
300
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
301
|
+
prog="okstra git-reconcile")
|
|
282
302
|
p.add_argument("--project-root", default=".")
|
|
283
303
|
p.add_argument("--plan-run-root", required=True,
|
|
284
304
|
help="consumers.jsonl 이 있는 runs/implementation-planning/ 경로")
|
|
@@ -422,10 +422,33 @@ def _parse_stages_csv(raw: str) -> List[int]:
|
|
|
422
422
|
return out
|
|
423
423
|
|
|
424
424
|
|
|
425
|
+
_CLI_EPILOG = r"""JSON 출력.
|
|
426
|
+
|
|
427
|
+
Usage:
|
|
428
|
+
okstra handoff eligible --plan-run-root <dir> --approved-plan <md>
|
|
429
|
+
okstra handoff assemble --plan-run-root <dir> --approved-plan <md> \
|
|
430
|
+
--project-root <dir> --project-id <id> --task-group <g> --task-id <t> \
|
|
431
|
+
--work-category <c> --stages 2,3 --base <branch>
|
|
432
|
+
okstra handoff record-verified --plan-run-root <dir> --stage <N> \
|
|
433
|
+
--report-path <md> --data-json <json>
|
|
434
|
+
okstra handoff record-pr --plan-run-root <dir> --stages 2,3 \
|
|
435
|
+
--branch <b> --url <u>
|
|
436
|
+
okstra handoff local-checkout --project-root <dir> --project-id <id> \
|
|
437
|
+
--task-group <g> --task-id <t> [--stage <N>]
|
|
438
|
+
|
|
439
|
+
Exit codes: 0 ok / 1 자격·전제 위반 / 2 stage 간 merge 충돌(conflicts 동봉)
|
|
440
|
+
"""
|
|
441
|
+
_CLI_DESCRIPTION = "Release-handoff stage-group \ubcf4\uc870 (\uc790\uaca9/\uc218\uc9d1/\uae30\ub85d)."
|
|
442
|
+
|
|
443
|
+
|
|
425
444
|
def main(argv: Optional[list] = None) -> int:
|
|
426
445
|
import argparse
|
|
427
446
|
|
|
428
|
-
p = argparse.ArgumentParser(
|
|
447
|
+
p = argparse.ArgumentParser(
|
|
448
|
+
description=_CLI_DESCRIPTION,
|
|
449
|
+
epilog=_CLI_EPILOG,
|
|
450
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
451
|
+
prog="okstra handoff")
|
|
429
452
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
430
453
|
|
|
431
454
|
def common(sp, *, plan=True):
|
|
@@ -5,7 +5,9 @@ import hashlib as _hashlib
|
|
|
5
5
|
import re as _re
|
|
6
6
|
from typing import Dict
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
# leaf 규칙 모듈에서 바로 가져온다. state 를 거치면 okstra_ctl 패키지 __init__ 이
|
|
9
|
+
# 상태 접근자 전체를 끌어와 okstra_project ↔ okstra_ctl 순환이 된다.
|
|
10
|
+
from okstra_project.slug import slugify as _slugify
|
|
9
11
|
|
|
10
12
|
# docker compose project name 길이 상한. compose 는 [a-z0-9][a-z0-9_-]* 만 허용한다.
|
|
11
13
|
CONTAINER_PROJECT_NAME_MAX = 60
|
|
@@ -73,28 +75,16 @@ def _escape_segment_for_join(segment: str) -> str:
|
|
|
73
75
|
return segment.replace("-", "--")
|
|
74
76
|
|
|
75
77
|
|
|
76
|
-
def run_id_to_session_name(run_id: str) -> str:
|
|
77
|
-
"""tmux 세션명으로 변환한다. 'okstra-' 접두사 + 세그먼트 escape 후 `-` 조인.
|
|
78
|
-
예: 'a/b-c/r01' → 'okstra-a-b--c-r01'. 세그먼트 내부 `-` 는 `--` 로
|
|
79
|
-
이스케이프되어, 슬래시 위치가 다른 두 runId 가 동일 세션명으로
|
|
80
|
-
충돌하지 않는다. 세그먼트는 fs-safe 슬러그로 강제 정규화해
|
|
81
|
-
`..` 같은 traversal 토큰이 세션명에 그대로 노출되지 않게 한다.
|
|
82
|
-
"""
|
|
83
|
-
parts = [_escape_segment_for_join(_safe_fs_segment(seg))
|
|
84
|
-
for seg in run_id.split("/")]
|
|
85
|
-
return "okstra-" + "-".join(parts)
|
|
86
|
-
|
|
87
|
-
|
|
88
78
|
def slugify_task_segment(value: str) -> str:
|
|
89
79
|
"""Match okstra.sh slugify_value for task directory segments."""
|
|
90
80
|
return _re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
|
|
91
81
|
|
|
92
82
|
|
|
93
|
-
def
|
|
94
|
-
"""container
|
|
83
|
+
def build_container_run_trace(project_id: str, group: str, task_id: str) -> str:
|
|
84
|
+
"""container 배포의 `okstra.run-trace` 라벨값을 합친다 → 'okstra-container-<slug>'.
|
|
95
85
|
|
|
96
86
|
세그먼트는 task_dir 가 쓰는 slugify_task_segment 와 동일 규칙으로 정규화해
|
|
97
|
-
|
|
87
|
+
라벨 slug 가 프로젝트 디스크 경로 slug 와 분기하지 않게 한다.
|
|
98
88
|
"""
|
|
99
89
|
seg = slugify_task_segment
|
|
100
90
|
return f"okstra-container-{seg(project_id)}-{seg(group)}-{seg(task_id)}"
|