@topy-ai/maggie 0.7.48 → 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 (49) hide show
  1. package/README-zh-TW.md +2 -2
  2. package/README.md +31 -4
  3. package/bin/maggie.js +17 -4
  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-ops/marketing-run-v1.schema.json +89 -0
  9. package/bundled-contracts/maggie-ops/notification-catalogue-example-v1.json +941 -0
  10. package/bundled-contracts/maggie-ops/notification-catalogue-v1.schema.json +273 -0
  11. package/bundled-contracts/maggie-service-booking/fact-provenance-v1.schema.json +128 -0
  12. package/bundled-contracts/maggie-service-booking/import-stages-v1.schema.json +191 -0
  13. package/bundled-references/google-integrations-runbook.md +54 -0
  14. package/bundled-skills/maggie-content-localization/SKILL.md +17 -1
  15. package/bundled-skills/maggie-content-localization/references/email-and-lifecycle.md +83 -0
  16. package/bundled-skills/maggie-deployment/SKILL.md +89 -7
  17. package/bundled-skills/maggie-deployment/references/vps.md +38 -6
  18. package/bundled-skills/maggie-design/SKILL.md +55 -0
  19. package/bundled-skills/maggie-feedback/SKILL.md +29 -0
  20. package/bundled-skills/maggie-ops/SKILL.md +37 -0
  21. package/bundled-skills/maggie-ops/references/notifications-operations.md +154 -0
  22. package/bundled-skills/maggie-service-booking/SKILL.md +59 -0
  23. package/bundled-skills/maggie-social-share/SKILL.md +19 -2
  24. package/bundled-skills/maggie-social-share/references/marketing-run.md +57 -0
  25. package/bundled-skills/maggie-social-share/references/queue-and-connections.md +51 -0
  26. package/bundled-tools/clis/maggie_deployment.py +58 -17
  27. package/bundled-tools/clis/maggie_deployment_auth_canary.py +216 -0
  28. package/bundled-tools/clis/maggie_deployment_collect.py +225 -0
  29. package/bundled-tools/clis/maggie_deployment_readiness.py +53 -3
  30. package/bundled-tools/clis/maggie_design.py +8 -0
  31. package/bundled-tools/clis/maggie_design_verify.py +359 -0
  32. package/bundled-tools/clis/maggie_docs.py +82 -4
  33. package/bundled-tools/clis/maggie_email_localization.py +382 -0
  34. package/bundled-tools/clis/maggie_feedback.py +9 -2
  35. package/bundled-tools/clis/maggie_icon_glyph_check.py +382 -0
  36. package/bundled-tools/clis/maggie_icon_release_gate.py +28 -1
  37. package/bundled-tools/clis/maggie_localization.py +5 -0
  38. package/bundled-tools/clis/maggie_marketing_run.py +272 -0
  39. package/bundled-tools/clis/maggie_notifications.py +528 -0
  40. package/bundled-tools/clis/maggie_ops.py +87 -0
  41. package/bundled-tools/clis/maggie_schedule.py +12 -2
  42. package/bundled-tools/clis/maggie_service_import.py +520 -0
  43. package/bundled-tools/clis/maggie_social_queue.py +104 -0
  44. package/bundled-tools/clis/maggie_social_share.py +6 -2
  45. package/bundled-tools/runtime/google_ads.py +306 -0
  46. package/bundled-tools/runtime/google_capabilities.py +18 -2
  47. package/bundled-tools/runtime/google_remediation.py +152 -0
  48. package/package.json +1 -1
  49. package/references/google-integrations-runbook.md +54 -0
