@topy-ai/maggie 0.7.37 → 0.7.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-zh-TW.md +65 -7
- package/README.md +80 -11
- package/bin/maggie.js +10 -2
- package/bundled-contracts/maggie-design/css-utility-evidence-v1.schema.json +10 -0
- package/bundled-contracts/maggie-design/dashboard-surface-v1.schema.json +11 -0
- package/bundled-contracts/maggie-design/progressive-enhancement-v1.schema.json +12 -0
- package/bundled-contracts/maggie-design/sample-surface-v1.schema.json +13 -0
- package/bundled-contracts/maggie-seo/privacy-origin-evidence-v1.schema.json +11 -0
- package/bundled-contracts/maggiedash/README.md +1 -1
- package/bundled-contracts/maggiedash/booking-access-v1.json +5 -4
- package/bundled-contracts/maggiedash/booking-customer-surface-v1.json +12 -1
- package/bundled-contracts/maggiedash/booking-email-templates-v1.json +2 -0
- package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +16 -2
- package/bundled-contracts/maggiedash/booking-runtime.v1.json +41 -0
- package/bundled-contracts/maggiedash/execution-board.json +529 -28
- package/bundled-contracts/maggiedash/host-capabilities-v1.schema.json +10 -0
- package/bundled-contracts/maggiedash/site-structure-v1.schema.json +13 -0
- package/bundled-references/maggiedash-booking/ARCHITECTURE.md +218 -0
- package/bundled-references/maggiedash-booking/CURRENT-STATE.md +92 -0
- package/bundled-references/maggiedash-booking/DATA-FLOW.md +143 -0
- package/bundled-references/maggiedash-booking/DATA-MODEL.md +367 -0
- package/bundled-references/maggiedash-booking/DECISIONS.md +94 -0
- package/bundled-references/maggiedash-booking/EXECUTION-BOARD.json +2387 -0
- package/bundled-references/maggiedash-booking/HOST-ADAPTER.md +314 -0
- package/bundled-references/maggiedash-booking/ORAWELLNESS-INTEGRATION-AUDIT.md +227 -0
- package/bundled-references/maggiedash-booking/PAYMENT-GATEWAY.md +267 -0
- package/bundled-references/maggiedash-booking/PRD.md +228 -0
- package/bundled-references/maggiedash-booking/PROGRESS.md +2434 -0
- package/bundled-references/maggiedash-booking/QA-TEST-PLAN.md +235 -0
- package/bundled-references/maggiedash-booking/README.md +271 -0
- package/bundled-references/maggiedash-booking/RUNTIME-OPERATIONS.md +152 -0
- package/bundled-references/maggiedash-booking/SECURITY-COMPLIANCE.md +158 -0
- package/bundled-references/maggiedash-booking/SKILLS-AND-CLI.md +542 -0
- package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +129 -0
- package/bundled-references/maggiedash-booking/TASK-RUNBOOK.md +107 -0
- package/bundled-references/maggiedash-booking/TASKS.md +137 -0
- package/bundled-references/maggiedash-booking/USER-JOURNEYS.md +224 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.excalidraw +1 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.mmd +16 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-dfd.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.mmd +19 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.excalidraw +1 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.mmd +11 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/manager-journey.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/payment-sequence.mmd +20 -0
- package/bundled-references/maggiedash-booking/diagrams/payment-sequence.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/payment-sequence.svg +1 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.excalidraw +1 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.mmd +10 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.png +0 -0
- package/bundled-references/maggiedash-booking/diagrams/system-context.svg +1 -0
- package/bundled-skills/maggie-blog-bootstrap/SKILL.md +16 -0
- package/bundled-skills/maggie-booking/SKILL.md +103 -22
- package/bundled-skills/maggie-design/SKILL.md +35 -0
- package/bundled-skills/maggie-seo-geo/SKILL.md +12 -0
- package/bundled-skills/maggie-service-booking/SKILL.md +14 -0
- package/bundled-tools/clis/maggie_booking.py +231 -31
- package/bundled-tools/clis/maggie_contracts.py +282 -0
- package/bundled-tools/clis/maggie_dash.py +28 -6
- package/bundled-tools/clis/maggie_design.py +29 -12
- package/bundled-tools/clis/maggie_service_booking.py +50 -1
- package/bundled-tools/clis/site_audit.py +49 -0
- package/package.json +1 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# Booking data model
|
|
2
|
+
|
|
3
|
+
The model is normalized around a tenant, location, service, allocation, booking,
|
|
4
|
+
payment, and event boundary. The exact SQL migration belongs in MaggieDash, but
|
|
5
|
+
this document is the contract that the migration and adapters must implement.
|
|
6
|
+
|
|
7
|
+
## 0. MaggieDash physical mapping
|
|
8
|
+
|
|
9
|
+
The current MaggieDash schema uses the existing `maggiedash_projects` identity
|
|
10
|
+
as the Booking tenant. Booking tables therefore use `project_id` as the
|
|
11
|
+
physical tenant key rather than introducing a second `tenant_id` hierarchy.
|
|
12
|
+
The host resolves the authenticated email/password session through
|
|
13
|
+
`maggiedash_memberships` before every query and mutation. This is an intentional
|
|
14
|
+
MVP simplification: a future multi-brand tenancy layer may add a logical
|
|
15
|
+
`booking_tenants` view or bridge without changing the source-of-truth rule.
|
|
16
|
+
|
|
17
|
+
The migration includes locations, catalog, staff/resources, schedules,
|
|
18
|
+
customers, holds, bookings, segments, allocations, payment accounts/attempts/
|
|
19
|
+
transactions/refunds, package purchases/refunds, webhook events, outbox, notifications, audit, and
|
|
20
|
+
idempotency records. `blocks` and `closures` remain host-owned schedule
|
|
21
|
+
projections for this version; the current UI contract represents them through
|
|
22
|
+
the schedule DTO until their dedicated policy commands are implemented.
|
|
23
|
+
|
|
24
|
+
Run the source and database checks with:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm run booking:schema:validate
|
|
28
|
+
node scripts/validate-booking-schema.mjs \
|
|
29
|
+
--database-url "$DATABASE_URL" --confirm
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The second command is an explicitly mutating migration check and must only be
|
|
33
|
+
used against a disposable or approved migration-test database.
|
|
34
|
+
|
|
35
|
+
## 1. Entity map
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
Tenant
|
|
39
|
+
-> Location
|
|
40
|
+
-> UserMembership
|
|
41
|
+
-> Staff
|
|
42
|
+
-> Service -> ServiceVariant
|
|
43
|
+
-> Resource -> ServiceResourceRequirement
|
|
44
|
+
-> AvailabilityRule / Shift / TimeOff / Block / Closure
|
|
45
|
+
-> Customer
|
|
46
|
+
-> Booking -> BookingSegment -> Allocation
|
|
47
|
+
-> CustomerPackageEntitlement -> PackageRedemption
|
|
48
|
+
-> PackagePurchase -> PackagePurchaseRefund
|
|
49
|
+
-> PaymentAttempt -> PaymentTransaction -> Refund
|
|
50
|
+
-> PaymentWebhookEvent
|
|
51
|
+
-> Notification / OutboxEvent / AuditEvent
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 2. Core entities
|
|
55
|
+
|
|
56
|
+
### Tenant and access
|
|
57
|
+
|
|
58
|
+
`booking_tenants` extends or references the existing MaggieDash project identity.
|
|
59
|
+
Every booking-owned table carries `tenant_id`; location-scoped records also
|
|
60
|
+
carry `location_id` where the query boundary needs it.
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
booking_tenants
|
|
64
|
+
id, project_id, name, default_timezone, default_currency, status
|
|
65
|
+
|
|
66
|
+
booking_memberships
|
|
67
|
+
tenant_id, user_id, role, location_scope_json, status
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use the existing host email/password session. Do not create a second login
|
|
71
|
+
system in the booking bundle.
|
|
72
|
+
|
|
73
|
+
### Locations
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
booking_locations
|
|
77
|
+
id, tenant_id, name, slug, timezone, locale, currency
|
|
78
|
+
address_json, phone, active, booking_enabled
|
|
79
|
+
created_at, updated_at
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`timezone` is an IANA identifier such as `Europe/London`. It is not a browser
|
|
83
|
+
offset or a fixed `+01:00` string.
|
|
84
|
+
|
|
85
|
+
### Catalog
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
booking_services
|
|
89
|
+
id, tenant_id, category_id, name, description
|
|
90
|
+
booking_enabled, online_booking_enabled, status
|
|
91
|
+
default_duration_minutes, default_amount_minor, currency
|
|
92
|
+
buffer_before_minutes, buffer_after_minutes
|
|
93
|
+
processing_minutes, finishing_minutes
|
|
94
|
+
cancellation_policy_json, deposit_policy_json
|
|
95
|
+
source_provider, source_record_id, source_url, source_revision
|
|
96
|
+
created_at, updated_at
|
|
97
|
+
|
|
98
|
+
booking_service_variants
|
|
99
|
+
id, service_id, name, duration_minutes
|
|
100
|
+
amount_minor, currency, active, sort_order
|
|
101
|
+
provider_variant_id, source_json
|
|
102
|
+
|
|
103
|
+
booking_staff_services
|
|
104
|
+
tenant_id, staff_id, service_id, location_id
|
|
105
|
+
duration_override_minutes, amount_override_minor, currency_override
|
|
106
|
+
active
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Imported provider facts should map to the existing universal service catalogue
|
|
110
|
+
or an explicit bridge table. AI-generated copy is not a source of price,
|
|
111
|
+
duration, provider ID, or booking URL.
|
|
112
|
+
|
|
113
|
+
### Treatment packages and customer entitlements
|
|
114
|
+
|
|
115
|
+
The package catalogue and a customer's prepaid entitlement are separate
|
|
116
|
+
aggregates. A package definition is reusable; an entitlement is tenant-scoped
|
|
117
|
+
to one customer and records the granted balance, expiry, source, and audit
|
|
118
|
+
history.
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
booking_treatment_packages
|
|
122
|
+
id, tenant_id, name, description, price_minor, currency
|
|
123
|
+
expiration_days, active, metadata_json
|
|
124
|
+
|
|
125
|
+
booking_treatment_package_items
|
|
126
|
+
package_id, service_variant_id, quantity, sort_order
|
|
127
|
+
|
|
128
|
+
booking_customer_packages
|
|
129
|
+
id, tenant_id, customer_id, package_id
|
|
130
|
+
status, source, purchased_at, expires_at, metadata_json
|
|
131
|
+
|
|
132
|
+
booking_customer_package_items
|
|
133
|
+
entitlement_id, service_variant_id
|
|
134
|
+
quantity_granted, quantity_redeemed
|
|
135
|
+
|
|
136
|
+
booking_package_redemptions
|
|
137
|
+
id, tenant_id, booking_id, entitlement_id
|
|
138
|
+
service_variant_id, quantity, status, created_at
|
|
139
|
+
reversed_at, reversal_reason, reversed_by
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The current release supports manager-granted entitlements, balance reads,
|
|
143
|
+
expiry projection, atomic redemption while creating a manager or public
|
|
144
|
+
customer booking, and a separate customer package purchase/refund aggregate.
|
|
145
|
+
Cancelling a booking reverses its active redemption rows exactly once and
|
|
146
|
+
restores the matching `quantity_redeemed` values in the same transaction;
|
|
147
|
+
completed and no-show bookings intentionally retain their debits. A reversed
|
|
148
|
+
ledger row is never deleted, so the package balance history remains auditable.
|
|
149
|
+
Public package checkout uses the same tenant-bound customer surface as
|
|
150
|
+
appointment checkout; Stripe webhooks or provider reads fulfill the
|
|
151
|
+
entitlement, while refunds are rejected after any visit has been redeemed.
|
|
152
|
+
|
|
153
|
+
### Staff and resources
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
booking_staff
|
|
157
|
+
id, tenant_id, user_id, display_name, email, phone
|
|
158
|
+
role, active, bookable, commission_policy_json
|
|
159
|
+
|
|
160
|
+
booking_staff_locations
|
|
161
|
+
tenant_id, staff_id, location_id, active
|
|
162
|
+
|
|
163
|
+
booking_resources
|
|
164
|
+
id, tenant_id, location_id, name, type, capacity, active
|
|
165
|
+
|
|
166
|
+
booking_service_resource_requirements
|
|
167
|
+
service_id, resource_id, quantity, mode
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`mode` can later support exclusive, shared-capacity, or quantity-based
|
|
171
|
+
allocation. The MVP should implement exclusive allocation first.
|
|
172
|
+
|
|
173
|
+
### Schedule and allocation
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
booking_working_hours
|
|
177
|
+
id, tenant_id, location_id, staff_id, weekday
|
|
178
|
+
start_local, end_local, timezone, active
|
|
179
|
+
|
|
180
|
+
booking_shifts
|
|
181
|
+
id, tenant_id, location_id, staff_id, start_at, end_at, status
|
|
182
|
+
|
|
183
|
+
booking_time_off
|
|
184
|
+
id, tenant_id, staff_id, start_at, end_at, reason
|
|
185
|
+
|
|
186
|
+
booking_blocks
|
|
187
|
+
id, tenant_id, location_id, staff_id, resource_id
|
|
188
|
+
start_at, end_at, reason, status
|
|
189
|
+
|
|
190
|
+
booking_closures
|
|
191
|
+
id, tenant_id, location_id, start_at, end_at, reason
|
|
192
|
+
|
|
193
|
+
booking_holds
|
|
194
|
+
id, tenant_id, location_id, customer_id
|
|
195
|
+
expires_at, status, idempotency_key, release_reason
|
|
196
|
+
|
|
197
|
+
booking_allocations
|
|
198
|
+
id, tenant_id, location_id, hold_id, booking_id, segment_id
|
|
199
|
+
staff_id, resource_id, start_at, end_at, status
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Active allocations must be protected by a database-level non-overlap rule. The
|
|
203
|
+
exact active statuses must be explicit in the migration and match the service
|
|
204
|
+
transition code.
|
|
205
|
+
|
|
206
|
+
### Booking
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
booking_bookings
|
|
210
|
+
id, tenant_id, location_id, customer_id
|
|
211
|
+
status, payment_status, source, channel
|
|
212
|
+
start_at, end_at, timezone, timezone_fold
|
|
213
|
+
subtotal_minor, discount_minor, tax_minor, total_minor
|
|
214
|
+
currency, payment_policy_json, initial_due_minor
|
|
215
|
+
deposit_due_minor, paid_minor, balance_due_minor
|
|
216
|
+
cancellation_policy_snapshot_json
|
|
217
|
+
attribution_json, notes_redacted_or_policy_checked
|
|
218
|
+
created_by, created_at, updated_at
|
|
219
|
+
|
|
220
|
+
booking_segments
|
|
221
|
+
id, booking_id, service_id, variant_id
|
|
222
|
+
staff_id, resource_id, start_at, end_at
|
|
223
|
+
amount_minor, currency, duration_minutes, sort_order
|
|
224
|
+
|
|
225
|
+
booking_events
|
|
226
|
+
id, tenant_id, booking_id, type, from_status, to_status
|
|
227
|
+
actor_id, reason, correlation_id, payload_json, created_at
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Do not use a boolean `booked`. Booking lifecycle and payment lifecycle are
|
|
231
|
+
separate state machines.
|
|
232
|
+
|
|
233
|
+
### Customer and consent
|
|
234
|
+
|
|
235
|
+
```text
|
|
236
|
+
booking_customers
|
|
237
|
+
id, tenant_id, email, phone, name
|
|
238
|
+
marketing_consent, service_consent_json
|
|
239
|
+
provider_customer_ref, provider_payment_method_ref
|
|
240
|
+
notes_policy_state, created_at, updated_at
|
|
241
|
+
|
|
242
|
+
booking_customer_events
|
|
243
|
+
id, tenant_id, customer_id, type, source, created_at
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Medical, allergy, patch-test, or consultation data requires a separate consent
|
|
247
|
+
and retention design. It is not part of the basic customer table.
|
|
248
|
+
|
|
249
|
+
### Payments and providers
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
booking_payment_accounts
|
|
253
|
+
id, tenant_id, provider, account_ref, mode, capabilities_json, status
|
|
254
|
+
|
|
255
|
+
booking_payment_attempts
|
|
256
|
+
id, tenant_id, booking_id, purpose, provider
|
|
257
|
+
amount_minor, currency, status, idempotency_key
|
|
258
|
+
external_intent_ref, client_reference, error_code_safe
|
|
259
|
+
created_at, updated_at
|
|
260
|
+
|
|
261
|
+
booking_payment_transactions
|
|
262
|
+
id, tenant_id, booking_id, attempt_id, provider
|
|
263
|
+
type, amount_minor, currency, external_ref
|
|
264
|
+
status, occurred_at, metadata_json
|
|
265
|
+
|
|
266
|
+
booking_refunds
|
|
267
|
+
id, tenant_id, booking_id, payment_transaction_id
|
|
268
|
+
amount_minor, currency, provider, external_ref, status
|
|
269
|
+
idempotency_key, reason, created_at, updated_at
|
|
270
|
+
|
|
271
|
+
booking_payment_webhook_events
|
|
272
|
+
id, tenant_id, provider, external_event_id
|
|
273
|
+
customer_id, provider_payment_method_ref
|
|
274
|
+
event_type, signature_verified, received_at, processed_at
|
|
275
|
+
processing_status, safe_error, payload_hash
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The raw provider body is not stored by default. A short-lived encrypted
|
|
279
|
+
quarantine or provider dashboard link may be used under a separately reviewed
|
|
280
|
+
retention policy.
|
|
281
|
+
|
|
282
|
+
Provider customer and payment-method values are opaque external references;
|
|
283
|
+
they are tenant-scoped and never exposed as raw provider objects to the
|
|
284
|
+
browser. Customer service consent records the `noShowFee` consent used by the
|
|
285
|
+
separate explicit no-show adjustment route. `adjustment` attempts are excluded
|
|
286
|
+
from ordinary paid/refundable booking totals and are written only after a
|
|
287
|
+
confirmed off-session provider result.
|
|
288
|
+
|
|
289
|
+
### Events, notifications, and audit
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
booking_outbox_events
|
|
293
|
+
id, tenant_id, aggregate_type, aggregate_id, event_type
|
|
294
|
+
schema_version, correlation_id, payload_json
|
|
295
|
+
status, attempts, next_attempt_at, last_error_safe
|
|
296
|
+
|
|
297
|
+
booking_notifications
|
|
298
|
+
id, tenant_id, booking_id, waitlist_entry_id, package_purchase_id
|
|
299
|
+
channel, template, recipient_hash
|
|
300
|
+
status, idempotency_key, provider_ref, last_error_safe
|
|
301
|
+
|
|
302
|
+
booking_audit_events
|
|
303
|
+
id, tenant_id, actor_id, action, entity_type, entity_id
|
|
304
|
+
reason, correlation_id, before_summary_json, after_summary_json
|
|
305
|
+
created_at
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## 3. State machines
|
|
309
|
+
|
|
310
|
+
### Booking state
|
|
311
|
+
|
|
312
|
+
```text
|
|
313
|
+
draft -> held -> pending_payment -> confirmed
|
|
314
|
+
confirmed -> checked_in -> in_progress -> completed
|
|
315
|
+
held -> expired
|
|
316
|
+
held -> cancelled
|
|
317
|
+
pending_payment -> payment_failed -> held | cancelled
|
|
318
|
+
confirmed -> rescheduled -> confirmed
|
|
319
|
+
confirmed -> cancelled | no_show
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The implementation may combine `rescheduled` into an event rather than a long-
|
|
323
|
+
lived status, but the old and new allocations must be auditable.
|
|
324
|
+
|
|
325
|
+
### Payment state
|
|
326
|
+
|
|
327
|
+
```text
|
|
328
|
+
not_required
|
|
329
|
+
requires_payment -> action_required -> processing -> paid
|
|
330
|
+
requires_payment -> failed
|
|
331
|
+
processing -> failed | paid
|
|
332
|
+
paid -> partially_refunded -> refunded
|
|
333
|
+
paid -> refund_pending -> partially_refunded | refunded | refund_failed
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Hold state
|
|
337
|
+
|
|
338
|
+
```text
|
|
339
|
+
active -> converted
|
|
340
|
+
active -> released
|
|
341
|
+
active -> expired
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## 4. Required constraints and indexes
|
|
345
|
+
|
|
346
|
+
- Unique `(tenant_id, slug)` for locations.
|
|
347
|
+
- Unique `(tenant_id, provider, provider_service_id)` for imported services.
|
|
348
|
+
- Unique `(tenant_id, idempotency_key, command_type)` for commands.
|
|
349
|
+
- Unique `(provider, external_event_id)` for webhook deduplication.
|
|
350
|
+
- Unique `(tenant_id, booking_id, active_attempt_sequence)` or an equivalent
|
|
351
|
+
local attempt sequence.
|
|
352
|
+
- Index bookings by tenant, location, start time, status, and customer.
|
|
353
|
+
- Index allocations by tenant, staff, resource, time range, and active status.
|
|
354
|
+
- Index outbox by tenant, status, and next attempt time.
|
|
355
|
+
- Database-level overlap protection for active staff/resource allocations.
|
|
356
|
+
|
|
357
|
+
## 5. Migration order
|
|
358
|
+
|
|
359
|
+
1. Add tenant/location/access bridge and feature flag.
|
|
360
|
+
2. Add catalog, staff, resource, schedule, and policy tables.
|
|
361
|
+
3. Add holds, allocations, bookings, segments, and events.
|
|
362
|
+
4. Add payment accounts, attempts, transactions, refunds, and webhook events.
|
|
363
|
+
5. Add treatment packages, customer entitlements, redemption ledger, outbox,
|
|
364
|
+
notifications, audit, and read-model indexes.
|
|
365
|
+
6. Backfill only reviewed provider facts; never backfill invented availability.
|
|
366
|
+
7. Run migration preflight, shadow reads, and conflict tests before enabling
|
|
367
|
+
writes.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Booking decision log
|
|
2
|
+
|
|
3
|
+
This is the durable decision record for the Booking workspace. D-001, D-002,
|
|
4
|
+
D-007 and the v1 contract boundaries have landed in MaggieDash. The remaining
|
|
5
|
+
runtime decisions stay explicit until the host services and production tests
|
|
6
|
+
land.
|
|
7
|
+
|
|
8
|
+
## D-001: Separate `/_maggie/booking` workspace
|
|
9
|
+
|
|
10
|
+
Status: accepted and implemented in MaggieDash
|
|
11
|
+
|
|
12
|
+
Create a separate booking workspace beside `/_maggie/admin`. Booking has a
|
|
13
|
+
different information architecture, permission model, operational density, and
|
|
14
|
+
failure vocabulary. Sharing primitives and session infrastructure is useful;
|
|
15
|
+
merging the screens would make the existing admin surface harder to operate.
|
|
16
|
+
|
|
17
|
+
## D-002: PostgreSQL is business truth
|
|
18
|
+
|
|
19
|
+
Status: accepted for the starter schema; host migration pending
|
|
20
|
+
|
|
21
|
+
The local database owns tenants, catalog, schedule, availability, holds,
|
|
22
|
+
bookings, customers, payment projections, audit, and outbox. External providers
|
|
23
|
+
are sources of external facts and money state, not the local booking database.
|
|
24
|
+
|
|
25
|
+
## D-003: Holds are first-class
|
|
26
|
+
|
|
27
|
+
Status: accepted and implemented locally; host transaction evidence pending
|
|
28
|
+
|
|
29
|
+
A selected slot is reserved before payment starts. Holds have expiry, scope,
|
|
30
|
+
idempotency, and release reason. This prevents two successful payments for one
|
|
31
|
+
allocation and gives the customer a bounded payment window.
|
|
32
|
+
|
|
33
|
+
## D-004: Database constraints enforce allocation correctness
|
|
34
|
+
|
|
35
|
+
Status: accepted and implemented in the starter schema and repository seams;
|
|
36
|
+
host concurrency evidence pending
|
|
37
|
+
|
|
38
|
+
The availability UI is advisory. A database-level overlap constraint and
|
|
39
|
+
transactional allocation are the final double-booking guard.
|
|
40
|
+
|
|
41
|
+
## D-005: Payment adapter, not payment logic in Booking
|
|
42
|
+
|
|
43
|
+
Status: accepted and implemented locally; live provider evidence pending
|
|
44
|
+
|
|
45
|
+
Booking uses a `PaymentGateway` port. Stripe is implemented first. This keeps
|
|
46
|
+
provider changes out of booking transitions, data tables, and manager screens.
|
|
47
|
+
|
|
48
|
+
## D-006: Webhook or provider reconciliation is payment proof
|
|
49
|
+
|
|
50
|
+
Status: accepted and implemented locally; signed live webhook evidence pending
|
|
51
|
+
|
|
52
|
+
A redirect is a navigation event. A verified provider event or server-side
|
|
53
|
+
provider read is required before local payment projection and booking
|
|
54
|
+
confirmation.
|
|
55
|
+
|
|
56
|
+
## D-007: Email/password auth stays host-owned
|
|
57
|
+
|
|
58
|
+
Status: accepted from current MaggieDash contract
|
|
59
|
+
|
|
60
|
+
MaggieDash uses the host's traditional email/password session. Booking must use
|
|
61
|
+
the same boundary and must not add passkeys, passwordless login, or a second
|
|
62
|
+
identity store.
|
|
63
|
+
|
|
64
|
+
## D-008: Provider capability flags fail closed
|
|
65
|
+
|
|
66
|
+
Status: accepted and implemented locally; host account verification pending
|
|
67
|
+
|
|
68
|
+
If a provider does not support live availability, refunds, saved methods, or
|
|
69
|
+
Terminal, the UI and API do not pretend that it does. Public links remain a
|
|
70
|
+
safe fallback where native booking is not enabled.
|
|
71
|
+
|
|
72
|
+
## D-009: Domain features are phased
|
|
73
|
+
|
|
74
|
+
Status: accepted phased rollout; packages and bounded waitlist are in the local
|
|
75
|
+
v1 slice, while memberships, gifts, loyalty, and recurring appointments remain
|
|
76
|
+
future modules
|
|
77
|
+
|
|
78
|
+
Ship catalog, schedule, availability, holds, booking, payment, treatment
|
|
79
|
+
packages, and a bounded priority/FIFO waitlist matcher before memberships,
|
|
80
|
+
gifts, loyalty, marketplace, and AI concierge. Advanced optimization across
|
|
81
|
+
multiple services, locations, and alternative staff remains a later phase. The
|
|
82
|
+
first proof is reliable Calendar -> Availability -> Booking -> Payment behavior.
|
|
83
|
+
|
|
84
|
+
## Remaining decisions
|
|
85
|
+
|
|
86
|
+
1. Which PostgreSQL versions and deployment platforms belong in the supported
|
|
87
|
+
production matrix?
|
|
88
|
+
2. Whether future multi-location tenants need separate payment accounts per
|
|
89
|
+
location; v1 currently uses one tenant-scoped account.
|
|
90
|
+
3. Which future module gets a separate contract first: memberships, gift cards,
|
|
91
|
+
recurring appointments, loyalty, or SMS/WhatsApp.
|
|
92
|
+
|
|
93
|
+
Until these are resolved, implementation should use the provider-neutral
|
|
94
|
+
interfaces and avoid irreversible migrations.
|