|
| 1 | +# NSP-12 Public Decision Record — the EIP pipeline as a civic event grammar |
| 2 | + |
| 3 | +**Date:** 2026-07-31 |
| 4 | +**Status:** DRAFT — design only; no implementation in this slice |
| 5 | +**Follows:** [Nostr Citizen identity bridge](2026-07-27-nostr-citizen-identity-bridge-design.md), [NSP-9 federation](2026-07-27-nsp9-federation-design.md), proposal pointers (kind 32100, shipped in `packages/publisher`), [fork-with-fallback](2026-07-31-fork-with-fallback-design.md) |
| 6 | +**Companion post:** `Netizen-Labs/apps/web/content/writing/2026-07-31-what-towns-can-learn-from-ethereum-governance.md` |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## 0. Why this spec |
| 11 | + |
| 12 | +Ethereum runs the largest functioning digital governance system in the world without a |
| 13 | +single binding vote. Its actual mechanism is three-layered: a **legible proposal pipeline** |
| 14 | +(EIP-1 lifecycle, editors who gatekeep process but never content), **rough consensus among |
| 15 | +the people who must implement changes** (client teams; a proposal ships only when an |
| 16 | +implementer commits), and **exit rights** (node operators can decline to upgrade; |
| 17 | +communities can fork with the state). Since 2025 the Ethereum Foundation added a fourth |
| 18 | +layer that turned out to be the most transferable: **legibility tooling**. Forkcast gives |
| 19 | +every proposal a plain-language page with per-stakeholder impact, publishes call |
| 20 | +transcripts within about two hours, names a champion per proposal, and enforces headliner |
| 21 | +discipline (one big thing per upgrade cycle, pitched in an open, calendarized window). |
| 22 | + |
| 23 | +Two lessons drive this design: |
| 24 | + |
| 25 | +1. **The coordination layer is the product, not the vote.** A German Kommune cannot and |
| 26 | + should not move formal decisions out of the Stadtvertretung — and Ethereum's model |
| 27 | + never asks for that. AllCoreDevs does not bind node operators either. What transfers is |
| 28 | + legitimate advisory infrastructure: the pipeline, the record, the translation layer. |
| 29 | +2. **Process tooling must outlive its operator.** In July 2026 the EF dissolved the team |
| 30 | + that ran Forkcast and the core-dev calls; maintenance of the tooling is now an open |
| 31 | + question. The Netizen answer is structural: encode the process in the protocol |
| 32 | + (this spec) and the installer (manifest-rendered, per the standing rule that everything |
| 33 | + on a node ships through `netizen render`/`up`), so any node operator — including the |
| 34 | + community itself — can keep the process alive without Netizen Labs. |
| 35 | + |
| 36 | +The number: NSP-10 is the indexer, NSP-11 is operations. The Public Decision Record is |
| 37 | +**NSP-12**. |
| 38 | + |
| 39 | +## 1. Scope |
| 40 | + |
| 41 | +**In.** |
| 42 | +- The event grammar: kinds, tags, and lifecycle for proposals, status transitions, |
| 43 | + meeting records, Meinungsbild results, impact summaries, and decision cycles. |
| 44 | +- Roles and keys: editor-agents, impact-agents, champions, implementers, facilitators. |
| 45 | +- The manifest surface (NSP-0) and how NSP-10 (indexer), NSP-9 (federation), and the |
| 46 | + agent-watcher consume it. |
| 47 | +- Legal rails: advisory-only framing, AI Act Art. 50 labeling, GDPR posture. |
| 48 | + |
| 49 | +**Out**, each deliberately: |
| 50 | + |
| 51 | +| Deferred | Why | |
| 52 | +|---|---| |
| 53 | +| Binding on-chain votes | Never in scope. Formal decisions stay with the Stadtvertretung; the record mirrors outcomes, it does not produce them. | |
| 54 | +| MACI / coordinator-as-a-service internals | Owned by the [coordinator-as-a-service spec](2026-07-31-coordinator-as-a-service-design.md). NSP-12 only defines the *result pointer* event. | |
| 55 | +| Meetings plane (calls, transcripts, recording) | Owned by the [Netizen Workspace meetings spec](2026-07-31-netizen-workspace-meetings-ai-design.md) (NSP-7 surface). NSP-12 takes pointers to transcripts, it does not produce them. | |
| 56 | +| Honorariums / contributor funding | The Gemeinschaftskasse and Münzen tips already exist; wiring champion rewards is a later, separate decision. | |
| 57 | +| New explorer UI build | The explorer page set ("Vorhaben") is sketched here as a consumer but implemented in its own slice. | |
| 58 | +| Cross-community proposal portability | Needs the federation trust model from NSP-9 slice 2+. The grammar is shaped so records mirror cleanly, nothing more. | |
| 59 | + |
| 60 | +**Success condition.** A proposal born in the Röbel app exists as a kind-32100 head with a |
| 61 | +signed, append-only transition trail, an agent-labeled plain-language impact summary, and |
| 62 | +(where run) a Meinungsbild result — all resolvable from the explorer without Supabase |
| 63 | +(fork-with-fallback holds), and reproducible on testnode from the manifest alone. |
| 64 | + |
| 65 | +## 2. The lifecycle |
| 66 | + |
| 67 | +German-first stage names, mapped from EIP-1 + EIP-7723. The record never claims decision |
| 68 | +power: stages that imply a formal act (`beschlossen`, `abgelehnt`) may only be entered by |
| 69 | +mirroring a signed civic notice (kind 32102) from the body that made the decision. |
| 70 | + |
| 71 | +| Stage | EIP analog | Who may move it here | Meaning | |
| 72 | +|---|---|---|---| |
| 73 | +| `idee` | Idea | any citizen npub | Untracked sketch; becomes tracked when an editor-agent confirms completeness | |
| 74 | +| `entwurf` | Draft | editor-agent (process check only) | Structurally complete: problem, proposal, affected parties named | |
| 75 | +| `diskussion` | Review | author | Author declares it ready; open deliberation thread | |
| 76 | +| `meinungsbild` | Last Call | facilitator (human) | Advisory MACI signal runs; result published as kind 32104 | |
| 77 | +| `beschlussvorlage` | Scheduled for Inclusion | implementer | An implementer (Verwaltung, Stadtwerke, Verein) has committed a champion and capacity; forwarded to the formal body | |
| 78 | +| `beschlossen` / `abgelehnt` | Included / Declined | mirror of kind-32102 civic notice only | The formal body decided; the record cites the notice | |
| 79 | +| `umgesetzt` | Deployed | implementer | Done and verifiable | |
| 80 | +| `ruhend` | Stagnant | editor-agent (mechanical: 6 months without activity) | Parked, revivable | |
| 81 | +| `zurueckgezogen` | Withdrawn | author | Permanent; also the GDPR withdrawal path (§8) | |
| 82 | + |
| 83 | +Rules carried over from Ethereum verbatim because they are the load-bearing ones: |
| 84 | + |
| 85 | +- **Editors gatekeep process, never content.** An editor-agent may request missing fields, |
| 86 | + deduplicate, translate, and park stale drafts. It may never score, rank, or reject an |
| 87 | + idea on merit. This is EIP-1's editor mandate, and it is exactly the mandate an AI agent |
| 88 | + can hold accountably: mechanical, neutral, auditable. |
| 89 | +- **Implementer commitment is the gate**, not enthusiasm. `beschlussvorlage` requires a |
| 90 | + named implementer champion — the analog of "client devs signal intent to implement." |
| 91 | +- **Facilitation stays human.** Agents structure and translate; a human facilitator runs |
| 92 | + Meinungsbild windows and cycle scoping. |
| 93 | + |
| 94 | +## 3. Event grammar |
| 95 | + |
| 96 | +Existing kinds stay untouched: 32100 proposal head (shipped), 32101 menu, 32102 civic |
| 97 | +notice. New kinds below are **provisional**; final numbers are claimed in |
| 98 | +`packages/publisher/src/mappers.ts` at implementation time, which is the kind registry of |
| 99 | +record. All are addressable (parameterized-replaceable) except the transition, which is a |
| 100 | +regular immutable event — the audit trail must not be rewritable. |
| 101 | + |
| 102 | +| Kind | Name | Form | Content | |
| 103 | +|---|---|---|---| |
| 104 | +| 32100 | Proposal head (exists, extended) | addressable, `d` = proposal slug | Markdown abstract + motivation. New tags: `status` (stage from §2), `champion` (`p` tag with role marker), `cycle` (`a` tag to 32106) | |
| 105 | +| 2100 | Status transition | **regular, immutable** | `a` tag to the head, `from`/`to` stage tags, free-text reason, signed by whoever §2 authorizes for that transition. For `beschlossen`/`abgelehnt`: an `a` tag citing the 32102 civic notice is mandatory | |
| 106 | +| 32103 | Meeting record | addressable, `d` = `<body>-<date>` | Agenda published before the meeting, minutes and transcript pointers after (pointers into the workspace plane, NSP-7) | |
| 107 | +| 32104 | Meinungsbild result | addressable, `d` = poll id | MACI tally pointer, parameters (threshold band, anonymity-set size), verification pointer, and an explicit `advisory` tag — always | |
| 108 | +| 32105 | Impact summary | addressable, `d` = `<proposal-slug>-<audience>` | "Was bedeutet das für dich" per stakeholder group (`anwohner`, `gewerbe`, `vereine`, `verwaltung`), authored by a labeled agent npub | |
| 109 | +| 32106 | Decision cycle | addressable, `d` = cycle slug (e.g. `massnahmen-2027`) | The Meta-EIP analog: list of `a` tags to proposal heads, each with a stage marker (`vorgeschlagen` / `in-pruefung` / `eingeplant` / `nicht-aufgenommen` — the PFI/CFI/SFI/DFI mapping), plus the headliner designation and the pitch-window dates | |
| 110 | + |
| 111 | +Head example (abridged): |
| 112 | + |
| 113 | +```json |
| 114 | +{ |
| 115 | + "kind": 32100, |
| 116 | + "tags": [ |
| 117 | + ["d", "radweg-seeufer"], |
| 118 | + ["title", "Radweg am Seeufer"], |
| 119 | + ["status", "beschlussvorlage"], |
| 120 | + ["p", "<champion-npub-hex>", "", "champion"], |
| 121 | + ["a", "32106:<node-pubkey>:massnahmen-2027", "", "cycle"] |
| 122 | + ], |
| 123 | + "content": "## Problem\n…\n## Vorschlag\n…" |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +**Reference rule (hard):** every cross-reference to a status-bearing document is an `a` tag |
| 128 | +(`kind:pubkey:d-tag`), resolved at render time — never an event id. We have already been |
| 129 | +burned by replaceable-event id links going stale; NSP-12 makes the addressable form the |
| 130 | +only legal citation between decision-record events. |
| 131 | + |
| 132 | +**Deliberation** needs no new kind: replies to the head via standard NIP-10 threading are |
| 133 | +the discussion thread, and the explorer renders them under the proposal page. |
| 134 | + |
| 135 | +## 4. Roles and keys |
| 136 | + |
| 137 | +| Role | Key | Mandate | Bounds | |
| 138 | +|---|---|---|---| |
| 139 | +| Editor-agent | agent npub, NIP-24 `bot: true`, Art. 50 label | EIP-1 editor rule: completeness, dedup, translation, mechanical stage moves (`entwurf`, `ruhend`) | Pinned role in the agent-watcher; kinds and rate bounds in the manifest | |
| 140 | +| Impact-agent | agent npub, labeled | Writes 32105 per stakeholder group when a head enters `diskussion` or any later stage, and refreshes on head edits after that point | Same watcher bounds; output always attributed and filterable by key | |
| 141 | +| Champion | citizen npub (identity bridge) | Single accountable point of contact per proposal, named in the head | Human only | |
| 142 | +| Implementer | org npub (Verwaltung, Stadtwerke, Verein) | Signs the `beschlussvorlage` transition; publishes `umgesetzt` | Human-operated org keys | |
| 143 | +| Facilitator | human npub | Opens Meinungsbild windows, runs cycle scoping, chairs the process | Never an agent | |
| 144 | + |
| 145 | +Every agent output is a signed event under the agent's own key: humans can audit, filter, |
| 146 | +or ignore any agent wholesale. Display surfaces resolve keys to display names — raw |
| 147 | +addresses never appear in UI (standing rule). |
| 148 | + |
| 149 | +## 5. The cycle — Maßnahmenpaket with headliner discipline |
| 150 | + |
| 151 | +One 32106 event per planning cycle (typically the Haushaltsjahr). Imported from Ethereum's |
| 152 | +2025 process because it solved scope creep in a body with limited implementer capacity — |
| 153 | +which describes a small town better than it describes Ethereum: |
| 154 | + |
| 155 | +- **At most one headliner per cycle**: the single big project, chosen first, everything |
| 156 | + else scoped only after the headliner is stable. |
| 157 | +- **Open, calendarized pitch window**: dates live in the 32106 event itself. A pitch must |
| 158 | + name the problem, the affected groups, the rough cost, and a would-be champion — the |
| 159 | + same four things Ethereum requires of headliner pitches (need, impact, readiness, |
| 160 | + champion). |
| 161 | +- **Stages are re-proposed each cycle**: a proposal not `eingeplant` this cycle does not |
| 162 | + carry over silently; it re-enters next cycle's window. Nothing rots in a backlog |
| 163 | + invisibly. |
| 164 | + |
| 165 | +## 6. Meinungsbild rail |
| 166 | + |
| 167 | +The 32104 result event is a *pointer*, not a tally system: MACI poll id, the published |
| 168 | +tally, its verification artifacts, and the parameters that make the signal honest |
| 169 | +(threshold band, anonymity-set size). Two hard rules: |
| 170 | + |
| 171 | +1. The `advisory` tag is mandatory and the explorer renders the advisory framing |
| 172 | + unconditionally. Wording is "Meinungsbild", never "Abstimmung" (standing legal rule). |
| 173 | +2. A 32104 may only be published by the coordinator pipeline after tally verification — |
| 174 | + the same signed path the coordinator already uses, no manual results. |
| 175 | + |
| 176 | +## 7. Manifest and installer surface |
| 177 | + |
| 178 | +Everything above is configuration, rendered by `netizen render`/`up` — nothing hand-wired. |
| 179 | +Sketch of the NSP-0 block: |
| 180 | + |
| 181 | +```json |
| 182 | +"record": { |
| 183 | + "decisions": { |
| 184 | + "kinds": { "head": 32100, "transition": 2100, "meeting": 32103, |
| 185 | + "meinungsbild": 32104, "impact": 32105, "cycle": 32106 }, |
| 186 | + "agents": { |
| 187 | + "editor": { "npub": "<npub>", "staleAfterDays": 180 }, |
| 188 | + "impact": { "npub": "<npub>", "audiences": ["anwohner", "gewerbe", "vereine", "verwaltung"] } |
| 189 | + }, |
| 190 | + "bodies": [ { "id": "stadtvertretung", "noticeAuthor": "<org-npub>" } ], |
| 191 | + "cycle": { "current": "massnahmen-2027" } |
| 192 | + } |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +Touchpoints, all additive: |
| 197 | +- **NSP-10 indexer**: the six kinds join the indexed set (kind widening is a visible |
| 198 | + manifest edit, per existing rule). |
| 199 | +- **Agent-watcher**: `editor` and `impact` become pinned roles with the standard bounds. |
| 200 | +- **NSP-9 federation**: peers may mirror the decision kinds like any public civic kind; |
| 201 | + a town's governance record becomes mirrorable the way archive nodes mirror chains. |
| 202 | +- **Explorer / Atlas**: a "Vorhaben" page set — per-proposal page with status, transition |
| 203 | + trail, impact summaries, thread, and Meinungsbild result; per-cycle page with the |
| 204 | + headliner and stage matrix. This is the Forkcast-shaped surface. |
| 205 | + |
| 206 | +## 8. Legal rails |
| 207 | + |
| 208 | +- **Advisory only, everywhere.** The record informs the formal process; `beschlossen` / |
| 209 | + `abgelehnt` enter the record exclusively by citing the signed 32102 notice. Public |
| 210 | + framing follows the standing rule: civic technology, never a "Blockchain-Verwaltungsprojekt". |
| 211 | +- **AI Act Art. 50**: every agent-authored event carries the already-implemented labeling; |
| 212 | + the explorer renders the AI disclosure on 32105 and any agent-signed transition. |
| 213 | +- **GDPR**: proposals are voluntarily published public civic content; the only personal |
| 214 | + data is the author's key and chosen display name. Withdrawal = `zurueckgezogen` |
| 215 | + transition plus deletion honored on the authoring relay; mirrors follow the NSP-9 |
| 216 | + write-policy behavior. Impact summaries and meeting records must not name private |
| 217 | + individuals. |
| 218 | + |
| 219 | +## 9. Slices (each gets its own plan; none started by this spec) |
| 220 | + |
| 221 | +1. **Grammar**: NSP-12 zod schemas in `@netizen-labs/protocol`, kind constants + mappers |
| 222 | + in `packages/publisher`, transition-trail validation. |
| 223 | +2. **Editor-agent**: pinned watcher role, completeness checks, dedup, `ruhend` sweep, |
| 224 | + labeling. |
| 225 | +3. **Explorer "Vorhaben"**: per-proposal and per-cycle pages on the index/Atlas surface, |
| 226 | + NIP-10 thread rendering, advisory framing for 32104. |
| 227 | +4. **First cycle**: publish `massnahmen-2027` as a 32106 with a real pitch window, and run |
| 228 | + one proposal end-to-end through the pipeline in Röbel — the dogfood exit test. |
| 229 | + |
| 230 | +## 10. Open questions (for the user, not blockers to slice 1) |
| 231 | + |
| 232 | +- Should meeting records (32103) start with the Stadtvertretung's public agendas only, or |
| 233 | + also include Vereins-/Ausschuss meetings from day one? |
| 234 | +- Does the champion role require citizen-tier identity, or is resident-tier enough? |
| 235 | +- Cycle cadence: strictly the Haushaltsjahr, or a shorter first cycle to dogfood the |
| 236 | + pipeline before budget season? |
0 commit comments