Skip to content

feat: close the loop from reader to member to vote - #90

Merged
catomean merged 1 commit into
mainfrom
feat/make-solon-usable
Aug 15, 2026
Merged

feat: close the loop from reader to member to vote#90
catomean merged 1 commit into
mainfrom
feat/make-solon-usable

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

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/join binds 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/propose files one, /proposals lists 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 honestytreasuryReport returns a four-state status instead of a boolean. allSourcesResolved was vacuously true when 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 verify green (lint + typecheck + design:check + test)
  • 44 tests pass, including a new membership integration spine against real Postgres
  • Production build clean; all 16 routes walked in a browser against a live server

🤖 Generated with Claude Code

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>
@catomean
catomean merged commit 080eed0 into main Aug 15, 2026
2 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant