Implementation Plan: Fix Doctrine Migration Test Failures

Branch: feature/agent-profile-implementation-rebased | Date: 2026-04-02 | Spec: spec.md Input: Feature specification from kitty-specs/062-fix-doctrine-migration-test-failures/spec.md

Summary

Fix 120 test failures + 43 errors caused by commit bd7a288c moving mission YAML/templates from src/specify_cli/missions/ to src/doctrine/missions/. Additionally fix two dashboard bugs discovered during triage (scanner NameError, JS key mismatch), add a contract test to prevent future key drift, and achieve risk-proportional diff-coverage (>= 90% on critical paths, advisory on remainder).

Technical Context

Language/Version: Python 3.11+ Primary Dependencies: pytest, spec-kitty-cli (editable install) Storage: Filesystem only (YAML, JSONL, Markdown) Testing: pytest with rtk test runner, diff-cover for coverage gating Target Platform: Linux CI (GitHub Actions) Project Type: Single Python package Constraints: Test-only changes; production code touched only for the two dashboard bugs (already fixed in-conversation)

Constitution Check

Constitution loaded from .kittify/constitution/constitution.md (generated 2026-04-03).

PrincipleStatusNotes
pytest with risk-proportional coverageAlignedConstitution amended to adopt risk-proportional model: >= 90% on critical paths, advisory on rest.
Tests pass, lint clean, type checks passAlignedEach WP has verification subtask. Lint (ruff) and type-check (mypy) run as part of WP07 cross-cutting gate.
At least one focused reviewer approvesAlignedWP08 assigns architect review.
Must run on macOS and LinuxAlignedCI targets Linux; tests are platform-independent.
DIRECTIVE_001 (separation of concerns)AlignedWPs are file-scoped with clear boundaries.
DIRECTIVE_010 (faithful to specs)AlignedWP09 now has backing requirements FR-011, FR-012.
DIRECTIVE_030 (pass tests before handoff)AlignedEach WP has independent test commands.
DIRECTIVE_033 (stage only expected deliverables)AlignedWP scoping is file-granular.

Project Structure

Files Modified by This Mission

tests/
├── missions/
│   ├── test_mission_software_dev_integration.py   # WP01: path fix
│   ├── test_documentation_mission.py              # WP01: path fix
│   ├── test_documentation_templates.py            # WP01: path fix
│   └── test_feature_lifecycle_unit.py             # WP02: assertion fix
├── specify_cli/
│   └── test_command_template_cleanliness.py       # WP01: path fix
├── init/
│   ├── test_feature_detection_integration.py      # WP03: import fix
│   └── test_worktree_topology.py                  # WP03: mock target fix
├── agent/cli/commands/
│   └── test_workflow_profile_injection.py          # WP03: fixture fix
├── sync/
│   └── test_emitter_origin.py                     # WP02: terminology fix
├── upgrade/
│   └── test_m_0_12_0_documentation_mission_unit.py # WP04: assertion fix
└── test_dashboard/
    └── test_api_contract.py                       # WP06: NEW file

src/specify_cli/dashboard/
├── scanner.py                                     # WP05: NameError fix (done)
└── static/dashboard/dashboard.js                  # WP05: key mismatch fix (done)

Detailed Fix Map

WP01: Update Hardcoded Mission Paths (Cat A)

Four test files hardcode src/specify_cli/missions/ — update to use MissionTemplateRepository.default_missions_root() where possible, or src/doctrine/missions/ as fallback.

FileLine(s)CurrentFix
tests/missions/test_mission_software_dev_integration.py34-40Path(__file__).parents[2] / "src" / "specify_cli" / "missions" / "software-dev"Use MissionTemplateRepository.default_missions_root() / "software-dev"
tests/missions/test_documentation_mission.py12-13, 19REPO_ROOT / "src" / "specify_cli" then missions/documentationUse MissionTemplateRepository.default_missions_root() / "documentation"
tests/missions/test_documentation_templates.py11MISSION_DIR = REPO_ROOT / "src" / "specify_cli" / "missions" / "documentation"Use MissionTemplateRepository.default_missions_root() / "documentation"
tests/specify_cli/test_command_template_cleanliness.pyvariesHardcoded src/specify_cli/missions/software-dev/command-templatesUse MissionTemplateRepository.default_missions_root() / "software-dev" / "command-templates"

