1use std::path::PathBuf;
2
3use clap::{Parser, Subcommand};
4
5use crate::config::DemoConfig;
6use crate::datasets::{
7 prepare_davis_dataset, prepare_sintel_dataset, validate_standard_external_package,
8};
9use crate::engine_native::{run_engine_native_import, run_engine_native_replay};
10use crate::error::Result;
11use crate::external_validation::probe_external_gpu_only;
12use crate::mixed_regime::confirm_mixed_regime;
13use crate::pipeline::{
14 export_evaluator_handoff, export_minimal_report, generate_scene_artifacts, run_all,
15 run_all_filtered, run_check_signing, run_demo_a, run_demo_a_filtered, run_demo_b,
16 run_demo_b_efficiency_only, run_demo_b_filtered, run_engine_realistic_bridge,
17 run_external_replay_only, run_fast_path_only, run_gpu_path_only, run_realism_bridge_only,
18 run_resolution_scaling_only, run_sbir_demo, run_sensitivity_only, run_timing_only,
19 validate_artifact_bundle, validate_engine_native_gates, validate_final_bundle,
20};
21use crate::unreal_native::run_unreal_native;
22
23#[derive(Debug, Parser)]
24#[command(
25 author,
26 version,
27 about = "DSFB computer-graphics evaluation and artifact pipeline"
28)]
29pub struct Cli {
30 #[command(subcommand)]
31 pub command: Command,
32}
33
34#[derive(Debug, Subcommand)]
35pub enum Command {
36 PrepareDavis {
37 #[arg(long, default_value = "data/external/davis")]
38 output: PathBuf,
39 },
40 PrepareSintel {
41 #[arg(long, default_value = "data/external/sintel")]
42 output: PathBuf,
43 },
44 GenerateScene {
45 #[arg(long, default_value = "generated")]
46 output: PathBuf,
47 #[arg(long)]
48 scenario: Option<String>,
49 },
50 RunDemoA {
51 #[arg(long, default_value = "generated")]
52 output: PathBuf,
53 #[arg(long)]
54 scenario: Option<String>,
55 },
56 RunDemoB {
57 #[arg(long, default_value = "generated")]
58 output: PathBuf,
59 #[arg(long)]
60 scenario: Option<String>,
61 },
62 RunAblations {
63 #[arg(long, default_value = "generated")]
64 output: PathBuf,
65 },
66 RunScenario {
67 scenario: String,
68 #[arg(long, default_value = "generated")]
69 output: PathBuf,
70 },
71 RunAll {
72 #[arg(long, default_value = "generated")]
73 output: PathBuf,
74 #[arg(long)]
75 scenario: Option<String>,
76 },
77 RunTiming {
78 #[arg(long, default_value = "generated")]
79 output: PathBuf,
80 },
81 RunResolutionScaling {
82 #[arg(long, default_value = "generated")]
83 output: PathBuf,
84 },
85 RunSensitivity {
86 #[arg(long, default_value = "generated")]
87 output: PathBuf,
88 },
89 RunDemoBEfficiency {
90 #[arg(long, default_value = "generated")]
91 output: PathBuf,
92 },
93 RunGpuPath {
94 #[arg(long, default_value = "generated")]
95 output: PathBuf,
96 },
97 #[command(visible_aliases = ["run-external-replay", "replay-external"])]
98 ImportExternal {
99 #[arg(long)]
100 manifest: PathBuf,
101 #[arg(long, default_value = "generated")]
102 output: PathBuf,
103 },
104 #[command(visible_alias = "run-realism-bridge")]
105 RunRealismSuite {
106 #[arg(long, default_value = "generated")]
107 output: PathBuf,
108 },
109 ExportEvaluatorHandoff {
110 #[arg(long, default_value = "generated")]
111 output: PathBuf,
112 },
113 Validate {
114 #[arg(long, default_value = "generated")]
115 output: PathBuf,
116 },
117 ValidateFinal {
118 #[arg(long, default_value = "generated")]
119 output: PathBuf,
120 #[arg(long)]
123 allow_pending_engine_native: bool,
124 },
125 ImportEngineNative {
128 #[arg(long)]
129 manifest: PathBuf,
130 #[arg(long, default_value = "generated/engine_native")]
131 output: PathBuf,
132 },
133 RunEngineNativeReplay {
136 #[arg(long)]
137 manifest: PathBuf,
138 #[arg(long, default_value = "generated/engine_native")]
139 output: PathBuf,
140 },
141 RunUnrealNative {
144 #[arg(long)]
145 manifest: PathBuf,
146 #[arg(long, default_value = "generated/unreal_native_runs")]
147 output: PathBuf,
148 #[arg(long)]
149 run_name: Option<String>,
150 },
151 ConfirmMixedRegime {
154 #[arg(long, default_value = "generated")]
155 output: PathBuf,
156 },
157 ValidateArtifacts {
158 #[arg(long, default_value = "generated")]
159 output: PathBuf,
160 },
161 ExportMinimalReport {
162 #[arg(long, default_value = "generated")]
163 output: PathBuf,
164 },
165 #[command(hide = true)]
167 ProbeExternalGpu {
168 #[arg(long)]
169 manifest: PathBuf,
170 #[arg(long, default_value = "generated")]
171 output: PathBuf,
172 #[arg(long)]
173 capture_label: Option<String>,
174 #[arg(long)]
175 width: Option<usize>,
176 #[arg(long)]
177 height: Option<usize>,
178 },
179 RunEngineRealisticBridge {
181 #[arg(long, default_value = "generated/engine_realistic")]
182 output: PathBuf,
183 },
184 RunCheckSigning {
186 #[arg(long, default_value = "generated/final_bundle")]
187 output: PathBuf,
188 },
189 RunFastPath {
195 #[arg(long, default_value = "generated/fast_path_runs")]
196 output: PathBuf,
197 },
198 SbirDemo {
204 #[arg(long, default_value = "generated/sbir_demo")]
205 output: PathBuf,
206 },
207}
208
209pub fn run(cli: Cli) -> Result<()> {
210 let config = DemoConfig::default();
211 match cli.command {
212 Command::PrepareDavis { output } => {
213 let manifest = prepare_davis_dataset(&output)?;
214 println!("DAVIS manifest: {}", manifest.display());
215 }
216 Command::PrepareSintel { output } => {
217 let manifest = prepare_sintel_dataset(&output)?;
218 println!("Sintel manifest: {}", manifest.display());
219 }
220 Command::GenerateScene { output, .. } => {
221 let manifest = generate_scene_artifacts(&config, &output)?;
222 println!(
223 "generated canonical scene manifest for {} at {}",
224 manifest.scenario_id,
225 output.display()
226 );
227 }
228 Command::RunDemoA { output, scenario } => {
229 let artifacts = if let Some(scenario) = scenario.as_deref() {
230 run_demo_a_filtered(&config, &output, Some(scenario))?
231 } else {
232 run_demo_a(&config, &output)?
233 };
234 print_demo_a_artifacts(&artifacts);
235 }
236 Command::RunDemoB { output, scenario } => {
237 let artifacts = if let Some(scenario) = scenario.as_deref() {
238 run_demo_b_filtered(&config, &output, Some(scenario))?
239 } else {
240 run_demo_b(&config, &output)?
241 };
242 print_demo_b_artifacts(&artifacts);
243 }
244 Command::RunAblations { output } => {
245 let artifacts = run_demo_a_filtered(&config, &output, Some("thin_reveal"))?;
246 print_demo_a_artifacts(&artifacts);
247 }
248 Command::RunScenario { scenario, output } => {
249 let artifacts = run_all_filtered(&config, &output, Some(&scenario))?;
250 println!("scenario output: {}", artifacts.output_dir.display());
251 println!("report: {}", artifacts.demo_a.report_path.display());
252 }
253 Command::RunAll { output, scenario } => {
254 let artifacts = if let Some(scenario) = scenario.as_deref() {
255 run_all_filtered(&config, &output, Some(scenario))?
256 } else {
257 run_all(&config, &output)?
258 };
259 println!("run output: {}", artifacts.output_dir.display());
260 println!("manifest: {}", artifacts.manifest_path.display());
261 println!("report: {}", artifacts.demo_a.report_path.display());
262 println!("demo b report: {}", artifacts.demo_b.report_path.display());
263 println!(
264 "mentor audit: {}",
265 artifacts.five_mentor_audit_path.display()
266 );
267 println!(
268 "blocker report: {}",
269 artifacts.blocker_report_path.display()
270 );
271 println!(
272 "demo b decision report: {}",
273 artifacts.demo_b_decision_report_path.display()
274 );
275 }
276 Command::RunTiming { output } => {
277 let report = run_timing_only(&config, &output)?;
278 println!("timing report: {}", report.display());
279 }
280 Command::RunResolutionScaling { output } => {
281 let report = run_resolution_scaling_only(&config, &output)?;
282 println!("resolution scaling report: {}", report.display());
283 }
284 Command::RunSensitivity { output } => {
285 let report = run_sensitivity_only(&config, &output)?;
286 println!("parameter sensitivity report: {}", report.display());
287 }
288 Command::RunDemoBEfficiency { output } => {
289 let report = run_demo_b_efficiency_only(&config, &output)?;
290 println!("demo b efficiency report: {}", report.display());
291 }
292 Command::RunGpuPath { output } => {
293 let report = run_gpu_path_only(&config, &output)?;
294 println!("gpu execution report: {}", report.display());
295 }
296 Command::ImportExternal { manifest, output } => {
297 let report = run_external_replay_only(&config, &manifest, &output)?;
298 println!("external replay report: {}", report.display());
299 }
300 Command::RunRealismSuite { output } => {
301 let report = run_realism_bridge_only(&config, &output)?;
302 println!("realism bridge report: {}", report.display());
303 }
304 Command::ExportEvaluatorHandoff { output } => {
305 let report = export_evaluator_handoff(&config, &output)?;
306 println!("evaluator handoff: {}", report.display());
307 }
308 Command::Validate { output } => {
309 validate_standard_external_package(&output)?;
310 println!(
311 "validated standard external package at {}",
312 output.display()
313 );
314 }
315 Command::ValidateFinal {
316 output,
317 allow_pending_engine_native,
318 } => {
319 validate_final_bundle(&output)?;
320 let engine_native_dir = output
322 .parent()
323 .unwrap_or(std::path::Path::new("."))
324 .join("engine_native");
325 validate_engine_native_gates(&engine_native_dir, allow_pending_engine_native)?;
326 println!("validated final bundle at {}", output.display());
327 }
328 Command::ImportEngineNative { manifest, output } => {
329 let artifacts = run_engine_native_import(&config, &manifest, &output)?;
330 println!("engine-native import report: {}", artifacts.import_report_path.display());
331 println!("resolved manifest: {}", artifacts.resolved_manifest_path.display());
332 println!("ENGINE_NATIVE_CAPTURE_MISSING={}", artifacts.capture_missing);
333 }
334 Command::RunEngineNativeReplay { manifest, output } => {
335 let artifacts = run_engine_native_replay(&config, &manifest, &output)?;
336 println!("engine-native replay report: {}", artifacts.replay_report_path.display());
337 println!("GPU report: {}", artifacts.gpu_report_path.display());
338 println!("Demo A: {}", artifacts.demo_a_report_path.display());
339 println!("Demo B: {}", artifacts.demo_b_report_path.display());
340 println!("validation: {}", artifacts.validation_report_path.display());
341 println!("ENGINE_NATIVE_CAPTURE_MISSING={}", artifacts.capture_missing);
342 }
343 Command::RunUnrealNative {
344 manifest,
345 output,
346 run_name,
347 } => {
348 let cli_args = std::env::args().collect::<Vec<_>>();
349 let artifacts = run_unreal_native(
350 &config,
351 &manifest,
352 &output,
353 run_name.as_deref(),
354 &cli_args,
355 )?;
356 println!("run dir: {}", artifacts.run_dir.display());
357 println!(
358 "materialized manifest: {}",
359 artifacts.materialized_manifest_path.display()
360 );
361 println!("summary: {}", artifacts.summary_path.display());
362 println!("metrics csv: {}", artifacts.metrics_csv_path.display());
363 println!("metrics summary: {}", artifacts.metrics_summary_path.display());
364 println!(
365 "comparison summary: {}",
366 artifacts.comparison_summary_path.display()
367 );
368 println!("failure modes: {}", artifacts.failure_modes_path.display());
369 println!("provenance: {}", artifacts.provenance_path.display());
370 println!(
371 "notebook manifest: {}",
372 artifacts.notebook_manifest_path.display()
373 );
374 println!(
375 "executive sheet: {}",
376 artifacts.executive_sheet_path.display()
377 );
378 println!("pdf: {}", artifacts.pdf_path.display());
379 println!("zip: {}", artifacts.zip_path.display());
380 }
381 Command::ConfirmMixedRegime { output } => {
382 let report = confirm_mixed_regime(&config, &output)?;
383 println!("mixed regime confirmation: {}", report.display());
384 }
385 Command::ValidateArtifacts { output } => {
386 validate_artifact_bundle(&output)?;
387 println!("validated artifact bundle at {}", output.display());
388 }
389 Command::ExportMinimalReport { output } => {
390 let report = export_minimal_report(&config, &output)?;
391 println!("minimal report: {}", report.display());
392 }
393 Command::ProbeExternalGpu {
394 manifest,
395 output,
396 capture_label,
397 width,
398 height,
399 } => {
400 let scaled_resolution = match (width, height) {
401 (Some(width), Some(height)) => Some((width, height)),
402 (None, None) => None,
403 _ => {
404 return Err(crate::error::Error::Message(
405 "probe-external-gpu requires both --width and --height when scaling is requested"
406 .to_string(),
407 ))
408 }
409 };
410 let metrics_path = probe_external_gpu_only(
411 &config,
412 &manifest,
413 &output,
414 capture_label.as_deref(),
415 scaled_resolution,
416 )?;
417 println!("gpu probe metrics: {}", metrics_path.display());
418 }
419 Command::RunEngineRealisticBridge { output } => {
420 let report = run_engine_realistic_bridge(&config, &output)?;
421 println!("engine-realistic bridge report: {}", report.display());
422 }
423 Command::RunCheckSigning { output } => {
424 let report = run_check_signing(&config, &output)?;
425 println!("check-signing report: {}", report.display());
426 }
427 Command::RunFastPath { output } => {
428 let artifacts = run_fast_path_only(&output)?;
429 println!("fast-path output: {}", artifacts.output_dir.display());
430 println!("timing json: {}", artifacts.timing_json_path.display());
431 println!("summary json: {}", artifacts.summary_json_path.display());
432 println!("trust svg: {}", artifacts.trust_svg_path.display());
433 println!("summary md: {}", artifacts.summary_md_path.display());
434 }
435 Command::SbirDemo { output } => {
436 let artifacts = run_sbir_demo(&config, &output)?;
437 println!("sbir-demo output: {}", artifacts.output_dir.display());
438 println!("pdf: {}", artifacts.pdf_path.display());
439 println!("test results: {}", artifacts.test_results_path.display());
440 }
441 }
442 Ok(())
443}
444
445fn print_demo_a_artifacts(artifacts: &crate::pipeline::DemoAArtifacts) {
446 println!("demo output: {}", artifacts.output_dir.display());
447 println!("metrics: {}", artifacts.metrics_path.display());
448 println!("report: {}", artifacts.report_path.display());
449 println!(
450 "reviewer summary: {}",
451 artifacts.reviewer_summary_path.display()
452 );
453 println!(
454 "ablation report: {}",
455 artifacts.ablation_report_path.display()
456 );
457 println!("cost report: {}", artifacts.cost_report_path.display());
458 println!(
459 "completion note: {}",
460 artifacts.completion_note_path.display()
461 );
462 for figure in &artifacts.figure_paths {
463 println!("figure: {}", figure.display());
464 }
465}
466
467fn print_demo_b_artifacts(artifacts: &crate::pipeline::DemoBArtifacts) {
468 println!("demo output: {}", artifacts.output_dir.display());
469 println!("metrics: {}", artifacts.metrics_path.display());
470 println!("report: {}", artifacts.report_path.display());
471 for figure in &artifacts.figure_paths {
472 println!("figure: {}", figure.display());
473 }
474}