pr-verify · git:20260812.062e083 · 2026-08-12 · sha256 1dbcd3b11746888c
pr-verify git:20260812.062e083A
Immutable. This exact content never changes and is served at /api/v1/blob/1dbcd3b11746888c.
---
name: pr-verify
description: Verify a Docglow change actually works before submitting or merging a PR. Runs the conformance suite, then a behavioral verification pass (flag matrix, artifact-join spot checks, pipeline contract sweep, payload budget). Use when reviewing a PR, self-reviewing a branch before opening a PR, or when asked to "verify this change" or "run pr-verify".
---
# PR verification for Docglow
Two phases, in order. Phase 1 proves the author's own tests pass. Phase 2 probes
what those tests couldn't see — feature interactions and data semantics. **Do not
state a verdict ("mergeable", "looks good") until Phase 2 is complete.** Green
checks in Phase 1 are not evidence of correctness; both real blockers found in
past reviews lived entirely in Phase 2.
## Phase 0 — Setup
Work in a git worktree so the main checkout stays untouched:
```bash
git worktree add /tmp/pr-verify-<branch> <branch>
cd /tmp/pr-verify-<branch>
```
Gotcha: an editable install (`pip install -e`) resolves `docglow` from wherever
it was installed, not the worktree. Prefix Python commands with
`PYTHONPATH=$PWD/src` or new modules will fail to import.
## Phase 1 — Conformance
```bash
PYTHONPATH=$PWD/src python -m pytest -q
ruff check src/ tests/
ruff format --check src/ tests/
mypy src/docglow
cd frontend && npm ci && npx tsc --noEmit && npm run test -- --run
```
If the PR touches `src/docglow/static/` (the vendored frontend bundle), verify
it is a faithful rebuild — the minified blob cannot be reviewed by eye:
```bash
cd frontend && npm run build
diff -r dist/assets ../src/docglow/static/assets # filenames AND content must match
diff dist/index.html ../src/docglow/static/index.html
```
A hash mismatch means the bundle was built from different source than the PR
contains. Treat that as a blocker.
If the PR adds a top-level payload key, both exact-key assertions must be
updated: `tests/test_data_transformer.py` and `tests/test_dbt_versions.py`.
## Phase 2 — Behavioral verification
### 2a. Generation matrix
Generate real sites under every mode that could interact with the change, and
**inspect the emitted payload each time** — exit codes prove nothing. Minimum
matrix for generator-touching changes:
| Mode | What to check |
|---|---|
| plain `--static` | new data present and correct |
| `--select <one model>` / `--exclude` | new data respects filtering; no links to pages that were not generated |
| `--slim` | new payload key included/excluded deliberately, not by accident |
| artifacts missing `run_results.json` | absent data reads as "unknown/not run", never as passing |
| project with none of the relevant resources | empty state, no crash |
```bash
PYTHONPATH=$PWD/src python -m docglow generate --project-dir examples/jaffle-shop --static --output-dir /tmp/site-plain
PYTHONPATH=$PWD/src python -m docglow generate --project-dir examples/jaffle-shop --static --select stg_customers --output-dir /tmp/site-select
```
Then read the embedded payload (grep the output `index.html` for the new key)
and click through the generated site if the change is user-visible.
### 2b. Enumerate the enum
For any change that joins or maps dbt artifact data: list every category of
input (each `resource_type`, each `test_type`, each materialization — whatever
the relevant axis is) and hand-verify **one instance of each category** against
the raw `manifest.json` / `run_results.json` / `catalog.json`. Bugs cluster in
the outlier category — e.g. `relationships` tests order `depends_on.nodes` with
the *referenced* model first, so naive `[0]` indexing picks the wrong resource;
`attached_node` is the authoritative field. Aggregate counts can be correct
while identities are wrong, so check identities, not counts.
Fixtures to cross-check against: `tests/fixtures/`, `tests/fixtures/dbt-1.8/`,
`tests/fixtures/dbt-1.9/`, `examples/jaffle-shop/target/`.
### 2c. Pipeline contract sweep
If the change adds or modifies a pipeline stage: open `default_stages()` in
`src/docglow/generator/pipeline.py` and walk every existing stage, asking "does
the new code respect what this stage established?" In particular:
- `filter_nodes` — does the new stage honor `--select`/`--exclude`, or does it
read `ctx.artifacts.manifest` raw and leak filtered-out resources back in?
- `build_search_index` — should the new data be searchable?
- ordering — does the new stage read anything produced by a later stage?
### 2d. Payload budget
Any new payload key gets measured, not guessed:
```bash
python -c "import json,sys; d=json.load(open('payload.json')); print(len(json.dumps(d['<key>'])))"
```
Report bytes-per-item and project at 5,000 and 20,000 items. Publish and page
load are size-sensitive; unbounded keys need a mitigation (omit null fields,
drop verbose fields on happy-path items, paginate/virtualize rendering) or an
explicit decision to defer.
### 2e. Compatibility
Payloads generated by older versions lack new keys, and older viewers may
render newer payloads. New `DocglowData` keys are **optional**
(`readonly key?: T`) in `packages/shared-types/src/site.ts` — follow the
`relationships?` precedent. The frontend-local mirror/augmentation in
`frontend/src/types/index.ts` must declare the same optionality, or the build
breaks with TS2717 when shared-types is next republished. Frontend code guards
against the key being absent.
## Reporting
Tier every finding and attach a reproduction to each blocker:
- **Blocker** — user-visible wrong behavior or broken contract; include the
exact command and output that reproduces it
- **Should fix** — will bite soon (compat breaks, unbounded growth, contradicted
invariants)
- **Nit** — convention, naming, style; never blocks a merge
Also report what was verified and how (commands run, sites generated, fixtures
cross-checked), so the next reviewer knows what is already covered.
## Cleanup
```bash
git worktree remove --force /tmp/pr-verify-<branch>
```