infrastructure tier

infrastructure-screencast

Scripted, screen-recorded demo sessions: ffmpeg-subprocess recording (MKV-record-then-remux for crash tolerance), window orchestration (class-matched find/move/focus with the wmctrl→xdotool fallback chain — physical focus produces TRUSTED input events synthetic DOM events cannot), and a typed phase runner with health-wait probes. Deliberately-persistent artifacts — the opposite contract to infrastructure-screen-capture (DLP stills, never-persist), hence a separate crate. v1 is Linux/X11; CaptureBackend/WindowBackend seams are cross-platform, unsupported platforms fail with typed errors.

Scripted, screen-recorded demo sessions: ffmpeg-subprocess recording (MKV-record-then-remux for crash tolerance), window orchestration (class-matched find/move/focus with the wmctrl→xdotool fallback chain — physical focus produces TRUSTED input events synthetic DOM events cannot), and a typed phase runner with health-wait probes. Deliberately-persistent artifacts — the opposite contract to infrastructure-screen-capture (DLP stills, never-persist), hence a separate crate. v1 is Linux/X11; CaptureBackend/WindowBackend seams are cross-platform, unsupported platforms fail with typed errors.

Tierinfrastructure
Roleunclassified (baselined)
Pathcrates/infrastructure/screencast
Edition2021
Targetsinfrastructure_screencast
Public items58 across 5 modules
Tests36

What it is for

# infrastructure-screencast

Scripted, screen-recorded demo sessions as a composable library crate. Lifted from scripts/tools-demo-shopping{,-full}.sh via the tools-script-scan pipeline (Sprint 3.4); the shopping flow itself stays in the consumer.

Three capabilities, each usable alone:

crash-tolerant Matroska temp file, stops gracefully (q on stdin → deadline → kill fallback), then remuxes to mp4 (-c copy) with an atomic rename. An unclean stop keeps the .mkv.part for diagnosis and never claims an mp4. Drop performs bounded best-effort cleanup only — finalization is always the explicit recorder::Recorder::finish.

(wmctrl -lx) with the wmctrl→xdotool fallback chain the bash originals evolved from real breakage. Physical focus matters: real window focus + input produce TRUSTED events that synthetic DOM events cannot (the anti-bot humanization contract, and the known class where file inputs ignore synthetic events). Operator tiebreak (Gate 1.5): this layer is a correctness requirement, not demo cosmetics.

