Raw brand feed (.xlsx) + a marketplace template pack (.xlsx) → a valid, submittable listing template, with only the genuinely-uncertain cells routed to a human, and zero invented values. Built for Culture Kings → Macy's Men's Jackets; the pack is the input, so the next category/marketplace/brand is new files + a config file, no code change.
Live app: https://h8jsylyxhwwdmmwjfg3lzl.streamlit.app/ — click Load sample batch to watch the full backbone run (no API, no spend).
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m pytest tests/ -q # 4 suites green: 15 golden + 9 frontier + 7 compliance + round-trip
python -m pipeline.run # process the sample batch, print invariants, write out/*.xlsx
python -m golden.score # golden-set score (bar 0.95) + pack-drift report
streamlit run app.py # the web app: file in → completed 3-tab template out59 feed rows → 14 set-aside (licensed IP) → 45 sellable → 6 parents + 45 variants = 51 output rows · 14 grouped operator decisions (no exception scales per-SKU — missing dims across 38 rows is ONE batch decision covering 152 cells) · 3 compliance items on a separate stream (FTC care labeling · Prop 65 legal warnings · reference-price gate) · 0 P0 · validation PASS.
Output workbook: three tabs — Template (the pack's exact 40-column layout; flagged
required cells ship blank + highlighted, never a placeholder) · Exceptions Ledger (both
streams via the stream column, severity-tagged) · Validation Report.
- Config/Code: the pack (Field Guide = schema, Template = shape, Allowed Values =
vocabulary) is parsed at runtime, verbatim.
config/*.yamlholds only what the pack doesn't declare (filters, size canon, defaults, the business-set description word range). - Deterministic/AI: a static DAG — ingest → set-aside filters → grouping → deterministic
mapping + gated AI enrich → validate → triage → correction overlay → workbook. Exactly
three bounded AI nodes (description rewrite, color vision, image-one qualification), each
behind the ACI
(value, confidence, rationale, source)and a deterministic validator gate. Today they answer from an offline stub-cache (stubs/cache.json, built with Claude); a live Claude API / Snowflake Cortex call is one edit inpipeline/enrich.py. Cache miss →NEEDS_LIVE_CALLflag; the backbone still completes with no API and no spend. - Human/Machine: below-threshold cells become reason-coded flags, grouped into decisions
keyed to value/style/category (never a SKU). The operator PICKS from pack-legal
candidates; answers land in
memory/(append-only) and suppress their questions on the next run — the run-twice test proves it. - System/World: typed adapters. Files today; Snowflake in / Acenda out later without touching the pipeline.
pipeline/ the DAG (pure: no pandas, no network — grep-provable)
config/ what the pack doesn't declare (filters, size canon, defaults, word range)
data/ the sample feed, worked example, and Macy's jackets pack
stubs/ offline AI answer cache (regenerate: tools/build_stubs.py)
memory/ correction memory: mappings.csv (generalizing) + overrides.csv (row-specific)
golden/ golden set + promotion gate (score.py, promote.py) — growth is gated, never direct
tests/ the canonical numbers: golden / frontier / compliance / round-trip WAIVERS
tools/ regenerators for stubs and golden seeds
app.py Streamlit surface (runs the full backbone live per upload)
CLAUDE.md (working contract, commands, DO/DON'Ts) · DEPLOY.md (Streamlit Community Cloud +
persistence posture). The tests are the canonical numbers; code comments carry the per-rule
rationale (edge cases, size/color mapping doctrine).