Skip to content

A proposal · Article I, Section 2 · 30k Protocol · v0.13 draft

Open the House.

The Constitution allows as many as one Representative for every 30,000 people, and the framers meant the House to be large and to grow with the people.[Op. I.A, I.B] For 120 years it did. Then Congress stopped at 435 in 1911, and in 1929 froze it there.[Op. II] Open The House is a proposal to restore the House the Constitution describes, by statute, with no amendment needed: by the 2020 Census, 11,013 members, one for every 30,000 people in each state,[Op. IV.A, IV.B] as diverse as the country they serve, where bills are drafted in the open and advance through the House when published rules are met, not when leadership allows.

Where it stands: the 30k Protocol is at its v0.13 draft, tested so far in 5 simulated sessions. There is no implementation yet, so early builders shape what comes next. Until the design has been tested in the open, implementations run synthetic Houses only, never real elections.

A country this diverse doesn't fit in 435 seats.

Article I of the Constitution allows at most one Representative for every 30,000 people, and it counts people, not citizens.[Op. I.A] Congress has capped the House at 435 seats by statute since 1929, so after the 2020 Census each member speaks for 761,169 people on average, from 542,704 in Montana to 990,837 in Delaware.[Op. II] A few control the agenda. Fewer seats mean fewer voices: whole communities and points of view never get a member of their own.

The framers meant otherwise. On the Convention's last day, they lowered the ceiling from one Representative per 40,000 people to one per 30,000, without opposition. George Washington, who had kept his views to himself all summer, rose to support it: the small number of Representatives, he said, "had always appeared to himself among the exceptionable parts of the plan."[Op. I.B] As President, he cast the first veto against a bill that gave eight states more than one Representative for every 30,000 of their people. Congress then set the House at one Member for every 33,000 people in each state.[Op. I.E]

The Constitution sets a ceiling, not a mandate. Every court to reach the merits has upheld the cap, and since 2010 the courts have dismissed challenges to it for lack of jurisdiction, so a larger House will come from Congress, not a lawsuit.[Op. III.A, III.B] Congress set the House's size by ordinary statute after every census from 1790 to 1910, and it can again: up to 11,013 members by the 2020 Census, without amending the Constitution.[Op. IV.A, IV.B] Big rooms were the old limit. Software can count and record the votes of eleven thousand Members under rules the House sets for itself, while the power to legislate stays with them.[Op. V.A, V.C]

435
seats in the House, capped by statute
761,169
people per member, on average
11,013
members at most: one per 30,000 people in each state

Figures from the 2020 Census, which counted 331,108,434 people for apportioning the House (U.S. Census Bureau). The law behind each claim is in the Legal Opinion: a note like [Op. II] links to the part that supports it. Read the founding paper

Diversity is the fix.

A democracy is stronger when more of its people are in the room, and when no one group can decide alone. A House of 11,013 makes both true.

  • More members, more voices

    At one member for every 30,000 people, a member serves a community, not a region the size of a state. Many more kinds of people can serve, and many more points of view reach the floor.

  • No faction rules alone

    With many blocs instead of two sides, a bill passes only by winning people unlike its authors. In Simulation 1, four bills written by single factions all failed; a coalition bill drafted on its own branch passed 6,230 to 4,230.

  • Open to everyone who builds

    No one company runs the House. Independent implementations check each other, and anyone can publish an opinion, a fact-check or a forecast on the record, labeled for what it is.

See it work · a simulation of digital democracy

How would it work?

Pick an issue and see what it would look like: all 11,000 members of the simulated House in a stadium, seated by bloc, voting from their seats, while each bloc makes its case from the stage and the stakeholders work the suites above.

Simulation 1: Can an 11,000-member House help end the Iran war? Four bills, each written by one group, all failed. A coalition bill written across groups passed 6,230 to 4,230. Read it

A simulation of digital democracy

The issue: Ending the Iran war · Simulation 1

No votes to replay yet

Click to walk there, or use the arrow keys; drag to look around. E to talk, M for missions, F for full screen.

simulated A simulation of digital democracy, in a stadium modeled on LA's. Every one of the simulated House's 11,000 members (the 2020 Census allows 11,013, one per 30,000 people in each state) has a seat in the lower bowl, with their bloc, and votes from it; the public fills the upper tier. A delegate from each voting bloc steps up to the podium to argue the vote, their words in the chat; the stakeholders who negotiated the protocol mingle in the suites, where you walk. Take on the missions to meet them all.

Drafting like software, deciding by open rules

The protocol treats law as versioned text. Anyone can propose a change, everyone can see exactly what it does, and a published rule decides when it advances. Those rules are House rules, which the Constitution lets each House set and change for itself.[Op. V.A] The software counts and records, and the Members decide. A bill the House passes still goes to the Senate and the President, and until the courts settle whether a quorum must be present in person, the House would keep one that is, in one place or several.[Op. V.C, V.B]

  1. 01

    Branch

    Any group forks the current law into its own branch: a party, a caucus, a committee, a coalition of ten.

  2. 02

    Propose

    A merge request shows exactly what would change, with automatic checks against a formal grammar of the law's structure and for broken cross-references.

  3. 03

    Decide by rule

    Published, deterministic rules decide: designated approvers, a threshold vote, or a limited budget of voting capital.

  4. 04

    Advance

    When the rule is satisfied and the notice period has passed, the change merges automatically. Every node can re-check the result for itself.

Opinions happen in the open too. Anyone can publish a signed opinion on any proposal, from members to advisors to AI agents. Non-members are always labeled, AI is always disclosed, and opinions never count as votes. Fact-checks work the same way: quotes are verified against the exact text, claims about the record are checked by every node, and no one, including the protocol, gets to declare what is true. Anyone can also publish a formal model of a law, tied sentence by sentence to its text, so reasoners can hunt for contradictions. The text stays the law; the model is an interpretation. Markets, forecasters, and rankers can publish too: resolution rules, forecasts, and rankings that every node checks, with financial stakes disclosed. The protocol itself never hosts markets, quotes odds, or ranks anything. Parties and interest groups speak as groups with full disclosure, outside drafting leaves a public footprint, and only members cast ballots. Every refusal by a node is signed and logged, so anyone can act as an ombudsman and check a complaint.

Who can help build it

  • Builders

    Implement the API your way: a replicated ledger, a Git-backed forge, a database with a transparency log, or peer-to-peer replicas.

  • Researchers and legal scholars

    Test the proposal's claims with evidence. Run sessions that differ only in their rules, export roll-call data, and publish results anyone can replicate.

  • Educators and civic technologists

    Run a simulated House in a classroom, a hackathon, or a student government. Classroom sessions stay private, never link keys to students, and can be erased after the term.

  • AI-agent developers

    The spec ships as a prompt written for coding agents, so an agent can start on a working implementation right away.

  • Fact-checkers and watchdogs

    Check proposals, laws, and voting records for false claims and contradictions. Quotes are verified against the exact text, and claims about votes are checked by every node.

  • Markets, forecasters, and rankers

    Settle questions on outcomes, publish forecasts, and rank proposals and members by the binding record. Every node can check your resolutions and rankings.

  • Parties, interest groups, and ombudsmen

    Publish group positions and model text on the record, and hold nodes to account with signed receipts and a public log of every refusal.

  • Judges and attorneys

    Look up the exact text in force on any date, read labeled legislative history, and verify any record offline as evidence.

  • Budget, defense, and security professionals

    Publish cost estimates before any spending vote, check that orders can be carried out safely, and stress-test the system against foreign influence and stolen keys.

Testing the proposal

A proposal to change how a country governs itself has to work before anyone should trust it. A design council of stakeholders negotiated the API in 14 sessions, and simulated Houses have put it to real questions. One AI model played every part, so real people from each group still need to review every decision.

  • 62

    Stakeholders

    Implementers, creators, fact-checkers, markets, parties, judges, doctors and nurses, cities, farm towns, regions and faiths, each with what it optimizes for and the line it won't cross.

    Meet them
  • 79

    Decisions

    Each with the alternatives rejected, what would make the council look again, and the round where it was argued, plus 40 questions still open.

    Read the log
  • 5

    Simulations

    A simulated House of 11,000 members takes on a real question, and the council changes the spec from what went wrong.

    See the runs

The API · v0.13 draft

One write endpoint. Many reads. Any architecture.

Implementations can differ inside, but they speak one protocol: signed transactions, content-addressed objects, and verifiable checkpoints. Consensus and storage are left to you.

Every write is a signed envelope

{
  "v": "0.10",
  "type": "ballot.cast",
  "session": "sha256:...",
  "author": "ed25519:...",
  "parents": ["sha256:..."],
  "body": {
    "mr": "sha256:...",
    "choice": "approve"
  },
  "ts": "2026-09-21T12:00:00Z",
  "sig": "..."
}

Endpoints

  • GET /.well-known/oth
    Capability document
    L0
  • GET /log
    Accepted envelopes, in node order
    L0
  • GET /objects/{id}
    Any content-addressed object
    L0
  • GET /checkpoint
    Signed log head
    L0
  • POST /transactions
    Submit a signed envelope
    L1
  • GET /branches/{name}
    Branch state and subtrees
    L1
  • GET /merge-requests/{id}/diff
    Structural diff of a proposal
    L1
  • GET /law?date=
    The text in force on a given date
    L1
  • GET /merge-requests/{id}/facts
    Fact sheet: what a proposal mechanically changes
    L1
  • GET /merge-requests/{id}/tally
    Live tally, labeled provisional or final
    L1
  • GET /contests/{id}
    Ranked contest among competing proposals
    L2
  • GET /rules
    Registry of approval rules
    L1
  • GET /opinions
    Signed opinions with funding and AI disclosure
    L2
  • GET /opinions/{id}/evaluation
    Verified quotes and checked claims about the record
    L2
  • GET /events
    Live event stream (SSE) with finality
    L2
  • GET /cite/{hash}
    Permanent citation of any version
    L2
  • GET /stats
    Verifiable metrics for rankings, from ballots only
    L2
  • GET /export/popolo
    Voting record in a standard open-data format
    L2
  • GET /extract/{hash}
    Evidence file anyone can verify offline
    L2
  • GET /refusals
    Signed log of every submission a node refused
    L2
  • GET /peers
    Federation with other nodes
    L3

Approval rules

  • always_allow
    Approved once a merge request is open and structurally valid.
  • designated_approvers
    A set number of named approvers must approve, like committee control.
  • threshold_vote
    A fraction of the electorate or of votes cast, with quorum and a closing mode.
  • contests
    Members rank competing proposals and the status quo; the winner must still pass its own rule.
  • capital_vote
    Members spend a limited voting budget, linear or quadratic. Strong-consistency nodes only.

