@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
|
@@ -67,6 +67,32 @@ then stores the prompt as
|
|
|
67
67
|
`CUSTOMIZATION_PROMPT.md` beside the package. It creates a resumable job under
|
|
68
68
|
`marketplace/.maggie/clone-to-template-jobs/`.
|
|
69
69
|
|
|
70
|
+
### Neutral display identity
|
|
71
|
+
|
|
72
|
+
A package's customer-facing identity is separate from its source evidence.
|
|
73
|
+
`template.json` `name` and `id` are internal; `sourceMetadata` records the
|
|
74
|
+
source title only as provenance. Pass a designed, neutral style name and a short
|
|
75
|
+
description when planning:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
maggie tool maggie_clone_to_template.py plan <url> "<approved brief>" --rename <template-id> \
|
|
79
|
+
--display-name "Olive Atelier" \
|
|
80
|
+
--display-description "A light editorial beauty homepage with olive accents."
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Packaging forwards both to the marketplace importer. Without them, completion
|
|
84
|
+
derives a name from the measured design (accent hue, display typeface,
|
|
85
|
+
industry). The validator rejects a name that repeats a distinctive source-title
|
|
86
|
+
word, looks like a counter (`Beauty Studio 20 001`), or duplicates another
|
|
87
|
+
catalogue entry. Change it later with `maggie_marketplace.py identity <id>
|
|
88
|
+
--display-name …`. Template pickers must show `displayName` and
|
|
89
|
+
`displayDescription`, never the source business name.
|
|
90
|
+
|
|
91
|
+
Packaging also measures `DESIGN.md` from the rendered CSS, records
|
|
92
|
+
`screenshotReview`, and lists every asset with its provenance. Review captures
|
|
93
|
+
stay at viewport height; tile any full-page capture as described in the
|
|
94
|
+
`maggie-marketplace` package quality gates.
|
|
95
|
+
|
|
70
96
|
If the full clone run fails or cannot reach `verified`, stop and report the
|
|
71
97
|
clone checkpoint. Do not invoke the marketplace importer as a fallback. If the
|
|
72
98
|
Maggie design review fails, stop before declaring the template ready.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: maggie-content-localization
|
|
3
3
|
description: Manage translation, polish, rewrite, market localization, review, stale detection, and publishing for pages, guides, posts, services, products, and categories.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.4.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Maggie Content Localization
|
|
@@ -70,6 +70,20 @@ Norwegian, Finnish, Greek, Hebrew, Czech, Romanian, Hungarian, Thai, Persian,
|
|
|
70
70
|
and Swahili. Use BCP 47 regions such as `es-MX`, `pt-BR`, `ar-SA`, or `ja-JP`
|
|
71
71
|
when market-specific copy is required.
|
|
72
72
|
|
|
73
|
+
## Email and lifecycle email
|
|
74
|
+
|
|
75
|
+
Localized email is a content surface with its own checks. Resolve the
|
|
76
|
+
recipient locale as user preference > market > account > fallback; never
|
|
77
|
+
hard-code a locale or rely on `Accept-Language` alone in fulfilment paths.
|
|
78
|
+
`maggie localization email-matrix` checks missing locales, untranslated
|
|
79
|
+
values, changed or undeclared variables, HTML/plain-text parity, market CTA
|
|
80
|
+
routes, glossary terms, and approval, and can write fixture-filled previews.
|
|
81
|
+
`maggie localization lifecycle` validates a consent-aware marketing plan
|
|
82
|
+
(consent basis, unsubscribe and preference-centre paths, frequency cap,
|
|
83
|
+
attribution, no send until approved) and `--library` drafts the standard
|
|
84
|
+
template set with nothing sendable. Both are read-only and never send. See
|
|
85
|
+
[email and lifecycle email](references/email-and-lifecycle.md).
|
|
86
|
+
|
|
73
87
|
## Invocation lifecycle
|
|
74
88
|
|
|
75
89
|
At the start, run the memory hook for the current skill and load only relevant
|
|
@@ -98,6 +112,8 @@ maggie tool maggie_localization.py review .maggie/localization/<job>.json \
|
|
|
98
112
|
maggie tool maggie_localization.py publish .maggie/localization/<job>.json --confirm
|
|
99
113
|
maggie tool maggie_localization.py stale --project .
|
|
100
114
|
maggie tool maggie_localization.py glossary --project .
|
|
115
|
+
maggie tool maggie_localization.py email-matrix emails/manifest.json --preview-dir .maggie/localization/email-preview
|
|
116
|
+
maggie tool maggie_localization.py lifecycle emails/lifecycle.json
|
|
101
117
|
```
|
|
102
118
|
|
|
103
119
|
Operations are distinct: `translate` preserves meaning, `polish` improves
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Localized email and consent-aware lifecycle email
|
|
2
|
+
|
|
3
|
+
Email is a localized content surface. Treat every template × locale × market
|
|
4
|
+
as a reviewable variant, and keep transactional/security messages separate
|
|
5
|
+
from marketing consent. Both commands below are read-only: they never call a
|
|
6
|
+
provider and never send.
|
|
7
|
+
|
|
8
|
+
## Localized templates (`email-matrix`)
|
|
9
|
+
|
|
10
|
+
Describe templates in a `maggie-email-localization.v1` manifest (schema:
|
|
11
|
+
`contracts/maggie-content/email-localization-v1.schema.json`, shipped as
|
|
12
|
+
`bundled-contracts/maggie-content/` in the npm package).
|
|
13
|
+
|
|
14
|
+
Locale precedence is fixed: **user preference > market > account > fallback**.
|
|
15
|
+
A send path must pass the resolved recipient locale. Two patterns fail:
|
|
16
|
+
|
|
17
|
+
- a hard-coded locale (e.g. a fulfilment path that always renders `en`);
|
|
18
|
+
- `Accept-Language` as the only source for fulfilment or transactional email.
|
|
19
|
+
For other paths it is only a hint and produces a warning.
|
|
20
|
+
|
|
21
|
+
Declare each send path under `callSites` with its `localeSource` so the check
|
|
22
|
+
covers code paths, not only templates.
|
|
23
|
+
|
|
24
|
+
The check fails when:
|
|
25
|
+
|
|
26
|
+
- a required locale or its subject/HTML/plain text is missing;
|
|
27
|
+
- a translated field is identical to the source text (untranslated);
|
|
28
|
+
- variables differ from the source field, are undeclared, or differ between
|
|
29
|
+
the HTML and plain-text bodies;
|
|
30
|
+
- a CTA route is not a site path or `https` URL, uses a market whose prefix it
|
|
31
|
+
does not carry, targets a market that does not serve that locale, or changes
|
|
32
|
+
the template route's parameters.
|
|
33
|
+
|
|
34
|
+
It warns about glossary terms not rendered as their approved translation,
|
|
35
|
+
CTA routes missing from a body, unused variables, and unapproved variants.
|
|
36
|
+
A variant is publishable only when its `approval` has `status: approved` and a
|
|
37
|
+
`reviewer`; `--require-approved` turns that into a failure for release gates.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
maggie localization email-matrix emails/manifest.json \
|
|
41
|
+
--preview-dir .maggie/localization/email-preview
|
|
42
|
+
maggie localization email-matrix emails/manifest.json --require-approved
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`--preview-dir` writes one `.html` and one `.txt` file per template × locale,
|
|
46
|
+
filled from the template's synthetic `fixtures` (HTML-escaped) and labelled as
|
|
47
|
+
a preview. Never put customer data in fixtures.
|
|
48
|
+
|
|
49
|
+
## Lifecycle and marketing email (`lifecycle`)
|
|
50
|
+
|
|
51
|
+
A `maggie-lifecycle-email.v1` plan (schema:
|
|
52
|
+
`contracts/maggie-content/lifecycle-email-v1.schema.json`) runs through fixed
|
|
53
|
+
stages: plan → segment → consent-check → preview → approve → schedule → send →
|
|
54
|
+
report. `defaults.send` must be `disabled`: nothing is sent until approved.
|
|
55
|
+
|
|
56
|
+
Start from the library, which drafts every kind with nothing sendable:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
maggie localization lifecycle --library > emails/lifecycle.json
|
|
60
|
+
maggie localization lifecycle emails/lifecycle.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Library kinds: opt-in confirmation, welcome, digest, recommendation, gift /
|
|
64
|
+
voucher / waitlist follow-up, merchant lead digest, campaign announcement,
|
|
65
|
+
re-engagement.
|
|
66
|
+
|
|
67
|
+
Rules the validator enforces:
|
|
68
|
+
|
|
69
|
+
- Marketing templates need `consent.basis` (opt-in, double-opt-in,
|
|
70
|
+
soft-opt-in, or legitimate-interest-b2b) and a preference-centre
|
|
71
|
+
`consent.category`, an unsubscribe path, a preference-centre path, and
|
|
72
|
+
attribution fields (source/UTM) so outcomes can be measured.
|
|
73
|
+
- Every template has a frequency cap (`maxPerWindow` + ISO 8601 `window`).
|
|
74
|
+
- The opt-in confirmation is sent before consent exists, so it must be
|
|
75
|
+
transactional and carry no promotional content.
|
|
76
|
+
- `sendable: true` requires an approval with `status: approved`, `reviewer`
|
|
77
|
+
and `approvedAt` — on the template and on the campaign.
|
|
78
|
+
- Campaigns using marketing templates require consent, must honour
|
|
79
|
+
suppression, must deduplicate contacts, and need a schedule before they are
|
|
80
|
+
sendable. Declare aggregate report metrics; never report per-recipient data.
|
|
81
|
+
|
|
82
|
+
The host owns consent records, segments, suppression lists, delivery and
|
|
83
|
+
reporting. Maggie validates the plan; it does not send or store addresses.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: maggie-deployment
|
|
3
3
|
description: Deploy and operate Maggie blog projects with Cloudflare Workers as the default target, while preserving an adapter boundary for VPS, GCP, and AWS.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.6.0
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Maggie Deployment
|
|
@@ -88,6 +88,74 @@ not fabricate browser, rendered, or deployment evidence and does not deploy
|
|
|
88
88
|
from this command. See
|
|
89
89
|
[`readiness-v1.schema.json`](../../bundled-contracts/maggie-deployment/readiness-v1.schema.json).
|
|
90
90
|
|
|
91
|
+
### Collect host evidence
|
|
92
|
+
|
|
93
|
+
Instead of hand-written SSH, systemd, and retention scripts per host, declare
|
|
94
|
+
the standard adapters once in `.maggie/deployment/collector.json`
|
|
95
|
+
(`maggie-evidence-collector.v1`) and let one command run them and emit the
|
|
96
|
+
same `maggie-deployment-readiness.v1` report:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"schemaVersion": "maggie-evidence-collector.v1",
|
|
101
|
+
"release": "2026-10-10-a",
|
|
102
|
+
"adapters": {
|
|
103
|
+
"canary": {"assets": ["https://example.test/_astro/app.js=<sha256>"], "renderReport": ".maggie/rendered-canary.json"},
|
|
104
|
+
"health": {"urls": ["https://example.test/robots.txt", "https://example.test/sitemap.xml"]},
|
|
105
|
+
"rollback": {"evidence": "docs/deployment-rollback-smoke.json", "plan": ".maggie/deployment/vps/deployment-plan.json"},
|
|
106
|
+
"releaseIdentity": {"command": ["ssh", "-o", "BatchMode=yes", "maggie-deploy@host", "readlink -f /var/www/example/current"]},
|
|
107
|
+
"authCanary": {"plan": ".maggie/deployment/auth-canary-plan.json"}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
maggie deployment collect --project . --config .maggie/deployment/collector.json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Adapters write their evidence under `.maggie/deployment/collected/` and the
|
|
117
|
+
readiness report adds the optional `hostHealth`, `releaseIdentity`,
|
|
118
|
+
`rollbackEvidence`, and `authenticatedCanary` slots next to the five required
|
|
119
|
+
slots. `releaseIdentity` proves the host's `current` release (from an argv
|
|
120
|
+
command such as `readlink -f`, or a `{"url", "field"}` version endpoint)
|
|
121
|
+
equals the expected `release`. Commands are argv lists, never shell strings,
|
|
122
|
+
and are rejected if they contain a literal credential. The report records the
|
|
123
|
+
config SHA-256, the release, and per-adapter run metadata so it can be
|
|
124
|
+
reproduced; it never deploys.
|
|
125
|
+
|
|
126
|
+
### Authenticated API canaries
|
|
127
|
+
|
|
128
|
+
Public canaries cannot see failures on signed-in account or business APIs
|
|
129
|
+
(database permission errors, schema mismatches, 403s). Declare role-scoped
|
|
130
|
+
probes in a `maggie-auth-canary-plan.v1` plan:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"schemaVersion": "maggie-auth-canary-plan.v1",
|
|
135
|
+
"baseUrl": "https://example.test",
|
|
136
|
+
"roles": {"owner": {"tokenEnv": "CANARY_OWNER_TOKEN"}, "staff": {"cookieEnv": "CANARY_STAFF_COOKIE"}},
|
|
137
|
+
"probes": [
|
|
138
|
+
{"id": "account", "role": "owner", "path": "/api/account", "expectJsonKeys": ["id", "role"]},
|
|
139
|
+
{"id": "business", "role": "staff", "path": "/api/business", "expectStatus": [200]},
|
|
140
|
+
{"id": "fixture-create", "role": "owner", "method": "POST", "path": "/api/canary-fixtures", "fixture": "disposable", "expectStatus": [201]}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
CANARY_OWNER_TOKEN=... maggie deployment auth-canary \
|
|
147
|
+
--plan .maggie/deployment/auth-canary-plan.json --output .maggie/deployment/auth-canary.json
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Credentials come only from the named environment variables of dedicated
|
|
151
|
+
canary accounts; a plan containing a literal token or cookie is rejected.
|
|
152
|
+
Mutating probes must target a `disposable` fixture and run only with
|
|
153
|
+
`--allow-mutations`. Evidence (`maggie-deployment-auth-canary.v1`) records
|
|
154
|
+
probe id, role, method, path, status, and contract-key counts, classifying
|
|
155
|
+
failures as `authorization`, `server`, `route`, `contract`, or `network`;
|
|
156
|
+
it never stores headers, cookies, tokens, response bodies, or personal data.
|
|
157
|
+
A missing credential makes the probe `inconclusive`, not passed.
|
|
158
|
+
|
|
91
159
|
Classify host/runtime failures before a release can move:
|
|
92
160
|
|
|
93
161
|
```bash
|
|
@@ -207,7 +275,7 @@ The output contains only the immutable release/current layout, systemd unit,
|
|
|
207
275
|
Nginx reverse-proxy config, health commands, rollback command, and an ordered
|
|
208
276
|
reviewable release runner. The runner keeps migrations before the symlink flip,
|
|
209
277
|
carries `.agents`/`.claude` state, restarts before verification, and prunes to
|
|
210
|
-
the current
|
|
278
|
+
the plan's retention policy (default: current plus one rollback candidate). It never
|
|
211
279
|
contains credentials and never SSHs, changes DNS, restarts systemd or deploys.
|
|
212
280
|
|
|
213
281
|
VPS plans use a dedicated deployer account by default:
|
|
@@ -225,9 +293,23 @@ when they are unmarked or stale; use `--check-runner PATH` for a runner stored
|
|
|
225
293
|
elsewhere. Regenerate from the reviewed VPS plan, inspect the diff, and replace
|
|
226
294
|
the checked-in runner only after review.
|
|
227
295
|
|
|
228
|
-
VPS plans
|
|
229
|
-
|
|
230
|
-
|
|
296
|
+
VPS plans carry one explicit retention policy, `--keep-releases N` (1-5,
|
|
297
|
+
default 2). The same value drives the plan's prune command, the generated
|
|
298
|
+
runner's prune step, `--retention-plan`, preflight, and rollback evidence, so
|
|
299
|
+
Maggie never advertises a rollback target the host deletes:
|
|
300
|
+
|
|
301
|
+
- `N >= 2` keeps the `current` target plus the release it replaced; the plan's
|
|
302
|
+
`commands.rollback` switches back to it.
|
|
303
|
+
- `N = 1` keeps only the active release. The plan reports
|
|
304
|
+
`retention.rollbackAvailable: false` and `commands.rollback: null`, the
|
|
305
|
+
runner prints `rollback target: unavailable`, and production rollback
|
|
306
|
+
evidence must declare `codeRollback: "unavailable"` with a `recoveryPlan`
|
|
307
|
+
(redeploy the previous commit as a new release and restore its data
|
|
308
|
+
checkpoint). Preflight's `rollback_target_retained` check fails when a plan
|
|
309
|
+
keeps one release but still advertises a previous-release rollback.
|
|
310
|
+
|
|
311
|
+
Review prune candidates before any operator executes cleanup. Generate a
|
|
312
|
+
read-only candidate report:
|
|
231
313
|
|
|
232
314
|
```bash
|
|
233
315
|
maggie tool maggie_deployment.py --retention-plan \
|
|
@@ -236,8 +318,60 @@ maggie tool maggie_deployment.py --retention-plan \
|
|
|
236
318
|
--keep-releases 2 --output .maggie/deployment/retention-plan.json
|
|
237
319
|
```
|
|
238
320
|
|
|
239
|
-
The CLI never deletes release directories
|
|
240
|
-
|
|
321
|
+
The CLI never deletes release directories; only the reviewed runner prunes,
|
|
322
|
+
and only after health checks pass.
|
|
323
|
+
|
|
324
|
+
### Multi-host secret parity
|
|
325
|
+
|
|
326
|
+
When several hosts serve the same routes, check that each one holds every
|
|
327
|
+
required secret. The check compares key names and SHA-256 fingerprints and
|
|
328
|
+
never prints, stores, or transmits values:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# Local env files (one per host):
|
|
332
|
+
maggie deployment secret-parity \
|
|
333
|
+
--host api-1=.secrets/api-1.env --host api-2=.secrets/api-2.env \
|
|
334
|
+
--required SOCIAL_SHARE_ENCRYPTION_KEY
|
|
335
|
+
|
|
336
|
+
# Or fingerprint manifests produced on each host:
|
|
337
|
+
maggie deployment secret-fingerprint --env-file /etc/example.env \
|
|
338
|
+
--host api-1 --output api-1.json
|
|
339
|
+
maggie deployment secret-parity --manifest api-1.json --manifest api-2.json \
|
|
340
|
+
--required SOCIAL_SHARE_ENCRYPTION_KEY
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
A required key that is missing or differs on any host fails the check. Other
|
|
344
|
+
drifting keys are warnings unless `--strict` is set. A host without Maggie can
|
|
345
|
+
use the no-echo shell snippet in [the VPS reference](references/vps.md); the
|
|
346
|
+
parity command accepts its `KEY <sha256>` output directly. Manifests that
|
|
347
|
+
contain anything other than names and 64-character hex digests are rejected.
|
|
348
|
+
|
|
349
|
+
### Local and production database isolation
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
maggie deployment env-isolation --project . \
|
|
353
|
+
--production-env .env.production
|
|
354
|
+
# or: --production-db-host db.example.net
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The check fails when the local `.env` points at the production database (same
|
|
358
|
+
host, and port/name when known). The same server with a different database is
|
|
359
|
+
a warning. Deployment preflight runs the check automatically, as a non-blocking
|
|
360
|
+
warning, when `.env.production` exists or `MAGGIE_PRODUCTION_DB_HOST` is set.
|
|
361
|
+
Database URLs are reduced to host/port; credentials are never reported.
|
|
362
|
+
|
|
363
|
+
### Release ordering for workers and notification templates
|
|
364
|
+
|
|
365
|
+
Deploy the worker and its templates before any code path can enqueue an event
|
|
366
|
+
that uses a new template. When local development or a staged app shares a
|
|
367
|
+
queue with production, an event that names a template the running worker does
|
|
368
|
+
not know yet must be held and retried, not dead-lettered: an unknown template
|
|
369
|
+
during a rollout is a release-ordering state, not a permanent failure. Check
|
|
370
|
+
that the outbox worker (MaggieDash Booking or the host's own) does not let
|
|
371
|
+
unknown-template deliveries consume the retry budget, and after the release
|
|
372
|
+
confirm the held deliveries were sent.
|
|
373
|
+
If dead-lettered rows already exist, requeue them only after the worker that
|
|
374
|
+
knows the template is live.
|
|
241
375
|
|
|
242
376
|
Validate the database release separately before a VPS migration:
|
|
243
377
|
|
|
@@ -121,6 +121,29 @@ before staging migration and require a successful restore test before a
|
|
|
121
121
|
production release. Keep the backup artifact and migration version in the
|
|
122
122
|
handover record; never put a database URL or password in the manifest.
|
|
123
123
|
|
|
124
|
+
### Running migrations as the database owner
|
|
125
|
+
|
|
126
|
+
`sudo -u postgres psql -f /home/ubuntu/site/releases/<release>/db/073.sql`
|
|
127
|
+
fails with `Permission denied` when the release lives under a deploy user's
|
|
128
|
+
home: the `postgres` account cannot traverse that path. Do not widen
|
|
129
|
+
filesystem permissions. Stream the file from a root-readable read into the
|
|
130
|
+
owner-level `psql` instead (the generated plan's `commands.migrate_as_db_owner`):
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
sudo cat /home/ubuntu/site/releases/<release>/db/073.sql \
|
|
134
|
+
| sudo -u postgres psql -v ON_ERROR_STOP=1 --single-transaction -d <database> -f -
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If a file path is required, copy it to a private temporary file readable only
|
|
138
|
+
by the owner and remove it afterwards:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
tmp=$(sudo -u postgres mktemp) && sudo cp <migration.sql> "$tmp" \
|
|
142
|
+
&& sudo chown postgres: "$tmp" && sudo chmod 600 "$tmp" \
|
|
143
|
+
&& sudo -u postgres psql -v ON_ERROR_STOP=1 -d <database> -f "$tmp"; \
|
|
144
|
+
sudo rm -f "$tmp"
|
|
145
|
+
```
|
|
146
|
+
|
|
124
147
|
## Release workflow
|
|
125
148
|
|
|
126
149
|
```text
|
|
@@ -132,6 +155,56 @@ inspect → plan → backup/migrate → npm ci → npm run build
|
|
|
132
155
|
Deploy staging first. A passing build does not prove Nginx, systemd, DNS, TLS,
|
|
133
156
|
robots, sitemap, or private-route correctness.
|
|
134
157
|
|
|
158
|
+
### Release retention
|
|
159
|
+
|
|
160
|
+
The retention policy is one explicit number, `--keep-releases N` (default 2),
|
|
161
|
+
shared by the plan, the runner, `--retention-plan`, preflight, and rollback
|
|
162
|
+
evidence. The default keeps the `current` release plus one previous release as
|
|
163
|
+
the rollback target. `N = 1` is allowed for hosts that intentionally keep only
|
|
164
|
+
the active release, but then no on-disk rollback exists: the plan reports
|
|
165
|
+
`rollbackAvailable: false`, the runner says so after pruning, and rollback
|
|
166
|
+
evidence must name a recovery plan (redeploy the previous commit, restore the
|
|
167
|
+
data checkpoint) instead of a previous release.
|
|
168
|
+
|
|
169
|
+
Prune after every deploy, and only after the new release passed its health
|
|
170
|
+
checks: an unhealthy release must still have its predecessor on disk. The
|
|
171
|
+
generated release runner does this as its final step; it never removes the
|
|
172
|
+
release `current` points at, and with `N >= 2` it also keeps the release it
|
|
173
|
+
replaced. Without pruning,
|
|
174
|
+
`releases/` grows by one full `node_modules` tree per deploy until the disk
|
|
175
|
+
fills. Use `--retention-plan` for a read-only list of prune candidates on hosts
|
|
176
|
+
whose runner predates this step, and review it before deleting anything.
|
|
177
|
+
|
|
178
|
+
### Multi-host secret parity
|
|
179
|
+
|
|
180
|
+
When more than one host can serve the same routes (failover, load-balanced API
|
|
181
|
+
nodes, workers), every host must hold the same required secrets. A key that
|
|
182
|
+
exists on only one host is invisible until traffic fails over to another and
|
|
183
|
+
returns 5xx. Compare hosts by name and SHA-256 fingerprint, never by value:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# On each host (prints names and digests only; values are never echoed):
|
|
187
|
+
for k in SOCIAL_SHARE_ENCRYPTION_KEY DATABASE_URL; do
|
|
188
|
+
v=$(sed -n "s/^$k=//p" /etc/example.env | head -1)
|
|
189
|
+
printf '%s %s\n' "$k" "$(printf %s "$v" | sha256sum | cut -c1-64)"
|
|
190
|
+
done > /tmp/api-1.fingerprints
|
|
191
|
+
|
|
192
|
+
# Locally, after copying each host's fingerprint file:
|
|
193
|
+
maggie deployment secret-parity \
|
|
194
|
+
--manifest api-1.fingerprints --manifest api-2.fingerprints \
|
|
195
|
+
--manifest api-3.fingerprints \
|
|
196
|
+
--required SOCIAL_SHARE_ENCRYPTION_KEY --required DATABASE_URL
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Run it after adding a secret and before enabling a host in a pool.
|
|
200
|
+
|
|
201
|
+
### Environment isolation
|
|
202
|
+
|
|
203
|
+
A local `.env` must never point at the production database. Shared databases
|
|
204
|
+
let local test records (vouchers, palettes, emails) appear on live pages and be
|
|
205
|
+
picked up by production workers. `maggie deployment env-isolation` fails when
|
|
206
|
+
the local database host, port, and name match production.
|
|
207
|
+
|
|
135
208
|
## Verification and rollback
|
|
136
209
|
|
|
137
210
|
Verify the homepage, changed service routes, `/robots.txt`, `/sitemap.xml`,
|
|
@@ -139,7 +212,7 @@ canonical URLs, booking links, and protected Ops/API routes. Check both apex
|
|
|
139
212
|
and `www` when configured. Record HTTP status, commit, and release.
|
|
140
213
|
|
|
141
214
|
Rollback switches `current` to the previous immutable release and restarts the
|
|
142
|
-
service. If migrations are not backward-compatible, stop before deployment and
|
|
215
|
+
service, when the retention policy keeps one. If migrations are not backward-compatible, stop before deployment and
|
|
143
216
|
provide a tested restore or database rollback plan. The first production
|
|
144
217
|
release may declare rollback as not applicable only with `releaseKind: "first"`,
|
|
145
218
|
a non-empty `notApplicableReason`, and `RELEASE_KIND=first`; upgrades still
|
|
@@ -84,6 +84,61 @@ a successful `assetStatus` (2xx), and a non-empty accessible name. This
|
|
|
84
84
|
gate is independent from typecheck/build and is read-only apart from its
|
|
85
85
|
explicit output.
|
|
86
86
|
|
|
87
|
+
### Rendered glyph proof
|
|
88
|
+
|
|
89
|
+
Collect that browser evidence with the shared collector instead of a
|
|
90
|
+
hand-written report. It renders each route at mobile, tablet, and desktop in
|
|
91
|
+
headless Chrome — through a local GET-only proxy for a running host
|
|
92
|
+
(`--base-url`) or directly from a static build (`--dist`) — and records, per
|
|
93
|
+
representative icon, whether its icon font reached `document.fonts` status
|
|
94
|
+
`loaded`, whether the glyph paints ink that differs from the browser fallback
|
|
95
|
+
font (a missing glyph falls back or paints blank), whether runtime CSS hides
|
|
96
|
+
it (`display`, `visibility`, `opacity`, zero size, or a hidden pseudo-element),
|
|
97
|
+
its font family, accessible name, and the served font asset status:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
maggie design icon-glyph-check --base-url http://localhost:4321 \
|
|
101
|
+
--route / --route /contact --family ph=Phosphor \
|
|
102
|
+
--output .maggie/icon-browser-evidence.json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`--family PREFIX=FAMILY` asserts the expected visual family for a class
|
|
106
|
+
prefix. Evidence from this collector makes `icon-release-gate` also require
|
|
107
|
+
`fontLoaded`, `glyphPainted`, matching family, and an icon at every declared
|
|
108
|
+
viewport; hand-written evidence still passes the original checks but the gate
|
|
109
|
+
reports `glyphIdentityProven: false` with a warning. The collector forwards GET
|
|
110
|
+
requests only and never stores page text beyond icon names and labels.
|
|
111
|
+
|
|
112
|
+
## Design job verification
|
|
113
|
+
|
|
114
|
+
`design step` records evidence one step at a time. `design verify` runs every
|
|
115
|
+
step that can be proven automatically against a host adapter and records it
|
|
116
|
+
with durable evidence under `.maggie/design-evidence/<job>/`:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# section-scoped job: snapshot before editing, then verify after
|
|
120
|
+
maggie design verify section-<id> --project . --base-url http://localhost:4321 --stage before
|
|
121
|
+
maggie design verify section-<id> --project . --base-url http://localhost:4321
|
|
122
|
+
|
|
123
|
+
# in-place or author job against a static build, including the host build
|
|
124
|
+
maggie design verify in-place-<id> --project . --dist dist --build-command "npm run build"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Automated steps: `inspect-route`/`inspect-shell`/`inspect-section` (2xx HTML,
|
|
128
|
+
shell landmarks, target section present), `declare-content-source` (no
|
|
129
|
+
unproven catch-all resolution), `capture-before`/`capture-after` (per-section
|
|
130
|
+
structure hashes from `<section id>` or `data-section-id`),
|
|
131
|
+
`assert-unchanged-rest`, `capture-responsive-screenshots` (390/768/1440 PNGs
|
|
132
|
+
with hashes), `accessibility-check` (lang, one h1, alt text, named controls,
|
|
133
|
+
labelled fields, `<main>`), `route-validation` (2xx, title, h1, no template
|
|
134
|
+
placeholders, no accidental `noindex`), and `build-check` only with an explicit
|
|
135
|
+
`--build-command`. A failed check leaves its step pending with `lastCheck`
|
|
136
|
+
evidence. `apply-design-contract`, `implement-*`, `visual-review`, and
|
|
137
|
+
`approval` are never completed by `verify`; record them with `design step` and
|
|
138
|
+
real implementer or reviewer evidence, so a job cannot reach `done` without
|
|
139
|
+
evidence. Client-rendered sections need host-adapter evidence through
|
|
140
|
+
`design step` when the server HTML does not contain them.
|
|
141
|
+
|
|
87
142
|
## Hero video playback contract
|
|
88
143
|
|
|
89
144
|
Hero video is an enhancement, not the only way to see the first-band message.
|
|
@@ -285,6 +340,57 @@ all required camera states, and valid PNGs at 390px, 768px, and desktop
|
|
|
285
340
|
widths. The machine-readable contract is
|
|
286
341
|
[mobile-app-surface-v1.schema.json](../../bundled-contracts/maggie-design/mobile-app-surface-v1.schema.json).
|
|
287
342
|
|
|
343
|
+
### Onboarding wizards
|
|
344
|
+
|
|
345
|
+
Use one pattern for multi-step setup (brand, logo, voice, posting hours, and
|
|
346
|
+
similar first-run flows) instead of designing each flow ad hoc:
|
|
347
|
+
|
|
348
|
+
- A segmented progress bar with one segment per step; the current step is
|
|
349
|
+
visibly distinct and completed steps are tappable.
|
|
350
|
+
- Each step shows Maggie (or the product guide) with exactly one headline and,
|
|
351
|
+
at most, one supporting line. Put help in the control, not in paragraphs.
|
|
352
|
+
- Record step state on the server (`pending`, `done`, `skipped`) after every
|
|
353
|
+
step, and resume from the first unfinished step on any device. Local state
|
|
354
|
+
alone loses progress on reinstall or a second device.
|
|
355
|
+
- Every optional step offers **Later**. A skipped step reopens on the next
|
|
356
|
+
sign-in until it is done or explicitly dismissed; never block the app on it.
|
|
357
|
+
- Completed steps keep a redo action (for example "Retrain voice" or
|
|
358
|
+
"Choose another logo") that reopens that step with the saved value
|
|
359
|
+
preselected, without restarting the wizard.
|
|
360
|
+
- The primary action stays in the fixed footer; Back never discards saved
|
|
361
|
+
server state.
|
|
362
|
+
|
|
363
|
+
### Pickers with previews
|
|
364
|
+
|
|
365
|
+
For choices that need to be seen (logo styles, templates, palettes, voices):
|
|
366
|
+
|
|
367
|
+
- The list sheet shows a thumbnail, name, and selection state per row; the
|
|
368
|
+
row itself selects, and a separate preview control opens a larger view.
|
|
369
|
+
- The large preview opens as a sheet nested in the list sheet, so dismissing
|
|
370
|
+
it returns to the same scroll position in the list. It offers **Use this**
|
|
371
|
+
and close; it never replaces the list route.
|
|
372
|
+
- Thumbnails are generated or cached assets, not live renders of every option.
|
|
373
|
+
|
|
374
|
+
### React Native Web pressables
|
|
375
|
+
|
|
376
|
+
React Native Web renders `Pressable` and the `Touchable*` family as button-like
|
|
377
|
+
DOM elements. A chip with a nested remove control becomes `<button>` inside
|
|
378
|
+
`<button>`: the DOM is invalid, browsers repair it differently, and taps land
|
|
379
|
+
on the wrong control (for example, removing a tag opens its preview). Render
|
|
380
|
+
the chip body and its remove control as **sibling** pressables inside a plain
|
|
381
|
+
`View`, each with its own accessible label. The same rule applies to `<a>`
|
|
382
|
+
and `<button>` on the web.
|
|
383
|
+
|
|
384
|
+
Run the static check before browser QA; it scans `.tsx`/`.jsx`, skips build
|
|
385
|
+
and dependency folders, and treats `asChild` wrappers (such as an expo-router
|
|
386
|
+
`Link`) as a pass-through:
|
|
387
|
+
|
|
388
|
+
maggie design pressable-check --project . [--pressable Chip --pressable IconButton]
|
|
389
|
+
|
|
390
|
+
Pass `--pressable` for project components that render a pressable. A rare,
|
|
391
|
+
reviewed exception can carry `maggie-allow-nested-pressable` in a comment on
|
|
392
|
+
the inner element's line.
|
|
393
|
+
|
|
288
394
|
## Explicit homepage rebrand mode
|
|
289
395
|
|
|
290
396
|
The homepage `review` mode only compares screenshots. It does not rebrand a
|
|
@@ -543,6 +649,54 @@ collapsed-rail, mobile-drawer, and modal evidence. Sample plans require
|
|
|
543
649
|
noindex, sitemap exclusion, a visible sample banner, placeholders, and a
|
|
544
650
|
finish checklist.
|
|
545
651
|
|
|
652
|
+
### Tailwind v4 cascade layers
|
|
653
|
+
|
|
654
|
+
Tailwind v4 emits utilities inside `@layer utilities`. An unlayered rule beats
|
|
655
|
+
every layered rule regardless of specificity, so an unlayered
|
|
656
|
+
`h1, h2, h3 { font-family: … }` silently defeats template `font-*` utilities,
|
|
657
|
+
and an unlayered icon runtime rule such as `.ph { display: inline-block }`
|
|
658
|
+
defeats `hidden` and `group-open:inline` (a mobile menu then shows both the
|
|
659
|
+
open and close icons). Static class checks pass; only a browser shows it.
|
|
660
|
+
|
|
661
|
+
- Put app-wide element rules (headings, links, body copy) in `@layer base`.
|
|
662
|
+
- Put icon-font runtime rules and component classes in `@layer components`,
|
|
663
|
+
or import the icon stylesheet with `@import "./icons.css" layer(components);`.
|
|
664
|
+
- Keep `@theme`, `@utility`, and `@font-face` as they are; they are not
|
|
665
|
+
competing rules.
|
|
666
|
+
|
|
667
|
+
Run the static check whenever CSS or the icon package changes:
|
|
668
|
+
|
|
669
|
+
maggie design layer-check --project .
|
|
670
|
+
|
|
671
|
+
It runs only when an `@import "tailwindcss"` entry exists (or with
|
|
672
|
+
`--tailwind-v4`), honours `@layer` blocks and `layer(...)` imports, and fails on
|
|
673
|
+
unlayered element or icon rules that set utility-competing properties.
|
|
674
|
+
Unlayered `html`/`body` rules and class rules that set `display` are reported
|
|
675
|
+
as warnings. Keep `css-cascade-check` for the rendered-browser proof.
|
|
676
|
+
|
|
677
|
+
### Palette role contract
|
|
678
|
+
|
|
679
|
+
A template that offers brand colour choices exposes semantic roles instead of
|
|
680
|
+
raw hex values in markup: `canvas`, `panel`, `soft`, `text`, `accent`,
|
|
681
|
+
`accentText`, `onAccent`, `dark`, and `deep`. Bind them to CSS variables (for
|
|
682
|
+
example `--color-canvas`) so a palette swap never edits markup. The generator
|
|
683
|
+
keeps the signature palette and derives named variants by moving hue while
|
|
684
|
+
keeping lightness; neutrals never gain chroma, accent saturation is capped
|
|
685
|
+
(uncapped variants turn garish), `accentText` is forced to 4.5:1 on canvas,
|
|
686
|
+
and `onAccent` is the most legible of white, deep, text, and canvas:
|
|
687
|
+
|
|
688
|
+
maggie design palette generate --template <template-id> \
|
|
689
|
+
--canvas '#F7F3EE' --text '#1F1B16' --accent '#B5651D' \
|
|
690
|
+
[--roles roles.json] [--count 17] [--max-saturation 0.65] \
|
|
691
|
+
--output design/palettes.json
|
|
692
|
+
maggie design palette validate --palette design/palettes.json
|
|
693
|
+
|
|
694
|
+
Measure the signature roles from the template's rendered CSS, not from a
|
|
695
|
+
reference palette. Generation stops if the signature itself fails the
|
|
696
|
+
contrast rules; fix the signature rather than lowering the threshold. The
|
|
697
|
+
contract is
|
|
698
|
+
[palette-roles-v1.schema.json](../../bundled-contracts/maggie-design/palette-roles-v1.schema.json).
|
|
699
|
+
|
|
546
700
|
For generated or converted pages, run the markup gate before handing the page
|
|
547
701
|
to a host editor:
|
|
548
702
|
|