leviath-cli 0.2.0

Command-line interface for Leviath agent framework
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
//! Command dispatch: the `Commands` enum and the `dispatch()` function that
//! routes a parsed subcommand to its executor.
//!
//! This lives in the library crate (not `main.rs`) so its routing logic can be
//! unit-tested under `cargo llvm-cov`'s `--lib` scope. The subcommands whose
//! real execution performs I/O a unit test must never trigger - a real
//! terminal takeover (`dash`), blocking stdin (`setup` interactive,
//! foreground `run`), binding a real port (`serve`), spawning a detached
//! worker or running a real inference loop (`run` background / `__run-worker`) -
//! are routed through the [`RiskyExecutors`] trait rather than called
//! directly. That way:
//!
//! * unit tests drive `dispatch()`'s full routing match against a
//!   `#[cfg(test)]` mock (`MockRisky`) that touches nothing real, and
//! * the real implementations live in the (coverage-unmeasured) `lev` binary
//!   as `main.rs`'s `RealExecutors`, which simply wires real I/O into the
//!   library's already-tested command cores.
//!
//! Injection gives a "routing is tested, real I/O is never touched by a test"
//! guarantee without any coverage escape hatch in library code.

use crate::commands;

#[derive(clap::Subcommand)]
pub enum Commands {
    /// Create a new agent blueprint
    Create(commands::create::CreateArgs),

    /// Configure API keys and defaults
    Setup(commands::setup::SetupArgs),

    /// Run an agent
    Run(commands::run::RunArgs),

    /// List agents running in the shared-world daemon
    #[command(long_about = commands::ps::PS_LONG_ABOUT)]
    Ps(commands::ps::PsArgs),

    /// Send a message to a running agent
    Msg(commands::ctl::MsgArgs),

    /// Cancel a running agent (alias: `kill`)
    #[command(alias = "kill")]
    Cancel(commands::ctl::CancelArgs),

    /// Pause a running agent (it finishes its in-flight step, then holds)
    Pause(commands::ctl::PauseArgs),

    /// Resume a paused agent
    Resume(commands::ctl::ResumeArgs),

    /// Answer a pending interaction (or list open ones with no request id)
    Respond(commands::ctl::RespondArgs),

    /// Check that provider wiring works, end to end
    #[command(long_about = commands::doctor::DOCTOR_LONG_ABOUT)]
    Doctor(commands::doctor::DoctorArgs),

    /// List available and installed blueprints
    List(commands::list::ListArgs),

    /// Install a blueprint
    Add(commands::add::AddArgs),

    /// Remove an installed blueprint
    Remove(commands::remove::RemoveArgs),

    /// Run blueprint tests
    Test(commands::test::TestArgs),

    /// Bundle a blueprint for distribution
    Pack(commands::pack::PackArgs),

    /// Interactive agent dashboard
    #[command(name = "dash")]
    Dashboard(commands::dashboard::DashboardArgs),

    /// List and inspect available models
    Models(commands::models::ModelsArgs),

    /// Validate an agent blueprint
    Validate(commands::validate::ValidateArgs),

    /// List and validate the global Rhai script tools
    Tools(commands::tools::ToolsArgs),

    /// Manage taint tracking policy rules
    Policy(commands::policy::PolicyArgs),

    /// Start the REST + WebSocket API server
    Serve(commands::serve::ServeArgs),

    /// Serve this agent over the Agent Client Protocol (JSON-RPC over stdio)
    #[command(name = "agent-client")]
    AgentClient(commands::agent_client::AgentClientArgs),

    /// Run the shared-world daemon in the foreground
    Daemon(commands::daemon::DaemonArgs),

    /// Show a run's context-window history (from its run.lvr archive)
    Context(commands::context::ContextArgs),

    /// Manage MCP tool servers and their authentication
    Mcp(commands::mcp::McpArgs),

    /// Inspect and move the secrets Leviath holds
    Auth(commands::auth::AuthArgs),
}

