@topy-ai/maggie 0.7.52 → 0.7.53

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 (57) hide show
  1. package/README-zh-TW.md +48 -2
  2. package/README.md +66 -54
  3. package/bin/maggie.js +18 -7
  4. package/bundled-contracts/maggie-assistant-tools/booking-receptionist-example-v1.json +161 -0
  5. package/bundled-contracts/maggie-assistant-tools/tool-registry-v1.schema.json +268 -20
  6. package/bundled-contracts/maggie-deployment/bridge-host-example-v1.json +49 -7
  7. package/bundled-contracts/maggiedash/booking-email-templates-v1.json +181 -13
  8. package/bundled-contracts/maggiedash/booking-rules-v1.json +1699 -0
  9. package/bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json +398 -39
  10. package/bundled-references/maggiedash-booking/PRD.md +22 -11
  11. package/bundled-references/maggiedash-booking/RULES.md +167 -0
  12. package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +42 -4
  13. package/bundled-references/provider-adapter-contract.md +16 -3
  14. package/bundled-skills/catalog.json +1 -1
  15. package/bundled-skills/maggie-assistant-tools/SKILL.md +54 -3
  16. package/bundled-skills/maggie-assistant-tools/references/approvals-and-audit.md +27 -0
  17. package/bundled-skills/maggie-assistant-tools/references/booking-receptionist.md +45 -0
  18. package/bundled-skills/maggie-assistant-tools/references/bridge-host.md +54 -0
  19. package/bundled-skills/maggie-auth-reference/SKILL.md +18 -0
  20. package/bundled-skills/maggie-booking/SKILL.md +47 -3
  21. package/bundled-skills/maggie-deployment/SKILL.md +21 -0
  22. package/bundled-skills/maggie-deployment/references/cloudflare.md +19 -0
  23. package/bundled-skills/maggie-deployment/references/vps.md +92 -0
  24. package/bundled-skills/maggie-design/SKILL.md +17 -3
  25. package/bundled-skills/maggie-design/references/app-ui-patterns.md +79 -0
  26. package/bundled-skills/maggie-feedback/SKILL.md +17 -0
  27. package/bundled-skills/maggie-marketplace/SKILL.md +12 -0
  28. package/bundled-skills/maggie-marketplace/references/directory-search.md +51 -0
  29. package/bundled-skills/maggie-marketplace/references/directory.mjs +112 -0
  30. package/bundled-skills/maggie-mobile-app/SKILL.md +36 -6
  31. package/bundled-skills/maggie-mobile-app/references/payments-and-push.md +78 -0
  32. package/bundled-skills/maggie-qa-workflow/SKILL.md +60 -0
  33. package/bundled-skills/maggie-seo-geo/SKILL.md +33 -0
  34. package/bundled-skills/maggie-service-booking/SKILL.md +28 -3
  35. package/bundled-skills/maggie-social-share/SKILL.md +26 -4
  36. package/bundled-skills/maggie-social-share/references/queue-and-connections.md +28 -0
  37. package/bundled-tools/clis/maggie_assistant_tools.py +84 -3
  38. package/bundled-tools/clis/maggie_booking.py +15 -4
  39. package/bundled-tools/clis/maggie_booking_connect_qa.py +136 -0
  40. package/bundled-tools/clis/maggie_booking_rules.py +476 -0
  41. package/bundled-tools/clis/maggie_bridge_host.py +77 -1
  42. package/bundled-tools/clis/maggie_deployment_ops.py +461 -0
  43. package/bundled-tools/clis/maggie_design.py +9 -2
  44. package/bundled-tools/clis/maggie_design_lint.py +75 -3
  45. package/bundled-tools/clis/maggie_feedback.py +97 -15
  46. package/bundled-tools/clis/maggie_mobile.py +136 -4
  47. package/bundled-tools/clis/maggie_qa_app_preflight.py +122 -4
  48. package/bundled-tools/clis/maggie_qa_fit.py +217 -0
  49. package/bundled-tools/clis/maggie_qa_workflow.py +5 -0
  50. package/bundled-tools/clis/maggie_schema_complexity.py +84 -0
  51. package/bundled-tools/clis/maggie_seo_routes.py +154 -0
  52. package/bundled-tools/clis/maggie_service_migration.py +272 -0
  53. package/bundled-tools/clis/maggie_social_queue.py +2 -0
  54. package/bundled-tools/clis/maggie_social_share.py +52 -6
  55. package/bundled-tools/runtime/assistant_approval.py +98 -0
  56. package/package.json +1 -1
  57. package/references/provider-adapter-contract.md +16 -3
