---
title: "From Content Validity to Empirical Validation"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{From Content Validity to Empirical Validation}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(contentvalidR)
```

## Where content validity stops

A content-validity pretest answers questions you can ask before anyone responds
to the scale: are these items relevant to the construct, does the set cover the
domain, do experts agree, and is each item congruent with its intended
objective. It cannot answer whether an item will correlate with its construct,
load on the intended factor, behave the same across groups, or relate to other
constructs as theory predicts. Those questions need response data.

So a pretest ends with a decision: which items go forward into data collection.
`content_handoff()` packages that decision, so the item set and the reasons
behind it travel together into the next stage.

## A worked panel

Four experts rate five items for relevance on a 1-4 scale. The last item is
rated poorly, and one is borderline:

```{r panel}
relevance <- matrix(
  c(4,4,4,3,  4,4,3,4,  3,4,4,4,  3,2,3,2,  2,2,1,2),
  nrow = 4,
  dimnames = list(NULL, paste0("Item", 1:5))
)

fit <- expert_validity(relevance, mode = "relevance", lo = 1, hi = 4, seed = 1)
fit$results[, c("item", "N", "V", "I_CVI", "kappa_mod", "status")]
```

## The handoff

```{r handoff}
handoff <- content_handoff(fit)
handoff
```

## What the object holds

```{r components}
handoff$items
handoff$item_evidence
head(handoff$item_statistics)
```

`items` is what the next stage consumes: the carried items only, so what you
read here is exactly what you pass along. `item_evidence` keeps every reviewed
item, including those held back, with the decision rule that was applied and
the effective number of judges behind it. `item_statistics` stacks the
statistics long, one row per item per statistic, with the criterion each was
compared against where the method sets one.

```{r provenance}
str(handoff$provenance[c("schema_version", "workflow", "mode", "keep",
                         "method", "citation")])
```

By default only items with a `Supported` status travel. Some protocols carry
items flagged for review, on the grounds that a weak content-validity result
deserves an empirical test rather than deletion. Say so explicitly:

```{r keep}
content_handoff(fit, keep = c("Supported", "Review"))$items
```

Items held back are never silently dropped:

```{r held}
handoff$item_evidence[!handoff$item_evidence$carried,
                      c("item", "status", "recommendation")]
```

## Constructs

Item-sort and construct-rating designs map each item to a target construct, so
the handoff carries that mapping as `scales`, ready for a workflow that models
several scales at once:

```{r scales}
sort_dat <- data.frame(
  item = rep(c("A1", "A2", "B1"), each = 12),
  rater = rep(1:12, 3),
  target_construct = c(rep("A", 24), rep("B", 12)),
  assigned_construct = c(rep("A", 11), "B", rep("A", 9), rep("B", 3),
                         rep("B", 10), rep("A", 2))
)
content_handoff(sort_validity(sort_dat), keep = c("Supported", "Review"))$scales
```

An expert panel rating one item set for relevance has no construct column, so
`scales` is `NULL`. That is a real difference, not a gap to paper over: nothing
in that design says which construct each item belongs to, so a downstream
workflow should screen those items rather than invent membership for them.

## Into an empirical workflow

Once responses are collected, the item set carries into the empirical stage.
[nomologR](https://github.com/JUhalt/nomologR) is the companion package for
that stage: it begins where item-level data exist and follows the measure
through screening, dimensionality, measurement models, reliability, invariance,
and nomological networks.

```{r nomologr, eval=FALSE}
# install.packages("nomologR", repos = "https://juhalt.r-universe.dev")
library(nomologR)

# `responses` is your collected data: one row per respondent, one column per item.
scr <- nomo_screen(responses, items = handoff)
scr$item_summary
```

That chunk is not evaluated here, because this vignette builds without nomologR
installed and because a content-validity pretest has no responses to screen.

The object shape is agreed between the two packages as schema version 1, and
every field is a base type: character, logical, numeric, integer, data frame,
list, and Date. Neither package depends on the other, so any downstream
workflow can read the handoff, including one you write yourself:

```{r plain}
class(handoff)
```

## What the handoff does not claim

Carrying an item forward is not a prediction that it will perform. An item can
be clearly relevant to experts and still correlate poorly with its construct,
load on an unintended factor, or behave differently across groups. The reverse
also happens: an item held back on content grounds cannot be rescued by a
strong loading, because a factor loading says nothing about whether the item
belongs to the construct as defined.

Both stages follow the same rule. Flag, explain, and document; never silently
delete. The handoff therefore carries evidence rather than instructions, and
the empirical stage is free to flag an item that content review supported.

## Reporting the transition

A methods section can state, in one or two sentences, how many items entered
the pretest, how many were carried forward and under which rule, and where the
held-back items went. `item_evidence` is the table behind that sentence, and
`provenance` records the analysis that produced it:

```{r reporting}
ev <- handoff$item_evidence
sprintf("%d of %d items were carried forward (%s); %d were held back.",
        length(handoff$items), length(unique(ev$item)),
        paste(handoff$provenance$keep, collapse = ", "), sum(!ev$carried))
```
