ecom -- Ecommerce Business Review Toolkit
D2C ecommerce analytics system. The Python backend computes KPIs, runs health checks, and scores performance from order transaction data; you (Claude) interpret the numbers and write the human-readable report.
Input: Order transaction data only (CSV).
Quick Reference
| Command | What it does | Output |
|---|---|---|
| /ecom review | Full business review (auto-selects periods from data) | REVIEW.md |
| /ecom review 30d | Focused on the last 30 days | REVIEW_30D.md |
| /ecom review 90d | Focused on the last 90 days | REVIEW_90D.md |
| /ecom review 365d | Focused on the last 365 days | REVIEW_365D.md |
| /ecom review [question] | Answers a specific question from the data | Inline response |
One command. Two modes: full report or focused answer.
Response Modes
Mode 1: Full Review (default)
Triggered when: no natural-language question in the user's input. Output: REVIEW.md (or REVIEW_{PERIOD}.md for period-specific runs) following the full 6-part structure defined below.
Examples: /ecom review, /ecom review 30d, /ecom review 365d
Mode 2: Focused Query
Triggered when: user's input includes a natural-language question or topic. Output: inline conversational response (no file creation).
Examples:
- "how was last month?" → extract from monthly_trend
- "how was last year?" → 365d data
- "how was Q4 last year?" → extract from monthly_trend
- "how's retention looking?" → customer metrics focus
Query-to-Period Mapping
| Query pattern | Data source |
|---|---|
| last 30 days | --period 30d |
| last 3 months / last 90 days | --period 90d |
| last year / this year | --period 365d |
| last month / specific month name | full run, extract from monthly_trend |
| Q1-Q4 / specific quarter | full run, extract from monthly_trend |
Calendar-month questions ("last month", "January") use the 365d monthly_trend, NOT a 30d trailing window. Use --period 30d only when the user explicitly asks for "last 30 days".
Fallback when 365d coverage is unavailable: Calendar-month and quarter queries require 365d monthly_trend data. If 365d coverage is missing (data < 400 days):
- Answer from the best available trailing window data
- State that exact calendar-month/quarter breakdown is not available
- Suggest running a full review for deeper analysis
monthly_trend limitation: no year-over-year comparison by month/quarter monthly_trend covers only the most recent 12 months. Comparing "Q4 this year vs Q4 last year" is not possible. Instead:
- Present absolute values (revenue, orders) for the requested period
- Calculate its share of annual revenue (seasonality concentration)
- Compare against Q1-Q3 average for relative scale
- Add 365d overall YoY growth rate as context
- Do not mention the absence of YoY comparison (follow Handling Incomplete Data rules — omit what you can't measure)
If the query doesn't map to a clear period, run full (no --period flag) and answer from the most relevant data.
Focused Query Response Format
- Direct answer (2-3 sentences with key numbers from review.json)
- Supporting context (1-2 observations that add nuance — tensions, comparisons)
- One actionable takeaway (if warranted by the data)
Total: 10-30 lines. Headings, KPI tree, and Finding format are NOT required. Use whatever structure best fits the answer. For example, "what's the new vs returning split?" is clearest as a partial KPI tree. Apply the same data rules: all numbers from review.json only, match user's language. Do NOT create a REVIEW.md file.
Data Input
Order transaction data (CSV). Each row = one order or line item.
Required columns:
- Order ID, Order date, Customer ID (or email)
- Revenue (after discounts, before tax/shipping)
- Quantity, SKU/Product ID
- Discount amount (if available)
~/.claude/skills/ecom/bin/ecom review orders.csv --output <output-dir>
~/.claude/skills/ecom/bin/ecom review orders.csv --period 90d --output <output-dir>Workflow
Phase 1: Compute (Python)
~/.claude/skills/ecom/bin/ecom review orders.csv --output <output-dir>The engine internally:
- Period analysis -- computes KPIs for each time period (30d, 90d, 365d) with prior-period comparisons, decompositions, and trend detection.
- Health checks -- evaluates ~30 checks across Revenue, Customer, and Product. Each check produces a pass/watch/fail signal with severity weighting. These power the 🟢/🟡/🔴 markers in the KPI tree.
Output: review.json (or review_{period}.json for period-specific runs). See Schema section below.
Phase 2: Interpret (You -- Claude)
- Read
review.json(orreview_{period}.jsonfor period-specific runs) - Load reference files on demand (see Reference Files below)
- Write REVIEW.md (or
REVIEW_{PERIOD}.md) following the Output Format below
Your job is to weave trends and diagnostics into one coherent story. Period analysis tells you *where things are heading*. Health checks tell you *what's broken right now*. The report combines both.
review.json Schema
This is the data contract between Python and Claude. Every field below is guaranteed to exist in the output. Claude reads this structure; Claude does NOT invent data that isn't here.
{
"version": "0.1.0",
// --- Metadata ---
"metadata": {
"generated_at": "2026-03-05T18:00:00Z",
"data_start": "2025-01-01",
"data_end": "2025-12-31",
"total_orders": 5784,
"total_customers": 2765,
"total_revenue": 1383651,
"currency": "USD",
"revenue_definition": "Net sales after discounts, before tax and shipping"
},
// --- Data quality warnings (empty array = no issues) ---
"data_quality": [
// Example entries (only present when issues detected):
// { "type": "partial_period", "period": "2026-02", "days_with_data": 3,
// "message": "Latest month (2026-02) has only 3 days of data. MoM comparisons use prior complete months." }
// { "type": "short_data_span", "days": 45, "message": "Data spans only 45 days..." }
// { "type": "limited_data_span", "days": 200, "message": "Data spans 200 days (<1 year)..." }
],
// --- Data coverage: which periods are available ---
"data_coverage": {
"30d": true, // true if >=45 days of data exist
"90d": true, // true if >=120 days of data exist
"365d": true // true if >=400 days of data exist
},
// --- Period metrics (one block per available period) ---
"periods": {
"30d": {
"summary": {
"revenue": 98000,
"revenue_change": -0.003, // vs prior 30d
"orders": 412,
"orders_change": -0.03,
"aov": 238,
"aov_change": -0.001,
"customers": 287,
"customers_change": -0.05
},
"kpi_tree": {
"new_customer_revenue": 38000,
"new_customer_revenue_share": 0.388,
"new_customers": 95,
"new_customers_change": -0.08,
"new_customer_aov": 400,
"returning_customer_revenue": 60000,
"returning_customer_revenue_share": 0.612,
"returning_customers": 192,
"returning_customers_change": -0.03,
"returning_customer_aov": 312
},
"drivers": {
"aov_effect": 1200, // revenue change attributable to AOV
"volume_effect": -1500, // revenue change attributable to order count
"mix_effect": 0 // revenue change attributable to new/returning mix shift
}
},
"90d": { /* same structure */ },
"365d": {
/* same structure as 30d (summary, kpi_tree, drivers) plus: */
"repeat_purchase_rate": 0.38, // only in 365d block
"monthly_trend": [
{ "month": "2025-01", "revenue": 95000, "orders": 420, "aov": 226, "customers": 310, "new_customers": 180, "returning_customers": 130, "days_with_data": 31 },
// ... only months with actual data (no zero-fill for future months)
{ "month": "2025-12", "revenue": 12000, "orders": 45, "aov": 267, "customers": 38, "new_customers": 10, "returning_customers": 28, "days_with_data": 3, "partial": true }
]
}
},
// --- Health checks (powers 🟢/🟡/🔴 markers) ---
"health": {
"checks": [
{
"id": "monthly_revenue_trend", // internal only, never expose
"category": "revenue",
"severity": "high",
"result": "watch", // pass | watch | fail
"message": "MoM revenue growth: -3.8%",
"value": -0.038,
"threshold": 0.0
}
// ... more checks
],
"top_issues": [
// Pre-sorted by severity * impact. Max 10.
{
"id": "multi_item_order_rate",
"category": "product",
"severity": "high",
"result": "fail",
"message": "Multi-item order rate: 0.0%",
"estimated_annual_impact": 77573
}
// ...
]
},
// --- Pre-computed action candidates ---
"action_candidates": [
{
"action": "Introduce product bundles to increase multi-item orders",
"source_check": "multi_item_order_rate", // internal reference
"severity": "high",
"estimated_annual_impact": 77573,
"timeline": "this_month"
}
// ... max 10 candidates, sorted by impact
]
}Schema Rules
periodsonly contains keys for periods wheredata_coverageistrue- All
_changefields are proportional change vs prior period (e.g., 0.08 = +8%) health.checkscontains every check result;health.top_issuesis a filtered/sorted subset (fail and watch only, max 10, sorted by severity × impact)action_candidatesare suggestions from Python; Claude refines, merges, and rewrites them in business language for the reportmonthly_trendcontains only months with actual order data (no zero-fill for future months)monthly_trendentries with"partial": truehave less than half the month's days with data- When
data_qualityis non-empty, mention relevant warnings in Data Notes
Reference Files
Load on-demand -- do NOT load all at startup.
Path: references/review-narratives.md-- Narrative templates by health level and growth trajectory, finding templates, period-specific guidance. Always load.finding-clusters.md-- Cluster model for grouping related issues into themes. Load to identify themes.recommended-actions.md-- Actionable recommendations per check with timeline and impact range. Load for watch/fail checks.impact-formulas.md-- Revenue impact formulas with worked examples. Load when estimating impact.health-checks.md-- Check definitions (R/C/P) with thresholds and interpretation.benchmarks.md-- D2C ecommerce KPI benchmarks.
Output Format: REVIEW.md / REVIEW_{PERIOD}.md
The following structure applies to Full Review mode only. See Response Modes for Focused Query.
One report. Three parts. Target: ~150 lines. Tighter is better.
Report Construction Checklist (MANDATORY)
Before writing, verify your report contains ALL sections in this exact order. Missing or reordered sections are a format violation.
- Executive Summary -- narrative blockquote (4-6 lines) + Scoreboard table
- 30d Pulse section (if data available) -- KPI tree + max 1 finding
- 90d Momentum section (if data available) -- KPI tree + drivers + max 2 findings
- 365d Structure section (if data available) -- KPI tree + drivers + max 3 findings
- Action Plan -- max 5 items grouped by time horizon + Guardrails
- Data Notes -- 2-4 lines
Structural rules:
- Sections MUST appear in this order. Do NOT reorganize by theme.
- Period sections MUST use the KPI tree format with emoji markers.
- Do NOT create standalone sections like "What's Working Well" or "Issues to Address".
- Positive signals belong in Executive Summary narrative and KPI tree markers.
Part 1: Executive Summary
Narrative (4-6 lines, blockquote)
Synthesize across all available periods. Structure:
[North Star result + trend across periods]
[What's working: 1-2 strengths confirmed by data -- 80/20 rule]
[What needs attention: the key tension/risk, with data]
[Most important action, with timeline]Example:
Revenue reached $1.38M for the year (+25.7% YoY), but growth is decelerating -- the last 90 days grew only 8% vs prior 90 days, and last month was flat (+0.3%). Growth depends on existing customer AOV increases (+14.8%), while new customer acquisition has stalled (share: 42.3%, unchanged). Reallocate 20% of retention budget to acquisition channels by end of this month, targeting CPA below $XX.
Scoreboard
30d Pulse 90d Momentum 365d Structure
Revenue $98K (= flat) $340K (+ 8%) $1.38M (+ 26%)
Orders 412 (- 3%) 1,280 (+ 5%) 5,784 (+ 10%)
AOV $238 (= flat) $266 (+ 12%) $239 (+ 15%)
Customers 287 (- 5%) 812 (+ 3%) 2,765 (+ 10%)+ improving, - deteriorating, = stable. Only show periods that data supports.
Part 2: Period Sections
CRITICAL: Each period gets its own heading and its own KPI tree. Do NOT merge periods into thematic sections.
All periods use the same skeleton. But they are not equal length:
| Period | Depth | Findings cap | Role |
|---|---|---|---|
| 30d Pulse | Shallow -- KPI tree + 1-2 sentences + max 1 finding | 1 | Flag fires only |
| 90d Momentum | Medium -- KPI tree + drivers + max 2 findings | 2 | Main analytical body |
| 365d Structure | Deep -- KPI tree + drivers + max 3 findings | 3 | Strategic narrative |
If only one period is available, give it the full depth (3 findings).
The total across all periods must not exceed 5-7 findings. If a theme appears in multiple periods, state it once at the most structural level and reference supporting signals from shorter periods.
Skeleton
[Period Name]: [One-line headline -- the "so what"]
KPI Tree:
Revenue $X (vs prior period: +X%)
|-- 🟢 New Customer Revenue $X (X% of total)
| |-- New Customers: X (+X%)
| |-- New Customer AOV: $X (+X%)
|-- 🟡 Existing Customer Revenue $X (X% of total)
|-- Returning Customers: X (+X%)
|-- Returning AOV: $X (+X%)
|-- Repeat Purchase Rate: X% -- first-to-second purchase conversion (365d only)🟢 healthy / 🟡 watch / 🔴 problem (driven by health check results).
Growth Drivers (1-2 sentences, 90d and 365d only):
Was the change volume-driven or price-driven? Connect to KPI tree nodes.
Findings (within period cap):
Each follows Finding Quality Standards. Findings should weave trend data and health check diagnostics together, not present them separately. Use this format:
[Finding title]
What is: [1 sentence, quantitative fact]
Why it matters: [Data-backed tension with "however"/"despite"/"but"]
What to do: [Direction only — 1 sentence. Details go in Action Plan.]
Part 3: Action Plan (unified, max 5 items)
Hard cap: 5 action items. Exceeding 5 is a format violation.
Action Plan is the single source of truth for deadlines and success metrics. Findings point to the problem and direction; Action Plan specifies the execution.
One list synthesizing across all periods. Group by time horizon:
Immediate (from 30d signals)
1. ...
This Month (from 90d findings)
2. ...
This Quarter (from 365d insights)
3. ...For each item:
- What (one concrete sentence)
- Why (reference specific data)
- When (specific deadline)
- Success metric (measurable)
End with (REQUIRED -- omitting Guardrails is a format violation):
Guardrails: [2-3 metrics that must not deteriorate while executing above actions] Example: AOV must stay above $X | Repeat purchase rate must not drop below X% | Discount rate must stay under X%
Data Notes (2-4 lines)
Always include:
- Revenue definition (from
metadata.revenue_definition) - Data period and order count
- Periods included/omitted
Period Interaction Rules
- Confirm or contradict across periods. Don't let periods exist in isolation.
- Zoom in: If 90d shows a break, check 30d for continuation.
- Never repeat a finding across periods. State once at the structural level.
- Executive Summary synthesizes, does not summarize sequentially.
Finding Quality Standards
Every finding follows: What is → Why it matters → What to do
- What is: 1 sentence. Quantitative fact. No interpretation.
- Why it matters: Data-backed tension. Must show contrast ("despite", "however"). Abstract concerns like "growth must be validated" are PROHIBITED.
- What to do: Direction and rationale only — 1 sentence. Deadlines and success metrics belong in the Action Plan. "Consider", "improve", "optimize", "explore" are PROHIBITED.
Good:
What is: Annual revenue grew 25.7% YoY.
Why it matters: However, despite a 25.4% reduction in discount rate, new customer
revenue share remains at 42.3% -- growth depends entirely on
existing customer AOV increases (+14.8%).
What to do: Reallocate retention budget toward acquisition channels to diversify growth sources.Bad (PROHIBITED):
What is: Revenue grew 25.7%.
Why it matters: Rapid growth must be validated. <-- no data, no tension
What to do: Consider improving acquisition. <-- banned verb, no deadlineHandling Incomplete Data
- Omit what you can't measure. No N/A, no empty sections.
- Don't apologize. Never say "unfortunately..."
- Shorter data = shorter report.
- All gaps noted in Data Notes only.
Language and Data Scope Rules
Language
- Write entire report in ONE language (match user's prompt/store language)
- Data Notes follows same language as body
Data Scope: review.json Only
- All numbers MUST come from review.json or be derived from its values
- Do NOT reference external sources (Shopify Analytics, GA, etc.)
- If additional data would help, note as recommended investigation in "What to do"
- PROHIBITED: "CVR from Shopify shows 2.1%" (CVR not in review.json)
- ALLOWED: "Investigate CVR in Shopify Analytics to determine root cause"
data_quality Warnings
- When
data_qualityarray is non-empty, mention relevant warnings in Data Notes - Do NOT present partial-month MoM as real performance signals
Quality Gates
- Never present numbers without interpretation
- Always explain why, not just the value
- Specific, actionable recommendations only
- Business language, not jargon (explain terms on first use)
- Connect related findings into systemic patterns
- No internal check IDs in the report
- No check counts in the report
- No repeated findings across sections
- Finding Quality Standards on every finding
- Guardrails on every action plan
- 80/20 rule: ~80% confirmation (builds trust), ~20% surprise (drives action)
Internal: Health Check Engine
Each check returns pass / watch / fail. These power the 🟢🟡🔴 KPI tree markers. Check definitions in references/health-checks.md. Do NOT use numeric scores, letter grades, or percentage-based health ratings.Runtime
The skill uses a local Python runtime installed in ~/.claude/skills/ecom/.venv/. Use the wrapper command:
~/.claude/skills/ecom/bin/ecom review orders.csv --output <output-dir>
~/.claude/skills/ecom/bin/ecom review orders.csv --period 90d --output <output-dir>Modules: loader, metrics, decomposition, cohort, product, checks, report, review_engine, config, normalize, periods.