Skip to contents

ledgr_strategy_preflight() statically inspects a function(ctx, params) strategy before execution. It classifies the strategy into ledgr's reproducibility tiers:

Usage

ledgr_strategy_preflight(strategy)

Arguments

strategy

A function with signature function(ctx, params).

Value

A ledgr_strategy_preflight object with fields tier, allowed, reason, unresolved_symbols, package_dependencies, qualified_package_dependencies, attached_package_dependencies, worker_dependencies, ambient_rng_symbols, and notes.

Details

  • tier_1: self-contained strategy logic using only explicit params, base/recommended R references, and ledgr's exported public namespace.

  • tier_2: inspectable strategy logic that also uses package-qualified calls outside the active R distribution, such as pkg::fn(), or resolved immutable non-function closure objects that ledgr does not store as standalone replayable artifacts.

  • tier_3: strategy logic with unresolved free symbols, user helpers, forbidden nondeterministic calls, or global assignment that ledgr cannot recover from stored run metadata.

The preflight is static analysis, not proof of semantic reproducibility. Dynamic dispatch, mutable captured environments, and dynamically constructed code remain user responsibilities.

Tier 3 strategies fail before execution with condition classes ledgr_strategy_tier3 and ledgr_strategy_preflight_error; there is no force = TRUE override on ledgr_run() or ledgr_sweep(). Covered forbidden calls include direct wall-clock/process-environment calls such as Sys.time(), Sys.Date(), and Sys.getenv(), visible indirection such as do.call("Sys.time", list()), dynamic lookup/evaluation helpers such as get(), eval(), and assign(), global assignment with <<-, and visible context mutation such as attr(ctx, "secret") <- 1.

Strategies read the pulse context only through its public fields, its public functions and exported ledgr helpers such as ledgr_history_frame(). The preflight refuses as Tier 3 common access to private context internals: extracting a private context field from any object (ctx$.history, x[[".notes"]]); naming a field that holds prepared data beyond the pulse anywhere, also inside quote(); naming a non-exported ledgr object (ledgr:::name, or the bare name other than a member or a name the strategy binds itself); calling, assigning through (base::body(f) <-), or putting in a functional's function slot an environment-reaching function (environment(), parent.frame(), asNamespace(), getFromNamespace(), get0(), mget(), body<-, formals<-, as.function(), unserialize() and their rlang equivalents, also attached, as lapply(x, "environment") or as baseenv()$environment(...)); and substitute() or bquote() with an environment to evaluate in. A field, column or label called environment, a strategy's own config_hash and a dot-prefixed name such as a model's $.fitted are ordinary data.

The check is static and best-effort, not a sandbox: R offers no private memory. A strategy that deliberately subverts evaluation by a route the check does not recognise (a function reached through an alias, parentheses or getElement(), another package's replacement function, trace(), names built at run time, iteration over the context, objects from outside its body) is outside ledgr's guarantee, as is one that reads a future-data file. ledgr guarantees that its own history, feature and fact APIs return only data known at the decision time.

Ambient strategy RNG calls such as runif(1) are Tier 2 under the execution seed contract for ordinary sequential runs, but they are not certified for resume or parallel equivalence. Strategies that need pulse-specific stochastic inputs in those paths should derive them from ctx$pulse_seed without reading ambient .Random.seed. Custom indicator generation has a stricter deterministic feature contract; do not infer indicator RNG policy from strategy preflight.

Articles

Reproducibility model: vignette("reproducibility", package = "ledgr") system.file("doc", "reproducibility.html", package = "ledgr")

Examples

strategy <- function(ctx, params) {
  targets <- ctx$flat()
  targets
}
ledgr_strategy_preflight(strategy)
#> ledgr Strategy Preflight
#> =========================
#> 
#> Tier:    tier_1
#> Allowed: TRUE
#> Reason:  Strategy is self-contained under ledgr's static preflight rules.