Add theorem prover docs page with Phase 2 constraint solving
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled

- prove.sx Phase 2: bounded model checking with 34 algebraic properties
  (commutativity, associativity, distributivity, inverses, bounds, transitivity)
- prove.sx generates SMT-LIB for unbounded Z3 verification via z3-expr
- New docs page /plans/theorem-prover with live results (91/91 sat, 34/34 verified)
- Page helper runs both proof phases and returns structured data
- Parser: re-add ' quote syntax (removed by prior edit)
- Load prove.sx alongside z3.sx at startup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 23:17:09 +00:00
parent 7b8ae473a5
commit 00e7ba4650
7 changed files with 747 additions and 2 deletions

View File

@@ -626,10 +626,14 @@
:sub-nav (~section-nav :items plans-nav-items
:current (find-current plans-nav-items slug))
:selected (or (find-current plans-nav-items slug) ""))
:data (case slug
"theorem-prover" (prove-data)
:else nil)
:content (case slug
"status" (~plan-status-content)
"reader-macros" (~plan-reader-macros-content)
"reader-macro-demo" (~plan-reader-macro-demo-content)
"theorem-prover" (~plan-theorem-prover-content)
"sx-activity" (~plan-sx-activity-content)
"predictive-prefetch" (~plan-predictive-prefetch-content)
"content-addressed-components" (~plan-content-addressed-components-content)

View File

@@ -31,6 +31,7 @@ def _register_sx_helpers() -> None:
"optimistic-demo-data": _optimistic_demo_data,
"action:add-demo-item": _add_demo_item,
"offline-demo-data": _offline_demo_data,
"prove-data": _prove_data,
})
@@ -1000,6 +1001,109 @@ def _add_demo_item(**kwargs) -> dict:
}
def _prove_data() -> dict:
"""Run prove.sx against the SX spec — both phases.
Phase 1: Translate all define-* forms via z3.sx, verify satisfiability.
Phase 2: Evaluate algebraic properties via bounded model checking.
Returns results for the docs page to render.
"""
import time
from shared.sx.parser import parse_all
from shared.sx.evaluator import evaluate
from shared.sx.primitives import all_primitives
from shared.sx.evaluator import _trampoline, _call_lambda
env = all_primitives()
ref_dir = _ref_dir()
for lib in ("z3.sx", "prove.sx"):
path = __import__("os").path.join(ref_dir, lib)
with open(path, encoding="utf-8") as f:
for expr in parse_all(f.read()):
evaluate(expr, env)
# Phase 1: definitional satisfiability
with open(__import__("os").path.join(ref_dir, "primitives.sx"), encoding="utf-8") as f:
prim_exprs = parse_all(f.read())
t0 = time.monotonic()
phase1 = _trampoline(_call_lambda(env["prove-file"], [prim_exprs], env))
phase1_ms = round((time.monotonic() - t0) * 1000)
# Phase 2: property-based constraint solving
t1 = time.monotonic()
phase2 = _trampoline(_call_lambda(env["prove-all-properties"], [], env))
phase2_ms = round((time.monotonic() - t1) * 1000)
# Flatten Phase 1 results for rendering
phase1_results = []
for r in phase1.get("results", []):
phase1_results.append({
"name": r.get("name", "?"),
"status": r.get("status", "?"),
})
# Flatten Phase 2 results for rendering
phase2_results = []
total_tested = 0
for r in phase2.get("results", []):
tested = r.get("tested", 0)
skipped = r.get("skipped", 0)
total_tested += tested
entry = {
"name": r.get("name", "?"),
"status": r.get("status", "?"),
"tested": tested,
"skipped": skipped,
}
ce = r.get("counterexample")
if ce:
entry["counterexample"] = str(ce)
phase2_results.append(entry)
# Generate SMT-LIB sample for a few properties
props = env["sx-properties"]
smtlib_samples = []
for p in props[:3]:
smt = _trampoline(_call_lambda(env["prove-property-smtlib"], [p], env))
smtlib_samples.append(smt)
# One with precondition
for p in props:
if p.get("given-expr"):
smt = _trampoline(_call_lambda(env["prove-property-smtlib"], [p], env))
smtlib_samples.append(smt)
break
return {
"phase1-total": phase1.get("total", 0),
"phase1-sat": phase1.get("sat", 0),
"phase1-all-sat": phase1.get("all-sat", False),
"phase1-ms": phase1_ms,
"phase1-results": phase1_results,
"phase2-total": phase2.get("total", 0),
"phase2-verified": phase2.get("verified", 0),
"phase2-falsified": phase2.get("falsified", 0),
"phase2-all-verified": phase2.get("all-verified", False),
"phase2-ms": phase2_ms,
"phase2-results": phase2_results,
"phase2-total-tested": total_tested,
"smtlib-sample": "\n".join(smtlib_samples),
"prove-source": _read_spec_file("prove.sx"),
"z3-source": _read_spec_file("z3.sx"),
}
def _ref_dir() -> str:
"""Return the path to the SX ref directory."""
import os
# Same resolution as _read_spec_file
ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref")
if not os.path.isdir(ref_dir):
ref_dir = "/app/shared/sx/ref"
return ref_dir
def _offline_demo_data() -> dict:
"""Return demo data for the offline data layer test page."""
from datetime import datetime, timezone