Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ You don't need server access to ship — push is enough. Ask Hugo for anything i

Read [`docs-split-rooms-spike.md`](docs-split-rooms-spike.md) — the original design doc and the 2026-07-06 review — and the "Design choices" section of [`changelog-july-25.md`](changelog-july-25.md), which explains why the settle path is shaped the way it is. Known-open issues:

- `formatMoney` assumes 2 decimals until `/split/currencies` loads, so a JPY room can flash a hero balance 100× off.
- `convertToBaseMinor` (apps/api) round-trips through `Number`, losing integer precision past 2^53.
- ~~`formatMoney` assumes 2 decimals until `/split/currencies` loads~~ — fixed: decimals are a build-time fact via the generated `apps/web/src/lib/currency-catalog.ts` (162 codes, CLDR decimals), `/api/currencies` serves the same table, and the only remaining 2-decimal default is the deliberate custom-ticker placeholder (verified 2026-09-02: no live surface can render a 0- or 3-decimal amount at the wrong scale, even before the catalog query resolves).
- `convertToBaseMinor` (apps/api) still round-trips through `Number` past 2^53 — but apps/api is off-path: no public route, zero traffic (it survives only until the apps/web collapse). `apps/web`'s money path is string/BigInt end-to-end; its one Number crossing (`minorToExactNumber`, NumberFlow display only) refuses anything that fails an exact round-trip.
- ~~FX re-priced on edit~~ and ~~no rate limiting~~ — both fixed 2026-07-28 (`src/server/expenses.ts`, `src/server/rateLimit.ts`).
- **The settle loop cannot complete against real Peanut, and V1 ships without it.** `apps/api/src/peanut/index.ts` assumes a signed `charge:confirmed` webhook. In `peanut-api-ts`, `CHARGE_CONFIRMED` is an enum member nothing emits, charge delivery carries no signature, and charge creation rejects any currency but USD/ARS — so the verified path could not serve a EUR or THB room even if the event existed. **Decided 2026-07-27 (Konrad): the soft launch ships the settle flow as it stands** — `apps/web`'s SettleDrawer opens peanut.me and the room records the payment on the payer's tap, unverified. Nobody is polling anything. If you are here to "finish the receipts", that is a reopened product decision, not a TODO. Polling the public `GET /charges/:chargeId` remains the cheapest route if it is reopened.
64 changes: 64 additions & 0 deletions ops/steward/HANDOFF-2026-09-02-showhn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Handoff from Hugo (via his local Claude session), 2026-09-02 ~12:30Z

Hugo is preparing a Show HN for peanutsplit.com, target today 13:00-14:00 UTC or Tue 2026-09-08.
HN readers will try to break the money math in the first hour (JPY, 3-decimal currencies,
huge amounts, 0.01 three-way splits) and will try the self-host path. This handoff opens a
bounded fix lane for that. It is ordered by Hugo, so it widens the "observation only" posture
for the four tasks below and nothing else. PR only, never push main. No prod env changes.

## Task 1 — JPY / 0-decimal hero balance (validate, then fix if real)

peanutsplit CLAUDE.md "Known-open issues" says: "`formatMoney` assumes 2 decimals until
`/split/currencies` loads, so a JPY room can flash a hero balance 100x off." That note was
written against the old apps/api path. apps/web now has a bundled `src/lib/currency-catalog.ts`
with per-currency `decimals`, and `money.ts` is BigInt-based. Determine whether ANY live
apps/web surface (room hero balance, expense list, recap, share image, push text) can render a
0- or 3-decimal amount with the wrong scale, even briefly, before `useCurrencies()` resolves or
when the catalog entry is missing. Read the code; run the web unit tests; add a failing test
first if you find it. If real: fix, test, PR. If not real: say so with evidence.

## Task 2 — 2^53 precision note

Same list says `convertToBaseMinor` (apps/api) loses precision past 2^53. Confirm apps/api is
vestigial (Dokploy `split-org-api-uuwwxp`, no Traefik router, zero traffic — your own
inventory says so). Do NOT fix vestigial code. Confirm apps/web has no equivalent Number
round-trip on the money path (`money.ts` looks BigInt-only; `minorToExactNumber` guards).

## Task 3 — make CLAUDE.md truthful

Whatever Tasks 1 and 2 find, update the "Known-open issues" list in peanutsplit CLAUDE.md so
it describes the live product (remove or re-word the two lines, note apps/api is off-path).
Same PR as Task 1 if there is a fix, otherwise its own small PR.

## Task 4 — clean-machine self-host smoke (report only, no fix)

docs/current/SELF-HOSTING.md and the rights register both say the clean-machine
`docker compose up --build` smoke test is still an open publication-gate item. Run it:
fresh clone of the PUBLIC repo at main into a throwaway dir (not the deploy checkout), follow
the "Baseline" steps in SELF-HOSTING.md literally, on a random high port, `nice`d. Report:
does it come up, does http://localhost:<port> create a room, exact error text if not. Tear
everything down after (containers, images you built, the dir). Skip and say so if disk is
above 85% or the box is under load. Do not fix anything here; a doc PR for a wrong step is
fine.

## Task 5 — HN tester scenarios

Check whether the web unit tests already cover: JPY equal split, BHD (3 decimals), a
1,000,000,000 amount, 0.01 split three ways, percentage split that does not sum to 100. Do not
add speculative code. Add a test only where it exposes a real defect, and report the gaps
otherwise.

## PR mechanics

`split-gh` as usual. Every commit ends with:
Ordered-By: Hugo0
Order: https://github.com/peanutprotocol/peanutsplit/blob/main/ops/steward/HANDOFF-2026-09-02-showhn.md
(this file; copy it into ops/steward/ in the same PR so the link resolves). Keep each PR
minimal and independently mergeable. Do not touch Konrad's surfaces beyond the fix line.

## Reporting

Append one INCIDENTS.md entry per task outcome, post the usual Discord one-liner, and leave a
short status summary in this session so Hugo can read it when he checks in. If you can finish
Task 1 and 3 before 13:00Z today, say so loudly on Discord; that decides whether he posts
today or next Tuesday.
Loading