Conformance levels

  • L0
    Verifier: replay a peer log and check every result.
  • L1
    Core: branches, merge requests, structure grammar and consistency checks, fact sheets, law in force by date, and three rule kinds.
  • L2
    Capital votes, restricted drafts, opinions, fact-checks, formal models, forecasts and rankings, group positions, complaints and refusal logs, notice periods, contests and emergency votes, legislative history, evidence extracts, events, citations, record exports, and proofs.
  • L3
    Federation with a second implementation, adversarial and scale tests.

The full spec lives inside the coding prompt below. There is no live endpoint to call yet: this site hosts the specification, and each implementation hosts its own node.

The coding prompt

Paste it into an AI coding agent and it builds one independent implementation of the protocol. Fill in the three fields, copy, and go. Leave a field empty to keep its placeholder.

Open the raw file
# 30k Protocol: AI Coding Prompt (v0.13-draft)

**How to use:** Start a fresh AI coding agent session with the repo checked out. Copy everything below the line into it, after filling in the three `{{PLACEHOLDERS}}`. Each implementer runs this prompt independently, so implementations differ internally but interoperate through the API in section 5.

**Status:** Draft, produced from four sessions of a simulated design council (`DESIGN_COUNCIL.md`): implementers in session 1, joined by opinion creators in session 2 (D15-D19) and fact-checkers in session 3 (D20-D25), then a whole-council session 4 on formal grammars and ontologies (D26-D30) market and ranking stakeholders in session 5 (D31-D35), interest groups, parties, and an ombudsman in session 6 (D36-D40), political scientists, judges, and attorneys in session 7 (D41-D46), a session 8 review of the first simulated legislative session (D47-D55), military, law-enforcement, national-security, education, and budget stakeholders in session 9 (D53, D56-D60), a session 10 assessment of a full-cast rerun (D61-D65), a session 11 assessment of the appropriations route (D66-D67), a session 12 assessment of a health-care simulation with eight health-care stakeholders (D68-D71), and a session 13 that seated urban, suburban, rural, regional, and religious stakeholders and assessed an immigration simulation (D72-D77), and a session 14 that limited every council persona to observing the protocol, with no role in voting or drafting (D78-D79). Every council persona was simulated by one AI model, so all decisions await review by real stakeholders (OQ-22). Three spec bugs found by opinions on the spec have been fixed. Expect mistakes. The right response to a wrong or ambiguous requirement is an RFC (section 12), not a silent workaround.

---

## 1. Role and goal

You are a senior engineer building one independent implementation of the **30k Protocol**: an open, implementation-neutral API for a *simulated* legislature in which thousands of members (about one per 30,000 constituents, roughly 11,000) collaboratively draft and advance changes to a body of law, with no leadership controlling the agenda.

Several teams are building different implementations. They interoperate through the API in section 5 and are compared through the shared conformance suite in section 10.

- Implementation name: `{{IMPLEMENTATION_NAME}}`
- Architecture style: `{{ARCHITECTURE_STYLE}}` (for example: BFT-replicated state machine, Git-backed forge, event-sourced service with transparency log, local-first CRDT, or read-only analyzer)
- Language/runtime: `{{LANGUAGE}}`

Everything internal is your choice. Anything marked **MUST** in sections 4-9 is not.

## 2. Background (context, not instructions)

