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).
| Principle | Status | Notes |
|---|---|---|
| pytest with risk-proportional coverage | Aligned | Constitution amended to adopt risk-proportional model: >= 90% on critical paths, advisory on rest. |
| Tests pass, lint clean, type checks pass | Aligned | Each WP has verification subtask. Lint (ruff) and type-check (mypy) run as part of WP07 cross-cutting gate. |
| At least one focused reviewer approves | Aligned | WP08 assigns architect review. |
| Must run on macOS and Linux | Aligned | CI targets Linux; tests are platform-independent. |
| DIRECTIVE_001 (separation of concerns) | Aligned | WPs are file-scoped with clear boundaries. |
| DIRECTIVE_010 (faithful to specs) | Aligned | WP09 now has backing requirements FR-011, FR-012. |
| DIRECTIVE_030 (pass tests before handoff) | Aligned | Each WP has independent test commands. |
| DIRECTIVE_033 (stage only expected deliverables) | Aligned | WP 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.
| File | Line(s) | Current | Fix |
|---|---|---|---|
tests/missions/test_mission_software_dev_integration.py | 34-40 | Path(__file__).parents[2] / "src" / "specify_cli" / "missions" / "software-dev" | Use MissionTemplateRepository.default_missions_root() / "software-dev" |
tests/missions/test_documentation_mission.py | 12-13, 19 | REPO_ROOT / "src" / "specify_cli" then missions/documentation | Use MissionTemplateRepository.default_missions_root() / "documentation" |
tests/missions/test_documentation_templates.py | 11 | MISSION_DIR = REPO_ROOT / "src" / "specify_cli" / "missions" / "documentation" | Use MissionTemplateRepository.default_missions_root() / "documentation" |
tests/specify_cli/test_command_template_cleanliness.py | varies | Hardcoded src/specify_cli/missions/software-dev/command-templates | Use 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.
| File | Line(s) | Current | Fix |
|---|---|---|---|
tests/sync/test_emitter_origin.py | 203 | assert event["aggregate_type"] == "Feature" | Change to "Mission" (confirmed by commit 1c5a7927) |
tests/missions/test_feature_lifecycle_unit.py | 117 | mock_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.
| File | Issue | Fix Strategy |
|---|---|---|
tests/init/test_worktree_topology.py (4 tests) | Mocks specify_cli.core.worktree_topology.read_frontmatter but function moved | Find 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 exist | Check 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 module | Read 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.
| File | Issue | Fix Strategy |
|---|---|---|
tests/upgrade/test_m_0_12_0_documentation_mission_unit.py | Asserts command-templates/ should not be copied, but migration copies full tree | Read 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:
| File | Line | Fix Applied |
|---|---|---|
src/specify_cli/dashboard/scanner.py | 367, 371 | feature_dir → mission_dir |
src/specify_cli/dashboard/static/dashboard/dashboard.js | 1244-1246 | data.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):
| File | Current | Why Critical |
|---|---|---|
status/ modules | 100% | Status model is the source of truth for WP state |
core/mission_detection.py | 85% | Incorrect detection breaks all mission workflows |
dashboard/handlers/missions.py | 7.5% | API response shapes drive the entire dashboard UI |
dashboard/scanner.py | 93% | Scanner crash hides all missions from users |
Non-critical paths (no coverage target — let recovered tests cover what they cover):
| File | Current | Why 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 / feature → mission_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.
| File | Change |
|---|---|
src/specify_cli/dashboard/static/dashboard/dashboard.js | Include 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.py | Extract 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
| Risk | Likelihood | Mitigation |
|---|---|---|
| WP03 fix reveals deeper API change requiring test rewrite | Medium | Read production code before fixing; if rewrite needed, document scope increase |
| WP04 migration logic is genuinely wrong (not just test) | Low | Fix migration if needed; document as exception to C-001 |
| CI flat coverage gate still fails after critical-path tests | Medium | Document waiver or adjust CI threshold; don't write obtuse tests for low-risk code |
| Backward-compat JS keys (`data.missions \ | \ | data.features`) create confusion |