pedrohcgs/capture-environment
Snapshot the computational environment for a replication package — detects the analysis stack (R / Stata / Python) and emits the right lockfiles (renv.lock + sessionInfo.txt, requirements.txt / environment.yml / uv.lock, Stata version + ado package list), records seeds and RNG kind, optionally writes a pinning Dockerfile, and produces a paste-ready "Computational requirements" block. Use when user says "capture the environment", "snapshot my dependencies", "pin the versions", "make a renv.lock / requirements.txt", "make this byte-reproducible", or before releasing a replication package to openICPSR / the AEA Data Editor.
npx skills add https://github.com/pedrohcgs/claude-code-my-workflow --skill capture-environment
/capture-environment — snapshot the computational environmentA replication package that runs on the author's laptop in 2026 and nowhere else in 2029 is not reproducible. This skill captures the *exact* computational environment — language versions, package versions, seeds, RNG kind, and (optionally) the OS layer — so a referee, the AEA Data Editor, or future-you can reconstruct it. It detects which stack the project uses and emits the artifacts that stack's ecosystem expects, then verifies the lockfile installs clean.
Core principle: Pin everything a result depends on. Display rounding aside, a re-run on a pinned environment should reproduce the paper to the replication-protocol.md tolerances — *byte-identical* when the optional Dockerfile is used.
/audit-reproducibility — that skill checks the *numbers*; this one captures the *environment* those numbers were produced in (its sessionInfo.txt requirement is satisfied by this skill).$0 — project directory. Defaults to the repo root. The skill looks under scripts/R/, scripts/stata/, scripts/python/.--docker — also emit a Dockerfile pinning OS + language version + system libraries for byte-identical reproduction.--no-verify — skip Phase 3 (the best-effort clean-install check). Useful in CI or when the toolchain isn't installed locally.Glob for stack signals and decide which capture paths to run (a project may be multi-language — DiD in R, an IV robustness check in Stata):
| Signal | Stack | Capture path |
|---|---|---|
| scripts/R/*.R, DESCRIPTION, renv/, *.Rproj | R | renv + sessionInfo |
| scripts/python/*.py, *.ipynb, pyproject.toml, requirements.txt, environment.yml, uv.lock | Python | pip / conda / uv |
| scripts/stata/*.do | Stata | version + ado list |
If no signal is found, report and stop — there is no environment to capture.
R — emit two artifacts:
renv.lock via renv::snapshot() (run renv::init(bare = TRUE) first if the project isn't renv-managed; snapshot records every package + version + source/remote and the R version). Honors the seed conventions in r-code-conventions.md.sessionInfo.txt via Rscript -e "writeLines(capture.output(sessionInfo()), 'scripts/R/_outputs/sessionInfo.txt')" — the human-readable companion /audit-reproducibility looks for.Python — emit whichever matches the project's existing tooling (do not invent a new one):
uv.lock (preferred when pyproject.toml + uv present — fully-resolved, hashed, cross-platform): uv lock / uv export --format requirements-txt > requirements.txt.requirements.txt via pip freeze (or python -m pip freeze) for a venv/pip project — pin == exactly.environment.yml via conda env export --no-builds for a conda project.Always also record the interpreter version (python --version) in the report.
Stata — Stata has no lockfile, so capture the closest equivalents (mirrors stata-code-conventions.md §3):
version line each .do file declares (e.g. version 18) — grep scripts/stata/*.do and report the version actually pinned..do that runs which on the user-installed commands the pipeline uses (reghdfe, ivreg2, estout/esttab, rdrobust, csdid, …) plus ado dir and about, logged to scripts/stata/_outputs/sessionInfo.txt.version 18 fixes command behavior), not a binary pin — the Dockerfile (Phase 2) cannot help here because Stata is licensed and not redistributable; record the exact Stata version + flavor (SE/MP/IC) + update level in the report so a replicator can match it.Grep the analysis scripts for the master seed and RNG kind so the "Computational requirements" block can state them:
set.seed(YYYYMMDD), and RNGkind() — flag "L'Ecuyer-CMRG" if parallel/Monte Carlo work is present (see simulation-conventions.md).set seed and set sortseed.numpy.random.default_rng(seed) / random.seed() / framework seeds.If the pipeline does randomized work (bootstrap, MC, RCT re-randomization, permutation inference) and no seed is found, surface it as a WARNING — an unseeded random result is not reproducible.
--docker)Emit a Dockerfile that pins the OS + language version + system libraries for byte-identical reproduction:
FROM rocker/r-ver:<X.Y.Z> (Rocker pins the R version), COPY renv.lock, RUN R -e "renv::restore()", plus apt-get install for system libs the packages need (e.g. libcurl4-openssl-dev, libgdal-dev for spatial work).FROM python:<X.Y.Z>-slim, COPY requirements.txt / uv.lock, RUN pip install -r requirements.txt (or uv sync --frozen).Dockerfile stub that documents the expected Stata version + flavor and leaves the stata install/license step to the replicator (with a comment pointing at the AEA's guidance on Stata images).Pin a digest where possible (FROM image@sha256:…) so the base image can't drift.
--no-verify)Attempt a clean restore in a throwaway location and report PASS / FAIL — never overwrite the working environment:
renv::restore() into a temp library, or Rscript -e "renv::status()" for a dry check.uv sync --frozen / pip install --dry-run -r requirements.txt into a fresh venv.--docker): docker build the image.A FAIL here means the lockfile references a package version that can't be resolved (yanked release, private remote, platform-specific wheel). Report it; do not auto-edit the lockfile.
Print a paste-ready block and write it to scripts/<lang>/_outputs/computational_requirements.md:
## Computational requirements
**Software:** R 4.4.1 (or: Stata 18.0 SE, update 2026-01-15; Python 3.12.3)
**OS used:** macOS 15.5 (arm64) — Dockerfile pins Ubuntu 24.04 for portability
**Key packages:** fixest 0.12.1, did 2.1.2 (full list in renv.lock)
**Random seeds:** set.seed(20260609); RNGkind("L'Ecuyer-CMRG") for the bootstrap
**Approx. runtime:** [author confirms — e.g. ~12 min, 8 cores]
**Lockfiles in package:** renv.lock, scripts/R/_outputs/sessionInfo.txt[, Dockerfile]
Pre-fill software/package/seed lines from the captured artifacts; leave runtime for the author to confirm.
| Stack | Files written |
|---|---|
| R | renv.lock, scripts/R/_outputs/sessionInfo.txt |
| Python | requirements.txt *or* environment.yml *or* uv.lock (matching project tooling) |
| Stata | scripts/stata/_outputs/sessionInfo.txt (version + ado list) |
| Any (--docker) | Dockerfile |
| Always | scripts/<lang>/_outputs/computational_requirements.md (the paste-ready block) |
--no-verify): exit 0, requirements block printed./commit. Report the unresolvable package; do not silently "fix" the lockfile..claude/rules/replication-protocol.md — the tolerance contract a pinned environment is meant to reproduce..claude/rules/r-code-conventions.md — R seeding + output-path conventions this skill reads..claude/rules/stata-code-conventions.md — §3 sessionInfo.txt + version-pinning the Stata path mirrors..claude/rules/simulation-conventions.md — L'Ecuyer streams for reproducible parallel/MC work..claude/rules/confidential-data.md — when raw data is restricted, the *environment* still ships even though the data does not; coordinate the README's "data availability" section with this block./audit-reproducibility — consumes the sessionInfo.txt this skill produces; run it after./data-analysis, /stata-replication, /simulation-study — the pipelines whose environment this snapshots./audit-reproducibility verifies the manuscript's numeric claims against the outputs.confidential-data.md.Take pedrohcgs/capture-environment from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference pip, apt.
Without those the skill loads but fails at the first command.