Import to add: from doctrine.missions.repository import MissionTemplateRepository

Pattern: Replace hardcoded path construction with:

MISSIONS_ROOT = MissionTemplateRepository.default_missions_root()
MISSION_DIR = MISSIONS_ROOT / "software-dev"

WP02: Fix Terminology and Assertion Mismatches (Cat B)

Two test files assert old terminology or parameter names.

FileLine(s)CurrentFix
tests/sync/test_emitter_origin.py203assert event["aggregate_type"] == "Feature"Change to "Mission" (confirmed by commit 1c5a7927)
tests/missions/test_feature_lifecycle_unit.py117mock_accept.assert_called_once_with(feature=None, ...)Change feature= to mission= (parameter renamed in accept_feature())

Verification: Read the production function signatures to confirm the parameter names before fixing.

WP03: Repair Mock Targets and Missing Fixtures (Cat C)

Three test files with broken mocks or missing files.

FileIssueFix Strategy
tests/init/test_worktree_topology.py (4 tests)Mocks specify_cli.core.worktree_topology.read_frontmatter but function movedFind where read_frontmatter is actually imported in worktree_topology.py and update mock target
tests/agent/cli/commands/test_workflow_profile_injection.py (1 test)References src/doctrine/agent_profiles/_proposed/human-in-charge.agent.yaml_proposed/ doesn't existCheck if _proposed/ should exist (created by another WP in flight) or if test should use shipped/ path
tests/init/test_feature_detection_integration.py (2 tests)Import validation assertions fail against refactored moduleRead the test assertions and update to match the actual import structure in cli/commands/implement.py

Investigation required: Each fix needs reading the production code to find the correct target. Do not blindly swap paths — verify the import chain.

WP04: Fix Migration Test Logic (Cat D)

One test file with assertion mismatch.

FileIssueFix Strategy
tests/upgrade/test_m_0_12_0_documentation_mission_unit.pyAsserts command-templates/ should not be copied, but migration copies full treeRead the migration code (m_0_12_0_documentation_mission.py) to determine whether the test or the migration is wrong. Fix whichever is incorrect.

Decision rule: If the migration intentionally copies command-templates/, update the test assertion. If the migration should filter it out, fix the migration (exception to C-001).

WP05: Fix Dashboard Scanner + JS Key Mismatch (Already Done)

Two fixes already applied in this conversation:

