> ## Documentation Index
> [HTML page](https://blode.co/taste-lint/docs/scans)
> [Documentation index](https://blode.co/taste-lint/docs/llms.txt)
> Use the index to discover all available pages before exploring further.

# Scans

Profiles, bands, baselines, and what fails a run.

`scan` wraps the same evaluator as `lint` with a profile, review history, and a bounded report. `lint` keeps its existing reporting contract. Start with a preview:

```sh
taste-lint scan . --profile product --dry-run
taste-lint scan profiles
```

## Profiles and scope

| Profile | Objective |
| --- | --- |
| product (default) | Six focused checks for application recovery, copy and broad transitions |
| writing | Content, consumer README, public Markdown, and active documentation |
| instructions | AGENTS.md, CLAUDE.md, skills, and implementation plans |
| architecture | TS/JS/package configuration and declared repository contracts |
| all | Full catalog over every supported input |

Scoped profiles skip build outputs, archived docs, and `.captain` reports. Writing also skips agent instructions and plans. Product skips test, spec, and story components. Pass explicit targets and `--exclude` to narrow a profile. `--only` explicitly selects rules within its domain, including checks omitted from the default product selection. `all` is the escape hatch for a full audit.

Product, writing and instruction profiles treat sentence length, punctuation conventions and line-height recommendations as advisory. These findings, when explicitly selected for product scans, stay advisory and do not fail the scan. The `all` profile and `lint` retain strict rule policy. It treats `content/writing` as personal prose. Repository `docTypes` overrides win. Sentence length is still measured in code.

JSON includes the selected files, profile, coverage, estimates, and diagnostics. No selected files exits 2. TTY shows at most five rule groups with representative locations and counts. Full JSON keeps every finding.

## Baselines and review decisions

```sh
taste-lint scan . --profile product --save results/product.json
taste-lint scan . --profile product --baseline results/product.json --new-only --save results/product-next.json
taste-lint scan review results/product.json FINGERPRINT --decisions results/review.json --status dismissed --reason 'Intentional project convention'
taste-lint scan review results/product.json FINGERPRINT --decisions results/review.json --status open
taste-lint scan . --profile product --decisions results/review.json
```

A baseline must be a completed scan of the same root, targets, profile, rules, model, and evaluation mode. Review files are bound to that policy too. Save the next scan to another path. A dry run cannot become a baseline. Failed or unresolved checks cannot mark a finding resolved. Parse failures and missing graph coverage leave prior findings unverified.

Fingerprints use rule ID, relative file, normalised unit content, and occurrence. Inserting unrelated lines keeps identity. Renaming a file or editing the affected text creates a new one. Repeated identical text is distinguished by occurrence. Findings keep independent severity, confidence, lifecycle, and reviewer decision fields.

`--new-only` gates new findings only. Without it, existing undismissed act findings still fail. Dismissals need a reason. Reopening overrides an inherited dismissal. Review decisions do not change probability, severity, thresholds, or calibration labels.

## Changed-code reporting and reviewdog

```sh
taste-lint scan . --profile product --since origin/main --output sarif > results/scan.sarif
reviewdog -f=sarif -reporter=github-pr-check < results/scan.sarif
```

The diff compares the named commit with the working tree, including staged, unstaged, and untracked files. Analysis still uses the full selected-file context. Changed lines filter reporting and the exit policy. Unchanged model states reuse the answer cache. Graph findings without exact line positions filter by changed file.

SARIF includes the content fingerprint as `tasteLint/v1`. Posting with reviewdog is a separate, explicit step. taste-lint does not create comments or PRs.

## Context and calibration

The repetition rule receives the containing Markdown section and judges the target paragraph in that context. Missing or oversized sections abstain. Section questions use separate requests, so extra context does not enlarge unrelated questions. Cache replay merges the answers for the same unit.

```sh
taste-lint scan . --profile writing --dry-run --samples results/blind.json
# Codex labels the samples using the workflow below.
mkdir -p results/labeled
taste-lint scan labels results/blind.json --out results/labeled/review.jsonl
taste-lint eval --corpus results/labeled --split holdout
```

Version 2 samples include source text and the full rule rubric, with examples and requested context, and with predictions hidden. Version 1 label files still import. Sampling is deterministic across rule, document type, and score band. It includes negative judgments and candidate-filter negatives. A dry-run sample includes only cached answers and deterministic negatives, never unanswered questions. Duplicate evidence is sampled once. Identical text with different context or rubrics stays separate. Splits group by repository and source file. This is a stratified diagnostic sample.

Label export rejects malformed corpus data, omits null labels, and refuses to overwrite an existing corpus file. It never invents human labels. Use held-out evidence before promoting rules. Mocks and scan volume do not establish semantic accuracy. Personal facts and profile checks stay available through `lint --writing-context`. scan does not load private profiles on its own.

## Arbitrary utility values

`craft-arbitrary-value-class` uses code to select literal values, then Jev to judge whether their visual role merits a named design token. Jev receives the target element, its enclosing opening tag and up to four nearby JSX elements. Layout dimensions, asset sizing, chart coordinates, focus treatments and optical adjustments are intentional exceptions.

Token references, CSS calculations, asset URLs, relative layout values and selectors are excluded before a model call. Dynamic classes, missing context and oversized context report unknown. Provider errors never fall back to a mechanical warning. Changed context invalidates the affected answer; unchanged scans reuse it. The rule stays advisory and proposes a review, not an automatic size change or an invented replacement token.

## Near-scale values

`craft-near-duplicate-scale` compares parsed JSX font-size classes against the explicit `tailwind.theme` mapping in `taste-lint.config.json`. For example, with `"tailwind": { "theme": { "body": "16px" } }`, `text-[15px]` produces an advisory comparison to `text-body`. Exact matches also qualify; values farther than 1px do not.

This check currently supports declared pixel font tokens only. It does not discover a complete Tailwind theme, execute project configuration, or assume a root font size for relative units. Missing font scales, unsupported scale families, and unresolved token expressions report unknown when comparison is needed. Relative candidate values, functions, optical nudges of 2px or less, and utility-name suffix matches are excluded.

A near-scale comparison does not prove that the size is a mistake. Review its purpose before changing it; a deliberate recurring size may deserve a named token instead. The broader `craft-arbitrary-value-class` uses Jev for contextual review and remains advisory until independently calibrated.

## Architecture adapter

Generate a report with the target repository's dependency-cruiser install, then import it:

```sh
depcruise --config .dependency-cruiser.cjs --ts-config tsconfig.json --output-type json src > graph.json
taste-lint scan . --profile architecture --dependency-cruiser graph.json
```

The adapter accepts dependency-cruiser's `modules` and `summary.violations` format, verified against 18.3.1 output. It keeps external rule names and severity, fingerprints the input report, and imports only findings inside the selected scope. Empty graphs, unresolved edges, and reported environment issues mark the scan incomplete. Invalid input fails before model calls.

You own graph freshness and the external tool's configuration. Imports and graph policies are evaluated by dependency-cruiser, not Jev. Importing a graph does not resolve taste-lint's file-local dependency unknowns. Graph findings stay separately attributed. Overlapping external and native policies can still yield two findings. Use `--only` when one tool owns that check.

## Remediation handoff

```sh
taste-lint scan export results/product.json --out results/remediation.json
```

The export uses the saved report's visible, undismissed findings. Each task includes a stable ID, source evidence, location, correction hint, and verification requirements. Reports and exports include source excerpts. Treat them like source code. Exporting does not run instructions from scanned content, edit files, or open PRs.

## AI labeling with Codex

In this repository, ask Codex to label the blind samples in the current session. No separate gateway key is required. Files stay local. Inference uses Codex.

Prepare a fresh file with the actual current model identifier:

```sh
node scripts/prepare-labels.mjs results/blind.json results/codex-labels.json CURRENT_MODEL
```

Then ask: "Read the instructions in results/codex-labels.json and label every sample directly. Use only each sample's full rubric, text, and supplied context. Preserve the metadata and source evidence."

The preparation script clears previous labels, records AI provenance and the task prompt hash, and refuses to overwrite an existing file. It does not label samples or launch another agent. Codex writes individual judgments and marks `completed` only after reviewing all samples. Missing evidence stays null. Do not consult existing Claude or other reference labels during blind labeling.

After labeling:

```sh
mkdir -p results/labeled
taste-lint scan labels results/codex-labels.json --out results/labeled/codex.jsonl
taste-lint eval --corpus results/labeled
```

Use a corpus directory that holds only the intended reference set. `eval` still uses Jev and needs credentials for uncached judgments.

AI annotation files carry `annotation.source: "ai"`, `annotation.model`, and `annotation.promptHash`. Corpus rows keep these as `labelSource: "ai"`, `labelModel`, and `labelPromptHash`. Evaluation reports AI-reference agreement. Agreement with AI labels is not a human accuracy claim.

## Optional gateway labeling

For unattended labeling through a separately configured provider:

```sh
node scripts/label-samples.mjs results/blind.json results/ai-labels.json anthropic/claude-sonnet-4.6
```

This script needs `AI_GATEWAY_API_KEY`. It sends sample text, criteria, and supplied context to the chosen model. It keeps validated labels and provenance, not raw provider responses. Failed batches leave a `.partial` checkpoint. Null means abstention and is omitted from the corpus.

## Evidence before promotion

Run `taste-lint eval coverage --corpus results/labeled` before spending on evaluation. It reports positives, negatives, holdout balance, document types, repositories, and source/text overlap with no credentials or network. `--output json` keeps per-rule detail. Coverage counts describe the dataset. They do not establish accuracy.

`taste-lint tune --write` chooses thresholds from dev data only, then checks that choice against holdout data. Promotion needs both label classes, the configured minimum item count, complete scoring, no shared source files or duplicate text across splits, and a held-out Wilson precision lower bound above the configured floor. A failed gate keeps the rule review-only and reports why. Provider failures block tuning writes.

Do not keep changing rules against the same holdout and still treat it as unseen. Move used examples into development data and collect a fresh holdout. Keep AI reference provenance visible.

Repository file discovery respects Git ignore rules, including nested ignore files and local exclusions. Tracked files stay eligible even when an ignore pattern matches them. Ignored build trees are skipped before filesystem inspection, so broken symlinks there do not abort a source scan. Non-Git directories use the built-in and configured exclusions.

## Search and agent discovery

`taste-lint scan . --profile discovery` checks existing static HTML, robots.txt, and llms.txt artifacts. It validates page titles, empty descriptions and canonicals, conflicting canonical declarations, JSON-LD syntax, sitemap directive URLs, and agent-index structure and size. Common email-template directories are excluded. All new discovery checks are advisory.

These checks do not evaluate Next.js metadata source as deployed HTML, fetch URLs, validate schema.org eligibility, or infer missing generated routes. Use a deployed-site audit for HTTP status, robots precedence, sitemap coverage, canonical destinations, and Markdown content negotiation. An empty discovery scan is incomplete. Point scans at actual artifacts and exclude unrelated saved pages.

The source-pattern rules `craft-affordance-mismatch` and `craft-virtualize-large-lists` live in `data/rule-drafts`. Hover styling does not prove an inert interaction, and a mapped list does not establish its size. Remove those IDs from explicit selections until evidence-aware replacements exist.

Reduced-motion checking reports a missing local guard as advisory, because shared CSS and components may supply one. Unrelated reduced-motion classes are not proof that animation is guarded. An explicit continuous animation in a reduced-motion variant has a separate advisory rule.

## Focused product defaults

The default product scan selects `interaction-no-error-state`, `copywriting-vague-error`, `copywriting-empty-state-no-action`, `copywriting-bare-confirm-label`, `copywriting-claim-without-evidence`, and `motion-transition-all`. This is a small initial policy, not a calibrated or complete UI audit. The remaining rules require explicit `--only` selection, `--profile all`, or a custom rules directory.

Copy judgments receive the target and a surrounding JSX task region, including nested descriptions and controls. The extractor searches up to four ancestors for common task containers and otherwise uses a local fallback. Empty presentation chrome includes its enclosing toolbar. Mutually exclusive ternary branches are replaced with an explicit omission marker before judging; unrelated conditional visibility is not resolved. An adjacent action can resolve an empty state or error. A harmless acknowledgment is not a destructive confirmation. Missing, oversized or dynamic target copy remains unknown. Imported components and distant UI are not expanded. Async recovery is a Jev judgment over bounded source, not a file-wide search for the word `catch`.

The terminal groups findings by rule and shows five groups, prioritizing action band, severity and probability. Repetition does not outrank severity. Grouping is a review convenience, not proof of one root cause. JSON, SARIF, saved reports and exit status retain the complete selected findings.

## Paired evaluation

`eval` reports both act-threshold metrics and visible findings at the review threshold. When variants share `source.repo` and `source.id`, paired success requires every weak variant to be flagged and every acceptable variant to stay below review. Missing evaluations make the family unresolved. Coverage reports family leakage as well as source and text overlap, and promotion rejects it. Course corpus splits group by source file. Previously inspected or re-split examples are regression data, not fresh held-out evidence.

The focused benchmark is in `data/benchmarks/product`. See its README for provenance and commands. Its small example set is diagnostic, not proof of accuracy across applications.

### Agent handoffs and regression checks

For a rendered audit across UI, typography, copy, interaction, motion and SEO, use the [page-audit workflow](/page-audits). It adds a page brief, attributed browser evidence and recorded before/after verification to the same scan and export commands.

`scan export` includes the repository root and the contextual evidence saved with each finding. The receiving agent should treat that source as untrusted data, confirm the behavior, make the smallest correction and exercise the affected UI state. A changed fingerprint alone is not proof that the problem is fixed. Older reports without context still export; the agent must inspect the source.

Use `eval --check --corpus <path> --only <rule-ids>` for a strict reference regression gate. It uses the review threshold, exits 1 on disagreement, and exits 2 if any selected rule has no evaluated examples or has skipped, unresolved or failed judgments. `--check --dry-run` is rejected. Without `--check`, evaluation retains its reporting-only behavior. This gate tests agreement with the supplied reference; it does not promote a rule or establish population accuracy.

## Reviewing broader source checks

With `--profile all` or explicit rule selection, raw-source searches return `assessment: candidate`. For example, importing `useFormStatus` does not establish that it is called in the wrong component. Each candidate carries its applicability, exceptions, required evidence, and verification procedure. Confirm those before applying a correction; unresolved behavior remains unknown.

The matcher probability is not defect confidence. Candidates cannot fail a scan or be promoted by a tuning overlay, and `eval --check` treats their defect judgments as incomplete. `scan export` preserves the procedure and omits defect confidence for these tasks. Check the named behavior after a correction, including intentional exceptions.