Repository navigation
Allow proof imports with measured impact - #183
Conversation
Adversarial review at 788c6f1Five review passes ran on this head: logic, evidence binding, CLI and docs contract, test teeth (mutation testing), and golf. A separate refuter re-ran every finding and tried to knock it down; only what survived is below. All four bugs were reproduced on real Lean at this head. Verdict: not merge-ready yet. Two cases silently under-report the import scope, and two valid project layouts make every query fail. Each fix is local. CI is green and the PR merges cleanly onto main (landing notes at the end). Bugs1. A root module missing from the snapshot inventory silently cuts import edges ( The probe emits evidence only for inventory modules (:134) and drops every import of a non-inventory module (:142). A module that Lake builds into the root artifact dir, but whose source the snapshot skips, gets no node, so every reverse-import path through it disappears.
2. Removing an import can drop modules from the build, and the report never mentions them ( The scope is the reverse-import closure of MODULE, which covers what an added import can affect. A removed import can also take modules out of a roots-only library's build.
3. Module names that need «» make every query fail with a false error ( The probe prints
4. A library nested inside another library's srcDir blocks every query ( The strict one-to-one check maps every captured file under every srcDir, not just the modules a library selects. Take
Should fix5. A case-mismatched
6. The owner clause adds another article's claim target when that article owns, through the parent chain, a helper in an affected module. That article is not in
7. Formalize contradicts itself on downstream edits ( The import section says "If any article actually needs changes, atomically acquire the full held set plus its targets". The claim lifecycle keeps "any validated expansion", and so do AGENTS.md and the comment at Test gapsThese come from mutation testing of
Golf (optional)Each item was applied alone to a copy of this head. Each passed ruff and
Nits
Landing
Posted by PR swarm: PR Swarm Lead |
The import-impact probe emitted module evidence only for modules in the captured source inventory and dropped every import of any other module. A module Lake builds into the root package but whose source the inventory skips (a nested Git checkout, an ignored directory such as `build`, a symlinked directory, or a numeric component such as Demo/1.lean, which the inventory names with Name.num while Lean uses a string) got no node, so every reverse-import path through it vanished and the report silently under-reported its scope. The probe now takes the root package's artifact directory and fails when any other loaded module's OLean lies under it, naming the module. Dependency and toolchain modules build elsewhere and still load. A module the inventory misses and nothing imports is never loaded, yet an `M.*` or `M.+` glob still builds it, so the articles naming its declarations dropped out unseen. The probe now walks each root library's `M.*` and `M.+` glob directories with Lean.forEachModuleInDir, the walk Lake's globs make, which follows symlinked directories and skips no name, and fails on any module it finds that the inventory lacks. A nested checkout with no Lean files yields no module, so it passes. run_probe passes both refusals through without the "is the project built" hint, since the project is built. render_impact_probe takes the artifact directory in place of its include_module_evidence flag, as the evidence branch now needs it.
The import-impact probe prints Lean's spelling of each loaded module (`H.«2_1»`, `H.«my-lemmas»`), while the source inventory keyed modules by their raw path parts (`H.2_1`). Any project with such a module refused every query with "the impact probe loaded H.«2_1» without a captured root-project source". Joining path parts also made the dotted stem of H/a.b.lean two components and the stem of H/1.lean a number, so the probe refused both as root-package modules outside the captured inventory, and the expected artifact of H/a.b.lean was H/a/b.olean. Module evidence now meets the inventory by name component, whichever side quotes them, and the expected artifact path is built from the components. The inventory quotes a dotted or numeric component, so the name parses back as the one string component Lean builds from the path.
import-impact required every captured file under every srcDir to map to one module and every module name to one file. A library nested inside another library's srcDir breaks the first: with lean_lib T on "." and lean_lib TTests on "tests", tests/TTests.lean is both `tests.TTests` and `TTests`, and every query failed before the probe with "project source tests/TTests.lean maps to more than one module". Sibling srcDirs break the second: with aa/Scratch.lean and bb/Scratch.lean, every query failed with "project module Scratch maps to more than one source". No library selects `tests.TTests` or `Scratch` and Lake builds neither, so the refusals protected nothing. Both checks now cover only the modules a library selects, by a root that prefixes whole name components or by a glob, as Lake's LeanLib.isLocalModule decides, so a file two libraries select and a module two selected files claim are still refused. Past that check, the probe still found a module's source through the first search-path entry holding a directory named like the module's root. With lean_lib Tests on "tst" declared after a library on ".", a `Tests` directory beside the lakefile, or on a case-insensitive file system a srcDir named "tests", sent it to a Tests.lean there that does not exist, and every query failed. The probe now takes the first entry where the module's source file exists.
import-impact reports MODULE and every loaded module that imports it, directly or transitively, which covers what an added import can affect. Removing an import can also take modules out of the build: roots and single-module globs bring in only what they import, transitively, so a module only the removed import reached is no longer built, and the articles naming its declarations get no claim check. The docstrings and the CLI help called the scope conservative, and nothing said what it leaves out. The docstrings, the CLI help, the README, and the text report now say that the reverse-import scope covers added imports and that removing an import can also drop modules from the build, which the report does not list. Formalize's import workflow now asks for baselines of the articles of modules only a removed import kept in the build. The README also names the two refusals behind its claim that affected_modules is complete: a loaded root-package module, or a module a glob finds, that the captured source inventory lacks.
Brings in #182 (quoted module names in the impact probe), #171 (`autoform search`), and the rest of main since 4c80749. Conflict resolutions: - autoform_cli/impact.py, _source_modules: took #182's naming. It quotes every component that is not a plain identifier (`H.«2_1»`, `H.«my-lemmas»`, `H.«α»`), keeps a dotted stem one component, and sets names containing `»` apart as unimportable. This branch quoted only dotted and all-digit components (`H.«a.b»`, `H.«1»`). Both spell the same string components, which is all the by-component lookups compare, and #182's spelling is the one Lean prints except for `α`. - autoform_cli/impact.py, _module_source_paths: kept this branch's strict loop (only modules a library selects, one source per module and one module per source) over the first map #182's _source_modules now returns. Unimportable names can never load, so the strict map leaves them out, as main's non-strict map does. - autoform_cli/README.md and autoform_cli/__main__.py: kept both sides, this branch's `work import-impact` block followed by #171's `search` block. Adjusted to #182 without a textual conflict: - _validated_module_evidence keys module names with _name_key, the helper #182's locator uses to match Lean's module spellings, in place of this branch's _module_parts, which did the same job. The probe check and the locator now agree on quoted and non-identifier names: Lean prints `H.α` where the inventory spells `H.«α»`. _module_artifact_paths still builds each path from the components of the inventory name, keyed the same way. - Two comments that described the pre-#182 inventory spelling `H.2_1` now describe `H.«α»`.
The glob walk refused a module only when the source inventory held no module of that name. A file in another library's srcDir that spells the same name, such as tools/Demo/Sym/Y.lean beside a symlinked Demo/Sym, filled that gap, so the unimported glob module still dropped out of the scope. The probe imports every glob module the inventory holds, so the walk now refuses any module it finds that is not loaded. The real-Lean glob test adds that stray file under a second library; without this change the symlinked case exits 0 with affected_modules [Demo, Demo.Base].
Fixes for the four bugs, pushed as
|
Brings in #172 (implementation notes), #111, #168 and #223 to #229. Three files conflicted, each because both sides added text at the same spot; every resolution keeps both: - AGENTS.md: main's rewritten roadmap bullet (implementation notes under blueprint/.implementation-notes/), then this branch's import bullet. - skills/formalize/SKILL.md: this branch's "renew the complete claim set" joined with main's audit run and stale-note deletion, rewrapped. - tests/test_skill_examples.py: this branch's import-scope test and main's implementation-notes test, side by side.
No behavior change. - __main__: one _work_impact handler runs work impact and work import-impact under their shared error contract. It replaces two lambdas and _run_impact_command, whose formatter annotation fit neither caller. - impact.py: article_local reads as one set check, the text report's two claim lines share a _keyed helper, and revision_impact drops a check that _load_impact_evidence already makes. The docstrings describe the glob refusal as it now works, mention allow_empty, and put one space between sentences like the rest of the module. - README: the import-impact paragraph gains the trusted-checkout sentence that work impact has, and its JSON paragraph is rewrapped. - Formalize: rewrapped, and "On any acquisition refusal" now says a refused import-claim acquisition, since a refused lake-build claim is waited on instead. - develop-plugin: restores "repository" and "releases" and splits the garden-path sentence about Cabannes facts and roots, within the 220-word budget. - AGENTS.md: rewraps the import bullet. - tests: one _lake_build helper replaces four copies of the build call.
Items 5 to 7 of our review are merge blockersThe four bugs from our review of
We are fixing all three on this branch now, each with a regression test. Posted by PR swarm: PR Swarm Lead |
import-impact compared Lean's real paths for each module's source and OLean with paths built from --lean-root as strings. Path.resolve() keeps the typed spelling, so on a case-insensitive file system a root typed in another case (PROJ for proj) failed on the first module, with a message that read like a shadowing failure, although work impact accepts the same root. The comparison keeps string equality as its fast path and falls back to os.path.samefile, so two spellings of one file match while a different file, or a missing one, is still refused. The regression test reaches the same two spellings through a symbolic link, so it runs on any file system.
An article that owns, through the parent chain, a helper in an affected module (a structure's API lemma in a later file, for example) became a claim target without appearing in affected_articles, so the text report asked to check its claim with no reason given. article_local could also be true while affected_claim_targets named that article or an unowned helper, and Formalize baselines only the named affected articles. affected_articles now lists such owners with the modules of the helpers they own, so every affected claim target is an affected article or an unowned helper, and the owner clause in the claim targets goes away. article_local now holds only when the selected article is affected, no other claim target is reported, and no other article has unresolved declarations, like contained in work impact. The README documents article, module_claim_key, affected_articles, selected_affected and article_local, which the v1 contract did not describe.
The import workflow, the claim lifecycle, AGENTS.md and impact.py all expect an import edit to acquire and repair any article that validation shows must change, but the general rules still forbade modifying another article except for implementation notes and declaration revisions, and sent "a change another article needs" back to Roadmap. A worker following the skill either stalled or edited a downstream article without the review the revision contract requires. Import repairs are now a listed exception, made under the revision contract's review gates once the import set holds the article's claim, and the Roadmap sentence excludes them.
Final review at 60d20ddVerdict: merge-ready. Our review of What changed since
|
| Review item | Commits | Change |
|---|---|---|
| Bug 1: a root module outside the snapshot inventory silently cut import edges | ef2eac6b, c9a48f95 |
import-impact now refuses and names the module. It refuses a loaded root-package module whose OLean lies under the root artifact directory but whose source the inventory skipped. It also refuses a module that an M.* or M.+ glob reaches, found by walking the glob directory the way Lake does, when the probe did not load it. |
| Bug 2: removing an import dropped modules without reporting them | 67931c70 |
As decided for this PR, the scope stays the reverse-import closure, which covers what an added import can affect. The docstrings, the README, the CLI help, the text report and the Formalize import workflow now say that removing an import can drop modules from the build that the report does not list. |
| Bug 3: module names that need «» failed every query | 2c21b426 |
Names are matched by component on both sides, and artifact paths are built from components. |
| Bug 4: a nested srcDir blocked every query | 1d8197ee |
The ownership checks cover only modules a library selects. The probe finds a module's source by exact file existence, which also fixes nested libraries on case-insensitive file systems. |
5: a case-mismatched --lean-root was refused on macOS |
e5e3ab4a |
The source and artifact comparisons fall back to os.path.samefile, so two spellings of one file match while a different or missing file is still refused. |
6: article_local could be true while other articles were claim targets |
05220c70 |
An article that owns, through the parent chain, a helper in an affected module is listed in affected_articles, so every affected claim target has a visible reason. article_local now holds only when nothing else is a claim target, like contained in work impact. The README documents article, module_claim_key, affected_articles, selected_affected and article_local. |
| 7: Formalize contradicted itself on downstream edits | 60d20dd6 |
Repairing another article's Lean that a validated import edit breaks is now a listed exception, made under the revision contract's review gates, and the Roadmap rule leaves import repairs out. |
| Style | 95a6f57b |
One CLI handler for work impact and work import-impact, docstrings that match the code, rewrapped docs, and a shared _lake_build test helper. |
Merges of main |
41b3f708, a2b5018a |
Takes #182's module naming and keeps both sides of the #171 and #172 documentation changes. |
Validation
- CI at
60d20dd6: the pull-request run passes all four jobs (Python 3.10, Python 3.13, real Lean with the fixture toolchain, and the Windows hardening job), and so did the push run of the same commit on the scratch branchswarm/183-blockers. On this branch's push run, the real-Lean job timed out in its "Install the fixture's Lean toolchain" step, before any repository code ran; that job has been re-run. - The tests for bugs 1, 3 and 4 were checked locally to fail without their fixes. At the first merge of
main, real-Leantests/test_impact.pypassed (110 tests), real-Leantests/test_skeleton.pypassed (172), and the full suite without Lean passed (2058). The style pass and the fixes for items 5 to 7 were checked by CI only, because the machine we test on was overloaded.
Follow-ups, not blockers
- Test gaps from our first review that remain: the exact
module_claim_targetvalue, two libraries sharing srcDir., the default text output ofwork import-impactthrough the CLI (every successful CLI test passes--json), an article with nolean:declaration, and the--timeoutwiring forimport-impact. work impacthas the same two blind spots this PR closes for import-impact: a loaded root module outside the inventory, and an unimported glob module in a directory the inventory skips.- The probe takes a module's source from the first srcDir that holds the path, so a stray file in an earlier srcDir gives a false refusal. It fails closed.
MODULEmust be spelled as Lean prints it, soH.2_1is refused whereH.«2_1»works.
Posted by PR swarm: PR Swarm Lead
Brings in main up to b1b0094, including #248 (package srcDir), #231 (Lean root errors) and #165. Two files conflicted, and every resolution keeps both sides: - autoform_cli/impact.py: this branch's claims import and main's LeanSourceError and resolve_lean_root import. Main's switch to resolve_lean_root merged into _load_impact_evidence, where this branch moved revision_impact's body. - autoform_cli/skeleton.py: lean_libraries computes this branch's root artifact directory and main's package srcDir. Library srcDirs are relative to the package srcDir, and a package without library targets uses the package srcDir and the artifact directory.
Still merge-ready at 3c4631e
Posted by PR swarm: PR Swarm Lead |
#183 left skills/develop-plugin/SKILL.md at exactly 220 words, and #235, written against an older main, rewrote one sentence one word longer. Both merged cleanly, so main now has 221 words and fails test_each_skill_points_to_its_thesis_example. "Normally run:" becomes "Run:", which says the same thing. (cherry picked from commit 7a13fcf)
#183 left skills/develop-plugin/SKILL.md at exactly 220 words, and #235, written against an older main, rewrote one sentence one word longer. Both merged cleanly, so main now has 221 words and fails test_each_skill_points_to_its_thesis_example. "Normally run:" becomes "Run:", which says the same thing. (cherry picked from commit 7a13fcf)
#183 left skills/develop-plugin/SKILL.md at exactly 220 words, and #235, written against an older main, rewrote one sentence one word longer. Both merged cleanly, so main now has 221 words and fails test_each_skill_points_to_its_thesis_example. "Normally run:" becomes "Run:", which says the same thing. (cherry picked from commit 7a13fcf)
#183 left skills/develop-plugin/SKILL.md at exactly 220 words, and #235, written against an older main, rewrote one sentence one word longer. Both merged cleanly, so main now has 221 words and fails test_each_skill_points_to_its_thesis_example. "Normally run:" becomes "Run:", which says the same thing. (cherry picked from commit 7a13fcf)
Fixes facebookresearch#276. facebookresearch#183 taught the impact probe to refuse a root-package module the captured source inventory lacks: a loaded module whose OLean lies under the root artifact directory, and a module an M.* or M.+ glob finds by Lake's own walk that the probe did not load. Both checks ran only when module evidence was requested, so work impact still recorded nothing for such a module, and every use of a revised declaration in it, with every article resting on it, dropped out of the report. The probe now runs the inventory checks whenever it is given the root artifact directory, which both commands pass, and emits module evidence only for import-impact. The real-Lean refusal tests run work impact beside import-impact, and once the inventory captures the module, work impact reports chapter/top as statement-impacted. The README says the work impact probe refuses these modules.
facebookresearch#183 left skills/develop-plugin/SKILL.md at exactly 220 words, and facebookresearch#235, written against an older main, rewrote one sentence one word longer. Both merged cleanly, so main now has 221 words and fails test_each_skill_points_to_its_thesis_example. "Normally run:" becomes "Run:", which says the same thing.
Follow-up to facebookresearch#183, whose final review listed five behaviors no test pinned: - the default text report through the CLI, line by line; - --timeout, which reaches the probe; - the exact module claim target, and a module whose slug is cut at 48 characters while its digest keeps long names apart; - two libraries on one srcDir that both select a module, which is one module from one file, not an ownership conflict; - an article that names no Lean declaration, which import-impact accepts instead of asking for --declaration.
Summary
Policy
Lean imports are allowed. Autoform measures their loaded-build impact and reports active coordination risks; it does not impose a blanket import ban. Unresolved declarations are disclosed without globally blocking unrelated edits. Claims are acquired atomically, renewed and released as a complete set, and released before waiting after any refusal or recheck conflict.
Validation