@topy-ai/maggie 0.7.47 → 0.7.49

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 (91) hide show
  1. package/README-zh-TW.md +2 -2
  2. package/README.md +76 -15
  3. package/bin/maggie.js +23 -6
  4. package/bundled-contracts/google-integrations/capability-report-v2.schema.json +2 -1
  5. package/bundled-contracts/maggie-content/email-localization-v1.schema.json +89 -0
  6. package/bundled-contracts/maggie-content/lifecycle-email-v1.schema.json +76 -0
  7. package/bundled-contracts/maggie-deployment/readiness-v1.schema.json +6 -1
  8. package/bundled-contracts/maggie-design/palette-roles-v1.schema.json +47 -0
  9. package/bundled-contracts/maggie-marketplace/template-content-v1.schema.json +103 -0
  10. package/bundled-contracts/maggie-ops/marketing-run-v1.schema.json +89 -0
  11. package/bundled-contracts/maggie-ops/notification-catalogue-example-v1.json +941 -0
  12. package/bundled-contracts/maggie-ops/notification-catalogue-v1.schema.json +273 -0
  13. package/bundled-contracts/maggie-service-booking/fact-provenance-v1.schema.json +128 -0
  14. package/bundled-contracts/maggie-service-booking/import-stages-v1.schema.json +191 -0
  15. package/bundled-contracts/maggiedash/booking-browser-evidence-v1.json +21 -0
  16. package/bundled-contracts/maggiedash/booking-browser-evidence-v2.json +191 -0
  17. package/bundled-contracts/maggiedash/booking-email-templates-v1.json +3 -1
  18. package/bundled-contracts/maggiedash/booking-voucher-lifecycle-v1.json +85 -0
  19. package/bundled-contracts/maggiedash/execution-board.json +4629 -1553
  20. package/bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json +1 -0
  21. package/bundled-marketplace/README.md +17 -0
  22. package/bundled-marketplace/catalog.json +56 -7
  23. package/bundled-references/browser-inspection.md +25 -0
  24. package/bundled-references/google-integrations-runbook.md +54 -0
  25. package/bundled-references/maggiedash-booking/CURRENT-STATE.md +128 -10
  26. package/bundled-references/maggiedash-booking/EXECUTION-BOARD.json +4629 -1553
  27. package/bundled-references/maggiedash-booking/HOST-ADAPTER.md +5 -0
  28. package/bundled-references/maggiedash-booking/ORAWELLNESS-INTEGRATION-AUDIT.md +64 -21
  29. package/bundled-references/maggiedash-booking/PRD.md +9 -1
  30. package/bundled-references/maggiedash-booking/PROGRESS.md +244 -2399
  31. package/bundled-references/maggiedash-booking/QA-TEST-PLAN.md +37 -6
  32. package/bundled-references/maggiedash-booking/README.md +23 -6
  33. package/bundled-references/maggiedash-booking/RUNTIME-OPERATIONS.md +64 -8
  34. package/bundled-references/maggiedash-booking/SKILLS-AND-CLI.md +11 -2
  35. package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +66 -2
  36. package/bundled-references/maggiedash-booking/TASK-RUNBOOK.md +1 -1
  37. package/bundled-references/maggiedash-booking/TASKS.md +44 -3
  38. package/bundled-references/maggiedash-booking/VOUCHERS.md +47 -0
  39. package/bundled-references/provider-adapter-contract.md +33 -0
  40. package/bundled-skills/maggie-booking/SKILL.md +8 -0
  41. package/bundled-skills/maggie-clone-to-template/SKILL.md +26 -0
  42. package/bundled-skills/maggie-content-localization/SKILL.md +17 -1
  43. package/bundled-skills/maggie-content-localization/references/email-and-lifecycle.md +83 -0
  44. package/bundled-skills/maggie-deployment/SKILL.md +141 -7
  45. package/bundled-skills/maggie-deployment/references/vps.md +74 -1
  46. package/bundled-skills/maggie-design/SKILL.md +154 -0
  47. package/bundled-skills/maggie-feedback/SKILL.md +67 -1
  48. package/bundled-skills/maggie-marketplace/SKILL.md +91 -0
  49. package/bundled-skills/maggie-ops/SKILL.md +37 -0
  50. package/bundled-skills/maggie-ops/references/notifications-operations.md +154 -0
  51. package/bundled-skills/maggie-qa-workflow/SKILL.md +41 -1
  52. package/bundled-skills/maggie-service-booking/SKILL.md +82 -0
  53. package/bundled-skills/maggie-social-share/SKILL.md +56 -7
  54. package/bundled-skills/maggie-social-share/references/marketing-run.md +57 -0
  55. package/bundled-skills/maggie-social-share/references/queue-and-connections.md +152 -0
  56. package/bundled-skills/maggie-template/SKILL.md +57 -3
  57. package/bundled-skills/maggie-template/references/template-content-v1.example.json +34 -0
  58. package/bundled-tools/clis/maggie_booking.py +199 -26
  59. package/bundled-tools/clis/maggie_booking_vouchers.py +185 -0
  60. package/bundled-tools/clis/maggie_clone_to_template.py +14 -0
  61. package/bundled-tools/clis/maggie_deployment.py +307 -18
  62. package/bundled-tools/clis/maggie_deployment_auth_canary.py +216 -0
  63. package/bundled-tools/clis/maggie_deployment_collect.py +225 -0
  64. package/bundled-tools/clis/maggie_deployment_readiness.py +53 -3
  65. package/bundled-tools/clis/maggie_design.py +17 -0
  66. package/bundled-tools/clis/maggie_design_lint.py +277 -0
  67. package/bundled-tools/clis/maggie_design_verify.py +359 -0
  68. package/bundled-tools/clis/maggie_docs.py +82 -4
  69. package/bundled-tools/clis/maggie_email_localization.py +382 -0
  70. package/bundled-tools/clis/maggie_feedback.py +115 -5
  71. package/bundled-tools/clis/maggie_icon_glyph_check.py +382 -0
  72. package/bundled-tools/clis/maggie_icon_release_gate.py +28 -1
  73. package/bundled-tools/clis/maggie_localization.py +5 -0
  74. package/bundled-tools/clis/maggie_marketing_run.py +272 -0
  75. package/bundled-tools/clis/maggie_marketplace.py +199 -5
  76. package/bundled-tools/clis/maggie_notifications.py +528 -0
  77. package/bundled-tools/clis/maggie_ops.py +87 -0
  78. package/bundled-tools/clis/maggie_palette.py +280 -0
  79. package/bundled-tools/clis/maggie_qa_workflow.py +156 -1
  80. package/bundled-tools/clis/maggie_schedule.py +12 -2
  81. package/bundled-tools/clis/maggie_service_import.py +520 -0
  82. package/bundled-tools/clis/maggie_social_queue.py +442 -0
  83. package/bundled-tools/clis/maggie_social_share.py +46 -9
  84. package/bundled-tools/clis/maggie_template_package.py +1141 -0
  85. package/bundled-tools/runtime/google_ads.py +306 -0
  86. package/bundled-tools/runtime/google_capabilities.py +18 -2
  87. package/bundled-tools/runtime/google_remediation.py +152 -0
  88. package/package.json +1 -1
  89. package/references/browser-inspection.md +25 -0
  90. package/references/google-integrations-runbook.md +54 -0
  91. package/references/provider-adapter-contract.md +33 -0
