Something went wrong. Try again.
Hardware-in-the-loop development platform for the Commodore 64.
Something went wrong. Try again.
75 kB · 2035 lines
Rust
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036use std::{ fs::{self, OpenOptions}, io::{self, Write}, net::Ipv4Addr, path::{Path, PathBuf}, process::ExitCode, time::Duration,};
use chrono::{DateTime, Utc};use clap::{Args, Parser, Subcommand, ValueEnum};use serde::Serialize;use serde_json::{Value, json};use sha2::{Digest, Sha256};use ultimate_tool_core::{ DebugCaptureAuthorization, DebugCapturePreparationRequest, Error, LoadedManifest, LocationSemantics, NativeAuthorizationMode, NativeHealthCheckRequest, NativeRepository, NativeRunRequest, ProbeRequest, ProgressEvent, ResolveMatch, RiscvSmokeRequest, RunOutcome, RunRepository, SourceLinkedCaptureAuthorization, SourceLinkedCapturePreparationRequest, TIMELINE_DEFAULT_LIMIT, TargetKind, TestRequest, TimelineAccess, TimelineCursor, TimelineItem, TimelineQuery, execute_source_linked_capture, inspect_ue2, pack_elf_as_ue2, parse_address, parse_timeline_address_range, prepare_source_linked_capture, probe_ultimate, run_native, run_native_health_check, run_riscv_smoke, run_test_matrix_observed, validate_ue2_for_elf,};use uuid::Uuid;
const CLI_SCHEMA_VERSION: u32 = 1;
#[derive(Debug, Parser)]#[command(version, about = "Hardware-in-the-loop C64 development")]struct Cli { /// Select human output or a versioned machine-readable envelope. #[arg(long, global = true, value_enum, default_value_t = OutputFormat::Human)] format: OutputFormat, #[command(subcommand)] command: Command,}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]enum OutputFormat { Human, Json, Jsonl,}
#[derive(Debug, Subcommand)]enum Command { /// Validate a c64dev.toml project contract. Validate(ManifestArgs), /// Inspect local prerequisites without executing commands or contacting targets. Doctor { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long, value_enum, default_value_t = TargetChoice::Fake)] target: TargetChoice, }, /// Probe a target without mutating it. Probe { #[command(subcommand)] target: ProbeTarget, }, /// Build once and execute a named test on one or more targets. Test(TestArgs), /// Capture one bounded C64 Ultimate 6510 debug stream. Capture { #[command(subcommand)] command: CaptureCommand, }, /// Inspect immutable run records and registered evidence. Runs { #[command(subcommand)] command: RunsCommand, }, /// Inspect, validate, or package a volatile U64-II update container. Ue2 { #[command(subcommand)] command: Ue2Command, }, /// Run the trusted volatile U64-II RISC-V smoke and recover stock firmware. Riscv { #[command(subcommand)] command: RiscvCommand, }, /// Produce reviewed, sanitized static derivatives without changing run evidence. Showcase { #[command(subcommand)] command: ShowcaseCommand, }, /// Start the loopback development service and embedded dashboard. Serve { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long, default_value_t = 6464)] port: u16, },}
impl Command { const fn name(&self) -> &'static str { match self { Self::Validate(_) => "validate", Self::Doctor { .. } => "doctor", Self::Probe { .. } => "probe", Self::Test(_) => "test", Self::Capture { command: CaptureCommand::DebugStream(_), } => "capture.debug_stream", Self::Capture { command: CaptureCommand::SourceLinked(_), } => "capture.source_linked", Self::Runs { command: RunsCommand::List(_), } => "runs.list", Self::Runs { command: RunsCommand::Show(_), } => "runs.show", Self::Runs { command: RunsCommand::Evidence(_), } => "runs.evidence", Self::Runs { command: RunsCommand::Resolve(_), } => "runs.resolve", Self::Runs { command: RunsCommand::Timeline(_), } => "runs.timeline", Self::Runs { command: RunsCommand::CrashReport(_), } => "runs.crash_report", Self::Runs { command: RunsCommand::Compare(_), } => "runs.compare", Self::Runs { command: RunsCommand::Report(_), } => "runs.report", Self::Runs { command: RunsCommand::ReportItems(_), } => "runs.report_items", Self::Runs { command: RunsCommand::CompareCheckpoints(_), } => "runs.compare_checkpoints", Self::Runs { command: RunsCommand::Comparisons(_), } => "runs.comparisons", Self::Runs { command: RunsCommand::Comparison(_), } => "runs.comparison", Self::Ue2 { command: Ue2Command::Inspect(_), } => "ue2.inspect", Self::Ue2 { command: Ue2Command::Validate(_), } => "ue2.validate", Self::Ue2 { command: Ue2Command::Pack(_), } => "ue2.pack", Self::Riscv { command: RiscvCommand::Smoke(_), } => "riscv.smoke", Self::Riscv { command: RiscvCommand::Prepare(_), } => "riscv.prepare", Self::Riscv { command: RiscvCommand::Run(_), } => "riscv.run", Self::Riscv { command: RiscvCommand::HealthCheck(_), } => "riscv.health_check", Self::Riscv { command: RiscvCommand::Intent { command: RiscvIntentCommand::Create(_), }, } => "riscv.intent.create", Self::Riscv { command: RiscvCommand::Intent { command: RiscvIntentCommand::Show(_), }, } => "riscv.intent.show", Self::Riscv { command: RiscvCommand::Intent { command: RiscvIntentCommand::Revoke(_), }, } => "riscv.intent.revoke", Self::Showcase { command: ShowcaseCommand::Export(_), } => "showcase.export", Self::Serve { .. } => "serve", } }}
#[derive(Debug, Subcommand)]enum CaptureCommand { /// Capture the configured multicast stream with exact start/stop authorization. DebugStream(DebugCaptureArgs), /// Build, source-link, launch, and capture the bounded timeline probe. SourceLinked(SourceLinkedCaptureArgs),}
#[derive(Debug, Args)]struct DebugCaptureArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, /// Literal wired IPv4 address of the intended C64 Ultimate. #[arg(long)] device: String, /// Local stream-capable interface IPv4 address. #[arg(long)] interface: Ipv4Addr, #[arg(long)] expect_device_id: Option<String>, #[arg(long)] expect_hostname: Option<String>, /// Must be exactly 1. #[arg(long)] confirm_stream_start_count: u16, /// Must be exactly 1; cleanup is attempted even if start fails. #[arg(long)] confirm_stream_stop_count: u16,}
#[derive(Debug, Args)]struct SourceLinkedCaptureArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long)] test: String, /// Literal wired IPv4 address of the intended C64 Ultimate. #[arg(long)] device: String, /// Local stream-capable interface IPv4 address. #[arg(long)] interface: Ipv4Addr, #[arg(long)] expect_device_id: Option<String>, #[arg(long)] expect_hostname: Option<String>, /// Must be exactly 1; run_prg performs one reset, DMA load, and launch. #[arg(long)] confirm_reset_count: u16, /// Must be exactly 1. #[arg(long)] confirm_stream_start_count: u16, /// Must be exactly 1; cleanup is attempted on every post-preflight path. #[arg(long)] confirm_stream_stop_count: u16,}
#[derive(Debug, Args)]struct ManifestArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Subcommand)]enum ProbeTarget { /// Read C64 Ultimate REST identity and version information. Ultimate { #[arg(long)] device: String, #[arg(long, default_value_t = 8_000)] timeout_ms: u64, /// Issue each probe read once, with no retry, redirect, or swallowed /// optional-capability failure. A strict restriction, not a permission. #[arg(long)] strict_gate: bool, },}
#[derive(Debug, Args)]struct TestArgs { test: String, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long, value_enum, required = true)] target: Vec<TargetChoice>, #[arg(long)] device: Option<String>, #[arg(long)] expect_device_id: Option<String>, #[arg(long)] expect_hostname: Option<String>, #[arg(long)] allow_reset: bool, /// Permit one declared REU image write per Ultimate iteration. #[arg(long)] allow_reu_write: bool, /// Compare this build with one immutable baseline and preserve the result. #[arg(long)] baseline_run: Option<Uuid>, /// Compare frozen checkpoints, requiring VICE then Ultimate and one iteration. #[arg(long)] compare_checkpoints: bool, /// Opt-in strict physical-gate restriction. Defaults to off; when enabled it /// fails closed on the first transport, status, body, or parse error instead /// of retrying or falling back. It adds no authorization. #[arg(long)] strict_gate: bool, #[arg(long, default_value_t = 1)] repeat: u16,}
#[derive(Debug, Clone, Copy, ValueEnum)]enum TargetChoice { Fake, Vice, Ultimate,}
#[derive(Debug, Subcommand)]enum RunsCommand { /// List newest immutable runs. List(RunsListArgs), /// Show one complete immutable run. Show(RunsShowArgs), /// Locate or copy one registered evidence file. Evidence(RunsEvidenceArgs), /// Resolve an address through one immutable run's snapshotted build analysis. Resolve(RunsResolveArgs), /// Page through one immutable debug-stream bus timeline. Timeline(RunsTimelineArgs), /// Report an immutable run's exact instrumented crash site. CrashReport(RunsCrashReportArgs), /// Compare build artifacts, sections, objects, and symbols between two runs. Compare(RunsCompareArgs), /// Explain recorded expectations, outcomes and verified evidence. Report(RunsReportArgs), /// Read one bounded page of recorded diagnostic detail. ReportItems(RunsReportItemsArgs), /// Compare verified frozen checkpoints from two completed runs without target access. CompareCheckpoints(RunsCompareCheckpointsArgs), /// List separately persisted checkpoint-comparison records. Comparisons(ComparisonsListArgs), /// Inspect a checksummed immutable checkpoint-comparison record. Comparison(ComparisonShowArgs),}
#[derive(Debug, Args)]struct RunsListArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long, default_value_t = 50)] limit: usize,}
#[derive(Debug, Args)]struct RepositoryArgs { /// Inspect a repository without loading a current manifest. #[arg(long, conflicts_with = "manifest")] project_root: Option<PathBuf>, /// Use this manifest's project root (defaults to c64dev.toml). #[arg(long, conflicts_with = "project_root")] manifest: Option<PathBuf>,}
impl RepositoryArgs { fn root(&self) -> ultimate_tool_core::Result<PathBuf> { if let Some(root) = &self.project_root { Ok(root.clone()) } else { Ok( LoadedManifest::load(self.manifest.as_deref().unwrap_or(Path::new("c64dev.toml")))? .project_root, ) } }
fn repository(&self) -> ultimate_tool_core::Result<RunRepository> { Ok(RunRepository::new(&self.root()?)) }}
#[derive(Debug, Args)]struct RunsCompareCheckpointsArgs { baseline: Uuid, candidate: Uuid, #[command(flatten)] repository: RepositoryArgs,}
#[derive(Debug, Args)]struct ComparisonsListArgs { #[arg(long, default_value_t = 50)] limit: usize, #[command(flatten)] repository: RepositoryArgs,}
#[derive(Debug, Args)]struct ComparisonShowArgs { comparison_id: Uuid, #[command(flatten)] repository: RepositoryArgs,}
#[derive(Debug, Subcommand)]enum ShowcaseCommand { /// Export a new static directory; this does not publish it. Export(ShowcaseExportArgs),}
#[derive(Debug, Args)]struct ShowcaseExportArgs { #[arg(long)] run: Uuid, #[arg(long)] output: PathBuf, #[arg(long, default_value = "Recorded development evidence")] label: String, #[arg(long)] section: Option<String>, #[arg(long, default_value_t = 50)] limit: usize, #[arg(long)] cursor: Option<String>, #[command(flatten)] repository: RepositoryArgs,}
#[derive(Debug, Args)]struct RunsReportArgs { run_id: Uuid, #[command(flatten)] repository: RepositoryArgs,}
#[derive(Debug, Args)]struct RunsReportItemsArgs { run_id: Uuid, /// checkpoints, budget, assertions, comparison_sections, comparison_objects or comparison_symbols. #[arg(long)] section: String, #[arg(long, default_value_t = 50)] limit: usize, #[arg(long)] cursor: Option<String>, #[command(flatten)] repository: RepositoryArgs,}
#[derive(Debug, Args)]struct RunsShowArgs { run_id: Uuid, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Args)]struct RunsEvidenceArgs { run_id: Uuid, name: String, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, /// Copy exact evidence bytes to a new file. Existing files are never replaced. #[arg(long)] output: Option<PathBuf>,}
#[derive(Debug, Args)]struct RunsResolveArgs { run_id: Uuid, address: String, #[arg(long)] context: Option<String>, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Clone, Copy, ValueEnum)]enum TimelineAccessChoice { Read, Write,}
impl From<TimelineAccessChoice> for TimelineAccess { fn from(value: TimelineAccessChoice) -> Self { match value { TimelineAccessChoice::Read => Self::Read, TimelineAccessChoice::Write => Self::Write, } }}
#[derive(Debug, Args)]struct RunsTimelineArgs { run_id: Uuid, #[arg(long)] cursor: Option<TimelineCursor>, #[arg(long, default_value_t = TIMELINE_DEFAULT_LIMIT)] limit: usize, /// One address or an inclusive range such as $D020-$D021. #[arg(long)] address: Option<String>, #[arg(long, value_enum)] access: Option<TimelineAccessChoice>, #[arg(long)] context: Option<String>, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Args)]struct RunsCrashReportArgs { run_id: Uuid, #[arg(long)] context: Option<String>, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Args)]struct RunsCompareArgs { baseline_run_id: Uuid, candidate_run_id: Uuid, #[arg(long)] context: Option<String>, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Subcommand)]enum Ue2Command { /// Parse and enforce the bounded canonical container policy. Inspect(Ue2InspectArgs), /// Prove that a UE2 is the exact canonical encoding of an ELF. Validate(Ue2ValidateArgs), /// Create a canonical UE2 from an ELF without replacing an existing file. Pack(Ue2PackArgs),}
#[derive(Debug, Args)]struct Ue2InspectArgs { file: PathBuf,}
#[derive(Debug, Args)]struct Ue2ValidateArgs { file: PathBuf, #[arg(long)] elf: PathBuf,}
#[derive(Debug, Args)]struct Ue2PackArgs { #[arg(long)] elf: PathBuf, #[arg(long)] output: PathBuf,}
#[derive(Debug, Subcommand)]enum RiscvCommand { /// Build, validate, and immutably snapshot a standalone trusted native project. Prepare(RiscvPrepareArgs), /// Create and manage hash-bound native launch authorization. Intent { #[command(subcommand)] command: RiscvIntentCommand, }, /// Execute the exact prepared UE2 authorized by a durable launch intent. Run(RiscvRunArgs), /// Complete a pending post-recovery C64 health check without relaunching native code. HealthCheck(RiscvHealthCheckArgs), /// Execute one bounded trusted-payload smoke over Wi-Fi. Smoke(RiscvSmokeArgs),}
#[derive(Debug, Args)]struct RiscvPrepareArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Subcommand)]enum RiscvIntentCommand { /// Bind one preparation hash to an exact probed Ultimate. Create(RiscvIntentCreateArgs), /// Show one durable launch intent. Show(RiscvIntentShowArgs), /// Permanently revoke one launch intent. Revoke(RiscvIntentRevokeArgs),}
#[derive(Debug, Clone, Copy, ValueEnum)]enum RiscvAuthorizationChoice { OneShot, Standing,}
impl From<RiscvAuthorizationChoice> for NativeAuthorizationMode { fn from(value: RiscvAuthorizationChoice) -> Self { match value { RiscvAuthorizationChoice::OneShot => Self::OneShot, RiscvAuthorizationChoice::Standing => Self::Standing, } }}
#[derive(Debug, Args)]struct RiscvIntentCreateArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long)] preparation: Uuid, #[arg(long)] device: String, #[arg(long)] expect_device_id: Option<String>, #[arg(long)] expect_hostname: Option<String>, #[arg(long, value_enum)] authorization: RiscvAuthorizationChoice, #[arg(long)] expires_at: Option<DateTime<Utc>>, #[arg(long)] maximum_runs: Option<u32>, /// Confirm that standing mode grants repeated bounded native mutations. #[arg(long)] confirm_standing_native_execution: bool,}
#[derive(Debug, Args)]struct RiscvIntentShowArgs { intent: Uuid, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf,}
#[derive(Debug, Args)]struct RiscvIntentRevokeArgs { intent: Uuid, #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long)] reason: String,}
#[derive(Debug, Args)]struct RiscvRunArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long)] intent: Uuid, #[arg(long)] device: String, #[arg(long)] expect_device_id: Option<String>, #[arg(long)] expect_hostname: Option<String>,}
#[derive(Debug, Args)]struct RiscvHealthCheckArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long)] intent: Uuid, #[arg(long)] after_run: Uuid, #[arg(long)] device: String, #[arg(long)] expect_device_id: Option<String>, #[arg(long)] expect_hostname: Option<String>,}
#[derive(Debug, Args)]struct RiscvSmokeArgs { #[arg(long, default_value = "c64dev.toml")] manifest: PathBuf, #[arg(long)] device: String, #[arg(long)] expect_device_id: Option<String>, #[arg(long)] expect_hostname: Option<String>, /// Use an exact eight-character uppercase hexadecimal nonce for a new smoke. #[arg(long)] nonce: Option<String>, /// Resume one eligible pre-Run-Update failure without creating another FTP file. #[arg(long)] resume_staged_run: Option<Uuid>, #[arg(long)] allow_ftp_create: bool, #[arg(long)] allow_run_update: bool, #[arg(long)] allow_machine_reboot: bool, #[arg(long)] allow_recovery_reset: bool, #[arg(long)] allow_startup_write: bool,}
#[derive(Debug)]struct ExecutionOutput { data: Value, human: Vec<String>, exit_code: u8,}
#[derive(Debug, Serialize)]struct MachineEnvelope<'a> { schema_version: u32, record_kind: &'a str, command: &'a str, data: Value, #[serde(skip_serializing_if = "Option::is_none")] error: Option<MachineError>,}
#[derive(Debug, Serialize)]struct MachineError { code: &'static str, layer: &'static str, message: String, exit_code: u8,}
#[derive(Debug, Serialize)]struct EvidenceInfo { run_id: Uuid, name: String, canonical_path: PathBuf, size: u64, sha256: String, copied_to: Option<PathBuf>,}
#[tokio::main]async fn main() -> ExitCode { let cli = Cli::parse(); let format = cli.format; let command = cli.command.name(); match execute(cli, command).await { Ok(output) => { render_success(format, command, &output); ExitCode::from(output.exit_code) } Err(error) => { render_error(format, command, &error); ExitCode::from(error.exit_code()) } }}
async fn execute(cli: Cli, command: &'static str) -> ultimate_tool_core::Result<ExecutionOutput> { let format = cli.format; match cli.command { Command::Validate(arguments) => { let manifest = LoadedManifest::load(arguments.manifest)?; Ok(ExecutionOutput { data: json!({ "project": manifest.manifest.project.name, "manifest_schema_version": manifest.manifest.schema_version, "manifest": manifest.path, }), human: vec![format!( "valid: {} (schema {})", manifest.manifest.project.name, manifest.manifest.schema_version )], exit_code: 0, }) } Command::Doctor { manifest, target } => { let target = match target { TargetChoice::Fake => TargetKind::Fake, TargetChoice::Vice => TargetKind::Vice, TargetChoice::Ultimate => TargetKind::Ultimate, }; let report = ultimate_tool_core::doctor::doctor(&manifest, target); let mut human = vec![format!( "local prerequisites: {:?}; target runtime is not verified", report.status )]; human.extend( report.checks.iter().map(|check| { format!(" {:?}: {:?} — {}", check.id, check.status, check.detail) }), ); Ok(ExecutionOutput { data: serde_json::to_value(&report) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: report.exit_code(), }) } Command::Probe { target: ProbeTarget::Ultimate { device, timeout_ms, strict_gate, }, } => { let identity = tokio::task::spawn_blocking(move || { probe_ultimate(&ProbeRequest { host: device, request_timeout: Duration::from_millis(timeout_ms), strict_gate, }) }) .await .map_err(|error| Error::Preflight(format!("probe worker failed: {error}")))??; Ok(ExecutionOutput { data: serde_json::to_value(&identity) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![ serde_json::to_string_pretty(&identity) .map_err(|error| Error::Evidence(error.to_string()))?, ], exit_code: 0, }) } Command::Test(arguments) => { let (summaries, comparison_pair) = tokio::task::spawn_blocking(move || { let observer = |progress: ProgressEvent| { if format == OutputFormat::Jsonl { emit_machine(MachineEnvelope { schema_version: CLI_SCHEMA_VERSION, record_kind: "progress", command, data: serde_json::to_value(progress).unwrap_or_else( |error| json!({"serialization_error": error.to_string()}), ), error: None, }); } }; let request = TestRequest { manifest_path: arguments.manifest, test: arguments.test, targets: arguments .target .into_iter() .map(|target| match target { TargetChoice::Fake => TargetKind::Fake, TargetChoice::Vice => TargetKind::Vice, TargetChoice::Ultimate => TargetKind::Ultimate, }) .collect(), device: arguments.device, expected_device_id: arguments.expect_device_id, expected_hostname: arguments.expect_hostname, allow_reset: arguments.allow_reset, allow_reu_write: arguments.allow_reu_write, baseline_run_id: arguments.baseline_run, repeat: arguments.repeat, strict_gate: arguments.strict_gate, }; if arguments.compare_checkpoints { let matrix = ultimate_tool_core::runner::run_checkpoint_matrix_observed( &request, &observer, )?; Ok((matrix.runs, Some(matrix.pair))) } else { run_test_matrix_observed(&request, &observer).map(|runs| (runs, None)) } }) .await .map_err(|error| Error::Preflight(format!("test worker failed: {error}")))??; let mut exit_code = summaries .iter() .filter(|summary| summary.outcome != RunOutcome::Passed) .map(|summary| summary.exit_code) .max() .unwrap_or(0); if comparison_pair.as_ref().is_some_and(|pair| { pair.state != ultimate_tool_core::checkpoints::CheckpointComparisonState::Matched }) { exit_code = exit_code.max(4); } let outcome = if exit_code == 0 && summaries .iter() .all(|summary| summary.outcome == RunOutcome::Passed) { RunOutcome::Passed } else if summaries .iter() .any(|summary| summary.outcome == RunOutcome::Error) { RunOutcome::Error } else { RunOutcome::Failed }; let mut human = Vec::new(); for summary in &summaries { human.push(format!( "{} iteration {}/{}: {:?} evidence={}", summary.target, summary.iteration, summary.iteration_total, summary.outcome, summary.directory.display() )); if let Some(error) = &summary.error { human.push(format!(" {error}")); } } if let Some(pair) = &comparison_pair { human.push(format!( "checkpoint comparison {:?}: {} (earliest recorded logical checkpoint only)", pair.state, pair.comparison_id )); } let mut data = json!({"outcome": outcome, "exit_code": exit_code, "runs": summaries}); if let Some(pair) = &comparison_pair { data["checkpoint_comparison"] = serde_json::to_value(pair) .map_err(|error| Error::Evidence(error.to_string()))?; } Ok(ExecutionOutput { data, human, exit_code, }) } Command::Capture { command: CaptureCommand::DebugStream(arguments), } => { let summary = tokio::task::spawn_blocking(move || { let observer = |progress: ProgressEvent| { if format == OutputFormat::Jsonl { emit_machine(MachineEnvelope { schema_version: CLI_SCHEMA_VERSION, record_kind: "progress", command, data: serde_json::to_value(progress).unwrap_or_else( |error| json!({"serialization_error": error.to_string()}), ), error: None, }); } }; let preparation = ultimate_tool_core::prepare_debug_capture(&DebugCapturePreparationRequest { manifest_path: arguments.manifest, device: arguments.device, interface: arguments.interface, expected_device_id: arguments.expect_device_id, expected_hostname: arguments.expect_hostname, })?; ultimate_tool_core::execute_debug_capture( preparation, DebugCaptureAuthorization { confirmed_stream_start_count: arguments.confirm_stream_start_count, confirmed_stream_stop_count: arguments.confirm_stream_stop_count, }, &observer, ) }) .await .map_err(|error| Error::Preflight(format!("capture worker failed: {error}")))??; let mut human = vec![format!( "debug stream: {:?} evidence={}", summary.outcome, summary.directory.display() )]; if let Some(error) = &summary.error { human.push(format!(" {error}")); } Ok(ExecutionOutput { data: serde_json::to_value(&summary) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: summary.exit_code, }) } Command::Capture { command: CaptureCommand::SourceLinked(arguments), } => { if arguments.confirm_reset_count != 1 || arguments.confirm_stream_start_count != 1 || arguments.confirm_stream_stop_count != 1 { return Err(Error::Configuration( "source-linked capture requires --confirm-reset-count 1, --confirm-stream-start-count 1, and --confirm-stream-stop-count 1" .into(), )); } let summary = tokio::task::spawn_blocking(move || { let observer = |progress: ProgressEvent| { if format == OutputFormat::Jsonl { emit_machine(MachineEnvelope { schema_version: CLI_SCHEMA_VERSION, record_kind: "progress", command, data: serde_json::to_value(progress).unwrap_or_else( |error| json!({"serialization_error": error.to_string()}), ), error: None, }); } }; let preparation = prepare_source_linked_capture(&SourceLinkedCapturePreparationRequest { manifest_path: arguments.manifest, test: arguments.test, device: arguments.device, interface: arguments.interface, expected_device_id: arguments.expect_device_id, expected_hostname: arguments.expect_hostname, })?; execute_source_linked_capture( preparation, SourceLinkedCaptureAuthorization { confirmed_reset_count: arguments.confirm_reset_count, confirmed_stream_start_count: arguments.confirm_stream_start_count, confirmed_stream_stop_count: arguments.confirm_stream_stop_count, }, &observer, ) }) .await .map_err(|error| Error::Preflight(format!("capture worker failed: {error}")))??; let mut human = vec![format!( "source-linked timeline: {:?} evidence={}", summary.outcome, summary.directory.display() )]; if let Some(hash) = &summary.artifact_sha256 { human.push(format!(" artifact_sha256={hash}")); } if let Some(error) = &summary.error { human.push(format!(" {error}")); } Ok(ExecutionOutput { data: serde_json::to_value(&summary) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: summary.exit_code, }) } Command::Runs { command: RunsCommand::CompareCheckpoints(arguments), } => { let bundle = ultimate_tool_core::checkpoints::compare_run_checkpoints( &arguments.repository.repository()?, arguments.baseline, arguments.candidate, )?; let matched = bundle.comparison.state == ultimate_tool_core::checkpoints::CheckpointComparisonState::Matched; Ok(ExecutionOutput { human: vec![format!( "checkpoint comparison {:?}; earliest recorded logical checkpoint, not execution-time cause", bundle.comparison.state )], data: serde_json::to_value(&bundle.comparison) .map_err(|error| Error::Evidence(error.to_string()))?, exit_code: if matched { 0 } else { 4 }, }) } Command::Runs { command: RunsCommand::Comparisons(arguments), } => { let repository = ultimate_tool_core::checkpoints::CheckpointComparisonRepository::new( &arguments.repository.root()?, ); let listing = repository.list(arguments.limit)?; Ok(ExecutionOutput { human: listing .summaries .iter() .map(|entry| format!("{} {:?}", entry.comparison_id, entry.state)) .collect(), data: serde_json::to_value(listing) .map_err(|error| Error::Evidence(error.to_string()))?, exit_code: 0, }) } Command::Runs { command: RunsCommand::Comparison(arguments), } => { let repository = ultimate_tool_core::checkpoints::CheckpointComparisonRepository::new( &arguments.repository.root()?, ); let record = repository.load(arguments.comparison_id)?; Ok(ExecutionOutput { human: vec![format!("{} {:?}", record.comparison_id, record.state)], data: serde_json::to_value(record) .map_err(|error| Error::Evidence(error.to_string()))?, exit_code: 0, }) } Command::Runs { command: RunsCommand::Report(arguments), } => { let report = arguments .repository .repository()? .diagnostic_report(arguments.run_id)?; let mut human = vec![format!( "{} {:?}: {} on {} (evidence {:?})", report.run_id, report.outcome, report.test, report.target_kind, report.evidence.integrity )]; if let Some(failure) = &report.failure { human.push(format!( " {} / {}: {}", failure.layer, failure.stage, failure.message )); } human.push(format!( " expectations {:?}; terminal {:?}; screen {:?}", report.expectations.availability, report.results.terminal_verdict, report.results.screen_verdict )); Ok(ExecutionOutput { data: serde_json::to_value(&report) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Runs { command: RunsCommand::ReportItems(arguments), } => { use ultimate_tool_core::report::{ DIAGNOSTIC_REPORT_SCHEMA_VERSION, DiagnosticCursor, DiagnosticItemsQuery, }; let query = DiagnosticItemsQuery { schema_version: DIAGNOSTIC_REPORT_SCHEMA_VERSION, section: arguments.section.parse()?, limit: arguments.limit, cursor: arguments .cursor .as_deref() .map(DiagnosticCursor::parse) .transpose()?, }; let page = arguments .repository .repository()? .diagnostic_items(arguments.run_id, &query)?; let mut human = vec![format!( "{} {}: {} of {} rows", page.run_id, page.section, page.items.len(), page.total )]; for item in &page.items { human.push( serde_json::to_string(item) .map_err(|error| Error::Evidence(error.to_string()))?, ); } if let Some(cursor) = &page.next_cursor { human.push(format!("next cursor: {cursor}")); } Ok(ExecutionOutput { data: serde_json::to_value(&page) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Runs { command: RunsCommand::List(arguments), } => { let manifest = LoadedManifest::load(arguments.manifest)?; let listing = RunRepository::new(&manifest.project_root).list(arguments.limit)?; let mut human = listing .runs .iter() .map(|run| { format!( "{} {} {} {:?} iteration {}/{}", run.run_id, run.target_kind, run.test, run.outcome, run.iteration, run.iteration_total ) }) .collect::<Vec<_>>(); human.extend( listing .warnings .iter() .map(|warning| format!("warning: {warning}")), ); Ok(ExecutionOutput { data: serde_json::to_value(&listing) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Runs { command: RunsCommand::Show(arguments), } => { let manifest = LoadedManifest::load(arguments.manifest)?; let record = RunRepository::new(&manifest.project_root).load(arguments.run_id)?; Ok(ExecutionOutput { data: serde_json::to_value(&record) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![ serde_json::to_string_pretty(&record) .map_err(|error| Error::Evidence(error.to_string()))?, ], exit_code: 0, }) } Command::Runs { command: RunsCommand::Evidence(arguments), } => { let manifest = LoadedManifest::load(arguments.manifest)?; let repository = RunRepository::new(&manifest.project_root); let source = repository.evidence_path(arguments.run_id, &arguments.name)?; let bytes = fs::read(&source)?; let copied_to = arguments .output .as_deref() .map(|output| copy_create_new(output, &bytes)) .transpose()?; let info = EvidenceInfo { run_id: arguments.run_id, name: arguments.name, canonical_path: fs::canonicalize(&source)?, size: bytes.len() as u64, sha256: format!("{:x}", Sha256::digest(&bytes)), copied_to, }; Ok(ExecutionOutput { data: serde_json::to_value(&info) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![format!( "{} {} bytes sha256={}{}", info.canonical_path.display(), info.size, info.sha256, info.copied_to .as_ref() .map_or_else(String::new, |path| format!(" copied={}", path.display())) )], exit_code: 0, }) } Command::Runs { command: RunsCommand::Resolve(arguments), } => { let manifest = LoadedManifest::load(arguments.manifest)?; let address = parse_address(&arguments.address)?; let result = RunRepository::new(&manifest.project_root).resolve_address( arguments.run_id, address, arguments.context.as_deref(), )?; let mut human = vec![format!( "{} {} at {}", arguments.run_id, format!("{:?}", result.state).to_lowercase(), result.address_hex )]; human.extend(result.matches.iter().map(|resolved| { format!( " {} {}+{} [{}]{}{}", resolved.rank, resolved.symbol, resolved.offset, resolved.context, resolved .section .as_ref() .map_or_else(String::new, |section| format!(" section={section}")), human_source_location(resolved) ) })); Ok(ExecutionOutput { data: serde_json::to_value(&result) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Runs { command: RunsCommand::Timeline(arguments), } => { let manifest = LoadedManifest::load(arguments.manifest)?; let address = arguments .address .as_deref() .map(parse_timeline_address_range) .transpose()?; let page = RunRepository::new(&manifest.project_root).timeline( arguments.run_id, &TimelineQuery { cursor: arguments.cursor, limit: arguments.limit, address, access: arguments.access.map(Into::into), context: arguments.context, ..TimelineQuery::default() }, )?; let mut human = vec![format!( "{} {:?} complete={} truncated={} gap={} next={}", arguments.run_id, page.mode, page.capture_complete, page.capture_truncated, page.crosses_gap, page.next_cursor .map_or_else(|| "end".into(), |cursor| cursor.to_string()) )]; human.extend(page.items.iter().map(|item| match item { TimelineItem::Bus(item) => format!( " {} seq={} {} {}={} data={} phi2={} irq_n={} nmi_n={} source={:?}", item.cursor, item.packet_sequence, item.capture_phase, match item.access { TimelineAccess::Read => "read", TimelineAccess::Write => "write", }, item.address_hex, item.data_hex, u8::from(item.signals.phi2), u8::from(item.signals.irq_n), u8::from(item.signals.nmi_n), item.address_resolution.result.state, ), TimelineItem::Gap(item) => format!( " {} gap={:?} expected={} observed={} uncertain={} missing={} length={}", item.cursor, item.discontinuity, item.expected_sequence .map_or_else(|| "unknown".into(), |value| value.to_string()), item.observed_sequence .map_or_else(|| "unknown".into(), |value| value.to_string()), item.uncertain_intervening_datagrams, item.missing_packet_count .map_or_else(|| "unknown".into(), |value| value.to_string()), item.payload_length .map_or_else(|| "unknown".into(), |value| value.to_string()), ), TimelineItem::Instruction(item) => format!( " {} instruction pc=${:04X} bytes={:02X?} class={}", item.cursor, item.pc, item.bytes, item.instruction_class ), })); Ok(ExecutionOutput { data: serde_json::to_value(&page) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Runs { command: RunsCommand::CrashReport(arguments), } => { let manifest = LoadedManifest::load(arguments.manifest)?; let report = RunRepository::new(&manifest.project_root) .crash_report(arguments.run_id, arguments.context.as_deref())?; let mut human = vec![format!( "{} {}{}", arguments.run_id, format!("{:?}", report.state).to_lowercase(), report .site_address_hex .as_ref() .map_or_else(String::new, |address| format!(" at {address}")) )]; if let Some(reason) = report.unavailable_reason { human.push(format!(" reason={reason}")); } human.extend(report.matches.iter().map(|resolved| { format!( " {} {}+{} [{}]{}{}", resolved.rank, resolved.symbol, resolved.offset, resolved.context, resolved .section .as_ref() .map_or_else(String::new, |section| format!(" section={section}")), human_source_location(resolved) ) })); for warning in &report.warnings { human.push(format!(" warning {}: {}", warning.code, warning.message)); } Ok(ExecutionOutput { data: serde_json::to_value(&report) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Runs { command: RunsCommand::Compare(arguments), } => { let manifest = LoadedManifest::load(arguments.manifest)?; let comparison = RunRepository::new(&manifest.project_root).compare_runs( arguments.baseline_run_id, arguments.candidate_run_id, arguments.context.as_deref(), )?; let human = vec![ format!( "{} -> {}: {:?}", arguments.baseline_run_id, arguments.candidate_run_id, comparison.state ), format!( " artifact={} sections={} objects={} symbols={} warnings={}", comparison.artifact.as_ref().map_or_else( || "unavailable".into(), |delta| format!("{:+} bytes", delta.delta_bytes) ), comparison.sections.len(), comparison.objects.len(), comparison.symbols.len(), comparison.warnings.len() ), ]; Ok(ExecutionOutput { data: serde_json::to_value(&comparison) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Ue2 { command: Ue2Command::Inspect(arguments), } => { let bytes = fs::read(&arguments.file).map_err(|error| { Error::Artifact(format!( "cannot read UE2 file {}: {error}", arguments.file.display() )) })?; let inspection = inspect_ue2(&bytes)?; let mut human = vec![format!( "{}: {} records, {} loaded bytes, entry={} sha256={}", arguments.file.display(), inspection.record_count, inspection.total_loaded_bytes, inspection.entry_point_hex, inspection.sha256 )]; human.extend(inspection.records.iter().map(|record| { format!( " record {}: {:#010x}..{:#010x} length={} start={:#010x} sha256={}", record.index, record.load_address, record.end_address, record.length, record.start_address, record.payload_sha256 ) })); Ok(ExecutionOutput { data: serde_json::to_value(&inspection) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: 0, }) } Command::Ue2 { command: Ue2Command::Validate(arguments), } => { let ue2_bytes = fs::read(&arguments.file).map_err(|error| { Error::Artifact(format!( "cannot read UE2 file {}: {error}", arguments.file.display() )) })?; let elf_bytes = fs::read(&arguments.elf).map_err(|error| { Error::Artifact(format!( "cannot read ELF file {}: {error}", arguments.elf.display() )) })?; let validation = validate_ue2_for_elf(&ue2_bytes, &elf_bytes)?; Ok(ExecutionOutput { data: serde_json::to_value(&validation) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![format!( "valid: {} exactly matches {} (entry={}, sha256={})", arguments.file.display(), arguments.elf.display(), validation.elf_entry_point_hex, validation.inspection.sha256 )], exit_code: 0, }) } Command::Ue2 { command: Ue2Command::Pack(arguments), } => { let elf_bytes = fs::read(&arguments.elf).map_err(|error| { Error::Artifact(format!( "cannot read ELF file {}: {error}", arguments.elf.display() )) })?; let packed = pack_elf_as_ue2(&elf_bytes)?; let canonical_output = write_artifact_create_new(&arguments.output, &packed.bytes)?; Ok(ExecutionOutput { data: json!({ "elf": arguments.elf, "output": canonical_output, "validation": packed.validation, }), human: vec![format!( "{}: {} bytes, {} records, entry={}, sha256={}", canonical_output.display(), packed.validation.inspection.file_size, packed.validation.inspection.record_count, packed.validation.elf_entry_point_hex, packed.validation.inspection.sha256 )], exit_code: 0, }) } Command::Riscv { command: RiscvCommand::Prepare(arguments), } => { let loaded = LoadedManifest::load(&arguments.manifest)?; let repository = NativeRepository::new(&loaded.project_root); let prepared = tokio::task::spawn_blocking(move || repository.prepare(&arguments.manifest)) .await .map_err(|error| { Error::Build(format!("native preparation worker failed: {error}")) })??; Ok(ExecutionOutput { data: serde_json::to_value(&prepared.record) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![ format!( "prepared native application {}", prepared.record.preparation_id ), format!( " ELF {} bytes sha256={}", prepared.record.elf.size, prepared.record.elf.sha256 ), format!( " UE2 {} bytes sha256={} policy={}", prepared.record.ue2.size, prepared.record.ue2.sha256, prepared.record.ue2_validation.policy ), format!(" evidence={}", prepared.directory.display()), ], exit_code: 0, }) } Command::Riscv { command: RiscvCommand::Intent { command: RiscvIntentCommand::Create(arguments), }, } => { let loaded = LoadedManifest::load(&arguments.manifest)?; if matches!(arguments.authorization, RiscvAuthorizationChoice::Standing) && !arguments.confirm_standing_native_execution { return Err(Error::Preflight( "standing authorization requires --confirm-standing-native-execution".into(), )); } let device = arguments.device.clone(); let request_timeout = Duration::from_millis(loaded.manifest.ultimate.request_timeout_ms); let identity = tokio::task::spawn_blocking(move || { probe_ultimate(&ProbeRequest { host: device, request_timeout, strict_gate: false, }) }) .await .map_err(|error| Error::Preflight(format!("probe worker failed: {error}")))??; ultimate_tool_core::target::ultimate::UltimateTarget::verify_expected_identity( &identity, arguments.expect_device_id.as_deref(), arguments.expect_hostname.as_deref(), )?; let intent = NativeRepository::new(&loaded.project_root).create_intent( arguments.preparation, &identity, arguments.authorization.into(), arguments.expires_at, arguments.maximum_runs, )?; Ok(ExecutionOutput { data: serde_json::to_value(&intent.record) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![ format!( "created {:?} native launch intent", intent.record.authorization ), format!(" intent={}", intent.record.intent_id), format!(" ue2_sha256={}", intent.record.ue2_sha256), format!(" remote_path={}", intent.record.remote_path), format!( " device={} firmware={:?} fpga={:?} core={:?}", intent.record.target.product, intent.record.target.firmware_version, intent.record.target.fpga_version, intent.record.target.core_version ), ], exit_code: 0, }) } Command::Riscv { command: RiscvCommand::Intent { command: RiscvIntentCommand::Show(arguments), }, } => { let loaded = LoadedManifest::load(&arguments.manifest)?; let intent = NativeRepository::new(&loaded.project_root).load_intent(arguments.intent)?; Ok(ExecutionOutput { data: serde_json::to_value(&intent.record) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![ format!( "native intent {}: {:?} {:?}", intent.record.intent_id, intent.record.authorization, intent.record.state ), format!( " uses={} maximum={:?} expires={:?}", intent.record.use_count, intent.record.maximum_runs, intent.record.expires_at ), format!( " ue2_sha256={} remote_path={}", intent.record.ue2_sha256, intent.record.remote_path ), ], exit_code: 0, }) } Command::Riscv { command: RiscvCommand::Intent { command: RiscvIntentCommand::Revoke(arguments), }, } => { let loaded = LoadedManifest::load(&arguments.manifest)?; let repository = NativeRepository::new(&loaded.project_root); repository.revoke_intent(arguments.intent, &arguments.reason)?; let intent = repository.load_intent(arguments.intent)?; Ok(ExecutionOutput { data: serde_json::to_value(&intent.record) .map_err(|error| Error::Evidence(error.to_string()))?, human: vec![format!( "revoked native launch intent {}: {}", intent.record.intent_id, intent.record.state_reason.as_deref().unwrap_or("") )], exit_code: 0, }) } Command::Riscv { command: RiscvCommand::Run(arguments), } => { let summary = tokio::task::spawn_blocking(move || { run_native(&NativeRunRequest { manifest_path: arguments.manifest, intent_id: arguments.intent, device: arguments.device, expected_device_id: arguments.expect_device_id, expected_hostname: arguments.expect_hostname, }) }) .await .map_err(|error| Error::Preflight(format!("native run worker failed: {error}")))??; let mut human = vec![ format!( "trusted native run {:?}: run={} display={} evidence={}", summary.outcome, summary.run_id, summary.invocation_display_id, summary.directory.display() ), format!( " intent={} preparation={}", summary.intent_id, summary.preparation_id ), ]; if let Some(health_check) = summary.post_recovery_health_check_run_id { human.push(format!( " post-recovery C64 health-check run={health_check}" )); } if let Some(error) = &summary.error { human.push(format!(" {error}")); } Ok(ExecutionOutput { data: serde_json::to_value(&summary) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: summary.exit_code, }) } Command::Riscv { command: RiscvCommand::HealthCheck(arguments), } => { let summary = tokio::task::spawn_blocking(move || { run_native_health_check(&NativeHealthCheckRequest { manifest_path: arguments.manifest, intent_id: arguments.intent, after_run_id: arguments.after_run, device: arguments.device, expected_device_id: arguments.expect_device_id, expected_hostname: arguments.expect_hostname, }) }) .await .map_err(|error| { Error::Preflight(format!("native health-check worker failed: {error}")) })??; let mut human = vec![ format!( "post-recovery C64 health check {:?}: run={} evidence={}", summary.outcome, summary.health_check_run_id, summary.directory.display() ), format!( " native_run={} intent={}", summary.after_run_id, summary.intent_id ), ]; if let Some(error) = &summary.error { human.push(format!(" {error}")); } Ok(ExecutionOutput { data: serde_json::to_value(&summary) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: summary.exit_code, }) } Command::Riscv { command: RiscvCommand::Smoke(arguments), } => { let summary = tokio::task::spawn_blocking(move || { run_riscv_smoke(&RiscvSmokeRequest { manifest_path: arguments.manifest, device: arguments.device, expected_device_id: arguments.expect_device_id, expected_hostname: arguments.expect_hostname, nonce: arguments.nonce, resume_staged_run_id: arguments.resume_staged_run, allow_ftp_create: arguments.allow_ftp_create, allow_run_update: arguments.allow_run_update, allow_machine_reboot: arguments.allow_machine_reboot, allow_recovery_reset: arguments.allow_recovery_reset, allow_startup_write: arguments.allow_startup_write, }) }) .await .map_err(|error| Error::Preflight(format!("RISC-V worker failed: {error}")))??; let mut human = vec![format!( "RISC-V smoke {:?}: run={} evidence={}", summary.outcome, summary.run_id, summary.directory.display() )]; if let Some(recovery) = summary.recovery_run_id { human.push(format!(" post-recovery C64 health-check run={recovery}")); } if let Some(error) = &summary.error { human.push(format!(" {error}")); } Ok(ExecutionOutput { data: serde_json::to_value(&summary) .map_err(|error| Error::Evidence(error.to_string()))?, human, exit_code: summary.exit_code, }) } Command::Showcase { command: ShowcaseCommand::Export(arguments), } => { use ultimate_tool_core::showcase::{ ShowcaseExportRequest, ShowcaseRowWindow, ShowcaseSection, ShowcaseSelection, export_showcase, }; let root = arguments.repository.root()?; if arguments.cursor.is_some() && arguments.section.is_none() { return Err(Error::Configuration( "showcase --cursor requires --section".into(), )); } let mut selection = ShowcaseSelection::new(vec![ShowcaseSection::Workflow]); if let Some(section) = arguments.section { let window = if section == "timeline" { ShowcaseRowWindow::Timeline { limit: arguments.limit, cursor: arguments.cursor.clone(), } } else { let section = section.parse()?; match section { ultimate_tool_core::report::DiagnosticSection::Budget => { selection.sections.push(ShowcaseSection::Budget) } ultimate_tool_core::report::DiagnosticSection::Assertions => { selection.sections.push(ShowcaseSection::Assertions) } ultimate_tool_core::report::DiagnosticSection::ComparisonSections | ultimate_tool_core::report::DiagnosticSection::ComparisonObjects | ultimate_tool_core::report::DiagnosticSection::ComparisonSymbols => { selection.sections.push(ShowcaseSection::Comparison) } ultimate_tool_core::report::DiagnosticSection::Checkpoints => {} } ShowcaseRowWindow::DiagnosticItems { section, limit: arguments.limit, cursor: arguments.cursor.clone(), } }; selection = selection.with_row_window(window); } let output = if arguments.output.is_absolute() { arguments.output } else { std::env::current_dir()?.join(arguments.output) }; let request = ShowcaseExportRequest::new(root.clone(), output, selection) .with_review_label(arguments.label); let export = export_showcase(&RunRepository::new(&root), arguments.run, &request)?; Ok(ExecutionOutput { human: vec![format!( "sanitized derivative: {}; not published", export.html_file.display() )], data: serde_json::to_value(&export) .map_err(|error| Error::Evidence(error.to_string()))?, exit_code: 0, }) } Command::Serve { manifest, port } => { ultimate_tool_server::serve(ultimate_tool_server::ServerConfig { manifest_path: manifest, port, }) .await?; Ok(ExecutionOutput { data: json!({"stopped": true}), human: Vec::new(), exit_code: 0, }) } }}
fn human_source_location(resolved: &ResolveMatch) -> String { let Some(location) = &resolved.location else { return String::new(); }; let mut rendered = location.file.clone(); if let Some(line) = location.line { rendered.push_str(&format!(":{line}")); if let Some(column) = location.column { rendered.push_str(&format!(":{column}")); } } match resolved.location_semantics { Some(LocationSemantics::AddressCallsite) => format!(" callsite={rendered}"), Some(LocationSemantics::SymbolStart) => format!(" symbol-start-source={rendered}"), None => format!(" unlabeled-source={rendered}"), }}
fn copy_create_new(output: &Path, bytes: &[u8]) -> ultimate_tool_core::Result<PathBuf> { let mut file = OpenOptions::new() .write(true) .create_new(true) .open(output) .map_err(|error| { Error::Evidence(format!( "cannot create evidence output {}: {error}", output.display() )) })?; file.write_all(bytes)?; file.sync_all()?; fs::canonicalize(output).map_err(Error::Io)}
fn write_artifact_create_new(output: &Path, bytes: &[u8]) -> ultimate_tool_core::Result<PathBuf> { let mut file = OpenOptions::new() .write(true) .create_new(true) .open(output) .map_err(|error| { Error::Artifact(format!( "cannot create UE2 output {}: {error}", output.display() )) })?; file.write_all(bytes).map_err(|error| { Error::Artifact(format!( "cannot write UE2 output {}: {error}", output.display() )) })?; file.sync_all().map_err(|error| { Error::Artifact(format!( "cannot sync UE2 output {}: {error}", output.display() )) })?; fs::canonicalize(output).map_err(|error| { Error::Artifact(format!( "cannot canonicalize UE2 output {}: {error}", output.display() )) })}
fn render_success(format: OutputFormat, command: &'static str, output: &ExecutionOutput) { match format { OutputFormat::Human => { for line in &output.human { println!("{line}"); } } OutputFormat::Json | OutputFormat::Jsonl => emit_machine(MachineEnvelope { schema_version: CLI_SCHEMA_VERSION, record_kind: "result", command, data: output.data.clone(), error: None, }), }}
fn render_error(format: OutputFormat, command: &'static str, error: &Error) { match format { OutputFormat::Human => eprintln!("error: {error}"), OutputFormat::Json | OutputFormat::Jsonl => emit_machine(MachineEnvelope { schema_version: CLI_SCHEMA_VERSION, record_kind: "error", command, data: Value::Null, error: Some(MachineError { code: error.code(), layer: error.layer(), message: error.to_string(), exit_code: error.exit_code(), }), }), }}
fn emit_machine(envelope: MachineEnvelope<'_>) { let stdout = io::stdout(); let mut lock = stdout.lock(); serde_json::to_writer(&mut lock, &envelope).expect("machine envelope serializes"); lock.write_all(b"\n").expect("stdout accepts envelope"); lock.flush().expect("stdout flushes envelope");}
#[cfg(test)]mod tests { use tempfile::tempdir;
use super::*;
#[test] fn evidence_copy_refuses_overwrite() { let root = tempdir().unwrap(); let output = root.path().join("evidence.bin"); fs::write(&output, b"existing").unwrap(); assert!(copy_create_new(&output, b"replacement").is_err()); assert_eq!(fs::read(output).unwrap(), b"existing"); }
#[test] fn ue2_output_refuses_overwrite() { let root = tempdir().unwrap(); let output = root.path().join("native.ue2"); fs::write(&output, b"existing").unwrap(); assert!(write_artifact_create_new(&output, b"replacement").is_err()); assert_eq!(fs::read(output).unwrap(), b"existing"); }
#[test] fn human_output_labels_exact_and_symbol_start_locations() { let mut resolved = ResolveMatch { rank: "containing".into(), context: "resident".into(), symbol: "crash".into(), raw_symbol: "crash".into(), aliases: Vec::new(), symbol_address: 0x1000, symbol_address_hex: "$1000".into(), symbol_size: 16, offset: 4, section: Some(".text".into()), object_file: None, kind: "text".into(), location: Some(ultimate_tool_core::analysis::SourceLocation { file: "src/main.rs".into(), line: Some(42), column: Some(7), }), location_semantics: Some(LocationSemantics::AddressCallsite), provenance: vec!["elf".into()], }; assert_eq!( human_source_location(&resolved), " callsite=src/main.rs:42:7" ); resolved.location_semantics = Some(LocationSemantics::SymbolStart); assert_eq!( human_source_location(&resolved), " symbol-start-source=src/main.rs:42:7" ); }}