Implementation Plan: Fix plan-mission guard/manifest artifact-vocabulary mismatch
Branch: fix/plan-guard-artifact-vocabulary | Date: 2026-10-18 | Spec: spec.md Input: Mission specification from kitty-specs/plan-guard-artifact-vocabulary-01M57FKP/spec.md
Summary
A built-in plan mission cannot pass specify: the plan guard chain checks spec.md/research.md/ plan.md (what the plan step contracts produce) but plan/expected-artifacts.yaml declares goals.md/research.md/plan.md under phase step-keys goals/research/draft, so a committed spec.md is never gathered (reads missing) and guard_failure_paths is empty. The fix reconciles the manifest to the runtime action vocabulary (specify/research/plan/review) on both axes — re-key goals/draft→specify/plan and re-path goals.md→spec.md — leaving the guard untouched; reconciles the two descriptive mission.yaml copies; adds the secondary guard_failure_paths regression, a cross-family parity test routed through the real gather_artifact_presence, and a vocabulary-coherence CI guard; and corrects the now-false manifest header comment.
Technical Context
Language/Version: Python 3.11+ Primary Dependencies: charter.activation.manifest_loader.load_manifest, charter.offering.missions.step_projection.project_artifact_name_set, the runtime guard/gather surfaces (runtime_bridge_cores, runtime_bridge_io), specify_cli.runtime.resolver (required_artifacts_for, resolve_configured_artifact_name), ruamel.yaml Storage: pack YAML files on disk (packs/built-in/missions/plan/expected-artifacts.yaml, two mission.yaml copies); no database Testing: pytest — tests/runtime/, tests/runtime/next/, tests/next/, tests/specify_cli/runtime/, tests/dossier/, specific tests/architectural/ gate files; baseline make test-fast. No make test-full; no .github/ci-shard-timings.json regen. Target Platform: Linux/macOS/Windows CLI Project Type: single (CLI package + built-in packs) Performance Goals: none (correctness fix; no hot-path change) Constraints: do NOT touch _evaluate_plan_guards (C-001); one canonical vocabulary = runtime action ids (C-002); keep MISSION_GOALS (C-003); defect #2 out of scope, verify at gather/guard structural level (C-004); manifest stays extra="forbid" valid (NFR-002); no non-plan-family regression (NFR-001) Scale/Scope: one manifest + two mission.yaml copies + 1 doc, 3 new/updated tests, 1 CI guard, ~4 change-together test-mirror updates
Charter Check
GATE: Must pass before Phase 0. Re-check after Phase 1.
vocabulary; the manifest and both mission.yaml copies derive from it (C-002). No second authority re-introduced.
layer is untouched; no import-direction change.
level) before the manifest change; the parity test routes through the REAL gather_artifact_presence (non-vacuity — synthetic present_artifacts= is why the bug hid).
coherence corpus guards, byte-compat characterization preserved for non-plan families.
expand scope to remove MISSION_GOALS (flagged follow-up).
- Single canonical authority — PASS by design: the runtime step-contract action ids become the one
- Architectural alignment / layer direction — PASS: fix is pack-data + runtime read-side; the guard
- ATDD-first / red-first — PASS: FR-001/FR-003/FR-004/FR-005 land red-first (gather/guard structural
- Tiered rigour — runtime guard-vocabulary + release-gating pack contract → high rigour: parity +
- Campsite/terminology — reconcile the stale comment and the descriptive
mission.yaml; do NOT
No violations to justify.
Key Design Decisions
of four surfaces (contracts, guard, runtime dispatch) already agree on specify/research/plan; only the manifest disagrees, on two axes. Re-path goals.md→spec.md (clears the block: spec.md enters _presence_filenames_for('plan')) and re-key goals→specify, draft→plan (restores guard_failure_paths: required_artifacts_for('specify','plan') is step-keyed, returns [] while the key is goals). A path-only rename fixes only symptom (a). Reject Option B (retag the guard): the guard dispatches on action id specify, and the step contract writes spec.md, so a guard checking goals.md would recreate the block in the opposite direction.
step_projection.py over expected-artifacts.yaml; nothing reads mission.yaml's artifacts.required / workflow.phases on the gather/guard path. Reconcile both copies for coherence; non-load-bearing.
present_artifacts synthetically, bypassing the manifest-sourced presence set — which is exactly why the bug passed all of them. The new parity test builds an on-disk artifact-present fixture and routes through the real gather_artifact_presence.
manifest step-keys match its runtime step-contract action ids; that absence let the three-vocabulary drift ship. Add it in tests/architectural/.
manifest; schema_version/manifest_version/field shapes preserved (NFR-002).
- D1 — Manifest-side fix (Option A), both axes. Truth flows step-contract → guard → manifest. Three
- D2 —
mission.yamlis descriptive-only (confirmed). Grep shows the onlyrequired_*reader is - D3 — Parity test must use the real gatherer. Every existing plan guard test injected
- D4 — Coherence CI guard is the highest-value hardening. No check today asserts a built-in type's
- D5 —
extra="forbid"discipline. Only step-keys,path_patterns, and comments change in the
Project Structure
Documentation (this mission)
kitty-specs/plan-guard-artifact-vocabulary-01M57FKP/
├── plan.md # this file
├── research.md # Phase 0
├── data-model.md # Phase 1
├── quickstart.md # Phase 1
├── contracts/ # Phase 1 (behavioural contracts)
└── tasks.md # Phase 2 (/spec-kitty.tasks)
Source / packs (repository root)
packs/built-in/missions/plan/expected-artifacts.yaml # IC-01: re-key + re-path + header comment
packs/built-in/missions/plan/mission.yaml # IC-02: descriptive reconcile
src/specify_cli/missions/plan/mission.yaml # IC-02: duplicate descriptive reconcile
src/runtime/next/runtime_bridge_cores.py # NOT MODIFIED (guard is correct, C-001)
tests/next/test_guard_failure_paths_3883.py # IC-03: plan guard_failure_paths regression
tests/runtime/next/test_cli_guard_family.py # IC-04: cross-family parity via real gather
tests/architectural/<new or existing corpus guard> # IC-05: vocabulary-coherence CI guard
tests/specify_cli/runtime/test_configured_artifact_name.py # change-together mirror update
tests/dossier/test_manifest.py # change-together mirror update
src/charter/offering/skills/spec-kitty-mission-system/references/mission-comparison-matrix.md # doc
Structure Decision: Single CLI package + built-in packs. The fix is pack-data + runtime read-side + tests; the guard code is explicitly untouched.
Complexity Tracking
No Charter Check violations — section intentionally empty.
Implementation Concern Map
> Concerns are NOT work packages. /spec-kitty.tasks translates these into WPs.
IC-01 — Reconcile the plan expected-artifacts manifest (P0 core)
- Purpose: Re-key
required_by_stepgoals/draft→specify/planand re-pathgoals.md→spec.mdso a presentspec.mdreads present andrequired_artifacts_for('specify','plan')resolves{spec.md}; fix the stale header comment. - Relevant requirements: FR-001, FR-002, NFR-002, SC-001, SC-004
- Affected surfaces:
packs/built-in/missions/plan/expected-artifacts.yaml; red-first tests exercising_presence_filenames_for/required_artifacts_for/_evaluate_plan_guards/gather_artifact_presencefor the plan family. - Sequencing/depends-on: none (the core; everything else verifies or hardens it).
- Risks: keep schema valid; verify
test_bridge_parity.py::test_coverage_floor_is_metdoes not re-trip (the WP04 note warns a step-scoped presence design over-blocked plan); update the change-together mirrors (test_configured_artifact_name.pyrow + ten-tags map,test_manifest.pystep ids/goals.md row) as intentional contract updates.
IC-02 — Reconcile the two descriptive mission.yaml copies
- Purpose: Align
required:/workflow.phasesin bothmission.yamlcopies to the action vocabulary (nogoals.mdphantom) for coherence; non-load-bearing. - Relevant requirements: FR-006, C-002
- Affected surfaces:
packs/built-in/missions/plan/mission.yaml,src/specify_cli/missions/plan/mission.yaml; the doc matrix. - Sequencing/depends-on: none (independent of IC-01; descriptive).
- Risks: confirmed no runtime consumer of these fields (D2) — keep it coherence-only; do not let it become a second authority.
IC-03 — Secondary: plan missing-artifact block names its path
- Purpose: Regression test that a genuine plan
specifymissing-artifact block carries a non-emptyguard_failure_paths(restored by IC-01's step-key rename). - Relevant requirements: FR-003, SC-002
- Affected surfaces:
tests/next/test_guard_failure_paths_3883.py. - Sequencing/depends-on: logically rides IC-01 (the path is only restored once the key is
specify). - Risks: assert against the real
guard_failure_artifact_paths+ plan family, not a synthetic stub.
IC-04 — Cross-family parity test through the real gatherer
- Purpose: Dispatch each registered guard family (software-dev/research/documentation/plan) through the REAL
gather_artifact_presenceagainst an artifact-present on-disk fixture, asserting no spurious block — the structural invariant that was missing. - Relevant requirements: FR-004, SC-003
- Affected surfaces:
tests/runtime/next/test_cli_guard_family.py. - Sequencing/depends-on: red-first before IC-01 (plan arm red pre-fix, green post-fix).
- Risks: MUST go through the real gatherer, not
present_artifacts=; build a minimal on-disk fixture per family.
IC-05 — Vocabulary-coherence CI/corpus guard (hardening)
- Purpose: Fail CI when a shipped built-in mission type's manifest step-keys diverge from its runtime step-contract action ids (or it is missing step contracts).
- Relevant requirements: FR-005, SC-003
- Affected surfaces:
tests/architectural/(beside the workflow-coherence corpus guards). - Sequencing/depends-on: red-first before IC-01 (red against the pre-fix plan divergence, green after).
- Risks: scope the guard to built-in types; ensure it reads both the manifest step-keys and the runtime step-contract action ids from their canonical loaders, not hardcoded lists.