{"slug":"all-designerwallcoverings","total":142,"limit":100,"offset":0,"since":null,"commits":[{"hash":"ecdd60e","date":"2026-09-24 23:11:04 -0700","author":"Steve Abrams","subject":"TK-12173: document versa-designed-surfaces 404 report as false positive (Versace mis-join; Versa sells as Hollywood private label)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01HFbfYmHuR8kNNNUrqEujkw"},{"hash":"24927fc","date":"2026-09-24 13:33:34 -0700","author":"Steve Abrams","subject":"build-data.py: harden active_count against stale registry counts; regenerate vendors.json (drops phantom Versa Designed Surfaces 842)","body":"active_count is sourced only from the live shopify_products ACTIVE query; removed\nunused vendor_registry.shopify_count/total_products SELECT columns that invited\nreintroducing the phantom count class. Regenerated local vendors.json: Versa\nDesigned Surfaces (0 ACTIVE, archived under Hollywood Wallcoverings) now absent.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"c38381b","date":"2026-09-09 12:49:57 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-09T12:49:50 (2 data files) — tk11330-applied.json tk11330-product-candidates.json","body":""},{"hash":"f859e9a","date":"2026-09-09 12:14:53 -0700","author":"Steve","subject":"Record published collection image audit","body":""},{"hash":"50a60e8","date":"2026-08-31 02:01:07 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-31T02:00:54 (1 data files) — package-lock.json","body":""},{"hash":"60c344a","date":"2026-08-31 01:29:45 -0700","author":"Steve","subject":"chore: v1.7.3 (session close — crawl-microsites pm2-CLI serialization)","body":""},{"hash":"2d035d0","date":"2026-08-30 22:57:40 -0700","author":"Steve Abrams","subject":"add creds-in-URL fetch guard to gated pages (TK-10984)","body":""},{"hash":"c9e5e8e","date":"2026-08-30 20:54:49 -0700","author":"Steve Abrams","subject":"use owner-safe PM2 CLI wrapper","body":""},{"hash":"43684c2","date":"2026-08-30 14:37:34 -0700","author":"Steve Abrams","subject":"crawl-microsites: coherent pm2-lock constants (deadline 10s > STALE 8s) — Kimi review fix (TK-10970)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"94b6673","date":"2026-08-30 14:33:34 -0700","author":"Steve Abrams","subject":"crawl-microsites: serialize seedFromPm2 pm2 jlist via shared mkdir-lock (TK-10970)","body":"Share the ~/.pm2/cli.lock.d lock with keep-alive so the two scheduled pm2-CLI callers\nnever hit rpc.sock concurrently and stop auto-forking orphan God daemons (pm2-fracture,\nTK-10932). FAIL-OPEN: crawl proceeds if lock unattainable. Local commit only — Kamatera\ndeploy stays Steve-gated per repo CLAUDE.md (never full-tree, never data/).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"c142012","date":"2026-08-28 10:31:45 -0700","author":"Steve Abrams","subject":"build full internal catalog for every unified vendor","body":""},{"hash":"76b09d5","date":"2026-08-28 10:16:43 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-28T10:16:39 (2 data files) — cta/rec/chrome/page@35bf18e98f593c7becba91687e1ed4f7.webm cta/rec/chrome/page@ff4c16d68b48a1506d15b684ad89bf1c.webm","body":""},{"hash":"2752be2","date":"2026-08-28 09:54:52 -0700","author":"Steve Abrams","subject":"record complete unified vendor endpoint proof","body":""},{"hash":"9cd80ae","date":"2026-08-28 09:51:30 -0700","author":"Steve Abrams","subject":"keep internal vendor hostnames within DNS limits","body":""},{"hash":"30003d2","date":"2026-08-28 09:43:43 -0700","author":"Steve Abrams","subject":"probe shared vendor pairs without downloading duplicate feeds","body":""},{"hash":"6032ba8","date":"2026-08-28 09:17:45 -0700","author":"Steve Abrams","subject":"serve every unified vendor through shared host endpoints","body":""},{"hash":"10fd1a2","date":"2026-08-28 09:09:18 -0700","author":"Steve Abrams","subject":"record live vendor pair deployment proof","body":""},{"hash":"59dee87","date":"2026-08-28 08:59:37 -0700","author":"Steve Abrams","subject":"preserve public vendor keys while pairing internal sites","body":""},{"hash":"3ae884e","date":"2026-08-28 08:59:20 -0700","author":"Steve Abrams","subject":"keep internal vendor twins distinct in the crawl","body":""},{"hash":"88bd22e","date":"2026-08-28 08:45:52 -0700","author":"Steve Abrams","subject":"load every unified vendor as a public and internal site pair","body":""},{"hash":"ce50840","date":"2026-08-25 10:48:53 -0700","author":"Steve","subject":"creds-safe fetch guard: resolve relative fetch vs credential-free location (creds-in-URL trap)","body":"Fleet inoculation — opening this basic-auth app with credentials in the URL\npoisoned document.baseURI and made relative fetch('/api/…') throw. Guard resolves\nnon-absolute request URLs against location instead. Ref: creds-in-url-fetch-guard-fleet-pattern.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"cb52b7a","date":"2026-08-25 10:16:58 -0700","author":"Steve","subject":"all.dw: dedupe grid — suppress sample-twin + cross-source-vendor duplicates (TK-10841)","body":"Add dedupeRows() post-concat pass in loadSnapshot(): drop a -sample card only\nwhen its non-sample base twin also renders (sample-only products untouched), and\ndrop microsite/catalog rows whose vendor is already a live Shopify vendor (incl.\nBespoke->DW Bespoke Studio alias). No DB writes; in-RAM only; git-revertable.\nVerified on running :9958: default grid 214,081 -> 188,434, 0 dupes remaining,\n38,245 sample-only + Fromental(shopify) preserved."},{"hash":"5f3f6eb","date":"2026-08-24 10:08:38 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-24T10:08:33 (1 data files) — package-lock.json","body":""},{"hash":"f0c0681","date":"2026-08-24 09:56:53 -0700","author":"Steve Abrams","subject":"chore: version bump v1.7.2 (session close)","body":""},{"hash":"c78be5a","date":"2026-08-24 09:07:04 -0700","author":"Steve Abrams","subject":"all.dw: split 'Catalog only' out of 'Staged' lifecycle + fix status-facet double-count","body":"- deriveMicrositeRow: never-on-Shopify *_catalog staging rows now read\n  'Catalog only' instead of 'Staged', so 'Staged' means only real Shopify\n  DRAFT / unpublished-ACTIVE go-live candidates (148,534 -> 42,854 local).\n- LIFE_ORDER: add 'Catalog only' chip.\n- /api/facets: status facet was passing {skip:'lifecycle'} (should be\n  {skip:'status'}); status facet now reconciles to total (314,254 -> 214,358).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"3446057","date":"2026-08-20 13:34:49 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-20T13:34:43 (1 data files) — package-lock.json","body":""},{"hash":"ea049e2","date":"2026-08-20 13:21:04 -0700","author":"Steve Abrams","subject":"chore: v1.7.1 (session close)","body":""},{"hash":"292b078","date":"2026-08-20 10:04:37 -0700","author":"Steve Abrams","subject":"fix: replace SELECT * with explicit columns in shopify-push (pre-deploy lint)","body":""},{"hash":"7ae6937","date":"2026-08-20 08:13:36 -0700","author":"Steve Abrams","subject":"nav-agent: sync v1.3 (host left-sidebar collapse on load)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"f421126","date":"2026-08-19 14:30:58 -0700","author":"Steve","subject":"nav-agent: List data full-width tight columns (grid) — layout fix","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"7e30b82","date":"2026-08-19 14:05:19 -0700","author":"Steve","subject":"Refresh nav-agent to v1.2 (adds kv-list compact List view, default-off)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"3b584b2","date":"2026-08-13 08:29:17 -0700","author":"Steve Abrams","subject":"nav-agent: universal grid-controls drop-in on internal viewer pages (STRONG-only)","body":""},{"hash":"7f2ae6c","date":"2026-08-11 14:21:21 -0700","author":"Steve","subject":"viewer control standard: add Fields on/off menu to index.html (+Image toggle, mfr sorters) and list-toggle+Fields menu to vendors.html","body":""},{"hash":"95446fa","date":"2026-08-11 14:07:30 -0700","author":"Steve","subject":"microsites.html: add list/grid toggle + per-field on/off menu (full viewer control standard)","body":""},{"hash":"47d2616","date":"2026-08-10 16:16:41 -0700","author":"steve","subject":"all.: add greenland to BANNED (private-label leak) + drop rows whose image/url path leaks a banned mill (china /img/greenland fix)","body":""},{"hash":"fca6523","date":"2026-08-10 16:11:42 -0700","author":"steve","subject":"all.: register phillipe-romano brand (Greenland cork private-labeled) seeded via localhost:9831 feed for microsite directory","body":""},{"hash":"e98a86b","date":"2026-08-10 16:05:51 -0700","author":"steve","subject":"all.: repoint Phillipe Romano line-viewer off dead philliperomano.dw to live DW Shopify collection (crawl-resistant override)","body":""},{"hash":"ba032a8","date":"2026-08-01 21:06:39 -0700","author":"Steve Abrams","subject":"all-designerwallcoverings: adopt href-to-deeper-data primitives — LOCAL only, deploy Steve-gated (TK-10093)","body":""},{"hash":"09077cc","date":"2026-07-27 15:21:39 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-27T15:21:37 (1 files) — package-lock.json","body":""},{"hash":"699a5fd","date":"2026-07-27 15:13:57 -0700","author":"Steve Abrams","subject":"chore: v1.7.0 — async job+poll live-check feature (session close)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"26c4bba","date":"2026-07-27 15:05:58 -0700","author":"Steve","subject":"astek CF-Access: env-gated service-token headers on crawler + feed proxy (TK-11, memo 260727A)","body":"Inert/byte-identical (Basic-Auth-only) until CF_ACCESS_ASTEK_ID/SECRET are set;\nscoped to astek.designerwallcoverings.com only. Deploy to Kamatera stays gated."},{"hash":"ae33f6d","date":"2026-07-27 14:51:28 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-27T14:51:27 (1 files) — server.js","body":""},{"hash":"76b0372","date":"2026-07-27 14:45:14 -0700","author":"Steve","subject":"all.dw: raise live-scrape worker timeout 52s->75s (async/poll decoupled from proxy cap; genuine ~63s scrapes now complete)","body":""},{"hash":"ad479fd","date":"2026-07-27 14:39:37 -0700","author":"Steve Abrams","subject":"all.dw: async job+poll for metered live check (never 504s the proxy)","body":"DB_AUTHORITATIVE (Kravet) tier stays fully synchronous (instant local PG read,\nreturns status:done inline). Browserbase tiers now fire-and-poll: the initiating\nrequest starts the scrape WITHOUT awaiting it and returns status:pending\nimmediately (well under the ~60s Cloudflare/nginx proxy cap), so slow portals\n(WallQuest/Romo ~63s) can no longer 504. Spend-accrual + result caching moved\ninto the background .then(); .catch caches an error result so a later poll\nresolves cleanly and no unhandled rejection can occur. Front-end polls\nlivestock?...&poll=1 every poll_after_ms (max ~30 polls/~90s), renders on\nstatus:done, falls back to the Email-Vendor copy on status:gone/exhaustion.\nLIVE_WORKER made env-overridable for mocked local verification.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"fde98d3","date":"2026-07-23 12:20:58 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-23T12:20:57 (1 files) — server.js","body":""},{"hash":"eb95b2f","date":"2026-07-16 17:13:52 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-16T17:13:50 (1 files) — package-lock.json","body":""},{"hash":"9d4985f","date":"2026-07-16 17:06:24 -0700","author":"Steve Abrams","subject":"chore: v1.6.1 (session close — lint ✓ clean, refactor ✓ no-change, code verified clean as shipped)","body":""},{"hash":"763b16b","date":"2026-07-16 17:04:16 -0700","author":"Steve Abrams","subject":"Keep Schumacher visible on all-dw (internal 401-gated aggregator, Steve OK 2026-07-16); revert schumacher from BANNED — private-label vendor names stay banned","body":""},{"hash":"c3f43e4","date":"2026-07-16 16:43:47 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-16T16:43:46 (1 files) — data/live-stock-coverage.json","body":""},{"hash":"1e3c12a","date":"2026-07-16 16:36:07 -0700","author":"Steve Abrams","subject":"Kravet dash→dot W-code match: Shopify stores wallcovering codes dash-form (W3909-50), price file dot-form (W3909.50.0) — add the conversion to kravet-db candidateKeys + coverage counter. Kravet resolvable 840→2250 (32%→86%), ~1,410 more SKUs priced $0","body":""},{"hash":"fc6f2ba","date":"2026-07-16 10:34:22 -0700","author":"Steve","subject":"all-dw: add schumacher to BANNED regex (internal-only vendor)","body":"Schumacher is INTERNAL-ONLY (Steve 2026-07-16). BANNED already drops private-label\nupstream names from the public search grid + microsite directory; adding schumacher\nfilters any Schumacher product row AND the internal.schumacher / schumacher microsite\ntiles from this aggregator. Local commit only — deploy to the gated public surface\nstays Steve-gated per this repo's CLAUDE.md.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"81aa19f","date":"2026-07-15 17:44:25 -0700","author":"Steve Abrams","subject":"Live-scrape 504 fix: kill scrape at 52s (below the 60s CF/nginx proxy cap) → honest in-app 'timed out — use Email Vendor' JSON instead of a raw gateway 504; document the 3-step Kravet prod light-up recovery in build-coverage.js","body":""},{"hash":"8a79e7b","date":"2026-07-15 16:42:45 -0700","author":"Steve Abrams","subject":"Backfill mfr_sku for 163 mfr-less Kravet-family SKUs at $0 (title-exact + price-consensus tiers, local-model tier for residual honestly declines no-signal cases) — kravet_mfr_backfill table wired into grid loader, kravet-db adapter, coverage counter; Kravet resolvable 840→1003","body":""},{"hash":"3658c7c","date":"2026-07-15 16:38:38 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-15T16:38:37 (4 files) — scripts/adapters/kravet-db.js scripts/build-coverage.js server.js scripts/backfill-kravet-mfr.js","body":""},{"hash":"c5afd0d","date":"2026-07-15 14:39:50 -0700","author":"Steve Abrams","subject":"contrarian fixes: price-only live result gets neutral dot + 'availability unknown — price file only' disclosure + human tooltip; STOCK lookup falls back to -Sample-stripped key","body":""},{"hash":"add8bb3","date":"2026-07-15 14:34:37 -0700","author":"Steve Abrams","subject":"coverage hot-reload: re-read live-stock-coverage.json on mtime change (10-min check) so regenerated coverage flows without a pm2 restart","body":""},{"hash":"750e7f4","date":"2026-07-15 14:31:33 -0700","author":"Steve Abrams","subject":"Every SKU card gets a smart face button: live price check (tier-labeled cost) → stored stock → Email Vendor fallback; shared per-card popovers + price-forward live render","body":""},{"hash":"f9158ab","date":"2026-07-15 14:18:28 -0700","author":"Steve Abrams","subject":"Kravet family gets a $0 authoritative-price live check (kravet-db adapter, DB_AUTHORITATIVE tier)","body":"- new scripts/adapters/kravet-db.js: resolves the grid SKU's mfr code and answers from\n  dw_unified.kravet_authoritative_pricing with the MAP retail (new_map, else whls x 1.5 derived);\n  session_opened:false so nothing bills; wholesale never emitted\n- live-scrape.js: DB adapters run without the Browserbase lib (only session adapters need it)\n- server.js: session_opened:false success -> cost 0, no ledger/live-spend entry; DB_AUTHORITATIVE\n  tier exempt from the daily $-cap only (per-IP + inflight limits still apply)\n- build-coverage.js: KRAVET_FAMILY -> DB_AUTHORITATIVE first-branch, kapMatchCount resolvability\n  gate, Kravet removed from NO_LIVE_STOCK_PORTAL; coverage regenerated (12 vendors in tier,\n  7 live-capable: Brunschwig 2636, Gaston 2142, Lee Jofa 2110, Cole & Son 1236, GP&J Baker 1185,\n  Kravet 840, Threads 69)"},{"hash":"016fb39","date":"2026-07-15 13:57:41 -0700","author":"Steve Abrams","subject":"chore: vendor_contacts self-heal DDL, microsites compose validation, v1.6.0 (session close)","body":""},{"hash":"3fa3f9f","date":"2026-07-15 13:33:23 -0700","author":"Steve Abrams","subject":"Email Vendor everywhere: stock & price inquiry compose on every product card + microsite card — /api/vendor-contact + /api/vendor-inquiry (vendor_contacts upsert + George Gmail DRAFT, never auto-send, mailto fallback)","body":""},{"hash":"9317a40","date":"2026-07-14 13:25:11 -0700","author":"Steve","subject":"chore: v1.5.0 (session close — pm2 viewer auto-discovery + /api/skus feed)","body":""},{"hash":"80c128c","date":"2026-07-14 13:11:20 -0700","author":"Steve Abrams","subject":"Auto-discover all co-located pm2 vendor viewers + read /api/skus feed → 49→156 vendors live in all.dw (fulfills 'every vendor viewer must be live'); private-label sources stay BANNED","body":""},{"hash":"2a6cffc","date":"2026-07-14 13:08:38 -0700","author":"Steve","subject":"chore: v1.4.1 (session close) — vendor facet alpha-sort","body":""},{"hash":"ce9854c","date":"2026-07-14 11:19:49 -0700","author":"Steve","subject":"all.dw: vendor facet sorts alphabetically by name on load (was by count)","body":""},{"hash":"7e64421","date":"2026-07-14 09:53:02 -0700","author":"Steve Abrams","subject":"Contrarian fixes: scope crawler cred to internal hosts only; correct zuber-internal note :10105; de-mislead quadrille internalViewer (public-served)","body":""},{"hash":"26c1985","date":"2026-07-14 09:48:55 -0700","author":"Steve Abrams","subject":"Crawler: derive membership handle + title from mfr_sku/pattern_name when feed omits them (fixes gracie/zuber 0-count)","body":""},{"hash":"22ee077","date":"2026-07-14 08:11:32 -0700","author":"Steve Abrams","subject":"Fix crezana internalViewer port 10104->10106; vendor-name fallback for gated 401 directory titles","body":""},{"hash":"f5cefcb","date":"2026-07-13 14:38:36 -0700","author":"Steve","subject":"chore: v1.4.0 — similar-pattern search feature (session close)","body":""},{"hash":"80f0bae","date":"2026-07-13 09:35:12 -0700","author":"Steve Abrams","subject":"disco banner: 'search the catalog' is now a button — /api/similar scores live substitutes (material/style/color/type/vendor/title-token) and renders matched cards below the banner","body":""},{"hash":"301550f","date":"2026-07-09 22:42:32 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-09T22:42:31 (2 files) — data/vendors.json scripts/__pycache__/","body":""},{"hash":"1fa387b","date":"2026-07-09 22:29:26 -0700","author":"steve","subject":"internal category: plug 3 front-facing leaks — build-data.py vendor directory drops internal, /api/vendors+/api/microsites Cache-Control private no-store, microsite external URL suppressed for internal (contrarian FIX-FIRST)","body":""},{"hash":"f37876c","date":"2026-07-09 22:12:25 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-09T22:12:24 (1 files) — server.js","body":""},{"hash":"9d3836e","date":"2026-07-09 16:38:57 -0700","author":"Steve","subject":"microsite crawler: seed from Cloudflare zone (authoritative, self-completing) + register -internal viewers","body":"- add cfZoneSubdomains() 3rd seed source: fetch all A/CNAME for zone e4b88c70,\n  derive <sub>.designerwallcoverings.com, merge with known+DB seeds (dedup)\n- guarded: missing token / CF failure logs a warning + falls back to existing seeds\n- denylist _-prefixed / _domainconnect / _dmarc / apex; www kept out (apex dup)\n- token via env (CLOUDFLARE_API_TOKEN) + MICROSITE_CF_ZONE default = the zone\n- register fromental/crezana/gracie/zuber -internal viewers in internal[]\n- fix stale internalViewer ports 10071-10074 -> real Kamatera 10103-10106\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"c22ea62","date":"2026-07-09 16:20:45 -0700","author":"Steve","subject":"all.dw: make microsite feed ports env-overridable (MICROSITE_FEED_PORTS)","body":"Prod's co-located internal viewers listen on different localhost ports than Mac2\ndev (prod: fromental=10103/gracie=10104/zuber=10105/crezana=10106; dev defaults\n10071/10073/10074/10072). Drive the per-line feed port from MICROSITE_FEED_PORTS\n(\"slug=port,...\"), falling back to the dev default so behavior is unchanged when\nunset. Keeps the ingest portable across environments without hardcoding prod ports.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"b2fc39c","date":"2026-07-09 16:17:55 -0700","author":"Steve","subject":"all.dw: surface 4 luxury lines (Fromental/Gracie/Zuber/Crezana) via microsite full-feeds","body":"Ingest the FULL /api/products feed of the 4 DW-family microsites over localhost\n(bypassing the public 401 with the shared internal cred), normalize each feed's\nheterogeneous shape to the grid row shape (colors/styles/materials via the same\ntag lexicons, quote->null price), tag source='microsite', dedupe by vendor+handle,\nand append to ROWS at each snapshot cycle. No Shopify import, no dw_unified write.\nAdditive + guarded: a failed feed keeps the prior cycle's rows for that line.\nVerified locally: /api/facets vendor = Fromental 735 / Gracie 150 / Zuber 192 /\nCrezana Design 443; products filter + color/style facets cross-filter; internal\n/api/catalog-full unaffected (0 microsite rows).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"5def1fd","date":"2026-07-09 09:37:54 -0700","author":"steve","subject":"all.dw: register Fromental/Gracie/Zuber/Crezana brands (onboarding/staging, quote-only, import+deploy gated)","body":""},{"hash":"cdc1dcf","date":"2026-07-09 09:08:41 -0700","author":"Steve","subject":"chore: lint (node --check ✓), v1.3.0 (session close)","body":""},{"hash":"287e09e","date":"2026-07-08 15:19:39 -0700","author":"Steve Abrams","subject":"Designtex dw_sku backfill on mac3 CANONICAL dw_unified: provenance note (288 rows, artifacts durable on-disk, gitignore honored)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"fe345ea","date":"2026-07-08 12:34:14 -0700","author":"Steve","subject":"all. cards: gallery count badge + hover-cycle through full Shopify image set","body":""},{"hash":"08c6237","date":"2026-07-08 12:32:55 -0700","author":"Steve","subject":"Full Shopify image galleries: bulk-sync product_images + serve images[] in feed","body":"- scripts/shopify-images-sync.mjs: Shopify Bulk Operations export of every non-archived\n  product's full image set -> dw_unified.product_images (201,568 images / 87,599 products)\n- server.js: load product_images by product_id, add images[] + image_count to every row,\n  gap-fill the primary image from the gallery when featured image_url is null\n- flows to /api/catalog-full so all./substitute/new inherit galleries; No-image dropped to 477"},{"hash":"75328f4","date":"2026-07-08 12:24:32 -0700","author":"steve@designerwallcoverings.com","subject":"all.dw grid: manufacturer-SKU search — backfill mfr_sku from *_catalog tables (sync leaves shopify_products.mfr_sku empty for new lines); feeds mfr_number display + search haystack","body":""},{"hash":"de111d4","date":"2026-07-08 12:06:11 -0700","author":"Steve","subject":"shopify-push: portable Shopify-lib resolution (env/mac3/co-located) for deploy safety","body":""},{"hash":"25613cb","date":"2026-07-08 12:01:50 -0700","author":"Steve","subject":"List view: add Mfr # column with DW·Shop·FM mismatch breakout","body":""},{"hash":"d3b82a5","date":"2026-07-08 11:33:22 -0700","author":"Steve","subject":"Mismatch chip: show DW number alongside Shopify + FileMaker mfr numbers","body":""},{"hash":"f088c73","date":"2026-07-08 11:07:37 -0700","author":"Steve Abrams","subject":"chore: v1.1.0 → v1.2.0 (session close — feed + discontinued-successor features)","body":""},{"hash":"8ed57a0","date":"2026-07-08 11:03:12 -0700","author":"Steve","subject":"FileMaker WALLPAPER as source of truth across all./substitute/photo","body":"- scripts/fm-wallpaper-sync.mjs: mirror FileMaker WALLPAPER (Shopify Detail layout,\n  clean-query rule, Record Type=Master) into dw_unified.filemaker_wallpaper\n- server.js: LEFT-JOIN the mirror by dw_sku -> fm_mfr_number, fm_discontinued,\n  fm_width, fm_specs, mfr_mismatch; hide FMPro-discontinued by default (union);\n  FM stats on /healthz + /api/facets; fields flow to /api/catalog-full (substitute+photo)\n- public/index.html: show both mfr numbers with mismatch flag, FM specs on cards,\n  FM header chip + Mismatches/Show-FM-disco toggles + bulk Preview-sync modal,\n  per-card Update Shopify button (dry-run)\n- scripts/shopify-push.mjs: FileMaker->Shopify diff engine (name/mfr/width/specs);\n  writes only non-empty changed fields, never downgrades richer Shopify data, never\n  pushes a name into the mfr field; commit gated behind SHOPIFY_PUSH_ENABLED"},{"hash":"55b78db","date":"2026-07-08 10:59:46 -0700","author":"Steve","subject":"Add /api/discontinued + red successor banner: dead/archived SKU resolves (dash/-SAMPLE insensitive) → links live successor","body":""},{"hash":"ecdf294","date":"2026-07-08 10:35:16 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-08T10:35:15 (1 files) — scripts/fm-wallpaper-sync.mjs","body":""},{"hash":"949b520","date":"2026-07-08 09:18:35 -0700","author":"Steve Abrams","subject":"Stop tracking data/backfills/ ops artifacts; gitignore the dir (repo hygiene)","body":""},{"hash":"2ac9b38","date":"2026-07-08 09:16:36 -0700","author":"steve","subject":"all.dw internal tool: default scope = all-but-archived (incl. Staged/DRAFT); fold DELETED_FROM_SHOPIFY into Archived","body":""},{"hash":"02ea04b","date":"2026-07-08 09:11:30 -0700","author":"Steve","subject":"env-ify Basic-Auth: support BASIC_AUTH_USER/PASS, default unchanged (admin:DW2024!)","body":""},{"hash":"7d0202a","date":"2026-07-08 09:06:32 -0700","author":"steve","subject":"Enable Designtex live-stock (13→14): drop from SCHEMA_PARSE_PENDING after dw_sku backfill","body":"build-coverage.js: Designtex removed from SCHEMA_PARSE_PENDING (it was the only\nblocker) and given a VERIFIED entry; it now takes the enabled schema-public path and\nclears the resolvability gate 417/417 (100%). Coverage rebuilt on prod against the\nKamatera dw_unified (the DB the aggregator loads) → live-capable 14, all 13 prior\nvendors intact. Designtex proven live on prod: DWDX-220000 → in_stock:true via\nschema-public-live (login-free, $0.03), kill-switch counter incremented. pm2\nrestarted to load the fresh coverage."},{"hash":"005a048","date":"2026-07-08 09:04:56 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-08T09:04:55 (1 files) — scripts/build-coverage.js","body":""},{"hash":"f78c637","date":"2026-07-08 09:01:29 -0700","author":"Steve","subject":"Document targeted-rsync deploy guardrail (no full-tree --delete on this repo)","body":""},{"hash":"104aa8f","date":"2026-07-08 09:00:49 -0700","author":"steve","subject":"Designtex dw_sku backfill on Kamatera dw_unified: backup + rollback artifacts","body":"Wrote authoritative DWDX/DWAH dw_sku into 417 active Designtex rows in shopify_products\n(base(variant_sku)=designtex_catalog.dw_sku, 1:1, 0 unmatched, 0 ambiguous). dw_sku ONLY;\nmfr_sku/variant_sku/sku untouched; 0 non-Designtex rows touched. Target = the LOCAL\nKamatera dw_unified the live aggregator loads (repl_user reads it; write ran as table\nowner dw_admin via .pgpass). Reversible: on-DB _backup_designtex_dwsku_20260708 +\ndata/backfills/*.jsonl + ROLLBACK.sql."},{"hash":"aa87b85","date":"2026-07-08 09:00:14 -0700","author":"Steve Abrams","subject":"add internal /api/catalog-full feed (every non-archived SKU, leak-sanitized, product_id/handle/tags) for substitute + photo apps","body":""},{"hash":"5dfa17e","date":"2026-07-08 08:58:11 -0700","author":"Steve","subject":"deploy: infinite-scroll fix (5cdaf8a) live to all.designerwallcoverings.com (targeted public/index.html)","body":""},{"hash":"053a837","date":"2026-07-08 08:57:28 -0700","author":"steve","subject":"Deploy staged +Koroseal live-stock build to prod (Kamatera 45.61.58.125)","body":"Targeted rsync of scripts/adapters/ + build-coverage.js + live-stock-coverage.json,\npm2 restart --update-env. Smoke: healthz=200, root=401, microsites=200.\nKoroseal DWK-29052 authed live scrape on prod → in_stock:true, price:null,\nsource=koroseal-live, $0.17 (Browserbase 1.16 session_min), kill-switch counter\nincremented (live-spend.json 2026-07-08:0.1739, cap $5). 13 live vendors confirmed."},{"hash":"5cdaf8a","date":"2026-07-08 08:47:36 -0700","author":"Steve","subject":"Fix stuck infinite scroll: pump on live sentinel geometry, not stale observer flag","body":""},{"hash":"59f752c","date":"2026-07-07 20:32:37 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-07T20:32:33 (2 files) — config/known-subdomains.json scripts/crawl-microsites.js","body":""}]}