Skip to contents

insideR (insider) makes R package calls transparent, replayable, and editable. It resolves what a call runs, captures the inputs and relevant hidden state, extracts inspectable R code where possible, and produces artifacts that can be reviewed and compared before a change is adopted.

insideR is strongest when the behavior of interest is implemented in ordinary R functions, S3 methods, and helper chains that R can inspect. It does not decompile compiled code or make opaque external side effects reproducible.

Install

From a cloned checkout:

install.packages("remotes")
remotes::install_local(".")

If the GitHub repository is visible to your account, install it directly:

# Set GITHUB_PAT in your environment first if private access is required.
remotes::install_github("lennon-li/insider")

Do not put a real token in a script or commit it. The package imports cli, codetools, methods, rlang, tools, and utils; remotes installs these dependencies as needed. Optional features use packages listed in Suggests.

Quick start

This example uses stats::fivenum(), which is available in a standard R installation and needs no data or package-specific setup:

library(insider)

x <- c(1, 3, 5, 7, 100)
explanation <- explain_call(stats::fivenum(x))
print(explanation)

replay_dir <- file.path(tempdir(), "fivenum-replay")
unpacked <- unpack_call(
  stats::fivenum(x),
  output_dir = replay_dir,
  overwrite = TRUE
)

source(file.path(replay_dir, "replay.R"), chdir = TRUE)
scan_secrets(unpacked)

explain_call() reports dispatch, the inspected call path, replay status, and static risk findings. unpack_call() runs the original call once, captures the values it uses, and creates a replay project. The generated replay checks its result against the captured original result; it may still require the original package or other external dependencies when those boundaries could not be extracted.

Capabilities

Understand

  • explain_call(expr, max_depth = 5L, eval_dispatch = TRUE) resolves the executed function or S3 method, walks same-package helpers, and reports replayability and static risk findings.
  • trace_call(expr) observes the shallow runtime path and dispatch boundaries while the call runs, reporting unresolved portions instead of inferring them.
  • explain_error(expr) captures an error-time package traceback when the call fails.
  • build_graph(x, cache = TRUE, refresh = FALSE) indexes an R source directory or installed package. graph_search(), graph_node(), graph_callers(), and graph_callees() query definitions and call edges.
  • explain_function(graph, name) summarizes a graph node without requiring a concrete call. build_context(graph, entry, task = NULL, format = c("md", "json"), depth = 1L) creates compact static context for a named entry point.

Replay and slice

unpack_call(
  expr,
  output_dir,
  max_depth = 5L,
  overwrite = FALSE,
  dispatch = c("candidates", "static"),
  eval_dispatch = TRUE
)

slice_call(
  expr,
  output_dir,
  max_depth = 5L,
  overwrite = FALSE,
  dispatch = c("candidates", "static"),
  eval_dispatch = TRUE
)

unpack_call() creates this project shape:

replay_dir/
  replay.R                    # extracted code, input loading, verification
  customize.R                 # editable extracted function definitions
  compare.R                   # rerun customize.R and compare results
  insider_manifest.rds        # machine-readable metadata
  data/
    *.rds                     # captured inputs
    original_result.rds       # result from the original call
    hidden_state.rds          # captured options/RNG state when relevant

Hidden state is scoped and restored where supported. Behavior-changing options and RNG state are handled when detected; locale, timezone, and other uncaptured state are reported rather than silently promised reproducible.

slice_call() adds dependencies.json, insider_context.md, insider_context.json, and validation_plan.md, and returns an insider_slice object. resolve_dependencies() summarizes extracted and unresolved functions, captured objects, packages, compiled boundaries, dispatch candidates, and runtime unknowns. It accepts an insider_unpack or insider_trace object.

Graph and change

  • change_impact(graph, name, transitive = TRUE) reports callers, exported APIs reached, risk notes, related tests, and suggested validation commands.
  • find_modification_points(graph, name) ranks reachable functions that may be safer places to customize.
  • extract_function(package, name, output_file, max_depth = 5L, dispatch = c("candidates", "static"), overwrite = FALSE) writes extracted source without invoking the target function.
  • diff_versions(package, name, v1, v2, lib = .libPaths(), timeout = 30) compares one function across two already-installed package versions.

Compare and patch

compare_call(custom_result, original_result, ignore = character(), tolerance = sqrt(.Machine$double.eps)) distinguishes identical results, equality within tolerance, structural differences, and results that are not meaningfully comparable. compare.R uses this function after you edit customize.R.

For in-memory experiments, with_patch(expr, patches, ignore = character(), tolerance = sqrt(.Machine$double.eps)) temporarily replaces named function bindings for one call, restores them afterward, and compares the result. propose_change(expr, patches, graph, ignore = character(), tolerance = sqrt(.Machine$double.eps), output_dir = NULL) combines a temporary patch with graph impact and an advisory validation handoff. If output_dir is supplied, it writes proposal.md and proposal.json.

Store

insider_store(dir = ".", create = FALSE) finds or creates a project-local .insider/ store. Saving is explicit:

store <- insider_store(".", create = TRUE)
id <- store_save(store, explanation, label = "fivenum explanation")
store_list(store)
saved <- store_get(store, id)
store_remove(store, id)

The store is intended for single-user curation; concurrent writers are not coordinated.

Safety

  • explain_call(), unpack_call(), and related extraction paths perform a static AST-oriented scan of inspected R code for calls that warrant review, such as shell execution, network access, dynamic evaluation, file changes, global-state mutation, and compiled entry points.
  • scan_secrets(unpacked) scans character values in captured .rds inputs and generated textual artifacts for common secret-shaped patterns. It reports redacted previews; it does not inspect arbitrary binary objects or guarantee that secrets are absent.
  • check_dependency_risk(x, lookup = oysteR_lookup) checks dependency/version pairs from a dependency report, or package names, through an injectable lookup. The default uses oysteR when available; a lookup failure is reported as not checked.

These are review aids, not malware detection, complete secret detection, or a security guarantee. Review captured .rds files and generated scripts before sharing or executing them.

Status and limitations

The capabilities above are implemented and exported in the current package. insideR is not a replacement for R packages, Git, a debugger, a dependency manager, or a full security scanner. It does not mutate installed package source when applying a temporary patch.

Expect partial extraction, unresolved dependencies, or replay differences for:

  • compiled C/C++/Fortran internals and other opaque boundaries;
  • complex S4/R6 systems or runtime dispatch that cannot be resolved;
  • heavy tidy evaluation or non-standard evaluation;
  • database, API, filesystem, time, locale, timezone, or other external side effects;
  • Shiny/reactive workflows and parallel execution;
  • hidden global state that is not captured by the supported state snapshot.

Graph edges and dependency classifications are evidence from static analysis, namespace inspection, and (for traces) observed execution. They are not a complete proof of every possible runtime path. Validate any customization with the generated replay, relevant package tests, and R CMD check as appropriate.

Philosophy

insideR supports a deliberate promotion path:

local customization -> repeated useful pattern -> documented recipe
  -> formal option -> package feature

Users get visibility and room to experiment, maintainers retain control of the package API, and coding agents receive focused context plus explicit unknowns and validation prompts.