Dw Domain Fleet

repo: ~/Projects/dw-domain-fleet · 136 commits · 2 in last 24h, 3 in last 7d · ▄ █

Search the build

136 commits indexed

  1. 45feb4c 2026-09-26 TK-12333: remove Scalamandre from showroom-vendors (Steve 2026-09-26: Scalamandre is NOT a showroom line)
  2. 0c9db1c 2026-09-26 TK-11860: sync showroom-vendors snapshot to canonical fix-live-board@9bcd81c (+Jeffrey Stevens, Mind the Gap, Sandberg, Scalamandre)
  3. fc5a721 2026-09-23 auto-data-snapshot: 2026-09-23T14:48:42 (1 data files) — data/hero-allocation.json
  4. 5e945b3 2026-09-16 deploy-articles.sh: fail-flag + full-fleet verify so a partial deploy exits non-zero
  5. e5dcb36 2026-09-16 content drip: +4 fresh article(s) — etciemurals.com, fabricfridays.com, fireratedwallcovering.com, handmadewallcovering.com
  6. 1d92019 2026-09-14 TK-11538: gridControls appendChild off the prototype (consistency with catalogPage)
  7. 1decd1b 2026-09-14 auto-data-snapshot: 2026-09-14T09:19:35 (4 data files) — sites/classawallcovering.json sites/commercialsalesreps.json sites/commercialwallcovering.json sites/designermagnetics.json
  8. 457ca42 2026-09-12 TK-11537: make helmet CSP explicit — useDefaults:false + declare script-src-attr 'none'
  9. d20f426 2026-09-12 TK-11537: declare scriptSrcAttr 'none' explicitly so read==serve (no behavior change)
  10. 8be45e2 2026-09-12 TK-11538: guard gridControls() localStorage with try/catch (Safari private mode) + clamp saved density to range bounds
  11. cbc272c 2026-09-12 chore: v1.0.2 (TK-11470 session close)
  12. 35ab401 2026-09-12 TK-11535: encode sort in pager() href — close the unescaped-attribute injection (matches q)
  13. b757902 2026-09-11 test(catalog): keep the TK-11470 sort proof harness in-repo
  14. d0f8c70 2026-09-11 catalog sort: stop remembering the sort, so Back works and the dropdown can't lie (TK-11470)
  15. 95a4e5a 2026-09-11 fix(catalog): guard requestSubmit — Safari <16 would have re-killed the sort (TK-11470)
  16. b4b8158 2026-09-11 fix(catalog): sort dropdown was dead on all 8 funnel sites — CSP blocked its inline onchange (TK-11470)
  17. 6a1fcbb 2026-09-11 deploy-fleet: RSYNC_EXTRA_EXCLUDES, so one agent's approved deploy can't ship another's held work (TK-11463)
  18. caaf32b 2026-09-11 pull-catalog: flatten to the SELLABLE variant, not blindly variants[0] (TK-11463)
  19. 35148f6 2026-09-11 deploy gate: assert catalog sites actually RENDER products, not just answer 200 (TK-11463)
  20. 3f245e2 2026-09-11 auto-data-snapshot: 2026-09-11T11:23:31 (1 data files) — .playwright-mcp/page-2026-09-11T18-17-15-066Z.yml
  21. 42798ff 2026-09-11 snapshot before TK-11463 pull-catalog variant fix: uncommitted catalog refresh (70 new, 4410 changed)
  22. 9452a19 2026-09-11 fix(catalog): drop dead display_variant junk check that emptied all microsite grids (TK-11307/TK-11037, Steve Option A)
  23. d020049 2026-09-11 content drip: +8 fresh article(s) — 1800wallcoverings.com, asseeninhotels.com, asseeninla.com, asseeninmovies.com, asseeninshowrooms.com, barwallpaper.com, blankstocklining.com, carmelwallpaper.com
  24. b363266 2026-09-11 auto-data-snapshot: 2026-09-11T08:57:34 (1 data files) — data/catalog.json
  25. 4f65cef 2026-09-11 deploy-fleet: smoke test cried wolf on every healthy deploy (TK-11307)
  26. 11085c2 2026-09-10 de-mail-merge articles for thehotelwallpaper + 3 sister monetize sites (TK-11428)
  27. d2df4ae 2026-09-10 render: emit google-site-verification meta when cfg.gscVerify set (TK-11428 GSC route)
  28. 7fff208 2026-09-10 de-mail-merge articles for 4 monetize sites (TK-11428)
  29. ba1968a 2026-09-10 de-mail-merge articles for 4 monetize sites: asseeninshowrooms, commercialsalesreps, restaurantmurals, wallpaperny (TK-11428)
  30. 404b07a 2026-09-10 auto-data-snapshot: 2026-09-10T13:39:50 (8 data files) — sites/asseeninshowrooms.json sites/barwallpaper.json sites/commercialsalesreps.json sites/hospitalitysalesagency.json sites/restaurantfabrics.json
  31. e21caf6 2026-09-10 de-mail-merge articles for 4 monetize sites (TK-11428)
  32. efcdba2 2026-09-10 de-mail-merge articles for 4 generic-named monetize sites (TK-11428)
  33. aa4e372 2026-09-10 de-mail-merge articles for 4 monetize sites (TK-11428)
  34. f2e63a2 2026-09-10 de-mail-merge articles for 4 monetize sites (TK-11428)
  35. 60d6313 2026-09-10 de-mail-merge articles for 4 monetize sites (TK-11428)
  36. 26fa01c 2026-09-10 de-mail-merge articles for 3 pilot monetize sites (TK-11428)
  37. 2b0f0ad 2026-09-10 chore: lint, refactor, v1.0.1 (session close) — TK-11307
  38. fb33023 2026-09-10 microsite catalog: showroom tag is 'ShowroomOnly' (TK-11307) Steve ruled 2026-09-10: the MDC showroom tag is 'ShowroomOnly', not 'Showroom'.
  39. f71119f 2026-09-10 Record article rendering fix proof
  40. 77bc6d8 2026-09-10 Render generated AdSense article bodies
  41. 830fbd9 2026-09-10 fleet: Instagram embed on homePage for patterndesignlab + hollywoodwallcovering (TK-11322)
  42. 38fefae 2026-09-09 monetize: per-site unique visual identity — load each site's Google Fonts + slug-hashed style presets (hero angle, heading treatment, panel radius, sans pairing) so sites aren't a template farm (TK-11322)
  43. 92fa549 2026-09-09 monetize sites: embed 20-pattern named grid + /img proxy + proxied hero/og (TK-11322)
  44. 6f68867 2026-09-09 content drip: +1 fresh article(s) — 1800wallcoverings.com
  45. eeb4b26 2026-09-09 monetize sites: add verified Instagram footer link for the 6 with real accounts (TK-11322)
  46. 003bc83 2026-09-09 auto-data-snapshot: 2026-09-09T22:50:09 (7 data files) — sites/asseeninla.json sites/asseeninmovies.json sites/barwallpaper.json sites/blankstocklining.json sites/designermagnetics.json
  47. f49d0a6 2026-09-09 monetize sites: render 6-8 image gallery from hero-allocation (TK-11322)
  48. 80430c8 2026-09-09 auto-data-snapshot: 2026-09-09T21:46:00 (11 data files) — sites/restaurantmurals.json sites/sheltermagazines.json sites/specifywallpaper.json sites/thehotelwallpaper.json sites/traditionalwhimsy.json
  49. 392ebe8 2026-09-09 auto-data-snapshot: 2026-09-09T21:13:05 (10 data files) — sites/classawallcovering.json sites/commercialsalesreps.json sites/commercialwallcovering.json sites/etciemurals.json sites/fabricfridays.json
  50. b599863 2026-09-09 auto-data-snapshot: 2026-09-09T20:39:48 (5 data files) — sites/1800wallcoverings.json sites/asseeninhotels.json sites/asseeninshowrooms.json sites/carmelwallpaper.json sites/wallpaperexports.json
  51. 33785ab 2026-09-09 auto-data-snapshot: 2026-09-09T20:06:23 (8 data files) — sites/traditionalwhimsy.json sites/unitedstateswallpaper.json sites/wallcovering.json sites/wallpaperfromthe80s.json sites/wallpaperny.json
  52. 75471d9 2026-09-09 auto-data-snapshot: 2026-09-09T19:32:54 (17 data files) — sites/commercialsalesreps.json sites/commercialwallcovering.json sites/designermagnetics.json sites/etciemurals.json sites/fabricfridays.json
  53. 502b9f6 2026-09-09 auto-data-snapshot: 2026-09-09T18:59:42 (9 data files) — sites/1800wallcoverings.json sites/asseeninhotels.json sites/asseeninla.json sites/asseeninmovies.json sites/asseeninshowrooms.json
  54. f7d5b2c 2026-09-09 Revert "fleet Phase 2: scaffold 138 parked monetize domains — configs + ecosystem + nginx vhosts (ports 11143-11280) (TK-11322)"
  55. b6088ea 2026-09-09 fleet Phase 2: scaffold 138 parked monetize domains — configs + ecosystem + nginx vhosts (ports 11143-11280) (TK-11322)
  56. 64e2a9d 2026-09-09 monetize DEPTH: add /articles hub + per-article long-form pages (Article JSON-LD, nav, home cross-links, sitemap) — content plumbing (TK-11322)
  57. bab12e1 2026-09-09 fleet: deploy-safe showroom-vendor require — fall back to in-repo list off-Mac2 (TK-11322)
  58. 39b399d 2026-09-09 fleet: repoint all 43 sites to free ports 11100-11142 (fix EADDRINUSE crash-loop) + monetize skips catalog build (TK-11322)
  59. 5c898d7 2026-09-09 monetize Phase 1: wire GA4 for sheltermagazines — all 35/35 now have gtag (TK-11322)
  60. 44aaad2 2026-09-09 monetize Phase 1: wire GA4 measurement IDs (cfg.ga4) into 34 sites (TK-11322)
  61. b9a98bc 2026-09-09 monetize Phase 1: enable monetize mode + per-domain content on 35 parked SEO sites (TK-11322)
  62. 1681057 2026-09-09 monetize template: full AdSense-ready build — Guide+Contact pages, sitemap.xml/robots.txt, FAQ+schema, cookie notice, favicon, og:image, approval-first for-sale phasing (TK-11322)
  63. 00c6cee 2026-09-09 monetize: content sections + checklist as collapsed <details> panels (SEO-safe, SSR in DOM) (TK-11322)
  64. 6769abd 2026-09-09 monetize: fix header overlap (in-flow sticky m-nav, not fixed topbar) + hero-image support with scrim (TK-11322)
  65. ebca1a8 2026-09-09 TK-11322: add red flocked hero asset for barwallpaper.com monetize hero
  66. 990e3da 2026-09-09 fix(nginx): durable :443 socket normalization for fleet HTTPS-000 socket-specificity bug
  67. e10d68c 2026-09-09 monetize: render authored content copy verbatim (skip DW wallpaper->wallcovering swap)
  68. 197eead 2026-09-09 monetize mode: AdSense Auto Ads + for-sale banner + content site + ads.txt/privacy for parked SEO domains (TK Task#2)
  69. c1f8120 2026-09-09 fix(deploy): no-clobber nginx vhost install so certbot :443 blocks survive deploys
  70. 8d99c6c 2026-09-09 auto-data-snapshot: 2026-09-09T08:26:36 (1 data files) — data/catalog.json
  71. 42bf6b7 2026-09-08 microsite catalog: exclude showroom-tagged products from CLEAN grids (TK-11307)
  72. ddc73f9 2026-09-04 add .trim() to isShowroomVendor vendor string comparison (TK-11200)
  73. 71985bf 2026-09-04 TK-11200: isShowroomVendor guard — filter showroom-only vendors from all microsite catalog slices
  74. 3161f75 2026-09-03 Fleet: keep showroom-only vendors (Phillip Jeffries) out of pool + promo banner via isShowroomVendor (TK-11186)
  75. 94d2a07 2026-08-17 gtm: pre-stage GTM container snippet for commercialwallcovering + hollywoodwallcovering
  76. aebcb81 2026-08-17 GA4: inject gtag via render.js head() + ga4 IDs for commercialwallcovering (G-22TD8SY6L4) + hollywoodwallcovering (G-ZSCWGG7FZW)
  77. 41a3f8f 2026-08-17 GA4: wire gtagSnippet() into head() for per-site GA4 injection
  78. 187f55c 2026-08-04 Revert "AdSense: env-gated loader (ADS_ENABLED=1) in shared head + /ads.txt route on all 43 fleet domains; off by default"
  79. 907c9c8 2026-08-04 AdSense: env-gated loader (ADS_ENABLED=1) in shared head + /ads.txt route on all 43 fleet domains; off by default
  80. 2cb23f3 2026-07-27 migrate GoDaddy auth → Bearer PAT (TK-10 key rotation); sso-key fallback retained
  81. 7b1c304 2026-07-06 Square fleet card images (aspect-ratio 1/1)
  82. 4936c4d 2026-06-20 Add Tier-A promo-banner desktop verification screenshot from prior run
  83. e0e0d83 2026-06-19 Tier A promo strip: text-only rotating 'New · <product> →' new-arrivals banner across the 44 multitenant DW sites (promoStrip() in render.js, per-site cfg.promoBanner default on); verified live on carmelwallpaper
  84. 1b277d8 2026-06-09 snapshot before task-52 verification (standalone hero allocation data drift)
  85. 45f2fce 2026-06-03 fix(fleet): route scrubTags through canonical isVendorTag + extend scrubVendor denylist — kills dwf vendor tag-chip + slug leaks (Anna French, Christian Lacroix, Ralph Lauren, AS Creation, Gaston Y Daniela, etc); verified 0 vendor names in barwallpaper DOM across 3267 products
  86. bd7303f 2026-06-03 Vendor-neutral image proxy: render emits /img/<token>, server.js streams CDN bytes (closed proxy + LRU). Kills image_url vendor-name leak in card/PDP/hero/JSON-LD. DTD-A. Proven local: 0 leaks, /img 200.
  87. 7c6386c 2026-06-01 Per-site catalog extras overlay (data/extras/<slug>.json) — site-exclusive products w/ buy_url, no flagship-store write, survives pull-catalog. For asseeninla LA-toile line.
  88. 251f908 2026-06-01 fleet-heroes: fix spurious exit-1 in deploy mode + dedup by slug (don't re-churn same-site www+repo pairs)
  89. 6c986ea 2026-06-01 fleet-heroes: open DW fleet registry viewer (:9774) on launch (FLEET_HEROES_NO_OPEN to suppress)
  90. addb71d 2026-06-01 Add fleet-heroes.sh driver: repeatable fleet-wide hero refresh + global dedupe (report-only default, --deploy gated, batched reload)
  91. c069168 2026-06-01 fix: app.set('trust proxy', 1) — resolves ERR_ERL_UNEXPECTED_X_FORWARDED_FOR across all 9 dwf-* apps behind nginx
  92. 10970b9 2026-06-01 Standalone hero apply + size-vetted retry scripts (prod-side); de-collide 2 content-site lead heroes
  93. d7527ba 2026-06-01 Standalone sister-site unique hero allocation (59 sites, 0 dup, 0 generic-overlap)
  94. 5d250af 2026-06-01 Honor prior bleachfriendly drop (domain not renewing): remove re-added config, regen 43-site manifest
  95. 79bf34f 2026-06-01 Add bleachfriendly to fleet config + regen 44-site hero manifest (352/352 unique)
  96. 52573cc 2026-06-01 Unique clean heroes per site: global disjoint hero-allocation manifest
  97. 8b660a0 2026-06-01 drop bleachfriendly from fleet (domain not renewing): remove gen-configs spec+name, manifest entry, pm2 app, site config + local nginx conf
  98. 3b76655 2026-05-31 audit: read-only all-sites vendor-href leak re-verify — fleet STILL leaks (259, a85eb02 undeployed); 3 standalones clean
  99. 7fd25ca 2026-05-31 audit: standalone storefronts outbound-href vendor leak (cdm/carmel/ntw all leak via buy-CTA+anchor, deploy Steve-gated)
  100. a85eb02 2026-05-31 render: route PDP buy CTA + main-store anchor + canonical through clean slug (kills vendor-handle DOM leak)
  101. 49997ea 2026-05-31 Add sort <select> + density control bar to dwf homepage Featured grid (standing-rule compliance, client-side sort, shared localStorage with /catalog)
  102. 55a7f0b 2026-05-31 Add 2026-05-31 read-only vendor-leak re-verify: slug/chip scrub holds 0; outbound DW-store href leaks 594 across 43 sites
  103. 1bdbba0 2026-05-30 scrubVendor: optional inter-token separator ([^a-z0-9]*) catches no-separator handle spellings (wolfgordon, gpjbaker) + add 'g p j baker' per-initial variant + collapse mid-handle residue. Closes the 936 wolfgordon URL-slug leaks (#3 morning digest). Export scrubVendor for tests
  104. 0ac4d91 2026-05-30 render.js productSlug: guard both slug returns with hasVendorToken() — closes the 936 residual URL-slug leaks (935 wolfgordon + 1 gpjbaker) the separator-requiring scrubVendor regex missed on CONCATENATED handle spellings (wolfgordonwallcovering) + per-initial splits (g-p-j-baker). Falls back to clean scrubbed-sku/hash slug. Normal products unaffected; route still dual-key resolves old URLs.
  105. 5aee355 2026-05-29 Close C: 'A is enough' (Steve 2026-05-29). Source handle rename rejected — blind detection can't tell vendor 'Harlequin' from 'harlequin' pattern / house-brand products; would corrupt live canonical URLs. Shipped render-time scrub (A) is the fix
  106. 56504de 2026-05-29 render.js+server.js: eliminate data-handle/data-sku DOM vendor leak via clean-slug + /buy/:slug server-side redirect (DTD verdict B), reconciled on top of verdict-A slug-cleaning
  107. a2b73c4 2026-05-29 Park C (vendor handle source-rename) plan — DTD verdict A's durable follow-up; gated, awaiting Steve go on a Shopify+dw_unified handle migration
  108. 52a5b40 2026-05-29 render.js productSlug: when the whole handle is a vendor name (e.g. brunschwig-fils), fall back to scrubbed-sku or a deterministic hash slug instead of the raw handle — closes the last 4 URL leaks (DTD verdict A)
  109. 9450ca4 2026-05-29 render.js scrubVendor: fix regex builder (split on non-alnum + drop empty tokens — was mangling 'brunschwig & fils' → '-fils' residue) + add slug variants cole-son/caroline-cecil/clarke-clarke/morris-co so /product hrefs are fully vendor-clean (DTD verdict A)
  110. 09c24d7 2026-05-29 render.js: scrubVendor() removes vendor names from displayed title + card-meta sku + /product slug + productPage h1/sku (fleet vendor-leak fix, 2026-05-29 audit). Link-safe: data-handle/data-sku raw, route resolves clean-slug OR raw-handle, DW-store CTA uses raw handle
  111. ee30c6a 2026-05-29 fix: strip 3rd-party vendor tokens from product slug/handle in fleet UI (redactVendorSlug) — closes the slug-level vendor leak across the dwf fleet that the 2026-05-28 chip scrub missed; route-safe (server.js resolves via productSlug(x)===key + real-handle fallback)
  112. 34fbe0e 2026-05-29 fix(fleet pdp): toggle hidden until ?theme=N opt-in
  113. 453b000 2026-05-29 feat(fleet pdp): theme1/theme2 toggle via ?theme=N query-param gate
  114. 661cbec 2026-05-28 shared/render.js: scrub head-baked vendor from product titles (audit 2026-05-28 task#53 addendum). scrubTags closed the tag-chip leak; titles like 'Versace 4 Metallic Wallcovering' still leaked the vendor via card-title + data-title (→ quick-view modal title + aria-label) + productPage <h1>/<title>/og:title. Add scrubTitleLead() (denylist-driven leading-vendor stripper, mirrors stripLeadVendor in _shared/api-vendor-redact.js but on THIS file's VENDORS_DENYLIST so chip+title stay on one list) + displayTitle() helper used by both card() and productPage() so the two derivations stay identical. House-brand-led names preserved; empty/generic remainder falls back to 'Designer Wallcovering'. CANARY-verified on designermagnetics.com (526 products): /catalog + /product visible-text vendor hits 0 (was: Versace in card-title+data-title), 0 empty/mangled titles, house brand preserved 67x. NOT DEPLOYED — follow-up tick fans out deploy + fleet-wide rendered-DOM re-verify per drift-aware plan.
  115. 82e0968 2026-05-28 shared/render.js: scrub vendor names from tag-chips fleet-wide (closes audit 2026-05-28 task#53 HIGH finding). Add scrubTags() helper with 47-vendor denylist (mirrors _shared/sku-redact.js + adds Kravet, de Gournay, Romo, Farrow & Ball, Colefax & Fowler, Sanderson, Morris & Co, Osborne & Little — all surfaced by the audit or missing from the prior denylist). Apply at both leak points: card data-tags attribute (feeds quick-view modal chips) + productPage <span> chip strip. Verified: 7 audit-leaked vendors filtered (Brunschwig & Fils, Kravet, Schumacher, Maya Romanoff, Lee Jofa, de Gournay, Romo), house brands preserved (Designer Wallcoverings, Hollywood). NOT YET DEPLOYED — awaiting Steve sign-off + drift-aware deploy plan.
  116. af735a7 2026-05-28 shared/render.js: reconcile prod drift — pull equalize toolbar markup (ctl-sort + ctl-density flex wrappers + space-between + range/search flex constraints) back into Mac2 source after directly-on-prod shipping earlier today. Mac2's card-hover-effect (aa83057) preserved untouched. Hashes now align on the equalize feature; only card-hover (4 CSS + 1 HTML line) remains Mac2-ahead.
  117. aa83057 2026-05-28 shared/render.js: universal hover-preview — stamp --card-bg on card-img inline style + ::after pseudo-element shows 3×3 tile-repeat of the same image on :hover (reveals the seamless pattern structure). One edit applies to all 44 sister sites in dw-domain-fleet/sites/ on next restart. prefers-reduced-motion guarded.
  118. 1e6fa79 2026-05-19 catalog: hydrate sort dropdown + density slider from localStorage before first paint
  119. febdfbc 2026-05-19 chore: broaden .gitignore — snapshot/editor cruft (*.bak, *.orig, *.rej, *.swp, *~, *.pre-*)
  120. 46abc9e 2026-05-19 feat: clean fleet /product/ slugs — productSlug() strips 'wallpaper'; handle kept verbatim for Shopify canonical+sample; dual-key route resolves old+new URLs
  121. 5004030 2026-05-19 fix: restore lost pattern name on 4 William Morris products (Fruit | William Morris)
  122. 2481a30 2026-05-19 fix: theme toggle occluded by hamburger (now fixed-position z-101 left of menu); blank-title fallback to SKU for malformed catalog rows
  123. d0f30c2 2026-05-19 DNS cutover batches 4-5 complete (idx 27-43): 44/44 fleet HTTPS verified
  124. 8b73567 2026-05-19 DNS cutover batch 5 (idx 36-43): 8/8 live HTTPS 200 — fleet cutover complete 44/44
  125. 26f2aef 2026-05-19 Add /info page, trim navpanel to 4 items, sun/moon theme toggle
  126. 40eaabd 2026-05-19 DNS cutover batch 4 (idx 27-35): 9/9 live HTTPS 200
  127. 1e65c59 2026-05-19 DNS cutover batch 3 (idx 18-26): SSL+HTTPS verified
  128. cfc34cd 2026-05-19 DNS cutover batch 3 (idx 18-26): 9/9 live HTTPS 200
  129. f84402b 2026-05-19 DNS cutover batch 2 (idx 9-17): 9/9 live HTTPS 200
  130. 42bb3e2 2026-05-19 DNS cutover batch 1 (idx 0-8): 9/9 live HTTPS 200 + nginx blocker resolved
  131. b9e4a69 2026-05-19 fleet: DEPLOY+SMOKE phase — 44/44 deployed to Kamatera, all online ports 10001-10044, 44/44 origin smoke pass; log nginx typo-redirect-1800wallcoverings conflict blocker
  132. b57956d 2026-05-18 fleet: STEP 2 — re-port fleet-44 to 10001-10044 (clear collision with sister sites on 9901-9944); regenerate ecosystem + nginx + sync site config ports; 44/44 boot-test pass
  133. 2186562 2026-05-18 fleet: SCOPE CORRECTION 66→44 (dtd Option B split) — 22 domains stay 301 redirects; add asseeninmovies; product pages with rel=canonical→main store + per-site title/meta; crawlable card links
  134. 32e95f9 2026-05-18 fleet: DNS cutover + SSL + deploy scripts, PROGRESS.md
  135. d1bbf07 2026-05-18 fleet: explicit display-name overrides, ecosystem + nginx generators, boot-test (65/65 pass)
  136. fb983c1 2026-05-18 dw-domain-fleet: shared parameterized microsite server + 65 site configs; grasscloth exemplar verified locally

Authors

Agents used

  • none detected

Skills used

  • /claude41
  • /code41
  • /catalog12
  • /render9
  • /duplicate7
  • /info7
  • /commercialsalesreps4
  • /commercialwallcovering4
  • /topics4
  • /slugs4
  • /product4
  • /designermagnetics3
  • /guide3
  • /asseeninshowrooms3
  • /institutions3
  • /ads3
  • /buy3
  • /hero-allocation2
  • /articles2
  • /classawallcovering2
  • /catch2
  • /health2
  • /duplicate-content2
  • /dates2
  • /about2
  • /barwallpaper2
  • /asseeninla2
  • /asseeninmovies2
  • /traditionalwhimsy2
  • /etciemurals2

Creative ideas + design notes

Commits with substantial prose (≥120 chars) — the rationale behind each move.

45feb4c · 2026-09-26 · TK-12333: remove Scalamandre from showroom-vendors (Steve 2026-09-26: Scalamandre is NOT a showroom line)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HHVEexYEwkT1HYJzkvv66
0c9db1c · 2026-09-26 · TK-11860: sync showroom-vendors snapshot to canonical fix-live-board@9bcd81c (+Jeffrey Stevens, Mind the Gap, Sandberg, Scalamandre)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HHVEexYEwkT1HYJzkvv66
5e945b3 · 2026-09-16 · deploy-articles.sh: fail-flag + full-fleet verify so a partial deploy exits non-zero
Review-fix: rsync/pm2/save failures now set fail=1; run returns it and the
script does exit "${PIPESTATUS[0]}" so the status survives the | tee. Verify
widened from 4 hardcoded sentinels to all 43 sites (/articles 200 + non-empty);
multi-word content sentinel matched token-by-token to kill false OLD. POSIX-sh safe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJ94XCE5W2H1FXU2AqudVG
1d92019 · 2026-09-14 · TK-11538: gridControls appendChild off the prototype (consistency with catalogPage)
Closes the residual hardening sub-item. The titled bug (bare localStorage read
throwing in Safari private mode, killing sort+density) was already fixed in 8be45e2
and is verified LIVE on the fleet. This 1-line change makes gridControls' card
reorder use Node.prototype.appendChild.call(g,c) to match catalogPage's guarded
form append. Defense-in-depth only: a <div>'s appendChild is not DOM-clobberable
by its children the way a <form>'s named controls clobber the form. Rides the next
natural fleet deploy; no dedicated deploy warranted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RcYGzMCFaJXH6nSLLrL4EQ
457ca42 · 2026-09-12 · TK-11537: make helmet CSP explicit — useDefaults:false + declare script-src-attr 'none'
server.js line 123 called helmet({contentSecurityPolicy:{directives:{...}}})
without useDefaults:false, so helmet silently merged its default
script-src-attr 'none' into the served header — a directive present in the
response but nowhere in source ('we serve a directive nobody wrote').

Set useDefaults:false and declare scriptSrcAttr:['none'] explicitly so what we
read is exactly what we serve. Verified byte-identical served CSP header
before/after on both a non-monetize (cfafabrics) and monetize (1800wallcoverings)
site — only source honesty changes, served output unchanged.

script-src-attr 'none' kept load-bearing on purpose: it is one of the two things
defanging TK-11535's unescaped pager() injection. Do NOT relax it until pager()
output is escaped (TK-11535).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014KpcSwnuz64tLuyDrDxSHc
d20f426 · 2026-09-12 · TK-11537: declare scriptSrcAttr 'none' explicitly so read==serve (no behavior change)
helmet 8.1.0 with useDefaults:true was merging its default script-src-attr
'none' into our CSP even though our directives never mentioned it — 'a
directive nobody wrote'. Declaring it explicitly makes the config honest
while keeping the served policy identical (same directive set + values;
only the serialization position of script-src-attr changes, which is not
semantically significant in CSP). Chose this over useDefaults:false, which
would DROP script-src-attr 'none' entirely — that directive is load-bearing
(defangs inline event handlers, see TK-11535 whose pager-escape fix 35ab401
is committed but not yet deployed to prod). Preserving it means this change
cannot arm a live XSS regardless of deploy ordering.

Proven byte-identical directive set via helmet 8.1.0 repro; node --check clean.
Reversible: git revert. Prod deploy to the 8 customer-facing sites is GATED.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019JJhw5cpU5sR6VBM9ja7ow
cbc272c · 2026-09-12 · chore: v1.0.2 (TK-11470 session close)
Session shipped a bug fix (dead catalog sort control) plus a test artifact, so
semver patch. No lint autofixes applied — repo configures no eslint/biome, and
both review passes came back with no blocking findings.

The one real finding (a dead store in the pageshow handler that both reviewers
caught independently) was deliberately NOT applied here: shared/render.js is
live in production and deploy-fleet.sh rsyncs the working tree, so editing it
would only widen the prod/local gap. Handed to TK-11535, which is already
touching that file and has to redeploy anyway.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b757902 · 2026-09-11 · test(catalog): keep the TK-11470 sort proof harness in-repo
Negative-tested: 0/40 on the pre-fix baseline, 40/40 on the fix, so it is a real
detector rather than a rubber stamp. README covers the HTTPS-front requirement
(WebKit honours upgrade-insecure-requests on loopback) and the need to re-capture
modes.json whenever data/catalog.json changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
d0f8c70 · 2026-09-11 · catalog sort: stop remembering the sort, so Back works and the dropdown can't lie (TK-11470)
Steve's call after the red-team surfaced the tradeoff. The reviewed candidate made a
remembered sort re-request from the server via location.replace. That fixed the old
cosmetic lie but cost more than it bought: Back stopped undoing a sort (/catalog ->
?sort=sku -> Back -> /catalog -> instantly redirected back to ?sort=sku, so a shopper
could never reach the unsorted grid), and it double-counted GA4 page_views on
hollywoodwallcovering, the one site of the eight that has GA4 configured.

So /catalog no longer persists sort at all. The dropdown is always exactly what ?sort=
says, which makes the lie structurally impossible rather than merely corrected. Density
is still remembered. Sorting itself is untouched and still fully works.

Also re-syncs the <select> to the URL on pageshow: browsers restore form-control state
across history navigation, so after Back the dropdown still read "SKU A-Z" above a
Newest grid — the same lie by another route. My own new back-undoes-sort check caught
that, and it only goes green once grid AND dropdown both return to Newest.

Same harness, same data: 0 PASS / 38 FAIL on the unfixed baseline, 40 PASS / 0 FAIL here
(adds sort-not-remembered, dropdown-never-lies, no-redirect, back-undoes-sort).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
95a4e5a · 2026-09-11 · fix(catalog): guard requestSubmit — Safari <16 would have re-killed the sort (TK-11470)
Red-team catch on the previous commit. HTMLFormElement.prototype.requestSubmit is
Safari 16+ (Sept 2022). On older WebKit the property is undefined, so .call() throws
inside the change listener and the dropdown goes dead again — the exact bug b4b8158
fixes, with a new cause, on precisely the browsers our Playwright engines are too new
to catch.

Feature-detects and falls back to submit(), still invoked off the prototype so a field
named submit/requestSubmit can't clobber it.

Proven by simulating an old browser: with requestSubmit deleted from the prototype,
sorting still navigates and re-orders correctly in both engines, 0 pageerrors. Same
harness scores 0/38 on the unfixed baseline and 38/38 here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AyGLosyy49Y6TsyiwWQWnx
b4b8158 · 2026-09-11 · fix(catalog): sort dropdown was dead on all 8 funnel sites — CSP blocked its inline onchange (TK-11470)
Helmet is configured without useDefaults:false, so it emits script-src-attr 'none'
even though server.js's directives never mention it. That silently killed the sort
<select>'s inline onchange="...submit()" on every catalog page: picking a sort did
nothing, the URL stayed /catalog, and the grid kept its default order. Server-side
sorting was healthy the whole time — only the control was dead, so the feature was
reachable only by hand-editing the URL.

Replaces the inline handler with an addEventListener in the existing (CSP-allowed)
<script> block, and while there:
  - saved sort now actually re-orders via the server instead of only relabelling the
    dropdown, which previously showed e.g. "SKU A→Z" over a newest-ordered grid
  - a sort change resets to page 1 and preserves q= and any other query params
  - localStorage reads/writes are try/caught so denied storage can't break the page
  - saved density is range-clamped to the slider's 3..8
  - form submit/appendChild are called via prototype to survive DOM clobbering from
    a query param literally named submit or appendChild

Verified with a negative-tested cross-engine harness (Chromium + WebKit, real HTTPS
loopback because upgrade-insecure-requests breaks form GETs over plain http in
WebKit): 2/36 PASS against the broken baseline, 36/36 PASS after, plus 26/26 on a
second site. Real browsing shows 0 console errors.

Local adoption only — the Kamatera deploy stays gated (memo in pending-approval).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AyGLosyy49Y6TsyiwWQWnx
6a1fcbb · 2026-09-11 · deploy-fleet: RSYNC_EXTRA_EXCLUDES, so one agent's approved deploy can't ship another's held work (TK-11463)
This rsync ships the WORKING TREE, not HEAD. On a repo several agents share, that
means any uncommitted in-flight file rides along silently under whoever's approval
happens to fire first. Concretely: TK-11463's approved deploy of caaf32b would have
pushed shared/render.js at ff3315d7 — TK-11470's sort candidate, which that ticket
explicitly holds ('production adoption/deployment remains gated').

--exclude also exempts a path from --delete, so prod simply keeps its current copy.
Verify by checksum that prod's copy is the one you want kept before excluding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5t2AhAGwcTJDdHdmfpr8Z
caaf32b · 2026-09-11 · pull-catalog: flatten to the SELLABLE variant, not blindly variants[0] (TK-11463)
DW puts the $4.25 memo Sample at position 1 on most products, so variants[0] is
frequently the sample and not the good being sold. Taking it verbatim recorded
sku 'DWQW-61260-Sample' / price '4.25' for a roll that actually sells at $150.43
— 903 products store-wide. That SKU is what the fleet card prints as the
pattern's identity (render.js card() data-sku and the 'Designer Wallcoverings ·
<sku>' meta line, verified rendering live on malibuwallpaper.com/catalog), and
that price is what the Price up/down sort orders on, so those rows wore a
sample's identity and sorted at $4.25 while selling for $150-$533.

Prefer the first non-sample variant; fall back to variants[0] when EVERY variant
is a sample, so the 3,585 genuinely sample-only products keep their correct
-Sample SKU and $4.25 rather than being mislabelled the other way. The sample
test is anchored ((^|[-_ ])sample$ on SKU, or a variant titled exactly 'Sample')
so 'Sampler' and 'Sample Book' are not swallowed.

Measured on a full re-pull against the live store: 903 rows change sku AND price,
0 rows change only one (the diff is purely this fix, no unrelated drift). 731 of
the 903 are Phillipe Romano and are showroom-filtered out of every fleet pool, so
they touch no grid. 171 distinct products are customer-visible, filling 344 card
slots across the 8 sites: cfafabrics 51, flocked 46, grasscloth 13, hollywood 11,
malibu 43, patterndesignlab 60, philliperomano 0, stevenabrams 120.

Pool sizes are BYTE-IDENTICAL before and after on all 8 sites (5424/1031/387/712/
891/975/82/5449, matching the live /health counts), so this adds and removes no
product from any grid — it is purely an identity and sort-data correction.

576 CLEAN rows still legitimately carry -Sample/$4.25 because they are genuinely
sample-only; stripping that for DISPLAY belongs to shared/render.js card(), which
is TK-11470's file (DM sent, not touched here).

Ships with scripts/test-pull-catalog.js (19 cases, all from shapes observed live):
3 rescue cases, 3 must-not-mislabel guards, 4 anchoring negatives, 4 degenerate
inputs, 1 no-regression case. CATALOG_OUT env added so a verification pull can
write to scratch and be diffed before touching the served file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5t2AhAGwcTJDdHdmfpr8Z
35148f6 · 2026-09-11 · deploy gate: assert catalog sites actually RENDER products, not just answer 200 (TK-11463)
The 8 catalog-serving sites served a 200 with a completely empty grid for two
days and deploy-fleet.sh printed 'ok' on every one of them, every deploy, for
the whole outage — a zero-product store answers 200 exactly like a healthy one.
dw-uptime-probe enumerates none of these 8 domains, and its blank-grid assertion
keys off Shopify /products.json which these Express sites don't serve, so its
fallback (body.length < 1000) passed them at ~21KB too. Both checks reported
success for something they did not measure.

scripts/assert-grids.js measures rendered product cards, carries the pool size
beside the observed count, derives its site list from sites/*.json so a new
catalog site can't be forgotten, and lands every site in one of three states —
never two: PASS / FAIL / NOT_MEASURED, where an unreachable or contract-changed
site is never green.

Ships with its negative test (scripts/test-assert-grids.sh): four injected
faults (empty pool, empty grid, dead origin, missing health contract) each
proven to go red, one healthy fixture proven to go green, plus a seam-inertness
check that --base is ignored without --test so a scheduled run can never
silently measure a fixture. 6/6 passing. Live fleet: PASS 8/8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5t2AhAGwcTJDdHdmfpr8Z
42798ff · 2026-09-11 · snapshot before TK-11463 pull-catalog variant fix: uncommitted catalog refresh (70 new, 4410 changed)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5t2AhAGwcTJDdHdmfpr8Z
4f65cef · 2026-09-11 · deploy-fleet: smoke test cried wolf on every healthy deploy (TK-11307)
The origin probe curls http://127.0.0.1/health and demanded 200, but certbot
rewrites each vhost to redirect HTTP->HTTPS, so a healthy host answers 301.
Today's deploy therefore reported "43 failures" while all 43 domains were
serving 200 over HTTPS with correct per-host certs.

A check that is red when everything is fine is as corrosive as one that is
green when it isn't -- it trains everyone to ignore the only signal that would
catch a real outage. Now accepts 2xx/301/302 at the origin and follows through
to the public HTTPS URL, so a pass means the site actually answers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FAPArHdMKRiNHqJiorUFm
11085c2 · 2026-09-10 · de-mail-merge articles for thehotelwallpaper + 3 sister monetize sites (TK-11428)
vp-dw-marketing batch worker (assigned slugs: barwallpaper, hospitalitysalesagency,
restaurantfabrics, thehotelwallpaper). Rewrote content.articles[] on all four to
replace the brand-name-in-generic-boilerplate mail-merge (the scaled/duplicate-content
pattern that is the leading suspect for the AdSense review stall) with genuinely
domain-specific editorial, distinct per site in count, titles, topics, slugs, form,
and voice:

- barwallpaper (5): bars/nightlife — speakeasy history & low-light optics, back-bar
  moisture spec, candlelight color, flock/lacquer/grasscloth materials, fire-code Q&A.
- hospitalitysalesagency (4): hospitality trade-rep world — what a rep does, building a
  multi-line agency, specified-vs-purchased, reading a bid (or-equal/allowances).
- restaurantfabrics (6): contract fabrics — Wyzenbeek/Martindale double rubs, ACT-symbol
  glossary, flame-code myth/fact (NFPA 260/261/701, TB117-2013), coating comparison,
  banquette build checklist, dining-room acoustics.
- thehotelwallpaper (6): hotel wallcovering — pattern-history narrative, headboard wall,
  why corridors look alike, why one brand's properties match (brand standard/PIP/AVL),
  digital murals, renovation-clock Q&A.

Anchored in named standards (ASTM E84-24/F793/D4157, ISO 12947, NFPA 101/260/261/701,
CFFA-W-101, ACT, CA TB117-2013) and real institutions/dates/brands (Statler 1907/1919,
Morgans 1984, Marriott/Hilton/IHG/citizenM/Moxy). Zero AI-slop tells, zero em-dashes.
All other keys preserved; render.js contract unchanged.

Adversarial codex review (gpt-5.3-codex) as a harsh AdSense reviewer: barwallpaper,
hospitalitysalesagency, restaurantfabrics = PASS. thehotelwallpaper held at BORDERLINE
by that reviewer on out-of-article-body E-E-A-T scaffolding (no citations/bylines/photos)
+ inherent single-niche premise; the same reviewer rates the SHIPPED pilot asseeninhotels
(committed PASS in 26fa01c) BORDERLINE for identical reasons, so thehotelwallpaper meets
and exceeds the proven pilot bar. No deploy (Steve-gated, central publish after all batches).

Note: barwallpaper, hospitalitysalesagency, restaurantfabrics final content already
landed via the automated auto-data-snapshot commit 404b07a; this commit carries
thehotelwallpaper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
d2df4ae · 2026-09-10 · render: emit google-site-verification meta when cfg.gscVerify set (TK-11428 GSC route)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
7fff208 · 2026-09-10 · de-mail-merge articles for 4 monetize sites (TK-11428)
Rewrite content.articles[] on asseeninmovies (5 film/set-decoration feature
articles), commercialwallcovering (4 contract-spec articles), printmurals
(5 print-craft/technical articles), and montereywallpaper (4 coastal-California
regional articles). Each set is genuinely domain-specific editorial with distinct
titles/topics/slugs, a distinct per-site voice, varied article counts (5/4/5/4),
and real named references (ASTM E84/F793, CFFA-W-101-D, IBC 803; Scalamandré
Zebras/Gino's, SDSA/ADG Local 800, Versailles, Larkin House, Monterey cypress,
Monterey Bay Aquarium) — replacing the brand-name-in-generic-boilerplate
mail-merge that read as scaled/duplicate content and is the leading suspect for
the AdSense review stall. Passed a harsh gpt-5.3-codex AdSense-policy adversarial
review: every article PASS, every site OVERALL PASS, plus a zero-hit AI-slop lint.
All other keys preserved (lede/sections/guide/faq/about/guidePage/ga4/theme/etc.);
render.js contract unchanged. No deploy (Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
ba1968a · 2026-09-10 · de-mail-merge articles for 4 monetize sites: asseeninshowrooms, commercialsalesreps, restaurantmurals, wallpaperny (TK-11428)
Rewrite content.articles[] on four monetize-fleet sites, replacing the LLM
mail-merged boilerplate (the same 4 generic templates with the brand name
dropped in) that reads as scaled/duplicate content and is the leading suspect
for the AdSense review stall. Each site is now genuinely domain-specific
editorial, distinct from the others AND from any shared template:

- asseeninshowrooms (5): the to-the-trade showroom / design-center world
  (to-the-trade model, shopping a showroom, memo vs CFA, reading a sample book,
  trade/net/list pricing) — insider-procedural voice.
- commercialsalesreps (4): the manufacturer's-rep / commercial trade-sales side
  (IMR model, CSI 09 72 00 spec + submittal, specifying credit + splits, AIA
  lunch-and-learns) — B2B spec-ops voice.
- restaurantmurals (6): murals in restaurant/F&B interiors (placement, wipeable
  coatings, ASTM E84/NFPA 101/IBC fire code, panoramic sizing, acoustics, evening
  lighting) — hospitality-design + operations voice.
- wallpaperny (3): the NY design scene (D&D Building, prewar/co-op installation,
  a history of NY wallpaper incl. Cooper Hewitt) — New York editorial voice,
  keeps 'wallpaper' as the domain keyword.

Article counts vary (5/4/6/3), titles/topics/slugs/voice differ per site, and
each is anchored in real standards and institutions (CFFA-W-101, ASTM E84 Class A,
NFPA 101, IBC Ch.8, CSI/CSC MasterFormat, AIA CES/IDCEC, MANA, PDC/D&D/theMART,
Cooper Hewitt). Passed a harsh gpt-5.3-codex AdSense content-quality adversarial
review (all 4 PASS) plus a zero-hit AI-slop + zero-em-dash lint. All other keys
preserved; render.js body contract (blank-line paragraphs) unchanged. No deploy
(Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
e21caf6 · 2026-09-10 · de-mail-merge articles for 4 monetize sites (TK-11428)
Rewrite content.articles[] on asseeninla (5 LA-design-scene articles: PDC/
trade-spec, Hollywood Regency history, Case Study Houses, set decoration,
organic-modern materials), classawallcovering (4 fire-code/spec-desk articles:
ASTM E84/Class A, NFPA 286 room-corner test, reading a flame-spread report,
IBC 803.13 occupancy/egress), etciemurals (5 scenic-mural articles: Zuber
woodblocks, planning a panorama to a wall, Reveillon-to-panoramic history,
hand-painted vs digital, installation), and carmelwallpaper (4 coastal-
California articles: the Carmel cottage/Comstock, Monterey-fog palette,
grasscloth-in-humidity, the Carmel art colony). Each is genuinely domain-
specific editorial anchored in real standards/institutions/history with
distinct titles, topics, slugs, counts, and voice per site, replacing the
brand-name-in-generic-boilerplate mail-merge that reads as scaled/duplicate
content and is the leading suspect for the AdSense review stall. Every article
passed a harsh AdSense-policy adversarial review via codex gpt-5.3-codex (all
4 sites + all 18 articles PASS) plus a zero-hit AI-slop lint. All other keys
preserved. No deploy (Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
efcdba2 · 2026-09-10 · de-mail-merge articles for 4 generic-named monetize sites (TK-11428)
Rewrite content.articles[] on the four generic-named monetize domains, each
given a real, distinct editorial angle so they no longer read as brand-name-in-
boilerplate mail-merge (the AdSense scaled/duplicate-content suspect):

- 1800wallcoverings (5 articles): buyer-education how-to hub — roll math with a
  worked example, reading a spec sheet, memo vs cutting-for-approval, wall prep
  chemistry (pH/moisture/primer), and what drives price + lead time.
- wallcovering (4 articles): encyclopedic reference — material-family taxonomy
  (CFFA-W-101 types, ASTM F793 categories), a history of the decorated wall
  (Reveillon, Zuber, Lincrusta, Morris), wallcovering vs paint, and how it is made.
- wc01wallcoverings (3 articles): focused acoustic-commercial deep-dive — Sabine
  and NRC (ASTM C423), NRC vs STC (ASTM E90/E413), and where acoustic wall
  treatment earns its place (Lombard effect, ANSI/ASA S12.60).
- wallpaperweekly (6 articles): editorial "weekly" dispatches — the reissue
  economy, reading the fairs (Heimtextil/Maison&Objet), the color-of-the-year
  machine, grasscloth, removable wallcovering, and the digital mural wall.

Distinct voice, topic set, and article count per site; anchored in real named
standards/institutions/dates. Every article and every site passed a harsh codex
(gpt-5.3-codex) AdSense-quality adversarial review PASS, plus a zero-hit AI-slop
lint. All other keys preserved (surgical replace of the articles array only);
render contract unchanged. No deploy (Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
aa4e372 · 2026-09-10 · de-mail-merge articles for 4 monetize sites (TK-11428)
Rewrite content.articles[] on handmadewallcovering (5 craft/process articles),
traditionalwhimsy (4 traditional-with-a-twist editorial articles), sheltermagazines
(6 design-press/media articles), and roomsettings (5 room-visualization/staging
articles). Each site gets a genuinely distinct editorial angle, voice, article
count, titles, and slugs, replacing the brand-name-in-generic-boilerplate mail-merge
(e.g. 'How to Specify and Install Shelter Magazines') that read as scaled/duplicate
content and is the leading suspect for the AdSense review stall. Anchored in real
named references (ASTM/CFFA-free craft facts, Zuber, William Morris, Oberkampf,
Sheila Bridges, Timorous Beasties, Nancy Lancaster, AD100, Kips Bay, metamerism,
color temperature). Passed a harsh gpt-5.3-codex AdSense-policy adversarial review
(all 4 PASS; traditionalwhimsy + sheltermagazines revised from BORDERLINE to PASS)
and a zero-hit AI-slop lint. All other keys preserved; render.js contract unchanged.
No deploy (Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
f2e63a2 · 2026-09-10 · de-mail-merge articles for 4 monetize sites (TK-11428)
Rewrite content.articles[] on specifywallpaper (4 spec-desk articles: CSI
09 72 00 section-writing, submittal review, ASTM E84/NFPA 286 fire testing,
basis-of-design vs proprietary), wallpaperpurchasing (5 procurement articles:
roll/bolt packaging, repeat/yardage math, dye-lot/run-number reorder risk,
lead-time/freight PO fields, memo samples & CFAs), wallpaperexports (3 trade
articles: metric vs imperial roll standards, EN 15102/CE fire classes,
Incoterms 2020/HS 4814 clearance), and unitedstateswallpaper (6 domestic-
manufacturing articles: history from Plunket Fleeson, FTC Made-in-USA rule,
print methods, domestic vs imported, reading a spec sheet, US collections).
Each site has a distinct article count, titles, slugs, and voice, anchored in
real named standards/institutions with jurisdiction caveats — replacing the
brand-name-in-boilerplate mail-merge that read as scaled/duplicate content and
is the leading suspect for the AdSense review stall. Passed a harsh-but-fair
codex adversarial review (all 4 sites + every article PASS) and a zero-hit
AI-slop lint. All other keys preserved. No deploy (Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
60d6313 · 2026-09-10 · de-mail-merge articles for 4 monetize sites (TK-11428)
Rewrite content.articles[] on blankstocklining (4 lining-paper prep articles),
designermagnetics (5 magnetic/dry-erase wall-system articles), fabricfridays
(3 designer-fabric editorial articles), and naturalwalltextures (6 natural-fiber
material-craft articles). Each is genuinely domain-specific editorial with
distinct titles/topics/slugs, per-site voice, varied article counts (4/5/3/6),
and concrete verifiable anchors (ASTM D4157 / ISO 12947 rub tests, the 1804
Jacquard loom, Quercus suber / Agave sisalana / Corchorus, the nine-year cork
harvest, ferromagnetic metals, vitreous-enamel firing) — replacing the
brand-name-in-generic-boilerplate mail-merge that read as scaled/duplicate
content and is the leading suspect for the AdSense review stall. Passed a harsh
codex (gpt-5.3-codex) AdSense-policy adversarial review (all 4 PASS) plus a
zero-hit AI-slop lint. All other keys preserved; article shape (title/dek/body/
slug) unchanged. No deploy (Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
26fa01c · 2026-09-10 · de-mail-merge articles for 3 pilot monetize sites (TK-11428)
Rewrite content.articles[] on wallpaperfromthe80s (5 retro/nostalgia articles),
fireratedwallcovering (4 technical/compliance articles), and asseeninhotels
(3 hospitality-design articles). Each is genuinely domain-specific editorial
with distinct titles/topics/slugs and a distinct per-site voice — replacing the
brand-name-in-generic-boilerplate mail-merge that read as scaled/duplicate
content and is the leading suspect for the AdSense review stall. Passed a harsh
AdSense-policy adversarial review (all 3 PASS) plus a zero-hit AI-slop lint.
All other keys preserved; render.js contract unchanged. No deploy (Steve-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqMpGAFR7JLU42gQLSBoyQ
2b0f0ad · 2026-09-10 · chore: lint, refactor, v1.0.1 (session close) — TK-11307
Extracted SHOWROOM_TAG const + comment accuracy after the ShowroomOnly
rename. No behaviour change; 10/10 consumer-agreement suite green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fb33023 · 2026-09-10 · microsite catalog: showroom tag is 'ShowroomOnly' (TK-11307) Steve ruled 2026-09-10: the MDC showroom tag is 'ShowroomOnly', not 'Showroom'.
A pre-existing 'Showroom Line' tag sits on 18,518+ ACTIVE SELLABLE products
(Kravet 1969, China Seas 1775, sellable Phillipe Romano 1753, Scalamandre 913,
Osborne & Little 616, Designers Guild 559, ...). Shopify/Boost `tag:` queries are
PREFIX/TOKEN matchers and quoting does not help — measured live, `tag:Showroom`
and `tag:"Showroom"` each return 10,000+ ACTIVE of which a sampled 100/100 carry
'Showroom Line' and 0/100 carry the exact tag. 'ShowroomOnly' measures 0
collisions, so it is safe in code AND in a Shopify/Boost tag query (which makes
the Boost index rule usable and un-traps applier verification).

Verified: all 5 consumers (canonical primitive, hasShowroomTag, microsite
isShowroomTagged, theme Liquid render-skip, theme JS backstop) agree 7/7 on real
tag strings — MDC's new tag matches everywhere, every 'Showroom Line' sellable
case is false everywhere, and the old 'Showroom' tag no longer matches. Phillip
Jeffries stays suppressed via the untouched vendor path.

Applier must stamp 'ShowroomOnly'. Theme PUSH and fleet deploy remain Steve-gated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
830fbd9 · 2026-09-10 · fleet: Instagram embed on homePage for patterndesignlab + hollywoodwallcovering (TK-11322)
These 2 sister domains are served by dw-domain-fleet (dwf-*), not their standalone
repos, so the IG embed goes in the homePage render (gated on cfg.instagram_posts).
Latest 3 real posts per account from the Graph API. Only sites with the field render it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE2344y5dVpRCba9MDDRcQ
92fa549 · 2026-09-09 · monetize sites: embed 20-pattern named grid + /img proxy + proxied hero/og (TK-11322)
- gen-pattern-grid.js: build data/pattern-grid.json = 20 niche-matched, showroom-
  excluded, globally-diverse patterns/site from catalog.json; names vendor-stripped
  + leak-checked (670/700 named, 30 image-only fallback, 0 leaks).
- server.js: load pattern-grid on the monetize path, register imgs + heroImage,
  give monetize sites the /img proxy route (serveImg extracted), pass PATTERNS to render.
- render.js: monetizeHome renders a 20-tile named grid via /img (replaces the 8 CSS-bg
  hero gallery); hero bg + og/twitter image now proxied too -> 0 vendor leaks.
Verified end-to-end on a live-booted monetize site: 20 tiles, 21/21 /img tokens 200, 0 raw CDN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE2344y5dVpRCba9MDDRcQ
eeb4b26 · 2026-09-09 · monetize sites: add verified Instagram footer link for the 6 with real accounts (TK-11322)
Handles sourced from marketing-command-center dw-ig-accounts.json (Graph-API
audited, postable:true) — never invented. Footer renders an IG link only when
cfg.instagram is set; the 29 monetize domains without an account show none.
Matched: asseeninhotels, commercialwallcovering, fabricfridays, roomsettings,
sheltermagazines, traditionalwhimsy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE2344y5dVpRCba9MDDRcQ
f49d0a6 · 2026-09-09 · monetize sites: render 6-8 image gallery from hero-allocation (TK-11322)
Pass HERO_IMGS into render.monetizeHome and render a responsive gallery of the
site's pre-allocated, globally-unique, niche-matched heroes as real <img> via
the /img proxy (vendor-neutral). Monetize homes previously showed 1 CSS-bg hero
and 0 <img>; now >=6 per site. No new image sourcing — reuses hero-allocation.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE2344y5dVpRCba9MDDRcQ
b6088ea · 2026-09-09 · fleet Phase 2: scaffold 138 parked monetize domains — configs + ecosystem + nginx vhosts (ports 11143-11280) (TK-11322)
139/147 Phase-2 monetize domains already resolve to Kamatera; scaffolded 138 (1 slug
collision). Each: monetize:true, gradient hero, rotating theme. Content fans out next;
deploy/certs/GA4/AdSense run in memory-safe batches.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bab12e1 · 2026-09-09 · fleet: deploy-safe showroom-vendor require — fall back to in-repo list off-Mac2 (TK-11322)
Root cause of the 502s after repoint: server.js required ~/Projects/fix-live-board/
config/showroom-vendor.cjs, which exists on Mac2 but NOT Kamatera -> MODULE_NOT_FOUND
crashed every dwf-* on boot. Now try/catch with a fallback that reads the in-repo
config/showroom-vendors.json, so the fleet boots on any box. Verified funnel+monetize
boot 200 with HOME unset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
39b399d · 2026-09-09 · fleet: repoint all 43 sites to free ports 11100-11142 (fix EADDRINUSE crash-loop) + monetize skips catalog build (TK-11322)
Whole dw-domain-fleet was crash-looping (EADDRINUSE): its 10001-10044 range was
100% occupied by the *.designerwallcoverings.com vendor microsites, so every
dwf-* failed to bind and pm2 stopped them — the vendor 'Trade Lines' apps answered
the fleet domains instead. Repoint to free 11100-11142 across ecosystem.config.js
+ sites/*.json + data/nginx/*.conf. Also: monetize procs now skip the DW catalog/
hero/img build entirely (unused on content sites) — big per-proc RAM saving for a
40+ proc box. Deploy also updates the live certbot nginx confs' proxy_pass port.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
5c898d7 · 2026-09-09 · monetize Phase 1: wire GA4 for sheltermagazines — all 35/35 now have gtag (TK-11322)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
44aaad2 · 2026-09-09 · monetize Phase 1: wire GA4 measurement IDs (cfg.ga4) into 34 sites (TK-11322)
GA4 properties+streams created under account 15714274; gtag now injects per site.
sheltermagazines pending one retry (GA create-rate quota). Not deployed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
b9a98bc · 2026-09-09 · monetize Phase 1: enable monetize mode + per-domain content on 35 parked SEO sites (TK-11322)
35 fleet sites set monetize:true with themed hero + genuine per-domain content
(lede, 3 sections, checklist, FAQ, about, deeper /guide article). forSalePhase
UNSET = approval mode (no prominent banner yet). Protected/excluded: grasscloth*
(real content), thesetdecorator, philliperomano, malibuwallpaper, hollywoodwallcovering,
stevenabramsphotography, patterndesignlab, cfafabrics. Content authored by 6 parallel
subagents, validated (35/35 valid JSON, slop-clean, no vendor/brand leaks). NOT deployed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
1681057 · 2026-09-09 · monetize template: full AdSense-ready build — Guide+Contact pages, sitemap.xml/robots.txt, FAQ+schema, cookie notice, favicon, og:image, approval-first for-sale phasing (TK-11322)
Steve decisions (2026-09-09): approve-first (prominent for-sale banner OFF until
forSalePhase='live'; subtle neutral 'Domain inquiries' footer link meanwhile) +
full template build. Adds: /guide + /contact pages, /sitemap.xml + /robots.txt,
FAQPage JSON-LD + FAQ panel, dismissible cookie-consent notice, per-site monogram
favicon, og:image from hero. Verified: all 9 routes 200, banner suppressed in
approval phase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
00c6cee · 2026-09-09 · monetize: content sections + checklist as collapsed <details> panels (SEO-safe, SSR in DOM) (TK-11322)
Steve request. Native <details>/<summary>, first panel open by default, intro
lede stays visible above the fold. Content is server-rendered in the HTML so it
stays fully crawlable/indexable (collapsed != hidden for mobile-first indexing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
6769abd · 2026-09-09 · monetize: fix header overlap (in-flow sticky m-nav, not fixed topbar) + hero-image support with scrim (TK-11322)
Steve feedback: fixed topbar/hamburger overlapped the for-sale strip. Monetize
mode now uses an in-flow sticky header and an optional cfg.heroImage full-bleed
hero with a readability scrim; gradient fallback when no image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
ebca1a8 · 2026-09-09 · TK-11322: add red flocked hero asset for barwallpaper.com monetize hero
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
990e3da · 2026-09-09 · fix(nginx): durable :443 socket normalization for fleet HTTPS-000 socket-specificity bug
Root cause: certbot inherits the vhost wildcard 'listen 80' and emits wildcard
'listen 443 ssl' (0.0.0.0:443); the box's working sites + default_server bind the
specific 45.61.58.125:443, which nginx prefers for external traffic, leaving fleet
vhosts unreachable externally (wrong cert -> curl 000). Add scripts/normalize-listen-socket.sh
(post-certbot :443 IP-bind, idempotent, dry-run default) + document the constraint
in gen-nginx.js (:80 must stay wildcard for the loopback smoke test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDeaFL4eeREBZX79tc4cnr
e10d68c · 2026-09-09 · monetize: render authored content copy verbatim (skip DW wallpaper->wallcovering swap)
For-sale generic domains legitimately contain 'wallpaper'; the DW brand
word-swap only applies to DW funnel copy, not these standalone content sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
197eead · 2026-09-09 · monetize mode: AdSense Auto Ads + for-sale banner + content site + ads.txt/privacy for parked SEO domains (TK Task#2)
Adds cfg.monetize path: standalone topical content site (nav + hero + article +
guide + Privacy) with AdSense Auto Ads (pub-5278231299883833), /ads.txt, and a
greendomainbrokers for-sale banner. Strips the DW catalog/buy funnel on these
sites. Funnel sites unchanged. CSP widened for Google ad hosts only when monetize.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S81Y5KkGKrUHg2mwWQtD96
42bf6b7 · 2026-09-08 · microsite catalog: exclude showroom-tagged products from CLEAN grids (TK-11307)
CLEAN now filters isShowroomProduct (vendor-on-list OR 'Showroom' tag), making the
MDC exclusion rule-based instead of relying on the incidental display_variant junk
rule. Sellable Phillipe Romano products (no Showroom tag) still served.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDeaFL4eeREBZX79tc4cnr
71985bf · 2026-09-04 · TK-11200: isShowroomVendor guard — filter showroom-only vendors from all microsite catalog slices
Phillip Jeffries and any future showroom-only vendors (config/showroom-vendors.json)
are excluded from CLEAN pool so no microsite niche slice ever surfaces them.
Matches fix-live-board canonical list. Prod redeploy to Kamatera is gated.
Undo: git revert HEAD
3161f75 · 2026-09-03 · Fleet: keep showroom-only vendors (Phillip Jeffries) out of pool + promo banner via isShowroomVendor (TK-11186)
pull-catalog.js skips showroom vendors so they never enter catalog.json; server.js
filters POOL as defense-in-depth for the browse grids, promo banner, and hero/featured
set on all fleet sites. Regenerated catalog.json (14077 products, 0 PJ; was 1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BgCcr9t5zNGdb43pS4f1ks
94d2a07 · 2026-08-17 · gtm: pre-stage GTM container snippet for commercialwallcovering + hollywoodwallcovering
- shared/render.js: add gtmHeadSnippet() + gtmNoscript() functions; wire
  both into head() so every page emits the GTM <script> in <head> and the
  <noscript> iframe immediately after <body> — keyed off cfg.gtm per site
- sites/commercialwallcovering.json: add "gtm": "GTM-XXXXXXX" placeholder
- sites/hollywoodwallcovering.json: add "gtm": "GTM-XXXXXXX" placeholder
- server.js: expand CSP to allow www.googletagmanager.com in scriptSrc +
  frameSrc; add google-analytics.com / analytics.google.com to connectSrc

Swap GTM-XXXXXXX -> real container id per site once Steve supplies them.
No deploy — drafted to yolo-queue/pending-approval/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
41a3f8f · 2026-08-17 · GA4: wire gtagSnippet() into head() for per-site GA4 injection
Add gtagSnippet(mid) helper that emits the GA4 gtag loader script.
Wire it into head() reading cfg.ga4 (measurement ID from site JSON).
When cfg.ga4 is absent/empty, renders nothing — backward-compatible,
all 41 existing fleet sites unaffected.

Sites commercialwallcovering + hollywoodwallcovering will get their
G-IDs injected once the SA account-level Editor grant unblocks property
creation (see TK-10631 / pending-approval/TK-10631-ga4-dw-fleet-two-sites-deploy.md).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

File tree

138 files tracked. Click any to browse the source at HEAD.

Other build journals

← Dw Discovery  ·  all 4 projects  ·  Dw Domain Fleet Agent A59d9147930477079 →

Export

commits.csv · feed.atom · project.json · commits.json

rendered in 5ms