[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-trail-of-bits-writing-lean-proofs":3,"mdc--os30ha-key":35,"related-repo-trail-of-bits-writing-lean-proofs":2012,"related-org-trail-of-bits-writing-lean-proofs":2112},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":23,"repoUrl":24,"updatedAt":25,"license":26,"forks":27,"topics":28,"repo":30,"sourceUrl":33,"mdContent":34},"writing-lean-proofs","write and review Lean 4 proofs","Writes and reviews structured Lean 4 proofs and designs Lean libraries following Mathlib conventions. Use when proving theorems in Lean, formalizing mathematics or specifications in Lean 4, defining new types or definitions in a Lean library, reviewing Lean proofs for readability and maintainability, refactoring long tactic proofs into lemmas, filling in sorry placeholders in a Lean development, setting up CI or linters for a Lean project, diagnosing slow proofs or maxHeartbeats timeouts, or writing custom tactics, macros, or linters.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"trail-of-bits","Trail of Bits","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Ftrail-of-bits.png","trailofbits",[13,17,20],{"name":14,"slug":15,"type":16},"Mathematics","mathematics","tag",{"name":18,"slug":19,"type":16},"Engineering","engineering",{"name":21,"slug":22,"type":16},"Code Analysis","code-analysis",6139,"https:\u002F\u002Fgithub.com\u002Ftrailofbits\u002Fskills","2026-08-08T04:19:06.461752",null,541,[29],"agent-skills",{"repoUrl":24,"stars":23,"forks":27,"topics":31,"description":32},[29],"Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows","https:\u002F\u002Fgithub.com\u002Ftrailofbits\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fwriting-lean-proofs\u002Fskills\u002Fwriting-lean-proofs","---\nname: writing-lean-proofs\ndescription: \"Writes and reviews structured Lean 4 proofs and designs Lean libraries following Mathlib conventions. Use when proving theorems in Lean, formalizing mathematics or specifications in Lean 4, defining new types or definitions in a Lean library, reviewing Lean proofs for readability and maintainability, refactoring long tactic proofs into lemmas, filling in sorry placeholders in a Lean development, setting up CI or linters for a Lean project, diagnosing slow proofs or maxHeartbeats timeouts, or writing custom tactics, macros, or linters.\"\n---\n\n# Writing Lean Proofs\n\n## Contents\n\n- [When to Use](#when-to-use)\n- [When NOT to Use](#when-not-to-use)\n- [The workflow](#the-workflow)\n- [The extraction ladder](#the-extraction-ladder)\n- [Quick reference](#quick-reference)\n- [Rationalizations to reject](#rationalizations-to-reject)\n- [References](#references)\n\nStructured Lean 4 proof writing and library design, distilled from Mathlib's\nstyle and review conventions and from the methodology of large formalization\nprojects (Liquid Tensor Experiment, PFR, Fermat's Last Theorem).\n\n**Core principle: design top-down, prove bottom-up.** Lean propositions are\nproof-irrelevant — only a theorem's *statement* can affect later declarations.\nStatements are the stable interface; proofs are disposable and freely\nreplaceable. Put design effort into definitions and statements, then fill in\nproofs against skeletons that already compile (modulo `sorry`).\n\n## When to Use\n\n- Proving theorems in Lean 4, from single lemmas to multi-file developments\n- Formalizing mathematics, protocols, or software specifications in Lean\n- Defining new types, structures, or functions in a Lean library\n- Reviewing Lean code for readability, maintainability, or Mathlib readiness\n- Refactoring a long or fragile tactic proof into lemmas\n- Setting up a formalization project that several people or agents will\n  contribute to in parallel\n- Setting up CI, linters, or verification gates for a Lean project — do this\n  at project start, before patterns propagate\n- Diagnosing slow proofs, `maxHeartbeats` timeouts, or expensive reduction\n- Writing custom tactics, macros, or project-specific linters\n\n## When NOT to Use\n\n- Lean 4 as a general-purpose programming language (no proofs involved) —\n  most of this skill targets proof and API structure\n- Coq, Isabelle, Agda, or Lean 3 — conventions and tactic names differ;\n  Lean 3 idioms (`ge_or_gt` linting, `discrete_field`) are obsolete\n- Verified-software Lean projects with their own house style (e.g.\n  spec-traceability-first codebases): Mathlib conventions are the community\n  default, but check the project's CONTRIBUTING first and defer to it\n\n## The workflow\n\n### 1. Design definitions and their API first\n\nDefinitions carry the design weight. Before proving anything about a new\nconcept:\n\n- **Prefer total functions with junk values** over subtypes or `Option` in\n  signatures (Mathlib: `(0 : ℝ)⁻¹ = 0`). Side conditions then appear only on\n  the lemmas that need them, not at every use site.\n- **Bundle**: new morphism kinds are structures with a `FunLike` instance;\n  new subobject kinds use `SetLike`; carry property proofs as structure\n  fields, not separate `IsHom`-style predicates.\n- **Pick the canonical spelling** (simp-normal form) for every concept with\n  multiple equivalent forms, and state all API lemmas for that form only.\n- **Write the API in the same file, immediately**: `ext`, `@[simp]`,\n  coercion, and injectivity lemmas — before the definition is used anywhere.\n  Downstream proofs use the API, never `unfold`\u002F`show ... from rfl`.\n\nSee [library-design.md](references\u002Flibrary-design.md) for the full set of\ndesign rules with rationale.\n\n### 2. Build a sorry skeleton\n\nState everything before proving anything, at every scale:\n\n- **Project scale**: state the target theorem and the lemmas it needs, all\n  with `:= sorry`, and make the file compile. Each `sorry` is now an\n  independent work unit — a contributor (human or LLM) can discharge one\n  without understanding the rest. This is how LTE, PFR, and FLT scale to\n  dozens of parallel contributors.\n- **Proof scale**: inside a proof, lay out the `have`\u002F`suffices`\u002F`calc`\n  skeleton with `sorry` justifications, get Lean to accept the structure,\n  then fill each step. Keeping the structure intact is what produces useful\n  error messages while you work.\n\n```lean\nexample (a b c d : ℝ) (h : c = d * a + b) (h' : b = a * d) : c = 2 * a * d := by\n  calc\n    c = d * a + b     := sorry\n    _ = d * a + a * d := sorry\n    _ = 2 * a * d     := sorry\n```\n\n### 3. Fill goals, one focused goal at a time\n\n- Every new subgoal gets a focusing dot `·` with an indented block — never\n  leave several goals active in unfocused sequence (Mathlib's `multiGoal`\n  linter enforces this). This is what kills fragile goal-ordering dependence.\n- Open each block with a redundant `show` stating its goal. The proof works\n  without it; reviewers and future editors need it. If `show` would *change*\n  the goal, use `change` instead — keep stated goals honest.\n- Chained rewrites of (in)equalities become `calc` blocks, relations aligned\n  vertically.\n- `have` for forward stepping stones (\"we first establish X\"); `suffices`\n  for backward reduction (\"it suffices to show X\").\n- While drafting, annotate the goal state as a comment before non-obvious\n  tactics — emitted by Lean, never imagined. In a headless workflow, insert\n  `trace_state` at the point of interest or a deliberate `done` where goals\n  should be closed, then run `lake env lean Path\u002FTo\u002FFile.lean`; copy the\n  reported hypotheses, case name, and target. Strip routine probes after the\n  proof works. This is the single most effective technique for LLM-written\n  proofs (see [llm-techniques.md](references\u002Fllm-techniques.md)).\n\nSee [proof-style.md](references\u002Fproof-style.md) for the full tactic-style\nrules, and [naming-conventions.md](references\u002Fnaming-conventions.md) for\nnaming lemmas so their names are guessable from their statements.\n\n### 4. Verify mechanically\n\nDo not eyeball-check style — run the checkers. `lake build` is the floor,\nand it is *only* the floor: `sorry` is a warning, so a green build exits 0\nwith sorries still present.\n\n- **Gate unproved obligations by asking the kernel, never by grepping.**\n  `#print axioms myTheorem` for a spot check; for CI, collect axioms per\n  declaration with `Lean.collectAxioms` and assert the *whole* expected\n  footprint (`[propext, Classical.choice, Quot.sound]` unless deliberately\n  widened), so a stray `sorry` *or* a new trust assumption like\n  `native_decide` fails loudly. Grep is wrong in both directions: it matches\n  the word in comments, and it misses a theorem whose own text is clean but\n  which applies an unproved helper. Working script in\n  [linting.md](references\u002Flinting.md).\n- **Choose lints by project role and put them in CI at project start.** Do not\n  enable `linter.mathlibStandardSet` wholesale in a downstream project: it\n  combines proof-maintenance checks with public-API checks, house style, and\n  Mathlib-specific repository policy. For a self-contained proof, start with\n  `linter.auxLemma`, `linter.style.maxHeartbeats`,\n  `linter.style.multiGoal`, `linter.style.setOption`, and\n  `linter.style.show`. A reusable library should additionally enable\n  `linter.flexible`, `linter.style.missingEnd`,\n  `linter.style.openClassical`, and the two `unused*InType` checks. Treat\n  `nativeDecide` as a trust-policy choice and formatting or deprecated-syntax\n  checks as project style. No warning gates anything unless warnings fail\n  the build. Run Batteries' declaration-level `#lint` checks, including\n  `simpNF`, separately. Verify every option against the pinned Mathlib source\n  and with a known-trigger fixture: a misspelled `weak.` option is\n  intentionally ignored. The complete 26-member audit and lakefile profiles\n  are in [linting.md](references\u002Flinting.md).\n- **Write a custom linter for every project-specific convention** (simp-set\n  discipline, summary-lemma coverage, required attributes) — a\n  declaration-level `@[env_linter]` is one structure, and it is the only\n  thing that reliably catches \"the attribute is missing on 29 of 30\n  declarations\". See [linting.md](references\u002Flinting.md) for the recipe and\n  the engineering rules (vacuity anchors, prove-it-can-fail, allowlists).\n\n## The extraction ladder\n\nWhen does proof structure graduate into separate lemmas?\n\n0. **Before extracting, state the fragment's type and search by shape.** Put\n   the proposed statement in a scratch `example`, run `exact?` and `apply?`\n   on the bare goal, then try a type-pattern and source search. If an existing\n   theorem fits, use it. Do not report an API gap without recording the\n   searches that failed.\n\n1. **A sub-argument repeats within one proof** → name it as a local `have`.\n\n   ```lean\n   theorem min_comm (a b : ℝ) : min a b = min b a := by\n     have h : ∀ x y : ℝ, min x y ≤ min y x := by\n       intro x y\n       apply le_min\n       · show min x y ≤ y\n         exact min_le_right x y\n       · show min x y ≤ x\n         exact min_le_left x y\n     apply le_antisymm\n     · show min a b ≤ min b a\n       exact h a b\n     · show min b a ≤ min a b\n       exact h b a\n   ```\n\n2. **The statement is independently interesting, or extraction sheds\n   hypotheses the sub-argument does not need** → standalone lemma. Dropping\n   unneeded hypotheses is the stronger trigger: the extracted lemma becomes\n   more general than the proof it came from.\n3. **The proof reads as \"long and unwieldy\"** → split it. This is Mathlib's\n   review criterion, and it is deliberately qualitative — there is no line\n   threshold. Resolve doubt by attempting the extraction: if a fragment has\n   a clean statement, it wanted to be a lemma.\n\n## Quick reference\n\n| Rule | Why | Enforced by |\n|------|-----|-------------|\n| Never unfold definitions downstream; `erw` or trailing `rfl` = missing API | API lemmas are the abstraction boundary | review (\"missing API\" smell) |\n| Terminal `simp` stays unsqueezed; non-terminal `simp` becomes `simp only [...]` | squeezed terminal calls bury the key lemmas and break on renames | style guide |\n| One focused goal at a time (`·` blocks) | kills goal-ordering fragility | `linter.style.multiGoal` |\n| `show` must not change the goal (use `change`) | stated goals stay honest | `linter.style.show` |\n| No `set_option` debug\u002Ftrace\u002Fprofiler or unscoped `maxHeartbeats` in final code | debugging scaffolding | `linter.style.setOption` |\n| State lemmas in simp-normal form, `\u003C` not `>` | simp matches syntactically | `simpNF` linter |\n| Golf only when the result is at least as readable; trivial results exempt | short ≠ better | review |\n| `Fact` instances are local, never global | global instances degrade all typeclass search | review |\n| Name lemmas from their statements (see naming reference) | names become guessable without search | `linter.style.nameCheck` catches only `__`; `#lint defsWithUnderscore` and review cover more |\n| Search a bare goal by shape before writing a helper or claiming an API gap | names are not always guessable from the target | `exact?`, `apply?`, type\u002Fsource search |\n| Generally one tactic invocation per line; a one-line closing proof is the exception | preserves readable proof structure without inventing an absolute rule | style guide |\n| Gate `sorry` with `collectAxioms`\u002F`#print axioms`, never grep | grep matches comments, misses unproved helpers | axiom audit in CI |\n| Prefer simp-lemma LHSs keyed on structure, not numerals; one spelling per constant | `2 ^ 32` never matches a goal normalized to `4294967296` | `simpNF`, review |\n| Re-derive every `simp only` list with `simp?` at its own site | lists do not transfer between look-alike goals | `linter.flexible` |\n| Every `maxHeartbeats` override is an unproven claim — measure before believing | copy-pasted budgets carry no information | `#count_heartbeats`, bisection |\n| Conditional simp lemma fires shallow but not deep → raise `maxDischargeDepth` (default 2) | chained side conditions truncate silently, no diagnostic | diagnosis (proof-style, simp discipline) |\n| Every project-specific convention gets a custom linter, in CI from day one | review misses the 29-of-30 failure mode | `@[env_linter]` + `#lint` |\n\nFull rationale for each row, plus the library-level anti-patterns, in\n[anti-patterns.md](references\u002Fanti-patterns.md).\n\n## Rationalizations to reject\n\n| Excuse | Reality |\n|--------|---------|\n| \"The proof compiles, ship it\" | Compiling is the floor. A monolithic tactic block that only Lean can read will break silently at the next Mathlib bump and no one will be able to repair it. |\n| \"Unfolding the definition is simpler than writing API lemmas\" | Every downstream `unfold` couples a proof to the implementation. The first refactor breaks all of them at once. Write the missing lemma. |\n| \"Squeezing every simp makes the proof faster and more robust\" | Backwards for *terminal* simp calls: the squeezed list breaks on every rename and drowns the signal. Squeeze non-terminal calls only. |\n| \"It's shorter, therefore better\" | Mathlib review policy: golfing is fine *only* when it does not sacrifice readability. Length is not the target; legibility is. |\n| \"I'll restructure it into lemmas after it works\" | After it works, the structure is load-bearing and tangled. State the skeleton first; the lemmas fall out for free. |\n| \"Adding `show` lines is redundant noise\" | They are redundant to the kernel and essential to every human or model that reads the proof next. |\n| \"This helper is too specific to be a lemma\" | If it has a clean statement, extract it — dropping the hypotheses it doesn't need usually reveals it was general all along. |\n| \"We'll add linters once the library stabilizes\" | Backwards: patterns propagate by copy-paste, so a deferred linter meets a 400-warning backlog instead of one bad line. Enable what is already clean and gate it now. |\n| \"The check passed, so we're clean\" | A check that can't fail proves nothing — sweeps reach zero files, misspelled `weak.` options are ignored, pipelines swallow exit codes. Prove every gate can fail before trusting that it passes. |\n| \"The proof is slow, raise maxHeartbeats\" | An unmeasured budget is a claim, not a fix — and it masks the regression the next reader needs to see. Measure with `#count_heartbeats`; restructure the definition or decompose the goal. |\n\n## References\n\n- [library-design.md](references\u002Flibrary-design.md) — definitions, APIs,\n  bundling, abstraction boundaries, spec-driven project decomposition\n- [proof-style.md](references\u002Fproof-style.md) — tactic proof structure:\n  calc, have\u002Fsuffices, focusing, and simp discipline including the\n  why-doesn't-this-lemma-fire diagnoses (discharge depth, traversal order,\n  numeral spellings)\n- [naming-conventions.md](references\u002Fnaming-conventions.md) — Mathlib naming\n  so lemma names are computable from statements\n- [anti-patterns.md](references\u002Fanti-patterns.md) — recognized anti-patterns,\n  why each is harmful, and which linter catches it\n- [llm-techniques.md](references\u002Fllm-techniques.md) — evidence-based\n  techniques specific to LLM-written proofs\n- [linting.md](references\u002Flinting.md) — axiom-based sorry gates, enabling\n  project-specific linter profiles in CI early, the full Mathlib standard-set\n  audit, adopting linters with a backlog, writing custom linters for\n  project-specific constructs, and proving every gate can fail\n- [performance.md](references\u002Fperformance.md) — measuring per-declaration\n  cost, where reduction cost comes from, optimizing definitions without\n  losing semantics\n- [tactics.md](references\u002Ftactics.md) — metaprogramming discipline:\n  extension-point selection, metavariable and recovery safeguards, bounded\n  search, actionable errors, structured tracing, generated declarations,\n  and failure-surface testing\n",{"data":36,"body":37},{"name":4,"description":6},{"type":38,"children":39},"root",[40,48,55,124,130,158,163,219,224,258,263,270,275,390,403,409,414,481,539,545,668,688,694,721,939,944,949,1143,1148,1712,1724,1729,1922,1927,2006],{"type":41,"tag":42,"props":43,"children":44},"element","h1",{"id":4},[45],{"type":46,"value":47},"text","Writing Lean Proofs",{"type":41,"tag":49,"props":50,"children":52},"h2",{"id":51},"contents",[53],{"type":46,"value":54},"Contents",{"type":41,"tag":56,"props":57,"children":58},"ul",{},[59,70,79,88,97,106,115],{"type":41,"tag":60,"props":61,"children":62},"li",{},[63],{"type":41,"tag":64,"props":65,"children":67},"a",{"href":66},"#when-to-use",[68],{"type":46,"value":69},"When to Use",{"type":41,"tag":60,"props":71,"children":72},{},[73],{"type":41,"tag":64,"props":74,"children":76},{"href":75},"#when-not-to-use",[77],{"type":46,"value":78},"When NOT to Use",{"type":41,"tag":60,"props":80,"children":81},{},[82],{"type":41,"tag":64,"props":83,"children":85},{"href":84},"#the-workflow",[86],{"type":46,"value":87},"The workflow",{"type":41,"tag":60,"props":89,"children":90},{},[91],{"type":41,"tag":64,"props":92,"children":94},{"href":93},"#the-extraction-ladder",[95],{"type":46,"value":96},"The extraction ladder",{"type":41,"tag":60,"props":98,"children":99},{},[100],{"type":41,"tag":64,"props":101,"children":103},{"href":102},"#quick-reference",[104],{"type":46,"value":105},"Quick reference",{"type":41,"tag":60,"props":107,"children":108},{},[109],{"type":41,"tag":64,"props":110,"children":112},{"href":111},"#rationalizations-to-reject",[113],{"type":46,"value":114},"Rationalizations to reject",{"type":41,"tag":60,"props":116,"children":117},{},[118],{"type":41,"tag":64,"props":119,"children":121},{"href":120},"#references",[122],{"type":46,"value":123},"References",{"type":41,"tag":125,"props":126,"children":127},"p",{},[128],{"type":46,"value":129},"Structured Lean 4 proof writing and library design, distilled from Mathlib's\nstyle and review conventions and from the methodology of large formalization\nprojects (Liquid Tensor Experiment, PFR, Fermat's Last Theorem).",{"type":41,"tag":125,"props":131,"children":132},{},[133,139,141,147,149,156],{"type":41,"tag":134,"props":135,"children":136},"strong",{},[137],{"type":46,"value":138},"Core principle: design top-down, prove bottom-up.",{"type":46,"value":140}," Lean propositions are\nproof-irrelevant — only a theorem's ",{"type":41,"tag":142,"props":143,"children":144},"em",{},[145],{"type":46,"value":146},"statement",{"type":46,"value":148}," can affect later declarations.\nStatements are the stable interface; proofs are disposable and freely\nreplaceable. Put design effort into definitions and statements, then fill in\nproofs against skeletons that already compile (modulo ",{"type":41,"tag":150,"props":151,"children":153},"code",{"className":152},[],[154],{"type":46,"value":155},"sorry",{"type":46,"value":157},").",{"type":41,"tag":49,"props":159,"children":161},{"id":160},"when-to-use",[162],{"type":46,"value":69},{"type":41,"tag":56,"props":164,"children":165},{},[166,171,176,181,186,191,196,201,214],{"type":41,"tag":60,"props":167,"children":168},{},[169],{"type":46,"value":170},"Proving theorems in Lean 4, from single lemmas to multi-file developments",{"type":41,"tag":60,"props":172,"children":173},{},[174],{"type":46,"value":175},"Formalizing mathematics, protocols, or software specifications in Lean",{"type":41,"tag":60,"props":177,"children":178},{},[179],{"type":46,"value":180},"Defining new types, structures, or functions in a Lean library",{"type":41,"tag":60,"props":182,"children":183},{},[184],{"type":46,"value":185},"Reviewing Lean code for readability, maintainability, or Mathlib readiness",{"type":41,"tag":60,"props":187,"children":188},{},[189],{"type":46,"value":190},"Refactoring a long or fragile tactic proof into lemmas",{"type":41,"tag":60,"props":192,"children":193},{},[194],{"type":46,"value":195},"Setting up a formalization project that several people or agents will\ncontribute to in parallel",{"type":41,"tag":60,"props":197,"children":198},{},[199],{"type":46,"value":200},"Setting up CI, linters, or verification gates for a Lean project — do this\nat project start, before patterns propagate",{"type":41,"tag":60,"props":202,"children":203},{},[204,206,212],{"type":46,"value":205},"Diagnosing slow proofs, ",{"type":41,"tag":150,"props":207,"children":209},{"className":208},[],[210],{"type":46,"value":211},"maxHeartbeats",{"type":46,"value":213}," timeouts, or expensive reduction",{"type":41,"tag":60,"props":215,"children":216},{},[217],{"type":46,"value":218},"Writing custom tactics, macros, or project-specific linters",{"type":41,"tag":49,"props":220,"children":222},{"id":221},"when-not-to-use",[223],{"type":46,"value":78},{"type":41,"tag":56,"props":225,"children":226},{},[227,232,253],{"type":41,"tag":60,"props":228,"children":229},{},[230],{"type":46,"value":231},"Lean 4 as a general-purpose programming language (no proofs involved) —\nmost of this skill targets proof and API structure",{"type":41,"tag":60,"props":233,"children":234},{},[235,237,243,245,251],{"type":46,"value":236},"Coq, Isabelle, Agda, or Lean 3 — conventions and tactic names differ;\nLean 3 idioms (",{"type":41,"tag":150,"props":238,"children":240},{"className":239},[],[241],{"type":46,"value":242},"ge_or_gt",{"type":46,"value":244}," linting, ",{"type":41,"tag":150,"props":246,"children":248},{"className":247},[],[249],{"type":46,"value":250},"discrete_field",{"type":46,"value":252},") are obsolete",{"type":41,"tag":60,"props":254,"children":255},{},[256],{"type":46,"value":257},"Verified-software Lean projects with their own house style (e.g.\nspec-traceability-first codebases): Mathlib conventions are the community\ndefault, but check the project's CONTRIBUTING first and defer to it",{"type":41,"tag":49,"props":259,"children":261},{"id":260},"the-workflow",[262],{"type":46,"value":87},{"type":41,"tag":264,"props":265,"children":267},"h3",{"id":266},"_1-design-definitions-and-their-api-first",[268],{"type":46,"value":269},"1. Design definitions and their API first",{"type":41,"tag":125,"props":271,"children":272},{},[273],{"type":46,"value":274},"Definitions carry the design weight. Before proving anything about a new\nconcept:",{"type":41,"tag":56,"props":276,"children":277},{},[278,304,338,348],{"type":41,"tag":60,"props":279,"children":280},{},[281,286,288,294,296,302],{"type":41,"tag":134,"props":282,"children":283},{},[284],{"type":46,"value":285},"Prefer total functions with junk values",{"type":46,"value":287}," over subtypes or ",{"type":41,"tag":150,"props":289,"children":291},{"className":290},[],[292],{"type":46,"value":293},"Option",{"type":46,"value":295}," in\nsignatures (Mathlib: ",{"type":41,"tag":150,"props":297,"children":299},{"className":298},[],[300],{"type":46,"value":301},"(0 : ℝ)⁻¹ = 0",{"type":46,"value":303},"). Side conditions then appear only on\nthe lemmas that need them, not at every use site.",{"type":41,"tag":60,"props":305,"children":306},{},[307,312,314,320,322,328,330,336],{"type":41,"tag":134,"props":308,"children":309},{},[310],{"type":46,"value":311},"Bundle",{"type":46,"value":313},": new morphism kinds are structures with a ",{"type":41,"tag":150,"props":315,"children":317},{"className":316},[],[318],{"type":46,"value":319},"FunLike",{"type":46,"value":321}," instance;\nnew subobject kinds use ",{"type":41,"tag":150,"props":323,"children":325},{"className":324},[],[326],{"type":46,"value":327},"SetLike",{"type":46,"value":329},"; carry property proofs as structure\nfields, not separate ",{"type":41,"tag":150,"props":331,"children":333},{"className":332},[],[334],{"type":46,"value":335},"IsHom",{"type":46,"value":337},"-style predicates.",{"type":41,"tag":60,"props":339,"children":340},{},[341,346],{"type":41,"tag":134,"props":342,"children":343},{},[344],{"type":46,"value":345},"Pick the canonical spelling",{"type":46,"value":347}," (simp-normal form) for every concept with\nmultiple equivalent forms, and state all API lemmas for that form only.",{"type":41,"tag":60,"props":349,"children":350},{},[351,356,358,364,366,372,374,380,382,388],{"type":41,"tag":134,"props":352,"children":353},{},[354],{"type":46,"value":355},"Write the API in the same file, immediately",{"type":46,"value":357},": ",{"type":41,"tag":150,"props":359,"children":361},{"className":360},[],[362],{"type":46,"value":363},"ext",{"type":46,"value":365},", ",{"type":41,"tag":150,"props":367,"children":369},{"className":368},[],[370],{"type":46,"value":371},"@[simp]",{"type":46,"value":373},",\ncoercion, and injectivity lemmas — before the definition is used anywhere.\nDownstream proofs use the API, never ",{"type":41,"tag":150,"props":375,"children":377},{"className":376},[],[378],{"type":46,"value":379},"unfold",{"type":46,"value":381},"\u002F",{"type":41,"tag":150,"props":383,"children":385},{"className":384},[],[386],{"type":46,"value":387},"show ... from rfl",{"type":46,"value":389},".",{"type":41,"tag":125,"props":391,"children":392},{},[393,395,401],{"type":46,"value":394},"See ",{"type":41,"tag":64,"props":396,"children":398},{"href":397},"references\u002Flibrary-design.md",[399],{"type":46,"value":400},"library-design.md",{"type":46,"value":402}," for the full set of\ndesign rules with rationale.",{"type":41,"tag":264,"props":404,"children":406},{"id":405},"_2-build-a-sorry-skeleton",[407],{"type":46,"value":408},"2. Build a sorry skeleton",{"type":41,"tag":125,"props":410,"children":411},{},[412],{"type":46,"value":413},"State everything before proving anything, at every scale:",{"type":41,"tag":56,"props":415,"children":416},{},[417,442],{"type":41,"tag":60,"props":418,"children":419},{},[420,425,427,433,435,440],{"type":41,"tag":134,"props":421,"children":422},{},[423],{"type":46,"value":424},"Project scale",{"type":46,"value":426},": state the target theorem and the lemmas it needs, all\nwith ",{"type":41,"tag":150,"props":428,"children":430},{"className":429},[],[431],{"type":46,"value":432},":= sorry",{"type":46,"value":434},", and make the file compile. Each ",{"type":41,"tag":150,"props":436,"children":438},{"className":437},[],[439],{"type":46,"value":155},{"type":46,"value":441}," is now an\nindependent work unit — a contributor (human or LLM) can discharge one\nwithout understanding the rest. This is how LTE, PFR, and FLT scale to\ndozens of parallel contributors.",{"type":41,"tag":60,"props":443,"children":444},{},[445,450,452,458,459,465,466,472,474,479],{"type":41,"tag":134,"props":446,"children":447},{},[448],{"type":46,"value":449},"Proof scale",{"type":46,"value":451},": inside a proof, lay out the ",{"type":41,"tag":150,"props":453,"children":455},{"className":454},[],[456],{"type":46,"value":457},"have",{"type":46,"value":381},{"type":41,"tag":150,"props":460,"children":462},{"className":461},[],[463],{"type":46,"value":464},"suffices",{"type":46,"value":381},{"type":41,"tag":150,"props":467,"children":469},{"className":468},[],[470],{"type":46,"value":471},"calc",{"type":46,"value":473},"\nskeleton with ",{"type":41,"tag":150,"props":475,"children":477},{"className":476},[],[478],{"type":46,"value":155},{"type":46,"value":480}," justifications, get Lean to accept the structure,\nthen fill each step. Keeping the structure intact is what produces useful\nerror messages while you work.",{"type":41,"tag":482,"props":483,"children":488},"pre",{"className":484,"code":485,"language":486,"meta":487,"style":487},"language-lean shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","example (a b c d : ℝ) (h : c = d * a + b) (h' : b = a * d) : c = 2 * a * d := by\n  calc\n    c = d * a + b     := sorry\n    _ = d * a + a * d := sorry\n    _ = 2 * a * d     := sorry\n","lean","",[489],{"type":41,"tag":150,"props":490,"children":491},{"__ignoreMap":487},[492,503,512,521,530],{"type":41,"tag":493,"props":494,"children":497},"span",{"class":495,"line":496},"line",1,[498],{"type":41,"tag":493,"props":499,"children":500},{},[501],{"type":46,"value":502},"example (a b c d : ℝ) (h : c = d * a + b) (h' : b = a * d) : c = 2 * a * d := by\n",{"type":41,"tag":493,"props":504,"children":506},{"class":495,"line":505},2,[507],{"type":41,"tag":493,"props":508,"children":509},{},[510],{"type":46,"value":511},"  calc\n",{"type":41,"tag":493,"props":513,"children":515},{"class":495,"line":514},3,[516],{"type":41,"tag":493,"props":517,"children":518},{},[519],{"type":46,"value":520},"    c = d * a + b     := sorry\n",{"type":41,"tag":493,"props":522,"children":524},{"class":495,"line":523},4,[525],{"type":41,"tag":493,"props":526,"children":527},{},[528],{"type":46,"value":529},"    _ = d * a + a * d := sorry\n",{"type":41,"tag":493,"props":531,"children":533},{"class":495,"line":532},5,[534],{"type":41,"tag":493,"props":535,"children":536},{},[537],{"type":46,"value":538},"    _ = 2 * a * d     := sorry\n",{"type":41,"tag":264,"props":540,"children":542},{"id":541},"_3-fill-goals-one-focused-goal-at-a-time",[543],{"type":46,"value":544},"3. Fill goals, one focused goal at a time",{"type":41,"tag":56,"props":546,"children":547},{},[548,569,603,615,632],{"type":41,"tag":60,"props":549,"children":550},{},[551,553,559,561,567],{"type":46,"value":552},"Every new subgoal gets a focusing dot ",{"type":41,"tag":150,"props":554,"children":556},{"className":555},[],[557],{"type":46,"value":558},"·",{"type":46,"value":560}," with an indented block — never\nleave several goals active in unfocused sequence (Mathlib's ",{"type":41,"tag":150,"props":562,"children":564},{"className":563},[],[565],{"type":46,"value":566},"multiGoal",{"type":46,"value":568},"\nlinter enforces this). This is what kills fragile goal-ordering dependence.",{"type":41,"tag":60,"props":570,"children":571},{},[572,574,580,582,587,589,594,596,601],{"type":46,"value":573},"Open each block with a redundant ",{"type":41,"tag":150,"props":575,"children":577},{"className":576},[],[578],{"type":46,"value":579},"show",{"type":46,"value":581}," stating its goal. The proof works\nwithout it; reviewers and future editors need it. If ",{"type":41,"tag":150,"props":583,"children":585},{"className":584},[],[586],{"type":46,"value":579},{"type":46,"value":588}," would ",{"type":41,"tag":142,"props":590,"children":591},{},[592],{"type":46,"value":593},"change",{"type":46,"value":595},"\nthe goal, use ",{"type":41,"tag":150,"props":597,"children":599},{"className":598},[],[600],{"type":46,"value":593},{"type":46,"value":602}," instead — keep stated goals honest.",{"type":41,"tag":60,"props":604,"children":605},{},[606,608,613],{"type":46,"value":607},"Chained rewrites of (in)equalities become ",{"type":41,"tag":150,"props":609,"children":611},{"className":610},[],[612],{"type":46,"value":471},{"type":46,"value":614}," blocks, relations aligned\nvertically.",{"type":41,"tag":60,"props":616,"children":617},{},[618,623,625,630],{"type":41,"tag":150,"props":619,"children":621},{"className":620},[],[622],{"type":46,"value":457},{"type":46,"value":624}," for forward stepping stones (\"we first establish X\"); ",{"type":41,"tag":150,"props":626,"children":628},{"className":627},[],[629],{"type":46,"value":464},{"type":46,"value":631},"\nfor backward reduction (\"it suffices to show X\").",{"type":41,"tag":60,"props":633,"children":634},{},[635,637,643,645,651,653,659,661,667],{"type":46,"value":636},"While drafting, annotate the goal state as a comment before non-obvious\ntactics — emitted by Lean, never imagined. In a headless workflow, insert\n",{"type":41,"tag":150,"props":638,"children":640},{"className":639},[],[641],{"type":46,"value":642},"trace_state",{"type":46,"value":644}," at the point of interest or a deliberate ",{"type":41,"tag":150,"props":646,"children":648},{"className":647},[],[649],{"type":46,"value":650},"done",{"type":46,"value":652}," where goals\nshould be closed, then run ",{"type":41,"tag":150,"props":654,"children":656},{"className":655},[],[657],{"type":46,"value":658},"lake env lean Path\u002FTo\u002FFile.lean",{"type":46,"value":660},"; copy the\nreported hypotheses, case name, and target. Strip routine probes after the\nproof works. This is the single most effective technique for LLM-written\nproofs (see ",{"type":41,"tag":64,"props":662,"children":664},{"href":663},"references\u002Fllm-techniques.md",[665],{"type":46,"value":666},"llm-techniques.md",{"type":46,"value":157},{"type":41,"tag":125,"props":669,"children":670},{},[671,672,678,680,686],{"type":46,"value":394},{"type":41,"tag":64,"props":673,"children":675},{"href":674},"references\u002Fproof-style.md",[676],{"type":46,"value":677},"proof-style.md",{"type":46,"value":679}," for the full tactic-style\nrules, and ",{"type":41,"tag":64,"props":681,"children":683},{"href":682},"references\u002Fnaming-conventions.md",[684],{"type":46,"value":685},"naming-conventions.md",{"type":46,"value":687}," for\nnaming lemmas so their names are guessable from their statements.",{"type":41,"tag":264,"props":689,"children":691},{"id":690},"_4-verify-mechanically",[692],{"type":46,"value":693},"4. Verify mechanically",{"type":41,"tag":125,"props":695,"children":696},{},[697,699,705,707,712,714,719],{"type":46,"value":698},"Do not eyeball-check style — run the checkers. ",{"type":41,"tag":150,"props":700,"children":702},{"className":701},[],[703],{"type":46,"value":704},"lake build",{"type":46,"value":706}," is the floor,\nand it is ",{"type":41,"tag":142,"props":708,"children":709},{},[710],{"type":46,"value":711},"only",{"type":46,"value":713}," the floor: ",{"type":41,"tag":150,"props":715,"children":717},{"className":716},[],[718],{"type":46,"value":155},{"type":46,"value":720}," is a warning, so a green build exits 0\nwith sorries still present.",{"type":41,"tag":56,"props":722,"children":723},{},[724,792,915],{"type":41,"tag":60,"props":725,"children":726},{},[727,732,738,740,746,748,753,755,761,763,768,770,775,777,783,785,791],{"type":41,"tag":134,"props":728,"children":729},{},[730],{"type":46,"value":731},"Gate unproved obligations by asking the kernel, never by grepping.",{"type":41,"tag":150,"props":733,"children":735},{"className":734},[],[736],{"type":46,"value":737},"#print axioms myTheorem",{"type":46,"value":739}," for a spot check; for CI, collect axioms per\ndeclaration with ",{"type":41,"tag":150,"props":741,"children":743},{"className":742},[],[744],{"type":46,"value":745},"Lean.collectAxioms",{"type":46,"value":747}," and assert the ",{"type":41,"tag":142,"props":749,"children":750},{},[751],{"type":46,"value":752},"whole",{"type":46,"value":754}," expected\nfootprint (",{"type":41,"tag":150,"props":756,"children":758},{"className":757},[],[759],{"type":46,"value":760},"[propext, Classical.choice, Quot.sound]",{"type":46,"value":762}," unless deliberately\nwidened), so a stray ",{"type":41,"tag":150,"props":764,"children":766},{"className":765},[],[767],{"type":46,"value":155},{"type":46,"value":769}," ",{"type":41,"tag":142,"props":771,"children":772},{},[773],{"type":46,"value":774},"or",{"type":46,"value":776}," a new trust assumption like\n",{"type":41,"tag":150,"props":778,"children":780},{"className":779},[],[781],{"type":46,"value":782},"native_decide",{"type":46,"value":784}," fails loudly. Grep is wrong in both directions: it matches\nthe word in comments, and it misses a theorem whose own text is clean but\nwhich applies an unproved helper. Working script in\n",{"type":41,"tag":64,"props":786,"children":788},{"href":787},"references\u002Flinting.md",[789],{"type":46,"value":790},"linting.md",{"type":46,"value":389},{"type":41,"tag":60,"props":793,"children":794},{},[795,800,802,808,810,816,817,823,825,831,832,838,840,846,848,854,855,861,862,868,870,876,878,884,886,892,894,900,902,908,910,914],{"type":41,"tag":134,"props":796,"children":797},{},[798],{"type":46,"value":799},"Choose lints by project role and put them in CI at project start.",{"type":46,"value":801}," Do not\nenable ",{"type":41,"tag":150,"props":803,"children":805},{"className":804},[],[806],{"type":46,"value":807},"linter.mathlibStandardSet",{"type":46,"value":809}," wholesale in a downstream project: it\ncombines proof-maintenance checks with public-API checks, house style, and\nMathlib-specific repository policy. For a self-contained proof, start with\n",{"type":41,"tag":150,"props":811,"children":813},{"className":812},[],[814],{"type":46,"value":815},"linter.auxLemma",{"type":46,"value":365},{"type":41,"tag":150,"props":818,"children":820},{"className":819},[],[821],{"type":46,"value":822},"linter.style.maxHeartbeats",{"type":46,"value":824},",\n",{"type":41,"tag":150,"props":826,"children":828},{"className":827},[],[829],{"type":46,"value":830},"linter.style.multiGoal",{"type":46,"value":365},{"type":41,"tag":150,"props":833,"children":835},{"className":834},[],[836],{"type":46,"value":837},"linter.style.setOption",{"type":46,"value":839},", and\n",{"type":41,"tag":150,"props":841,"children":843},{"className":842},[],[844],{"type":46,"value":845},"linter.style.show",{"type":46,"value":847},". A reusable library should additionally enable\n",{"type":41,"tag":150,"props":849,"children":851},{"className":850},[],[852],{"type":46,"value":853},"linter.flexible",{"type":46,"value":365},{"type":41,"tag":150,"props":856,"children":858},{"className":857},[],[859],{"type":46,"value":860},"linter.style.missingEnd",{"type":46,"value":824},{"type":41,"tag":150,"props":863,"children":865},{"className":864},[],[866],{"type":46,"value":867},"linter.style.openClassical",{"type":46,"value":869},", and the two ",{"type":41,"tag":150,"props":871,"children":873},{"className":872},[],[874],{"type":46,"value":875},"unused*InType",{"type":46,"value":877}," checks. Treat\n",{"type":41,"tag":150,"props":879,"children":881},{"className":880},[],[882],{"type":46,"value":883},"nativeDecide",{"type":46,"value":885}," as a trust-policy choice and formatting or deprecated-syntax\nchecks as project style. No warning gates anything unless warnings fail\nthe build. Run Batteries' declaration-level ",{"type":41,"tag":150,"props":887,"children":889},{"className":888},[],[890],{"type":46,"value":891},"#lint",{"type":46,"value":893}," checks, including\n",{"type":41,"tag":150,"props":895,"children":897},{"className":896},[],[898],{"type":46,"value":899},"simpNF",{"type":46,"value":901},", separately. Verify every option against the pinned Mathlib source\nand with a known-trigger fixture: a misspelled ",{"type":41,"tag":150,"props":903,"children":905},{"className":904},[],[906],{"type":46,"value":907},"weak.",{"type":46,"value":909}," option is\nintentionally ignored. The complete 26-member audit and lakefile profiles\nare in ",{"type":41,"tag":64,"props":911,"children":912},{"href":787},[913],{"type":46,"value":790},{"type":46,"value":389},{"type":41,"tag":60,"props":916,"children":917},{},[918,923,925,931,933,937],{"type":41,"tag":134,"props":919,"children":920},{},[921],{"type":46,"value":922},"Write a custom linter for every project-specific convention",{"type":46,"value":924}," (simp-set\ndiscipline, summary-lemma coverage, required attributes) — a\ndeclaration-level ",{"type":41,"tag":150,"props":926,"children":928},{"className":927},[],[929],{"type":46,"value":930},"@[env_linter]",{"type":46,"value":932}," is one structure, and it is the only\nthing that reliably catches \"the attribute is missing on 29 of 30\ndeclarations\". See ",{"type":41,"tag":64,"props":934,"children":935},{"href":787},[936],{"type":46,"value":790},{"type":46,"value":938}," for the recipe and\nthe engineering rules (vacuity anchors, prove-it-can-fail, allowlists).",{"type":41,"tag":49,"props":940,"children":942},{"id":941},"the-extraction-ladder",[943],{"type":46,"value":96},{"type":41,"tag":125,"props":945,"children":946},{},[947],{"type":46,"value":948},"When does proof structure graduate into separate lemmas?",{"type":41,"tag":950,"props":951,"children":953},"ol",{"start":952},0,[954,988,1123,1133],{"type":41,"tag":60,"props":955,"children":956},{},[957,962,964,970,972,978,980,986],{"type":41,"tag":134,"props":958,"children":959},{},[960],{"type":46,"value":961},"Before extracting, state the fragment's type and search by shape.",{"type":46,"value":963}," Put\nthe proposed statement in a scratch ",{"type":41,"tag":150,"props":965,"children":967},{"className":966},[],[968],{"type":46,"value":969},"example",{"type":46,"value":971},", run ",{"type":41,"tag":150,"props":973,"children":975},{"className":974},[],[976],{"type":46,"value":977},"exact?",{"type":46,"value":979}," and ",{"type":41,"tag":150,"props":981,"children":983},{"className":982},[],[984],{"type":46,"value":985},"apply?",{"type":46,"value":987},"\non the bare goal, then try a type-pattern and source search. If an existing\ntheorem fits, use it. Do not report an API gap without recording the\nsearches that failed.",{"type":41,"tag":60,"props":989,"children":990},{},[991,996,998,1003,1004],{"type":41,"tag":134,"props":992,"children":993},{},[994],{"type":46,"value":995},"A sub-argument repeats within one proof",{"type":46,"value":997}," → name it as a local ",{"type":41,"tag":150,"props":999,"children":1001},{"className":1000},[],[1002],{"type":46,"value":457},{"type":46,"value":389},{"type":41,"tag":482,"props":1005,"children":1007},{"className":484,"code":1006,"language":486,"meta":487,"style":487},"theorem min_comm (a b : ℝ) : min a b = min b a := by\n  have h : ∀ x y : ℝ, min x y ≤ min y x := by\n    intro x y\n    apply le_min\n    · show min x y ≤ y\n      exact min_le_right x y\n    · show min x y ≤ x\n      exact min_le_left x y\n  apply le_antisymm\n  · show min a b ≤ min b a\n    exact h a b\n  · show min b a ≤ min a b\n    exact h b a\n",[1008],{"type":41,"tag":150,"props":1009,"children":1010},{"__ignoreMap":487},[1011,1019,1027,1035,1043,1051,1060,1069,1078,1087,1096,1105,1114],{"type":41,"tag":493,"props":1012,"children":1013},{"class":495,"line":496},[1014],{"type":41,"tag":493,"props":1015,"children":1016},{},[1017],{"type":46,"value":1018},"theorem min_comm (a b : ℝ) : min a b = min b a := by\n",{"type":41,"tag":493,"props":1020,"children":1021},{"class":495,"line":505},[1022],{"type":41,"tag":493,"props":1023,"children":1024},{},[1025],{"type":46,"value":1026},"  have h : ∀ x y : ℝ, min x y ≤ min y x := by\n",{"type":41,"tag":493,"props":1028,"children":1029},{"class":495,"line":514},[1030],{"type":41,"tag":493,"props":1031,"children":1032},{},[1033],{"type":46,"value":1034},"    intro x y\n",{"type":41,"tag":493,"props":1036,"children":1037},{"class":495,"line":523},[1038],{"type":41,"tag":493,"props":1039,"children":1040},{},[1041],{"type":46,"value":1042},"    apply le_min\n",{"type":41,"tag":493,"props":1044,"children":1045},{"class":495,"line":532},[1046],{"type":41,"tag":493,"props":1047,"children":1048},{},[1049],{"type":46,"value":1050},"    · show min x y ≤ y\n",{"type":41,"tag":493,"props":1052,"children":1054},{"class":495,"line":1053},6,[1055],{"type":41,"tag":493,"props":1056,"children":1057},{},[1058],{"type":46,"value":1059},"      exact min_le_right x y\n",{"type":41,"tag":493,"props":1061,"children":1063},{"class":495,"line":1062},7,[1064],{"type":41,"tag":493,"props":1065,"children":1066},{},[1067],{"type":46,"value":1068},"    · show min x y ≤ x\n",{"type":41,"tag":493,"props":1070,"children":1072},{"class":495,"line":1071},8,[1073],{"type":41,"tag":493,"props":1074,"children":1075},{},[1076],{"type":46,"value":1077},"      exact min_le_left x y\n",{"type":41,"tag":493,"props":1079,"children":1081},{"class":495,"line":1080},9,[1082],{"type":41,"tag":493,"props":1083,"children":1084},{},[1085],{"type":46,"value":1086},"  apply le_antisymm\n",{"type":41,"tag":493,"props":1088,"children":1090},{"class":495,"line":1089},10,[1091],{"type":41,"tag":493,"props":1092,"children":1093},{},[1094],{"type":46,"value":1095},"  · show min a b ≤ min b a\n",{"type":41,"tag":493,"props":1097,"children":1099},{"class":495,"line":1098},11,[1100],{"type":41,"tag":493,"props":1101,"children":1102},{},[1103],{"type":46,"value":1104},"    exact h a b\n",{"type":41,"tag":493,"props":1106,"children":1108},{"class":495,"line":1107},12,[1109],{"type":41,"tag":493,"props":1110,"children":1111},{},[1112],{"type":46,"value":1113},"  · show min b a ≤ min a b\n",{"type":41,"tag":493,"props":1115,"children":1117},{"class":495,"line":1116},13,[1118],{"type":41,"tag":493,"props":1119,"children":1120},{},[1121],{"type":46,"value":1122},"    exact h b a\n",{"type":41,"tag":60,"props":1124,"children":1125},{},[1126,1131],{"type":41,"tag":134,"props":1127,"children":1128},{},[1129],{"type":46,"value":1130},"The statement is independently interesting, or extraction sheds\nhypotheses the sub-argument does not need",{"type":46,"value":1132}," → standalone lemma. Dropping\nunneeded hypotheses is the stronger trigger: the extracted lemma becomes\nmore general than the proof it came from.",{"type":41,"tag":60,"props":1134,"children":1135},{},[1136,1141],{"type":41,"tag":134,"props":1137,"children":1138},{},[1139],{"type":46,"value":1140},"The proof reads as \"long and unwieldy\"",{"type":46,"value":1142}," → split it. This is Mathlib's\nreview criterion, and it is deliberately qualitative — there is no line\nthreshold. Resolve doubt by attempting the extraction: if a fragment has\na clean statement, it wanted to be a lemma.",{"type":41,"tag":49,"props":1144,"children":1146},{"id":1145},"quick-reference",[1147],{"type":46,"value":105},{"type":41,"tag":1149,"props":1150,"children":1151},"table",{},[1152,1176],{"type":41,"tag":1153,"props":1154,"children":1155},"thead",{},[1156],{"type":41,"tag":1157,"props":1158,"children":1159},"tr",{},[1160,1166,1171],{"type":41,"tag":1161,"props":1162,"children":1163},"th",{},[1164],{"type":46,"value":1165},"Rule",{"type":41,"tag":1161,"props":1167,"children":1168},{},[1169],{"type":46,"value":1170},"Why",{"type":41,"tag":1161,"props":1172,"children":1173},{},[1174],{"type":46,"value":1175},"Enforced by",{"type":41,"tag":1177,"props":1178,"children":1179},"tbody",{},[1180,1215,1254,1282,1315,1351,1388,1406,1429,1469,1498,1515,1555,1590,1627,1658,1684],{"type":41,"tag":1157,"props":1181,"children":1182},{},[1183,1205,1210],{"type":41,"tag":1184,"props":1185,"children":1186},"td",{},[1187,1189,1195,1197,1203],{"type":46,"value":1188},"Never unfold definitions downstream; ",{"type":41,"tag":150,"props":1190,"children":1192},{"className":1191},[],[1193],{"type":46,"value":1194},"erw",{"type":46,"value":1196}," or trailing ",{"type":41,"tag":150,"props":1198,"children":1200},{"className":1199},[],[1201],{"type":46,"value":1202},"rfl",{"type":46,"value":1204}," = missing API",{"type":41,"tag":1184,"props":1206,"children":1207},{},[1208],{"type":46,"value":1209},"API lemmas are the abstraction boundary",{"type":41,"tag":1184,"props":1211,"children":1212},{},[1213],{"type":46,"value":1214},"review (\"missing API\" smell)",{"type":41,"tag":1157,"props":1216,"children":1217},{},[1218,1244,1249],{"type":41,"tag":1184,"props":1219,"children":1220},{},[1221,1223,1229,1231,1236,1238],{"type":46,"value":1222},"Terminal ",{"type":41,"tag":150,"props":1224,"children":1226},{"className":1225},[],[1227],{"type":46,"value":1228},"simp",{"type":46,"value":1230}," stays unsqueezed; non-terminal ",{"type":41,"tag":150,"props":1232,"children":1234},{"className":1233},[],[1235],{"type":46,"value":1228},{"type":46,"value":1237}," becomes ",{"type":41,"tag":150,"props":1239,"children":1241},{"className":1240},[],[1242],{"type":46,"value":1243},"simp only [...]",{"type":41,"tag":1184,"props":1245,"children":1246},{},[1247],{"type":46,"value":1248},"squeezed terminal calls bury the key lemmas and break on renames",{"type":41,"tag":1184,"props":1250,"children":1251},{},[1252],{"type":46,"value":1253},"style guide",{"type":41,"tag":1157,"props":1255,"children":1256},{},[1257,1269,1274],{"type":41,"tag":1184,"props":1258,"children":1259},{},[1260,1262,1267],{"type":46,"value":1261},"One focused goal at a time (",{"type":41,"tag":150,"props":1263,"children":1265},{"className":1264},[],[1266],{"type":46,"value":558},{"type":46,"value":1268}," blocks)",{"type":41,"tag":1184,"props":1270,"children":1271},{},[1272],{"type":46,"value":1273},"kills goal-ordering fragility",{"type":41,"tag":1184,"props":1275,"children":1276},{},[1277],{"type":41,"tag":150,"props":1278,"children":1280},{"className":1279},[],[1281],{"type":46,"value":830},{"type":41,"tag":1157,"props":1283,"children":1284},{},[1285,1302,1307],{"type":41,"tag":1184,"props":1286,"children":1287},{},[1288,1293,1295,1300],{"type":41,"tag":150,"props":1289,"children":1291},{"className":1290},[],[1292],{"type":46,"value":579},{"type":46,"value":1294}," must not change the goal (use ",{"type":41,"tag":150,"props":1296,"children":1298},{"className":1297},[],[1299],{"type":46,"value":593},{"type":46,"value":1301},")",{"type":41,"tag":1184,"props":1303,"children":1304},{},[1305],{"type":46,"value":1306},"stated goals stay honest",{"type":41,"tag":1184,"props":1308,"children":1309},{},[1310],{"type":41,"tag":150,"props":1311,"children":1313},{"className":1312},[],[1314],{"type":46,"value":845},{"type":41,"tag":1157,"props":1316,"children":1317},{},[1318,1338,1343],{"type":41,"tag":1184,"props":1319,"children":1320},{},[1321,1323,1329,1331,1336],{"type":46,"value":1322},"No ",{"type":41,"tag":150,"props":1324,"children":1326},{"className":1325},[],[1327],{"type":46,"value":1328},"set_option",{"type":46,"value":1330}," debug\u002Ftrace\u002Fprofiler or unscoped ",{"type":41,"tag":150,"props":1332,"children":1334},{"className":1333},[],[1335],{"type":46,"value":211},{"type":46,"value":1337}," in final code",{"type":41,"tag":1184,"props":1339,"children":1340},{},[1341],{"type":46,"value":1342},"debugging scaffolding",{"type":41,"tag":1184,"props":1344,"children":1345},{},[1346],{"type":41,"tag":150,"props":1347,"children":1349},{"className":1348},[],[1350],{"type":46,"value":837},{"type":41,"tag":1157,"props":1352,"children":1353},{},[1354,1373,1378],{"type":41,"tag":1184,"props":1355,"children":1356},{},[1357,1359,1365,1367],{"type":46,"value":1358},"State lemmas in simp-normal form, ",{"type":41,"tag":150,"props":1360,"children":1362},{"className":1361},[],[1363],{"type":46,"value":1364},"\u003C",{"type":46,"value":1366}," not ",{"type":41,"tag":150,"props":1368,"children":1370},{"className":1369},[],[1371],{"type":46,"value":1372},">",{"type":41,"tag":1184,"props":1374,"children":1375},{},[1376],{"type":46,"value":1377},"simp matches syntactically",{"type":41,"tag":1184,"props":1379,"children":1380},{},[1381,1386],{"type":41,"tag":150,"props":1382,"children":1384},{"className":1383},[],[1385],{"type":46,"value":899},{"type":46,"value":1387}," linter",{"type":41,"tag":1157,"props":1389,"children":1390},{},[1391,1396,1401],{"type":41,"tag":1184,"props":1392,"children":1393},{},[1394],{"type":46,"value":1395},"Golf only when the result is at least as readable; trivial results exempt",{"type":41,"tag":1184,"props":1397,"children":1398},{},[1399],{"type":46,"value":1400},"short ≠ better",{"type":41,"tag":1184,"props":1402,"children":1403},{},[1404],{"type":46,"value":1405},"review",{"type":41,"tag":1157,"props":1407,"children":1408},{},[1409,1420,1425],{"type":41,"tag":1184,"props":1410,"children":1411},{},[1412,1418],{"type":41,"tag":150,"props":1413,"children":1415},{"className":1414},[],[1416],{"type":46,"value":1417},"Fact",{"type":46,"value":1419}," instances are local, never global",{"type":41,"tag":1184,"props":1421,"children":1422},{},[1423],{"type":46,"value":1424},"global instances degrade all typeclass search",{"type":41,"tag":1184,"props":1426,"children":1427},{},[1428],{"type":46,"value":1405},{"type":41,"tag":1157,"props":1430,"children":1431},{},[1432,1437,1442],{"type":41,"tag":1184,"props":1433,"children":1434},{},[1435],{"type":46,"value":1436},"Name lemmas from their statements (see naming reference)",{"type":41,"tag":1184,"props":1438,"children":1439},{},[1440],{"type":46,"value":1441},"names become guessable without search",{"type":41,"tag":1184,"props":1443,"children":1444},{},[1445,1451,1453,1459,1461,1467],{"type":41,"tag":150,"props":1446,"children":1448},{"className":1447},[],[1449],{"type":46,"value":1450},"linter.style.nameCheck",{"type":46,"value":1452}," catches only ",{"type":41,"tag":150,"props":1454,"children":1456},{"className":1455},[],[1457],{"type":46,"value":1458},"__",{"type":46,"value":1460},"; ",{"type":41,"tag":150,"props":1462,"children":1464},{"className":1463},[],[1465],{"type":46,"value":1466},"#lint defsWithUnderscore",{"type":46,"value":1468}," and review cover more",{"type":41,"tag":1157,"props":1470,"children":1471},{},[1472,1477,1482],{"type":41,"tag":1184,"props":1473,"children":1474},{},[1475],{"type":46,"value":1476},"Search a bare goal by shape before writing a helper or claiming an API gap",{"type":41,"tag":1184,"props":1478,"children":1479},{},[1480],{"type":46,"value":1481},"names are not always guessable from the target",{"type":41,"tag":1184,"props":1483,"children":1484},{},[1485,1490,1491,1496],{"type":41,"tag":150,"props":1486,"children":1488},{"className":1487},[],[1489],{"type":46,"value":977},{"type":46,"value":365},{"type":41,"tag":150,"props":1492,"children":1494},{"className":1493},[],[1495],{"type":46,"value":985},{"type":46,"value":1497},", type\u002Fsource search",{"type":41,"tag":1157,"props":1499,"children":1500},{},[1501,1506,1511],{"type":41,"tag":1184,"props":1502,"children":1503},{},[1504],{"type":46,"value":1505},"Generally one tactic invocation per line; a one-line closing proof is the exception",{"type":41,"tag":1184,"props":1507,"children":1508},{},[1509],{"type":46,"value":1510},"preserves readable proof structure without inventing an absolute rule",{"type":41,"tag":1184,"props":1512,"children":1513},{},[1514],{"type":46,"value":1253},{"type":41,"tag":1157,"props":1516,"children":1517},{},[1518,1545,1550],{"type":41,"tag":1184,"props":1519,"children":1520},{},[1521,1523,1528,1530,1536,1537,1543],{"type":46,"value":1522},"Gate ",{"type":41,"tag":150,"props":1524,"children":1526},{"className":1525},[],[1527],{"type":46,"value":155},{"type":46,"value":1529}," with ",{"type":41,"tag":150,"props":1531,"children":1533},{"className":1532},[],[1534],{"type":46,"value":1535},"collectAxioms",{"type":46,"value":381},{"type":41,"tag":150,"props":1538,"children":1540},{"className":1539},[],[1541],{"type":46,"value":1542},"#print axioms",{"type":46,"value":1544},", never grep",{"type":41,"tag":1184,"props":1546,"children":1547},{},[1548],{"type":46,"value":1549},"grep matches comments, misses unproved helpers",{"type":41,"tag":1184,"props":1551,"children":1552},{},[1553],{"type":46,"value":1554},"axiom audit in CI",{"type":41,"tag":1157,"props":1556,"children":1557},{},[1558,1563,1580],{"type":41,"tag":1184,"props":1559,"children":1560},{},[1561],{"type":46,"value":1562},"Prefer simp-lemma LHSs keyed on structure, not numerals; one spelling per constant",{"type":41,"tag":1184,"props":1564,"children":1565},{},[1566,1572,1574],{"type":41,"tag":150,"props":1567,"children":1569},{"className":1568},[],[1570],{"type":46,"value":1571},"2 ^ 32",{"type":46,"value":1573}," never matches a goal normalized to ",{"type":41,"tag":150,"props":1575,"children":1577},{"className":1576},[],[1578],{"type":46,"value":1579},"4294967296",{"type":41,"tag":1184,"props":1581,"children":1582},{},[1583,1588],{"type":41,"tag":150,"props":1584,"children":1586},{"className":1585},[],[1587],{"type":46,"value":899},{"type":46,"value":1589},", review",{"type":41,"tag":1157,"props":1591,"children":1592},{},[1593,1614,1619],{"type":41,"tag":1184,"props":1594,"children":1595},{},[1596,1598,1604,1606,1612],{"type":46,"value":1597},"Re-derive every ",{"type":41,"tag":150,"props":1599,"children":1601},{"className":1600},[],[1602],{"type":46,"value":1603},"simp only",{"type":46,"value":1605}," list with ",{"type":41,"tag":150,"props":1607,"children":1609},{"className":1608},[],[1610],{"type":46,"value":1611},"simp?",{"type":46,"value":1613}," at its own site",{"type":41,"tag":1184,"props":1615,"children":1616},{},[1617],{"type":46,"value":1618},"lists do not transfer between look-alike goals",{"type":41,"tag":1184,"props":1620,"children":1621},{},[1622],{"type":41,"tag":150,"props":1623,"children":1625},{"className":1624},[],[1626],{"type":46,"value":853},{"type":41,"tag":1157,"props":1628,"children":1629},{},[1630,1642,1647],{"type":41,"tag":1184,"props":1631,"children":1632},{},[1633,1635,1640],{"type":46,"value":1634},"Every ",{"type":41,"tag":150,"props":1636,"children":1638},{"className":1637},[],[1639],{"type":46,"value":211},{"type":46,"value":1641}," override is an unproven claim — measure before believing",{"type":41,"tag":1184,"props":1643,"children":1644},{},[1645],{"type":46,"value":1646},"copy-pasted budgets carry no information",{"type":41,"tag":1184,"props":1648,"children":1649},{},[1650,1656],{"type":41,"tag":150,"props":1651,"children":1653},{"className":1652},[],[1654],{"type":46,"value":1655},"#count_heartbeats",{"type":46,"value":1657},", bisection",{"type":41,"tag":1157,"props":1659,"children":1660},{},[1661,1674,1679],{"type":41,"tag":1184,"props":1662,"children":1663},{},[1664,1666,1672],{"type":46,"value":1665},"Conditional simp lemma fires shallow but not deep → raise ",{"type":41,"tag":150,"props":1667,"children":1669},{"className":1668},[],[1670],{"type":46,"value":1671},"maxDischargeDepth",{"type":46,"value":1673}," (default 2)",{"type":41,"tag":1184,"props":1675,"children":1676},{},[1677],{"type":46,"value":1678},"chained side conditions truncate silently, no diagnostic",{"type":41,"tag":1184,"props":1680,"children":1681},{},[1682],{"type":46,"value":1683},"diagnosis (proof-style, simp discipline)",{"type":41,"tag":1157,"props":1685,"children":1686},{},[1687,1692,1697],{"type":41,"tag":1184,"props":1688,"children":1689},{},[1690],{"type":46,"value":1691},"Every project-specific convention gets a custom linter, in CI from day one",{"type":41,"tag":1184,"props":1693,"children":1694},{},[1695],{"type":46,"value":1696},"review misses the 29-of-30 failure mode",{"type":41,"tag":1184,"props":1698,"children":1699},{},[1700,1705,1707],{"type":41,"tag":150,"props":1701,"children":1703},{"className":1702},[],[1704],{"type":46,"value":930},{"type":46,"value":1706}," + ",{"type":41,"tag":150,"props":1708,"children":1710},{"className":1709},[],[1711],{"type":46,"value":891},{"type":41,"tag":125,"props":1713,"children":1714},{},[1715,1717,1723],{"type":46,"value":1716},"Full rationale for each row, plus the library-level anti-patterns, in\n",{"type":41,"tag":64,"props":1718,"children":1720},{"href":1719},"references\u002Fanti-patterns.md",[1721],{"type":46,"value":1722},"anti-patterns.md",{"type":46,"value":389},{"type":41,"tag":49,"props":1725,"children":1727},{"id":1726},"rationalizations-to-reject",[1728],{"type":46,"value":114},{"type":41,"tag":1149,"props":1730,"children":1731},{},[1732,1748],{"type":41,"tag":1153,"props":1733,"children":1734},{},[1735],{"type":41,"tag":1157,"props":1736,"children":1737},{},[1738,1743],{"type":41,"tag":1161,"props":1739,"children":1740},{},[1741],{"type":46,"value":1742},"Excuse",{"type":41,"tag":1161,"props":1744,"children":1745},{},[1746],{"type":46,"value":1747},"Reality",{"type":41,"tag":1177,"props":1749,"children":1750},{},[1751,1764,1784,1804,1823,1836,1856,1869,1882,1902],{"type":41,"tag":1157,"props":1752,"children":1753},{},[1754,1759],{"type":41,"tag":1184,"props":1755,"children":1756},{},[1757],{"type":46,"value":1758},"\"The proof compiles, ship it\"",{"type":41,"tag":1184,"props":1760,"children":1761},{},[1762],{"type":46,"value":1763},"Compiling is the floor. A monolithic tactic block that only Lean can read will break silently at the next Mathlib bump and no one will be able to repair it.",{"type":41,"tag":1157,"props":1765,"children":1766},{},[1767,1772],{"type":41,"tag":1184,"props":1768,"children":1769},{},[1770],{"type":46,"value":1771},"\"Unfolding the definition is simpler than writing API lemmas\"",{"type":41,"tag":1184,"props":1773,"children":1774},{},[1775,1777,1782],{"type":46,"value":1776},"Every downstream ",{"type":41,"tag":150,"props":1778,"children":1780},{"className":1779},[],[1781],{"type":46,"value":379},{"type":46,"value":1783}," couples a proof to the implementation. The first refactor breaks all of them at once. Write the missing lemma.",{"type":41,"tag":1157,"props":1785,"children":1786},{},[1787,1792],{"type":41,"tag":1184,"props":1788,"children":1789},{},[1790],{"type":46,"value":1791},"\"Squeezing every simp makes the proof faster and more robust\"",{"type":41,"tag":1184,"props":1793,"children":1794},{},[1795,1797,1802],{"type":46,"value":1796},"Backwards for ",{"type":41,"tag":142,"props":1798,"children":1799},{},[1800],{"type":46,"value":1801},"terminal",{"type":46,"value":1803}," simp calls: the squeezed list breaks on every rename and drowns the signal. Squeeze non-terminal calls only.",{"type":41,"tag":1157,"props":1805,"children":1806},{},[1807,1812],{"type":41,"tag":1184,"props":1808,"children":1809},{},[1810],{"type":46,"value":1811},"\"It's shorter, therefore better\"",{"type":41,"tag":1184,"props":1813,"children":1814},{},[1815,1817,1821],{"type":46,"value":1816},"Mathlib review policy: golfing is fine ",{"type":41,"tag":142,"props":1818,"children":1819},{},[1820],{"type":46,"value":711},{"type":46,"value":1822}," when it does not sacrifice readability. Length is not the target; legibility is.",{"type":41,"tag":1157,"props":1824,"children":1825},{},[1826,1831],{"type":41,"tag":1184,"props":1827,"children":1828},{},[1829],{"type":46,"value":1830},"\"I'll restructure it into lemmas after it works\"",{"type":41,"tag":1184,"props":1832,"children":1833},{},[1834],{"type":46,"value":1835},"After it works, the structure is load-bearing and tangled. State the skeleton first; the lemmas fall out for free.",{"type":41,"tag":1157,"props":1837,"children":1838},{},[1839,1851],{"type":41,"tag":1184,"props":1840,"children":1841},{},[1842,1844,1849],{"type":46,"value":1843},"\"Adding ",{"type":41,"tag":150,"props":1845,"children":1847},{"className":1846},[],[1848],{"type":46,"value":579},{"type":46,"value":1850}," lines is redundant noise\"",{"type":41,"tag":1184,"props":1852,"children":1853},{},[1854],{"type":46,"value":1855},"They are redundant to the kernel and essential to every human or model that reads the proof next.",{"type":41,"tag":1157,"props":1857,"children":1858},{},[1859,1864],{"type":41,"tag":1184,"props":1860,"children":1861},{},[1862],{"type":46,"value":1863},"\"This helper is too specific to be a lemma\"",{"type":41,"tag":1184,"props":1865,"children":1866},{},[1867],{"type":46,"value":1868},"If it has a clean statement, extract it — dropping the hypotheses it doesn't need usually reveals it was general all along.",{"type":41,"tag":1157,"props":1870,"children":1871},{},[1872,1877],{"type":41,"tag":1184,"props":1873,"children":1874},{},[1875],{"type":46,"value":1876},"\"We'll add linters once the library stabilizes\"",{"type":41,"tag":1184,"props":1878,"children":1879},{},[1880],{"type":46,"value":1881},"Backwards: patterns propagate by copy-paste, so a deferred linter meets a 400-warning backlog instead of one bad line. Enable what is already clean and gate it now.",{"type":41,"tag":1157,"props":1883,"children":1884},{},[1885,1890],{"type":41,"tag":1184,"props":1886,"children":1887},{},[1888],{"type":46,"value":1889},"\"The check passed, so we're clean\"",{"type":41,"tag":1184,"props":1891,"children":1892},{},[1893,1895,1900],{"type":46,"value":1894},"A check that can't fail proves nothing — sweeps reach zero files, misspelled ",{"type":41,"tag":150,"props":1896,"children":1898},{"className":1897},[],[1899],{"type":46,"value":907},{"type":46,"value":1901}," options are ignored, pipelines swallow exit codes. Prove every gate can fail before trusting that it passes.",{"type":41,"tag":1157,"props":1903,"children":1904},{},[1905,1910],{"type":41,"tag":1184,"props":1906,"children":1907},{},[1908],{"type":46,"value":1909},"\"The proof is slow, raise maxHeartbeats\"",{"type":41,"tag":1184,"props":1911,"children":1912},{},[1913,1915,1920],{"type":46,"value":1914},"An unmeasured budget is a claim, not a fix — and it masks the regression the next reader needs to see. Measure with ",{"type":41,"tag":150,"props":1916,"children":1918},{"className":1917},[],[1919],{"type":46,"value":1655},{"type":46,"value":1921},"; restructure the definition or decompose the goal.",{"type":41,"tag":49,"props":1923,"children":1925},{"id":1924},"references",[1926],{"type":46,"value":123},{"type":41,"tag":56,"props":1928,"children":1929},{},[1930,1939,1948,1957,1966,1975,1984,1995],{"type":41,"tag":60,"props":1931,"children":1932},{},[1933,1937],{"type":41,"tag":64,"props":1934,"children":1935},{"href":397},[1936],{"type":46,"value":400},{"type":46,"value":1938}," — definitions, APIs,\nbundling, abstraction boundaries, spec-driven project decomposition",{"type":41,"tag":60,"props":1940,"children":1941},{},[1942,1946],{"type":41,"tag":64,"props":1943,"children":1944},{"href":674},[1945],{"type":46,"value":677},{"type":46,"value":1947}," — tactic proof structure:\ncalc, have\u002Fsuffices, focusing, and simp discipline including the\nwhy-doesn't-this-lemma-fire diagnoses (discharge depth, traversal order,\nnumeral spellings)",{"type":41,"tag":60,"props":1949,"children":1950},{},[1951,1955],{"type":41,"tag":64,"props":1952,"children":1953},{"href":682},[1954],{"type":46,"value":685},{"type":46,"value":1956}," — Mathlib naming\nso lemma names are computable from statements",{"type":41,"tag":60,"props":1958,"children":1959},{},[1960,1964],{"type":41,"tag":64,"props":1961,"children":1962},{"href":1719},[1963],{"type":46,"value":1722},{"type":46,"value":1965}," — recognized anti-patterns,\nwhy each is harmful, and which linter catches it",{"type":41,"tag":60,"props":1967,"children":1968},{},[1969,1973],{"type":41,"tag":64,"props":1970,"children":1971},{"href":663},[1972],{"type":46,"value":666},{"type":46,"value":1974}," — evidence-based\ntechniques specific to LLM-written proofs",{"type":41,"tag":60,"props":1976,"children":1977},{},[1978,1982],{"type":41,"tag":64,"props":1979,"children":1980},{"href":787},[1981],{"type":46,"value":790},{"type":46,"value":1983}," — axiom-based sorry gates, enabling\nproject-specific linter profiles in CI early, the full Mathlib standard-set\naudit, adopting linters with a backlog, writing custom linters for\nproject-specific constructs, and proving every gate can fail",{"type":41,"tag":60,"props":1985,"children":1986},{},[1987,1993],{"type":41,"tag":64,"props":1988,"children":1990},{"href":1989},"references\u002Fperformance.md",[1991],{"type":46,"value":1992},"performance.md",{"type":46,"value":1994}," — measuring per-declaration\ncost, where reduction cost comes from, optimizing definitions without\nlosing semantics",{"type":41,"tag":60,"props":1996,"children":1997},{},[1998,2004],{"type":41,"tag":64,"props":1999,"children":2001},{"href":2000},"references\u002Ftactics.md",[2002],{"type":46,"value":2003},"tactics.md",{"type":46,"value":2005}," — metaprogramming discipline:\nextension-point selection, metavariable and recovery safeguards, bounded\nsearch, actionable errors, structured tracing, generated declarations,\nand failure-surface testing",{"type":41,"tag":2007,"props":2008,"children":2009},"style",{},[2010],{"type":46,"value":2011},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2013,"total":2111},[2014,2033,2043,2061,2076,2088,2098],{"slug":2015,"name":2015,"fn":2016,"description":2017,"org":2018,"tags":2019,"stars":23,"repoUrl":24,"updatedAt":2032},"address-sanitizer","detect memory errors during fuzzing","AddressSanitizer detects memory errors during fuzzing. Use when fuzzing C\u002FC++ code to find buffer overflows and use-after-free bugs.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2020,2023,2026,2029],{"name":2021,"slug":2022,"type":16},"C#","c",{"name":2024,"slug":2025,"type":16},"Debugging","debugging",{"name":2027,"slug":2028,"type":16},"Security","security",{"name":2030,"slug":2031,"type":16},"Testing","testing","2026-07-17T06:05:14.925095",{"slug":2034,"name":2034,"fn":2035,"description":2036,"org":2037,"tags":2038,"stars":23,"repoUrl":24,"updatedAt":2042},"aflpp","perform multi-core fuzzing of C\u002FC++ projects","AFL++ is a fork of AFL with better fuzzing performance and advanced features. Use for multi-core fuzzing of C\u002FC++ projects.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2039,2040,2041],{"name":2021,"slug":2022,"type":16},{"name":2027,"slug":2028,"type":16},{"name":2030,"slug":2031,"type":16},"2026-07-17T06:05:12.433192",{"slug":2044,"name":2044,"fn":2045,"description":2046,"org":2047,"tags":2048,"stars":23,"repoUrl":24,"updatedAt":2060},"agentic-actions-auditor","audit GitHub Actions for security vulnerabilities","Audits GitHub Actions workflows for security vulnerabilities in AI agent integrations including Claude Code Action, Gemini CLI, OpenAI Codex, and GitHub AI Inference. Detects attack vectors where attacker-controlled input reaches AI agents running in CI\u002FCD pipelines, including env var intermediary patterns, direct expression injection, dangerous sandbox configurations, and wildcard user allowlists. Use when reviewing workflow files that invoke AI coding agents, auditing CI\u002FCD pipeline security for prompt injection risks, or evaluating agentic action configurations.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2049,2052,2055,2056,2059],{"name":2050,"slug":2051,"type":16},"Agents","agents",{"name":2053,"slug":2054,"type":16},"CI\u002FCD","ci-cd",{"name":21,"slug":22,"type":16},{"name":2057,"slug":2058,"type":16},"GitHub Actions","github-actions",{"name":2027,"slug":2028,"type":16},"2026-07-18T05:47:48.564744",{"slug":2062,"name":2062,"fn":2063,"description":2064,"org":2065,"tags":2066,"stars":23,"repoUrl":24,"updatedAt":2075},"algorand-vulnerability-scanner","scan Algorand smart contracts for vulnerabilities","Scans Algorand smart contracts for 11 common vulnerabilities including rekeying attacks, unchecked transaction fees, missing field validations, and access control issues. Use when auditing Algorand projects (TEAL\u002FPyTeal).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2067,2070,2071,2072],{"name":2068,"slug":2069,"type":16},"Audit","audit",{"name":21,"slug":22,"type":16},{"name":2027,"slug":2028,"type":16},{"name":2073,"slug":2074,"type":16},"Smart Contracts","smart-contracts","2026-07-18T05:47:43.989063",{"slug":2077,"name":2077,"fn":2078,"description":2079,"org":2080,"tags":2081,"stars":23,"repoUrl":24,"updatedAt":2087},"atheris","fuzz Python code with Atheris","Atheris is a coverage-guided Python fuzzer based on libFuzzer. Use for fuzzing pure Python code and Python C extensions.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2082,2085,2086],{"name":2083,"slug":2084,"type":16},"Python","python",{"name":2027,"slug":2028,"type":16},{"name":2030,"slug":2031,"type":16},"2026-07-17T06:05:14.575191",{"slug":2089,"name":2089,"fn":2090,"description":2091,"org":2092,"tags":2093,"stars":23,"repoUrl":24,"updatedAt":2097},"audit-augmentation","augment code graphs with audit findings","Augments Trailmark code graphs with external audit findings from SARIF static analysis results, weAudit annotation files, and version-gated Trailmark 0.4.x binary-analysis graph exports. Maps findings to graph nodes by file and line overlap, creates severity-based subgraphs, and enables cross-referencing findings with pre-analysis data (blast radius, taint, etc.). Use when projecting SARIF results onto a code graph, overlaying weAudit annotations, importing binary graph findings, cross-referencing Semgrep, CodeQL, or binary-analysis findings with call graph data, or visualizing audit findings in the context of code structure.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2094,2095,2096],{"name":2068,"slug":2069,"type":16},{"name":21,"slug":22,"type":16},{"name":2027,"slug":2028,"type":16},"2026-08-01T05:44:54.920542",{"slug":2099,"name":2099,"fn":2100,"description":2101,"org":2102,"tags":2103,"stars":23,"repoUrl":24,"updatedAt":2110},"audit-context-building","build architectural context for code analysis","Understand a codebase before looking for bugs in it - what each function assumes, what it guarantees, and what it depends on elsewhere. Use when starting an audit, threat model, or architecture review on unfamiliar code, and before any vulnerability-hunting pass.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2104,2107,2108,2109],{"name":2105,"slug":2106,"type":16},"Architecture","architecture",{"name":2068,"slug":2069,"type":16},{"name":21,"slug":22,"type":16},{"name":18,"slug":19,"type":16},"2026-08-08T03:55:04.309014",75,{"items":2113,"total":2219},[2114,2121,2127,2135,2142,2148,2154,2161,2172,2184,2195,2206],{"slug":2015,"name":2015,"fn":2016,"description":2017,"org":2115,"tags":2116,"stars":23,"repoUrl":24,"updatedAt":2032},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2117,2118,2119,2120],{"name":2021,"slug":2022,"type":16},{"name":2024,"slug":2025,"type":16},{"name":2027,"slug":2028,"type":16},{"name":2030,"slug":2031,"type":16},{"slug":2034,"name":2034,"fn":2035,"description":2036,"org":2122,"tags":2123,"stars":23,"repoUrl":24,"updatedAt":2042},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2124,2125,2126],{"name":2021,"slug":2022,"type":16},{"name":2027,"slug":2028,"type":16},{"name":2030,"slug":2031,"type":16},{"slug":2044,"name":2044,"fn":2045,"description":2046,"org":2128,"tags":2129,"stars":23,"repoUrl":24,"updatedAt":2060},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2130,2131,2132,2133,2134],{"name":2050,"slug":2051,"type":16},{"name":2053,"slug":2054,"type":16},{"name":21,"slug":22,"type":16},{"name":2057,"slug":2058,"type":16},{"name":2027,"slug":2028,"type":16},{"slug":2062,"name":2062,"fn":2063,"description":2064,"org":2136,"tags":2137,"stars":23,"repoUrl":24,"updatedAt":2075},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2138,2139,2140,2141],{"name":2068,"slug":2069,"type":16},{"name":21,"slug":22,"type":16},{"name":2027,"slug":2028,"type":16},{"name":2073,"slug":2074,"type":16},{"slug":2077,"name":2077,"fn":2078,"description":2079,"org":2143,"tags":2144,"stars":23,"repoUrl":24,"updatedAt":2087},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2145,2146,2147],{"name":2083,"slug":2084,"type":16},{"name":2027,"slug":2028,"type":16},{"name":2030,"slug":2031,"type":16},{"slug":2089,"name":2089,"fn":2090,"description":2091,"org":2149,"tags":2150,"stars":23,"repoUrl":24,"updatedAt":2097},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2151,2152,2153],{"name":2068,"slug":2069,"type":16},{"name":21,"slug":22,"type":16},{"name":2027,"slug":2028,"type":16},{"slug":2099,"name":2099,"fn":2100,"description":2101,"org":2155,"tags":2156,"stars":23,"repoUrl":24,"updatedAt":2110},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2157,2158,2159,2160],{"name":2105,"slug":2106,"type":16},{"name":2068,"slug":2069,"type":16},{"name":21,"slug":22,"type":16},{"name":18,"slug":19,"type":16},{"slug":2162,"name":2162,"fn":2163,"description":2164,"org":2165,"tags":2166,"stars":23,"repoUrl":24,"updatedAt":2171},"audit-prep-assistant","prepare codebases for security audits","Prepares codebases for security review using Trail of Bits' checklist. Helps set review goals, runs static analysis tools, increases test coverage, removes dead code, ensures accessibility, and generates documentation (flowcharts, user stories, inline comments).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2167,2168,2169,2170],{"name":2068,"slug":2069,"type":16},{"name":21,"slug":22,"type":16},{"name":18,"slug":19,"type":16},{"name":2027,"slug":2028,"type":16},"2026-07-18T05:47:39.210985",{"slug":2173,"name":2173,"fn":2174,"description":2175,"org":2176,"tags":2177,"stars":23,"repoUrl":24,"updatedAt":2183},"burpsuite-project-parser","parse Burp Suite project files","Searches and explores Burp Suite project files (.burp) from the command line. Use when searching response headers or bodies with regex patterns, extracting security audit findings, dumping proxy history or site map data, or analyzing HTTP traffic captured in a Burp project.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2178,2179,2182],{"name":2068,"slug":2069,"type":16},{"name":2180,"slug":2181,"type":16},"CLI","cli",{"name":2027,"slug":2028,"type":16},"2026-07-17T06:05:33.198077",{"slug":2185,"name":2185,"fn":2186,"description":2187,"org":2188,"tags":2189,"stars":23,"repoUrl":24,"updatedAt":2194},"c-review","audit C and C++ code","Performs comprehensive C\u002FC++ security review for memory corruption, integer overflows, race conditions, and platform-specific vulnerabilities. Use when auditing native C\u002FC++ applications, reviewing daemons or services for memory safety, or hunting integer overflow \u002F use-after-free \u002F race conditions in userspace code.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2190,2191,2192,2193],{"name":2068,"slug":2069,"type":16},{"name":2021,"slug":2022,"type":16},{"name":21,"slug":22,"type":16},{"name":2027,"slug":2028,"type":16},"2026-07-17T06:05:11.333374",{"slug":2196,"name":2196,"fn":2197,"description":2198,"org":2199,"tags":2200,"stars":23,"repoUrl":24,"updatedAt":2205},"cairo-vulnerability-scanner","scan Cairo and StarkNet contracts for vulnerabilities","Scans Cairo\u002FStarkNet smart contracts for 6 critical vulnerabilities including felt252 arithmetic overflow, L1-L2 messaging issues, address conversion problems, and signature replay. Use when auditing StarkNet projects.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2201,2202,2203,2204],{"name":2068,"slug":2069,"type":16},{"name":21,"slug":22,"type":16},{"name":2027,"slug":2028,"type":16},{"name":2073,"slug":2074,"type":16},"2026-07-18T05:47:42.84568",{"slug":2207,"name":2207,"fn":2208,"description":2209,"org":2210,"tags":2211,"stars":23,"repoUrl":24,"updatedAt":2218},"cargo-fuzz","fuzz Rust projects with cargo-fuzz","cargo-fuzz is the de facto fuzzing tool for Rust projects using Cargo. Use for fuzzing Rust code with libFuzzer backend.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2212,2213,2216,2217],{"name":18,"slug":19,"type":16},{"name":2214,"slug":2215,"type":16},"Rust","rust",{"name":2027,"slug":2028,"type":16},{"name":2030,"slug":2031,"type":16},"2026-07-17T06:05:18.942184",109]