@@ -19,6 +19,7 @@ use crate::core::runner_execution_envelope::{
1919 PathMaterializationPlan , RunnerExecutionArtifactRef , RunnerExecutionNextAction ,
2020 RunnerExecutionRecord ,
2121} ;
22+ use clap:: Command ;
2223use std:: collections:: BTreeSet ;
2324
2425use super :: spec:: {
@@ -42,6 +43,127 @@ pub const RUNNER_ARTIFACT_MANIFEST_SCHEMA: &str = crate::core::artifacts::ARTIFA
4243pub const RUNNER_ARTIFACT_MANIFEST_FILE : & str = crate :: core:: artifacts:: ARTIFACT_MANIFEST_FILE ;
4344pub const RUNNER_ARTIFACT_ROOT_DIR_SUFFIX : & str = "-homeboy-artifacts" ;
4445
46+ const LAB_CLI_ARGUMENT_IDS : & [ & str ] = & [
47+ "placement" ,
48+ "detach_after_handoff" ,
49+ "artifact_root" ,
50+ "runner" ,
51+ "allow_dirty_lab_workspace" ,
52+ "skip_deps_hydration" ,
53+ "runner_env" ,
54+ "lab_env_json" ,
55+ "runner_workspace_root" ,
56+ ] ;
57+
58+ impl crate :: cli_surface:: Placement {
59+ /// Explicitly permit controller execution when an intended Lab offload
60+ /// cannot proceed. `Auto` retains the existing default routing behavior.
61+ pub const fn allows_local_fallback ( self ) -> bool {
62+ matches ! ( self , Self :: LabOrLocal )
63+ }
64+
65+ /// Whether the operator requested a Lab attempt instead of leaving the
66+ /// command to its automatic routing policy.
67+ pub const fn requests_lab ( self ) -> bool {
68+ matches ! ( self , Self :: Lab | Self :: LabOrLocal )
69+ }
70+ }
71+
72+ /// Projects the generated Clap tree onto the commands whose portability
73+ /// contract can route work through Lab. Parsing remains global so placement
74+ /// options keep working before or after a subcommand, but help only advertises
75+ /// options a command can honor.
76+ pub fn scope_lab_cli_arguments ( command : Command ) -> Command {
77+ let lab_args = command
78+ . get_arguments ( )
79+ . filter ( |arg| LAB_CLI_ARGUMENT_IDS . contains ( & arg. get_id ( ) . as_str ( ) ) )
80+ . cloned ( )
81+ . collect :: < Vec < _ > > ( ) ;
82+ let command = LAB_CLI_ARGUMENT_IDS . iter ( ) . fold ( command, |command, id| {
83+ command. mut_arg ( id, |arg| arg. hide ( true ) )
84+ } ) ;
85+
86+ scope_lab_cli_arguments_at_path ( command, & [ ] , & lab_args)
87+ }
88+
89+ fn scope_lab_cli_arguments_at_path (
90+ command : Command ,
91+ path : & [ String ] ,
92+ lab_args : & [ clap:: Arg ] ,
93+ ) -> Command {
94+ let visible = lab_cli_arguments_are_visible_for_path ( path) ;
95+ let command = if visible {
96+ lab_args. iter ( ) . fold ( command, |command, arg| {
97+ command. arg ( arg. clone ( ) . global ( false ) . hide ( false ) )
98+ } )
99+ } else {
100+ command
101+ } ;
102+
103+ command. mut_subcommands ( |subcommand| {
104+ let mut subcommand_path = path. to_vec ( ) ;
105+ subcommand_path. push ( subcommand. get_name ( ) . to_string ( ) ) ;
106+ scope_lab_cli_arguments_at_path ( subcommand, & subcommand_path, lab_args)
107+ } )
108+ }
109+
110+ fn lab_cli_arguments_are_visible_for_path ( path : & [ String ] ) -> bool {
111+ matches ! (
112+ path. iter( )
113+ . map( String :: as_str)
114+ . collect:: <Vec <_>>( )
115+ . as_slice( ) ,
116+ [ "agent-task" , "cook" ]
117+ | [ "agent-task" , "run-plan" ]
118+ | [ "agent-task" , "run" ]
119+ | [ "agent-task" , "run-next" ]
120+ | [ "agent-task" , "status" ]
121+ | [ "agent-task" , "list" ]
122+ | [ "agent-task" , "active" ]
123+ | [ "agent-task" , "latest" ]
124+ | [ "agent-task" , "logs" ]
125+ | [ "agent-task" , "artifacts" ]
126+ | [ "agent-task" , "evidence" ]
127+ | [ "agent-task" , "review" ]
128+ | [ "agent-task" , "retry" ]
129+ | [ "agent-task" , "promote" ]
130+ | [ "agent-task" , "providers" ]
131+ | [ "agent-task" , "fanout" , "submit-batch" ]
132+ | [ "agent-task" , "fanout" , "status" ]
133+ | [ "agent-task" , "fanout" , "artifacts" ]
134+ | [ "agent-task" , "auth" , "status" ]
135+ | [ "agent-task" , "controller" , "from-spec" ]
136+ | [ "agent-task" , "controller" , "run-from-spec" ]
137+ | [ "agent-task" , "controller" , "materialize" ]
138+ | [ "agent-task" , "controller" , "resume" ]
139+ | [ "bench" ]
140+ | [ "bench" , "matrix" ]
141+ | [ "fuzz" ]
142+ | [ "fuzz" , "run" ]
143+ | [ "fuzz" , "run-campaign" ]
144+ | [ "fuzz" , "list" ]
145+ | [ "fuzz" , "plan" ]
146+ | [ "fuzz" , "doctor" ]
147+ | [ "review" ]
148+ | [ "review" , "audit" ]
149+ | [ "review" , "lint" ]
150+ | [ "review" , "test" ]
151+ | [ "trace" ]
152+ | [ "refactor" ]
153+ | [ "rig" , "check" ]
154+ | [ "rig" , "run" ]
155+ | [ "runtime" , "refresh" ]
156+ | [ "worktree" , "cleanup" ]
157+ | [ "extension" , "update" ]
158+ | [ "extension" , "refresh" ]
159+ | [ "extension" , "dev-run" ]
160+ | [ "extension" , "show" ]
161+ | [ "tunnel" , "preview-consumer" , "run" ]
162+ | [ "tunnel" , "service" , "expose" ]
163+ | [ "tunnel" , "service" , "start" ]
164+ )
165+ }
166+
45167/// Routing-policy flags owned by the Lab command contract and retained through
46168/// route planning, offload, and runner dispatch.
47169#[ derive( Debug , Clone , Copy , Default , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
0 commit comments