Skip to content

Repository files navigation

draft

A group drafting engine, and the web app built on it: docs.vote.

A membership writes a document together. Anybody may propose a change; proposals that conflict race each other; the membership votes on them in blind pairwise comparisons — which of these two wordings?, with no names attached and no standings shown — and a race adopts the wording the membership prefers once enough of them have voted on it, the current text standing until then. Whatever is still undecided at the close ships with the document as a ranked backlog. The output is not just the agreed text but the record of what was contested, by how much, and what the minority cared about. The document's own rules — who is a member, how many must vote, whether the document ever ends — are decided inside the document by the same mechanism, constitutional changes needing everybody.

First target: constitutional conventions for Newspeak House cohorts (5–20 members), designed not to preclude much larger instances.

docs.vote is live, in alpha. A document is created by naming it and verifying an email address; everything after that happens in the document. The banner on every page says what alpha means here: this is early, and nothing in it should yet be trusted with a decision that matters.

Run it locally

Node 24 or later.

git clone https://github.com/edsaperia/draft && cd draft
npm install
npm run server        # → draft server on http://localhost:8140
npm test              # every package

Without RESEND_API_KEY the server runs a dev inbox: every mail, magic links intact, goes to the console and to packages/server/data/outbox.jsonl, and the page grows a 📬 button that opens them — so a whole membership can be played from one browser. That route does not exist in the production build.

Command What it does
npm run typecheck · npm run lint · npm run spec-check · npm run copy-check · npm run clock-check Five of the seven gates CI runs at every push — npm test and npm run build are the other two: types per package, eslint, the spec and surface tables against the code, the surface copy against its golden, and the page's clocks evaluated in a VM.
npm run build The production artifacts, dist/server.mjs and dist/draft-tools.mjs, with the dev routes dropped from the bytes. npm start boots the artifact and refuses without DRAFT_SECRET, an https:// DRAFT_BASE_URL and RESEND_API_KEY.
npm run verify <url> The live-environment checks (TLS, headers, no dev outbox). It writes nothing: the POSTs it makes go to routes that must refuse them — the dropped dev routes, and the pause key against a stranger — and it asserts they do. Safe against production.
npm run design Serves design/ at the address it prints (8137 by default, DESIGN_PORT or an argument otherwise), with the fixture documents: /session-view.html a blank arrival, /session-view.html?fixture=session a session mid-flight, &closed=1 a closed one. Every address names the page — at / the page boots as the live birth, which has no API behind this server. Needs no server and no account.
npm run sim -w @draft/sim-harness -- --mode scripted --scenario clubhouse --seeds 5 A deterministic simulated session, scored against the scenario's ground truth. No network.
npm run sweep -w @draft/sim-harness The calibration sweep: 425 scripted runs over the constitution's seven knobs (25 baseline seeds, plus 25 for each of the 16 variant values). LLM-free.
npm run test:pg The server suite against a real Postgres (a local postgres:17 on 127.0.0.1:5433); npm test skips those 19 tests without one.

Packages

TypeScript end to end; pg and @anthropic-ai/sdk (for the demo document's bots) are the only runtime dependencies. Tests measured 2026-10-03 with npm test: 1,759 passing (9 todo, 19 skipped without Postgres). What has changed, deploy by deploy: CHANGELOG.md.

Package What it is Tests
packages/engine-core The mechanism as a pure, deterministic, dependency-free library: diffs and footprints, the races, Bradley–Terry ranking with ties, the session state machine, the hash-chained event log, the feed router, and the participant API — the one blind surface that people, simulated members and personal AIs all speak identically. Notes: NOTES.md. 542
packages/constitution The document's rules as a module, equally pure: the settings catalogue, the blind founding (each member states the least they will accept; the document takes the maximum), motions on both routes — ordinary ones race, constitutional ones need everybody — applications, lapse, its own hash-chained log. Runs in the browser too, as the committed bundle design/constitution.js. Notes: NOTES.md. 781
packages/server The product host: node:http with no framework, one hash-chained log per document plus the people rows written beside it — identity never goes in the log (decision 1253) — as JSONL on disk or a row per entry in Postgres, which is what docs.vote has served from since 2026-08-20; magic-link login, stateless HMAC cookies, the engine riding every commit. Notes: NOTES.md. 397 (+19 Postgres)
packages/sim-harness Simulated members driving whole sessions: deterministic scripted personas with ground-truth welfare scoring, LLM personas speaking the same participant API with no back door, a calibration sweep whose findings are folded into SPEC §4.2 and §8.3, and a live commentator. README.md. 32
design/ The surface itself, served by the server off disk: session-view.html is the one page — arrival, founding and the live document — with its machinery in session.js, setup.js and cards.js, and every string a member can read in copy.js. Beside it feed.html is the spectator feed at /d/:slug/feed: new proposals and proposals that pass, read from the engine's strictly-public spectator-api and nothing else. —

Documents

Rule files hold rules; the reasoning behind them lives in design/. Where two disagree, SPEC.md wins.

Document What it is Read it when
SPEC.md The mechanism, v0.147 — tables and numbered rules, each pointing at its reasons as → why: R-nnn. The single source of truth. First, to understand what the engine does.
SURFACE.md What the surface tells a member and what a control does: the event matrix, the marks, the wallets, the founding order, the card kinds. Asserted against the page's own tables by npm run spec-check. Second, to understand what a member sees.
CLAUDE.md The project's operative reference: the vocabulary, the glossary of every named part, and the post-mortems that bite. Before contributing.
design/STYLE.md The surface-copy checklist every string a member can read has to pass. Before touching copy.js.
design/DECISIONS.md · design/SPEC-REASONING.md The archives: why a thing is the way it is, what it replaced, what was rejected. The second is keyed to the spec's R-nnn rulings. When a rule seems arbitrary.
QUESTIONS.md Open and deferred items, on one project-wide number sequence. To see what is undecided.
PRODUCTION.md The roadmap: the staged rollout to docs.vote and what is left of it. To see what is next.
design/MOBILE.md docs.vote on a phone: the plan, and its Status — a first cut is live since 2026-09-12 (one column, both rails as drawers, read and judge), with the two-tap, the tap targets and mobile-walk still to build. Before touching layout for narrow screens.
docs/OPERATING.md The operator's map: what runs where, every environment variable, how a deploy happens. Procedures in docs/runbooks/. Before running an instance.

Checks, walks and deploys

A push to main is a deploy — CI deploys on green and verifies docs.vote afterwards, and nothing gates a merge; the jobs at every push, the sprint tier on a push that carries a merge, and what each holds are docs/OPERATING.md §3. dev.docs.vote is a second, throwaway instance on the dev path — a ⏭ control bottom-left walks a document through its whole life, and the 📬 outbox is public, so it must never hold anything real; every deploy wipes it.

The rest of package.json's scripts are instruments, in two kinds:

Kind Scripts Needs
Headless over design/ probe, probe-coverage, card-audit, a11y-audit, toc-travel, drawer-walk, picture-walk, slider-walk, founder-answers, founding-golden, copy-check -- --walk Playwright's Chromium (npm run playwright:install); each serves design/ itself. clock-check needs only node.
Against a running dev server journey, applicants-walk, after-begin-walk, invite-walk, member-questions-walk, slug-walk, powers-walk, proposal-shapes, ladder, room-walk, seat-matrix, room-bots -- <document url> npm run server in another terminal, with no RESEND_API_KEY. Each checks it is talking to a server built from your tree before it starts. room-bots alone also runs against docs.vote itself: invite bots at *@bots.docs.vote, whose mail the host catches, and pass --key=<DRAFT_BOT_KEY> (docs/OPERATING.md §10).

What each asserts is in design/GLOSSARY.md under Tooling.

Licence

MIT.

About

A compiler for group agreement: patches race, blind pairwise judgments rank them, adoption clears a rising confidence bar

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages