The 80% is a number the ledger can stand behind now - #13
Merged
Conversation
Phase 3 of the Knowledge Influencer PRD. Until now an operator could climb to 80% and the number was a label on a page. An earning now arrives, is finalised, and at that instant is divided between the niche's operators and the platform. The rule that shapes everything: an allocation records the share that was in force when the event settled. Reaching Expert tomorrow does not reach back and re-price yesterday's sale at 40%. The niche revenue page shows today's split and each settled allocation's own share side by side, and says why they can disagree. Verified by driving it: a sale split 2000bps/194c, the operator was then promoted to 4000bps, the next sale split 4000bps/400c, and the first row still says 2000bps/194c. Money is integer minor units and shares are integer basis points the whole way. The only division is formatMinor, at the moment a number is handed to a person. The things that would pay somebody twice are constraints rather than code paths that have to remember. A row whose net is not gross minus cost cannot be written. external_id is unique, so a settlement delivered twice books once. One allocation per party per event, including the platform row whose influencer is null, which a plain unique index over a nullable column would not catch. And payout_allocations has its primary key on allocation_id alone: an allocation belongs to at most one payout, so the same earning cannot be paid twice however many times the button is pressed. Finalisation claims the event inside the transaction that writes the split, so a race produces one split rather than two. Nothing sends money, and that is deliberate rather than unfinished. CoinPay's payout API pays a connected merchant account, not an arbitrary third party's address, so there is no automated disbursement to call. An operator saves an address, an admin confirms it and schedules, the money moves out of band, the reference is recorded. Same shape as the partner programme. Changing an address clears its confirmation, and a failed payout returns its allocations to owed rather than stranding them. Phase 4 is now small: machineRevenueEvent already normalises what the x402 gateway hands onSale, and it is tested. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W9NGGLDvNayi6uWheSXDGF
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.
Phase 3 of the Knowledge Influencer PRD: the ledger that makes the 20–80% share something the database can stand behind, rather than a number rendered on a page.
What is here
0011—revenue_events,revenue_allocations,payout_accounts,payouts,payout_allocations.packages/db/src/revenue.js— recording, finalisation, balances, payout scheduling and settlement./api/v1/me/revenue,/api/v1/me/payouts,/dashboard/payouts,/dashboard/niches/<slug>/revenue,/admin/payouts.docs/revenue-ledger.md.The allocation arithmetic (
allocate,attributableNetMinor) was already written and tested in Phase 1; this wires it to storage.The rule that shapes it
An allocation records the share that was in force when the event settled. Reaching Expert tomorrow does not reach back and re-price yesterday's sale at 40%.
Verified by driving it against a real Postgres:
The niche revenue page shows today's split and each settled allocation's own share side by side, and explains why they can disagree.
What the database refuses
Each of these is a way somebody gets paid twice or paid wrong, so each is a constraint rather than a code path that has to remember:
revenue_events_adds_upexternal_iduniquerevenue_allocations_onceinfluencer_idis null, which a plain unique index over a nullable column would not catchpayout_allocationsPK onallocation_idalonepayouts_positiveFinalisation claims the event with
update … where finalized_at is nullinside the transaction that writes the allocations, so two requests racing produce one split.One of these caught a bug in my own test, which had tried to allocate five times to the same person out of one earning.
Payouts send nothing, deliberately
CoinPay's payout API pays a connected merchant account (us), not an arbitrary third party's address — there is no automated disbursement to call. The partner programme already has this shape, so this matches it: accrue accurately, pay deliberately. An operator saves an address, an admin confirms it and schedules, the money moves out of band, the reference is recorded.
Two safeguards: changing an address clears its confirmation (whoever confirmed the old one did not confirm this one), and a failed payout returns its allocations to owed rather than stranding them in a state nothing picks up again.
Verification
bun test— 165 pass, 0 fail (12 new ledger schema tests, 4 money-rendering, plus the existing suite). Needs no server.bun run lint— clean; 4 CSS warnings and thebiome.jsondeprecation are pre-existing onmain.$45.00-style currency with no raw cent integers leaking.No new environment variables (the endpoint reuses
CHOVY_SIGNING_SECRET).What is next
Phase 4 is now small:
machineRevenueEventalready normalises what the x402 gateway handsonSale, and it is tested — it needs wiring torecordRevenueEvent.🤖 Generated with Claude Code
https://claude.ai/code/session_01W9NGGLDvNayi6uWheSXDGF