in ALL JSON-LD (fleet-wide injection fix, Cody-hardened)","b":""},{"h":"41049f5","d":"2026-08-03","s":"auto-save: 2026-08-03T11:23:33 (1 files) — server.js","b":""},{"h":"1bd3992","d":"2026-08-03","s":"security: response headers + non-breaking CSP (frame-ancestors/object-src/base-uri) + patch /api/render error-message leak (Cody-hardened)","b":""},{"h":"c34917c","d":"2026-08-03","s":"snapshot before deploy: in-tree security headers (nosniff/frame/referrer/permissions, drop x-powered-by) + don't leak raw error to /api/render client","b":""},{"h":"81d733e","d":"2026-08-03","s":"refine: shared fmtStamp (storefront+admin, NaN/future-safe), sortable products table (Newest|Most clicks), post-migration schema assertion — v0.4.1","b":""},{"h":"3f3869c","d":"2026-08-03","s":"a11y: working skip-to-content link (focus-moving, tabindex=-1) + prefers-reduced-motion support (Cody-hardened)","b":""},{"h":"85a3efa","d":"2026-08-03","s":"seo+integrity: per-page OG images + saved rooms/guides/catalog fully honor suppression & exclude wall paints (Cody-hardened)","b":""},{"h":"303f890","d":"2026-08-03","s":"chore: sync package-lock to v0.4.0","b":""},{"h":"7c3f401","d":"2026-08-03","s":"chore: v0.4.0 (session close — date+time-added on store cards, admin analytics, deploy schema-migration safety)","b":"Co-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"c8362bb","d":"2026-08-03","s":"auto-save: 2026-08-03T10:53:14 (2 files) — lib/rooms.js server.js","b":""},{"h":"181743c","d":"2026-08-03","s":"deploy: schema-migration step + /shop smoke test (stop code-ahead-of-schema)","b":"Prevents a repeat of the 2026-08-03 regression where shipped code referenced a\nprod-missing column (products.suppressed) + tables (affiliate_settings,\nsuppress_rules) and /shop 500'd while the DB-blind /healthz smoke test stayed\ngreen.\n\n- db/schema.sql: explicit ADD COLUMN IF NOT EXISTS guards (inline, before\n dependent indexes) so the file is a complete idempotent migration on a fresh\n OR drifted DB, not just a fresh-create.\n- scripts/migrate.sh: applies schema.sql with ON_ERROR_STOP; loads DATABASE_URL\n from the remote's own .env.\n- .deploy.conf: BUILD_CMD runs the migration on the remote after install/before\n reload; HEALTH_URL now hits /shop (exercises the real catalog query, not the\n DB-blind /healthz); INSTALL_CMD bakes in the sharp-safe (scripts-enabled) install.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"ffa8079","d":"2026-08-03","s":"chore: v0.3.0 — keyword/id suppression feature (session close)","b":"Author: session that shipped 72708fc (admin Suppress page). Version bump only;\nworking-tree edits to lib/rooms.js/routes/admin.js/server.js + new lib/ids.js belong\nto a concurrent session and are intentionally left uncommitted."},{"h":"39571bf","d":"2026-08-03","s":"robustness: bigint-safe id guard across all client-value->id paths (/go, product_ids, wall_paint_id, admin+suppress routes) — no more 500 on malformed/overflow ids","b":""},{"h":"aae117f","d":"2026-08-03","s":"harden: intOrNull() guard on client-supplied ids reaching bigint columns","b":"Non-numeric wall_paint_id (and similar) now coerces to null instead of\nNumber()->NaN reaching a bigint query and throwing a 500. Applied at\n/api/rooms and /api/render call sites. (Pre-existing working-tree change,\nsnapshotted before deploy so shipped code == committed code.)\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"78c986b","d":"2026-08-03","s":"store: date + time added on product cards — New badge + 'Added …' line","b":"Every storefront product card now shows when it was added to the store: a NEW\nbadge over the image for items added in the last 14 days (exact date+time in the\nhover title), plus a muted, dated 'Added Mon D, YYYY, H:MM AM' line under the\nprice. New shared addedStamp() helper in render.js mirrors the admin when()\nformat; card-new + card-added styles added to site.css. Chosen treatment per\n2026-08-03 decision (overrides the usual 'customer cards omit created date').\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"72708fc","d":"2026-08-03","s":"admin: keyword/id suppression rules (Suppress page) + Vacuum re-apply + Refresh","b":"- New suppress_rules table (durable keyword/id hide rules, distinct from source-level\n affiliate_settings and the raw suppressed flag).\n- /admin/suppress: enter keywords (and/or ids) -> immediately flip suppressed=TRUE on\n every current match (title/brand/advertiser). Reuses the pervasive NOT suppressed gate\n so hides propagate to /shop, /rooms, guides, and the sitemap.\n- Vacuum re-applies all rules (catches products ingested later matching an old keyword);\n Refresh reloads/recounts; View shop -> /shop?room=decor&style=modern.\n- Per-rule live match/hidden counts, created date+time, Re-apply, Remove, Remove+unhide.\n- Verified end-to-end: add velvet (7 hidden), storefront drop confirmed, vacuum idempotent,\n remove+unhide restores state; CSRF guard returns 403."},{"h":"82f2795","d":"2026-08-03","s":"admin: per-product click analytics column (total, 7d, last-clicked date)","b":"Fold click aggregates into the products query via one grouped LEFT JOIN and\nrender a Clicks column per row — lifetime total, a 7-day sub-count, and the\nlast-clicked timestamp (dated with the standard when() helper). No-click\nproducts show a muted — instead of fake zeros.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"5b5102f","d":"2026-08-03","s":"seo: breadcrumbs (nav + BreadcrumbList) across facet/room/guide/looks pages, multi-JSON-LD layout (Cody-hardened)","b":""},{"h":"ecc8fe0","d":"2026-08-03","s":"nav: promote kitchen/bathroom to rooms + upper-right room hamburger (collapsed on load, all room types)","b":""},{"h":"6a15502","d":"2026-08-03","s":"seo: indexable facet landing pages — sitemap + self-canonical whitelist, ratio-gated, orphan rooms 404 (Cody-hardened)","b":""},{"h":"3e86ee5","d":"2026-08-03","s":"IDS admin: implement EZ-Join DTD verdict (C+relabel), fix smart-quote regression, sync rooms.hotspots schema (v0.2.1)","b":"- EZ-Join verdict C-as-superset-of-A: joinTarget() resolves a per-network\n marketplace SEARCH deep-link (brand pre-filled) where searchVerified, else the\n plain landing (safe fallback). All networks land today (unverified); flip\n searchVerified per network after a live test. Honest relabel 'EZ Join' ->\n 'Join via '.\n- Fix: refactor pass had introduced curly quotes as HTML attr delimiters on the\n join button (class=\"ezjoin\"/href broke styling+link) — restored straight quotes.\n- db/rooms.sql: declare + idempotently ALTER-add the hotspots JSONB column that\n createRoom() writes (schema file was drifted from the live DB).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"562d723","d":"2026-08-03","s":"chore: lint, refactor, v0.2.0 (session close)","b":"Lint: colon-safe Basic-auth parse. Refactor (behavior-preserving): move\nAFFILIATE_ENABLED const above buildWhere; NUL->KEY_SEP; chip param key->netKey;\ndedup joinedNets label. Verified: auth 401/200, /shop + affiliates + suggested\n200, toggle round-trip. Minor bump — session shipped the affiliate on/off admin.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"33bf504","d":"2026-08-03","s":"IDS admin: confirm CJ PID 101848244, drop bogus 1062321 (TK-10171)","b":".env CJ_WEBSITE_ID=101848244 matches the PID in the live tracking links, so CJ\nattribution is correct. 1062321 was identified as a FileMaker customer acct\n(client Claire, Shopify order #32749), not a CJ id — removed from the record.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"8fb23cd","d":"2026-08-03","s":"IDS admin: record CJ account identifiers on Affiliates tab (TK-10171)","b":"Adds a CJ Account reference bar to /admin/affiliates from data/cj-account.json:\nlogin steve@designerwallcoverings.com, CID 7896980, live-link PID 101848244\n(all evidence-confirmed). 1062321 shown as 'unverified' — it matches neither the\nCID nor the PID in the live click-[PID]-[AID] tracking links, so it is NOT wired\ninto anything pending confirmation under CJ Account > Websites.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"770915b","d":"2026-08-03","s":"admin: keyword line-hider with preview — catch a line across reseller-mislabeled brands (Honiture gap)","b":""},{"h":"dcba584","d":"2026-08-03","s":"admin: brand-level line selector — hide/show a whole brand across every advertiser","b":""},{"h":"99f7508","d":"2026-08-03","s":"auto-save: 2026-08-03T08:52:19 (7 files) — db/schema.sql lib/catalog.js lib/render.js lib/rooms.js public/css/site.css","b":""},{"h":"95495fd","d":"2026-08-03","s":"IDS admin: 'Suggested lines to join' affiliate tab (TK-10171)","b":"Adds a second tab under /admin/affiliates: a curated gap-finder of on-brand\nhome/decor affiliate programs across CJ + other networks. Flags lines already\nin the catalog as Joined, gives an EZ Join deep-link into the right network's\nmarketplace for the rest, filterable by network. Roster is data-driven\n(data/suggested-affiliates.json), read live so it extends without a restart.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"e72a006","d":"2026-08-03","s":"moodboard drawer: Pieces tab defaults open (board visible on open); Project/Create stay collapsed; explicit collapse still persists","b":""},{"h":"d44578c","d":"2026-08-03","s":"Switch Honiture affiliate OFF (kept, hidden) + drop its ingest keyword","b":""},{"h":"81400f7","d":"2026-08-03","s":"moodboard drawer: in-drawer project switcher + collapsible Project/Pieces/Create tabs (collapsed on load, state persisted)","b":""},{"h":"590fca7","d":"2026-08-03","s":"shop: collapse facet rail on load, 4-across grid + columns slider, mood-board project select","b":"- facet rail -> native , all collapsed on load (auto-opens only the group holding an active filter)\n- grid controls: columns slider (2-6, default 4) driving #grid via --gcols; grid starts 4-across even pre-JS\n- sort: date/color/style/brand/title/price all present (Newest relabelled 'Date - Newest')\n- mood board -> named PROJECTS: #mbProjectSel switcher, create/switch, per-project item lists; legacy single board migrated to 'My Board'"},{"h":"f14bc91","d":"2026-08-03","s":"refactor: replace all SELECT * with explicit column lists (lib/cols.js) — clears the pre-deploy lint block so the standard deploy.sh path works for IDS (no more surgical-rsync workaround); result sets identical","b":"Co-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"28e228f","d":"2026-08-03","s":"feat(moodboard): client-side spend guard on Create Room Setting — two-step confirm (\"Generate room ~$0.039? Tap again\") + per-session cap of 8 renders, so a public visitor can't silently rack up Gemini spend (server per-IP limit still applies)","b":"Co-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"e5ef1c4","d":"2026-08-03","s":"feat: shop-time Mood Board — collect pieces while shopping, assemble, one button turns them into a photoreal room setting","b":"- public/js/moodboard.js: self-contained client feature (mirrors cart.js). Injects a Mood Board button on every product card, floating FAB + slide-in drawer that assembles all selections, a star-Primary toggle, room-type picker, and a Create Room Setting button that POSTs the selected ids to the existing /api/render (Gemini 2.5 Flash Image, ~$0.039) and shows the rendered room + shoppable pieces inline\n- lib/render.js: load moodboard.js globally so selection works site-wide (shop, rooms, home, looks)\n- Primary piece is sent first so it anchors the room (scene refs the first 4 pieces)\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"713ee0d","d":"2026-08-03","s":"fix(render): match bigint product ids type-safe — /api/render was silently ignoring explicit product_ids (string p.id === number id never matched), rendering a generic room; also fixes /build explicit-piece selection","b":"Co-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"96f59a1","d":"2026-08-02","s":"ingest: partner-scoped CJ sweeps (partnerIds) + TWOPAGES program joined 2026-08-02 — keyword search can't reach niche joined advertisers; feed indexes 24-48h post-join, re-runs pick it up","b":""},{"h":"b8892c3","d":"2026-08-02","s":"ingest: add robot/cordless vacuum + honiture brand sweep — Honiture CJ program joined 2026-08-02 (56 products, all tracked, $20-$260)","b":"Co-Authored-By: Claude Fable 5 "},{"h":"53a67e4","d":"2026-08-02","s":"fix .deploy.conf: IDS runs on 9876 (9861 belongs to staged.dw) — smoke test was probing the wrong app","b":""},{"h":"8898a07","d":"2026-08-02","s":"docs: go-live runbook for auto-guides (gated)","b":""},{"h":"213ca8b","d":"2026-08-02","s":"feat: auto guide generator — gen-guide.js builds a room (shared lib/roomgen) + authors an editorial guide around its exact pieces (local qwen3:14b, template fallback); room-type rotation for variety; 5 guides published locally","b":""},{"h":"325e399","d":"2026-08-02","s":"add tracked-link coverage canary: samples live /go redirects, alerts on worsening CJ-tracking coverage (CNCP), daily 3:15am","b":"Co-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"64a5449","d":"2026-08-01","s":"feat: affiliate cart — collect pieces, checkout opens each product in its own tab (per-merchant). Server-rendered add-cart buttons + client cart.js (localStorage list, floating cart button, slide-in drawer, tab-fanout checkout with popup-block fallback)","b":""},{"h":"47e1b8d","d":"2026-08-01","s":"room page: 3x-larger click-to-shop thumbnails (info + Buy on click); big thumbs are the shop surface on scene-less rooms so they no longer dead-end","b":"- roomThumbs: each thumb is now a that expands product info (brand/title/price) + a tracked Buy button inline, instead of jumping straight out to the affiliate link\n- thumbs 56px -> 168px (3x); new /js/room-thumbs.js toggle (one-open, Esc/outside-click closes), mirrors hotspot-card grammar\n- server room route: render the big thumbs as the shoppable surface when a room has no scene_image (fixes glam-room dead-end); scene rooms unchanged (scene + side rail + grid)\n\nCo-Authored-By: Claude Opus 4.8 (1M context) "},{"h":"a9db00f","d":"2026-08-01","s":"feat(build): right panel shows the products IN the generated room (shoppable) + vibe-only render back-fills a full 6-piece room; middle keeps the glowing hotspot dots; Browse-all restores the catalog","b":""},{"h":"9906389","d":"2026-08-01","s":"feat(build): infinite-scroll the products panel — offset pagination in searchProducts/API, scroll-to-load-more in build.js (48/page, stable id-tiebreak sort, no dup rows); left filters reset to page 0","b":""},{"h":"bede8a7","d":"2026-08-01","s":"feat(room): thumbnail rail beside the scene — stacked right on desktop, below on mobile; image-only thumbs, item name on hover; each a tracked shop link","b":""},{"h":"439a773","d":"2026-08-01","s":"ui(build): persistent 3-column Room Builder — left words, right products column, middle images/room-settings centered (desktop); products no longer hidden under hamburger; hint text updated","b":""},{"h":"b7e8531","d":"2026-08-01","s":"feat: shoppable hotspots on the live Room Builder gallery too (/api/render returns vision-located hotspots; build.js overlays dots w/ hover-card + click-to-shop; saved rooms persist builder hotspots)","b":""},{"h":"ffe693d","d":"2026-08-01","s":"feat: shoppable room-setting images (vision-located hotspots w/ IoU dedup) + auto room generator from CJ inventory (Gemini scene + hotspots, ~$0.04/room); rooms.hotspots jsonb; inline hotspot cards on /room/:slug","b":""},{"h":"8df6b7b","d":"2026-08-01","s":"auto-save: 2026-08-01T22:40:01 (3 files) — lib/rooms.js lib/hotspots.js scripts/gen-room-setting.js","b":""},{"h":"4a02b54","d":"2026-08-01","s":"refine: real Gemini guide heroes (replace picsum placeholders, 1200x630 jpg ~90kb) + unpublish junk test rooms from index","b":""},{"h":"082c438","d":"2026-08-01","s":"refine: soften freshness chip to 'Price checked' + SEO structured-data (Organization/WebSite/SearchAction on home, ItemList+Product on shop/rooms, per-page OG image, sitemap adds looks/build/public-rooms + lastmod)","b":""},{"h":"1ead1eb","d":"2026-08-01","s":"refine: price-freshness trust chip on cards (price_checked_at) + PNG/apple-touch favicon fallbacks","b":""},{"h":"7f7de63","d":"2026-08-01","s":"harden /api/render + admin: persistent restart-proof render cost-guard (cf-connecting-ip + daily cap), scene_image allowlist, admin CSRF origin check","b":""},{"h":"8d27aa6","d":"2026-08-01","s":"Frontend refinement: consistency + edge/empty states + mobile + a11y + loading states across storefront and Room Builder","b":"Co-Authored-By: Claude Sonnet 4.6 "},{"h":"96155e2","d":"2026-08-01","s":"Data quality: dead-image guard (in_stock flag), image-based color enrichment for the 12-color filter, dedup","b":""},{"h":"582513e","d":"2026-08-01","s":"Fix products panel + close button z-index (were behind sticky header); panel now overlays correctly","b":""},{"h":"012c874","d":"2026-08-01","s":"Room Builder: fix hamburger click (was behind sticky header); show room's pieces as a shoppable grid below the generated render; single-color product column at all widths","b":""},{"h":"03357d1","d":"2026-08-01","s":"Room Builder: right products = single image-forward column; whole card click selects into the top bar; larger selected-product thumbnails in the tray","b":""},{"h":"c3798f4","d":"2026-08-01","s":"Add idempotent nightly live-catalog refresh (SSH-tunnel upsert into prod; swaps in tracked links as CJ advertisers are approved)","b":""},{"h":"1012ec8","d":"2026-08-01","s":"Elevate site UI to steelcase.com-caliber editorial design","b":""},{"h":"3448c99","d":"2026-08-01","s":"auto-save: 2026-08-01T21:09:29 (7 files) — lib/scene.js package-lock.json package.json server.js _ctest.js","b":""},{"h":"61d7a3c","d":"2026-08-01","s":"Add Herman Miller + Steelcase brand ingest for testing","b":""},{"h":"27d7ce4","d":"2026-08-01","s":"Room Builder v4 layout: hamburger-collapsible products panel + generated-rooms gallery, styled","b":""},{"h":"bcddff0","d":"2026-08-01","s":"Room Builder: 12-color multi-select swatches (Alabaster→Blush, 1-to-many, maps to catalog color buckets); include color in search results for hover popup","b":""},{"h":"628a4c7","d":"2026-08-01","s":"Room Builder: AI photoreal render (Gemini nano-banana, feeds real product images as refs, $0.039/img, rate-limited) in the middle + saved as room scene; hover-detail popup on items; tray images bar","b":""},{"h":"1e3d7b1","d":"2026-08-01","s":"Room Builder: tray+GO, collapsed left tabs (trending/style/hue/color/theme/period/categories/brand), draggable served products, live preview+ideas, Samplize wall color, style-only generation w/ loosening back-fill; rooms schema + /looks gallery + /room/:slug + /api catalog/paints/brands/rooms","b":""},{"h":"365ba46","d":"2026-08-01","s":"Scaled CJ catalog ingest (48 categories, image-required) -> 2697 real products/27 brands incl. appliances; idempotent, swaps in tracked links as advertisers activate","b":""},{"h":"145b8c5","d":"2026-08-01","s":"auto-save: 2026-08-01T20:09:07 (1 files) — lib/adapters/cj.js","b":""},{"h":"4fc3f55","d":"2026-08-01","s":"GOLIVE: launched — site live on interiordesignershowroom.com with LE cert (IP-bound :443 fix)","b":""},{"h":"67a527f","d":"2026-08-01","s":"Cycle 1 hardening: favicon (IDS monogram), OG social image + meta; browser-verified 0 console errors","b":""},{"h":"4dad839","d":"2026-08-01","s":"Update GOLIVE.md: Kamatera deploy complete; only NS swap + optional CF Full remain","b":""},{"h":"d55b94c","d":"2026-08-01","s":"Add faceted filtering (room/style/color/price) to /shop + /rooms","b":"- lib/catalog.js: URL-addressable filters, self-excluding facet counts, active chips\n- every facet is a real drillable /shop?... link (data-points-href-to-deeper-data rule)\n- verified locally: room/color filters narrow the grid, chips clear individually"},{"h":"86a89da","d":"2026-08-01","s":"auto-save: 2026-08-01T11:36:26 (2 files) — server.js lib/catalog.js","b":""},{"h":"fee7e91","d":"2026-08-01","s":"Add robots.txt (block /admin + /go), go-live staging checklist (GOLIVE.md)","b":""},{"h":"5637f88","d":"2026-08-01","s":"Add gated /admin curation dashboard + dotenv env loading","b":"- routes/admin.js: basic-auth (timing-safe), featured-toggle, guide publish/unpublish,\n click analytics (top-clicked, 7-day counts), created date+time chips per admin-card rule\n- server.js loads .env via dotenv (CJ_COMPANY_ID etc.); admin router mounted\n- verified: /admin 401 unauth / 200 authed, timestamps render, existing routes intact"},{"h":"9d15d2b","d":"2026-08-01","s":"Fix adapter bugs from adversarial review: Rakuten token endpoint Basic (was Bearer, would 401 forever); CJ add category to GraphQL selection","b":""},{"h":"7c4cdd2","d":"2026-08-01","s":"Add 3 editorial buying guides (small-space sectionals, lighting layers, coastal living room)","b":""},{"h":"619c451","d":"2026-08-01","s":"Add 4 credential-gated feed adapters (CJ, Amazon PA-API v5, Rakuten, ShareASale) + ingest orchestrator","b":"- Each adapter implements the shared {network, enabled(env), fetch(env,{limit})} contract,\n maps its network's API into the unified rawProduct shape, uses only node built-ins\n (global fetch + crypto for SigV4/HMAC), and returns [] when creds absent (never throws)\n- scripts/ingest.js runs enabled adapters -> normalize -> UPSERT; idempotent, cron-safe\n- .env.example documents every network's required credentials\n- Verified: registry loads all 4, ingest skips all gracefully with no creds"},{"h":"020e430","d":"2026-08-01","s":"Add CJ Affiliate GraphQL product-feed adapter (TK-10112)","b":""},{"h":"7f2672c","d":"2026-08-01","s":"Scaffold interiordesignershowroom: multi-network affiliate showroom foundation","b":"- Express + Postgres, unified product contract (normalize.js) feeding one catalog\n from 4 networks (CJ/Amazon/Rakuten/ShareASale) with room/style/color classifier\n- Showroom home, /shop + /rooms grids with sort+density, editorial buying guides\n- /go/:id affiliate redirect w/ click logging, FTC disclosure baked into shell,\n SEO (canonical/OG/JSON-LD/sitemap), legal pages\n- Seeded 24 sample products + 2 guides; running under pm2 on :9861"},{"h":"5617cca","d":"2026-08-01","s":"auto-save: 2026-08-01T11:06:15 (7 files) — .deploy.conf .gitignore db/ lib/ package.json","b":""}];
const q = document.getElementById('q');
const list = document.getElementById('bp-commits');
const tally = document.getElementById('bp-tally');
function applyFilter() {
const term = q.value.toLowerCase().trim();
let shown = 0;
for (const li of list.children) {
const match = !term || li.dataset.blob.includes(term);
li.style.display = match ? '' : 'none';
if (match) shown++;
}
tally.textContent = term ? `${shown} / ${DATA.length} match "${q.value}"` : `${DATA.length} commits indexed`;
// Reflect search state in URL so a filtered view is shareable. Use
// replaceState so the back button still escapes the project page.
const url = new URL(window.location.href);
if (term) url.searchParams.set('q', q.value); else url.searchParams.delete('q');
history.replaceState(null, '', url);
}
q.addEventListener('input', applyFilter);
// Apply the initial ?q= from the server-rendered value field.
if (q.value) applyFilter();
Interiordesignershowroom
repo: ~/Projects/interiordesignershowroom · 120 commits · 0 in last 24h, 0 in last 7d ·
Search the build
120 commits indexed
420a619
2026-09-11
Bump interiordesignershowroom to 0.5.1 after verified socket migration
a482499
2026-09-11
Use Unix socket for showroom Postgres fallback
6145bf4
2026-09-10
Disclose AdSense cookies in privacy page
b58941c
2026-09-10
Allow AdSense through public CSP
0358e6e
2026-09-09
TK-11341: add AdSense Auto-Ads loader + ads.txt (revert to remove ads)
323ecdb
2026-09-05
TK-11260: HOTSPOT_PROVIDER dispatcher + openai fallback, backfill 14 empty-hotspot rooms
156ccf7
2026-09-04
TK-10402: undo script for Flux room-scene backfill
9c27c31
2026-09-04
deploy: guard prod-only runtime data from rsync --delete
abbea82
2026-09-03
TK-10402: route guide heroes through the SCENE_PROVIDER dispatcher + add spend caps
1a8e5c7
2026-08-27
TK-10402: AbortSignal 30s timeout + ok-check on Flux image download (inert path hardening)
797a158
2026-08-25
chore: refactor review (clean, no changes), v0.5.0 (session close)
50e7120
2026-08-25
TK-10402: pluggable scene-provider seam + Flux-Kontext fallback (inert until funded)
4e4edbb
2026-08-10
chore: v0.4.5 (session close) — backfill-room-scenes.js lint-clean; Option B pipeline ready, blocked on Gemini prepay
a89299b
2026-08-10
auto-data-snapshot: 2026-08-10T11:29:10 (6 data files) — .playwright-mcp/page-2026-08-10T18-04-30-272Z.yml .playwright-mcp/page-2026-08-10T18-04-41-320Z.yml .playwright-mcp/page-2026-08-10T18-04-50-448Z.yml ids-guide-modern-bedroom.png ids-home.png
cb0b958
2026-08-10
TK-10402: backfill-room-scenes.js — regenerate scene_image for reconciled rooms via nano-banana (reuses lib/scene + lib/hotspots, idempotent, per-image cost)
666bd0c
2026-08-10
chore: bump v0.4.4 (session close) — guide-image fallback (3407b7f) verified; no refactor (already committed for gated deploy)
3407b7f
2026-08-10
fix(TK-10390): guide image fallback — no more imageless guides after reconcile
771434d
2026-08-10
chore: bump v0.4.3 (session close) — 404/CSP/image-degrade/reconcile all live-verified; code syntax-clean, no refactor (already deployed, would desync prod)
286edb3
2026-08-10
auto-data-snapshot: 2026-08-10T07:52:37 (4 data files) — .playwright-mcp/page-2026-08-10T14-40-45-681Z.yml .playwright-mcp/page-2026-08-10T14-43-02-390Z.yml .playwright-mcp/page-2026-08-10T14-43-15-486Z.yml .playwright-mcp/page-2026-08-10T14-52-10-562Z.yml
c6f5c6b
2026-08-10
TK-10390: reconcile-dead-images.js — dry-run tool to NULL dead scene_image/hero_image paths
0acaddb
2026-08-10
add scripts/fix-dangling-scenes.js — NULL out dangling scene_image DB refs on prod (TK-10390)
9530ee0
2026-08-09
auto-data-snapshot: 2026-08-09T17:28:38 (2 data files) — .playwright-mcp/page-2026-08-10T00-09-07-501Z.yml .playwright-mcp/page-2026-08-10T00-20-51-095Z.yml
63069ec
2026-08-09
fix: graceful-degrade broken guide-hero + room-scene images (interim)
23f8ed4
2026-08-09
fix(csp): allow static.cloudflareinsights.com in public script-src
cb035b8
2026-08-09
hardening: branded 404 page + remove orphan public/js/cart.js
e5b86aa
2026-08-05
Remove AdSense loader + ads.txt (luxury/commerce surface, not in AdSense account) — staged; deploy is Steve-gated
31b8301
2026-08-04
AdSense: loader in head + /ads.txt (ca-pub-5278231299883833) — approved ad site
160817d
2026-08-03
chore: bump v0.4.2 (session close) — GA4-under-CSP fix; quality gate clean (no linter, refactor no-op)
0e28b93
2026-08-03
GA4 under strict CSP: allow-list gtag.js origin + self-host inline bootstrap (/js/ga.js, deferred)
c988220
2026-08-03
deploy: add push-full-kamatera.sh (full-source sync, avoids per-file divergence 502s)
bc6f4d9
2026-08-03
GA4: inject gtag.js (G-DH9G5HL7YJ) into layout head — analytics for interiordesignershowroom.com
c686c5d
2026-08-03
fix(server): graceful listen error handler + honest HOST bind (was unhandled crash on EADDRINUSE + log lied about bind host)
27b1775
2026-08-03
security: full script-src 'self' CSP on public (delegated all img onerror incl client JS) + fix /admin path-guard prefix collision (Cody-hardened)
93f0ffc
2026-08-03
perf: content-hash asset cache-buster (?v=) + immutable css/js caching — deploy-safe, logged fallback, dev-guard (Cody-hardened)
3b62dda
2026-08-03
auto-save: 2026-08-03T11:53:46 (3 files) — lib/render.js server.js lib/assetv.js
6c5123a
2026-08-03
seo: honest Offer schema (data-driven itemCondition + conditional availability) + complete article OG tags (Cody-hardened)
f1385c9
2026-08-03
perf: correct static caching — content-hashed images immutable 1y, non-hashed 1d, css/js revalidate (deploy-safe, Cody-hardened)
cdb2c85
2026-08-03
seo+security: guides CollectionPage schema + Article url/dateModified + escape </script> in ALL JSON-LD (fleet-wide injection fix, Cody-hardened)
41049f5
2026-08-03
auto-save: 2026-08-03T11:23:33 (1 files) — server.js
1bd3992
2026-08-03
security: response headers + non-breaking CSP (frame-ancestors/object-src/base-uri) + patch /api/render error-message leak (Cody-hardened)
c34917c
2026-08-03
snapshot before deploy: in-tree security headers (nosniff/frame/referrer/permissions, drop x-powered-by) + don't leak raw error to /api/render client
81d733e
2026-08-03
refine: shared fmtStamp (storefront+admin, NaN/future-safe), sortable products table (Newest|Most clicks), post-migration schema assertion — v0.4.1
3f3869c
2026-08-03
a11y: working skip-to-content link (focus-moving, tabindex=-1) + prefers-reduced-motion support (Cody-hardened)
85a3efa
2026-08-03
seo+integrity: per-page OG images + saved rooms/guides/catalog fully honor suppression & exclude wall paints (Cody-hardened)
303f890
2026-08-03
chore: sync package-lock to v0.4.0
7c3f401
2026-08-03
chore: v0.4.0 (session close — date+time-added on store cards, admin analytics, deploy schema-migration safety)
c8362bb
2026-08-03
auto-save: 2026-08-03T10:53:14 (2 files) — lib/rooms.js server.js
181743c
2026-08-03
deploy: schema-migration step + /shop smoke test (stop code-ahead-of-schema)
ffa8079
2026-08-03
chore: v0.3.0 — keyword/id suppression feature (session close)
39571bf
2026-08-03
robustness: bigint-safe id guard across all client-value->id paths (/go, product_ids, wall_paint_id, admin+suppress routes) — no more 500 on malformed/overflow ids
aae117f
2026-08-03
harden: intOrNull() guard on client-supplied ids reaching bigint columns
78c986b
2026-08-03
store: date + time added on product cards — New badge + 'Added …' line
72708fc
2026-08-03
admin: keyword/id suppression rules (Suppress page) + Vacuum re-apply + Refresh
82f2795
2026-08-03
admin: per-product click analytics column (total, 7d, last-clicked date)
5b5102f
2026-08-03
seo: breadcrumbs (nav + BreadcrumbList) across facet/room/guide/looks pages, multi-JSON-LD layout (Cody-hardened)
ecc8fe0
2026-08-03
nav: promote kitchen/bathroom to rooms + upper-right room hamburger (collapsed on load, all room types)
6a15502
2026-08-03
seo: indexable facet landing pages — sitemap + self-canonical whitelist, ratio-gated, orphan rooms 404 (Cody-hardened)
3e86ee5
2026-08-03
IDS admin: implement EZ-Join DTD verdict (C+relabel), fix smart-quote regression, sync rooms.hotspots schema (v0.2.1)
562d723
2026-08-03
chore: lint, refactor, v0.2.0 (session close)
33bf504
2026-08-03
IDS admin: confirm CJ PID 101848244, drop bogus 1062321 (TK-10171)
8fb23cd
2026-08-03
IDS admin: record CJ account identifiers on Affiliates tab (TK-10171)
770915b
2026-08-03
admin: keyword line-hider with preview — catch a line across reseller-mislabeled brands (Honiture gap)
dcba584
2026-08-03
admin: brand-level line selector — hide/show a whole brand across every advertiser
99f7508
2026-08-03
auto-save: 2026-08-03T08:52:19 (7 files) — db/schema.sql lib/catalog.js lib/render.js lib/rooms.js public/css/site.css
95495fd
2026-08-03
IDS admin: 'Suggested lines to join' affiliate tab (TK-10171)
e72a006
2026-08-03
moodboard drawer: Pieces tab defaults open (board visible on open); Project/Create stay collapsed; explicit collapse still persists
d44578c
2026-08-03
Switch Honiture affiliate OFF (kept, hidden) + drop its ingest keyword
81400f7
2026-08-03
moodboard drawer: in-drawer project switcher + collapsible Project/Pieces/Create tabs (collapsed on load, state persisted)
590fca7
2026-08-03
shop: collapse facet rail on load, 4-across grid + columns slider, mood-board project select
f14bc91
2026-08-03
refactor: replace all SELECT * with explicit column lists (lib/cols.js) — clears the pre-deploy lint block so the standard deploy.sh path works for IDS (no more surgical-rsync workaround); result sets identical
28e228f
2026-08-03
feat(moodboard): client-side spend guard on Create Room Setting — two-step confirm ("Generate room ~$0.039? Tap again") + per-session cap of 8 renders, so a public visitor can't silently rack up Gemini spend (server per-IP limit still applies)
e5ef1c4
2026-08-03
feat: shop-time Mood Board — collect pieces while shopping, assemble, one button turns them into a photoreal room setting
713ee0d
2026-08-03
fix(render): match bigint product ids type-safe — /api/render was silently ignoring explicit product_ids (string p.id === number id never matched), rendering a generic room; also fixes /build explicit-piece selection
96f59a1
2026-08-02
ingest: partner-scoped CJ sweeps (partnerIds) + TWOPAGES program joined 2026-08-02 — keyword search can't reach niche joined advertisers; feed indexes 24-48h post-join, re-runs pick it up
b8892c3
2026-08-02
ingest: add robot/cordless vacuum + honiture brand sweep — Honiture CJ program joined 2026-08-02 (56 products, all tracked, $20-$260)
53a67e4
2026-08-02
fix .deploy.conf: IDS runs on 9876 (9861 belongs to staged.dw) — smoke test was probing the wrong app
8898a07
2026-08-02
docs: go-live runbook for auto-guides (gated)
213ca8b
2026-08-02
feat: auto guide generator — gen-guide.js builds a room (shared lib/roomgen) + authors an editorial guide around its exact pieces (local qwen3:14b, template fallback); room-type rotation for variety; 5 guides published locally
325e399
2026-08-02
add tracked-link coverage canary: samples live /go redirects, alerts on worsening CJ-tracking coverage (CNCP), daily 3:15am
64a5449
2026-08-01
feat: affiliate cart — collect pieces, checkout opens each product in its own tab (per-merchant). Server-rendered add-cart buttons + client cart.js (localStorage list, floating cart button, slide-in drawer, tab-fanout checkout with popup-block fallback)
47e1b8d
2026-08-01
room page: 3x-larger click-to-shop thumbnails (info + Buy on click); big thumbs are the shop surface on scene-less rooms so they no longer dead-end
a9db00f
2026-08-01
feat(build): right panel shows the products IN the generated room (shoppable) + vibe-only render back-fills a full 6-piece room; middle keeps the glowing hotspot dots; Browse-all restores the catalog
9906389
2026-08-01
feat(build): infinite-scroll the products panel — offset pagination in searchProducts/API, scroll-to-load-more in build.js (48/page, stable id-tiebreak sort, no dup rows); left filters reset to page 0
bede8a7
2026-08-01
feat(room): thumbnail rail beside the scene — stacked right on desktop, below on mobile; image-only thumbs, item name on hover; each a tracked shop link
439a773
2026-08-01
ui(build): persistent 3-column Room Builder — left words, right products column, middle images/room-settings centered (desktop); products no longer hidden under hamburger; hint text updated
b7e8531
2026-08-01
feat: shoppable hotspots on the live Room Builder gallery too (/api/render returns vision-located hotspots; build.js overlays dots w/ hover-card + click-to-shop; saved rooms persist builder hotspots)
ffe693d
2026-08-01
feat: shoppable room-setting images (vision-located hotspots w/ IoU dedup) + auto room generator from CJ inventory (Gemini scene + hotspots, ~$0.04/room); rooms.hotspots jsonb; inline hotspot cards on /room/:slug
8df6b7b
2026-08-01
auto-save: 2026-08-01T22:40:01 (3 files) — lib/rooms.js lib/hotspots.js scripts/gen-room-setting.js
4a02b54
2026-08-01
refine: real Gemini guide heroes (replace picsum placeholders, 1200x630 jpg ~90kb) + unpublish junk test rooms from index
082c438
2026-08-01
refine: soften freshness chip to 'Price checked' + SEO structured-data (Organization/WebSite/SearchAction on home, ItemList+Product on shop/rooms, per-page OG image, sitemap adds looks/build/public-rooms + lastmod)
1ead1eb
2026-08-01
refine: price-freshness trust chip on cards (price_checked_at) + PNG/apple-touch favicon fallbacks
7f7de63
2026-08-01
harden /api/render + admin: persistent restart-proof render cost-guard (cf-connecting-ip + daily cap), scene_image allowlist, admin CSRF origin check
8d27aa6
2026-08-01
Frontend refinement: consistency + edge/empty states + mobile + a11y + loading states across storefront and Room Builder
96155e2
2026-08-01
Data quality: dead-image guard (in_stock flag), image-based color enrichment for the 12-color filter, dedup
582513e
2026-08-01
Fix products panel + close button z-index (were behind sticky header); panel now overlays correctly
012c874
2026-08-01
Room Builder: fix hamburger click (was behind sticky header); show room's pieces as a shoppable grid below the generated render; single-color product column at all widths
03357d1
2026-08-01
Room Builder: right products = single image-forward column; whole card click selects into the top bar; larger selected-product thumbnails in the tray
c3798f4
2026-08-01
Add idempotent nightly live-catalog refresh (SSH-tunnel upsert into prod; swaps in tracked links as CJ advertisers are approved)
1012ec8
2026-08-01
Elevate site UI to steelcase.com-caliber editorial design
3448c99
2026-08-01
auto-save: 2026-08-01T21:09:29 (7 files) — lib/scene.js package-lock.json package.json server.js _ctest.js
61d7a3c
2026-08-01
Add Herman Miller + Steelcase brand ingest for testing
27d7ce4
2026-08-01
Room Builder v4 layout: hamburger-collapsible products panel + generated-rooms gallery, styled
bcddff0
2026-08-01
Room Builder: 12-color multi-select swatches (Alabaster→Blush, 1-to-many, maps to catalog color buckets); include color in search results for hover popup
628a4c7
2026-08-01
Room Builder: AI photoreal render (Gemini nano-banana, feeds real product images as refs, $0.039/img, rate-limited) in the middle + saved as room scene; hover-detail popup on items; tray images bar
1e3d7b1
2026-08-01
Room Builder: tray+GO, collapsed left tabs (trending/style/hue/color/theme/period/categories/brand), draggable served products, live preview+ideas, Samplize wall color, style-only generation w/ loosening back-fill; rooms schema + /looks gallery + /room/:slug + /api catalog/paints/brands/rooms
365ba46
2026-08-01
Scaled CJ catalog ingest (48 categories, image-required) -> 2697 real products/27 brands incl. appliances; idempotent, swaps in tracked links as advertisers activate
145b8c5
2026-08-01
auto-save: 2026-08-01T20:09:07 (1 files) — lib/adapters/cj.js
4fc3f55
2026-08-01
GOLIVE: launched — site live on interiordesignershowroom.com with LE cert (IP-bound :443 fix)
67a527f
2026-08-01
Cycle 1 hardening: favicon (IDS monogram), OG social image + meta; browser-verified 0 console errors
4dad839
2026-08-01
Update GOLIVE.md: Kamatera deploy complete; only NS swap + optional CF Full remain
d55b94c
2026-08-01
Add faceted filtering (room/style/color/price) to /shop + /rooms
86a89da
2026-08-01
auto-save: 2026-08-01T11:36:26 (2 files) — server.js lib/catalog.js
fee7e91
2026-08-01
Add robots.txt (block /admin + /go), go-live staging checklist (GOLIVE.md)
5637f88
2026-08-01
Add gated /admin curation dashboard + dotenv env loading
9d15d2b
2026-08-01
Fix adapter bugs from adversarial review: Rakuten token endpoint Basic (was Bearer, would 401 forever); CJ add category to GraphQL selection
7c4cdd2
2026-08-01
Add 3 editorial buying guides (small-space sectionals, lighting layers, coastal living room)
619c451
2026-08-01
Add 4 credential-gated feed adapters (CJ, Amazon PA-API v5, Rakuten, ShareASale) + ingest orchestrator
020e430
2026-08-01
Add CJ Affiliate GraphQL product-feed adapter (TK-10112)
7f2672c
2026-08-01
Scaffold interiordesignershowroom: multi-network affiliate showroom foundation
5617cca
2026-08-01
auto-save: 2026-08-01T11:06:15 (7 files) — .deploy.conf .gitignore db/ lib/ package.json
Authors
Steve 111 steve 6 auto-commit-fleet 3
Skills used
/rooms 13 /shop 10 /render 10 /page-2026-08- 9 /room 6 /scene 5 /color 5 /claude 4 /code 4 /guides 4 /catalog 4 /style 4 /hotspots 3 /roomgen 3 /brand 3 /price 3 /gen-guide-heroes 2 /scene-providers 2 /cart 2 /healthz 2 /schema 2 /looks 2 /affiliates 2 /title 2 /mid-century-room 1 /hotspot-providers 1 /backfill-hotspots 1 /yolo-queue 1 /executed-reversible 1 /ledger 1
Creative ideas + design notes
Commits with substantial prose (≥120 chars) — the rationale behind each move.
323ecdb · 2026-09-05 · TK-11260: HOTSPOT_PROVIDER dispatcher + openai fallback, backfill 14 empty-hotspot rooms
Gemini AI Studio prepay is still depleted (confirmed live 429
RESOURCE_EXHAUSTED), so the 12 Flux-regenerated room scenes from TK-10402
plus steelcase-office/mid-century-room stayed at hotspots=[] (rendered but
not shoppable). Mirrors the existing SCENE_PROVIDER pattern (lib/scene.js):
lib/hotspots.js is now a thin dispatcher over lib/hotspot-providers/{gemini,openai}.js,
same {locateProducts, COST_PER_CALL} contract, so server.js and
backfill-room-scenes.js are untouched.
Ran scripts/backfill-hotspots.js (new, hotspots-only — ~10x cheaper than a
full scene regen) with HOTSPOT_PROVIDER=openai against the LOCAL dev DB:
14 rooms updated, $0.07 total (gpt-5.2 vision, ~$0.005/room). 13/14 rooms
now carry at least one hotspot; "room" got a genuine 0-match (frontend
edge-chip fallback covers it, same as Gemini's own omit-if-unsure behavior).
Ledgered as reversible (undo = UPDATE rooms SET hotspots='[]' for the 14
slugs) at ~/.claude/yolo-queue/executed-reversible/ledger.jsonl.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAXbNq4CYRmd9hRam2NU6P
156ccf7 · 2026-09-04 · TK-10402: undo script for Flux room-scene backfill
12 NULL room scene_images regenerated via Replicate-Flux fallback (Gemini
prepay depleted). This reverts scene_image->NULL + deletes the generated PNGs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EgcJfyrTaPp4vJYtptSXGG
9c27c31 · 2026-09-04 · deploy: guard prod-only runtime data from rsync --delete
deploy.sh runs 'rsync -az --delete' with no data/ exclude, so a deploy
overwrote the box's runtime file with the local copy and destroyed
everything written since the last deploy. Added a surgical
RSYNC_EXTRA_EXCLUDES for this project's runtime path(s) only, so genuine
build artifacts under data/ keep syncing.
Verified by itemized rsync --dry-run against prod: the runtime file is
listed for transfer WITHOUT the exclude and absent WITH it.
Fleet sweep, TK-11227. Same bug class as the July 2026 artmura/quadrille
mis-target fix and the abramsagency lead-ledger fix (TK-10536).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013mq9RQtVZZEWvreDqqTgv2
abbea82 · 2026-09-03 · TK-10402: route guide heroes through the SCENE_PROVIDER dispatcher + add spend caps
The documented Flux runbook was broken for the guides half: gen-guide-heroes.js
called the Gemini endpoint directly and silently ignored SCENE_PROVIDER, so
`SCENE_PROVIDER=replicate-flux node scripts/gen-guide-heroes.js` would have failed
once per guide against the depleted Gemini key (~109 failures, prod scope).
- lib/scene-providers/{gemini,replicate-flux}.js: add generateEditorial({subject})
-> {buffer, cost}. Gemini's prompt text is byte-identical to the inline one it
replaces, so the default path is unchanged.
- lib/scene.js: export generateEditorial through the dispatcher.
- scripts/gen-guide-heroes.js: use scene.generateEditorial (provider-agnostic).
- lib/run-guard.js (new): shared --dry-run / --limit / --max-cost parsing so a cap
behaves identically across both paid drivers.
- both drivers: --dry-run ($0 target list + estimate), --max-cost hard cap (default
$12, pre-flight abort + per-image stop), and a consecutive-failure abort so an
unfunded credential can't emit the same error once per row across the batch.
Verified $0: dry-runs on both drivers, cap abort, default provider still gemini
($0.039), replicate-flux dispatch resolves, node --check on all touched files,
server.js + lib/roomgen unaffected. No provider call made; ticket stays gated on
Steve's spend + funded credential.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011X4xabTP7zwUuN7zeDPfbJ
1a8e5c7 · 2026-08-27 · TK-10402: AbortSignal 30s timeout + ok-check on Flux image download (inert path hardening)
Prevents a hung Replicate CDN download from wedging the batch indefinitely;
adds an explicit HTTP-status check. No behavior change on the funded path,
no spend (fallback stays inert until a REPLICATE_API_TOKEN is routed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
797a158 · 2026-08-25 · chore: refactor review (clean, no changes), v0.5.0 (session close)
Session added the SCENE_PROVIDER seam + Flux-Kontext fallback (50e7120) — a
backward-compatible feature, default gemini unchanged. javascript-pro refactor pass:
no edits warranted. Steve chose the no-Gemini path; Flux fallback stays inert until a
REPLICATE_API_TOKEN is funded (Steve-gated).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50e7120 · 2026-08-25 · TK-10402: pluggable scene-provider seam + Flux-Kontext fallback (inert until funded)
Extracts the Gemini nano-banana impl behind a SCENE_PROVIDER dispatcher (lib/scene.js)
so a depleted provider is a one-flag flip. Default=gemini is byte-identical ($0.039,
same output path/contract) — backfill-room-scenes.js + lib/roomgen untouched.
Adds lib/scene-providers/replicate-flux.js: reference-capable (keeps 'real product in
the room'), throws a clear actionable error + spends nothing until a REPLICATE_API_TOKEN
is funded. Gemini prepay top-up remains the recommended path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3407b7f · 2026-08-10 · fix(TK-10390): guide image fallback — no more imageless guides after reconcile
The 257-path reconcile NULLed 107 guides.hero_image, leaving guides imageless
(JSON-LD Article.image dropped, og:image -> logo, home/index cards blank) — a
real SEO/social regression cre-agent's contrarian gate caught (M-00475).
Option A (render-layer, $0): guides now fall back to a representative product
image (first non-suppressed product of the guide) — exactly like rooms fall back
to product thumbs. Applied to all 3 surfaces: detail route (Article.image +
og:image + visible hero via guideImg), home guide-strip + /guides index (via a
first-product-image subquery). data-hide-on-error still guards the img.
Verified: fallback subquery returns real product images 5/5 guides; absUrl passes
external CDN URLs through unchanged (no double-prefix); simulated prod-NULL path
-> product image; syntax OK, routes 200; guides WITH a real hero unchanged.
Deploy gated -> pending-approval.
c6f5c6b · 2026-08-10 · TK-10390: reconcile-dead-images.js — dry-run tool to NULL dead scene_image/hero_image paths
Finds rooms.scene_image / guides.hero_image pointing to a missing local file and,
with --apply (GATED prod write, snapshot-first + single transaction), NULLs them so
pages fall back cleanly and the dead path leaves the OG/JSON-LD image tags too.
Dry-run by default, read-only. Interim cleanup; proper fix = regenerate the missing
PNGs (claude-email's roomgen lane). Verified: syntax OK, local dry-run reports clean.
63069ec · 2026-08-09 · fix: graceful-degrade broken guide-hero + room-scene images (interim)
Interim mitigation for TK-10390 (150 auto-rooms + some guide heroes point to
/img/rooms/*.png files that don't exist -> broken-image icons on /room/:slug,
/guides, and home). Root-cause regeneration stays claude-email's auto-room lane.
- imgfallback.js: data-hide-on-error now hides the whole figure.room-scene-wrap
when a .room-scene img fails (so the hotspot layer doesn't float over an empty
box); other opt-in imgs hide themselves as before.
- sceneFigure (render.js) + 3 guide-hero <img> sites (home/guides/detail): added
data-hide-on-error.
- No-op for images that load; complementary to regeneration (not conflicting).
Verified in real Chrome: broke a scene src -> figure display:none, roomThumbs
rail stays visible, valid images unaffected. Deploy GATED -> pending-approval.
23f8ed4 · 2026-08-09 · fix(csp): allow static.cloudflareinsights.com in public script-src
CF auto-injects its Web-Analytics beacon on the proxied zone; the strict
public script-src ('self' + googletagmanager) was blocking it -> a CSP
console error on every page. Added the CF Insights origin (public only;
/admin unchanged). Verified: public CSP now lists the origin, /admin still
'unsafe-inline'-only, server boots clean.
cb035b8 · 2026-08-09 · hardening: branded 404 page + remove orphan public/js/cart.js
- 404 handler was a bare 'Not found / back to showroom' line; now a branded
Error-404 page with helpful destinations (Shop/Rooms/Build/Guides), inline
styles matching the footer pattern (no CSP/style-src impact), reusing layout
header+footer. Verified: /nope -> 404 status + new copy, home still 200.
- public/js/cart.js was dead scaffolding from the initial generic-ecommerce
base — loaded by no script (this is an affiliate showroom; click-through is
/go, there is no cart/checkout). Removed; moodboard.js comment de-referenced.
- /shop no-results state already handled (server.js grid-empty); sitemap
lastmod left as-is (guides/rooms already carry accurate lastmod; fabricating
static-page dates would be an SEO-negative, not an improvement).
Deploy to Kamatera is GATED -> drafted to pending-approval.
0e28b93 · 2026-08-03 · GA4 under strict CSP: allow-list gtag.js origin + self-host inline bootstrap (/js/ga.js, deferred)
The parallel GA4 injection (bc6f4d9) added an external googletagmanager.com script
+ an INLINE gtag config to the layout head — both blocked by C11's script-src 'self'
(dead analytics on next reload). Fix keeps the CSP strict: allow-list
www.googletagmanager.com for the library, move the inline bootstrap to a self-hosted
deferred /js/ga.js so 'self' admits it (no 'unsafe-inline' reintroduced). connect-src
left intentionally open (documented) so GA4 regional beacons keep working — enumerating
them is brittle. Cody-gated, verified live (CSP header, ga.js 200+deferred, 0 inline gtag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
181743c · 2026-08-03 · deploy: schema-migration step + /shop smoke test (stop code-ahead-of-schema)
Prevents a repeat of the 2026-08-03 regression where shipped code referenced a
prod-missing column (products.suppressed) + tables (affiliate_settings,
suppress_rules) and /shop 500'd while the DB-blind /healthz smoke test stayed
green.
- db/schema.sql: explicit ADD COLUMN IF NOT EXISTS guards (inline, before
dependent indexes) so the file is a complete idempotent migration on a fresh
OR drifted DB, not just a fresh-create.
- scripts/migrate.sh: applies schema.sql with ON_ERROR_STOP; loads DATABASE_URL
from the remote's own .env.
- .deploy.conf: BUILD_CMD runs the migration on the remote after install/before
reload; HEALTH_URL now hits /shop (exercises the real catalog query, not the
DB-blind /healthz); INSTALL_CMD bakes in the sharp-safe (scripts-enabled) install.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ffa8079 · 2026-08-03 · chore: v0.3.0 — keyword/id suppression feature (session close)
Author: session that shipped 72708fc (admin Suppress page). Version bump only;
working-tree edits to lib/rooms.js/routes/admin.js/server.js + new lib/ids.js belong
to a concurrent session and are intentionally left uncommitted.
aae117f · 2026-08-03 · harden: intOrNull() guard on client-supplied ids reaching bigint columns
Non-numeric wall_paint_id (and similar) now coerces to null instead of
Number()->NaN reaching a bigint query and throwing a 500. Applied at
/api/rooms and /api/render call sites. (Pre-existing working-tree change,
snapshotted before deploy so shipped code == committed code.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
78c986b · 2026-08-03 · store: date + time added on product cards — New badge + 'Added …' line
Every storefront product card now shows when it was added to the store: a NEW
badge over the image for items added in the last 14 days (exact date+time in the
hover title), plus a muted, dated 'Added Mon D, YYYY, H:MM AM' line under the
price. New shared addedStamp() helper in render.js mirrors the admin when()
format; card-new + card-added styles added to site.css. Chosen treatment per
2026-08-03 decision (overrides the usual 'customer cards omit created date').
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
72708fc · 2026-08-03 · admin: keyword/id suppression rules (Suppress page) + Vacuum re-apply + Refresh
- New suppress_rules table (durable keyword/id hide rules, distinct from source-level
affiliate_settings and the raw suppressed flag).
- /admin/suppress: enter keywords (and/or ids) -> immediately flip suppressed=TRUE on
every current match (title/brand/advertiser). Reuses the pervasive NOT suppressed gate
so hides propagate to /shop, /rooms, guides, and the sitemap.
- Vacuum re-applies all rules (catches products ingested later matching an old keyword);
Refresh reloads/recounts; View shop -> /shop?room=decor&style=modern.
- Per-rule live match/hidden counts, created date+time, Re-apply, Remove, Remove+unhide.
- Verified end-to-end: add velvet (7 hidden), storefront drop confirmed, vacuum idempotent,
remove+unhide restores state; CSRF guard returns 403.
82f2795 · 2026-08-03 · admin: per-product click analytics column (total, 7d, last-clicked date)
Fold click aggregates into the products query via one grouped LEFT JOIN and
render a Clicks column per row — lifetime total, a 7-day sub-count, and the
last-clicked timestamp (dated with the standard when() helper). No-click
products show a muted — instead of fake zeros.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3e86ee5 · 2026-08-03 · IDS admin: implement EZ-Join DTD verdict (C+relabel), fix smart-quote regression, sync rooms.hotspots schema (v0.2.1)
- EZ-Join verdict C-as-superset-of-A: joinTarget() resolves a per-network
marketplace SEARCH deep-link (brand pre-filled) where searchVerified, else the
plain landing (safe fallback). All networks land today (unverified); flip
searchVerified per network after a live test. Honest relabel 'EZ Join' ->
'Join via <Network>'.
- Fix: refactor pass had introduced curly quotes as HTML attr delimiters on the
join button (class="ezjoin"/href broke styling+link) — restored straight quotes.
- db/rooms.sql: declare + idempotently ALTER-add the hotspots JSONB column that
createRoom() writes (schema file was drifted from the live DB).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
562d723 · 2026-08-03 · chore: lint, refactor, v0.2.0 (session close)
Lint: colon-safe Basic-auth parse. Refactor (behavior-preserving): move
AFFILIATE_ENABLED const above buildWhere; NUL->KEY_SEP; chip param key->netKey;
dedup joinedNets label. Verified: auth 401/200, /shop + affiliates + suggested
200, toggle round-trip. Minor bump — session shipped the affiliate on/off admin.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
33bf504 · 2026-08-03 · IDS admin: confirm CJ PID 101848244, drop bogus 1062321 (TK-10171)
.env CJ_WEBSITE_ID=101848244 matches the PID in the live tracking links, so CJ
attribution is correct. 1062321 was identified as a FileMaker customer acct
(client Claire, Shopify order #32749), not a CJ id — removed from the record.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8fb23cd · 2026-08-03 · IDS admin: record CJ account identifiers on Affiliates tab (TK-10171)
Adds a CJ Account reference bar to /admin/affiliates from data/cj-account.json:
login steve@designerwallcoverings.com, CID 7896980, live-link PID 101848244
(all evidence-confirmed). 1062321 shown as 'unverified' — it matches neither the
CID nor the PID in the live click-[PID]-[AID] tracking links, so it is NOT wired
into anything pending confirmation under CJ Account > Websites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
95495fd · 2026-08-03 · IDS admin: 'Suggested lines to join' affiliate tab (TK-10171)
Adds a second tab under /admin/affiliates: a curated gap-finder of on-brand
home/decor affiliate programs across CJ + other networks. Flags lines already
in the catalog as Joined, gives an EZ Join deep-link into the right network's
marketplace for the rest, filterable by network. Roster is data-driven
(data/suggested-affiliates.json), read live so it extends without a restart.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
590fca7 · 2026-08-03 · shop: collapse facet rail on load, 4-across grid + columns slider, mood-board project select
- facet rail -> native <details>, all collapsed on load (auto-opens only the group holding an active filter)
- grid controls: columns slider (2-6, default 4) driving #grid via --gcols; grid starts 4-across even pre-JS
- sort: date/color/style/brand/title/price all present (Newest relabelled 'Date - Newest')
- mood board -> named PROJECTS: #mbProjectSel switcher, create/switch, per-project item lists; legacy single board migrated to 'My Board'
e5ef1c4 · 2026-08-03 · feat: shop-time Mood Board — collect pieces while shopping, assemble, one button turns them into a photoreal room setting
- public/js/moodboard.js: self-contained client feature (mirrors cart.js). Injects a Mood Board button on every product card, floating FAB + slide-in drawer that assembles all selections, a star-Primary toggle, room-type picker, and a Create Room Setting button that POSTs the selected ids to the existing /api/render (Gemini 2.5 Flash Image, ~$0.039) and shows the rendered room + shoppable pieces inline
- lib/render.js: load moodboard.js globally so selection works site-wide (shop, rooms, home, looks)
- Primary piece is sent first so it anchors the room (scene refs the first 4 pieces)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
47e1b8d · 2026-08-01 · room page: 3x-larger click-to-shop thumbnails (info + Buy on click); big thumbs are the shop surface on scene-less rooms so they no longer dead-end
- roomThumbs: each thumb is now a <button> that expands product info (brand/title/price) + a tracked Buy button inline, instead of jumping straight out to the affiliate link
- thumbs 56px -> 168px (3x); new /js/room-thumbs.js toggle (one-open, Esc/outside-click closes), mirrors hotspot-card grammar
- server room route: render the big thumbs as the shoppable surface when a room has no scene_image (fixes glam-room dead-end); scene rooms unchanged (scene + side rail + grid)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d55b94c · 2026-08-01 · Add faceted filtering (room/style/color/price) to /shop + /rooms
- lib/catalog.js: URL-addressable filters, self-excluding facet counts, active chips
- every facet is a real drillable /shop?... link (data-points-href-to-deeper-data rule)
- verified locally: room/color filters narrow the grid, chips clear individually
5637f88 · 2026-08-01 · Add gated /admin curation dashboard + dotenv env loading
- routes/admin.js: basic-auth (timing-safe), featured-toggle, guide publish/unpublish,
click analytics (top-clicked, 7-day counts), created date+time chips per admin-card rule
- server.js loads .env via dotenv (CJ_COMPANY_ID etc.); admin router mounted
- verified: /admin 401 unauth / 200 authed, timestamps render, existing routes intact
619c451 · 2026-08-01 · Add 4 credential-gated feed adapters (CJ, Amazon PA-API v5, Rakuten, ShareASale) + ingest orchestrator
- Each adapter implements the shared {network, enabled(env), fetch(env,{limit})} contract,
maps its network's API into the unified rawProduct shape, uses only node built-ins
(global fetch + crypto for SigV4/HMAC), and returns [] when creds absent (never throws)
- scripts/ingest.js runs enabled adapters -> normalize -> UPSERT; idempotent, cron-safe
- .env.example documents every network's required credentials
- Verified: registry loads all 4, ingest skips all gracefully with no creds
7f2672c · 2026-08-01 · Scaffold interiordesignershowroom: multi-network affiliate showroom foundation
- Express + Postgres, unified product contract (normalize.js) feeding one catalog
from 4 networks (CJ/Amazon/Rakuten/ShareASale) with room/style/color classifier
- Showroom home, /shop + /rooms grids with sort+density, editorial buying guides
- /go/:id affiliate redirect w/ click logging, FTC disclosure baked into shell,
SEO (canonical/OG/JSON-LD/sitemap), legal pages
- Seeded 24 sample products + 2 guides; running under pm2 on :9861
File tree
88 files tracked. Click any to browse the source at HEAD.
rendered in 2ms