tests/testthat/helper-timeout-sweep.R

# M70: the domain the silence rule quantifies over, COMPUTED rather than
# recalled.
#
# M69 promised a partition of the package's timeout behavior and wrote it by
# hand. Three review passes each found one more member the list omitted, and the
# third return's own finding named `remove_audio()` -- a function this package
# does not export (D048). A promise whose domain is fixed by what the author
# recalled is not repaired by recalling harder, so the domain is derived here
# from the package's own call graph.
#
# The route is M59's: `mget(ls(asNamespace("tidymedia"), all.names = TRUE))`
# reads the INSTALLED namespace, so this runs under `R CMD check` as well as
# `devtools::test()` -- a sweep that reads the source tree finds nothing in
# exactly the run the release gate uses (LESSONS M51/M59).

# The spawn primitives. `run_program()` is deliberately NOT listed: it is a
# package function whose own body names `system2()`, so the closure below picks
# it up. Listing it would make the sweep partly a hand-list again.
tm_spawn_primitives <- c("system", "system2")

# R's own condition API -- the handler set, recorded here rather than inferred.
# A function that installs none of these cannot stop a condition raised beneath
# it: it propagates by R's semantics. That is the stated reason such a function
# is out of the absorber partition below, and it is the reason the partition is
# explanatory rather than the domain itself (see `tm_timeout_domain()`).
tm_condition_api <- c(
  "tryCatch", "withCallingHandlers", "try", "try_fetch",
  "suppressWarnings", "suppressMessages"
)

# tm_symbol_graph(): name -> every symbol its body mentions.
#
# NOT M62's `tm_call_graph()` (`helper-input-paths.R`), and the difference is
# load-bearing rather than a second copy of one graph (M40). That one records
# the HEAD of each call, which is right for its question and wrong for this one:
# `probe_all()` reaches a spawn only through `purrr::map(infile, probe_one)`,
# where `probe_one` is an argument and never a head, so a call-head walk drops
# `probe_all()` and the four `probe_*()` accessors and `verify_media()` out of
# the domain entirely.
#
# The two guards therefore err in OPPOSITE directions on purpose. M62's asks
# which verbs must carry a check, so a spurious member there is a false alarm.
# This one asks where a hang can hide, so a missing member is a silent hang --
# the exact failure M69 shipped three times. Mentioning a name it does not call
# costs one test cell; not mentioning one it does costs the promise.
#
# `all.names()` on the language object rather than a regex over `deparse()`:
# both read the same object, but the parser already knows which tokens are
# names, so a program name inside a STRING ("run_program failed") cannot fake
# an edge.
tm_symbol_graph <- function() {
  ns <- asNamespace("tidymedia")
  objs <- mget(ls(ns, all.names = TRUE), envir = ns, ifnotfound = list(NULL))
  fns <- objs[vapply(objs, is.function, logical(1))]
  lapply(fns, function(f) unique(all.names(body(f))))
}

# tm_spawn_sites(): the namespace functions that name a spawn primitive in
# their OWN body -- the seed set `tm_reaches_spawn()` grows its closure from.
#
# Named rather than left inline (M070 wrote it as the first statement of
# `tm_reaches_spawn()`, which returns only the closure) because M072 asks a
# question about the seeds themselves: a per-call limit has to arrive at every
# place a process is actually started, and that is this set, not the closure
# around it. `seeds` stays a parameter for the same reason it is one below.
tm_spawn_sites <- function(graph = tm_symbol_graph(),
                           seeds = tm_spawn_primitives) {
  sort(names(graph)[
    vapply(graph, function(x) any(seeds %in% x), logical(1))
  ])
}

# tm_reaches_spawn(): every namespace function from which a spawn primitive is
# reachable. `seeds` is a parameter so a test can vary it -- passing an empty
# seed set must collapse the result, which is what proves membership comes from
# the closure and not from the recorded list.
tm_reaches_spawn <- function(graph = tm_symbol_graph(),
                             seeds = tm_spawn_primitives) {
  reaches <- tm_spawn_sites(graph, seeds)
  repeat {
    grown <- union(reaches, names(graph)[
      vapply(graph, function(x) any(reaches %in% x), logical(1))
    ])
    if (setequal(grown, reaches)) break
    reaches <- grown
  }
  sort(reaches)
}

# tm_timeout_domain(): the exported functions the silence rule binds.
#
# EXPORTED, because "a condition the caller can see" is a claim about the
# caller, and the only caller a package can promise anything to is the one
# outside it. An internal helper legitimately hands its outcome up by return
# value -- `probe_one()` returns the absorbed-timeout sentinel and `probe_all()`
# turns it into the warning -- so a rule quantified over internals would forbid
# the package's own working design.
#
# NO handler filter is applied here, and that is deliberate. Every spawn goes
# through `run_program()`, which installs `guard_timeout()`'s handler, so
# "absorption is possible somewhere below" is true of every member and filtering
# on it excludes nothing. `tm_timeout_absorbers()` keeps the partition as the
# explanation it is.
tm_timeout_domain <- function() {
  sort(intersect(getNamespaceExports("tidymedia"), tm_reaches_spawn()))
}

# tm_timeout_absorbers(): the reaching functions that install a handler in their
# OWN body -- the ones that can turn a timeout into a return value instead of
# letting it through. Explanatory, not the domain: it is what a reader consults
# to see where a silence could come from.
tm_timeout_absorbers <- function(graph = tm_symbol_graph()) {
  reaches <- tm_reaches_spawn(graph)
  installs <- names(graph)[
    vapply(graph, function(x) any(tm_condition_api %in% x), logical(1))
  ]
  sort(intersect(reaches, installs))
}

# tm_timeout_recorded_domain(): membership as it stands, recorded so drift is
# visible. This is NOT the domain -- `tm_timeout_domain()` computes that -- and
# the difference is the whole point of M70: an export that starts reaching a
# spawn joins the computed set on its own, and this list reddens until someone
# looks at it.
tm_timeout_recorded_domain <- function() {
  sort(c(
    "anonymize_video", "anonymize_video_batch", "compare_videos",
    "compare_videos_batch", "concatenate_videos", "concatenate_videos_batch",
    "convert_audio", "convert_audio_batch", "crop_video", "crop_video_batch",
    "extract_audio", "extract_audio_batch", "extract_frame",
    "extract_frame_batch", "ffm_batch", "ffm_run", "ffmpeg", "ffmpeg_codecs",
    "ffmpeg_encoders", "ffprobe", "format_for_web", "format_for_web_batch",
    "get_duration", "get_frame_rate", "get_height", "get_sample_rate",
    # has_encoder() joined at M138: it reads the limit above its own location
    # question, and reaches a spawn through cached_encoder_names().
    "get_width", "has_encoder", "has_hardware_encoder", "mediainfo",
    "mediainfo_parameter",
    "mediainfo_query", "mediainfo_template",
    "normalize_audio", "normalize_audio_batch", "picture_in_picture",
    "picture_in_picture_batch", "probe_all", "probe_audio", "probe_container",
    "probe_streams", "probe_video", "program_status", "sample_frames",
    "sample_frames_batch",
    "segment_video", "segment_video_batch", "separate_audio_video",
    "separate_audio_video_batch", "standardize_video",
    "standardize_video_batch", "strip_metadata", "strip_metadata_batch",
    "verify_media"
  ))
}

# tm_timeout_post_baseline(): domain members that did not exist at the ref the
# three recorded censuses were measured at (`tm_timeout_valid_baseline_ref`).
#
# Two censuses -- `tm_timeout_blame_master()` and the committed
# `timeout-valid-baseline.rds` -- say what a member did BEFORE M094 changed who
# is blamed, and their membership is a written list. An export added afterwards
# has no such
# reading and cannot be given one: measuring it today and filing it under
# "measured at ae5ff1c" would be composing a history rather than recording one.
# So it is excluded from those two membership comparisons, by name, here.
# `tm_timeout_reached_master()` once needed no exclusion, being computed from the
# live domain; since M138 it carries its own dated post-baseline vectors instead,
# so a member added later is measured there rather than excluded here.
#
# Exclusion is not exemption. Every name below is still swept by the live rules
# -- it blames itself for an invalid limit, and a reached limit still reaches
# the caller -- and `tm_timeout_baseline_domain()` refuses an exclusion that is
# hiding a member the record does have a cell for.
#
# `program_status()` entered the domain at M113: it probes each program's
# version, which spawns.
tm_timeout_post_baseline <- function() c("has_encoder", "program_status")

# tm_timeout_baseline_domain(): the domain the recorded censuses can be compared
# against, with the exclusion guarded rather than trusted.
tm_timeout_baseline_domain <- function() {
  post <- tm_timeout_post_baseline()
  domain <- tm_timeout_domain()
  # An excluded name that is not in the live domain excludes nothing and has
  # been left behind; one the record DOES hold a cell for is being hidden.
  stopifnot(all(post %in% domain))
  stopifnot(!any(post %in% names(tm_timeout_blame_master())))
  setdiff(domain, post)
}

# tm_timeout_call_specs(): one valid argument set per domain member, so a forced
# timeout can be driven THROUGH each of them.
#
# This is a hand-written table, and it is bounded by a procedure anyway: the
# domain comes from the sweep, and a member with no cell here fails
# `test-timeout-silence.R` rather than being quietly skipped. That is the
# property M69's hand-list did not have.
#
# The files are empty placeholders. Nothing reads them: every spawn is
# intercepted before a binary sees the path, and the verbs only need the paths
# to exist for their own `check_file_exists()`.
tm_timeout_call_specs <- function(dir) {
  vid <- file.path(dir, "in.mp4")
  vid2 <- file.path(dir, "in2.mp4")
  for (f in c(vid, vid2)) if (!file.exists(f)) file.create(f)
  outv <- file.path(dir, "out.mp4")
  outa <- file.path(dir, "out.m4a")
  png <- file.path(dir, "out.png")
  regions <- tibble::tibble(x = 0, y = 0, width = 10, height = 10)

  jobs_v <- tibble::tibble(input = vid, output = outv)
  jobs_a <- tibble::tibble(input = vid, output = outa)
  jobs_multi <- tibble::tibble(inputs = list(c(vid, vid2)), output = outv)

  list(
    anonymize_video = list(infile = vid, outfile = outv, regions = regions),
    anonymize_video_batch =
      list(jobs = tibble::tibble(input = vid, output = outv,
                                 regions = list(regions))),
    compare_videos = list(infiles = c(vid, vid2), outfile = outv),
    compare_videos_batch = list(jobs = jobs_multi),
    concatenate_videos = list(infiles = c(vid, vid2), outfile = outv),
    concatenate_videos_batch = list(jobs = jobs_multi),
    convert_audio = list(infile = vid, outfile = outa),
    convert_audio_batch = list(jobs = jobs_a),
    crop_video = list(infile = vid, outfile = outv, width = 10, height = 10),
    crop_video_batch = list(jobs = jobs_v, width = 10, height = 10),
    extract_audio = list(infile = vid, outfile = outa),
    extract_audio_batch = list(jobs = jobs_a),
    extract_frame = list(infile = vid, outfile = png, timestamp = 1),
    extract_frame_batch =
      list(jobs = tibble::tibble(input = vid, output = png, timestamp = 1)),
    ffm_batch = list(jobs = jobs_v,
                     .f = function(input, output, ...) ffm_files(input, output)),
    ffm_run = list(object = ffm_files(vid, outv)),
    ffmpeg = list(command = "-version"),
    ffmpeg_codecs = list(),
    ffmpeg_encoders = list(),
    ffprobe = list(command = "-version"),
    # program_status() takes no arguments: it probes all four programs by name.
    program_status = list(),
    format_for_web = list(infile = vid, outfile = outv),
    format_for_web_batch = list(jobs = jobs_v),
    get_duration = list(file = vid),
    get_frame_rate = list(file = vid),
    get_height = list(file = vid),
    get_sample_rate = list(file = vid),
    get_width = list(file = vid),
    # has_encoder() takes one encoder name and reads the limit above the
    # location question, so it joins the domain on every machine (M138).
    has_encoder = list(name = "libx264"),
    has_hardware_encoder = list(hardware = "nvenc"),
    mediainfo = list(command = "--Version"),
    mediainfo_parameter =
      list(file = vid, section = "General", parameter = "Duration"),
    mediainfo_query =
      list(file = vid, section = "General", parameters = "Duration"),
    mediainfo_template = list(file = vid),
    normalize_audio = list(infile = vid, outfile = outa),
    normalize_audio_batch = list(jobs = jobs_a),
    picture_in_picture = list(main = vid, overlay = vid2, outfile = outv),
    picture_in_picture_batch =
      list(jobs = tibble::tibble(main = vid, overlay = vid2, output = outv)),
    probe_all = list(infile = vid),
    probe_audio = list(infile = vid),
    probe_container = list(infile = vid),
    probe_streams = list(infile = vid),
    probe_video = list(infile = vid),
    sample_frames = list(infile = vid, outdir = dir, fps = 1),
    sample_frames_batch =
      list(jobs = tibble::tibble(input = vid, outdir = dir), fps = 1),
    segment_video = list(infile = vid, start = 0, end = 1, outfiles = outv),
    segment_video_batch =
      list(jobs = tibble::tibble(input = vid, start = 0, end = 1,
                                 output = outv)),
    separate_audio_video =
      list(infile = vid, audiofile = outa, videofile = outv),
    separate_audio_video_batch =
      list(jobs = tibble::tibble(input = vid, audiofile = outa,
                                 videofile = outv)),
    standardize_video = list(infile = vid, outfile = outv),
    standardize_video_batch = list(jobs = jobs_v),
    strip_metadata = list(infile = vid, outfile = outv),
    strip_metadata_batch = list(jobs = jobs_v),
    verify_media = list(file = vid, width = 320)
  )
}

# tm_force_timeout(): drive a forced timeout through one domain member and
# report every condition it signalled.
#
# The timeout is INJECTED at the package's two spawn wrappers -- `run_program()`
# and `guard_timeout()`, which between them stand in front of every
# `system()`/`system2()` call the package makes -- rather than produced by a
# real hung binary. The injected object is `abort_timeout()`'s own, the same
# condition the kernel path raises, and the writer-less-FIFO tests at the end of
# this file are what tie that object to what a hung binary really produces. A
# FIFO per member would cost ~42 s each (base R's SIGINT/SIGTERM/SIGKILL ladder,
# M69/D047) and could not reach the members that take no file argument at all.
#
# Injecting at the wrappers rather than at `is_timeout()` is what keeps this
# platform-independent: forcing the VERDICT would still let the three Layer 0
# hatches really shell out, and on a machine with no media binaries
# `system(intern = TRUE)` raises a cmdError on "command not found" before the
# verdict is ever consulted -- so those cells would measure the runner's PATH.
# CI's macOS and Windows runners install no media binaries at all.
#
# Resolution is intercepted for the same reason, at `find_program()`, which is
# the one place the package turns a program name into a path. Injecting at the
# spawn wrappers alone leaves a member that decides WHETHER to spawn by asking
# the machine what it has: `program_status()` probes a program's version only
# for a program it resolved, so on a runner with no binaries it spawns nothing,
# reaches no limit, and reads as silent. That is what CI's macOS and Windows
# legs reported at M113 -- both red on this grid and on the blame sweep with
# `program_status absorbed the timeout silently: no condition at all`,
# reproduced locally under `PATH=""` with both config dirs redirected empty.
# The mocked resolver hands back a path under a directory that does not exist;
# nothing on this path reads the file, because every spawn is already
# intercepted, so the only thing the path has to be is non-empty.
#
# What each cell therefore asks is exactly AC1's question: given a timeout
# signalled beneath it, does this function let the caller see it? Whether the
# wrappers themselves detect a real kill is M69's question, answered by
# `is_timeout()`'s own tests and by the FIFO anchors.
tm_force_timeout <- function(name, args, limit = 2) {
  warns <- character()
  err <- NULL
  withr::with_options(list(tidymedia.timeout = limit), {
    testthat::local_mocked_bindings(
      find_program = function(program = "ffmpeg", ...) {
        file.path("/nonexistent", program[[1]])
      },
      run_program = function(location, args, program = "the program", ...) {
        abort_timeout(program, limit)
      },
      guard_timeout = function(program, limit, expr, ...) {
        abort_timeout(program, limit)
      },
      .package = "tidymedia"
    )
    withCallingHandlers(
      tryCatch(
        do.call(name, args, envir = asNamespace("tidymedia")),
        error = function(e) err <<- e
      ),
      warning = function(w) {
        warns <<- c(warns, cli::ansi_strip(conditionMessage(w)))
        invokeRestart("muffleWarning")
      }
    )
  })
  list(
    error = err,
    aborted = inherits(err, "tidymedia_timeout"),
    warned = any(grepl("timed out", warns, fixed = TRUE)),
    warnings = warns
  )
}

# local_blocking_input(): a FIFO nobody writes to, so a media program blocks on
# its header forever and the test does not race the machine's encoding speed.
#
# Defined here rather than in M69's test file because two suites now anchor
# against it: M69's execution tests and M70's grid, which needs the REAL hang to
# tie its injected condition to what a binary actually produces. A second copy
# is how the two stop agreeing (M40). The reasoning behind the fixture and its
# skips is in test-runtime-timeout.R, above the tests that use it.
#
# Windows has no mkfifo, so the gate skips there. The fixture is built INSIDE
# the gate -- a platform that cannot create it must not reach the creation
# call -- and the gate skips rather than fail()s, because testthat::fail()
# RECORDS a failure and RETURNS, falling on into the operation it guards (M68).
local_blocking_input <- function(env = parent.frame()) {
  skip_on_os("windows")
  skip_on_cran()
  skip_if_no_ffmpeg()
  path <- withr::local_tempfile(fileext = ".mp4", .local_envir = env)
  ok <- suppressWarnings(system2("mkfifo", shQuote(path)))
  if (!identical(as.integer(ok), 0L) || !file.exists(path)) {
    skip("could not create a FIFO to block on")
  }
  path
}

# tm_program_literals(): every program name a timeout refusal can be built from.
#
# AC4 asks that one literal names each program across every path that can abort
# about it. The receiving functions are named because they are the ones that
# BUILD a refusal -- run_program()/guard_timeout()/abort_timeout() take the name
# straight into the message, and capture_version() hands it to run_program().
# The alternative, sweeping every "ffmpeg"-ish string in the namespace, would
# rope in find_program("ffmpeg") and set_program("mediainfo"), where the
# lowercase form is the binary's real name and not a display literal.
#
# `fns` is a parameter so the mutation probe can feed a mutant body in: a guard
# whose only falsifier is deleting it re-certifies the mock rather than the
# code, which is the defect this task exists to fix.
tm_program_naming_calls <- c("run_program", "guard_timeout", "abort_timeout",
                             "capture_version")

tm_program_literals <- function(fns = NULL) {
  if (is.null(fns)) {
    ns <- asNamespace("tidymedia")
    objs <- mget(ls(ns, all.names = TRUE), envir = ns, ifnotfound = list(NULL))
    fns <- objs[vapply(objs, is.function, logical(1))]
  }
  out <- list()
  walk <- function(e, where) {
    if (is.call(e)) {
      head <- e[[1]]
      if (is.name(head) && as.character(head) %in% tm_program_naming_calls) {
        lit <- tm_program_arg(e, as.character(head))
        if (!is.null(lit)) {
          out[[length(out) + 1L]] <<- stats::setNames(lit, where)
        }
      }
    }
    if (is.call(e) || is.pairlist(e)) {
      for (i in seq_along(e)) {
        if (rlang::is_missing(e[[i]]) || is.null(e[[i]])) next
        walk(e[[i]], where)
      }
    }
  }
  for (nm in names(fns)) walk(body(fns[[nm]]), nm)
  unlist(out)
}

# The program name's position differs by callee, and three of the four take it
# positionally at their own spawn sites -- guard_timeout("FFmpeg", limit, ...)
# in the Layer 0 hatches especially. A named argument wins where one is given;
# otherwise the k-th argument that carries no name is read, which is what
# R itself would match.
tm_program_positions <- list(
  run_program = list(name = "program", pos = 3L),
  guard_timeout = list(name = "program", pos = 1L),
  abort_timeout = list(name = "program", pos = 1L),
  capture_version = list(name = "name", pos = 2L)
)

tm_program_arg <- function(e, callee) {
  spec <- tm_program_positions[[callee]]
  args <- as.list(e)[-1]
  nms <- names(args)
  if (is.null(nms)) nms <- rep("", length(args))
  val <- if (spec$name %in% nms) {
    args[[which(nms == spec$name)[[1]]]]
  } else {
    unnamed <- which(!nzchar(nms))
    if (length(unnamed) >= spec$pos) args[[unnamed[[spec$pos]]]] else NULL
  }
  if (is.character(val) && length(val) == 1L) val else NULL
}


# M094: what an invalid `tidymedia.timeout` did on master, recorded ----------

# tm_timeout_bad_forms(): the invalid values `resolve_timeout()`'s own comment
# names (R/timeout.R:19-26) -- the negative, the fractional, the missing, the
# string and the length-2 forms. Recorded here rather than at each test site so
# the sweep and the wording test quantify over the same set, and so a form
# added to the checker's comment has one place to join.
tm_timeout_bad_forms <- function() {
  list(
    negative = -1,
    fractional = 0.5,
    missing = NA,
    string = "2",
    length_two = c(1, 2)
  )
}

# tm_blame_head(): drive one domain member under `limit` and report the head of
# whatever condition it raised.
#
# The member is called BY NAME, for helper-blame.R's reason: do.call() on a
# function OBJECT records the anonymous object as the condition call and hides
# the blame target this sweep exists to watch. `"<none>"` is a real answer, not
# a failure -- AC1's `has_hardware_encoder()` carve-out is exactly that cell.
tm_blame_head <- function(name, args, limit) {
  withr::with_options(list(tidymedia.timeout = limit), {
    cnd <- tryCatch(
      do.call(name, args, envir = asNamespace("tidymedia")),
      error = function(e) e
    )
    if (!inherits(cnd, "error")) "<none>" else blamed_verb(cnd)
  })
}

# tm_timeout_blame_master(): the head of the condition each domain member raised
# on master under an invalid limit -- measured 2026-08-30 at ae5ff1c, identical
# across all five `tm_timeout_bad_forms()` values at every member.
#
# Recorded because AC1's claim is a CHANGE, and the repo held no referent for
# what the blame was before. Six members already named themselves; the other 46
# named a function the caller never typed, which is the defect. `tm_blame_head()`
# regenerates any cell of this table.
#
# The measured BLAME of each cell is frozen; the table's membership is not, and
# tracks the recorded domain -- M112 removed `mediainfo_summary` from it, which
# is why the count above reads 46 rather than the 47 measured at ae5ff1c. The
# frozen merge-base census that is never rewritten is
# `tm_corrupt_dropped_master()`, not this.
tm_timeout_blame_master <- function() {
  ffm_run_class <- c(
    "anonymize_video", "compare_videos", "concatenate_videos", "convert_audio",
    "crop_video", "extract_audio", "extract_frame", "format_for_web",
    "normalize_audio", "picture_in_picture", "sample_frames",
    "separate_audio_video", "standardize_video", "strip_metadata"
  )
  ffm_batch_class <- c(
    "anonymize_video_batch", "compare_videos_batch", "concatenate_videos_batch",
    "convert_audio_batch", "crop_video_batch", "extract_audio_batch",
    "extract_frame_batch", "format_for_web_batch", "normalize_audio_batch",
    "picture_in_picture_batch", "sample_frames_batch", "segment_video",
    "segment_video_batch", "separate_audio_video_batch",
    "standardize_video_batch", "strip_metadata_batch"
  )
  ffmpeg_class <- c("ffmpeg_codecs", "ffmpeg_encoders", "has_hardware_encoder")
  mediainfo_parameter_class <- c(
    "get_duration", "get_frame_rate", "get_height", "get_sample_rate",
    "get_width"
  )
  mediainfo_read_class <- c(
    "mediainfo_query", "mediainfo_template"
  )
  probe_map_class <- c(
    "probe_all", "probe_audio", "probe_container", "probe_streams",
    "probe_video", "verify_media"
  )
  # The six that already named themselves.
  correct <- c("ffm_batch", "ffm_run", "ffmpeg", "ffprobe", "mediainfo",
               "mediainfo_parameter")
  out <- c(
    stats::setNames(rep("ffm_run", length(ffm_run_class)), ffm_run_class),
    stats::setNames(rep("ffm_batch", length(ffm_batch_class)),
                    ffm_batch_class),
    stats::setNames(rep("ffmpeg", length(ffmpeg_class)), ffmpeg_class),
    stats::setNames(rep("mediainfo_parameter",
                        length(mediainfo_parameter_class)),
                    mediainfo_parameter_class),
    stats::setNames(rep("mediainfo_read", length(mediainfo_read_class)),
                    mediainfo_read_class),
    stats::setNames(rep("purrr::map", length(probe_map_class)),
                    probe_map_class),
    stats::setNames(correct, correct)
  )
  out[order(names(out))]
}

# tm_timeout_reached_master(): what each domain member did when the limit was
# actually REACHED -- "abort" or "warn" -- through `tm_force_timeout()`.
#
# D049 promises only that a reached limit is never SILENT, so the grid at
# test-timeout-silence.R records the disjunction and nothing finer. This table
# records which of the two each member does, so AC6 can say the blame sweep left
# that choice alone rather than only that it left some condition behind.
#
# TWO vectors, because a value carries the date it was measured on. The first
# was measured 2026-08-30 at ae5ff1c. A member that did not exist then cannot
# be filed under that measurement, and the default of the `ifelse()` below is
# "warn", so an unlisted new member would be compared against a value nobody
# ever took -- which is what happened to `has_encoder()` at M138, where the
# default read "warn" and the member aborts. The second vector is where a
# post-baseline member's own measurement goes, with its own date, so every
# member of the live domain is compared and each value says when it was taken.
tm_timeout_reached_master <- function() {
  aborts <- c(
    "anonymize_video", "compare_videos", "concatenate_videos", "convert_audio",
    "crop_video", "extract_audio", "extract_frame", "ffm_run", "ffmpeg",
    "ffmpeg_codecs", "ffmpeg_encoders", "ffprobe", "format_for_web",
    "has_hardware_encoder", "mediainfo", "normalize_audio", "picture_in_picture",
    "sample_frames", "separate_audio_video", "standardize_video",
    "strip_metadata", "verify_media"
  )
  # Measured 2026-10-10 at M138, each through tm_force_timeout(): has_encoder()
  # aborts, because it reaches the spawn through cached_encoder_names() and
  # ffmpeg_encoders() aborts on a reached limit, and program_status() warns,
  # which is what D088's absorber partition expects of it. Neither reading is
  # attributed to ae5ff1c: program_status() did not exist at that ref (M113
  # added it), and has_encoder() was added here.
  aborts_post <- c("has_encoder")
  warns_post <- c("program_status")
  dom <- tm_timeout_domain()
  # Both post vectors are load-bearing. `aborts_post` decides a value;
  # `warns_post` is what makes a MISSING measurement loud. Without this check
  # the `ifelse()` below would default a new domain member to "warn", which is
  # a value nobody took -- exactly how `has_encoder()` came to be compared
  # against a reading it does not match (M138 review).
  unrecorded <- setdiff(tm_timeout_post_baseline(), c(aborts_post, warns_post))
  if (length(unrecorded)) {
    stop(
      "no reached-limit measurement is recorded for: ",
      paste(unrecorded, collapse = ", "),
      ". Measure each through tm_force_timeout() and add it to aborts_post or ",
      "warns_post with the date.",
      call. = FALSE
    )
  }
  stats::setNames(
    ifelse(dom %in% c(aborts, aborts_post), "abort", "warn"),
    dom
  )
}

# M094: what the valid and unset paths did on master, recorded ---------------

# tm_spawn_interception_complete(): is mocking `guard_timeout()` enough to see
# every spawn the package makes?
#
# `tm_spawn_sites()` computes which namespace functions name `system`/`system2`
# in their own body. This asks the next question about that same computed set:
# at every one of them, is the spawn call syntactically an ARGUMENT of a
# `guard_timeout(...)` call? It is an argument, and `guard_timeout()` evaluates
# it lazily inside its own handler, so a mocked `guard_timeout()` that never
# forces `expr` intercepts the spawn rather than counting it after the fact.
#
# Without this the counter below would be trusted for a reason nobody checked:
# a new spawn added OUTSIDE the wrapper would be invisible to it and the AC5
# assertion would read "0 spawns" while a process ran.
#
# `fns` is a parameter for `tm_program_literals()`'s reason, a few lines below:
# a guard whose only falsifier is deleting it re-certifies nothing. Feeding it a
# mutant body that spawns outside the wrapper is what shows it can say FALSE.
tm_spawn_interception_complete <- function(graph = tm_symbol_graph(),
                                           fns = NULL) {
  if (is.null(fns)) {
    ns <- asNamespace("tidymedia")
    sites <- tm_spawn_sites(graph)
    fns <- stats::setNames(lapply(sites, get, envir = ns), sites)
  }
  all(vapply(fns, function(f) {
    ok <- TRUE
    walk <- function(e, guarded) {
      if (!is.call(e)) return(invisible(NULL))
      head <- e[[1]]
      if (is.name(head) && as.character(head) %in% tm_spawn_primitives &&
          !guarded) {
        ok <<- FALSE
      }
      is_guard <- is.name(head) && identical(as.character(head),
                                             "guard_timeout")
      for (i in seq_along(e)) {
        if (rlang::is_missing(e[[i]]) || is.null(e[[i]])) next
        # Every ARGUMENT of guard_timeout() is guarded, its head is not: R
        # binds arguments lazily, and the mock that stands in for the wrapper
        # returns without forcing any of them.
        walk(e[[i]], guarded || (is_guard && i > 1L))
      }
    }
    walk(body(f), FALSE)
    ok
  }, logical(1)))
}


# tm_scrub_paths(): make one digest comparable across sessions and checkouts.
#
# Two volatile paths reach a compiled command. The fixture directory is the
# caller's, passed in. The other is the session temp dir, where
# `concatenate_videos()` writes the `-f concat` list file under a name randomized
# per CALL -- so two runs of the same code in one session already differ there,
# and comparing that string would compare R's RNG rather than the package.
# tm_dir_pattern(): `dir` as a regex matching it with EITHER path separator at
# every position.
#
# On Windows one directory reaches the digest with a MIX of them -- R's
# tempdir() hands back forward slashes and the verbs concatenate Windows'
# backslashes onto it -- and str() prints each backslash doubled. A `fixed =
# TRUE` substitution of `dir` therefore matched nothing there, so every member's
# digest carried the runner's absolute path and no comparison against the
# recorded table could pass. It went unseen while an earlier failure in the same
# file stopped testthat before this comparison ran (M094 T13).
#
# Only the SEPARATORS are made flexible. The digest's own `\"` escapes are left
# alone, so a reading taken where the separator is already "/" is byte-identical
# to what it was -- which is why the fixture recorded on macOS stays valid.
tm_dir_pattern <- function(dir) {
  parts <- strsplit(gsub("\\\\", "/", dir), "/+")[[1]]
  escaped <- gsub("([][{}()+*^$|?.\\\\])", "\\\\\\1", parts)
  paste(escaped, collapse = "[/\\\\]+")
}

tm_scrub_paths <- function(x, dir) {
  x <- gsub(tm_dir_pattern(dir), "<dir>", x)
  x <- gsub(tm_dir_pattern(normalizePath(tempdir(), winslash = "/",
                                         mustWork = FALSE)), "<tmp>", x)
  x <- gsub(tm_dir_pattern(tempdir()), "<tmp>", x)
  x <- gsub("ffm-concat[0-9a-f]+(\\.txt)?", "<concat-list>", x)
  # The separator JOINING the two placeholders survives both substitutions, and
  # it is the platform's: `concatenate_videos()` builds this path with
  # file.path(), so Windows writes `<tmp>\<concat-list>` where POSIX writes
  # `<tmp>/<concat-list>`. That is the runner showing through a digest that is
  # meant to compare the package, so it is normalized like the separators inside
  # each path (M094 T13).
  gsub("<tmp>[/\\\\]+<concat-list>", "<tmp>/<concat-list>", x)
}

# tm_spawn_trace(): run one domain member under `limit` with every spawn
# intercepted, and report what came back and how many spawns it took.
#
# `guard_timeout()` is the single mock, which `tm_spawn_interception_complete()`
# above proves is enough. It returns a fixed empty-output vector rather than
# anything a real program would print: the question here is whether the two refs
# do the SAME thing, not what FFmpeg says, and a canned answer keeps the reading
# off the runner's PATH and its media binaries (M070's reason for injecting at
# the wrappers rather than at a real hang).
#
# The return value is reduced to a printed digest with the fixture directory
# scrubbed, so two refs measured in two checkouts compare as values rather than
# as two temp paths.
tm_spawn_trace <- function(name, args, limit, dir) {
  # The digest is printed output, so it has to be printed under the same
  # conventions everywhere. Without this the recorded table and the live reading
  # disagree on tibble's dimension glyph alone -- testthat runs with
  # `cli.unicode = FALSE` and a plain Rscript does not, so the same value prints
  # `1 x 5` in one and `1 <times> 5` in the other.
  testthat::local_reproducible_output()
  spawns <- 0L
  warns <- character()
  err <- NULL
  value <- NULL
  opts <- if (is.null(limit)) {
    list(tidymedia.timeout = NULL)
  } else {
    list(tidymedia.timeout = limit)
  }
  withr::with_options(opts, {
    testthat::local_mocked_bindings(
      guard_timeout = function(program, limit, expr, ...) {
        spawns <<- spawns + 1L
        character(0)
      },
      # The three locators are pinned too, so the reading is the package's and
      # not the runner's PATH: without this a machine with no media binaries
      # measures `Could not locate FFmpeg` for half the domain and the recorded
      # table stops being comparable anywhere else. CI's macOS and Windows
      # runners install no media binaries at all (M070's reason, same trap).
      find_ffmpeg = function() "/nonexistent/ffmpeg",
      find_ffprobe = function() "/nonexistent/ffprobe",
      find_mediainfo = function() "/nonexistent/mediainfo",
      .package = "tidymedia"
    )
    withCallingHandlers(
      tryCatch(
        value <- do.call(name, args, envir = asNamespace("tidymedia")),
        error = function(e) err <<- e
      ),
      warning = function(w) {
        warns <<- c(warns, cli::ansi_strip(conditionMessage(w)))
        invokeRestart("muffleWarning")
      }
    )
  })
  digest <- paste(
    utils::capture.output(utils::str(
      value, max.level = 2L, give.attr = FALSE,
      # str()'s 128-character default truncates a compiled FFmpeg command well
      # before its output path, which would leave the digest unable to tell two
      # different commands apart -- the dimension this comparison exists to
      # watch.
      nchar.max = 4000L, vec.len = 20L
    )),
    collapse = " | "
  )
  list(
    spawns = spawns,
    value = tm_scrub_paths(digest, dir),
    error = if (is.null(err)) NA_character_ else class(err)[[1]],
    warnings = length(warns)
  )
}

# tm_blame_condition(): the whole condition one domain member raised under
# `limit`, message and class vector both.
#
# Separate from `tm_blame_head()` because AC4 asks two things that
# conditionCall() cannot answer: whether every member says the SAME sentence,
# and whether the six FFprobe readers still arrive wrapped in purrr's indexed
# error. The message is ansi-stripped, and the caller pins `cli.width` -- cli
# wraps to the console, so an unpinned width compares two terminals.
tm_blame_condition <- function(name, args, limit) {
  # Same reason as tm_spawn_trace(): cli wraps and decorates to the console, and
  # this compares message text across 53 members and one reference.
  testthat::local_reproducible_output()
  withr::with_options(list(tidymedia.timeout = limit), {
    cnd <- tryCatch(
      do.call(name, args, envir = asNamespace("tidymedia")),
      error = function(e) e
    )
    if (!inherits(cnd, "error")) return(NULL)
    list(
      message = cli::ansi_strip(conditionMessage(cnd)),
      classes = class(cnd)
    )
  })
}

# tm_resolve_timeout_message(): the sentence the ONE checker site writes for a
# given invalid value, read from that site rather than from any verb.
#
# This is AC4's referent. Comparing the 53 members only to each other would go
# green on 53 copies of a second, drifted wording; comparing them to what
# `resolve_timeout()` itself produces is what pins them to the single
# `rlang::check_number_whole()` call in R/timeout.R.
tm_resolve_timeout_message <- function(limit) {
  testthat::local_reproducible_output()
  withr::with_options(list(tidymedia.timeout = limit), {
    cli::ansi_strip(
      tryCatch(resolve_timeout(), error = function(e) conditionMessage(e))
    )
  })
}

# tm_timeout_valid_baseline(): the recorded pre-change return values and spawn
# counts. Regenerate with data-raw/timeout-valid-baseline.R.
#
# The provenance the generator attaches is CHECKED here, not merely carried.
# Recording it and never reading it makes the reproducibility rule an attribute
# nobody consults: a blob regenerated from the wrong ref, or by hand, would keep
# comparing green against the wrong reading (M094 review F10). The ref is pinned
# to the sha this milestone branched from, so a re-record against a later master
# has to be a deliberate edit here.
tm_timeout_valid_baseline_ref <- "ae5ff1c"

tm_timeout_valid_baseline <- function() {
  table <- readRDS(testthat::test_path("fixtures", "timeout-valid-baseline.rds"))
  if (!tm_provenance_ok(table)) {
    testthat::fail(
      "the recorded baseline's provenance is missing or names another source"
    )
  }
  # The blob was recorded at ae5ff1c, where the availability predicate was
  # exported as `has_nvenc()`; the rename to `has_hardware_encoder()` changed
  # the export's name and nothing it spawns or returns, so the recorded cell is
  # read under the current name rather than re-recorded from a ref that cannot
  # name it. The blob itself stays as its generator wrote it. A re-recorded
  # blob carries the new key, at which point this remap is dead and must go.
  stopifnot("has_nvenc" %in% names(table))
  names(table)[names(table) == "has_nvenc"] <- "has_hardware_encoder"
  # The same treatment for a cell whose export is GONE rather than renamed.
  # `mediainfo_summary()` was a second exported name for `mediainfo_template()`
  # -- one object, two `export()` lines -- and M112 removed it. Its recorded
  # cell is byte-identical to `mediainfo_template`'s, which is still in the
  # table, so dropping it removes a duplicate reading rather than a measurement.
  # Asserted, not assumed: an unequal pair means the blob measured two
  # different things and this drop would be hiding one of them. Presence is
  # asserted first, exactly as the `has_nvenc` remap above does it -- once the
  # blob is re-recorded without the cell, this drop is dead code and must go,
  # and the maintainer should be told that rather than shown an opaque
  # `identical(NULL, <cell>) is not TRUE`.
  stopifnot("mediainfo_summary" %in% names(table))
  stopifnot(identical(table[["mediainfo_summary"]],
                      table[["mediainfo_template"]]))
  table[["mediainfo_summary"]] <- NULL
  table
}

# tm_provenance_ok(): the provenance predicate, separate so it can be shown to
# say NO. A checker whose only falsifier is deleting it certifies nothing.
tm_provenance_ok <- function(table, ref = tm_timeout_valid_baseline_ref) {
  prov <- attr(table, "provenance")
  is.list(prov) &&
    all(c("source", "generator", "seed", "recorded") %in% names(prov)) &&
    grepl(ref, prov$source, fixed = TRUE) &&
    identical(prov$generator, "data-raw/timeout-valid-baseline.R")
}

# tm_baseline_shape_ok(): does the recorded BLOB still describe what
# tm_spawn_trace() measures?
#
# The provenance predicate above reads the attribute, so a blob whose contents
# no longer match the helper that produced them passes it (M094 review G5). The
# coupling is real: `value` is `str()` output, and an edit to tm_spawn_trace()'s
# print conventions or its returned fields would leave 106 cells mismatching
# with nothing to say WHY. This compares one live trace's shape -- its field
# names and their types -- against every recorded cell, so that edit fails once
# and names itself instead of arriving as a wall of diffs.
#
# Shape only, deliberately: the VALUES are what the AC5 comparison is for, and a
# predicate that recomputed them would be that comparison written twice.
tm_baseline_shape_ok <- function(table, live) {
  shape <- function(x) {
    is.list(x) &&
      identical(names(x), names(live)) &&
      vapply(names(live), function(f) identical(class(x[[f]]), class(live[[f]])),
             logical(1)) |> all() &&
      is.character(x$value) && length(x$value) == 1L && nzchar(x$value)
  }
  is.list(table) && length(table) > 0 &&
    all(vapply(table, function(cell) {
      is.list(cell) && identical(names(cell), c("unset", "valid")) &&
        shape(cell$unset) && shape(cell$valid)
    }, logical(1)))
}

# tm_timeout_variant_specs(): the argument cells the one-cell-per-member table
# above cannot carry, and where M094's review found the refusal still missing.
#
# `tm_timeout_call_specs()` holds exactly one valid argument set per member, so
# an argument that steers the verb down a DIFFERENT path -- a GPU encode, the
# frame half of extract_frame()'s "provide exactly one of" pair, a two-pass
# normalization -- is invisible to every sweep built on it. Each of those three
# hid a member that went on blaming a function the caller never typed (M094
# review F2, F4, F3).
#
# The axes are computed from `formals()`, not listed, for the reason the domain
# itself is computed (M70): a verb that gains `hardware` joins this table on its
# own. `extract_frame` is named because its pair is a documented argument
# contract, not a formal a sweep can see.
#
# Every cell here is exercised under an INVALID limit only, where AC5 pins the
# spawn count at 0 -- so `hardware = "nvenc"` never asks a real FFmpeg anything.
tm_timeout_variant_specs <- function(dir) {
  specs <- tm_timeout_call_specs(dir)
  out <- list()
  for (nm in tm_timeout_domain()) {
    fmls <- names(formals(get(nm, envir = asNamespace("tidymedia"))))
    if ("hardware" %in% fmls) {
      args <- specs[[nm]]
      args$hardware <- "nvenc"
      # A re-encoding codec is named where the verb has one, because a
      # `video_codec = "copy"` default plus `hardware = "nvenc"` is a
      # contradiction the verb refuses on its own (D036) -- a correct refusal
      # that would leave this cell testing that instead of the limit.
      if ("video_codec" %in% fmls) args$video_codec <- "libx264"
      out[[paste0(nm, " [hardware = nvenc]")]] <- list(name = nm, args = args)
    }
    if ("two_pass" %in% fmls) {
      args <- specs[[nm]]
      args$two_pass <- TRUE
      out[[paste0(nm, " [two_pass = TRUE]")]] <- list(name = nm, args = args)
    }
  }
  frame_args <- specs$extract_frame
  frame_args$timestamp <- NULL
  frame_args$frame <- 1
  out[["extract_frame [frame = ]"]] <-
    list(name = "extract_frame", args = frame_args)
  out
}

# tm_refusal_head(): `tm_blame_head()` with the condition's IDENTITY checked
# too.
#
# The head alone cannot tell M094's refusal from any other error raised in the
# same frame, so a member that aborted on something else entirely would read as
# a pass (M094 review F9). This compares the message to what the one checker
# site writes for that value before reporting the head, and names what it saw
# otherwise -- so a wrong-condition cell fails with the wrong condition in the
# message rather than going quietly green.
tm_refusal_head <- function(name, args, limit) {
  reference <- tm_resolve_timeout_message(limit)
  testthat::local_reproducible_output()
  withr::with_options(list(tidymedia.timeout = limit), {
    cnd <- tryCatch(
      do.call(name, args, envir = asNamespace("tidymedia")),
      error = function(e) e
    )
    if (!inherits(cnd, "error")) return("<none>")
    msg <- cli::ansi_strip(conditionMessage(cnd))
    if (!identical(msg, reference)) {
      return(paste0("<other: ", blamed_verb(cnd), ": ", msg, ">"))
    }
    blamed_verb(cnd)
  })
}

# M094 review round 2, widened at M096: the cells that carry a WRONG argument --

# tm_timeout_corrupt_specs(): every (member, formal, wrong form) cell -- each
# member of the domain with ONE of its own formals replaced by one of the five
# wrong forms `tm_nvenc_wrong_forms()` names.
#
# Every cell in `tm_timeout_call_specs()` and `tm_timeout_variant_specs()`
# carries VALID arguments, so no leg built on them can see the refusal DISPLACE
# an error the caller's own call earned -- which is how one class of that defect
# survived round 1 at four verbs and recurred at nine more (review F1, G1).
#
# M094 corrupted `args[[1]]` with `123` alone. That single form on a single
# formal is what let two front doors through three review rounds: `123` is a
# number where every member's FIRST argument wants a path, a job table or a
# pipeline, so the narrow instrument saw only the guards that sit at the top of
# each verb. An argument whose only guard is a token check, a missingness
# check, a length check or a shape check is invisible to it, and an argument
# that is not the first is invisible to it entirely. The cross-product is the
# widening: every formal, and the five forms M095 crossed the nvenc probe with,
# which between them span type, token shape, missingness, length and container.
#
# `...` is excluded because it is not an argument a caller can hand a wrong
# value to by name. `run = FALSE` and `parallel = FALSE` go into the base cell
# so a cell that IS refused spawns nothing on the way there; the sweep below
# intercepts the spawns a truthy wrong value would otherwise reach.
#
# Every member is present, including the three whose valid cell in
# `tm_timeout_call_specs()` holds no user-chosen value -- `ffmpeg_codecs`,
# `ffmpeg_encoders` and `has_hardware_encoder` all have formals to corrupt even
# though their cell is empty (or, for `has_hardware_encoder`, holds only the
# backend its required `hardware` argument has no default for), and
# `ffmpeg_codecs`'s is the argument M096 exists to guard.
# tm_timeout_argumentless(): domain members with no formal a caller can name.
#
# A wrong-argument sweep has nothing to cross these with -- there is no argument
# to pass a wrong value to -- so they contribute no cells and are not part of
# the corrupt table's membership. Computed, not listed: the test that uses it
# pins the answer, so a second argumentless export joining the domain reddens
# there rather than disappearing from the sweep.
tm_timeout_argumentless <- function() {
  ns <- asNamespace("tidymedia")
  Filter(
    function(nm) {
      !length(setdiff(names(formals(get(nm, envir = ns))), "..."))
    },
    tm_timeout_domain()
  )
}

tm_timeout_corrupt_specs <- function(dir) {
  specs <- tm_timeout_call_specs(dir)
  forms <- tm_nvenc_wrong_forms()
  ns <- asNamespace("tidymedia")
  out <- list()
  for (nm in tm_timeout_domain()) {
    fmls <- names(formals(get(nm, envir = ns)))
    base <- specs[[nm]]
    if ("parallel" %in% fmls) base$parallel <- FALSE
    if ("run" %in% fmls) base$run <- FALSE
    for (arg in setdiff(fmls, "...")) {
      for (form in names(forms)) {
        args <- base
        args[[arg]] <- forms[[form]]
        out[[paste0(nm, " [", arg, " = ", form, "]")]] <-
          list(name = nm, arg = arg, form = form, args = args)
      }
    }
  }
  out
}

# tm_corrupt_limit_sweep(): drive every cell with no limit set, and again under
# each invalid `tidymedia.timeout` in `tm_timeout_bad_forms()`.
#
# The no-limit reading is the referent, measured rather than recorded: what a
# call reports with no limit set at all is what it must still report under one
# base R cannot use. That keeps the leg indifferent to each verb's own wording
# and to any later change in it.
#
# A cell is KEPT when that referent was refused by the member ITSELF -- the
# blamed frame is the member's own name -- and dropped otherwise, whether the
# refusal came from a frame below the member or never came. Dropping is a
# measurement here and never a list: `tm_corrupt_dropped_master()` records what
# the measurement returned so a reader can see it, but the sweep consults no
# list to decide.
#
# Spawns are intercepted at the two wrappers `tm_force_timeout()` uses, for
# `tm_nvenc_sweep()`'s reason: without it a cell whose wrong value happens to be
# TRUTHY really executes FFmpeg on an empty placeholder file, so the frame that
# refused it is FFmpeg's exit status on this machine and a missing binary on a
# runner with none. `character(0)` is a clean exit with no output, so such a
# cell reads as "not refused", which is what it is.
tm_corrupt_limit_sweep <- function(cells) {
  # Before the mocked bindings below, never after. `tm_timeout_corrupt_specs()`
  # computes its own domain by reading each function's `body()` off the
  # namespace and asking which of them reach a spawn primitive; once
  # `run_program()` and `guard_timeout()` are mocked, no body names one, the
  # domain halves, and the census silently runs over half of what it claims.
  # Measured: 1530 cells forced here, 705 forced inside the mocks. The working
  # directory is NOT what matters -- forcing from another directory with no
  # mocks in force still yields 1530 (corrected M96 review F4).
  force(cells)
  testthat::local_reproducible_output()
  # The sweep runs from a scratch directory, and this is load-bearing rather
  # than tidy. A cell that corrupts a path argument with the token form hands a
  # verb the RELATIVE path "bad fmt!", and `sample_frames()`'s `outdir` creates
  # what it is given -- so a sweep run from the package root leaves a directory
  # called "bad fmt!" behind it, and from then on `file.exists("bad fmt!")` is
  # TRUE and every later path cell reads that token as a perfectly good path.
  # Measured: a sweep run twice from the working tree turned eight of M095's
  # kept cells into dropped ones the second time, and reddened
  # test-nvenc-probe-blame.R, which measures the same paths.
  scratch <- withr::local_tempdir()
  withr::local_dir(scratch)
  withr::local_options(tidymedia.hardware_encoders = NULL)
  testthat::local_mocked_bindings(
    run_program = function(location, args, program = "the program", ...) {
      character(0)
    },
    guard_timeout = function(program, limit, expr, ...) character(0),
    # The encoder pool and the three locators are pinned for the same reason,
    # one layer up: without them a cell that reaches the nvenc probe or a
    # binary lookup measures the runner's FFmpeg build and its PATH, and the
    # census below stops being the same table on two machines -- and the
    # encoder pool is remembered for the session, so it need not even be the
    # same table twice on one. `character()` is the build with no nvenc
    # encoders -- the
    # pool that makes an availability abort fire, so a cell reading as kept
    # under it is kept everywhere.
    cached_encoder_names = function() character(),
    find_ffmpeg = function() "/nonexistent/ffmpeg",
    find_ffprobe = function() "/nonexistent/ffprobe",
    find_mediainfo = function() "/nonexistent/mediainfo",
    .package = "tidymedia"
  )
  forms <- tm_timeout_bad_forms()
  # Warnings are muffled, not measured. What this sweep reads is the condition
  # that ABORTS a call; a cell whose mocked spawn returns `character(0)` makes
  # the six `probe_*()` readers warn that they could not probe the file, which
  # is an artifact of the interception and not of any behavior under test.
  muffled <- function(expr) {
    withCallingHandlers(expr, warning = function(w) invokeRestart("muffleWarning"))
  }
  rows <- lapply(names(cells), function(key) {
    spec <- cells[[key]]
    ref <- muffled(withr::with_options(
      list(tidymedia.timeout = NULL),
      tm_nvenc_condition(spec$name, spec$args)
    ))
    head <- sub(" \\|\\| .*$", "", ref)
    kept <- identical(head, spec$name)
    bad <- character()
    if (kept) {
      for (fm in names(forms)) {
        got <- muffled(withr::with_options(
          list(tidymedia.timeout = forms[[fm]]),
          tm_nvenc_condition(spec$name, spec$args)
        ))
        if (!identical(got, ref)) bad <- c(bad, fm)
      }
    }
    tibble::tibble(
      cell = key, member = spec$name, arg = spec$arg, form = spec$form,
      none = ref, kept = kept, refused_by = head,
      mismatch = paste(bad, collapse = ",")
    )
  })
  do.call(rbind, rows)
}

# tm_corrupt_master_ref: the commit the census below was measured at -- the tip
# of the default branch when this branch was cut, so a reader can regenerate it.
tm_corrupt_master_ref <- "4063faa"