The model comes from the paper "A Blockchain Greenfield for Cato" (J. N. Phillips, 2017, https://www.openthehouse.org/1to30k.pdf). The legislative process is treated as version control of statutory text:

- **Branches** (owned by parties, caucuses, committees, coalitions) fork from the current law.
- A **merge** integrates one branch into another, after a **differential comparison** shows exactly what would change.
- A merge advances **automatically** when a statutory **approval rule** is satisfied, instead of at leadership's discretion.

The paper proposes a blockchain as the trust layer. This project treats the trust layer as an implementation choice to be **tested, not assumed**.

Scope: House-side drafting and advancement only. Bicameral reconciliation, presentment, and real elections are out of scope.

## 3. Hard constraints and non-goals

- **Simulation only.** Never handle real voter identity, real ballots, or real personal data. Rosters are synthetic. Your README MUST say: "Not for use in real elections or governance."
- **No secret law.** Anything that can affect a public branch must be revealed before the vote on it opens (section 4, restricted branches).
- **Determinism.** Same inputs produce byte-identical outputs on every implementation. No floating point anywhere in rule evaluation (integer/rational arithmetic only). No wall-clock dependence in rule evaluation except the explicit `on_deadline` mechanism (section 7).
- **Equal disclosure.** Every non-member opinion MUST disclose funding, affiliations, and AI assistance, under the same rules for every kind of author: advisors, creators, AI agents and their operators alike (section 8).
- **No truth authority.** The protocol never labels a proposal, statement, or member true or false. It computes only deterministic results (checks, fact sheets, quote verification, assertion evaluation). Every judgment, including a fact-check, is an Opinion (section 8).
- **No tokens or coins, no markets.** The paper's Bitcoin analogy is about trustless verification, not money. Nodes never host, price, settle, or hold funds for markets, and never publish aggregate odds or popularity rankings of their own. Outside markets and rankers may use the public record like anyone else (section 8).
- **No special access** (D57). No authority (law enforcement, intelligence, node operators, or the project) gets access to restricted content, keys, or data beyond what the protocol gives everyone. The protocol stores no personal data; threats and lawful process are handled outside it.
- **Equal access** (D33). Nodes MUST NOT give anyone, paying or not, earlier or richer access to log data, events, or derived views than the public endpoints provide.
- **v0.13 non-goals:** secret ballots, any religious, ethnic, nativity, party, or income attribute on members (D72), multi-winner contests, delegated surveys, real identity or sybil resistance, standardizing a consensus algorithm, semantic contradiction detection as an automated check, any canonical or binding formal model of the law, accuracy scores for members, fact-checkers, or forecasters, aggregate odds, any UI beyond a minimal read-only one.

## 4. Encoding and object model (MUST)

**Encoding.** JSON, canonicalized per RFC 8785 (JCS), UTF-8. Text is Unicode NFC. Times are RFC 3339 UTC and informational, except where section 7 says otherwise.

**Hashes.** SHA-256, written `sha256:<64 lowercase hex>`. An object's id is the hash of its canonical JSON body.

**Keys and signatures.** Ed25519 (RFC 8032). Public keys are written `ed25519:<base64url>` and a member's id is its public key. Signatures are over the bytes `"oth/0.10\n" + JCS(envelope without "sig")`.

### Objects

**Node** (a statute tree node). `nid` is a stable, path-like id (USLM-style, e.g. `/us/usc/t2/s2a/a`).
`node_hash = sha256(JCS({nid, kind, heading, text, children:[child node_hash...]}))`, with missing `heading`/`text` treated as `""`. A snapshot id is the root node's hash.

**NodeSpec** (used in ops): `{nid, kind, heading?, text?, children?: [NodeSpec]}`. Text may contain cross-references written `[[ref:<nid>]]` and definition markers written `[[def:<term>]]`, which declare that this node defines `<term>` (compared exactly after NFC). A definition's **scope** is the subtree of the defining node's parent, and its stable id is `term:<scope nid>#<term>` (D29), which formalizations, findings, and outside ontologies use to refer to it. A `[[sunset:<days>]]` marker (D58) makes `/law` drop the node's text once that many days have passed after the MR's effective date; the node remains in history. A `[[effective:YYYY-MM-DD]]` marker (D70) delays one node: for dates before it, `/law` shows that node as it stood before the MR (or omits it, if the MR added it), and a sunset on the same node counts from that date instead of the MR's effective date. A node may carry at most one of each marker (else `structure` fails). **Appropriation markers** (D66): `[[approp:<account>:<dollars>:<fiscal year>]]` tags an amount (dollars as an integer), and `[[avail:<condition id>]]` in the same node ties it to an availability condition described in that node's text. **Jurisdiction markers** (D73): `[[applies:<code>,<code>...]]` limits a node to the listed jurisdictions, and `[[opt_in:<program id>]]` applies it only in jurisdictions with a recorded opt-in to that program (section 8, research mode); codes must appear in the session's `jurisdictions` list, else `structure` fails. **No incorporation of unpublished material** (D56): text may not reference any document by hash or identifier that is not itself published in the session; such a reference fails `structure`. Laws may require classified reports to committees, but the reports live outside the protocol.

**Structure profiles** (D26). `kind` is not free text. Every session declares a structure profile, which lists the allowed kinds and which kinds may contain which. v0.4 defines two, as data in `spec/profiles/<id>.json`:
- `uslm-1`: the US hierarchy used by USLM: `title`, `subtitle`, `chapter`, `subchapter`, `part`, `subpart`, `division`, `subdivision`, `section`, `subsection`, `paragraph`, `subparagraph`, `clause`, `subclause`, `item`, `subitem`, `subsubitem`, each containing only kinds below it in that order, plus `root` at the top.
- `akn-generic`: Akoma Ntoso-style `root`, `hcontainer` (any heading container, may nest), and `content` (a leaf holding text).

A node whose kind or nesting violates the session's profile fails `structure` (section 6), which blocks. New profiles arrive by RFC. JSON stays canonical (D3); Akoma Ntoso and USLM XML are optional derived exports.

**Change**
```json
{"base":"sha256:<snapshot>","ops":[],"result":"sha256:<snapshot>","parents":["sha256:<change>"],"message":"...","author":"ed25519:...",
 "contributors":[{"key":"ed25519:...|null","name":"...","role":"drafter|reviewer|sponsor"}]}
```
`contributors` is optional and self-declared by the author: voluntary credit for outside drafters (D36). The derived footprint in the fact sheet (section 6) does not depend on it.
Ops:
- `{"op":"insert","parent_nid":"...","after_nid":"...|null","node":NodeSpec}`
- `{"op":"replace","nid":"...","pre":"sha256:<node_hash>","node":NodeSpec}`
- `{"op":"delete","nid":"...","pre":"sha256:<node_hash>"}`
- `{"op":"move","nid":"...","pre":"sha256:<node_hash>","parent_nid":"...","after_nid":"...|null"}`

You MUST verify that applying `ops` to `base` yields `result`. Derived (system) changes use author `oth:system`.

**Group:** `{id, members:[member ids], admins:[member ids]}`. Membership changes are signed by any listed admin (a v0.13 simplification). Group membership is **frozen at MR open** (below).

**Branch** (mutable reference; state derived from the log):
`{name, session, owner:"group:<id>", source:{branch, change}, head:<change id>, visibility:"public"|"restricted", protected:bool, merge_rule:RuleRef, governance_rule:RuleRef}`
- `merge_rule` governs merges *into* this branch from other branches.
- `governance_rule` governs changes to the branch's rules and ownership.
- `protected: true` forbids direct appends; only approved MRs may change the head. The session root is always protected.
- Members of the owner group may append Changes directly to an unprotected branch (as in the paper, owners control their own branch).
- No implicit defaults: creators MUST specify both rules.

**Restricted branches (commit-then-reveal).** A restricted branch's public `name` MUST be opaque (for example `r-<random>`) and MUST NOT describe its topic. For each restricted change, the public log records only a **salted commitment** `sha256(salt || change_id)` (salt: 32 random bytes, base64url), its size, and the owner group; never the bare `change_id`, because statute edits are low-entropy and an unsalted hash can be confirmed by guessing. Content is served only to authorized owner-group members (signed-challenge auth). A `branch.reveal` transaction publishes each change with its salt, and every node MUST verify it against the earlier commitment, so anyone holding a leaked copy can check it too. An MR from a restricted source is rejected (`unrevealed_source`) until revealed, and a reveal that fails its commitment is rejected (`commitment_mismatch`).

**MergeRequest** (immutable; a new version supersedes the old):
```json
{"kind":"merge","session":"sha256:...","source":{"branch":"...","head":"sha256:<change>"},
 "target":{"branch":"...","head":"sha256:<change>"},"resolutions":[{"nid":"...","node":NodeSpec|null}],
 "electorate":{"group":"group:<id>","snapshot":"sha256:<hash of sorted member ids>"},
 "rule":RuleRef,"summary":"<=280 chars, optional","supersedes":"sha256:<mr>|null",
 "effective":{"type":"on_merge"}|{"type":"date","date":"YYYY-MM-DD"},"emergency":bool,"fiscal":bool,
 "variant_of":"sha256:<mr>|null"}
```
- `effective` (D43) defaults to `on_merge`, meaning the UTC date of the checkpoint that includes the executed determination. See "Law in force" in section 6.
- `variant_of` (D68) marks the MR as a variant of a base MR that is still open; see "Variants" in section 7.
- `target.head` is the head *at open time*. `mr_id = sha256(JCS(body))`.
- `summary` is the **proposer's own** description. Nodes MUST present it attributed to the opener of the MR, never as an official or neutral summary. Any other summary is an Opinion (section 8). The derived diff is the only neutral view.
- **Governance MRs** have `kind:"governance"` and carry `set:{merge_rule?, governance_rule?, owner?}` instead of a source; the rule used is the target branch's `governance_rule`. This is how rules change: through the system itself.

**Rule reference:** `{"kind":"...","version":1,"params":{},"min_notice":int|null,"text_ref":"sha256:<plain-language statute text>"}`. `min_notice` is in seconds (D42); see section 7.

**Ballot** (body of `ballot.cast`): `{"mr":"sha256:...","choice":"approve|reject|abstain","weight":int?,"emergency":"yes|no"?,"supersedes":"sha256:<ballot>|null"}`. `emergency` is used only on MRs that request emergency handling (section 7). Ballots commit to `mr_id`, so they cannot be re-pointed at different content.

**Determination** (body of `mr.determine`): `{"mr":"sha256:...","ballots":["sha256:..."],"outcome":"approved|rejected|expired|pending"}`. A determination is a **verifiable claim, not an authority**: any keyholder may submit one, and every node re-evaluates it (section 7). Once an `approved` determination is executed, the MR is `merged` and closed; later ballots are ignored. ("Closed" is deliberate: whether the result is `final` is a separate question answered by `finality`, section 5. v0.6 said "final" here, which a session 7 reader found ambiguous.)

**Opinion:** section 8.

### Envelope (the only write format)
```json
{"v":"0.10","type":"ballot.cast","session":"sha256:...","author":"ed25519:...",
 "parents":["sha256:<tx>"],"body":{},"ts":"2026-09-21T12:00:00Z","sig":"<base64url>"}
```
`tx_id = sha256(JCS(envelope including sig))`. `parents` are the tx ids the author had seen (causal dependencies; empty only for `session.create`).

Transaction types: `session.create`, `group.create`, `group.update`, `branch.create`, `branch.append`, `branch.reveal`, `mr.open`, `ballot.cast`, `mr.determine`, `contest.open`, `contest.rank`, `contest.determine`, `key.register`, `key.rotate`, `opinion.publish`.

**Keys** (D61, D62). A non-member key MUST be introduced with `key.register` (body `{"key":"ed25519:..."}`, self-signed) and may publish opinions only after that transaction has been in a checkpoint for the node's declared `key_aging` period (default 86400 seconds; else `key_too_new`). Each session names a `registrar` group. `key.rotate` (body `{"member":"ed25519:<old>","new":"ed25519:<new>","compromised_after":"sha256:<checkpoint root>|null"}`) must be signed by both the member's new key and a registrar admin. After rotation, ballots and opinions signed by the old key in checkpoints after `compromised_after` are **ignored**, not treated as equivocation, so a thief holding a stolen key cannot void a member's vote. The registrar alone cannot rotate a member's key.

`session.create` body: `{label, seed:"sha256:<snapshot>", simulation:bool, education:bool, registrar:"group:<id>", profile:"uslm-1|akn-generic", roster:"group:<id>", root_rule:RuleRef, root_governance_rule:RuleRef, jurisdictions:[{code,name}]?, districts:"sha256:<attribute table>"?, observers:"group:<id>"?}`. `observers` (D78) names the session's observer group; see "Observers" below. `jurisdictions` (D73) declares the codes jurisdiction markers may use. `districts` (D72) is allowed only when `simulation` is true; see "District attributes" under research mode (section 10). The seed snapshot MUST conform to the profile. **Simulation flag** (D54): when `simulation` is true, every JSON response, event, export, evidence extract, and citation concerning the session MUST include `"simulation": true`, and any resolution spec on it MUST contain the word "simulated" in its `question` (else `resolution_invalid`). Text describing invented events in a simulated session must label them as simulated. **Education sessions** (D59): `education: true` requires `simulation: true`; such sessions run on private, non-federated nodes, keys are never linked to student identities in the protocol, the operator MAY purge the session after its term (the capability document declares `ephemeral: true` and every extract is labeled so), and the notice floors of D42 and D38 MAY be scaled down for rosters under 100. **Entrenchment floor** (D38): `root_governance_rule` MUST be a `threshold_vote` with `basis: electorate` and `num/den >= 2/3`, and it is fixed for the session: no governance MR may change the root branch's `governance_rule` (reject with `not_authorized`). It changes only by creating a new session. Other branches' rules are unaffected. **Notice floor** (D42): the root branch's `merge_rule` MUST have `min_notice >= 259200` (72 hours), and no governance MR may lower it within the session. The session id is the hash of that body; peers agree on it out of band. A session corresponds to one "Congress" and resets the corpus from a seed snapshot.

Authorization summary: `mr.open` and `ballot.cast` need a roster member (plus read access for `mr.open`); `branch.create` needs an admin of the owner group plus read access to `source`; `branch.append` needs an owner-group member on an unprotected branch; `opinion.publish` see section 8. Only the member who casts a ballot may sign it: no group, party, or delegate can cast or bind a ballot (D37).

**Observers** (D78). A key in the session's `observers` group MUST NOT be in the roster (else `session.create` and `group.update` are rejected with `observer_not_allowed`). Nodes MUST reject with `observer_not_allowed` every transaction signed by an observer key except `key.register` and `opinion.publish` whose `subject.type` is `spec` or `rfc` and which carries none of `proposes`, `estimate`, `feasibility`, `forecast`, `resolution`, `ranking`, `translation`, `exchange`, `attest`, or `jurisdiction_choice`. Observers therefore never cast ballots or rankings, open or determine MRs or contests, create or append to branches, sign group positions, rotate keys, or publish anything about a specific MR, change, or branch. An observer's opinion MAY list `responds_to: ["sha256:<complaint opinion>"]` (D79).

## 5. API (MUST)

Base path `/oth/v0`. JSON. Errors are RFC 9457 problem+json with a machine-readable `code`.

**One write endpoint, many read endpoints.**

| Endpoint | Purpose | Level |
|---|---|---|
| `GET /.well-known/oth` | Capability document | L0 |
| `GET /log?after=&limit=` | Accepted envelopes, in this node's order (opaque cursor) | L0 |
| `GET /objects/{id}` | Any content-addressed object (tx, change, node, MR, rule, opinion) | L0 |
| `GET /checkpoint` | Signed log head | L0 |
| `GET /heads` | Causal frontier (tx ids with no children). Required for `eventual` nodes | L0 |
| `POST /transactions` | Body `{envelope, attachments:[objects]}`; returns a Receipt | L1 |
| `GET /sessions/{id}`, `/groups/{id}` | Derived state | L1 |
| `GET /branches`, `/branches/{name}`, `/branches/{name}/tree?nid=` | Branches and subtrees at head | L1 |
| `GET /merge-requests`, `/merge-requests/{id}` | MRs with status `open|merged|rejected|superseded|stale` | L1 |
| `GET /merge-requests/{id}/diff` | Structural diff plus a unified-text rendering | L1 |
| `GET /merge-requests/{id}/checks` | Automated checks (section 6) | L1 |
| `GET /merge-requests/{id}/facts` | Deterministic fact sheet of what the MR mechanically does (section 6) | L1 |
| `GET /merge-requests/{id}/tally?by=region|density` | Derived tally under the MR's rule, with `finality` and both shares (D50); `by` only in sessions with district attributes (D72) | L1 |
| `GET /contests/{id}` | Contest options, pairwise matrix, each round's locked pairs and winner, `ranked_above_winner` after a fallback, and result (D47, D71, D77) | L2 |
| `GET /rules`, `/rules/{kind}` | Rule registry | L1 |
| `GET /opinions?subject=&author_kind=&stance=&verdict=&after=` | Opinions, with corrections and subject replies attached (section 8) | L2 |
| `GET /opinions/{id}/evaluation` | Quote verification, assertion, resolution, forecast, and ranking results for one opinion, with `finality` | L2 |
| `GET /stats?metric=&scope=&as_of=` | Registry metrics over the binding record, as of a checkpoint (D35) | L2 |
| `GET /events?nid_prefix=` | Server-sent events for new log entries, each with `finality`; optional filter to changes and MRs touching a subtree | L2 |
| `GET /law?date=YYYY-MM-DD&jurisdiction=` | The root text in force on that date (D43), optionally in one declared jurisdiction (D73), with `finality` | L1 |
| `GET /history/{nid}` | Labeled, unweighted legislative history of one node (D45) | L2 |
| `GET /extract/{hash}` | Offline-verifiable evidence extract (D44) | L2 |
| `GET /export/rollcall?session=` | Members-by-MRs choice matrix for research (D46) | L2 |
| `GET /cite/{hash}` | Hash-pinned citation: the object exactly as of `hash`, plus where it stands now | L2 |
| `GET /export/popolo?session=` | The voting record in Popolo's model: members, groups, vote events, votes, counts, each with `finality` (D30) | L2 |
| `GET /export/akn/{snapshot}`, `/export/uslm/{snapshot}` | Optional XML renderings of a snapshot | optional |
| `GET /proofs/inclusion?tx=`, `/proofs/consistency?from=&to=` | Merkle proofs (RFC 9162 style) | L2 |
| `GET /refusals?after=`, `GET /refusals/checkpoint` | Refusal log of rejected well-formed submissions, with its own signed checkpoint (D40) | L2 |
| `GET/POST /peers`, `GET /quarantine` | Federation | L3 |

Any `sha256:` reference in a body MUST be resolvable: already known, or supplied in `attachments`.

**Receipt** (signed, D39): `{"tx":"sha256:...","envelope":"sha256:<hash of the submitted envelope>","status":"accepted|rejected","log_index":int|null,"refusal_index":int|null,"errors":[{"code":"...","detail":"..."}],"checkpoint_size":N,"node":"ed25519:...","sig":"..."}`, signed over `"oth/0.10-receipt\n" + JCS(receipt without "sig")`. A signed rejection is evidence the node cannot later deny.

**Refusal log** (D40). Every rejected submission that is well-formed and correctly signed is appended to a separate append-only refusal log: `{envelope, type, author, code, checkpoint_size}`, with an RFC 9162 Merkle root and a signed checkpoint, like the main log. `schema` and `bad_signature` rejections MAY be omitted; `rate_limited` rejections MAY be summarized as one entry per key per checkpoint with a count. The envelope itself is not stored; its submitter holds it and can present it.

**Error codes:** `schema`, `bad_signature`, `unknown_parent`, `not_authorized`, `no_common_ancestor`, `unrevealed_source`, `commitment_mismatch`, `stale_mr`, `ballot_invalid`, `capital_exceeded`, `rule_unknown`, `rule_not_supported`, `disclosure_missing`, `rate_limited`, `quote_mismatch`, `assertion_unknown`, `correction_invalid`, `profile_unknown`, `anchor_invalid`, `metric_unknown`, `resolution_invalid`, `group_not_authorized`, `evidence_invalid`, `notice_not_elapsed`, `effective_invalid`, `contest_invalid`, `emergency_invalid`, `unpublished_reference`, `estimate_missing`, `key_too_new`, `rotation_invalid`, `deadline_too_short`, `district_attribute_invalid`, `jurisdiction_unknown`, `attestation_invalid`, `observer_not_allowed`, `complaint_invalid`, `addressee_invalid`, `variant_invalid`, `estimate_incomplete`.

**Finality** (D17, clarified in v0.3). Every event, tally, MR status, determination, and assertion result a node serves carries `finality`:
- `provisional`: anything not yet included in a signed checkpoint. Every live tally, and every MR status or assertion result that depends on an MR with no executed determination, is `provisional`.
- `final`: included in a signed checkpoint on a `strong` node, and, for anything describing an MR's outcome, only once its determination is executed and checkpointed.
- `final_advisory`: the same conditions met on an `eventual` node's per-replica checkpoint (it may still be contested, see OQ-1 and OQ-3).

An `eventual` node MUST NOT emit `final` at all. (v0.2 defined `final` only for determinations, leaving checkpointed opinions and other objects without a label; a session 3 finding.) The protocol cannot control how outside parties report results; this field exists so the accurate framing is the easy one.

**Citations** (D16). `GET /cite/{hash}` takes any content-addressed id (change, snapshot, node, MR, opinion) and returns `{"object":{...},"as_of":"sha256:...","latest":"sha256:...|null","diff_to_latest":"<url>|null","status":"...","finality":"..."}`. `object` is always the content at `as_of`, never a newer version. `latest` points to the current version of the same thing where one exists (a branch head containing the node, a superseding MR). Citations MUST use hashes, never branch names. Restricted content resolves only after reveal.

**Capability document:**
```json
{"protocol":"oth/0.10","implementation":{"name":"...","version":"...","source":"https://github.com/..."},
 "consistency":"strong|eventual","trust_model":"bft-replicated|single-writer-transparency|crdt-replicas|git-signed|read-only",
 "rule_kinds":["always_allow","designated_approvers","threshold_vote","capital_vote"],
 "assertion_kinds":["ballot_choice","mr_outcome","tally","authored","text_contains"],
 "profiles":["uslm-1","akn-generic"],"exports":["popolo","akn"],
 "refusal_log":true,"contests":true,"simulation":false,"education":false,"ephemeral":false,"key_aging":86400,"extract_cosigners":["ed25519:<peer node key>"],
 "metrics":["approve_share","turnout","ballots_cast","mrs_opened","mrs_merged","checkpoints_to_determination"],
 "conformance_level":"L2","opinion_policy":"members-and-signed-advisors","opinion_rate_limit":"<human-readable per-key policy>","node_key":"ed25519:..."}
```

**Checkpoint:** `{"size":N,"root":"sha256:...","ts":"...","node":"ed25519:...","sig":"..."}`, where `root` is the RFC 9162 Merkle tree hash over tx ids in log order. `strong` nodes MUST produce checkpoints. `eventual` nodes MAY produce per-replica checkpoints and MUST expose `/heads`. Any trust model (BFT consensus, single-writer transparency log, per-replica logs, signed git history) can satisfy this.

## 6. Merge semantics (MUST)

1. **Merge-base:** the nearest common ancestor of `source.head` and `target.head` in the Change DAG. If several, choose the smallest id. If none, reject with `no_common_ancestor`.
2. **Derived patch:** compare base to source. For each `nid` touched:
   - Changed on one side only: take that side.
   - Changed identically on both: take it.
   - Changed differently: a **conflict**, which MUST have a matching entry in `resolutions` or the MR is rejected. A resolution's `node` is the final NodeSpec (or `null` to delete).
3. The patch is applied to `target.head`. If any `pre` hash no longer matches when an approval is executed, the MR becomes `stale` and must be re-opened as a new version (ballots do not carry over).
4. **On approval**, the implementation derives one merge Change (`parents:[target head, source head]`, author `oth:system`) and sets it as the target head. Every node MUST derive the identical change and head.
5. **Checks** (`/checks`, D24). All are computed on the target as it would be after the merge. Only `structure` blocks; every other check is a warning shown on the MR and never affects evaluation.
   - `structure`: unique `nid`s, valid parents, `result` verifies, and every node's `kind` and nesting conform to the session's profile. Failure blocks the MR.
   - `references`: every `[[ref:nid]]` anywhere in the merged target resolves, so an MR that breaks a cross-reference elsewhere is caught, not only in the nodes it touches.
   - `definitions`: two nodes define the same term with different node text and their scopes overlap (one scope root is an ancestor-or-self of the other).
   - `reference_cycles`: a cycle in the graph of `[[ref:]]` edges between nodes.
   - `approp_unmarked` (D66): a node in a `fiscal: true` MR contains a dollar amount with no `[[approp:]]` marker (warning).
   - `fiscal_hint` (D53): the MR is declared `fiscal: false` but its text mentions a dollar amount (`$` followed by a digit) or the words "appropriat", "funds", or "obligat" (case-insensitive prefix match).
   - `effective_before_feasible` (D70): a node's effective date (its `[[effective:]]` marker, else the MR's `effective` date; `on_merge` is not checked) is earlier than the `earliest` date in any accepted feasibility opinion on the MR that names that node.

   Each warning lists the `nid`s involved. Semantic problems (provisions that contradict in meaning, undefined concepts, misleading drafting) are not checks: they are published as findings with verdict `inconsistent` (section 8).
