# Clean Room (R)

> **Beta Access** — Clean Room is currently in beta and not yet generally available. To request early access, email <info@gofigr.io>.

> Working in Python instead? See [Clean Room (Python)](/features/clean-room-python.md).

## Overview

Clean Room turns an R function into a self-contained, reproducible, interactive application. You wrap a function with `reproducible()`, and GoFigr draws a clean boundary around it: the function can only access the parameters you declare, the packages you list, and `publish()`. When the function produces a figure, everything—source, data, parameters, environment—is captured and published automatically.

The workflow:

1. **Explore** — work however you normally work in RStudio or R Markdown
2. **Distill** — pull the core logic into a function with declared parameters
3. **Run** — wrap it in `reproducible()`; GoFigr captures everything
4. **Share** — send a link; stakeholders interact with the figure in the browser

## Quick Start

Load the package and configure it:

```r
library(gofigR)
library(ggplot2)

gofigR::enable(workspace_name = "Analytics",
               analysis_name  = "Penguins")
```

### Simplest Example

```r
reproducible(
  function(data = static(iris)) {
    p <- ggplot(data, aes(x = Sepal.Length, y = Petal.Length, color = Species)) +
      geom_point()
    publish(p, figure_name = "Iris Scatter")
  },
  packages = c("ggplot2")
)
```

That's it. When the function runs, GoFigr captures:

* **Source code** — the function body, extracted via R's AST
* **Parameters** — types, defaults, and widget metadata for every argument
* **Data** — data frames passed via `static()`, serialized as Parquet
* **Environment** — package names and versions, R version
* **Output** — the figures published by the run

## Interactive Mode

Add `interactive = TRUE` to launch a Shiny gadget with parameter widgets. Adjusting any control re-runs the function and updates the plot. Click **Publish** to push a revision with the current parameter values.

```r
reproducible(
  function(
      point_size = slider(2, min = 0.5, max = 5, step = 0.5),
      alpha      = slider(0.7, min = 0.1, max = 1.0, step = 0.1),
      color_by   = dropdown("Species",
                            choices = c("Species", "Sepal.Width", "Petal.Width")),
      show_smooth = checkbox(FALSE),
      data        = static(iris)
    ) {
      p <- ggplot(data, aes(x = Sepal.Length, y = Petal.Length,
                            color = .data[[color_by]])) +
        geom_point(size = point_size, alpha = alpha) +
        theme_minimal()

      if (show_smooth) {
        p <- p + geom_smooth(method = "lm", se = FALSE)
      }

      publish(p, figure_name = "Iris Scatter")
    },
    packages = c("ggplot2"),
    interactive = TRUE
)
```

Interactive mode requires a live, interactive R session. Inside `knitr`/R Markdown rendering or non-interactive scripts the gadget is skipped automatically and the function runs once with its default parameter values.

### Choosing the viewer

By default the gadget opens in the RStudio Viewer pane (when RStudio is available) or in a dialog window. Override with the `viewer` argument:

```r
# RStudio Viewer pane
reproducible(fn, packages = "ggplot2", interactive = TRUE,
             viewer = shiny::paneViewer())

# External browser
reproducible(fn, packages = "ggplot2", interactive = TRUE,
             viewer = shiny::browserViewer())

# Modal dialog
reproducible(fn, packages = "ggplot2", interactive = TRUE,
             viewer = shiny::dialogViewer("Clean Room", width = 1100, height = 700))
```

## Parameter Widgets

Parameters are declared as **function defaults** using one of the parameter constructors below. Plain defaults (e.g. `bins = 20`) are accepted and treated as static values, but for interactive mode you'll want explicit widget constructors.

### `slider()`

Numeric slider for `int` and `numeric` values.

```r
bins  = slider(20L, min = 5L,  max = 50L,  step = 5L)    # integer
alpha = slider(0.7, min = 0.1, max = 1.0,  step = 0.05)  # numeric
```

Pass an integer literal (`20L`) to get an integer slider; the gadget preserves integer type when feeding the value back into your function.

### `dropdown()`

Categorical dropdown with explicit choices.

```r
species = dropdown("Adelie", choices = c("Adelie", "Chinstrap", "Gentoo"))
```

### `checkbox()`

Boolean toggle.

```r
show_grid = checkbox(TRUE)
```

### `text_input()`

Free-form text input.

```r
title = text_input("Flipper Length Distribution")
```

### `static()`

