Implement contract testing to catch CLI schema drift early:
Contract Test Suite (fixture_contract_tests.rs):
- parse_lore_me_empty_fixture: Validates LoreMeResponse on empty data
- parse_lore_me_with_activity_fixture: Real lore output with activity
- parse_br_list_empty_fixture: Empty beads list
- parse_br_list_with_beads_fixture: Real br output with beads
Fixtures (captured from real CLI output):
- fixtures/lore/me_empty.json: Synthetic empty response
- fixtures/lore/me_with_activity.json: Real 'lore --robot me' output
- fixtures/br/list_empty.json: Empty array []
- fixtures/br/list_with_beads.json: Real 'br list --json' output
- fixtures/br/bv_triage.json: Real 'bv --robot-triage' output
Fixture Regeneration:
- scripts/regenerate-fixtures.sh: Captures fresh CLI output
- Run periodically to update fixtures
- CI can diff against committed fixtures to detect drift
Why Contract Tests Matter:
MC depends on external CLIs (lore, br, bv) whose output format may
change. Contract tests fail fast when our Rust types diverge from
actual CLI output, preventing runtime deserialization errors.
The tests use include_str!() for compile-time fixture embedding,
ensuring tests fail to compile if fixtures are missing.
Implement the data layer with mockable CLI wrappers for testability:
CLI Traits (data/*.rs):
- LoreCli: Trait for lore --robot commands (get_me, health_check)
- BeadsCli: Trait for br commands (create, close, list)
- Both use #[automock] for unit testing without real CLI
Real Implementations:
- RealLoreCli: Shells to 'lore --robot me', parses JSON response
- RealBeadsCli: Shells to 'br create/close/list --json'
Type Definitions:
- LoreMeResponse: Full response from 'lore --robot me'
- open_issues, open_mrs_authored, reviewing_mrs, activity
- since_last_check with EventGroup for inbox functionality
- All fields use #[serde(default)] for forward compatibility
- Bead: Task from br list (id, title, status, priority, issue_type)
Local State Management (data/state.rs):
- GitLabBeadMap: Deduplication mapping (GitLab event -> bead ID)
- MappedBead: Tracks miss_count for two-strike orphan detection
- DecisionLog: Append-only JSONL for learning from user choices
- Atomic writes via .tmp files + rename pattern
Tauri Commands (commands/mod.rs):
- greet: Placeholder for IPC testing
- get_lore_status: Exposes lore health to frontend
This establishes the CLI-over-library pattern from PLAN.md:
clean boundaries, no schema coupling, full testability via mocks.