---
title: "Validity: robustness, personas, and scoped claims"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Validity: robustness, personas, and scoped claims}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include=FALSE}
knitr::opts_chunk$set(
  collapse = TRUE, comment = "#>",
  eval = identical(tolower(Sys.getenv("LLMRAGENT_RUN_VIGNETTES", "false")), "true")
)
```

A language model will answer questions about populations it has not observed.
This vignette covers the tools that put that problem on the record:
`agent_robustness()` measures how results move under perturbation,
`persona_audit()` inspects persona prompts, and `mark_claim_type()` states
the kind of claim a design supports.

```{r setup}
library(LLMRagent)
cfg <- LLMR::llm_config("groq", "openai/gpt-oss-20b", temperature = 0.7)
```

## Robustness under perturbation

Suppose a model rates how risky an activity is. A result deserves scrutiny if
temperature, option order, persona wording, or a faithful paraphrase moves the
answer. `agent_robustness()` builds that perturbation grid and reports the
movement by axis.

```{r robustness}
rate_risk <- function(cond, rep, perturb) {
  a <- agent("Rater", perturb$config)
  answer <- a$reply(perturb$prompt(
    "On a scale of 1 to 10, how risky is skydiving? Reply with one integer."
  ))
  as.numeric(gsub("[^0-9].*$", "", trimws(answer)))
}

battery <- agent_robustness(
  rate_risk,
  vary = list(temperature = c("0", "1")),
  reps = 3,
  measure = function(x) x,
  config = cfg
)

battery$by_axis
battery$overall
```

For numeric measures, the per-axis agreement statistic uses interval
Krippendorff alpha. Categorical measures use nominal alpha. The `fragile` flag
is a screening result, not a conclusion. Read the axis rows to locate the
source and size of instability.

`vary_prompt(paraphrase =, prompt =, config =)` generates a fixed paraphrase
set before the experiment. This turns prompt wording into a declared design
axis instead of a hidden search for favorable wording.

## Personas are prompts with provenance

A synthetic persona is not a sampled person. `persona_frame()` stores the
brief, its source, scope conditions, varied attributes, and a content hash.
`persona_variants()` builds a designed set without treating a demographic
attribute as a theory of behavior.

```{r personas}
base <- persona_frame(
  "A first-time voter in a competitive district.",
  source = "synthetic",
  scope = list(country = "US")
)

set <- persona_variants(
  base,
  vary = list(age = c("22", "52"),
              employment = c("salaried", "hourly"))
)

audit <- persona_audit(set)
audit
diagnostics(audit)
```

The offline audit flags a small set of essentializing constructions. An
optional model-based audit can add caricature scores through
`persona_audit(set, config = cfg)`. Neither layer certifies a persona. The
briefs remain primary evidence and should be read.

## Claim types state the design's scope

`mark_claim_type()` records whether a run is an instrument pilot, a theory
probe, or a coding exercise. These labels do not convert model output into a
human population estimate.

```{r claims}
coder <- agent("Coder", cfg)
coder$chat("Is this abstract about climate? Answer yes or no.\n...")
run <- mark_claim_type(as_agent_run(coder), "coding")

report(run)
```

`llm_claim_lint()` scans prose for population-estimate constructions. With
`action = "scope"`, it appends an ordinary scope sentence. With
`action = "error"`, it raises `llmragent_claim_error` so custom reporting code
can refuse the claim.

```{r lint}
llm_claim_lint(
  "We find that 60% of Americans support the policy.",
  run = run,
  action = "scope"
)
```

## A defensible sequence

Pilot the instrument and call it a pilot. Audit persona prompts before using
them. Treat coding as coding, with separate human validation appropriate to the
substantive measurement problem. Stress the full procedure across declared
perturbations. Archive the run and the robustness results. These steps do not
make model output representative of people; they make the study's limitations
legible.
