claude-code-arcane 1.3.0 → 1.4.0

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.
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: commerce-data-model
3
+ description: "Design and audit the core e-commerce domain model — products, variants, SKUs, options, collections, attributes, taxonomy, faceted search and pagination. Language- and platform-agnostic. Triggers: modeling a catalog, reviewing an existing product schema, designing filtering/search."
4
+ argument-hint: "[model | audit | taxonomy]"
5
+ category: "ecommerce"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Write, Edit
8
+ ---
9
+
10
+ # Commerce Data Model
11
+
12
+ Design the backbone of any storefront: a product catalog model that survives variants, bundles, localization, and faceted search — independent of language, framework, or database.
13
+
14
+ > **Note:** This skill may create or modify files in your project (a model spec or schema doc). It will ask before writing.
15
+
16
+ ## Before Starting
17
+
18
+ Gather: business context (physical/digital/services, single vs multi-vendor, B2C/B2B), scale (SKU count, variant explosion, locales/currencies), and goal (model from scratch, audit an existing schema, or design taxonomy + filtering).
19
+
20
+ ## How This Skill Works
21
+
22
+ | Mode | Trigger | Output |
23
+ |------|---------|--------|
24
+ | **Model** | `model` | Entity model spec: Product, Variant, SKU, Option, Collection, Attribute |
25
+ | **Audit** | `audit` | Gap analysis of an existing schema against commerce best practices |
26
+ | **Taxonomy** | `taxonomy` | Category tree, attribute schema, faceted-search and pagination design |
27
+
28
+ ---
29
+
30
+ ## Phase 1 — Core Entities
31
+
32
+ The canonical separation that prevents 90% of catalog pain:
33
+
34
+ | Entity | Is | Is NOT | Key fields |
35
+ |--------|----|--------|-----------|
36
+ | **Product** | The marketing/SEO unit ("Acme Tee") | A buyable thing | title, description, slug, status, brand |
37
+ | **Variant** | A buyable combination ("Tee / Red / M") | A category | option values, price, weight, barcode |
38
+ | **SKU** | The stock-keeping identifier | A variant's price | code, variant_id |
39
+ | **Option** | An axis of variation (Color, Size) | A value | name, position |
40
+ | **Collection** | A merchandising grouping | A taxonomy node | title, rules (manual/automatic) |
41
+
42
+ **Rule:** Price, inventory, and barcode live on the **Variant**, never the Product. A product with no options still has exactly one variant.
43
+
44
+ For the full field-level spec, identifiers (GTIN/MPN), bundles/kits, and digital vs physical modeling, see [references/data-model-spec.md](references/data-model-spec.md).
45
+
46
+ ---
47
+
48
+ ## Phase 2 — Attributes & Taxonomy
49
+
50
+ - **Taxonomy** (category tree): hierarchical, navigational, one primary path per product. Stable URLs.
51
+ - **Collections**: many-to-many merchandising overlays (manual or rule-based). A product lives in one category branch but many collections.
52
+ - **Attributes**: typed key-values (`material: cotton`, `sleeve: short`). Drive facets. Define a controlled vocabulary — free-text attributes break filtering.
53
+
54
+ **Attribute typing:** `text | enum | number | boolean | measurement`. Enums must reference a value list. Measurements carry a unit.
55
+
56
+ ---
57
+
58
+ ## Phase 3 — Faceted Search & Pagination
59
+
60
+ - Facets come from `enum`/`boolean`/`measurement` attributes, not free text.
61
+ - Multi-select within a facet = OR; across facets = AND.
62
+ - Always show counts; never show zero-result facet values.
63
+ - **Pagination:** prefer cursor/keyset over offset for deep catalogs (offset degrades and double-counts under writes). Cap page size.
64
+
65
+ ---
66
+
67
+ ## Output Artifacts
68
+
69
+ | Request | Output |
70
+ |---------|--------|
71
+ | Model from scratch | Entity spec + ER sketch + identifier strategy |
72
+ | Audit schema | Verdict per entity: COMPLIANT / CONCERNS, with fixes |
73
+ | Design taxonomy | Category tree + attribute schema + facet plan |
74
+
75
+ A schema is **COMPLIANT** when price/inventory sit on variants, every product has ≥1 variant, attributes are typed against vocabularies, and URLs are stable.
76
+
77
+ ---
78
+
79
+ ## Related Skills
80
+
81
+ - **storefront-architecture**: consumes this model to design PLP/PDP pages. Next step after modeling.
82
+ - **merchant-feed**: maps these entities to Google/Meta feed attributes.
83
+ - **inventory-stock**: owns stock levels that hang off the SKU/variant.
84
+ - **order-lifecycle**: references variants as order line items.
@@ -0,0 +1,79 @@
1
+ # Commerce Data Model — Field-Level Spec & Worked Example
2
+
3
+ A language- and database-agnostic reference for the core catalog entities. Use it as the template for a model spec.
4
+
5
+ ## Entities & Fields
6
+
7
+ ### Product (marketing/SEO unit)
8
+ | Field | Type | Notes |
9
+ |-------|------|-------|
10
+ | id | id | Internal key |
11
+ | title | text | Display name |
12
+ | slug | text | Stable URL segment, unique |
13
+ | description | rich text | Long copy |
14
+ | brand | text | Brand/manufacturer |
15
+ | status | enum | draft / active / archived |
16
+ | options | Option[] | Axes of variation (0..n) |
17
+ | variants | Variant[] | ≥1 always |
18
+ | collections | Collection[] | Many-to-many |
19
+ | primary_category | Category | One navigational path |
20
+ | attributes | Attribute[] | Typed key-values |
21
+
22
+ ### Variant (buyable unit)
23
+ | Field | Type | Notes |
24
+ |-------|------|-------|
25
+ | id | id | |
26
+ | sku | text | Stock-keeping code, unique |
27
+ | option_values | map | e.g. {Color: Red, Size: M} |
28
+ | price | integer (minor units) | Never a float |
29
+ | compare_at_price | integer | For showing discounts |
30
+ | barcode | text | GTIN/UPC/EAN |
31
+ | weight / dimensions | number + unit | For shipping/carrier rates |
32
+ | requires_shipping | bool | False for digital |
33
+
34
+ ### Option / Option Value
35
+ `Option { name, position }` → `OptionValue { option_id, value, position }`. Example: Option "Size" → values S, M, L.
36
+
37
+ ### Collection (merchandising)
38
+ `Collection { title, slug, type: manual|automatic, rules? }`. Automatic collections select by attribute rules (e.g. `tag = summer`).
39
+
40
+ ### Attribute (faceting)
41
+ `Attribute { key, type: text|enum|number|boolean|measurement, value, unit? }`. Enum attributes reference a controlled value list.
42
+
43
+ ## Identifiers
44
+ - **SKU** — your internal stock identifier (required, unique).
45
+ - **GTIN** (UPC/EAN/ISBN) — global trade identifier; required for marketplaces/feeds where one exists.
46
+ - **MPN** — manufacturer part number; fallback when no GTIN.
47
+
48
+ ## Bundles / Kits
49
+ Model as a Product whose variant references component variants with quantities. Decide inventory rule: track by components (decrement each) vs by kit (own stock).
50
+
51
+ ## Worked Example — "Acme Tee"
52
+
53
+ ```yaml
54
+ product:
55
+ title: "Acme Cotton Tee"
56
+ slug: "acme-cotton-tee"
57
+ brand: "Acme"
58
+ status: active
59
+ options:
60
+ - { name: Color, values: [Red, Blue] }
61
+ - { name: Size, values: [S, M, L] }
62
+ attributes:
63
+ - { key: material, type: enum, value: cotton }
64
+ - { key: sleeve, type: enum, value: short }
65
+ variants:
66
+ - sku: ACME-TEE-RED-M
67
+ option_values: { Color: Red, Size: M }
68
+ price: 2100 # 21.00 in minor units
69
+ barcode: "0123456789012"
70
+ weight: { value: 180, unit: g }
71
+ ```
72
+
73
+ ## Checklist (verdict: COMPLIANT when all true)
74
+ - [ ] Price + inventory + barcode live on the variant, not the product
75
+ - [ ] Every product has ≥1 variant (even with no options)
76
+ - [ ] Attributes are typed and enums reference a vocabulary
77
+ - [ ] Primary category is single; collections are many-to-many
78
+ - [ ] Money stored as integer minor units
79
+ - [ ] Slugs/URLs stable and unique
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: inventory-stock
3
+ description: "Design inventory and stock control — availability tracking, optimistic locking to prevent overselling, reservations, multi-location, backorders, and restock forecasting. Includes a safety-stock/reorder-point calculator. Triggers: designing inventory, fixing overselling, forecasting restock."
4
+ argument-hint: "[design | forecast | audit]"
5
+ category: "ecommerce"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash
8
+ ---
9
+
10
+ # Inventory & Stock
11
+
12
+ Stock is where data integrity meets revenue: oversell and you lose trust, over-reserve and you lose sales. Design availability that is correct under concurrency and forecast restocks from real demand — platform-agnostic.
13
+
14
+ ## Before Starting
15
+
16
+ Gather: inventory model (single vs multi-location), sales velocity and lead times, fulfillment style (own stock, dropship, backorder), and goal (design the model, forecast restock, or audit for oversell risk).
17
+
18
+ ## How This Skill Works
19
+
20
+ | Mode | Trigger | Output |
21
+ |------|---------|--------|
22
+ | **Design** | `design` | Availability model + concurrency strategy |
23
+ | **Forecast** | `forecast` | Safety stock + reorder points (via script) |
24
+ | **Audit** | `audit` | Oversell-risk and accuracy review |
25
+
26
+ ---
27
+
28
+ ## Phase 1 — Availability Model
29
+
30
+ Track three numbers per SKU per location, never one:
31
+
32
+ | Quantity | Meaning |
33
+ |----------|---------|
34
+ | **on_hand** | Physically present |
35
+ | **reserved** | Allocated to unfulfilled orders/checkouts |
36
+ | **available** | `on_hand − reserved` — the only number a shopper sees |
37
+
38
+ **Rule:** Reserve at checkout start with a TTL; release on timeout/abandonment. Decrement `on_hand` at fulfillment, not at order placement.
39
+
40
+ ---
41
+
42
+ ## Phase 2 — Preventing Overselling
43
+
44
+ The classic race: two shoppers buy the last unit. Defenses, in order of preference:
45
+
46
+ 1. **Optimistic locking** — version/row check on decrement; retry on conflict. Default choice.
47
+ 2. **Atomic conditional update** — `UPDATE ... WHERE available >= qty` and check rows-affected.
48
+ 3. **Pessimistic lock** — only for high-contention single rows; costs throughput.
49
+
50
+ Always treat "reserve stock" as a transaction that can FAIL and surface that to checkout cleanly. Multi-location adds an allocation step (which location fulfills) before the decrement.
51
+
52
+ For backorder rules, multi-location allocation strategies, and reservation TTL tuning, see the patterns above and hand off to `order-lifecycle` for fulfillment state.
53
+
54
+ ---
55
+
56
+ ## Phase 3 — Restock Forecasting
57
+
58
+ Use the script to turn a sales CSV into reorder points:
59
+
60
+ ```bash
61
+ python skills/inventory-stock/scripts/restock_forecast.py # demo data
62
+ python skills/inventory-stock/scripts/restock_forecast.py sales.csv --lead-time 14 --service-level 0.95
63
+ ```
64
+
65
+ It computes average daily demand, demand variability, **safety stock** (`z × σ_demand × √lead_time`), and **reorder point** (`demand × lead_time + safety_stock`). CSV columns: `sku,date,units_sold`.
66
+
67
+ ---
68
+
69
+ ## Output Artifacts
70
+
71
+ | Request | Output |
72
+ |---------|--------|
73
+ | Design | Availability model + chosen concurrency strategy |
74
+ | Forecast | Per-SKU reorder point + safety stock table |
75
+ | Audit | Oversell-risk findings: PASS / FAIL per check |
76
+
77
+ An inventory design **PASSES** when shoppers only see `available`, reservations expire, decrements are atomic/versioned, and forecasts drive reorder points instead of guesswork.
78
+
79
+ ---
80
+
81
+ ## Related Skills
82
+
83
+ - **commerce-data-model**: defines the SKU/variant that stock hangs off.
84
+ - **order-lifecycle**: consumes reservations and triggers decrements at fulfillment.
85
+ - **cart-checkout**: starts the reservation when checkout begins.
86
+ - **commerce-analytics**: sell-through and turnover metrics that feed forecasts.
@@ -0,0 +1,173 @@
1
+ """
2
+ restock_forecast.py — Safety stock & reorder points from a sales history CSV.
3
+
4
+ Turns daily sales into per-SKU reorder signals using the classic
5
+ reorder-point model. No external dependencies (stdlib only), cross-platform.
6
+
7
+ Usage:
8
+ python restock_forecast.py # built-in demo data
9
+ python restock_forecast.py sales.csv # your data, defaults
10
+ python restock_forecast.py sales.csv --lead-time 14 --service-level 0.95
11
+ python restock_forecast.py sales.csv --on-hand on_hand.csv # flag what to reorder
12
+
13
+ CSV format (header required):
14
+ sku,date,units_sold
15
+ ACME-TEE-RED-M,2026-05-01,4
16
+ ACME-TEE-RED-M,2026-05-02,2
17
+ ...
18
+
19
+ Optional on-hand CSV (header required): sku,on_hand
20
+
21
+ Model:
22
+ avg_daily_demand = mean(units_sold per day)
23
+ sigma_demand = stdev(units_sold per day)
24
+ safety_stock = z(service_level) * sigma_demand * sqrt(lead_time)
25
+ reorder_point = avg_daily_demand * lead_time + safety_stock
26
+
27
+ When on-hand is provided, a SKU is flagged REORDER when on_hand <= reorder_point.
28
+ """
29
+
30
+ import argparse
31
+ import csv
32
+ import math
33
+ import sys
34
+ from collections import defaultdict
35
+
36
+
37
+ # Embedded demo data: two SKUs, ~3 weeks of daily sales.
38
+ DEMO_ROWS = [
39
+ ("ACME-TEE-RED-M", d, u)
40
+ for d, u in zip(
41
+ [f"2026-05-{n:02d}" for n in range(1, 22)],
42
+ [4, 2, 5, 3, 6, 8, 7, 3, 4, 5, 2, 6, 9, 7, 4, 3, 5, 6, 8, 4, 5],
43
+ )
44
+ ] + [
45
+ ("ACME-MUG-BLUE", d, u)
46
+ for d, u in zip(
47
+ [f"2026-05-{n:02d}" for n in range(1, 22)],
48
+ [1, 0, 2, 1, 1, 3, 2, 0, 1, 1, 0, 2, 1, 2, 1, 0, 1, 2, 3, 1, 1],
49
+ )
50
+ ]
51
+ DEMO_ON_HAND = {"ACME-TEE-RED-M": 40, "ACME-MUG-BLUE": 60}
52
+
53
+
54
+ def z_for_service_level(p):
55
+ """Inverse standard-normal CDF (Acklam's rational approximation)."""
56
+ if not 0.0 < p < 1.0:
57
+ raise ValueError("service-level must be between 0 and 1 (exclusive)")
58
+ a = [-3.969683028665376e+01, 2.209460984245205e+02, -2.759285104469687e+02,
59
+ 1.383577518672690e+02, -3.066479806614716e+01, 2.506628277459239e+00]
60
+ b = [-5.447609879822406e+01, 1.615858368580409e+02, -1.556989798598866e+02,
61
+ 6.680131188771972e+01, -1.328068155288572e+01]
62
+ c = [-7.784894002430293e-03, -3.223964580411365e-01, -2.400758277161838e+00,
63
+ -2.549732539343734e+00, 4.374664141464968e+00, 2.938163982698783e+00]
64
+ d = [7.784695709041462e-03, 3.224671290700398e-01, 2.445134137142996e+00,
65
+ 3.754408661907416e+00]
66
+ plow, phigh = 0.02425, 1 - 0.02425
67
+ if p < plow:
68
+ q = math.sqrt(-2 * math.log(p))
69
+ return (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / \
70
+ ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)
71
+ if p > phigh:
72
+ q = math.sqrt(-2 * math.log(1 - p))
73
+ return -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / \
74
+ ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)
75
+ q = p - 0.5
76
+ r = q * q
77
+ return (((((a[0]*r+a[1])*r+a[2])*r+a[3])*r+a[4])*r+a[5])*q / \
78
+ (((((b[0]*r+b[1])*r+b[2])*r+b[3])*r+b[4])*r+1)
79
+
80
+
81
+ def load_sales(path):
82
+ rows = []
83
+ with open(path, newline="", encoding="utf-8") as f:
84
+ reader = csv.DictReader(f)
85
+ required = {"sku", "date", "units_sold"}
86
+ if not required.issubset({c.strip() for c in (reader.fieldnames or [])}):
87
+ print(f"[ERROR] {path}: header must include sku,date,units_sold")
88
+ sys.exit(1)
89
+ for r in reader:
90
+ try:
91
+ rows.append((r["sku"].strip(), r["date"].strip(), float(r["units_sold"])))
92
+ except (KeyError, ValueError):
93
+ print(f"[WARN] skipping malformed row: {r}")
94
+ return rows
95
+
96
+
97
+ def load_on_hand(path):
98
+ out = {}
99
+ with open(path, newline="", encoding="utf-8") as f:
100
+ for r in csv.DictReader(f):
101
+ try:
102
+ out[r["sku"].strip()] = float(r["on_hand"])
103
+ except (KeyError, ValueError):
104
+ pass
105
+ return out
106
+
107
+
108
+ def stdev(xs):
109
+ if len(xs) < 2:
110
+ return 0.0
111
+ mean = sum(xs) / len(xs)
112
+ return math.sqrt(sum((x - mean) ** 2 for x in xs) / (len(xs) - 1))
113
+
114
+
115
+ def forecast(rows, lead_time, service_level, on_hand):
116
+ z = z_for_service_level(service_level)
117
+ by_sku = defaultdict(list)
118
+ for sku, _date, units in rows:
119
+ by_sku[sku].append(units)
120
+
121
+ results = []
122
+ for sku in sorted(by_sku):
123
+ daily = by_sku[sku]
124
+ avg = sum(daily) / len(daily)
125
+ sigma = stdev(daily)
126
+ safety = z * sigma * math.sqrt(lead_time)
127
+ rop = avg * lead_time + safety
128
+ oh = on_hand.get(sku)
129
+ flag = ""
130
+ if oh is not None:
131
+ flag = "REORDER" if oh <= rop else "OK"
132
+ results.append((sku, avg, sigma, safety, rop, oh, flag))
133
+ return results
134
+
135
+
136
+ def main():
137
+ ap = argparse.ArgumentParser(description="Safety stock & reorder points from sales CSV.")
138
+ ap.add_argument("csv", nargs="?", help="sales CSV (sku,date,units_sold). Omit for demo.")
139
+ ap.add_argument("--lead-time", type=float, default=14, help="supplier lead time in days (default 14)")
140
+ ap.add_argument("--service-level", type=float, default=0.95, help="0-1 target fill rate (default 0.95)")
141
+ ap.add_argument("--on-hand", help="optional CSV (sku,on_hand) to flag what to reorder")
142
+ args = ap.parse_args()
143
+
144
+ if args.csv:
145
+ rows = load_sales(args.csv)
146
+ on_hand = load_on_hand(args.on_hand) if args.on_hand else {}
147
+ else:
148
+ print("[INFO] No CSV given - using built-in demo data.\n")
149
+ rows = DEMO_ROWS
150
+ on_hand = DEMO_ON_HAND
151
+
152
+ if not rows:
153
+ print("[ERROR] No sales rows to analyze.")
154
+ sys.exit(1)
155
+
156
+ results = forecast(rows, args.lead_time, args.service_level, on_hand)
157
+
158
+ z = z_for_service_level(args.service_level)
159
+ print(f"Lead time: {args.lead_time:g}d | Service level: {args.service_level:g} (z={z:.2f})\n")
160
+ header = f"{'SKU':<20} {'avg/day':>8} {'sigma':>7} {'safety':>8} {'reorder_pt':>11} {'on_hand':>8} {'flag':>8}"
161
+ print(header)
162
+ print("-" * len(header))
163
+ for sku, avg, sigma, safety, rop, oh, flag in results:
164
+ oh_s = f"{oh:g}" if oh is not None else "-"
165
+ print(f"{sku:<20} {avg:>8.2f} {sigma:>7.2f} {safety:>8.1f} {rop:>11.1f} {oh_s:>8} {flag:>8}")
166
+
167
+ reorder = [r for r in results if r[6] == "REORDER"]
168
+ if on_hand:
169
+ print(f"\n{'PASS' if not reorder else 'ACTION'}: {len(reorder)} SKU(s) at/below reorder point.")
170
+
171
+
172
+ if __name__ == "__main__":
173
+ main()
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: merchant-feed
3
+ description: "Build and validate product feeds for shopping channels — Google Merchant Center and Meta Catalog — including required/recommended attributes, attribute mapping, and feed validation. Includes a feed validator/generator script. Triggers: building a product feed, fixing feed disapprovals, mapping catalog to feed attributes."
4
+ argument-hint: "[validate | generate | map]"
5
+ category: "ecommerce"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash
8
+ ---
9
+
10
+ # Merchant Feed
11
+
12
+ Shopping ads and free listings live or die on feed quality. A clean, complete feed gets products approved and surfaced; a sloppy one gets disapprovals. Map your catalog to channel attributes and validate before upload — platform-agnostic.
13
+
14
+ ## Before Starting
15
+
16
+ Gather: target channel (Google Merchant / Meta Catalog), source of truth (DB export, CSV, platform), product categories, and goal (validate an existing feed, generate one, or map catalog → feed attributes).
17
+
18
+ ## How This Skill Works
19
+
20
+ | Mode | Trigger | Output |
21
+ |------|---------|--------|
22
+ | **Validate** | `validate` | Pass/fail report against required attributes |
23
+ | **Generate** | `generate` | Feed emitted from a source CSV |
24
+ | **Map** | `map` | Catalog field → feed attribute mapping |
25
+
26
+ ---
27
+
28
+ ## Phase 1 — Required Attributes
29
+
30
+ Google Merchant / Meta share a common core. Required for every item:
31
+
32
+ | Attribute | Notes |
33
+ |-----------|-------|
34
+ | `id` | Stable, unique per variant (use SKU) |
35
+ | `title` | Front-load product; ≤150 chars; match landing page |
36
+ | `description` | Plain, accurate; no promo text/caps |
37
+ | `link` | Canonical PDP URL |
38
+ | `image_link` | Main image, meets resolution minimums |
39
+ | `availability` | in_stock / out_of_stock / preorder |
40
+ | `price` | With currency; must match landing page exactly |
41
+ | `brand` | Required unless exempt category |
42
+ | `gtin` / `mpn` | Required where a GTIN exists — improves matching |
43
+ | `condition` | new / refurbished / used |
44
+
45
+ **Rule:** Feed price and availability must **exactly match** the live PDP, or the item is disapproved and the account is penalized.
46
+
47
+ ---
48
+
49
+ ## Phase 2 — Validate / Generate
50
+
51
+ Run the validator on a CSV export:
52
+
53
+ ```bash
54
+ python skills/merchant-feed/scripts/feed_validate.py # demo data
55
+ python skills/merchant-feed/scripts/feed_validate.py feed.csv
56
+ ```
57
+
58
+ It checks every row for required attributes, flags missing/empty values, validates `availability`/`condition` enums and price+currency format, and reports a per-row and summary verdict. For the full attribute reference and category-specific requirements, see [references/feed-attribute-map.md](references/feed-attribute-map.md).
59
+
60
+ ---
61
+
62
+ ## Phase 3 — Recommended Attributes & Quality
63
+
64
+ Beyond required, these lift performance: `google_product_category`, `product_type`, `item_group_id` (links variants), `color`, `size`, `gender`, `age_group`, `sale_price`, `shipping`, `gtin`. Use `item_group_id` so variants cluster correctly — without it, variants compete as separate products.
65
+
66
+ ---
67
+
68
+ ## Output Artifacts
69
+
70
+ | Request | Output |
71
+ |---------|--------|
72
+ | Validate | Per-row + summary PASS / FAIL report |
73
+ | Generate | Channel-ready feed file |
74
+ | Map | Catalog → feed attribute mapping table |
75
+
76
+ A feed is **READY** when every item has all required attributes, price/availability match the PDP, variants share an `item_group_id`, and GTINs are present where they exist.
77
+
78
+ ---
79
+
80
+ ## Related Skills
81
+
82
+ - **commerce-data-model**: the catalog entities mapped into feed attributes.
83
+ - **product-listing**: titles/descriptions reused between PDP and feed.
84
+ - **shipping-tax**: shipping attribute values in the feed.
85
+ - **schema-markup**: Product structured data complements the feed for organic.
@@ -0,0 +1,47 @@
1
+ # Product Feed — Attribute Reference & Catalog Mapping
2
+
3
+ Maps the commerce data model to Google Merchant / Meta Catalog feed attributes. The `feed_validate.py` script enforces the required core.
4
+
5
+ ## Required attributes (every item)
6
+ | Feed attribute | Source (data model) | Rule |
7
+ |----------------|---------------------|------|
8
+ | id | variant.sku | Stable, unique per variant |
9
+ | title | product.title (+ variant) | Front-load product; ≤150 chars; match PDP |
10
+ | description | product.description | Plain, accurate; no promo/ALL CAPS |
11
+ | link | product URL | Canonical, absolute https |
12
+ | image_link | main image | Absolute https; meets resolution min |
13
+ | availability | variant stock | in_stock / out_of_stock / preorder / backorder |
14
+ | price | variant.price | `"21.00 USD"` — matches PDP exactly |
15
+ | brand | product.brand | Required unless exempt category |
16
+ | condition | — | new / refurbished / used |
17
+
18
+ ## Strongly recommended
19
+ | Feed attribute | Source | Why |
20
+ |----------------|--------|-----|
21
+ | gtin | variant.barcode | Improves matching / eligibility |
22
+ | mpn | variant.mpn | Fallback when no GTIN |
23
+ | item_group_id | product.id | Clusters variants (else they compete) |
24
+ | google_product_category | category map | Taxonomy alignment |
25
+ | product_type | primary_category | Your own taxonomy |
26
+ | color / size / gender / age_group | attributes | Apparel facets |
27
+ | sale_price | compare_at logic | Promo pricing |
28
+ | shipping | shipping-tax rules | Overrides account settings |
29
+
30
+ ## Hard rules (disapproval triggers)
31
+ - Feed **price + availability must match the live PDP** exactly.
32
+ - No promotional text in `title`/`description` (no "Free shipping!", no emojis/caps).
33
+ - `image_link` must resolve and meet the channel's minimum resolution.
34
+ - Variants without a shared `item_group_id` are treated as separate products.
35
+
36
+ ## Worked example (CSV row)
37
+ ```csv
38
+ id,title,description,link,image_link,availability,price,brand,condition,gtin,item_group_id
39
+ ACME-TEE-RED-M,"Acme Cotton Tee — Red, Medium","Soft 100% cotton crew-neck tee.",https://shop.example.com/p/acme-tee-red-m,https://shop.example.com/img/acme-tee-red-m.jpg,in_stock,21.00 USD,Acme,new,0123456789012,ACME-TEE
40
+ ```
41
+
42
+ ## Verdict: READY when
43
+ - [ ] All required attributes present and non-empty
44
+ - [ ] price/availability match PDP
45
+ - [ ] valid availability + condition enums
46
+ - [ ] price formatted `amount ISO-currency`
47
+ - [ ] variants share item_group_id; GTIN present where it exists