---
title: "How GoldenVizR Checks Work"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{How GoldenVizR Checks Work}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(message = FALSE, warning = FALSE)
```

GoldenVizR checks `ggplot2` objects by reading plot metadata and built plot
data. It does not inspect a rendered chart image with computer vision.

The goal is to catch common issues early and make review more systematic. The
result is still heuristic: some visualization decisions require human judgment.

## Inspection pipeline

When you call `analyze_plot(plot)`, GoldenVizR:

1. Confirms that `plot` is a `ggplot2` plot.
2. Builds the plot with `ggplot2` internals so layer data can be inspected.
3. Extracts labels, titles, captions, scales, guides, layers, geoms, facets,
   coordinates, theme hints, and plotted data summaries.
4. Converts those observations into an inspection list.
5. Runs the 25 ordered GoldenVizR rule checks.
6. Returns a `goldenviz_report` with `plot_summary`, `rule_results`,
   `status_counts`, and `summary`.

## Status logic

Each rule returns one status:

| Status | Meaning |
|---|---|
| `PASS` | The available inspection data satisfied the rule. |
| `WARNING` | GoldenVizR found a likely issue or improvement opportunity. |
| `FAIL` | GoldenVizR found a stronger integrity, accessibility, scale, or encoding issue. |
| `INFO` | GoldenVizR did not have enough evidence to make a responsible judgment. |

The report-level status is derived from the rule statuses:

| Report status | How it is chosen |
|---|---|
| `fail` | At least one rule returned `FAIL`. |
| `warning` | No failures, but at least one rule returned `WARNING`. |
| `pending_checks` | No failures or warnings, but at least one rule returned `INFO`. |
| `pass` | All assessed rules returned `PASS`. |

## Evidence used by the rules

GoldenVizR uses evidence that can be read from a `ggplot2` object, including:

- title, subtitle, caption, axis labels, and scale labels
- mapped aesthetics such as `x`, `y`, `colour`, `fill`, `shape`, `size`, and
  `linetype`
- layer count, geom types, visible data rows, point count, and missing values
- legend mappings, guide settings, legend titles, and legend labels
- theme hints such as text size, grid visibility, and legend position
- coordinate and scale hints, including zero-baseline situations
- data summaries that can indicate overplotting, outliers, dense encodings, or
  limited context

GoldenVizR is conservative. If the plot object does not expose enough evidence,
the rule should return `INFO` instead of pretending to know.

## Current rule behavior

The table below documents the current implemented behavior. It describes the
evidence GoldenVizR uses and the normal conditions that lead to each status.

```{r rule-behavior-tables, echo=FALSE}
rule_behavior <- data.frame(
  Family = c(
    rep("Completeness", 6),
    rep("Readability", 10),
    rep("Integrity", 9)
  ),
  Rule = c(
    "R1 Clear title",
    "R2 Axis labels",
    "R3 Missing values and gaps",
    "R4 Legend clarity",
    "R5 Source and context",
    "R6 Annotation for key message",
    "R7 Readable text",
    "R8 Accessible color choices",
    "R9 Avoid clutter",
    "R10 Grid and guide balance",
    "R11 Consistent formatting",
    "R12 Appropriate chart type",
    "R13 Readable scale labels",
    "R14 Overplotting management",
    "R15 Direct labeling when useful",
    "R16 Visual hierarchy",
    "R17 Truthful scale",
    "R18 No distorted baseline",
    "R19 Avoid misleading encodings",
    "R20 Handle outliers honestly",
    "R21 Proportional areas",
    "R22 Uncertainty when needed",
    "R23 Avoid cherry picking",
    "R24 Respect data granularity",
    "R25 Neutral framing"
  ),
  Evidence = c(
    "`title` label",
    "`x_label`, `y_label`",
    "Missing-value and gap signals from built data",
    "Mapped legend aesthetics, guide settings, legend title, legend labels",
    "Caption, subtitle-like context, source fields",
    "Annotation layers and key-message need signals",
    "Theme text size and related text metadata",
    "Color count and palette accessibility signals",
    "Layer count, mark count, clutter score",
    "Gridline and guide metadata",
    "Formatting consistency signal",
    "Geom type, mapped data, chart-type plausibility signal",
    "Scale labels and scale readability signal",
    "Point count and overplotting signal",
    "Legend size, direct-label signal, direct-label usefulness signal",
    "Title/text hierarchy and hierarchy signal",
    "Scale truthfulness signal",
    "Baseline requirement and zero-baseline signal",
    "Encoding integrity signal",
    "Outlier detection and outlier handling signal",
    "Area encoding and proportionality signal",
    "Uncertainty need and uncertainty layer signals",
    "Scale limits, data selection, and context signals",
    "Data granularity and geom/data compatibility signal",
    "Title/subtitle wording and framing signal"
  ),
  PASS = c(
    "Title text is present.",
    "Both axis labels are present.",
    "No missing values or gaps were reported.",
    "A legend appears to have usable title or label text.",
    "A caption or source/context field is present.",
    "Annotation is present, or no annotation need was reported.",
    "Text size appears readable.",
    "Palette is reported accessible.",
    "No high clutter signal was reported.",
    "Grid/guide information appears acceptable.",
    "Formatting is reported consistent.",
    "Chart type information appears plausible or available.",
    "Scale labels appear readable.",
    "No overplotting signal was reported.",
    "Direct labels are reported.",
    "Visual hierarchy information is supplied and not weak.",
    "Scale is reported truthful.",
    "Required baseline is present, or no zero baseline is required.",
    "No misleading encoding was reported.",
    "No outliers are reported, or outliers appear handled.",
    "Area encoding is proportional, or no area encoding is used.",
    "Uncertainty is shown when needed, or no uncertainty need is reported.",
    "No cherry-picking risk is reported, or full context is supplied.",
    "Data granularity is respected.",
    "Framing is reported neutral."
  ),
  WARNING = c(
    "Title field exists but is empty.",
    "One or both labels appear missing.",
    "Missing values or gaps were found.",
    "A legend is present but lacks clear text.",
    "Not currently used.",
    "A key-message annotation appears needed but missing.",
    "Explicit text size appears small.",
    "Many colors are used and should be reviewed.",
    "Many layers, marks, or clutter signals are present.",
    "Grid or guide balance may need review.",
    "Formatting is reported inconsistent.",
    "Chart type is reported potentially inappropriate.",
    "Scale labels are reported hard to read.",
    "Dense point layers may need transparency, aggregation, jitter, or binning.",
    "Direct labels may improve readability.",
    "Visual hierarchy is reported weak.",
    "Not currently used.",
    "Not currently used.",
    "Not currently used.",
    "Outliers are present without handling context.",
    "Not currently used.",
    "Uncertainty appears needed but missing.",
    "Data selection context may be incomplete.",
    "Visual form may mismatch the data granularity.",
    "Framing is reported non-neutral."
  ),
  FAIL = c(
    "Not used.",
    "Not used.",
    "Not used.",
    "Not currently used.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Palette is reported inaccessible.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Scale is reported potentially untruthful.",
    "A zero baseline appears required but missing.",
    "A misleading encoding was reported.",
    "Not used.",
    "Area encoding is reported non-proportional.",
    "Not used.",
    "Not used.",
    "Not used.",
    "Not used."
  ),
  INFO = c(
    "Title field is unavailable.",
    "Axis label fields are unavailable.",
    "Missing-value information is unavailable.",
    "No legend evidence is available. This can mean no legend is needed, or that the current metadata does not expose enough legend information.",
    "No source/context field is available.",
    "Annotation need cannot be determined.",
    "Text-size information is unavailable.",
    "Color accessibility cannot be assessed.",
    "Clutter evidence is unavailable.",
    "Grid/guide evidence is unavailable.",
    "Formatting consistency cannot be assessed.",
    "Chart-type evidence is unavailable.",
    "Scale-label evidence is unavailable.",
    "Overplotting evidence is unavailable.",
    "Direct-labeling need cannot be determined.",
    "Hierarchy evidence is unavailable.",
    "Scale truthfulness cannot be assessed.",
    "Baseline requirement cannot be assessed.",
    "Encoding integrity cannot be assessed.",
    "Outlier evidence is unavailable.",
    "Area-encoding evidence is unavailable.",
    "Uncertainty need cannot be assessed.",
    "Data-selection context is unavailable.",
    "Granularity evidence is unavailable.",
    "Framing evidence is unavailable."
  ),
  check.names = FALSE
)

