@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.
- package/README-zh-TW.md +2 -2
- package/README.md +76 -15
- package/bin/maggie.js +23 -6
- package/bundled-contracts/google-integrations/capability-report-v2.schema.json +2 -1
- package/bundled-contracts/maggie-content/email-localization-v1.schema.json +89 -0
- package/bundled-contracts/maggie-content/lifecycle-email-v1.schema.json +76 -0
- package/bundled-contracts/maggie-deployment/readiness-v1.schema.json +6 -1
- package/bundled-contracts/maggie-design/palette-roles-v1.schema.json +47 -0
- package/bundled-contracts/maggie-marketplace/template-content-v1.schema.json +103 -0
- package/bundled-contracts/maggie-ops/marketing-run-v1.schema.json +89 -0
- package/bundled-contracts/maggie-ops/notification-catalogue-example-v1.json +941 -0
- package/bundled-contracts/maggie-ops/notification-catalogue-v1.schema.json +273 -0
- package/bundled-contracts/maggie-service-booking/fact-provenance-v1.schema.json +128 -0
- package/bundled-contracts/maggie-service-booking/import-stages-v1.schema.json +191 -0
- package/bundled-contracts/maggiedash/booking-browser-evidence-v1.json +21 -0
- package/bundled-contracts/maggiedash/booking-browser-evidence-v2.json +191 -0
- package/bundled-contracts/maggiedash/booking-email-templates-v1.json +3 -1
- package/bundled-contracts/maggiedash/booking-voucher-lifecycle-v1.json +85 -0
- package/bundled-contracts/maggiedash/execution-board.json +4629 -1553
- package/bundled-contracts/maggiedash/stripe-booking-capabilities-v1.json +1 -0
- package/bundled-marketplace/README.md +17 -0
- package/bundled-marketplace/catalog.json +56 -7
- package/bundled-references/browser-inspection.md +25 -0
- package/bundled-references/google-integrations-runbook.md +54 -0
- package/bundled-references/maggiedash-booking/CURRENT-STATE.md +128 -10
- package/bundled-references/maggiedash-booking/EXECUTION-BOARD.json +4629 -1553
- package/bundled-references/maggiedash-booking/HOST-ADAPTER.md +5 -0
- package/bundled-references/maggiedash-booking/ORAWELLNESS-INTEGRATION-AUDIT.md +64 -21
- package/bundled-references/maggiedash-booking/PRD.md +9 -1
- package/bundled-references/maggiedash-booking/PROGRESS.md +244 -2399
- package/bundled-references/maggiedash-booking/QA-TEST-PLAN.md +37 -6
- package/bundled-references/maggiedash-booking/README.md +23 -6
- package/bundled-references/maggiedash-booking/RUNTIME-OPERATIONS.md +64 -8
- package/bundled-references/maggiedash-booking/SKILLS-AND-CLI.md +11 -2
- package/bundled-references/maggiedash-booking/STRIPE-INTEGRATION.md +66 -2
- package/bundled-references/maggiedash-booking/TASK-RUNBOOK.md +1 -1
- package/bundled-references/maggiedash-booking/TASKS.md +44 -3
- package/bundled-references/maggiedash-booking/VOUCHERS.md +47 -0
- package/bundled-references/provider-adapter-contract.md +33 -0
- package/bundled-skills/maggie-booking/SKILL.md +8 -0
- package/bundled-skills/maggie-clone-to-template/SKILL.md +26 -0
- package/bundled-skills/maggie-content-localization/SKILL.md +17 -1
- package/bundled-skills/maggie-content-localization/references/email-and-lifecycle.md +83 -0
- package/bundled-skills/maggie-deployment/SKILL.md +141 -7
- package/bundled-skills/maggie-deployment/references/vps.md +74 -1
- package/bundled-skills/maggie-design/SKILL.md +154 -0
- package/bundled-skills/maggie-feedback/SKILL.md +67 -1
- package/bundled-skills/maggie-marketplace/SKILL.md +91 -0
- package/bundled-skills/maggie-ops/SKILL.md +37 -0
- package/bundled-skills/maggie-ops/references/notifications-operations.md +154 -0
- package/bundled-skills/maggie-qa-workflow/SKILL.md +41 -1
- package/bundled-skills/maggie-service-booking/SKILL.md +82 -0
- package/bundled-skills/maggie-social-share/SKILL.md +56 -7
- package/bundled-skills/maggie-social-share/references/marketing-run.md +57 -0
- package/bundled-skills/maggie-social-share/references/queue-and-connections.md +152 -0
- package/bundled-skills/maggie-template/SKILL.md +57 -3
- package/bundled-skills/maggie-template/references/template-content-v1.example.json +34 -0
- package/bundled-tools/clis/maggie_booking.py +199 -26
- package/bundled-tools/clis/maggie_booking_vouchers.py +185 -0
- package/bundled-tools/clis/maggie_clone_to_template.py +14 -0
- package/bundled-tools/clis/maggie_deployment.py +307 -18
- package/bundled-tools/clis/maggie_deployment_auth_canary.py +216 -0
- package/bundled-tools/clis/maggie_deployment_collect.py +225 -0
- package/bundled-tools/clis/maggie_deployment_readiness.py +53 -3
- package/bundled-tools/clis/maggie_design.py +17 -0
- package/bundled-tools/clis/maggie_design_lint.py +277 -0
- package/bundled-tools/clis/maggie_design_verify.py +359 -0
- package/bundled-tools/clis/maggie_docs.py +82 -4
- package/bundled-tools/clis/maggie_email_localization.py +382 -0
- package/bundled-tools/clis/maggie_feedback.py +115 -5
- package/bundled-tools/clis/maggie_icon_glyph_check.py +382 -0
- package/bundled-tools/clis/maggie_icon_release_gate.py +28 -1
- package/bundled-tools/clis/maggie_localization.py +5 -0
- package/bundled-tools/clis/maggie_marketing_run.py +272 -0
- package/bundled-tools/clis/maggie_marketplace.py +199 -5
- package/bundled-tools/clis/maggie_notifications.py +528 -0
- package/bundled-tools/clis/maggie_ops.py +87 -0
- package/bundled-tools/clis/maggie_palette.py +280 -0
- package/bundled-tools/clis/maggie_qa_workflow.py +156 -1
- package/bundled-tools/clis/maggie_schedule.py +12 -2
- package/bundled-tools/clis/maggie_service_import.py +520 -0
- package/bundled-tools/clis/maggie_social_queue.py +442 -0
- package/bundled-tools/clis/maggie_social_share.py +46 -9
- package/bundled-tools/clis/maggie_template_package.py +1141 -0
- package/bundled-tools/runtime/google_ads.py +306 -0
- package/bundled-tools/runtime/google_capabilities.py +18 -2
- package/bundled-tools/runtime/google_remediation.py +152 -0
- package/package.json +1 -1
- package/references/browser-inspection.md +25 -0
- package/references/google-integrations-runbook.md +54 -0
- 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
|
|
32
|
-
|
|
33
|
-
|
|
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
|
+
}
|