okstra 0.206.0 → 0.207.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 +3 -3
- package/dist/cli-registry.mjs +7 -1
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/lifecycle/install.mjs +1 -1
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/docs/architecture/storage-model.md +1 -0
- package/docs/architecture.md +40 -16
- package/docs/cli.md +17 -15
- package/docs/contributor-change-matrix.md +3 -2
- package/docs/performance-improvement-plan-v2.md +1 -1
- package/docs/project-structure-overview.md +43 -20
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/operations/code-review.json +1 -1
- package/runtime/bin/lib/okstra/usage.sh +3 -3
- package/runtime/bin/okstra-compact-reminder.sh +1 -1
- package/runtime/bin/okstra-spawn-followups.py +2 -2
- package/runtime/prompts/duties/direction-selection-worker.json +1 -1
- package/runtime/prompts/launch.template.md +2 -2
- package/runtime/prompts/lead/adapters/cmux.md +4 -3
- package/runtime/prompts/lead/context-loader.md +1 -1
- package/runtime/prompts/lead/convergence.md +44 -12
- package/runtime/prompts/lead/okstra-lead-contract.md +44 -73
- package/runtime/prompts/lead/phase-routing.md +64 -0
- package/runtime/prompts/lead/report-writer.md +10 -8
- package/runtime/prompts/lead/team-contract.md +1 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +4 -4
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/forbidden-actions.json +0 -94
- package/runtime/prompts/wizard/prompts.ko.json +2 -1
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +5 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +3 -2
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +17 -26
- package/runtime/python/okstra_ctl/agent/prompt_cli/batch.py +183 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +60 -10
- package/runtime/python/okstra_ctl/agent/prompt_cli/corrections.py +1 -1
- package/runtime/python/okstra_ctl/agent/prompt_cli/jobs.py +21 -4
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +10 -1
- package/runtime/python/okstra_ctl/analysis_inputs.py +0 -39
- package/runtime/python/okstra_ctl/analysis_scope.py +31 -0
- package/runtime/python/okstra_ctl/approval_decisions.py +32 -2
- package/runtime/python/okstra_ctl/asset_roots.py +19 -0
- package/runtime/python/okstra_ctl/assignment_resolver.py +8 -0
- package/runtime/python/okstra_ctl/blocking_checks.py +7 -0
- package/runtime/python/okstra_ctl/code_review_target.py +92 -6
- package/runtime/python/okstra_ctl/consumers.py +12 -0
- package/runtime/python/okstra_ctl/dispatch_checkpoints.py +121 -0
- package/runtime/python/okstra_ctl/dispatch_core.py +54 -32
- package/runtime/python/okstra_ctl/dispatch_state.py +34 -5
- package/runtime/python/okstra_ctl/doctor.py +2 -1
- package/runtime/python/okstra_ctl/domain/provider.py +5 -0
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -2
- package/runtime/python/okstra_ctl/domain/write_policy.py +2 -1
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +46 -9
- package/runtime/python/okstra_ctl/handoff.py +11 -466
- package/runtime/python/okstra_ctl/handoff_error.py +5 -0
- package/runtime/python/okstra_ctl/implementation_direction.py +0 -477
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +13 -1
- package/runtime/python/okstra_ctl/lead_progress.py +33 -1
- package/runtime/python/okstra_ctl/manager_view.py +26 -19
- package/runtime/python/okstra_ctl/model_io/lines.py +21 -4
- package/runtime/python/okstra_ctl/models.py +4 -1
- package/runtime/python/okstra_ctl/operation_invocation.py +11 -2
- package/runtime/python/okstra_ctl/option_comparison.py +3 -165
- package/runtime/python/okstra_ctl/option_votes.py +3 -191
- package/runtime/python/okstra_ctl/paths.py +8 -6
- package/runtime/python/okstra_ctl/phases/catalog.py +56 -12
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/boundary.json +11 -0
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/entry.py +39 -0
- package/runtime/python/okstra_ctl/{report_html/view_models/change_impact_analysis.py → phases/change_impact_analysis/report.py} +3 -3
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/spec.md +26 -0
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/validation.py +23 -0
- package/runtime/python/okstra_ctl/phases/error_analysis/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/error_analysis/boundary.json +9 -0
- package/runtime/{prompts/profiles/error-analysis.md → python/okstra_ctl/phases/error_analysis/profile.md} +2 -2
- package/runtime/python/okstra_ctl/{report_html/view_models/error_analysis.py → phases/error_analysis/report.py} +9 -8
- package/runtime/{templates/reports → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis-input.template.md +1 -1
- package/runtime/python/okstra_ctl/phases/error_analysis/spec.md +118 -0
- package/runtime/python/okstra_ctl/phases/error_analysis/validation.py +241 -0
- package/runtime/python/okstra_ctl/phases/feature_analysis/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/feature_analysis/boundary.json +8 -0
- package/runtime/python/okstra_ctl/phases/feature_analysis/entry.py +63 -0
- package/runtime/python/okstra_ctl/{report_html/view_models/feature_analysis.py → phases/feature_analysis/report.py} +12 -5
- package/runtime/python/okstra_ctl/phases/feature_analysis/spec.md +22 -0
- package/runtime/python/okstra_ctl/phases/feature_analysis/validation.py +27 -0
- package/runtime/python/okstra_ctl/phases/feature_analysis/wizard.py +95 -0
- package/runtime/python/okstra_ctl/phases/final_verification/boundary.json +8 -0
- package/runtime/python/okstra_ctl/phases/final_verification/profile.md +2 -2
- package/runtime/{templates/reports → python/okstra_ctl/phases/final_verification/report_assets}/final-verification-input.template.md +1 -1
- package/runtime/python/okstra_ctl/phases/final_verification/spec.md +1 -1
- package/runtime/python/okstra_ctl/phases/implementation/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/implementation/boundary.json +17 -0
- package/runtime/python/okstra_ctl/{implementation_stage.py → phases/implementation/entry.py} +22 -10
- package/runtime/{prompts/host-orchestration/implementation.md → python/okstra_ctl/phases/implementation/host-rules.md} +1 -1
- package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-deliverable.md +1 -1
- package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-executor.md +4 -3
- package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-verifier.md +18 -7
- package/runtime/{prompts/profiles/implementation.md → python/okstra_ctl/phases/implementation/profile.md} +5 -5
- package/runtime/python/okstra_ctl/{report_html/view_models/implementation.py → phases/implementation/report.py} +3 -3
- package/runtime/{templates/reports → python/okstra_ctl/phases/implementation/report_assets}/implementation-input.template.md +1 -1
- package/runtime/python/okstra_ctl/phases/implementation/spec.md +238 -0
- package/runtime/python/okstra_ctl/phases/implementation/validation.py +205 -0
- package/runtime/python/okstra_ctl/phases/implementation/wizard.py +39 -0
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/authoring.py +80 -0
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/boundary.json +10 -0
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/comparison.py +168 -0
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/entry.py +27 -0
- package/runtime/{prompts/profiles/implementation-option-selection.md → python/okstra_ctl/phases/implementation_option_selection/profile.md} +3 -3
- package/runtime/python/okstra_ctl/{report_html/view_models/implementation_option_selection.py → phases/implementation_option_selection/report.py} +2 -2
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/spec.md +83 -0
- package/runtime/python/okstra_ctl/{implementation_options.py → phases/implementation_option_selection/validation.py} +3 -3
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/votes.py +194 -0
- package/runtime/python/okstra_ctl/phases/implementation_planning/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +2345 -0
- package/runtime/python/okstra_ctl/phases/implementation_planning/boundary.json +12 -0
- package/runtime/python/okstra_ctl/phases/implementation_planning/entry.py +161 -0
- package/runtime/python/okstra_ctl/phases/implementation_planning/guidance.py +178 -0
- package/runtime/{prompts/lead → python/okstra_ctl/phases/implementation_planning/instructions}/plan-body-verification.md +61 -51
- package/runtime/python/okstra_ctl/phases/implementation_planning/plan_body.py +3295 -0
- package/runtime/{prompts/profiles/implementation-planning.md → python/okstra_ctl/phases/implementation_planning/profile.md} +74 -25
- package/runtime/python/okstra_ctl/phases/implementation_planning/report.py +237 -0
- package/runtime/{templates/reports → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning-input.template.md +2 -2
- package/runtime/python/okstra_ctl/phases/implementation_planning/spec.md +204 -0
- package/runtime/python/okstra_ctl/phases/implementation_planning/validation.py +597 -0
- package/runtime/python/okstra_ctl/phases/implementation_planning/wizard.py +166 -0
- package/runtime/python/okstra_ctl/phases/improvement_discovery/boundary.json +12 -0
- package/runtime/python/okstra_ctl/{improvement_lenses.py → phases/improvement_discovery/lenses.py} +1 -6
- package/runtime/{prompts/profiles/improvement-discovery.md → python/okstra_ctl/phases/improvement_discovery/profile.md} +5 -5
- package/runtime/python/okstra_ctl/{report_html/view_models/improvement_discovery.py → phases/improvement_discovery/report.py} +3 -3
- package/runtime/{templates/reports → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery-input.template.md +1 -2
- package/runtime/python/okstra_ctl/phases/improvement_discovery/spec.md +29 -0
- package/runtime/{validators/validate_improvement_report.py → python/okstra_ctl/phases/improvement_discovery/validation.py} +5 -14
- package/runtime/python/okstra_ctl/phases/project_analysis/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/project_analysis/boundary.json +8 -0
- package/runtime/python/okstra_ctl/phases/project_analysis/entry.py +11 -0
- package/runtime/python/okstra_ctl/{report_html/view_models/project_analysis.py → phases/project_analysis/report.py} +3 -3
- package/runtime/python/okstra_ctl/phases/project_analysis/spec.md +33 -0
- package/runtime/python/okstra_ctl/phases/project_analysis/validation.py +55 -0
- package/runtime/python/okstra_ctl/phases/release_handoff/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/release_handoff/boundary.json +17 -0
- package/runtime/python/okstra_ctl/phases/release_handoff/entry.py +147 -0
- package/runtime/python/okstra_ctl/phases/release_handoff/operations.py +446 -0
- package/runtime/{prompts/profiles/release-handoff.md → python/okstra_ctl/phases/release_handoff/profile.md} +3 -3
- package/runtime/python/okstra_ctl/{report_html/view_models/release_handoff.py → phases/release_handoff/report.py} +3 -3
- package/runtime/{templates/reports → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff-input.template.md +1 -1
- package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +233 -0
- package/runtime/python/okstra_ctl/phases/release_handoff/wizard.py +84 -0
- package/runtime/python/okstra_ctl/phases/requirements_discovery/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/requirements_discovery/boundary.json +9 -0
- package/runtime/{prompts/profiles/requirements-discovery.md → python/okstra_ctl/phases/requirements_discovery/profile.md} +2 -3
- package/runtime/python/okstra_ctl/{report_html/view_models/requirements_discovery.py → phases/requirements_discovery/report.py} +3 -3
- package/runtime/python/okstra_ctl/phases/requirements_discovery/spec.md +132 -0
- package/runtime/{validators/validate_fanout.py → python/okstra_ctl/phases/requirements_discovery/validation.py} +11 -12
- package/runtime/python/okstra_ctl/phases/technical_verification/__init__.py +1 -0
- package/runtime/python/okstra_ctl/phases/technical_verification/boundary.json +9 -0
- package/runtime/python/okstra_ctl/phases/technical_verification/entry.py +100 -0
- package/runtime/{prompts/profiles/technical-verification.md → python/okstra_ctl/phases/technical_verification/profile.md} +2 -2
- package/runtime/python/okstra_ctl/{report_html/view_models/technical_verification.py → phases/technical_verification/report.py} +2 -2
- package/runtime/python/okstra_ctl/phases/technical_verification/spec.md +37 -0
- package/runtime/python/okstra_ctl/phases/technical_verification/validation.py +90 -0
- package/runtime/python/okstra_ctl/plan_approval.py +70 -0
- package/runtime/python/okstra_ctl/plan_items_cli.py +2 -2130
- package/runtime/python/okstra_ctl/process_group.py +118 -0
- package/runtime/python/okstra_ctl/profile_show.py +3 -3
- package/runtime/python/okstra_ctl/render.py +15 -4
- package/runtime/python/okstra_ctl/report_assembly.py +28 -92
- package/runtime/python/okstra_ctl/report_finalize.py +106 -2
- package/runtime/python/okstra_ctl/report_html/context_links.py +1 -1
- package/runtime/python/okstra_ctl/report_projections.py +1 -36
- package/runtime/python/okstra_ctl/report_routing.py +23 -0
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +4 -73
- package/runtime/python/okstra_ctl/report_validation_identity.py +38 -0
- package/runtime/python/okstra_ctl/report_views.py +1 -1
- package/runtime/python/okstra_ctl/run.py +68 -350
- package/runtime/python/okstra_ctl/run_artifact_prune.py +200 -0
- package/runtime/python/okstra_ctl/stage_map.py +13 -0
- package/runtime/python/okstra_ctl/team.py +108 -9
- package/runtime/python/okstra_ctl/technical_verification_facts.py +52 -0
- package/runtime/python/okstra_ctl/wizard/__init__.py +31 -31
- package/runtime/python/okstra_ctl/wizard/api.py +18 -0
- package/runtime/python/okstra_ctl/wizard/outcome.py +3 -12
- package/runtime/python/okstra_ctl/wizard/registry.py +20 -12
- package/runtime/python/okstra_ctl/wizard/steps_analysis.py +0 -97
- package/runtime/python/okstra_ctl/wizard/steps_options.py +8 -0
- package/runtime/python/okstra_ctl/wizard/steps_plan.py +10 -263
- package/runtime/python/okstra_ctl/wizard/steps_roles.py +2 -1
- package/runtime/python/okstra_ctl/work_categories.py +1 -1
- package/runtime/python/okstra_ctl/worker_dispatch.py +44 -3
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +36 -0
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +19 -0
- package/runtime/python/okstra_ctl/worker_runner.py +21 -3
- package/runtime/python/okstra_ctl/workflow.py +26 -143
- package/runtime/python/okstra_ctl/write_policy.py +57 -7
- package/runtime/python/okstra_project/dirs.py +14 -0
- package/runtime/python/okstra_project/resolver.py +2 -1
- package/runtime/schemas/execution-manifest-v2.schema.json +2 -1
- package/runtime/skills/okstra-brief-gen/SKILL.md +3 -3
- package/runtime/skills/okstra-code-review/SKILL.md +70 -32
- package/runtime/skills/okstra-code-review/references/review-calibration.md +26 -6
- package/runtime/skills/okstra-run/SKILL.md +3 -3
- package/runtime/templates/manager/view.template.html +18 -1
- package/runtime/templates/reports/quick-input.template.md +1 -1
- package/runtime/templates/reports/task-brief.template.md +1 -1
- package/runtime/validators/validate-brief.py +2 -2
- package/runtime/validators/validate-run.py +299 -3940
- package/runtime/validators/validate_analysis_report.py +14 -126
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +0 -147
- package/runtime/python/okstra_ctl/technical_verification.py +0 -195
- /package/runtime/{prompts/profiles/change-impact-analysis.json → python/okstra_ctl/phases/change_impact_analysis/profile.json} +0 -0
- /package/runtime/{prompts/profiles/change-impact-analysis.md → python/okstra_ctl/phases/change_impact_analysis/profile.md} +0 -0
- /package/runtime/{templates/reports → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis-input.template.md +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis.template.md +0 -0
- /package/runtime/{prompts/profiles/error-analysis.json → python/okstra_ctl/phases/error_analysis/profile.json} +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis.template.md +0 -0
- /package/runtime/{prompts/profiles/feature-analysis.json → python/okstra_ctl/phases/feature_analysis/profile.json} +0 -0
- /package/runtime/{prompts/profiles/feature-analysis.md → python/okstra_ctl/phases/feature_analysis/profile.md} +0 -0
- /package/runtime/{templates/reports → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis-input.template.md +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis.template.md +0 -0
- /package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-diff-review.md +0 -0
- /package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-self-check.md +0 -0
- /package/runtime/{prompts/profiles/implementation.json → python/okstra_ctl/phases/implementation/profile.json} +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation/report_assets}/implementation.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation/report_assets}/implementation.template.md +0 -0
- /package/runtime/{prompts/profiles/implementation-option-selection.json → python/okstra_ctl/phases/implementation_option_selection/profile.json} +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation_option_selection/report_assets}/implementation-option-selection.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation_option_selection/report_assets}/implementation-option-selection.template.md +0 -0
- /package/runtime/{prompts/host-orchestration/implementation-planning.md → python/okstra_ctl/phases/implementation_planning/host-rules.md} +0 -0
- /package/runtime/{prompts/profiles/implementation-planning.json → python/okstra_ctl/phases/implementation_planning/profile.json} +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning.template.md +0 -0
- /package/runtime/{prompts/profiles/improvement-discovery.json → python/okstra_ctl/phases/improvement_discovery/profile.json} +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery.template.md +0 -0
- /package/runtime/{prompts/profiles/project-analysis.json → python/okstra_ctl/phases/project_analysis/profile.json} +0 -0
- /package/runtime/{prompts/profiles/project-analysis.md → python/okstra_ctl/phases/project_analysis/profile.md} +0 -0
- /package/runtime/{templates/reports → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis-input.template.md +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis.template.md +0 -0
- /package/runtime/{prompts/profiles/release-handoff.json → python/okstra_ctl/phases/release_handoff/profile.json} +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff.template.md +0 -0
- /package/runtime/python/okstra_ctl/{fanout.py → phases/requirements_discovery/fanout.py} +0 -0
- /package/runtime/{prompts/profiles/requirements-discovery.json → python/okstra_ctl/phases/requirements_discovery/profile.json} +0 -0
- /package/runtime/{templates/reports → python/okstra_ctl/phases/requirements_discovery/report_assets}/fan-out-unit.template.md +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/requirements_discovery/report_assets}/requirements-discovery.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/requirements_discovery/report_assets}/requirements-discovery.template.md +0 -0
- /package/runtime/{prompts/profiles/technical-verification.json → python/okstra_ctl/phases/technical_verification/profile.json} +0 -0
- /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/technical_verification/report_assets}/technical-verification.template.html +0 -0
- /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/technical_verification/report_assets}/technical-verification.template.md +0 -0
|
@@ -14,9 +14,15 @@ state passing, and are read once at the start.
|
|
|
14
14
|
"""
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
from .
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
from okstra_ctl.phases.feature_analysis.entry import (
|
|
18
|
+
resolve_analysis_target, validate_feature_target_input,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from okstra_ctl.phases.implementation_planning.validation import validate_selected_direction_plan
|
|
22
|
+
|
|
23
|
+
from .phases.technical_verification.entry import (
|
|
24
|
+
prepare_technical_verification_input,
|
|
25
|
+
materialize_technical_input_packet,
|
|
20
26
|
)
|
|
21
27
|
|
|
22
28
|
from argparse import (
|
|
@@ -40,6 +46,13 @@ from typing import Mapping, Sequence
|
|
|
40
46
|
|
|
41
47
|
from okstra_project import project_json_path, upsert_project_json
|
|
42
48
|
from okstra_project.slug import slugify
|
|
49
|
+
from .phases.catalog import host_rules
|
|
50
|
+
from .phases.implementation_planning.entry import (
|
|
51
|
+
_resolve_planning_input_path,
|
|
52
|
+
_validate_planning_entry_inputs,
|
|
53
|
+
_resolve_planning_direction,
|
|
54
|
+
_validate_reverify_scope,
|
|
55
|
+
)
|
|
43
56
|
from . import fix_cycles
|
|
44
57
|
from .analysis_packet import build_analysis_packet
|
|
45
58
|
from . import group_context
|
|
@@ -55,7 +68,6 @@ from .analysis_inputs import (
|
|
|
55
68
|
load_candidate_map,
|
|
56
69
|
parse_evidence_paths,
|
|
57
70
|
resolve_analysis_head,
|
|
58
|
-
resolve_analysis_target,
|
|
59
71
|
resolve_evidence_inputs,
|
|
60
72
|
)
|
|
61
73
|
from .stage_fix_carry import derive_stage_fix_carry
|
|
@@ -67,13 +79,11 @@ from .clarification_items import (
|
|
|
67
79
|
scan_approval_gate,
|
|
68
80
|
)
|
|
69
81
|
from .error_report import prior_run_error_digest
|
|
70
|
-
from .incremental_scope import
|
|
82
|
+
from .incremental_scope import parse_user_reverify_scope
|
|
71
83
|
from .implementation_direction import (
|
|
72
84
|
DirectionSelectionError,
|
|
73
85
|
SelectedDirection,
|
|
74
86
|
lexical_absolute_path,
|
|
75
|
-
resolve_selected_direction,
|
|
76
|
-
validate_selected_direction_plan,
|
|
77
87
|
validate_task_artifact_path,
|
|
78
88
|
write_selected_direction_snapshot,
|
|
79
89
|
)
|
|
@@ -205,7 +215,9 @@ from .session import (
|
|
|
205
215
|
resolve_inproc_lead_session_id,
|
|
206
216
|
write_claude_resume_command_file,
|
|
207
217
|
)
|
|
208
|
-
from .
|
|
218
|
+
from .phases.release_handoff.entry import (
|
|
219
|
+
materialize_handoff_input, resolve_pr_template,
|
|
220
|
+
)
|
|
209
221
|
from .worker_artifacts import worker_provider_id
|
|
210
222
|
from .workers import (
|
|
211
223
|
normalize_workers,
|
|
@@ -213,7 +225,7 @@ from .workers import (
|
|
|
213
225
|
resolve_profile_workers,
|
|
214
226
|
validate_workers_against_profile,
|
|
215
227
|
)
|
|
216
|
-
from .workflow import compute_workflow_state,
|
|
228
|
+
from .workflow import compute_workflow_state, load_phase_boundary
|
|
217
229
|
from .locks import worktree_provision_mutex
|
|
218
230
|
from . import stage_targets as _stage_targets
|
|
219
231
|
from .phases.catalog import PhaseAssetError, profile_markdown, resolve_include
|
|
@@ -225,8 +237,7 @@ from .phases.final_verification.entry import (
|
|
|
225
237
|
verification_target_request,
|
|
226
238
|
write_verification_target_snapshot,
|
|
227
239
|
)
|
|
228
|
-
from .
|
|
229
|
-
from . import implementation_stage as _implementation_stage
|
|
240
|
+
from .phases.implementation import entry as _implementation_stage
|
|
230
241
|
from .work_categories import resolve_work_category
|
|
231
242
|
from .worktree import (
|
|
232
243
|
WorktreeProvision,
|
|
@@ -236,7 +247,6 @@ from .brief_frontmatter import (
|
|
|
236
247
|
has_reporter_confirmation_contract,
|
|
237
248
|
read_brief_frontmatter,
|
|
238
249
|
)
|
|
239
|
-
from .scope_provenance import brief_end_state_id_sequence
|
|
240
250
|
|
|
241
251
|
# Frontmatter approval-flag matcher.
|
|
242
252
|
#
|
|
@@ -517,6 +527,9 @@ def _set_data_json_approved_true_if_present(path: Path) -> bool:
|
|
|
517
527
|
# PrepareError 는 stage_targets(저수준)에 정의돼 final-verification 과 container
|
|
518
528
|
# 가 whole-task 통합 실패를 동일 타입으로 받는다. run.py 는 재노출만 한다.
|
|
519
529
|
PrepareError = _stage_targets.PrepareError
|
|
530
|
+
from .phases.implementation_option_selection.entry import (
|
|
531
|
+
validate_analyser_roster, validate_brief_requirements,
|
|
532
|
+
)
|
|
520
533
|
|
|
521
534
|
|
|
522
535
|
def _normalize_lead_runtime(value: str) -> str:
|
|
@@ -696,17 +709,7 @@ def _validate_stage_structure(plan_path: str) -> None:
|
|
|
696
709
|
RUN_STEP_BUDGET = _stage_targets.RUN_STEP_BUDGET
|
|
697
710
|
|
|
698
711
|
|
|
699
|
-
|
|
700
|
-
"""Parse the approved plan into context records for execution consumers."""
|
|
701
|
-
from .stage_map import StageMapError, parse_stage_map_file, stage_map_records
|
|
702
|
-
|
|
703
|
-
try:
|
|
704
|
-
return stage_map_records(parse_stage_map_file(Path(plan_path)))
|
|
705
|
-
except StageMapError as exc:
|
|
706
|
-
raise PrepareError(
|
|
707
|
-
"approved-plan 의 Stage Map 을 신뢰할 수 없어 거부합니다 "
|
|
708
|
-
f"({plan_path}): {exc.reason}. plan 의 Stage Map 을 점검하세요."
|
|
709
|
-
) from exc
|
|
712
|
+
from .stage_map import parse_stage_map_for_prepare as _parse_stage_map_into_ctx
|
|
710
713
|
|
|
711
714
|
|
|
712
715
|
def _apply_cli_approval(path: str) -> None:
|
|
@@ -1352,9 +1355,7 @@ def _resolve_runtime_assets(workspace_root: Path, inp: PrepareInputs) -> _Resolv
|
|
|
1352
1355
|
raise PrepareError(
|
|
1353
1356
|
f"required okstra template or lead contract missing: {required}.{_INSTALL_HINT}"
|
|
1354
1357
|
)
|
|
1355
|
-
host_rules_file = (
|
|
1356
|
-
workspace_root / "prompts" / "host-orchestration" / f"{inp.task_type}.md"
|
|
1357
|
-
)
|
|
1358
|
+
host_rules_file = host_rules(workspace_root, inp.task_type)
|
|
1358
1359
|
return _ResolvedAssets(
|
|
1359
1360
|
profile_file=profile_file,
|
|
1360
1361
|
prompt_template=prompt_template,
|
|
@@ -1434,17 +1435,12 @@ def _validate_prepare_inputs(project_root: Path, inp: PrepareInputs) -> list:
|
|
|
1434
1435
|
if inp.task_type == "release-handoff":
|
|
1435
1436
|
# brief 는 entry phase 의 입력물 — release-handoff 는 검증 보고서를
|
|
1436
1437
|
# 인용하는 input 문서를 prepare 가 직접 생성해 brief 자리에 채운다
|
|
1437
|
-
# (
|
|
1438
|
+
# (materialize_handoff_input, 이 검증 직후 실행).
|
|
1438
1439
|
pass
|
|
1439
1440
|
elif not inp.brief_path.is_file():
|
|
1440
1441
|
raise PrepareError(f"task brief not found: {inp.brief_path}")
|
|
1441
|
-
elif inp.task_type == "implementation-option-selection"
|
|
1442
|
-
inp.brief_path
|
|
1443
|
-
):
|
|
1444
|
-
raise PrepareError(
|
|
1445
|
-
"regenerate the brief with stable end-state IDs before starting "
|
|
1446
|
-
"implementation-option-selection"
|
|
1447
|
-
)
|
|
1442
|
+
elif inp.task_type == "implementation-option-selection":
|
|
1443
|
+
validate_brief_requirements(inp.brief_path)
|
|
1448
1444
|
ctx_stage_map: list = []
|
|
1449
1445
|
# implementation 과 final-verification 은 둘 다 승인된 plan 의 Stage Map 을
|
|
1450
1446
|
# 입력으로 받는다(전자는 실행 scope, 후자는 검증 scope).
|
|
@@ -1491,143 +1487,6 @@ def _validate_prepare_inputs(project_root: Path, inp: PrepareInputs) -> list:
|
|
|
1491
1487
|
return ctx_stage_map
|
|
1492
1488
|
|
|
1493
1489
|
|
|
1494
|
-
_PLANNING_REPORT_RE = re.compile(
|
|
1495
|
-
r"^final-report-implementation-planning-\d{3,}\.(?:md|data\.json)$"
|
|
1496
|
-
)
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
def _resolve_planning_input_path(
|
|
1500
|
-
path_value: str, project_root: Path
|
|
1501
|
-
) -> Path | None:
|
|
1502
|
-
"""Resolve cwd-first planning inputs without following symbolic links."""
|
|
1503
|
-
raw_path = Path(path_value).expanduser()
|
|
1504
|
-
cwd_candidate = lexical_absolute_path(raw_path)
|
|
1505
|
-
if cwd_candidate.is_file():
|
|
1506
|
-
return cwd_candidate
|
|
1507
|
-
if not raw_path.is_absolute():
|
|
1508
|
-
project_candidate = lexical_absolute_path(project_root / raw_path)
|
|
1509
|
-
if project_candidate.is_file():
|
|
1510
|
-
return project_candidate
|
|
1511
|
-
return None
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
def _is_existing_planning_report(
|
|
1515
|
-
path_value: str, project_root: Path, inp: PrepareInputs
|
|
1516
|
-
) -> bool:
|
|
1517
|
-
from .paths import task_dir
|
|
1518
|
-
|
|
1519
|
-
try:
|
|
1520
|
-
report = lexical_absolute_path(Path(path_value))
|
|
1521
|
-
task_root = lexical_absolute_path(
|
|
1522
|
-
task_dir(project_root, inp.task_group, inp.task_id)
|
|
1523
|
-
)
|
|
1524
|
-
relative = report.relative_to(task_root)
|
|
1525
|
-
except ValueError:
|
|
1526
|
-
return False
|
|
1527
|
-
has_planning_layout = (
|
|
1528
|
-
_PLANNING_REPORT_RE.fullmatch(report.name) is not None
|
|
1529
|
-
and relative.parts[:3] == (
|
|
1530
|
-
"runs",
|
|
1531
|
-
"implementation-planning",
|
|
1532
|
-
"reports",
|
|
1533
|
-
)
|
|
1534
|
-
and len(relative.parts) == 4
|
|
1535
|
-
)
|
|
1536
|
-
if not has_planning_layout:
|
|
1537
|
-
return False
|
|
1538
|
-
try:
|
|
1539
|
-
validate_task_artifact_path(report, task_root, "planning report")
|
|
1540
|
-
except DirectionSelectionError:
|
|
1541
|
-
return False
|
|
1542
|
-
return True
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
def _validate_planning_entry_inputs(
|
|
1546
|
-
project_root: Path, inp: PrepareInputs
|
|
1547
|
-
) -> None:
|
|
1548
|
-
if inp.task_type != "implementation-planning":
|
|
1549
|
-
if inp.selected_direction_path:
|
|
1550
|
-
raise PrepareError(
|
|
1551
|
-
"--selected-direction is only meaningful with --task-type "
|
|
1552
|
-
f"implementation-planning; got {inp.task_type}"
|
|
1553
|
-
)
|
|
1554
|
-
return
|
|
1555
|
-
if inp.selected_direction_path and inp.clarification_response_path:
|
|
1556
|
-
raise PrepareError(
|
|
1557
|
-
"implementation-planning accepts either --selected-direction for a "
|
|
1558
|
-
"new plan or --clarification-response for a rerun, not both"
|
|
1559
|
-
)
|
|
1560
|
-
if inp.selected_direction_path:
|
|
1561
|
-
return
|
|
1562
|
-
if inp.clarification_response_path:
|
|
1563
|
-
if not _is_existing_planning_report(
|
|
1564
|
-
inp.clarification_response_path, project_root, inp
|
|
1565
|
-
):
|
|
1566
|
-
raise PrepareError(
|
|
1567
|
-
"implementation-planning rerun --clarification-response must point "
|
|
1568
|
-
"to an existing implementation-planning report for the same task"
|
|
1569
|
-
)
|
|
1570
|
-
return
|
|
1571
|
-
raise PrepareError(
|
|
1572
|
-
"implementation-planning requires --selected-direction for a new plan or "
|
|
1573
|
-
"--clarification-response to an existing implementation-planning report"
|
|
1574
|
-
)
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
def _resolve_planning_direction(inp: PrepareInputs) -> SelectedDirection | None:
|
|
1578
|
-
if inp.task_type != "implementation-planning" or not inp.selected_direction_path:
|
|
1579
|
-
return None
|
|
1580
|
-
from .paths import task_dir
|
|
1581
|
-
|
|
1582
|
-
try:
|
|
1583
|
-
report = lexical_absolute_path(Path(inp.selected_direction_path))
|
|
1584
|
-
expected_task_root = lexical_absolute_path(
|
|
1585
|
-
task_dir(Path(inp.project_root), inp.task_group, inp.task_id)
|
|
1586
|
-
)
|
|
1587
|
-
report.relative_to(expected_task_root)
|
|
1588
|
-
except ValueError as exc:
|
|
1589
|
-
raise PrepareError(
|
|
1590
|
-
"selected direction report must belong to the same task"
|
|
1591
|
-
) from exc
|
|
1592
|
-
try:
|
|
1593
|
-
validate_task_artifact_path(
|
|
1594
|
-
report, expected_task_root, "selected direction report"
|
|
1595
|
-
)
|
|
1596
|
-
return resolve_selected_direction(
|
|
1597
|
-
report,
|
|
1598
|
-
expected_task_key=(
|
|
1599
|
-
f"{inp.project_id}:{inp.task_group}:{inp.task_id}"
|
|
1600
|
-
),
|
|
1601
|
-
)
|
|
1602
|
-
except DirectionSelectionError as exc:
|
|
1603
|
-
raise PrepareError(str(exc)) from exc
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
def _validate_reverify_scope(inp: PrepareInputs) -> None:
|
|
1607
|
-
"""A pinned re-verification scope is only actionable on a planning re-run.
|
|
1608
|
-
|
|
1609
|
-
Every other phase renders the tokens too (the template always reads them),
|
|
1610
|
-
but nothing consumes them there — so a value outside the one phase that
|
|
1611
|
-
acts on it is a caller mistake, not a preference to honour silently.
|
|
1612
|
-
"""
|
|
1613
|
-
if not (inp.reverify_scope or "").strip():
|
|
1614
|
-
return
|
|
1615
|
-
if inp.task_type != "implementation-planning":
|
|
1616
|
-
raise PrepareError(
|
|
1617
|
-
"--reverify-scope is only meaningful with --task-type "
|
|
1618
|
-
f"implementation-planning; got {inp.task_type}"
|
|
1619
|
-
)
|
|
1620
|
-
if not inp.clarification_response_path:
|
|
1621
|
-
raise PrepareError(
|
|
1622
|
-
"--reverify-scope needs --clarification-response: there is no prior "
|
|
1623
|
-
"report to narrow re-verification against"
|
|
1624
|
-
)
|
|
1625
|
-
try:
|
|
1626
|
-
parse_user_reverify_scope(inp.reverify_scope)
|
|
1627
|
-
except ReverifyScopeError as exc:
|
|
1628
|
-
raise PrepareError(str(exc)) from exc
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
1490
|
def _implementation_plan_contract(
|
|
1632
1491
|
inp: PrepareInputs,
|
|
1633
1492
|
) -> tuple[str, Path, dict | None]:
|
|
@@ -1773,13 +1632,12 @@ def _validate_analysis_prepare_inputs(project_root: Path, inp: PrepareInputs) ->
|
|
|
1773
1632
|
)
|
|
1774
1633
|
return
|
|
1775
1634
|
if inp.task_type == "project-analysis":
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
raise PrepareError("project-analysis does not accept evidence inputs")
|
|
1635
|
+
from .phases.project_analysis.entry import validate_project_analysis_inputs
|
|
1636
|
+
|
|
1637
|
+
validate_project_analysis_inputs(inp.analysis_target, inp.evidence_inputs_raw)
|
|
1780
1638
|
return
|
|
1781
|
-
if inp.task_type == "feature-analysis"
|
|
1782
|
-
|
|
1639
|
+
if inp.task_type == "feature-analysis":
|
|
1640
|
+
validate_feature_target_input(inp.analysis_target)
|
|
1783
1641
|
if inp.task_type != "feature-analysis" and inp.analysis_target:
|
|
1784
1642
|
raise PrepareError(f"{inp.task_type} does not accept an analysis target")
|
|
1785
1643
|
paths = parse_evidence_paths(inp.evidence_inputs_raw, project_root)
|
|
@@ -1794,127 +1652,10 @@ def _validate_analysis_prepare_inputs(project_root: Path, inp: PrepareInputs) ->
|
|
|
1794
1652
|
raise PrepareError(str(exc)) from exc
|
|
1795
1653
|
|
|
1796
1654
|
|
|
1797
|
-
def _collect_handoff_source_report_rows(
|
|
1798
|
-
rows: list, nums: list,
|
|
1799
|
-
) -> list:
|
|
1800
|
-
"""선택 stage 들의 마지막 verified 행에서 인용 표 행을 만든다 (last-wins)."""
|
|
1801
|
-
last_verified: dict = {}
|
|
1802
|
-
for r in rows:
|
|
1803
|
-
if r.get("status") == "verified" and isinstance(r.get("stage"), int):
|
|
1804
|
-
last_verified[r["stage"]] = r
|
|
1805
|
-
return [(str(n), Path(last_verified[n]["report_path"])) for n in nums]
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
def _render_handoff_sources(sources: list, project_root: Path) -> tuple[str, str]:
|
|
1809
|
-
"""인용 표와 미해결 조건은 같은 원본 판정에서 렌더한다."""
|
|
1810
|
-
from .release_gate import release_handoff_allowed, verdict_token
|
|
1811
|
-
|
|
1812
|
-
rows, conditions = [], []
|
|
1813
|
-
for stage, report in sources:
|
|
1814
|
-
data = load_owned_object(report, artifact="handoff verification report")
|
|
1815
|
-
if not release_handoff_allowed(data):
|
|
1816
|
-
raise PrepareError(f"verification no longer release-ready: {report}")
|
|
1817
|
-
relative = relative_to_project_root(report, project_root) or str(report)
|
|
1818
|
-
rows.append(f"| {stage} | {relative} | `{verdict_token(data)}` |")
|
|
1819
|
-
if verdict_token(data) == "conditional-accept":
|
|
1820
|
-
for condition in data["finalVerdict"]["conditionalAcceptanceConditions"]:
|
|
1821
|
-
conditions.append(
|
|
1822
|
-
f"- Stage {stage}, `{condition.get('id', '')}`\n"
|
|
1823
|
-
f" - Condition: {condition.get('condition', '')}\n"
|
|
1824
|
-
f" - Evidence required: {condition.get('evidenceRequired', '')}\n"
|
|
1825
|
-
f" - blocksReleaseHandoff: {json.dumps(condition['blocksReleaseHandoff'])}"
|
|
1826
|
-
)
|
|
1827
|
-
section = "## Unresolved Conditional Acceptance Conditions\n\n" + "\n".join(conditions) if conditions else ""
|
|
1828
|
-
return "\n".join(rows), section
|
|
1829
1655
|
|
|
1830
1656
|
|
|
1831
|
-
def _materialize_release_handoff_input(
|
|
1832
|
-
workspace_root: Path, project_root: Path, inp: "PrepareInputs",
|
|
1833
|
-
) -> dict:
|
|
1834
|
-
"""release-handoff 의 입력 문서를 검증 보고서 인용으로 자동 생성한다.
|
|
1835
|
-
|
|
1836
|
-
brief 는 entry phase 의 입력물이라 release-handoff 에는 없다 — 이 run 의
|
|
1837
|
-
`--task-brief` 자리는 여기서 생성된 input 문서가 채운다. stage 자격은
|
|
1838
|
-
okstra_ctl.handoff 의 SSOT 판정을 prepare 에서도 그대로 강제한다.
|
|
1839
|
-
반환: ctx 에 올릴 {"HANDOFF_STAGES": ...}."""
|
|
1840
|
-
from .consumers import read_consumers
|
|
1841
|
-
from .handoff import HandoffError, _require_eligible, compute_eligibility
|
|
1842
|
-
from .paths import task_dir
|
|
1843
|
-
from .render import render_template_with_ctx
|
|
1844
|
-
from .run_context import _now_task_date
|
|
1845
1657
|
|
|
1846
|
-
# Path("") 는 "." 로 정규화된다 — 빈 brief 센티널은 둘 다로 들어올 수 있다.
|
|
1847
|
-
if str(inp.brief_path).strip() not in ("", "."):
|
|
1848
|
-
raise PrepareError(
|
|
1849
|
-
"--task-brief is not accepted for --task-type release-handoff — "
|
|
1850
|
-
"the input document is generated from the cited final-verification "
|
|
1851
|
-
"reports (pick stages via --stages, or leave it empty to take "
|
|
1852
|
-
"every eligible stage)"
|
|
1853
|
-
)
|
|
1854
|
-
if not inp.approved_plan_path:
|
|
1855
|
-
raise PrepareError(
|
|
1856
|
-
"release-handoff requires --approved-plan "
|
|
1857
|
-
"<implementation-planning final-report> — the Stage Map and the "
|
|
1858
|
-
"consumers ledger drive stage eligibility"
|
|
1859
|
-
)
|
|
1860
|
-
plan = Path(inp.approved_plan_path)
|
|
1861
|
-
if not plan.is_file():
|
|
1862
|
-
raise PrepareError(f"approved plan not found: {plan}")
|
|
1863
|
-
stage_map = _parse_stage_map_into_ctx(str(plan))
|
|
1864
|
-
rows = read_consumers(plan_run_root_from_approved_plan(plan))
|
|
1865
1658
|
|
|
1866
|
-
if inp.stages:
|
|
1867
|
-
try:
|
|
1868
|
-
nums = sorted({int(x) for x in inp.stages.split(",") if x.strip()})
|
|
1869
|
-
except ValueError:
|
|
1870
|
-
raise PrepareError(
|
|
1871
|
-
f"--stages must be a comma-separated int list, got {inp.stages!r}"
|
|
1872
|
-
)
|
|
1873
|
-
if not nums:
|
|
1874
|
-
raise PrepareError("--stages must select at least one stage")
|
|
1875
|
-
try:
|
|
1876
|
-
_require_eligible(stage_map, rows, nums)
|
|
1877
|
-
except HandoffError as exc:
|
|
1878
|
-
raise PrepareError(str(exc)) from exc
|
|
1879
|
-
else:
|
|
1880
|
-
eligibility = compute_eligibility(stage_map, rows)
|
|
1881
|
-
nums = [e["stage"] for e in eligibility if e["eligible"]]
|
|
1882
|
-
if not nums:
|
|
1883
|
-
blocked = "; ".join(
|
|
1884
|
-
f"stage {e['stage']}: {', '.join(e['reasons'])}"
|
|
1885
|
-
for e in eligibility) or "(no stage in the Stage Map)"
|
|
1886
|
-
raise PrepareError(
|
|
1887
|
-
"release-handoff has no eligible stage — every stage must "
|
|
1888
|
-
"carry a release-ready single-stage final-verification of its "
|
|
1889
|
-
f"recorded implementation commit. {blocked}"
|
|
1890
|
-
)
|
|
1891
|
-
stages_csv = ",".join(str(n) for n in nums)
|
|
1892
|
-
report_rows = _collect_handoff_source_report_rows(rows, nums)
|
|
1893
|
-
|
|
1894
|
-
source_rows, source_conditions = _render_handoff_sources(report_rows, project_root)
|
|
1895
|
-
|
|
1896
|
-
template = (workspace_root / "templates" / "reports"
|
|
1897
|
-
/ "release-handoff-input.template.md")
|
|
1898
|
-
if not template.is_file():
|
|
1899
|
-
raise PrepareError(
|
|
1900
|
-
f"release-handoff input template missing: {template}.{_INSTALL_HINT}"
|
|
1901
|
-
)
|
|
1902
|
-
out = (task_dir(project_root, inp.task_group, inp.task_id)
|
|
1903
|
-
/ "release-handoff-input.md")
|
|
1904
|
-
out.parent.mkdir(parents=True, exist_ok=True)
|
|
1905
|
-
render_template_with_ctx(str(template), str(out), {
|
|
1906
|
-
"TASK_KEY": f"{inp.project_id}:{inp.task_group}:{inp.task_id}",
|
|
1907
|
-
"TASK_ID": inp.task_id,
|
|
1908
|
-
"TASK_GROUP": inp.task_group,
|
|
1909
|
-
"PROJECT_ID": inp.project_id,
|
|
1910
|
-
"TASK_TYPE": inp.task_type,
|
|
1911
|
-
"TASK_DATE": _now_task_date(),
|
|
1912
|
-
"HANDOFF_STAGES": stages_csv,
|
|
1913
|
-
"HANDOFF_SOURCE_REPORTS": source_rows,
|
|
1914
|
-
"HANDOFF_CONDITIONS": source_conditions,
|
|
1915
|
-
})
|
|
1916
|
-
inp.brief_path = out
|
|
1917
|
-
return {"HANDOFF_STAGES": stages_csv}
|
|
1918
1659
|
|
|
1919
1660
|
|
|
1920
1661
|
QA_COMMAND_EXECUTING_TASK_TYPES = ("implementation", "final-verification")
|
|
@@ -2659,28 +2400,11 @@ def _read_model_defaults(path: Path) -> dict[str, tuple[str, ...]]:
|
|
|
2659
2400
|
return defaults
|
|
2660
2401
|
|
|
2661
2402
|
|
|
2662
|
-
def _validate_option_selection_roster(
|
|
2663
|
-
task_type
|
|
2664
|
-
)
|
|
2665
|
-
"""Require independent analysis from three workers before option selection."""
|
|
2666
|
-
if task_type != "implementation-option-selection":
|
|
2667
|
-
return
|
|
2668
|
-
analyser_count = sum(worker != "report-writer" for worker in workers)
|
|
2669
|
-
if analyser_count < 3:
|
|
2670
|
-
raise PrepareError(
|
|
2671
|
-
"implementation-option-selection requires at least 3 analyser workers"
|
|
2672
|
-
)
|
|
2403
|
+
def _validate_option_selection_roster(task_type: str, workers: Sequence[str]) -> None:
|
|
2404
|
+
if task_type == "implementation-option-selection":
|
|
2405
|
+
validate_analyser_roster(workers)
|
|
2673
2406
|
|
|
2674
2407
|
|
|
2675
|
-
def _resolve_pr_template(inp: PrepareInputs) -> tuple[str, str]:
|
|
2676
|
-
"""release-handoff 전용 PR 본문 템플릿 경로 + 출처를 해소한다 (그 외엔 빈 값)."""
|
|
2677
|
-
if inp.task_type != "release-handoff":
|
|
2678
|
-
return "", ""
|
|
2679
|
-
try:
|
|
2680
|
-
resolved_tpl = resolve_pr_template_path(Path(inp.project_root), inp.pr_template_path)
|
|
2681
|
-
except PrTemplateError as exc:
|
|
2682
|
-
raise PrepareError(f"PR template resolution failed: {exc}") from exc
|
|
2683
|
-
return str(resolved_tpl.path), resolved_tpl.source
|
|
2684
2408
|
|
|
2685
2409
|
|
|
2686
2410
|
@dataclass
|
|
@@ -2774,7 +2498,7 @@ def recommended_role_models(
|
|
|
2774
2498
|
codex 이고 다른 provider 요청은 거부된다(`resolve_lead_provider`). 그래서
|
|
2775
2499
|
`lead_provider` 를 받아 그 provider 의 기본값으로 해소한다. 받지 않으면
|
|
2776
2500
|
role 별 레거시 기본값(claude 계열)으로 떨어지는데, 그 값을 codex 호스트
|
|
2777
|
-
화면에 그대로 쓰면 안내는 `opus` 인데 prepare 는 `gpt-6-sol` 을 배정한다.
|
|
2501
|
+
화면에 그대로 쓰면 안내는 `opus` 인데 prepare 는 `gpt-6.1-sol` 을 배정한다.
|
|
2778
2502
|
"""
|
|
2779
2503
|
lead_provider_id = lead_provider or "claude"
|
|
2780
2504
|
report_writer_provider_id = report_writer_provider or "claude"
|
|
@@ -3562,6 +3286,9 @@ def _write_instruction_set_sources(
|
|
|
3562
3286
|
profile_rendered = profile_content
|
|
3563
3287
|
if inp.task_type == "implementation":
|
|
3564
3288
|
profile_rendered += "\n\n{{DESIGN_PREP_CONTEXT}}\n\n{{FIX_RUN_CONTEXT}}"
|
|
3289
|
+
profile_rendered = _implementation_stage.resolve_instruction_paths(
|
|
3290
|
+
profile_rendered, Path(ctx["WORKSPACE_ROOT"])
|
|
3291
|
+
)
|
|
3565
3292
|
profile_tokens = (
|
|
3566
3293
|
"EXECUTOR_WORKER_ID",
|
|
3567
3294
|
"EXECUTOR_PROVIDER",
|
|
@@ -3584,13 +3311,14 @@ def _write_instruction_set_sources(
|
|
|
3584
3311
|
profile_rendered = profile_rendered.replace("{{" + key + "}}", ctx.get(key, ""))
|
|
3585
3312
|
(instruction_set / "analysis-profile.md").write_text(profile_rendered, encoding="utf-8")
|
|
3586
3313
|
if inp.task_type == "implementation":
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3314
|
+
from .phases.catalog import profile_sidecar
|
|
3315
|
+
|
|
3316
|
+
executor_source = profile_sidecar(
|
|
3317
|
+
Path(ctx["WORKSPACE_ROOT"]), "prompts/profiles/_implementation-executor.md"
|
|
3318
|
+
)
|
|
3319
|
+
executor_rendered = _implementation_stage.resolve_instruction_paths(
|
|
3320
|
+
executor_source.read_text(encoding="utf-8"), Path(ctx["WORKSPACE_ROOT"])
|
|
3592
3321
|
)
|
|
3593
|
-
executor_rendered = executor_source.read_text(encoding="utf-8")
|
|
3594
3322
|
for key in profile_tokens:
|
|
3595
3323
|
executor_rendered = executor_rendered.replace(
|
|
3596
3324
|
"{{" + key + "}}", ctx.get(key, "")
|
|
@@ -3700,17 +3428,10 @@ def _write_instruction_set_sources(
|
|
|
3700
3428
|
),
|
|
3701
3429
|
)
|
|
3702
3430
|
if inp.task_type == "technical-verification":
|
|
3703
|
-
|
|
3704
|
-
|
|
3431
|
+
packet += materialize_technical_input_packet(
|
|
3432
|
+
ctx["TECHNICAL_VERIFICATION_INPUT_JSON"],
|
|
3705
3433
|
Path(ctx["RUN_DIR"]), ctx["RUN_MANIFESTS_SEQ"],
|
|
3706
3434
|
)
|
|
3707
|
-
packet += (
|
|
3708
|
-
"\n## Technical Verification Input\n\n"
|
|
3709
|
-
f"- Frozen input: `{verification_path}`\n"
|
|
3710
|
-
f"- Experiment copies only: `{ctx['RUN_DIR']}/experiments/{ctx['RUN_MANIFESTS_SEQ']}/<worker-id>/`\n"
|
|
3711
|
-
"- Read the input before planning experiments; copy each fact identity verbatim into results.\n"
|
|
3712
|
-
+ "\n```json\n" + ctx["TECHNICAL_VERIFICATION_INPUT_JSON"] + "\n```\n"
|
|
3713
|
-
)
|
|
3714
3435
|
if inp.task_type in ANALYSIS_TASK_TYPES:
|
|
3715
3436
|
packet += (
|
|
3716
3437
|
"\n## Analysis Evidence\n\n"
|
|
@@ -4021,7 +3742,7 @@ def _record_fix_cycle_events(inp: PrepareInputs, ctx: dict) -> str:
|
|
|
4021
3742
|
|
|
4022
3743
|
def _finalize_status_and_render_manifests(
|
|
4023
3744
|
inp: PrepareInputs, ctx: dict, task_index_template: Path,
|
|
4024
|
-
|
|
3745
|
+
boundary: dict[str, str],
|
|
4025
3746
|
) -> None:
|
|
4026
3747
|
"""최종 task/run status 를 확정하고 workflow state 를 재계산한 뒤 team-state,
|
|
4027
3748
|
task-manifest, task-index, run-manifest, timeline, discovery 산출물을 렌더한다."""
|
|
@@ -4038,7 +3759,7 @@ def _finalize_status_and_render_manifests(
|
|
|
4038
3759
|
current_run_status=ctx["CURRENT_RUN_STATUS"],
|
|
4039
3760
|
current_task_status=ctx["CURRENT_TASK_STATUS"],
|
|
4040
3761
|
render_only=inp.render_only,
|
|
4041
|
-
|
|
3762
|
+
boundary=boundary,
|
|
4042
3763
|
work_category=inp.work_category,
|
|
4043
3764
|
))
|
|
4044
3765
|
render_team_state(ctx["TEAM_STATE_PATH"], ctx)
|
|
@@ -4329,7 +4050,7 @@ def _reconcile_prior_runs(ctx: dict) -> None:
|
|
|
4329
4050
|
|
|
4330
4051
|
|
|
4331
4052
|
def _initial_workflow_ctx(
|
|
4332
|
-
inp: PrepareInputs,
|
|
4053
|
+
inp: PrepareInputs, boundary: dict
|
|
4333
4054
|
) -> dict[str, str]:
|
|
4334
4055
|
"""Workflow tokens for a run that has been prepared but not started.
|
|
4335
4056
|
|
|
@@ -4346,7 +4067,7 @@ def _initial_workflow_ctx(
|
|
|
4346
4067
|
current_run_status=run_status,
|
|
4347
4068
|
current_task_status=task_status,
|
|
4348
4069
|
render_only=inp.render_only,
|
|
4349
|
-
|
|
4070
|
+
boundary=boundary,
|
|
4350
4071
|
),
|
|
4351
4072
|
}
|
|
4352
4073
|
|
|
@@ -4700,16 +4421,11 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
4700
4421
|
technical_input = None
|
|
4701
4422
|
from .paths import task_dir
|
|
4702
4423
|
if inp.task_type == "technical-verification":
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
task_dir(project_root, inp.task_group, inp.task_id),
|
|
4709
|
-
f"{inp.project_id}:{inp.task_group}:{inp.task_id}",
|
|
4710
|
-
)
|
|
4711
|
-
except ValueError as exc:
|
|
4712
|
-
raise PrepareError(str(exc)) from exc
|
|
4424
|
+
technical_input = prepare_technical_verification_input(
|
|
4425
|
+
inp.clarification_response_path, project_root,
|
|
4426
|
+
task_dir(project_root, inp.task_group, inp.task_id),
|
|
4427
|
+
f"{inp.project_id}:{inp.task_group}:{inp.task_id}",
|
|
4428
|
+
)
|
|
4713
4429
|
if inp.task_type == "implementation":
|
|
4714
4430
|
ctx_stage_map = _prepare_implementation_approved_plan(inp)
|
|
4715
4431
|
|
|
@@ -4717,11 +4433,13 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
4717
4433
|
# 이후의 모든 brief 소비 경로(material/instruction-set 복사)는 그대로 동작한다.
|
|
4718
4434
|
handoff_tokens = {"HANDOFF_STAGES": ""}
|
|
4719
4435
|
if inp.task_type == "release-handoff":
|
|
4720
|
-
handoff_tokens =
|
|
4721
|
-
workspace_root, project_root, inp)
|
|
4436
|
+
handoff_tokens = materialize_handoff_input(
|
|
4437
|
+
workspace_root, project_root, inp, install_hint=_INSTALL_HINT)
|
|
4722
4438
|
|
|
4723
4439
|
workers, selected_reviewers = _resolve_roster(inp, profile_file)
|
|
4724
|
-
pr_template_path_str, pr_template_source =
|
|
4440
|
+
pr_template_path_str, pr_template_source = (
|
|
4441
|
+
resolve_pr_template(inp) if inp.task_type == "release-handoff" else ("", "")
|
|
4442
|
+
)
|
|
4725
4443
|
|
|
4726
4444
|
assignment_context, assignment_plan = _resolve_prepare_assignments(
|
|
4727
4445
|
inp=inp,
|
|
@@ -4882,7 +4600,7 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
4882
4600
|
if inp.approved_plan_path else ""
|
|
4883
4601
|
)
|
|
4884
4602
|
|
|
4885
|
-
|
|
4603
|
+
boundary = load_phase_boundary(workspace_root, inp.task_type)
|
|
4886
4604
|
|
|
4887
4605
|
# ---- assemble full ctx (the values render functions expect) ----
|
|
4888
4606
|
ctx.update({
|
|
@@ -4908,7 +4626,7 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
4908
4626
|
"BRIEF_RELATIVE_PATH": brief_relative,
|
|
4909
4627
|
**_model_ctx(models),
|
|
4910
4628
|
**_related_tasks_ctx(ctx, inp),
|
|
4911
|
-
**_initial_workflow_ctx(inp,
|
|
4629
|
+
**_initial_workflow_ctx(inp, boundary),
|
|
4912
4630
|
"VALIDATION_STATUS": "not-run",
|
|
4913
4631
|
"VALIDATION_UPDATED_AT": "",
|
|
4914
4632
|
"VALIDATION_FAILURES_JSON": "[]",
|
|
@@ -4975,7 +4693,7 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
4975
4693
|
|
|
4976
4694
|
# ---- final status + manifest/discovery renders ----
|
|
4977
4695
|
_finalize_status_and_render_manifests(
|
|
4978
|
-
inp, ctx, task_index_template,
|
|
4696
|
+
inp, ctx, task_index_template, boundary
|
|
4979
4697
|
)
|
|
4980
4698
|
persist_static_execution_identity(
|
|
4981
4699
|
run_manifest_path=Path(ctx["RUN_MANIFEST_PATH"]),
|