---
title: "Specification Closure: Strict Readiness and Governed Validation"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Specification Closure: Strict Readiness and Governed Validation}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
library(gp3bayes)
```

## Purpose

This article closes the remaining Phase-0 validation requirements without
expanding gp3bayes beyond its two approved families. The new checks are
observable-data diagnostics. They do not establish posterior adequacy, choose
a model automatically, or justify deleting observations.

## Binary strict readiness

```{r}
bin_sim <- simulate_hierarchical_binary_data(
  n_participants = 20,
  trials_per_participant = 10,
  n_items = 10,
  seed = 2026
)

bin_contract <- create_model_contract(
  family = "binary",
  outcome_col = "selected",
  participant_col = "participant_id",
  item_col = "item_id",
  trial_col = "trial_id",
  condition_col = "condition",
  predictors = c("participant_covariate", "trial_covariate"),
  interaction = c("condition", "participant_covariate"),
  random_slope = FALSE
)

balance <- summarise_condition_balance(bin_sim$data, bin_contract)
balance

variation <- summarise_binary_group_variation(
  bin_sim$data,
  bin_contract,
  group = "participant"
)
variation

strict_binary <- audit_model_readiness_strict(
  bin_sim$data,
  bin_contract,
  run_separation = FALSE
)
strict_binary
```

The strict audit adds explicit overall condition imbalance, participant outcome
variation, identifier-like predictor review, and fixed-effect rank checks. When
`detectseparation` is installed, the optional separation screen can also be
integrated by setting `run_separation = TRUE`.

```{r, fig.width=7, fig.height=4.5}
plot(balance)
plot(strict_binary, type = "status")
```

## Identifier-like predictors are review signals

```{r}
id_data <- bin_sim$data
id_data$row_id <- seq_len(nrow(id_data))

id_contract <- create_model_contract(
  family = "binary",
  outcome_col = "selected",
  participant_col = "participant_id",
  item_col = "item_id",
  trial_col = "trial_id",
  condition_col = "condition",
  predictors = c("participant_covariate", "row_id")
)

identify_identifier_like_predictors(id_data, id_contract)
```

The heuristic never silently removes a declared predictor. A flag means that
the analyst must verify whether the numeric column is substantively meaningful
or is an identifier accidentally entered into the model matrix.

## Duration extremes, impossible ranges, and censoring

```{r}
dur_sim <- simulate_hierarchical_duration_data(
  n_participants = 20,
  trials_per_participant = 10,
  n_items = 10,
  outcome_unit = "milliseconds",
  seed = 2027
)

dur_contract <- create_model_contract(
  family = "duration",
  outcome_col = "duration",
  participant_col = "participant_id",
  item_col = "item_id",
  trial_col = "trial_id",
  condition_col = "condition",
  predictors = c("participant_covariate", "trial_covariate"),
  interaction = c("condition", "participant_covariate"),
  outcome_unit = "milliseconds"
)

extremes <- review_duration_extremes(dur_sim$data, dur_contract)
extremes

bounds <- audit_duration_boundaries(
  dur_sim$data,
  dur_contract,
  allowed_range = c(50, 10000)
)
bounds

strict_duration <- audit_model_readiness_strict(
  dur_sim$data,
  dur_contract,
  duration_allowed_range = c(50, 10000),
  run_separation = FALSE
)
strict_duration
```

Extreme values remain in the data. Censoring and impossible-range violations
are contract failures for the positive uncensored lognormal workflow; they do
not trigger an automatic switch to another likelihood.

## Traceability

```{r}
gp3bayes_specification_traceability()
```

The table is intended to make specification closure auditable: every remaining
Phase-0 requirement has an explicit implementation point and all automatic
decision flags remain `FALSE`.
