Market Discovery Phase 5: CLI Integration (Final)
This post covers Phase 5, the final phase of ADR-017 - CLI command integration for the discovery workflow.
The Problem
Phases 1-4 built the complete discovery infrastructure:
- Storage and data types
- Text similarity matching
- API clients for both platforms
- Scanner actor and approval workflow
But operators had no way to interact with this system. Phase 5 bridges that gap.
CLI Commands
Four commands enable the human-in-the-loop workflow:
# Trigger discovery scan
cargo run --features discovery -- --discover-markets
# List candidates by status
cargo run --features discovery -- --list-candidates --status pending
cargo run --features discovery -- --list-candidates --status approved
cargo run --features discovery -- --list-candidates --status rejected
cargo run --features discovery -- --list-candidates --status all
# Approve a candidate (with optional warning acknowledgment)
cargo run --features discovery -- --approve-candidate <uuid>
cargo run --features discovery -- --approve-candidate <uuid> --acknowledge-warnings
# Reject with required reason
cargo run --features discovery -- --reject-candidate <uuid> --reason "Different settlement criteria"
Testable Command Handlers
The CLI handlers are separated from main.rs into src/discovery/cli.rs for testability:
pub struct DiscoveryCli {
storage: Arc<Mutex<CandidateStorage>>,
mapping_manager: Arc<Mutex<MappingManager>>,
config: DiscoveryCliConfig,
}
impl DiscoveryCli {
pub fn list_candidates(&self, status: Option<CandidateStatus>) -> CliResult { ... }
pub fn approve_candidate(&self, id: Uuid, acknowledge_warnings: bool) -> CliResult { ... }
pub fn reject_candidate(&self, id: Uuid, reason: &str) -> CliResult { ... }
}
This separation allows comprehensive unit testing without spawning the full async runtime.
Safety Enforcement at CLI Layer
The CLI layer preserves FR-MD-003 safety guarantees:
pub fn approve_candidate(&self, id: Uuid, acknowledge_warnings: bool) -> CliResult {
let workflow = ApprovalWorkflow::new(...);
match workflow.approve(id, acknowledge_warnings) {
Ok(mapping_id) => CliResult::Success(format!(
"Candidate {} approved. Verified mapping ID: {}", id, mapping_id
)),
Err(ApprovalError::WarningsNotAcknowledged) => CliResult::Error(
"Cannot approve: candidate has semantic warnings. \
Use --acknowledge-warnings to proceed.".to_string()
),
// ... other error handling
}
}
Error messages guide operators to the correct action.
Feature Gate Error Handling
When the discovery feature is not enabled, helpful error messages are shown:
#[cfg(not(feature = "discovery"))]
{
if is_discovery_command {
eprintln!("Discovery commands require the 'discovery' feature.");
eprintln!(" Build with: cargo build --features discovery");
eprintln!(" Run with: cargo run --features discovery -- --discover-markets");
return Ok(());
}
}
Test Coverage
Phase 5 adds 8 tests (48 total for discovery, 377 overall):
| Test | Focus |
|---|---|
test_cli_list_candidates_empty | Empty database handling |
test_cli_list_candidates_with_data | Data formatting |
test_cli_approve_candidate_success | Happy path approval |
test_cli_approve_requires_warning_acknowledgment | Safety: FR-MD-003 |
test_cli_reject_candidate_success | Happy path rejection |
test_cli_reject_requires_reason | Audit: reason required |
test_cli_approve_not_found | Error handling |
test_parse_status | Status string parsing |
ADR-017 Complete
With Phase 5, ADR-017 is fully implemented:
| Phase | Focus | Tests | Council |
|---|---|---|---|
| 1 | Data Types & Storage | 12 | PASS (0.89) |
| 2 | Text Matching Engine | 10 | PASS (0.88) |
| 3 | Discovery API Clients | 8 | PASS (0.87) |
| 4 | Scanner & Approval | 10 | PASS (0.91) |
| 5 | CLI Integration | 8 | PASS (0.95) |
| Total | 48 |
Council Review
Phase 5 passed council verification with confidence 0.95 (Safety focus). Key findings:
- FR-MD-003 enforcement verified at CLI layer
- Warning acknowledgment required for candidates with semantic warnings
- Rejection requires non-empty reason for audit trail
- Clear error messages guide operators
- Feature gate prevents confusion when feature disabled
- No code path bypasses human review
Implementation: arbiter-engine/src/discovery/cli.rs | Issue: #48 | ADR: 017