# tm_corrupt_dropped_master(): every (member, argument) the widened sweep DROPS,
# named with the frame that refused it -- measured 2026-08-31 at
# `tm_corrupt_master_ref`, before either M096 guard landed.
#
# Recorded for `tm_nvenc_dropped_master()`'s reason: AC1 drops a cell BY
# MEASUREMENT and never by a list, and a measurement nobody can see is
# indistinguishable from a list nobody wrote down. It is compared to a live
# sweep rather than consulted by one.
#
# Eight frames appear. `<none>` is a cell no front door refuses at all, and most
# often means the wrong form is not wrong for that formal -- `123` is a
# perfectly good `fps`, `width` or `tolerance`. `ffm_finish`, `ffm_batch` and
# `if` are the gate booleans `run` and `parallel`, refused by the runner rather
# than the verb; `purrr::pmap` is the per-row fan-out; `hardware_encoder` and `<-`
# are two single-member classes. Those five classes are named in this
# milestone's Scope Out and carried by a ROADMAP row; they are here so they stay
# visible. `:` is the sixth and the one that does not stay: `ffmpeg_codecs()`
# reaching its own output parsing with an unchecked `sort_by_type`, which is
# what AC3 closes.
#
# What that `:` entry does and does NOT prove (M96 review F8). Under this
# sweep's mocks `ffmpeg("-hide_banner -codecs")` (spelled `ffmpeg("-codecs")`
# before M139) returns `character(0)`, so the parse below
# it aborts at `1:integer(0)` for EVERY value -- measured at
# `tm_corrupt_master_ref`: `TRUE`, `FALSE` and `123` all give "argument of
# length 0". So the drop says only that no front door refused the cell; it is
# form-independent and is NOT evidence that `sort_by_type` was unguarded. AC3's
# defect is shown by the spawn-count and message-parity test in
# test-unguarded-argument-front-doors.R, not by this entry. What the entry is
# good for is the two-way difference: the cell is refused by `ffmpeg_codecs`
# itself at HEAD and so leaves this list, which a guard sited BELOW
# `resolve_timeout()` would not have achieved.
#
# A pair whose two entries name different frames (`segment_video/outfiles`,
# `anonymize_video_batch/color`) is not an inconsistency: different wrong forms
# of one argument are caught at different depths.
tm_corrupt_dropped_master <- function() {
  tm_sort_c(c(
    "anonymize_video/audio_stream -> <none>",
    "anonymize_video/color -> <none>",
    "anonymize_video/outfile -> <none>",
    "anonymize_video/run -> ffm_finish",
    "anonymize_video_batch/audio_stream -> <none>",
    "anonymize_video_batch/color -> <none>",
    "anonymize_video_batch/color -> purrr::pmap",
    "anonymize_video_batch/fallback -> purrr::pmap",
    "anonymize_video_batch/parallel -> ffm_batch",
    "anonymize_video_batch/pixel_format -> purrr::pmap",
    "anonymize_video_batch/run -> ffm_batch",
    "compare_videos/outfile -> <none>",
    "compare_videos/run -> ffm_finish",
    "compare_videos_batch/fallback -> purrr::pmap",
    "compare_videos_batch/parallel -> ffm_batch",
    "compare_videos_batch/run -> ffm_batch",
    "concatenate_videos/outfile -> <none>",
    "concatenate_videos/run -> ffm_finish",
    "concatenate_videos_batch/parallel -> ffm_batch",
    "concatenate_videos_batch/run -> ffm_batch",
    "convert_audio/audio_stream -> <none>",
    "convert_audio/outfile -> <none>",
    "convert_audio/run -> ffm_finish",
    "convert_audio_batch/audio_stream -> <none>",
    "convert_audio_batch/parallel -> ffm_batch",
    "convert_audio_batch/run -> ffm_batch",
    "crop_video/audio_stream -> <none>",
    "crop_video/height -> <none>",
    "crop_video/outfile -> <none>",
    "crop_video/run -> ffm_finish",
    "crop_video/width -> <none>",
    "crop_video/x -> <none>",
    "crop_video/y -> <none>",
    "crop_video_batch/audio_stream -> <none>",
    "crop_video_batch/fallback -> purrr::pmap",
    "crop_video_batch/height -> <none>",
    "crop_video_batch/parallel -> ffm_batch",
    "crop_video_batch/run -> ffm_batch",
    "crop_video_batch/width -> <none>",
    "crop_video_batch/x -> <none>",
    "crop_video_batch/y -> <none>",
    "extract_audio/audio_stream -> <none>",
    "extract_audio/outfile -> <none>",
    "extract_audio/run -> ffm_finish",
    "extract_audio_batch/audio_stream -> <none>",
    "extract_audio_batch/parallel -> ffm_batch",
    "extract_audio_batch/run -> ffm_batch",
    "extract_frame/outfile -> <none>",
    "extract_frame/run -> ffm_finish",
    "extract_frame/timestamp -> <none>",
    "extract_frame_batch/format -> <none>",
    "extract_frame_batch/parallel -> ffm_batch",
    "extract_frame_batch/run -> ffm_batch",
    "ffmpeg/command -> <none>",
    "ffmpeg_codecs/sort_by_type -> :",
    "ffprobe/command -> <none>",
    "format_for_web/audio_stream -> <none>",
    "format_for_web/outfile -> <none>",
    "format_for_web/run -> ffm_finish",
    "format_for_web_batch/audio_stream -> <none>",
    "format_for_web_batch/fallback -> purrr::pmap",
    "format_for_web_batch/parallel -> ffm_batch",
    "format_for_web_batch/run -> ffm_batch",
    "get_duration/file -> <none>",
    "get_frame_rate/file -> <none>",
    "get_height/file -> <none>",
    "get_sample_rate/file -> <none>",
    "get_width/file -> <none>",
    "has_hardware_encoder/codec -> hardware_encoder",
    "mediainfo/command -> <none>",
    "mediainfo_parameter/file -> <none>",
    "mediainfo_parameter/parameter -> <none>",
    "mediainfo_parameter/section -> <none>",
    "mediainfo_query/file -> <none>",
    "mediainfo_query/names -> <none>",
    "mediainfo_query/parameters -> <none>",
    "mediainfo_query/section -> <none>",
    "mediainfo_summary/file -> <none>",
    "mediainfo_template/file -> <none>",
    "normalize_audio/audio_stream -> <none>",
    "normalize_audio/channels -> <none>",
    "normalize_audio/outfile -> <none>",
    "normalize_audio/run -> ffm_finish",
    "normalize_audio/sample_rate -> <none>",
    "normalize_audio_batch/audio_stream -> <none>",
    "normalize_audio_batch/channels -> <none>",
    "normalize_audio_batch/channels -> purrr::pmap",
    "normalize_audio_batch/parallel -> ffm_batch",
    "normalize_audio_batch/run -> ffm_batch",
    "normalize_audio_batch/sample_rate -> <none>",
    "normalize_audio_batch/sample_rate -> purrr::pmap",
    "picture_in_picture/margin -> <none>",
    "picture_in_picture/outfile -> <none>",
    "picture_in_picture/run -> ffm_finish",
    "picture_in_picture_batch/fallback -> purrr::pmap",
    "picture_in_picture_batch/margin -> <none>",
    "picture_in_picture_batch/parallel -> ffm_batch",
    "picture_in_picture_batch/run -> ffm_batch",
    "probe_all/infile -> <none>",
    "probe_audio/infile -> <none>",
    "probe_container/infile -> <none>",
    "probe_streams/infile -> <none>",
    "probe_video/infile -> <none>",
    "sample_frames/fps -> <none>",
    "sample_frames/outdir -> <none>",
    "sample_frames/prefix -> <none>",
    "sample_frames/run -> ffm_finish",
    "sample_frames_batch/fps -> <none>",
    "sample_frames_batch/outdir -> <none>",
    "sample_frames_batch/parallel -> ffm_batch",
    "sample_frames_batch/run -> ffm_batch",
    "segment_video/audio_stream -> <none>",
    "segment_video/end -> <none>",
    "segment_video/fallback -> purrr::pmap",
    "segment_video/infile -> <none>",
    "segment_video/outfiles -> <none>",
    "segment_video/outfiles -> purrr::pmap",
    "segment_video/parallel -> ffm_batch",
    "segment_video/run -> ffm_batch",
    "segment_video/start -> <none>",
    "segment_video_batch/audio_stream -> <none>",
    "segment_video_batch/fallback -> purrr::pmap",
    "segment_video_batch/parallel -> ffm_batch",
    "segment_video_batch/run -> ffm_batch",
    "separate_audio_video/audio_stream -> <none>",
    "separate_audio_video/audiofile -> <none>",
    "separate_audio_video/infile -> <none>",
    "separate_audio_video/run -> <none>",
    "separate_audio_video/run -> if",
    "separate_audio_video/videofile -> <none>",
    "separate_audio_video_batch/audio_stream -> <none>",
    "separate_audio_video_batch/parallel -> ffm_batch",
    "separate_audio_video_batch/run -> ffm_batch",
    "standardize_video/audio_stream -> <none>",
    "standardize_video/fps -> <none>",
    "standardize_video/height -> <none>",
    "standardize_video/infile -> <none>",
    "standardize_video/outfile -> <none>",
    "standardize_video/run -> ffm_finish",
    "standardize_video/width -> <none>",
    "standardize_video_batch/audio_stream -> <none>",
    "standardize_video_batch/fallback -> purrr::pmap",
    "standardize_video_batch/fps -> <none>",
    "standardize_video_batch/height -> <none>",
    "standardize_video_batch/parallel -> ffm_batch",
    "standardize_video_batch/run -> ffm_batch",
    "standardize_video_batch/width -> <none>",
    "strip_metadata/infile -> <none>",
    "strip_metadata/outfile -> <none>",
    "strip_metadata/run -> ffm_finish",
    "strip_metadata_batch/parallel -> ffm_batch",
    "strip_metadata_batch/run -> ffm_batch",
    "verify_media/audio_codec -> <-",
    "verify_media/audio_codec -> <none>",
    "verify_media/duration -> <-",
    "verify_media/duration -> <none>",
    "verify_media/file -> <none>",
    "verify_media/height -> <-",
    "verify_media/height -> <none>",
    "verify_media/sample_rate -> <-",
    "verify_media/sample_rate -> <none>",
    "verify_media/tolerance -> <none>",
    "verify_media/video_codec -> <-",
    "verify_media/video_codec -> <none>",
    "verify_media/width -> <-",
    "verify_media/width -> <none>"
  ))
}

# tm_masked_condition(): the whole condition one call raised, as one comparable
# string -- the head that was blamed and the sentence it carried.
#
# Both halves matter here. A member whose argument error survives the limit but
# starts naming a different frame has moved blame the milestone did not intend
# to move, and a member that reports the limit's sentence instead has masked the
# argument error outright; comparing head and message together catches each.
# `"<none>"` is a real answer and a failing one for this leg: the call was given
# an argument every front door refuses.
tm_masked_condition <- function(name, args, limit) {
  testthat::local_reproducible_output()
  withr::with_options(list(tidymedia.timeout = limit), {
    cnd <- tryCatch(
      do.call(name, args, envir = asNamespace("tidymedia")),
      error = function(e) e
    )
    if (!inherits(cnd, "error")) return("<none>")
    paste0(blamed_verb(cnd), " || ", cli::ansi_strip(conditionMessage(cnd)))
  })
}

# M095: a wrong argument, crossed with the nvenc availability probe -----------

# tm_nvenc_wrong_forms(): five wrong values spanning the axes an argument is
# free in -- type, token shape, missingness, length, and container.
#
# Five rather than one because `tm_timeout_corrupt_specs()` above corrupts with
# `123` alone, and a number is refused by a type check at the top of every front
# door. An argument whose only guard is a token check (`pixel_format`), a
# missingness check (`NA`), a length check, or a shape check is invisible to
# that single form, and each of those guards sits at a different depth -- which
# is the depth this milestone is measuring against the probe.
tm_nvenc_wrong_forms <- function() {
  list(
    number = 123,
    token = "bad fmt!",
    missing = NA,
    length_two = c(1, 2),
    list = list(1)
  )
}