/// The subset of commands whose real execution performs I/O that a unit test
/// must never trigger. `dispatch()` routes these through this trait so its
/// routing logic stays unit-testable with a mock; the real implementations are
/// supplied by the binary (`main.rs`'s `RealExecutors`).
///
/// `async fn` in a trait is fine here: `dispatch` takes `&impl RiskyExecutors`
/// (static dispatch, no `dyn`), so no boxing or `Send` bound is required.
#[allow(async_fn_in_trait)]
pub trait RiskyExecutors {
    /// `lev run` - auto-starts the daemon (real process spawn) if needed and
    /// spawns the agent into the shared world over the control socket.
    async fn run(&self, args: commands::run::RunArgs) -> anyhow::Result<()>;
    /// `lev ps` - resolves the control-socket path and queries the daemon.
    async fn ps(&self, args: commands::ps::PsArgs) -> anyhow::Result<()>;
    /// `lev msg` - resolves the control-socket path and sends a message.
    async fn msg(&self, args: commands::ctl::MsgArgs) -> anyhow::Result<()>;
    /// `lev cancel` - resolves the control-socket path and cancels a run.
    async fn cancel(&self, args: commands::ctl::CancelArgs) -> anyhow::Result<()>;
    /// `lev pause` - resolves the control-socket path and pauses a run.
    async fn pause(&self, args: commands::ctl::PauseArgs) -> anyhow::Result<()>;
    /// `lev resume` - resolves the control-socket path and resumes a run.
    async fn resume(&self, args: commands::ctl::ResumeArgs) -> anyhow::Result<()>;
    /// `lev respond` - resolves the control-socket path and answers/lists interactions.
    async fn respond(&self, args: commands::ctl::RespondArgs) -> anyhow::Result<()>;
    /// `lev doctor` - makes real billed inference calls, and (unless
    /// `--no-daemon`) auto-starts the daemon and spawns a throwaway run.
    async fn doctor(&self, args: commands::doctor::DoctorArgs) -> anyhow::Result<()>;
    /// `lev setup` - interactive (blocking stdin) or `--non-interactive`.
    async fn setup(&self, args: commands::setup::SetupArgs) -> anyhow::Result<()>;
    /// `lev dash` - takes over the real terminal and blocks on real keyboard input.
    async fn dashboard(&self, args: commands::dashboard::DashboardArgs) -> anyhow::Result<()>;
    /// `lev serve` - binds a real port and serves indefinitely.
    async fn serve(&self, args: commands::serve::ServeArgs) -> anyhow::Result<()>;
    /// `lev agent-client` - takes over real stdin/stdout to speak the Agent
    /// Client Protocol against the shared-world daemon.
    async fn agent_client(
        &self,
        args: commands::agent_client::AgentClientArgs,
    ) -> anyhow::Result<()>;
    /// `lev daemon` - binds the control socket and serves the shared world.
    async fn daemon(&self, args: commands::daemon::DaemonArgs) -> anyhow::Result<()>;
    /// `lev mcp` - rewrites config, opens a browser for OAuth, touches the token store.
    async fn mcp(&self, args: commands::mcp::McpArgs) -> anyhow::Result<()>;

    /// `lev auth` - reads the config file and may write the OS credential store.
    async fn auth(&self, args: commands::auth::AuthArgs) -> anyhow::Result<()>;
}

/// Inject argv-prescanned dynamic `--<region>` seed flags into a parsed
/// `run` command. A no-op for every other subcommand. Kept here (a tested lib
/// seam) so the bin entrypoint's post-parse wiring stays branch-free.
pub fn apply_region_flags(
    command: &mut Commands,
    regions: std::collections::HashMap<String, String>,
) {
    if let Commands::Run(args) = command {
        args.regions = regions;
    }
}

