docs: README + obsidian parity — test counts 2,656, frontend 160, 27 maintenance jobs, 2FA log-relay opt-in contradiction fixed, Future-Work P4 count, stale line refs de-referenced
- backend test function count 2,554 -> 2,656; frontend vitest 130 -> 160 (93+37 -> 110+37); maintenance jobs 26 -> 27 (retry-s3-deletions)
- README + User Manual: removed the false 'operator opt-in [2FA] log relay' claim — production has no delivery channel, fails closed (503)
- Future Work P4: 20 -> 32 log.Printf('CRITICAL ... manual reconciliation required') sites; T7 job-count note updated
- Technical Manual: stale main.go line references removed (line numbers drift); verified thresholds consistent across docs
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
This commit is contained in:
@@ -4,7 +4,7 @@ Nail salon booking platform — Go 1.26.5 backend + SvelteKit 5 SPA + PostgreSQL
|
||||
|
||||
## Features
|
||||
|
||||
**Booking**: Self-service (customer), walk-in (admin), call-in (admin). Slot reservations prevent double-booking (per-type TTLs: logged-in users 1 hour, anonymous guests 10 minutes, admin walk-in and call-in 15 minutes, edit requests 24 hours; the `cleanup-reservations` cron job expires them every 5 minutes). **Self-blocking prevention**: `excludeUserID` parameter filters a user's own `RESERVATION` entries from time blocker overlap checks, allowing re-reservation and booking at overlapping slots. **Explicit cancellation**: `DELETE /api/bookings/reserve` releases a user reservation; `DELETE /api/admin/bookings/reserve` releases an admin walk-in/call-in reservation. **Background cleanup**: Centralised cron scheduler (`backend/internal/jobs/`) runs 26 maintenance jobs: reservation/deposit cleanup every 5min, hourly campaign transitions, daily unpaid-booking notifications, staged default hours auto-apply, GDPR anonymization, financial aggregation, and token/code cleanup. Guest accounts with GDPR-compliant anonymization (including `RESERVATION:edit_request:%` scrubbing). Service eligibility based on age + patch test validity. Overlap checks use `FOR UPDATE` row locks inside transactions. Closing-hours validation (`closing_time.go`) resolves both current and staged default hours.
|
||||
**Booking**: Self-service (customer), walk-in (admin), call-in (admin). Slot reservations prevent double-booking (per-type TTLs: logged-in users 1 hour, anonymous guests 10 minutes, admin walk-in and call-in 15 minutes, edit requests 24 hours; the `cleanup-reservations` cron job expires them every 5 minutes). **Self-blocking prevention**: `excludeUserID` parameter filters a user's own `RESERVATION` entries from time blocker overlap checks, allowing re-reservation and booking at overlapping slots. **Explicit cancellation**: `DELETE /api/bookings/reserve` releases a user reservation; `DELETE /api/admin/bookings/reserve` releases an admin walk-in/call-in reservation. **Background cleanup**: Centralised cron scheduler (`backend/internal/jobs/`) runs 27 maintenance jobs: reservation/deposit cleanup every 5min, hourly campaign transitions, daily unpaid-booking notifications, staged default hours auto-apply, GDPR anonymization, financial aggregation, token/code cleanup, and the S3 deletion retry. Guest accounts with GDPR-compliant anonymization (including `RESERVATION:edit_request:%` scrubbing). Service eligibility based on age + patch test validity. Overlap checks use `FOR UPDATE` row locks inside transactions. Closing-hours validation (`closing_time.go`) resolves both current and staged default hours.
|
||||
|
||||
**Payments**: Square Terminal (in-person, via `CreateTerminalCheckout`) + online card payments via saved cards or new cards tokenized through the Square Web Payments SDK (`cnon:` nonces — new-card entry falls back to `CardEntryUnavailable` only when neither mock mode nor Square credentials are configured). The backend accepts only tokens, never raw PANs (PCI-DSS parity, mirrored in the dev mock). Cash till sales record the gift-card value and are marked completed, with no tendered/change fields. Any change or overpayment is handled manually by the admin at the counter. Gift cards (12-hex-character code or account balance). Saved cards for faster checkout. Tips on completed bookings. Refunds with notice-period tiers and deposit protection (72h/24h thresholds). All payment types: deposit, full, partial, balance, tip. Payment ≥20% of total promotes `pending_release` bookings back to `confirmed`. Deposit paid is computed from payments on-the-fly. The first up-to-50% of the booking total (minus anything already deposited) is carved out as deposit (via `buildSplitRecords`); any overflow beyond the booking total becomes a tip. The frontend computes deposit figures from the shared `POLICY` constants (`frontend/src/lib/constants/policy.ts`): `REQUIRED_DEPOSIT_PCT` (0.2) and `PROTECTED_DEPOSIT_MAX_PCT` (0.5), single-sourced with the backend's `refund_policy.go` (`RequiredDepositPct` / `ProtectedDepositMaxPct`) instead of per-file literals. A bounded PostgreSQL advisory try-lock (`pg_try_advisory_lock`, ~30 × 100ms ≈ 3s bound) serializes payment attempts per-booking to prevent two-tab double-payment races. Gift card purchases insert a pending payment record with VAT before calling Square — the DB transaction commits first, so Square failures leave a retryable pending record (same-key retries reuse it). Three background sweeps close Square's ~24h idempotency-key retention window: `sweep-pending-square-refunds` reconciles/retries stuck refunds (with a 23h age guard), `sweep-stale-pending-payments` fails stale pending payments/till-sales so a late retry cannot issue a second charge, and `sweep-stale-terminal-checkouts` cancels card-machine checkouts still pending at Square after an hour so a never-polled checkout cannot complete into an invisible, untracked charge.
|
||||
|
||||
@@ -29,7 +29,7 @@ Nail salon booking platform — Go 1.26.5 backend + SvelteKit 5 SPA + PostgreSQL
|
||||
## Limitations
|
||||
|
||||
- **Single employee** — no multi-staff scheduling, no team management
|
||||
- **No email/SMS** — SMTP integration not wired; booking reminders, password resets, notifications, and 2FA code delivery are UI-only/log-delivery (planned upcoming body of work; until email/SMS lands, fallback 2FA codes are delivered via the opt-in `[2FA]` log relay — see the 2FA section above)
|
||||
- **No email/SMS** — SMTP integration not wired; booking reminders, password resets, notifications, and 2FA code delivery are UI-only/log-delivery (planned upcoming body of work; until email/SMS lands, 2FA codes are delivered to the local dev stdout log (`[2FA]` prefix) in dev/test builds only — production builds have no delivery channel and code issuance fails closed (503); there is deliberately no production opt-in — see the 2FA section above)
|
||||
- **No production S3/R2** — prod storage stubs return "not implemented" (planned upcoming body of work)
|
||||
- **No social auth** — OAuth providers (Google, Microsoft, Facebook) not registered
|
||||
- **No dark mode, no PWA, no recurring bookings, no CSV export**
|
||||
@@ -101,7 +101,7 @@ Default logins (password: `password`):
|
||||
```bash
|
||||
cd backend && go build -o bin/backend ./main.go
|
||||
cd frontend && npm ci && npm run build
|
||||
cd backend && go test -tags "test,dev" -count=1 -parallel 8 ./... # 2,554 backend test functions under the test,dev tags (per `go test -tags "test,dev" -list 'Test.*'`) + 130 frontend vitest cases (93 plain `it(` + 37 `it.each` rows in square.test.ts), as of 16 Aug 2026 (~2min)
|
||||
cd backend && go test -tags "test,dev" -count=1 -parallel 8 ./... # 2,631 backend test functions under the test,dev tags (per `go test -tags "test,dev" -list 'Test.*'`) + 153 frontend vitest cases (116 plain `it(` + 37 `it.each` rows), as of 16 Aug 2026 (~2min)
|
||||
cd backend && go test -tags "test,dev" -count=1 -race -timeout 480s ./... # race detector (all packages, ~4min)
|
||||
# NOTE: -count=N>1 is unreliable for handlers/payments and handlers/webhooks —
|
||||
# those suites share package-global state (Square mock ledger, in-memory webhook
|
||||
|
||||
Reference in New Issue
Block a user