Designing a Mathlib definition and its API
A mathlib definition is an interface contract paid for by every future
user, and the API around it is the rest of that contract: the statements
every consumer will actually call. The core insight of this workflow:
mathlib has already run the experiment for most design choices — in
its git history, its PR reviews, and its Zulip threads. Excavate that
experiment instead of re-arguing it from first principles; then
demonstrate the result with compiling code, not prose.
Load each reference file when its phase begins — nothing else up front.
(Workflow distilled from the Group.Generators / Group.Presentation
design project, 2026-07; exemplar artifacts in that project root:
scratch_field_vs_parameter.lean for a definition axis,
scratch_closure_vs_lift.lean for an API axis.)
Outputs, in order
- A precedent dossier — chat summary (durable conclusions also go to a
memory note) of what mathlib tried, what it refactored to, and why,
every claim pinned to a PR URL, commit hash,
path:line, or Zulip
message. Covers both the structure shape and the surface its
shape-mates ship.
- The definition itself, each field's form chosen by the API that will
consume it ("the verb decides the container").
- The API surface — each statement admitted with its consumer named,
stated in the form the consumer demands, attributed (
@[simp],
@[ext], @[simps]) deliberately, and audited against bloat.
- An example sheet — one compiling contrasting-cases
.lean file in
which the reader watches each rejected design fail and each chosen
design succeed. Two genres: the full sheet with historical
reconstruction and a closing /-! ## References -/ dossier section,
and the lighter API-scratch genre when the precedent is shape-level
and all pins are local.
Phase 0 — Name the consumers
Before any design choice, write down the downstream API that will consume
each candidate — for each field of the structure (FreeGroup.lift wants
α → G; Subgroup.normalClosure and PresentedGroup want
Set (FreeGroup α); instance search wants class arguments on
parameters), and for each candidate statement the argument slot or
proof site that will call it. Every later decision asks what form the
consumer demands — "the header is a contract; make it speak the caller's
language" — never symmetry or aesthetics.
Phase 1 — Precedent excavation
Follow references/precedent-excavation.md end to end: pick the search
instrument before searching (§0 — declaration counts come from loogle,
source-site counts from grep, and asking the wrong one silently returns a
different quantity, while a lopsided count may measure absent consumers
rather than a preference); survey live
mathlib and classify precedents into its four buckets; git archaeology
(pickaxe-dating, pre-refactor quotes via git show <hash>^:<path>, blast
radius); pin provenance (§2b — a merged precedent is evidence in
proportion to the contest it survived; date the decision, not just the
code, and say when it is an uncontested first draft); the PR trail via
gh (reviewer arguments and benchmark numbers
are design evidence); Zulip via the spectator JSON API (web search does
not index recent Zulip), keeping stated doctrine separate from the
precedent record; the direction-of-travel count including failed
reverse experiments, stalled parallel constructions, and the flip census
(zero migrations between two encodings marks a judgment call); cost
accounting of holdouts; and adversarial
verification of every claimed precedent against source — in the case study
3 of 49 claims died there. When the deliverable includes an API surface,
survey what the shape-mates ship, not just how they are declared — the
surface taxonomy to survey against is references/api-surface.md §1.
Deliver the dossier as a chat summary before touching the definition.
Phase 2 — Decide each definition axis, then stress-test
Work the checklist in references/design-axes.md; for each axis record
the alternatives, the consumer-driven argument, the precedent, the
honest flip side, and the verdict's kind (§0) — a dossier with no flip
sides is advocacy, not analysis, and one that hides a close call behind a
confident verdict is worse, because it looks like analysis. Check first
that each axis exists: twins that compile as example : A = B := rfl
are one design with two spellings, and the evidence shifts to what rw
and simp see (§0, §10). Then
stress-test before committing:
- Simulate variants in scratch, never in the repo — build the
alternative live (
lean_run_code, or a scratch file in the project root
so the IDE elaborates it) and run the same probes against both.
- Dogfood: build a small consumer API on top of the candidate
definition; its defects surface as friction in the consumer's proofs.
- Process review feedback analysis-first: check a reviewer's
suggestion against the consumer principle before writing code — it may
quietly reintroduce the exact problem just agreed decisive.
- Regression check: "Is any consumer made worse off?" A change is safe
when the old form is recoverable by a total conversion at the use site.
- Instance registrations get measured, not argued: the cost of a new
instance lives in failing searches, which priority cannot shield. Run
the A/B toggle protocol in
references/design-axes.md §7b before the
verdict hardens.
Phase 3 — Build the API surface
Follow references/api-surface.md: the standard surface a bundled-data
structure ships (constructors, projection lemmas, ext, transport,
bridges, the existential layer); the per-statement admission tests (a
named consumer, a precedent-standard slot, or a conversion paid once in
the API — otherwise it stays out); statement form (index type over image
set, one simp-normal form, iff vs directional corollaries, weakest
assumptions); and the driver test — prove the downstream targets using
only the proposed API; friction names a missing lemma, and a lemma no
driver uses is a bloat candidate. The same stress-test discipline as
Phase 2 applies: simulate contested statement shapes in scratch before
committing to one.
Phase 4 — The example sheet
Follow references/example-sheet.md (the recipe and the compile/citation
contracts; the learning-science grounding behind the format is split into
references/example-sheet-evidence.md, loaded only when the format itself
is questioned or amended). In one line: minimal structure twins differing
in exactly one design choice, numbered tests chunked per caps-labeled
style, deliberate failures at labeled EXPECTED ERROR examples with
verbatim compiler text, escape hatches priced honestly, the real
pre-refactor API reconstructed from pinned git history, and the references
dossier as the sheet's closing section, mirroring the argument. For a
contested API choice, the lighter genre in references/api-surface.md §5
usually suffices — same contracts, no historical reconstruction. A
module-boundary axis (what an importer can unfold) needs the multi-file
trio genre — the genre note at the top of references/example-sheet.md.
Phase 5 — The PR justification
- Citation order: same-subject-area precedent first (for
Group.Generators: #7698, whose commit message makes the
geometric-group-theory argument), cross-area refactors second (#25085,
#37928), Zulip authority third (the 2021 "Bundled basis" thread),
performance numbers fourth.
- Link the example sheet as evidence a reviewer can run.
- Where the axis was a judgment call, the PR says it was one and gives
the alternative. A reviewer who reopens it is then reopening a question
the PR already admitted was open, which is cheaper than discovering
that the confident paragraph was covering a coin flip.
- Keep the PR minimal: one statement shape per PR, general lemmas
upstreamed to their natural home file in a separate prerequisite PR, no
riders.
Companion skill — clarifying questions (all phases)
When the user asks a clarifying or follow-up question while this skill is
active, invoke adaptive-teacher: it reads its learner model first,
answers per its protocol, and logs the question. Two obligations flow
back: compress in dossiers and example sheets whatever its learner model
marks known-well, and when a logged question reveals a user-independent
defect in this skill's reference text, fix the reference file here and
commit, citing the log entry date.
Hard rules (all phases)
- Always cite the source. Every quote, reconstruction, or historical
reference carries
path:line, a commit-pinned form
(git show <hash>^:<path>) for historical content, and the PR/Zulip
URL. Citing obliges quoting exactly — verbatim attributes, signatures,
and punctuation; verify quotes against the source before writing them,
not after.
- Truth is the floor, not a slider. A statement can be true and still
misleading; reject misleading forms even when technically correct. Never
overstate a failure — if a rejected design's proof happens to work, say
so and rewrite the test to show the real pain.
- Report, don't advocate. Every verdict names its kind — mechanism, a
count, or a judgment call (
references/design-axes.md §0). Every count
names the instrument that produced it, by giving the command; grep and
loogle answer different questions and a count is only as good as the
match between the two (references/precedent-excavation.md §0). A judgment
call says so in its first sentence and stops there; it is not settled
by a tiebreaker invented for the occasion, and "the evidence does not
separate these" is a complete answer. Before claiming any asymmetry,
try to erase it by writing the line the other side is missing. Give a
recommendation only when asked, in one sentence, marked as a
preference and placed after the evidence.
- Demonstrations must be honest. An escape hatch must recover the tool
under test; a workaround that sidesteps the tool stays only if labeled
as a contrast case.
- Scratch files stay unstaged. Every scratch/example-sheet
.lean file the
workflow produces (project-root scratch*.lean, the scratch_*.lean twins,
the example sheet itself) is left as an unstaged working-tree change — never
git added into the mathlib commit. They are local evidence you keep running
in the IDE, not part of the PR; leave them out of the staging area so the
contribution stays minimal.
- Comment style: all free commentary in
/- ... -/ blocks (docstrings
/-- -/, module docs /-! -/); never -- line comments.
- Compile contract: an example sheet compiles with exactly the
intended deliberate errors at labeled
EXPECTED ERROR examples and zero
warnings; re-verify the full diagnostic set after every edit.
- Relators-adjacent docstrings say "evaluates to the identity in
G",
never "dies in G".
1---2name: mathlib-api3description: Evidence-driven workflow for designing a Mathlib definition and the API around it. Use when adding a new structure/def intended for mathlib; when choosing between design alternatives (index type as field vs parameter, Set vs indexed family, coarse set vs finer indexed structure and when the fine one should exist at all, data vs Prop, instance vs hypothesis, existential shape, `Nat.find` vs `sInf`-style encodings and where their classical plumbing lives, junk values of totalized quantities, sealed vs exposed bodies under the module system — `@[expose]`/`@[no_expose]` — and which object should carry an instance); when deciding which statements an API should ship and in what form (which Prop is the field, iff vs directional corollaries, simp orientation, smart constructors, bridges to existing idioms, bloat audits); when justifying a design in PR review; or when asked to "check the precedents" for a definition or an API, or to build a contrasting-cases example sheet. Clarifying and follow-up questions about a de4---56# Designing a Mathlib definition and its API78A mathlib definition is an interface contract paid for by every future9user, and the API around it is the rest of that contract: the statements10every consumer will actually call. The core insight of this workflow:11**mathlib has already run the experiment for most design choices** — in12its git history, its PR reviews, and its Zulip threads. Excavate that13experiment instead of re-arguing it from first principles; then14*demonstrate* the result with compiling code, not prose.1516Load each reference file when its phase begins — nothing else up front.17(Workflow distilled from the `Group.Generators` / `Group.Presentation`18design project, 2026-07; exemplar artifacts in that project root:19`scratch_field_vs_parameter.lean` for a definition axis,20`scratch_closure_vs_lift.lean` for an API axis.)2122## Outputs, in order23241. **A precedent dossier** — chat summary (durable conclusions also go to a25 memory note) of what mathlib tried, what it refactored to, and why,26 every claim pinned to a PR URL, commit hash, `path:line`, or Zulip27 message. Covers both the structure shape and the surface its28 shape-mates ship.292. **The definition itself**, each field's form chosen by the API that will30 consume it ("the verb decides the container").313. **The API surface** — each statement admitted with its consumer named,32 stated in the form the consumer demands, attributed (`@[simp]`,33 `@[ext]`, `@[simps]`) deliberately, and audited against bloat.344. **An example sheet** — one compiling contrasting-cases `.lean` file in35 which the reader *watches* each rejected design fail and each chosen36 design succeed. Two genres: the full sheet with historical37 reconstruction and a closing `/-! ## References -/` dossier section,38 and the lighter API-scratch genre when the precedent is shape-level39 and all pins are local.4041## Phase 0 — Name the consumers4243Before any design choice, write down the downstream API that will consume44each candidate — for each *field* of the structure (`FreeGroup.lift` wants45`α → G`; `Subgroup.normalClosure` and `PresentedGroup` want46`Set (FreeGroup α)`; instance search wants class arguments on47*parameters*), and for each candidate *statement* the argument slot or48proof site that will call it. Every later decision asks what form the49consumer demands — "the header is a contract; make it speak the caller's50language" — never symmetry or aesthetics.5152## Phase 1 — Precedent excavation5354Follow `references/precedent-excavation.md` end to end: pick the search55instrument before searching (§0 — declaration counts come from loogle,56source-site counts from grep, and asking the wrong one silently returns a57different quantity, while a lopsided count may measure absent consumers58rather than a preference); survey live59mathlib and classify precedents into its four buckets; git archaeology60(pickaxe-dating, pre-refactor quotes via `git show <hash>^:<path>`, blast61radius); pin provenance (§2b — a merged precedent is evidence in62proportion to the contest it survived; date the decision, not just the63code, and say when it is an uncontested first draft); the PR trail via64`gh` (reviewer arguments and benchmark numbers65are design evidence); Zulip via the spectator JSON API (web search does66not index recent Zulip), keeping stated doctrine separate from the67precedent record; the direction-of-travel count including failed68reverse experiments, stalled parallel constructions, and the flip census69(zero migrations between two encodings marks a judgment call); cost70accounting of holdouts; and adversarial71verification of every claimed precedent against source — in the case study723 of 49 claims died there. When the deliverable includes an API surface,73survey what the shape-mates *ship*, not just how they are declared — the74surface taxonomy to survey against is `references/api-surface.md` §1.75Deliver the dossier as a chat summary before touching the definition.7677## Phase 2 — Decide each definition axis, then stress-test7879Work the checklist in `references/design-axes.md`; for each axis record80the alternatives, the consumer-driven argument, the precedent, the81*honest flip side*, and the verdict's kind (§0) — a dossier with no flip82sides is advocacy, not analysis, and one that hides a close call behind a83confident verdict is worse, because it looks like analysis. Check first84that each axis exists: twins that compile as `example : A = B := rfl`85are one design with two spellings, and the evidence shifts to what `rw`86and `simp` see (§0, §10). Then87stress-test before committing:8889- **Simulate variants in scratch**, never in the repo — build the90 alternative live (`lean_run_code`, or a scratch file in the project root91 so the IDE elaborates it) and run the same probes against both.92- **Dogfood**: build a small consumer API on top of the candidate93 definition; its defects surface as friction in the consumer's proofs.94- **Process review feedback analysis-first**: check a reviewer's95 suggestion against the consumer principle before writing code — it may96 quietly reintroduce the exact problem just agreed decisive.97- **Regression check**: "Is any consumer made worse off?" A change is safe98 when the old form is recoverable by a total conversion at the use site.99- **Instance registrations get measured, not argued**: the cost of a new100 instance lives in *failing* searches, which priority cannot shield. Run101 the A/B toggle protocol in `references/design-axes.md` §7b before the102 verdict hardens.103104## Phase 3 — Build the API surface105106Follow `references/api-surface.md`: the standard surface a bundled-data107structure ships (constructors, projection lemmas, ext, transport,108bridges, the existential layer); the per-statement admission tests (a109named consumer, a precedent-standard slot, or a conversion paid once in110the API — otherwise it stays out); statement form (index type over image111set, one simp-normal form, iff vs directional corollaries, weakest112assumptions); and the driver test — prove the downstream targets using113only the proposed API; friction names a missing lemma, and a lemma no114driver uses is a bloat candidate. The same stress-test discipline as115Phase 2 applies: simulate contested statement shapes in scratch before116committing to one.117118## Phase 4 — The example sheet119120Follow `references/example-sheet.md` (the recipe and the compile/citation121contracts; the learning-science grounding behind the format is split into122`references/example-sheet-evidence.md`, loaded only when the format itself123is questioned or amended). In one line: minimal structure twins differing124in exactly one design choice, numbered tests chunked per caps-labeled125style, deliberate failures at labeled `EXPECTED ERROR` examples with126verbatim compiler text, escape hatches priced honestly, the real127pre-refactor API reconstructed from pinned git history, and the references128dossier as the sheet's closing section, mirroring the argument. For a129contested API choice, the lighter genre in `references/api-surface.md` §5130usually suffices — same contracts, no historical reconstruction. A131module-boundary axis (what an importer can unfold) needs the multi-file132trio genre — the genre note at the top of `references/example-sheet.md`.133134## Phase 5 — The PR justification135136- Citation order: same-subject-area precedent first (for137 `Group.Generators`: #7698, whose commit message makes the138 geometric-group-theory argument), cross-area refactors second (#25085,139 #37928), Zulip authority third (the 2021 "Bundled basis" thread),140 performance numbers fourth.141- Link the example sheet as evidence a reviewer can run.142- Where the axis was a judgment call, the PR says it was one and gives143 the alternative. A reviewer who reopens it is then reopening a question144 the PR already admitted was open, which is cheaper than discovering145 that the confident paragraph was covering a coin flip.146- Keep the PR minimal: one statement shape per PR, general lemmas147 upstreamed to their natural home file in a separate prerequisite PR, no148 riders.149150## Companion skill — clarifying questions (all phases)151152When the user asks a clarifying or follow-up question while this skill is153active, invoke `adaptive-teacher`: it reads its learner model first,154answers per its protocol, and logs the question. Two obligations flow155back: compress in dossiers and example sheets whatever its learner model156marks known-well, and when a logged question reveals a *user-independent*157defect in this skill's reference text, fix the reference file here and158commit, citing the log entry date.159160## Hard rules (all phases)161162- **Always cite the source.** Every quote, reconstruction, or historical163 reference carries `path:line`, a commit-pinned form164 (`git show <hash>^:<path>`) for historical content, and the PR/Zulip165 URL. Citing obliges quoting *exactly* — verbatim attributes, signatures,166 and punctuation; verify quotes against the source before writing them,167 not after.168- **Truth is the floor, not a slider.** A statement can be true and still169 misleading; reject misleading forms even when technically correct. Never170 overstate a failure — if a rejected design's proof happens to work, say171 so and rewrite the test to show the real pain.172- **Report, don't advocate.** Every verdict names its kind — mechanism, a173 count, or a judgment call (`references/design-axes.md` §0). Every count174 names the instrument that produced it, by giving the command; grep and175 loogle answer different questions and a count is only as good as the176 match between the two (`references/precedent-excavation.md` §0). A judgment177 call says so in its first sentence and stops there; it is not settled178 by a tiebreaker invented for the occasion, and "the evidence does not179 separate these" is a complete answer. Before claiming any asymmetry,180 try to erase it by writing the line the other side is missing. Give a181 recommendation only when asked, in one sentence, marked as a182 preference and placed after the evidence.183- **Demonstrations must be honest.** An escape hatch must recover the tool184 under test; a workaround that sidesteps the tool stays only if labeled185 as a contrast case.186- **Scratch files stay unstaged.** Every scratch/example-sheet `.lean` file the187 workflow produces (project-root `scratch*.lean`, the `scratch_*.lean` twins,188 the example sheet itself) is left as an unstaged working-tree change — never189 `git add`ed into the mathlib commit. They are local evidence you keep running190 in the IDE, not part of the PR; leave them out of the staging area so the191 contribution stays minimal.192- **Comment style**: all free commentary in `/- ... -/` blocks (docstrings193 `/-- -/`, module docs `/-! -/`); never `--` line comments.194- **Compile contract**: an example sheet compiles with *exactly* the195 intended deliberate errors at labeled `EXPECTED ERROR` examples and zero196 warnings; re-verify the full diagnostic set after every edit.197- Relators-adjacent docstrings say "evaluates to the identity in `G`",198 never "dies in `G`".