family_order <- c("Completeness", "Readability", "Integrity")
family_rows <- split(rule_behavior, factor(rule_behavior$Family, levels = family_order))

status_cell <- function(label, text) {
  htmltools::tags$div(
    class = "gv-check-status-cell",
    htmltools::tags$span(class = paste("gv-mini-status", paste0("gv-mini-status-", tolower(label))), label),
    htmltools::tags$p(text)
  )
}

behavior_family_table <- function(family_name, family_data) {
  htmltools::tags$section(
    class = "gv-rules-family gv-check-family",
    htmltools::tags$h2(class = "gv-rules-family-title", family_name),
    htmltools::tags$table(
      class = "gv-check-table",
      htmltools::tags$colgroup(
        htmltools::tags$col(class = "gv-check-col-rule"),
        htmltools::tags$col(class = "gv-check-col-evidence"),
        htmltools::tags$col(class = "gv-check-col-status"),
        htmltools::tags$col(class = "gv-check-col-status"),
        htmltools::tags$col(class = "gv-check-col-status"),
        htmltools::tags$col(class = "gv-check-col-status")
      ),
      htmltools::tags$thead(
        htmltools::tags$tr(
          htmltools::tags$th("Rule"),
          htmltools::tags$th("Evidence inspected"),
          htmltools::tags$th("PASS"),
          htmltools::tags$th("WARNING"),
          htmltools::tags$th("FAIL"),
          htmltools::tags$th("INFO")
        )
      ),
      htmltools::tags$tbody(
        lapply(seq_len(nrow(family_data)), function(i) {
          htmltools::tags$tr(
            htmltools::tags$td(
              htmltools::tags$span(class = "gv-rule-number", sub(" .*", "", family_data$Rule[[i]])),
              htmltools::tags$span(class = "gv-rule-name", sub("^R[0-9]+ ", "", family_data$Rule[[i]]))
            ),
            htmltools::tags$td(htmltools::HTML(gsub("`([^`]+)`", "<code>\\1</code>", family_data$Evidence[[i]]))),
            htmltools::tags$td(status_cell("PASS", family_data$PASS[[i]])),
            htmltools::tags$td(status_cell("WARNING", family_data$WARNING[[i]])),
            htmltools::tags$td(status_cell("FAIL", family_data$FAIL[[i]])),
            htmltools::tags$td(status_cell("INFO", family_data$INFO[[i]]))
          )
        })
      )
    )
  )
}

