Research: Fix plan-mission guard/manifest artifact-vocabulary mismatch (#6388)
R1 — Root cause (reproduced on main @ 4.0.0rc8)
_presence_filenames_for("plan") = {goals.md, research.md, plan.md} (no spec.md) → _evaluate_plan_guards(step_id=specify) → ['Required artifact missing: spec.md']; required_artifacts_for('specify','plan') = [] while ('goals','plan') = ['goals.md'] → guard_failure_paths={}. The 4 plan step contracts write spec.md/research.md/plan.md; nothing writes goals.md (phantom).
specify and the step contract writes spec.md, so B recreates the block in reverse and pushes the dead vocabulary into the authoritative layer.
- Decision: Manifest
plan/expected-artifacts.yamlis the stale outlier; the guard is correct. - Evidence (grounding squad, direct call on a real plan mission dir with committed spec.md):
- Alternatives: Option B (retag guard to goals.md) — rejected: guard dispatches on action id
R2 — Both axes required
guard_failure_paths (step-keyed required_artifacts_for). A path-only change leaves symptom (b).
- Decision: Re-path (
goals.md→spec.md) AND re-key (goals/draft→specify/plan). - Rationale: path rename fixes the block (presence union); step-key rename fixes
R3 — mission.yaml is descriptive-only (confirmed this session)
expected-artifacts.yaml. No consumer of mission.yaml artifacts.required/workflow.phases on the gather/guard path.
- Decision: Reconcile both
mission.yamlcopies for coherence; non-load-bearing. - Evidence: grep — only
step_projection.pyreadsrequired_*, and only from
R4 — Parity test non-vacuity
the manifest-sourced presence set — the exact reason the bug passed all of them.
- Decision: Route the cross-family parity test through the REAL
gather_artifact_presence. - Rationale: every existing plan guard test injected
present_artifactssynthetically, bypassing
R5 — AST/corpus risk
Watch: test_configured_artifact_name.py ten-tags source-pair map + the ('plan','output.goals.main', 'goals.md') row; test_manifest.py plan step ids + goals.md row; test_bridge_parity.py:: test_coverage_floor_is_met (confirm the re-key does not re-trip the WP04 over-block).
- Decision: No decision-site risk. The
_materialize_decisionexact-count test is now a floor (≥1).
R6 — Supply-chain / dependencies
- Decision: N/A — no dependency added/changed. DIRECTIVE_051 controls not triggered.
Out of scope
the gather/guard structural level. MISSION_GOALS orphan removal is a separate campsite follow-up.
- Defect #2 (
profile_hintcomposition default — charter#42), fires upstream; verification here is at