docs: add feature catalog and update gap backlog

Feature Catalog: comprehensive audit of all 15 feature areas with cross-references, verified against source code (18 parallel deep-dive agents). Gap Backlog: rewritten with dev-to-prod integration framing, 40 items across Pre-Launch/MVP/Stretch/Tech Debt. Gitleaks: whitelist obsidian/ docs containing curl examples.
This commit is contained in:
2026-08-22 00:34:49 +01:00
parent df4c9afeb9
commit 1f3d834f0c
3 changed files with 1096 additions and 56 deletions
+2
View File
@@ -18,4 +18,6 @@ paths = [
"sabredav/composer.json", "sabredav/composer.json",
# Frontend env examples # Frontend env examples
"frontend/.env.production", "frontend/.env.production",
# Obsidian docs — contain API curl examples with Authorization headers
"obsidian/",
] ]
File diff suppressed because it is too large Load Diff
+94 -56
View File
@@ -1,37 +1,61 @@
# Future Work — Gap Backlog # Future Work — Gap Backlog
**Last Updated:** June 2026 **Last Updated:** July 2026
**Status:** Living backlog — add to this as gaps are discovered **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 is two lists: **MVP** (must do before launch) and **Stretch** (nice-to-have after launch). Items are numbered sequentially. All done items are removed — not crossed out, not tracked. If you need to know what was done, check the git history. 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.
### 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.
Those dev placeholders have **not kept pace** with the application's feature growth. As we approach production, each needs:
1. A **production-side implementation** wired alongside the dev mock
2. The **dev stub itself** may need updating to better reflect real-world behaviour
3. **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 |
|---|---|---|---|---|---|
| P1 | **Square payments: wire prod client alongside dev mock** | XL (5-7d) | Backend | Mock exists (`internal/square/square_dev.go:MockClient`). Prod client (`internal/square/square.go`) returns "not yet configured" for all 8 methods. Dev `devProdClient` (`square_dev.go:38-61`) also returns stubs when `SQUARE_ENVIRONMENT=sandbox` or `production`. Only `SQUARE_ENVIRONMENT=mock` processes payments (in-memory). The health endpoint reports `"not_implemented"`. | The in-memory Square mock was great for development — it let us build the full payment flow, refund logic, split records, VAT calculation, and saved cards without touching Square's API. Now we need the production SDK wired beside it. The mock already has the interface; implement `ProdClient` with real SDK calls. |
| 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 | Webhook signature verification works (HMAC-SHA256, references Square docs). Event parsing works. But `handlePaymentUpdated` and `handleRefundUpdated` (`square.go:88-94`) only log the event data — they never update booking/payment state. | The webhook receiver was built first (parse + verify). The handlers that act on events were deferred. Now they need to: 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 | 11 `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. | 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. |
| P5 | **Till Purchases: wire the backend payment flow** | M (1d) | Frontend + Backend | Frontend (`TillPurchases.svelte:203-208`) has the UI built but the Charge button is disabled with "Payment flow and backend integration coming soon." The till sale submission path was deferred. | The till UI is fully designed — service selection, gift card types, payment method selection. Only the final "submit payment" path was left as a placeholder. Needs the backend `till.go` sale endpoint wired. |
| 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. |
| P9 | **Tip payments: replace placeholder card tokens** | S (1d) | Frontend | 3 tip endpoints send `card_token: 'placeholder'` — a literal string. (`UserBookingModal:190`, `tip/+page.svelte:141`, `pay-tip/[id]/+page.svelte:170`). The Square Web Payments token from the frontend card form was never wired. | The tip UI flow works (select amount, confirm). The actual card token from Square's payment form was never plumbed through. Needs to capture the real nonce/token and pass it to the backend. |
| 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. |
--- ---
## MVP — Must Do Before Launch ## MVP — Must Do Before Launch
These are blockers: missing functionality that prevents daily operations, legal compliance, or basic security. No external dependencies. Each is local, can be implemented today. 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 | | # | Gap | Effort | Area | Notes |
|---|---|---|---|---| |---|---|---|---|---|
| 1 | **CurrentAppointment action stubs** | M (1d) | Frontend | `Extend` and `Cancel` buttons on Today page are dead. Staff cannot cancel or extend an in-progress appointment from the Today page. Edit, Take Payment, and Reschedule are already wired. | | 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. |
| 2 | **~~Reservation/cleanup background cron~~** | S (3h) | Backend | **DONE**: All cleanup functions migrated to `backend/internal/jobs/` — centralised cron scheduler. 21 jobs registered with staggered schedules: cleanup-reservations (5min), cleanup-expired-deposits (5min), cleanup-rate-limiters (5min), cleanup-gdpr-export-cache (5min), cleanup-progressive-rate-limiter (1min), cleanup-expired-loyalty-redemptions (hourly), cleanup-old-idempotency-keys (hourly), cleanup-revoked-jtis (hourly), cleanup-stale-login-entries (hourly), transition-discount-campaigns (hourly), notify-unpaid-1-week (7am daily), notify-unpaid-1-month (7am daily), cleanup-verification-codes (2am daily), cleanup-refresh-tokens (2am daily), anonymize-stale-guest-accounts (3am daily), cleanup-idle-accounts (3:30am daily), cleanup-expired-financial-records (4am daily), cleanup-old-name-history (4:30am daily), cleanup-expired-gift-cards (5am daily), apply-default-hours (00:05 daily). | | 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. |
| 3 | **VAT/Tax export endpoints** | M (1-2d) | Backend | `get_vat_return_data()` and `export_sales_transactions()` SQL functions exist. No admin API to trigger them. Needed for HMRC Making Tax Digital compliance. | | 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. |
| 4 | **Password reset flow** | S (2-3h) | Frontend | Backend has `/api/verify/generate` and `/api/verify/check`. Login page has no "forgot password" link or form. Customers who forget their password must call the salon. | | M4 | **CSRF protection** | S (2-3h) | Backend | SvelteKit handles CSRF for its own forms, but direct API calls to `/api/*` bypass it. |
| 5 | **Email verification flow** | S (2-3h) | Frontend | Users register with `unverified_email` role. No UI to enter verification code or resend. `+layout.svelte` has an alert-based prototype that needs to be wired properly. | | 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. |
| 6 | **Booking cancellation from user account** | S (2-3h) | Frontend | `UserBookingModal` shows booking details but no cancel button. Users must call/email to cancel. Backend endpoint `DELETE /api/bookings/{id}` exists. | | M6 | **Square webhook signature verification — enforce always** | S (1-2h) | Backend | Currently skips if `SQUARE_WEBHOOK_SIGNATURE_KEY` is empty. Prod must always verify. Implementation exists (HMAC-SHA256), just needs enforcement. |
| 7 | **Business settings management UI** | M (1-2d) | Frontend | `GET/PUT /api/admin/settings` backend endpoints exist (VAT, business name, gift card expiry months, voucher type SPV/MPV). No admin page. Staff must use `curl` or direct SQL. | | 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. |
| 8 | **CSV/Excel export for bookings/payments** | M (1d) | Backend | Admin can't export data for accounting software. SQL functions exist but no endpoint to download CSV. | | M8 | **Business settings management UI** | M (1-2d) | Frontend | `GET/PUT /api/admin/settings` endpoints exist. No admin page — staff use curl or SQL. |
| 9 | **XSS input sanitization** | S (2-3h) | Backend | Backend validates format (regex, length) but doesn't sanitize HTML entities. Stored XSS risk in `notes`, `name`, `description` fields. **Partially addressed June 2026:** toast.error() messages now escape HTML. `{@html statusBadge()` replaced with proper component. ICS injection sanitized. Error messages no longer reflect user input. CSP header added. Remaining: backend-level HTML sanitization of stored fields. | | M9 | **CSV/Excel export for bookings/payments** | M (1d) | Backend | No endpoint for accounting software export. SQL functions exist but not wired. |
| 10 | **CSRF protection** | S (2-3h) | Backend | SvelteKit handles CSRF for its own forms, but direct API calls to `/api/*` bypass it. Consider double-submit cookie or SameSite cookies. | | M10 | **CurrentAppointment action stubs** | M (1d) | Frontend | Extend and Cancel buttons on Today page are dead. Edit/TakePayment/Reschedule are already wired. |
| 11 | **Automated database backups** | M (1d) | Infrastructure | No backup strategy. PostgreSQL volume is persistent but no automated dumps, no point-in-time recovery. Can use `pg_dump` cron on the host — no external service needed. | | M11 | **`/terms` and `/privacy` routes don't exist** | S (1h) | Frontend | Login and account pages link to these — both 404. |
| 12 | **Square webhook production verification** | S (2-3h) | Backend | Current handler skips signature check if `SQUARE_WEBHOOK_SIGNATURE_KEY` is empty. Production must ALWAYS verify. See `TODO(PROD)` in `handlers/webhooks/square.go` for detailed implementation guide including Go SDK usage, base64 HMAC-SHA256, and notification URL matching. | | 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. |
### External MVP (Requires Third-Party Access)
| # | Gap | Effort | Area | Blocked On | Notes |
|---|---|---|---|---|---|
| E5 | **Email/SMS notification system** | XL (5-7d) | Backend | SMTP provider (Resend, SendGrid, Twilio) | `user_notification_preferences` table exists but no delivery system. Blocks: booking reminders, password reset emails, deposit reduction notifications. |
| E8 | **S3/R2 production storage** | M (1d) | Backend | Cloudflare R2 or AWS S3 credentials | `s3.go` (`!dev` build tag) returns "not implemented". Prod builds cannot store portfolio images. Need AWS SDK v2 + credentials. |
--- ---
@@ -39,43 +63,57 @@ These are blockers: missing functionality that prevents daily operations, legal
These improve the experience or add features, but the business can operate without them. These improve the experience or add features, but the business can operate without them.
| # | Gap | Effort | Area | Notes | | # | Gap | Effort | Area | Notes |
| --- | ---------------------------------------- | -------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |---|---|---|---|---|
| 12 | **One-off custom services** | M (1-2d) | Full-stack | Admin can't create single-use services. Every custom job (bridal party, special request) must be added to the permanent catalog. | | S1 | **One-off custom services** | M (1-2d) | Full-stack | Single-use services must be added to permanent catalog then deleted. |
| 13 | **One-off exceptional hours** | M (1d) | Full-stack | Single-day overrides (dentist appointment, afternoon off) require creating a full exceptional group. Time blockers handle unavailable periods; one-off *open* hours (e.g., "open Sunday 2pm-5pm") still need simplification. | | 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. |
| 14 | **Referral system UI** | M (1-2d) | Full-stack | Backend complete — registration validates codes, relationships recorded. Users can't see their referral code or track uses. Admin can't manage referral campaigns. | | 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. |
| 15 | **Analytics endpoints** | M (1-2d) | Backend | `handlers/admin/analytics.go` is 1 line. `get_monthly_business_summary()`, `get_sales_totals()` SQL functions exist but not wired. | | S4 | **Analytics endpoints** | M (1-2d) | Backend | `analytics.go` is 1 line. 4 SQL summary functions exist but no Go handler. |
| 16 | **API documentation** | M (1-2d) | Backend | No OpenAPI/Swagger spec. No generated docs. New developers must read code. | | S5 | **API documentation** | M (1-2d) | Backend | No OpenAPI/Swagger spec. |
| 17 | **Per-user rate limiting** | M (1d) | Backend | Rate limiter is IP-based. Authenticated users could abuse from multiple IPs. Should track by user ID + IP. Pure Go — no Redis needed for single-instance. **Partially addressed June 2026:** L3 ProgressiveRateLimit added for login/register (per-IP dual-window: 30 req/5s burst + 120 req/60s sustained). Account lockout added (5+ failures → progressive 15min→2h). Remaining: user-ID tracking for authenticated endpoints. | | S6 | **Per-user rate limiting** | M (1d) | Backend | Currently IP-based only. |
| 18 | **Begin button (Today page)** | S (2-3h) | Full-stack | Manual start for early arrivals. Currently auto-inferred only. Gray out if >3 hours away. | | S7 | **Begin button (Today page)** | S (2-3h) | Full-stack | Manual appointment start for early arrivals. |
| 19 | **Booking conflict detection for users** | S (2-3h) | Backend | Users can theoretically double-book themselves in two tabs. Reservation system helps but doesn't fully prevent. | | S8 | **Booking conflict detection for users** | S (2-3h) | Backend | Users can double-book themselves in two tabs. |
| 20 | **Service category/tag management** | M (1-2d) | Full-stack | Services have no category field. Admin scrolls through a flat list. No way to group (manicure vs pedicure vs nail art). | | S9 | **Service category/tag management** | M (1-2d) | Full-stack | Flat service list, no grouping. |
| 21 | **No-show tracking dashboard** | S (2-3h) | Frontend | `forgiven_no_shows` table exists but no UI. Admin can't see which users have accumulated no-shows. | | S10 | **No-show tracking dashboard** | S (2-3h) | Frontend | `forgiven_no_shows` table exists but no UI. |
| 22 | **Dark mode** | M (1-2d) | Frontend | SvelteKit + Tailwind supports it. No toggle or `prefers-color-scheme` support. | | S11 | **Dark mode** | M (1-2d) | Frontend | Tailwind supports it. No toggle. |
| 23 | **PWA support** | L (3-5d) | Frontend | No service worker, no manifest.json, no offline support. Customers can't "install" the app. | | S12 | **PWA support** | L (3-5d) | Frontend | No service worker, manifest, or offline support. |
| 24 | **Recurring bookings** | L (3-5d) | Full-stack | Customers can't book the same slot weekly/monthly. Would need `recurring_bookings` table + background job. | | S13 | **Recurring bookings** | L (3-5d) | Full-stack | No weekly/monthly booking support. |
| 25 | **Idempotency key cleanup** | S (1h) | Backend | `idempotency_key` columns added to `bookings`, `payments`, `till_sales` with unique constraints. No retention policy — keys accumulate indefinitely. | | S14 | **Gift card self-service portal** | M (1d) | Frontend | Users see balance but can't redeem without admin. |
| 26 | **Gift card self-service portal** | M (1d) | Frontend | Users can see gift card balance on Account page but can't independently redeem to balance 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. |
### External Stretch (Requires Third-Party Access) | 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. |
| # | Gap | Effort | Area | Blocked On | Notes |
|---|---|---|---|---|---|
| E9 | **Social auth (Google/Microsoft/Facebook)** | L (2-3d) | Backend | OAuth app registrations + client secrets | `handlers/auth/social.go` is 1 line. |
| E10 | **Error tracking / monitoring** | M (1-2d) | Backend | Sentry DSN or equivalent | `log.Printf()` only. No alerting on 5xx. |
--- ---
## Dependency Map ## Technical Debt — Cleanup
``` These don't add features but reduce maintenance cost and risk.
E5 Email/SMS → E6 deposit reduction notifications
→ booking reminders
→ password reset emails
E8 S3/R2 → portfolio images in production | # | Task | Effort | Area | Notes |
E9 OAuth → social login flow |---|---|---|---|---|
E10 Sentry → error tracking, 5xx alerting | 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: 20 not 21** | S (5min) | Docs | README says "21 maintenance jobs", code registers 20. |
| 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 | `square_dev_test.go:410``rune('0'+idx)` breaks for indices >= 10 (`:` not a digit). |
| T14 | **Error tracking / monitoring (Sentry)** | M (1-2d) | Backend | `log.Printf()` only. No alerting on 5xx. 39 ALERT + 11 CRITICAL logs will never be seen. |
All local (MVP + Stretch) items have zero external dependencies. ---
## Previously Completed Items (June 2026 backlog)
- ~~Reservation/cleanup background cron~~ — All 20 jobs migrated to centralized scheduler
- ~~XSS input sanitization (partial)~~ — CSP added, error messages sanitized, ICS injection fixed
- ~~Per-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.*