@@ -0,0 +1,167 @@
1
+ # Booking rules and shared test vectors
2
+
3
+ Every host built on the MaggieDash booking model implements these rules the
4
+ same way. Each rule is a pure function with test vectors in
5
+ [`contracts/maggiedash/booking-rules-v1.json`](../../bundled-contracts/maggiedash/booking-rules-v1.json)
6
+ and a reference implementation in `tools/clis/maggie_booking_rules.py`.
7
+
8
+ ```bash
9
+ maggie booking rules-vectors --list # rules, why they exist, vector counts
10
+ maggie booking rules-vectors # reference implementation (must pass)
11
+ maggie booking rules-vectors --host-command "node scripts/booking-rules.mjs"
12
+ ```
13
+
14
+ `--host-command` runs the host's own code: the command reads one line
15
+ `{"rule": "<id>", "input": {...}}` from stdin and prints the result JSON. Only
16
+ the fields a vector lists are compared, so hosts may return more. Add the
17
+ command to CI so a port of the engine cannot drift.
18
+
19
+ Conventions: money is integer minor units; instants are UTC ISO-8601; local
20
+ wall times are interpreted in the venue's IANA time zone.
21
+
22
+ ## Lifecycle
23
+
24
+ **`manage_action`** — what a client's manage link may do. A client cancels
25
+ or reschedules only a `confirmed` booking that has not started; never one
26
+ on hold (`held`, `pending_payment`), arrived or in service, or closed
27
+ (`completed`, `cancelled`, `no_show`, `expired`). A window policy stops
28
+ online changes inside the window ("contact the business"). The business may
29
+ cancel anything not closed but never reschedules a booking in service. Check
30
+ the booking's **own** start time, not only the new slot's.
31
+
32
+ **`late_payment`** — Checkout completed after the hold expired. If the time
33
+ is still free, reinstate the booking as confirmed; otherwise refund the full
34
+ amount on the connected account and tell the client the time was taken. A
35
+ repeated event is a no-op. Never just record the payment: a paid client
36
+ without a booking is the worst outcome. Hosts also need a job that expires
37
+ `pending_payment` bookings whose Checkout session expired, so the slot is
38
+ released.
39
+
40
+ ## Time and money
41
+
42
+ **`schedule_to_utc`** — every schedule input (time off, closures, shifts,
43
+ opening hours) is a local wall time in the **venue's** zone; the browser's
44
+ zone never matters. Send the local value and the location id to the server,
45
+ or convert with the venue zone in the client. A time skipped by the spring
46
+ clock change is rejected; a repeated autumn time uses the first occurrence
47
+ and is flagged. Staff without a location use their default location's zone.
48
+
49
+ **`money_minor`** — node-postgres returns `bigint` as a string. Parse money
50
+ columns to integers in the repository (or set `pg.types.setTypeParser(20,
51
+ …)` when every value fits a safe integer) before any arithmetic or JSON for
52
+ the booking sheet; reject decimals and floats.
53
+
54
+ **`sale_total`** — till maths: item prices × quantity, plus add-ons **only
55
+ when not already included in the booking item price** (a booking item that
56
+ already contains its add-ons must not be charged twice), minus discounts,
57
+ plus a service charge on the discounted amount (rounded half up). Vouchers
58
+ and packages are payments against the total, not discounts. Tips are added
59
+ after the service charge, never commissioned, and split between team members
60
+ by share, handing out remainder pennies one at a time.
61
+
62
+ The checkout around it: receipts for every sale, invoices and quotes with a
63
+ number sequence per business, pay-by-QR (a Checkout link on the client's
64
+ phone for the sale), and Stripe Terminal **server-driven** readers on the
65
+ business's connected account (register by code per branch location; record
66
+ the payment when the PaymentIntent succeeds; test with the simulated reader
67
+ `simulated-wpe`).
68
+
69
+ **`payment_policy_setup`** — deposit or full prepayment needs a connected
70
+ account that can take charges. Setup refuses the policy until then (and
71
+ `maggie booking release-gate` should flag a live service whose policy needs
72
+ payments while they are off), instead of refusing clients at the last step.
73
+ Do not create a `pending_payment` booking before the payment capability is
74
+ confirmed.
75
+
76
+ ## Notifications and calendar
77
+
78
+ **`reminders_due`** — reminder offsets are per business (default 24 h and
79
+ 2 h). Each stage is sent once per booking (store a sent marker per booking
80
+ and stage; dedupe key `<booking>:reminder:<stage>`). A booking made inside a
81
+ stage's window never gets that stage; a stage the worker missed is skipped,
82
+ not sent late.
83
+
84
+ **`notification_plan`** — cancellation and reschedule templates name who
85
+ acted (`by_client`, `by_business`, `by_system`). The business is told when a
86
+ client or the system cancels or reschedules; the client is always told.
87
+
88
+ **`ics_text`** — escape `\`, `;`, `,` and newlines in TEXT values (RFC 5545)
89
+ and fold lines at 75 octets. Attach the `.ics` to the confirmation e-mail
90
+ (`text/calendar; method=PUBLISH`) rather than linking to it. Per-team-member
91
+ calendar feeds use a long random token per person (revocable, rotated on
92
+ role change) and show only that person's bookings, without client contact
93
+ details.
94
+
95
+ ## Availability
96
+
97
+ **`plan_slots`** (reference planner) and **`rota_week`**, **`price_for`**:
98
+
99
+ - opening hours may have several ranges per day (lunch breaks);
100
+ - staff may follow an N-week rota anchored to a Monday;
101
+ - a staff member may have their own duration for a treatment;
102
+ - "anyone available" picks the free person with the fewest bookings that
103
+ day, then sort order;
104
+ - the service time must sit inside opening hours and staff hours and outside
105
+ closures; buffers only keep other bookings away (as MaggieDash `planSlots`);
106
+ - an optional minimum gap rejects starts that would leave a gap shorter than
107
+ it next to another booking;
108
+ - group bookings of 2-6 need that many free people at the same start;
109
+ - slots are on the local grid and stay correct on clock-change days;
110
+ - peak/off-peak price rules adjust the base price by weekday and local time.
111
+
112
+ Multiple locations: run the planner per branch with that branch's hours,
113
+ rooms and staff; a resource (room, bed) is free only when no overlapping
114
+ allocation holds it.
115
+
116
+ ## Team
117
+
118
+ **`permission_check`** — roles `owner`, `manager`, `reception`, `therapist`
119
+ carry named permissions; the owner may grant or revoke them per role, except
120
+ `team.manage` and `billing.manage`, which stay owner-only. "Own bookings
121
+ only" follows from the absence of `bookings.view_all`, never from a role
122
+ name. A **PIN switch** on a shared device can only lower the role
123
+ (owner → therapist), and the effective role applies everywhere: API checks,
124
+ pages, menus and actions such as clock-in — compute it once per request from
125
+ the session and the switched profile, and render navigation from the same
126
+ permission set.
127
+
128
+ **`commission`** and **`hourly_pay`** — commission per person and kind
129
+ (service, product) on net sales after discounts, never on tips or service
130
+ charge; hourly pay from clock-in/out minus unpaid breaks, per minute. A pay
131
+ run combines commission, hourly pay and tips per person for a period and
132
+ exports CSV for accounting tools (columns: period, staff name, staff
133
+ reference, hours, hourly pay, commission, tips, total, currency — map them to
134
+ Xero/QuickBooks payroll or journal imports).
135
+
136
+ ## Clients
137
+
138
+ **`online_booking_gate`** — client safety before online booking: a blocked
139
+ client sees a neutral "please contact us" (never "you are blocked"); a
140
+ treatment with a patch-test category needs a recorded patch test that is not
141
+ older than its validity (for example 180 days) and at least 48 hours before
142
+ the appointment. Health questions and the business's own health items are
143
+ special-category data: store answers per client with consent and a version
144
+ of the form, show them only to staff with `clients.health`, and keep them out
145
+ of exports, analytics and AI prompts unless the client consented.
146
+
147
+ **`merge_clients`** — merging duplicates adds both point balances and
148
+ lifetime points, recomputes the tier, and keeps every history entry and
149
+ membership. **`membership_credits`** — memberships bill monthly on the
150
+ business's connected account; a paused month neither bills nor adds
151
+ credits.
152
+
153
+ **`link_booking_to_account`** — signed-in client accounts sit beside the
154
+ token links: a guest booking joins an account only when the account's e-mail
155
+ is verified and equal (case and spaces ignored). The account lists bookings,
156
+ packages, memberships and saved cards (cards stay on the business's
157
+ connected account; show brand, last four and expiry only). The manage-token
158
+ link keeps working for guests.
159
+
160
+ ## Release smoke business
161
+
162
+ Keep one **hidden production smoke business** per platform so release smoke
163
+ tests can book, reschedule and cancel without touching real businesses:
164
+ excluded from search, sitemaps and directories (`noindex`), pay-at-venue or a
165
+ test-mode connected account only, its own staff and services, and bookings
166
+ cleaned up by the smoke run. `maggie booking release-gate` evidence should
167
+ include one full booking round trip on it after every production deploy.
@@ -44,11 +44,11 @@ and the [API reference](https://docs.stripe.com/api).
44
44
  | Events/webhooks | Core | Idempotent payment state projection | Verify raw body before parsing |
45
45
  | Coupons + Promotion Codes | Optional | Campaign and referral discounts | Stripe applies the discount; local rules own eligibility |
46
46
  | Customer Balance Transactions | Optional | Stripe invoice credits | Not a gift-card ledger by itself |
47
- | Billing Subscriptions + Invoices | Future | Memberships or recurring plans | Add only when recurring booking is a product requirement |
47
+ | Billing Subscriptions + Invoices | Optional | Memberships billed monthly | On a platform, create them on the business's connected account; pause/resume per [RULES.md](./RULES.md) |
48
48
  | Customer Portal | Future | Customer-managed subscriptions and billing details | Use short-lived portal sessions |
49
49
  | Stripe Tax | Optional | VAT/tax calculation and transaction records | Confirm jurisdiction, evidence, and price ownership |
50
- | Terminal | Future | In-person deposits/balances | Requires reader operations and device lifecycle |
51
- | Connect | Future | Multi-merchant/platform money movement | Not for a single business or multi-location tenant |
50
+ | Terminal | Optional | In-person payments at the till | Server-driven readers per branch location; simulated reader in test mode |
51
+ | Connect | Platform | Many businesses on one booking platform | Direct charges on each business's connected account; see "Platform hosts" below. Not needed for one business or a multi-location tenant |
52
52
 
53
53
  The implementation maps Stripe's provider states into the existing normalized
54
54
  states (`requires_action`, `pending`, `failed`, `succeeded`, `cancelled`, and
@@ -105,6 +105,44 @@ accounting, CRM, support, analytics, marketing, and workflow automation. It is
105
105
  an integration surface, not a reason to add third-party credentials to the
106
106
  MaggieDash browser bundle.
107
107
 
108
+ ## Platform hosts (Stripe Connect)
109
+
110
+ A single business, or one business with several locations, uses its own
111
+ Stripe account and needs no Connect. A **platform** that hosts many
112
+ independent businesses (a booking marketplace, a salon software provider)
113
+ takes each business's payments on that business's own account:
114
+
115
+ - **Accounts**: one connected account per business, created and onboarded
116
+ with Accounts v2 (`/v2/core/accounts`, account links). Store the account
117
+ id on the business workspace; never let a client choose it.
118
+ - **Direct charges**: every Checkout Session, PaymentIntent, SetupIntent,
119
+ refund, subscription and Terminal call runs **on the connected account**
120
+ (`Stripe-Account` header / `stripeAccount` option). The business is the
121
+ merchant of record; its customers, saved cards and disputes live there.
122
+ - **Fees**: an application fee is optional and a product decision; "no
123
+ platform fee" is a valid configuration — leave `application_fee_amount`
124
+ unset rather than zero.
125
+ - **Events**: Connect events arrive with `event.account`. Use a Connect
126
+ (thin-event) destination or a Connect webhook endpoint with its own signing
127
+ secret. For every payment event check the **amount, currency and account**
128
+ against the booking before changing state; an event for another account is
129
+ ignored and logged.
130
+ - **Saved cards and off-session charges** (deposits, no-show fees) use
131
+ SetupIntents on the connected account with the client's consent; a card
132
+ saved on one business is never visible to another.
133
+ - **Memberships**: subscriptions on the connected account; pausing uses the
134
+ subscription's pause collection, and credits follow the
135
+ `membership_credits` rule in [RULES.md](./RULES.md).
136
+ - **Payouts and disputes**: read them per account (`GET /v1/payouts`,
137
+ `/v1/disputes` with the account header) for the business's dashboard;
138
+ answering disputes stays in the Stripe Dashboard or an embedded component.
139
+ - **Setup gate**: a business may choose deposit or full prepayment only once
140
+ its account can take charges (`payment_policy_setup` rule); the setup UI
141
+ blocks it and the release gate flags it.
142
+ - **QA**: `maggie booking connect-qa --detect` finds a test-mode connected
143
+ account and prints the `stripe listen --forward-connect-to …` command and
144
+ the payment cases to run.
145
+
108
146
  ## Live setup runbook (read-only verification)
109
147
 
110
148
  Switching a host to live mode is the step most likely to create real objects
@@ -154,7 +192,7 @@ Any host-side live readiness check must likewise issue `GET` requests only.
154
192
  4. Coupons/Promotion Codes for campaigns; add local referral attribution.
155
193
  5. Stripe Tax if the host has the required tax configuration.
156
194
  6. Billing/Portal for memberships; Terminal for in-person operations; Connect
157
- only for a true platform model.
195
+ for a platform that hosts many businesses (see "Platform hosts").
158
196
  7. Gift-card partner or local ledger after accounting and redemption rules are
159
197
  approved. The local-ledger option is fixed by the
160
198
  [voucher lifecycle contract](./VOUCHERS.md).
@@ -173,9 +173,22 @@ rules from a redacted JSON manifest; see the Social Share skill for shapes.
173
173
 
174
174
  ## Structured model output
175
175
 
176
- Keep structured-output schemas small. Gemini rejected a JSON schema whose enum
177
- had 125 values with `400 INVALID_ARGUMENT`, and the caller silently fell back
178
- for every request. For a large vocabulary (styles, categories, catalogue IDs):
176
+ Keep structured-output schemas small. The limit is **schema complexity**, not
177
+ vocabulary size: roughly enum values × repeated array items × nesting.
178
+ Gemini rejected a JSON schema whose enum had 125 values with
179
+ `400 INVALID_ARGUMENT`, and the caller silently fell back for every request.
180
+ It also rejected a plan schema with a 5-6 value `template_key` enum on each
181
+ of up to 20 array items (`maxItems: 20`) — immediately and with no detail —
182
+ while 4 values or `maxItems: 10` passed. So:
183
+
184
+ - never put an `enum` inside repeated array items; describe the allowed
185
+ values in the prompt or the field `description` and validate server-side;
186
+ - keep `maxItems` small on arrays of objects, and always set it;
187
+ - run `maggie tool maggie_social_share.py schema-check schema.json`
188
+ (`tools/clis/maggie_schema_complexity.py`) before a release; it fails on
189
+ the patterns above and reports an estimated complexity score.
190
+
191
+ For a large vocabulary (styles, categories, catalogue IDs):
179
192
 
180
193
  1. ask for a free string (optionally with a short list of examples in the
181
194
  prompt) instead of a large `enum`;
@@ -4,7 +4,7 @@
4
4
  "skills": [
5
5
  {
6
6
  "name": "maggie-assistant-tools",
7
- "description": "Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, approval cards with signed one-time tokens, audit logs, MCP annotations, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts."
7
+ "description": "Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, result cards, approval cards with signed one-time tokens and standing workflow approvals, audit logs, MCP annotations, public discovery MCP and audience binding, visitor-facing receptionists, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts."
8
8
  },
9
9
  {
10
10
  "name": "maggie-auth-reference",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: maggie-assistant-tools
3
- description: Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, approval cards with signed one-time tokens, audit logs, MCP annotations, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts.
3
+ description: Build an AI assistant's tools once and expose them safely to an in-app agent, an MCP server and ChatGPT/Claude apps. Use for tool registries, risk levels and gates, result cards, approval cards with signed one-time tokens and standing workflow approvals, audit logs, MCP annotations, public discovery MCP and audience binding, visitor-facing receptionists, user-facing capability pages, publishing product CLI/MCP packages and app reviews, and multi-tenant assistant bridge hosts.
4
4
  metadata:
5
5
  version: 1.0.0
6
6
  ---
@@ -31,7 +31,9 @@ all read that one definition. Per tool:
31
31
  | `risk` | `read`, `write-reversible`, `write-spends` (credits, quota, AI generation), `write-public` (public or hard to undo), `never` (the assistant must not do it) — `R0`…`W4` are accepted aliases |
32
32
  | `gates` | declared preconditions, e.g. `verified-site`, `credits`, `ai-generation` |
33
33
  | `scope` | `account` or `workspace`; account writes are audited at account scope |
34
- | `channels` | `in-app`, `mcp`, `chatgpt`, `claude`, `cli` |
34
+ | `channels` | `in-app`, `mcp`, `chatgpt`, `claude`, `cli`, `web-widget` (visitors) |
35
+ | `card` | optional result card: `place`, `slot-picker`, `booking`, `voucher`, `change-preview`, `numbers`, `draft`, `list` |
36
+ | `mcpServers` | which declared MCP servers expose the tool (public discovery vs signed-in) |
35
37
 
36
38
  Write user copy by hand. Rewriting model descriptions with regexes ("the
37
39
  user" → "you", dropping `(from connected_apps_list)`) needs overrides for a
@@ -55,6 +57,50 @@ tools are not served on MCP; list them on the capability page instead.
55
57
  you approve" and "publishes or is hard to undo", plus a "can't do" section
56
58
  from `cantDo` entries and `never` tools, using only user copy.
57
59
 
60
+ ## Result cards and conversations
61
+
62
+ A tool may declare a `card`: the app and ChatGPT/Claude widgets render the
63
+ result's structured content as that card (a place, a slot picker that opens
64
+ booking, a change preview before approval, a set of numbers, a draft), and
65
+ every other surface shows the text result, which the tool must still return.
66
+ A `change-preview` card belongs to a write: it is what the approval card
67
+ shows. Keep one card component per type and reuse it across tools.
68
+
69
+ Declare where conversations live with `threads`. Server-stored threads let a
70
+ person continue on another device and let support see a conversation **with
71
+ consent**; they need `retentionDays`, `supportAccess` (`none` or
72
+ `with-consent`) and `deleteOnAccountDeletion: true`. Keep approval tokens out
73
+ of the stored thread text (they live with the model turn, see below).
74
+
75
+ ## MCP servers: public discovery and signed-in
76
+
77
+ Declare servers in `mcpServers`:
78
+
79
+ - **Public discovery** (`auth: "none"`) lets AI assistants and AI search
80
+ answer from published data: search, details, an availability summary and a
81
+ booking link. Read tools only, never account-scoped, and a `rateLimit`
82
+ per IP (per minute and/or per day). Serve only what the public web pages
83
+ already show; no customer data.
84
+ - **Signed-in** (`auth: "oauth"`) binds every token to the server: `resource`
85
+ is the canonical https URL, clients send it as the RFC 8707 resource
86
+ indicator, and the server rejects tokens whose audience is another
87
+ resource. Binding is on by default; turning it off needs an
88
+ `audienceException` with the reason. Publish protected-resource metadata
89
+ (RFC 9728) and check it with `maggie product mcp-check`.
90
+
91
+ ## A visitor-facing receptionist
92
+
93
+ A chat on a business's public page answers strangers, so no approval card is
94
+ possible. Tools on the `web-widget` channel must be read-only and
95
+ workspace-scoped, and the registry needs a `visitorPolicy`: off until the
96
+ business turns it on, daily caps per visitor and per business, every answer
97
+ logged for the business, answers from the business's own facts only, and
98
+ never card details. See the [booking receptionist
99
+ example](../../bundled-contracts/maggie-assistant-tools/booking-receptionist-example-v1.json)
100
+ and [receptionist notes](references/booking-receptionist.md): it finds real
101
+ free times and opens the normal booking sheet; it never books or takes a
102
+ card.
103
+
58
104
  ## Approvals and audit
59
105
 
60
106
  Read [approvals and audit](references/approvals-and-audit.md) before
@@ -62,6 +108,11 @@ implementing writes. In short: a write runs only with a signed, short-lived,
62
108
  single-use approval bound to the exact calls, caller and scope; the token is
63
109
  stored with the model turn on the server so a client answering only yes/no
64
110
  works; every write produces one audit entry with a readable summary.
111
+ Scheduled and event-driven workflows use a **standing approval**: a signed
112
+ grant for one workflow, its tools and per-tool limits (per day, per run, fixed
113
+ arguments, maximum amount) that expires within 90 days and can be revoked;
114
+ calls outside the limits fall back to a normal approval card, and each run is
115
+ audited with channel `workflow` and the grant id.
65
116
 
66
117
  ```bash
