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
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# implementation process
|
|
2
|
+
|
|
3
|
+
## Index
|
|
4
|
+
|
|
5
|
+
- [1. Purpose](#1-purpose)
|
|
6
|
+
- [2. okstra-run wizard flow](#2-okstra-run-wizard-flow)
|
|
7
|
+
- [Carry-in](#carry-in)
|
|
8
|
+
- [3. runtime gate](#3-runtime-gate)
|
|
9
|
+
- [3.1 design-preparation preflight](#31-design-preparation-preflight)
|
|
10
|
+
- [4. executor and verifier](#4-executor-and-verifier)
|
|
11
|
+
- [5. stage and consumers](#5-stage-and-consumers)
|
|
12
|
+
- [6. Deliverables](#6-deliverables)
|
|
13
|
+
- [7. Forbidden actions](#7-forbidden-actions)
|
|
14
|
+
- [8. Verified code](#8-verified-code)
|
|
15
|
+
|
|
16
|
+
## 1. Purpose
|
|
17
|
+
|
|
18
|
+
`implementation` executes an approved `implementation-planning` final report into actual code changes and local commits. Source edit is allowed only in this phase, but scope is limited to the approved plan and recorded out-of-plan justification.
|
|
19
|
+
|
|
20
|
+
## 2. okstra-run wizard flow
|
|
21
|
+
|
|
22
|
+
```mermaid
|
|
23
|
+
flowchart TD
|
|
24
|
+
Start[/okstra-run/] --> Common[common task identity flow]
|
|
25
|
+
Common --> Type[task-type = implementation]
|
|
26
|
+
Type --> Worktree{active task worktree?}
|
|
27
|
+
Worktree -->|yes| PlanPick[approved plan pick/text]
|
|
28
|
+
Worktree -->|no| BaseRef[base-ref pick/text]
|
|
29
|
+
BaseRef --> PlanPick
|
|
30
|
+
PlanPick --> Approved{APPROVED marker present?}
|
|
31
|
+
Approved -->|no| Retry[re-prompt same step]
|
|
32
|
+
Approved -->|yes| Stage[stage multi-pick<br/>ready/active markers]
|
|
33
|
+
Stage --> Chain[render-args<br/>stage + chain-stages]
|
|
34
|
+
Chain --> RoleCount[role-count min..max<br/>omit uses recommended; skip if min==max]
|
|
35
|
+
RoleCount --> RoleModel[role-model provider/model per slot]
|
|
36
|
+
RoleModel --> RoleAdd[min=0 roles via role-add only<br/>default skip]
|
|
37
|
+
RoleAdd --> Extras[directive, related tasks, clarification]
|
|
38
|
+
Extras --> Confirm
|
|
39
|
+
Confirm --> Render[render-bundle]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Launch selection uses role slots and model refs only: current-session lead is this session (listed on the confirmation summary), then each static role's count in `min..max` (default **recommended**; the count step is skipped when `min == max`), then one `provider/model` per slot. Roles with `min = 0` stay closed unless the user opens them with role-add (default skip). Duplicate model refs in the same role are rejected. There is no provider roster multi-pick and no defaults-vs-customize fork. `executor` is only a compatibility alias for `implementer` in model refs; implementer slots are chosen through role-count / role-model. Dynamic verifiers are not chosen at launch. `--workers` is a CLI compatibility input only, not a launch picker. `stage_pick` is a multi-pick that shows done/in-progress/ready/waiting status. The selected stage set goes through dependency closure and topological sort into a `chain-stages` CSV, and each actual run executes only one of those stages.
|
|
43
|
+
|
|
44
|
+
The current okstra-run wizard path does not expose `--approve` that directly flips the approval checkbox. The plan file must already have a recognized approval marker.
|
|
45
|
+
|
|
46
|
+
## Carry-in
|
|
47
|
+
|
|
48
|
+
Three facts about what reaches an implementation run from its approved plan, gathered
|
|
49
|
+
here because they were previously readable only by tracing the runtime sources.
|
|
50
|
+
|
|
51
|
+
- **What is attached automatically.** The user's answers to the approved plan's `## 1.
|
|
52
|
+
Clarification Items` rows — the `user-response-*.md` sidecars under
|
|
53
|
+
`runs/implementation-planning/user-responses/`, a sibling of the directory holding the
|
|
54
|
+
plan itself — are collected into `instruction-set/clarification-response.md`. Those
|
|
55
|
+
sidecars are written by the user, not by the report renderer. The report HTML's `Export
|
|
56
|
+
user response` button downloads a file the user then saves there. The in-session flow
|
|
57
|
+
reads `user-response list-view` and `user-response show-view --report <path>
|
|
58
|
+
--project-root <root>`, then uses `user-response begin`, typed `user-response answer`
|
|
59
|
+
and decision commands, and `user-response finalize` to publish the sidecar. The
|
|
60
|
+
renderer at most pre-creates that directory empty so the user does not
|
|
61
|
+
have to; it never puts a sidecar in it.
|
|
62
|
+
The plan document is *not* copied: it reaches the run as the `--approved-plan` path and
|
|
63
|
+
the executor re-reads it there. An explicit `--clarification-response` wins when given;
|
|
64
|
+
the automatic attachment is the fallback for an implementation run that supplies none
|
|
65
|
+
(`scripts/okstra_ctl/run.py`, the `implementation` carry-in branch).
|
|
66
|
+
- **Who reads it, and when.** The executor, before its first edit
|
|
67
|
+
(`scripts/okstra_ctl/phases/implementation/instructions/_implementation-executor.md`). A CLI executor (codex/antigravity)
|
|
68
|
+
cannot reach that path from inside its sandbox, so the lead transcribes the file's body
|
|
69
|
+
into the dispatched executor prompt — a path reference alone never arrives.
|
|
70
|
+
- **What happens when an answer contradicts the plan.** Each answer is an authoritative
|
|
71
|
+
refinement of its matching row's scope, but an answer that contradicts the approved plan
|
|
72
|
+
or expands scope beyond it is a re-plan trigger: it routes to a new
|
|
73
|
+
`implementation-planning` run rather than being absorbed silently mid-run. Quietly
|
|
74
|
+
widening scope inside an implementation run is what this branch exists to prevent.
|
|
75
|
+
|
|
76
|
+
## 3. runtime gate
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
sequenceDiagram
|
|
80
|
+
participant W as okstra-run
|
|
81
|
+
participant P as prepare_task_bundle
|
|
82
|
+
participant Plan as approved final-report
|
|
83
|
+
participant QA as project.json qaCommands
|
|
84
|
+
participant Stage as stage target policy
|
|
85
|
+
participant Reg as worktree registry
|
|
86
|
+
participant WT as stage worktree
|
|
87
|
+
|
|
88
|
+
W->>P: task-type=implementation, approved-plan, stage, executor
|
|
89
|
+
P->>Plan: file exists?
|
|
90
|
+
P->>Plan: approval marker regex matches?
|
|
91
|
+
P->>Plan: unresolved Blocks=approval rows?
|
|
92
|
+
P->>Stage: build Stage Lifecycle Snapshot
|
|
93
|
+
P->>Reg: read active stage-key reservations
|
|
94
|
+
P->>Stage: select exactly one ready stage
|
|
95
|
+
P->>Plan: resolve selected stage design preparation
|
|
96
|
+
P->>WT: provision stage-N worktree + branch
|
|
97
|
+
P->>QA: validate qaCommands deny-list
|
|
98
|
+
P->>P: executor provider in resolved roster?
|
|
99
|
+
P->>P: namespace run artifacts under stage-N
|
|
100
|
+
P-->>W: prepared implementation prompt or PrepareError
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`--approve` exists in the Python runtime, but the okstra-run wizard does not emit it as args. On the shell path, `--approve` sets the report record `frontmatter.approved` to `true` and then follows the same validation path.
|
|
104
|
+
|
|
105
|
+
### 3.1 design-preparation preflight
|
|
106
|
+
|
|
107
|
+
Right after stage selection, before worktree provisioning and appending `status:"started"` to `consumers.jsonl`, it resolves the design preparation of the approved plan. The resolver reads only items whose `stageRefs` includes the selected stage, so an undecided decision in another stage does not block the current run.
|
|
108
|
+
|
|
109
|
+
| outcome | runtime behavior |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `proceed` | Inject the effective AI proposal, confirmed override, guardrail, and provisional working assumption into the executor prompt as `DESIGN_PREP_CONTEXT`, and create the worktree. |
|
|
112
|
+
| `wait_for_input` | Stop with `stage <N> waits for design input: ...; <request paths>`. Do not create the worktree or the started consumer row. |
|
|
113
|
+
| `replan` | Stop with `stage <N> requires implementation-planning rerun: ...`. Let a newly approved/edited decision change the planning snapshot or the Stage Map. |
|
|
114
|
+
|
|
115
|
+
`ready`, `not-applicable`, and `no-design-inputs` proceed. `provisional` can proceed even without a response because there is a safe working assumption, and a non-triggering approval/edit states that assumption and override in the prompt. A `blocked` non-response makes only that stage wait, and when a blocked draft is approved/edited, it replans so that authorization is reflected into the approved plan. A markerless legacy plan proceeds with a `legacy-unassessed` warning without modifying the report.
|
|
116
|
+
|
|
117
|
+
`manual-user-test` input uses the same flexible status. The planning draft is a seed for implementation to concretize the verification method against the actual diff, and the SSOT of the final execution method is the implementation report's `implementation.manualUserTest`. final-verification does not directly execute the planning sidecar.
|
|
118
|
+
|
|
119
|
+
Tier 3 conformance uses the same ownership boundary during execution and
|
|
120
|
+
verification.
|
|
121
|
+
|
|
122
|
+
| Entry policy | PASS | FAIL / MISSING / unavailable |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `requires` contains `db`, `http`, or `external` | Evidence recorded | Advisory; user rerun method recorded; run continues |
|
|
125
|
+
| `requires=[]` or `requires=[io]` | Evidence recorded | Blocking |
|
|
126
|
+
|
|
127
|
+
These outcomes are enforced by
|
|
128
|
+
`scripts/okstra_ctl/conformance.py::decide_conformance_gate` and
|
|
129
|
+
`validators/validate-run.py::_validate_conformance`.
|
|
130
|
+
|
|
131
|
+
## 4. executor and verifier
|
|
132
|
+
|
|
133
|
+
```mermaid
|
|
134
|
+
flowchart TD
|
|
135
|
+
Lead[Okstra lead<br/>host native] --> Exec[Executor<br/>selected provider]
|
|
136
|
+
Lead --> CV[Claude verifier<br/>read-only]
|
|
137
|
+
Lead --> XV[Codex verifier<br/>read-only]
|
|
138
|
+
Lead --> GV{Antigravity in roster?}
|
|
139
|
+
GV -->|yes| Gem[Antigravity verifier<br/>read-only]
|
|
140
|
+
Exec --> Diff[Source edits + local commits]
|
|
141
|
+
CV --> QA[Independent QA rerun]
|
|
142
|
+
XV --> QA
|
|
143
|
+
Gem --> QA
|
|
144
|
+
QA --> External{External Tier 3<br/>non-PASS?}
|
|
145
|
+
External -->|yes| Advisory[ADVISORY<br/>user-owned rerun]
|
|
146
|
+
External -->|no| Verdict[PASS / CONCERNS / FAIL]
|
|
147
|
+
Advisory --> Report
|
|
148
|
+
Verdict --> Report[Final report preserves dissent]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Only the executor may mutate project files. The verifier independently re-runs the diff and validation command read-only in the same worktree. Even a verifier with the same provider as the executor runs again in a separate fresh CLI session. This is to prevent a structure where the same session approves a diff the same session wrote.
|
|
152
|
+
|
|
153
|
+
## 5. stage and consumers
|
|
154
|
+
|
|
155
|
+
```mermaid
|
|
156
|
+
flowchart LR
|
|
157
|
+
Plan[approved plan<br/>Stage Map] --> Parse[parse stage map]
|
|
158
|
+
Parse --> Snapshot[Stage Lifecycle Snapshot<br/>carry + consumers + reservations]
|
|
159
|
+
Snapshot --> Resolve{stage arg}
|
|
160
|
+
Resolve -->|auto| Next[lowest ready<br/>not done/started/reserved]
|
|
161
|
+
Resolve -->|number| Forced[selected stage]
|
|
162
|
+
Next --> Prep{selected-stage<br/>design preflight}
|
|
163
|
+
Forced --> Prep
|
|
164
|
+
Prep -->|proceed| Base[resolve stage base commit]
|
|
165
|
+
Prep -->|wait / replan| Stop[stop before worktree<br/>and started consumer]
|
|
166
|
+
Base --> WT[create/reuse stage worktree]
|
|
167
|
+
WT --> Started[append consumer status=started]
|
|
168
|
+
Started --> Run[implementation executes one selected stage]
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Stage selection is `auto` or a number. If `--stage` comes from another task-type, it is a `PrepareError`. The runtime reads `done`/`started` of `consumers.jsonl`, carry sidecar backfill, and the active stage-key of the registry together in the Stage Lifecycle Snapshot, and excludes occupied stages. The Snapshot is not a new stored file but a read-side view of `stage_targets.py`.
|
|
172
|
+
|
|
173
|
+
The stage worktree base is decided by dependency shape. An independent stage uses the task-key worktree HEAD fixed at first implementation entry as its anchor, and a single-dependency stage branches from the predecessor stage's done `head_commit`. A multi-dependency stage branches from a commit holding exactly its predecessors: the predecessor commit that already contains the others, or else an integration branch merging them (`<work-category-namespace>/<task-id-segment>-g<n1>-<n2>`). okstra creates that branch itself, so a predecessor that has not been merged into the task branch no longer blocks the stage; release-handoff later reuses the same branch as that stage's PR base.
|
|
174
|
+
|
|
175
|
+
## 6. Deliverables
|
|
176
|
+
|
|
177
|
+
```mermaid
|
|
178
|
+
flowchart TD
|
|
179
|
+
Code[Commits] --> Report[implementation final report]
|
|
180
|
+
Diff[git diff --stat base..HEAD] --> Report
|
|
181
|
+
TDD[TDD evidence] --> Report
|
|
182
|
+
Validation[Validation evidence<br/>actual output + exit code] --> Report
|
|
183
|
+
Verifiers[Verifier results<br/>command logs + verdicts] --> Report
|
|
184
|
+
Rollback[Rollback verification] --> Report
|
|
185
|
+
Report --> Next[Routing recommendation<br/>final-verification or loop back]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The final report requires at least the following.
|
|
189
|
+
|
|
190
|
+
- approved plan path and quoted approval marker
|
|
191
|
+
- selected stage, isolated stage worktree path, run artifact path (`runs/implementation/stage-<N>/`)
|
|
192
|
+
- commit SHA, message, plan step mapping
|
|
193
|
+
- diff summary and per-file summary
|
|
194
|
+
- out-of-plan edits block
|
|
195
|
+
- actual stdout/stderr and exit code of the plan validation command
|
|
196
|
+
- TDD failing-then-passing evidence
|
|
197
|
+
- per-verifier independent validation rerun result
|
|
198
|
+
- `carry/stage-<N>.json` evidence sidecar and `consumers.jsonl` started/done row
|
|
199
|
+
- rollback verification
|
|
200
|
+
- `implementation.manualUserTest` finalized against the actual diff and whether it is executable
|
|
201
|
+
- follow-up tasks table
|
|
202
|
+
|
|
203
|
+
## 7. Forbidden actions
|
|
204
|
+
|
|
205
|
+
```mermaid
|
|
206
|
+
flowchart TD
|
|
207
|
+
Impl[implementation] --> Allowed[local edit/write/build/test/git add/git commit]
|
|
208
|
+
Impl -. forbidden .-> Push[git push]
|
|
209
|
+
Impl -. forbidden .-> Publish[publish/release/deploy]
|
|
210
|
+
Impl -. forbidden .-> RealDB[source migration or<br/>shared/staging/prod datastore write]
|
|
211
|
+
Impl -. forbidden .-> VerifierWrite[verifier edit/write]
|
|
212
|
+
Impl -. forbidden .-> Scope[silent scope expansion]
|
|
213
|
+
Impl -. forbidden .-> Acceptance[declaring final acceptance]
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
This phase does not declare final acceptance. It says only ready for final-verification or needs new loop.
|
|
217
|
+
|
|
218
|
+
## 8. Verified code
|
|
219
|
+
|
|
220
|
+
- [`scripts/okstra_ctl/phases/implementation/profile.md`](profile.md)
|
|
221
|
+
- [`scripts/okstra_ctl/phases/implementation/report_assets/implementation-input.template.md`](report_assets/implementation-input.template.md)
|
|
222
|
+
- [`scripts/okstra_ctl/run.py`](../../run.py)
|
|
223
|
+
- [`scripts/okstra_ctl/wizard/`](../../wizard)
|
|
224
|
+
- [`validators/validate-implementation-plan-stages.py`](../../../../validators/validate-implementation-plan-stages.py)
|
|
225
|
+
- [`scripts/okstra_ctl/qa_commands.py`](../../qa_commands.py)
|
|
226
|
+
- [`prompts/lead/okstra-lead-contract.md`](../../../../prompts/lead/okstra-lead-contract.md)
|
|
227
|
+
|
|
228
|
+
## Phase ownership
|
|
229
|
+
|
|
230
|
+
`entry.py` owns stage claim preparation, design-preparation checks and execution context publication. `wizard.py` owns implementation stage selection policy. `validation.py` enforces independent rerun and discrepancy verdicts. `tests/test_implementation_stage.py` checks claims and consumer publication; `tests/test_verifier_results.py` checks verifier verdicts. Shared plan records, stage lifecycle state and worktree registry remain common contracts. Report schema definitions remain in `schemas/final-report-v2.0.schema.json` and `schemas/final-report-v3.0.schema.json` under `implementation`.
|
|
231
|
+
|
|
232
|
+
## Guarantees
|
|
233
|
+
|
|
234
|
+
| ID | Guarantee | Enforcement |
|
|
235
|
+
|---|---|---|
|
|
236
|
+
| I-1 | An empty independent rerun record fails validation. | `scripts/okstra_ctl/phases/implementation/validation.py::_validate_verifier_reran_independently` |
|
|
237
|
+
| I-2 | A verifier cannot report a discrepancy with a PASS verdict. | `scripts/okstra_ctl/phases/implementation/validation.py::_validate_verifier_discrepancy_is_not_passed` |
|
|
238
|
+
| I-3 | Frozen legacy instruction pointers resolve within the selected installation without rewriting the frozen profile. | `scripts/okstra_ctl/phases/implementation/tests/test_assets.py::test_frozen_sidecar_reference_reads_the_owned_installation` |
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""구현 검증자의 독립 재현과 판정 일관성 검사."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
|
|
7
|
+
_PASSING_VERDICT_TOKENS = frozenset({"accepted", "conditional-accept"})
|
|
8
|
+
_CHECKLIST_ID_RE = re.compile(r"\bVC-\d{3,}\b")
|
|
9
|
+
_CHECKLIST_PHASE_RE = re.compile(r"\bphase\W{0,3}(pre|mid|post)\b", re.IGNORECASE)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _validate_verifier_reran_independently(data: dict, failures: list[str]) -> None:
|
|
13
|
+
"""`independentValidationRerun` 칸이 비어 있지 않아야 한다.
|
|
14
|
+
|
|
15
|
+
스키마는 필드 존재만 강제하고 값은 보지 않는다. 빈 칸은 재현 없이
|
|
16
|
+
통과시킨 것과 구별되지 않는다.
|
|
17
|
+
|
|
18
|
+
executor 인용 표현을 잡던 정규식 갈래는 삭제했다. 표현을 세는 검사라
|
|
19
|
+
같은 재현을 어떻게 서술했느냐로 통과가 갈렸다.
|
|
20
|
+
"""
|
|
21
|
+
for who, row in _verifier_rows(data):
|
|
22
|
+
rerun = row.get("independentValidationRerun")
|
|
23
|
+
if not isinstance(rerun, str) or not rerun.strip():
|
|
24
|
+
failures.append(
|
|
25
|
+
f"verifier-rerun: {who} 가 independentValidationRerun 을 비워 뒀다 — "
|
|
26
|
+
"재현 없이 통과시킨 것과 구별되지 않는다"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _validate_verifier_discrepancy_is_not_passed(
|
|
31
|
+
data: dict, failures: list[str]
|
|
32
|
+
) -> None:
|
|
33
|
+
"""재현 결과가 executor 보고와 갈렸는데 PASS 로 넘기지 못하게 한다.
|
|
34
|
+
|
|
35
|
+
규칙(§"Discrepancy rule")은 Tier 1/2 와 blocking io-only Tier 3 의 divergence 에
|
|
36
|
+
`FAIL` 을 요구하고, Tier 3 외부 자문 divergence 만 제외한다. 리포트 구조에는
|
|
37
|
+
tier 필드가 없어 그 둘을 여기서 가릴 수 없다. 그래서 `PASS` 만 막는다 —
|
|
38
|
+
자문 divergence 는 `CONCERNS` 로 기록할 자리가 이미 있고, `PASS` 는 "갈렸는데
|
|
39
|
+
아무 일도 없었다" 는 뜻이라 어느 tier 로도 정당화되지 않는다.
|
|
40
|
+
"""
|
|
41
|
+
for who, row in _verifier_rows(data):
|
|
42
|
+
discrepancy = row.get("discrepancy")
|
|
43
|
+
if not isinstance(discrepancy, str) or not discrepancy.strip():
|
|
44
|
+
continue
|
|
45
|
+
if row.get("verdict") == "PASS":
|
|
46
|
+
failures.append(
|
|
47
|
+
f"verifier-discrepancy: {who} 가 divergence 를 기록하고도 PASS 를 냈다 "
|
|
48
|
+
f"— FAIL(또는 자문 divergence 면 CONCERNS)이어야 한다: "
|
|
49
|
+
f"{discrepancy.strip()[:120]}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _verifier_rows(data: dict):
|
|
54
|
+
"""(표시 이름, verifierResults 행) 쌍."""
|
|
55
|
+
implementation = data.get("implementation")
|
|
56
|
+
if not isinstance(implementation, dict):
|
|
57
|
+
return
|
|
58
|
+
for row in implementation.get("verifierResults") or []:
|
|
59
|
+
if not isinstance(row, dict):
|
|
60
|
+
continue
|
|
61
|
+
yield str(row.get("verifier") or row.get("role") or "verifier"), row
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _warn_out_of_plan_edits_not_in_diff(data: dict, warnings: list[str], changed_files: list[str]) -> None:
|
|
65
|
+
"""소스 차이 목록은 별도 QA 산출물의 변경 여부를 증명하지 못한다."""
|
|
66
|
+
implementation = data.get("implementation")
|
|
67
|
+
if not isinstance(implementation, dict):
|
|
68
|
+
return
|
|
69
|
+
rows = implementation.get("outOfPlanEdits")
|
|
70
|
+
if not isinstance(rows, list) or not rows:
|
|
71
|
+
return
|
|
72
|
+
changed = set(changed_files)
|
|
73
|
+
if not changed:
|
|
74
|
+
return
|
|
75
|
+
for row in rows:
|
|
76
|
+
if not isinstance(row, dict):
|
|
77
|
+
continue
|
|
78
|
+
target = row.get("file")
|
|
79
|
+
if isinstance(target, str) and target and target not in changed:
|
|
80
|
+
warnings.append(
|
|
81
|
+
f"out-of-plan-edit: {row.get('id') or 'OOP-???'} 가 `{target}` 을 "
|
|
82
|
+
"계획 밖 편집으로 신고했지만 diffSummary 에 그 파일이 없다"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _validate_verifier_discrepancy_names_checklist_phase(
|
|
87
|
+
data: dict,
|
|
88
|
+
plan: dict | None,
|
|
89
|
+
failures: list[str],
|
|
90
|
+
) -> None:
|
|
91
|
+
"""계획 `validationChecklist` 행을 근거로 적은 divergence 는 그 행의 `phase` 를 인용한다.
|
|
92
|
+
|
|
93
|
+
2026-09-05 실측(fontsninja-v3-site dev-10626 stage-1): codex 검증자가 `VC-003` 의
|
|
94
|
+
`git diff --name-only` 가 커밋 뒤 빈 출력이라며 FAIL 을 냈다. 그 행은 계획 레코드에
|
|
95
|
+
`phase: mid` — 편집과 커밋 사이의 체크포인트 — 로 선언돼 있어, 커밋 뒤의 빈 출력은
|
|
96
|
+
계획의 단계 순서 그 자체였다. 수렴에서 제기자 본인이 반대 읽기에 AGREE 했지만 FAIL
|
|
97
|
+
행은 남아 stage 가 `failed` 로 갔고, 리드도 그 주장을 열어 보지 않고 라우팅에 옮겼다.
|
|
98
|
+
`pre`/`mid`/`post` 는 행이 언제 성립하는지를 정하므로, 행을 인용하는 문장이 그 값을
|
|
99
|
+
함께 적어야 한다 — 읽지 않은 행을 근거로 쓰는 문장은 그러면 쓸 수 없다.
|
|
100
|
+
|
|
101
|
+
행에 `phase` 가 없거나 계획 레코드를 못 찾으면 판정하지 않는다.
|
|
102
|
+
"""
|
|
103
|
+
if plan is None:
|
|
104
|
+
return
|
|
105
|
+
planning = plan.get("implementationPlanning")
|
|
106
|
+
rows = planning.get("validationChecklist") if isinstance(planning, dict) else None
|
|
107
|
+
phases = {
|
|
108
|
+
str(row["id"]): str(row["phase"]).strip().lower()
|
|
109
|
+
for row in (rows if isinstance(rows, list) else [])
|
|
110
|
+
if isinstance(row, dict)
|
|
111
|
+
and isinstance(row.get("id"), str)
|
|
112
|
+
and isinstance(row.get("phase"), str)
|
|
113
|
+
}
|
|
114
|
+
if not phases:
|
|
115
|
+
return
|
|
116
|
+
for who, row in _verifier_rows(data):
|
|
117
|
+
discrepancy = row.get("discrepancy")
|
|
118
|
+
if not isinstance(discrepancy, str) or not discrepancy.strip():
|
|
119
|
+
continue
|
|
120
|
+
cited = sorted(set(_CHECKLIST_ID_RE.findall(discrepancy)) & set(phases))
|
|
121
|
+
if not cited:
|
|
122
|
+
continue
|
|
123
|
+
named = {
|
|
124
|
+
match.group(1).lower()
|
|
125
|
+
for match in _CHECKLIST_PHASE_RE.finditer(discrepancy)
|
|
126
|
+
}
|
|
127
|
+
missing = [
|
|
128
|
+
f"{row_id} (phase: {phases[row_id]})"
|
|
129
|
+
for row_id in cited
|
|
130
|
+
if phases[row_id] not in named
|
|
131
|
+
]
|
|
132
|
+
if missing:
|
|
133
|
+
failures.append(
|
|
134
|
+
f"verifier-discrepancy: {who} 가 계획 체크리스트 행을 근거로 divergence 를 "
|
|
135
|
+
f"적었지만 그 행의 phase 를 인용하지 않았다 — {', '.join(missing)}. "
|
|
136
|
+
"`pre`/`mid`/`post` 는 행이 언제 성립하는지를 정하므로 인용 문장에 "
|
|
137
|
+
"`VC-NNN (phase: <값>)` 으로 적는다 (`_implementation-verifier.md` § Tier 1)."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _validate_verifier_command_log_is_read_only(
|
|
142
|
+
data: dict,
|
|
143
|
+
failures: list[str],
|
|
144
|
+
verifier_mutation_hits: Callable[[str], list[tuple[str, str]]],
|
|
145
|
+
) -> None:
|
|
146
|
+
"""검증자의 Read-only command log 에 변조 모드가 없어야 한다.
|
|
147
|
+
|
|
148
|
+
규칙은 prompts/profiles/_implementation-verifier.md 가 "런타임 AND 검증자가
|
|
149
|
+
거부해야 한다"고 BLOCKING 으로 선언해 왔지만 런타임 검사는 없었다. 스키마는
|
|
150
|
+
로그의 **존재**만 강제하고 내용은 아무도 읽지 않았다 — 검증자가
|
|
151
|
+
`eslint --fix` 로 자기가 검증할 소스를 고쳐 놓아도 통과한다.
|
|
152
|
+
|
|
153
|
+
로그는 리포트에 그대로 복사되므로 여기서 읽는 것이 정본이다.
|
|
154
|
+
"""
|
|
155
|
+
implementation = data.get("implementation")
|
|
156
|
+
if not isinstance(implementation, dict):
|
|
157
|
+
return
|
|
158
|
+
rows = [r for r in (implementation.get("verifierResults") or []) if isinstance(r, dict)]
|
|
159
|
+
if not rows:
|
|
160
|
+
return
|
|
161
|
+
for row in rows:
|
|
162
|
+
log = row.get("readOnlyCommandLog")
|
|
163
|
+
if not isinstance(log, str) or not log.strip():
|
|
164
|
+
continue
|
|
165
|
+
who = str(row.get("workerId") or row.get("role") or "verifier")
|
|
166
|
+
for label, line in verifier_mutation_hits(log):
|
|
167
|
+
failures.append(
|
|
168
|
+
f"verifier-command-log: {who} 의 read-only 로그에 변조 모드 "
|
|
169
|
+
f"`{label}` 이 있다: {line[:120]}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _validate_verifier_fail_blocks_verdict(data: dict, failures: list[str]) -> None:
|
|
174
|
+
"""A verifier FAIL cannot be dropped during synthesis.
|
|
175
|
+
|
|
176
|
+
`implementation.verifierResults[]` was written, read by the stage-fix carry
|
|
177
|
+
helper, and by nothing else — no check compared a recorded `FAIL` against
|
|
178
|
+
the verdict the lead published. A FAIL lost in synthesis lets
|
|
179
|
+
`final-verification` reach `accepted` and `release-handoff` push work a
|
|
180
|
+
verifier rejected.
|
|
181
|
+
"""
|
|
182
|
+
implementation = data.get("implementation")
|
|
183
|
+
if not isinstance(implementation, dict):
|
|
184
|
+
return
|
|
185
|
+
failed = sorted({
|
|
186
|
+
str(row.get("verifier") or "<unknown>")
|
|
187
|
+
for row in (implementation.get("verifierResults") or [])
|
|
188
|
+
if isinstance(row, dict) and str(row.get("verdict") or "").strip() == "FAIL"
|
|
189
|
+
})
|
|
190
|
+
if not failed:
|
|
191
|
+
return
|
|
192
|
+
token = str((data.get("finalVerdict") or {}).get("verdictToken") or "").strip()
|
|
193
|
+
if token in _PASSING_VERDICT_TOKENS:
|
|
194
|
+
failures.append(
|
|
195
|
+
f"final-report data.json: verifier(s) {failed} recorded "
|
|
196
|
+
f"`verdict: FAIL` but `finalVerdict.verdictToken` is `{token}`. A "
|
|
197
|
+
"verifier rejection MUST survive into the published verdict — "
|
|
198
|
+
"dropping it during synthesis is how rejected work reaches "
|
|
199
|
+
"`release-handoff`. Carry the FAIL into a blocking verdict. There "
|
|
200
|
+
"is no synthesis-time override: a verifier that produced no "
|
|
201
|
+
"verdict records `not-run` and a Tier 3 advisory divergence "
|
|
202
|
+
"records `CONCERNS`, and both are the verifier's to write, not "
|
|
203
|
+
"the lead's to substitute "
|
|
204
|
+
'(`_implementation-verifier.md` "All-verifier-failure policy").'
|
|
205
|
+
)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""구현 단계 선택의 상태 표기와 선택 가능 조건."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from okstra_ctl.wizard.ids import ALL_STAGES
|
|
5
|
+
from okstra_ctl.wizard.state import WizardError
|
|
6
|
+
|
|
7
|
+
_STAGE_MARKER_KEYS = {
|
|
8
|
+
"done": "mark_done",
|
|
9
|
+
"active": "mark_active",
|
|
10
|
+
"ready": "mark_ready",
|
|
11
|
+
"blocked": "mark_blocked",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _impl_stage_marker(t, lifecycle) -> str:
|
|
16
|
+
return t["options"][_STAGE_MARKER_KEYS[lifecycle.status]]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _impl_chosen_stages(t, picks, answer, all_nums, occupied) -> set:
|
|
20
|
+
if ALL_STAGES in picks:
|
|
21
|
+
if len(picks) > 1:
|
|
22
|
+
raise WizardError(t["errors"]["all_exclusive"])
|
|
23
|
+
chosen = {n for n in all_nums if n not in occupied}
|
|
24
|
+
if not chosen:
|
|
25
|
+
raise WizardError(t["errors"]["nothing_selectable"])
|
|
26
|
+
return chosen
|
|
27
|
+
try:
|
|
28
|
+
nums = {int(p) for p in picks}
|
|
29
|
+
except ValueError:
|
|
30
|
+
raise WizardError(t["errors"]["bad_number"].format(answer=answer))
|
|
31
|
+
unknown = sorted(nums - all_nums)
|
|
32
|
+
if unknown:
|
|
33
|
+
raise WizardError(t["errors"]["unknown_stage"].format(
|
|
34
|
+
bad=", ".join(map(str, unknown))))
|
|
35
|
+
bad = sorted(nums & occupied)
|
|
36
|
+
if bad:
|
|
37
|
+
raise WizardError(t["errors"]["occupied"].format(
|
|
38
|
+
bad=", ".join(map(str, bad))))
|
|
39
|
+
return nums
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""구현 방향 비교와 후보 검증 단계의 정본."""
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""후보 평가 결과를 작성하는 보고서 계약."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Sequence
|
|
5
|
+
|
|
6
|
+
from .validation import (
|
|
7
|
+
EVALUATION_CRITERIA,
|
|
8
|
+
MAX_CRITERION_VALUE,
|
|
9
|
+
MAX_RANKED_OPTIONS,
|
|
10
|
+
MAX_RAW_CANDIDATES_PER_ANALYSER,
|
|
11
|
+
MIN_CRITERION_VALUE,
|
|
12
|
+
MIN_FEASIBLE_VOTES,
|
|
13
|
+
)
|
|
14
|
+
from okstra_ctl.exact_coverage import COVERAGE_VERDICT_PRECEDENCE
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def requirement_instructions(original_requirement_ids: Sequence[str], participating_analysers: Sequence[str]) -> list[str]:
|
|
18
|
+
criteria = ", ".join(f"`{item}`" for item in EVALUATION_CRITERIA)
|
|
19
|
+
requirement_ids = ", ".join(
|
|
20
|
+
f"`{item}`" for item in original_requirement_ids
|
|
21
|
+
)
|
|
22
|
+
analysers = ", ".join(
|
|
23
|
+
f"`{item}`" for item in participating_analysers
|
|
24
|
+
)
|
|
25
|
+
return [
|
|
26
|
+
f"Original requirement ids, in order: {requirement_ids}.",
|
|
27
|
+
f"Participating analysers: {analysers}.",
|
|
28
|
+
f"Evaluation criteria, in exact order: {criteria}.",
|
|
29
|
+
"Every `rankedOptions` and `candidateAudit` row must contain one "
|
|
30
|
+
"`requirementCoverage` row per original requirement id and one "
|
|
31
|
+
"`criterionScores` row per evaluation criterion. Weights and scores "
|
|
32
|
+
f"are integers from {MIN_CRITERION_VALUE} through {MAX_CRITERION_VALUE}; "
|
|
33
|
+
"`weightedScore` is the recalculated weighted mean.",
|
|
34
|
+
"`proposedBy` names exactly one participating analyser id, never a "
|
|
35
|
+
"list. Counting `rankedOptions` and `candidateAudit` together, each "
|
|
36
|
+
f"analyser may propose at most {MAX_RAW_CANDIDATES_PER_ANALYSER} raw "
|
|
37
|
+
"candidates and the run may hold at most "
|
|
38
|
+
f"{MAX_RAW_CANDIDATES_PER_ANALYSER} times the analyser count.",
|
|
39
|
+
"`coverageSummary` is recalculated from the row's `requirementCoverage` "
|
|
40
|
+
"statuses and `scopeCommitments`, and every field must equal the "
|
|
41
|
+
"recalculation: `totalCount` = number of original requirement ids; "
|
|
42
|
+
"`coveredCount` = rows with status `covered`; `coveragePercent` = "
|
|
43
|
+
"round(coveredCount / totalCount * 100, 2); `unmappedCommitments` = "
|
|
44
|
+
"commitment ids whose `requirementIds` is empty or names an id outside "
|
|
45
|
+
"the original set; `scopePrecisionPercent` = round((commitments - "
|
|
46
|
+
"unmapped) / commitments * 100, 2); `contradictedRequirements` = "
|
|
47
|
+
"original ids with status `contradicted`, in original order; "
|
|
48
|
+
"`coverageVerdict` = the first that applies of "
|
|
49
|
+
+ ", ".join(f"`{verdict}`" for verdict in COVERAGE_VERDICT_PRECEDENCE)
|
|
50
|
+
+ " (any contradicted; coveredCount below totalCount; any unmapped; "
|
|
51
|
+
"otherwise).",
|
|
52
|
+
"A ranked option is valid only when every participating analyser "
|
|
53
|
+
"supplied one feasibility vote, at least "
|
|
54
|
+
f"{MIN_FEASIBLE_VOTES} votes are `feasible`, and both `safetyBlockers` "
|
|
55
|
+
"and `unresolvedFeasibilityFacts` are empty.",
|
|
56
|
+
("Classify each `unresolvedFeasibilityFacts` entry with `resolutionKind`: "
|
|
57
|
+
"`user-decision` requires nonempty `clarificationRefs` linking that fact "
|
|
58
|
+
"to actual C-NNN records, including answered records; `technical-verification` "
|
|
59
|
+
"requires no user question. Preserve the fact, whyItMatters, and evidence. "
|
|
60
|
+
"Historical unclassified facts remain readable but require classification "
|
|
61
|
+
"before blocked report reassembly. Saving does not lift blocked routing."),
|
|
62
|
+
("After a linked decision is answered, preserve its disposition and selected "
|
|
63
|
+
"value. Update humanSummary.actions, verdictCard.nextStep, and selection "
|
|
64
|
+
"guidance to name remaining technical verification separately from unanswered "
|
|
65
|
+
"user decisions; never reopen an answered question to permit saving."),
|
|
66
|
+
"Each `feasibilityVotes` row states that analyser's own verdict, "
|
|
67
|
+
"rationale, and strongest counterevidence as its result gives them. "
|
|
68
|
+
"Two non-`uncertain` votes with identical rationale and "
|
|
69
|
+
"counterevidence are rejected as one worker's text copied under "
|
|
70
|
+
"another's name.",
|
|
71
|
+
f"Rank at most {MAX_RANKED_OPTIONS} valid options by descending "
|
|
72
|
+
"`requirement-fit`, `correctness-risk`, `architecture-fit`, "
|
|
73
|
+
"`weightedScore`, then id.",
|
|
74
|
+
"In `candidate-comparison`, keep `preselectedDirection` null. In "
|
|
75
|
+
"`preselected-validation`, emit exactly one validated option, an empty "
|
|
76
|
+
"`candidateAudit`, and a cited `preselectedDirection`.",
|
|
77
|
+
"Record candidate validity and selection mode; the lead chooses the next "
|
|
78
|
+
"phase under `## implementation-option-selection` in "
|
|
79
|
+
"`prompts/lead/phase-routing.md`.",
|
|
80
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"allowed": " - candidate-comparison evidence for up to three options per worker\n - preselected-validation of exactly ONE candidate: every analyser returns its own feasibility verdict on that one direction, `candidateAudit` stays empty, and no analyser generates an alternative. It is not a re-evaluation of the merged set — `okstra_ctl.phases.implementation_option_selection.validation._validate_mode` rejects a preselected-validation report carrying more than one option or a non-empty audit\n - a ranked display of at most three options with requirement mappings\n - an audit record for every rejected candidate\n - one endStateCoverage row per brief end-state id (this phase authors no goal of its own)",
|
|
3
|
+
"forbidden": [
|
|
4
|
+
"source or configuration edits, refactors, or fix attempts",
|
|
5
|
+
"tests, builds, migrations, deployments, or any state-mutating command",
|
|
6
|
+
"detailed file lists, stage maps, execution commands, or plan approval",
|
|
7
|
+
"starting `implementation-planning` or `implementation` inside this run",
|
|
8
|
+
"displaying more than three merged candidates or omitting rejected-candidate audit records"
|
|
9
|
+
]
|
|
10
|
+
}
|