/// Route a parsed subcommand to its executor. Safe commands are called
/// directly (and are exercised through `dispatch()` by the tests below); the
/// I/O-risky ones go through `ex` (see [`RiskyExecutors`]).
pub async fn dispatch(command: Commands, ex: &impl RiskyExecutors) -> anyhow::Result<()> {
    match command {
        Commands::Create(args) => commands::create::execute(args).await,
        Commands::Setup(args) => ex.setup(args).await,
        Commands::Run(args) => ex.run(args).await,
        Commands::Ps(args) => ex.ps(args).await,
        Commands::Msg(args) => ex.msg(args).await,
        Commands::Cancel(args) => ex.cancel(args).await,
        Commands::Pause(args) => ex.pause(args).await,
        Commands::Resume(args) => ex.resume(args).await,
        Commands::Respond(args) => ex.respond(args).await,
        Commands::Doctor(args) => ex.doctor(args).await,
        Commands::List(args) => commands::list::execute(args).await,
        Commands::Add(args) => commands::add::execute(args).await,
        Commands::Remove(args) => commands::remove::execute(args).await,
        Commands::Test(args) => commands::test::execute(args).await,
        Commands::Pack(args) => commands::pack::execute(args).await,
        Commands::Dashboard(args) => ex.dashboard(args).await,
        Commands::Models(args) => commands::models::execute(args).await,
        Commands::Validate(args) => commands::validate::execute(args).await,
        Commands::Tools(args) => commands::tools::execute(args).await,
        Commands::Policy(args) => commands::policy::execute(args).await,
        Commands::Serve(args) => ex.serve(args).await,
        Commands::AgentClient(args) => ex.agent_client(args).await,
        Commands::Daemon(args) => ex.daemon(args).await,
        Commands::Context(args) => commands::context::execute(args).await,
        Commands::Mcp(args) => ex.mcp(args).await,
        Commands::Auth(args) => ex.auth(args).await,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Test double for [`RiskyExecutors`]: every method is a no-op returning
    /// `Ok(())`, so `dispatch()`'s risky routing arms are exercised without
    /// touching a real terminal / stdin / port / subprocess.
    struct MockRisky;

    impl RiskyExecutors for MockRisky {
        async fn run(&self, _args: commands::run::RunArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn ps(&self, _args: commands::ps::PsArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn msg(&self, _args: commands::ctl::MsgArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn respond(&self, _args: commands::ctl::RespondArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn doctor(&self, _args: commands::doctor::DoctorArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn cancel(&self, _args: commands::ctl::CancelArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn pause(&self, _args: commands::ctl::PauseArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn resume(&self, _args: commands::ctl::ResumeArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn setup(&self, _args: commands::setup::SetupArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn dashboard(&self, _args: commands::dashboard::DashboardArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn serve(&self, _args: commands::serve::ServeArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn agent_client(
            &self,
            _args: commands::agent_client::AgentClientArgs,
        ) -> anyhow::Result<()> {
            Ok(())
        }
        async fn daemon(&self, _args: commands::daemon::DaemonArgs) -> anyhow::Result<()> {
            Ok(())
        }
        async fn auth(&self, _args: commands::auth::AuthArgs) -> anyhow::Result<()> {
            Ok(())
        }

        async fn mcp(&self, _args: commands::mcp::McpArgs) -> anyhow::Result<()> {
            Ok(())
        }
    }

    fn create_args() -> commands::create::CreateArgs {
        commands::create::CreateArgs {
            name: "unused".to_string(),
            template: "software-engineer".to_string(),
        }
    }

    // ─── apply_region_flags ──────────────────────────────────────────────────

    #[test]
    fn apply_region_flags_populates_run_and_noops_other_commands() {
        let mut run = Commands::Run(commands::run::RunArgs::default());
        let flags = std::collections::HashMap::from([("criteria".to_string(), "safe".to_string())]);
        apply_region_flags(&mut run, flags);
        assert!(
            matches!(&run, Commands::Run(a) if a.regions.get("criteria").map(String::as_str) == Some("safe")),
            "region flag was injected into the Run args"
        );
        // A non-run command hits the no-op branch: it must not panic (and there
        // is nothing to inject). Asserting the variant here would leave an
        // always-false `matches!` arm uncovered, so the call itself is the check.
        let mut other = Commands::Ps(commands::ps::PsArgs::default());
        apply_region_flags(&mut other, std::collections::HashMap::new());
    }

    // ─── Risky variants: routed through the injected executor ────────────────

    #[tokio::test]
    async fn dispatch_run_variant_is_routed_through_the_executor() {
        let result = dispatch(Commands::Run(commands::run::RunArgs::default()), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_setup_variant_is_routed_through_the_executor() {
        let args = commands::setup::SetupArgs {
            non_interactive: true,
            no_verify: false,
            install_agents: false,
            anthropic_key: None,
            openai_key: None,
            google_key: None,
            openrouter_key: None,
            ollama_url: None,
            default_model: None,
            claude_code: None,
            claude_code_effort: None,
        };
        let result = dispatch(Commands::Setup(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_dashboard_variant_is_routed_through_the_executor() {
        let args = commands::dashboard::DashboardArgs {};
        let result = dispatch(Commands::Dashboard(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_msg_variant_is_routed_through_the_executor() {
        let args = commands::ctl::MsgArgs {
            agent_id: "a".to_string(),
            content: "c".to_string(),
        };
        assert!(dispatch(Commands::Msg(args), &MockRisky).await.is_ok());
    }

    #[tokio::test]
    async fn dispatch_respond_variant_is_routed_through_the_executor() {
        let args = commands::ctl::RespondArgs {
            request_id: None,
            value: None,
            choice: None,
            approve: false,
            deny: false,
            session: false,
            json: false,
        };
        assert!(dispatch(Commands::Respond(args), &MockRisky).await.is_ok());
    }

    #[tokio::test]
    async fn dispatch_doctor_variant_is_routed_through_the_executor() {
        // Routed, not called directly: `lev doctor` bills two inferences and
        // auto-starts a daemon, so a unit test must never reach the real one.
        let args = commands::doctor::DoctorArgs::default();
        assert!(dispatch(Commands::Doctor(args), &MockRisky).await.is_ok());
    }

    #[tokio::test]
    async fn dispatch_cancel_variant_is_routed_through_the_executor() {
        let args = commands::ctl::CancelArgs {
            run_id: "r".to_string(),
            force: false,
        };
        assert!(dispatch(Commands::Cancel(args), &MockRisky).await.is_ok());
    }

    #[tokio::test]
    async fn dispatch_pause_variant_is_routed_through_the_executor() {
        let args = commands::ctl::PauseArgs {
            run_id: "r".to_string(),
        };
        assert!(dispatch(Commands::Pause(args), &MockRisky).await.is_ok());
    }

    #[tokio::test]
    async fn dispatch_resume_variant_is_routed_through_the_executor() {
        let args = commands::ctl::ResumeArgs {
            run_id: "r".to_string(),
        };
        assert!(dispatch(Commands::Resume(args), &MockRisky).await.is_ok());
    }

    #[tokio::test]
    async fn dispatch_ps_variant_is_routed_through_the_executor() {
        let result = dispatch(Commands::Ps(commands::ps::PsArgs::default()), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_daemon_variant_is_routed_through_the_executor() {
        let args = commands::daemon::DaemonArgs {
            action: None,
            socket: None,
        };
        let result = dispatch(Commands::Daemon(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_auth_variant_is_routed_through_the_executor() {
        let args = commands::auth::AuthArgs::status_for_test();
        let result = dispatch(Commands::Auth(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_mcp_variant_is_routed_through_the_executor() {
        let args = commands::mcp::McpArgs::list_for_test();
        let result = dispatch(Commands::Mcp(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_serve_variant_is_routed_through_the_executor() {
        let args = commands::serve::ServeArgs {
            port: 0,
            host: "127.0.0.1".to_string(),
            cors: None,
            token: Some("test-token".to_string()),
            allow_admin: false,
            workdir_root: None,
            no_remote_yolo: false,
        };
        let result = dispatch(Commands::Serve(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_agent_client_variant_is_routed_through_the_executor() {
        let args = commands::agent_client::AgentClientArgs::default();
        let result = dispatch(Commands::AgentClient(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    // ─── Safe variants: called directly, driven through dispatch() ───────────

    #[tokio::test]
    async fn dispatch_create_variant_is_routed() {
        // An already-existing directory makes `create::execute` return a real,
        // harmless `Err` without touching anything outside a tempdir.
        let dir = tempfile::tempdir().unwrap();
        let args = commands::create::CreateArgs {
            name: dir.path().to_str().unwrap().to_string(),
            ..create_args()
        };
        let result = dispatch(Commands::Create(args), &MockRisky).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn dispatch_list_variant_is_routed() {
        // Isolated: this reaches `Config::load()`, which reads process-wide
        // environment. Unisolated it races every `temp_env` test in the binary.
        crate::config::with_isolated_config_path_async("dispatch-list", |_fake_dir| async move {
            let args = commands::list::ListArgs {
                filter: "all".to_string(),
                json: false,
            };
            let result = dispatch(Commands::List(args), &MockRisky).await;
            assert!(result.is_ok());
        })
        .await;
    }

    #[tokio::test]
    async fn dispatch_add_variant_is_routed() {
        let args = commands::add::AddArgs {
            package: "definitely-not-a-real-bundle-xyz.leviath-bundle".to_string(),
        };
        // `add` loads the real config to report the `[read_paths]` grant status
        // of what it installs, so it needs the same isolation every other
        // config-touching test takes.
        let result = crate::config::with_isolated_config_path_async("dispatch-add", |_| {
            dispatch(Commands::Add(args), &MockRisky)
        })
        .await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn dispatch_remove_variant_is_routed() {
        let args = commands::remove::RemoveArgs {
            name: "definitely-not-an-installed-agent-xyz".to_string(),
        };
        let result = dispatch(Commands::Remove(args), &MockRisky).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn dispatch_test_variant_is_routed() {
        let dir = tempfile::tempdir().unwrap();
        let args = commands::test::TestArgs {
            path: Some(dir.path().to_str().unwrap().to_string()),
            filter: None,
            dry_run: true,
        };
        let result = dispatch(Commands::Test(args), &MockRisky).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn dispatch_pack_variant_is_routed() {
        let dir = tempfile::tempdir().unwrap();
        let args = commands::pack::PackArgs {
            path: Some(dir.path().to_str().unwrap().to_string()),
            output: None,
        };
        let result = dispatch(Commands::Pack(args), &MockRisky).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn dispatch_models_variant_is_routed() {
        crate::config::with_isolated_config_path_async("dispatch-models", |_fake_dir| async move {
            let args = commands::models::ModelsArgs {
                command: commands::models::ModelsCommand::List(commands::models::ListArgs {
                    provider: None,
                    remote: false,
                    all: false,
                    json: false,
                }),
            };
            let result = dispatch(Commands::Models(args), &MockRisky).await;
            assert!(result.is_ok());
        })
        .await;
    }

    #[tokio::test]
    async fn dispatch_validate_variant_is_routed() {
        // `validate` loads the real config to answer "can this install reach
        // the providers this blueprint names", so it needs the same isolation
        // every other config-touching test takes.
        crate::config::with_isolated_config_path_async("dispatch-validate", |_| async {
            let dir = tempfile::tempdir().unwrap();
            let args = commands::validate::ValidateArgs {
                path: dir
                    .path()
                    .join("does-not-exist")
                    .to_str()
                    .unwrap()
                    .to_string(),
                deny_warnings: false,
                json: false,
            };
            let result = dispatch(Commands::Validate(args), &MockRisky).await;
            assert!(result.is_err());
        })
        .await;
    }

    #[tokio::test]
    async fn dispatch_tools_variant_is_routed() {
        // Point LEVIATH_HOME at a temp dir so the scan is hermetic; an empty
        // tools dir just lists nothing and returns Ok (routing is exercised).
        let home = tempfile::tempdir().unwrap();
        let result = temp_env::async_with_vars(
            [("LEVIATH_HOME", Some(home.path().to_str().unwrap()))],
            async {
                let args = commands::tools::ToolsArgs { json: false };
                dispatch(Commands::Tools(args), &MockRisky).await
            },
        )
        .await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_context_variant_is_routed() {
        // A run with no archive → the command errors (routing is exercised).
        let args = commands::context::ContextArgs {
            run_id: "no-such-run-xyzzy".to_string(),
            json: false,
            full: false,
        };
        let result = dispatch(Commands::Context(args), &MockRisky).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn dispatch_policy_list_variant_is_routed() {
        let args = commands::policy::PolicyArgs {
            command: commands::policy::PolicyCommand::List(commands::policy::PolicyListArgs {}),
        };
        let result = dispatch(Commands::Policy(args), &MockRisky).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn dispatch_policy_test_variant_is_routed() {
        let args = commands::policy::PolicyArgs {
            command: commands::policy::PolicyCommand::Test(commands::policy::PolicyTestArgs {
                tool: "shell".to_string(),
                target: None,
                taint: "public".to_string(),
            }),
        };
        let result = dispatch(Commands::Policy(args), &MockRisky).await;
        assert!(result.is_ok());
    }
}