feat: close the loop from reader to member to vote - #90
Merged
Conversation
Solon could show governance but not let you join it. Every page ended in prose: the audit trail had no way to add to the record, the dashboard told a signed-out reader nothing they could do, and there was no route at all from "I read this" to "I hold a vote". This adds the missing spine. Membership. /join binds a Bitcoin key to an identity, signed. The signed text carries the actor id (registrationMessage) so a signature proving control of an address cannot be replayed to bind that address to someone else's account — the roster's claim is "this account and this key are the same person", so that is what gets signed. Proposals. /propose files one, /proposals lists them with each one's next step, and /proposals/[id] shows the detail with the action it is waiting for. Sessions open from the proposal itself. Wayfinding. Every page now offers at least one action — verified by walking all 16 routes in a real browser and counting controls per page; the count was 0 on nine of them before. The dashboard leads with the one next action the visitor can actually take. Treasury honesty. treasuryReport now returns a four-state status instead of a boolean. "Were all sources resolved?" is vacuously true when no source is registered, so a caller reading that as "this total is trustworthy" was told yes about nothing. no_sources / resolved / partial / unavailable say which of the four it actually is. Still watch-only. Multi-tenancy fix. The dashboard and voting page selected the newest voting session in the entire database with no organization filter, so a second org's vote rendered on this org's dashboard. Caught by rendering against a database that had more than one org in it — production has one, which is exactly why it would have gone unnoticed. Now scoped, with the reason recorded at each call site. npm run verify green; 44 tests pass including the membership integration spine against a real Postgres. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
github-actions Bot
pushed a commit
that referenced
this pull request
Sep 4, 2026
Fix drift proven against package.json, the tree and the live site: - TypeScript 5.5 -> 6.0 (README badge + stack table, AGENTS.md; ^6.0.3 in package.json, 6.0.3 in pnpm-lock.yaml) - Tailwind CSS 3.4 -> 4 (package.json 4.3.3), and tokens no longer live in src/app/globals.css — they come from @fleet/design-tokens (README, AGENTS.md stack + design sections) - verify description now includes format:check, matching the actual script (README, AGENTS.md, .claude/CLAUDE.md) - README pages list: /why was deleted in #90 (404 live); replaced with the real /join, /propose, /proposals pages - README domain list: add membership and org modules that exist in src/lib/domain/ - docs/TOWNSISM.md: stop claiming the site speaks the thesis at /why Claude-Session: https://claude.ai/code/session_01WqKqMnHQHSmkGFfc5t7Rxn Co-authored-by: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Solon could show governance but not let you join it. Nine of the sixteen routes ended in prose with nothing to click. This adds the missing spine: read → join → propose → vote.
The four pieces
Membership —
/joinbinds a Bitcoin key to an identity. The signed text carries the actor id, so a signature proving control of an address can't be replayed to bind that address to someone else's account.Proposals —
/proposefiles one,/proposalslists them with each one's next step,/proposals/[id]shows the detail and the action it's waiting for.Wayfinding — every page now offers at least one action. Verified by walking all 16 routes in a real browser and counting interactive controls per page; nine had zero before, all have ≥1 now.
Treasury honesty —
treasuryReportreturns a four-state status instead of a boolean.allSourcesResolvedwas vacuouslytruewhen no source was registered, so a caller reading it as "this total is trustworthy" was told yes about nothing. Still watch-only; no spend path added.One real bug, found by rendering
The dashboard and voting page selected the newest voting session in the entire database with no organization filter — a second org's vote rendered on this org's dashboard. Production has exactly one org, which is why unit tests and prod both stayed quiet. It surfaced only when the page rendered against a database that had more than one. Now scoped, with the reason recorded at each call site.
Verification
npm run verifygreen (lint + typecheck + design:check + test)🤖 Generated with Claude Code