Skip to content

Weekly Merchandising

The platform’s first deployed automation. One agent, three read-only tools, runs every Monday at 06:00 UTC, produces a Markdown report. Deliberately simple — no delegation, no memory, no events, no async. The kind of thing that can ship before the infrastructure for those features exists.

This is what shipping looks like at the start. Complexity gets added when scenarios demand it.

A small e-commerce store has dozens of products, occasional inventory issues, and an operator who’d benefit from a weekly “here’s where things stand” snapshot. Pulling that snapshot by hand takes 15 minutes; the operator skips weeks; signal degrades.

The merchandising agent runs every Monday morning, reads the store’s current state from Shopify, and produces a Markdown report covering inventory, last week’s activity, and one suggested promotion. The report appears in the Worker logs; the operator reads it; they decide what to do with it.

Read-only. The agent cannot create campaigns, modify products, or change anything in Shopify. The output is a recommendation a human reviews and acts on. This is the scope locked in ADR-0025.

A weekly run, end to end:

Cloudflare cron triggers the Worker:

  1. Cloudflare cron fires the Worker’s scheduled handler on the configured schedule (0 6 * * MON — every Monday at 06:00 UTC)
  2. Worker binds a fresh logger with a job_id and a single-use Shopify client; constructs the agent definition and the runtime

The agent assembles its facts:

  1. Agent → get_shop_info tool: confirms the shop name, currency, and locale. (One Shopify GraphQL call.)
  2. Agent → list_products tool: retrieves the current catalog with title, type, status, inventory, tags, and variant pricing. (Default limit: 50 products.)
  3. Agent → recent_orders tool with daysBack=7: lists orders from the past week with line items and totals.

The agent produces the report:

  1. Agent runs an LLM turn, composes the Markdown report following the prescribed structure
  2. Agent → Worker: returns the report as AgentReport.summary
  3. Worker logs the report at INFO level with structured fields (shop_name, report_length, tool_calls, cost)

The operator reads the log. That’s the deployment. No queue, no event bus, no email — the report appears in Cloudflare’s tail logs and (when configured) in any log sink the Worker is wired to.

Platform featureHow this scenario uses it
Cron-triggered executionWorker’s scheduled handler invoked by Cloudflare cron; no HTTP path needed
Per-job agent constructionEach cron firing builds a fresh agent + Shopify client; no connection pooling
Custom toolsget_shop_info, list_products, recent_orders — each wraps one Shopify GraphQL call
Hard constraints in prompts”Never invent data,” “output ONLY the Markdown report,” “recommendations must reference specific products” — the LLM enforces them
Single-agent, no delegationsub_agents: [], max_delegation_depth: 0 — proves the platform doesn’t require multi-agent setups
No long-term memorylong_term_enabled: false — proves the memory subsystem is opt-in per agent
Stable Markdown outputThe system prompt locks the report structure; reports are diff-able week to week

The thing this scenario doesn’t exercise is also worth naming: no delegation, no memory, no events. It’s the smallest possible useful agent on the platform. If a Phase 4 customer asks “can I just have a single agent that runs on a schedule?” the answer is “yes, here’s how” — and the merchandising agent is the example.

Merchandising predates the YAML loader (ADR-0031), so its definition lives in TypeScript at apps/worker/src/merchandising.ts. Future migration to YAML is straightforward (the schema is identical) but hasn’t been prioritized — it works.

apps/worker/src/merchandising.ts (excerpt)

