SPB Git

Repository Public

PLAN.md 7.1 KB

# Plan and validation record

State on 2026-11-11, commit deployed on M4M64c. Each phase was closed only after its checks ran.

# Phases

Phase Content Validation performed
1. Audit and design Environment audit (ARCHITECTURE.md), schema, decisions (DECISIONS.md) DNS, manifests, sibling repos, runtimes inspected
2. Foundations Schema + migration runner, passkeys, sessions, CSRF, settings, bootstrap Migration applied on 4 databases; passkey enrolment, login, logout, recovery code in e2e
3. Availability Engine, weekly hours, exceptions, private events, agenda 25 engine tests (DST spring/autumn, midnight, buffers, limits, zones)
4. Bookings Public pages, types, requests, approve / decline / propose, double-booking protection 32 service tests incl. concurrency; e2e acceptance scenarios
5. Communications Outbox, Resend + SMTP transports, FR/EN templates, ICS, cancel / reschedule, reminders Retries / dead / unconfigured tested; e2e delivers through real SMTP to a sink and checks ICS
6. Integrations ICS subscription (read-only), outgoing feed, stale policy 9 tests (recurrence across DST, all-day, 304, failures, SSRF); e2e failure reporting
7. Finishing Mobile, accessibility, security headers, backup / restore, docs, HTTPS deployment axe on every screen (desktop + mobile), backup → restore compared, production walkthrough

# Results of the checks (2026-11-11)

  • tsc --noEmit: clean. eslint: clean.
  • Vitest: 66 / 66 (real PostgreSQL 17).
  • Playwright: 28 / 28 (14 scenarios × desktop Chrome and Pixel 7), production build.
  • Backup → restore into an empty database: same row counts, exclusion constraint present; wrong passphrase refused.
  • Production (https://calendar.spboucher.ai): health 200, TLS valid, security headers present, admin redirects to sign-in, admin API 401 without session; one real request submitted and withdrawn through the visitor link; web and worker restarted with PM2, data intact, health back in seconds; first encrypted backup written.

# Acceptance tests of the brief

# Test Where it is proven
1 Monday 9–12, block 10–11 → only valid slots tests/engine.test.ts, tests/booking.test.ts, e2e/admin.spec.ts (through the API and the public slots)
2 Confirmed appointment → slot disappears at once tests/booking.test.ts, e2e/admin.spec.ts
3 Two visitors, same slot → policy applied, one approval tests/booking.test.ts (hold and no-hold, 6 concurrent approvals, 8 concurrent auto-confirmations, raw SQL), e2e/admin.spec.ts (two simultaneous approvals: 200 + 409)
4 Request is pending for the owner, never "confirmed" for the visitor e2e/public.spec.ts
5 Approval: atomic re-check, real booking, notification, ICS tests/booking.test.ts, e2e/admin.spec.ts
6 Decline / propose another time tests/booking.test.ts, e2e/admin.spec.ts
7 DST, several zones, limits, midnight tests/engine.test.ts, tests/external.test.ts, e2e/public.spec.ts (Paris visitor)
8 Cancel / reschedule, expired / revoked links tests/booking.test.ts, e2e/admin.spec.ts
9 External calendar unavailable / stale, e-mail failure and retries tests/booking.test.ts, tests/external.test.ts, e2e/admin.spec.ts
10 Double booking, IDOR, CSRF, spam, race, errors, restart tests above + e2e/*.spec.ts (401 / 403 / 404, honeypot, forged token, cross-origin) + restart on production
11 Passkey flow and recovery e2e/admin.spec.ts with Chromium's virtual authenticator (user verification on). Touch ID / Face ID on real hardware: not verified — to be done by the owner at first sign-in.
12 Responsive and accessibility with Playwright every public and admin screen: axe WCAG 2.1 A/AA, no horizontal overflow, desktop + mobile

# Status — functional / not configured / blocked / not implemented

Area Status Note
Public site: home, booking flow, time-zone conversion, FR/EN, light/dark Functional
Requests subject to approval; automatic confirmation per type Functional
Visitor link: follow, withdraw, cancel, reschedule, accept a proposal, download ICS Functional
Availability engine, weekly hours (default and per type), exceptions, vacations, recurring absences Functional
Double-booking protection (lock + re-check + exclusion constraint), idempotency Functional
Admin: dashboard, inbox, agenda (day / week / month / list), private events, drag-to-move (mouse) Functional Drag is disabled on touch screens (use "Déplacer").
Admin: appointment types, contacts (history, export, erasure), settings, audit, sessions Functional
Passkeys (WebAuthn), recovery codes, shell recovery, revocable sessions, CSRF Functional Real-device biometrics to be confirmed by the owner.
ICS subscription of an external calendar (read-only free/busy), stale policy Functional No calendar connected yet.
Private ICS feed of confirmed appointments Functional Off until enabled in Settings → Integrations.
Encrypted daily backup, restore script Functional On the node only: no off-node copy yet.
Health endpoint, worker heartbeat, dashboard alerts Functional No external alerting (e-mail / push) when the site is down.
Transactional e-mail (acknowledgement, confirmation + ICS, decline, proposal, cancellation, reschedule, reminder, owner alerts) Not configured Code and both transports are tested; production has MAIL_TRANSPORT=none, so notifications are parked and shown as such. Needs a Resend key or SMTP credentials (OPERATIONS.md → E-mail).
Video link Not configured Each type's "location detail" is empty: type your meeting link / phone number in Types.
Public profile (headline, bio, photo), Reply-to address Not configured Defaults: name only.
Google Calendar / Microsoft 365 (OAuth), CalDAV, writing events to an external calendar Not implemented No OAuth client credentials were available; schema ready (D-10).
Automatic creation of video meetings Not implemented Manual link only (D-12).
Embeddable booking widget Not implemented Planned; today frame-ancestors 'none' forbids embedding.
Offline mode / service worker, push notifications Not implemented Installable (web manifest, icons) but online only.
Nonce-based CSP, CAPTCHA, several owners Not implemented See SECURITY.md (known limits).
Reminder e-mails Functional, partly tested Scheduling, cancellation and obsolescence are tested; a reminder actually sent hours later was not observed end to end.
Nothing Blocked

# Next steps (not started)

  1. Configure the e-mail transport; send the test e-mail; re-queue parked notifications.
  2. Owner: enrol the passkey on a real device, add a second one, fill the public profile and the meeting link.
  3. Off-node copy of the encrypted backups (rsync to another node).
  4. Google / Microsoft connectors once OAuth clients exist; then optional write-back of confirmed appointments.
  5. Embeddable widget (frame-ancestors allow-list + compact layout).
Files main
Menu

Theme