feat(governance): schema v2 + domain core — humans and agents as first-class voting members - #65
Merged
Merged
Conversation
…t-class voting members S1+S2 of the Solon v1 plan: replace the facade data model with an honest one and put the governance logic under test for the first time. Schema v2 (drop-and-rebuild baseline; prod schema was verified empty and pre-reset on bitbaum so the new baseline applies cleanly): - Member carries memberType HUMAN|AGENT, its own bitcoinAddress + publicKeyHex, votingWeight, and optional ocActorId (OrangeCat identity link) - VotingSession snapshots its rules at open (electorate, threshold, quorum, eligibleCount, eligibleWeight) so past decisions stay explainable forever - Vote stores the exact signedMessage + signature — every tally is re-verifiable - Policy versions are unique per (org, key, version); only an APPROVED session can create v2+ (approvedBySessionId null is reserved for the v1 bootstrap) - Append-only AuditEvent, watch-only TreasurySource, sha256-hashed AgentApiKey - Idempotent seed migration: org `orangecat` + allocation_policy v1 ACTIVE Domain layer (lib/domain, no HTTP, no UI): - config/governance.ts is the electorate SSOT: agents vote in ALLOCATION_POLICY / TREASURY_SPEND / OPERATIONS; AID_DISBURSEMENT / MEMBERSHIP / SAFETY / GOVERNANCE_RULES are HUMANS_ONLY (red lines: agents can never vote money to people or expand their own suffrage) - tally.ts: pure tally + decideOutcome (abstain counts toward quorum, not threshold; silence is not consent — below-quorum sessions EXPIRE) - voting.ts: openSession (gate + snapshot at open), submitVote (signature → member → electorate check → weighted upsert + audit), closeSession (outcome from snapshots; APPROVED policy proposals mint the next ACTIVE version) - treasury.ts: live mempool.space balances, null on failure — never a guess Bitcoin message layer extended (load-bearing for real wallets): - BIP137 headers 27-42 accepted; claimed address matched against P2PKH, P2WPKH (bech32), and P2SH-P2WPKH derivations, so Sparrow/Electrum/Core signatures all verify — including Electrum's legacy-header-on-segwit quirk - proposalMessage() canonical form alongside voteMessage() Vitest wired into verify (lint + typecheck + test); 25 tests cover golden message strings, sign/verify roundtrips incl. tamper/replay, segwit derivation and header re-banding, the tally/threshold/quorum matrix, and config completeness over every DecisionCategory. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 7, 2026
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.
S1+S2 of the Solon v1 plan (see #64 for S0). The facade data model is replaced with an honest one, and the governance logic goes under test for the first time (0 → 25 unit tests).
Schema v2
memberType HUMAN|AGENT, its ownbitcoinAddress+publicKeyHex,votingWeight, and optionalocActorId(OrangeCat identity link). Solon never holds private keys.signedMessage+signature: every tally is independently re-verifiable.approvedBySessionId IS NULLreserved for the bootstrap v1).orangecat+allocation_policyv1 ACTIVE + genesis audit events.solonDB was verified empty (0 rows in every table) and its schema pre-reset, so the regenerated baseline applies cleanly on deploy.Domain layer (
lib/domain, no HTTP/UI)config/governance.ts— electorate SSOT: agents vote in ALLOCATION_POLICY / TREASURY_SPEND / OPERATIONS; AID_DISBURSEMENT / MEMBERSHIP / SAFETY / GOVERNANCE_RULES are HUMANS_ONLY (red lines: agents can never vote money to people or expand their own suffrage).tally.ts— pure tally + decideOutcome: abstain counts toward quorum but not threshold; below-quorum sessions EXPIRE (silence is not consent).voting.ts— openSession (gate + snapshot at open), submitVote (signature → member → electorate check → weighted upsert + audit event), closeSession (outcome from snapshots; APPROVED policy proposals supersede + mint the next ACTIVE version — the only path that creates governed policy versions).treasury.ts— live mempool.space balances per source;nullon failure, never a guessed number.Bitcoin message layer (load-bearing for real wallets)
proposalMessage()canonical form added alongsidevoteMessage().Verification
npm run verifygreen: lint clean,tsc --noEmitclean, 25/25 vitest tests (golden message strings, tamper/replay rejection, segwit derivations + header re-banding, tally/threshold/quorum matrix, config completeness over every DecisionCategory).npm run buildgreen, all routes compile.🤖 Generated with Claude Code