Read-only value — no widget is rendered. Use this for data frames and any other value you want captured but not edited interactively. Plain defaults are wrapped in `static()` automatically, so `data = iris` and `data = static(iris)` are equivalent.

```r
data = static(penguins)
```

In the Clean Room studio the value is available for inspection, and data frames are serialized alongside the revision as Parquet.

## Packages

Declare every package the function needs in the `packages` argument. The clean execution environment will only have access to functions exported by those packages (plus base R and `publish()`).

```r
reproducible(fn, packages = c("ggplot2", "dplyr"))
```

### Pinning versions

`packages` accepts a named list to pin explicit versions in the manifest:

```r
reproducible(fn, packages = list(ggplot2 = "3.5.0", dplyr = "1.1.4"))
```

If you pass an unnamed character vector, the currently installed versions are resolved automatically.

### Imports / aliases

Use the `imports` argument to record alias-to-package mappings in the manifest (useful for parity with Python clients and for re-creating the environment elsewhere):

```r
reproducible(fn,
             packages = c("ggplot2"),
             imports  = list(plt = "ggplot2"))
```

## Publishing

Call `publish()` inside the function body. It is always injected into the clean environment — there is no separate "publisher" argument like in Python. The active session set up by `gofigR::enable()` determines where the figure goes.

```r
reproducible(
  function(data = static(mtcars)) {
    p <- ggplot(data, aes(mpg)) + geom_histogram(bins = 20)
    publish(p, figure_name = "MPG Distribution")
  },
  packages = c("ggplot2")
)
```

In interactive mode `publish()` is replaced with a no-op preview while you tweak parameters; clicking **Publish** in the gadget runs the function one more time with the real `publish()` and shows a link plus QR code to the new revision.

### What gets stored

Each published revision includes:

* **Source code** — the function body, extracted from the R AST
* **Manifest** — JSON with parameter types, widget metadata, package versions, and R version
* **DataFrame parameters** — serialized as Parquet
* **Revision flag** — marks the revision as a Clean Room revision

### Naming the function

Pass `name = "..."` to give the clean room function a display name in the webapp. Without it the function is anonymous.

```r
reproducible(fn, packages = c("ggplot2"), name = "Flipper Histogram")
```

## Edge Cases and Caveats

**Clean room isolation** — The function cannot access variables from your global R environment. Only declared package exports, base R, parameters, and `publish()` are available. This is by design.

**DataFrames are copied** — Data frame arguments are round-tripped through Parquet serialization before the function sees them. The function receives a deserialized copy, ensuring the clean room version matches what gets stored.

**Other params round-trip through JSON** — Atomic parameter values go through `jsonlite` so the function sees the same values that will be persisted in the manifest.

**100 MB limit** — Total data frame size (via `object.size()`) must be under 100 MB. If exceeded, a warning is issued and the function runs normally without clean room metadata.

**Unsupported parameter types** — Only atomic values (numeric, integer, logical, character), data frames, and nested lists of those types are serializable. Anything else triggers a warning and falls back to direct execution.

**`nanoparquet` required** — Clean room support requires the [`nanoparquet`](https://cran.r-project.org/package=nanoparquet) package. Without it a warning is issued and the function runs without clean room metadata.

**`interactive = TRUE` outside an interactive session** — Inside `knitr` or non-interactive R, the gadget is skipped and the function runs once with default parameters.

**Source code extraction** — The function body is captured from the R AST via `body(fn)`. Functions defined inline as the first argument to `reproducible()` work out of the box. Programmatically constructed functions also work as long as `body(fn)` returns a valid expression.

## Usage in R Markdown / knitr

`reproducible()` works inside R Markdown chunks. Interactive mode is automatically downgraded to a single-shot run during knitting:

````markdown
```{r setup, include=FALSE}
library(gofigR)
library(ggplot2)
gofigR::enable(workspace_name = "Scratchpad",
               analysis_name  = "Clean room in R")
```

```{r iris_scatter, fig.width=8, fig.height=6}
reproducible(
  function(
      point_size = slider(2, min = 0.5, max = 5, step = 0.5),
      data       = static(iris)
    ) {
      p <- ggplot(data, aes(Sepal.Length, Petal.Length, color = Species)) +
        geom_point(size = point_size) +
        theme_minimal()
      publish(p, figure_name = "Iris Scatter")
    },
    packages = c("ggplot2")
)
```
````

The chunk renders the figure inline, captures the clean room metadata, and publishes a revision to GoFigr — all from a single function call.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.gofigr.io/features/clean-room-r.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
