Dawn → Horizon: Agentic Cart Functions
Migration guideline · storefront. Move the legacy Dawn storefront (business logic in client JS + Script Editor) to Horizon's thin, blocks-native presentation — with cart, pricing, and checkout logic re-homed to server-side Shopify Functions and the Tool Runner, driveable by an agent under an AP2 mandate.
Immovable clock. Shopify's Script Editor is removed as of 2026-06-30. Any reorder/hide-payment, shipping, or line-item-discount Script still live means checkout logic has silently stopped. Checkout-touching work and the Scripts→Functions conversion are the urgent thread — do these before the broader theme rebuild.
The clock, in full
Every date below is an obligation that assumes the evidence already exists when it arrives.
| Date | What changes |
|---|---|
| 2018 | Wayfair (585 U.S. 162) makes tax borders real; GDPR makes consent law. The mandates begin. |
| May 2022 | Omnibus Directive applies: 30-day price evidence per market — a ledger obligation, not a lookup. |
| 2023 | Netflix paid sharing: the first mass entitlement retrofit — built for the fee, not the subject. |
| March 2024 | Consent Mode v2 becomes mandatory for EEA conversion measurement — the stack's first enforced consent coordinate. |
| June 2024 | FTC v. Adobe filed: subscription exit dark patterns reach federal court. |
| October 2024 | Shopify's REST Admin API goes legacy: GraphQL required for new apps; the schema grows weekly. |
| June 15 2026 | Google Signals retired as a control — Consent Mode ad_storage becomes the sole control of the GA4 → Google Ads flow. |
| June 30 2026 | Shopify Script Editor removed — surviving payment, shipping, and discount Scripts stop silently. |
| August 2026 · now | EU AI Act Article 50 transparency obligations apply. Cloudflare Wallets launches. |
| August 18 2026 | Content API for Shopping shuts down. |
| September 2026 · next month | Cyber Resilience Act vulnerability-reporting obligations begin. |
| February 2027 | Battery Digital Product Passport: the first product class where passport data is mandatory at the border. |
| 2027 | GS1 Sunrise 2027: retail point-of-sale worldwide expected to scan 2D barcodes. The GS1 Digital Link QR becomes the product's on-pack URL — one code carrying identity, passport, and evidence. |
| December 2027 | Cyber Resilience Act applies in full — €15M or 2.5% of global turnover. |
Two of these are already behind us and are the ones most estates have not absorbed: since June 15 the consent signal is the ads data control, and since June 30 any un-migrated Script is a checkout rule that stopped without an error.
The same timeline, live and machine-readable (human table plus a JSON block any agent can parse): crm-sync.dev/pages/difference#calendar
Globalization and time are first-class in the new API
The Merchant API is not a rename of the Content API — it is a reshape, and the axes it adds are exactly the ones a single-border stack never had.
- Time is scheduled, not assumed. Data sources carry explicit fetch settings —
timeZone(IANA, UTC by default),frequency,dayOfWeek,dayOfMonth,timeOfDay— so a feed states when it is true, per market, in a declared zone. A nightly job with an implicit server clock is no longer a description of anything. - Location is a declared axis. Regions, regional inventory, and local inventory are separate resources; availability and price can differ per region without forking the catalog.
- Prices changed shape.
value: string+currency: stringbecomesamountMicros: int64+currencyCode— a data-model change, not a rename, and the reason "just repoint the URL" migrations fail. - Identifiers consolidate. Resource
namestrings replace separate IDs, andcustomBatchgives way to parallel async calls.
The consequence for a global estate: time zone, region, language, and currency stop being feed columns and become coordinates. A record that carries them can answer per market; one that doesn't has to be re-exported per market, forever. This is the same fusion argument as above, wearing the feed's clothes — machine-addressable and correctly qualified, or neither.
GraphQL + GA4 signals — one seismic move, not two chores
Enterprises are forced through two migrations they treat as unrelated: REST → GraphQL (REST Admin API declared legacy October 2024; GraphQL required for new apps since April 2025) and GA4 Consent Mode v2 (four signals — ad_storage, analytics_storage, ad_user_data, ad_personalization — mandatory for EEA/UK ads features since March 2024). The recognition worth having: they are the same migration toward one thing — a consent-gated, agent-addressable data plane.
| Move | Delivers | Missing alone |
|---|---|---|
| REST → GraphQL | Typed, bulk, server-resolved data — machine-addressable | Reach without permission: the agent can query but not know what it may do |
| GA4 Consent Mode v2 | Consent as a first-class signal on every event | Permission without reach: data still un-queryable at scale |
| Fused | Every datum machine-addressable AND consent-qualified | — |
Neither migration delivers the agent-ready plane alone; the value is the fusion. (Since June 15, 2026, ad_storage is also the sole control over what GA4 sends to Google Ads — the consent signal is no longer a formality beside the pipeline, it is the valve.)
The trust foundation the fusion rides on
- Orthogonal trust — consent and access are independent axes that compose without coupling; the agent carries zero inherited trust.
- Hexagonal trust — the GraphQL plane sits behind ports; every consumer (agent, GA4, ESB, CDP, storefront) crosses one consent-enforcing port.
- Loop engineering — the agent's query loop re-checks consent every iteration, fail-closed. Trust is re-earned per loop, never inherited.
The enterprise unlock: AI workflows that are powerful (reach all the data) and safe (consent lineage per loop). Most stacks force that trade-off; the fused plane removes it — and it coexists with an existing ESB/CDP rather than replacing them.
Enforcement is a benefit — revenue, trust, reputation
Compliance and permissions are not a cost center or a checkbox. Enforced at the data plane — fail-closed, per-loop — they protect revenue, compound trust, and defend reputation.
| Benefit | Capability | Enforcement |
|---|---|---|
| Ad ROAS preserved | Consent Mode v2 signals keep measurement and remarketing lawful — lost consent = lost signal = wasted spend | Four signals required, fail-closed before analytics fire |
| Promotions run legally | Omnibus 30-day prior-lowest price — discount in the EU without "fake sale" exposure | Price-history ledger, read-gated |
| Market access retained | Data rights (opt-out, export, delete) — the price of selling in the EU or California at all | Wired to live endpoints, fail-closed |
| Safe delegation → new revenue | Scoped, revocable per-tenant tokens + capability permissions — sell to teams and agencies without exposure | Data-plane; revoke = instant 401 |
| Agentic revenue, no rogue-agent risk | AP2 mandate + agent permissions — autonomous purchase, safely | Per-loop re-check, fail-closed |
| Enterprise deals unlocked | PII-free audit trail — provable governance is what procurement requires before it can buy | Audit by construction |
The reframe: permissions enforcement converts compliance from risk and cost into revenue enablement and a trust asset. The governance is the reason the data is safe to open — which is the core offer: accessibility, made safe by construction.
The general form of this argument — across AEM, WordPress and Drupal, and why a theme that looks right is not a system that is right — is Server-Side, Or It Didn't Happen. This page is the Shopify instance, with dates.
1 · Why Horizon fits
Horizon is blocks-first: a blocks/ directory, nested blocks via {% content_for 'blocks' %}, per-component {% stylesheet %} / {% javascript %}, no framework runtime. That posture — thin presentation, logic pushed out — is the native Shopify endpoint for a server-resolved stack, not a fight against it.
| Dawn coupling (what breaks) | The layer that absorbs it |
|---|---|
Business rules in section <script> / Script Editor | Execution → Tool Runner (worker) + Shopify Functions |
| Cart / price math recomputed in the browser | Data plane → server-resolved (cart-transform / draft order) |
| Product data hardcoded in Liquid | Source of truth → hydrate from the PIM via GraphQL |
| Auth and gating in theme JS | Identity → JWE claim, derived server-side, never in Liquid |
| Brand look re-built per surface | Look → design-token sync; one theme.css keys every surface |
2 · Agentic cart functions — guideline
Two audiences hit the same cart: a human on the Horizon storefront and an agent acting under a mandate. The rule is that both cross the same server-side boundary — no logic path exists only in the browser.
2.1 · Script → Function replacement map
| Legacy Script (removed 2026-06-30) | Shopify Function | Runs at |
|---|---|---|
| Reorder / hide payment methods | payment_customization | checkout |
| Reorder / hide / rename shipping | delivery_customization | checkout |
| Line-item / order discount | product_discount / order_discount | cart + checkout |
| Bundle / merge / expand lines | cart_transform | cart |
| Block invalid carts | cart_checkout_validation | cart + checkout |
Functions are WASM, run server-side on Shopify's infrastructure, and are deterministic and input-bounded — which is exactly why an agent can be allowed to trigger them: the merchant's rules hold regardless of who or what filled the cart.
2.2 · The agentic cart path
JWE claim → AP2 mandate (consent-to-purchase) → Tool Runner → add_to_cart → cart_transform + discount → agentic_checkout → Functions enforce
The agent never asserts price, discount, or identity. It calls a tool; the tool decrypts the JWE claim, checks caps at the data plane, and submits a cart. Merchant Functions then apply pricing, payment, and shipping rules on the server — the same rules a human would hit. The AP2 mandate is the user's scoped, revocable authorization for the agent to transact, re-checked on each loop, fail-closed.
2.3 · Rules
- No money math in the browser. Price, tax, discount, and shipping totals are server-resolved. Liquid renders the resolved number; it never computes it.
- One cart boundary for human and agent. The Tool Runner's
add_to_cart/checkouttools hit the same Cart/Checkout as the storefront — never a private, unguarded path. - Agent authority = mandate, not session. Authorization comes from the decrypted AP2 mandate plus caps, re-verified every tool call. A prior grant is never inherited across the loop.
- Deterministic rules live in Functions. Anything that must hold for every cart (discounts, payment/shipping gating, bundle logic) is a WASM Function — not worker code, not theme JS.
- Orchestration lives in the Tool Runner. Multi-step, stateful, or cross-system logic (PIM lookup, consent, settlement-rail choice) is a worker tool — Functions are input-bounded and side-effect-free.
- Catalog from the PIM, not Liquid. PDP and collection hydrate via GraphQL; no product data hardcoded in templates.
- Consent gates the cart, not just analytics. The AP2 mandate and Consent Mode v2 signals must be present before an agent transacts; absent consent = fail-closed.
- Present as Horizon blocks. Cart, upsell, and PDP UI are theme blocks with per-component CSS/JS — thin presentation over server-resolved state, themed by synced design tokens.
3 · Migration checklist
Work top to bottom. Groups A–B are the hard-dated urgent thread; C–F are the broader spine.
A · Step-0 audit (do first)
- Inventory Script Editor scripts — every payment / shipping / discount Script becomes one row in group B.
- Grep the theme for inline
<script>business logic — cart math, gating, price recompute, eligibility rules living in section/asset JS. - Find REST calls and hardcoded catalog data — mark each for GraphQL + PIM hydration.
- List per-locale theme forks — route to edge translation instead of forked templates.
B · Scripts → Functions (2026-06-30)
- Payment Script →
payment_customization - Shipping Script →
delivery_customization - Discount Script →
product_discount/order_discount - Bundle / merge logic →
cart_transform - Cart guards →
cart_checkout_validation - Deploy Functions and confirm active on live checkout — verify each merchant rule fires with a real test cart, not just unit input.
C · Agentic cart wiring
- Tool Runner
add_to_cart/checkouthit the same Cart API — no private agent-only path. - AP2 mandate created, scoped, and revocable — bound to the JWE actor.
- Caps re-checked every tool call, fail-closed — trust re-earned per iteration, never inherited.
- Redact tokens and mandates from tool-call logs.
D · Consent and signals
- All four Consent Mode v2 signals emitted on every surface, headless included.
- Consent captured server-side with timestamp, method, and version — the banner is not the record.
- Declines logged and enforced at egress, not just in the browser.
E · Data plane
- Catalog hydrated from the PIM over GraphQL; no hardcoded product data in Liquid.
- REST calls retired ahead of the sunset; feeds ride the Merchant API from 2026-08-18.
- Price observations recorded so any markdown claim has a 30-day reference behind it.
F · Presentation
- Cart, upsell, and PDP rebuilt as Horizon blocks with per-component CSS/JS.
- Design tokens synced so the same brand keys Horizon and every other surface.
- Per-locale forks replaced with edge translation.
See it running
Every claim in this guideline is demonstrable on a live store rather than described:
- The calendar — the dates above, rendered for humans and for machines from one array: crm-sync.dev/pages/difference#calendar
- Price evidence, fail-closed — a real markdown with its 30-day window bounds, and a refusal where coverage is short: crm-sync.dev/pages/omnibus · raw record:
/pim/omnibus?handle=asset-sample-model - The identity plane — how a login binds accounts you already own, and why permissions are rows: Trust With Login
- The same records on another CMS — the identical elements rendering on AEM, no data stored there: trust-login · omnibus
- Server-side or it didn't happen — the doctrine behind the guideline: the dev doc
- Machine discovery — what the estate advertises to agents:
crm-sync.dev/stack/config - The whole catalog plane, any frontend — PIM Anywhere
The migration is not a theme project with a compliance appendix. It is one move — logic to the server, consent into the data plane — and the theme rebuild is what falls out of it.