# 00 Scan

> Use when starting or re-baselining a pytest optimization pass by profiling tests and fixtures without editing the suite.

- Skill: `tony/00-scan` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tony/00-scan`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tony/00-scan/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tony (https://skillmd.com/u/tony)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tony/00-scan

---



# 00-scan

Profile, detect, hypothesize. This phase **only reads** the suite and **only
writes** JSON to the memory directory. No test is edited here.

`$ARGUMENTS` may scope the run to a path or marker, set `--runs=N` (baseline
sample count, default 5), `--force` to ignore the idempotency token, and
`--memory-dir` to override the memory location.

## Step 1: Preflight

Detect preferred tools (`rg`/`ag`/`fd`/`jq`/`uv`/`uvx`) and read the project's
**own** test command from `AGENTS.md`, `CLAUDE.md`, `justfile`, `tox.ini`, or
`pyproject.toml`. Do not hardcode a runner. Record the resolved command; every
later phase reuses it. Substitute it wherever this file writes `pytest`.

## Step 2: Resolve memory and check idempotency

Resolve the memory directory per `../../references/memory-schema.md`
(repo-root `.pytest-optimizer/`, gitignored; XDG fallback). Compute the
idempotency token (git HEAD + collection node-ids + pytest/plugin versions +
environment fingerprint). If `state.json` shows `scan` completed with an unchanged
token and
`hypotheses.json` exists, report "already scanned" and stop — unless `--force`.

## Step 3: Detect capabilities

Probe the installed pytest and plugins against
`../../references/version-matrix.md`. Write
`capabilities.json`. Use only available features below; fall back as the matrix
directs (e.g. no `--durations-min` pre-6.1 → post-filter rows).

## Step 4: Establish the baseline and noise band

Run the suite serially, cache disabled, `--runs` times, and record total
wall-time each run:

```bash
pytest -p no:cacheprovider -p no:randomly -q
```

Compute the median and MAD of the per-run totals; set the noise band
(`median + k·MAD`, default `k = 3`) and the ~50ms trust floor. Write the env
fingerprint, versions, capabilities, and the band to `baseline.json`. See
`../../references/durations-parsing.md`.

## Step 5: Profile slowest tests and fixtures

Capture per-phase timings and bucket rows by the `when` token (`call` vs
`setup`/`teardown`), de-duplicating by `(nodeid, when)`:

```bash
pytest --durations=0 --durations-min=0.005 -p no:cacheprovider -p no:randomly
```

Rank slowest **test bodies** (`call`, H01) and slowest **fixture
setup/teardown** (`setup`/`teardown`, H02). For shared higher-scope fixtures whose
cost is billed to one nodeid (H03), note the attribution; offer the opt-in timing
plugin if precise per-fixture cost is needed. Persist the timings to
`baseline.json`.

## Step 6: Run the static detectors

Work through `../../references/heuristic-catalog.md`, using
`../../references/fixture-analysis.md` for the fixture recipes and
`../../references/parametrize-convention.md` for the typing/
parametrize gaps:

- Fixtures: unused (H08/H09/H41), mis-scoped (H10/H13), duplicate (H11), autouse
  (H12/H15), mark-on-fixture (H43), empty conftest (H42).
- Collection/capability errors (H14/H22/H44).
- Typing and parametrize gaps (H26–H32).
- I/O, sleep, markers, timeouts (H15/H33/H37/H39/H40).
- Recomputation and duplication (H24/H25/H34/H35/H36/H38).

Apply the `getfixturevalue` guard (H09) before proposing any fixture deletion.

## Step 7: Emit hypotheses

Write `hypotheses.json`: one entry per firing detector, each tagged with its
heuristic id, the goal(s) it addresses, the raw evidence, and prior risk/effort.
Do **not** estimate impact here — `01-benchmark` measures it. Update `state.json`
(`phase=scan`, `completed.scan=true`, token).

## Step 8: Report

Emit the `00-scan` sections from
`../../references/output-contract.md`: a hero block, then
`## Environment`, `## Baseline`, `## Slowest tests`, `## Slowest fixtures`,
`## Hypotheses`. Close with an `AskUserQuestion` panel offering to benchmark the
hypotheses, narrow scope, or stop.

