Data / Schema Model — Step authority (S-B)

MissionStep (the authority) — schema changes

Current (src/doctrine/missions/models.py:87, frozen, extra="forbid"): id, display_name, step_type, prompt_template, agent_profile, guidance, delegates_to, depends_on.

S-B adds (all must be registered in _STEP_YAML_TO_MODEL, mission_step_repository.py:120):

FieldTypeMeaningNotes
sequence_index`int \None`Position within the mission type's ordered sequence
in_action_sequenceboolMembership in the ordered sequencesoftware-dev: true for 5 of 12; false for retrospect etc. (keeps scope edges, D5)
recommended_model_tier`str \None`Net-new advisory model-tier offer
template`(artifact_key, template_file) \None`Reference to the step's content template

recommended_role is not a new field — the existing agent_profile is the advisory role offer (DD-05).

prompt_template stays required (str, structure enforced — NOT made optional): documentation (7) + research (5) steps have guidelines.md but no prompt.md, and plan (4) has neither — 16 steps total. For those, seed a blank/empty prompt.md so the required field points at a real file; a red test on prompt emptiness/dummy-content flags each seeded blank until S-C fills it (FR-013). A blank placeholder is not fabricated content.

Projections (derived, no longer authored)

Removed from mission_types/*.yaml. The value is injected by MissionTypeRepository._load (project builtin steps with pack_context=None, feed into model_validate) — not a computed property on the frozen model. default() is memoized (@functools.cache, idiom at mission_type_repository.py:140) so the hot path doesn't re-load step.yaml per call (NFR-007). _validate_action_sequence (non-empty + unique) runs on the injected projection at construction.

Removed from the YAML. software-dev → {spec: spec-template.md, plan: plan-template.md}; 3 types → null.

  • action_sequence = [step.id for step in steps if step.in_action_sequence] sorted by sequence_index.
  • template_set = {step.template.artifact_key: step.template.template_file for step in steps if step.template}.

Projection seam (new module, doctrine layer)

src/doctrine/missions/step_projection.py:

  • project_action_sequence(steps: Iterable[MissionStep]) -> list[str]
  • project_template_set(steps: Iterable[MissionStep]) -> dict[str, str] | None

Pure, deterministic. Consumed by both the DRG extractor and the charter/runtime (one implementation).

Invariants

1. Parity (software-dev)project_action_sequence(sw-dev steps) == pre-mission [specify, plan, tasks, implement, review]; project_template_set == {spec, plan}. Byte-for-byte (NFR-001a). 2. Referential integrity (3 types) — projected action_sequence round-trips to the pre-mission value; every template/prompt_template ref that exists resolves to a moved-byte-identical file; missing → red test (NFR-001b). 3. DRG 0-delta — 280/757/10 fresh (NFR-002); FR-011 deferred → no contract edges. 4. No routing leak — a charter/runtime override always beats the step recommended_model_tier offer (NFR-003). 5. Dispatch invariance — adding step.yaml to the 3 types does not change spec-kitty next (NFR-006).

Structural layout (unification, FR-005)

Target for all 4 types: src/doctrine/missions/mission-steps/<type>/<step>/{step.yaml, prompt.md, guidelines.md} (software-dev's existing layout). Move documentation/research content in; plan's 4 steps have no prompt → red test.