@@ -0,0 +1,273 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://maggie.noblox.app/contracts/maggie-ops/notification-catalogue-v1.schema.json",
4
+ "title": "Maggie website notification catalogue v1",
5
+ "description": "Provider-neutral catalogue of every website email, in-app and SMS notification. Validated by `maggie ops notifications catalogue validate`, which also enforces cross-field rules (marketing consent/unsubscribe/queue, operations alerts on the operations queue, durable outbox for external lead email).",
6
+ "type": "object",
7
+ "required": [
8
+ "schemaVersion",
9
+ "retention",
10
+ "entries"
11
+ ],
12
+ "properties": {
13
+ "schemaVersion": {
14
+ "const": "maggie-notification-catalogue.v1"
15
+ },
16
+ "project": {
17
+ "type": "string"
18
+ },
19
+ "retention": {
20
+ "type": "object",
21
+ "required": [
22
+ "webhook-payload",
23
+ "delivery-record",
24
+ "rendered-snapshot",
25
+ "template-variables"
26
+ ],
27
+ "properties": {
28
+ "webhook-payload": {
29
+ "type": "object",
30
+ "required": [
31
+ "days"
32
+ ],
33
+ "properties": {
34
+ "days": {
35
+ "type": "integer",
36
+ "minimum": 1
37
+ },
38
+ "storeRawPayload": {
39
+ "type": "boolean"
40
+ },
41
+ "minimizedFields": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "string"
45
+ }
46
+ }
47
+ }
48
+ },
49
+ "delivery-record": {
50
+ "type": "object",
51
+ "required": [
52
+ "days"
53
+ ],
54
+ "properties": {
55
+ "days": {
56
+ "type": "integer",
57
+ "minimum": 1
58
+ },
59
+ "storeRawPayload": {
60
+ "type": "boolean"
61
+ },
62
+ "minimizedFields": {
63
+ "type": "array",
64
+ "items": {
65
+ "type": "string"
66
+ }
67
+ }
68
+ }
69
+ },
70
+ "rendered-snapshot": {
71
+ "type": "object",
72
+ "required": [
73
+ "days"
74
+ ],
75
+ "properties": {
76
+ "days": {
77
+ "type": "integer",
78
+ "minimum": 1
79
+ },
80
+ "storeRawPayload": {
81
+ "type": "boolean"
82
+ },
83
+ "minimizedFields": {
84
+ "type": "array",
85
+ "items": {
86
+ "type": "string"
87
+ }
88
+ }
89
+ }
90
+ },
91
+ "template-variables": {
92
+ "type": "object",
93
+ "required": [
94
+ "days"
95
+ ],
96
+ "properties": {
97
+ "days": {
98
+ "type": "integer",
99
+ "minimum": 1
100
+ },
101
+ "storeRawPayload": {
102
+ "type": "boolean"
103
+ },
104
+ "minimizedFields": {
105
+ "type": "array",
106
+ "items": {
107
+ "type": "string"
108
+ }
109
+ }
110
+ }
111
+ }
112
+ },
113
+ "additionalProperties": false
114
+ },
115
+ "entries": {
116
+ "type": "array",
117
+ "minItems": 1,
118
+ "items": {
119
+ "type": "object",
120
+ "required": [
121
+ "alias",
122
+ "section",
123
+ "trigger",
124
+ "audience",
125
+ "category",
126
+ "consent",
127
+ "locales",
128
+ "variables",
129
+ "idempotencyKey",
130
+ "retentionDays",
131
+ "provider",
132
+ "channels",
133
+ "status",
134
+ "delivery",
135
+ "queue"
136
+ ],
137
+ "properties": {
138
+ "alias": {
139
+ "type": "string",
140
+ "pattern": "^[a-z0-9][a-z0-9._-]{0,99}$"
141
+ },
142
+ "section": {
143
+ "enum": [
144
+ "identity-security",
145
+ "account",
146
+ "business",
147
+ "billing",
148
+ "support",
149
+ "marketing",
150
+ "deliverability-ops"
151
+ ]
152
+ },
153
+ "trigger": {
154
+ "type": "string",
155
+ "minLength": 1
156
+ },
157
+ "audience": {
158
+ "enum": [
159
+ "visitor",
160
+ "user",
161
+ "merchant",
162
+ "team",
163
+ "operator"
164
+ ]
165
+ },
166
+ "category": {
167
+ "enum": [
168
+ "transactional",
169
+ "support",
170
+ "marketing"
171
+ ]
172
+ },
173
+ "consent": {
174
+ "enum": [
175
+ "not-required",
176
+ "opt-in-required",
177
+ "operator-only"
178
+ ]
179
+ },
180
+ "locales": {
181
+ "type": "array",
182
+ "minItems": 1,
183
+ "items": {
184
+ "type": "string"
185
+ }
186
+ },
187
+ "markets": {
188
+ "type": "array",
189
+ "items": {
190
+ "type": "string"
191
+ }
192
+ },
193
+ "variables": {
194
+ "type": "array",
195
+ "items": {
196
+ "type": "string",
197
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$"
198
+ }
199
+ },
200
+ "cta": {
201
+ "type": [
202
+ "object",
203
+ "null"
204
+ ],
205
+ "properties": {
206
+ "label": {
207
+ "type": "string"
208
+ },
209
+ "route": {
210
+ "type": "string"
211
+ }
212
+ }
213
+ },
214
+ "idempotencyKey": {
215
+ "type": "string",
216
+ "pattern": "\\{"
217
+ },
218
+ "retentionDays": {
219
+ "type": "integer",
220
+ "minimum": 1
221
+ },
222
+ "provider": {
223
+ "type": "string"
224
+ },
225
+ "channels": {
226
+ "type": "array",
227
+ "minItems": 1,
228
+ "items": {
229
+ "enum": [
230
+ "email",
231
+ "in-app",
232
+ "sms"
233
+ ]
234
+ }
235
+ },
236
+ "status": {
237
+ "enum": [
238
+ "implemented",
239
+ "provider-owned",
240
+ "reserved",
241
+ "missing"
242
+ ]
243
+ },
244
+ "delivery": {
245
+ "enum": [
246
+ "outbox",
247
+ "direct",
248
+ "provider"
249
+ ]
250
+ },
251
+ "queue": {
252
+ "enum": [
253
+ "user",
254
+ "operations",
255
+ "marketing"
256
+ ]
257
+ },
258
+ "lead": {
259
+ "type": "boolean"
260
+ },
261
+ "unsubscribe": {
262
+ "type": "boolean"
263
+ },
264
+ "frequency": {
265
+ "type": "string"
266
+ }
267
+ },
268
+ "additionalProperties": false
269
+ }
270
+ }
271
+ },
272
+ "additionalProperties": false
273
+ }
@@ -0,0 +1,128 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://maggie.noblox.app/contracts/service-fact-provenance-v1.json",
4
+ "title": "Maggie field-level provenance for imported business facts",
5
+ "description": "Per-field evidence from the user and from providers (Fresha, Google, website, ...). User-entered values are authoritative. Provider disagreement above policy.minConfidence becomes a needs-review conflict; a provider error is recorded and never blocks other sources. Merge with `maggie service fact-provenance`, which writes a maggie-service-fact-merge.v1 artifact for the host review UI.",
6
+ "type": "object",
7
+ "required": [
8
+ "schemaVersion",
9
+ "entityId",
10
+ "fields"
11
+ ],
12
+ "properties": {
13
+ "schemaVersion": {
14
+ "const": "maggie-service-fact-provenance.v1"
15
+ },
16
+ "entityId": {
17
+ "type": "string",
18
+ "minLength": 1
19
+ },
20
+ "policy": {
21
+ "type": "object",
22
+ "properties": {
23
+ "precedence": {
24
+ "type": "array",
25
+ "items": {
26
+ "type": "string"
27
+ },
28
+ "prefixItems": [
29
+ {
30
+ "const": "user"
31
+ }
32
+ ],
33
+ "minItems": 1
34
+ },
35
+ "fieldPrecedence": {
36
+ "type": "object",
37
+ "additionalProperties": {
38
+ "type": "array",
39
+ "prefixItems": [
40
+ {
41
+ "const": "user"
42
+ }
43
+ ],
44
+ "items": {
45
+ "type": "string"
46
+ }
47
+ }
48
+ },
49
+ "minConfidence": {
50
+ "type": "number",
51
+ "minimum": 0,
52
+ "maximum": 1
53
+ }
54
+ },
55
+ "additionalProperties": false
56
+ },
57
+ "fields": {
58
+ "type": "object",
59
+ "minProperties": 1,
60
+ "additionalProperties": {
61
+ "$ref": "#/$defs/field"
62
+ }
63
+ }
64
+ },
65
+ "$defs": {
66
+ "field": {
67
+ "type": "object",
68
+ "properties": {
69
+ "user": {
70
+ "type": [
71
+ "object",
72
+ "null"
73
+ ],
74
+ "required": [
75
+ "value"
76
+ ],
77
+ "properties": {
78
+ "value": {},
79
+ "at": {
80
+ "type": "string",
81
+ "format": "date-time"
82
+ },
83
+ "actor": {
84
+ "type": "string"
85
+ }
86
+ }
87
+ },
88
+ "sources": {
89
+ "type": "array",
90
+ "items": {
91
+ "type": "object",
92
+ "required": [
93
+ "source"
94
+ ],
95
+ "properties": {
96
+ "source": {
97
+ "type": "string",
98
+ "pattern": "^[a-z][a-z0-9_-]{0,39}$",
99
+ "not": {
100
+ "const": "user"
101
+ }
102
+ },
103
+ "value": {},
104
+ "confidence": {
105
+ "type": "number",
106
+ "minimum": 0,
107
+ "maximum": 1
108
+ },
109
+ "observedAt": {
110
+ "type": "string",
111
+ "format": "date-time"
112
+ },
113
+ "sourceRef": {
114
+ "type": "string",
115
+ "description": "public page or record reference; never credentials"
116
+ },
117
+ "error": {
118
+ "type": "string",
119
+ "pattern": "^[a-z0-9][a-z0-9._-]{0,79}$"
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ },
127
+ "additionalProperties": true
128
+ }
@@ -0,0 +1,191 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://maggie.noblox.app/contracts/service-import-stages-v1.json",
4
+ "title": "Maggie provider-neutral multi-stage service import state",
5
+ "description": "One import runs source, enrichment, research and verification stages. Stage keys are idempotent and reused on retry; running stages heartbeat and become stale after staleAfterSeconds; downstream stages record the source revision they consumed so superseded (out-of-order) results are discarded; cancellation is propagated to every stage and late results are discarded. Validate with `maggie service import-stages-validate`.",
6
+ "type": "object",
7
+ "required": [
8
+ "schemaVersion",
9
+ "importId",
10
+ "idempotencyKey",
11
+ "state",
12
+ "staleAfterSeconds",
13
+ "stages"
14
+ ],
15
+ "properties": {
16
+ "schemaVersion": {
17
+ "const": "maggie-service-import-stages.v1"
18
+ },
19
+ "importId": {
20
+ "type": "string",
21
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$"
22
+ },
23
+ "idempotencyKey": {
24
+ "type": "string",
25
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,159}$"
26
+ },
27
+ "provider": {
28
+ "type": "string"
29
+ },
30
+ "state": {
31
+ "enum": [
32
+ "queued",
33
+ "running",
34
+ "succeeded",
35
+ "partial",
36
+ "failed",
37
+ "cancelled",
38
+ "stale"
39
+ ]
40
+ },
41
+ "staleAfterSeconds": {
42
+ "type": "integer",
43
+ "minimum": 30,
44
+ "maximum": 86400
45
+ },
46
+ "evaluatedAt": {
47
+ "type": "string",
48
+ "format": "date-time"
49
+ },
50
+ "nextAction": {
51
+ "type": "string",
52
+ "minLength": 1
53
+ },
54
+ "cancellation": {
55
+ "type": "object",
56
+ "properties": {
57
+ "requested": {
58
+ "type": "boolean"
59
+ },
60
+ "requestedAt": {
61
+ "type": "string",
62
+ "format": "date-time"
63
+ },
64
+ "propagated": {
65
+ "type": "boolean"
66
+ }
67
+ },
68
+ "additionalProperties": false
69
+ },
70
+ "stages": {
71
+ "type": "array",
72
+ "minItems": 4,
73
+ "maxItems": 4,
74
+ "items": {
75
+ "$ref": "#/$defs/stage"
76
+ }
77
+ }
78
+ },
79
+ "$defs": {
80
+ "stage": {
81
+ "type": "object",
82
+ "required": [
83
+ "name",
84
+ "stageKey",
85
+ "state",
86
+ "attempt",
87
+ "maxAttempts",
88
+ "progress"
89
+ ],
90
+ "properties": {
91
+ "name": {
92
+ "enum": [
93
+ "source",
94
+ "enrichment",
95
+ "research",
96
+ "verification"
97
+ ]
98
+ },
99
+ "stageKey": {
100
+ "type": "string",
101
+ "description": "<importId>:<stage>[:<input hash>]; reused on retry"
102
+ },
103
+ "dependsOn": {
104
+ "type": "array",
105
+ "items": {
106
+ "enum": [
107
+ "source",
108
+ "enrichment",
109
+ "research",
110
+ "verification"
111
+ ]
112
+ },
113
+ "description": "defaults: enrichment/research -> source; verification -> enrichment, research"
114
+ },
115
+ "state": {
116
+ "enum": [
117
+ "queued",
118
+ "running",
119
+ "succeeded",
120
+ "failed",
121
+ "skipped",
122
+ "cancelled"
123
+ ]
124
+ },
125
+ "attempt": {
126
+ "type": "integer",
127
+ "minimum": 0
128
+ },
129
+ "maxAttempts": {
130
+ "type": "integer",
131
+ "minimum": 1
132
+ },
133
+ "progress": {
134
+ "type": "object",
135
+ "required": [
136
+ "done",
137
+ "total"
138
+ ],
139
+ "properties": {
140
+ "done": {
141
+ "type": "integer",
142
+ "minimum": 0
143
+ },
144
+ "total": {
145
+ "type": "integer",
146
+ "minimum": 0
147
+ }
148
+ }
149
+ },
150
+ "startedAt": {
151
+ "type": "string",
152
+ "format": "date-time"
153
+ },
154
+ "heartbeatAt": {
155
+ "type": "string",
156
+ "format": "date-time"
157
+ },
158
+ "completedAt": {
159
+ "type": "string",
160
+ "format": "date-time"
161
+ },
162
+ "resultRevision": {
163
+ "type": [
164
+ "string",
165
+ "integer"
166
+ ],
167
+ "description": "source stage: revision of its current result"
168
+ },
169
+ "inputRevision": {
170
+ "type": [
171
+ "string",
172
+ "integer"
173
+ ],
174
+ "description": "downstream stages: source revision consumed"
175
+ },
176
+ "resultDiscarded": {
177
+ "type": "boolean"
178
+ },
179
+ "errorCode": {
180
+ "type": [
181
+ "string",
182
+ "null"
183
+ ],
184
+ "pattern": "^[a-z0-9][a-z0-9._-]{0,79}$"
185
+ }
186
+ },
187
+ "additionalProperties": true
188
+ }
189
+ },
190
+ "additionalProperties": true
191
+ }
@@ -119,6 +119,60 @@ selected `resource`. A report that only proves account discovery, or comes from
119
119
  the wrong browser account/property, is rejected before a write workflow can use
120
120
  it.
121
121
 
122
+ ## Failure remediation
123
+
124
+ A failed check must name the one prerequisite that failed, not a generic
125
+ "access denied". Put only the provider error code/reason and HTTP status in the
126
+ row's optional `failure` field; the validator returns `remediation` with a
127
+ stable `code`, the failed `prerequisite`, and one safe `nextAction`. It never
128
+ echoes the input text. `maggie ops google-diagnose` applies the same mapping to
129
+ one error.
130
+
131
+ | Signal | Failed prerequisite | Safe next action |
132
+ |---|---|---|
133
+ | `invalid_scope` (ADC) | OAuth scope request | Re-run the ADC/impersonation command with the exact allowlisted scope |
134
+ | `ACCESS_TOKEN_SCOPE_INSUFFICIENT` | Granted token scope | Mint a new token that includes the provider scope |
135
+ | `redirect_uri_mismatch` | OAuth client redirect URI | Add the exact callback URL to the client, or use the client that lists it |
136
+ | Consent screen not configured / app not verified | OAuth consent configuration | Configure user type, test users, or verification in the client's project |
137
+ | `SERVICE_DISABLED` / `accessNotConfigured` | Cloud API enablement | Enable the API in the project that owns the client or service account |
138
+ | `RESOURCE_EXHAUSTED` / HTTP 429 | Provider quota | Stop retrying; wait for the window or request quota; record `blocked` |
139
+ | `DEVELOPER_TOKEN_PARAMETER_MISSING` | Ads developer token | Install the token in the host secret store; send the `developer-token` header |
140
+ | `DEVELOPER_TOKEN_NOT_APPROVED` | Ads token access level | Apply for Basic/Standard access, or use a test account |
141
+ | `DEVELOPER_TOKEN_PROHIBITED` | Developer token/project pairing | Use the OAuth client from the paired Cloud project, or request access for the new project |
142
+ | `USER_PERMISSION_DENIED` with a manager | `login-customer-id` | Set it to the manager that links the target, or omit it for direct access |
143
+ | `USER_PERMISSION_DENIED` / `CUSTOMER_NOT_FOUND` | Ads account access | Grant the user access to the customer or its manager |
144
+ | GA4 "insufficient permissions for this property" | GA4 property membership | Add the principal to the property with Viewer or higher |
145
+ | `iam.serviceAccounts.getAccessToken` denied | Service-account impersonation | Grant `roles/iam.serviceAccountTokenCreator` on the target service account |
146
+ | Other `PERMISSION_DENIED` / HTTP 403 | Product role | Grant the product-level role; Cloud IAM alone is insufficient |
147
+ | `invalid_grant` / HTTP 401 | Credential validity | Reconnect; the stored token is expired or revoked |
148
+
149
+ ## Google Ads read-only preflight and Keyword Planner
150
+
151
+ `maggie ops google-ads-preflight` checks, in order and independently: customer
152
+ and `login-customer-id` format (10 digits; hyphens stripped), developer-token
153
+ presence (environment variable name only), the `adwords` scope via token
154
+ introspection (token sent in a POST body, never a URL),
155
+ `customers:listAccessibleCustomers`, the manager hierarchy through
156
+ `customer_client` when a manager is used, a `customer` read (currency, manager,
157
+ test-account flags), and Keyword Planner readiness. The Keyword Planner check
158
+ reports `ready` without spending a request unless `--probe-keyword-planner` is
159
+ passed, which sends one one-result `generateKeywordIdeas` call. Every failure
160
+ carries the same `remediation` mapping as above.
161
+
162
+ `maggie ops google-ads-keyword-ideas` sends read-only `generateKeywordIdeas`
163
+ requests (keyword, URL, or keyword+URL seed; language and geo-target constants;
164
+ bounded pagination) and normalizes each idea to `text`, `avgMonthlySearches`,
165
+ `competition`, `competitionIndex`, and `lowTopOfPageBid`/`highTopOfPageBid`
166
+ converted from micros into the customer currency.
167
+
168
+ The adapter refuses any request outside `listAccessibleCustomers`,
169
+ `googleAds:search`, and `generateKeywordIdeas`, so it cannot mutate campaigns,
170
+ budgets, or keywords. The REST API version defaults to `v21` and can be set with
171
+ `--api-version` or `MAGGIE_GOOGLE_ADS_API_VERSION`; check Google's sunset
172
+ schedule and use a currently supported version. Keyword Planner volumes are
173
+ estimates for planning and require an approved developer token on a real
174
+ account; test accounts return no metrics.
175
+
122
176
  ## Capability matrix
123
177
 
124
178
  Every provider/resource row must expose this shape:
@@ -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.3.0
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