through a script::Probe seam (the library crate performs NO HTTP — an in-memory step log the caller may persist.

(ffprobe + a full -f null - decode pass) plus single-frame PNG extraction for visual confirmation. Distinct from infrastructure-screen-capture::capture_primary, which grabs the LIVE screen right now (sensitive, zeroizing, never-persist) — this reads an already-recorded file back off disk.

Relationship to infrastructure-screen-capture

Deliberately a SEPARATE crate: screen-capture's contract is sensitive, transient, zeroizing, never-persist (DLP stills); this crate's whole purpose is a persistent audiovisual artifact. A feature flag would blur that boundary (Gate 1.5, both AIs concurring).

Platforms

v1 implements Linux/X11. The recorder::CaptureBackend and window::WindowBackend seams are cross-platform by design; Windows (ddagrab + the windows crate behind cfg(windows)) and macOS (avfoundation + osascript, Screen-Recording TCC permission — same one infrastructure-screen-capture documents) are additive later work. Unsupported platforms fail with typed errors — never a silent no-op. Wayland has no global window control by design: recording fails fast, window operations report window::Outcome::Skipped loudly.

Capabilities

Caption

Burn timed text captions onto a finished recording, via ffmpeg drawtext.

Item
pub struct Caption
Caption :: fn new(start_secs : f64, duration_secs : f64, text : impl Into <String>) -> Self
fn build_drawtext_filter(captions : & Caption) -> String
fn burn_captions(input : & Path, captions : & Caption, output : & Path, ffmpeg_bin : & str,) -> Result <(), CaptionError>

CaptionError

Burn timed text captions onto a finished recording, via ffmpeg drawtext.

Item
pub enum CaptionError

recorder (other)

ffmpeg-subprocess screen recording with crash-tolerant finalization.

Item
fn capture_args(config : & RecorderConfig, mkv_part : & Path,) -> Result <Vec <String>, RecorderError>
fn remux_args(mkv : & Path, mp4_part : & Path) -> Vec <String>
fn validate_config(config : & RecorderConfig) -> Result <(), RecorderError>
fn x11_session_available(display : Option <& str>, xdg_session_type : Option <& str>,) -> Result <(), String>

CaptureBackend

ffmpeg-subprocess screen recording with crash-tolerant finalization.

Item
pub enum CaptureBackend

Recorder

ffmpeg-subprocess screen recording with crash-tolerant finalization.

Item
pub struct Recorder
Recorder :: fn start(config : RecorderConfig) -> Result <Self, RecorderError>
Recorder :: fn pause(& mut self) -> Result <(), RecorderError>
Recorder :: fn resume(& mut self) -> Result <(), RecorderError>
Recorder :: fn is_paused(& self) -> bool
Recorder :: fn finish(mut self) -> Result <PathBuf, RecorderError>
Recorder :: fn drop(& mut self)

RecorderConfig

ffmpeg-subprocess screen recording with crash-tolerant finalization.

Item
pub struct RecorderConfig
RecorderConfig :: fn x11_default(output : impl Into <PathBuf>) -> Self

RecorderError

ffmpeg-subprocess screen recording with crash-tolerant finalization.

Item
pub enum RecorderError

Demo

The typed demo phase-runner: narration, health-waits, keep-focus

Item
pub struct Demo
Demo :: fn new(name : impl Into <String>) -> Self
Demo :: fn phase(& mut self, name : impl Into <String>)
Demo :: fn narrate(& mut self, line : impl Into <String>)
Demo :: fn wait_ready(& mut self, label : & str, mut probe : impl Probe, timeout : Duration, interval : Duration,) -> Result <(), WaitError>
Demo :: fn monitor_while(& mut self, label : & str, mut alive : impl FnMut() -> bool, interval : Duration, mut tick : impl FnMut(& mut Demo),) -> u32
Demo :: fn records(& self) -> & StepRecord
Demo :: fn dropped_records(& self) -> usize

F

The typed demo phase-runner: narration, health-waits, keep-focus

Item
F :: fn check(& mut self) -> Result <Readiness, ProbeError>

Probe

The typed demo phase-runner: narration, health-waits, keep-focus

Item
pub trait Probe

ProbeError

The typed demo phase-runner: narration, health-waits, keep-focus

Item
pub struct ProbeError
ProbeError :: fn new(message : impl Into <String>) -> Self

Readiness

The typed demo phase-runner: narration, health-waits, keep-focus

Item
pub enum Readiness

StepRecord

The typed demo phase-runner: narration, health-waits, keep-focus

Item
pub struct StepRecord

WaitError

The typed demo phase-runner: narration, health-waits, keep-focus

Item
pub enum WaitError

verify (other)

Post-remux integrity verification: does a finished recording actually

Item
fn verify_playable(path : & Path, ffmpeg_bin : & str, ffprobe_bin : & str,) -> Result <VerifyReport, VerifyError>

VerifyError

Post-remux integrity verification: does a finished recording actually

Item
pub enum VerifyError
fn extract_frame(path : & Path, ffmpeg_bin : & str, at_secs : f64, output_png : & Path,) -> Result <(), VerifyError>

VerifyReport

Post-remux integrity verification: does a finished recording actually

Item
pub struct VerifyReport

window (other)

Real-window orchestration: find, move/resize, focus.

Item
fn match_window <'a>(windows : & 'a WindowInfo, matcher : & Matcher, pick : Pick,) -> Option <& 'a WindowInfo>
fn binary_on_path(bin : & str) -> bool

Geometry

Real-window orchestration: find, move/resize, focus.

Item
pub struct Geometry
fn split_layout(screen_w : u32, screen_h : u32, left_w : u32) ->(Geometry, Geometry)

Matcher

Real-window orchestration: find, move/resize, focus.

Item
pub enum Matcher

Outcome

Real-window orchestration: find, move/resize, focus.

