Skip to content

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