@@ -0,0 +1,57 @@
1
+ # Marketing runs, schedule layers, and regulated-content review
2
+
3
+ A marketing run joins content, Social Share, Ads, and measurement under one
4
+ project-scoped `runId`. It is a reference record: it points at source
5
+ revisions, capability evidence, approvals, mutations, and readbacks by ID and
6
+ never stores provider secrets or large payloads.
7
+
8
+ Schema: `contracts/maggie-ops/marketing-run-v1.schema.json`
9
+ (`schemaVersion: maggie-marketing-run-v1`). Validate with:
10
+
11
+ ```bash
12
+ maggie tool maggie_marketing_run.py validate .maggie/marketing-runs/<runId>.json
13
+ maggie tool maggie_marketing_run.py layers # print the layer model
14
+ ```
15
+
16
+ ## Run fields
17
+
18
+ | Field | Purpose |
19
+ |---|---|
20
+ | `runId`, `projectRef` | One project-scoped correlation ID across every channel. |
21
+ | `objective`, `conversion` | What the run is for and which event (and source) proves it. |
22
+ | `channels`, `timezone`, `mode` | Channels involved, the run's IANA zone, and `draft`/`scheduled`/`executing`/`completed`/`cancelled`. |
23
+ | `sourceRevisions` | `{sourceId, revision}` of every content source used. |
24
+ | `capabilityEvidence` | IDs/paths of capability reports (for example a Google capability report). |
25
+ | `approval` | `state`, `actor`, `approvalId`; external mutations require `approved`. |
26
+ | `scheduleLayers` | Named layers (below); a run never carries a single `scheduled_at`. |
27
+ | `mutations` | `{mutationId, kind, channel, status, idempotencyKey, approvalRef}`; external publication and Ads changes need an idempotency key. |
28
+ | `readbacks` | `{readbackId, mutationRef}`; a succeeded mutation needs one before the run is `completed`. |
29
+ | `policy`, `stages` | Regulated-content review (below). |
30
+
31
+ ## Schedule layers
32
+
33
+ | Layer | Meaning | Owner | Notes |
34
+ |---|---|---|---|
35
+ | `infrastructure` | When a worker wakes up (cron, systemd timer, queue). | Operations | Points at a `maggie_schedule.py` job via `scheduleRef`; never a publish time. |
36
+ | `content_calendar` | When reviewed content or social posts may publish. | Editor/marketer | Social Share's slot queue; `start` required. |
37
+ | `campaign_flight` | When paid campaigns may run and spend. | Ads owner | `start` and `end` required. |
38
+ | `measurement_window` | Collection and attribution period. | Analytics | `start`, `end`, and `lagDays` for conversion lag. |
39
+
40
+ Every layer declares `timezone`, `owner`, `status`, and a `reconciliation`
41
+ rule describing how its real state is read back. `maggie_schedule.py` jobs are
42
+ always the `infrastructure` layer and reject publish-time fields.
43
+
44
+ ## Regulated-content review
45
+
46
+ `policy.regulatedContent` is off by default, so existing runs are unchanged.
47
+ When it is on, only the **external publication** action waits for
48
+ `policy.reviewState: approved`. Content delivery and pull (including legacy
49
+ plugins), draft creation, SEO/GEO work, and measurement continue; policy
50
+ labels are additive metadata, never a gate in the delivery path.
51
+
52
+ The validator fails when:
53
+
54
+ - a stage other than `external_publication` is `blocked` with reason
55
+ `policy_review`;
56
+ - an `external_publication` mutation is pending or succeeded before the review
57
+ is approved.
@@ -0,0 +1,152 @@
1
+ # Social Share queue, campaigns, and connections
2
+
3
+ All commands read one redacted JSON document, make no provider or model calls,
4
+ and print a JSON report. Validation commands exit `1` when the contract fails.
5
+
6
+ ## Adapter scope contract — `adapters`
7
+
8
+ ```json
9
+ {
10
+ "adapters": [{
11
+ "provider": "linkedin",
12
+ "canonical_scopes": ["openid", "profile", "email", "w_member_social"],
13
+ "client_id_ref": "linkedin-app-main",
14
+ "connect_paths": [
15
+ {"id": "dashboard", "scopes": ["openid", "profile", "email", "w_member_social"]},
16
+ {"id": "mobile", "scopes": ["openid", "profile", "email", "w_member_social"]}
17
+ ],
18
+ "redirect_uris": ["https://app.example.com/oauth/linkedin/callback"],
19
+ "client_shared_with": []
20
+ }]
21
+ }
22
+ ```
23
+
24
+ - Fails when any connect path requests a scope set different from
25
+ `canonical_scopes` (order is ignored). LinkedIn revokes a member's earlier
26
+ tokens when a sign-in requests a different scope set.
27
+ - Fails when secret fields (`client_secret`, tokens) appear in the manifest.
28
+ `client_id_ref` is a label, not the credential.
29
+ - Warns when the OAuth client is shared with other products, or redirect URIs
30
+ span several hosts without a `client_shared_with` record.
31
+
32
+ ## Connection health — `connections`
33
+
34
+ ```json
35
+ {"connections": [{
36
+ "id": "conn-1", "provider": "linkedin", "status": "active",
37
+ "token_decryptable": true,
38
+ "provider_check": {"status": "valid", "checked_at": "2026-10-10T08:00:00Z"},
39
+ "avatar_url": "https://media.licdn.com/dms/image/...?...&e=1790000000",
40
+ "avatar_cached": false, "avatar_refresh_on_read": false,
41
+ "scheduled_items": 4
42
+ }]}
43
+ ```
44
+
45
+ - `status` is derived: `needs_reconnect` when the token cannot be decrypted on
46
+ the serving host or the provider reports it `revoked`/`invalid`/`expired`.
47
+ A stored `active` that should be `needs_reconnect` fails, and the report
48
+ counts scheduled items that would otherwise fail at publish time.
49
+ - Run the decrypt check on every host that serves social routes; a missing
50
+ encryption key on a failover host looks exactly like an undecryptable token.
51
+ - A signed, expiring avatar URL (LinkedIn `e=`, Meta `oe=`, S3/GCS signed
52
+ URLs) fails unless `avatar_cached` (bytes in owned storage) or
53
+ `avatar_refresh_on_read` is true. Use `--now` for deterministic checks.
54
+
55
+ ## Campaign idea stage — `campaigns`
56
+
57
+ ```text
58
+ idea (planned, no copy) --write (1 credit)--> written (draft_id) --all written + start_day + posts_per_day--> Start
59
+ ```
60
+
61
+ - Ideas are visible and manageable on every client (dashboard and mobile), not
62
+ only where they were planned. Written drafts keep their `campaign_id`.
63
+ - A campaign can start only when every idea is `written` with a `draft_id`,
64
+ orders are unique integers, `start_day` is a date, and `posts_per_day >= 1`.
65
+ The report lists blockers; a `started` campaign with blockers fails.
66
+
67
+ ## Slot-based posting queue — `queue`
68
+
69
+ Posts do not carry a fixed publish time. The person's posting hours are slots:
70
+
71
+ ```json
72
+ {
73
+ "timezone": "Europe/London",
74
+ "posting_hours": {"mon": ["09:00", "17:00"], "wed": ["12:30"]},
75
+ "queue": [{"id": "post-7", "queued_at": "2026-10-09T10:00:00Z", "not_before": null}],
76
+ "campaigns": [{"id": "autumn", "status": "started", "start_day": "2026-10-12", "posts_per_day": 1,
77
+ "ideas": [{"id": "i1", "order": 1, "status": "written", "draft_id": "post-31"}],
78
+ "posted_idea_ids": []}]
79
+ }
80
+ ```
81
+
82
+ ```bash
83
+ maggie tool maggie_social_share.py queue queue.json --from 2026-10-12T00:00:00Z --slots 14
84
+ ```
85
+
86
+ Rules the planner applies, in order:
87
+
88
+ 1. Each slot (local wall time in `timezone`) posts at most one item.
89
+ 2. The standalone queue is newest-first: a new post goes to the front.
90
+ 3. A stored time is `not_before` only. An overdue item is simply eligible; it
91
+ is never stranded behind a past `scheduled_at`.
92
+ 4. A started campaign publishes written ideas in idea order from `start_day`,
93
+ at most `posts_per_day` per local day.
94
+ 5. The standalone queue and each started campaign take turns (round-robin);
95
+ a source with nothing eligible is skipped for that slot.
96
+ 6. Wall times keep their local meaning across DST. A time that does not exist
97
+ (spring-forward gap) is skipped and reported; an ambiguous time uses its
98
+ first occurrence.
99
+
100
+ The plan is advisory output for the host scheduler, which re-plans after every
101
+ publish, approval, or queue edit instead of persisting stale per-post times.
102
+
103
+ ## Publish contract — `adapters` with `"publishes": true`
104
+
105
+ An adapter that publishes must declare, in addition to its scope set:
106
+
107
+ | Field | Meaning |
108
+ |---|---|
109
+ | `limits.text_max` | Provider text limit used by `validate`. |
110
+ | `media.types`, `media.max_bytes` | Accepted media and size budget. |
111
+ | `pending.states`, `pending.max_pending_seconds` | Provider "processing" states and how long a post may stay there before it is treated as failed. |
112
+ | `retry_classes.retryable` / `.terminal` / `.unknown_outcome` | Provider error classes. A timeout is always `unknown_outcome`: the post may already exist. |
113
+ | `idempotency.key_source`, `idempotency.provider_supported` | How the stable key is derived (for example `queue_item_id+revision`) and whether the provider honours it. |
114
+ | `readback.lookup` | `by_idempotency_key` (only when the provider supports keys), `by_marker` (key embedded in the post), or `by_recent_posts`. |
115
+
116
+ Adapters without `publishes` keep the scope-only shape.
117
+
118
+ ## Publish reconciliation — `reconcile`
119
+
120
+ `reconcile` reads the publish-attempt log and a provider readback and returns
121
+ one safe action per queue item. It never publishes.
122
+
123
+ ```json
124
+ {
125
+ "attempts": [{"queue_item_id": "q1", "idempotency_key": "q1:r3", "status": "timeout"}],
126
+ "readback": {"complete": true, "posts": [{"id": "urn:li:share:1", "marker": "#mg-q1:r3"}]}
127
+ }
128
+ ```
129
+
130
+ ```bash
131
+ maggie tool maggie_social_share.py reconcile attempts.json
132
+ ```
133
+
134
+ | Situation | Action |
135
+ |---|---|
136
+ | A readback post carries the item's key or marker | `adopt` it as the published post (timeout-after-success). |
137
+ | An attempt reported a post ID that readback does not show | `verify_readback` before anything else. |
138
+ | Last outcome `timeout`/`pending`/`unknown`, readback incomplete | `await_readback`; no retry. |
139
+ | Same, readback complete and no match | `retry_same_key`. |
140
+ | `failed_retryable` | `retry_same_key`. |
141
+ | `failed_terminal` | `fail`. |
142
+ | Two provider posts for one item, or retries that changed the key | invalid; `resolve_duplicate` (keep the first, delete extras). |
143
+
144
+ Immediate and scheduled publishing share this one path, so a retry can never
145
+ create a second post.
146
+
147
+ ## Marketing runs and schedule layers
148
+
149
+ Social work that belongs to a wider campaign is recorded in a
150
+ [marketing run](marketing-run.md) (`maggie-marketing-run-v1`). The slot queue
151
+ above is the `content_calendar` layer; it is never the infrastructure timer
152
+ that wakes the publish worker.
@@ -28,9 +28,10 @@ maggie tool maggie_marketplace.py apply \
28
28
  --id medical-spa-landing-001 --project /path/to/astro-project
29
29
  ```
30
30
 
31
- `list` shows ID, lifecycle status, industries, and style tags. `suggest`
32
- matches the user's brief against those fields and ranks published entries
33
- first. `inspect` returns metadata and available artifacts. `apply` validates
31
+ `list` shows ID, the neutral `displayName`, lifecycle status, industries, and
32
+ style tags. Show customers the `displayName` and `displayDescription`, never the
33
+ source title in `sourceMetadata`. `suggest` matches the user's brief against
34
+ those fields and ranks published entries first. `inspect` returns metadata and available artifacts. `apply` validates
34
35
  the template, writes a namespaced reference to `.maggie/marketplace/<id>/`,
35
36
  copies the homepage, assets, screenshots, and `DESIGN.md`, and records
36
37
  `.maggie/template-selection.json`. It does not silently overwrite application
@@ -39,6 +40,59 @@ routes.
39
40
  Draft entries are for review only. Use `--allow-draft` only when the user
40
41
  explicitly wants to preview an unpublished template.
41
42
 
43
+ ## Preview studio
44
+
45
+ Compare a template across viewports and palettes before applying it:
46
+
47
+ ```bash
48
+ maggie tool maggie_marketplace.py measure-design <id> --palette-roles /tmp/<id>-palettes.json
49
+ maggie tool maggie_design.py palette generate --roles /tmp/<id>-palettes.json \
50
+ --template <id> --output /tmp/<id>-palettes.json # optional: contrast-safe variants
51
+ maggie tool maggie_marketplace.py studio <id> --palettes /tmp/<id>-palettes.json
52
+ maggie tool maggie_marketplace.py studio <id> --palettes /tmp/<id>-palettes.json --check-overflow [--palette <variant-id>]
53
+ ```
54
+
55
+ `studio` writes `.maggie/template-studio/<id>/index.html` under the
56
+ marketplace root: the homepage in scaled frames at 390, 768, 1024, and
57
+ 1440 px, a palette dropdown fed by a `maggie-palette-roles-v1` file (the
58
+ measured signature when none is given), and labels on sample copy. Each label
59
+ links to `content-fields.html`, which names the `maggie-template-content-v1`
60
+ field and where the owner replaces it. Elements marked
61
+ `data-maggie-sample="hero.headline"` are labelled exactly; otherwise headings,
62
+ contact links, and section headings are matched heuristically. A palette is
63
+ applied by swapping the signature role colours for the variant's and by
64
+ setting `--maggie-<role>` CSS variables. Open `#<palette-id>` to link a variant.
65
+
66
+ Serve the marketplace root over HTTP (`python3 -m http.server`) to view it.
67
+ `--check-overflow` serves it locally, renders it with headless Chrome, writes
68
+ `overflow-report.json`, and exits non-zero when any viewport has horizontal
69
+ overflow, naming up to five offending elements. Templates that load Tailwind or
70
+ fonts from a CDN need network access for a faithful render.
71
+
72
+ ## Data-driven component export
73
+
74
+ A template reused across many businesses must render data, not hand-edited
75
+ copy. The neutral content contract is
76
+ [`contracts/maggie-marketplace/template-content-v1.schema.json`](../../bundled-contracts/maggie-marketplace/template-content-v1.schema.json)
77
+ (`brand`, `contact`, `hero`, `categories` with `services`, `about`, `gallery`,
78
+ `reviews`, `hours`, `faqs`, plus `samples` and an optional palette). An example
79
+ is in [`references/template-content-v1.example.json`](references/template-content-v1.example.json).
80
+
81
+ ```bash
82
+ maggie tool maggie_marketplace.py content-validate --input content.json
83
+ maggie tool maggie_marketplace.py content-css --input content.json # :root { --maggie-<role>: … }
84
+ ```
85
+
86
+ `content-validate` enforces the business rules the schema cannot: service
87
+ prices and durations, `HH:MM` opening hours, reviews that carry a `source` or
88
+ are explicitly `sample: true` (never invent reviews), and `samples` paths that
89
+ resolve. Export path: build one Astro component per template that takes the
90
+ content object as props, maps each `[data-maggie-sample]` region to its
91
+ content field, and reads colours from the `--maggie-<role>` variables emitted
92
+ by `content-css`. Converting the existing marketplace templates into such
93
+ components is not automated yet; do it per template and keep the static
94
+ `homepage.html` as the visual reference.
95
+
42
96
  ## Selection workflow
43
97
 
44
98
  Use the deterministic marketplace CLI for listing, recommendation, inspection,
@@ -0,0 +1,34 @@
1
+ {
2
+ "schemaVersion": "maggie-template-content-v1",
3
+ "brand": { "name": "Maggie Studio", "tagline": "Considered care in London", "logo": { "src": "assets/maggie-mark.png", "alt": "Maggie Studio" } },
4
+ "contact": { "phone": "020 0000 0000", "email": "hello@example.co.uk", "address": "1 Example Street, London", "bookingUrl": "/book" },
5
+ "hero": {
6
+ "headline": "Calm, personalised treatments",
7
+ "subheadline": "Book facials, massage and body treatments online.",
8
+ "image": { "src": "assets/maggie-hero.png", "alt": "Treatment room" },
9
+ "cta": { "label": "Book now", "href": "/book" }
10
+ },
11
+ "categories": [
12
+ {
13
+ "name": "Facials",
14
+ "services": [
15
+ { "name": "Signature facial", "price": 85, "currency": "GBP", "durationMinutes": 60, "bookingRef": null },
16
+ { "name": "Express facial", "price": 50, "currency": "GBP", "durationMinutes": 30, "bookingRef": null }
17
+ ]
18
+ }
19
+ ],
20
+ "about": { "heading": "About us", "body": "A small team offering unhurried appointments.", "image": null },
21
+ "gallery": [{ "src": "assets/maggie-care-team.png", "alt": "Care team" }],
22
+ "reviews": [{ "quote": "Thoughtful and relaxing.", "author": "A. Client", "rating": 5, "sample": true }],
23
+ "hours": [
24
+ { "day": "mon", "opens": "09:00", "closes": "18:00" },
25
+ { "day": "sun", "closed": true }
26
+ ],
27
+ "faqs": [{ "question": "Do you take walk-ins?", "answer": "Please book online so we can prepare your room." }],
28
+ "samples": ["brand.name", "contact.phone", "contact.email", "contact.address", "hero.headline", "reviews.0"],
29
+ "palette": {
30
+ "id": "signature",
31
+ "name": "Signature",
32
+ "roles": { "canvas": "#FDFBF7", "panel": "#FFFFFF", "soft": "#F2EFE9", "text": "#2A241E", "accent": "#2D5A27", "accentText": "#2D5A27", "onAccent": "#FFFFFF", "dark": "#2D5A27", "deep": "#000000" }
33
+ }
34
+ }