What's Changed
- Show "not activated" page for pre-setup sites; link assignment emails to /setup by @stefan-burke in #1112
- Add multi-day capacity engine and booking flow by @stefan-burke in #1086
- Add SumUp as a payment provider alongside Stripe and Square by @stefan-burke in #1104
- Seed effective domain from request host before DB init by @stefan-burke in #1113
- Show "Update In Progress" page during pre-migration backup by @stefan-burke in #1114
- Add admin UI and display for multi-day bookings by @stefan-burke in #1087
- Detect arguments always passed the same constant literal by @stefan-burke in #1115
- Mark every day of multi-day bookings as selectable in calendar by @stefan-burke in #1117
- Hide Booking Duration field for standard events by @stefan-burke in #1116
- Keep scanner check-ins working past the 1-hour CSRF window by @stefan-burke in #1118
- test: cover intra-cart group caps and all-or-nothing cart rollback by @stefan-burke in #1120
- Improve admin backups page and add backup retention by @stefan-burke in #1119
- Help diagnose Square webhook setup mistakes by @stefan-burke in #1121
- Streamline admin questions management by @stefan-burke in #1125
- Link attendee name column to edit page by @stefan-burke in #1126
- style: apply biome formatting fixes by @stefan-burke in #1129
- Add a link-based month calendar to the date picker by @stefan-burke in #1131
- Rename "events" domain to "listings" by @stefan-burke in #1132
- Inline checkboxes and radios by @stefan-burke in #1134
- Remove stray diff.html artifact by @stefan-burke in #1139
- Standardise action buttons and guide links with icons by @stefan-burke in #1137
- Run biome after deno lint in lint tasks by @stefan-burke in #1141
- Add icons to admin form submit buttons by @stefan-burke in #1142
- Add bulk emailing with audiences, marketing unsubscribe, and mailto fallback by @stefan-burke in #1136
- Refine /admin/emails page, add listing-scoped email log and info flash by @stefan-burke in #1143
- Reach 100% test coverage by removing unreachable branches by @stefan-burke in #1146
- Add admin attendees browser with filter, sort and pagination by @stefan-burke in #1144
- Fix docs workflow referencing non-existent events.ts module by @stefan-burke in #1147
- Add markdown preview link to editors by @stefan-burke in #1149
- Add Botpoison-protected contact form to the public site by @stefan-burke in #1148
- Link attendee payment ID to provider dashboard by @stefan-burke in #1150
- Guard contact form against owner self-spoof Reply-To by @stefan-burke in #1151
- Add copyable recipient address list to bulk email preview by @stefan-burke in #1154
- Detect contact-form spoofing by email host by @stefan-burke in #1152
- Update admin guide for SumUp, multi-day bookings, and rename fixes by @stefan-burke in #1153
- Attendee statuses, reservation deposits & balance management by @stefan-burke in #1145
- Group attendee status checkboxes inline by @stefan-burke in #1157
- Add customisable-days listings with per-day-count pricing by @stefan-burke in #1155
- Wrap heading+paragraph blocks in prose, style "Back to …" links as small buttons by @stefan-burke in #1160
- Narrow the bulk email body preview width by @stefan-burke in #1158
- Use native Uint8Array base64/hex in crypto utils by @stefan-burke in #1161
- Add Runtime section to /admin/debug by @stefan-burke in #1162
- Hide scroll nav only after scrolling past it by @stefan-burke in #1166
- Expand /admin/debug with site, availability, payment mode, and schema info by @stefan-burke in #1167
- Scale main horizontal padding fluidly with viewport by @stefan-burke in #1165
- Rework edit attendee page around a read-only summary by @stefan-burke in #1168
- Extract cachedTable and SettingsSection shared units by @stefan-burke in #1164
- Replace hand-rolled email validation with valibot by @stefan-burke in #1163
- Email a single attendee from their edit page by @stefan-burke in #1159
- Remove "No status" option from attendee form by @stefan-burke in #1170
- Adopt valibot's slug() validator; allow underscores in slugs by @stefan-burke in #1171
- Suppress attendee import clone with jscpd:ignore, restore absolute ticket URL by @stefan-burke in #1169
- Adopt valibot isoDate for YYYY-MM-DD validation by @stefan-burke in #1172
- ci: run the full precommit battery in the Test workflow by @stefan-burke in #1174
- Group the listing form into sections; reveal day prices under its toggle by @stefan-burke in #1176
- Adopt valibot for remaining case-conversion, the email-provider guard, and test format checks by @stefan-burke in #1173
- Validate public-listing API responses with a valibot schema in tests by @stefan-burke in #1178
- Replace remaining hand-rolled utilities with Deno natives by @stefan-burke in #1175
- Harden CI: fail lint on Biome warnings, and run Biome read-only in CI by @stefan-burke in #1180
- Fix edge bundle JSX transform (React-is-not-defined regression) by @stefan-burke in #1181
- Flex/gap layout for checkbox & radio groups, legend & dark-mode tweaks by @stefan-burke in #1177
- Show runtime uptime in the admin debug footer by @stefan-burke in #1179
- Hoist PublicListingSchema to shared test-utils and reuse it by @stefan-burke in #1182
- Auto-size admin textareas to their content with field-sizing by @stefan-burke in #1183
- Add N+1 read guard to the DB query layer by @stefan-burke in #1185
- Address mailto bulk email to a single recipient directly; BCC from business email by @stefan-burke in #1186
- Match footer padding-inline to main by @stefan-burke in #1187
- refactor(admin): split settings.ts into per-feature route modules by @stefan-burke in #1188
- refactor(test): align admin-settings tests with per-feature modules by @stefan-burke in #1192
- API hardening, standardization & admin-integration (audit follow-up) by @stefan-burke in #1191
- Move test utilities to test/ and apply code-quality rules to test code by @stefan-burke in #1184
- Add owner-only Support page gated on ADMIN_EMAIL_ADDRESS by @stefan-burke in #1193
- Rename .attendees-filter to .filter-row and reset form styling by @stefan-burke in #1194
- Move support nag into the form (bold), rename "Built Sites" nav link to "Builds" by @stefan-burke in #1197
- Re-fill form fields after a redirect via a warm-isolate stash by @stefan-burke in #1195
- Remove dark-mode filter-row box-shadow on focus too by @stefan-burke in #1198
- Fix scroll-hide nav never hiding on desktop (sticky offsetTop) by @stefan-burke in #1199
- Listings: per-key TTL cache + targeted reads; index/order fixes for unbounded scans by @stefan-burke in #1196
- Add secrets insight and backfill for existing built sites by @stefan-burke in #1200
- Add quote-cart page on a shared booking-page framework by @stefan-burke in #1189
- Extract shared bump/override deadline forms in built-site panels by @stefan-burke in #1201
- Scope listing deletion so it never affects another listing's attendees by @stefan-burke in #1203
- Move listing-type filter from public page to admin dashboard by @stefan-burke in #1202
- Use @std/collections to replace DIY collection utilities by @stefan-burke in #1205
- Calendar availability checker + attendee form overhaul by @stefan-burke in #1204
- Add focused test runner that builds and cleans up static assets by @stefan-burke in #1206
- Polish calendar row-select and attendee date fields by @stefan-burke in #1207
- Fix dashboard "Add Attendee" button linking to a 404 by @stefan-burke in #1211
- Add SumUp API Keys link to guide and clarify the connection-test 401 by @stefan-burke in #1212
- Add delivery settings, agents, and per-attendee logistics by @stefan-burke in #1210
- Make SumUp "Test Connection" 401 message clear and useful by @stefan-burke in #1214
- Add margin-right to inputs inside .radios and .checkboxes labels by @stefan-burke in #1217
- Show all listings by default on a bare create-attendee form by @stefan-burke in #1216
- Clarify SumUp private vs public API key and disable field autocomplete by @stefan-burke in #1215
- Use gap-s instead of gap-m for label elements by @stefan-burke in #1218
- Re-establish i18n (ICU MessageFormat) on current main by @stefan-burke in #1190
- Batch migration schema probes to stay under the edge subrequest cap by @stefan-burke in #1221
- Encrypt bulk email draft + add reusable email templates by @stefan-burke in #1219
- Make email template delete a typed-confirmation page by @stefan-burke in #1222
- Add delivery-agent user class with a login-gated run sheet by @stefan-burke in #1220
- Pin migration schema reads to the primary by @stefan-burke in #1223
- Fix stuck post-payment reservations and balance-amount mismatches by @stefan-burke in #1208
- Route hot symmetric crypto through node:crypto by @stefan-burke in #1224
- Group delivery run sheet by booking with labelled nested lists by @stefan-burke in #1225
- Maintain listing aggregates with triggers to cut row-read billing by @stefan-burke in #1226
- Simplify delivery run sheet markup and show ticket token by @stefan-burke in #1227
- Replace admin table Actions columns with name links and edit-page actions by @stefan-burke in #1228
- Tidy logistics forms, assign users to agents, footer logout by @stefan-burke in #1229
- Fix admin footer layout: logout sized to content, debug menu below by @stefan-burke in #1230
- Highlight Attendees nav on attendee sub-pages by @stefan-burke in #1231
- Price modifiers: pricing pipeline, engine, CRUD, exact discounts, live checkout by @stefan-burke in #1213
- Modifiers: scope admin UI (link to listings / groups) by @stefan-burke in #1232
- Add min-width: 0 to .admin-footer-top > .inline by @stefan-burke in #1233
- Modifiers: promo codes and opt-in add-ons in the public order form by @stefan-burke in #1235
- Add SMS gateway design doc: minimal first-party polling APK by @stefan-burke in #1209
- Fix overlap index migration failing on pre-rename databases by @stefan-burke in #1237
- Clarify SMS gateway credential labels in advanced settings by @stefan-burke in #1238
- Disable browser autofill on admin customer-info fields by @stefan-burke in #1239
- Log promo code usage to activity log on payment completion by @stefan-burke in #1236
- Require a minimum-length SMS end-to-end passphrase by @stefan-burke in #1240
- Add trigger-maintained aggregate columns to modifiers by @stefan-burke in #1242
- Fix code-quality test failing on i18n format cache by @stefan-burke in #1244
- Remove settings.loadAll() — migrate all routes to targeted loadKeys() by @stefan-burke in #1241
- Remove diff cruft by @stefan-burke in #1243
- Add CRM-lite contact preferences and precommit guardrails by @stefan-burke in #1234
- Use targeted user queries for logistics admin by @stefan-burke in #1249
- Force https:// domains - even on redirects - for all URLs we might fetch by @stefan-burke in #1246
- Optimize admin calendar queries by @stefan-burke in #1255
- Disable aggregate database triggers by default in tests by @stefan-burke in #1256
- SMS webhook freshness and replay protection by @stefan-burke in #1259
- Strict form integers by @stefan-burke in #1257
- Parallelize static asset builds; add fresh DB initializer; adjust date tests timezone by @stefan-burke in #1260
- Split migration files up by @stefan-burke in #1248
- Add GitHub Actions workflow to apply Biome fixes and create PR by @stefan-burke in #1263
- Reservation deposit rounding by @stefan-burke in #1253
- Support modifiers for reservation checkouts by @stefan-burke in #1247
- Make aggregate running totals editable and mismatch-aware by @stefan-burke in #1254
- Internationalize logistics admin templates by @stefan-burke in #1264
- Soften running total mismatch notice by @stefan-burke in #1265
- Gate public checkout by final modifier-aware total by @stefan-burke in #1245
- Slim number input widths by @stefan-burke in #1266
- Track exact modifier applications for usage reporting by @stefan-burke in #1258
- Slim admin nav to 6 items by @stefan-burke in #1267
- Reject invalid UTF-8 on SMS E2E decrypt and relax wrong-passphrase test by @stefan-burke in #1272
- Internationalize remaining public booking labels by @stefan-burke in #1270
- Initialize in-memory test DB from SCHEMA and apply default attendee status by @stefan-burke in #1273
- Validate and tighten URL handling across embed, iframe, redirect, and fetch utilities by @stefan-burke in #1269
- Improve recalculate UI and redirect to edit after recalculation by @stefan-burke in #1271
- Make “Log out” an inline link and add /admin/logout confirmation page by @stefan-burke in #1274
- Add contact follow-up preferences and precommit guardrails by @stefan-burke in #1268
- Add question
display_type(radio/select) with DB migration and UI support by @stefan-burke in #1276 - tests: remove flaky/environment-dependent tests and clean up imports by @stefan-burke in #1277
- Bump schema by @stefan-burke in #1279
- Remove empty and stale cruft files by @stefan-burke in #1278
- Fix escaped ampersands in guide headings by @stefan-burke in #1281
- Add per-answer price modifiers: DB, UI, pricing integration and tests by @stefan-burke in #1275
- Fix setup cache invalidation by @stefan-burke in #1283
- Upgrade GitHub Actions to Node 24 by @stefan-burke in #1282
- Refactor public templates into focused modules by @stefan-burke in #1280
- Add deno deploy task by @stefan-burke in #1286
- Upgrade bug by @stefan-burke in #1287
- Add staging and production Bunny deploy workflows by @stefan-burke in #1284
- Tighten question display type validation by @stefan-burke in #1285
- Admin: add Listings page, rename Dashboard nav to Home, move Add Attendee and tweak attendees UI by @stefan-burke in #1289
- Continue i18n for public reservation page by @stefan-burke in #1291
- Format bookable days with spaces by @stefan-burke in #1290
- Show upcoming holidays on admin dashboard by @stefan-burke in #1292
- Collapse listing embed codes by default by @stefan-burke in #1293
- Replace "Purchase Only" wording with clearer "No Check-In" label by @stefan-burke in #1294
- Harden i18n + public↔webhook pricing seams (fixes answer-modifier ref collision) by @stefan-burke in #1295
- Add 'Assign to all' option for custom questions by @stefan-burke in #1288
- Add authenticated CalDAV calendar feeds and admin settings by @stefan-burke in #1296
- Show listing price in admin details by @stefan-burke in #1297
- Make schema migration DDL race tolerant by @stefan-burke in #1298
- Document modifiers in the admin guide by @stefan-burke in #1299
- Remove redundant DB query from getActiveListingStats by @stefan-burke in #1303
- perf(admin): stop loading all users for the owner settings nag by @stefan-burke in #1305
- admin/calendar: overlap availability query with date-picker loads by @stefan-burke in #1306
- fix(superuser): guard account-state cache against invalidation races by @stefan-burke in #1307
- Link question answers to price modifiers via an "answer" trigger by @stefan-burke in #1301
- Add answer edit page with stats, text, modifier and delete by @stefan-burke in #1310
- Gate answer-tier sold-out block on eligibility; consume tier stock on free bookings by @stefan-burke in #1311
- Move calendar feeds form to settings page and fix rendering by @stefan-burke in #1304
- Column-aware cache invalidation for listing_attendees → listings dependency by @stefan-burke in #1308
- docs: discourage SELECT * and broad get-all queries in AGENTS.md by @stefan-burke in #1315
- Make jscpd failures tell the agent how to fix duplication by @stefan-burke in #1318
- Make debug-footer SQL timing parallelism-aware by @stefan-burke in #1316
- Remove dead sr-only span from custom-question select fields by @stefan-burke in #1312
- Promo-code field visibility + SCSS stylesheet with a :has() visibility mixin by @stefan-burke in #1314
- Alias listings table as
listinginstead of single-letterein SQL by @stefan-burke in #1319 - Improve /admin/questions UI: tables, listings counts, editable answer stats by @stefan-burke in #1313
- Unify CSV/table-action code; add listings & attendees CSV exports by @stefan-burke in #1317
- Sign checkout prices so the payment webhook can trust them instead of recalculating by @stefan-burke in #1300
- Fix cookie-authenticated calendar feed GETs failing CSRF by @stefan-burke in #1320
- Fix listing deletion FK failure and clarify attendee unlinking by @stefan-burke in #1322
- Migrate timezone/date utilities to Temporal (bundled polyfill) by @stefan-burke in #1321
- Add per-site "update" button on /admin/built-sites/:id by @stefan-burke in #1324
- Reject zero-value modifiers and show the validation error by @stefan-burke in #1325
- Add a Privacy page under Settings by @stefan-burke in #1323
- Invalidate settings cache on settings table writes by @stefan-burke in #1326
- Update admin privacy explainer copy by @stefan-burke in #1327
- Internationalize scanner UI copy by @stefan-burke in #1329
- Add curl-backed Deno CLI scaffold by @stefan-burke in #1328
- Improve test browser form submission defaults by @stefan-burke in #1338
- Batch modifier scope resolution by @stefan-burke in #1337
- Improve compact TAP reporter progress total handling by @stefan-burke in #1340
- Avoid balance summary listing N+1 queries by @stefan-burke in #1334
- Refactor admin recalculate page table layout and update tests by @stefan-burke in #1331
- Align the CLI to the admin API resources that actually exist by @stefan-burke in #1341
- Add a public /scheduled endpoint: cron-guaranteed pruning + builder fleet-walk by @stefan-burke in #1330
- Add progressively-enhanced booking-form running total by @stefan-burke in #1343
- Out-of-band DB backups: paginated dumps, fleet credentials endpoint, and a pre-update backup gate by @stefan-burke in #1342
- Add contact history and per-channel admin notes (email + phone) to attendee admin UI and DB by @stefan-burke in #1332
- Back up before staging/production deploys (filtered to the matching site) by @stefan-burke in #1347
- Scope backups to a per-site storage folder by @stefan-burke in #1348
- Remove aspect-ratio sizing from textareas by @stefan-burke in #1349
- ci: use ubicloud-standard-4 for all workflows except test by @stefan-burke in #1350
- Align the whole toolchain to the pinned Deno 2.5.6 and make the suite green on it by @stefan-burke in #1352
- Run the strict read-only lint in precommit everywhere by @stefan-burke in #1355
- Split contact booking stats by source; add a per-contact record editor by @stefan-burke in #1333
- Test-quality methodology, mutation-resistance criteria, and example tests by @stefan-burke in #1356
- Skip backup on staging auto-deploys so merges to main need no key by @stefan-burke in #1354
- Reuse markdown editor box for contact record private notes by @stefan-burke in #1357
- Reduce false positives/negatives in the test-quality audit by @stefan-burke in #1359
- Add a bookings summary table to the attendee edit page by @stefan-burke in #1358
- Free-text custom questions + string created timestamps by @stefan-burke in #1344
- Bind attendee PII at rest to the account password (KEK v2) by @stefan-burke in #1351
- Hide show-total button once running-total JS enhances the form by @stefan-burke in #1361
- Strengthen recent tests flagged in test-quality review by @stefan-burke in #1362
- Fix Modifiers guide section pulling in unrelated listing FAQs by @stefan-burke in #1360
- Cover attendees-list type filter directly + strengthen assertions by @stefan-burke in #1364
- Test migrations against populated historical fixtures; skip index sync when columns are missing by @stefan-burke in #1346
- Record full booking value as owed when no payment provider is set up by @stefan-burke in #1365
- Add I18N_REPLACEMENTS for case-aware substring rebranding of i18n copy by @stefan-burke in #1368
- Render flash on save across admin pages; add follow-and-render redirect assertion by @stefan-burke in #1370
- Remove flash boilerplate the Layout backstop made redundant; fix bulk-email form-targeting by @stefan-burke in #1371
- Split admin listings.ts into per-feature modules by @stefan-burke in #1372
- Add encryption overview to the admin guide by @stefan-burke in #1373
- Fix free-text answer string ids dropped by replica reads at checkout by @stefan-burke in #1375
- Fix flaky CLI e2e test: pin subprocess cwd, drop global chdir by @stefan-burke in #1377
- Add Attendee and Listing columns to the global activity log by @stefan-burke in #1374
- Add in-house mutation testing (deno task mutation) by @stefan-burke in #1376
- Mutation tester: treat zero-mutant runs as inconclusive by @stefan-burke in #1378
- Add double-entry accounting design doc by @stefan-burke in #1369
- Test validatePrice directly to close its mutation gaps by @stefan-burke in #1379
- Unify ledger leg-identity into one shared field table by @stefan-burke in #1380
- Store transfers time as INTEGER epoch-millis; Temporal-backed instant validation by @stefan-burke in #1384
- Pin lineListPrice and the reservation feeSubtotal branch (mutation gaps) by @stefan-burke in #1385
- Mutation tester: new mutation operators + known-equivalent ignore-list by @stefan-burke in #1383
- Pin error signalling + syncReadOnlyFrom to close site-assignment mutation gaps by @stefan-burke in #1382
- test: harden reservation-amount and balance tests against surviving mutants by @stefan-burke in #1386
- Unify ledger balance, post guards, and row-account builders by @stefan-burke in #1387
- Make table rebuilds atomic with a Bunny interactive transaction by @stefan-burke in #1388
- Restore a site to a point in time: data + the commit it was running by @stefan-burke in #1389
- Harden restore-deploy workflow; fix commit/version marker edge cases by @stefan-burke in #1390
- Don't clear the commit marker on a dev/source boot by @stefan-burke in #1392
- Pin admin nav as a sticky left sidebar on desktop by @stefan-burke in #1391
- Make locale (country) write-once: set at /setup, remove from admin settings by @stefan-burke in #1394
- Label the admin nav landmarks for screen readers by @stefan-burke in #1393
- Harden code-quality tests: 35.8% → 100% mutation score by @stefan-burke in #1397
- Encrypt the activity log with the owner key, not DB_ENCRYPTION_KEY by @stefan-burke in #1396
- docs: prefer libsql batches/transactions for complex writes by @stefan-burke in #1398
- Cross-fade between page loads with cross-document view transitions by @stefan-burke in #1399
- Add AGENTS.md guidance on build order and completeness by @stefan-burke in #1401
- Universal link styling with an "Underline links" setting by @stefan-burke in #1400
- Double-entry accounting: every money figure now comes from one ledger by @stefan-burke in #1381
- Retry migration verify() on read-your-writes schema lag by @stefan-burke in #1402
- Log completed SQL statements to system logs by @stefan-burke in #1403
- Backfill the ledger in one batch per page, not one per attendee by @stefan-burke in #1404
- Extract shared retryWithBackoff helper by @stefan-burke in #1405
- Harden query-log tests to kill mutation survivors by @stefan-burke in #1406
- Assert the retry backoff actually suspends between attempts by @stefan-burke in #1408
- Stop stretching tables; codify column-width schema by @stefan-burke in #1409
- Decision 17: require a money decision when a merge discards a paid booking (+ eventGroup batch guard) by @stefan-burke in #1410
- Read the session private key from AsyncLocalStorage, not threaded args by @stefan-burke in #1407
- Unify table action rows under .table-actions; left-align; table width: fit-content by @stefan-burke in #1413
- Add view-transition-name to admin sidebar by @stefan-burke in #1411
- Post-merge tidy-up: remove the merged accounting design doc by @stefan-burke in #1412
- Rework the Ledger page: range filter, stats, hidden cash legs by @stefan-burke in #1414
- Tidy-up: unify account-balance reads + harden transfer tests via mutation testing by @stefan-burke in #1415
- Extract a shared queryIdColumn db helper (dedup) by @stefan-burke in #1416
- Reverse attendee ledger statement figures for readability by @stefan-burke in #1418
- test(refund-ledger): extract expectSingleRefundCash helper by @stefan-burke in #1421
- fix: finalize ticket session atomically inside the creation transaction by @stefan-burke in #1417
- Add system_notes: per-attendee operator and system notes by @stefan-burke in #1420
- Add no-quantity booking lines (quantity = 0 sentinel) by @stefan-burke in #1366
- Extend duplication detection to the test suite by @stefan-burke in #1419
- test(e2e): retrofit existing specs onto the shared e2e helpers by @stefan-burke in #1423
- Properly typed pipes by @stefan-burke in #1425
- Pipe fix by @stefan-burke in #1427
- Add Sentry server-side error reporting by @stefan-burke in #1429
- Copy SENTRY_URL to built sites by @stefan-burke in #1430
- Keep unhonorable signed payments as quantity-0 placeholders by @stefan-burke in #1422
- Require a regression test with every bug fix by @stefan-burke in #1431
- Surface paid-line no-quantity block at top of page, disable the box by @stefan-burke in #1428
- Bundle client assets with the deno-loader, like the edge build by @stefan-burke in #1426
- Make saved settings take effect immediately via a settings version counter by @stefan-burke in #1432
- Add regression tests for promo code discounts in /calculate running total by @stefan-burke in #1433
- Reduce dupes by @stefan-burke in #1424
- Fix paid-booking "Transaction timed-out" by writing the booking finalize as one batch by @stefan-burke in #1435
- Show list prices before modifiers in the order summary by @stefan-burke in #1434
- Eliminate chatty interactive transactions; add a round-trip guard by @stefan-burke in #1437
- Test dedup by @stefan-burke in #1436
- Refactor built site field mapping by @stefan-burke in #1441
- Add per-site update channel to built_sites and tier-filter the deploy fleet by @stefan-burke in #1439
- Mutation tests by @stefan-burke in #1442
- Guard batched booking writes against existing ledger legs by @stefan-burke in #1438
- Test and mutation-harden test utilities by @stefan-burke in #1446
- Harden test utils by @stefan-burke in #1448
- Add admin ledger entry maintenance by @stefan-burke in #1444
- Retry migration up() on transient verify failure, not verify() alone by @stefan-burke in #1445
- De-duping loads of tests by @stefan-burke in #1447
- Test dedup mk2 by @stefan-burke in #1450
- Add end-to-end test to validate request-scoped N+1 guard by @stefan-burke in #1345
- Add Deno Deploy hosting and Turso database providers by @stefan-burke in #1449
- Add servicing events planning doc by @stefan-burke in #1395
- Fix boot loop after DB reset and improve backup restore robustness by @stefan-burke in #1451
- Parent/child listings: required child selection at booking by @stefan-burke in #1363
- Servicing events: follow-up fixes doc by @stefan-burke in #1453
- Read-only mode: default-deny for all mutating methods by @stefan-burke in #1454
- Replace DELETE with upsert when clearing stale commit marker by @stefan-burke in #1456
- Servicing admin form: remove hidden indicator & questions, add button icons by @stefan-burke in #1455
- Add global boot checks and MAIN_INSTANCE_KEY validation by @stefan-burke in #1457
- Add listing template picker and dim-hide form by @stefan-burke in #1452
- Reduce log verbosity in deploy-clients: omit site names by @stefan-burke in #1460
- Add workflow to clean up workflow runs older than 7 days by @stefan-burke in #1461
- Reduce duplication by @stefan-burke in #1458
- Allow 0 days by @stefan-burke in #1459
- List slow tests by @stefan-burke in #1468
- Add external order library spec by @stefan-burke in #1443
- Add Editor user class by @stefan-burke in #1465
- perf(tests): reduce RSA key size and batch migration setup to speed up slow tests by @stefan-burke in #1469
- Harden the oldest-untouched test files against mutation by @stefan-burke in #1466
- Enable extra-strict TypeScript compiler flags by @stefan-burke in #1471
- Add staged-file mutation gate to precommit by @stefan-burke in #1470
- Drop jscpd test minTokens to 48 and fix all raised duplications by @stefan-burke in #1472
- Add a self-contained Deno Deploy bundle target by @stefan-burke in #1473
- Listing defaults: operator-set defaults that listings inherit live by @stefan-burke in #1467
- Add feature-complete, unify-systems, and shared-interface principles to AGENTS.md by @stefan-burke in #1474
- Question descriptions by @stefan-burke in #1476
- Batch mutation test runs to bound open file descriptors by @stefan-burke in #1478
- Add Custom CSS advanced setting served from /custom.css by @stefan-burke in #1477
- Add login links to setup-complete and account-activated pages by @stefan-burke in #1479
- Add /order.js debug flag and make the widget hot path efficient by @stefan-burke in #1481
- Buffer body-bearing POSTs at the request entrypoint by @stefan-burke in #1480
- Fix flaky tests at the source (no parallelism changes) by @stefan-burke in #1483
- Make the writeoff-adjustment timing tests deterministic by @stefan-burke in #1485
- Standardize FakeTime clock restoration on
usingin tests by @stefan-burke in #1486 - Request-scope iframe/CSRF/saved-form state to fix concurrent-request leakage by @stefan-burke in #1487
- Add a suite-level storage backend option to describeWithEnv by @stefan-burke in #1488
- Add browser-driven payment sandbox e2e harness by @stefan-burke in #1489
- Harden payment sandbox e2e: reject live keys, honest Stripe scope, assert paid amount by @stefan-burke in #1491
- Tighten payment sandbox e2e: Stripe key guard, income-ledger assertion, currency scope by @stefan-burke in #1492
- Plan: top-level Site, user Pages, and recursive public nav by @stefan-burke in #1490
- Always serve /custom.css as a stylesheet, never an HTML system page by @stefan-burke in #1495
- Add a listing_prices table synced with the listing price columns by @stefan-burke in #1494
- Fix payment sandbox e2e login hang over the tunnel by @stefan-burke in #1493
- Document the ten qualities every new system should have, with exemplars by @stefan-burke in #1498
- Servicing deep-dive: fix P2 bugs, unify money parsing, schema-tize UI by @stefan-burke in #1499
- Entity pages: schema-driven tabbed framework + attendee migration (slice 1) by @stefan-burke in #1500
- Site → Pages: admin CRUD + item manager, public /page route, recursive nav by @stefan-burke in #1496
- Attendees fully tab-native: remove the legacy action URLs by @stefan-burke in #1502
- Servicing follow-ups: fix dateless cost replay, unify money parsing app-wide by @stefan-burke in #1501
- Entity pages: left-align tabs, section-panel grouping, prose chunks by @stefan-burke in #1503
- Groups as packages: multi-group membership + bookable bundles with per-member pricing, quantities, capacity & privacy by @stefan-burke in #1462
- Retire completed planning docs; track remaining follow-ups in TODO.md by @stefan-burke in #1505
- Make modifier fixed calc_value and min_subtotal currency-aware by @stefan-burke in #1504
- Ledger deep-dive: dedup, schema-tize, valibot, and mutation-harden the whole feature by @stefan-burke in #1506
- i18n: throw on a missing translation key instead of rendering it raw by @stefan-burke in #1507
- Kill the mutation-gate survivors the attendee tab migration left behind by @stefan-burke in #1508
- Fix parallel-suite fd exhaustion behind the CI flakiness (+ harden describeWithEnv storage option) by @stefan-burke in #1510
- Strengthen tests for attendee merge/refund/roster logging and logic by @stefan-burke in #1512
- Multiple parents per child + "Can be booked by itself" (bookable_alone) by @stefan-burke in #1497
- Finish switching per-listing pricing onto listing_prices (flat package price + day_prices) by @stefan-burke in #1513
- Listings on the entity-pages framework: collapse detail + edit into one tabbed page by @stefan-burke in #1514
- Fix listing-save crash from reading a row back on a lagging replica by @stefan-burke in #1509
- Fix cross-worker READ_ONLY_FROM leak behind the merge-queue flakes by @stefan-burke in #1515
- Import/export a listing or group as JSON, with cross-entity name uniqueness by @stefan-burke in #1511
- Harden catalog transfer: strict keys, editor export links, cell-fidelity by @stefan-burke in #1516
- Group attendee tables by attendee with an ordered Listings column by @stefan-burke in #1517
- Run the listings-tag data migration on upgraded sites (LATEST_UPDATE bump) by @stefan-burke in #1518
- Make listing Overview tab read collated aggregates, not every attendee by @stefan-burke in #1519
- Document the last month's features in the admin guide by @stefan-burke in #1520
- Improve mutation testing suite by @stefan-burke in #1521
- Unify admin and public navs onto one shared leveled-nav renderer by @stefan-burke in #1523
- Add a ProseMirror rich markdown editor to markdown textareas by @stefan-burke in #1524
- Explain why un-enhanceable order links are skipped by @stefan-burke in #1525
- Transcode uploaded images to WebP at two sizes (full + thumbnail) by @stefan-burke in #1527
- Turn on jscpd for TSX and drive it to 0% with shared helpers by @stefan-burke in #1528
- Replace admin file-level jscpd ignores with import-block markers by @stefan-burke in #1529
- Unify near-miss admin pages onto shared page helpers by @stefan-burke in #1530
- Unify QR and balance signed tokens onto a defineSignedToken factory by @stefan-burke in #1532
- Add mutation-hardened unit tests for listing-filter by @stefan-burke in #1531
- Add mutation-hardened unit tests for filter-bar by @stefan-burke in #1533
- Add mutation-hardened unit tests for projection-sql by @stefan-burke in #1534
- Style the badge variants and document building stripe-mock with Go by @stefan-burke in #1535
- Add mutation-hardened unit tests for SelectField by @stefan-burke in #1536
- Add mutation-hardened unit tests for API request schemas by @stefan-burke in #1538
- Drop dead applyFlash from listing recalculate; test its activity log by @stefan-burke in #1539
- Migrate the groups admin page onto the entity-pages framework by @stefan-burke in #1537
- Add mutation-hardened unit tests for ticket-url by @stefan-burke in #1540
- Unify listingInfoColumns across the two CSV exporters by @stefan-burke in #1543
- Consolidate effective-domain localhost fallback to one place by @stefan-burke in #1541
- Add line-count script and remove wasm blobs by @stefan-burke in #1548
- Rely on shared types to cut duplicated shape definitions by @stefan-burke in #1542
- Collapse payment-provider settings routes onto one factory + a metadata registry by @stefan-burke in #1549
- docs: add ~400-line file limit and pure/non-pure split guidance by @stefan-burke in #1546
- Mutation-harden modifiers.ts and drop its dead recalc applyFlash by @stefan-burke in #1545
- Extract shared edge-bundle pipeline from the two production builds by @stefan-burke in #1544
- Kill mutants that produce lint-forbidden code via a lint gate by @stefan-burke in #1550
- Mutation-harden the payment-provider settings routes and registry by @stefan-burke in #1551
- Promote saveSecret helper + add single-field settings section factories by @stefan-burke in #1547
- Mutation-harden questions.ts admin routes by @stefan-burke in #1552
- Fix payment sandbox e2e for tabbed listing page, add ntfy failure alerts by @stefan-burke in #1553
- Unify the aggregate-recalculate POST flow into runRecalculatePost by @stefan-burke in #1554
- Mutation-harden column-order.ts's template tests by @stefan-burke in #1555
- Fix Attendees-tab click scoping and harden ntfy notification in e2e-payments by @stefan-burke in #1556
- Fix column-order.ts's premature date conversion in filter pipelines by @stefan-burke in #1557
- Add a shared Badge component and route every badge through it by @stefan-burke in #1558
- Mutation-harden sort-listings.ts's comparator tests by @stefan-burke in #1559
- Mutation-harden session-ledger.ts's IO loader tests by @stefan-burke in #1560
- Extract plain functional booking model by @stefan-burke in #1562
- Mutation-harden 5 under-tested src/shared files by @stefan-burke in #1561
- Mutation-harden src/shared/booking/model.ts by @stefan-burke in #1564
- Split and schema-tize admin listing templates by @stefan-burke in #1565
- Further de-dupage by @stefan-burke in #1566
- Mutation-harden src/shared/booking/package-cap.ts by @stefan-burke in #1567
- Mutation-harden src/shared/booking/build-tree.ts by @stefan-burke in #1568
- Mutation-harden src/shared/cache-registry.ts by @stefan-burke in #1569
- Mutation-harden src/shared/listing-parents-rules.ts + fix ICU quoting bug by @stefan-burke in #1571
- Schema-tize string settings registry by @stefan-burke in #1570
- Promote images to first-class resources by @stefan-burke in #1563
- Backfill listing images in the first-class images migration by @stefan-burke in #1573
- Test file organisation WIP by @stefan-burke in #1572
- Fix restore failing with "missing table settings" after the wipe by @stefan-burke in #1575
- Harden the restore rebuild, show backup timestamps, correct credential docs by @stefan-burke in #1576
- Restore backups that predate a column-dropping migration by @stefan-burke in #1577
- Harden ATTENDEE_TABLE_COLUMNS test coverage (mutation score 30.5% → 100%) by @stefan-burke in #1574
- Refuse restoring backups from newer builds; gate legacy-column re-adds by @stefan-burke in #1581
- Fail closed on unknown same-date migration ids in restored dumps by @stefan-burke in #1582
- Harden band-name-generator.ts test coverage (mutation score 58.2% → 100%) by @stefan-burke in #1584
- Order flow, listings, and admin nav/actions updates by @stefan-burke in #1579
- Detect duplicate type shapes; unify genuine duplicate types by @stefan-burke in #1585
- Harden logistics-filter.ts test coverage (mutation score 89.2% → 100%) by @stefan-burke in #1580
- Split server-images.test.ts and server-bulk-email.test.ts, drop biome grandfathering by @stefan-burke in #1583
- Split two files out of the noExcessiveLinesPerFile ignore list by @stefan-burke in #1586
- Split two grandfathered over-1000-line files, drop their biome overrides by @stefan-burke in #1587
- Split test/lib/db/listings.test.ts, drop its biome override by @stefan-burke in #1588
- Harden largest-remainder.ts test coverage (mutation score 89.8% -> 100%) by @stefan-burke in #1591
- Harden square-validation.ts test coverage (mutation score 75.0% -> 100%) by @stefan-burke in #1592
- Split test/lib/questions.test.ts, drop its biome override by @stefan-burke in #1590
- Harden renewal-helpers.ts test coverage (mutation score 82.9% -> 100%) by @stefan-burke in #1593
- Split test/lib/server-reservation.test.ts, drop its biome override by @stefan-burke in #1595
- Harden package-privacy.ts test coverage (mutation score 96.4% -> 100%) by @stefan-burke in #1594
- Let customers fill in their address by searching their postcode by @stefan-burke in #1589
- Document the == null exception to the equivalent-mutants no-=== rule by @stefan-burke in #1596
- Add unit-tests-report task to track per-file test coverage by @stefan-burke in #1597
- Harden slug.ts test coverage (mutation score 58.1% -> 100%) by @stefan-burke in #1599
- Harden url-safety.ts test coverage (mutation score 64.0% -> 100%) by @stefan-burke in #1602
- Move payment-processing tests onto their mirror path + harden the report by @stefan-burke in #1601
- Tidy address lookup panel layout by @stefan-burke in #1598
- Make booking address field always editable by @stefan-burke in #1603
- Fix admin nav: keep create links inside their section and highlight the current page everywhere by @stefan-burke in #1600
- Multi-package orders: book packages alongside listings in one order by @stefan-burke in #1578
- Tidy admin menus and address form: concise sub-nav, postcode prefill by @stefan-burke in #1606
- Scroll to validation errors and fade error alerts in by @stefan-burke in #1605
- Consolidate SQL reads and updates into shared builders by @stefan-burke in #1607
- Continue menu consolidation: quick actions, Images sub-nav, tidy titles by @stefan-burke in #1608
- Sealed string types: encrypted values carry their encryption scheme in the type system by @stefan-burke in #1610
- Group linked-item checkboxes by type with a shared component by @stefan-burke in #1612
- Click SumUp's "Back to merchant website" after paying by @stefan-burke in #1613
- Admin pages: unify guide links to the bottom, trim redundant titles by @stefan-burke in #1609
- Add a news system under the Site tab by @stefan-burke in #1611
- Reuse the linked-items checkbox component across modifiers, questions & groups by @stefan-burke in #1614
- Document shipped features in the README by @stefan-burke in #1616
- Split the modifiers page templates into a folder with mirrored unit tests by @stefan-burke in #1617
- Attendee Logistics tab: pin the address on a map and see everyone booked that day by @stefan-burke in #1615
- Fix wide tables stretching pages sideways on mobile by @stefan-burke in #1618
- Split the payment-helpers tests into a folder and drop their line-limit exemption by @stefan-burke in #1619
- Split admin listings API tests into a folder and drop its max-lines exemption by @stefan-burke in #1621
- Move mis-placed unit tests onto the paths the coverage report expects by @stefan-burke in #1620
- Split the modifier tests off the max-lines list (and tidy the create shard) by @stefan-burke in #1622
- Split the admin-questions tests off the max-lines list by @stefan-burke in #1623
- Split the multi-day-bookings e2e tests off the max-lines list by @stefan-burke in #1624
- Split the admin-groups tests off the max-lines list by @stefan-burke in #1626
- Let staff open any date on the deliveries run sheet by @stefan-burke in #1627
- Split the giant attendee-merge test into a mirrored folder suite by @stefan-burke in #1625
- Share the listing-activity-log assertions and CSV-export test helper by @stefan-burke in #1628
- Tidy up the deliveries date picker wiring by @stefan-burke in #1630
- Roll back partial bookings in the bookTestAttendee test helper by @stefan-burke in #1629
- Group the listings DB tests into a mirrored folder suite by @stefan-burke in #1631
- Group the questions DB tests into a mirrored folder suite by @stefan-burke in #1634
- Move the migrations DB test to its mirror path by @stefan-burke in #1636
- Tabbed News & Pages editors, editable slugs with public links, and page images by @stefan-burke in #1632
- Split two more test files off the max-lines list (attendee form + test-utils) by @stefan-burke in #1633
- Split the signed-price webhook tests off the max-lines list by @stefan-burke in #1639
- Give every admin form section a proper legend heading by @stefan-burke in #1637
- Split the public-page template tests off the max-lines list by @stefan-burke in #1640
- Mirror and harden the fields.ts form-field tests by @stefan-burke in #1638
- Move the Site content editors' guide link into a GuideFooter by @stefan-burke in #1642
- Tighten types and remove repeated boilerplate to shrink src/ by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1635
- Harden the payments.ts booking-line and status tests by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1641
- Split the admin listings template tests off the max-lines list by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1643
- Fix "Logistics saved for {value}" showing a raw placeholder by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1644
- Make quantity-0 pay-link limits obvious to owner and customer by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1646
- Delete cached-table alias exports and call the cached objects directly by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1649
- Give every substantive admin page a guide footer; guard against dead anchors by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1647
- Merge the attendee Balance tab into the Ledger tab by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1645
- Pin entity tabs and attendee notes across page navigations by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1651
- Delete the remaining stray alias exports and call the objects directly by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1652
- Split the public API route tests off the max-lines list by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1648
- Hold the page header still when switching between an entity's tabs by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1653
- Consolidate and harden the auth.ts tests at the mirror path by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1650
- Schematize the api_keys table onto defineTable; share the id/created + column fragments by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1657
- Split the accounting e2e lifecycle tests off the max-lines list by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1654
- Harden the chart-of-accounts tests by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1656
- Hide the "Add" sub-menu when viewing a single attendee (and other single-item pages) by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1658
- Harden image deletion and payment sandbox e2e by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1661
- Add opencode action by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1663
- Harden OpenCode comment workflow by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1664
- Split long files: fields.ts and migrate e2e-payments to Deno by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1662
- Opencode for issues by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1666
- Split settings.ts into settings/ folder, fixed jscpd by @opencode-agent[bot] in https://github.com/chobbledotcom/tickets/pull/1669
- Add a direct unit test for anyPackageBundleMismatch by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1659
- Change wording of promo codes by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1670
- De-duplicate CRUD stuff by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1671
- Reduce duplicate and unused code paths by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1672
- Split the public API routes into one module per job by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1673
- [codex] Reduce route and generated asset line count by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1674
- [codex] Use shared Deno cache setup by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1675
- [codex] clear coverage before test runs by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1677
- Isolate stripe-mock test runs by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1676
- Fix payment sandbox browser regression by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1680
- Split server-parents-booking.test.ts into themed files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1679
- Show previous bookings by email or phone by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1660
- Split server-attendees.test.ts into themed test files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1681
- Split the parents-gate test monolith into themed files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1684
- Split questions.ts up by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1678
- [codex] Isolate mutation test runs by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1682
- Land the CodeRabbit thread fixes and AGENTS.md guidance missed by the #1684 squash merge by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1687
- Split the payment-processing file into smaller, focused files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1692
- Split the listing-parents test file into a folder of focused files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1691
- Use a test-mode Stripe key in the payment dashboard-link test by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1695
- Split the payments test monolith into focused files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1690
- Keep return_url when an attendee action's typed name doesn't match by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1696
- Split the Stripe and Square test files into focused suites by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1694
- Assert the real validation error in the add-attendee failure test by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1700
- Build form dropdowns from their schemas instead of hand-typed lists by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1699
- Schema-tize admin nav, redirect targets, read-only patterns, and limits by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1689
- Assert the payment stays un-refunded after a ledger-record failure by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1701
- Speed up the test suite without reducing coverage by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1702
- Make price-rule precedence a single declarative table by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1698
- Keep modifier form options off the cold-start path by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1703
- Guard the allocations metadata field against the provider length cap by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1705
- Put all the capacity rules in one declarative table by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1704
- Add listing attributes for display and filtering by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1683
- Consult capacity rules for feature-layer capacity dates (stage 3) by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1706
- Shard the legacy-migration test so its runs parallelise by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1707
- Fix hidden sole-child listing attributes leaking on public ticket pages by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1708
- Remove public.tsx template barrel file by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1709
- Balance payments for any status: merge main + address remaining review P2s by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1697
- Make the database migration tests faster by batching DB round-trips by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1688
- Give each attribute option its own edit page, with listing usage tables by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1710
- Secure delivery mark-done date scope by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1711
- Write the admin-guide "Money Ledger" section by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1715
- Read only the listings an attribute uses, in one query by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1716
- Load only the code a request needs: admin route manifest and import-graph slimming by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1717
- Split the booking-page template into focused files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1693
- Write the remaining admin-guide sections by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1718
- Pin demo sample pools and the error-code table against blanked-string mutants by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1722
- Restrict opencode action to new issues only by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1723
- Remove test/test-utils barrel file by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1712
- Lay out the listing attributes tab's options as checkbox rows by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1727
- Await the resend webhook deterministically instead of a 0ms timer by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1725
- Close the demo-override and logger mutation gates, drop magic test floors by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1726
- Extend jscpd coverage to scripts/ and remove all duplication by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1713
- Clean up stale references to the removed test-utils barrel by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1731
- Add the admin-guide Getting Help / Support section by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1724
- Cold start: 7→2 first-request DB round trips and fewer eager modules, with benchmarks by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1714
- Enable useConsistentArrayType biome rule by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1730
- Harden expected-actual.tsx tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1728
- Keep a standalone parent's child selector when its package is sold out by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1732
- Harden refunds/provider.ts tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1733
- Turn the child-listing error checks into one ordered rule list by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1734
- Enforce cognitive-complexity limit everywhere; narrow scripts biome override by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1729
- Keep seeded PRNG output strictly below 1 in checkout-pricing test by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1738
- Unify the helpers left behind by the big file splits by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1735
- Document offensive programming: never suppress errors by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1737
- Move 104 test files to their sources' mirror locations by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1736
- Harden admin logistics template tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1739
- Harden payments.ts tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1742
- Keep the SMS webhook off the CRUD API import path by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1740
- Split the database schema file into a folder of focused files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1720
- Wrap admin footer Chobble link in a span so it doesn't stretch by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1743
- Make the fold test prove a rejected booking leaves the count untouched by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1741
- Add docs/importer-plan.md — the CSV importer plan (ledger-native, verified against main) by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1339
- Split the booking-page template into a folder of focused files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1721
- Address PR queue review comments and cover the lock-refresh stop path by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1744
- Cold start: load migration implementations and the superuser email lazily by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1745
- Harden ticket token-utils tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1748
- Add merge check workflow by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1750
- importer-plan: address the final Codex review round from #1339 by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1747
- Harden listing aggregates template tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1752
- Share Claude Code settings for this repo: fewer prompts, auto-setup, auto-format by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1751
- Harden public error-page tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1753
- Harden attendee PII crypto tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1755
- Speed up tests and every request by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1749
- Make the unit-tests-report import-aware, so it can find misplaced tests by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1754
- Harden data-table component tests to 100% mutation kill rate by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1756
- Log start times by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1757
- Keep admin routes, navigation, and read-only controls in sync by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1746
- Add a direct test for the "other attendees on these days" query by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1758
- Strengthen the overlap test and add a test for the order-option builders by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1759
- Add a test for the shared "send us a message" form helpers by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1761
- Enable all useSorted* Biome flags and canonicalising rules to catch duplication by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1760
- Add a test for the ledger "adjust this figure" corrections by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1762
- Rule out mutants with a type-check before running the tests by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1763
- Move six misplaced unit tests to their source mirror paths by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1765
- Fail fast when a mutation-CLI flag is given no value by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1766
- Move eleven more misplaced unit tests to their source mirror paths by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1769
- Explain why a listing can't join a package (and a few other blocked combinations) by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1770
- Serve shared build assets from an optional CDN by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1768
- Move the servicing db-module tests to their mirror location by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1772
- Move the servicing feature tests into the integration and e2e trees by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1774
- Give every page the same spacing structure by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1767
- Add a "simple language" rule for the words we show people, and enforce it by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1773
- Widen the public site to 1080px by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1776
- Fix two servicing tests that weren't actually testing anything by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1777
- Make money histories easier to understand by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1771
- Make the Privacy page read like a settings page, not a lecture by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1778
- Move nine more misplaced unit tests to their source mirror paths by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1781
- Use one shared method for simple record links by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1780
- Strengthen the servicing ledger test assertions by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1779
- Trim cold-start work: load the storage SDK and the admin guide on demand by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1782
- Split the large servicing ledger test into focused files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1783
- Stop re-scanning the ledger for a listing's profit by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1784
- Add "annotate return types, keep types easy to compile" to our house rules by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1786
- Build the last start-up translations only when they're needed by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1788
- Tighten the duplicate-code check and share more code across the app by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1787
- Skip ICU compilation for plain-text messages by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1789
- Make attendee reads faster and load them all one way by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1790
- Make money history and service costs easier to manage by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1785
- Add a script that splits a branch's diff into code vs imports, comments and blanks by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1793
- Tighten the duplicate-code check one more notch and remove the duplication it found by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1792
- Run the test suite in roughly half the time by sharing work between test files by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1794
- Run new Bunny databases on every region, not just Europe by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1797
- Fix the "[object Object]" error box on the listing Questions and Attributes tabs by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1796
- Show a red pixel for broken images, and delete the broken records left by the images migration by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1798
- Make the duplicate-code check stricter and remove the duplication it finds by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1795
- Rename "Money history" to "Money" by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1799
- Address CodeRabbit review feedback on the Money rename by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1801
- Run the duplicate-code check on the faster jscpd v5, and clear every duplicate it found by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1800
- Fold repeated code onto shared helpers across the app by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1803
- Give the ledger's leg-matching one home (ledger/legs.ts) by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1804
- Give the two admin write paths one shared write step by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1805
- Let the shared write step decide when a save needs a transaction by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1806
- Curry two repeated config blocks into small factories by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1807
- Wrap top-of-file import blocks in jscpd:ignore by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1808
- Merge three more duplication clusters by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1814
- Address review feedback on the merged jscpd cleanup by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1811
- Ignore the shared migration-schema column fragments in jscpd by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1812
- Merge four more genuine duplication clusters by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1813
- Unify listing/modifier recalculate routes, collapse migration verify checks by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1815
- Merge four more genuine duplication clusters (mt18 batch) by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1817
- Reuse VALID_DAY_NAMES in the catalog-transfer schema, keep it dependency-free by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1818
- Unify the session/form/entity guard type shapes by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1819
- Unify attendee dependent-row purges across single and orphan paths by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1821
- Post placeholder payment and refund atomically by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1822
- Make public listings faster and keep requests within Bunny limits by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1820
- Only complete a Stripe refund when its status is "succeeded" by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1824
- Pass QR checkout errors through with their message and status by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1823
- Use one response handler and organise capacity checks by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1825
- Gate note ledger links by owner role by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1826
- Strengthen functional utility mutation coverage by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1828
- Harden Stripe webhook setup: same-URL cleanup, atomic credentials, shared URL helper by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1827
- Add one shared builder for signed paid booking rows by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1829
- Add bundle composition reporting and harden Sentry by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1831
- Save each booking completely or not at all by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1833
- Use a small MIME list for attachments by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1835
- Move backup storage helpers into one file by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1837
- Separate database snapshot exports by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1836
- Use native Apple Wallet signing and verify Sentry delivery by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1834
- Consolidate shared entity routes, data helpers, and test setup by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1830
- Add checkout stage database foundation by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1840
- Keep Stripe selected when webhook cleanup fails by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1841
- Let owners choose optional site features by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1838
- Use form definitions for rendering and validation by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1843
- Split request handling into focused modules by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1847
- Keep database upgrades within edge request limits by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1845
- Load routes and page copy only when needed by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1839
- Harden lazy route mutation tests by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1849
- Simplify dormant checkout stage cleanup by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1848
- Stripe best practices by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1846
- Keep image uploads and database reads consistent by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1842
- Batch related database reads and transaction writes by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1851
- Split massive forms.tsx file up by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1852
- Use one page system across admin records by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1850
- Make targeted mutation tests faster and fail safely by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1854
- Use plural wording for multiple ticket links by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1855
- Allow percentage charges above 100% by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1856
- Keep cached pages fresh after database changes by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1859
- Keep changed settings fresh after database writes by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1860
- Add reusable screenshots for representative pages by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1857
- Share admin ordering and content management code by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1858
- Organize tests and strengthen covered boundaries by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1861
- Fix booking balances and mobile controls by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1863
- Improve checkout screenshot fixtures by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1868
- Normalize repeated form choices and table layouts by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1866
- Standardize nullable lookups and explicit required failures by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1867
- Reject invalid structured values in storage by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1865
- Split storage tests by concern by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1870
- Move admin settings page test to settings-advanced/page.test.ts by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1871
- Move eight integration tests to test/integration/ by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1873
- Centralize booking payment and failure rules by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1874
- Switch to 8-core runner by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1876
- Move admin debug tests and add a template rendering test by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1875
- Test existing authentication and site assignment contracts by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1877
- Replace Stripe SDK with a smaller edge client by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1864
- Run scheduled maintenance securely on every site by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1862
- Keep settings tests in the right place by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1878
- Finish scheduled maintenance safety checks by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1880
- Forbid ../ in imports — use a # alias instead by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1872
- Retry fleeting database gateway errors on reads only by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1869
- Count Stripe calls and finish activity backfill once by @stefan-burke in https://github.com/chobbledotcom/tickets/pull/1881
Full Changelog: v2026-06-12-135909...v2026-07-22-105033