Skip to contents

insideR (insider) is an R package-call x-ray for inspectable R code.

It opens one R package call, shows what actually runs, captures the inputs, extracts the relevant R logic where possible, and creates a self-verifying replay script that a human or coding agent can inspect, modify, and compare.

It is best suited to package behavior that is implemented in R or can be traced through R-visible source. It still works when that logic calls base R or other R packages, but it does not decompile compiled internals or fully model opaque non-R side effects.

The goal is not to replace R packages with loose scripts. The goal is to make package-based work less black-box while keeping the discipline of packages: namespaces, documentation, tests, maintainers, and reusable APIs.

Why this exists

R packages are the right way to build reusable statistical and data-science software. But in applied work, package calls can become opaque:

  • Which method actually ran?
  • Which internal helpers were used?
  • Which inputs and hidden state affected the result?
  • Which parts are pure R and which parts depend on external packages, compiled code, or other opaque boundaries?
  • Where can I safely customize behavior without editing the installed package?
  • What changed after customization?
  • What should a coding agent read before touching the repo?

insideR is designed for that gap.

It turns this:

predict(fit, newdata = recent_data)

into a small, inspectable replay artifact:

predict_replay/
  replay.R              # extracted functions + input loading + verification
  data/
    fit.rds             # captured inputs
    recent_data.rds
    original_result.rds # original package result
  insider_manifest.rds  # machine-readable unpack metadata

replay.R records provenance, includes a static security/risk scan, reruns the extracted logic, and verifies the replayed result against the stored original result.

The core idea

Most projects use only a small slice of a package.

insideR focuses on that slice:

call -> resolved method -> internal helpers -> external dependencies -> result

The intended workflow is:

trace -> resolve dependencies -> slice -> replay -> customize -> compare -> handoff

That workflow serves two audiences:

  • Humans get comfort through transparency: they can see what the package call did before trusting or changing it.
  • Agents get compact context: they can see the relevant files, functions, dependencies, risks, and validation steps before editing anything.

What works now

The current package already supports the first practical layer: explain, unpack, replay, and graph.

Explain a call

insider::explain_call(
  predict(fit, newdata = recent_data)
)

explain_call() reports:

  • the original call;
  • the resolved function or S3 method;
  • internal and external calls;
  • replayability status;
  • compiled-code boundaries;
  • static security/risk findings.

Unpack a call

insider::unpack_call(
  predict(fit, newdata = recent_data),
  output_dir = "predict_replay"
)

unpack_call() creates a replay folder with captured inputs, extracted functions, the original result, a manifest, and a self-verifying replay.R.

Run:

source("predict_replay/replay.R")

The replay script verifies that the replayed output still matches the original result. When the source package is installed, it can also compare against a live package call.

Build a package graph

g <- insider::build_graph("mgcv")
g <- insider::build_graph("~/repos/myPackage")

insider::graph_search(g, "predict")
insider::graph_node(g, "build_features")
insider::graph_callers(g, "build_features")
insider::graph_callees(g, "predict.my_model")

build_graph() indexes source directories or installed packages into a queryable R code graph: functions, classes, methods, imports, and call edges. Source-directory graphs include file and line-number information.

Current API

Implemented:

explain_call(expr, max_depth = 5)
unpack_call(expr, output_dir, max_depth = 5, overwrite = FALSE)

build_graph(x, cache = TRUE, refresh = FALSE)
graph_search(graph, pattern, kind = NULL)
graph_node(graph, name)
graph_callers(graph, name)
graph_callees(graph, name, internal_only = FALSE)

Planned next:

trace_call(expr)
resolve_dependencies(expr)
slice_call(expr, output_dir)
build_context(target, task, format = c("md", "json"))
compare_call(path)
propose_change(path)

Detailed planning and prioritization are tracked in Obsidian project memory, not in the package repository.

Human use case

A human analyst can ask:

I trust this package enough to use it, but I need to understand and customize one behavior. Where is that behavior, and can I change it safely?

insideR should help them:

  1. identify the actual method/function used;
  2. inspect the internal helper path;
  3. replay the call outside the installed package;
  4. make a local customization;
  5. compare the changed result to the original;
  6. decide whether the change is a one-off local variation or a package-worthy improvement.

Agent use case

A coding agent should not blindly search an entire package repo for one requested change.

insideR should give the agent a compact task packet:

insider_context.md
insider_context.json
dependencies.json
call_graph.json
modification_points.md
validation_plan.md

Those artifacts should answer:

  • which functions matter for this task;
  • which files define them;
  • which functions call or are called by them;
  • which dependencies are internal, external, compiled, or unresolved;
  • where modification is safest;
  • what validation commands should be run.

This makes insideR a bridge between R package internals and agent-assisted maintenance.

What insideR is not

insideR is not:

  • a replacement for R packages;
  • a replacement for Git;
  • a promise that every R call can become package-free code;
  • a full debugger;
  • a full dependency manager;
  • a full security scanner;
  • a chat-style autonomous agent;
  • a way to bypass maintainers and mutate production packages directly.

It is a transparency and replay layer around package calls.

Security and provenance

Transparency includes knowing what extracted code can do.

Every explain/unpack operation includes a static risk scan for calls worth reviewing, including:

  • shell command execution;
  • network access;
  • dynamic code evaluation;
  • file-system modification;
  • environment/global-state mutation;
  • compiled/internal entry points.

The generated replay script also records package provenance: package name, version, source repository where available, R version, and generation time.

The static scan is a review aid, not a malware detector.

Scope and limitations

Current support is strongest for:

  • plain R functions;
  • exported package functions;
  • S3 methods;
  • non-exported namespace helpers where possible;
  • input capture using .rds files;
  • source extraction using R introspection;
  • simple call-tree reporting;
  • replay project generation;
  • static package/source graphs.

Known hard cases should be reported honestly rather than hidden:

  • compiled C/C++/Fortran internals;
  • complex S4/R6 systems;
  • heavy tidy-evaluation/non-standard evaluation;
  • database/API calls;
  • Shiny/reactive workflows;
  • parallel execution;
  • hidden global state;
  • runtime dispatch inside internal call trees.

In practice, the package is strongest when the interesting behavior lives in ordinary R functions, S3 methods, and helper chains that R can inspect.

Beyond the unit test suite, this scope claim is checked against real, unmodified CRAN packages — currently httr and mgcv, chosen for different domains and failure modes. See notes/validation.md for the methodology and results.

Roadmap

The next product direction is package-slice extraction for human-agent workflows.

Priority sequence:

  1. trace_call() — capture the actual runtime path for a concrete call.
  2. resolve_dependencies() — separate internal functions, external packages, captured objects, compiled boundaries, and unresolved references.
  3. slice_call() — create a lightweight call-specific mini-pipeline.
  4. graph provenance/confidence/unresolved references.
  5. explain_function() — explain a function even before a concrete call exists.
  6. build_context() — produce markdown/JSON context for coding agents.
  7. compare_call() and proposal bundles.

The detailed roadmap is maintained in Obsidian project memory so the package repository stays focused on user-facing docs and implementation.

Philosophy

The package should protect both sides:

  • users get visibility, safety, and freedom to experiment;
  • maintainers keep a clean package API and decide what becomes official;
  • agents get compact context and validation plans before they edit.

The promotion path is:

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

insideR exists to make that path visible, testable, and manageable.