Section titled “apps/worker/src/merchandising.ts (excerpt)”
export function createMerchandisingAgent(): AgentDefinition {
return {
metadata: {
id: 'agent-merchandising' as AgentId,
name: 'merchandising_assistant',
version: '0.1.0',
role: 'main',
tags: ['ganimarka', 'merchandising'],
},
core_context: {
system_prompt: MERCHANDISING_SYSTEM_PROMPT,
identity: 'a merchandising assistant for a small e-commerce store',
hard_constraints: [
'never invent data — only use what tools return',
'if recent_orders returns no orders, say so explicitly; never imply false sales',
'output ONLY the Markdown report — no preamble, no meta-commentary',
'recommendations must reference specific products from list_products',
],
},
characteristics: {
personality: 'pragmatic and concrete',
decision_style: 'balanced',
tone: 'concise, business-direct',
},
tools: ['get_shop_info', 'list_products', 'recent_orders'],
sub_agents: [],
memory_config: {
working_memory_window: 10,
long_term_enabled: false,
shared_context_scopes: [],
},
autonomy: {
max_delegation_depth: 0,
requires_human_approval: [],
allowed_sub_agents: [],
},
escalation_rules: [],
model_tier: 'main',
};
}
You are a merchandising assistant for a small e-commerce
store. Each week you produce one report.
Use your tools to gather facts FIRST:
1. Call get_shop_info to confirm shop name, currency, and locale.
2. Call list_products to see the current catalog.
3. Call recent_orders with daysBack=7 to see last week's activity.
Then produce a Markdown report with EXACTLY these sections,
in this order:
# Weekly Merchandising Report — {Shop name}, week of {ISO date}
## Inventory snapshot
A short paragraph listing how many products are active, how
much total inventory is on hand, and which products are
running low (under 5 units) or out of stock.
## Last week's activity
If there were orders, summarize: how many, total revenue,
what sold. If there were no orders, say so explicitly. Do
not pad.
## This week's recommendation
One concrete action: a promotion, a featured product, a
campaign angle. Tie it to specific products from the catalog.
Provide:
- A one-sentence rationale
- A draft banner headline (max 8 words)
- A draft landing page subhead (max 20 words)
- A suggested discount or angle
## Notes for the operator
Anything that doesn't fit above. Bullets, max 5 items.
Constraints:
- Do not invent inventory numbers, order counts, or product
details. Use only what the tools return.
- Keep the entire report under 600 words.
- Use plain Markdown.

The system prompt is the agent’s identity. Output structure is prescribed; constraints are enforced; the LLM fills in the specifics from tool output.

Here’s a real report from a deployed run, lightly redacted (real shop name and product titles). It’s the kind of thing that lands in the operator’s inbox / log every Monday morning:

# Weekly Merchandising Report — Ganimarka, week of 2026-04-27
## Inventory snapshot
42 active products, 1,247 total units on hand. Three products
are running low: "Iznik Cotton Towel — Sand" (4 units), "Sultan
Linen Robe — XL" (3 units), "Bursa Silk Scarf — Indigo"
(2 units). One product is out of stock: "Ottoman Hammam Towel —
Burgundy."
## Last week's activity
12 orders, 18 units total, revenue 4,892 SEK. The top performer
was "Iznik Cotton Towel — Sand" (4 units sold). No
out-of-the-ordinary geographic clustering; orders shipped to
6 different countries.
## This week's recommendation
Featured product: "Bursa Silk Scarf — Indigo." Inventory is
nearly depleted (2 units) and the SKU isn't currently
discounted. A "last-2-units" urgency banner converts well at
this price point.
- **Rationale:** scarcity-driven banner on a low-stock,
full-margin SKU
- **Draft banner headline:** "Only 2 left — Bursa Silk Scarf"
- **Draft landing page subhead:** "Hand-loomed indigo silk,
finished in Bursa. When they're gone, they're gone."
- **Suggested angle:** scarcity, no discount
## Notes for the operator
- "Ottoman Hammam Towel — Burgundy" has been out of stock for
3 weeks. Consider removing from the homepage or restocking.
- Three product titles use inconsistent casing: "Iznik Cotton
Towel" vs "iznik cotton towel — sand." Worth normalizing.

The full report is ~280 words, under the 600-word cap. Cost per run: ~$0.012 (one Sonnet call, three tool roundtrips). Wall time: ~8 seconds.

Terminal window
# 1. Deploy the Worker (one-time)
cd apps/worker
pnpm wrangler deploy
# 2. Trigger the merchandising cron manually
pnpm wrangler triggers deploy
curl -X POST https://<your-worker>.workers.dev/admin/run-merchandising \
-H "Authorization: Bearer $WORKER_AUTH_TOKEN"
# 3. Tail the logs to see the report
pnpm wrangler tail

The cron itself fires automatically every Monday at 06:00 UTC once the Worker is deployed. The /admin/run-merchandising endpoint exists for manual testing — same code path as the cron firing.

The merchandising scenario is feature-complete for Phase 1. Phase 2 doesn’t change it. Phase 3 (or 4) might:

  • Multi-store merchandising — the same agent against multiple Shopify stores, results aggregated
  • Memory-aware merchandising — recall last week’s recommendation, note whether it was acted on, adjust this week’s accordingly
  • Action-emitting merchandising — emit shopify_actions events for the recommended promotions instead of just reporting on them

None of these are committed to a roadmap. The agent works as designed; a Phase 4 customer who wants more can configure more.