# tm_nvenc_unmappable_codec(): a codec token that is WELL FORMED and names no
# codec family -- deliberately NOT a member of the table above (M107).
#
# The seam test wants a `video_codec` that survives `check_token()` and is then
# refused by `codec_family()`, because all five forms above are refused by a
# type or shape check and none of them gets that far. It is a separate helper
# rather than a sixth entry because the table above is a table of values that
# are WRONG for any argument, spanning type, token shape, missingness, length
# and container, and "notacodec" is none of those: it is a legal string
# everywhere except as a `video_codec` under a hardware backend. Crossing it
# over every formal of every member was measured 2026-09-04 -- 1535 cells to
# 1842, and 26 new census entries recording that nothing refuses it -- which is
# a measurement of its legality, not of a guard.
tm_nvenc_unmappable_codec <- function() "notacodec"

# tm_nvenc_wrong_arg_cells(): every (member, other formal, wrong form) cell.
#
# The members are computed -- `tm_timeout_domain()` filtered to the ones whose
# `formals()` carry `hardware` -- for the reason the domain itself is computed
# (M70): a verb that gains `hardware` joins this sweep on its own. The ARGUMENTS
# are computed the same way, off each member's own `formals()`, so an argument
# added to a verb joins too.
#
# `hardware` is excluded because it is the axis being crossed, and `...` because
# it is not an argument a caller can hand a wrong value to by name. `video_codec`
# is set to a re-encoding token in the base cell for `tm_timeout_variant_specs()`'s
# reason: the `"copy"` default plus `hardware = "nvenc"` is a contradiction the
# verb refuses on its own (D036), which would leave every cell of that member
# measuring the contradiction instead of its own argument. `run = FALSE` and
# `parallel = FALSE` keep a cell that IS refused from spawning anything.
tm_nvenc_wrong_arg_cells <- function(dir) {
  specs <- tm_timeout_call_specs(dir)
  forms <- tm_nvenc_wrong_forms()
  ns <- asNamespace("tidymedia")
  out <- list()
  # The two capability helpers are out of this domain for the reason
  # nvenc_hardware_exports() states: they grew a `hardware` argument too, but
  # this sweep crosses each member's OTHER formals with wrong forms and asks
  # which frame refused them, and the helpers have no pipeline to blame.
  for (nm in setdiff(tm_timeout_domain(), nvenc_hardware_helpers())) {
    fmls <- names(formals(get(nm, envir = ns)))
    if (!"hardware" %in% fmls) next
    base <- specs[[nm]]
    if ("video_codec" %in% fmls) base$video_codec <- "libx264"
    if ("parallel" %in% fmls) base$parallel <- FALSE
    if ("run" %in% fmls) base$run <- FALSE
    for (arg in setdiff(fmls, c("hardware", "..."))) {
      for (form in names(forms)) {
        args <- base
        args[[arg]] <- forms[[form]]
        out[[paste0(nm, " [", arg, " = ", form, "]")]] <-
          list(name = nm, arg = arg, form = form, args = args)
      }
    }
  }
  out
}

# tm_nvenc_condition(): one call's condition as a single comparable string --
# the frame that was blamed and the sentence it carried.
#
# Both halves, for `tm_masked_condition()`'s reason: a cell that keeps naming
# the member but starts saying something else has still changed what the caller
# reads. `"<none>"` is a real answer -- a cell the member does not refuse at all.
tm_nvenc_condition <- function(name, args) {
  cnd <- tryCatch(
    do.call(name, args, envir = asNamespace("tidymedia")),
    error = function(e) e
  )
  if (!inherits(cnd, "error")) return("<none>")
  paste0(blamed_verb(cnd), " || ", cli::ansi_strip(conditionMessage(cnd)))
}

# tm_nvenc_sweep(): drive every cell under `hardware = "none"` and again under
# `hardware = "nvenc"`, with the encoder pool fixed to `encoders`.
#
# The mock is installed ONCE around the whole loop rather than per cell, and
# `tidymedia.hardware_encoders` is unset inside it: `nvenc_available()` reads that
# option seam FIRST and only falls through to `cached_encoder_names()` when it
# is NULL, so an option left set by another file would answer every cell and
# the mock -- the thing that makes `encoders = character()` mean "this build has
# no nvenc" -- would never be consulted.
#
# The `hardware = "none"` reference is re-measured inside each mock state rather
# than once outside, so the pair being compared differs in `hardware` and in
# nothing else.
#
# `limit` is AC3's axis: a `tidymedia.timeout` value the option refuses, set for
# the `hardware = "nvenc"` arm ONLY. The reference arm stays unlimited on
# purpose -- the question is whether a session limit changes what a caller is
# told about their argument, so the thing it is compared against must be the
# answer with no limit at all. `NULL` means no limit is set on either arm.
#
# Returns one row per cell: the reference condition, the nvenc condition,
# whether the reference was refused BY THE MEMBER ITSELF (`kept`), and the frame
# that refused it when it was not.
tm_nvenc_sweep <- function(cells, encoders, limit = NULL) {
  testthat::local_reproducible_output()
  withr::local_options(tidymedia.hardware_encoders = NULL)
  # Every spawn is intercepted too, at the two wrappers `tm_force_timeout()` uses
  # -- `run_program()` and `guard_timeout()` stand in front of every
  # `system()`/`system2()` call the package makes. Without this, a cell whose
  # wrong value happens to be TRUTHY (`run = 123`) really executes FFmpeg on an
  # empty placeholder file, so the frame that refused it is FFmpeg's exit status
  # on this machine and a missing binary on a runner with none -- a cell whose
  # answer is the runner's PATH, not the package's behavior. `character(0)` is a
  # clean exit with no output, so such a cell reads as "not refused", which is
  # what it is.
  testthat::local_mocked_bindings(
    cached_encoder_names = function() encoders,
    run_program = function(location, args, program = "the program", ...) {
      character(0)
    },
    guard_timeout = function(program, limit, expr, ...) character(0),
    .package = "tidymedia"
  )
  # `spec`, never `cell`: tibble() evaluates its arguments in sequence with the
  # earlier columns in scope, so a `cell =` column would shadow the loop
  # variable for every column after it.
  rows <- lapply(names(cells), function(key) {
    spec <- cells[[key]]
    none_args <- spec$args
    none_args$hardware <- "none"
    nvenc_args <- spec$args
    nvenc_args$hardware <- "nvenc"
    ref <- tm_nvenc_condition(spec$name, none_args)
    got <- if (is.null(limit)) {
      tm_nvenc_condition(spec$name, nvenc_args)
    } else {
      withr::with_options(
        list(tidymedia.timeout = limit),
        tm_nvenc_condition(spec$name, nvenc_args)
      )
    }
    head <- sub(" \\|\\| .*$", "", ref)
    tibble::tibble(
      cell = key, member = spec$name, arg = spec$arg, form = spec$form,
      none = ref, nvenc = got,
      kept = identical(head, spec$name),
      refused_by = head
    )
  })
  out <- do.call(rbind, rows)
  out$match <- out$none == out$nvenc
  out
}

# tm_hardware_encoder_pools(): the mocked builds every instrument in this repo
# hands `cached_encoder_names()`, DERIVED from `hardware_backend_families()`
# rather than spelled out (M107). Three levels:
#
#   nvenc         every encoder the nvenc row covers, and nothing else
#   videotoolbox  every encoder the videotoolbox row covers, and nothing else
#   absent        no encoder at all
#
# Derived because the literal `c("h264_nvenc", "hevc_nvenc", "av1_nvenc")` had
# been written out at three sites, and a fourth family added to the nvenc row
# would have left all three describing a build the package no longer resolves
# against. The encoder name is the family and the backend joined by "_", which
# is how FFmpeg spells every member of both rows and how `hardware_encoder()`
# builds the name it looks for.
#
# A cross-backend level is deliberately NOT collapsed away: mocking the nvenc
# pool while asking for `hardware = "videotoolbox"` is the harder half of a
# cross, because there the availability abort is what the caller would get if
# the check under test did not fire first.
tm_hardware_encoder_pools <- function() {
  fams <- asNamespace("tidymedia")$hardware_backend_families()
  pools <- lapply(names(fams), function(hw) paste0(fams[[hw]], "_", hw))
  names(pools) <- names(fams)
  c(pools, list(absent = character()))
}

# tm_nvenc_encoder_pools(): the two mocked answers AC1 crosses every kept cell
# with -- a build that has the nvenc encoders and one that has none. The second
# is what makes the availability abort fire, and so what a cell must survive for
# the argument error to have outranked the probe.
#
# Two levels, not the three above: adding a videotoolbox level here would add a
# third arm to a sweep whose recorded master tables are keyed on the nvenc
# probe, which is a different measurement from the one AC1 makes.
# tm_nvenc_encoder_keyed_args(): arguments whose refusal is KEYED ON THE
# ENCODER, and so legitimately reads differently under `hardware = "none"` and
# `hardware = "nvenc"` (M135). `quality` is the encoder's own rate-control
# value: its range and its refusal name the encoder the pair resolves to, and
# `hardware` is what picks that encoder. The difference is the caller's own
# argument, not the machine -- the refusal fires before the probe, so the
# present and absent pools read identically -- which is the invariant the
# match comparison guards. These cells stay in the sweep (counted, kept, and
# blamed on the member); only the message-equality comparison skips them.
tm_nvenc_encoder_keyed_args <- function() "quality"

tm_nvenc_encoder_pools <- function() {
  # `[[` and not `$`: a renamed or restructured nvenc row must ERROR here.
  # Under `$` it would return NULL, both pool levels would answer "no encoders",
  # and AC1's present/absent discrimination would go vacuous with nothing red.
  list(
    present = tm_hardware_encoder_pools()[["nvenc"]],
    absent = tm_hardware_encoder_pools()[["absent"]]
  )
}

# tm_sort_c(): sort in C collation, never the session's locale.
#
# The recorded tables below are compared element-by-element against a live
# sweep, and R's default sort follows LC_COLLATE -- which orders "segment/x"
# against "segment_video/x" differently on a C runner than on this machine. A
# table recorded under one collation and compared under another fails for a
# reason that has nothing to do with the behavior under test.
tm_sort_c <- function(x) sort(x, method = "radix")

# tm_nvenc_probe_master_ref: the commit the tables below were measured at -- the
# tip of master when this branch was cut, so a reader can regenerate them.
tm_nvenc_probe_master_ref <- "b538e63"

