Living documentation
SplitSheet scenario storyboard
Each scenario is a recipe e2e test, replayed as a storyboard: the setup, then every user action and the exact Telegram screen it produced, with the test's assertions shown where they hold. Pick one from the left.
onboarding: bot added to a group
command: /start
onboarding: which joins create a sheet
expense: add by reply (RULE 1)
expense: base change converts every amount (bare, currencied, settlement)
acceptance: 6250 HUF on a € sheet (bug 372390c7)
clarification: redelivered callback is deduped
expense: currencied (frozen rate via world.fx)
deleted sheet message (dangling reply anchor)
expense: edit re-parses (RULE 3)
expense: legacy symbol base
expense: modify by reply (RULE 2)
expense: multi-currency rendering
expense: duplicate update is deduped
expense: simplify-debts toggle (action route)
clarification: ask and pick
clarification: answer by replying with text
resolution: new-member notice lists the expenses they were left out of
resolution: @mention resolves when the user acts
menu: welcome language navigation
menu: set currency from the inline button
webapp: change currency
webapp: debts teaser badge
webapp: desktop two-column layout
webapp: add expense
- a chat reply to the announcement modifies the same expense
- a currencied add freezes its setRate, converts, and stays computable
- adds a new expense over HTTP: the bot replies to the sheet, its id becomes the replyTreeId, the pin updates
- adds even when the sheet has no pinned message, sending the announcement unanchored
- blocks an add at the cap with the expense-cap shape; the unlock payment lifts it
- escapes HTML-hostile description in the announcement and the pin, keeps raw text in storage
- fails with 409 when the bot cannot post the announcement, persisting nothing
- never cap-blocks edits, removes or restores of existing expenses
- rejects an add from a non-member with 403 and persists nothing
- rejects an invalid add with 400 (share for a non-member)
webapp: edit announcements
- a chat reply to the edit announcement reconciles against the announced body
- a chat reply-modify pushes the next webapp edit downthread (replies to the user message)
- a remove announced in place is re-rendered back on undo restore
- a webapp remove posts a struck-through announcement; restoring right after deletes it (undo)
- an in-place announcement edit failure falls back to the invisible write
- an in-place edit by the adder keeps the plain added label (Telegram badge marks the edit)
- chat-created: the first webapp edit replies to the source message, the second edits that reply in place
- remove → restore (undo) → remove again posts a fresh announcement
- webapp-created, untouched: an edit by another member rewrites the announcement in place
- when the undo delete fails (48h window), a normal restored announcement is posted
webapp: expense edit / remove / restore
- edits an expense over HTTP (amount, payers, shares, description change)
- edits to a new currency, seeding its setRate and keeping totals computable
- edits with an exact (unequal) split that round-trips over HTTP
- edits with two payers (multi-payer) that round-trip over HTTP
- persists an UNANNOUNCED webapp edit invisibly, then a later telegram edit re-parses
- reconciles a 1-cent share drift via processExpense instead of rejecting
- rejects a guest (non-member) save and remove with 403
- rejects a share sum that drifts beyond what processExpense can reconcile
- rejects an invalid payload with 400 (share for a non-member)
- removes an expense over HTTP (tombstoned, excluded from totals)
- restores a bare expense by re-sending its detected base currency, value-preserving
- restores a removed expense (save after remove, same body, removedAt cleared)
- stamps source: webapp on the modify and remove events
webapp: idempotency-keyed add
webapp: legacy Telegram client (initData without signature)
webapp: live updates while the view is open
webapp: paid-by card
- censors amounts and offers an Unlock-balances CTA when balances are locked
- expands in place via "Show all" when the payer list overflows
- lists each payer with amounts, biggest first, in the sheet currency
- stays hidden until there is a verdict to summarize
- tapping a redacted amount also opens the Debts payment methods
webapp: SSR preload endpoint
webapp: currencied expense with no base
webapp: edit a currency rate
webapp: rename the splitsheet
webapp: rename own display name
webapp: settle a debt
webapp: who-spent card
webapp: version endpoint (live-update polling)
voice: expense from a voice message
messages: activation code redemption
messages: non-text media
boost: crypto (NOWPayments IPN)
boost: SBP (podelim instance)
boost: Telegram Stars
infra: duplicate boost charge is deduped, tail still runs
infra: redelivered join does not duplicate the sheet
infra: selective 503 on clean pre-side-effect failures
infra: hung transcription aborts into the error reply
infra: hung LLM trips the webhook watchdog
chat upgraded to a supergroup
- a second tap does not post the sheet twice
- carries the sheet to the new chat and archives its old message
- migrates from from-only signal alone
- migrates from to-only signal alone
- onboarding does not fire: no welcome, no second sheet
- tapping the button posts the sheet into the new chat and disarms the notice
paywall: balances locked on stamped free sheets
paywall: expense caps + remedies
paywall: payment handlers are never display-gated
paywall: legacy (pre-paywall) sheets keep their deal
paywall: payment unlocks are permanent
paywall: stamp on create
paywall: webapp unlock route + Stars payment
splitsheet_events rows-only read (real D1)
events table: write-path fidelity
forum topics
- keeps replies, edits, voice, validation errors and clarification callbacks in the topic
- keeps retry and dismiss callbacks in the topic
- new-member notices use the topic of the sheet they joined
- podelim: payment messages and background confirmations stay in the sheet topic
- routes Mini App add/edit/remove/restore announcements to the saved topic
- scopes creation, direct commands and /add to the current topic, including General
- splitsheet: payment messages and background confirmations stay in the sheet topic
- welcomes a forum group with topic instructions and keeps them after a language change