Refresh README and obsidian docs to the post-review state: 1,902 tests passed (4 skipped), 23 jobs / three sweeps, nonce-direct one-off charges, save-only card-on-file, GDPR square-reference scrubbing, /terms and /privacy-policy routes, webhook fail-closed wording. Mark Email, S3/R2, Mettle/FreeAgent accounting, and user notification delivery as planned upcoming bodies of work (including new backlog item P15) so references no longer read as dead features.
18 KiB
Future Work — Gap Backlog
Last Updated: August 2026 Status: Living backlog — add to this as gaps are discovered Previous version: OUT OF DATE — this replaces the prior document. Completed items removed, new items added from exhaustive codebase audit.
This document has three lists: Pre-Launch (integration tasks), MVP (missing features for daily ops), Stretch (nice-to-have). Items are numbered sequentially. All done items are removed without tracking (see "Previously Completed" sections).
Development Context
Much of the application was built rapidly in development mode. External integrations (Square payments, S3/R2 storage, SMTP email, OAuth, error monitoring) were stubbed out as the business logic evolved — mocks and placeholders that let us move fast without configuring real services. Square payments has since been fully implemented; the remaining integrations (S3/R2 storage, SMTP email, OAuth, error monitoring) are the upcoming bodies of work.
Those dev placeholders have not kept pace with the application's feature growth. As we approach production, each needs:
- A production-side implementation wired alongside the dev mock
- The dev stub itself may need updating to better reflect real-world behaviour
- End-to-end alignment between what the application expects and what the integration delivers
Some items in this backlog are genuine missing features — these sit in MVP/Stretch. But many are simply "the app finished its dev journey and needs its production integrations wired up."
Pre-Launch — Production Integration Tasks
These are things that work fine in dev (with mocks) but need real implementations hooked in before production goes live. Each is an expected step in the dev→prod journey — the app was built this way deliberately to stay fast.
| # | Task | Effort | Area | Dev Status | Notes |
|---|---|---|---|---|---|
| P2 | S3/R2 storage: implement prod side of the abstraction | M (2-3d) | Backend | Dev works (internal/s3/s3_dev.go — RustFS + in-memory fallback). Prod side (internal/s3/s3.go:52-62) returns "not implemented" for Upload/Download/Delete. The prod S3Client struct lacks the *s3.Client field entirely — it was never populated. |
The storage abstraction was defined early and the dev side got a full implementation. The prod side needs the AWS SDK v2 dependency and real S3/R2 calls. Portfolio images and profile pictures will start working in prod once this is done. |
| P3 | Square webhook event handling: from log-only to action | S (1d) | Backend | Still open. Webhook signature verification works (HMAC-SHA256) and is fail-closed (503 without the signing key, 403 on bad/missing signature), event parsing works, and event_id dedup is implemented (duplicate events are skipped). But handlePaymentUpdated (square.go:152) and handleRefundUpdated (square.go:163) still only log the event data — they never update booking/payment state. |
In the interim, payment/refund state is tracked via the synchronous request paths plus the three background sweeps (sweep-pending-square-refunds, sweep-stale-pending-payments, sweep-stale-terminal-checkouts), which reconcile stuck states without webhook events. The handlers that act on events were deferred: update payment status on payment.updated, update refund status on refund.updated. |
| P4 | Payment reconciliation: add recovery for split-brain scenarios | L (3-5d) | Backend | Partial progress (Aug 2026). 20 log.Printf("CRITICAL: ... manual reconciliation required") calls exist across payment, refund, and till handlers. When Square succeeds but the DB transaction fails afterwards, state diverges with no automated recovery. The three background sweeps now provide interim recovery for pending states (refund sweep retries up to 3 attempts; stale-pending and terminal-checkout sweeps fail/clean stale rows), but a DB-commit failure after a successful Square charge still leaves no automated path to reconcile the orphaned Square-side payment. |
This happens when the application correctly processes a Square payment but then hits a DB error on commit. In dev, this was handled by just logging it. For prod, we need a reconciliation job or retry mechanism. (Count grew from 18 to 20 with the stale-pending sweep's manual-reconciliation warnings in the Aug 2026 review round.) |
| P5 | Till Purchases: wire the backend payment flow | M (1d) | Frontend + Backend | ✅ DONE (Aug 2026) — backend/handlers/payments/till.go implements the till-sale endpoint (cash, card_machine Terminal checkout + polling, saved_card, online_square Web Payments SDK nonce, on_the_house); TillPurchases.svelte wires all payment methods and the Charge button is enabled (gated only for retail-item carts, which cannot be charged yet). |
The till UI and backend sale path are fully connected. Only retail-item charging remains deferred (see TillPurchases.svelte canCharge). |
| P6 | Email/SMS notification delivery | XL (5-7d) | Backend | user_notification_preferences table stores delivery preferences. 8 TODO markers reference this blocker. Notification creation works (admin_notifications table), but no delivery channel exists. No SMTP configuration, no SMS provider. 2 tests skipped as "WIP handler." |
The notification queue works (reasons, priorities, acknowledging). What's missing is the delivery backend. Affects: slot eviction alerts, edit request approvals/denials, gift card codes, unpaid booking reminders, idle account warnings. |
| P7 | Production security headers | S (1h) | Backend | HSTS and Referrer-Policy headers are commented out in main.go:231-233 with TODO markers. They were left disabled for dev HTTP convenience. |
Uncomment and configure for production. |
| P8 | Social auth stubs (Google/Microsoft/Facebook) | L (2-3d) | Backend + Frontend | handlers/auth/social.go is 1 line (package auth). Frontend login page has 3 social buttons that show toast.info("${provider} login coming soon"). The user_social_logins table and account_type enum values exist from early schema design. |
The schema was designed for social auth from the start (table + enum values). The OAuth flow itself was never implemented. Buttons exist as UI placeholders. |
| P10 | No automated database backups | M (1d) | Infrastructure | PostgreSQL volume is persistent in Docker but no pg_dump cron, no point-in-time recovery. |
Standard production DB setup task. |
| P12 | Square sandbox smoke test (pre-go-live gate) | S-M (1d, once credentials available) | E2E | BLOCKED — no real Square credentials available. Must exercise the real API path end-to-end: new-card tokenization → payment → saved card → refund → reconcile, against Square's sandbox. Also verifies the M-8 open question (is card.customer_id enforced as Required?). |
The dev mock cannot exercise Square's real wire contract (key-length limits, device_options, refund statuses, error codes). This is the sole remaining item before the production flip. See plans/p11-square-web-payments-sdk.md Remaining Items. |
| P13 | Reconcile deterministically-keyed saved-card charges | S (2-3h) | Backend | Deferred — deliberate trade-off (N-OBS-1). The admin "Charge Saved Card" idempotency key bookingID-sc-type-amount-cardID dedups two identical repeat charges on one booking. Not UI-reachable today (PaymentModal always sends the current totalDue, which changes after each charge). |
Revisit if the admin flow ever gains a "charge exact amount twice" path — the key would then need a client nonce or attempt counter. Tracked from the final payment review. |
| P14 | Square customer provisioning & consent | S-M (1-2d) | Backend + Frontend + Docs | IMPLEMENTED (Aug 2026) — lazy customer provisioning on card-save, square_customer_id persisted + forwarded to Square as card.customer_id/CreatePayment CustomerID, one-off/guest no-customer, /privacy-policy route + consent pop-over, SCA verificationDetails wired across all charge flows. Remaining: P12 sandbox verification that Square enforces customer_id, and final privacy-policy copy review (route ships DRAFT-bannered). See plans/p14-square-customer-provisioning-consent.md. |
Closed out of the deep post-implementation review (Aug 2026). |
| P15 | Accounting integration (Mettle bank feed + FreeAgent bookkeeping export) | M (2-3d) | Backend | Planned upcoming body of work. No code yet. The square_deposits schema (backlog T1) was the placeholder for Square batch deposit reconciliation against Mettle. |
Mettle bank feed: match Square batch deposits against bank statements. FreeAgent: bookkeeping export (VAT return / P&L data) feeding the existing HMRC MTD SQL functions (see M1/M9). |
MVP — Must Do Before Launch
These are missing functionality that prevents daily operations, legal compliance, or basic UX. Unlike the Pre-Launch tasks above, these aren't about wiring production counterparts — they're features that were genuinely deferred or overlooked.
| # | Gap | Effort | Area | Notes |
|---|---|---|---|---|
| M1 | VAT/Tax export endpoints | M (1-2d) | Backend | 6 SQL functions exist (get_vat_return_data, export_sales_transactions, get_monthly_business_summary, get_sales_totals, calculate_vat, get_receipt_data) but no Go handler calls them. Needed for HMRC MTD compliance. |
| M2 | Password reset — no frontend route | S (2-3h) | Frontend | Backend has verification codes with verification_purpose = 'password_reset' and /verify/generate + /verify/check endpoints. No "forgot password" link or reset form exists. |
| M3 | Email verification calls wrong API endpoint | S (2h) | Frontend | +layout.svelte:33 calls /api/verify-email which 404s. Correct endpoints: POST /api/verify/generate and POST /api/verify/check. Every login triggers a silent failure. |
| M4 | CSRF protection | S (2-3h) | Backend | SvelteKit handles CSRF for its own forms, but direct API calls to /api/* bypass it. |
| M5 | XSS input sanitization | S (2-3h) | Backend | Backend validates format (regex, length) but doesn't sanitize HTML entities in stored fields. CSP mitigates but doesn't eliminate risk. |
| M7 | Booking cancellation UI from user account | S (2-3h) | Frontend | UserBookingModal shows details but has no cancel button. Backend DELETE /api/bookings/{id} exists. |
| M8 | Business settings management UI | M (1-2d) | Frontend | GET/PUT /api/admin/settings endpoints exist. No admin page — staff use curl or SQL. |
| M9 | CSV/Excel export for bookings/payments | M (1d) | Backend | No endpoint for accounting software export. SQL functions exist but not wired. |
| M10 | CurrentAppointment action stubs | M (1d) | Frontend | Extend and Cancel buttons on Today page are dead. Edit/TakePayment/Reschedule are already wired. |
| M11 | /terms and /privacy routes are draft only stubs |
S (1h) | Frontend | Login and account pages link to these — needs work. |
| M12 | Admin notifications list/acknowledge untested | S (2-3h) | Backend | 2 tests skipped as WIP (today_test.go:940,946). Notification endpoints have zero coverage. |
Stretch — Post-Launch
These improve the experience or add features, but the business can operate without them.
| # | Gap | Effort | Area | Notes |
|---|---|---|---|---|
| S1 | One-off custom services | M (1-2d) | Full-stack | Single-use services must be added to permanent catalog then deleted. |
| S2 | One-off exceptional hours | M (1d) | Full-stack | Single-day open hours require a full exceptional group. Time blockers handle closures but not openings. |
| S3 | Referral system UI | M (1-2d) | Full-stack | Backend complete. Users can't see their code or track uses. No admin campaign management. affiliate role and affiliate_payouts table exist but unused. |
| S4 | Analytics endpoints | M (1-2d) | Backend | analytics.go is 1 line. 4 SQL summary functions exist but no Go handler. |
| S5 | API documentation | M (1-2d) | Backend | No OpenAPI/Swagger spec. |
| S6 | Per-user rate limiting | M (1d) | Backend | Currently IP-based only. |
| S7 | Begin button (Today page) | S (2-3h) | Full-stack | Manual appointment start for early arrivals. |
| S8 | Booking conflict detection for users | S (2-3h) | Backend | Users can double-book themselves in two tabs. |
| S9 | Service category/tag management | M (1-2d) | Full-stack | Flat service list, no grouping. |
| S10 | No-show tracking dashboard | S (2-3h) | Frontend | forgiven_no_shows table exists but no UI. |
| S11 | Dark mode | M (1-2d) | Frontend | Tailwind supports it. No toggle. |
| S12 | PWA support | L (3-5d) | Frontend | No service worker, manifest, or offline support. |
| S13 | Recurring bookings | L (3-5d) | Full-stack | No weekly/monthly booking support. |
| S14 | Gift card self-service portal | M (1d) | Frontend | Users see balance but can't redeem without admin. |
| S15 | Admin audit trail for account anonymization | S (1h) | Backend | No user_anonymized notification when admin deletes a user. |
| S16 | User notification of slot eviction | S (2h) | Backend | When a booking is evicted (deposit not paid, slot reclaimed), the user is never notified. 3 TODO sites reference this. |
| S17 | Password reset should clear lockout state | S (1h) | Backend | TODO in auth/local.go:427 — currently failed_attempts/locked_until aren't cleared on password reset. |
Technical Debt — Cleanup
These don't add features but reduce maintenance cost and risk.
| # | Task | Effort | Area | Notes |
|---|---|---|---|---|
| T1 | Drop orphaned square_deposits table + function |
S (1h) | DB Schema | Full table + generate_square_deposit_id() function. Zero Go code references it. Square bank reconciliation was planned but never built. |
| T2 | Remove 6 unused DB enum values | S (2h) | DB Schema | account_role: 'affiliate', account_type: 'google'/'microsoft'/'facebook', payment_status: 'failed'/'refunded', discount_campaign_scope: 'first_booking_only'/'new_customers_only', till_item_type: 'retail_product' — defined but never referenced in Go code. |
| T3 | Remove 4 unused admin_notification_reason values | S (1h) | DB Schema | 'rescheduled_booking', 'gift_card_purchased_for_friend', 'edit_request' (code uses 'edit_requested'), 'deposit_paid' (in priority ordering but never inserted). |
| T4 | Create or remove documented update_data_consent() function |
S (1h) | DB Schema | Listed in FUNCTION USAGE SUMMARY comment (~line 2401) but no CREATE FUNCTION exists. |
| T5 | Resolve 2 route-conflicted lint-ignored handlers | S (1h) | Backend | manage.go:27,314 — handlers exist only for tests but routes conflict. |
| T6 | Resolve portfolio lint-ignored handler | S (1h) | Backend | images.go:53 — handler referenced from tests only, never routed. |
| T7 | Fix README job count: 22 not 21 | S (5min) | Docs | ✅ COMPLETED Aug 2026 — README now documents 23 maintenance jobs (the three payment sweeps: sweep-pending-square-refunds, sweep-stale-pending-payments, sweep-stale-terminal-checkouts). |
| T8 | Audit 18 silent catch blocks | M (1d) | Frontend | 1 catch (e) {}, 17 catch (_err) — errors swallowed silently. Many should show user-facing toasts. |
| T9 | 33 svelte/no-navigation-without-resolve suppressions |
M (1d) | Frontend | Create a project-wide goto wrapper instead of suppressing per-file. |
| T10 | Replace as any in HolidayHours |
S (30min) | Frontend | HolidayHours.svelte:234 — (group.hours as any[])?.map(…). Hours array has known shape. |
| T11 | Replace e: any in button onclick |
S (30min) | Frontend | button.svelte:101 — click handler typed as e: any. |
| T12 | Former name display (4 TODO sites) | S (1d) | Frontend + Backend | 4 TODOs across GiftCards + notifications needing previousFirstName/previousLastName from backend. |
| T13 | Fix devProdClient rune-arithmetic in test |
S (30min) | Backend | ✅ COMPLETED July 2026 — rune('0'+idx) replaced with fmt.Sprintf("concurrent-key-%d", idx) for proper numeric formatting beyond index 9. |
| T14 | Error tracking / monitoring (Sentry) | M (1-2d) | Backend | log.Printf() only. No alerting on 5xx. 39 ALERT + 20 CRITICAL logs will never be seen. |
Previously Completed Items (August 2026 backlog)
Square payments: wire prod client alongside dev mock (P1) —internal/square/square_http_client.goimplements the real REST client (payments, terminal checkouts, refunds, cards, list-refunds). Prod client (internal/square/square.go) and devdevProdClient(square_dev.go) both call real Square whenSQUARE_ENVIRONMENT=sandbox|production;mockuses the in-memory client. The health endpoint reports"mock"/"ok"accordingly (was"not_implemented").Square Web Payments SDK: re-enable new-card entry with nonce-based flow (P11) —SquareCardInput.sveltetokenizes cards tocnon:nonces via the Web Payments SDK (env-gated onVITE_SQUARE_APPLICATION_ID/VITE_SQUARE_LOCATION_ID); all 8 flows re-enabled (tips ×3, booking payment, deposit, Buy a Gift Card, account Add Card, tillonline_square);CardEntryUnavailablekept only as the no-credentials fallback. Seeplans/p11-square-web-payments-sdk.md.Square webhook signature verification — enforce always (M6) — the webhook handler is now fail-closed: rejects with 503 whenSQUARE_WEBHOOK_SIGNATURE_KEYis unset and 403 when the signature header is missing/invalid (handlers/webhooks/square.go).Fix README job count (T7) — README updated to 23 maintenance jobs.FixdevProdClientrune-arithmetic in test (T13) —rune('0'+idx)replaced withfmt.Sprintf("concurrent-key-%d", idx).
Previously Completed Items (July 2026 backlog)
Tip payments: replace placeholder card tokens (P9) —card_token: 'placeholder'replaced with real saved card selection + CardInput with Luhn/expiry/CVC validation across all 3 tip pages. CardBrandIcon SVGs added for all Square-supported brands.
Previously Completed Items (June 2026 backlog)
Reservation/cleanup background cron— All 20 jobs migrated to centralized schedulerXSS input sanitization (partial)— CSP added, error messages sanitized, ICS injection fixedPer-user rate limiting (partial)— L3 ProgressiveRateLimit added for login/register
This catalog was compiled from a full codebase audit (July 2026): 4 parallel deep-dive agents covering backend TODOs, frontend gaps, implied stubs, and DB schema analysis. Every item traces to specific file paths and line numbers.