The passes
Each pass names the attributes it judges (ids from attributes.md), whether it is
deterministic (scripts/llms_lint.py), model (an LLM reads and decides),
or live (an agent or an HTTP call is exercised), and for every pass: how it is used,
how a finding is judged, how the file is updated, what tools it leans on, and which sibling
skills it hands to. Passes are grouped into bundles that run concurrently; findings are
collected before any write (the family rule, convergence-and-severity.md § Convergence loop).
Contents
- Bundle map and dispatch rules
- P0 Detect kind and grammar
- P1 Structure
- P2 Links and reachability
- P3 Descriptions
- P4 Navigation design
- P5 Size ladder and budgets
- P6 Full-file fidelity
- P7 Facts-file shape
- P8 Facts truthfulness
- P9 Provenance, rights and steering
- P10 Family and nesting
- P11 Retrieval readiness (keyword + vector)
- P12 Agent usability test
- P13 Serving and headers
- P14 Hygiene
- P15 Regeneration parity
- Severity resolution across passes
- N/A rules
Bundle map and dispatch rules
| Bundle | Passes | Kind | Runs as |
|---|---|---|---|
| B0 | P0 | deterministic | inline, first — every other pass keys off the detected kind |
| B1 | P1 P2 P3 P5 P14 | deterministic | one llms_lint.py invocation, JSON findings |
| B2 | P4 P9 | model | one subagent reading the index (+ sample pages) |
| B3 | P6 P7 | deterministic | llms_lint.py --full / --facts (same invocation as B1 when the kind is full/facts) |
| B4 | P8 | model, sampled | one subagent, 20 units re-read against source spans |
| B5 | P10 | deterministic + model | only when kind = family or --family |
| B6 | P11 | live | docset_indexer.py keyword + query --layer facts probes |
| B7 | P12 | live agent | one fresh-context subagent given ONLY the file; opt-in --agent-test, default on for new files |
| B8 | P13 | live HTTP | only when a URL is given or --serve-check |
| B9 | P15 | deterministic | only when the export directory has a source mirror |
Dispatch: B1 and B3 in one tool call; B2, B4, B7 as three concurrent subagents when the Agent
tool exists (sequential otherwise); B5/B6/B8/B9 inline. Small profile (index < 4 KB, no full,
no facts): B3, B4, B6 are N/A (no layer), B7 shrinks to 5 questions.
P0 — Detect kind and grammar (deterministic)
Judges I6. Used first, always. Reads the first 4 KB and the file name and returns one of
index | family | full | small | facts | unknown, plus for full files the page grammar
(mintlify | anthropic-yaml | cloudflare-frontmatter | firecrawl | none).
Judged: unknown → High (the file cannot be optimized without knowing what it is; report
and stop unless --kind is passed). Two grammars in one full file → High (C1). A file named
llms.txt that parses as full → High (I6; it will be served as an index and blow every budget).
Updated: never rewrites; it sets the profile. --kind overrides.
Tools: llms_lint.py detect; llms_acquire.split_llms_full for grammar probing.
Relations: document-formats/references/llms-txt.md § grammars is the authority on what
counts as a grammar; this pass never invents a fourth.
P1 — Structure (deterministic)
Judges I1 I2 I4 I5 N4. Parses the index as spec v2: one H1, optional blockquote, optional
free text, H2 sections of - [name](url): notes lines, ## Optional last.
Judged: missing/multiple H1 → High. Blockquote missing → Medium; blockquote present but > 3
sentences or restating the H1 → Medium (model confirms in P4). H3+ headings or paragraphs after
the first H2 → Medium. List line not matching the link grammar → per-line finding, High if
< 90% of lines match. ## Optional not last → Medium.
Updated (--fix safe): demote H3 to a flat list under the nearest H2; move ## Optional to
the end; wrap a bare URL line into - [<last path segment>](url); strip prose after the first
H2 into a <!-- moved --> comment for the model pass to place. Never invents a blockquote —
that is P3/P4 model work.
Tools: llms_lint.py structure. Relations: the community validators’ strict rules
(llms-txt-validator, llmstxt-validator) are folded in where they agree with the spec; where
they demand more than the spec (e.g. “blockquote required”) the finding is Low, tagged
validator-only.
P2 — Links and reachability (deterministic, optionally live)
Judges N1 N6 N7 P2 F1. Resolves each link: relative → against the base URL or the export
directory; absolute → HEAD when --check-links (rate-limited, 8 concurrent, 10 s timeout,
one retry). Counts hops for family files.
Judged: 4xx/5xx or HTML-app-shell response (content-type text/html with no .md twin
probe success) → High per link, capped at one High finding listing all. Redirect chain > 2 →
Medium. Duplicate target across sections → Low (N7). Family file linking a page rather than an
llms.txt → High (F1). Link to a private mirror path (file://, 127.0.0.1, text-mirror/)
in a file not marked internal → High (P2).
Updated (--fix safe): rewrite page.html → page.md when the twin probe succeeded;
collapse redirect chains to the final URL; drop exact duplicate targets keeping the first. Dead
links are never deleted silently — they become BLOCKED rows unless the source mirror still has
the page (then P15 regenerates).
Tools: llms_lint.py links [--check-links]; llms_serve.py routes for hub-served files;
hub_llms_full_read to confirm a page exists in the mirror. Relations: web-text-mirror
owns re-crawling a page that has genuinely moved.
P3 — Descriptions (deterministic + model)
Judges D1 D3 D4 D5 D6. Deterministic part: missing notes, word-count band, duplicates, truncation ellipsis, family-line counts. Model part: for each description, is it a restated title, and does it name what the reader finds (exact tokens)?
Judged: no notes on a link → Medium (High if > 40% of links). Duplicate notes → Medium. Restated-title notes (model verdict on the sampled 30 worst) → Medium. Family line without counts → Medium. A model-written description whose claims are not on the page (P8-style spot check on 10) → High.
Updated: descriptions come from the definition unit of the page when a facts layer exists
(extractive, cheap, correct); else from the page’s H1 + first sentence via the mirror; the model
polishes ONLY entries still under 40 chars or flagged restated-title, and every polished entry is
re-verified against the page before write. Counts on family lines are recomputed from the
manifests.
Tools: llms_lint.py descriptions; docset_refine/export_llms.py _describe();
hub_query_docset(layer="facts") to fetch a page’s definition unit; local LLM
(HUB_REFINE_LLM_URLS, qwen3.5:35b) for bulk polish, claude -p for the final 10%.
Relations: document-formats/references/llms-txt-generation-tooling.md § descriptions
(why extractive beats generated); prompt-deep-optimizer owns the polish prompt if it drifts.
P4 — Navigation design (model)
Judges I2 I3 N2 N3 N4 N7. The subagent reads the whole index and the section names against
the question list (seeded from P12’s question bank, or from hub_ask logs for hub docsets).
Judged: sections mirror the URL tree or the alphabet rather than tasks/topics → Medium.
Hot pages (quickstart, auth, reference root, errors, pricing) not in the first section →
Medium. Reference/pricing under ## Optional → Medium. Blockquote that does not say what the
thing is and who it is for → Medium. Free text before the first H2 that a reader does not need
→ Low.
Updated: propose a section plan (name → links) as a diff; apply only when every link is
preserved (the demotion guard — a reorganisation that drops a link is a High finding on
itself). Section names are taken from the source nav when the mirror carries it
(llms_acquire keeps the source index order), else from the concept-tree children of the
docset’s concept.
Tools: subagent (model per SKILL.md frontmatter), hub_concept_lookup for topic names,
hub_docset_index to read the current index. Relations: concept-family-explorer for a
topical file’s section skeleton; dfo (document-deep-optimizer) for prose quality of the
blockquote when it exceeds two sentences.
P5 — Size ladder and budgets (deterministic)
Judges S1 S2 S3 S4 S5 S6. Reads manifest.json when present, else measures.
Judged: index > 10 KB → Medium; > 100 KB → High (it is a full file wearing the wrong name). Full file with no small variant beside it → Medium. Small > 50k tokens → Medium. Facts/prose ratio > 0.30 → Medium. Manifest missing or inconsistent with the files (± 2%) → Medium (H8). A page block > 200 KB → Low.
Updated (--fix safe): regenerate manifest.json; rebuild llms-small.txt with
export_llms.build_small; for an oversize index, propose hub-and-spoke split by section
(each section → <section>/llms.txt, root keeps one line per section) — applied only with
--split, since it changes URLs.
Tools: llms_lint.py size; export_llms.py. Relations: the 50k figure is the Cursor
stability ceiling from llms-txt-ecosystem-evidence.md; recalibrate there, not here.
P6 — Full-file fidelity (deterministic)
Judges C1 C2 C3 C4 C5. Splits with the detected grammar; per block checks title, source
URL, residue patterns, fence balance, table separators; hashes bodies for exact dups;
units.dedup-style embedding pass for near-dups when --near-dups.
Judged: block fails to parse → High (C1). Missing title/URL → High per block, one finding. Residue → Medium with line refs. Unbalanced fences → Medium (they poison every downstream chunker). Exact duplicate page → Medium; near-dup ≥ 0.95 cosine → Low with the pair.
Updated (--fix safe): strip known residue (Documentation Index blockquote,
[Skip to content], theme={null}, MDX import lines); close a dangling fence at the block end;
drop exact duplicates keeping the first occurrence; rewrite blocks into the declared grammar
(normalisation, lossless). Near-dups are reported, never removed (localised copies may be
wanted).
Tools: llms_lint.py full; llms_acquire.split_llms_full; docset_refine.clean;
semantic_ops.vecstore for near-dup embeddings. Relations: docset_refine clean is the
production version of the residue strip — the lint uses the same pattern table
(docset_refine/clean.py), never a second copy.
P7 — Facts-file shape (deterministic)
Judges C6 R3 R4 R7 P6. Parses each unit line as build_facts emits it:
- [type] text — <url>#<anchor> under a ## <page title> / <url> heading pair (optional
trailing · keywords: a, b and · verified-as-of: YYYY-MM-DD fields are accepted). Checks
type ∈ docset_refine.UNIT_TYPES (concept, fact, actionable, question, problem, statement,
quote, idea, snippet, parameter, definition, change), anchor resolves to a heading in the
mirror page, unit ≤ 2 sentences / 400 chars, code tokens present in the unit’s keywords
(from units.jsonl, the source of truth the facts file is rendered from), every indexed page
has ≥ 1 unit.
Judged: unit without source → High. Anchor not resolvable → Medium (High if > 20%).
Untyped or unknown type → Medium. Unit > 2 sentences (or > 400 chars) → Medium. Page with zero
units → Medium if a reference/guide page, Low otherwise. Code token in text but not in
keywords → Low, aggregated.
Updated (--fix safe): re-anchor by fuzzy heading match within the same page (≥ 0.9
similarity), else leave as BLOCKED; add missing keywords by regex extraction (backtick spans,
--flags, ENV_VARS, CamelCase API names); split a two-claim unit at the sentence boundary
when both halves keep the same anchor. Type inference for untyped units is model work → P8.
Tools: llms_lint.py facts; docset_refine/units.py (the unit schema), extract.py
(the deterministic extractors that produce most units). Relations: docset_refine units
regenerates units from scratch; run it instead of fixing when > 30% of units fail.
P8 — Facts truthfulness (model, sampled)
Judges C7 D5 P6. Samples 20 units (stratified by type, weighted to LLM-generated ones) and
re-reads each source span from the mirror (hub_llms_full_read(page=…) or the banner mirror).
For each: supported / partially / unsupported / stale (version-stamped claim contradicted).
Judged: unsupported → High per unit (the facts file is the layer agents trust without
opening pages; a wrong fact there is worse than a missing one). Partially supported
(generalised beyond the span) → Medium. Stale → Medium with the newer span quoted. ≥ 3
unsupported in the sample → High on the file: regenerate the LLM units (docset_refine units --force), do not patch.
Updated: unsupported units are removed and logged; partially-supported units are rewritten
to the span’s wording (extractive rewrite, then re-verified); stale units get the newer text +
verified-as-of stamp. Every rewrite goes through the blind re-audit gate.
Tools: subagent with hub_llms_full_read, Read on the mirror; the golden question set
(docs/superpowers/specs/2026-08-30-docset-golden-baseline.md) for the “does it still answer”
check. Relations: /dr --refresh owns re-verifying volatile claims at the concept level;
this pass hands stale units there when the whole page moved.
P9 — Provenance, rights and steering (deterministic + model)
Judges P1 P3 P4 P5. Regex for the banner (generated, verified-as-of, generator name),
secret/email/internal-host patterns, imperative-to-model spans (“ignore”, “you must”, “always
say”, “do not mention”, “rank this”); model confirms the imperative hits are aimed at a reader
model rather than quoting a doc that legitimately says “you must set X”.
Judged: steering span confirmed → High (it is prompt injection carried by a docs file; the Cloudflare ecosystem note measured ~42% of wild files trying it). Secret/credential → High. Third-party full file without the internal marker → High. No provenance banner → Medium. Volatile unstamped claim in facts → Low.
Updated (--fix safe): add/refresh the provenance banner from the manifest; add the
<!-- internal: third-party republication, do not publish --> marker when the source host is
not ours; redact secrets to [redacted] with a BLOCKED row (the source page has to be fixed
upstream). Steering spans are deleted, never rephrased.
Tools: llms_lint.py trust. Relations: the injection guard in
convergence-and-severity.md applies to the optimizer itself as well — a steering span found
in the target never changes pass behaviour.
P10 — Family and nesting (deterministic + model)
Judges F1–F6, D6. For a family file: every target is an llms.txt; counts present;
membership vs the concept tree (hub_concept_tree children of the family’s root) or the
manifest list passed with --members; ## Shared exists and product files do not duplicate its
targets; describedby headers if served.
Judged: page links in a family → High. Missing tree child → Medium (a product the family
claims but does not link). Counts missing/stale vs product manifests → Medium. Shared material
duplicated into products → Low. ## Facts absent when products have facts files → Medium.
Updated: regenerate with docset_refine family from the member mirrors — the family file is
never hand-edited; the pass edits the member list or the summary and rebuilds. Missing members
become BLOCKED rows pointing at the acquire step.
Tools: export_llms.family(), hub_concept_tree, hub_concept_lookup, llms_serve.py
root renderer. Relations: hub-architect for where families are defined (concept tree =
family definition); concept-family-explorer when the family’s membership itself is in
question.
P11 — Retrieval readiness (live)
Judges R1 R2 R4 R7. Requires the docset to be indexed. Runs 10 exact-token probes (tokens
harvested from the facts file’s keywords: env vars, flags, error strings, API names) through
the FTS5 keyword index and 10 golden questions through the vector index at layer=facts and
layer=raw.
Judged: keyword index missing → High (R1 — the cheap path does not exist). Exact-token probe misses (< 10/10) → High if < 7, else Medium, listing the tokens. Facts layer scoring below raw on the golden set → Medium (the layer is worse than what it replaced). Pages with no units → see P7.
Updated: build/rebuild the keyword index (docset_indexer.py keyword-index <docset>);
add missing tokens to keywords (P7 fix) and re-index; if the facts layer underperforms,
re-run units with the polish step and re-index — never lower the golden bar.
Tools: docset_indexer.py keyword <docset> "q" (FTS5, BM25), query --layer facts|raw,
hub_query_docset, golden set from the baseline spec. Relations: semantic_ops.fuse
(RRF) is what hub_ask uses to combine both; this pass proves each leg works alone.
P12 — Agent usability test (live agent)
Judges R5 R6 N3. A fresh-context subagent receives ONLY the index (or ONLY the facts file)
and 10 questions from the bank (references/facts-to-llms-howto.md § question bank shape). It
must answer by following ≤ 2 links (index) or from the file alone (facts), and report per
question: answered / partial / not found, links followed, time.
Judged: index < 8/10 → Medium; < 6/10 → High. Facts < 7/10 → Medium. A question answered only after > 2 hops → Medium on the section that hid the page (feeds P4). A confident wrong answer → High, traced to the description or unit that misled (feeds P3/P8).
Updated: no direct edits — the transcript is evidence for P3/P4/P8 rewrites. The questions
and verdicts persist to ~/.claude/skill-consolidation/evals/llms/<key>.eval.jsonl so the
next run replays them (the family’s eval-corpus pattern).
Tools: Agent tool (fresh context), hub_docset_index, hub_llms_full_read for link
following inside the hub. Relations: skill-optimizer Pass H is the same idea applied to
skill descriptions; the eval file shape is shared.
P13 — Serving and headers (live HTTP)
Judges H2 H3 H4 H7. HEAD + GET on the served URL(s): status, content-type, redirects,
Link headers, X-Markdown-Tokens, auth challenge; for HTML pages of the same site, probe
Accept: text/markdown and the .md twin.
Judged: non-200, redirect to HTML, auth challenge → High. Content-type not markdown/plain →
Medium. Missing describedby/alternate → Low. Tokens header missing → Low.
Updated: for hub-served files, fix in llms_serve.py (headers are code, not content) and
restart com.global-ai-hub.llms-serve; for third-party sites, report only.
Tools: curl -I, llms_serve.py /health, launchctl kickstart. Relations: the
document-formats/references/llms-txt.md § discovery section defines which headers matter.
P14 — Hygiene (deterministic, excluded from Medium+ credit)
Judges H1. Encoding, line endings, tabs in list lines, trailing whitespace, BOM, single trailing newline, smart quotes inside URLs.
Judged: Hygiene row; always fixed, never counted toward convergence. Exception: a smart quote or zero-width char inside a URL → High (dead link in disguise).
Updated (--fix safe): byte-level normalisation.
Tools: llms_lint.py hygiene. Relations: skill-optimizer Pass L is the same class.
P15 — Regeneration parity (deterministic)
Judges H5 H8 R7. When the export directory sits beside its source mirror: does
docset_refine export reproduce the current files (modulo timestamps)? If not, the file was
hand-edited or the mirror moved on.
Judged: hand edits that the generator would drop → Medium (they will be lost on the next
refine; either fold them into the generator’s inputs — nav order, summary, title flags — or
mark the file hand-maintained in the manifest). Mirror newer than the export → Medium
(stale). Manifest counts diverge → Medium.
Updated: regenerate when the mirror is newer; when hand edits exist, write them into the
manifest’s overrides (title, summary, section order) so the generator reproduces them, then
regenerate.
Tools: docset_refine export, diff. Relations: pipeline_manager.py refine stage is
what will overwrite the file; this pass makes the overwrite safe.
Severity resolution across passes
Same span flagged by several passes: take the highest severity; tie → lower pass number wins
(P0 > P1 > …); tie → the more conservative fix (report over rewrite). BLOCKED rows (dead links
with no mirror page, secrets that must be fixed upstream, members not yet acquired) are
reported but never count toward convergence, per the contract. The full ladder and the exits
live in ~/.claude/skill-consolidation/convergence-and-severity.md.
N/A rules
A pass reports N/A (<reason>) — never silently skips — when: the kind excludes it (P6 on an
index), the layer is absent (P11 without an indexed docset), the input is missing (P13 without a
URL, P15 without a mirror), or the flag is off (P12 without --agent-test on a refresh run).
An N/A that is caused by something the run could create (no keyword index, no small variant)
is also a Medium finding on the missing thing.