FileLineFix Applied
src/specify_cli/dashboard/scanner.py367, 371feature_dirmission_dir
src/specify_cli/dashboard/static/dashboard/dashboard.js1244-1246data.features → `data.missions \

Status: Code changes done. WP validates they work in CI.

WP06: Add Dashboard API Contract Test

New file: tests/test_dashboard/test_api_contract.py

Design: A pytest that reads the dashboard JS file and asserts it references the same response keys that the Python API handler emits. This catches key renames at CI time.

"""Contract test: dashboard JS must reference the keys the Python API emits."""

from pathlib import Path
import re
import pytest

pytestmark = pytest.mark.fast

DASHBOARD_JS = Path("src/specify_cli/dashboard/static/dashboard/dashboard.js")

# Keys emitted by handle_missions_list() in handlers/missions.py
MISSIONS_LIST_RESPONSE_KEYS = {
    "missions",
    "active_mission_id",
    "project_path",
    "worktrees_root",
    "active_worktree",
    "active_mission",
}

def test_js_references_missions_list_response_keys():
    """Frontend must destructure the same keys the backend emits."""
    js_content = DASHBOARD_JS.read_text(encoding="utf-8")
    for key in MISSIONS_LIST_RESPONSE_KEYS:
        assert (
            f"data.{key}" in js_content
            or f'data["{key}"]' in js_content
            or f"data['{key}']" in js_content
        ), f"Dashboard JS does not reference response key 'data.{key}'"

Extensible: Add similar sets for kanban, constitution, and diagnostics endpoints as needed.

WP07: Targeted Coverage for Critical Paths (Cat E)

Philosophy: Coverage effort should be proportional to risk. Chasing a flat 80% leads to obtuse tests for low-risk code (migrations, CLI scaffolding). Instead, focus test effort on critical paths where bugs cause data loss or broken workflows.

Critical paths (target >= 90% diff-coverage):

FileCurrentWhy Critical
status/ modules100%Status model is the source of truth for WP state
core/mission_detection.py85%Incorrect detection breaks all mission workflows
dashboard/handlers/missions.py7.5%API response shapes drive the entire dashboard UI
dashboard/scanner.py93%Scanner crash hides all missions from users

Non-critical paths (no coverage target — let recovered tests cover what they cover):

FileCurrentWhy Low Priority
tasks_cli.py (28%)CLI scaffolding — typos cause visible errors, not silent corruption
tracker/origin.py (0%)New code, not yet on critical path
verify_enhanced.py (36%)Validation helper — errors are caught by other tests
m_2_0_2_constitution_context_bootstrap.py (32%)One-time migration — runs once per project

Approach: After WP01-06, measure diff-coverage. Write tests only for critical-path files that are below 90%.

CI workflow change (.github/workflows/ci-quality.yml): Replace the single flat --fail-under=80 gate with a two-step split:

1. Enforced critical-path gate (--fail-under=90): ``bash diff-cover "${coverage_reports[@]}" \ --compare-branch=origin/${{ github.base_ref }} \ --fail-under=90 \ --include src/specify_cli/status/ \ --include src/specify_cli/core/mission_detection.py \ --include src/specify_cli/dashboard/handlers/ \ --include src/specify_cli/dashboard/scanner.py \ --include src/specify_cli/merge/ \ --include src/specify_cli/next/ ``

2. Advisory full-diff report (no --fail-under, informational only): ``bash diff-cover "${coverage_reports[@]}" \ --compare-branch=origin/${{ github.base_ref }} || true ``

This keeps the gate meaningful while not forcing obtuse tests for migrations and scaffolding. Evaluate after one release cycle before considering more complex mechanics.

WP08: Architectural Fitness Review

Assigned to: Architect Alphonso

Review scope: 1. Are the path fixes using MissionTemplateRepository consistently, or do some still hardcode src/doctrine/? 2. Does the dashboard JS backward-compat (data.missions || data.features) make sense, or should it be a clean break? 3. Are there other feature_dir / featuremission_dir / mission renames lurking? 4. Is the contract test approach sufficient, or should Phase 2 (TypedDict codegen) be prioritized?

Output: Approve or request-changes with documented follow-up items.

WP09: Dashboard In-Review Lane Display and WP Card Identity

Depends on: WP05

Two dashboard UI enhancements discovered during WP02 implementation:

1. In-review lane rendering: WPs with lane: in_review appear in the "For Review" column with a distinct border style (darker purple, all edges) to visually distinguish them from WPs awaiting review. 2. Agent identity on card modals: WP detail modal shows agent tool, profile, role, and model in a structured header section. Scanner needs to expose model field.

FileChange
src/specify_cli/dashboard/static/dashboard/dashboard.jsInclude in_review WPs in For Review lane; add agent identity to detail modal
src/specify_cli/dashboard/static/dashboard/dashboard.css.card.in-review style with distinct border
src/specify_cli/dashboard/scanner.pyExtract and emit model field from WP frontmatter

Verification: Playwright tests (headless).

Dependency Graph

WP01 ─┐
WP02 ─┤
WP03 ─┼──→ WP07 ──→ WP08
WP04 ─┤
WP05 ──→ WP06 ─┤
     └──→ WP09 ─┘

WP01-04 are independent (parallel). WP05 is independent. WP06 and WP09 both depend on WP05 and can run in parallel. WP07 depends on all others. WP08 depends on WP07.

Execution Strategy

Parallel wave 1 (Pedro): WP01, WP02, WP03, WP04, WP05 -- all independent Wave 2 after WP05 (Pedro): WP06, WP09 -- both depend on WP05, can run in parallel After all fixes (Pedro): WP07 -- measure coverage, fill gaps Final (Alphonso): WP08 -- architectural review

Risk Register

RiskLikelihoodMitigation
WP03 fix reveals deeper API change requiring test rewriteMediumRead production code before fixing; if rewrite needed, document scope increase
WP04 migration logic is genuinely wrong (not just test)LowFix migration if needed; document as exception to C-001
CI flat coverage gate still fails after critical-path testsMediumDocument waiver or adjust CI threshold; don't write obtuse tests for low-risk code
Backward-compat JS keys (`data.missions \\data.features`) create confusion