Item
pub enum Outcome

Pick

Real-window orchestration: find, move/resize, focus.

Item
pub enum Pick

WindowBackend

Real-window orchestration: find, move/resize, focus.

Item
pub trait WindowBackend

WindowControl

Real-window orchestration: find, move/resize, focus.

Item
pub struct WindowControl<B : WindowBackend>

WindowControl<B>

Real-window orchestration: find, move/resize, focus.

Item
WindowControl<B> :: fn new(backend : B) -> Self
WindowControl<B> :: fn position_and_focus(& self, matcher : & Matcher, pick : Pick, geo : Geometry) -> Outcome
WindowControl<B> :: fn focus(& self, matcher : & Matcher, pick : Pick) -> Outcome

WindowInfo

Real-window orchestration: find, move/resize, focus.

Item
pub struct WindowInfo
fn parse_wmctrl_lx(output : & str) -> Vec <WindowInfo>

X11Backend

Real-window orchestration: find, move/resize, focus.

Item
pub struct X11Backend
X11Backend :: fn default() -> Self
X11Backend :: fn list(& self) -> Result <Vec <WindowInfo>, String>
X11Backend :: fn move_resize(& self, id : & str, geo : Geometry) -> Result <(), String>
X11Backend :: fn focus(& self, id : & str) -> Result <(), String>

How to use it

No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.

Module structure

infrastructure_screencast

flowchart TD
  n_infrastructure_screencast["infrastructure_screencast"]
  n_infrastructure_screencast --> n_captions["captions"]
  n_infrastructure_screencast --> n_recorder["recorder"]
  n_infrastructure_screencast --> n_script["script"]
  n_infrastructure_screencast --> n_verify["verify"]
  n_infrastructure_screencast --> n_window["window"]

Public surface

`captions`

ItemWhat it is
pub struct CaptionOne on-screen caption, visible during [start_secs, start_secs + duration_secs).
Caption :: fn new(start_secs : f64, duration_secs : f64, text : impl Into <String>) -> Self
fn build_drawtext_filter(captions : & Caption) -> StringPure: build the ffmpeg -vf filter graph for captions
pub enum CaptionError
fn burn_captions(input : & Path, captions : & Caption, output : & Path, ffmpeg_bin : & str,) -> Result <(), CaptionError>Re-encode input with captions burned in, writing output

`recorder`

ItemWhat it is
pub enum CaptureBackendHow the screen is captured
pub struct RecorderConfigRecorder configuration
RecorderConfig :: fn x11_default(output : impl Into <PathBuf>) -> Self1920x1200 @15fps on :0.0 — the geometry the bash originals used.
pub enum RecorderErrorRecorder failures
fn capture_args(config : & RecorderConfig, mkv_part : & Path,) -> Result <Vec <String>, RecorderError>The ffmpeg capture argv for a backend (pure — unit-tested).
fn remux_args(mkv : & Path, mp4_part : & Path) -> Vec <String>The ffmpeg remux argv (pure — unit-tested)
fn validate_config(config : & RecorderConfig) -> Result <(), RecorderError>Validate a config before spawning anything (pure — unit-tested).
fn x11_session_available(display : Option <& str>, xdg_session_type : Option <& str>,) -> Result <(), String>Detect whether this session can host the X11 backend (pure — unit-tested)
pub struct RecorderA running screen recording
Recorder :: fn start(config : RecorderConfig) -> Result <Self, RecorderError>Start recording
Recorder :: fn pause(& mut self) -> Result <(), RecorderError>Suspend the capture process (SIGSTOP)
Recorder :: fn resume(& mut self) -> Result <(), RecorderError>Continue a paused capture (SIGCONT)
Recorder :: fn is_paused(& self) -> boolWhether the capture process is currently suspended via Self::pause.
Recorder :: fn finish(mut self) -> Result <PathBuf, RecorderError>Stop gracefully and finalize to mp4
Recorder :: fn drop(& mut self)

`script`