htmltools::tagList(Map(behavior_family_table, names(family_rows), family_rows))
```

## How legend clarity is interpreted

Rule 4 deserves special care because legends are sometimes necessary and
sometimes redundant.

GoldenVizR treats a legend as potentially needed when a variable is mapped to a
legend-producing aesthetic, such as `colour`, `fill`, `shape`, `linetype`,
`alpha`, or `size`. A constant setting outside `aes()`, such as
`colour = "red"`, does not by itself require a legend.

Current behavior:

| Situation | Current likely status |
|---|---|
| No legend evidence is available | `INFO` |
| Legend exists and title or labels are available | `PASS` |
| Legend exists but no useful title or labels are available | `WARNING` |

Important limitation: the current Rule 4 implementation does not yet fully
judge whether a legend title such as `factor(cyl)` is semantically weak. That
kind of wording may be technically available but still poor for readers. This
is a good target for future improvement.

## Why some rules return INFO

Some rules depend on evidence that is not always present in a `ggplot2` object.
For example, a plot object may not reveal the author’s intent, the full data
collection scope, the intended audience, or whether a missing uncertainty band
is scientifically required.

In those cases, GoldenVizR returns `INFO` rather than making a false claim.

## Practical interpretation

Use the report as an early review assistant:

- Start with `FAIL` and `WARNING` rows.
- Review `INFO` rows when the chart is important or publication-facing.
- Treat `PASS` as “no issue detected from available evidence,” not as a
  guarantee that the chart is perfect.
- Apply human judgment for context, audience, domain meaning, and ethics.
