//! A widget whose generated program panics must say so before it is run, and a widget //! that runs a substitute for the model or device it is named after must not be counted as //! shipping. //! //! A seam is codegen that emits `unimplemented!` at declared defaults, in process or //! init. The program compiles and then panics on the first run. That was the deliberate //! choice over fabricating APIs that do not exist (`AudioBuffer::stft_magnitude`, a //! one-call candle `txt2img`). The set is empty: every builtin emits a real call. //! //! A stand-in is the other way not to ship: it compiles AND runs, but what runs is a //! deterministic substitute — seeded noise for Stable Diffusion, a byte histogram for //! CLIP, a word rotation for a language model. Neither the seam scan nor the T0 census //! sees it, so it declares itself through `Widget::stand_in`. //! //! The live census is [`flowrs_core::graph::widget::seam_widget_ids`], //! [`flowrs_core::graph::widget::stand_in_widget_ids`] and //! [`flowrs_core::graph::widget::shipping_widget_count`], not this comment. //! [`the_launch_census_matches_the_registry`] locks the numbers those functions return. use flowrs_core::Graph; use flowrs_core::graph::diagnostics::check; #[test] fn a_widget_that_emits_no_seam_does_not_warn() { // `gen.txt2img` used to be a seam. It is a stand-in now — no seam, and not shipping // either — and `image.grayscale` always shipped. Neither generates a seam. for id in ["gen.txt2img", "image.grayscale"] { let mut g = Graph::new(); g.add_node(id, 0.0, 0.0); assert!( !check(&g) .iter() .any(|d| d.message.contains("generates a seam")), "{id} emits a real implementation and must not be flagged", ); } } /// The set is computed from what widgets emit, not from a hand-kept list — a list would /// drift the moment a seam was filled in. #[test] fn the_seam_set_matches_what_the_widgets_actually_emit() { let seams = flowrs_core::graph::widget::seam_widget_ids(); for id in seams { let mut g = Graph::new(); g.add_node(id, 0.0, 0.0); let src = g.to_rust().unwrap_or_default(); assert!( src.contains("unimplemented!"), "`{id}` is listed as a seam but its program has none", ); } // And nothing outside the set emits one. let registry = flowrs_core::graph::widget::builtin_registry(); for w in registry.all() { let id = w.id(); if seams.contains(&id) { continue; } let mut g = Graph::new(); g.add_node(&id, 0.0, 0.0); let Ok(src) = g.to_rust() else { continue }; assert!( !src.contains("unimplemented!"), "`{id}` emits a seam but is not listed, so no warning would be raised", ); } } /// Launch census. Quoted by the site from library code; this test is what stops the /// numbers drifting from what codegen actually emits and what the widgets declare. /// /// Shipping is total builtins minus seams minus stand-ins. A seam is a widget whose /// codegen emits `unimplemented!` (see [`the_seam_set_matches_what_the_widgets_actually_emit`]); /// a stand-in is a widget whose `stand_in()` names the model or device it does not reach. /// Counted from the builtin registry: 262 widgets, 0 seams, 16 stand-ins, 246 shipping. // +3 (2026-10-18): live.serial, live.rtt, live.symbol — shipping, no stand-in. // +10 (2026-10-18): capture.open and nine decode.* — shipping, no stand-in. // +1 (2026-10-19): live.biosignal — shipping, no stand-in. // +1 (2026-10-20): decode.harp — shipping, no stand-in. // +2 (2026-10-20): unit.convert, clock.convert — shipping, no stand-in. // -7 stand-ins (2026-10-20, work/mcuio): the UART, I2C, SPI, ADC and timer widgets lower // to flowrs-hal on Embassy and RTIC and run as firmware on the F4 and RP2040 twins. // +1 (2026-10-21, work/partition): imu.complementary — shipping, no stand-in. // +3 (2026-10-22, work/ros2edge): imu.decode, ros.publish, ros.subscribe — shipping, no // stand-in (the ROS endpoints publish and subscribe for real in the generated ROS 2 node). // +4 (2026-10-28, work/seamsg): mavlink.telemetry, mavlink.heartbeat, mavlink.setpoint, // mavlink.command — shipping, no stand-in (the generated program talks to the vehicle). // +6 (2026-10-29, work/mcudsp): embedded.adc.capture, dsp.samples, dsp.fir, dsp.biquad, // dsp.moving, dsp.decimate — shipping, no stand-in (fixed-size frames, lowered to the MCU). // +1 (2026-10-29, work/teststation): instrument.station — shipping, no stand-in (the editor // runs it on simulated instruments; the generated program drives the real ones). const LAUNCH_BUILTIN_WIDGETS: usize = 273; const LAUNCH_SEAM_WIDGETS: usize = 0; const LAUNCH_STAND_IN_WIDGETS: usize = 16; const LAUNCH_SHIPPING_WIDGETS: usize = LAUNCH_BUILTIN_WIDGETS - LAUNCH_SEAM_WIDGETS - LAUNCH_STAND_IN_WIDGETS; /// Seam ids in the order [`WidgetRegistry::seam_ids`](flowrs_core::graph::widget::WidgetRegistry::seam_ids) /// returns them. The count above is `LAUNCH_SEAM_IDS.len()`; both have to move together. const LAUNCH_SEAM_IDS: &[&str] = &[]; /// Stand-in ids, sorted, as [`WidgetRegistry::stand_in_ids`](flowrs_core::graph::widget::WidgetRegistry::stand_in_ids) /// returns them. Models: the generative pillar, whose ten widgets run `gen_runtime` /// substitutes at both tiers, and `ai.complete`, whose generated program answers with a /// word rotation. Devices: `audio.record` and `microphone` return a synthetic 1 kHz tone, /// `audio.play` only counts samples, and `video.open` / `video.write` move raw frames /// through flowRS's own container instead of a codec. `neural.detect` and `neural.segment` /// left this list when they began decoding the model they are given, and the seven /// embedded peripherals (UART read and write, I2C read and write, SPI, ADC, the timer) when /// Embassy and RTIC began lowering them to flowrs-hal: no embedded widget is on it now /// ([`no_embedded_widget_is_a_stand_in`]). const LAUNCH_STAND_IN_IDS: &[&str] = &[ "ai.complete", "audio.play", "audio.record", "gen.caption", "gen.clip_image", "gen.clip_text", "gen.img2img", "gen.inpaint", "gen.txt2img", "gen.upscale", "gen.vae_decode", "gen.vae_encode", "gen.whisper", "microphone", "video.open", "video.write", ]; /// Every embedded widget reaches real hardware on the MCU targets, so none is a stand-in. /// Pinned at zero: the plan's blocker was seven of eleven declared stand-ins (twelve /// embedded widgets since the ADC capture). #[test] fn no_embedded_widget_is_a_stand_in() { let registry = flowrs_core::graph::widget::builtin_registry(); let embedded: Vec = registry .all() .iter() .map(|w| w.id()) .filter(|id| id.starts_with("embedded.")) .collect(); assert_eq!(embedded.len(), 12, "{embedded:?}"); let stand_ins: Vec<&String> = embedded .iter() .filter(|id| flowrs_core::graph::widget::stand_in_widget_ids().contains(*id)) .collect(); assert_eq!(stand_ins.len(), 0, "embedded stand-ins: {stand_ins:?}"); for id in &embedded { assert!( flowrs_core::codegen::mcu::SURFACE.contains(&id.as_str()), "{id} is not on the MCU surface" ); } } #[test] fn the_launch_census_matches_the_registry() { let registry = flowrs_core::graph::widget::builtin_registry(); let seams = flowrs_core::graph::widget::seam_widget_ids(); let stand_ins = flowrs_core::graph::widget::stand_in_widget_ids(); let shipping = flowrs_core::graph::widget::shipping_widget_count(); assert_eq!(registry.len(), registry.all().len()); assert_eq!(registry.shipping_count(), shipping); // A stand-in is never counted as shipping: the shipping widgets are exactly those in // neither set, and the three numbers account for every builtin once. let shipped: Vec = registry .all() .iter() .map(|w| w.id()) .filter(|id| !seams.contains(id) && !stand_ins.contains(id)) .collect(); assert_eq!( shipping, shipped.len(), "shipping_widget_count() counts a stand-in or a seam as shipping" ); for id in stand_ins { assert!( !shipped.contains(id), "{id} is a stand-in and counted as shipping" ); } let overlap = seams.intersection(stand_ins).count(); assert_eq!( shipping + seams.len() + stand_ins.len() - overlap, registry.len() ); let listed = registry.seam_ids(); let mut cached: Vec<_> = seams.iter().cloned().collect(); cached.sort(); assert_eq!( listed, cached, "seam_ids() must be seam_widget_ids(), sorted" ); assert!( listed.windows(2).all(|w| w[0] < w[1]), "seam ids are sorted" ); assert_eq!(LAUNCH_SEAM_IDS.len(), LAUNCH_SEAM_WIDGETS); assert_eq!( listed.iter().map(String::as_str).collect::>(), LAUNCH_SEAM_IDS, "seam ids changed" ); let standing = registry.stand_in_ids(); assert!( standing.windows(2).all(|w| w[0] < w[1]), "stand-in ids are sorted" ); assert_eq!(LAUNCH_STAND_IN_IDS.len(), LAUNCH_STAND_IN_WIDGETS); assert_eq!( standing.iter().map(String::as_str).collect::>(), LAUNCH_STAND_IN_IDS, "stand-in ids changed. A widget that now runs the real model drops its \ `stand_in()`; one that runs a substitute must declare it" ); // Each names the real thing it stands in for, which is what the editor's badge says. for w in registry.all() { if let Some(real) = w.stand_in() { assert!( !real.trim().is_empty(), "{} declares a stand-in for nothing", w.id() ); } } let total = registry.len(); assert_eq!( (total, seams.len(), stand_ins.len(), shipping), ( LAUNCH_BUILTIN_WIDGETS, LAUNCH_SEAM_WIDGETS, LAUNCH_STAND_IN_WIDGETS, LAUNCH_SHIPPING_WIDGETS ), "launch census changed: total={total} seams={listed:?} stand-ins={standing:?} \ shipping={shipping}. Update LAUNCH_* only when codegen really gained or lost a \ seam, a widget stopped or started standing in, or a widget was added", ); } /// The substitute runtimes, and what each stands in for. A widget whose program calls one /// computes the substitute, whatever its name says, so it must declare a stand-in; the /// census would otherwise count it as shipping. `host_pcm`'s WAV codec and the embedded /// board's latches are not in the list: a WAV file is real, and the board is what the /// Host target simulates for widgets the MCU targets lower for real (`embedded.rs` /// holds that split to `codegen::mcu::SURFACE`). const SUBSTITUTES: &[&str] = &[ "gen_runtime::", "llm_local::", "host_pcm::capture", "host_pcm::playback", "video_io::", ]; #[test] fn every_widget_that_runs_a_substitute_declares_a_stand_in() { use flowrs_core::graph::widget::CodegenContext; use std::collections::HashMap; let registry = flowrs_core::graph::widget::builtin_registry(); let mut caught = 0usize; for w in registry.all() { let ctx = CodegenContext { var_names: HashMap::new(), imports: vec![], state_fields: vec![], profile: flowrs_core::graph::Profile::scientific(), params: w .parameters() .into_iter() .map(|p| (p.name, p.default)) .collect(), }; let inputs = w .inputs() .into_iter() .map(|p| (p.name.clone(), p.name)) .collect(); let process = w.codegen(&ctx, inputs).process; if let Some(sub) = SUBSTITUTES.iter().find(|s| process.contains(*s)) { caught += 1; assert!( w.stand_in().is_some(), "{} calls `{sub}` but does not declare a stand-in", w.id() ); } } // The scan has something to hold: the generative ten, ai.complete, the two audio // stand-ins, the microphone and the video pair. assert_eq!(caught, 16, "the substitute scan found {caught} widgets"); } /// A widget that used to be a seam must still build. A seam, when one exists, is a /// warning, not an error — refusing the build would block the person filling it in. #[test] fn a_seam_does_not_block_the_build() { let mut g = Graph::new(); g.add_node("gen.txt2img", 0.0, 0.0); assert!( flowrs_core::graph::diagnostics::is_buildable(&g), "a seam is a warning, not an error", ); }