ItemWhat it is
pub enum ReadinessWhat a probe observed.
pub struct ProbeErrorA probe failed outright (transport error, bad response…)
ProbeError :: fn new(message : impl Into <String>) -> Self
pub trait ProbeReadiness seam
F :: fn check(& mut self) -> Result <Readiness, ProbeError>
pub enum WaitErrorWaiting on a probe failed.
pub struct StepRecordOne narrated step, kept in memory
pub struct Demo
Demo :: fn new(name : impl Into <String>) -> Self
Demo :: fn phase(& mut self, name : impl Into <String>)Start a named phase (the ═══ Phase N ═══ banners of the originals).
Demo :: fn narrate(& mut self, line : impl Into <String>)Print + record one timestamped narration line.
Demo :: fn wait_ready(& mut self, label : & str, mut probe : impl Probe, timeout : Duration, interval : Duration,) -> Result <(), WaitError>Poll probe every interval until Ready or timeout
Demo :: fn monitor_while(& mut self, label : & str, mut alive : impl FnMut() -> bool, interval : Duration, mut tick : impl FnMut(& mut Demo),) -> u32Run tick every interval while alive() — the keep-focus-while- the-browser-works choreography of the originals
Demo :: fn records(& self) -> & StepRecordThe in-memory step log (caller persists if wanted).
Demo :: fn dropped_records(& self) -> usizeRecords dropped past the cap — nonzero means the log is TRUNCATED.

`verify`

ItemWhat it is
pub struct VerifyReportWhat a clean pass over the file found
pub enum VerifyError
fn verify_playable(path : & Path, ffmpeg_bin : & str, ffprobe_bin : & str,) -> Result <VerifyReport, VerifyError>Verify path decodes cleanly end-to-end
fn extract_frame(path : & Path, ffmpeg_bin : & str, at_secs : f64, output_png : & Path,) -> Result <(), VerifyError>Extract one frame as a PNG for visual confirmation — the same "don't just trust the exit code, look at it" instinct as taking a browser screenshot after a tools-browser-test round trip

`window`

ItemWhat it is
pub struct WindowInfoOne window as reported by wmctrl -lx.
pub enum MatcherHow to select a window.
pub enum PickWhich match wins when several windows qualify.
pub struct GeometryWindow geometry, in pixels.
pub enum OutcomeWhat happened to a window operation
fn parse_wmctrl_lx(output : & str) -> Vec <WindowInfo>Parse wmctrl -lx output (pure — unit-tested)
fn match_window <'a>(windows : & 'a WindowInfo, matcher : & Matcher, pick : Pick,) -> Option <& 'a WindowInfo>Select a window (pure — unit-tested).
fn split_layout(screen_w : u32, screen_h : u32, left_w : u32) ->(Geometry, Geometry)The bash originals' layout: a fixed-width column on the left (terminal), the remainder on the right (browser)
pub trait WindowBackendPlatform seam for window control
pub struct X11BackendX11 backend: wmctrl first, xdotool fallback — both as strict argv subprocesses with configurable (allowlist-by-config) binaries.
X11Backend :: fn default() -> Self
X11Backend :: fn list(& self) -> Result <Vec <WindowInfo>, String>
X11Backend :: fn move_resize(& self, id : & str, geo : Geometry) -> Result <(), String>
X11Backend :: fn focus(& self, id : & str) -> Result <(), String>
pub struct WindowControl<B : WindowBackend>High-level convenience over a backend: find-then-act, degrading to Outcome::Skipped instead of failing the demo.
WindowControl<B> :: fn new(backend : B) -> Self
WindowControl<B> :: fn position_and_focus(& self, matcher : & Matcher, pick : Pick, geo : Geometry) -> OutcomeMove/resize the matched window and bring it to the front.
WindowControl<B> :: fn focus(& self, matcher : & Matcher, pick : Pick) -> OutcomeBring the matched window to the front (the keep-focus choreography).
fn binary_on_path(bin : & str) -> boolA binary is plausibly present (used for preflight narration, not gating — the real error surfaces on first use).

Re-exports. Exported here, defined elsewhere.

