# `assay`: the Architectonic Grader ```{py:currentmodule} arch.tools.assay ``` `assay` mechanizes the 2026-07-02 divisions critique's apparatus (T1-T5 per-cut tests, the four-step opposition scale, the mapping-fidelity A-7 scale) into a tool that grades **every decision** in an architectonic at high resolution, then writes a MyST report from the findings. It is read-only on its object: no yaml, sheet, or docstring mutation, ever — every repair is a proposal in the report, not an applied edit. ## `1` The three stages 1. **`gather`** builds the **dossier**: every cell enriched with a provenance-tagged description (`sheet > docstring > snapshot`, `DescriptionSource`), cached at `$MY_CACHE/arch/assay/dossier.yaml`. 2. **`grade`** derives the examination **sites** from the fold itself and runs the tiered checks below, producing `findings.jsonl` + `spend.json`, cached per `(site, check)` content hash. 3. **`report`** renders one MyST document from `findings.jsonl` alone — no dossier, no live lattice — plus deterministic SVG grade-heatmaps colored from `arch.irix`. `run` composes all three; each is also independently invocable (`gather`/`grade`/`report`), and `report` accepts an existing findings directory (`--findings-dir`) from any prior `grade`/`run`. ## `2` The site taxonomy Every decision occupies a site whose shape falls out of the fold's own two-bit-per-digit structure (`sites.py`): | Site | Count | What it examines | | -------------- | ----- | --------------------------------------------------------- | | `PairSite` | 170 | one opposition pair per quad per role (adjective / noun) | | `QuadSite` | 85 | a concrete cell + its 4 children + 2 halves | | `CellSite` | 510 | one authored cell: name aptness, description fit | | `EchoSite` | 172 | one per distinct division word, across its recurring uses | | `PairEchoSite` | 84 | a recurring pair, re-applied at 2 sibling quads | | `ChainSite` | 256 | a leaf's composed 4-phrase address, read top-down | ## `3` Tiers and scales | Tier | Model | Checks | Scale | | ------------------- | --------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------- | | 0 — symbolic | none | `sym.theorem1/2/3`, `sym.grammar`, `sym.vocab`, `sym.typo`, `sym.stale`, `sym.holes` | `TestVerdict` (pass/strained/fail) | | 1 — micro-judgments | per-check (`models.CHECK_MODELS`) | `pair.opposition`, `pair.tests`, `echo.sense`, `cell.name`, `cell.fit` | `Opposition`, `TestVerdict`, `EchoGrade` | | 2 — quad probes | per-check (`models.CHECK_MODELS`) | `quad.blindpair`, `quad.moments`, `quad.register`, `chain.address`, `pairecho.cut` | `AxisMatch`, `TestVerdict`, `EchoGrade`, `CutConsistency` | | 3 — synthesis | `glm-5.2` | one section-scoped connective-prose call per report section | prose, not a grade | Every model output is clamped to its legal grade vocabulary; a failed or unrecognized reply degrades to `ungraded` (`checks.UNGRADED`) — never a guessed grade. `quad.blindpair` adds a second degrade path, `unmaskable` (`checks.UNMASKABLE`), for a quad whose masking would leak a name or leave too little text to judge. Tier 3's synthesizer is deliberately a different model architecture from tiers 1-2's grader (decorrelation posture) — never reuse `--model` for `--report-model`. ## `4` Cost posture Tiers 1-2 route through the LiteLLM gateway (preferred) or direct OpenRouter, under a scoped virtual key (`routing.scoped_environ` narrows the process environment to `LITELLM_*`/`OPENROUTER_*` for the whole batch); every call is cached by `(check_version, model, content_hash)`, so a re-run against an unchanged dossier is free (`spend.json` reads all zeros). Per-check models (`models.CHECK_MODELS`, overridable with `--model`/`--check-model`) are cache-keyed exactly as before — the model id is part of the cache key, so changing one check's model re-judges only that check. `--no-llm` (or no resolvable credential) degrades honestly to tier 0 only, with a report rendered from the scaffold alone — zero LM calls, still a complete, useful document (grade histograms, worst offenders, the hole inventory, the `quad.blindpair` predicted-vs-observed table). ## `5` CLI examples ```bash assay run --no-llm --out /tmp/assay-dry # tier 0 only, findings + scaffold report assay run # full tier 0-3 pass, per-check model map assay run --model google/gemini-2.5-flash # uniform tier-1/2 model override assay grade --only 21 --out /tmp/assay-chamber-21 # narrow to one chamber assay report --findings-dir /tmp/assay-dry --no-llm # re-render a report from existing findings assay diff /tmp/assay-old /tmp/assay-new # symbolic grade diff of two findings dirs ``` ```{eval-rst} .. autofunction:: arch.tools.assay.cli.main ```