lean-lockstep 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.
- lean_lockstep-0.1.0/CHANGELOG.md +22 -0
- lean_lockstep-0.1.0/LICENSE +22 -0
- lean_lockstep-0.1.0/MANIFEST.in +10 -0
- lean_lockstep-0.1.0/PKG-INFO +218 -0
- lean_lockstep-0.1.0/README.md +185 -0
- lean_lockstep-0.1.0/bin/lockstep +7 -0
- lean_lockstep-0.1.0/bin/lockstep-mcp +8 -0
- lean_lockstep-0.1.0/docs/automation.md +130 -0
- lean_lockstep-0.1.0/examples/minimal/.gitignore +2 -0
- lean_lockstep-0.1.0/examples/minimal/AGENTS.md +7 -0
- lean_lockstep-0.1.0/examples/minimal/Demo/Basic.lean +6 -0
- lean_lockstep-0.1.0/examples/minimal/Demo.lean +1 -0
- lean_lockstep-0.1.0/examples/minimal/DemoBlueprint.lean +2 -0
- lean_lockstep-0.1.0/examples/minimal/harness/allowlist.json +1 -0
- lean_lockstep-0.1.0/examples/minimal/harness/config.toml +48 -0
- lean_lockstep-0.1.0/examples/minimal/harness/destinations.txt +2 -0
- lean_lockstep-0.1.0/examples/minimal/harness/statements.lock +1 -0
- lean_lockstep-0.1.0/examples/minimal/lakefile.toml +13 -0
- lean_lockstep-0.1.0/examples/minimal/lean-toolchain +1 -0
- lean_lockstep-0.1.0/lean_lockstep.egg-info/PKG-INFO +218 -0
- lean_lockstep-0.1.0/lean_lockstep.egg-info/SOURCES.txt +118 -0
- lean_lockstep-0.1.0/lean_lockstep.egg-info/dependency_links.txt +1 -0
- lean_lockstep-0.1.0/lean_lockstep.egg-info/entry_points.txt +3 -0
- lean_lockstep-0.1.0/lean_lockstep.egg-info/requires.txt +9 -0
- lean_lockstep-0.1.0/lean_lockstep.egg-info/top_level.txt +1 -0
- lean_lockstep-0.1.0/lockstep/__init__.py +17 -0
- lean_lockstep-0.1.0/lockstep/__main__.py +3 -0
- lean_lockstep-0.1.0/lockstep/allowlist.py +40 -0
- lean_lockstep-0.1.0/lockstep/artifact_cache.py +515 -0
- lean_lockstep-0.1.0/lockstep/artifacts.py +185 -0
- lean_lockstep-0.1.0/lockstep/automation_schema.py +30 -0
- lean_lockstep-0.1.0/lockstep/build.py +468 -0
- lean_lockstep-0.1.0/lockstep/bundles.py +37 -0
- lean_lockstep-0.1.0/lockstep/cli.py +148 -0
- lean_lockstep-0.1.0/lockstep/commands/__init__.py +28 -0
- lean_lockstep-0.1.0/lockstep/commands/audit.py +46 -0
- lean_lockstep-0.1.0/lockstep/commands/automation.py +84 -0
- lean_lockstep-0.1.0/lockstep/commands/build.py +61 -0
- lean_lockstep-0.1.0/lockstep/commands/cache.py +58 -0
- lean_lockstep-0.1.0/lockstep/commands/check.py +81 -0
- lean_lockstep-0.1.0/lockstep/commands/claims.py +62 -0
- lean_lockstep-0.1.0/lockstep/commands/close.py +193 -0
- lean_lockstep-0.1.0/lockstep/commands/doctor.py +86 -0
- lean_lockstep-0.1.0/lockstep/commands/extract.py +169 -0
- lean_lockstep-0.1.0/lockstep/commands/falsify.py +108 -0
- lean_lockstep-0.1.0/lockstep/commands/index.py +16 -0
- lean_lockstep-0.1.0/lockstep/commands/jobs.py +188 -0
- lean_lockstep-0.1.0/lockstep/commands/layers.py +52 -0
- lean_lockstep-0.1.0/lockstep/commands/library.py +67 -0
- lean_lockstep-0.1.0/lockstep/commands/lock.py +58 -0
- lean_lockstep-0.1.0/lockstep/commands/log.py +132 -0
- lean_lockstep-0.1.0/lockstep/commands/node.py +429 -0
- lean_lockstep-0.1.0/lockstep/commands/note.py +76 -0
- lean_lockstep-0.1.0/lockstep/commands/probe.py +11 -0
- lean_lockstep-0.1.0/lockstep/commands/proof.py +31 -0
- lean_lockstep-0.1.0/lockstep/commands/ready.py +48 -0
- lean_lockstep-0.1.0/lockstep/commands/rules.py +61 -0
- lean_lockstep-0.1.0/lockstep/commands/search.py +37 -0
- lean_lockstep-0.1.0/lockstep/commands/status.py +172 -0
- lean_lockstep-0.1.0/lockstep/config.py +489 -0
- lean_lockstep-0.1.0/lockstep/context.py +33 -0
- lean_lockstep-0.1.0/lockstep/data/AuditCandidate.lean +18 -0
- lean_lockstep-0.1.0/lockstep/data/CacheArtifacts.lean +37 -0
- lean_lockstep-0.1.0/lockstep/data/Extract.lean.tmpl +362 -0
- lean_lockstep-0.1.0/lockstep/data/node.lean.tmpl +9 -0
- lean_lockstep-0.1.0/lockstep/delivery.py +45 -0
- lean_lockstep-0.1.0/lockstep/errors.py +28 -0
- lean_lockstep-0.1.0/lockstep/finalize.py +102 -0
- lean_lockstep-0.1.0/lockstep/gitutil.py +90 -0
- lean_lockstep-0.1.0/lockstep/hooks.py +143 -0
- lean_lockstep-0.1.0/lockstep/jobs.py +349 -0
- lean_lockstep-0.1.0/lockstep/layers.py +156 -0
- lean_lockstep-0.1.0/lockstep/lean.py +107 -0
- lean_lockstep-0.1.0/lockstep/library.py +188 -0
- lean_lockstep-0.1.0/lockstep/lock.py +68 -0
- lean_lockstep-0.1.0/lockstep/mcp_server.py +84 -0
- lean_lockstep-0.1.0/lockstep/metrics.py +12 -0
- lean_lockstep-0.1.0/lockstep/modules.py +197 -0
- lean_lockstep-0.1.0/lockstep/nodes.py +475 -0
- lean_lockstep-0.1.0/lockstep/operations.py +265 -0
- lean_lockstep-0.1.0/lockstep/output.py +45 -0
- lean_lockstep-0.1.0/lockstep/probes.py +72 -0
- lean_lockstep-0.1.0/lockstep/proofs.py +250 -0
- lean_lockstep-0.1.0/lockstep/resources.py +100 -0
- lean_lockstep-0.1.0/lockstep/shadow_index.py +47 -0
- lean_lockstep-0.1.0/lockstep/spec.py +36 -0
- lean_lockstep-0.1.0/lockstep/stage_runner.py +47 -0
- lean_lockstep-0.1.0/lockstep/storage.py +186 -0
- lean_lockstep-0.1.0/lockstep/store.py +338 -0
- lean_lockstep-0.1.0/lockstep/tags.py +84 -0
- lean_lockstep-0.1.0/pyproject.toml +56 -0
- lean_lockstep-0.1.0/setup.cfg +4 -0
- lean_lockstep-0.1.0/systemd/install.sh +21 -0
- lean_lockstep-0.1.0/systemd/lockstep-nightly.service +11 -0
- lean_lockstep-0.1.0/systemd/lockstep-nightly.timer +10 -0
- lean_lockstep-0.1.0/tests/conftest.py +103 -0
- lean_lockstep-0.1.0/tests/lean_automation_smoke.py +101 -0
- lean_lockstep-0.1.0/tests/test_artifact_boundaries.py +141 -0
- lean_lockstep-0.1.0/tests/test_artifact_cache.py +372 -0
- lean_lockstep-0.1.0/tests/test_audit_trail.py +292 -0
- lean_lockstep-0.1.0/tests/test_automation.py +95 -0
- lean_lockstep-0.1.0/tests/test_automation_boundaries.py +474 -0
- lean_lockstep-0.1.0/tests/test_cache_key_and_reservations.py +144 -0
- lean_lockstep-0.1.0/tests/test_cli.py +92 -0
- lean_lockstep-0.1.0/tests/test_close_and_note.py +50 -0
- lean_lockstep-0.1.0/tests/test_example_lifecycle.py +71 -0
- lean_lockstep-0.1.0/tests/test_hooks.py +85 -0
- lean_lockstep-0.1.0/tests/test_job_wait.py +84 -0
- lean_lockstep-0.1.0/tests/test_layers.py +84 -0
- lean_lockstep-0.1.0/tests/test_library.py +155 -0
- lean_lockstep-0.1.0/tests/test_node_add_names.py +92 -0
- lean_lockstep-0.1.0/tests/test_nodes.py +184 -0
- lean_lockstep-0.1.0/tests/test_rules.py +42 -0
- lean_lockstep-0.1.0/tests/test_shadow_index.py +28 -0
- lean_lockstep-0.1.0/tests/test_status_freshness.py +23 -0
- lean_lockstep-0.1.0/tests/test_storage.py +143 -0
- lean_lockstep-0.1.0/tests/test_store_and_jobs.py +66 -0
- lean_lockstep-0.1.0/tests/test_tags.py +49 -0
- lean_lockstep-0.1.0/tests/test_targeted_commands.py +141 -0
- lean_lockstep-0.1.0/tests/test_tool_resolution.py +32 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes are listed here. Versions follow [semantic versioning](https://semver.org);
|
|
4
|
+
before 1.0, a minor version may break compatibility.
|
|
5
|
+
|
|
6
|
+
## 0.1.0 (2026-10-18)
|
|
7
|
+
|
|
8
|
+
First public release. Extracted from the harness of two Lean research projects and generalized.
|
|
9
|
+
|
|
10
|
+
- State derived from the compiled environment: `lockstep extract` reports every blueprint node as
|
|
11
|
+
open, claimed or closed; `status`, `ready`, `node show`, `search`.
|
|
12
|
+
- Locked statements: `harness/statements.lock`, `node add`, `node restate`, `node retire`,
|
|
13
|
+
`lock refresh`, and `python -m lockstep.lock` for CI.
|
|
14
|
+
- Coordination: claims with intent and outcome, `log`, `journal`, `note`.
|
|
15
|
+
- Memory-guarded builds and jobs: `build`, `check`, `job run`, `job wait`, `worker`, under a shared
|
|
16
|
+
budget.
|
|
17
|
+
- Integrity: a cached per-module axiom audit, import-layer checks, `rules` (agent rule files in
|
|
18
|
+
step), and `hook pre-commit` for git.
|
|
19
|
+
- Client integration: an MCP server (`lockstep-mcp`) exposing every command; Claude Code hooks
|
|
20
|
+
(`hook session-start`, `hook pre-bash`).
|
|
21
|
+
- Experimental: durable operations, proof capture/check/prove, `node finish`, finite probes,
|
|
22
|
+
`context`, the research-library catalog, certificate layers and `storage share`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gavin Litchfield
|
|
4
|
+
Copyright (c) 2026 Matt Pocock (Wayfinder-derived portions)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lean-lockstep
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Coordinate several agents on a Lean 4 formalization: state derived from the compiled environment, locked statements, recorded claims and memory-guarded jobs
|
|
5
|
+
Author: Gavin Litchfield
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/GLitchfield/lean-lockstep
|
|
8
|
+
Project-URL: Repository, https://github.com/GLitchfield/lean-lockstep
|
|
9
|
+
Project-URL: Issues, https://github.com/GLitchfield/lean-lockstep/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/GLitchfield/lean-lockstep/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: lean4,formalization,theorem-proving,blueprint,ai-agents,mcp
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: mcp
|
|
27
|
+
Requires-Dist: mcp<3,>=2; extra == "mcp"
|
|
28
|
+
Provides-Extra: test
|
|
29
|
+
Requires-Dist: pytest; extra == "test"
|
|
30
|
+
Provides-Extra: research
|
|
31
|
+
Requires-Dist: numpy; extra == "research"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# lean-lockstep
|
|
35
|
+
|
|
36
|
+
A project harness for Lean 4 formalization work done by several agents at once: AI coding agents
|
|
37
|
+
of any kind (anything that can run a shell command or call an MCP tool) and people. It keeps the
|
|
38
|
+
state of the project (which statements are open, claimed, proved, and what depends on what)
|
|
39
|
+
derived from the compiled Lean environment, and it records who is doing what, why, and with how
|
|
40
|
+
much memory.
|
|
41
|
+
|
|
42
|
+
- **State comes from Lean, not from a document.** Each open statement is a *node*: a theorem
|
|
43
|
+
with a `sorry` in its own blueprint module, tagged in its docstring. `lockstep extract` reads the
|
|
44
|
+
compiled environment and reports each node as open, claimed or closed (sorry-free with
|
|
45
|
+
allowlisted axioms). There is no hand-kept status file that can drift.
|
|
46
|
+
- **Approved statements are locked.** `harness/statements.lock` records the type hash of every
|
|
47
|
+
approved statement. A proof that quietly weakens its statement shows up as a lock violation;
|
|
48
|
+
changing a statement takes `lockstep node restate`, which logs who approved it and why. This is the
|
|
49
|
+
failure mode that matters most when agents write the proofs.
|
|
50
|
+
- **Agents coordinate through the harness.** Claims expire and carry the intent (`--why`); every
|
|
51
|
+
release records an outcome; `lockstep log` and `lockstep journal` keep the trail; long commands run as
|
|
52
|
+
recorded, memory-capped jobs that every agent can see.
|
|
53
|
+
- **Audits scale.** The axiom audit is a per-module taint scan with a cache, which finishes on
|
|
54
|
+
projects where `#print axioms` does not.
|
|
55
|
+
- **No client or model is assumed.** Every command has a CLI and an MCP tool. A git pre-commit
|
|
56
|
+
hook guards commits for everyone; optional Claude Code hooks add a session banner and a guard
|
|
57
|
+
against compute launched outside the harness.
|
|
58
|
+
|
|
59
|
+
Status: experimental (0.1). Built and used in two research projects; expect rough edges outside
|
|
60
|
+
them. Linux first.
|
|
61
|
+
|
|
62
|
+
## Requirements
|
|
63
|
+
|
|
64
|
+
- Python 3.11 or newer. The core has no dependencies; extras: `mcp` (MCP server), `test`
|
|
65
|
+
(pytest), `research` (NumPy for research-library probes).
|
|
66
|
+
- Lean 4 through [elan](https://github.com/leanprover/elan), Lake, and git.
|
|
67
|
+
- The memory guard runs each `lake` invocation in a capped `systemd-run --user` scope, so it
|
|
68
|
+
needs Linux with a systemd user session. Elsewhere set `[build] guard = false`.
|
|
69
|
+
- `bwrap` (bubblewrap) for the sandboxed proof checks of the experimental automation layer.
|
|
70
|
+
|
|
71
|
+
## Try it
|
|
72
|
+
|
|
73
|
+
`examples/minimal` is a Lean-only Lake project (no Mathlib) with one core lemma and an empty
|
|
74
|
+
blueprint. From a checkout of this repository:
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
cd examples/minimal
|
|
78
|
+
export LOCKSTEP_AGENT=me:demo # every claim, job and log entry is recorded under this id
|
|
79
|
+
../../bin/lockstep build core
|
|
80
|
+
../../bin/lockstep extract # writes harness/nodes.json, index.json, status.md
|
|
81
|
+
../../bin/lockstep node add --name double_succ --ws A --kind formalization \
|
|
82
|
+
--type "(n : Nat) : (n + 1) + (n + 1) = 2 * n + 2" \
|
|
83
|
+
--description "Doubling a successor." --depends double --dangling-ok "example"
|
|
84
|
+
../../bin/lockstep ready # Demo.double_succ is open and ready
|
|
85
|
+
../../bin/lockstep claim double_succ --why "rewrite with Demo.double, then omega"
|
|
86
|
+
# replace the `sorry` in DemoBlueprint/A/DoubleSucc.lean with `rw [double]; omega`
|
|
87
|
+
../../bin/lockstep check double_succ # sorry=False status=closed
|
|
88
|
+
../../bin/lockstep close double_succ # moves it into the core library, records the attempt
|
|
89
|
+
../../bin/lockstep audit && ../../bin/lockstep journal
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`tests/test_example_lifecycle.py` runs the same sequence whenever `lake` is installed. To start
|
|
93
|
+
over, run `git checkout -- . && git clean -fdx .` in `examples/minimal`.
|
|
94
|
+
|
|
95
|
+
## Installing
|
|
96
|
+
|
|
97
|
+
The harness writes Lean files into the project (the extractor, node modules) and its lock hashes
|
|
98
|
+
depend on the toolchain, so pin the version a project uses. Either:
|
|
99
|
+
|
|
100
|
+
- **From PyPI**, which puts `lockstep` and `lockstep-mcp` on the `PATH`:
|
|
101
|
+
```console
|
|
102
|
+
uv tool install "lean-lockstep[mcp]==0.1.0" # or: pipx install "lean-lockstep[mcp]==0.1.0"
|
|
103
|
+
```
|
|
104
|
+
Without PyPI access, install a tag from git instead:
|
|
105
|
+
`uv tool install "lean-lockstep[mcp] @ git+https://github.com/GLitchfield/lean-lockstep@v0.1.0"`.
|
|
106
|
+
- **As a git submodule**, pinned by commit and upgraded by bumping the pin, which suits a project
|
|
107
|
+
that also develops the harness:
|
|
108
|
+
```console
|
|
109
|
+
git submodule add https://github.com/GLitchfield/lean-lockstep tools/lockstep
|
|
110
|
+
uv venv tools/lockstep/.venv && uv pip install -e "tools/lockstep[mcp]"
|
|
111
|
+
```
|
|
112
|
+
`tools/lockstep/bin/lockstep` and `tools/lockstep/bin/lockstep-mcp` prefer that virtualenv and
|
|
113
|
+
fall back to the system `python3`.
|
|
114
|
+
|
|
115
|
+
The commands below write `lockstep`; with a submodule, that is `tools/lockstep/bin/lockstep`.
|
|
116
|
+
|
|
117
|
+
## Adopting it in a project
|
|
118
|
+
|
|
119
|
+
1. Give the Lake package two libraries: a core library (proved results only) and a blueprint
|
|
120
|
+
library whose root file imports every node module. `lockstep node add` maintains that root.
|
|
121
|
+
2. Copy `examples/minimal/harness/` and edit `harness/config.toml`: namespaces, layers, build
|
|
122
|
+
targets, memory caps. Start `statements.lock` as `{}` and `allowlist.json` as `[]`.
|
|
123
|
+
3. Ignore the harness state directory (`.lockstep/` with `[project] state_home = ".lockstep"`).
|
|
124
|
+
4. Write the agent rules into the files your clients read (`AGENTS.md`, `CLAUDE.md`, ...) and
|
|
125
|
+
list them under `[rules]`; `lockstep rules` checks that they carry the same rule list (cheap enough
|
|
126
|
+
for CI). `examples/minimal/AGENTS.md` is a starting point. Tell each agent to export a stable
|
|
127
|
+
`LOCKSTEP_AGENT` (for example `<client>:<session id>`); claims and logs are recorded under it.
|
|
128
|
+
5. Guard commits with the git hook, which works whoever commits:
|
|
129
|
+
```console
|
|
130
|
+
printf '#!/bin/sh\nexec lockstep hook pre-commit\n' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
|
|
131
|
+
```
|
|
132
|
+
It refuses a commit that fails `lockstep layers`, `lockstep extract --check` (generated files out of date
|
|
133
|
+
or a locked statement changed) or `lockstep audit --changed`.
|
|
134
|
+
6. Register the MCP server with your client: command `lockstep-mcp` (or `tools/lockstep/bin/lockstep-mcp`), stdio,
|
|
135
|
+
no arguments. For example `.mcp.json`:
|
|
136
|
+
```json
|
|
137
|
+
{ "mcpServers": { "lockstep": { "command": "tools/lockstep/bin/lockstep-mcp", "args": [] } } }
|
|
138
|
+
```
|
|
139
|
+
7. Optionally, for Claude Code, add the session and shell hooks to `.claude/settings.json`:
|
|
140
|
+
```json
|
|
141
|
+
{ "hooks": {
|
|
142
|
+
"SessionStart": [{ "hooks": [{ "type": "command", "command": "tools/lockstep/bin/lockstep hook session-start" }] }],
|
|
143
|
+
"PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "tools/lockstep/bin/lockstep hook pre-bash" }] }] } }
|
|
144
|
+
```
|
|
145
|
+
`session-start` prints the session's agent id and `lockstep status --brief`. `pre-bash` blocks a
|
|
146
|
+
bare `nohup`/`systemd-run` (compute goes through `lockstep job run`) and runs the commit guard
|
|
147
|
+
before `git commit`/`git push`. Any client whose hooks pass the same JSON on stdin can use them.
|
|
148
|
+
8. In CI, `python3 -m lockstep.lock BASE_LOCK HEAD_LOCK` (with `PYTHONPATH=tools/lockstep` for a submodule)
|
|
149
|
+
prints the log files a lock change must touch: `restatements.md` when an approved statement
|
|
150
|
+
changed or disappeared, `retired.md` for a refutation. New entries need none.
|
|
151
|
+
9. Optionally, `tools/lockstep/systemd/install.sh` (from a checkout) installs a `lockstep-nightly-<project
|
|
152
|
+
id>` user timer (03:00) that runs the guarded core build, `lockstep extract --check` and `lockstep audit`.
|
|
153
|
+
|
|
154
|
+
## Commands
|
|
155
|
+
|
|
156
|
+
| Area | Commands |
|
|
157
|
+
|---|---|
|
|
158
|
+
| State | `status`, `ready`, `node show`, `search`, `extract`, `extract --check`, `doctor` |
|
|
159
|
+
| Statements | `node add`, `node restate`, `node retire`, `lock refresh` (after a toolchain bump) |
|
|
160
|
+
| Work | `claim`, `release`, `check`, `close`, `falsify` (conjecture nodes) |
|
|
161
|
+
| Builds and jobs | `build core` / `blueprint` / `node`, `job run`, `job wait`, `job list`, `worker` |
|
|
162
|
+
| Trail | `log`, `journal`, `note` (weekly summary) |
|
|
163
|
+
| Integrity | `audit`, `layers`, `rules`, `hook pre-commit` |
|
|
164
|
+
| Client hooks | `hook session-start`, `hook pre-bash` |
|
|
165
|
+
|
|
166
|
+
Every command takes `--json`, and all but the hooks are also MCP tools. Node names may be
|
|
167
|
+
dotted relative to the declaration namespace (`--name List.foo` declares `theorem List.foo` in
|
|
168
|
+
it), and `--depends` lists the project declarations a node waits on (library lemmas belong in
|
|
169
|
+
the description).
|
|
170
|
+
|
|
171
|
+
### Experimental
|
|
172
|
+
|
|
173
|
+
These work in the projects they came from but are less general; see
|
|
174
|
+
[`docs/automation.md`](docs/automation.md):
|
|
175
|
+
|
|
176
|
+
- **Automation layer**: durable `operation …` workflows run by `lockstep worker`, a results `inbox`,
|
|
177
|
+
`proof capture|check|prove`, `node finish`, `probe run|replay`, `context`.
|
|
178
|
+
- **Research library**: `lib search|show|list` over a project catalog
|
|
179
|
+
(`harness/research/catalog.json`).
|
|
180
|
+
- **Certificate layers**: `[layers]` rules for generated proof-module families, and
|
|
181
|
+
`storage share` for their compiled artifacts.
|
|
182
|
+
|
|
183
|
+
## Things that cost hours to learn
|
|
184
|
+
|
|
185
|
+
- **Never `import` the project modules at the top of a `lake env lean --run` script that also
|
|
186
|
+
calls `importModules` on them.** The script's own import maps the oleans; the second load
|
|
187
|
+
cannot mmap at the same addresses and reads them into anonymous memory instead (37 GB and a
|
|
188
|
+
cgroup OOM kill on a project with large certificate modules). The extractor template imports
|
|
189
|
+
only `Lean`; `main` imports the project.
|
|
190
|
+
- **Never use `importAll := true`** for the extractor environment (30+ GB). A plain import
|
|
191
|
+
exposes every theorem value in `env.header.moduleData`.
|
|
192
|
+
- **Never run bare `lake build` on big targets.** Lake 5 has no `-j`; two dozen parallel
|
|
193
|
+
compilers exhaust a 20 GB guard. `lockstep build` builds Lake's out-of-date list one module per
|
|
194
|
+
guarded invocation first.
|
|
195
|
+
- **`#print axioms` does not finish on large generated proof families** (16 TB read in 3.5 h);
|
|
196
|
+
the extractor's module-order taint scan with a per-module cache is the audit path.
|
|
197
|
+
- **A module-system core root (`module` header) cannot import plain files.** When the core root
|
|
198
|
+
uses it, nodes are written in module form from the start, so a proof elaborates against the
|
|
199
|
+
same imports before and after `lockstep close`.
|
|
200
|
+
- **The elaborator moves parts of a proof into auxiliary declarations** (`foo._proof_1`), so a
|
|
201
|
+
dependency scan of a theorem's value alone reports the auxiliaries instead of the lemmas they
|
|
202
|
+
use. The extractor looks through a declaration's own auxiliaries.
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
```console
|
|
207
|
+
uv venv .venv && uv pip install -e ".[test,mcp,research]"
|
|
208
|
+
.venv/bin/python -m pytest
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Releases follow semantic versioning (0.x: minor versions may break). Changes are listed in
|
|
212
|
+
[`CHANGELOG.md`](CHANGELOG.md). To release, bump `__version__` in `lockstep/__init__.py`, add the
|
|
213
|
+
changelog entry, and push a `vX.Y.Z` tag: `.github/workflows/release.yml` builds the sdist and
|
|
214
|
+
wheel and publishes them to PyPI through trusted publishing.
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT; see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# lean-lockstep
|
|
2
|
+
|
|
3
|
+
A project harness for Lean 4 formalization work done by several agents at once: AI coding agents
|
|
4
|
+
of any kind (anything that can run a shell command or call an MCP tool) and people. It keeps the
|
|
5
|
+
state of the project (which statements are open, claimed, proved, and what depends on what)
|
|
6
|
+
derived from the compiled Lean environment, and it records who is doing what, why, and with how
|
|
7
|
+
much memory.
|
|
8
|
+
|
|
9
|
+
- **State comes from Lean, not from a document.** Each open statement is a *node*: a theorem
|
|
10
|
+
with a `sorry` in its own blueprint module, tagged in its docstring. `lockstep extract` reads the
|
|
11
|
+
compiled environment and reports each node as open, claimed or closed (sorry-free with
|
|
12
|
+
allowlisted axioms). There is no hand-kept status file that can drift.
|
|
13
|
+
- **Approved statements are locked.** `harness/statements.lock` records the type hash of every
|
|
14
|
+
approved statement. A proof that quietly weakens its statement shows up as a lock violation;
|
|
15
|
+
changing a statement takes `lockstep node restate`, which logs who approved it and why. This is the
|
|
16
|
+
failure mode that matters most when agents write the proofs.
|
|
17
|
+
- **Agents coordinate through the harness.** Claims expire and carry the intent (`--why`); every
|
|
18
|
+
release records an outcome; `lockstep log` and `lockstep journal` keep the trail; long commands run as
|
|
19
|
+
recorded, memory-capped jobs that every agent can see.
|
|
20
|
+
- **Audits scale.** The axiom audit is a per-module taint scan with a cache, which finishes on
|
|
21
|
+
projects where `#print axioms` does not.
|
|
22
|
+
- **No client or model is assumed.** Every command has a CLI and an MCP tool. A git pre-commit
|
|
23
|
+
hook guards commits for everyone; optional Claude Code hooks add a session banner and a guard
|
|
24
|
+
against compute launched outside the harness.
|
|
25
|
+
|
|
26
|
+
Status: experimental (0.1). Built and used in two research projects; expect rough edges outside
|
|
27
|
+
them. Linux first.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- Python 3.11 or newer. The core has no dependencies; extras: `mcp` (MCP server), `test`
|
|
32
|
+
(pytest), `research` (NumPy for research-library probes).
|
|
33
|
+
- Lean 4 through [elan](https://github.com/leanprover/elan), Lake, and git.
|
|
34
|
+
- The memory guard runs each `lake` invocation in a capped `systemd-run --user` scope, so it
|
|
35
|
+
needs Linux with a systemd user session. Elsewhere set `[build] guard = false`.
|
|
36
|
+
- `bwrap` (bubblewrap) for the sandboxed proof checks of the experimental automation layer.
|
|
37
|
+
|
|
38
|
+
## Try it
|
|
39
|
+
|
|
40
|
+
`examples/minimal` is a Lean-only Lake project (no Mathlib) with one core lemma and an empty
|
|
41
|
+
blueprint. From a checkout of this repository:
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
cd examples/minimal
|
|
45
|
+
export LOCKSTEP_AGENT=me:demo # every claim, job and log entry is recorded under this id
|
|
46
|
+
../../bin/lockstep build core
|
|
47
|
+
../../bin/lockstep extract # writes harness/nodes.json, index.json, status.md
|
|
48
|
+
../../bin/lockstep node add --name double_succ --ws A --kind formalization \
|
|
49
|
+
--type "(n : Nat) : (n + 1) + (n + 1) = 2 * n + 2" \
|
|
50
|
+
--description "Doubling a successor." --depends double --dangling-ok "example"
|
|
51
|
+
../../bin/lockstep ready # Demo.double_succ is open and ready
|
|
52
|
+
../../bin/lockstep claim double_succ --why "rewrite with Demo.double, then omega"
|
|
53
|
+
# replace the `sorry` in DemoBlueprint/A/DoubleSucc.lean with `rw [double]; omega`
|
|
54
|
+
../../bin/lockstep check double_succ # sorry=False status=closed
|
|
55
|
+
../../bin/lockstep close double_succ # moves it into the core library, records the attempt
|
|
56
|
+
../../bin/lockstep audit && ../../bin/lockstep journal
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`tests/test_example_lifecycle.py` runs the same sequence whenever `lake` is installed. To start
|
|
60
|
+
over, run `git checkout -- . && git clean -fdx .` in `examples/minimal`.
|
|
61
|
+
|
|
62
|
+
## Installing
|
|
63
|
+
|
|
64
|
+
The harness writes Lean files into the project (the extractor, node modules) and its lock hashes
|
|
65
|
+
depend on the toolchain, so pin the version a project uses. Either:
|
|
66
|
+
|
|
67
|
+
- **From PyPI**, which puts `lockstep` and `lockstep-mcp` on the `PATH`:
|
|
68
|
+
```console
|
|
69
|
+
uv tool install "lean-lockstep[mcp]==0.1.0" # or: pipx install "lean-lockstep[mcp]==0.1.0"
|
|
70
|
+
```
|
|
71
|
+
Without PyPI access, install a tag from git instead:
|
|
72
|
+
`uv tool install "lean-lockstep[mcp] @ git+https://github.com/GLitchfield/lean-lockstep@v0.1.0"`.
|
|
73
|
+
- **As a git submodule**, pinned by commit and upgraded by bumping the pin, which suits a project
|
|
74
|
+
that also develops the harness:
|
|
75
|
+
```console
|
|
76
|
+
git submodule add https://github.com/GLitchfield/lean-lockstep tools/lockstep
|
|
77
|
+
uv venv tools/lockstep/.venv && uv pip install -e "tools/lockstep[mcp]"
|
|
78
|
+
```
|
|
79
|
+
`tools/lockstep/bin/lockstep` and `tools/lockstep/bin/lockstep-mcp` prefer that virtualenv and
|
|
80
|
+
fall back to the system `python3`.
|
|
81
|
+
|
|
82
|
+
The commands below write `lockstep`; with a submodule, that is `tools/lockstep/bin/lockstep`.
|
|
83
|
+
|
|
84
|
+
## Adopting it in a project
|
|
85
|
+
|
|
86
|
+
1. Give the Lake package two libraries: a core library (proved results only) and a blueprint
|
|
87
|
+
library whose root file imports every node module. `lockstep node add` maintains that root.
|
|
88
|
+
2. Copy `examples/minimal/harness/` and edit `harness/config.toml`: namespaces, layers, build
|
|
89
|
+
targets, memory caps. Start `statements.lock` as `{}` and `allowlist.json` as `[]`.
|
|
90
|
+
3. Ignore the harness state directory (`.lockstep/` with `[project] state_home = ".lockstep"`).
|
|
91
|
+
4. Write the agent rules into the files your clients read (`AGENTS.md`, `CLAUDE.md`, ...) and
|
|
92
|
+
list them under `[rules]`; `lockstep rules` checks that they carry the same rule list (cheap enough
|
|
93
|
+
for CI). `examples/minimal/AGENTS.md` is a starting point. Tell each agent to export a stable
|
|
94
|
+
`LOCKSTEP_AGENT` (for example `<client>:<session id>`); claims and logs are recorded under it.
|
|
95
|
+
5. Guard commits with the git hook, which works whoever commits:
|
|
96
|
+
```console
|
|
97
|
+
printf '#!/bin/sh\nexec lockstep hook pre-commit\n' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
|
|
98
|
+
```
|
|
99
|
+
It refuses a commit that fails `lockstep layers`, `lockstep extract --check` (generated files out of date
|
|
100
|
+
or a locked statement changed) or `lockstep audit --changed`.
|
|
101
|
+
6. Register the MCP server with your client: command `lockstep-mcp` (or `tools/lockstep/bin/lockstep-mcp`), stdio,
|
|
102
|
+
no arguments. For example `.mcp.json`:
|
|
103
|
+
```json
|
|
104
|
+
{ "mcpServers": { "lockstep": { "command": "tools/lockstep/bin/lockstep-mcp", "args": [] } } }
|
|
105
|
+
```
|
|
106
|
+
7. Optionally, for Claude Code, add the session and shell hooks to `.claude/settings.json`:
|
|
107
|
+
```json
|
|
108
|
+
{ "hooks": {
|
|
109
|
+
"SessionStart": [{ "hooks": [{ "type": "command", "command": "tools/lockstep/bin/lockstep hook session-start" }] }],
|
|
110
|
+
"PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "tools/lockstep/bin/lockstep hook pre-bash" }] }] } }
|
|
111
|
+
```
|
|
112
|
+
`session-start` prints the session's agent id and `lockstep status --brief`. `pre-bash` blocks a
|
|
113
|
+
bare `nohup`/`systemd-run` (compute goes through `lockstep job run`) and runs the commit guard
|
|
114
|
+
before `git commit`/`git push`. Any client whose hooks pass the same JSON on stdin can use them.
|
|
115
|
+
8. In CI, `python3 -m lockstep.lock BASE_LOCK HEAD_LOCK` (with `PYTHONPATH=tools/lockstep` for a submodule)
|
|
116
|
+
prints the log files a lock change must touch: `restatements.md` when an approved statement
|
|
117
|
+
changed or disappeared, `retired.md` for a refutation. New entries need none.
|
|
118
|
+
9. Optionally, `tools/lockstep/systemd/install.sh` (from a checkout) installs a `lockstep-nightly-<project
|
|
119
|
+
id>` user timer (03:00) that runs the guarded core build, `lockstep extract --check` and `lockstep audit`.
|
|
120
|
+
|
|
121
|
+
## Commands
|
|
122
|
+
|
|
123
|
+
| Area | Commands |
|
|
124
|
+
|---|---|
|
|
125
|
+
| State | `status`, `ready`, `node show`, `search`, `extract`, `extract --check`, `doctor` |
|
|
126
|
+
| Statements | `node add`, `node restate`, `node retire`, `lock refresh` (after a toolchain bump) |
|
|
127
|
+
| Work | `claim`, `release`, `check`, `close`, `falsify` (conjecture nodes) |
|
|
128
|
+
| Builds and jobs | `build core` / `blueprint` / `node`, `job run`, `job wait`, `job list`, `worker` |
|
|
129
|
+
| Trail | `log`, `journal`, `note` (weekly summary) |
|
|
130
|
+
| Integrity | `audit`, `layers`, `rules`, `hook pre-commit` |
|
|
131
|
+
| Client hooks | `hook session-start`, `hook pre-bash` |
|
|
132
|
+
|
|
133
|
+
Every command takes `--json`, and all but the hooks are also MCP tools. Node names may be
|
|
134
|
+
dotted relative to the declaration namespace (`--name List.foo` declares `theorem List.foo` in
|
|
135
|
+
it), and `--depends` lists the project declarations a node waits on (library lemmas belong in
|
|
136
|
+
the description).
|
|
137
|
+
|
|
138
|
+
### Experimental
|
|
139
|
+
|
|
140
|
+
These work in the projects they came from but are less general; see
|
|
141
|
+
[`docs/automation.md`](docs/automation.md):
|
|
142
|
+
|
|
143
|
+
- **Automation layer**: durable `operation …` workflows run by `lockstep worker`, a results `inbox`,
|
|
144
|
+
`proof capture|check|prove`, `node finish`, `probe run|replay`, `context`.
|
|
145
|
+
- **Research library**: `lib search|show|list` over a project catalog
|
|
146
|
+
(`harness/research/catalog.json`).
|
|
147
|
+
- **Certificate layers**: `[layers]` rules for generated proof-module families, and
|
|
148
|
+
`storage share` for their compiled artifacts.
|
|
149
|
+
|
|
150
|
+
## Things that cost hours to learn
|
|
151
|
+
|
|
152
|
+
- **Never `import` the project modules at the top of a `lake env lean --run` script that also
|
|
153
|
+
calls `importModules` on them.** The script's own import maps the oleans; the second load
|
|
154
|
+
cannot mmap at the same addresses and reads them into anonymous memory instead (37 GB and a
|
|
155
|
+
cgroup OOM kill on a project with large certificate modules). The extractor template imports
|
|
156
|
+
only `Lean`; `main` imports the project.
|
|
157
|
+
- **Never use `importAll := true`** for the extractor environment (30+ GB). A plain import
|
|
158
|
+
exposes every theorem value in `env.header.moduleData`.
|
|
159
|
+
- **Never run bare `lake build` on big targets.** Lake 5 has no `-j`; two dozen parallel
|
|
160
|
+
compilers exhaust a 20 GB guard. `lockstep build` builds Lake's out-of-date list one module per
|
|
161
|
+
guarded invocation first.
|
|
162
|
+
- **`#print axioms` does not finish on large generated proof families** (16 TB read in 3.5 h);
|
|
163
|
+
the extractor's module-order taint scan with a per-module cache is the audit path.
|
|
164
|
+
- **A module-system core root (`module` header) cannot import plain files.** When the core root
|
|
165
|
+
uses it, nodes are written in module form from the start, so a proof elaborates against the
|
|
166
|
+
same imports before and after `lockstep close`.
|
|
167
|
+
- **The elaborator moves parts of a proof into auxiliary declarations** (`foo._proof_1`), so a
|
|
168
|
+
dependency scan of a theorem's value alone reports the auxiliaries instead of the lemmas they
|
|
169
|
+
use. The extractor looks through a declaration's own auxiliaries.
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```console
|
|
174
|
+
uv venv .venv && uv pip install -e ".[test,mcp,research]"
|
|
175
|
+
.venv/bin/python -m pytest
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Releases follow semantic versioning (0.x: minor versions may break). Changes are listed in
|
|
179
|
+
[`CHANGELOG.md`](CHANGELOG.md). To release, bump `__version__` in `lockstep/__init__.py`, add the
|
|
180
|
+
changelog entry, and push a `vX.Y.Z` tag: `.github/workflows/release.yml` builds the sdist and
|
|
181
|
+
wheel and publishes them to PyPI through trusted publishing.
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT; see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Launcher: prefer the tools/lockstep virtualenv, fall back to the system python3 (core has no dependencies).
|
|
3
|
+
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
|
+
if [ -x "$here/../.venv/bin/python" ]; then
|
|
5
|
+
exec "$here/../.venv/bin/python" -m lockstep "$@"
|
|
6
|
+
fi
|
|
7
|
+
PYTHONPATH="$here/..${PYTHONPATH:+:$PYTHONPATH}" exec python3 -m lockstep "$@"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# MCP server launcher (stdio). Needs the `mcp` package: uv pip install -e "tools/lockstep[mcp]".
|
|
3
|
+
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
|
+
export LOCKSTEP_PROJECT_ROOT="${LOCKSTEP_PROJECT_ROOT:-$(cd "$here/../../.." && pwd)}"
|
|
5
|
+
if [ -x "$here/../.venv/bin/python" ]; then
|
|
6
|
+
exec "$here/../.venv/bin/python" -m lockstep.mcp_server "$@"
|
|
7
|
+
fi
|
|
8
|
+
PYTHONPATH="$here/..${PYTHONPATH:+:$PYTHONPATH}" exec python3 -m lockstep.mcp_server "$@"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Automation layer (experimental)
|
|
2
|
+
|
|
3
|
+
The automation layer runs multi-step work (proof checks, tactic attempts, finite searches, the
|
|
4
|
+
finish of a node) as durable *operations* that `lockstep worker` executes through the same guarded,
|
|
5
|
+
budgeted jobs as `lockstep job run`. It calls no model: whichever agent submits an operation reads
|
|
6
|
+
the result later, so it works the same for every client. Nothing in this layer establishes a
|
|
7
|
+
theorem: only the compiled Lean environment, `lockstep extract` and the audit do.
|
|
8
|
+
|
|
9
|
+
Every command here is also an MCP tool. Results are compact by default; large fields are stored
|
|
10
|
+
as immutable SHA-256 artifacts and referenced by id (`lockstep artifact show ID --offset N`, or repeat
|
|
11
|
+
the command with `--full`).
|
|
12
|
+
|
|
13
|
+
## Operations
|
|
14
|
+
|
|
15
|
+
```console
|
|
16
|
+
lockstep operation submit --spec work.json [--key IDEMPOTENCY_KEY]
|
|
17
|
+
lockstep operation show OP_ID # stage, stages_done, stages_total, receipt references
|
|
18
|
+
lockstep operation list
|
|
19
|
+
lockstep operation resume|retry|cancel OP_ID
|
|
20
|
+
lockstep operation reconcile # detect dead runners without replaying uncertain effects
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
A spec is a finite list of uniquely named stages, each with a memory cap and a time limit
|
|
24
|
+
(defaults: `[jobs] default_memory_max`, 10 minutes):
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{"schema_version": 1, "stages": [
|
|
28
|
+
{"name": "sweep", "action": "command", "argv": ["python3", "scripts/sweep.py"], "memory": "2G", "minutes": 30}
|
|
29
|
+
]}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- Stages run in order; a stage that succeeded is never rerun by `resume` or `retry`.
|
|
33
|
+
- Submission is idempotent per agent: the same spec (or the same `--key`) returns the existing
|
|
34
|
+
operation.
|
|
35
|
+
- Admission is atomic against the shared memory budget (`[jobs]`), so concurrent workers never
|
|
36
|
+
overcommit. A request that can never fit is reported as `blocked_resource`.
|
|
37
|
+
- Cancellation is a request; the operation counts as cancelled once the owning worker confirms
|
|
38
|
+
the process is gone. A runner that died mid-stage becomes `unknown_execution` and is never
|
|
39
|
+
replayed automatically.
|
|
40
|
+
- `lockstep resources reconcile` releases a memory reservation only after confirming its cgroup stopped.
|
|
41
|
+
|
|
42
|
+
Restart `lockstep worker` after upgrading the harness, so that it runs the current code.
|
|
43
|
+
|
|
44
|
+
## Results inbox
|
|
45
|
+
|
|
46
|
+
A finished operation leaves an event in its submitter's inbox (keyed by `LOCKSTEP_AGENT`):
|
|
47
|
+
|
|
48
|
+
```console
|
|
49
|
+
lockstep inbox list
|
|
50
|
+
lockstep inbox claim --consumer ID # lease one event (at-least-once delivery)
|
|
51
|
+
lockstep inbox ack EVENT_ID --consumer ID
|
|
52
|
+
lockstep inbox pause|resume # stop or restart delivery; compute continues
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The inbox wakes nobody. A client that wants push delivery polls `inbox claim`, or keeps its turn
|
|
56
|
+
open with `lockstep job wait`.
|
|
57
|
+
|
|
58
|
+
## Proofs
|
|
59
|
+
|
|
60
|
+
```console
|
|
61
|
+
lockstep proof capture NODE [--start BYTE --end BYTE] # freeze the proof span and its source/dependency identities
|
|
62
|
+
lockstep proof check FIXTURE_ID --body body.lean [--memory 4G --minutes 10]
|
|
63
|
+
lockstep proof prove FIXTURE_ID [--tactics omega simp ...]
|
|
64
|
+
lockstep node finish NODE --body body.lean [--extract-memory 16G]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- `proof capture` records the exact UTF-8 byte span of a node's proof plus hashes of the source
|
|
68
|
+
file, its compiled dependencies and the toolchain. Any later change to them requires a recapture.
|
|
69
|
+
- `proof check` compiles the candidate in a network-less `bwrap` sandbox that sees only the
|
|
70
|
+
compiled libraries and the toolchain, then audits the whole parent declaration in a separate
|
|
71
|
+
Lean process. Without `bwrap` it refuses rather than falling back. Failed checks return bounded
|
|
72
|
+
error headers with byte offsets into the full diagnostic artifact.
|
|
73
|
+
- `proof prove` tries a fixed portfolio of tactics (`rfl`, `simp`, `simp_all`, `omega`,
|
|
74
|
+
`norm_num`, `ring`, `linarith`, `nlinarith`, `positivity`, `grind`, `aesop`) and stops at the
|
|
75
|
+
first one the kernel accepts.
|
|
76
|
+
- `node finish` checks the claim, branch and statement lock, checks the candidate, substitutes
|
|
77
|
+
only its proof span, rebuilds the original module, runs a full extraction and the audits, and
|
|
78
|
+
writes a reviewable diff. It never commits or publishes (`--policy review` is the only policy).
|
|
79
|
+
|
|
80
|
+
## Finite probes
|
|
81
|
+
|
|
82
|
+
A probe is a finite computation (a search for a counterexample, a bounded enumeration) run by a
|
|
83
|
+
project plugin registered in `harness/config.toml`:
|
|
84
|
+
|
|
85
|
+
```toml
|
|
86
|
+
[research.plugins.my_search]
|
|
87
|
+
module = "harness.research.my_search" # a module in the project
|
|
88
|
+
sources = ["harness/research/my_search.py"] # hashed at admission; a change refuses the run
|
|
89
|
+
semantics = ["my-search-v1"] # the spec must name one of these
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The module provides `evaluate(spec) -> dict` (with an optional `witness`) and
|
|
93
|
+
`replay(witness) -> {"replayed": bool, ...}`. A spec names the plugin, its semantics and a
|
|
94
|
+
positive case and time budget:
|
|
95
|
+
|
|
96
|
+
```console
|
|
97
|
+
lockstep probe run --spec probe.json
|
|
98
|
+
lockstep probe replay RESULT_ARTIFACT_ID # recompute a saved witness independently
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A witness is replayed before the result is stored. A probe result never closes or refutes a
|
|
102
|
+
node: a counterexample still needs a Lean refutation, and a bounded negative proves nothing.
|
|
103
|
+
|
|
104
|
+
## Context and the research library
|
|
105
|
+
|
|
106
|
+
- `lockstep context NODE [--previous BUNDLE_ID]` returns a content-addressed bundle with the node, its
|
|
107
|
+
source hash, the signatures of the declarations it uses, recent events and operations, and
|
|
108
|
+
whether the index is fresh. With `--previous` it returns only what changed.
|
|
109
|
+
- `lockstep lib search "TASK"`, `lockstep lib show ID` and `lockstep lib list` search the project's curated catalog
|
|
110
|
+
of reusable code, `harness/research/catalog.json`: `schema_version: 1` and a list of entries
|
|
111
|
+
with `id`, `kind` (`primitive` or `workflow`), `title`, `summary`, `api`, `example`,
|
|
112
|
+
`semantics`, `keywords` and an optional `source`. The symbols a source lists are checked by
|
|
113
|
+
parsing it, never by importing it. A miss is a reason to search the source, not evidence that
|
|
114
|
+
nothing exists.
|
|
115
|
+
|
|
116
|
+
## Certificate layers and the artifact cache
|
|
117
|
+
|
|
118
|
+
For projects with large generated proof modules (SAT certificates, for example):
|
|
119
|
+
|
|
120
|
+
- A `[layers]` entry can mark a layer as generated, pin its member list, and freeze the
|
|
121
|
+
hand-written import closure it depends on; `lockstep layers` enforces this. `lockstep layers
|
|
122
|
+
--allow-rebuild` accepts changes to those inputs, and the Claude Code hook passes it for a
|
|
123
|
+
commit whose message carries `[certificate-rebuild]`.
|
|
124
|
+
- `[build.artifact_cache]` and `lockstep storage share` verify byte-identical compiled artifacts and
|
|
125
|
+
hard-link them through Lake's immutable cache, which saves disk across worktrees.
|
|
126
|
+
|
|
127
|
+
## Index shadowing
|
|
128
|
+
|
|
129
|
+
`lockstep index shadow` merges an incremental update of the index and compares it with a supplied full
|
|
130
|
+
extraction. It checks incremental extraction and never publishes an index.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Working rules
|
|
2
|
+
|
|
3
|
+
- Export a stable `LOCKSTEP_AGENT=<client>:<session id>` in every shell; claims, jobs and log entries are recorded under it.
|
|
4
|
+
- Project state comes from `lockstep status`; pick work with `lockstep ready`.
|
|
5
|
+
- Run `lockstep claim <node> --why "<plan>"` before starting, and `lockstep release <node> --outcome ... --note ...` when you stop.
|
|
6
|
+
- New statements go in only through `lockstep node add`; never edit a statement by hand.
|
|
7
|
+
- Commands that run longer than a minute go through `lockstep job run --why "<purpose>" --out runs/<dir> -- <command>`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import Demo.Basic
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|