6. **Fact sheet** (`/facts`, D23). A derived, neutral view of what the MR mechanically does, byte-identical on every node:
   ```json
   {"mr":"sha256:...","nodes":{"added":["nid"],"removed":["nid"],"modified":["nid"],"moved":["nid"]},
    "numbers":[{"nid":"...","before":["30"],"after":["45"]}],
    "references":{"added":[{"from":"nid","to":"nid"}],"removed":[{"from":"nid","to":"nid"}]},
    "definitions":{"added":["term id"],"changed":["term id"],"removed":["term id"]},
    "anchor_drift":[{"formalization":"sha256:<opinion>","nids":["nid"]}],
    "sunsets":[{"nid":"...","days":90}],"effective_dates":[{"nid":"...","date":"YYYY-MM-DD"}],
    "estimates":["sha256:<opinion>"],
    "estimate_totals":[{"estimate":"sha256:<opinion>","as_written":"<int>","if_permanent":"<int>|null"}],
    "feasibility":["sha256:<opinion>"],"versus_base":null,"ranked_above_winner":null,
    "coverage":[{"nid":"...","applies":["<code>"]|null,"opt_in":"<program id>|null"}],
    "appropriations":{"by_account":[{"account":"...","fy":2027,"dollars":"<int>","fenced":"<int>"}],"total":"<int>"},
    "origin":[{"nid":"...","opinion":"sha256:...","author":"ed25519:...","author_kind":"...","on_behalf_of":"group:<id>|null"}],
    "reverts":[{"nid":"...","to":"sha256:<node_hash>","previously_in":"sha256:<change>"}],
    "authoritative":false,"finality":"..."}
   ```
   Lists are sorted by code point. `anchor_drift` lists every accepted formalization (section 8) that has an anchor on a node this MR modifies, removes, or moves. `origin` is the **legislative footprint** (D36): every node in the MR's derived patch whose `node_hash` equals a node in a change listed in the `proposes` of an accepted non-member or group opinion that is a causal ancestor of the change introducing it. It shows what outside drafting is public, not everything that happened. `estimate_totals` (D69) sums each estimate's `by_year` outlays as written and, when present, its `if_permanent` series. `versus_base` (D68) is the structural diff against the base MR for a variant, else `null`. `ranked_above_winner` (D77) copies the contest's list when this MR was approved as a fallback winner, else `null`. `coverage` (D73) lists every node in the patch that carries a jurisdiction marker. `reverts` (D41) lists every node in the patch whose resulting `node_hash` equals a hash that same `nid` had at any earlier root head in this session, other than the current one.
