@topy-ai/maggie 0.7.31 → 0.7.33

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 (30) hide show
  1. package/README-zh-TW.md +2 -2
  2. package/README.md +101 -19
  3. package/bin/maggie.js +6 -0
  4. package/bundled-references/google-integrations-runbook.md +26 -0
  5. package/bundled-skills/maggie-blog/SKILL.md +18 -1
  6. package/bundled-skills/maggie-dash/SKILL.md +18 -3
  7. package/bundled-skills/maggie-deployment/SKILL.md +72 -0
  8. package/bundled-skills/maggie-deployment/references/vps.md +26 -0
  9. package/bundled-skills/maggie-design/SKILL.md +16 -0
  10. package/bundled-skills/maggie-feedback/SKILL.md +11 -2
  11. package/bundled-skills/maggie-seo-geo/SKILL.md +22 -1
  12. package/bundled-tools/clis/maggie.py +12 -0
  13. package/bundled-tools/clis/maggie_analytics.py +34 -0
  14. package/bundled-tools/clis/maggie_blog.py +13 -0
  15. package/bundled-tools/clis/maggie_dash.py +17 -0
  16. package/bundled-tools/clis/maggie_deployment.py +91 -7
  17. package/bundled-tools/clis/maggie_feedback.py +18 -4
  18. package/bundled-tools/clis/maggie_icon_release_gate.py +79 -0
  19. package/bundled-tools/clis/maggie_migration.py +71 -0
  20. package/bundled-tools/clis/maggie_release_manifest.py +81 -0
  21. package/bundled-tools/clis/maggie_runtime_preflight.py +76 -0
  22. package/bundled-tools/clis/site_audit.py +78 -5
  23. package/bundled-tools/integrations/analytics.md +20 -0
  24. package/bundled-tools/runtime/maggie_blog_publish.py +38 -0
  25. package/bundled-tools/runtime/maggie_dash_panels.py +111 -0
  26. package/bundled-tools/runtime/maggie_quality.py +1 -1
  27. package/bundled-tools/runtime/maggie_sections.py +39 -1
  28. package/bundled-tools/runtime/site_baseline.py +23 -2
  29. package/package.json +1 -1
  30. package/references/google-integrations-runbook.md +26 -0
package/README-zh-TW.md CHANGED
@@ -8,7 +8,7 @@ Codex、Claude Code 與相容的 coding agents。
8
8
  ## 安裝
9
9
 
10
10
  ```bash
11
- npx @topy-ai/maggie@0.7.31 init --agent all
11
+ npx @topy-ai/maggie@0.7.33 init --agent all
12
12
  npx @topy-ai/maggie doctor --project .
13
13
  ```
14
14
 
@@ -25,7 +25,7 @@ maggie doctor --project . --require-bootstrap --strict
25
25
  deployment、memory、feedback 和 MaggieDash。內容先 draft/review,外部寫入、
26
26
  publish 與 production deployment 需要明確確認。
27
27
 
28
- 目前 release 是 `0.7.31`,加入 update 差異摘要、atomic installer copy、subset install manifest merge,以及 inventory drift 時 doctor non-zero。0.7.30 補上 feedback tracking issue closeout gate 與 batch 14/15 audit ledger。0.7.29 修正 npm package landing page 使用英文 README;0.7.28 加入 versioned Gemini model policy、明確 fallback 與 provenance、bundled-first CLI
28
+ 目前 release 是 `0.7.33`,記錄並審查一筆更正回饋;確認 0.7.32 已正確處理 batch index,沒有修改已驗證的 shared behavior。0.7.32 加入 feedback 一基索引支援、MaggieDash panel 的 source/freshness/error evidence、Consent Mode 與實際 write/no-op reconciliation、SEO baseline recapture、sitemap origin rebasing、content-diff scope、migration ledger reconciliation、deployment credential preflight、release provenance/runtime preflight、icon release gate、opt-in blog auto-publish gate,以及 least-privilege VPS deployer。也修復 top-level `maggie feedback` dispatcher parity。0.7.31 加入 update 差異摘要、atomic installer copy、subset install manifest merge,以及 inventory drift 時 doctor non-zero。0.7.30 補上 feedback tracking issue closeout gate 與 batch 14/15 audit ledger。0.7.29 修正 npm package landing page 使用英文 README;0.7.28 加入 versioned Gemini model policy、明確 fallback 與 provenance、bundled-first CLI
29
29
  dispatch、`maggie --version`、feedback batch review 聚合與重複偵測、marketplace enrichment evidence、booking
30
30
  worker/resolver evidence,以及 read-only migration preflight。它也包含 host-owned mobile app surface contract、signed-in
31
31
  camera-state QA、直接 Astro route resolution、correlated feedback batch,以及
package/README.md CHANGED
@@ -60,21 +60,31 @@ Maggie keeps the existing project foundation and asks for decisions before
60
60
  shared routes, analytics, or publishing boundaries change. The current
61
61
  package ships 19 installable skills and a local-first MaggieDash foundation.
62
62
 
