okstra 0.206.0 → 0.206.1
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 +2 -2
- package/dist/commands/lifecycle/install.mjs +1 -1
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/docs/architecture.md +12 -12
- package/docs/cli.md +4 -4
- package/docs/contributor-change-matrix.md +3 -2
- package/docs/performance-improvement-plan-v2.md +1 -1
- package/docs/project-structure-overview.md +39 -18
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/okstra-spawn-followups.py +2 -2
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/lead/context-loader.md +1 -1
- package/runtime/prompts/lead/convergence.md +3 -3
- package/runtime/prompts/lead/okstra-lead-contract.md +13 -54
- package/runtime/prompts/lead/phase-routing.md +64 -0
- package/runtime/prompts/lead/report-writer.md +2 -2
- package/runtime/prompts/lead/team-contract.md +1 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/forbidden-actions.json +0 -94
- package/runtime/python/okstra_ctl/agent/prompt_cli/corrections.py +1 -1
- 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/asset_roots.py +19 -0
- package/runtime/python/okstra_ctl/consumers.py +12 -0
- package/runtime/python/okstra_ctl/dispatch_state.py +22 -0
- package/runtime/python/okstra_ctl/doctor.py +2 -1
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +27 -1
- 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 +8 -1
- 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 +1 -1
- 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 +4 -4
- 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} +2 -2
- 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} +1 -1
- 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/profile_show.py +3 -3
- package/runtime/python/okstra_ctl/render.py +9 -2
- package/runtime/python/okstra_ctl/report_assembly.py +11 -90
- 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 +67 -349
- package/runtime/python/okstra_ctl/stage_map.py +13 -0
- 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_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_prompt_contract.py +36 -0
- package/runtime/python/okstra_ctl/workflow.py +26 -143
- package/runtime/skills/okstra-brief-gen/SKILL.md +3 -3
- package/runtime/skills/okstra-run/SKILL.md +1 -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
|
@@ -91,7 +91,7 @@ profile document.
|
|
|
91
91
|
- if a schema-v1 table or an analysis-worker result table requires a recommended answer, alternatives, or an evidence-check note, encode it inside the existing 4-column schema: put evidence notes in `Statement` as `Evidence checked: <path:line>` or `Evidence checked: none — <human-only reason>`, and put recommendations/options in `Expected form` as `Recommended: (a) <answer> — <rationale>; Alternatives: (b) <option> (c) <option>`. The recommended answer is always the first option and MUST carry the `(a)` label; alternatives continue the same letter sequence from `(b)` (a lone alternative is `(b) <option>`, never restart at `(a)`), so the full option set reads `(a) (b) (c) …` in order and renders each as its own selectable option. Do **not** append a pick-one answer-space summary such as `(pick 1 of A / B)` or `(pick N of …)` to `<options>` — the rendered `<select>` already enforces single choice, and that annotation leaks verbatim into an option label. Do not add `Recommended`, `Evidence`, `Alternatives`, or `evidence-checked` columns, and do not break the merged record-meta cell back into separate columns.
|
|
92
92
|
- For schema v2, data.json is canonical and the HTML exports answers to a user-response sidecar; the source report is never edited. `--resume-clarification` carries those answers into the next run. The lower-level `--clarification-response <path>` remains available for scripted runs.
|
|
93
93
|
- When a response is carried in, reconcile every prior `clarificationItems[]` row against new evidence and update its status to `resolved` or `obsolete` before issuing the next verdict. Schema-v1 compatibility Markdown may additionally render its conditional Section 0; the schema-v2 full reading copy records decisions under `## Clarification and User Decisions`.
|
|
94
|
-
- **Supersession (BLOCKING).** Reconciling the `C-*` row is only half of incorporating an answer. An answer does not merely *add* a decision — it *invalidates* whatever the previous run wrote under the opposite assumption. Before issuing the next decision, walk the prior deliverable prose for every statement the answer makes false and **delete or rewrite it**, then record the retirement. Adding the new decision while leaving the contradicting sentence in place puts two opposite instructions for the same symbol in one document; the implementer must then guess which is live, and the next verification round correctly blocks on it. In `implementation-planning` this record is `implementationPlanning.supersessionLedger[]` — one entry per answered clarification, either `disposition: superseded` (with the retired statement, its replacement, and the sections revised) or `disposition: no-dependent-statement` (with a rationale). A plan built from a selected direction inherits the answers the option-selection record carried before it has any statement to retire, so those carried rows need no entry; the rows this plan itself raised and settled still do. **Enforced:** `
|
|
94
|
+
- **Supersession (BLOCKING).** Reconciling the `C-*` row is only half of incorporating an answer. An answer does not merely *add* a decision — it *invalidates* whatever the previous run wrote under the opposite assumption. Before issuing the next decision, walk the prior deliverable prose for every statement the answer makes false and **delete or rewrite it**, then record the retirement. Adding the new decision while leaving the contradicting sentence in place puts two opposite instructions for the same symbol in one document; the implementer must then guess which is live, and the next verification round correctly blocks on it. In `implementation-planning` this record is `implementationPlanning.supersessionLedger[]` — one entry per answered clarification, either `disposition: superseded` (with the retired statement, its replacement, and the sections revised) or `disposition: no-dependent-statement` (with a rationale). A plan built from a selected direction inherits the answers the option-selection record carried before it has any statement to retire, so those carried rows need no entry; the rows this plan itself raised and settled still do. **Enforced:** `scripts/okstra_ctl/phases/implementation_planning/plan_body.py` `_validate_supersession_ledger` requires an entry per answered clarification, exempting the ledger's `carriedDecisions[]` ids on a selected-direction plan; whether the claim is *true* is what the §5.5.9 adversarial round tests.
|
|
95
95
|
- Verdict Card data consistency (shared; schema-v1 Markdown keeps the legacy visible card):
|
|
96
96
|
- The Card carries no verdict token — the token lives once, in `finalVerdict.verdictToken`, and every gate reads it there. `verdictCard.direction` byte-matches `finalVerdict.direction`; next-step routing agrees with `recommendedNextSteps[0]`. The full reading copy and human summary are derived from the data fields without repeating both visible sections. **Enforced in part:** the v3.0 schema's `verdictCard` is `additionalProperties: false` with no verdict-token property, so the token cannot be duplicated onto the Card, and `scripts/okstra_ctl/report_narrative.py` `writer_owned_schema` applies the finished report's `$defs.Direction` enum to the narrative, rejecting an off-enum `direction` while the writer can still be re-run. The byte-match between the two `direction` fields is not compared by anything — assembly overwrites `nextStep` on both when the plan-body gate passes (`scripts/okstra_ctl/report_assembly.py:590-604`) but leaves `direction` as the writer wrote it.
|
|
97
97
|
- Cross-worker traceability (shared — applies to every analysis worker output and to the lead's `## 6.` / `## 2.` tables in the final-report):
|
|
@@ -14,4 +14,4 @@ mode:" when the include directive (placed at column 0) is resolved in-place.
|
|
|
14
14
|
Do NOT write the literal include directive token in this file's body — the
|
|
15
15
|
resolver matches it anywhere and would recurse on this file itself.
|
|
16
16
|
-->
|
|
17
|
-
- **Coverage critic (opt-in, one slot)**: critic `min` is 0 and `recommended`/`max` are 1 — the wizard asks whether to add the slot with 1 recommended, and the user picks the model when they add it (`--role-model critic=<provider>/<model>` or the wizard role-model step). `--critic off` and a 0-count selection are both accepted; the run then dispatches no critic pass, and an analyser 1-1 tie stays `needs-reverify` with no slot to settle it — `okstra plan-items next-dispatch` answers `user-decision` for those items and the lead opens an approval decision instead of another round. A reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface **both** findings nobody covered and work the findings propose that no requirement asked for (`category: "unrequested-scope"`); its candidates are judged only after a 1-round adversarial reverify that follows convergence. The two halves are disposed of differently — a contested coverage gap is dropped as a hallucination, while a contested over-scope candidate is recorded as a `## 5. Missing Information and Risks` row instead of vanishing. In `implementation-planning`, the same critic slot also settles plan-body 1-1 splits (`critic-worker` on `--tie-vote` items only). See `prompts/lead/convergence.md` "Coverage critic pass" and `
|
|
17
|
+
- **Coverage critic (opt-in, one slot)**: critic `min` is 0 and `recommended`/`max` are 1 — the wizard asks whether to add the slot with 1 recommended, and the user picks the model when they add it (`--role-model critic=<provider>/<model>` or the wizard role-model step). `--critic off` and a 0-count selection are both accepted; the run then dispatches no critic pass, and an analyser 1-1 tie stays `needs-reverify` with no slot to settle it — `okstra plan-items next-dispatch` answers `user-decision` for those items and the lead opens an approval decision instead of another round. A reused-worker critic pass is dispatched concurrently with the first convergence reverify round to surface **both** findings nobody covered and work the findings propose that no requirement asked for (`category: "unrequested-scope"`); its candidates are judged only after a 1-round adversarial reverify that follows convergence. The two halves are disposed of differently — a contested coverage gap is dropped as a hallucination, while a contested over-scope candidate is recorded as a `## 5. Missing Information and Risks` row instead of vanishing. In `implementation-planning`, the same critic slot also settles plan-body 1-1 splits (`critic-worker` on `--tie-vote` items only). See `prompts/lead/convergence.md` "Coverage critic pass" and `scripts/okstra_ctl/phases/implementation_planning/instructions/plan-body-verification.md` even-split rule.
|
|
@@ -1,100 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"requirements-discovery": [
|
|
3
|
-
"source code edits of any kind",
|
|
4
|
-
"implementation planning or detailed design beyond what is required to choose the next phase",
|
|
5
|
-
"executing builds, migrations, deployments, or any state-mutating command",
|
|
6
|
-
"starting `error-analysis`, `implementation-planning`, or `implementation` inside this run (each must be a separate run, and `implementation` additionally requires an approved `implementation-planning` deliverable)"
|
|
7
|
-
],
|
|
8
|
-
"improvement-discovery": [
|
|
9
|
-
"source code edits of any kind",
|
|
10
|
-
"implementation planning, root-cause analysis, builds, migrations, or deployments",
|
|
11
|
-
"starting `implementation-planning`, `implementation`, `error-analysis`, or any other lifecycle phase inside this run",
|
|
12
|
-
"generating candidates outside the lens whitelist (Lens enum violation rejects the report)",
|
|
13
|
-
"exceeding the candidate cap (absolute cap 12)",
|
|
14
|
-
"free external data fetch beyond the brief's Source Material or Phase 1.5 resolved scope",
|
|
15
|
-
"interpreting user phrases like `다음 단계 진행해` as authorisation to enter another phase"
|
|
16
|
-
],
|
|
17
|
-
"project-analysis": [
|
|
18
|
-
"source or configuration edits",
|
|
19
|
-
"tests, builds, migrations, or deployments",
|
|
20
|
-
"starting any other lifecycle phase inside this run"
|
|
21
|
-
],
|
|
22
|
-
"feature-analysis": [
|
|
23
|
-
"source or configuration edits",
|
|
24
|
-
"tests, builds, migrations, or deployments",
|
|
25
|
-
"starting any other lifecycle phase inside this run"
|
|
26
|
-
],
|
|
27
|
-
"change-impact-analysis": [
|
|
28
|
-
"source or configuration edits",
|
|
29
|
-
"tests, builds, migrations, or deployments",
|
|
30
|
-
"starting any other lifecycle phase inside this run",
|
|
31
|
-
"implementation alternatives",
|
|
32
|
-
"file change specifications",
|
|
33
|
-
"stepwise execution plans"
|
|
34
|
-
],
|
|
35
|
-
"error-analysis": [
|
|
36
|
-
"source code edits, refactors, or fix attempts",
|
|
37
|
-
"implementation design or planning artifacts",
|
|
38
|
-
"executing builds, migrations, deployments, or any state-mutating command",
|
|
39
|
-
"starting `implementation-planning` or `implementation` inside this run (each must be a separate run, and `implementation` additionally requires an approved `implementation-planning` deliverable)"
|
|
40
|
-
],
|
|
41
|
-
"implementation-option-selection": [
|
|
42
|
-
"source or configuration edits, refactors, or fix attempts",
|
|
43
|
-
"tests, builds, migrations, deployments, or any state-mutating command",
|
|
44
|
-
"detailed file lists, stage maps, execution commands, or plan approval",
|
|
45
|
-
"starting `implementation-planning` or `implementation` inside this run",
|
|
46
|
-
"displaying more than three merged candidates or omitting rejected-candidate audit records"
|
|
47
|
-
],
|
|
48
|
-
"implementation-planning": [
|
|
49
|
-
"source code edits of any kind (Edit/Write on project source files is forbidden)",
|
|
50
|
-
"file writes outside the run`s artifact directories (`reports/`, `prompts/`, `state/`, `manifests/`, `worker-results/`, `status/`, `sessions/`), including task-root QA scripts, manifest, and tsconfig (planning declares conformance commands and required dependencies; implementation writes these files); in particular, do not write to `docs/superpowers/specs/` or `docs/superpowers/plans/`",
|
|
51
|
-
"executing builds, migrations, deployments, or any state-mutating command",
|
|
52
|
-
"starting `implementation` inside this run (must be a separate run authorised by an approved deliverable from this phase), even if the user says \"다음 단계 진행해\"",
|
|
53
|
-
"dispatching parallel sub-agents beyond the required worker roster (okstra owns worker fan-out)",
|
|
54
|
-
"leaving placeholders such as TBD / TODO / \"handle edge cases\" / \"similar to Option N\" in the report",
|
|
55
|
-
"delegating the self-review pass — the Okstra lead must run it"
|
|
56
|
-
],
|
|
57
|
-
"implementation": [
|
|
58
|
-
"any Edit/Write or state-mutating Bash before the pre-implementation gate passes (gate requires --approved-plan pointing to a final-report.md whose frontmatter has `approved: true`)",
|
|
59
|
-
"`git push` of any kind (including `--dry-run` against a real remote that produces side-effects), `npm publish` / `cargo publish` / `pip publish`, `gh release`, `docker push`",
|
|
60
|
-
"real database migrations, schema changes against shared environments, or writes to non-local datastores",
|
|
61
|
-
"production credentials, deploy commands, infra mutation (`terraform apply`, `kubectl apply` against non-local cluster, etc.)",
|
|
62
|
-
"external API write calls (POST/PUT/PATCH/DELETE) to third-party services other than localhost test fixtures",
|
|
63
|
-
"source edits or Bash mutations performed by any verifier role (`Antigravity verifier`, `Codex verifier`, `Claude verifier` are read-only — recommend, do not apply)",
|
|
64
|
-
"dispatching parallel sub-agents beyond the required worker roster",
|
|
65
|
-
"silent scope expansion: every file edited outside the approved plan list MUST appear in the `Out-of-plan edits` block with rationale",
|
|
66
|
-
"leaving placeholders such as TBD / TODO / \"implement later\" / \"handle edge cases\" in newly-added lines of this run (check via `git diff <base>..HEAD | grep -E '^\\+[^+].*\\b(TBD|TODO|FIXME|XXX|implement later|handle edge cases|similar to|placeholder)\\b'`; pre-existing strings in untouched regions are out of scope)",
|
|
67
|
-
"lead substituting its own verdict when every verifier present in the resolved roster returned a non-result terminal status (`timeout`/`error`/`not-run`); in that case the run MUST end as `blocked` with routing recommendation back to `error-analysis`, never with a lead-only verdict",
|
|
68
|
-
"declaring overall task acceptance — that is `final-verification` ownership; this phase reports only \"ready for final-verification\" or \"needs new planning loop\"",
|
|
69
|
-
"delegating the self-review pass — the Okstra lead must run it"
|
|
70
|
-
],
|
|
71
|
-
"final-verification": [
|
|
72
|
-
"source code edits, follow-up bug fixes, or scope expansion",
|
|
73
|
-
"state-mutating commands against the project or shared environments; permitted mutations are limited to the run's own `.okstra` artifacts (reports, state, `<task_root>/qa/result-*.json` sidecars, `okstra handoff record-verified` on acceptance) and Tier3 conformance scripts that mutate only their qaEnv replica datastore — everything else is read-only execution of pre-existing test or validation commands",
|
|
74
|
-
"starting any follow-up phase inside this run; record findings and end the run"
|
|
75
|
-
],
|
|
76
|
-
"release-handoff": [
|
|
77
|
-
"entering this phase when the cited final-verification `Verdict Token` is `conditional-accept` or `blocked`, or when no final-verification report is cited",
|
|
78
|
-
"local commit commands of any kind (`git add`, `git commit`, `git restore --staged`, `git stash`), and any direct `git merge` / `git rebase` / `git rebase --onto` / `git cherry-pick` / `git commit --amend` run by the lead. The single exception is the merge commits `okstra handoff pr-plan` itself creates on a `merge-base` branch — the lead never merges by hand. Rewriting a stage branch breaks the PR stack that sits on it.",
|
|
79
|
-
"any git push variant that rewrites remote history, regardless of intent or whether the user said \"force it\": `git push --force`, `git push --force-with-lease`, `git push -f`, `git push +<refspec>`, or any other history-rewriting invocation",
|
|
80
|
-
"pushing directly to a release base branch — i.e. `git push origin <branch>` where `<branch>` is `main`, `master`, `prod`, `preprod`, `staging`, `dev`, or the branch the user chose as the release base in this run. The only permitted push targets are the branches `okstra handoff pr-plan` listed for this run (each stage `head_branch`, and any `merge-base` branch).",
|
|
81
|
-
"bypassing repo safeguards: `--no-verify` / `-n` on `git push`, bypassing GPG signing, disabling safeguards via equivalent flags, or any hook bypass.",
|
|
82
|
-
"release-publishing commands: `gh release create`, `gh release edit`, `npm publish`, `cargo publish`, `pip publish`, `twine upload`, `docker push`, `terraform apply`, `kubectl apply` against any non-local cluster.",
|
|
83
|
-
"source-code edits, refactors, or any modification to files outside the run's own artifact directories (`reports/`, `prompts/`, `state/`, `manifests/`, `worker-results/`, `status/`, `sessions/`). The diff being shipped MUST be exactly what the prior `implementation` run produced; release-handoff packages it, it does not re-author it.",
|
|
84
|
-
"executing any mutating command the user did NOT select. Examples: opening a PR when the user picked `local checkout`; pushing when the user picked `skip`; switching the release base branch silently after the user already chose one; opening a PR for a stage outside `HANDOFF_STAGES`.",
|
|
85
|
-
"squash-merging, or instructing anyone to squash-merge, a stage PR — and `gh pr merge` in any form. A squash replaces the commits the next stage's PR base points at, so the stack breaks; the PR body states merge-commit-or-rebase and the lead never merges.",
|
|
86
|
-
"retrying a failed git / gh command with weaker safety flags. If `git push` fails with non-fast-forward, the lead MUST stop, explain the failure to the user, and ask for instructions — it MUST NOT add `--force`.",
|
|
87
|
-
"worker dispatch of any kind, or any other parallel sub-agent fan-out. This phase runs entirely under the Okstra lead.",
|
|
88
|
-
"silently treating an unrecognised user reply as one of the menu options. If the user's answer does not match a presented choice, re-ask the question verbatim."
|
|
89
|
-
],
|
|
90
2
|
"unknown": [
|
|
91
3
|
"any action that belongs to a different lifecycle phase",
|
|
92
4
|
"source code edits or state-mutating commands unless this task type explicitly authorises them"
|
|
93
|
-
],
|
|
94
|
-
"technical-verification": [
|
|
95
|
-
"source edits or installs in the project checkout, task worktree, or another worker's experiment copy",
|
|
96
|
-
"production credentials, remote writes, deployments, migrations, publishing, commits or merging experiments into product branches",
|
|
97
|
-
"marking candidates feasible, selecting a direction, approving a plan, or declaring task acceptance",
|
|
98
|
-
"starting another lifecycle phase inside this run; return the evidence to implementation-option-selection"
|
|
99
5
|
]
|
|
100
6
|
}
|
|
@@ -25,7 +25,7 @@ from ..activity import (
|
|
|
25
25
|
)
|
|
26
26
|
from ...final_report_schema import load_schema_version
|
|
27
27
|
from ...json_boundary import load_owned_object
|
|
28
|
-
from ...
|
|
28
|
+
from ...phases.implementation_option_selection.validation import validate_implementation_option_selection
|
|
29
29
|
from ...report_contract import TASK_TYPE_DATA_PROPERTY
|
|
30
30
|
from ...report_assembly import validate_plan_draft
|
|
31
31
|
from ...report_corrections import (
|
|
@@ -34,7 +34,11 @@ from ...assignment_environment import load_assignment_context
|
|
|
34
34
|
from ...assignment_resolver import AssignmentContext, resolve_dispatch_assignment
|
|
35
35
|
from ...path_hints import hydrate_active_run_context
|
|
36
36
|
from ...worker_prompt_headers import worker_prompt_headers
|
|
37
|
-
from ...worker_prompt_contract import
|
|
37
|
+
from ...worker_prompt_contract import (
|
|
38
|
+
complete_reverify_instruction,
|
|
39
|
+
validate_plan_verify_dispatch_identity,
|
|
40
|
+
validate_reverify_prompt,
|
|
41
|
+
)
|
|
38
42
|
from ...worker_prompt_policy import (
|
|
39
43
|
critic_assignment_ref,
|
|
40
44
|
is_plan_critic_verification,
|
|
@@ -340,6 +344,11 @@ def _materialize_run(
|
|
|
340
344
|
active_context=active_context,
|
|
341
345
|
))
|
|
342
346
|
body = instruction_path.read_text(encoding="utf-8")
|
|
347
|
+
identity_errors = validate_plan_verify_dispatch_identity(
|
|
348
|
+
body, dispatch_kind=args.dispatch_kind, result_name=result_path.name,
|
|
349
|
+
)
|
|
350
|
+
if identity_errors:
|
|
351
|
+
raise AgentPromptCliError("; ".join(identity_errors))
|
|
343
352
|
is_reverify = is_verification_dispatch_kind(args.dispatch_kind)
|
|
344
353
|
if is_reverify:
|
|
345
354
|
body = _complete_run_reverify_body(args, manifest, active_context, assignment, body, instruction_path)
|
|
@@ -73,7 +73,6 @@ _EVIDENCE_RELATION_BY_TYPES = {
|
|
|
73
73
|
_REPORT_NAME_RE = re.compile(
|
|
74
74
|
r"^final-report-.+-(?P<run_seq>[^-]+)\.data\.json$"
|
|
75
75
|
)
|
|
76
|
-
_FEATURE_ID_RE = re.compile(r"PF-\d{3}")
|
|
77
76
|
_FULL_COMMIT_RE = re.compile(r"[0-9a-f]{40}")
|
|
78
77
|
_RUN_SEQ_RE = re.compile(r"[0-9]{3}")
|
|
79
78
|
|
|
@@ -347,41 +346,3 @@ def resolve_evidence_inputs(
|
|
|
347
346
|
freshness="exact" if candidate.source_commit == current_commit else "stale",
|
|
348
347
|
))
|
|
349
348
|
return tuple(resolved)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
def resolve_analysis_target(
|
|
353
|
-
raw_target: str,
|
|
354
|
-
evidence_inputs: Sequence[ResolvedEvidenceInput],
|
|
355
|
-
candidates: Mapping[Path, AnalysisReportCandidate],
|
|
356
|
-
) -> dict[str, object]:
|
|
357
|
-
"""Normalize a feature-index identifier or a non-empty free-text target."""
|
|
358
|
-
target = raw_target.strip()
|
|
359
|
-
if not target:
|
|
360
|
-
raise AnalysisInputError("analysis target must not be empty")
|
|
361
|
-
if not _FEATURE_ID_RE.fullmatch(target):
|
|
362
|
-
return {"inputMode": "free-text", "requestedValue": target}
|
|
363
|
-
matches: list[dict[object, object]] = []
|
|
364
|
-
for evidence in evidence_inputs:
|
|
365
|
-
if evidence.relation != "project-context":
|
|
366
|
-
continue
|
|
367
|
-
candidate = candidates.get(evidence.report_path)
|
|
368
|
-
if candidate is None:
|
|
369
|
-
candidate = next(
|
|
370
|
-
(value for path, value in candidates.items() if path.resolve() == evidence.report_path),
|
|
371
|
-
None,
|
|
372
|
-
)
|
|
373
|
-
if candidate is None:
|
|
374
|
-
continue
|
|
375
|
-
matches.extend(
|
|
376
|
-
feature for feature in candidate.feature_index
|
|
377
|
-
if isinstance(feature, dict) and feature.get("id") == target
|
|
378
|
-
)
|
|
379
|
-
if len(matches) != 1:
|
|
380
|
-
raise AnalysisInputError(
|
|
381
|
-
f"exactly one project-context feature must match {target}"
|
|
382
|
-
)
|
|
383
|
-
return {
|
|
384
|
-
"inputMode": "feature-index",
|
|
385
|
-
"requestedValue": target,
|
|
386
|
-
"feature": dict(matches[0]),
|
|
387
|
-
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""분석 증거와 단계 내용 검증이 공유하는 프로젝트 상대 경로 판독."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from pathlib import PurePosixPath
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _normalized_project_path(path: object) -> PurePosixPath | None:
|
|
8
|
+
if not isinstance(path, str):
|
|
9
|
+
return None
|
|
10
|
+
raw = path.strip().rstrip("/")
|
|
11
|
+
if not raw or raw == "." or raw.startswith("/"):
|
|
12
|
+
return None
|
|
13
|
+
if any(part in {"", ".", ".."} for part in raw.split("/")):
|
|
14
|
+
return None
|
|
15
|
+
normalized = PurePosixPath(raw)
|
|
16
|
+
if normalized.is_absolute():
|
|
17
|
+
return None
|
|
18
|
+
return normalized
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _path_is_included(path: object, included_paths: list[object]) -> bool:
|
|
22
|
+
candidate = _normalized_project_path(path)
|
|
23
|
+
if candidate is None:
|
|
24
|
+
return False
|
|
25
|
+
for raw in included_paths:
|
|
26
|
+
included = _normalized_project_path(raw)
|
|
27
|
+
if included is not None and (
|
|
28
|
+
candidate == included or included in candidate.parents
|
|
29
|
+
):
|
|
30
|
+
return True
|
|
31
|
+
return False
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Ancestor-based runtime asset lookup independent of run and phase state."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from collections.abc import Callable, Sequence
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def walk_to_asset_root(
|
|
9
|
+
relative: Sequence[str],
|
|
10
|
+
*,
|
|
11
|
+
start: Path,
|
|
12
|
+
is_present: Callable[[Path], bool] = Path.is_file,
|
|
13
|
+
) -> Path | None:
|
|
14
|
+
"""Return the nearest ancestor carrying the asset without home overrides."""
|
|
15
|
+
here = Path(start).resolve()
|
|
16
|
+
for parent in [here, *here.parents]:
|
|
17
|
+
if is_present(parent.joinpath(*relative)):
|
|
18
|
+
return parent
|
|
19
|
+
return None
|
|
@@ -430,3 +430,15 @@ def backfill_done_from_carry(plan_run_root: Path) -> int:
|
|
|
430
430
|
settled_stages.add(stage)
|
|
431
431
|
recovered += 1
|
|
432
432
|
return recovered
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def require_done_task_key(rows: List[Dict[str, Any]], stage: int) -> str:
|
|
436
|
+
from .handoff_error import HandoffError
|
|
437
|
+
|
|
438
|
+
done = latest_done_by_stage(rows)
|
|
439
|
+
row = done.get(stage)
|
|
440
|
+
if not row:
|
|
441
|
+
raise HandoffError(
|
|
442
|
+
f"stage {stage} has no done row in consumers.jsonl — "
|
|
443
|
+
"finish the implementation stage first")
|
|
444
|
+
return row.get("impl_task_key", "")
|
|
@@ -59,6 +59,7 @@ from .worker_prompt_body import REPORT_WRITER_WORKER_ID
|
|
|
59
59
|
from .worker_prompt_contract import (
|
|
60
60
|
PromptRecord,
|
|
61
61
|
validate_initial_prompt_records,
|
|
62
|
+
validate_plan_verify_dispatch_identity,
|
|
62
63
|
validate_prompt_model_header,
|
|
63
64
|
validate_reverify_prompt,
|
|
64
65
|
)
|
|
@@ -1905,6 +1906,26 @@ def validate_initial_prompts(
|
|
|
1905
1906
|
raise DispatchError(f"{task_type} prompt contract: " + "; ".join(errors))
|
|
1906
1907
|
|
|
1907
1908
|
|
|
1909
|
+
def _validate_plan_verify_identities(jobs: Sequence[WorkerJob]) -> None:
|
|
1910
|
+
errors: list[str] = []
|
|
1911
|
+
for job in jobs:
|
|
1912
|
+
try:
|
|
1913
|
+
text = job.prompt_path.read_text(encoding="utf-8")
|
|
1914
|
+
except OSError as exc:
|
|
1915
|
+
errors.append(f"{job.worker_id}: cannot read prompt {job.prompt_path}: {exc}")
|
|
1916
|
+
continue
|
|
1917
|
+
errors.extend(
|
|
1918
|
+
f"{job.worker_id}: {error}"
|
|
1919
|
+
for error in validate_plan_verify_dispatch_identity(
|
|
1920
|
+
text,
|
|
1921
|
+
dispatch_kind=job.dispatch_kind,
|
|
1922
|
+
result_name=job.worker_result_path.name,
|
|
1923
|
+
)
|
|
1924
|
+
)
|
|
1925
|
+
if errors:
|
|
1926
|
+
raise DispatchError("plan-body dispatch contract: " + "; ".join(errors))
|
|
1927
|
+
|
|
1928
|
+
|
|
1908
1929
|
def validate_dispatch_prompts(
|
|
1909
1930
|
manifest: Mapping[str, Any],
|
|
1910
1931
|
active_context: Mapping[str, Any],
|
|
@@ -1912,6 +1933,7 @@ def validate_dispatch_prompts(
|
|
|
1912
1933
|
) -> None:
|
|
1913
1934
|
if isinstance(manifest.get("agentContract"), Mapping):
|
|
1914
1935
|
_validate_agent_invocations(manifest, jobs)
|
|
1936
|
+
_validate_plan_verify_identities(jobs)
|
|
1915
1937
|
initial_jobs = [
|
|
1916
1938
|
job for job in jobs if not is_verification_dispatch_kind(job.dispatch_kind)
|
|
1917
1939
|
]
|
|
@@ -11,7 +11,8 @@ from typing import Iterable
|
|
|
11
11
|
from okstra_project import ResolverError, project_json_path, resolve_project_root
|
|
12
12
|
from okstra_project.resolver import resolve_review_rule_packs
|
|
13
13
|
|
|
14
|
-
from . import
|
|
14
|
+
from . import worktree_registry
|
|
15
|
+
from .phases.improvement_discovery import lenses as improvement_lenses
|
|
15
16
|
from .models import provider_wrappers
|
|
16
17
|
from .phases.catalog import PhaseAssetError, UnknownTaskType, profile_markdown
|
|
17
18
|
from .workers import resolve_profile_workers
|
|
@@ -281,7 +281,8 @@ class ExecutionMutationAudit:
|
|
|
281
281
|
+ tuple(f"unattributed change outside assigned worktree and artifact paths: {path}"
|
|
282
282
|
for path in sorted(external))
|
|
283
283
|
+ _plan_change_warnings(rows, source_changes | untracked_changes,
|
|
284
|
-
artifact_changed, out_of_plan_edits, unobserved)
|
|
284
|
+
artifact_changed, out_of_plan_edits, unobserved)
|
|
285
|
+
+ _linked_dependency_warnings(after.root),
|
|
285
286
|
changed_artifact_paths=tuple(sorted(artifact_changed - external)),
|
|
286
287
|
declared_out_of_plan_paths=tuple(out_of_plan_edits),
|
|
287
288
|
unobserved_artifact_paths=tuple(sorted(unobserved)),
|
|
@@ -370,6 +371,31 @@ def _audit_warnings(untracked_artifact_changes: set[str]) -> tuple[str, ...]:
|
|
|
370
371
|
)
|
|
371
372
|
|
|
372
373
|
|
|
374
|
+
def _linked_dependency_warnings(root: Path) -> tuple[str, ...]:
|
|
375
|
+
"""worktree 밖을 가리키는 `node_modules` 링크를 알린다.
|
|
376
|
+
|
|
377
|
+
감사는 `node_modules` 를 보지 않으므로, 그 링크를 통한 패키지 설치가 메인
|
|
378
|
+
체크아웃의 의존성 트리를 덮어써도 흔적이 남지 않는다(2026-09-25 실측).
|
|
379
|
+
"""
|
|
380
|
+
linked: list[str] = []
|
|
381
|
+
for current, directories, _ in os.walk(root, followlinks=False):
|
|
382
|
+
kept: list[str] = []
|
|
383
|
+
for name in directories:
|
|
384
|
+
path = Path(current) / name
|
|
385
|
+
if name == ".git":
|
|
386
|
+
continue
|
|
387
|
+
if name not in _INSTALLED_DEPENDENCY_DIRS:
|
|
388
|
+
kept.append(name)
|
|
389
|
+
elif path.is_symlink() and not _is_relative_to(path.resolve(), root.resolve()):
|
|
390
|
+
linked.append(f"{path.relative_to(root).as_posix()} -> {path.resolve()}")
|
|
391
|
+
directories[:] = kept
|
|
392
|
+
return tuple(
|
|
393
|
+
f"dependency tree links outside the assigned worktree: {row}; "
|
|
394
|
+
"a package install through it rewrites that target and this audit does not observe it"
|
|
395
|
+
for row in sorted(linked)
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
|
|
373
399
|
def _branch_switch_warnings(
|
|
374
400
|
before: MutationSnapshot, after: MutationSnapshot, switched: set[str],
|
|
375
401
|
) -> tuple[str, ...]:
|