From 31566ba0b10c423c88e007d5f22d58bc9f7dd2ba Mon Sep 17 00:00:00 2001 From: peanutsplit-steward Date: Wed, 2 Sep 2026 12:34:27 +0200 Subject: [PATCH] Make the Known-open issues list describe the live product MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JPY hero-balance line described the twelve-entry hand-typed catalog that src/lib/money.ts replaced with the generated 162-code table; the code's own comment already documents the fix, and money.test.ts pins JPY at 0 decimals in five locales. Verified today across every live surface: currencyInfo falls back to the bundled CLDR table before its 2-decimal placeholder, and /api/currencies serves the identical generated table, so the catalog query resolving can never change a decimal count. The 2^53 line is still true of apps/api, but apps/api serves no route and no traffic — saying so stops the next reader auditing dead code while implying the live money path shares the defect it does not have. Ordered-By: Hugo0 Order: https://github.com/peanutprotocol/peanutsplit/blob/main/ops/steward/HANDOFF-2026-09-02-showhn.md --- CLAUDE.md | 4 +- ops/steward/HANDOFF-2026-09-02-showhn.md | 64 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 ops/steward/HANDOFF-2026-09-02-showhn.md diff --git a/CLAUDE.md b/CLAUDE.md index 4c95f8c1..d09277de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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. diff --git a/ops/steward/HANDOFF-2026-09-02-showhn.md b/ops/steward/HANDOFF-2026-09-02-showhn.md new file mode 100644 index 00000000..9dccfaaa --- /dev/null +++ b/ops/steward/HANDOFF-2026-09-02-showhn.md @@ -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: 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.