minilaws 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
minilaws-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lua Japiassu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: minilaws
3
+ Version: 0.1.0
4
+ Summary: Curry-Howard for AI-edited Python: the AI writes code and proofs, a tiny kernel checks them, the laws stay human-owned
5
+ Author: Lua Japiassu
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/luajapiassu/minilaws
8
+ Keywords: proof-checking,dependent-types,verification,ai-safety,pytest
9
+ Classifier: Framework :: Pytest
10
+ Classifier: Topic :: Software Development :: Testing
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ # minilaws
17
+
18
+ **Types are statements, programs are proofs, and type checking is proof checking** (Curry-Howard).
19
+ minilaws applies this to AI-edited code, with a dependent-type proof checker in one pure-Python file (stdlib only).
20
+
21
+ You write ordinary Python, an AI is free to edit it, and the laws in `LAWS.laws` must keep holding. If an edit breaks a law, your tests fail.
22
+
23
+ ## The strategy
24
+
25
+ It's the approach of OpenAI's Navier-Stokes announcement, scaled down: let the AI do the work, and make checking that work cheap. There, a [swarm of agents](https://explainx.ai/blog/openai-navier-stokes-solution-agent-swarm-2026) produced the proof, and a Lean 4 formalization of it was checked in [17 hours](https://explainx.ai/blog/lean-4-formal-proof-cost-collapse-navier-stokes-2026). Checking it by hand was estimated at about 132,800 human-hours. Here, the AI writes the code and the proofs, and a small kernel checks them on every test run.
26
+
27
+ A kernel can't check one thing: whether the statement is the right one. For that, minilaws follows the [Lean proof of Fermat's Last Theorem built with Claude](https://explainx.ai/blog/anthropic-claude-fermats-last-theorem-lean-proof-2026). There, the canonical statement was fixed in advance, and Lean's [`comparator`](https://github.com/leanprover/comparator) required the proved theorem to be identical to it. Here, a human owns `LAWS.laws`, and `minilaws check --against origin/main` requires every law to still mean what it meant on `main`.
28
+
29
+ ## Usage
30
+
31
+ ```
32
+ pip install git+https://github.com/luajapiassu/minilaws # not on PyPI yet (https://github.com/luajapiassu/minilaws/issues/1)
33
+ ```
34
+
35
+ 1. Ask your AI to write your app's rules in `LAWS.laws` and to prove them.
36
+ 2. Run `pytest` as usual. Every project with a `.laws` file that pytest collects becomes one test, so a CI that already runs your tests now enforces the laws too.
37
+ 3. Protect the laws in review (see [Who owns the laws](#who-owns-the-laws)).
38
+
39
+ If your pytest config narrows collection (`testpaths`, `--ignore`), make sure it still reaches the `.laws` files, or run `minilaws check` in CI.
40
+
41
+ You can also check without pytest:
42
+
43
+ ```
44
+ minilaws check # current directory
45
+ minilaws check path/to/project
46
+ minilaws check --against origin/main # also: the laws are still the ones on main
47
+ ```
48
+
49
+ No configuration is needed. minilaws checks:
50
+ - every `*.laws` file;
51
+ - every `.py` file that does `from minilaws import Nat`.
52
+
53
+ File order doesn't matter: code is checked first, then laws, then proofs, and within each step a declaration comes after the ones it uses. To pick the files explicitly, add a `minilaws.toml` with `files = [...]`.
54
+
55
+ A folder with its own `LAWS.laws` or `minilaws.toml` is a separate project: the parent's check skips it, and it gets its own test.
56
+
57
+ Delete the `+ 1` in `examples/app.py`. The code still runs, but it's now wrong:
58
+
59
+ ```
60
+ REJECTED: proofs.laws: proof zero_add: type mismatch in 'cong succ ih'
61
+ ```
62
+
63
+ ## Who owns the laws
64
+
65
+ The laws are only worth something if the AI can't change what they say. minilaws closes the routes it can see:
66
+
67
+ - A file that contains `law`s is human-owned. Its laws, `def`s and `inductive`s may use only the prelude, Python code (`.py`) and its own declarations. A law can't depend on something defined in `proofs.laws`, where the AI could change its meaning. This includes `theorem`s in that file, so put helper lemmas in your proofs file ([#7](https://github.com/luajapiassu/minilaws/issues/7)).
68
+ - Python files may only import from `minilaws`, and may only call functions defined earlier in the same file, so the code that's checked is the code that runs.
69
+ - Deleting or renaming `LAWS.laws` leaves its proofs without laws, which fails the check.
70
+
71
+ The other route is changing the laws themselves: weakening one, or deleting a law together with its proof. Inside one version of the code that's invisible, since the weaker laws really are proved. So minilaws does what Lean's [`comparator`](https://github.com/leanprover/comparator) does with a challenge file: it takes the laws from a version the change can't touch and compares.
72
+
73
+ ```
74
+ minilaws check --against origin/main
75
+ ```
76
+
77
+ Every law at `origin/main` must still exist, with the same statement and the same dependencies, taken transitively. A `def` or `inductive` in a laws file must be identical. Code is compared by signature and by the file it comes from, because the code is what's allowed to change. Renaming a binder or editing comments isn't a change, and new laws are fine. Anything else is rejected:
78
+
79
+ ```
80
+ REJECTED: the laws differ from origin/main; a human must approve this:
81
+ law add_assoc: removed
82
+ ```
83
+
84
+ In CI, run it against the PR's base branch. The pytest plugin doesn't take `--against` yet ([#8](https://github.com/luajapiassu/minilaws/issues/8)), so it's a separate step:
85
+
86
+ ```yaml
87
+ - uses: actions/checkout@v4
88
+ with: { fetch-depth: 0 }
89
+ - run: pip install git+https://github.com/luajapiassu/minilaws
90
+ - run: minilaws check --against origin/${{ github.base_ref }}
91
+ ```
92
+
93
+ When a law change is intentional, this step fails by design, and a human has to approve the change. Protect `LAWS.laws` and `minilaws.toml` with `CODEOWNERS` plus branch protection, so that approval comes from someone the AI isn't. Other ways to approve are in [#5](https://github.com/luajapiassu/minilaws/issues/5). For example:
94
+
95
+ ```
96
+ LAWS.laws @you
97
+ minilaws.toml @you
98
+ ```
99
+
100
+ ## Syntax
101
+
102
+ ```
103
+ def name : T := t -- code
104
+ law name : T -- statement (LAWS.laws)
105
+ proof name := t -- proof of a law
106
+ theorem name : T := t -- helper lemma: statement + proof
107
+ inductive List (A : Type 0) : Type 0 where
108
+ | nil : List A
109
+ | cons : A -> List A -> List A -- generates List.rec (= induction on lists)
110
+ {x : A} -> B fun {x} => t -- implicit arguments, inferred by unification
111
+ @f A x -- pass implicit arguments explicitly
112
+ ```
113
+
114
+ The prelude defines `Nat` and `Eq` with `inductive` itself, so the kernel has no special cases.
115
+
116
+ ## Python subset
117
+
118
+ ```python
119
+ from minilaws import Nat
120
+
121
+ def add(n: Nat, m: Nat) -> Nat:
122
+ if m == 0:
123
+ return n
124
+ return add(n, m - 1) + 1
125
+ ```
126
+
127
+ Supported:
128
+ - Functions over `Nat`.
129
+ - Structural recursion `f(..., m - 1, ...)` after `if m == 0`, with the other arguments unchanged.
130
+ - Literals, `x + <int>`, and calls to functions defined earlier in the same file.
131
+ - Imports only from `minilaws`.
132
+
133
+ Anything else is refused with `unsupported Python (...)`, so a checked module holds only these functions: keep other code in modules that import them. Checked modules can't call each other yet ([#6](https://github.com/luajapiassu/minilaws/issues/6)). `Nat = int`, and the laws speak about `n >= 0`.
134
+
135
+ ## Optional: Claude Code plugin
136
+
137
+ This gives faster feedback during a session: Claude sees a broken law right after the edit, not only when the tests run.
138
+
139
+ ```
140
+ /plugin marketplace add luajapiassu/minilaws
141
+ /plugin install minilaws@minilaws
142
+ ```
143
+
144
+ Unlike pytest, the plugin needs a `minilaws.toml` with `files` and `laws`. It blocks edits to the laws files and to `minilaws.toml`, re-checks after every edit, and ships a skill with the usual proof patterns. If the hook itself fails (bad config, missing file), it rejects the edit rather than letting it through.
145
+
146
+ Edits made through Bash aren't watched during the session ([#3](https://github.com/luajapiassu/minilaws/issues/3)). The real enforcement is pytest/CI: `minilaws check --against` catches a changed law however it was edited.
147
+
148
+ ## Why the guarantee holds
149
+
150
+ - Predicative universes (`Type 0 : Type 1`), and constructor fields must fit in `Type 0`, so there's no `Type : Type` paradox.
151
+ - Inductive types must be strictly positive, and a definition can't call itself. Every term terminates, so there are no looping "proofs".
152
+ - An unproved `law` isn't in scope, so it can't be used as a hypothesis.
153
+ - For proofs, the elaborator (implicit arguments) is **not trusted**. The kernel (`whnf`, `conv`, `infer`, `check`, recursor generation) re-checks its fully explicit output.
154
+ - For law statements, the elaborator **is** trusted: the kernel checks that a statement is well-formed, not that it says what you wrote.
155
+ - The Python translator **is** trusted, so keep its subset small.
156
+
157
+ ## Limitations
158
+
159
+ - Recursors only eliminate into `Type 0`, so there is no large elimination: you can't prove that constructors differ (`zero ≠ succ n`) or other negative statements.
160
+ - Unary `Nat`: a huge literal like `n + 5000` is rejected as too deep to check.
161
+
162
+ Details and possible ways out: [#9](https://github.com/luajapiassu/minilaws/issues/9).
163
+
164
+ ## Development
165
+
166
+ ```
167
+ python -m venv .venv && .venv/Scripts/pip install -e . pytest # Windows; use .venv/bin on Unix
168
+ .venv/Scripts/python -m pytest
169
+ ```
170
+
171
+ ## References
172
+
173
+ - [OpenAI's Navier-Stokes proof and its Lean 4 formalization](https://explainx.ai/blog/lean-4-formal-proof-cost-collapse-navier-stokes-2026): proof written by AI, checking made cheap by a kernel (17 machine-hours against an estimated 132,800 human-hours). It also names the gap left open: a kernel proves that the proof follows from the statement, not that the statement is the right one. See also the [agent swarm](https://explainx.ai/blog/openai-navier-stokes-solution-agent-swarm-2026) behind it. The result itself is disputed.
174
+ - [Claude's Fermat's Last Theorem Lean proof](https://explainx.ai/blog/anthropic-claude-fermats-last-theorem-lean-proof-2026): closes that gap with a fixed statement, `comparator`, an axiom check, and human review of the statement.
175
+ - [Lean's `comparator`](https://github.com/leanprover/comparator): checks a solution against a challenge file the author controls (identical statements, including dependencies, plus permitted axioms). It's the model for `check --against`.
176
+ - [Bend](https://github.com/HigherOrderCO/Bend): a language with a `LAWS.bend` file (laws a human writes, proofs the AI must keep valid), the same split as `LAWS.laws` and its proofs.
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,165 @@
1
+ # minilaws
2
+
3
+ **Types are statements, programs are proofs, and type checking is proof checking** (Curry-Howard).
4
+ minilaws applies this to AI-edited code, with a dependent-type proof checker in one pure-Python file (stdlib only).
5
+
6
+ You write ordinary Python, an AI is free to edit it, and the laws in `LAWS.laws` must keep holding. If an edit breaks a law, your tests fail.
7
+
8
+ ## The strategy
9
+
10
+ It's the approach of OpenAI's Navier-Stokes announcement, scaled down: let the AI do the work, and make checking that work cheap. There, a [swarm of agents](https://explainx.ai/blog/openai-navier-stokes-solution-agent-swarm-2026) produced the proof, and a Lean 4 formalization of it was checked in [17 hours](https://explainx.ai/blog/lean-4-formal-proof-cost-collapse-navier-stokes-2026). Checking it by hand was estimated at about 132,800 human-hours. Here, the AI writes the code and the proofs, and a small kernel checks them on every test run.
11
+
12
+ A kernel can't check one thing: whether the statement is the right one. For that, minilaws follows the [Lean proof of Fermat's Last Theorem built with Claude](https://explainx.ai/blog/anthropic-claude-fermats-last-theorem-lean-proof-2026). There, the canonical statement was fixed in advance, and Lean's [`comparator`](https://github.com/leanprover/comparator) required the proved theorem to be identical to it. Here, a human owns `LAWS.laws`, and `minilaws check --against origin/main` requires every law to still mean what it meant on `main`.
13
+
14
+ ## Usage
15
+
16
+ ```
17
+ pip install git+https://github.com/luajapiassu/minilaws # not on PyPI yet (https://github.com/luajapiassu/minilaws/issues/1)
18
+ ```
19
+
20
+ 1. Ask your AI to write your app's rules in `LAWS.laws` and to prove them.
21
+ 2. Run `pytest` as usual. Every project with a `.laws` file that pytest collects becomes one test, so a CI that already runs your tests now enforces the laws too.
22
+ 3. Protect the laws in review (see [Who owns the laws](#who-owns-the-laws)).
23
+
24
+ If your pytest config narrows collection (`testpaths`, `--ignore`), make sure it still reaches the `.laws` files, or run `minilaws check` in CI.
25
+
26
+ You can also check without pytest:
27
+
28
+ ```
29
+ minilaws check # current directory
30
+ minilaws check path/to/project
31
+ minilaws check --against origin/main # also: the laws are still the ones on main
32
+ ```
33
+
34
+ No configuration is needed. minilaws checks:
35
+ - every `*.laws` file;
36
+ - every `.py` file that does `from minilaws import Nat`.
37
+
38
+ File order doesn't matter: code is checked first, then laws, then proofs, and within each step a declaration comes after the ones it uses. To pick the files explicitly, add a `minilaws.toml` with `files = [...]`.
39
+
40
+ A folder with its own `LAWS.laws` or `minilaws.toml` is a separate project: the parent's check skips it, and it gets its own test.
41
+
42
+ Delete the `+ 1` in `examples/app.py`. The code still runs, but it's now wrong:
43
+
44
+ ```
45
+ REJECTED: proofs.laws: proof zero_add: type mismatch in 'cong succ ih'
46
+ ```
47
+
48
+ ## Who owns the laws
49
+
50
+ The laws are only worth something if the AI can't change what they say. minilaws closes the routes it can see:
51
+
52
+ - A file that contains `law`s is human-owned. Its laws, `def`s and `inductive`s may use only the prelude, Python code (`.py`) and its own declarations. A law can't depend on something defined in `proofs.laws`, where the AI could change its meaning. This includes `theorem`s in that file, so put helper lemmas in your proofs file ([#7](https://github.com/luajapiassu/minilaws/issues/7)).
53
+ - Python files may only import from `minilaws`, and may only call functions defined earlier in the same file, so the code that's checked is the code that runs.
54
+ - Deleting or renaming `LAWS.laws` leaves its proofs without laws, which fails the check.
55
+
56
+ The other route is changing the laws themselves: weakening one, or deleting a law together with its proof. Inside one version of the code that's invisible, since the weaker laws really are proved. So minilaws does what Lean's [`comparator`](https://github.com/leanprover/comparator) does with a challenge file: it takes the laws from a version the change can't touch and compares.
57
+
58
+ ```
59
+ minilaws check --against origin/main
60
+ ```
61
+
62
+ Every law at `origin/main` must still exist, with the same statement and the same dependencies, taken transitively. A `def` or `inductive` in a laws file must be identical. Code is compared by signature and by the file it comes from, because the code is what's allowed to change. Renaming a binder or editing comments isn't a change, and new laws are fine. Anything else is rejected:
63
+
64
+ ```
65
+ REJECTED: the laws differ from origin/main; a human must approve this:
66
+ law add_assoc: removed
67
+ ```
68
+
69
+ In CI, run it against the PR's base branch. The pytest plugin doesn't take `--against` yet ([#8](https://github.com/luajapiassu/minilaws/issues/8)), so it's a separate step:
70
+
71
+ ```yaml
72
+ - uses: actions/checkout@v4
73
+ with: { fetch-depth: 0 }
74
+ - run: pip install git+https://github.com/luajapiassu/minilaws
75
+ - run: minilaws check --against origin/${{ github.base_ref }}
76
+ ```
77
+
78
+ When a law change is intentional, this step fails by design, and a human has to approve the change. Protect `LAWS.laws` and `minilaws.toml` with `CODEOWNERS` plus branch protection, so that approval comes from someone the AI isn't. Other ways to approve are in [#5](https://github.com/luajapiassu/minilaws/issues/5). For example:
79
+
80
+ ```
81
+ LAWS.laws @you
82
+ minilaws.toml @you
83
+ ```
84
+
85
+ ## Syntax
86
+
87
+ ```
88
+ def name : T := t -- code
89
+ law name : T -- statement (LAWS.laws)
90
+ proof name := t -- proof of a law
91
+ theorem name : T := t -- helper lemma: statement + proof
92
+ inductive List (A : Type 0) : Type 0 where
93
+ | nil : List A
94
+ | cons : A -> List A -> List A -- generates List.rec (= induction on lists)
95
+ {x : A} -> B fun {x} => t -- implicit arguments, inferred by unification
96
+ @f A x -- pass implicit arguments explicitly
97
+ ```
98
+
99
+ The prelude defines `Nat` and `Eq` with `inductive` itself, so the kernel has no special cases.
100
+
101
+ ## Python subset
102
+
103
+ ```python
104
+ from minilaws import Nat
105
+
106
+ def add(n: Nat, m: Nat) -> Nat:
107
+ if m == 0:
108
+ return n
109
+ return add(n, m - 1) + 1
110
+ ```
111
+
112
+ Supported:
113
+ - Functions over `Nat`.
114
+ - Structural recursion `f(..., m - 1, ...)` after `if m == 0`, with the other arguments unchanged.
115
+ - Literals, `x + <int>`, and calls to functions defined earlier in the same file.
116
+ - Imports only from `minilaws`.
117
+
118
+ Anything else is refused with `unsupported Python (...)`, so a checked module holds only these functions: keep other code in modules that import them. Checked modules can't call each other yet ([#6](https://github.com/luajapiassu/minilaws/issues/6)). `Nat = int`, and the laws speak about `n >= 0`.
119
+
120
+ ## Optional: Claude Code plugin
121
+
122
+ This gives faster feedback during a session: Claude sees a broken law right after the edit, not only when the tests run.
123
+
124
+ ```
125
+ /plugin marketplace add luajapiassu/minilaws
126
+ /plugin install minilaws@minilaws
127
+ ```
128
+
129
+ Unlike pytest, the plugin needs a `minilaws.toml` with `files` and `laws`. It blocks edits to the laws files and to `minilaws.toml`, re-checks after every edit, and ships a skill with the usual proof patterns. If the hook itself fails (bad config, missing file), it rejects the edit rather than letting it through.
130
+
131
+ Edits made through Bash aren't watched during the session ([#3](https://github.com/luajapiassu/minilaws/issues/3)). The real enforcement is pytest/CI: `minilaws check --against` catches a changed law however it was edited.
132
+
133
+ ## Why the guarantee holds
134
+
135
+ - Predicative universes (`Type 0 : Type 1`), and constructor fields must fit in `Type 0`, so there's no `Type : Type` paradox.
136
+ - Inductive types must be strictly positive, and a definition can't call itself. Every term terminates, so there are no looping "proofs".
137
+ - An unproved `law` isn't in scope, so it can't be used as a hypothesis.
138
+ - For proofs, the elaborator (implicit arguments) is **not trusted**. The kernel (`whnf`, `conv`, `infer`, `check`, recursor generation) re-checks its fully explicit output.
139
+ - For law statements, the elaborator **is** trusted: the kernel checks that a statement is well-formed, not that it says what you wrote.
140
+ - The Python translator **is** trusted, so keep its subset small.
141
+
142
+ ## Limitations
143
+
144
+ - Recursors only eliminate into `Type 0`, so there is no large elimination: you can't prove that constructors differ (`zero ≠ succ n`) or other negative statements.
145
+ - Unary `Nat`: a huge literal like `n + 5000` is rejected as too deep to check.
146
+
147
+ Details and possible ways out: [#9](https://github.com/luajapiassu/minilaws/issues/9).
148
+
149
+ ## Development
150
+
151
+ ```
152
+ python -m venv .venv && .venv/Scripts/pip install -e . pytest # Windows; use .venv/bin on Unix
153
+ .venv/Scripts/python -m pytest
154
+ ```
155
+
156
+ ## References
157
+
158
+ - [OpenAI's Navier-Stokes proof and its Lean 4 formalization](https://explainx.ai/blog/lean-4-formal-proof-cost-collapse-navier-stokes-2026): proof written by AI, checking made cheap by a kernel (17 machine-hours against an estimated 132,800 human-hours). It also names the gap left open: a kernel proves that the proof follows from the statement, not that the statement is the right one. See also the [agent swarm](https://explainx.ai/blog/openai-navier-stokes-solution-agent-swarm-2026) behind it. The result itself is disputed.
159
+ - [Claude's Fermat's Last Theorem Lean proof](https://explainx.ai/blog/anthropic-claude-fermats-last-theorem-lean-proof-2026): closes that gap with a fixed statement, `comparator`, an axiom check, and human review of the statement.
160
+ - [Lean's `comparator`](https://github.com/leanprover/comparator): checks a solution against a challenge file the author controls (identical statements, including dependencies, plus permitted axioms). It's the model for `check --against`.
161
+ - [Bend](https://github.com/HigherOrderCO/Bend): a language with a `LAWS.bend` file (laws a human writes, proofs the AI must keep valid), the same split as `LAWS.laws` and its proofs.
162
+
163
+ ## License
164
+
165
+ MIT
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: minilaws
3
+ Version: 0.1.0
4
+ Summary: Curry-Howard for AI-edited Python: the AI writes code and proofs, a tiny kernel checks them, the laws stay human-owned
5
+ Author: Lua Japiassu
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/luajapiassu/minilaws
8
+ Keywords: proof-checking,dependent-types,verification,ai-safety,pytest
9
+ Classifier: Framework :: Pytest
10
+ Classifier: Topic :: Software Development :: Testing
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Dynamic: license-file
15
+
16
+ # minilaws
17
+
18
+ **Types are statements, programs are proofs, and type checking is proof checking** (Curry-Howard).
19
+ minilaws applies this to AI-edited code, with a dependent-type proof checker in one pure-Python file (stdlib only).
20
+
21
+ You write ordinary Python, an AI is free to edit it, and the laws in `LAWS.laws` must keep holding. If an edit breaks a law, your tests fail.
22
+
23
+ ## The strategy
24
+
25
+ It's the approach of OpenAI's Navier-Stokes announcement, scaled down: let the AI do the work, and make checking that work cheap. There, a [swarm of agents](https://explainx.ai/blog/openai-navier-stokes-solution-agent-swarm-2026) produced the proof, and a Lean 4 formalization of it was checked in [17 hours](https://explainx.ai/blog/lean-4-formal-proof-cost-collapse-navier-stokes-2026). Checking it by hand was estimated at about 132,800 human-hours. Here, the AI writes the code and the proofs, and a small kernel checks them on every test run.
26
+
27
+ A kernel can't check one thing: whether the statement is the right one. For that, minilaws follows the [Lean proof of Fermat's Last Theorem built with Claude](https://explainx.ai/blog/anthropic-claude-fermats-last-theorem-lean-proof-2026). There, the canonical statement was fixed in advance, and Lean's [`comparator`](https://github.com/leanprover/comparator) required the proved theorem to be identical to it. Here, a human owns `LAWS.laws`, and `minilaws check --against origin/main` requires every law to still mean what it meant on `main`.
28
+
29
+ ## Usage
30
+
31
+ ```
32
+ pip install git+https://github.com/luajapiassu/minilaws # not on PyPI yet (https://github.com/luajapiassu/minilaws/issues/1)
33
+ ```
34
+
35
+ 1. Ask your AI to write your app's rules in `LAWS.laws` and to prove them.
36
+ 2. Run `pytest` as usual. Every project with a `.laws` file that pytest collects becomes one test, so a CI that already runs your tests now enforces the laws too.
37
+ 3. Protect the laws in review (see [Who owns the laws](#who-owns-the-laws)).
38
+
39
+ If your pytest config narrows collection (`testpaths`, `--ignore`), make sure it still reaches the `.laws` files, or run `minilaws check` in CI.
40
+
41
+ You can also check without pytest:
42
+
43
+ ```
44
+ minilaws check # current directory
45
+ minilaws check path/to/project
46
+ minilaws check --against origin/main # also: the laws are still the ones on main
47
+ ```
48
+
49
+ No configuration is needed. minilaws checks:
50
+ - every `*.laws` file;
51
+ - every `.py` file that does `from minilaws import Nat`.
52
+
53
+ File order doesn't matter: code is checked first, then laws, then proofs, and within each step a declaration comes after the ones it uses. To pick the files explicitly, add a `minilaws.toml` with `files = [...]`.
54
+
55
+ A folder with its own `LAWS.laws` or `minilaws.toml` is a separate project: the parent's check skips it, and it gets its own test.
56
+
57
+ Delete the `+ 1` in `examples/app.py`. The code still runs, but it's now wrong:
58
+
59
+ ```
60
+ REJECTED: proofs.laws: proof zero_add: type mismatch in 'cong succ ih'
61
+ ```
62
+
63
+ ## Who owns the laws
64
+
65
+ The laws are only worth something if the AI can't change what they say. minilaws closes the routes it can see:
66
+
67
+ - A file that contains `law`s is human-owned. Its laws, `def`s and `inductive`s may use only the prelude, Python code (`.py`) and its own declarations. A law can't depend on something defined in `proofs.laws`, where the AI could change its meaning. This includes `theorem`s in that file, so put helper lemmas in your proofs file ([#7](https://github.com/luajapiassu/minilaws/issues/7)).
68
+ - Python files may only import from `minilaws`, and may only call functions defined earlier in the same file, so the code that's checked is the code that runs.
69
+ - Deleting or renaming `LAWS.laws` leaves its proofs without laws, which fails the check.
70
+
71
+ The other route is changing the laws themselves: weakening one, or deleting a law together with its proof. Inside one version of the code that's invisible, since the weaker laws really are proved. So minilaws does what Lean's [`comparator`](https://github.com/leanprover/comparator) does with a challenge file: it takes the laws from a version the change can't touch and compares.
72
+
73
+ ```
74
+ minilaws check --against origin/main
75
+ ```
76
+
77
+ Every law at `origin/main` must still exist, with the same statement and the same dependencies, taken transitively. A `def` or `inductive` in a laws file must be identical. Code is compared by signature and by the file it comes from, because the code is what's allowed to change. Renaming a binder or editing comments isn't a change, and new laws are fine. Anything else is rejected:
78
+
79
+ ```
80
+ REJECTED: the laws differ from origin/main; a human must approve this:
81
+ law add_assoc: removed
82
+ ```
83
+
84
+ In CI, run it against the PR's base branch. The pytest plugin doesn't take `--against` yet ([#8](https://github.com/luajapiassu/minilaws/issues/8)), so it's a separate step:
85
+
86
+ ```yaml
87
+ - uses: actions/checkout@v4
88
+ with: { fetch-depth: 0 }
89
+ - run: pip install git+https://github.com/luajapiassu/minilaws
90
+ - run: minilaws check --against origin/${{ github.base_ref }}
91
+ ```
92
+
93
+ When a law change is intentional, this step fails by design, and a human has to approve the change. Protect `LAWS.laws` and `minilaws.toml` with `CODEOWNERS` plus branch protection, so that approval comes from someone the AI isn't. Other ways to approve are in [#5](https://github.com/luajapiassu/minilaws/issues/5). For example:
94
+
95
+ ```
96
+ LAWS.laws @you
97
+ minilaws.toml @you
98
+ ```
99
+
100
+ ## Syntax
101
+
102
+ ```
103
+ def name : T := t -- code
104
+ law name : T -- statement (LAWS.laws)
105
+ proof name := t -- proof of a law
106
+ theorem name : T := t -- helper lemma: statement + proof
107
+ inductive List (A : Type 0) : Type 0 where
108
+ | nil : List A
109
+ | cons : A -> List A -> List A -- generates List.rec (= induction on lists)
110
+ {x : A} -> B fun {x} => t -- implicit arguments, inferred by unification
111
+ @f A x -- pass implicit arguments explicitly
112
+ ```
113
+
114
+ The prelude defines `Nat` and `Eq` with `inductive` itself, so the kernel has no special cases.
115
+
116
+ ## Python subset
117
+
118
+ ```python
119
+ from minilaws import Nat
120
+
121
+ def add(n: Nat, m: Nat) -> Nat:
122
+ if m == 0:
123
+ return n
124
+ return add(n, m - 1) + 1
125
+ ```
126
+
127
+ Supported:
128
+ - Functions over `Nat`.
129
+ - Structural recursion `f(..., m - 1, ...)` after `if m == 0`, with the other arguments unchanged.
130
+ - Literals, `x + <int>`, and calls to functions defined earlier in the same file.
131
+ - Imports only from `minilaws`.
132
+
133
+ Anything else is refused with `unsupported Python (...)`, so a checked module holds only these functions: keep other code in modules that import them. Checked modules can't call each other yet ([#6](https://github.com/luajapiassu/minilaws/issues/6)). `Nat = int`, and the laws speak about `n >= 0`.
134
+
135
+ ## Optional: Claude Code plugin
136
+
137
+ This gives faster feedback during a session: Claude sees a broken law right after the edit, not only when the tests run.
138
+
139
+ ```
140
+ /plugin marketplace add luajapiassu/minilaws
141
+ /plugin install minilaws@minilaws
142
+ ```
143
+
144
+ Unlike pytest, the plugin needs a `minilaws.toml` with `files` and `laws`. It blocks edits to the laws files and to `minilaws.toml`, re-checks after every edit, and ships a skill with the usual proof patterns. If the hook itself fails (bad config, missing file), it rejects the edit rather than letting it through.
145
+
146
+ Edits made through Bash aren't watched during the session ([#3](https://github.com/luajapiassu/minilaws/issues/3)). The real enforcement is pytest/CI: `minilaws check --against` catches a changed law however it was edited.
147
+
148
+ ## Why the guarantee holds
149
+
150
+ - Predicative universes (`Type 0 : Type 1`), and constructor fields must fit in `Type 0`, so there's no `Type : Type` paradox.
151
+ - Inductive types must be strictly positive, and a definition can't call itself. Every term terminates, so there are no looping "proofs".
152
+ - An unproved `law` isn't in scope, so it can't be used as a hypothesis.
153
+ - For proofs, the elaborator (implicit arguments) is **not trusted**. The kernel (`whnf`, `conv`, `infer`, `check`, recursor generation) re-checks its fully explicit output.
154
+ - For law statements, the elaborator **is** trusted: the kernel checks that a statement is well-formed, not that it says what you wrote.
155
+ - The Python translator **is** trusted, so keep its subset small.
156
+
157
+ ## Limitations
158
+
159
+ - Recursors only eliminate into `Type 0`, so there is no large elimination: you can't prove that constructors differ (`zero ≠ succ n`) or other negative statements.
160
+ - Unary `Nat`: a huge literal like `n + 5000` is rejected as too deep to check.
161
+
162
+ Details and possible ways out: [#9](https://github.com/luajapiassu/minilaws/issues/9).
163
+
164
+ ## Development
165
+
166
+ ```
167
+ python -m venv .venv && .venv/Scripts/pip install -e . pytest # Windows; use .venv/bin on Unix
168
+ .venv/Scripts/python -m pytest
169
+ ```
170
+
171
+ ## References
172
+
173
+ - [OpenAI's Navier-Stokes proof and its Lean 4 formalization](https://explainx.ai/blog/lean-4-formal-proof-cost-collapse-navier-stokes-2026): proof written by AI, checking made cheap by a kernel (17 machine-hours against an estimated 132,800 human-hours). It also names the gap left open: a kernel proves that the proof follows from the statement, not that the statement is the right one. See also the [agent swarm](https://explainx.ai/blog/openai-navier-stokes-solution-agent-swarm-2026) behind it. The result itself is disputed.
174
+ - [Claude's Fermat's Last Theorem Lean proof](https://explainx.ai/blog/anthropic-claude-fermats-last-theorem-lean-proof-2026): closes that gap with a fixed statement, `comparator`, an axiom check, and human review of the statement.
175
+ - [Lean's `comparator`](https://github.com/leanprover/comparator): checks a solution against a challenge file the author controls (identical statements, including dependencies, plus permitted axioms). It's the model for `check --against`.
176
+ - [Bend](https://github.com/HigherOrderCO/Bend): a language with a `LAWS.bend` file (laws a human writes, proofs the AI must keep valid), the same split as `LAWS.laws` and its proofs.
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ minilaws.py
4
+ pyproject.toml
5
+ pytest_minilaws.py
6
+ minilaws.egg-info/PKG-INFO
7
+ minilaws.egg-info/SOURCES.txt
8
+ minilaws.egg-info/dependency_links.txt
9
+ minilaws.egg-info/entry_points.txt
10
+ minilaws.egg-info/top_level.txt
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ minilaws = minilaws:cli
3
+
4
+ [pytest11]
5
+ minilaws = pytest_minilaws
@@ -0,0 +1,2 @@
1
+ minilaws
2
+ pytest_minilaws