63
- The current release is `0.7.31`. It keeps the 0.7.30 feature set and adds
64
- bounded update diff evidence, atomic installer copies, subset-install manifest
65
- merge, and non-zero doctor failures for inventory drift. The 0.7.30 release
66
- adds the internal feedback issue closeout gate. The 0.7.29 release fixes npm
67
- metadata to render this English README as the package landing page. The 0.7.28
68
- release adds a versioned Gemini model policy with
69
- an explicit fallback and provenance, bundled-CLI version discovery, and a
70
- bundled-first dispatcher that avoids stale project wrappers. It also adds
71
- privacy-safe feedback batch review/duplicate detection, marketplace
72
- enrichment evidence, bounded booking worker/resolver reports, and read-only
73
- migration preflight contracts. The release retains the provider-neutral MaggieDash host
74
- adapter contract, idempotent PostgreSQL starter schema, type-only backend
75
- boundary, runtime endpoint conformance evidence, safe quota/error summaries,
76
- stale marketplace readiness revalidation, explicit VPS port/origin planning,
77
- first-release rollback evidence, and clean non-interactive bootstrap errors.
63
+ The current release is `0.7.33`. It records the reviewed correction for the
64
+ batch-index feedback workflow while keeping the verified 0.7.32 behavior
65
+ unchanged. The 0.7.32 release added explicit one-based feedback batch index
66
+ support, source/freshness/error evidence for MaggieDash measurement
67
+ panels, Consent Mode and actual-write reconciliation contracts, reviewed SEO
68
+ baseline recapture, requested-origin sitemap rebasing, explicit content-diff
69
+ scope, migration-ledger reconciliation, canonical deployment credential
70
+ preflight, unified release provenance, runtime preflight classification,
71
+ design icon-release gating, opt-in blog auto-publish gating, and a
72
+ least-privilege VPS deployer plan. It also restores top-level `maggie
73
+ feedback` dispatch parity. The 0.7.31 release added bounded update diff
74
+ evidence, atomic installer copies, subset-install manifest merge, and
75
+ non-zero doctor failures for inventory drift. The 0.7.30 release added the
76
+ internal feedback issue closeout gate. The 0.7.29 release fixed npm metadata
77
+ to render this English README as the package landing page. The 0.7.28
78
+ release added a versioned Gemini model policy with an explicit fallback and
79
+ provenance, bundled-CLI version discovery, a bundled-first dispatcher that
80
+ avoids stale project wrappers, privacy-safe feedback batch review/duplicate
81
+ detection, marketplace enrichment evidence, bounded booking worker/resolver
82
+ reports, and read-only migration preflight contracts. The release retains the
83
+ provider-neutral MaggieDash host adapter contract, idempotent PostgreSQL
84
+ starter schema, type-only backend boundary, runtime endpoint conformance
85
+ evidence, safe quota/error summaries, stale marketplace readiness
86
+ revalidation, explicit VPS port/origin planning, first-release rollback
87
+ evidence, and clean non-interactive bootstrap errors.
78
88
  It retains the host-owned mobile app surface
79
89
  contract (`maggie design app-init`/`app-validate`), direct Astro route
80
90
  resolution, correlated feedback batches, and runtime package-version capture.
@@ -131,6 +141,16 @@ evidence proves that a dashboard screen mounted and executed its checks, so a
131
141
  static component scan cannot pass by itself. MaggieDash hosts may also expose
132
142
  the provider-neutral activity-log and managed-navigation contracts.
133
143
 
144
+ Measurement panels must report their source, capture time/freshness, and
145
+ independent warning/error state. Validate the host-produced report before
146
+ using it in a release decision:
147
+
148
+ ```bash
149
+ maggie dash panels-validate \
150
+ --report .maggie/measurement-panels.json \
151
+ --output docs/measurement-panels-validation.json
152
+ ```
153
+
134
154
  The current package also includes reusable safeguards from the latest feedback
135
155
  review: `maggie dash api-contract` checks declared request and 2xx response
136
156
  shapes; `maggie blog check-gate`/`approve` enforces review before publish;
@@ -251,10 +271,17 @@ maggie ops favicon-check ... # served favicon behaviour check
251
271
  maggie deployment | migration | release | analytics | schedule
252
272
  maggie deployment readiness --project PATH
253
273
  maggie deployment parity --project PATH --evidence FILE --output FILE
274
+ maggie deployment runtime-preflight --evidence FILE --output FILE
275
+ maggie deployment release-manifest --evidence FILE --output FILE
276
+ maggie deployment credential-preflight [--expected-host HOST]
254
277
  maggie migration identity --identity-file FILE [--expected-file FILE]
255
278
  maggie migration preflight --evidence FILE [--output FILE]
279
+ maggie migration reconcile --evidence FILE --migration-dir DIR [--output FILE]
256
280
  maggie deployment canary --asset URL=SHA256 --render-report report.json
257
281
  maggie design icon-inventory --source-dir src --runtime assets/icons.css
282
+ maggie design icon-release-gate --icon-report FILE --browser-report FILE --output FILE
283
+ maggie dash panels-validate --report FILE [--output FILE]
284
+ maggie blog auto-publish-gate --evidence FILE --output FILE
258
285
  maggie api lifecycle | site-audit | ops audit
259
286
  maggie marketplace enrichment-report --project PATH --input FILE [--output FILE]
260
287
  maggie service worker-health --project PATH --evidence FILE [--output FILE]
@@ -320,6 +347,16 @@ The adapter is an argv array, receives a versioned request on stdin, and must
320
347
  print only the three review-policy fields. Provider stderr is suppressed and a
321
348
  missing local store without an adapter fails closed.
322
349
 
350
+ Automated blog publishing stays human-reviewed by default. If a host opts in,
351
+ the gate requires explicit review bypass evidence plus locale, image-alt, SEO,
352
+ sitemap, indexing-notification, and audit checks:
353
+
354
+ ```bash
355
+ maggie blog auto-publish-gate \
356
+ --evidence .maggie/blog/auto-publish-evidence.json \
357
+ --output docs/blog-auto-publish-gate.json
358
+ ```
359
+
323
360
  ### Design and deployment release gates
324
361
 
325
362
  Check source-to-runtime icon coverage before shipping a design:
@@ -334,6 +371,17 @@ and is only static name coverage. Pair changed icon controls with a
334
371
  browser-runtime `icon-rendered` assertion so the host adapter proves that the
335
372
  glyph is visible, has positive dimensions, and has an accessible label.
336
373
 
374
+ For a design release, compose the static inventory with browser-painted glyph
375
+ evidence. Every tested icon must be visible, have positive dimensions, expose
376
+ an accessible name, and load successfully:
377
+
378
+ ```bash
379
+ maggie design icon-release-gate \
380
+ --icon-report docs/icon-inventory.json \
381
+ --browser-report .maggie/icon-browser-evidence.json \
382
+ --output docs/icon-release-gate.json
383
+ ```
384
+
337
385
  After deployment, compare an immutable asset fingerprint and validate the
338
386
  browser adapter's rendered report:
339
387
 
@@ -362,6 +410,37 @@ privileges, route API status, stale resident processes, source-job IDs, and
362
410
  browser/driver compatibility. Add the resulting report to readiness with
363
411
  `--runtime-parity`; a failed report blocks the gate.
364
412
 
413
+ Use the runtime preflight to classify dependency, browser, worker, database,
414
+ and route failures independently. A non-pass check must include remediation:
415
+
416
+ ```bash
417
+ maggie deployment runtime-preflight \
418
+ --evidence .maggie/verification/runtime-preflight-input.json \
419
+ --output .maggie/verification/runtime-preflight.json
420
+ ```
421
+
422
+ Before activation, reconcile the host's `maggie_schema_migrations` ledger with
423
+ local SQL files. This is read-only and produces an ordered missing-migration
424
+ plan. Deployment credential checks similarly verify only the presence of the
425
+ canonical names, never their values:
426
+
427
+ ```bash
428
+ maggie migration reconcile \
429
+ --evidence .maggie/migration/ledger.json \
430
+ --migration-dir migrations \
431
+ --output docs/migration-reconcile.json
432
+ maggie deployment credential-preflight --expected-host example.com
433
+ ```
434
+
435
+ Finally, combine route, migration, runtime, asset/icon, edge-cache,
436
+ browser/render, and rollback evidence into one release provenance manifest:
437
+
438
+ ```bash
439
+ maggie deployment release-manifest \
440
+ --evidence .maggie/verification/release-evidence.json \
441
+ --output docs/release-manifest.json
442
+ ```
443
+
365
444
  Unit regression does not establish runtime release readiness. Produce unit
366
445
  evidence and then validate all required release evidence slots; add
367
446
  `--runtime-parity` when the deployment uses the worker/browser surfaces above:
@@ -421,8 +500,8 @@ artifact schemas.
421
500
  Recommended upgrade sequence for the current release:
422
501
 
423
502
  ```bash
424
- npx @topy-ai/maggie@0.7.31 update --project . --force
425
- npx @topy-ai/maggie@0.7.31 cleanup --project .
503
+ npx @topy-ai/maggie@0.7.33 update --project . --force
504
+ npx @topy-ai/maggie@0.7.33 cleanup --project .
426
505
  ```
427
506
 
428
507
  Maintainers should pass npm credentials through the repository helper, never
@@ -588,10 +667,13 @@ For related observations, create 1–50 local drafts with shared correlation
588
667
  metadata and review them before submitting:
589
668
 
590
669
  ```bash
591
- maggie feedback batch --project . --batch-file ./feedback-batch.json
670
+ maggie feedback batch --project . --batch-file ./feedback-batch.json \
671
+ --batch-base 0
592
672
  ```
593
673
 
594
- The batch command never submits automatically or writes active memory.
674
+ The batch command never submits automatically or writes active memory. External
675
+ one-based runners can pass `--batch-base 1`; Maggie stores canonical indexes
676
+ zero-based and validates the final item boundary explicitly.
595
677
 
596
678
  Aggregate a correlated batch before reviewing individual drafts:
597
679
 
package/bin/maggie.js CHANGED
@@ -91,6 +91,7 @@ Usage:
91
91
  maggie design app-init --project PATH --route /app/valuation --auth-mode email-password --confirm
92
92
  maggie design app-validate --project PATH --plan PATH --runtime-evidence FILE --rendered-dir PATH --confirm
93
93
  maggie design icon-inventory --project PATH --source-dir src --runtime assets/icons.css --output docs/icon-inventory.json
94
+ maggie design icon-release-gate --icon-report FILE --browser-report FILE --output FILE
94
95
  maggie design author --project PATH --route /about --purpose TEXT --audience TEXT --confirm
95
96
  maggie design in-place --project PATH --route /pricing [--content-source PATH]
96
97
  maggie design section --project PATH --route /pricing --section-id ID
@@ -117,6 +118,8 @@ Usage:
117
118
  maggie deployment readiness --project PATH
118
119
  maggie deployment parity --project PATH --evidence FILE --output FILE
119
120
  maggie deployment canary --project PATH --asset URL=SHA256 --render-report report.json --output docs/deployment-canary.json
121
+ maggie deployment release-manifest --evidence FILE --output FILE
122
+ maggie deployment runtime-preflight --evidence FILE --output FILE
120
123
  maggie migration preflight --evidence FILE --output FILE
121
124
  maggie migration --project PATH --environment staging
122
125
  maggie migration identity --identity-file FILE [--expected-file FILE]