ExportDefined in
{CaptureBackend,Recorder,RecorderConfig,RecorderError}recorder::{CaptureBackend,Recorder,RecorderConfig,RecorderError}
{Demo,Probe,ProbeError,Readiness,StepRecord}script::{Demo,Probe,ProbeError,Readiness,StepRecord}
{Geometry,Matcher,Outcome,Pick,WindowBackend,WindowControl,WindowInfo,X11Backend,}window::{Geometry,Matcher,Outcome,Pick,WindowBackend,WindowControl,WindowInfo,X11Backend,}
{build_drawtext_filter,burn_captions,Caption,CaptionError}captions::{build_drawtext_filter,burn_captions,Caption,CaptionError}
{extract_frame,verify_playable,VerifyError,VerifyReport}verify::{extract_frame,verify_playable,VerifyError,VerifyReport}

Boundary

Depends on no other workspace tier.

Shares tier infrastructure with 82 other crates: infrastructure-acquire, infrastructure-adapters-google-calendar, infrastructure-adapters-google-gmail, infrastructure-adapters-google-places, infrastructure-adapters-google-trends, infrastructure-adapters-shodan, infrastructure-adapters-yelp, infrastructure-agent, … (82 total).

_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._

Where it sits

Tier (ontology)infrastructure
Architectural role (taxonomy)unclassified (baselined)
Locationcrates/infrastructure/screencast
Vocabulary in force (lexicon)current

Dependencies

Runtime, from outside the workspace.

CrateRequirementFeaturesOptionalOnly on
chrono^0.4serdenoalways
libc^0.2nocfg(unix)
serde^1derivenoalways
serde_json^1noalways
thiserror^2noalways

Development. None.

Build. None.

Depended on by. 5 workspace crates.

Signal flow — what reaches this crate, and what it reaches.

flowchart LR
  n_tools_demo_buildsession["tools-demo-buildsession"] -->|uses| SELF
  n_tools_demo_shopping["tools-demo-shopping"] -->|uses| SELF
  n_tools_demo_systats["tools-demo-systats"] -->|uses| SELF
  n_tools_gui_harness["tools-gui-harness"] -->|uses| SELF
  n_tools_screen_record["tools-screen-record"] -->|uses| SELF
  SELF["infrastructure-screencast"]
  classDef self fill:#1f883d,stroke:#1f883d,color:#fff;
  class SELF self;

Feature flags

No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.

Targets

KindNameSource
libinfrastructure_screencast`src/lib.rs`

Error model

Error typeNamed by
CaptionErrorburn_captions
ProbeErrordeclared, no public signature returns it
RecorderErrorcapture_args, validate_config
VerifyErrorextract_frame, verify_playable
WaitErrordeclared, no public signature returns it

Operational characteristics

PropertyEvidence
async public surfacenone detected
async runtimenone detected
database accessnone detected
network I/Onone detected
unsafe codeyes
environment variablesyes

Unsafe code.

SiteSource
unsafe blocksrc/recorder.rs

Configuration

VariableRead in
CARGO_MANIFEST_DIRsrc/recorder.rs
DISPLAYsrc/recorder.rs
PATHsrc/window.rs
XDG_SESSION_TYPEsrc/recorder.rs

5 workspace crates depend on this one: tools-demo-buildsession, tools-demo-shopping, tools-demo-systats, tools-gui-harness, tools-screen-record.

Verification

KindCount
Unit tests36
Integration tests0
Examples0
Doctests0

Evidence by module. How often each public module is named by something executable.

ModuleTestsExamplesConsumers
captions403
recorder8011
script603
verify402
window12026

What the tests establish, by name:

Documentation coverage

MeasureDocumentedTotal
Public items with rustdoc4558
Public modules with a //! block55
pie showData
    title Public items with rustdoc
    "Documented" : 45
    "No rustdoc detected" : 13

Metrics

MetricValue
Rust source files6
Source lines2008
Code lines1544
Public API items58
Public modules5
Tests36
Examples0
Cargo features0
Direct runtime dependencies5
Workspace reverse dependencies5
pie showData
    title Public API by kind
    "enum" : 9
    "function" : 12
    "method" : 24
    "struct" : 11
    "trait" : 2
pie showData
    title Rust source composition
    "Code" : 1544
    "Blank or comment" : 464

Generation

Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.

All infrastructure · Manual