@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.
Files changed (67) hide show
  1. package/README-zh-TW.md +65 -7
  2. package/README.md +80 -11
  3. package/bin/maggie.js +10 -2
  4. package/bundled-contracts/maggie-design/css-utility-evidence-v1.schema.json +10 -0
  5. package/bundled-contracts/maggie-design/dashboard-surface-v1.schema.json +11 -0
  6. package/bundled-contracts/maggie-design/progressive-enhancement-v1.schema.json +12 -0
  7. package/bundled-contracts/maggie-design/sample-surface-v1.schema.json +13 -0
  8. package/bundled-contracts/maggie-seo/privacy-origin-evidence-v1.schema.json +11 -0
  9. package/bundled-contracts/maggiedash/README.md +1 -1
  10. package/bundled-contracts/maggiedash/booking-access-v1.json +5 -4
  11. package/bundled-contracts/maggiedash/booking-customer-surface-v1.json +12 -1
  12. package/bundled-contracts/maggiedash/booking-email-templates-v1.json +2 -0
  13. package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +16 -2
  14. package/bundled-contracts/maggiedash/booking-runtime.v1.json +41 -0
  15. package/bundled-contracts/maggiedash/execution-board.json +529 -28
  16. package/bundled-contracts/maggiedash/host-capabilities-v1.schema.json +10 -0
  17. package/bundled-contracts/maggiedash/site-structure-v1.schema.json +13 -0
  18. package/bundled-references/maggiedash-booking/ARCHITECTURE.md +218 -0
  19. package/bundled-references/maggiedash-booking/CURRENT-STATE.md +92 -0
  20. package/bundled-references/maggiedash-booking/DATA-FLOW.md +143 -0
  21. package/bundled-references/maggiedash-booking/DATA-MODEL.md +367 -0
  22. package/bundled-references/maggiedash-booking/DECISIONS.md +94 -0
  23. package/bundled-references/maggiedash-booking/EXECUTION-BOARD.json +2387 -0
  24. package/bundled-references/maggiedash-booking/HOST-ADAPTER.md +314 -0
  25. package/bundled-references/maggiedash-booking/ORAWELLNESS-INTEGRATION-AUDIT.md +227 -0
  26. package/bundled-references/maggiedash-booking/PAYMENT-GATEWAY.md +267 -0
  27. package/bundled-references/maggiedash-booking/PRD.md +228 -0
  28. package/bundled-references/maggiedash-booking/PROGRESS.md +2434 -0
  29. package/bundled-references/maggiedash-booking/QA-TEST-PLAN.md +235 -0
  30. package/bundled-references/maggiedash-booking/README.md +271 -0
  31. package/bundled-references/maggiedash-booking/RUNTIME-OPERATIONS.md +152 -0
  32. package/bundled-references/maggiedash-booking/SECURITY-COMPLIANCE.md +158 -0
  33. package/bundled-references/maggiedash-booking/SKILLS-AND-CLI.md +542 -0
  34. package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +129 -0
  35. package/bundled-references/maggiedash-booking/TASK-RUNBOOK.md +107 -0
  36. package/bundled-references/maggiedash-booking/TASKS.md +137 -0
  37. package/bundled-references/maggiedash-booking/USER-JOURNEYS.md +224 -0
  38. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.excalidraw +1 -0
  39. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.mmd +16 -0
  40. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.png +0 -0
  41. package/bundled-references/maggiedash-booking/diagrams/booking-dfd.svg +1 -0
  42. package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.mmd +19 -0
  43. package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.png +0 -0
  44. package/bundled-references/maggiedash-booking/diagrams/booking-state-machine.svg +1 -0
  45. package/bundled-references/maggiedash-booking/diagrams/manager-journey.excalidraw +1 -0
  46. package/bundled-references/maggiedash-booking/diagrams/manager-journey.mmd +11 -0
  47. package/bundled-references/maggiedash-booking/diagrams/manager-journey.png +0 -0
  48. package/bundled-references/maggiedash-booking/diagrams/manager-journey.svg +1 -0
  49. package/bundled-references/maggiedash-booking/diagrams/payment-sequence.mmd +20 -0
  50. package/bundled-references/maggiedash-booking/diagrams/payment-sequence.png +0 -0
  51. package/bundled-references/maggiedash-booking/diagrams/payment-sequence.svg +1 -0
  52. package/bundled-references/maggiedash-booking/diagrams/system-context.excalidraw +1 -0
  53. package/bundled-references/maggiedash-booking/diagrams/system-context.mmd +10 -0
  54. package/bundled-references/maggiedash-booking/diagrams/system-context.png +0 -0
  55. package/bundled-references/maggiedash-booking/diagrams/system-context.svg +1 -0
  56. package/bundled-skills/maggie-blog-bootstrap/SKILL.md +16 -0
  57. package/bundled-skills/maggie-booking/SKILL.md +103 -22
  58. package/bundled-skills/maggie-design/SKILL.md +35 -0
  59. package/bundled-skills/maggie-seo-geo/SKILL.md +12 -0
  60. package/bundled-skills/maggie-service-booking/SKILL.md +14 -0
  61. package/bundled-tools/clis/maggie_booking.py +231 -31
  62. package/bundled-tools/clis/maggie_contracts.py +282 -0
  63. package/bundled-tools/clis/maggie_dash.py +28 -6
  64. package/bundled-tools/clis/maggie_design.py +29 -12
  65. package/bundled-tools/clis/maggie_service_booking.py +50 -1
  66. package/bundled-tools/clis/site_audit.py +49 -0
  67. package/package.json +1 -1
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "MaggieDash host capability manifest",
4
+ "type": "object",
5
+ "required": ["schemaVersion", "endpoints"],
6
+ "properties": {
7
+ "schemaVersion": {"const": "maggie-host-capabilities.v1"},
8
+ "endpoints": {"type": "array", "minItems": 1, "items": {"type": "object", "required": ["id", "path", "enabled"]}}
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Provider-neutral Maggie site structure",
4
+ "type": "object",
5
+ "required": ["schemaVersion", "menus", "templates", "pages", "bands"],
6
+ "properties": {
7
+ "schemaVersion": {"const": "maggie-site-structure.v1"},
8
+ "menus": {"type": "array"},
9
+ "templates": {"type": "array"},
10
+ "pages": {"type": "array"},
11
+ "bands": {"type": "array"}
12
+ }
13
+ }
@@ -0,0 +1,218 @@
1
+ # Booking workspace architecture
2
+
3
+ This document explains the boundary between MaggieDash, the host project, the
4
+ booking domain, and external providers. The manager workspace, distribution
5
+ manifest, starter schema, versioned contracts, reference host, PostgreSQL
6
+ seams, Stripe boundary, Resend outbox, and Astro host scaffold are implemented
7
+ locally. Production credentials, migrations, scheduler enablement, and
8
+ real-host evidence remain the host integration boundary.
9
+
10
+ ## 1. Current boundary and target boundary
11
+
12
+ ### Current, verified in repository
13
+
14
+ - MaggieDash installs its React source into `./_maggie/admin`.
15
+ - The host owns the framework route, email/password session, database,
16
+ `/api/maggie/*` endpoints, provider credential boundary, and deployment.
17
+ Stripe may come from the deployment secret store or the owner/manager-only
18
+ dashboard setup path, where it is verified before authenticated encryption;
19
+ raw credentials never cross into the public browser bundle.
20
+ - The dashboard uses `/api/maggie` and a versioned host adapter contract.
21
+ - `maggie-service-booking` imports and validates provider-owned service data. It
22
+ does not claim live booking mutation or availability when provider evidence is
23
+ missing.
24
+ - The shared PostgreSQL starter schema covers projects, users, memberships,
25
+ sessions, documents, media, section presets, and audit events.
26
+ - MaggieDash distributes a sibling `./_maggie/booking` workspace with overview,
27
+ calendar, booking, customer, catalogue, staff, resource, payment, settings,
28
+ and audit screens.
29
+ - The Booking host adapter v1, provider-neutral starter schema, runtime worker,
30
+ reference host, and Astro host wiring are shipped with the distribution; the
31
+ host still supplies production database/provider credentials and evidence.
32
+
33
+ ### Host runtime extension
34
+
35
+ The first-party Booking workspace is installed under `/_maggie/booking`. It shares
36
+ the host session, project identity, design primitives, HTTP transport, audit
37
+ conventions, and runtime evidence model with `/_maggie/admin`, but owns booking
38
+ screens and booking-specific API contracts.
39
+
40
+ The two workspaces should be siblings, not one huge admin screen:
41
+
42
+ ```text
43
+ /_maggie/admin content, SEO, media, AI operations, project settings
44
+ /_maggie/booking calendar, bookings, customers, services, staff, payments
45
+ ```
46
+
47
+ Both routes may import shared UI primitives, but booking must not reach into
48
+ content-specific tables or assume that a provider page is a transactional
49
+ booking record.
50
+
51
+ ## 2. Runtime topology
52
+
53
+ ```text
54
+ Browser
55
+ |
56
+ | email/password host session cookie
57
+ v
58
+ Host framework route
59
+ |-- /_maggie/admin -> MaggieDash admin UI
60
+ |-- /_maggie/booking -> MaggieDash booking UI
61
+ |-- /api/maggie/* -> host adapter endpoints
62
+ |-- /api/maggie/booking/*-> booking adapter endpoints
63
+ |
64
+ | server-only
65
+ v
66
+ Booking application services
67
+ |-- catalog
68
+ |-- availability
69
+ |-- holds and allocation
70
+ |-- booking lifecycle
71
+ |-- payment orchestration
72
+ |-- customer and consent
73
+ |-- notifications and outbox
74
+ |-- audit and reporting
75
+ |
76
+ +--> PostgreSQL: business truth
77
+ +--> PaymentGateway: Stripe first, others later
78
+ +--> Email/SMS adapters
79
+ +--> Calendar adapters, optional
80
+ ```
81
+
82
+ The browser receives normalized DTOs and capability flags. It does not receive
83
+ database connections, Stripe secret keys, raw provider objects, or unrestricted
84
+ agent tools.
85
+
86
+ ## 3. Logical modules
87
+
88
+ | Module | Owns | Must not own |
89
+ |---|---|---|
90
+ | Tenant and access | project, tenant, location scope, role checks | provider credential values |
91
+ | Catalog | services, variants, treatment packages, provider facts | availability decisions, customer balances |
92
+ | Schedule | opening hours, shifts, time off, blocks, closures | payment status |
93
+ | Resource | rooms, equipment, capacity, requirements | customer identity |
94
+ | Availability | candidate slots, exclusion reasons, holds | gateway-specific objects |
95
+ | Booking | booking, segments, transitions, reschedules, cancellation | card data |
96
+ | Payment | attempts, transactions, refunds, reconciliation | staff schedules |
97
+ | Customer | contact, consent, notes, booking history, package entitlements | raw payment method |
98
+ | Notification | durable events, templates, delivery attempts | booking truth mutation |
99
+ | Reporting | read models, daily totals, utilization, reconciliation | source-of-truth writes |
100
+ | Audit | append-only actor/action/reason trail | secret or full payload storage |
101
+ | Provider adapters | translate external APIs and events | canonical business rules |
102
+
103
+ ## 4. Source-of-truth rules
104
+
105
+ | Question | Source of truth | Why |
106
+ |---|---|---|
107
+ | Does a slot conflict? | PostgreSQL allocation constraints plus availability service | UI checks alone race |
108
+ | Is a booking confirmed? | Booking state machine in PostgreSQL | Gateway success is not a booking transition |
109
+ | Did money settle? | Verified gateway event or provider read, projected into payment tables | Redirects can be abandoned or forged |
110
+ | What did the manager change? | Audit events and booking events | Support needs actor and reason |
111
+ | What can a provider do? | Provider capability declaration and health result | UI must fail closed |
112
+ | What does the customer see? | Host-rendered booking/public DTO | External provider UI is not the product contract |
113
+ | What is a service fact? | Approved catalogue provenance | AI copy must not invent price, duration, or URL |
114
+
115
+ ## 5. Request and command flow
116
+
117
+ Every mutating request follows this shape:
118
+
119
+ 1. Resolve the host session and project/tenant scope.
120
+ 2. Authorize the action against role, location, and object ownership.
121
+ 3. Validate the request DTO and idempotency key.
122
+ 4. Load current state and required policy version.
123
+ 5. Apply a transaction with database constraints.
124
+ 6. Append a domain event and audit event in the same transaction where possible.
125
+ 7. Commit an outbox record for notifications, provider side effects, and reports.
126
+ 8. Return the canonical result plus `correlationId`, current state, and safe
127
+ next actions.
128
+
129
+ External side effects are not allowed to decide whether the local booking
130
+ transaction commits. A worker handles retries and reconciliation.
131
+
132
+ ## 6. Booking allocation strategy
133
+
134
+ The availability query produces candidates. The hold or booking command takes
135
+ the final allocation lock. The database must reject overlapping active
136
+ allocations for a staff member or resource within the same tenant and location.
137
+
138
+ For PostgreSQL, the implementation should evaluate an exclusion constraint over
139
+ a range built from `start_at` and `end_at`, with an explicit status predicate
140
+ for active holds/bookings. The exact migration must be reviewed against the
141
+ host's PostgreSQL version and tenancy model before implementation.
142
+
143
+ Application checks remain useful for good error messages, but they are not the
144
+ correctness boundary.
145
+
146
+ ## 7. Time and money
147
+
148
+ - Persist instants as `timestamptz` or an equivalent UTC representation.
149
+ - Persist the venue IANA timezone on the location and copy the resolved
150
+ timezone/fold into booking input evidence.
151
+ - Reject nonexistent local times during DST transitions.
152
+ - Require an explicit fold for ambiguous local times.
153
+ - Persist amount in integer minor units and a three-letter ISO currency.
154
+ - Never derive money from a formatted display string.
155
+ - Store the policy, tax, discount, and rounding decisions used for the booking.
156
+
157
+ ## 8. Provider-neutral payment boundary
158
+
159
+ Booking services depend on the interface described in
160
+ [PAYMENT-GATEWAY.md](./PAYMENT-GATEWAY.md). The first implementation is
161
+ `StripePaymentGateway`. It translates local payment intents into Stripe
162
+ PaymentIntents, SetupIntents, refunds, or Terminal operations as configured.
163
+
164
+ The local `payment_attempts`, `payment_transactions`, and `payment_webhook_events`
165
+ tables remain the system's reconciliation record. A Stripe object ID is an
166
+ external reference, never the only local payment record.
167
+
168
+ ## 9. Failure isolation
169
+
170
+ The UI and write API must distinguish:
171
+
172
+ - configuration missing;
173
+ - provider unavailable;
174
+ - payment action required;
175
+ - payment failed;
176
+ - webhook pending;
177
+ - reconciliation pending;
178
+ - booking conflict;
179
+ - notification failed;
180
+ - permission denied.
181
+
182
+ One failed email or analytics delivery must not roll back a confirmed booking.
183
+ One failed payment read must not mark a captured payment as failed. One
184
+ provider timeout must not cause a second charge without an explicit new payment
185
+ attempt and idempotency key.
186
+
187
+ ## 10. Route integration
188
+
189
+ Astro currently rewrites underscore-prefixed dashboard filesystem paths once in
190
+ middleware because Astro excludes those segments. The booking adapter should
191
+ follow the same rule:
192
+
193
+ ```text
194
+ /_maggie/booking -> /maggie/booking
195
+ /_maggie/booking/* -> /maggie/booking/*
196
+ /_maggie/booking/book -> public host route using the customer page starter
197
+ ```
198
+
199
+ The manager URL stays `/_maggie/booking`; the internal route must not rewrite
200
+ to itself. The manager page is server-rendered only for the shell, carries
201
+ `noindex`, and redirects unauthenticated users to the host login. The optional
202
+ customer page is `/_maggie/booking/book`; it is public/noindex and must use the
203
+ host's separate tenant-bound public API policy.
204
+
205
+ ## 11. Extension points
206
+
207
+ The design intentionally leaves these ports stable:
208
+
209
+ - `PaymentGateway`: Stripe, Square, Adyen, Mollie, or a future provider;
210
+ - `NotificationProvider`: email, SMS, WhatsApp, push;
211
+ - `CalendarProvider`: internal calendar, Google, Outlook, or none;
212
+ - `BookingProvider`: local transactional engine, or a provider link for
213
+ projects that have not enabled native booking;
214
+ - `MediaStorageProvider`: the existing provider-neutral media contract;
215
+ - `AnalyticsSink`: local event/outbox consumer, never a booking dependency.
216
+
217
+ Provider-specific metadata is namespaced and stored as external references.
218
+ Canonical booking state, amount, and lifecycle remain provider-neutral.
@@ -0,0 +1,92 @@
1
+ # MaggieDash Booking current state and gaps
2
+
3
+ Status: current snapshot, 2026-09-21
4
+
5
+ This is the canonical status document for the Booking implementation. The
6
+ append-only [`PROGRESS.md`](./PROGRESS.md) keeps historical counts and decisions
7
+ from earlier delivery turns; those historical values must not be read as the
8
+ current contract.
9
+
10
+ ## Verified implementation surface
11
+
12
+ | Surface | Current state | Authoritative evidence |
13
+ |---|---|---|
14
+ | MaggieDash source | Package `0.2.9`, with host capability, site-structure, and treatment-group contracts; the working tree contains the current Booking release changes | `/home/balalior/Dev/MaggieDash/package.json`, `maggiedash.json`, `git status` |
15
+ | Manager host contract | 47 endpoint IDs, email/password session, server-side role/capability checks | `MaggieDash/contracts/booking-host-adapter-v1.json` |
16
+ | Public customer contract | 11 endpoint IDs, separate noindex/no-store boundary and token policy | `MaggieDash/contracts/booking-customer-surface-v1.json` |
17
+ | Booking access contract | 4 administrator-only access endpoints, staff/manager/finance roles and granular permissions | `MaggieDash/contracts/booking-access-v1.json` |
18
+ | Resend template contract | 10 tenant-scoped events, allowlisted variables, preview/publish/rollback and server-only recipient/provider data | `MaggieDash/contracts/booking-email-templates-v1.json` |
19
+ | Browser evidence contract | 15 manager routes × 3 viewports = 45 screens | `MaggieDash/contracts/booking-browser-evidence-v1.json` |
20
+ | Execution board | 87 tasks: 84 `passed`, 3 `needs-evidence` (`BK-030`, `BK-031`, `BK-032`) | [`EXECUTION-BOARD.json`](./EXECUTION-BOARD.json) |
21
+ | Local regression | AI-CMO Python suite: 364 tests passed; MaggieDash `npm test`, ORA contract checks, and ORA Astro build passed | recorded in the current verification output and [`PROGRESS.md`](./PROGRESS.md) |
22
+
23
+ ## What is implemented locally
24
+
25
+ - Traditional email/password owner registration, login, staff invitations,
26
+ password reset, revocation, disabled-account protection, and server-enforced
27
+ granular Booking permissions.
28
+ - Tenant-scoped locations, services/variants, treatment packages, customer
29
+ entitlements, staff, resources, schedules, closures, buffers, availability,
30
+ holds, multi-treatment bookings, rescheduling, cancellation, waitlist, and
31
+ customer-manage history.
32
+ - Provider-neutral payment orchestration plus the Stripe adapter seam,
33
+ PaymentIntent saved-method flow, SetupIntent setup-only flow, Stripe Customer
34
+ mapping, webhook normalization, refunds, package purchase/refund settlement,
35
+ and explicit consented no-show fee charging.
36
+ - Resend outbox delivery, ten event templates, invitations, reminders,
37
+ customer-manage links, waitlist/package/refund/no-show notification intents,
38
+ retry, dead-letter, and audited replay.
39
+ - PostgreSQL repository seams, schema/preflight tooling, worker scheduling
40
+ artifacts, reference-host validators, runtime/browser/ops evidence contracts,
41
+ and the ai-cmo-skills installer/inspect/release gates.
42
+
43
+ “Implemented locally” means source, contract, fixture, and local/reference-host
44
+ evidence exist. It does not mean a production tenant has been migrated or that
45
+ provider delivery has been proven on the live host.
46
+
47
+ ## Open gaps
48
+
49
+ | Priority | Gap | Current evidence | Exit evidence |
50
+ |---|---|---|---|
51
+ | P0 | ORA Booking schema and access tables are not installed in the configured database | Read-only schema check reported `needs-migration`; core exists, Booking/access tables do not | Owner-approved backup/restore rehearsal, then `node scripts/maggie-booking-schema.mjs --apply --confirm` and a post-migration check |
52
+ | P0 | Stripe test account is not fully verified for Booking | ORA `.env` has the existing `ST_SECRECT_KEY` test secret alias and `ST_PUB_KEY`; `STRIPE_WEBHOOK_SECRET` is absent | Add webhook signing secret, run the authenticated **Verify Stripe connection** action, and capture signed success/failure/delayed/duplicate/refund/no-show test evidence |
53
+ | P0 | Worker and alerting are not enabled on the real host | systemd/cron artifacts are generated, but no host schedule, lock, alert route, or recovery evidence has been supplied | Enable one scheduler, run a disposable/staging worker cycle, prove lock/retry/dead-letter/recovery alerts, and retain sanitized ops evidence |
54
+ | P0 | Real-host runtime and browser conformance is missing | Reference-host, fixture, and local build evidence pass; no authenticated HTTPS runtime/browser artifact exists | Run all 47 manager endpoints and all 45 browser screens against a dedicated staging/test tenant, then pass `release-gate --require-runtime --require-browser --require-ops` |
55
+ | P1 | Published npm package is behind the current Booking contracts | Local AI-CMO package source is `0.7.40`; publish and registry readback are the remaining release actions | Verify the installed public tarball, and rerun `maggie booking inspect` against ORA |
56
+ | P1 | ORA staging data and legacy allocation backfill need approval | `maggie-booking-buffer-backfill.sql` is present but not applied; active locations/catalogue/staff/schedule/payment policy still need real data verification | Apply the reviewed additive/backfill migration in staging, seed a test location/catalogue/staff schedule, and prove readiness plus conflict behavior |
57
+ | P1 | End-to-end Resend delivery is unproven | Outbox/worker code and validators pass, but invitation, reset, reminder, waitlist, package, refund, and no-show messages have not been observed in a staging mailbox | Capture redacted delivery, retry, dead-letter, replay, and duplicate-suppression evidence |
58
+ | P1 | Release/canary ownership is incomplete | `BK-032` remains `needs-evidence`; no final release artifact or canary report is recorded | Produce the board-complete release report, rollback rehearsal, canary window, and feedback closure |
59
+ | P2 | Product scope remains intentionally narrower than Fresha | Memberships, gift-card ledger, recurring appointments, SMS/WhatsApp, tax/accounting, advanced waitlist optimization, and multi-provider production adapters are not in v1 | Create separate contracts/PRDs before promising these capabilities |
60
+
61
+ ## Required host sequence
62
+
63
+ 1. Back up the database and rehearse restore into an empty disposable target.
64
+ 2. Apply the Booking and access migrations with explicit owner approval.
65
+ 3. Register the first owner and verify staff/role boundaries.
66
+ 4. Configure Stripe test secret plus webhook secret and verify capabilities.
67
+ 5. Configure and test the Resend worker and one scheduler with alerting.
68
+ 6. Seed a staging location, service variants, bookable staff, hours, and policy.
69
+ 7. Capture runtime, browser, and ops evidence; run the final release gate.
70
+ 8. Canary one location, monitor conflicts/payments/outbox, then decide whether to
71
+ enable public booking.
72
+
73
+ ## Re-audit commands
74
+
75
+ ```bash
76
+ # ai-cmo-skills
77
+ python3 -m unittest discover -s tools/tests -p 'test_*.py'
78
+ python3 tools/clis/maggie_booking.py tasks validate
79
+
80
+ # MaggieDash
81
+ npm test
82
+ node scripts/validate-booking-service-contract.mjs
83
+ node scripts/validate-booking-host-contract.mjs
84
+
85
+ # ORA host (read-only checks)
86
+ npm run contract:check
87
+ npm run build
88
+ npm run booking:schema -- --check
89
+ ```
90
+
91
+ Do not put Stripe, Resend, database, session, or customer secrets into this
92
+ document or into evidence artifacts.
@@ -0,0 +1,143 @@
1
+ # Booking data flow
2
+
3
+ This document defines the system's data-flow boundaries. The rendered diagrams
4
+ are linked images so they work in GitHub even when Mermaid rich display is
5
+ unavailable.
6
+
7
+ ## Level 0: context
8
+
9
+ ![Booking system context](./diagrams/system-context.svg)
10
+
11
+ [Open Mermaid source](./diagrams/system-context.mmd) · [PNG](./diagrams/system-context.png)
12
+
13
+ External actors exchange normalized commands, facts, and events with the
14
+ Booking application. The browser never talks directly to Stripe with a secret
15
+ or writes the database.
16
+
17
+ ## Level 1: booking DFD
18
+
19
+ ![Booking data-flow diagram](./diagrams/booking-dfd.svg)
20
+
21
+ [Open Mermaid source](./diagrams/booking-dfd.mmd) · [PNG](./diagrams/booking-dfd.png)
22
+
23
+ ### Flow descriptions
24
+
25
+ | Flow | Producer | Consumer | Data | Trust rule |
26
+ |---|---|---|---|---|
27
+ | `availability.query` | booking UI/public page | availability service | service, location, date, preferences | Treat all client values as untrusted; public response uses the customer DTO |
28
+ | `hold.create` | booking UI/public page/manager UI | booking service | candidate allocation, customer, policy version, idempotency key | Recheck availability inside transaction; public route is tenant-bound and rate-limited |
29
+ | `payment.start` | booking service | payment adapter | local payment attempt, amount, currency, return context | Provider receives only normalized payment request |
30
+ | `payment.event` | payment provider | webhook endpoint | signed provider event | Verify signature and deduplicate before projection |
31
+ | `booking.confirmed` | booking transaction | outbox consumers | local booking ID and safe facts | Notifications do not mutate booking truth; public customer intent is allowlisted |
32
+ | `package.grant` | manager dashboard | booking service | customer, package, quantities, expiry, idempotency key | Grant is tenant-scoped and audited; it is not a payment confirmation |
33
+ | `package.redeem` | manager or public customer booking transaction | entitlement ledger | booking, customer-bound matching service variants, quantities | Lock entitlement and item rows; tenant/customer binding and decrement happen in the same transaction as the booking |
34
+ | `package.redeem.reverse` | successful manager or eligible customer cancellation | entitlement ledger | booking, active redemption rows, cancellation reason | Lock the redemption/item rows; restore each quantity once, mark the ledger row reversed, and keep no-show/completed debits |
35
+ | `no_show_fee.charge` | authorized manager/finance action | Stripe adapter and payment repository | no-show booking, policy amount, saved method, explicit confirmation | Require customer consent, saved provider method, verified off-session capability, and idempotency; never charge on status transition |
36
+ | `notification.delivery` | notification worker | email/SMS provider | rendered message and delivery key | Failure is retryable and non-blocking |
37
+ | `audit.append` | command/event transaction | audit store and activity UI | actor, action, entity, reason, correlation ID | Redact secrets and customer-sensitive payloads |
38
+ | `report.project` | reporting worker | manager dashboard | aggregate counts and money totals | Derived data can be rebuilt |
39
+
40
+ The implemented provider-neutral command layer sits between the HTTP adapter
41
+ and the transaction boundary. It normalizes local wall time with an explicit
42
+ IANA timezone/fold, checks every segment against active allocations, then
43
+ returns the booking/hold mutation together with event and audit intent. A host
44
+ must persist that result atomically; the command layer does not bypass the
45
+ database overlap constraints.
46
+
47
+ ## Level 2: paid booking sequence
48
+
49
+ ![Payment sequence](./diagrams/payment-sequence.svg)
50
+
51
+ [Open Mermaid source](./diagrams/payment-sequence.mmd) · [PNG](./diagrams/payment-sequence.png)
52
+
53
+ The sequence has two independent confirmations:
54
+
55
+ 1. The local database confirms the allocation and booking transition.
56
+ 2. The provider confirms the money state through a verified event or a provider
57
+ read.
58
+
59
+ The final local transition joins those facts transactionally. A provider
60
+ redirect only starts reconciliation; it cannot complete the transition alone.
61
+
62
+ An ordinary paid booking may save a method through
63
+ `setup_future_usage=off_session`. A later no-show fee is a separate explicit
64
+ adjustment: the no-show transition produces review state, and only an
65
+ authorized manager/finance command can request the off-session charge. A
66
+ successful charge is persisted and then queues `payment.no_show_fee` through
67
+ the Resend outbox; pending or failed results remain operator-visible.
68
+
69
+ ## State flow
70
+
71
+ ![Booking state machine](./diagrams/booking-state-machine.svg)
72
+
73
+ [Open Mermaid source](./diagrams/booking-state-machine.mmd) · [PNG](./diagrams/booking-state-machine.png)
74
+
75
+ Booking and payment states are separate. A confirmed booking may have a deposit
76
+ paid and a balance due. A cancelled booking may have a refund pending. The UI
77
+ must show both values.
78
+
79
+ ## Event and outbox flow
80
+
81
+ ```text
82
+ Command request
83
+ -> database transaction
84
+ -> booking / payment / allocation rows
85
+ -> booking event
86
+ -> audit event
87
+ -> outbox event
88
+ -> response
89
+
90
+ Outbox worker
91
+ -> notification adapter
92
+ -> provider reconciliation
93
+ -> analytics/read model projection
94
+ -> retry / dead-letter record
95
+ ```
96
+
97
+ The outbox record contains a stable event ID, event type, tenant/project ID,
98
+ aggregate ID, correlation ID, schema version, safe payload, attempt count, next
99
+ attempt time, and processing state. It does not contain card details, provider
100
+ secrets, or raw webhook bodies.
101
+
102
+ ## Consistency rules
103
+
104
+ ### Strong consistency
105
+
106
+ - tenant and location authorization;
107
+ - allocation conflict checks;
108
+ - hold creation and expiry transition;
109
+ - booking lifecycle transition;
110
+ - payment/refund transaction totals;
111
+ - package entitlement balance and redemption ledger;
112
+ - idempotency-key response ownership;
113
+ - audit event creation for a successful material mutation.
114
+
115
+ ### Eventual consistency
116
+
117
+ - email/SMS/push delivery;
118
+ - analytics and attribution projections;
119
+ - daily metrics and utilization reports;
120
+ - provider health snapshots;
121
+ - search/read-model indexes;
122
+ - webhook retry and reconciliation queues.
123
+
124
+ ## Idempotency keys
125
+
126
+ Every external or retryable command has a scope-specific idempotency key:
127
+
128
+ ```text
129
+ tenant:{tenantId}:booking:create:{clientKey}
130
+ tenant:{tenantId}:booking:{bookingId}:payment:{attemptKey}
131
+ tenant:{tenantId}:payment:{paymentId}:refund:{refundKey}
132
+ provider:{providerId}:webhook:{providerEventId}
133
+ ```
134
+
135
+ The key is stored with request hash, response reference, status, and expiry.
136
+ Reusing a key with a different request hash is a conflict, not a new action.
137
+
138
+ ## Privacy boundary
139
+
140
+ Customer contact data is available only to authorized services and roles. The
141
+ activity screen gets a redacted summary. The dashboard never receives raw
142
+ webhook bodies, secret headers, card numbers, CVC, access tokens, or provider
143
+ credentials.