7. **Law in force** (D43). Order the session's merges into the root by log order. The law in force on date `d` is the root head after the longest prefix of those merges whose effective dates are all `<= d`, where an `on_merge` date is the UTC date of the checkpoint including its executed determination. A merge with a later effective date therefore holds back every merge after it. With `jurisdiction=<code>` (D73), the same snapshot is served with every node whose `[[applies:]]` list omits the code, or whose `[[opt_in:]]` program has no opt-in for that jurisdiction in effect on `d`, shown as it stood before the merge that added the marker (or omitted, if that merge added the node); an undeclared code returns `jurisdiction_unknown`. `GET /law?date=` returns that snapshot id and tree, labeled with `finality`. An `effective` date earlier than the MR's open date is rejected with `effective_invalid`.
8. **Authority labels** (D45). Every derived view that is not enacted text (fact sheets, checks, history, evaluations, exports of opinions) MUST include `"authoritative": false`. Only snapshots of the root branch are law, and only as served by `/law`. A **number** is a maximal match of `[0-9][0-9,]*(\.[0-9]+)?` in a node's heading or text, kept as the literal string; `numbers` lists, for each modified node whose number sequence changed, both full sequences in document order. The fact sheet never interprets or judges the MR's `summary`; it exists so that checking a summary against the change is quick for anyone.

## 7. Rules (MUST for the L1/L2 kinds)

Evaluation is a **pure function**: `evaluate(rule, electorate_snapshot, ballots, mr) -> {outcome, tally}`. Determinations are verified by re-running it over exactly the `ballots` the determination lists.

**Ballot validity:** (a) valid signature by the member's current key, or by an earlier key in a checkpoint not after its rotation's `compromised_after` (D62); (b) author is in the frozen electorate; (c) `ballot.mr == mr_id`; (d) one effective ballot per member. If a member has two or more ballots not linked by a `supersedes` chain, **all** of that member's ballots on this MR are void (equivocation), and the member is counted in `tally.void`.

Let N be the electorate size, A/R/B the effective approve/reject/abstain counts, and V the void-member count.

| Kind | Params | Evaluation |
|---|---|---|
| `always_allow` | none | Approved once the MR is open and `structure` passes. |
| `designated_approvers` | `approvers:"group:<id>"`, `required:int` | With n = group size: approved when A >= required; rejected when n - R - V < required (void approvers can no longer approve, matching `on_threshold`; a v0.3 fix from a session 3 finding); else pending. |
| `threshold_vote` | `num,den` (0 < num <= den), `cmp:"gt"|"gte"`, `basis:"electorate"|"votes_cast"`, `quorum_num,quorum_den`, `closing:"on_threshold"|"on_deadline"`, `deadline?`, `reversion?:{num,den}` | D = N if basis is `electorate`, else A + R. Passes iff quorum holds `(A+R+B)*quorum_den >= quorum_num*N`, D > 0, and `A*den cmp num*D`. If `reversion` is set and the MR's fact sheet has any `reverts`, `reversion.num/den` replaces `num/den` (D41). |
| `capital_vote` (strong nodes only) | `budget:int`, `cost:"linear"|"quadratic"`, `num,den`, `cmp`, `closing:"on_deadline"` | A member's capital = `budget` minus the cost of their effective non-abstain ballots across the session (linear cost = weight, quadratic = weight squared). A ballot that overspends is invalid (`capital_exceeded`). WA/WR are summed weights; passes iff `WA*den cmp num*(WA+WR)` and `WA+WR > 0`. A superseded ballot refunds its cost. |

**Closing.**
- `on_threshold` requires `basis: electorate`. Approved as soon as the rule passes on current ballots. Rejected as soon as approval becomes mathematically impossible: when `(N-R-V)*den cmp num*N` fails (the threshold can no longer be reached) **or** `(N-V)*quorum_den < quorum_num*N` (quorum can no longer be met). No clock is needed. (v0.3 omitted the quorum test, so an MR could stay pending forever; found by a machine-checked property in session 4.)
- **Notice** (D42): when the rule has `min_notice`, an `approved` determination is valid only at a checkpoint time at least `min_notice` seconds after the checkpoint time of the checkpoint that included the `mr.open`. `strong` nodes MUST refuse earlier ones (`notice_not_elapsed`); with `on_threshold` the MR simply stays pending until notice has elapsed. Rejections need no notice. `eventual` nodes treat notice as advisory and record the elapsed checkpoint time in the determination's evaluation.
- **Fiscal notice** (D53): for an MR with `fiscal: true`, the notice period starts only when at least one accepted opinion with an `estimate` (section 8) on that MR exists; until then a determination is refused (`estimate_missing`).
- **Amendment notice** (D48): an MR that `supersedes` another, and whose touched nodes are all among the nodes the superseded MR touched, needs `min(min_notice, 86400)` (24 hours) of notice. Its fact sheet includes `versus_superseded`, the structural diff between the two versions. Ballots never carry over (D9).
- **Emergency path** (D49): an MR with `emergency: true` is designated an emergency when at least 2/3 of the electorate cast ballots with `emergency: "yes"` (`yes*3 >= 2*N`), independent of their `choice`. A designated emergency MR needs only 24 hours of notice. If it merges, the node derives an **expiry change** (author `oth:system`) restoring the pre-merge text of every node it touched, applied at the first checkpoint 30 days of checkpoint time after the merge, except for nodes changed by a later non-emergency MR. If designation fails, the MR continues under normal notice. `emergency` on a governance MR is rejected (`emergency_invalid`). `eventual` nodes treat the 30 days as advisory, as with deadlines.
- **Minimum window** (D74): an `on_deadline` rule's `deadline`, and a contest's ranking deadline, MUST be at least 259200 seconds (72 hours) of checkpoint time after the later of the checkpoint including the `mr.open` and the one including the `contest.open`; else `deadline_too_short`. The protocol keeps no calendar of holidays or days of rest.
- `on_deadline`: pending until a determination is submitted at or after `deadline`. `strong` nodes MUST refuse earlier ones using checkpoint time. `eventual` nodes treat the deadline as advisory; the determination records the ballot set and verifiers re-evaluate exactly that set. Outcome is `approved` if the rule passes, `expired` if quorum is unmet, otherwise `rejected`.
- Rules that are not in a node's `rule_kinds` return `rule_not_supported`.

**Shares** (D50). Every tally, ranking, and fact sheet that reports support MUST show `approve_of_cast` (`A/(A+R)`), `approve_of_electorate` (`A/N`), and `rule_share`, naming the share and basis the MR's rule actually uses, side by side.

**Contests** (D47; L2). When several open MRs target the same branch and their touched nodes overlap, any member may group them with `contest.open` `{"options":["sha256:<mr>",...]}` (2–9 options, all under the same non-`capital_vote` rule; else `contest_invalid`). From then on, those MRs are decided only through the contest, and plain ballots on them are ignored. Members rank options with `contest.rank` `{"contest":"sha256:...","ranking":["sha256:<mr>"|"status_quo",...],"supersedes":...}`; unranked options tie below all ranked ones. Two unlinked rankings void the member (as D10).
- **Method: ranked pairs.** For every pair of options (including `status_quo`), count members ranking each above the other. Sort pairs by winning count descending, then by smaller losing count, then by option id. Lock each pair unless it creates a cycle. The winner is the option no locked pair beats.
- **The winner must still pass its own rule against the status quo**: approvals `A` are members ranking it above `status_quo`, rejections `R` those ranking `status_quo` above it, abstentions `B` those ranking them equal. Notice runs from the latest option's open. If it passes, that MR is approved and the others are rejected with reason `contest_lost`. If the winner is `status_quo`, all options are rejected.
- **Fallback** (D71). If the winner fails its rule, it is rejected with reason `contest_rule_failed`, removed, and the method re-runs on the remaining options (pairwise counts are reused; `status_quo` always remains). Rounds repeat until an option passes its rule, which is approved while the others are rejected with `contest_lost`, or until `status_quo` wins or is the only option left, which rejects all remaining options. `/contests/{id}` lists every round with its locked pairs and winner. **Fallback transparency** (D77): when the approved option is not the first-round winner, `/contests/{id}` and the approved MR's fact sheet MUST show `ranked_above_winner: [{"option":"sha256:<mr>","above_winner":int,"below_winner":int}]` for every removed option, from the pairwise counts. (D47 as first written rejected every option when the winner failed, which in Simulation 4 rejected an option that had a majority of the whole electorate.)
- **Variants** (D68). An MR with `variant_of` must target the same branch, use the same rule, and change only nodes its base's derived patch touches (else `variant_invalid`); its base must not itself be a variant. Opening a variant groups it with its base in a contest automatically, or adds it to the contest the base is already in (up to the 9-option limit). A variant needs `min(min_notice, 86400)` of notice, as in D48, and its fact sheet includes `versus_base`. This lets members decide a single provision of a package by ranking alternatives, without unbundling the package.
- `contest.determine` is a verifiable claim like `mr.determine`; every node re-runs the method. A contest never lowers the bar any option would face alone.

**Registry:** each kind has a statute-ready plain-language text in `spec/rules/<kind>.md`, and machine params MUST match it. Test vectors live in `conformance/vectors/rules/<kind>.json`.

**Formal reference** (D27). Each kind also has a normative reference implementation in Catala, `spec/rules/<kind>.catala_en`, written paragraph by paragraph alongside the plain text. The rule vectors are generated from it, and the spec repository machine-checks these properties for every kind: determinism; integer-only arithmetic; monotonicity of `on_threshold` (more approvals never turn approval into rejection); rejection soundness (never reject while approval is possible); rejection completeness (never stay pending once approval is impossible); and equivocation never increases `A`. **You do not need Catala or any proof tool.** Pass the vectors. If the plain text and the Catala reference disagree, that is a spec bug: open an RFC. New kinds arrive by RFC. Reserved for a future version: `ranked_choice` (needs a "contest" of competing MRs), `delegated_survey`.

## 8. Opinions (MUST at L2; MAY earlier)

Opinions are non-binding, signed, public statements, appended like any transaction.

```json
{"subject":{"type":"session|branch|change|mr|rule|opinion|rfc|spec","id":"..."},
 "author_kind":"member|delegate|advisor|implementation|group","on_behalf_of":"group:<id>|null",
 "disclosure":{"operator":"...","tool":"...","principal":"ed25519:...",
               "funding":[{"source":"...","kind":"ads|sponsor|subscribers|grant|employer|platform|none|other"}],
               "affiliations":["..."],"ai_assistance":"none|assisted|generated",
               "stakes":[{"subject":"sha256:...","direction":"yes|no|other","venue":"...","size_band":"<1k|1k-10k|10k-100k|>100k|null"}],
               "foreign_principal":{"country":"...","relationship":"..."}|null},
 "stance":"support|oppose|amend|question|neutral","summary":"<=280 chars","body":"markdown",
 "proposes":["sha256:<change>"],"replies_to":"sha256:<opinion>|null","evidence":[{"uri":"...","hash":"sha256:..."}],
 "quotes":[{"as_of":"sha256:<snapshot>","nid":"...","text":"..."}],
 "findings":[Finding],"corrects":"sha256:<opinion>|null","method":{"inputs":["sha256:..."]},
 "formalization":Formalization|null,"resolution":Resolution|null,"forecast":Forecast|null,"ranking":Ranking|null,
 "estimate":Estimate|null,"translation":Translation|null}
```
`quotes`, `findings`, `corrects`, `method`, `formalization`, `resolution`, `forecast`, `ranking`, `estimate`, `feasibility`, `translation`, `attest`, `jurisdiction_choice`, `responds_to`, and `complaint` are optional; `complaint` is defined under "Complaints" below.

- `member`: a roster member. `delegate`: acts for a member and MUST name `disclosure.principal`. `advisor`: any other keyholder, human or AI, including journalists, social-media creators, think tanks, and lobbyists. There is **no** creator-specific kind and **no** verified badge or status flag (D15). `implementation`: a node speaking about the protocol itself (`subject.type` is `rfc` or `spec`). `group`: a position of a group, such as a party, caucus, or coalition (D37): `on_behalf_of` names the group, the signer MUST be one of its admins at that causal point (else `group_not_authorized`), and the opinion MUST carry full disclosure (funding, affiliations, stakes, AI assistance) whoever signs it, since a group is not a member. Group positions are never counted, and nothing derived from them affects evaluation.
- Every non-member kind MUST include `disclosure` with `funding` (use `[{"kind":"none"}]` if unfunded), `affiliations` (may be empty), and `ai_assistance`. The rules are identical for every non-member kind. Missing fields are rejected with `disclosure_missing`. `ai_assistance` is `generated` when an AI agent wrote the opinion, `assisted` when a human used AI tools to write it, else `none`; when not `none`, `disclosure.tool` MUST name the model and tool. Members MAY include `disclosure` (whether they must disclose non-financial conflicts is OQ-9).
- **Foreign principals** (D60). `foreign_principal` is required for every non-member opinion (`null` if none) and covers only acting at the direction or expense of a foreign government or foreign political party. Nationality and residence are never asked.
- **Cost estimates** (D53). `estimate: {"mr":"sha256:...","baseline":"<=500 chars","by_year":[{"year":2026,"outlays":"<int dollars>","range_low":"...","range_high":"..."}],"assumptions":"<=2000 chars"}` with `method.inputs` listing sources by hash. Any keyholder may publish one; nodes show all estimates on an MR side by side, none official.
- **If-permanent series** (D69). An `estimate` on an MR whose fact sheet lists any sunset MUST include `"if_permanent":[{"year":2026,"outlays":"<int dollars>","range_low":"...","range_high":"..."}]`, the same years assuming every `[[sunset:]]` marker in the MR is removed, or `"if_permanent":null` with `"if_permanent_reason":"<=500 chars"`; otherwise it is rejected with `estimate_incomplete`. `by_year` on any estimate MUST cover at least the ten years starting with the effective year. The fact sheet shows both totals side by side (`estimate_totals`).
- **Distributional series** (D75). An `estimate` MAY include `"distribution":{"by_region":[{"region":"...","by_year":[...]}],"by_density":[{"density":"...","by_year":[...]}],"local_costs":[{"region":"...|null","density":"...|null","bearer":"state|county|city|other","by_year":[...]}]}`, using the session's declared region and density lists (D72; else `estimate_incomplete`). Nodes show distributions side by side with the rest of each estimate; none is official or required.
- **Affiliation attestations** (D76). Any key may publish `attest: {"key":"ed25519:...","organization":"<=200 chars","role":"<=200 chars","expires":"YYYY-MM-DD","revoked":bool}`. For each organization an opinion lists in `affiliations`, nodes MUST show `attested_by: "ed25519:<attesting key>"` when an unexpired, unrevoked attestation of the opinion's signing key for that organization exists from a key whose own disclosure lists that organization, else `unattested`. Malformed attestations are rejected with `attestation_invalid`. Attestations are never required, and nodes never judge which key truly speaks for an organization.
- **Feasibility** (D70). `feasibility: {"mr":"sha256:...","implementer":"<=200 chars","nodes":["nid"],"earliest":"YYYY-MM-DD","depends_on":["<=200 chars"],"notes":"<=2000 chars"}`. Any keyholder may publish one, with the usual disclosures. Nodes show all feasibility opinions on an MR side by side; they never block and are never counted.
- **Translations** (D63). `translation: {"language":"<BCP 47>","of":"sha256:<snapshot or opinion>","passages":[{"quote":0,"text":"..."}]}`; every passage is anchored to a verified quote of the source. Translations are opinions, always `authoritative: false`, and open to findings.
- **Statement mirrors** (D52). A member may publish an opinion with `mirror: {"kind":"statement|denial","source_uri":"...","archived":"sha256:<archived copy>","said_at":"YYYY-MM-DD"}` to put a statement made elsewhere on the record, or to deny one attributed to them. The opinion's `body` holds the statement text. Quotes (D21) may target an opinion body as `{"object":"sha256:<opinion>","text":"..."}`, verified the same way. Mirrors are voluntary.
- **Stakes** (D32). `stakes` lists any financial position the author holds on the outcome of the opinion's subject, or on any MR or assertion it names: direction and venue are required, `size_band` is optional. Every non-member opinion MUST include `stakes` (`[]` declares none; missing is `disclosure_missing`). Members SHOULD publish a `stakes` disclosure on an MR before voting on it; nodes MUST show a member's latest declared stakes on an MR next to their ballot in `/tally`. The protocol cannot see positions held elsewhere (OQ-17).
- Opinions are **never** counted in binding evaluation. (A future `delegated_survey` kind may consume member opinions.)
- Nodes MUST declare `opinion_policy` (e.g. `members-only`, `members-and-signed-advisors`, `open`) and MAY refuse others.
- **Rate limits, not reputation** (D18, D51). Nodes MAY rate-limit opinions per signing key (`rate_limited`), and MUST NOT group keys by any disclosure field, group membership, affiliation, or stance when doing so, MUST describe the policy in `opinion_rate_limit`, and report its effect as a metric. The protocol defines **no** reputation scores, endorsement counts, likes, or other per-opinion tallies, because counts on opinions become pseudo-votes. Nodes MAY cluster near-duplicate opinions in derived views, but every accepted opinion MUST remain individually retrievable through `/objects` and `/opinions`.
- **Dogfooding:** publish your opinions on the spec and RFCs as `implementation` opinions, and mirror them into `OPINIONS.md` (section 11).

### Fact-checking (D20-D25; L2)

Fact-checkers are ordinary keyholders publishing as `advisor` (or `implementation`, for an analyzer node). There is no fact-checker kind, accreditation, or badge.

**Verified quotes** (D21). Any opinion that quotes protocol content MUST declare each quote in `quotes`. A node MUST verify that `text` occurs exactly (after NFC, no other normalization) within the `heading` or `text` of node `nid` in snapshot `as_of`, and reject the opinion with `quote_mismatch` otherwise. Quotes from restricted content are rejected until it is revealed.

**Findings** (D20). A finding is one checked claim:
```json
{"target":{"type":"mr|change|opinion|node|ballot|external","id":"sha256:...|<uri>"},
 "claim":{"quote":0,"text":"..."},
 "verdict":"accurate|misleading|false|unsupported|inconsistent|unverifiable",
 "rationale":"<=1000 chars","evidence":[{"uri":"...","hash":"sha256:..."}],
 "assertion":Assertion|null,"related_quotes":[1,2]}
```
- `claim.quote` indexes into the opinion's verified `quotes` when the claim is protocol content; `claim.text` is used for claims made outside the protocol (an interview, a post), with `target.type: external` and evidence hashing the source.
- `inconsistent` requires at least two verified quotes (`claim.quote` plus `related_quotes`) showing the conflicting provisions or statements.
- When `ai_assistance` is `generated`, `method.inputs` MUST list the hashes of every protocol object given to the model, and `disclosure.tool` MUST pin the model version.
- Each finding maps to schema.org `ClaimReview` (`claimReviewed` = claim, `reviewRating.alternateName` = verdict, `itemReviewed` = target). Implementations MAY serve that rendering; the JSON above is normative.
- The `verdict` is the author's judgment. Nodes MUST present it attributed to its author and MUST NOT aggregate verdicts into labels on MRs, nodes, or members.

**Assertions** (D22). A closed registry of claims about the record that every node evaluates deterministically. Any opinion may carry them (inside findings, or a member stating their own record).

| Kind | Params | Holds when |
|---|---|---|
| `ballot_choice` | `member`, `mr`, `choice` | The member's effective ballot on the MR has that choice (`undetermined` if none; `fails` if void or different) |
| `mr_outcome` | `mr`, `outcome` | The MR's executed determination has that outcome (`undetermined` while open) |
| `tally` | `mr`, `approve`, `reject`, `abstain` | The counts match the tally at the executed determination (`undetermined` while open) |
| `authored` | `object`, `author` | The object's signed author is that key |
| `text_contains` | `as_of`, `nid`, `text` | The node's text at that snapshot contains `text` exactly |

`GET /opinions/{id}/evaluation` returns, for each quote and assertion, `{"index":n,"result":"holds|fails|undetermined","finality":"..."}`. Results are recomputed as the record changes and carry `finality` per section 5. Unknown kinds are rejected with `assertion_unknown`. New kinds arrive by RFC. `ballot_choice` depends on public ballots and will change if OQ-4 introduces secret ballots.

