| D-01 |
2026-11-11 |
Single Next.js app + tsx worker, postgres driver, hand-written SQL migrations. |
Same shape as clip-spboucher-ai, which mld already deploys; exclusion constraints and advisory locks are plain SQL. |
Prisma / Drizzle: no gain for constraints written in SQL anyway. |
| D-02 |
2026-11-11 |
Availability computed with the native Temporal API on the server only. |
Exact DST arithmetic without a dependency (Node 26). The browser formats with Intl. |
Polyfill in the browser: availability is never computed client-side. |
| D-03 |
2026-11-11 |
PostgreSQL as the job queue (transactional outbox); no Redis. |
A notification exists if and only if its change committed; one less service to run, back up and monitor. The brief's Redis was "to confirm after audit". |
BullMQ + Redis: dual-write problem, extra runtime. Revisit if volume ever needs it. |
| D-04 |
2026-11-11 |
Three layers against double booking: owner advisory lock, re-check in the lock, exclusion constraint on raw [start, end). |
Buffers are a preference enforced by the engine; the hard guarantee is "no two confirmed bookings overlap". |
Constraint on buffered ranges: would forbid legitimate adjacency when buffers differ. |
| D-05 |
2026-11-11 |
Pending requests: per-type policy hold (TTL, default 48 h, never past the slot) or none. An expired hold stops blocking; the request stays pending until its time. |
Avoids indefinite blocking; the owner can still approve late if the time is free. |
Auto-declining at hold expiry: loses requests the owner meant to answer. |
| D-06 |
2026-11-11 |
"Propose another time" uses status reschedule_requested, holds the proposed slot up to 72 h, and needs the visitor's acceptance (re-checked). |
Never confirms something the visitor has not agreed to. |
Confirming the proposed time directly. |
| D-07 |
2026-11-11 |
Admin authentication is passkey-only. First enrolment and last-resort recovery use a one-time link printed on the server (scripts/setup-token.ts); eight one-use recovery codes are shown at first enrolment. |
No password to phish or leak; recovery requires either a stored code or shell access to the node. |
Password + TOTP fallback; e-mail magic links (the mailbox would become the weakest link). |
| D-08 |
2026-11-11 |
Visitor links: token = HMAC(server secret, token row id); the database stores only SHA-256(token). |
The raw token can be rebuilt when an e-mail is rendered, without ever being stored. |
Storing raw tokens; putting them in job payloads. |
| D-09 |
2026-11-11 |
Only declining is offered in bulk. |
Each approval creates an appointment and must be looked at; bulk approval is not "safe". |
Bulk approve. |
| D-10 |
2026-11-11 |
External calendars V1 = ICS subscription (read-only). Google / Microsoft OAuth not built. |
Works today with iCloud, Google and Outlook published addresses, with no credentials; OAuth cannot be validated without client credentials, and unvalidated code would be a simulation. |
Shipping untested OAuth connectors. |
| D-11 |
2026-11-11 |
Stale external calendar → automatic confirmations are withheld (become requests), configurable to ignore. |
The brief's conservative default. |
Blocking all public slots when a calendar is stale. |
| D-12 |
2026-11-11 |
Video link is the type's location_detail, typed by the owner, sent only once confirmed. |
No real meeting integration is configured. |
Generating meeting links. |
| D-13 |
2026-11-11 |
Static CSP with 'unsafe-inline' for scripts and styles, no third-party origin. |
Next's inline bootstrap needs a nonce on every render otherwise; same choice as CLIP. Residual risk documented in SECURITY.md. |
Nonce-based CSP (possible later: every page is already dynamic). |
| D-14 |
2026-11-11 |
Rate limits in PostgreSQL (fixed windows). |
Shared across processes, survives restarts, no extra service. |
In-memory counters. |
| D-15 |
2026-11-11 |
The reason and the answers never leave the database except in the admin area. |
Brief: nothing confidential in URLs, logs, e-mails or ICS. Admin alert e-mails say "see the admin area". |
Including the reason in the owner's alert e-mail. |
| D-16 |
2026-11-11 |
Visual refresh (« SP » family, phone first): indigo → violet accent on primary actions and selected states, floating glass tab bar, bottom sheets, large titles, deep-black dark theme. The theme follows the system until the owner or visitor picks one. Tokens and components live in src/app/globals.css; no UI dependency was added. |
The public page is opened on phones; the admin is used on an iPhone. White on the accent stays ≥ 5.6:1, axe runs in the e2e suite. |
A component library; a web font (system fonts are SF Pro on Apple devices). |
| D-17 |
2026-11-11 |
Admin navigation uses next/link and React <ViewTransition>; booking steps change inside startTransition. Entrance animations use animation-fill-mode: backwards. |
Tab changes no longer reload the page. A transform animation that keeps applying (both) makes its element the containing block of position: fixed sheets. |
Full page loads with cross-document view transitions. |