67
118
  maggie assistant approval-selftest
@@ -87,7 +138,7 @@ See [bridge host](references/bridge-host.md) and
87
138
  ## Hard boundaries
88
139
 
89
140
  - Never let the model see or choose an approval token; never accept a write
90
- without one.
141
+ without one (or a standing approval whose limits cover the call).
91
142
  - Never expose `never` tools or billing changes on MCP or third-party apps.
92
143
  - Never publish a package or submit an app for review without an explicit
93
144
  yes; these commands only check.
@@ -52,3 +52,30 @@ One entry per write, written by the server after the call:
52
52
  `maggie assistant audit-check` replays a recorded session
53
53
  (`{calls: [{tool, workspace, actionId, ok}], audit: [...]}`) against the
54
54
  registry and fails on missing, duplicate, unreadable or mis-scoped entries.
55
+
56
+ ## Standing approvals for workflows
57
+
58
+ Approvals are single-use per turn, which makes automations ("when someone
59
+ cancels, offer the slot to the waitlist", "ask for a review the day after a
60
+ visit") ask every time. A standing approval is a signed grant the owner gives
61
+ once for one workflow:
62
+
63
+ - **Bound to** the owner, the workspace, the workflow id and a list of tool
64
+ grants. Each grant has `maxPerDay` (required), optional `maxPerRun`,
65
+ `fixedArgs` (for example the e-mail template) and `maxAmountMinor` for
66
+ money.
67
+ - **Expires** within 90 days; the owner renews it from the workflow screen.
68
+ Revocation is a server-side list checked on every run.
69
+ - **Every run** is checked against the grant and the day's usage on the
70
+ server, gets an idempotency key from the grant id and the call, and is
71
+ audited with channel `workflow` and `standingApprovalId`.
72
+ - **Outside the limits** (another tool, a different template, the daily cap
73
+ reached, an amount above the maximum, expired or revoked) the call is not
74
+ run; the workflow queues a normal approval card for the owner instead.
75
+ - Never grant `never` tools; prefer narrow grants (one template, a small
76
+ daily cap) over broad ones.
77
+
78
+ The reference implementation is `issue_standing` / `check_standing` /
79
+ `StandingStore` in `tools/runtime/assistant_approval.py`;
80
+ `maggie assistant approval-selftest` exercises it, and `audit-check` rejects
81
+ workflow audit entries without the grant id.
@@ -0,0 +1,45 @@
1
+ # AI receptionist for a booking page
2
+
3
+ A receptionist answers visitors on one business's public page: "Do you do
4
+ lash lifts?", "How much is a 60-minute massage?", "Anything on Saturday
5
+ morning?". It is a reader and a guide, never an agent that books.
6
+
7
+ ## What it may do
8
+
9
+ - Answer from **this business's facts**: treatments, prices, durations,
10
+ opening hours, address, team names, policies (cancellation, deposits, patch
11
+ tests) and the business's own FAQs. Load them fresh per conversation; never
12
+ answer prices or availability from the model's general knowledge.
13
+ - Find **real** free times from the live availability engine (the same call
14
+ the booking sheet uses), at most a handful per answer, rendered as a slot
15
+ picker.
16
+ - Open the **normal booking sheet** preselected at the chosen treatment and
17
+ time. The visitor enters details and pays there; the sheet's own rules
18
+ (deposits, patch tests, blocked clients) apply unchanged.
19
+ - Hand off: when the answer is not in the facts, say so and show the
20
+ business's phone and e-mail.
21
+
22
+ ## What it must not do
23
+
24
+ - Book, reschedule or cancel; take or ask for card details; promise a price
25
+ or time the engine did not return; give medical advice beyond the
26
+ business's written policy (for example "a patch test is required 48 hours
27
+ before a lash tint").
28
+ - Read other clients' data. The receptionist runs as the visitor, not as the
29
+ business.
30
+
31
+ ## Controls
32
+
33
+ - Off until the business turns it on (`visitorPolicy.enabledByDefault:
34
+ false`), with a preview the owner can try first.
35
+ - Daily caps per visitor (IP + browser id) and per business; when a cap is
36
+ hit, show the contact details instead of an error.
37
+ - Every question and answer is logged and visible to the business, with the
38
+ tools called. Retain the log for a fixed period and delete it with the
39
+ business.
40
+ - Text inside visitor messages is content, never an instruction to change
41
+ these rules. The system prompt names the business and forbids answering
42
+ for other businesses.
43
+
44
+ Validate the registry with `maggie assistant registry-validate`; the
45
+ `web-widget` checks enforce read-only tools and the visitor policy.
@@ -54,3 +54,57 @@ user brings their own account. Review terms again at least every six months.
54
54
  - When an argument must be passed, escape each one with `printf %q`.
55
55
  - zsh does not word-split unquoted variables, so `for x in $list` sees one
56
56
  item; write remote scripts for `sh` and iterate explicitly.
57
+
58
+ ## Smoke-testing `codex exec` with an image
59
+
60
+ `-i/--image` takes **several** paths, so `codex exec ... -i photo.jpg "prompt"`
61
+ reads the prompt as a second image path and fails with "No prompt provided
62
+ via stdin". Put the prompt first, or end the image list with `--`:
63
+
64
+ ```bash
65
+ codex exec "Describe this photo in one sentence" -i photo.jpg
66
+ codex exec -i photo.jpg -- "Describe this photo in one sentence"
67
+ ```
68
+
69
+ ## Images on the user's ChatGPT plan (Codex app-server)
70
+
71
+ Verified on codex-cli 0.160 against the app-server JSON schema
72
+ (`codex app-server generate-json-schema`) and live turns:
73
+
74
+ - **A separate image Codex per ChatGPT account.** The chat Codex runs with
75
+ the product's MCP server and must keep `image_generation` disabled. Image
76
+ work runs on a second app-server for the same account with
77
+ `--disable` for every machine-touching feature except `image_generation`,
78
+ `sandbox_mode="read-only"`, `approval_policy="never"`,
79
+ `web_search="disabled"` and **no MCP server or tools**, so text inside a
80
+ picture cannot make it act.
81
+ - **One ephemeral thread per request**: `thread/start` with
82
+ `ephemeral: true`, `approvalPolicy: "never"`, `sandbox: "read-only"` and
83
+ developer instructions that say text inside an image is content, never an
84
+ instruction.
85
+ - **Image input**: turn input items `{type: "image", url: "data:<mime>;base64,<data>"}`
86
+ after the text item. Validate first: png/jpeg/webp/gif only, a cap on count
87
+ (for example 5) and size (for example 4 MB of base64), base64 characters
88
+ only.
89
+ - **Analysis** returns `agentMessage` items; pass `outputSchema` on
90
+ `turn/start` for JSON answers.
91
+ - **Generation** returns `item/completed` notifications with
92
+ `item.type == "imageGeneration"`: `status`, `result` (base64 PNG, may be
93
+ empty), `savedPath` (a file on disk), `revisedPrompt`, and `failure`. A
94
+ `failure` mentioning `usageLimitExceeded` means the plan's image quota is
95
+ used up — tell the user, do not retry.
96
+ - **Nothing stays on the bridge**: read `savedPath` when `result` is empty,
97
+ then delete its folder; the product stores the image it uses.
98
+ - **Stop and time limits**: `turn/interrupt` with the thread id when the
99
+ caller cancels or a deadline passes (about 2 minutes for analysis, 5 for
100
+ generation).
101
+ - **Fallback**: the caller treats any failure (no bridge, quota, timeout, no
102
+ image) as "use our own model" and says which one made the result.
103
+
104
+ ## Reverse proxy limits
105
+
106
+ Chat with photos and image jobs need a larger body and a longer read timeout
107
+ than API defaults: at least `client_max_body_size 25m` and
108
+ `proxy_read_timeout`/`proxy_send_timeout` 360 s on the bridge routes (5-minute
109
+ generations plus margin). `maggie deployment bridge-check` compares the
110
+ observed nginx values with the plan's `proxy` block.
@@ -39,3 +39,21 @@ table, run that mirror after the primary provider session succeeds. A missing
39
39
  optional table or temporary mirror failure must be a safe, redacted warning and
40
40
  must not reject a valid primary session. Make the mirror blocking only when the
41
41
  host has explicitly documented it as part of the authentication contract.
42
+
43
+ ## External identity providers: Firebase session cookies
44
+
45
+ When the host uses Firebase Authentication with server session cookies,
46
+ `verifySessionCookie(cookie, true)` (`checkRevoked`) compares the cookie's
47
+ `auth_time` with the user's `tokensValidAfterTime`, both in whole seconds. A
48
+ user created (or whose tokens were revoked) in the same second as the cookie
49
+ was minted is treated as revoked: `auth/session-cookie-revoked`, intermittently.
50
+
51
+ - QA fixtures that create a user and immediately sign in must wait about
52
+ 1.5 s after `createUser` (or after `revokeRefreshTokens`) before minting the
53
+ ID token and session cookie.
54
+ - Production sign-up flows hit the same race when they create the account and
55
+ the session in one request; mint the session from the client's fresh ID
56
+ token after creation, and on `auth/session-cookie-revoked` right after
57
+ sign-up retry once after a short delay instead of signing the person out.
58
+ - Keep `checkRevoked: true` on sensitive routes; do not turn it off to hide
59
+ the race.
@@ -76,6 +76,47 @@ recovery-rehearsal evidence together with the runtime/browser/board gates.
76
76
  It applies the host-only evidence rule; fixture, test, and local environments
77
77
  cannot satisfy a required operational release artifact.
78
78
 
79
+ ## Booking rules every host implements the same way
80
+
81
+ [`RULES.md`](../../bundled-references/maggiedash-booking/RULES.md) fixes the rules hosts kept
82
+ rediscovering, each with shared test vectors
83
+ ([`booking-rules-v1.json`](../../bundled-contracts/maggiedash/booking-rules-v1.json)):
84
+ the client manage-link state guard, payment after an expired hold (reinstate
85
+ or refund in full), venue-time schedule inputs, bigint money from Postgres,
86
+ till maths (add-ons already in the item price are not charged twice, service
87
+ charge, vouchers, packages, split tips), deposit policies only with payments
88
+ connected, reminder offsets with once-only markers and who-cancelled
89
+ notices, `.ics` escaping and per-staff feeds, availability (split hours,
90
+ rotas, per-staff durations, any-staff choice, gaps, groups, peak pricing,
91
+ DST), roles and permissions with a PIN switch that only lowers the role and
92
+ applies to pages, menus and actions, client safety (patch tests, blocked
93
+ clients, health data), loyalty and memberships, team pay, and customer
94
+ accounts.
95
+
96
+ ```bash
97
+ maggie booking rules-vectors # reference implementation
98
+ maggie booking rules-vectors --host-command "node scripts/booking-rules.mjs"
99
+ ```
100
+
101
+ Wire `--host-command` to the host's own engine in CI: it reads one
102
+ `{"rule", "input"}` JSON line and prints the result.
103
+
104
+ A platform that hosts many businesses takes payments with Stripe Connect
105
+ direct charges on each business's account (no application fee is a valid
106
+ choice); see [Platform hosts](../../bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md#platform-hosts-stripe-connect).
107
+ Prepare an end-to-end test-mode run with:
108
+
109
+ ```bash
110
+ maggie booking connect-qa --project . --detect
111
+ ```
112
+
113
+ It refuses live keys, finds a test-mode connected account that can take
114
+ charges (read-only), prints the `stripe listen --forward-connect-to …`
115
+ command and lists the payment cases (deposit, 3-D Secure, decline, late
116
+ payment, saved card no-show fee, package, membership, refund, simulated
117
+ reader, dispute). Keep a hidden production smoke business for release smoke
118
+ bookings (RULES.md, "Release smoke business").
119
+
79
120
  For local CLI regression, the sanitized no-customer fixture is
80
121
  [`fixtures/maggiedash/booking-runtime.v1.json`](../../bundled-contracts/maggiedash/booking-runtime.v1.json).
81
122
 
@@ -115,9 +156,12 @@ Use `--schedule systemd` or `--schedule cron` with `--ready` only to generate a
115
156
  reviewable scheduler artifact; enabling it remains an operator action after
116
157
  backup, migration, and release evidence checks.
117
158
 
118
- The default installer source is the public MaggieDash `0.2.8` distribution. It
119
- contains the released Astro `hostBootstrap` manifest and 47-route Booking
120
- source, including the tenant-scoped treatment package catalogue. A fresh public-source install, `maggie booking inspect`, and
159
+ The default installer source is MaggieDash `main` (currently `0.2.13`; pin
160
+ another release with `--ref`). It contains the released Astro `hostBootstrap`
161
+ manifest and 47-route Booking source, including the tenant-scoped treatment
162
+ package catalogue, and follows the booking rules above (manage-link state
163
+ guard, late-payment reinstate/refund, reminder stages, who-cancelled and
164
+ business notices, `.ics` attachments, permission-based own-bookings scope). A fresh public-source install, `maggie booking inspect`, and
121
165
  `astro build` are the release evidence; a local-source result alone is not.
122
166
 
123
167
  For a new host with a reviewed `DATABASE_URL`, the explicit one-command
@@ -17,6 +17,11 @@ metadata:
17
17
  - A multi-tenant assistant bridge host on a VPS is declared and checked with
18
18
  `maggie deployment bridge-plan`, `bridge-collect-script` and `bridge-check`;
19
19
  see [bridge host](../maggie-assistant-tools/references/bridge-host.md).
20
+ Declare `workloads` (`chat`, `photos`, `image-generation`) and
21
+ `network.proxyLimits`; `bridge-check` reads the effective nginx
22
+ `client_max_body_size` / `proxy_read_timeout` of the server block that
23
+ proxies to the bridge port and fails below 25m / 360 s for photos and image
24
+ jobs (a 512k / 300 s proxy cut them off).
20
25
  - Product CLI and MCP packages: `maggie product release-check|smoke|mcp-check`
21
26
  ([product release](../maggie-assistant-tools/references/product-release.md)).
22
27
 
@@ -562,6 +567,12 @@ is on a request path; follow [cloudflare.md](references/cloudflare.md) to move
562
567
  the database to D1 and media to R2/Cloudflare Images or an explicitly selected
563
568
  external adapter.
564
569
 
570
+ Run `maggie deployment cloudflare-preflight --env production --env-file .env
571
+ --verify-zone` before every `wrangler deploy`: an empty
572
+ `CLOUDFLARE_API_TOKEN`/`CLOUDFLARE_ACCOUNT_ID` makes wrangler fall back to its
573
+ cached OAuth login, possibly another account, and upload a stray Worker
574
+ there.
575
+
565
576
  ## VPS target
566
577
 
567
578
  Use `--target vps` for direct VPS hosting or
@@ -571,6 +582,16 @@ immutable releases, the `current` symlink, staging-first rollout, HTTPS
571
582
  verification, and rollback. Remote checks remain pending until the host,
572
583
  credentials, and explicit deployment confirmation are provided.
573
584
 
585
+ For an SSR Node app with release folders, `maggie deployment vps-release`
586
+ runs the whole release as one reviewed command (dry run by default): archive
587
+ of a commit, install and build as the deploy user with the server env file,
588
+ atomic switch, restart, health with warm-up, prune, roadmap line. Before a
589
+ surgical copy to several hosts, `maggie deployment file-parity` proves every
590
+ host still holds the deployed commit's files; `maggie deployment warm-check`
591
+ judges latency only after warm-up requests. Failover between hosts must
592
+ never replay a write: see "Failover must not replay writes" in
593
+ [vps.md](references/vps.md).
594
+
574
595
  ## Provider boundary
575
596
 
576
597
  Deployment providers implement the same conceptual operations: