simulation-study — Claude Code skill: what it does, how to install
Scaffolds and runs reproducible Monte Carlo experiments in R. Evaluates estimator bias, RMSE, coverage, and power with Monte Carlo standard errors.
npx skills add pedrohcgs/claude-code-my-workflow --skill simulation-study -a claude-codeWhat simulation-study does
The skill sets up Monte Carlo simulation workflows in R around verified data-generating processes. It begins with a pre-flight report that defines the target estimand and required assumptions. If the ground truth cannot be computed directly from DGP parameters, the skill pauses. Guessing estimands invalidates the whole study.
Code is written to a numbered script in scripts/R/. Each iteration generates data and runs candidate estimators while tracking convergence. The runner initializes RNG seeds with set.seed() and supports parallel execution through furrr. Per-replication outcomes are saved directly to output/ via saveRDS() rather than collapsed immediately into averages.
Summary tables compute bias, RMSE, and coverage alongside Monte Carlo standard errors. Visual plots are produced with ggplot2. Before presenting the final figures, the workflow runs the sim-reviewer subagent to catch mismatched estimands or silent drops of failed iterations.
What it helps with
- scaffold reproducible Monte Carlo simulation studies in R
- evaluate estimator bias and confidence interval coverage
- track replication failures without dropping rows silently
- calculate Monte Carlo standard errors for headline metrics
Example prompts for the agent
- /simulation-study compare 2SLS vs LIML under weak instruments with heteroskedasticity
- Run a Monte Carlo simulation study to check the bias and coverage of our lasso estimator
- /simulation-study reproduce the simulation from scripts/R/original_experiment.R
How to install simulation-study
1Skills CLI (Vercel)
npx skills add pedrohcgs/claude-code-my-workflow --skill simulation-study -a claude-code2Manually: put the files in place
~/.claude/skills/simulation-study/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/simulation-study/
1Skills CLI (Vercel)
npx skills add pedrohcgs/claude-code-my-workflow --skill simulation-study -a codex2Manually: put the files in place
.agents/skills/simulation-study/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add pedrohcgs/claude-code-my-workflow --skill simulation-study -a cursor2Manually: put the files in place
.cursor/skills/simulation-study/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install pedrohcgs/claude-code-my-workflow simulation-study2Manually: put the files in place
.github/skills/simulation-study/SKILL.md1Skills CLI (Vercel)
npx skills add pedrohcgs/claude-code-my-workflow --skill simulation-study -a gemini-cli2Platform CLI
gemini skills install https://github.com/pedrohcgs/claude-code-my-workflow --path .claude/skills/simulation-studyManually: put the files in place
.devin/skills/simulation-study/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add pedrohcgs/claude-code-my-workflow --skill simulation-study -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about simulation-study
How do I install simulation-study in Claude Code?
Run npx skills add pedrohcgs/claude-code-my-workflow --skill simulation-study -a claude-code in your terminal.
What does the simulation-study skill do?
It scaffolds and executes Monte Carlo experiments in R, saving raw replication data as RDS files and computing performance metrics with Monte Carlo standard errors.
Why does simulation-study invoke the sim-reviewer agent?
The skill delegates script review to the sim-reviewer agent to detect coverage bugs, dropped iterations, or estimand mismatches before finalizing results.
Source text
SKILL.md · MIT
---
name: simulation-study
description: Scaffold and run a reproducible Monte Carlo simulation study in R — a declared assumption regime, a parameterized DGP, an estimator grid, a seeded replication loop, and a summary of bias, RMSE, empirical SE, coverage, size/power with Monte Carlo standard errors. Use when the user says "run a Monte Carlo simulation", "simulation study", "check the bias/coverage of an estimator", "compare estimators in simulation", "size and power simulation", "Monte Carlo experiment", or wants to demonstrate an estimator's finite-sample properties. Produces a numbered R script in `scripts/R/` and saves per-replication raw results + a summary table to `output/`.
argument-hint: "[estimator(s) and DGP to study, or path to a script/paper to simulate from]"
disable-model-invocation: true
allowed-tools: ["Read", "Grep", "Glob", "Write", "Edit", "Bash", "Agent", "Task", "Monitor"]
effort: high
metadata:
author: Claude Code Academic Workflow
version: 1.0.0
---
# `/simulation-study` — Monte Carlo Simulation Study
Design and run a Monte Carlo experiment that characterizes an estimator's finite-sample behavior, then review it for the bugs that quietly invalidate simulation evidence.
**Input:** `$ARGUMENTS` — a description of the estimator(s) and DGP to study (e.g., "compare 2SLS vs LIML under weak instruments with heteroskedasticity"), or a pointer to an existing script/paper whose simulation you want to reproduce or extend.
---
## Constraints
- **Follow [`.claude/rules/simulation-conventions.md`](../../rules/simulation-conventions.md)** — the simulation contract (DGP, truth, estimand, MCSE, assumption regime) is non-negotiable.
- **Declare the assumption regime in the script header** and respect the firewall — an out-of-assumption run never supports a within-assumption claim ([`simulation-conventions.md`](../../rules/simulation-conventions.md) §2).
- **Follow [`.claude/rules/r-code-conventions.md`](../../rules/r-code-conventions.md)** for general R standards (header, `library()` at top, relative paths, numerical discipline).
- **Save the script** to `scripts/R/` with a numbered, descriptive name (e.g., `scripts/R/sim_2sls_vs_liml.R`).
- **Save outputs** (per-rep raw tibble, summary table, figures) to `output/`.
- **`saveRDS()` the per-replication raw results**, not just the summary — re-aggregation and the review pass need them.
- **Run the `sim-reviewer` agent** on the generated script before presenting results, then address Critical/High findings.
---
## Workflow Phases
### Phase 0: Pre-Flight Report
**Before writing any code, produce a Pre-Flight Report** showing you have pinned down the experiment. This prevents the most common failure mode — a beautiful results table built on a mismatched estimand or a coverage-against-the-estimate bug.
```markdown
## Pre-Flight Report — Simulation Design
**Research question:** [what finite-sample property is being demonstrated]
**Target estimand:** [ATT / ATE / coefficient θ — and how its TRUE value is computed from the DGP params]
**Maintained assumptions:** [the FULL list the estimator(s) under study require — A1 … An, every one]
**Regime:** [IN-ASSUMPTION — all hold | OUT-OF-ASSUMPTION — relaxes A[k] only, severity grid {…}, targeting pseudo-estimand …]
**Verification:** [per assumption, the checkable property of the DGP that establishes it — by construction or by an assertion]
**DGP:** [structure + the parameters that define it; what is held fixed vs. varied]
**Estimator grid:** [list each estimator + which estimand it targets + how it returns est/se/CI]
**Design grid:** [sample sizes, parameter values, scenarios to sweep]
**Replications R:** [value] → implied MCSE on coverage ≈ sqrt(0.95·0.05/R) = [value]
**Metrics:** bias, empirical SE, RMSE, coverage, size/power — each with MCSE
**Conventions read:** simulation-conventions.md, r-code-conventions.md
```
If the estimand or its true value is ambiguous, **stop and ask** before writing code.
If an assumption cannot be verified — you cannot name the property of the DGP that establishes it — the run is **not** `IN-ASSUMPTION`, and per the firewall no within-assumption claim may rest on it. Say so in the Pre-Flight Report rather than letting the header assert what was never checked.
### Phase 1: The DGP
Write **one** parameterized function that returns a dataset. Compute and return (or store) the **true** target value from the parameters.
```r
generate_data <- function(n, params) {
# ... generate covariates, treatment, outcome from params ...
list(data = df, truth = compute_truth(params)) # truth from params, never from an estimate
}
```
The header's **Verified** lines are earned here: every assumption the regime block claims holds *by a check* gets that check written into the script (a large-draw assertion, a condition number, a `stopifnot()` on the parameter bounds), run once at setup. An assumption whose verification exists only in the comment is asserted, not verified.
### Phase 2: Estimator Grid
Each estimator is a function `data -> list(est, se, ci_lo, ci_hi, converged)`. State the estimand each one targets; an estimator scored against a mismatched truth is a bug, not a finding.
### Phase 3: Replication Engine
- `set.seed(YYYYMMDD)` **once**. For parallel reps use `RNGkind("L'Ecuyer-CMRG")` and `furrr::furrr_options(seed = TRUE)`.
- One run = generate data → run every estimator → record a row per estimator with `est, se, ci_lo, ci_hi, converged`.
- Pre-allocate / bind results into a tibble of `R × (#estimators)` rows. Track non-convergence; never silently drop.
### Phase 4: Metrics & Summary
Per estimator × scenario, against **truth**:
- **Bias** = `mean(est) - truth` (+ MCSE = `sd(est)/sqrt(R)`)
- **Empirical SE** = `sd(est)`; **RMSE** = `sqrt(mean((est - truth)^2))`
- **Coverage** = `mean(ci_lo <= truth & truth <= ci_hi)` (+ MCSE = `sqrt(p(1-p)/R)`)
- **Size / power** = rejection rate under the null / alternative DGP
- **Failures** = count of non-converged reps
Build a tidy summary table; report MCSE next to every headline metric.
### Phase 5: Figures
Use `ggplot2` with the project theme: bias / coverage vs. sample size (or scenario), with reference lines (0 bias, nominal coverage). Transparent background, explicit dimensions (per `r-code-conventions.md` §4).
### Phase 6: Save & Review
1. `saveRDS()` the **raw per-rep tibble** and the **summary table** to `output/`; also write the summary as `.csv`/`.tex`.
2. Run the review:
```
Delegate to the sim-reviewer agent:
"Review the simulation script at scripts/R/[name].R"
```
The agent is read-only and returns its report; save it to `quality_reports/[name]_sim_review.md`.
3. Address Critical/High findings (coverage-vs-truth, estimand mismatch, missing MCSE, dropped reps, an unstated or unverified regime) before presenting.
4. **Apply the firewall to the presentation itself.** Every claim you are about to make must cite a run whose regime can bear it — consistency, valid analytic standard errors, nominal coverage, and shipping a default require an `IN-ASSUMPTION` run and nothing else ([`simulation-conventions.md`](../../rules/simulation-conventions.md) §2). Carry the regime in every caption — and per row wherever a severity grid mixes the two.
---
## Script Structure
```r
# ============================================================
# [Title] — Monte Carlo simulation
# Author: [project context]
# Purpose: [property being demonstrated]
# Estimand: [target + how truth is computed]
# Maintained assumptions: [A1 ... An — the FULL list the estimator requires]
# Regime: [IN-ASSUMPTION | OUT-OF-ASSUMPTION: relaxes A[k] only, severity ...,
# targeting pseudo-estimand ...]
# Verified: [per assumption, the property that was actually checked, not asserted]
# Outputs: output/[name]_raw.rds, [name]_summary.{rds,csv}
# ============================================================
# 0. Setup ----
library(tidyverse)
library(furrr) # parallel reps (optional)
plan(multisession) # enable parallel workers; omit this line to run sequentially
RNGkind("L'Ecuyer-CMRG")
set.seed(20260531) # once, YYYYMMDD (simulation-conventions.md §3)
R <- 2000L # MCSE on coverage near .95 ≈ 0.005
dir.create("output", recursive = TRUE, showWarnings = FALSE)
# 1. DGP ----
generate_data <- function(n, params) { ... } # returns list(data, truth)
# 2. Estimators ----
estimators <- list(tsls = est_tsls, liml = est_liml) # each -> est, se, ci, converged
# 3. Run one replication ----
run_one_rep <- function(rep_id, n, params) { ... } # -> tibble rows (one per estimator)
# 4. Replicate ----
raw <- future_map_dfr(seq_len(R), run_one_rep, n = n, params = params,
.options = furrr_options(seed = TRUE))
# 5. Summarize (vs truth, with MCSE) ----
# Group by EVERY design-grid dimension you sweep (estimator, n, scenario, ...) so
# each group has a single true value. Use per-row `truth` — never `truth[1]` — so a
# truth that varies across the grid can't be silently mis-scored. Score only the
# converged reps; report failures separately.
summary_tbl <- raw |>
filter(converged) |>
group_by(estimator) |> # add n, scenario, ... as needed
summarise(
R_eff = n(),
bias = mean(est - truth),
emp_se = sd(est),
rmse = sqrt(mean((est - truth)^2)),
coverage = mean(ci_lo <= truth & truth <= ci_hi),
.groups = "drop"
) |>
mutate(
bias_mcse = emp_se / sqrt(R_eff),
cov_mcse = sqrt(coverage * (1 - coverage) / R_eff)
)
failures <- raw |> group_by(estimator) |> summarise(n_fail = sum(!converged), .groups = "drop")
# Size/power: add `power = mean(reject)` (+ `sp_mcse = sqrt(power*(1-power)/R_eff)`)
# to the summary above — each estimator must emit a per-rep `reject = p_value < alpha`
# column. Size = rejection rate under the null DGP; power = under the alternative.
# 6. Export ----
saveRDS(raw, "output/[name]_raw.rds")
saveRDS(summary_tbl, "output/[name]_summary.rds")
write_csv(summary_tbl, "output/[name]_summary.csv")
```
---
## Important
- **State the regime, then respect the firewall.** A DGP that violates a maintained assumption produces a table on which every other check here passes — and a within-assumption claim built on it is unsupported however clean the numbers look.
- **The truth comes from the DGP, never from an estimate.** Coverage is the CI containing the *true* parameter.
- **No result without an MCSE.** If two estimators differ by less than ~2× MCSE, say so.
- **Save raw, not just summary.** A number that exists only in the console cannot be audited or put on a slide.
- **Count your failures.** Silently dropped non-converged reps bias every metric.
## Long-running simulations: use the Monitor tool
Large grids (many scenarios × large `R`) can run for many minutes. Background-launch via Bash with `run_in_background: true`, writing R stdout and stderr to a log (e.g. `Rscript scripts/R/[name].R > output/[name].log 2>&1`), and run the **Monitor tool** with a command that tails that log through `grep --line-buffered`, matching progress milestones (e.g. a `progressr` update) and failure signatures (`Error`, `Execution halted`), instead of polling with `sleep`. Monitor has no job-id parameter: the stdout of its own command is the event stream. See [`data-analysis/SKILL.md`](../data-analysis/SKILL.md) and the guide's Cost-Conscious Parallelism section.
© Claude Code Academic Workflow · MIT · pedrohcgs/claude-code-my-workflow