@topy-ai/maggie 0.7.36 → 0.7.37
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 +45 -2
- package/README.md +99 -3
- package/bin/maggie.js +2 -0
- package/bundled-contracts/maggiedash/README.md +3 -0
- package/bundled-contracts/maggiedash/booking-access-v1.json +29 -0
- package/bundled-contracts/maggiedash/booking-customer-surface-v1.json +46 -0
- package/bundled-contracts/maggiedash/booking-email-templates-v1.json +38 -0
- package/bundled-contracts/maggiedash/booking-host-adapter-v1.json +76 -0
- package/bundled-contracts/maggiedash/booking-ops-evidence-v1.json +19 -0
- package/bundled-contracts/maggiedash/execution-board.json +1886 -0
- package/bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json +68 -0
- package/bundled-skills/README.md +1 -0
- package/bundled-skills/catalog.json +4 -0
- package/bundled-skills/maggie-booking/SKILL.md +462 -0
- package/bundled-tools/clis/maggie.py +10 -0
- package/bundled-tools/clis/maggie_booking.py +1080 -0
- package/bundled-tools/clis/maggie_dash.py +181 -5
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "https://maggiedash.topy.ai/contracts/stripe-booking-capabilities-v1.json",
|
|
3
|
+
"schemaVersion": "maggiedash-stripe-booking-capabilities.v1",
|
|
4
|
+
"provider": "stripe",
|
|
5
|
+
"scope": "booking-and-business-operations",
|
|
6
|
+
"x-source": "MaggieDash/contracts/stripe-booking-capabilities-v1.json",
|
|
7
|
+
"x-last-verified-at": "2026-09-20",
|
|
8
|
+
"x-api-version-policy": {
|
|
9
|
+
"accountScoped": true,
|
|
10
|
+
"pinServerVersion": true,
|
|
11
|
+
"upgradeInSandbox": true,
|
|
12
|
+
"avoidChargesForNewFlows": true
|
|
13
|
+
},
|
|
14
|
+
"x-api-matrix": [
|
|
15
|
+
{ "id": "customers", "status": "core", "resource": "Customer", "operations": ["POST /v1/customers", "GET /v1/customers/:id", "POST /v1/customers/:id"], "bookingUse": "customer mapping and receipts", "officialDocs": ["https://docs.stripe.com/api/customers"] },
|
|
16
|
+
{ "id": "checkout_sessions", "status": "core", "resource": "Checkout Session", "operations": ["POST /v1/checkout/sessions", "GET /v1/checkout/sessions/:id", "POST /v1/checkout/sessions/:id/expire"], "bookingUse": "hosted or embedded one-off booking payments", "officialDocs": ["https://docs.stripe.com/api/checkout/sessions"] },
|
|
17
|
+
{ "id": "payment_intents", "status": "core", "resource": "PaymentIntent", "operations": ["POST /v1/payment_intents", "GET /v1/payment_intents/:id", "POST /v1/payment_intents/:id/confirm", "POST /v1/payment_intents/:id/cancel", "POST /v1/payment_intents/:id/capture"], "bookingUse": "custom payment UI, SCA, capture, reconciliation", "officialDocs": ["https://docs.stripe.com/api/payment_intents"] },
|
|
18
|
+
{ "id": "setup_intents", "status": "optional", "resource": "SetupIntent", "operations": ["POST /v1/setup_intents", "GET /v1/setup_intents/:id", "POST /v1/setup_intents/:id/confirm"], "bookingUse": "consented saved payment methods", "officialDocs": ["https://docs.stripe.com/api/setup_intents"] },
|
|
19
|
+
{ "id": "refunds", "status": "core", "resource": "Refund", "operations": ["POST /v1/refunds", "GET /v1/refunds/:id", "POST /v1/refunds/:id/cancel"], "bookingUse": "full and partial refunds", "officialDocs": ["https://docs.stripe.com/api/refunds"] },
|
|
20
|
+
{ "id": "events", "status": "core", "resource": "Event", "operations": ["GET /v1/events/:id", "GET /v1/events"], "bookingUse": "event identity and reconciliation", "officialDocs": ["https://docs.stripe.com/api/events", "https://docs.stripe.com/api/events/types"] },
|
|
21
|
+
{ "id": "promotion_codes", "status": "optional", "resource": "Promotion Code", "operations": ["POST /v1/promotion_codes", "GET /v1/promotion_codes/:id", "GET /v1/promotion_codes"], "bookingUse": "campaign and referral discount codes", "officialDocs": ["https://docs.stripe.com/api/promotion_codes"] },
|
|
22
|
+
{ "id": "coupons", "status": "optional", "resource": "Coupon", "operations": ["POST /v1/coupons", "GET /v1/coupons/:id", "DELETE /v1/coupons/:id"], "bookingUse": "reusable discounts", "officialDocs": ["https://docs.stripe.com/api/coupons"] },
|
|
23
|
+
{ "id": "customer_balance_transactions", "status": "optional", "resource": "Customer Balance Transaction", "operations": ["POST /v1/customers/:id/balance_transactions", "GET /v1/customers/:id/balance_transactions"], "bookingUse": "invoice credit, not gift-card redemption", "officialDocs": ["https://docs.stripe.com/api/customer_balance_transactions"] },
|
|
24
|
+
{ "id": "subscriptions", "status": "future", "resource": "Subscription", "operations": ["POST /v1/subscriptions", "GET /v1/subscriptions/:id", "POST /v1/subscriptions/:id"], "bookingUse": "future memberships", "officialDocs": ["https://docs.stripe.com/api/subscriptions"] },
|
|
25
|
+
{ "id": "invoices", "status": "future", "resource": "Invoice", "operations": ["POST /v1/invoices", "GET /v1/invoices/:id", "POST /v1/invoices/:id/finalize"], "bookingUse": "future business invoicing", "officialDocs": ["https://docs.stripe.com/api/invoices"] },
|
|
26
|
+
{ "id": "customer_portal_sessions", "status": "future", "resource": "Billing Portal Session", "operations": ["POST /v1/billing_portal/sessions"], "bookingUse": "future customer self-service billing", "officialDocs": ["https://docs.stripe.com/api/customer_portal/sessions"] },
|
|
27
|
+
{ "id": "tax", "status": "optional", "resource": "Tax Calculation/Transaction", "operations": ["POST /v1/tax/calculations", "POST /v1/tax/transactions/create_from_calculation", "POST /v1/tax/transactions/create_reversal"], "bookingUse": "optional VAT/tax calculation", "officialDocs": ["https://docs.stripe.com/tax/payment_intent", "https://docs.stripe.com/api/tax/transactions"] },
|
|
28
|
+
{ "id": "terminal", "status": "future", "resource": "Terminal PaymentIntent", "operations": ["POST /v1/terminal/readers", "POST /v1/terminal/readers/:id/process_payment_intent"], "bookingUse": "future in-person payments", "officialDocs": ["https://docs.stripe.com/terminal/payments"] },
|
|
29
|
+
{ "id": "connect", "status": "future", "resource": "Connected Account", "operations": ["POST /v1/accounts", "POST /v1/account_links", "POST /v1/transfers"], "bookingUse": "future multi-merchant platforms only", "officialDocs": ["https://docs.stripe.com/connect"] }
|
|
30
|
+
],
|
|
31
|
+
"x-webhook-events": [
|
|
32
|
+
{ "eventType": "checkout.session.completed", "requiredFor": "hosted checkout completion", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
33
|
+
{ "eventType": "checkout.session.async_payment_succeeded", "requiredFor": "delayed payment completion", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
34
|
+
{ "eventType": "checkout.session.async_payment_failed", "requiredFor": "delayed payment failure", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
35
|
+
{ "eventType": "payment_intent.processing", "requiredFor": "payment pending", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
36
|
+
{ "eventType": "payment_intent.payment_failed", "requiredFor": "payment failure", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
37
|
+
{ "eventType": "payment_intent.succeeded", "requiredFor": "payment confirmation", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
38
|
+
{ "eventType": "payment_intent.requires_action", "requiredFor": "customer action", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
39
|
+
{ "eventType": "payment_intent.canceled", "requiredFor": "payment cancellation", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
40
|
+
{ "eventType": "refund.created", "requiredFor": "refund projection", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
41
|
+
{ "eventType": "refund.updated", "requiredFor": "refund status changes", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
42
|
+
{ "eventType": "refund.failed", "requiredFor": "refund failure", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
43
|
+
{ "eventType": "setup_intent.succeeded", "requiredFor": "saved method setup", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
44
|
+
{ "eventType": "setup_intent.setup_failed", "requiredFor": "saved method failure", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
45
|
+
{ "eventType": "invoice.paid", "requiredFor": "future recurring billing", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
46
|
+
{ "eventType": "invoice.payment_failed", "requiredFor": "future recurring billing failure", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
47
|
+
{ "eventType": "customer.subscription.updated", "requiredFor": "future membership state", "officialDocs": ["https://docs.stripe.com/api/events/types"] },
|
|
48
|
+
{ "eventType": "customer.subscription.deleted", "requiredFor": "future membership cancellation", "officialDocs": ["https://docs.stripe.com/api/events/types"] }
|
|
49
|
+
],
|
|
50
|
+
"x-native-capabilities": {
|
|
51
|
+
"giftCards": "not-native",
|
|
52
|
+
"referrals": "not-native",
|
|
53
|
+
"discountCodes": "native",
|
|
54
|
+
"customerBalanceCredits": "native",
|
|
55
|
+
"subscriptions": "native",
|
|
56
|
+
"tax": "native",
|
|
57
|
+
"terminal": "native",
|
|
58
|
+
"connect": "native"
|
|
59
|
+
},
|
|
60
|
+
"x-ecosystem-integrations": [
|
|
61
|
+
{ "id": "gift-up", "category": "gift-cards", "kind": "stripe-app", "status": "evaluate", "officialDocs": ["https://marketplace.stripe.com/apps/gift-up"], "maggieDecision": "separate partner gift-card system; retain local reference and reconciliation" },
|
|
62
|
+
{ "id": "partnerstack", "category": "affiliate-and-referrals", "kind": "stripe-app", "status": "evaluate", "officialDocs": ["https://marketplace.stripe.com/apps/partnerstack"], "maggieDecision": "affiliate payout candidate; local referral attribution remains authoritative" },
|
|
63
|
+
{ "id": "stripe-apps", "category": "business-operations", "kind": "stripe-product", "status": "recommended", "officialDocs": ["https://stripe.com/apps"], "maggieDecision": "prefer reviewed apps for accounting, CRM, support, analytics, and automation" },
|
|
64
|
+
{ "id": "stripe-tax", "category": "tax", "kind": "stripe-product", "status": "evaluate", "officialDocs": ["https://docs.stripe.com/tax"], "maggieDecision": "enable only after jurisdiction and tax ownership review" },
|
|
65
|
+
{ "id": "stripe-terminal", "category": "in-person-payments", "kind": "stripe-product", "status": "future", "officialDocs": ["https://docs.stripe.com/terminal"], "maggieDecision": "future reader operations" },
|
|
66
|
+
{ "id": "stripe-connect", "category": "multi-merchant", "kind": "stripe-product", "status": "future", "officialDocs": ["https://docs.stripe.com/connect"], "maggieDecision": "future platform-only capability" }
|
|
67
|
+
]
|
|
68
|
+
}
|
package/bundled-skills/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Skills are the agent-facing workflows. They compose with the tools in
|
|
|
6
6
|
| Skill | Primary job | Dependencies |
|
|
7
7
|
|---|---|---|
|
|
8
8
|
| `maggie-blog-bootstrap` | Build a complete blog in an existing project | site audit, analytics, API Pull |
|
|
9
|
+
| `maggie-booking` | Install and validate the MaggieDash manager workspace, public customer-flow boundary, lifecycle, payment, notification, and runtime gates | MaggieDash Booking contracts, host database/provider adapter |
|
|
9
10
|
| `maggie-dash` | Initialize and operate the provider-neutral MaggieDash foundation, content store, and approval lifecycle | MaggieDash contracts, project adapter |
|
|
10
11
|
| `maggie-clone` | Reverse-engineer authorized URLs into namespaced blog-project pages | browser MCP, clone planner CLI, bootstrap state |
|
|
11
12
|
| `maggie-clone-to-template` | Turn an authorized URL and prompt into a validated marketplace package | marketplace CLI, screenshot QA, design contract |
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"name": "maggie-blog-bootstrap",
|
|
15
15
|
"description": "Establish a MaggieDash-backed Astro blog or complete a small SEO/GEO-ready blog in an existing project."
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
"name": "maggie-booking",
|
|
19
|
+
"description": "Plan, install, validate, and release the MaggieDash provider-neutral Booking workspace at _maggie/booking. Use for manager booking operations, availability/hold contracts, payment reconciliation, or integrating a host adapter."
|
|
20
|
+
},
|
|
17
21
|
{
|
|
18
22
|
"name": "maggie-clone",
|
|
19
23
|
"description": "Reverse-engineer an authorized website homepage and create the Maggie homepage foundation inside an existing blog project. Use with /maggie-clone plus a homepage URL when the user wants to replicate the homepage structure, header, footer, assets, responsive behavior, and interactions."
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: maggie-booking
|
|
3
|
+
description: Plan, install, validate, and release the MaggieDash provider-neutral Booking workspace at _maggie/booking. Use for manager booking operations, availability/hold contracts, payment reconciliation, or integrating a host adapter.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Maggie Booking
|
|
7
|
+
|
|
8
|
+
Use this skill for the MaggieDash transactional Booking workspace, not only
|
|
9
|
+
for importing a provider's service catalogue. The existing
|
|
10
|
+
[`maggie-service-booking`](../maggie-service-booking/SKILL.md) skill remains the
|
|
11
|
+
right choice for provider-backed services, prices, durations, booking links,
|
|
12
|
+
and factual provider validation.
|
|
13
|
+
|
|
14
|
+
## Contract and safety boundary
|
|
15
|
+
|
|
16
|
+
Read [`../../docs/maggiedash-booking/README.md`](../../docs/maggiedash-booking/README.md),
|
|
17
|
+
[`TASKS.md`](../../docs/maggiedash-booking/TASKS.md), and the task's linked
|
|
18
|
+
reference before changing code. MaggieDash owns the manager UI and versioned
|
|
19
|
+
contracts. The host owns routes, traditional email/password sessions,
|
|
20
|
+
PostgreSQL connection/transaction lifetime, tenant authorization, provider
|
|
21
|
+
credentials, payment webhooks, notifications, and deployment. MaggieDash also
|
|
22
|
+
ships a tenant-scoped PostgreSQL manager repository for catalogue, availability,
|
|
23
|
+
booking, customer, configuration, schedule, health, audit, hold, and lifecycle
|
|
24
|
+
persistence; it does not replace host connection ownership or payment/public-
|
|
25
|
+
checkout adapters.
|
|
26
|
+
|
|
27
|
+
For implementation work, use the leaf board and runbook:
|
|
28
|
+
[`EXECUTION-BOARD.json`](../../docs/maggiedash-booking/EXECUTION-BOARD.json)
|
|
29
|
+
and [`TASK-RUNBOOK.md`](../../docs/maggiedash-booking/TASK-RUNBOOK.md). Do not
|
|
30
|
+
mark a task passed until its implementation gate, docs update, and evidence
|
|
31
|
+
record are complete.
|
|
32
|
+
|
|
33
|
+
Never put provider secrets, database credentials, payment card data, or a
|
|
34
|
+
customer export in UI props, browser bundles, fixtures, screenshots, or
|
|
35
|
+
feedback. A payment return URL is not proof of payment; only a verified
|
|
36
|
+
provider webhook or server-side provider read may advance payment state.
|
|
37
|
+
|
|
38
|
+
## Stable commands
|
|
39
|
+
|
|
40
|
+
Run read-only checks before any host migration or external mutation:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
maggie booking inspect --project .
|
|
44
|
+
maggie booking contract
|
|
45
|
+
maggie booking runtime-validate --project . \
|
|
46
|
+
--evidence .maggie/booking-runtime.json
|
|
47
|
+
maggie booking release-gate --project . --require-runtime \
|
|
48
|
+
--evidence .maggie/booking-runtime.json
|
|
49
|
+
maggie booking stripe-audit --project .
|
|
50
|
+
maggie booking access-contract
|
|
51
|
+
maggie booking email-templates-contract
|
|
52
|
+
maggie booking tasks validate
|
|
53
|
+
maggie booking tasks list
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The final gate must include every evidence class:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
maggie booking release-gate --project . --require-runtime \
|
|
60
|
+
--evidence .maggie/booking/runtime-evidence.json \
|
|
61
|
+
--require-browser --browser-evidence .maggie/booking/browser/evidence.json \
|
|
62
|
+
--require-ops --ops-evidence .maggie/booking/ops-evidence.json \
|
|
63
|
+
--require-board
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`--require-ops` validates sanitized backup/restore, alert-routing, and
|
|
67
|
+
recovery-rehearsal evidence together with the runtime/browser/board gates.
|
|
68
|
+
It applies the host-only evidence rule; fixture, test, and local environments
|
|
69
|
+
cannot satisfy a required operational release artifact.
|
|
70
|
+
|
|
71
|
+
For local CLI regression, the sanitized no-customer fixture is
|
|
72
|
+
[`fixtures/maggiedash/booking-runtime.v1.json`](../../fixtures/maggiedash/booking-runtime.v1.json).
|
|
73
|
+
|
|
74
|
+
Install the first-party workspace and the host integration through the stable
|
|
75
|
+
Booking installer:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
maggie booking install --project . --confirm
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
For a host project that does not already contain the Booking runtime packages,
|
|
82
|
+
let the installer add only the missing dependencies through the package manager
|
|
83
|
+
already declared by the project (`npm`, `pnpm`, `yarn`, or `bun`):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
maggie booking install --project . --confirm --install-dependencies
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This is an explicit dependency mutation. It preserves existing package
|
|
90
|
+
versions, updates the matching lockfile, and never installs a second copy of a
|
|
91
|
+
package already present in `dependencies` or `devDependencies`.
|
|
92
|
+
|
|
93
|
+
The default installer source is the public MaggieDash distribution. It must
|
|
94
|
+
contain the released Astro `hostBootstrap` manifest and 39-route Booking
|
|
95
|
+
source. While validating an unreleased local checkout, pass
|
|
96
|
+
`--source /home/balalior/Dev/MaggieDash`; do not present a local-source result
|
|
97
|
+
as proof that the public npm install path is released.
|
|
98
|
+
|
|
99
|
+
For a new host with a reviewed `DATABASE_URL`, the explicit one-command
|
|
100
|
+
bootstrap also applies the idempotent Booking schema:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
maggie booking install --project . --confirm --bootstrap
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`--bootstrap` is intentionally opt-in: take the host backup/migration decision
|
|
107
|
+
first. It runs the installed `scripts/maggie-booking-schema.mjs` with
|
|
108
|
+
`--apply --confirm`. When the host uses a local `.env`, it also generates
|
|
109
|
+
`BOOKING_TOKEN_SECRET` when the key is absent. The value is never printed and
|
|
110
|
+
an existing value is never replaced; a configured value shorter than 32
|
|
111
|
+
characters fails closed. External secret stores must provision the same key
|
|
112
|
+
separately. Bootstrap still does not create the owner, configure Stripe, send
|
|
113
|
+
email, or deploy.
|
|
114
|
+
|
|
115
|
+
Before changing host configuration, run the safe first-run checklist:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
maggie booking setup --project .
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The checklist reports whether the installed workspace, Astro middleware, and
|
|
122
|
+
required environment keys are present. It also lists the explicit schema,
|
|
123
|
+
first-owner, Stripe webhook, and Resend-worker steps. It never prints secret
|
|
124
|
+
values, connects to PostgreSQL, applies migrations, creates an account, sends
|
|
125
|
+
email, configures Stripe, or deploys. A `configured-pending-verification`
|
|
126
|
+
result means static configuration is present but host runtime evidence is still
|
|
127
|
+
required. The checklist also rejects a `BOOKING_TOKEN_SECRET` shorter than 32
|
|
128
|
+
characters and marks a one-sided Stripe key pair as degraded, so a
|
|
129
|
+
present-but-unusable secret cannot be mistaken for a ready installation.
|
|
130
|
+
|
|
131
|
+
Validate the installable staff-login boundary independently before touching a
|
|
132
|
+
database or provider:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
maggie booking access-contract
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This checks the versioned access contract for administrator-only account
|
|
139
|
+
creation, email/password invitations through Resend, `manager`/`staff`/
|
|
140
|
+
`finance` roles, granular permissions, password reset, revocation, audit
|
|
141
|
+
actions, tenant scoping, and disabled-account protection. It does not create
|
|
142
|
+
accounts or send email.
|
|
143
|
+
|
|
144
|
+
Validate the tenant-scoped Resend template boundary independently:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
maggie booking email-templates-contract
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
This freezes the seven supported Booking events, approved `{{variable}}`
|
|
151
|
+
placeholders, owner/admin/manager access, audit and idempotency requirements,
|
|
152
|
+
and the server-only provider/recipient privacy boundary. It does not connect to
|
|
153
|
+
Resend, send a message, or expose customer data.
|
|
154
|
+
|
|
155
|
+
After the first owner signs in, the Booking Overview includes the same
|
|
156
|
+
secret-safe readiness path: Stripe connection status, test/live mode, and a
|
|
157
|
+
copyable `/api/maggie/booking/webhooks/stripe` URL. It gives the operator the
|
|
158
|
+
next secret-store action without accepting or storing Stripe credentials in the
|
|
159
|
+
browser or Booking database.
|
|
160
|
+
|
|
161
|
+
For an Astro host, framework detection installs missing email/password login,
|
|
162
|
+
first-owner registration, manager/public Booking routes, PostgreSQL/Stripe
|
|
163
|
+
adapter wiring, the `/_maggie` rewrite, a schema command, Resend worker, and
|
|
164
|
+
`.env.maggie-booking.example`. It also installs the Booking access API: an
|
|
165
|
+
owner or administrator can create email/password staff, manager, and finance
|
|
166
|
+
accounts, link them to staff profiles, and set explicit route capabilities.
|
|
167
|
+
The permissions are enforced in the server route handler, not only hidden in
|
|
168
|
+
the UI. Use `--host astro` to make detection explicit;
|
|
169
|
+
use `--host none` when only the source distribution is wanted. The
|
|
170
|
+
distribution keeps the content admin at `./_maggie/admin` and Booking source at
|
|
171
|
+
`./_maggie/booking`. Existing files, including an existing `src/middleware.ts`,
|
|
172
|
+
are preserved unless `--force` is explicitly supplied. A conventional Astro
|
|
173
|
+
middleware receives the rewrite block automatically; unsupported middleware is
|
|
174
|
+
preserved and reported by `maggie booking inspect` until integrated manually.
|
|
175
|
+
Installation does not apply SQL, create an owner, or enable a payment provider.
|
|
176
|
+
The standard Booking migration must create
|
|
177
|
+
`maggiedash_booking_user_roles`; do not move that table into a project-only
|
|
178
|
+
extension, or a fresh install can authenticate the owner but cannot resolve a
|
|
179
|
+
Booking session.
|
|
180
|
+
|
|
181
|
+
The Schedule screen manages working hours, staff shifts, time off, and location
|
|
182
|
+
closures through the audited schedule contract. Managers can search and edit
|
|
183
|
+
existing rules by stable ID; each write preserves the full schedule snapshot
|
|
184
|
+
and removes only the selected row before applying its replacement, so unrelated
|
|
185
|
+
availability rows cannot be deleted by an ordinary edit.
|
|
186
|
+
|
|
187
|
+
The Catalogue screen supports searchable service/treatment management. Select
|
|
188
|
+
an existing service to edit its stable variant ID, label, duration, price,
|
|
189
|
+
currency, payment policy, or active/inactive state. These writes still go
|
|
190
|
+
through the same idempotent, audited `catalog.json` endpoint; the UI does not
|
|
191
|
+
create replacement records for ordinary edits.
|
|
192
|
+
|
|
193
|
+
Staff and Resources have the same edit-safe workflow. Managers can select an
|
|
194
|
+
existing profile or resource, update assignments, bookable/active state,
|
|
195
|
+
capacity, and identity fields, then save through the existing audited
|
|
196
|
+
`staff.json` or `resources.json` endpoint without changing the record ID.
|
|
197
|
+
|
|
198
|
+
Manager-created bookings use live tenant configuration: the form selects an
|
|
199
|
+
active service variant, location, staff member, or resource, and refuses to
|
|
200
|
+
submit without a staff/resource allocation. Do not reintroduce free-text ID
|
|
201
|
+
fields as the primary path; PostgreSQL booking creation requires the allocation
|
|
202
|
+
to be explicit.
|
|
203
|
+
|
|
204
|
+
The installer must supply those selectors from tenant data, not fixture IDs.
|
|
205
|
+
The manager catalogue response includes active locations, the schedule response
|
|
206
|
+
includes staff-visible locations, new forms default from the first bootstrapped
|
|
207
|
+
location/service, and resource writes persist their service assignments. Keep
|
|
208
|
+
the UI regression gate that rejects `location-1` and `service-1` fallbacks.
|
|
209
|
+
|
|
210
|
+
For a blank project, configure the first location before services, staff,
|
|
211
|
+
resources, or schedules. The Booking Locations screen uses the audited
|
|
212
|
+
`GET/POST /api/maggie/booking/locations.json` boundary to create or edit a
|
|
213
|
+
tenant location, including IANA timezone, locale, address, active state, and
|
|
214
|
+
online-booking availability. Do not require a seeded venue ID or make the host
|
|
215
|
+
invent one in the browser; the location write must remain tenant-scoped,
|
|
216
|
+
permission-checked, idempotent, and validated server-side.
|
|
217
|
+
|
|
218
|
+
A booking can contain up to 12 treatment segments. Each segment carries its
|
|
219
|
+
own service variant, start/end interval, price, currency, and staff/resource
|
|
220
|
+
allocation; all segments must use one location and currency, pass server-side
|
|
221
|
+
availability checks, and persist with their allocations in the same transaction.
|
|
222
|
+
The manager form exposes this with `Add treatment` and `Remove treatment`. A
|
|
223
|
+
direct host request uses `segments: [{ serviceVariantId, startAt, endAt,
|
|
224
|
+
staffId|resourceId, priceMinor, currency }]` and remains idempotent.
|
|
225
|
+
|
|
226
|
+
The public customer page supports the same bounded multi-treatment shape. It
|
|
227
|
+
requests availability for the combined duration, shows the estimated total,
|
|
228
|
+
creates one hold with ordered segments, and converts it into one booking. A
|
|
229
|
+
token-bound customer or manager reschedule shifts every segment and its
|
|
230
|
+
allocation by the same time delta, checks each staff/resource interval, and
|
|
231
|
+
preserves the original total duration. Single-treatment requests and legacy
|
|
232
|
+
rows remain compatible. Public DTOs expose only safe segment fields.
|
|
233
|
+
|
|
234
|
+
Every `staff` account must also be linked to a Booking staff profile. The host
|
|
235
|
+
passes that `staff_id` into the server context, and the PostgreSQL repository
|
|
236
|
+
filters bookings, calendar, availability, customer summaries, staff/resources,
|
|
237
|
+
schedule, and lifecycle mutations by that assignment. An unlinked staff
|
|
238
|
+
session fails closed. This is a data-security rule, not a UI convention.
|
|
239
|
+
The bootstrap now uses the host's server-side Resend configuration to send
|
|
240
|
+
single-use invitation and password-reset links; raw tokens are never stored.
|
|
241
|
+
The Resend worker separately delivers booking notifications. The default
|
|
242
|
+
worker cycle includes `match-waitlist` and `enqueue-reminders`. The waitlist
|
|
243
|
+
matcher orders eligible entries by priority/FIFO, requires explicit service and
|
|
244
|
+
location scope, persists the offered slot, and queues one
|
|
245
|
+
`booking.waitlist.available` notification. It never auto-creates or reserves a
|
|
246
|
+
booking. The reminder job queues one `booking.reminder`
|
|
247
|
+
email for each confirmed booking in the next 24 hours; the existing Resend
|
|
248
|
+
dispatcher resolves the customer address at delivery time. Reminder queueing
|
|
249
|
+
is idempotent, skips bookings without a customer email, and does not itself
|
|
250
|
+
prove delivery until the host worker reports Resend acceptance. Booking
|
|
251
|
+
checkout creates a short-lived payment-return token and a separate 30-day
|
|
252
|
+
sealed customer-manage token. The worker reuses or creates the manage token
|
|
253
|
+
for confirmation, reschedule, cancellation, and reminder emails, and places
|
|
254
|
+
only the safe URL in the email HTML; raw tokens are never written to the
|
|
255
|
+
outbox.
|
|
256
|
+
|
|
257
|
+
The Booking workspace also provides an Email templates screen. Owner,
|
|
258
|
+
administrator, and Booking manager sessions can edit a tenant draft, preview it
|
|
259
|
+
with sandboxed sample data, publish a new audited version, or roll back to a
|
|
260
|
+
prior version. Templates are restricted to the event-specific allowlist and
|
|
261
|
+
values are escaped before the Resend worker renders them. A published template
|
|
262
|
+
is resolved by `project_id` and event; if none exists, the safe provider-neutral
|
|
263
|
+
fallback is used. Resend credentials and recipient addresses remain
|
|
264
|
+
host/worker-only.
|
|
265
|
+
|
|
266
|
+
Payment policy is configured on the service variant and carried onto the
|
|
267
|
+
booking snapshot. The supported modes are `full`, `deposit`, `pay_later`, and
|
|
268
|
+
`no_payment`. Payment attempts must declare `deposit`, `balance`, `full`, or
|
|
269
|
+
`adjustment` purpose. The worker and payment adapter aggregate captured
|
|
270
|
+
attempts plus refunds into paid and balance-due amounts; never infer payment
|
|
271
|
+
truth from a browser redirect or only the latest attempt. A duplicate command
|
|
272
|
+
must replay before current-balance validation.
|
|
273
|
+
|
|
274
|
+
After the setup checklist is clear, the intended first-run path for an already
|
|
275
|
+
installed host is:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
node scripts/maggie-booking-schema.mjs --dry-run
|
|
279
|
+
node scripts/maggie-booking-schema.mjs --apply --confirm
|
|
280
|
+
# open /_maggie/register and create the first owner
|
|
281
|
+
# add Stripe and Resend secrets through the deployment secret store
|
|
282
|
+
node scripts/maggie-booking-worker.mjs --write --confirm
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The dry-run executes the complete additive migration in a transaction and
|
|
286
|
+
rolls it back, which verifies PostgreSQL permissions without changing data.
|
|
287
|
+
The apply command commits the migration as one transaction.
|
|
288
|
+
|
|
289
|
+
The worker should run from a scheduler with a single-flight lock. Do not run
|
|
290
|
+
the schema command or worker against production until the host's backup,
|
|
291
|
+
migration, Stripe, Resend, and release-evidence gates have passed.
|
|
292
|
+
|
|
293
|
+
It also installs the reviewable runtime starter under `maggiedash/backend/booking`,
|
|
294
|
+
the runtime/browser contracts, `maggiedash/ops/`, safe fixtures, and protected
|
|
295
|
+
GitHub workflow templates. Run the local worker in dry-run mode:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npm run booking:worker -- --job all
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
To preview only the reminder decision without sending email:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm run booking:worker -- --job enqueue-reminders
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
To preview only the automatic waitlist matching decision:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
npm run booking:worker -- --job match-waitlist
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The source-level domain and host-boundary checks are also available:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
npm run booking:domain:test
|
|
317
|
+
npm run booking:service:test
|
|
318
|
+
npm run booking:schema:validate
|
|
319
|
+
npm run booking:postgres-core-repository:test
|
|
320
|
+
npm run booking:postgres-manager-methods:test
|
|
321
|
+
npm run booking:postgres-payment-adapter:test
|
|
322
|
+
npm run booking:postgres-backup-restore:integration -- \
|
|
323
|
+
--source-database-url "$DATABASE_URL" \
|
|
324
|
+
--restore-database-url "$RESTORE_DATABASE_URL" \
|
|
325
|
+
--pg-bin-dir "$PG_BIN_DIR" --confirm
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
The backup/restore command requires two explicitly named disposable or
|
|
329
|
+
approved databases and an empty restore database with no public application
|
|
330
|
+
tables. It verifies the PostgreSQL client tools before `pg_dump`: `pg_dump`
|
|
331
|
+
and `pg_restore` must be the same major version and match or exceed both
|
|
332
|
+
server major versions. Use `--pg-bin-dir` when the matching client is not the
|
|
333
|
+
default `PATH`; otherwise a version-mismatch error provides the corrective
|
|
334
|
+
command. It compares protected Booking table counts afterward, never drops
|
|
335
|
+
either database, and rejects a non-empty target before restore.
|
|
336
|
+
|
|
337
|
+
The real PostgreSQL cycle is intentionally not read-only. Run it only against
|
|
338
|
+
an explicitly disposable or approved staging database:
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
npm run booking:postgres-core-repository:integration -- \
|
|
342
|
+
--database-url "$DATABASE_URL" --confirm
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
For local contract/browser development, MaggieDash also provides a fixture-only
|
|
346
|
+
reference host. It is never a production adapter:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
npm run booking:reference-host:test
|
|
350
|
+
npm run booking:reference-host -- --port 4175
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
For production, pass a host module with `loadState`, `saveState`, and
|
|
354
|
+
`dispatchNotification` to `run-booking-jobs.mjs --adapter`; do not point a
|
|
355
|
+
production service at the JSON fixture.
|
|
356
|
+
|
|
357
|
+
## Implementation order
|
|
358
|
+
|
|
359
|
+
Follow the task board rather than building a disconnected calendar first:
|
|
360
|
+
|
|
361
|
+
1. Confirm the host adapter, tenant, role, timezone, error, idempotency, and
|
|
362
|
+
capability decisions.
|
|
363
|
+
2. Run migration preflight; review backup/restore and `btree_gist` privileges.
|
|
364
|
+
3. Install the Booking workspace and expose the host route with the same
|
|
365
|
+
email/password session as the admin workspace.
|
|
366
|
+
4. Compose `createPostgresBookingCoreRepository` for tenant-scoped manager
|
|
367
|
+
reads, configuration writes, and booking/hold/lifecycle writes; run its
|
|
368
|
+
manager-method gate and explicit disposable/staging PostgreSQL integration
|
|
369
|
+
command. Verify staff assignment scope with a linked staff session before
|
|
370
|
+
exposing the dashboard to staff users.
|
|
371
|
+
5. Compose `createPostgresBookingPaymentRepository` with an injected gateway
|
|
372
|
+
for payment start/reconcile/refund persistence. Keep provider calls outside
|
|
373
|
+
its short database transaction; inject verified webhook, public checkout,
|
|
374
|
+
notification, and runtime-evidence adapters separately. The MaggieDash
|
|
375
|
+
repositories must not call provider SDKs or read secrets.
|
|
376
|
+
6. Add the provider-neutral payment port and a test-mode adapter. Reconcile
|
|
377
|
+
signed duplicate/out-of-order webhooks before enabling capture.
|
|
378
|
+
7. Run the UI source gate, contract gate, host runtime conformance, browser
|
|
379
|
+
journeys, and release gate. Record each command in
|
|
380
|
+
[`PROGRESS.md`](../../docs/maggiedash-booking/PROGRESS.md).
|
|
381
|
+
|
|
382
|
+
8. Verify the scheduled worker queues and delivers a reminder once for a
|
|
383
|
+
confirmed booking inside the 24-hour window. Check the notification
|
|
384
|
+
idempotency key, Resend provider response, retry/dead-letter state, and
|
|
385
|
+
absence of duplicate sends before enabling customer reminders.
|
|
386
|
+
|
|
387
|
+
For a production-like staff rollout, verify the account lifecycle end to end:
|
|
388
|
+
Resend invitation email, first-login password setup, password reset, session
|
|
389
|
+
revocation after reset/deactivation, and an authenticated browser test for each
|
|
390
|
+
permission profile. The host still owns delivery retries and production
|
|
391
|
+
evidence.
|
|
392
|
+
|
|
393
|
+
## Runtime evidence
|
|
394
|
+
|
|
395
|
+
`runtime-validate` accepts sanitized observations only. Each endpoint must
|
|
396
|
+
report its contract ID, method, route, 2xx status, and whether required fields
|
|
397
|
+
were present. The validator also requires exact endpoint cardinality, an ISO
|
|
398
|
+
timestamp, a dedicated tenant, and no sensitive evidence keys. Do not include
|
|
399
|
+
request bodies, tokens, customer PII, provider payloads, local paths, or
|
|
400
|
+
database details in evidence.
|
|
401
|
+
|
|
402
|
+
The MaggieDash source package also provides `npm run booking:runtime:plan:test`
|
|
403
|
+
to validate a host runtime plan before credentials are used. It covers all
|
|
404
|
+
contract endpoint IDs, concrete path parameters, required query parameters,
|
|
405
|
+
explicit mutation bodies, and unknown-route rejection.
|
|
406
|
+
|
|
407
|
+
The UI must visibly handle loading, empty, error, permission-denied,
|
|
408
|
+
provider-outage, and capability-disabled states. A static JSON contract is not
|
|
409
|
+
runtime proof; capture browser evidence after the host route and session are
|
|
410
|
+
actually wired.
|
|
411
|
+
|
|
412
|
+
## Customer surface
|
|
413
|
+
|
|
414
|
+
When the product needs customer self-booking, use the separate
|
|
415
|
+
`MaggieDash/contracts/booking-customer-surface-v1.json` contract and
|
|
416
|
+
`backend/booking/customer-route-handler.mjs`. It covers public-noindex
|
|
417
|
+
catalogue/availability, short-lived holds, checkout start, payment-return
|
|
418
|
+
recovery, token-bound customer-manage history, customer cancellation, and
|
|
419
|
+
basic waitlist joining when no suitable slot is available. Use
|
|
420
|
+
`backend/booking/customer-flow.mjs` for redacted public
|
|
421
|
+
DTOs, input validation, bounded payment-return states, and allowlisted
|
|
422
|
+
notification intents.
|
|
423
|
+
|
|
424
|
+
The installed `booking/src/customer.tsx` and
|
|
425
|
+
`maggiedash/adapters/astro/customer-booking-page.astro` provide the optional
|
|
426
|
+
public `/_maggie/booking/book` UI for catalog, availability, hold, checkout,
|
|
427
|
+
pending payment-return recovery, token-bound rescheduling, token-bound
|
|
428
|
+
cancellation, customer-manage history, and waitlist joining when availability
|
|
429
|
+
is empty. Validate it with
|
|
430
|
+
`npm run booking:customer-ui:test`; the host still owns the public route,
|
|
431
|
+
tenant binding, abuse controls, and payment/email adapters.
|
|
432
|
+
|
|
433
|
+
The public handler does not use the manager session. The host must inject a
|
|
434
|
+
tenant-bound context and enforce origin/CSRF, rate-limit, bot, token-codec,
|
|
435
|
+
provider, and notification controls. Compose the installable
|
|
436
|
+
`postgres-customer-adapter.mjs` with the manager/payment repositories when
|
|
437
|
+
PostgreSQL persistence is needed; it stores token hashes, token purpose, and
|
|
438
|
+
sealed ciphertext, never raw bearer tokens. Payment-return tokens are
|
|
439
|
+
short-lived while customer-manage tokens are 30-day links. A redirect is never
|
|
440
|
+
payment proof.
|
|
441
|
+
Run `npm run booking:customer-flow:test` in MaggieDash before wiring the
|
|
442
|
+
adapter, and retain real host/runtime/browser evidence as a separate gate.
|
|
443
|
+
The manager Booking UI also exposes the tenant-scoped waitlist queue. Managers
|
|
444
|
+
can filter entries, notify/cancel/reprioritize them, or select a concrete slot
|
|
445
|
+
and staff/resource allocation to create a real booking transaction linked by
|
|
446
|
+
`convertedBookingId`. The worker also performs bounded priority/FIFO matching
|
|
447
|
+
for explicitly scoped slots and queues a Resend availability notification; the
|
|
448
|
+
customer must still claim the live slot, so it never auto-reserves a booking.
|
|
449
|
+
|
|
450
|
+
For a deterministic end-to-end public journey before host wiring, also run
|
|
451
|
+
`npm run booking:customer-reference-host:test`. This validates the fixture
|
|
452
|
+
reference host only; it is not production tenant, payment, or notification
|
|
453
|
+
evidence.
|
|
454
|
+
The host's idempotency store must compare a normalized request hash: identical
|
|
455
|
+
retries replay the original result, while reusing a key with a different
|
|
456
|
+
payload fails with a conflict and never replays the wrong booking.
|
|
457
|
+
|
|
458
|
+
## Completion
|
|
459
|
+
|
|
460
|
+
A Booking task is complete only when implementation, its gate, docs, and the
|
|
461
|
+
dated progress entry all pass. The release gate is intentionally read-only: it
|
|
462
|
+
does not run migrations, create bookings, capture/refund payments, or deploy.
|
|
@@ -963,6 +963,13 @@ def command_feedback(args: argparse.Namespace) -> int:
|
|
|
963
963
|
return completed.returncode
|
|
964
964
|
|
|
965
965
|
|
|
966
|
+
def command_booking(args: argparse.Namespace) -> int:
|
|
967
|
+
"""Dispatch Booking contract checks through the canonical Booking CLI."""
|
|
968
|
+
cli = Path(__file__).with_name("maggie_booking.py")
|
|
969
|
+
completed = subprocess.run([sys.executable, str(cli), *args.booking_args], cwd=Path.cwd(), check=False)
|
|
970
|
+
return completed.returncode
|
|
971
|
+
|
|
972
|
+
|
|
966
973
|
def parser() -> argparse.ArgumentParser:
|
|
967
974
|
p = argparse.ArgumentParser(prog="maggie", description=__doc__)
|
|
968
975
|
sub = p.add_subparsers(dest="command", required=True)
|
|
@@ -1017,6 +1024,9 @@ def parser() -> argparse.ArgumentParser:
|
|
|
1017
1024
|
feedback = sub.add_parser("feedback", help="collect, review, and submit privacy-safe feedback")
|
|
1018
1025
|
feedback.add_argument("feedback_args", nargs=argparse.REMAINDER, help="arguments for maggie_feedback.py")
|
|
1019
1026
|
feedback.set_defaults(func=command_feedback)
|
|
1027
|
+
booking = sub.add_parser("booking", help="inspect and validate the MaggieDash Booking workspace")
|
|
1028
|
+
booking.add_argument("booking_args", nargs=argparse.REMAINDER, help="arguments for maggie_booking.py")
|
|
1029
|
+
booking.set_defaults(func=command_booking)
|
|
1020
1030
|
doctor = sub.add_parser("doctor", help="check blog routes and SEO output invariants")
|
|
1021
1031
|
doctor.add_argument("project", nargs="?", default=".")
|
|
1022
1032
|
doctor.add_argument("--require-bootstrap", action="store_true")
|