**Corrections and replies** (D25).
- `corrects` names an earlier opinion by the **same author**; otherwise reject with `correction_invalid`. The original is never altered or hidden. `/opinions` and `/cite` on the original MUST include `corrected_by` links.
- When a finding targets an object whose signed author is a member, every opinion by that member with `replies_to` set to the fact-check MUST be listed with it in `/opinions` results (`replies_from_subject`).
- The protocol defines **no** accuracy scores, "lie counts," or rankings of members or fact-checkers (extending D18). Anyone may compute such things from public data and publish them as an opinion, which is itself open to findings.

### Resolutions, forecasts, and rankings (D31, D34, D35; L2)

These let markets, forecasters, and rankers make claims every node can check. Nodes evaluate them; they never host markets, publish odds, or rank anything themselves.

**Resolution spec** (D31). States ahead of time how a question resolves:
```json
{"question":"<=280 chars","assertion":Assertion,"finality":"final|final_advisory",
 "nodes":["ed25519:<node key>"],"min_agree":2}
```
Resolves `resolved_true` when at least `min_agree` of the listed nodes report the assertion `holds` at the required finality, `resolved_false` when at least `min_agree` report `fails` at it, otherwise `unresolved`. A node MUST report `unsatisfiable` when it can determine the spec can never resolve, for example when it requires `final` and fewer than `min_agree` listed nodes declare `consistency: strong`. A malformed spec is rejected with `resolution_invalid`. A spec is immutable; a changed spec is a new opinion with `corrects`.

**Forecast** (D34): `{"assertion":Assertion,"p_num":int,"p_den":int,"as_of":"sha256:<checkpoint root>"}` with `0 <= p_num <= p_den`. Once the assertion is decided, nodes report it `holds` or `fails` in `/evaluation`. Nodes MUST NOT aggregate forecasts into odds on an MR and keep no forecaster scores.

**Metrics and rankings** (D35). A closed registry over the binding record only (never opinions):

| Metric | Scope | Value |
|---|---|---|
| `approve_share` | MR | `A/(A+R)` as an integer fraction, from the tally (also called `approve_of_cast`; always shown beside `approve_of_electorate`, D50) |
| `approve_of_electorate` | MR | `A/N` as an integer fraction |
| `turnout` | MR | `(A+R+B)/N` as an integer fraction |
| `ballots_cast` | MR, member, or session | count of effective ballots |
| `mrs_opened`, `mrs_merged` | member, group, or session | counts |
| `checkpoints_to_determination` | MR | checkpoints between open and executed determination |

`GET /stats` returns a metric for a scope as of a checkpoint, with `finality`. A **ranking** is `{"metric":"...","scope_type":"mr|member|group","filter":{"session":"..."},"as_of":"sha256:<checkpoint root>","order":"desc|asc","items":[{"id":"...","value":"<num>/<den>|int"}]}`; nodes recompute it and report `holds` if every value and the order match, else `fails`. Ties are ordered by id. Unknown metrics are rejected with `metric_unknown`. New metrics arrive by RFC and MUST be computable from ballots, MRs, and determinations alone.

### Complaints (D39; L2, re-validation L3)

There is no ombudsman office in the protocol. Anyone may file a complaint or investigate one, and reports are ordinary opinions with findings (section 8).

```json
{"respondent":{"type":"node|group|key|spec","id":"..."},
 "issue":"refusal|quarantine|rate_limit|opinion_policy|equal_access|entrenchment|protocol|other",
 "addressed_to":["ed25519:<observer key>"],
 "evidence":[{"receipt":Receipt}|{"refusal":{"node":"ed25519:...","index":int}}|{"envelope":Envelope}],
 "remedy_sought":"<=1000 chars"}
```
- Nodes verify every receipt signature and refusal-log inclusion in `evidence` (else `evidence_invalid`) and report results in `/opinions/{id}/evaluation`.
- **Protocol complaints** (D79). `respondent.type: "spec"` (with `id` naming the spec version or section) requires `issue: "protocol"`, and `issue: "protocol"` requires `respondent.type: "spec"` (else `complaint_invalid`). Any keyholder, member or not, may file one. `addressed_to` is optional and every key in it MUST be in the session's `observers` group (else `addressee_invalid`). `/opinions` MUST list, with each complaint, every observer opinion whose `responds_to` names it (`observer_replies`). Protocol complaints never affect evaluation or any node's behavior.
- At L3, a node MAY **re-validate** a refused envelope against the respondent node's published log as of the receipt's `checkpoint_size`, and reports `rejection_consistent | rejection_inconsistent | not_checked`.
- Complaints, findings, and recommendations have no binding effect on any node. Complainants who need confidentiality file outside the protocol; a published report cites evidence by hash.

### History, evidence, and research exports (D44-D46; L2)

- **`/history/{nid}`** returns, in merge order, every merged MR that changed the node, each with: the proposer's `summary` (labeled as the proposer's words), member opinions, group positions, and other opinions, in separate labeled lists. Nothing is ranked, filtered by stance, or weighted. `"authoritative": false`.
- **`/extract/{hash}`** returns `{"object":{...},"supporting":[objects needed to recompute its hash and, for an MR or determination, its ballots and open/determine transactions],"inclusion":{proof against the checkpoint},"checkpoint":Checkpoint,"cosigned":[Checkpoints from other nodes at the same or later size, with consistency proofs]}`. It MUST be verifiable offline with no network access. The spec repository ships a small reference verifier.
- **`/export/rollcall`** returns `{"members":[...],"mrs":[...],"choices":[[ "approve|reject|abstain|void|null", ...]],"weights":[[int|null]]|null,"finality":[...]}`. `weights` comes from `capital_vote` and MAY be omitted (OQ-20).

### Formalizations (D28; L2)

**The text is the law; a formal model is an interpretation.** Anyone may publish a formal model of statute text as an Opinion with a `formalization` field. It is never binding, never canonical, and nodes never execute it. Competing formalizations of the same text are expected.

```json
{"language":"catala|legalruleml|logical-english|owl|other","language_version":"...",
 "source":"sha256:<attached source file>",
 "anchors":[{"rule":"<identifier inside the source>","quote":0}],
 "terms":["term:<scope nid>#<term>"]}
```
- Every formal rule in the source MUST have at least one anchor, and every anchor points into the opinion's verified `quotes` (D21), so each rule maps back to the exact sentence it encodes. Nodes check that anchors reference valid quote indexes and that `source` resolves (else `anchor_invalid`); they do not parse the source language.
- `terms` links the model to defined-term ids (D29). An outside ontology of terms is published the same way, with `language: "owl"` or similar.
- A finding produced by running a deterministic reasoner or checker over a formalization MUST list that formalization's opinion id and the tool's version in `method.inputs` and `disclosure.tool`, so anyone can re-run it.
- When an MR touches an anchored node, the formalization appears in that MR's `anchor_drift` (section 6). Its author may publish a new formalization with `corrects` set to the old one.

## 9. Federation (MUST at L3)

- **Sync:** pull a peer's `/log` (or `/heads` plus missing parents for `eventual` nodes). Validate every envelope: signature, parents present, authorization against state at that causal point. Invalid envelopes go to `/quarantine`, never into state.
- **Never trust a peer's determination.** Re-evaluate it.
- **Forks:** `strong` nodes that receive a peer checkpoint failing the consistency proof MUST surface `fork_detected` and stop syncing from that peer. `eventual` nodes merge DAGs; two conflicting determinations for one MR are flagged `contradictory_determination` and left unexecuted until resolved (open question OQ-3).
- Peers are configured manually in v0.13.

## 10. Conformance (what "done" means)

**Levels**
- **L0 verifier:** ingest a peer's log, verify everything, recompute derived state, match its checkpoint. (A valid stopping point for an analyzer implementation.)
- **L1 core:** sessions, groups, branches, changes, MRs, merge semantics, all checks including structure profiles, the fact sheet, rule kinds `always_allow` / `designated_approvers` / `threshold_vote`.
- **L2:** `capital_vote` (strong only), restricted branches and reveal, opinions with verified quotes, findings, assertions, corrections and formalizations, stakes, resolutions, forecasts, rankings and `/stats`, events, citations, the Popolo export, signed receipts, the refusal log, complaints, group positions, notice, history, evidence extracts, the roll-call export, proofs. (`/law` and effective dates are L1.)
- **L3:** federation with a second implementation, adversarial scenarios, scale scenario, complaint re-validation.

**Vectors** (`conformance/vectors/`): canonicalization, hashing, node hashing, signatures, merge cases, rule evaluation, consistency checks, fact sheets (including number extraction), quote verification, assertion evaluation, structure profiles. Rule vectors are generated from the Catala references (section 7); if they exist, use them rather than writing your own for rules. If the directory is empty when you start, write provisional vectors for L0/L1 from this spec and mark them `"provisional": true`. They become normative only after a second independent implementation reproduces them.

**Scenarios** (harness drives your API as a black box with seeded synthetic rosters and voting blocs; it also checks all participating nodes converge on the same root snapshot hash):

| # | Scenario | Level |
|---|---|---|
| S1 | Linear amendment passes a majority vote | L1 |
| S2 | Concurrent conflicting edits, resolution, vote | L1 |
| S3 | Governance MR changes a branch's merge rule | L1 |
| S4 | Equivocation (double ballot) voids the member's ballots | L1 |
| S5 | Bait-and-switch: head moves after ballots, MR goes stale | L1 |
| S6 | Contested `capital_vote` with quadratic cost | L2 |
| S7 | Restricted draft is revealed, then merged | L2 |
| S8 | Opinion flood from many keys; per-key rate limits; disclosure enforcement (`funding`, `affiliations`, `ai_assistance`) | L2 |
| S9 | Partition and heal (`eventual`) or fail-over (`strong`) | L3 |
| S10 | Malicious operator withholds, reorders, or rewrites history; do independent verifiers detect it? | L3 |
| S11 | Scale: 11,000-member roster, ~300 MRs, mixed rules | L3 |
| S12 | Finality and citation: a vote crosses its threshold live; the harness checks events stay `provisional` until checkpointed, that an `eventual` node never emits `final`, and that `/cite` on a pre-amendment hash still returns the original text with a correct `latest` link | L2 |
| S13 | Fact-check loop: an MR's summary says a period becomes 35 days while its change sets 45; every node's `/facts` reports `30` → `45` identically; a finding with a verified quote is accepted and a misquote is rejected (`quote_mismatch`); a `ballot_choice` assertion that a member voted `reject` evaluates `fails` when they voted `approve`, `provisional` until checkpointed; a correction keeps the original retrievable with `corrected_by`; the member's reply appears with the finding; a new `[[def:]]` duplicate and a reference cycle raise warnings without blocking | L2 |
| S14 | Formal grammar and rules: a node with a kind the profile forbids blocks the MR (`structure`); an `on_threshold` MR whose quorum becomes unreachable through void ballots is rejected, not left pending; a formalization with an anchor on an amended node appears in that MR's `anchor_drift`; `/export/popolo` vote counts match `/tally` on every node | L2 |
| S15 | Markets and rankings: a resolution spec on an MR's outcome stays `unresolved` while results are `provisional` and resolves identically on all listed nodes once checkpointed; a spec requiring `final` from too few `strong` nodes is reported `unsatisfiable`; a non-member opinion without `stakes` is rejected; a member's declared stake appears beside their ballot; a forecast resolves `holds` or `fails`; a ranking by `approve_share` holds on every node and a ranking with one swapped item fails; no endpoint returns aggregate odds | L2 |
| S16 | Parties, interest groups, and complaints: a group position signed by a non-admin is rejected (`group_not_authorized`); a ballot signed by anyone but the member is rejected; a governance MR targeting the root branch's governance rule is rejected; text proposed by an advocacy group's opinion and later appended by a member appears in `origin` on every node; a node refuses a valid opinion, the submitter files a complaint with the signed receipt and refusal entry, and an L3 node re-validates it as `rejection_inconsistent` | L2/L3 |
| S17 | Procedure and evidence: an `on_threshold` MR on the root stays pending until 72 hours of checkpoint time pass, then merges; a determination submitted earlier is refused (`notice_not_elapsed`) on `strong` nodes; three blocs with cyclic preferences pass X to Z and Z to Y, and the reverting MR Y to X fails under a 3/4 `reversion` threshold; a merge with a future effective date holds back a later merge in `/law`; an evidence extract for a determination verifies offline; `/history` lists member and outside opinions separately | L2 |
| S18 | Simulation 1 replay: a simulated session carries `simulation: true` on every response; four MRs on one chapter grouped in a contest elect `status_quo` when none beats it with a majority of the electorate; a superseding version touching only the same nodes needs 24 hours of notice; an emergency MR with 2/3 `emergency: "yes"` merges after 24 hours and is reverted by an expiry change 30 days later; a node that rate-limits keys by shared affiliation fails; tallies show both shares | L2 |
| S19 | Simulation 2 replay: 2,000 fresh non-member keys cannot publish until aged; a stolen member key casts a conflicting ballot, the member and registrar rotate the key, and the member's real ballot counts; a fiscal MR's notice starts only after an estimate is published; a reference to an unpublished annex blocks the MR; a 90-day sunset drops text from `/law` on day 91; a translation passage that is not an exact verified quote is rejected; an `education` session's capability declares `ephemeral` | L2 |
| S20 | Simulation 4 replay: a six-option contest whose ranked-pairs winner fails its rule by 34 approvals falls back and approves the next winner, which passes, with both rounds in `/contests/{id}`; a variant touching a node its base does not touch is rejected `variant_invalid`, and a valid variant is grouped with its base automatically; an estimate on an MR with a sunset and no `if_permanent` is rejected `estimate_incomplete`; `/law` on a date before a node's `[[effective:]]` marker shows the prior text; a feasibility opinion later than that marker raises `effective_before_feasible` | L2 |
| S21 | Simulation 5 replay: a contest deadline 30 hours after open is refused `deadline_too_short`; a seven-option contest falls back from a winner that fails by 189 to one that passes, and `/contests/{id}` and the fact sheet show `ranked_above_winner`; a district table with a `religion` column is rejected `district_attribute_invalid` and one with `region` and `density` yields matching `?by=` tallies on every node; `/law?jurisdiction=` omits an `[[applies:]]` node outside its list and applies an `[[opt_in:]]` node only after that jurisdiction's opt-in; an opinion listing an organization shows `attested_by` with a valid attestation and `unattested` after revocation | L2 |
| S22 | Observers (D78, D79): a session declares an `observers` group; a ballot, a ranking, an `mr.open`, and a group position signed by an observer key are each refused `observer_not_allowed`, as is an observer opinion on an MR and an observer opinion on `spec` that carries an `estimate`; a plain observer opinion on `spec` is accepted; a member and a non-member each file a protocol complaint addressed to the observer, a complaint addressed to a non-observer key is rejected `addressee_invalid`, and the observer's reply with `responds_to` appears in `observer_replies` on every node | L2 |

**Research mode** (D46, D55, D64, D65). Research sessions set `simulation: true`. They may add **constituent panels** (synthetic views per district, declared and hashed), in which case the harness reports how closely each member's votes track their panel. They may add a **second chamber** with its own roster and rules (including a debate-closing threshold) and a **presentment model** (signature, veto, override at two-thirds of each chamber), all labeled hypothetical; bicameralism stays outside the protocol core. **Exchanges** (D67): offers between chambers and from a modeled executive are opinions with `exchange: {"mr":"sha256:...","from":"chamber:<id>|executive","offer":"sha256:<change>","prior":"sha256:<exchange>|null"}`; a conference MR lists the exchange chain it resolves in `supersedes` context, so the bargaining record is linked and verifiable. Any published result MUST include a turnout model (who abstains or misses votes, and why) and a sensitivity analysis: re-run with each bloc 10% larger and 10% smaller, chamber size fixed, and list every outcome that flips. The harness can also run sessions for research: synthetic blocs with declared preference models (published, hashed), rule sets assigned by seed so sessions differ only in their rules, and a `replication.json` recording the harness version, seed, preference-model hash, rule assignment, and final log root, so any published result can be reproduced. Results describe the programmed voters, not real legislators (OQ-25). **District attributes** (D72): a research session may attach a table mapping every roster member to `{"region":"...","density":"..."}`, with the allowed values declared in the table; tallies (`?by=`) and `/export/rollcall` then include the breakdown. Any other attribute (religion, ethnicity, nativity, party, income, or anything else) is rejected with `district_attribute_invalid`, and nodes reject the table outside `simulation: true` sessions. **Jurisdiction choices** (D73): a research session may name a key for each declared jurisdiction; that key records `jurisdiction_choice: {"program":"<id>","choice":"opt_in|opt_out","effective":"YYYY-MM-DD"}` opinions, which `/law?jurisdiction=` applies to `[[opt_in:]]` nodes. All labeled hypothetical.

**Metrics** (reported, not pass/fail in v0.13): sustained tx/s; p50/p95 latency from the deciding ballot to a merged head; storage growth per 1,000 ballots; time to sync a fresh node from genesis; two-node convergence time; share of S8 opinions rate-limited or clustered; time to compute `/facts` and `/checks` for the largest S11 MR; in an S11 variant where simulated blocs react to published forecasts, how much forecasts shift outcomes (OQ-18). These feed the project's central empirical question: what does each trust model buy, and at what cost, at legislative scale?

## 11. Deliverables in your implementation folder

```
implementations/{{IMPLEMENTATION_NAME}}/
  README.md              architecture, trust model, how to run, known deviations, the "not for real elections" notice
  DESIGN.md              1-2 pages: key choices, why, what you would change in the spec
  src/  tests/
  Dockerfile or compose  runs with one command
  conformance-report.json   output of the harness
  OPINIONS.md            your opinions on the spec and open RFCs (also published as Opinion txs)
```

## 12. Working method

1. Read this whole prompt. In `DESIGN.md`, restate the spec's key invariants in your own words before writing code.
2. Build canonicalization, hashing, and signature verification first, tested against vectors.
3. Implement L0, L1, L2, L3 in order and run conformance after each level. Commit at each green level.
4. **Do not edit `spec/`.** If a requirement is wrong, ambiguous, or unimplementable, open an issue using the `rfc` template with a minimal failing example and a proposed fix, publish an `implementation` opinion, and continue with the spec as written unless maintainers accept the change.
5. Prefer boring, auditable code. No hidden network calls. No secrets in the repo. Licenses of dependencies must be permissive.
6. Ask the human before adding a non-permissive dependency or touching anything that resembles real data.
7. When done, report: which levels pass, which scenarios fail and why, your metrics, and your top three objections to the spec.

**Open questions you may hit (do not resolve alone; raise as RFCs):** secret ballots vs. accountability under mass audiences, and which claims about votes stay checkable, the council's top priority (OQ-4); who owns the verdict scale (OQ-11); statements made outside the protocol (OQ-12); reproducing AI findings (OQ-13); whether some MRs should require a formalization (OQ-14); choosing among competing formalizations (OQ-15); structure profiles beyond the US (OQ-16); member trading on outcomes (OQ-17); forecasts that move votes (OQ-18); owner-blind restricted commitments, now urgent (OQ-19); coercion of members by parties and interest groups (OQ-20); donor privacy in disclosures (OQ-21); independent review of the council's simulated decisions (OQ-22); what would serve as an attested enrolled text (OQ-23); emergency exceptions to notice (OQ-24); what synthetic voters can show about real ones (OQ-25); helping coalitions find a missing option (OQ-26); how constituents take part (OQ-27); member safety (OQ-28); classified material (OQ-29); sybil resistance for non-member speech (OQ-30); a formal role for affected populations (OQ-31); content rules for budget-only vehicles (OQ-32); unrelated provisions on must-pass bills (OQ-33); personal information, such as health or immigration status, in a permanent log (OQ-34); contested inputs that are not costs (OQ-35); provisions that apply by state or jurisdiction, partly addressed by D73 (OQ-36); sovereign participants such as tribal nations (OQ-37); territories and the District of Columbia (OQ-38); whether constituent panels include non-citizen residents (OQ-39); keeping observers independent once real people fill the roles (OQ-40); late ballots and time in `eventual` mode (OQ-1); contests, now specified in section 7 (OQ-2); contradictory determinations across forks (OQ-3); issuing member keys (OQ-5); opinion spam (OQ-6); provisional results reported as final (OQ-8); member conflict disclosure (OQ-9); context for short clips of cited text (OQ-10).
  1. Step 1

    Fill in the fields and copy the prompt.

  2. Step 2

    Start a fresh coding agent session in an empty repository and paste it in.

  3. Step 3

    Join the mailing list to hear when the next draft of the spec is out.

Join in

Help open the House.

Hear about each new simulation, each new draft of the spec, and the first implementations. Everyone who cares about democracy is welcome: builders, researchers, students, and citizens.

Join the mailing list

We use your address only for Open The House news.

Support the project

Donations keep the spec, the simulations and this site going. Donors join the mailing list too.

Donate