@@ -504,6 +507,7 @@ try {
504
507
  else if (command === "clone") workflowCli("maggie_clone.py", args);
505
508
  else if (command === "clone-to-template") workflowCli("maggie_clone_to_template.py", args);
506
509
  else if (command === "design" && args[0] === "icon-inventory") workflowCli("maggie_icon_inventory.py", args.slice(1));
510
+ else if (command === "design" && args[0] === "icon-release-gate") workflowCli("maggie_icon_release_gate.py", args.slice(1));
507
511
  else if (command === "design") workflowCli("maggie_design.py", args);
508
512
  else if (command === "auth") workflowCli("maggie_auth.py", args);
509
513
  else if (command === "blog") workflowCli("maggie_blog.py", args);
@@ -514,6 +518,8 @@ try {
514
518
  else if (command === "deployment" && args[0] === "canary") workflowCli("maggie_deployment_canary.py", args.slice(1));
515
519
  else if (command === "deployment" && args[0] === "readiness") workflowCli("maggie_deployment_readiness.py", args.slice(1));
516
520
  else if (command === "deployment" && args[0] === "parity") workflowCli("maggie_deployment_parity.py", args.slice(1));
521
+ else if (command === "deployment" && args[0] === "release-manifest") workflowCli("maggie_release_manifest.py", args.slice(1));
522
+ else if (command === "deployment" && args[0] === "runtime-preflight") workflowCli("maggie_runtime_preflight.py", args.slice(1));
517
523
  else if (command === "deployment") workflowCli("maggie_deployment.py", args);
518
524
  else if (command === "migration") workflowCli("maggie_migration.py", args);
519
525
  else if (command === "schedule") workflowCli("maggie_schedule.py", args);
@@ -52,6 +52,32 @@ The validator accepts the complete provider allowlist, but a capability is
52
52
  verified only when its corresponding required scope is present. Scope presence
53
53
  alone never proves product access.
54
54
 
55
+ ### GTM operation scope matrix
56
+
57
+ Request the union of the rows needed by one operation; do not use a broad
58
+ scope merely because a later operation might need it:
59
+
60
+ | GTM operation | Minimum OAuth scope |
61
+ |---|---|
62
+ | List/read account, container, tag, trigger, or variable | `https://www.googleapis.com/auth/tagmanager.readonly` |
63
+ | Edit container configuration | `https://www.googleapis.com/auth/tagmanager.edit.containers` |
64
+ | Create or update a container version | `https://www.googleapis.com/auth/tagmanager.edit.containerversions` |
65
+ | Publish a container version | `https://www.googleapis.com/auth/tagmanager.publish` |
66
+
67
+ An operation that edits a container and creates a version must request both
68
+ edit scopes; publishing additionally requires the publish scope. Record the
69
+ operation name and exact scope union in the capability evidence so a successful
70
+ read cannot be mistaken for edit or publish access.
71
+
72
+ ### Missing fields are unknown
73
+
74
+ Google APIs can omit fields, especially when a proto3 field is absent from the
75
+ response or was not selected by the request. An omitted boolean is not
76
+ `false`, and an absent schema field is not evidence that the capability is
77
+ disabled. Reports must use `unknown`/`not_observed` in the provider adapter's
78
+ evidence and obtain a corroborating read or time-series observation before
79
+ making a negative claim. Never silently coerce a missing field to `false`.
80
+
55
81
  ## Provider setup and read-only preflight
56
82
 
57
83
  1. Select one provider and one resource. Enable only that provider API in the
@@ -54,7 +54,24 @@ explicit `approve` transition before `publish`. Stable `contentId` is the
54
54
  ingest identity and a published slug must not change during a rewrite.
55
55
  Search/sort views are not indexable; drafts never appear in public routes, RSS,
56
56
  or sitemap output. Provider keys remain server-side. Public publication and
57
- migrations always require explicit confirmation.
57
+ migrations always require explicit confirmation.
58
+
59
+ Automatic publication is an opt-in host policy, not the default. Keep human
60
+ review enabled unless the owner explicitly chooses otherwise. If a host has
61
+ that approval, validate its evidence before its scheduler calls the host's
62
+ publish path:
63
+
64
+ ```bash
65
+ maggie blog auto-publish-gate \
66
+ --evidence .maggie/blog-auto-publish-evidence.json \
67
+ --output .maggie/blog-auto-publish-gate.json
68
+ ```
69
+
70
+ The `maggie-blog-auto-publish.v1` evidence must explicitly set `optIn: true`
71
+ and `reviewDisabledExplicitly: true`, then pass locale variants, feature image
72
+ and alt text, SEO fields, sitemap generation, indexing notification, and audit
73
+ checks. The gate never publishes; missing evidence keeps content in human
74
+ review and remains non-indexable.
58
75
 
59
76
  The adapter runs as an argv array without a shell, receives a versioned request
60
77
  on stdin, and must print only an object containing
@@ -80,6 +80,14 @@ This is a runtime contract gate, not a source-only check; it fails when a
80
80
  declared endpoint is missing, uses the wrong route/method, returns a non-2xx
81
81
  status or omits required fields.
82
82
 
83
+ For analytics panels and Consent Mode, keep the same evidence boundary: the
84
+ default consent state must be written before the container loads, first-party
85
+ storage behavior must be explicitly `stop` or `degrade`, and intentional form
86
+ events must be declared separately as `record` or `omit`. A browser report must
87
+ prove each decision; an absent field is unknown and must not be interpreted as
88
+ false. Run the analytics release gate when the dashboard or host measurement
89
+ contract changes.
90
+
83
91
  MaggieDash 0.2.4 adds two reusable operator workspaces. Activity is a read-only
84
92
  view backed by `/api/maggie/activity.json`; it supports actor/action filters,
85
93
  refresh, and a safe unavailable state. Navigation is an ordered,
@@ -255,6 +263,10 @@ maggie dash schema-audit --inventory .maggie/schema-inventory.json \
255
263
  # Prove a browser check mounted the screen and executed its data calls:
256
264
  maggie dash ui runtime-validate --evidence .maggie/dashboard-runtime.json
257
265
 
266
+ # Validate independent dashboard measurement panels and freshness evidence:
267
+ maggie dash panels-validate --report .maggie/measurement-panels.json \
268
+ --output .maggie/measurement-panels-result.json
269
+
258
270
  # Classify docs, show inbound references and completed progress trackers:
259
271
  maggie docs audit --project . --docs-dir docs \
260
272
  --output docs/documentation-audit.json
@@ -339,9 +351,12 @@ category, collection, or service reference; an unresolved binding is a publish
339
351
  failure, not an empty state.
340
352
 
341
353
  Repair scripts must select all candidates, compare every target field, write
342
- only changed rows, and report both changed and unchanged rows. Validate that
343
- contract with `idempotency-validate`; a query that selects only rows that look
344
- unconverted cannot repair its own bad output.
354
+ only changed rows, and report both changed and unchanged rows. Each step must
355
+ also return a boolean `wrote`; the summary's `writes` count is the number of
356
+ true outcomes, not the number of executed steps. The shared section reconcile
357
+ command exposes `writeCount`, `unchangedCount`, and `writeOutcome` for this
358
+ purpose. Validate the repair contract with `idempotency-validate`; a query
359
+ that selects only rows that look unconverted cannot repair its own bad output.
345
360
 
346
361
  After any script or direct adapter write to translation data, invalidate the
347
362
  running process before verification. Record the restart and run the rendering
@@ -88,6 +88,36 @@ 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
+ Classify host/runtime failures before a release can move:
92
+
93
+ ```bash
94
+ maggie deployment runtime-preflight \
95
+ --evidence .maggie/verification/runtime-preflight-input.json \
96
+ --output .maggie/verification/runtime-preflight.json
97
+ ```
98
+
99
+ The `maggie-runtime-preflight.v1` input has independent checks for
100
+ dependencies, browser/driver compatibility, worker configuration, database
101
+ privileges, and routes/API. Each non-pass status must include an actionable
102
+ remediation. Missing checks are `unknown` and fail closed; the command never
103
+ connects to a host or mutates runtime state.
104
+
105
+ Before production approval, compose one cross-workflow manifest from the
106
+ sanitized outputs of the route, migration, runtime, asset/icon, edge-cache,
107
+ browser/render, and rollback checks:
108
+
109
+ ```bash
110
+ maggie deployment release-manifest \
111
+ --evidence .maggie/release-evidence.json \
112
+ --output .maggie/release-manifest.json
113
+ ```
114
+
115
+ The `maggie-release-evidence.v1` input links the release commit and Maggie
116
+ version to every required evidence slot and release fingerprint. The validator
117
+ fails if any slot is missing or not passed, keeps only normalized metadata, and
118
+ records `mutation: "not executed"`; it does not deploy or claim that a check
119
+ was run.
120
+
91
121
  ## Automatic memory hook
92
122
 
93
123
  Follow [Maggie Memory Hook](../../references/memory-hook.md) at invocation and completion.
@@ -115,6 +145,22 @@ This command never deploys. Production execution still requires the shared
115
145
  decision loop, explicit confirmation, migration/rollback details, and the
116
146
  provider-specific deploy command.
117
147
 
148
+ For a VPS adapter, check the project `.env` by variable name before reporting
149
+ credentials as unavailable:
150
+
151
+ ```bash
152
+ maggie deployment credential-preflight \
153
+ --env-file .env --expected-host example-host \
154
+ --output .maggie/deployment/credentials.json
155
+ ```
156
+
157
+ The preflight checks `DEPLOY_SERVER_IP`, `DEPLOY_SERVER_SSH_USER`,
158
+ `DEPLOY_SERVER_SSH_PASSWORD`, and `DEPLOY_SERVER_SSH_SUDOER` by presence only.
159
+ It never prints or stores values. `--expected-host` is optional, but should be
160
+ used for production to prevent deploying to an unintended server. SSH-key
161
+ adapters may use their own named secret contract; map it explicitly in their
162
+ local plan.
163
+
118
164
  When deployment verification depends on Google properties or accounts, use
119
165
  the shared [Google integrations runbook](../../references/google-integrations-runbook.md).
120
166
  Keep provider auth and capability evidence separate from deployment health;
@@ -146,6 +192,15 @@ carries `.agents`/`.claude` state, restarts before verification, and prunes to
146
192
  the current release plus one rollback candidate. It never
147
193
  contains credentials and never SSHs, changes DNS, restarts systemd or deploys.
148
194
 
195
+ VPS plans use a dedicated deployer account by default:
196
+ `--deployer-user maggie-deploy`. The generated sudoers contract permits only
197
+ the exact `/bin/systemctl restart <service>` and
198
+ `/bin/systemctl is-active <service>` commands for the named service. Release
199
+ files and the application remain separated (`maggie-deploy` versus
200
+ `www-data`); no wildcard, shell, `sudo su`, or broad root command is generated.
201
+ Install and review that narrow sudoers policy on the host before executing a
202
+ runner.
203
+
149
204
  VPS plans include a bounded retention policy: keep two immutable releases,
150
205
  preserve the `current` target and rollback target, and review prune candidates
151
206
  before any operator executes cleanup. Generate a read-only candidate report:
@@ -186,6 +241,23 @@ The gate fails on a missing or mismatched target identity and never executes a
186
241
  migration. A successful connection alone is not proof that the intended
187
242
  database was selected.
188
243
 
244
+ Before activation, reconcile the host's migration ledger rather than relying
245
+ on a remembered local migration list:
246
+
247
+ ```bash
248
+ maggie migration reconcile \
249
+ --evidence .maggie/migration/server-ledger.json \
250
+ --migration-dir migrations \
251
+ --output .maggie/migration/ledger-reconcile.json
252
+ ```
253
+
254
+ The host adapter must query `maggie_schema_migrations` and emit only the
255
+ version identifiers and environment. The read-only command orders local
256
+ numbered SQL files, reports `missing`, and remains `mutation: "not executed"`.
257
+ Apply every missing file through the host migration runner, re-read the ledger,
258
+ and only then continue to the release switch; a missing version is a failed
259
+ deployment preflight, not evidence that activation can proceed.
260
+
189
261
  If a release depends on existing rows or seeded data, set
190
262
  `dataDependencies: true` in `.maggie/migration-manifest.json` and provide
191
263
  `.maggie/deployment/data-release.json` before deployment. The checkpoint must
@@ -39,6 +39,19 @@ owner before any remote mutation.
39
39
 
40
40
  ### SSH and secret-source contract
41
41
 
42
+ First run the shared credential presence check against the project `.env`:
43
+
44
+ ```bash
45
+ maggie deployment credential-preflight --env-file .env \
46
+ --expected-host example-host
47
+ ```
48
+
49
+ The canonical password-based names are `DEPLOY_SERVER_IP`,
50
+ `DEPLOY_SERVER_SSH_USER`, `DEPLOY_SERVER_SSH_PASSWORD`, and
51
+ `DEPLOY_SERVER_SSH_SUDOER`. The command reports only presence and an optional
52
+ host match; it never prints a value. A provider may instead use an SSH key, but
53
+ must state that alternate name and source in its plan.
54
+
42
55
  Use these canonical project variables when a VPS adapter is configured:
43
56
 
44
57
  | Variable | Meaning | Precedence |
@@ -86,6 +99,19 @@ The generated Nginx/Astro boundary keeps `checkOrigin` enabled and requires
86
99
  canonical HTTPS `allowedDomains` plus trusted `X-Forwarded-Host`/
87
100
  `X-Forwarded-Proto` handling when TLS terminates at the proxy.
88
101
 
102
+ Use a dedicated least-privilege deployer when generating the plan:
103
+
104
+ ```bash
105
+ python3 tools/clis/maggie_deployment.py . --vps-plan \
106
+ --domain example.co.uk --service example --node-port 4321 \
107
+ --deployer-user maggie-deploy
108
+ ```
109
+
110
+ The plan separates the SSH/deploy account from the `www-data` application
111
+ account and allowlists only the exact service restart and status commands.
112
+ The runner uses non-interactive `sudo -n`; it never requires broad sudo or a
113
+ shell escalation. Review/install the host sudoers entry separately.
114
+
89
115
  Review the generated systemd and Nginx files, then obtain explicit approval
90
116
  before installing them on a host.
91
117
 
@@ -68,6 +68,22 @@ cannot render the icon, add an approved runtime definition or inline SVG
68
68
  fallback and rerun both checks. Never release a source token solely because
69
69
  typecheck or build succeeded.
70
70
 
71
+ Compose the static inventory with browser-painted evidence before declaring a
72
+ design release complete:
73
+
74
+ ```bash
75
+ maggie design icon-release-gate \
76
+ --icon-report docs/icon-inventory.json \
77
+ --browser-report .maggie/icon-browser-evidence.json \
78
+ --output docs/icon-release-gate.json
79
+ ```
80
+
81
+ The browser evidence must use `maggie-icon-browser-evidence.v1`, include at
82
+ least one route, and report each glyph as visible with positive dimensions,
83
+ a successful `assetStatus` (2xx), and a non-empty accessible name. This
84
+ gate is independent from typecheck/build and is read-only apart from its
85
+ explicit output.
86
+
71
87
  ## Automatic memory hook
72
88
 
73
89
  Follow [Maggie Memory Hook](../../references/memory-hook.md) at invocation and completion.
@@ -57,8 +57,17 @@ fields and supplies the normal `type`, `summary`, `expected`, `actual`,
57
57
  `errorFingerprint`, reproduction, resolution, validation, and screenshot
58
58
  metadata fields. It may also provide bounded `routeIds` and
59
59
  `validationEvidence` identifiers. Each generated draft stores only the safe
60
- batch ID, zero-based index, and bounded size. The batch command still creates
61
- local drafts only.
60
+ batch ID, canonical zero-based index, and bounded size. Direct `collect` calls
61
+ use zero-based `--batch-index` by default. If an external runner reports
62
+ one-based positions, pass `--batch-base 1`; the final item is then accepted and
63
+ normalized before it is stored. The batch command still creates local drafts
64
+ only.
65
+
66
+ For example, the third item in a three-item, one-based batch is collected as:
67
+
68
+ maggie feedback collect --project . --batch-id run-review-001 \
69
+ --batch-index 3 --batch-size 3 --batch-base 1 \
70
+ --summary "Final item" --type bug
62
71
 
63
72
  Aggregate a batch before manual review:
64
73
 
@@ -118,6 +118,22 @@ with a reviewer. Commit the approved change and new contract together; never
118
118
  automatically replace the baseline following failure. Dynamic dates, class
119
119
  names and copy can produce legitimate differences requiring review.
120
120
 
121
+ To approve an intentional drift, write a new file and require a reason for
122
+ every changed, added, removed, or content-changed URL:
123
+
124
+ ```bash
125
+ maggie site-audit https://example.com --crawl --json \
126
+ --baseline docs/seo-baseline-v1.json \
127
+ --recapture-baseline docs/seo-baseline-v2.json \
128
+ --baseline-id seo-v2 --reviewer maintainer \
129
+ --reason 'https://example.com/pricing=approved pricing refresh'
130
+ ```
131
+
132
+ The command fails closed when the current crawl is incomplete, a drift has no
133
+ reason, a reason names a URL without a reported drift, or the destination file
134
+ already exists. The new contract records its `baselineId`, `supersedes` value,
135
+ reviewer, and approved reasons; the old baseline is never overwritten.
136
+
121
137
  This covers server-rendered sitemap pages, not CSS rendering, JavaScript-only
122
138
  content, database translation keys or browser interactions. Query-string URLs
123
139
  are recorded as excluded from byte baselines because they often represent
@@ -256,7 +272,12 @@ metadata, valid entity JSON-LD, and image alt text. Non-HTML endpoints such as
256
272
  RSS must not be included in the HTML page sitemap. Sitemap `<loc>` values must
257
273
  be absolute HTTP(S) URLs; the audit reports relative values as a failure even
258
274
  when it can resolve them for continued crawling, so one malformed entry cannot
259
- hide a protocol violation.
275
+ hide a protocol violation. Absolute sitemap locations are rebased to the
276
+ origin passed to `site-audit` by default, so `--crawl` cannot silently audit
277
+ production from a staging/local command. The JSON crawl evidence records
278
+ `requestedOrigin` and `sitemapOriginPolicy: "requested-origin"`. Use
279
+ `--preserve-sitemap-origin` only when an explicitly reviewed cross-origin crawl
280
+ is intended; the report then says `"declared-origin"`.
260
281
  Use `--output <project>/docs/site-audit.json` to persist the evidence used by
261
282
  the release review.
262
283
 
@@ -933,6 +933,13 @@ def command_fixture(args: argparse.Namespace) -> int:
933
933
  return 0
934
934
 
935
935
 
936
+ def command_feedback(args: argparse.Namespace) -> int:
937
+ """Dispatch feedback subcommands through the canonical feedback CLI."""
938
+ cli = Path(__file__).with_name("maggie_feedback.py")
939
+ completed = subprocess.run([sys.executable, str(cli), *args.feedback_args], cwd=Path.cwd(), check=False)
940
+ return completed.returncode
941
+
942
+
936
943
  def parser() -> argparse.ArgumentParser:
937
944
  p = argparse.ArgumentParser(prog="maggie", description=__doc__)
938
945
  sub = p.add_subparsers(dest="command", required=True)
@@ -984,6 +991,9 @@ def parser() -> argparse.ArgumentParser:
984
991
  item = fixture_sub.add_parser(fixture_name)
985
992
  item.add_argument("project", nargs="?", default=".")
986
993
  item.set_defaults(func=command_fixture)
994
+ feedback = sub.add_parser("feedback", help="collect, review, and submit privacy-safe feedback")
995
+ feedback.add_argument("feedback_args", nargs=argparse.REMAINDER, help="arguments for maggie_feedback.py")
996
+ feedback.set_defaults(func=command_feedback)
987
997
  doctor = sub.add_parser("doctor", help="check blog routes and SEO output invariants")
988
998
  doctor.add_argument("project", nargs="?", default=".")
989
999
  doctor.add_argument("--require-bootstrap", action="store_true")
@@ -1121,5 +1131,7 @@ def parser() -> argparse.ArgumentParser:
1121
1131
 
1122
1132
 
1123
1133
  if __name__ == "__main__":
1134
+ if sys.argv[1:2] == ["feedback"]:
1135
+ raise SystemExit(command_feedback(argparse.Namespace(feedback_args=sys.argv[2:])))
1124
1136
  arguments = parser().parse_args()
1125
1137
  raise SystemExit(arguments.func(arguments))
@@ -67,6 +67,18 @@ def release_gate(args: argparse.Namespace) -> int:
67
67
  check("referral-contract", set(contract["referralSources"]).issubset({"search", "partner", "social", "email", "direct", "unknown"}), "referral categories use the redacted taxonomy", checks)
68
68
  else:
69
69
  check("referral-contract", False, "contract must declare referralSources", checks)
70
+ consent = contract.get("consent")
71
+ valid_consent = (
72
+ isinstance(consent, dict)
73
+ and consent.get("defaultBeforeContainer") is True
74
+ and consent.get("firstPartyStorageMode") in {"stop", "degrade"}
75
+ and consent.get("intentionalFormMode") in {"record", "omit"}
76
+ )
77
+ check(
78
+ "consent-contract", valid_consent,
79
+ "consent defaults precede the container and first-party/form behavior is explicit",
80
+ checks,
81
+ )
70
82
  browser, browser_errors = load_json(Path(args.render_report).resolve(), "render-report")
71
83
  errors.extend(browser_errors)
72
84
  network, network_errors = load_json(Path(args.network_report).resolve(), "network-report")
@@ -79,6 +91,28 @@ def release_gate(args: argparse.Namespace) -> int:
79
91
  check("browser-schema", browser.get("schemaVersion") == "maggie-analytics-browser.v1", "versioned browser evidence", checks)
80
92
  check("browser-render", browser.get("passed") is True and isinstance(browser.get("routes"), list) and bool(browser["routes"]), "routes rendered without a browser failure", checks)
81
93
  check("browser-console", isinstance(browser.get("consoleErrors"), list) and not browser["consoleErrors"], "no browser console errors", checks)
94
+ consent = contract.get("consent") if contract else None
95
+ browser_consent = browser.get("consent")
96
+ check(
97
+ "consent-ordering", isinstance(consent, dict) and isinstance(browser_consent, dict)
98
+ and browser_consent.get("defaultBeforeContainer") is True
99
+ and browser_consent.get("containerLoadedAfterDefault") is True,
100
+ "browser evidence proves default consent precedes container loading",
101
+ checks,
102
+ )
103
+ check(
104
+ "first-party-scope", isinstance(consent, dict) and isinstance(browser_consent, dict)
105
+ and browser_consent.get("firstPartyStorageMode") == consent.get("firstPartyStorageMode")
106
+ and browser_consent.get("firstPartyStorageObserved") is True,
107
+ "browser evidence proves the declared first-party storage behavior",
108
+ checks,
109
+ )
110
+ check(
111
+ "intentional-form-scope", isinstance(consent, dict) and isinstance(browser_consent, dict)
112
+ and browser_consent.get("intentionalFormMode") == consent.get("intentionalFormMode"),
113
+ "browser evidence records the declared intentional-form behavior",
114
+ checks,
115
+ )
82
116
  if network:
83
117
  pre = network.get("preConsentRequests")
84
118
  check("pre-consent-network", isinstance(pre, list) and not pre, "no analytics requests before consent", checks)