Skip to content

Slice 6: Reservations (CRM guest link) - #11

Merged
DanMat merged 1 commit into
nimbus-rebuildfrom
slice/reservations
Sep 6, 2026
Merged

Slice 6: Reservations (CRM guest link)#11
DanMat merged 1 commit into
nimbus-rebuildfrom
slice/reservations

Conversation

@DanMat

@DanMat DanMat commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Lights up the CRM integration — reservations link to CRM guests — while honoring the cross-plugin PII boundary the security review pinned: the restaurant stores only a contact_id and links out to the CRM's own gated page; it never reads, resolves or copies CRM contact data.

What's here

  • Schemarest_reservation (003_reservations). party_name / party_size / reserved_at / notes are the restaurant's own first-party data; table_id is a validated same-plugin ref; contact_id is a bare link to a CRM contact.
  • Reservations service — field allow-list, party name required, allow-listed status (booked/seated/cancelled/no_show), strict datetime (accepts datetime-local), validated table ref. contact_id is stored as given and not resolved or existence-checked — reading the CRM would breach its capability gate.
  • Admin — the book (list + status filter + create/edit). Shows only the booking's own data; a linked booking renders a "Guest in CRM →" link to /admin/crm?edit=<id> (core gates that page), never CRM data itself. danmat.restaurant:floor + CSRF, mobile-reflow.
  • MCPreservations / _get / _set / _status / _delete; contact_id passes through as a bare link.
  • Guide — reservations section + the CRM boundary.

Security posture

The restaurant never reads CRM PII: it stores the guest's id and links to the CRM's own capability-gated page, so a floor user without nimbuscms.crm access is denied there by core. party_name is first-party booking data, not laundered from a CRM record. A proper in-process guest picker (searching CRM) would need a CRM read service port (ADR 0019) — a documented follow-up, not built here.

Tests

ReservationsTest (round-trip, name required, datetime parsing, validated table ref, contact_id stored-not-resolved + bad-id rejected, status allow-list, ordering/filter), ReservationsAdminTest (escape-on-render; a linked booking links out to the CRM and shows no CRM data; an unlinked one shows no link), RestaurantToolsetTest (reservation tools listed; a reservation round-trips over MCP carrying only the link; content token can't reach), RestaurantPluginTest (the book is gated on :floor).

cs-fixer + PHPStan L6 green locally; phpunit in CI (8.2 + 8.3).

🤖 Generated with Claude Code

Lights up the CRM integration without ever reading CRM data.

- Schema: rest_reservation (003_reservations) — party_name/size/reserved_at/
  notes are the restaurant's own first-party data; table_id (validated,
  same-plugin) + contact_id (a bare link to a CRM contact).
- Reservations service: allow-list, party name required, allow-listed status
  (booked/seated/cancelled/no_show), strict datetime (accepts datetime-local),
  validated table ref. contact_id is stored as given and NOT resolved or
  existence-checked — reading the CRM would breach its capability gate.
- Admin: the book (list + status filter + create/edit). Shows only the booking's
  own data; a CRM-linked booking renders a "Guest in CRM" LINK to
  /admin/crm?edit=<id> (core gates that page) — never CRM data itself. Gated on
  danmat.restaurant:floor + CSRF, mobile-reflow.
- MCP: reservations / _get / _set / _status / _delete; contact_id passes through
  as a bare link.
- Guide: reservations section (+ the CRM boundary).
- Tests: ReservationsTest, ReservationsAdminTest (escape + gated CRM link, no
  CRM data), RestaurantToolsetTest (reservation tools + round-trip carrying only
  the link), RestaurantPluginTest (reservations page gated :floor).

Security: the restaurant stores only contact_id and links out to the CRM's own
gated page; no CRM PII is read, resolved or copied into rest_* tables. A proper
in-process guest picker would need a CRM read service port (ADR 0019) — a
documented follow-up.

cs-fixer + PHPStan green locally; phpunit runs in CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DanMat
DanMat merged commit 080a1b8 into nimbus-rebuild Sep 6, 2026
4 checks passed
@DanMat
DanMat deleted the slice/reservations branch September 6, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant