- Node.js 23+
- npm
Install dependencies once:
npm installnpm startThis starts the UI at http://localhost:3000/ender/ (the dev server redirects / to /ender/).
Open ProofObj Harness from the app to edit proofs live; checker errors appear in the editor (red lines and hover tooltips) and in the proof-wide issues list.
Run the checker on one proof file:
npm run checkProof -- src/checker/proofs/examples/tutorial.txtThe CLI checker does not require OpenAI/LLM configuration.
Proof samples live in src/checker/proofs/, organized into category subdirectories (examples/, circles/, lines_angles/, quadrilaterals/, triangles/) — for example: examples/tutorial.txt, examples/tutinc.txt, examples/s1c1.txt, examples/s2c2.txt.
The checker also runs as a standalone HTTP server used by the backend agents:
npm run checker-server # starts on http://localhost:4000POST /check with body { "text": "<proof text>" } returns the same JSON structure as the CLI.
Three containers are defined in docker-compose.yml:
| Container | Role | Port |
|---|---|---|
checker |
Node.js HTTP server wrapping the proof checker | 4000 |
backend |
Python/Flask server for the solver and feedback agents | 5000 |
interface |
Vite dev server for the React UI | 3000 |
The backend calls the checker via HTTP (CHECKER_URL=http://checker:4000). The interface proxies /api/* requests to the backend through Vite's dev server proxy.
Both backend and interface also mount an external, read-only volume named proof_data at /app/geo-proof-dataset. This volume is a named Docker volume produced by the geo-proof-dataset repo — it is declared here as external: true, so it is not created or seeded by this project. It packages that repo's proofs/ and wrong_proofs/ folders.
- Docker Desktop (includes Docker Compose)
- A
.envfile in the repo root with your OpenAI API key (copy from.env.example):
cp .env.example .env
# then edit .env and set OPENAI_API_KEY- The
proof_datavolume must exist before starting this project. Build and seed it once from thegeo-proof-datasetrepo:
cd ../geo-proof-dataset
docker compose up proof-data(See that repo's README for full details. If the volume doesn't exist yet, docker compose up here will fail with an error naming the missing external volume.)
geo-proof-dataset is a private repo, so it isn't available to everyone. If you don't have the proof_data volume, use the standalone compose file instead of the default one. It runs the same three services but drops the external volume, so only the proofs bundled in this repo under src/checker/proofs/ are available (that volume is only used by the manual eval scripts in backend/, not by the running services):
npm run docker:standaloneThis is a shortcut for docker compose -f docker-compose.standalone.yml up --build (builds the images and starts all three services). Everything else below (ports, logs, stop, backend API) works the same — just add -f docker-compose.standalone.yml to each docker compose command.
docker compose builddocker compose upThe interface is available at http://localhost:3000/ender/.
To start a specific service only:
docker compose up checker
docker compose up checker backenddocker compose downTo also remove built images:
docker compose down --rmi localdocker compose logs -f # all services
docker compose logs -f backend # one serviceOnce running, the backend exposes:
| Method | Path | Body | Description |
|---|---|---|---|
POST |
/api/solve |
{ "proofName": "s1c1", "prompt": "solver_with_valid_reasons_and_explanation" } |
Run the LLM solver on a proof |
POST |
/api/feedback |
{ "proofName": "s1c1", "solverPrompt": "solver_with_valid_reasons", "feedbackPrompt": "feedback" } |
Get Socratic feedback on a proof |
GET |
/api/health |
— | Health check |
proofName must match a file in src/checker/proofs/ (without the .txt extension), including its category subdirectory — for example examples/s1c1. The prompt fields default to the values shown above if omitted.
- Open a branch with the naming convention
<user-alias>/<description>(i.e.,hharriman/render-points) - Push your changes to your branch
- Open a PR to
main. The first word of your PR title should be one of: a.feat:A new feature is being added with this PR b.fix:A fix is implemented in this PR c.chore:Some utility/devops/upkeep is done - In the PR description provide a list of the changes that were made
Proofs are text files parsed into a ProofObj, checked by src/checker/proofChecker.ts, and rendered from that same object in the React interface. A change usually touches (1) tokenization and parsing, (2) type definitions and checker logic, and (3) UI text and diagram behavior. The tables below list the main files for each kind of change.
Conceptual pipeline
- Lexer (
src/checker/grammar/parser.ts) turns source text into tokens (points,t_ABC, keywords liketri:,stmtnames, step refs[01], etc.). - Parser (
src/checker/grammar/lezerParser.ts) buildsProofObj(premises, goal, steps withReason+Stmt). - Definitions — statement shapes live in
src/checker/grammar/defs/stmts.defs.ts; reason shapes insrc/checker/grammar/defs/reasons.defs.ts. These.defs.tsfiles are the source of truth; update them directly. - Checker uses definition maps +
src/checker/checker/graph.ts,validators.ts, andreasonApplication.ts(plusreasonChecks/for geometric checks). - Interface uses
src/interface/core/grammarToLayout/proofObjLayout.tsand helpers likeproofObjText.tsx,proofObjDiagramAdditions.ts, andproofObjBaseContent.ts.
Types to know
Stmt={ function: string; arguments: ParseObj[] }(src/checker/types/checkerTypes.ts).ParseObj={ type: Obj.*; v: string }(src/geometry-object/types/types.ts). The lexer +ProofParser.parseObjdecide how source spellings map toObjand storedv(often stripping prefixes liket_/a_for checks).
Use this when you introduce a new kind of literal in proofs (new premise section, new statement argument shape, and usually new diagram behavior).
| Area | What to change |
|---|---|
| Tokenization | src/checker/grammar/parser.ts — add a moo rule for the literal (put longer patterns before shorter ones so they are not swallowed). If you add a named premises list (like tri:), add a keyword token and a section label (e.g. circ:). |
AST / ProofObj premises |
src/checker/types/checkerTypes.ts — extend premises (e.g. add circles: ParseObj[]) if circles are listed in the premises block. |
| Object model | src/geometry-object/types/types.ts — add a value to Obj and extend the ParseObj union. Implement a geometry class if the diagram needs it (see geometry-object/geometry/*, ProofContent / DiagramContent). |
| Parser | src/checker/grammar/lezerParser.ts — parseObj, geomPremiseTokenTypes, assertPremiseListHead, the premises loop (new branch like tri / quad), parseStatement (if the lexer emits a new token type for literals inside stmt(...)), plus validators like validateTriangleToken for your spelling rules. |
| Normalization | src/checker/normalizeProofObj.ts — strip or canonicalize prefixes on objects if you use them in proofs. |
| Well-formedness | src/checker/checker/validators.ts — checkGeometricObjects: add a switch case for the new Obj (point sets, duplicate letters, etc.). |
| Semantic premises / diagram seed | src/checker/checker/premises.ts — only if givens or diagram premises must update geometric context (often switch on statement.function). |
| Reason machinery | src/checker/utils/utils.ts — getGeometricObject must support your Obj if any reason pulls live geometry from ProofContent (the function is imported by reasonApplication.ts and reasonChecks/). |
| Interface: diagram | src/interface/core/grammarToLayout/proofObjBaseContent.ts — seed DiagramContent from the new premise list. Add drawing helpers under src/interface/core/ as needed. |
| Docs / samples | src/checker/glossary.md, src/checker/README.md, and a proof under src/checker/proofs/tests/ that exercises the new syntax. |
Many statements only combine existing object kinds (segments, angles, triangles, …). The project already defines sim_tri with two triangle arguments in stmts.defs.ts; the checklist below is what you would repeat for a new statement name.
| Area | What to change |
|---|---|
| Definition (required) | src/checker/grammar/defs/stmts.defs.ts — add an entry under statements: name, parameters (human-readable labels like today’s triangle("t1")), optional isPremisesOnly, optional group for substitute-able statements (see congruent_angs). |
| Checker | checkStatementArguments in src/checker/checker/validators.ts currently checks arity against stmts.defs. Graph building in src/checker/checker/graph.ts uses statement defs for step validity. Add domain checks in reasonChecks/ or validators if you need more than arity. |
| Given / diagram premises | If the statement can appear in givens and affects the built diagram, extend src/checker/checker/premises.ts (and the same for proof.premises.diagramStatements). |
| Interface: text | src/interface/core/grammarToLayout/proofObjText.tsx — add a branch in stmtToText (or the step falls back to function(arg1, arg2, …) plain text). |
| Interface: diagram | src/interface/core/grammarToLayout/proofObjDiagramAdditions.ts — in applyStmtAdditions, map the new stmt.function to tick marks, labels, or overlays (see con_seg, con_ang, con_tri). If you need congruence-style tick tracking, extend buildCongruenceTickTracker. |
| Layout | src/interface/core/grammarToLayout/proofObjLayout.ts — only if the new statement needs special transversal/vertical-angle style behavior (rare; most changes stay in proofObjDiagramAdditions.ts). |
Reasons tie dependency step numbers to a conclusion statement. They are listed in reasons.defs.ts and checked in reasonApplication.ts.
| Area | What to change |
|---|---|
| Definition (required) | src/checker/grammar/defs/reasons.defs.ts — add a key (lowercase function name used in proof text). Set dependencies to statement names or groups from stmts.defs (see ReasonDefinition). Use diagramDependencies when refs come from diagram premises instead of step lists. Set conclusion to the statement name(s) allowed for this reason — comma-separated if multiple (see existing entries). |
| Collision with statements | lezerParser.ts — parseStatement rejects names that are only reasons; keep names consistent with defs. |
| Structural checks | src/checker/checker/validators.ts — reason/step shape and dependency typing vs defs (already wired through the graph). |
| Geometric / logical check | src/checker/checker/reasonApplication.ts — add a case "your_reason": that calls helpers in src/checker/checker/reasonChecks/. If you omit this, unknown reasons fall through to default and return true after syntax passes (placeholder behavior). |
| Interface: theorem panel | src/interface/theorems/reasons.ts — extend reasonFromFunction’s map so the step shows a proper title/body; otherwise the UI shows the raw function name. Add a Reasons.YourReason entry with title / body / optional src for assets. expectedDependenciesDescription is filled automatically from reasons.defs when possible. |
| Interface: highlights | Optional: add a small helper under src/interface/core/reasons/ (like Transversal.tsx) and hook it from proofObjLayout.ts if the step needs custom diagram emphasis. |
- Definitions updated in
stmts.defs.tsand/orreasons.defs.ts - Parser accepts new tokens / premise sections (
parser.ts,lezerParser.ts) -
ParseObj/ProofObjextended if premises or args introduced a new object kind -
validators.ts(objects) andreasonApplication.ts(reason semantic check, or accept placeholderdefault) -
proofObjText.tsxandproofObjDiagramAdditions.tsfor readable text and diagram updates - Sample proof in
src/checker/proofs/tests/andnpm run checkProofon it
The web interface is built with Vite. vite.config.ts sets base: "/ender/" for GitHub Pages.
| Command | Description |
|---|---|
npm start |
Dev server (port 3000, app at /ender/) |
npm run build |
Typecheck with tsc and production build to dist/ |
npm run preview |
Serve the dist/ build locally |
npm test / npm run test-core |
Run Jest (see jest.config.js) |
npm run deploy |
Build and publish dist/ to the gh-pages branch (see package.json homepage) |