# tm_nvenc_dropped_master(): every (member, argument) the AC1 sweep DROPS,
# named with the frame that refused it -- measured 2026-08-31 at
# `tm_nvenc_probe_master_ref` and identical under both encoder pools.
#
# Recorded because AC1 drops a cell BY MEASUREMENT and never by a list, and a
# measurement nobody can see is indistinguishable from a list nobody wrote down.
# Three frames appear. `<none>` is an argument no front door refuses at all --
# a path string that is only ever opened, or a value that is legal (`123` is a
# perfectly good `audio_stream`). `ffm_finish`/`ffm_batch` are the gate booleans
# `run` and `parallel`, refused by the runner rather than the verb. `purrr::pmap`
# is the per-row fan-out. The last two are the two classes the ROADMAP carries
# and this milestone does not close; they are here so they stay visible.
#
# A pair whose two entries name different frames (`segment_video/outfiles`,
# `anonymize_video_batch/color`) is not an inconsistency: different wrong forms
# of one argument are caught at different depths.
tm_nvenc_dropped_master <- function() {
  c(
    "anonymize_video/audio_stream -> <none>",
    "anonymize_video/color -> <none>",
    "anonymize_video/outfile -> <none>",
    "anonymize_video/run -> ffm_finish",
    "anonymize_video_batch/audio_stream -> <none>",
    "anonymize_video_batch/color -> <none>",
    "anonymize_video_batch/color -> purrr::pmap",
    "anonymize_video_batch/fallback -> purrr::pmap",
    "anonymize_video_batch/parallel -> ffm_batch",
    "anonymize_video_batch/pixel_format -> purrr::pmap",
    "anonymize_video_batch/run -> ffm_batch",
    "compare_videos/outfile -> <none>",
    "compare_videos/run -> ffm_finish",
    "compare_videos_batch/fallback -> purrr::pmap",
    "compare_videos_batch/parallel -> ffm_batch",
    "compare_videos_batch/run -> ffm_batch",
    "crop_video/audio_stream -> <none>",
    "crop_video/height -> <none>",
    "crop_video/outfile -> <none>",
    "crop_video/run -> ffm_finish",
    "crop_video/width -> <none>",
    "crop_video/x -> <none>",
    "crop_video/y -> <none>",
    "crop_video_batch/audio_stream -> <none>",
    "crop_video_batch/fallback -> purrr::pmap",
    "crop_video_batch/height -> <none>",
    "crop_video_batch/parallel -> ffm_batch",
    "crop_video_batch/run -> ffm_batch",
    "crop_video_batch/width -> <none>",
    "crop_video_batch/x -> <none>",
    "crop_video_batch/y -> <none>",
    "format_for_web/audio_stream -> <none>",
    "format_for_web/outfile -> <none>",
    "format_for_web/run -> ffm_finish",
    "format_for_web_batch/audio_stream -> <none>",
    "format_for_web_batch/fallback -> purrr::pmap",
    "format_for_web_batch/parallel -> ffm_batch",
    "format_for_web_batch/run -> ffm_batch",
    "picture_in_picture/margin -> <none>",
    "picture_in_picture/outfile -> <none>",
    "picture_in_picture/run -> ffm_finish",
    "picture_in_picture_batch/fallback -> purrr::pmap",
    "picture_in_picture_batch/margin -> <none>",
    "picture_in_picture_batch/parallel -> ffm_batch",
    "picture_in_picture_batch/run -> ffm_batch",
    "segment_video/audio_stream -> <none>",
    "segment_video/end -> <none>",
    "segment_video/fallback -> purrr::pmap",
    "segment_video/outfiles -> <none>",
    "segment_video/outfiles -> purrr::pmap",
    "segment_video/parallel -> ffm_batch",
    "segment_video/run -> ffm_batch",
    "segment_video/start -> <none>",
    "segment_video_batch/audio_stream -> <none>",
    "segment_video_batch/fallback -> purrr::pmap",
    "segment_video_batch/parallel -> ffm_batch",
    "segment_video_batch/run -> ffm_batch",
    "separate_audio_video/audio_stream -> <none>",
    "separate_audio_video/audiofile -> <none>",
    "separate_audio_video/run -> <none>",
    "separate_audio_video/run -> if",
    "separate_audio_video/videofile -> <none>",
    "separate_audio_video_batch/audio_stream -> <none>",
    "separate_audio_video_batch/parallel -> ffm_batch",
    "separate_audio_video_batch/run -> ffm_batch",
    "standardize_video/audio_stream -> <none>",
    "standardize_video/fps -> <none>",
    "standardize_video/height -> <none>",
    "standardize_video/outfile -> <none>",
    "standardize_video/run -> ffm_finish",
    "standardize_video/width -> <none>",
    "standardize_video_batch/audio_stream -> <none>",
    "standardize_video_batch/fallback -> purrr::pmap",
    "standardize_video_batch/fps -> <none>",
    "standardize_video_batch/height -> <none>",
    "standardize_video_batch/parallel -> ffm_batch",
    "standardize_video_batch/run -> ffm_batch",
    "standardize_video_batch/width -> <none>"
  )
}

# tm_nvenc_mismatch_master(): the kept cells whose condition under
# `hardware = "nvenc"` did NOT match its `hardware = "none"` reference on
# master -- the defect this milestone fixes, measured 2026-08-31 at
# `tm_nvenc_probe_master_ref`.
#
# 27 cells, all of them in the three pipelines that resolve the encoder above a
# machine-independent check, and every one of them reports the nvenc
# availability abort where the caller's own argument error belongs. They appear
# only under the `absent` pool: with the encoders present the probe succeeds and
# the argument error is reached anyway, which is why a sweep run on a machine
# whose FFmpeg happens to have nvenc would have measured nothing.
#
# `video_codec` is absent from this list. It is the one argument of the three
# verbs that already reported first, because its token check sits above the
# resolution -- which is why the disclosure that named it as the example was
# wrong (AC5).
tm_nvenc_mismatch_master <- function() {
  c(
    "anonymize_video [audio_codec = length_two]",
    "anonymize_video [audio_codec = list]",
    "anonymize_video [audio_codec = missing]",
    "anonymize_video [audio_codec = number]",
    "anonymize_video [audio_codec = token]",
    "anonymize_video [audio_stream = length_two]",
    "anonymize_video [audio_stream = list]",
    "anonymize_video [audio_stream = missing]",
    "anonymize_video [audio_stream = token]",
    "format_for_web [audio_stream = length_two]",
    "format_for_web [audio_stream = list]",
    "format_for_web [audio_stream = missing]",
    "format_for_web [audio_stream = token]",
    "standardize_video [audio_codec = length_two]",
    "standardize_video [audio_codec = list]",
    "standardize_video [audio_codec = missing]",
    "standardize_video [audio_codec = number]",
    "standardize_video [audio_codec = token]",
    "standardize_video [audio_stream = length_two]",
    "standardize_video [audio_stream = list]",
    "standardize_video [audio_stream = missing]",
    "standardize_video [audio_stream = token]",
    "standardize_video [pixel_format = length_two]",
    "standardize_video [pixel_format = list]",
    "standardize_video [pixel_format = missing]",
    "standardize_video [pixel_format = number]",
    "standardize_video [pixel_format = token]"
  )
}

# M096: what `segment_video()` COMPILED at the merge base -----------------------

# tm_outfiles_cells(): the `outfiles` values AC4's grid and AC2's two
# compiled-today controls name, as one table of calls.
#
# `run = FALSE` throughout: the question is what the verb compiles, not what
# FFmpeg does with it, and a compiled command is the same on a runner with no
# media binaries. `"bad fmt!"` is a legal output filename -- a space and a bang
# are nothing an output path may not contain -- and `list("a.mp4")` is a
# one-element list whose element is a string, which is what the per-row fan-out
# actually receives. Both are here because a front-door guard written as a check
# on `outfiles` AS A WHOLE would refuse them, and neither is refused today.
tm_outfiles_cells <- function(dir) {
  vid <- file.path(dir, "in.mp4")
  if (!file.exists(vid)) file.create(vid)
  list(
    "outfiles = NULL, start len 2" =
      list(infile = vid, start = c(0, 0.5), end = c(0.5, 1), outfiles = NULL),
    "outfiles = character vector, start len 2" =
      list(infile = vid, start = c(0, 0.5), end = c(0.5, 1),
           outfiles = c(file.path(dir, "a.mp4"), file.path(dir, "b.mp4"))),
    "outfiles = 'bad fmt!', start len 1" =
      list(infile = vid, start = 0, end = 1, outfiles = "bad fmt!"),
    "outfiles = list('a.mp4'), start len 1" =
      list(infile = vid, start = 0, end = 1,
           outfiles = list(file.path(dir, "a.mp4")))
  )
}

# tm_outfiles_commands(): drive those cells and return one scrubbed string per
# cell -- the segments' commands joined, or the message if the call was refused.
#
# The fixture directory is scrubbed to `<dir>` for `tm_spawn_trace()`'s reason:
# two checkouts measured in two temp directories would differ in the path and in
# nothing else, and this comparison exists to see everything else.
tm_outfiles_commands <- function(dir) {
  testthat::local_reproducible_output()
  cells <- tm_outfiles_cells(dir)
  vapply(names(cells), function(k) {
    res <- tryCatch(
      do.call("segment_video", c(cells[[k]], list(run = FALSE)),
              envir = asNamespace("tidymedia")),
      error = function(e) e
    )
    if (inherits(res, "error")) {
      return(paste0("<error: ", cli::ansi_strip(conditionMessage(res)), ">"))
    }
    tm_scrub_paths(paste(res$command, collapse = " ;; "), dir)
  }, character(1))
}

# tm_outfiles_baseline(): what those cells compiled at `tm_corrupt_master_ref`,
# measured 2026-08-31 in a worktree at that commit.
#
# Recorded because AC4's claim is that the guard refuses nothing the verb
# compiled before, and a comparison against the same checkout cannot see a
# change both sides make. `tm_outfiles_commands()` regenerates any cell.
tm_outfiles_baseline <- function() {
  c(
    "outfiles = NULL, start len 2" = paste(
      '-y -i "<dir>/in.mp4" -codec:a copy -ss 0 -to 0.5 -map "0:v?" -map',
      '"0:a?" "<dir>/in_1.mp4" ;; -y -i "<dir>/in.mp4" -codec:a copy -ss 0.5',
      '-to 1 -map "0:v?" -map "0:a?" "<dir>/in_2.mp4"'
    ),
    "outfiles = character vector, start len 2" = paste(
      '-y -i "<dir>/in.mp4" -codec:a copy -ss 0 -to 0.5 -map "0:v?" -map',
      '"0:a?" "<dir>/a.mp4" ;; -y -i "<dir>/in.mp4" -codec:a copy -ss 0.5 -to',
      '1 -map "0:v?" -map "0:a?" "<dir>/b.mp4"'
    ),
    "outfiles = 'bad fmt!', start len 1" = paste(
      '-y -i "<dir>/in.mp4" -codec:a copy -ss 0 -to 1 -map "0:v?" -map "0:a?"',
      '"bad fmt!"'
    ),
    "outfiles = list('a.mp4'), start len 1" = paste(
      '-y -i "<dir>/in.mp4" -codec:a copy -ss 0 -to 1 -map "0:v?" -map "0:a?"',
      '"<dir>/a.mp4"'
    )
  )
}

Try the tidymedia package in your browser

Any scripts or data that you put into this service are public.

tidymedia documentation built on Oct. 11, 2026, 5:08 p.m.