← back to Yolo Agent
untrack logs + done-task archive (approved cleanup)
8358fc1b898ef6e7489e65c143b32a21e620934b · 2026-06-09 13:05:25 -0700 · SteveStudio2
Files touched
M .gitignoreD logs/yolo-stderr.logD logs/yolo-stdout.logD tasks/done/00_1838-final-cleanup.mdD tasks/done/00_FREEZE-no-new-vendors.mdD tasks/done/00_URGENT-collection-menus.mdD tasks/done/00_URGENT-fix-hero-banner-collection.mdD tasks/done/00_URGENT-phillipe-romano-fullmonty.mdD tasks/done/00_URGENT-scraper-blitz.mdD tasks/done/00_URGENT-update-dw-exclusive-page.mdD tasks/done/00_anna-french-full-monte.mdD tasks/done/00_archive-gb-europe-dupes.mdD tasks/done/00_build-stock-check-agent.mdD tasks/done/00_clean-body-specs.mdD tasks/done/00_cmd54-west-coast-rename.mdD tasks/done/00_create-79-missing-collections.mdD tasks/done/00_create-hermosa-beach-impact.mdD tasks/done/00_crop-grs-images-to-square.mdD tasks/done/00_dwjs-enrich-specs.mdD tasks/done/00_dwpr-remove-specs-from-body.mdD tasks/done/00_enrich-thibaut-af.mdD tasks/done/00_fix-fabrics-remove-trim-new-arrivals.mdD tasks/done/00_fix-ledger-modal-flash.mdD tasks/done/00_fix-malibu-body-dedup.mdD tasks/done/00_fix-thibaut-af-fabrics.mdD tasks/done/00_fix-thibaut-titles-vendor.mdD tasks/done/00_full-monte-phase3-all.mdD tasks/done/00_hex-color-gemini-blitz.mdD tasks/done/00_norma-news-refresh-button.mdD tasks/done/00_overnight-status-watch.mdD tasks/done/00_push-repeat-metafields-all-vendors.mdD tasks/done/00_schumacher-logo-cleanup-finish_DONE-orchestrator-died-work-succeeded.mdD tasks/done/00_set-pattern-names.mdD tasks/done/00_stock-check-agent.mdD tasks/done/00a_brewster-line-manager.mdD tasks/done/00a_maya-room-settings.mdD tasks/done/00b_designtex-scraper-fix.mdD tasks/done/00b_harvest-dead-links.mdD tasks/done/00c_logo-hunter-summary.mdD tasks/done/01_01_karin-room-settings.mdD tasks/done/01_01_pm2-health-check.mdD tasks/done/01_01_silas-violations-report.mdD tasks/done/01_01_verify-archive-bodyhtml.mdD tasks/done/01_02_blog-homepage-refinement.mdD tasks/done/01_1838-cleanup-and-enrich.mdD tasks/done/01_ai-enrich-romo.mdD tasks/done/01_apex-finish-build.mdD tasks/done/01_arte-image-enrichment.mdD tasks/done/01_audit-and-fix-empty-collections.mdD tasks/done/01_blog-post.mdD tasks/done/01_blog-social-session112.mdD tasks/done/01_china-seas-width-repeat-update.mdD tasks/done/01_cleanup-37-empty-collections.mdD tasks/done/01_dexter-full-scan-cleanup.mdD tasks/done/01_enrich-prl-direct.mdD tasks/done/01_farrow-ball-specs-extract.mdD tasks/done/01_fix-phillipe-romano-vendors.mdD tasks/done/01_fix-vendor-name-duplicates.mdD tasks/done/01_health-check-restart.mdD tasks/done/01_health-check.mdD tasks/done/01_hire-page-black-redesign.mdD tasks/done/01_hire-page-overnight-refinement.mdD tasks/done/01_hollywood-color-name-cleanup.mdD tasks/done/01_hollywood-fix-color-names.mdD tasks/done/01_hollywood-width-backfill-from-gdrive.mdD tasks/done/01_interior-design-tags-all-vendors.mdD tasks/done/01_jd-fix-broken-images.mdD tasks/done/01_lod-line-followup.mdD tasks/done/01_monitor-crawls.mdD tasks/done/01_monitor-running-processes.mdD tasks/done/01_newmor-full-monty-test.mdD tasks/done/01_newmor-rescrape-widths.mdD tasks/done/01_norma-build-verify.mdD tasks/done/01_norma_email_center_demo_video.mdD tasks/done/01_purge-blog-duplicates.mdD tasks/done/01_push-fire-ratings.mdD tasks/done/01_push-versace-vi-shopify.mdD tasks/done/01_ralphy-boy-continue.mdD tasks/done/01_raoul-to-anna-french.mdD tasks/done/01_resume-pointe-updater.mdD tasks/done/01_resume-weight-fix.mdD tasks/done/01_small-vendor-image-sweep.mdD tasks/done/01_vendor-width-audit.mdD tasks/done/01_verify-dual-docs.mdD tasks/done/01_verify-kamism-spins.mdD tasks/done/02_02_enrich-coordonne.mdD tasks/done/02_02_fix-room-setting-images.mdD tasks/done/02_02_hollywood-unmatched-skus.mdD tasks/done/02_02_investigate-unmatched-patterns.mdD tasks/done/02_02_norma-git-commit.mdD tasks/done/02_02_vendor-catalog-audit.mdD tasks/done/02_02_versa-remaining-push.mdD tasks/done/02_100pct-readiness-push.mdD tasks/done/02_activate-versace-drafts.mdD tasks/done/02_ai-enrich-villa-nova.mdD tasks/done/02_archive-ncf-fabrics.mdD tasks/done/02_bespoke-width-inference.mdD tasks/done/02_blog-post.mdD tasks/done/02_build-as-creation-agent.mdD tasks/done/02_check-image-scrapers-complete.mdD tasks/done/02_china-seas-room-settings.mdD tasks/done/02_enrich-remaining-gaps.mdD tasks/done/02_fix-missing-sample-variants.mdD tasks/done/02_fix-out-of-range-skus.mdD tasks/done/02_full-monte-enrichment.mdD tasks/done/02_full-store-collection-audit.mdD tasks/done/02_git-backup.mdD tasks/done/02_hollywood-fix-fire-rating.mdD tasks/done/02_hollywood-shopify-orphan-reconcile.mdD tasks/done/02_jd-room-settings.mdD tasks/done/02_low-image-vendors-enrich.mdD tasks/done/02_newmor-brands-page.mdD tasks/done/02_newmor-bulk-phase3-ai.mdD tasks/done/02_norma-git-commit.mdD tasks/done/02_norma_email_analyzer_demo_video.mdD tasks/done/02_p2-09-vendor-standardization.mdD tasks/done/02_push-color-metafields.mdD tasks/done/02_rewrite-blogs-professional.mdD tasks/done/02_scraper-status.mdD tasks/done/02_system-health-check.mdD tasks/done/02_update-vcc-spec-standards.mdD tasks/done/02_verify-archive-duplicates.mdD tasks/done/02_versa-william-morris-enrich.mdD tasks/done/03_03_data-quality-audit.mdD tasks/done/03_03_enrich-kravet.mdD tasks/done/03_03_gls-tag-cleanup.mdD tasks/done/03_03_reseller-vendor-analysis.mdD tasks/done/03_03_server-health-check.mdD tasks/done/03_03_shopify-product-count.mdD tasks/done/03_ai-enrich-mark-alexander.mdD tasks/done/03_blog-post.mdD tasks/done/03_build-html-video-pipeline.mdD tasks/done/03_build-scraper-nobilis.mdD tasks/done/03_china-seas-metafields-push.mdD tasks/done/03_china-seas-width-fix.mdD tasks/done/03_db-maintenance.mdD tasks/done/03_fix-44-pricing.mdD tasks/done/03_fix-remaining-11-images.mdD tasks/done/03_fix-spec-gaps.mdD tasks/done/03_git-commit.mdD tasks/done/03_harlequin-post-scrape-enrich.mdD tasks/done/03_hollywood-scrape-tower-pricing.mdD tasks/done/03_jd-shopify-push.mdD tasks/done/03_launch-supervisor-audit-sweep.mdD tasks/done/03_newmor-full-monty-test-1.mdD tasks/done/03_orphan-product-audit.mdD tasks/done/03_p2-06-interior-design-tagger.mdD tasks/done/03_phillip-jeffries-specs-only.mdD tasks/done/03_phillipe-romano-shopify-push.mdD tasks/done/03_pj-browse-hidden-audit.mdD tasks/done/03_pm2-health-check.mdD tasks/done/03_pm2-health-zombie-cleanup.mdD tasks/done/03_private-label-vendor-audit.mdD tasks/done/03_push-hollywood.mdD tasks/done/03_run-http-scrapers.mdD tasks/done/03_server-health-check.mdD tasks/done/03_vcc-sync-and-report.mdD tasks/done/03_zoffany-enrich.mdD tasks/done/04_04_dead-agent-restart.mdD tasks/done/04_04_enrich-scalamandre.mdD tasks/done/04_04_shopify-product-audit.mdD tasks/done/04_04_shopify-product-quality.mdD tasks/done/04_04_thibaut-discontinued-archive.mdD tasks/done/04_04_vcc-stats-refresh.mdD tasks/done/04_add-auth-to-all-open-services.mdD tasks/done/04_ai-enrich-arte.mdD tasks/done/04_blog-post-overnight.mdD tasks/done/04_brewster-specs-fix-and-push.mdD tasks/done/04_build-scraper-pierre-frey.mdD tasks/done/04_catalog-audit.mdD tasks/done/04_china-seas-hex-colors.mdD tasks/done/04_china-seas-quadrille-retry.mdD tasks/done/04_enrichment-status-report.mdD tasks/done/04_enrichment-tracking-progress.mdD tasks/done/04_gemini-texture-classify-17k.mdD tasks/done/04_malibu-width-inference.mdD tasks/done/04_overall-stats-email.mdD tasks/done/04_pm2-health-check.mdD tasks/done/04_pm2-health-cleanup.mdD tasks/done/04_push-ascreation.mdD tasks/done/04_push-enrichment-to-shopify.mdD tasks/done/04_push-mf-york.mdD tasks/done/04_push-specs-pj.mdD tasks/done/04_recrawl-low-spec-vendors.mdD tasks/done/04_run-all-vendor-crawls-high-to-low.mdD tasks/done/04_scrape-repeats-thibaut.mdD tasks/done/04_session-blog-social.mdD tasks/done/04_shop-by-color-page.mdD tasks/done/04_shopify-body-html-cleanup.mdD tasks/done/04_shopify-product-audit.mdD tasks/done/04_vcc-dashboard-fix.mdD tasks/done/04_verify-body-html-patches.mdD tasks/done/05_05_disk-cleanup.mdD tasks/done/05_05_dupenny-sancar-push-check.mdD tasks/done/05_05_enrich-schumacher.mdD tasks/done/05_05_tag-enrichment.mdD tasks/done/05_05_thibaut-monitor.mdD tasks/done/05_ai-enrich-remaining.mdD tasks/done/05_blog-recently-viewed-feature.mdD tasks/done/05_build-scraper-zoffany.mdD tasks/done/05_check-running-scrapers.mdD tasks/done/05_china-seas-interior-tagger.mdD tasks/done/05_collection-image-refresh.mdD tasks/done/05_crawl-report-for-steve.mdD tasks/done/05_db-maintenance.mdD tasks/done/05_enrichment-gap-report.mdD tasks/done/05_final-audit-report.mdD tasks/done/05_git-commit-session107.mdD tasks/done/05_grahambrown-puppeteer-width.mdD tasks/done/05_no-image-product-audit.mdD tasks/done/05_push-color-metafields.mdD tasks/done/05_push-specs-york-contract.mdD tasks/done/05_push-wolfgordon.mdD tasks/done/05_roberto-cavalli-enrich.mdD tasks/done/05_sales-channel-audit.mdD tasks/done/05_schumacher-osborne-enrich.mdD tasks/done/05_social-posts.mdD tasks/done/05_thibaut-discontinued-archive.mdD tasks/done/05_thibaut-scrape-monitor.mdD tasks/done/05_upload-youtube-unlist-old.mdD tasks/done/05_vcc-dashboard-health.mdD tasks/done/06_06_brands-page-pills-fix.mdD tasks/done/06_06_brewster-specs-fix.mdD tasks/done/06_06_enrich-thibaut.mdD tasks/done/06_06_vcc-sync.mdD tasks/done/06_build-scraper-donghia.mdD tasks/done/06_china-seas-verify-all.mdD tasks/done/06_collections-cleanup-phase1.mdD tasks/done/06_db-maintenance.mdD tasks/done/06_enrich-ascreation.mdD tasks/done/06_enrich-rl-width.mdD tasks/done/06_git-backup-push.mdD tasks/done/06_git-backup.mdD tasks/done/06_hollywood-gemini-hex.mdD tasks/done/06_hollywood-hex-extraction.mdD tasks/done/06_lincrusta-width-enrichment.mdD tasks/done/06_missing-images-audit.mdD tasks/done/06_push-command54.mdD tasks/done/06_push-mf-scalamandre.mdD tasks/done/06_rebuild-astro-site.mdD tasks/done/06_vcc-sync-all.mdD tasks/done/06_wallquest-completion.mdD tasks/done/06_zoffany-shopify-prep.mdD tasks/done/07_07_dw-exclusive-page-update.mdD tasks/done/07_07_enrich-designtex.mdD tasks/done/07_07_hollywood-hex-cleanup.mdD tasks/done/07_07_thibaut-discontinued-archive.mdD tasks/done/07_07_width-enrichment-audit.mdD tasks/done/07_07_zombie-claude-kill.mdD tasks/done/07_archive-discontinued-zoffany.mdD tasks/done/07_build-scraper-glitter.mdD tasks/done/07_duplicate-sku-check.mdD tasks/done/07_elli-popp-verify-cleanup.mdD tasks/done/07_fix-blog-agent-generator.mdD tasks/done/07_git-backup.mdD tasks/done/07_legal-violations-report.mdD tasks/done/07_pm2-health.mdD tasks/done/07_push-classification-tags-shopify.mdD tasks/done/07_sync-and-final-report.mdD tasks/done/07_zimmer-rohde-enrich.mdD tasks/done/08_08_enrich-andrew-martin.mdD tasks/done/08_08_enrich-fabricut.mdD tasks/done/08_08_philippe-romano-metafields.mdD tasks/done/08_08_ssl-cert-audit.mdD tasks/done/08_blog-post.mdD tasks/done/08_newwall-enrichment-scrape.mdD tasks/done/08_nlxl-enrich.mdD tasks/done/08_phase3-enrichment-run.mdD tasks/done/08_phoebe-fix-and-dreamteam-health.mdD tasks/done/08_pm2-health-report.mdD tasks/done/08_pm2-health.mdD tasks/done/08_push-synced-repeats-shopify.mdD tasks/done/08_slack-notify-completion.mdD tasks/done/08_vcc-dashboard-fix.mdD tasks/done/09_09_blog-post.mdD tasks/done/09_09_pj-search-exclusion.mdD tasks/done/09_ai-enrich-zoffany.mdD tasks/done/09_collections-color-palettes.mdD tasks/done/09_pm2-restart-errored.mdD tasks/done/09_quinn-queue-health.mdD tasks/done/09_run-vendor-standardization.mdD tasks/done/10_10_enrich-koroseal.mdD tasks/done/10_10_pm2-log-cleanup.mdD tasks/done/10_10_vcc-dashboard-fix.mdD tasks/done/10_1838-push-prep.mdD tasks/done/10_build-scraper-york-contract.mdD tasks/done/10_catalog-spec-fill-audit.mdD tasks/done/10_collections-fix-excluded.mdD tasks/done/10_database-vacuum-analyze.mdD tasks/done/10_fix-missing-samples.mdD tasks/done/10_nobilis-enrich.mdD tasks/done/10_phase2-kravet-specs.mdD tasks/done/10_phoebe-dreamteam-fix.mdD tasks/done/10_resume-weight-fix.mdD tasks/done/10_scalamandre-mfr-sku-format-analysis.mdD tasks/done/10_shopify-imageless-audit.mdD tasks/done/11_11_bespoke-vendor-consolidation.mdD tasks/done/11_11_enrich-kravet.mdD tasks/done/11_11_enrich-newwall.mdD tasks/done/11_blog-post-session109.mdD tasks/done/11_blog-post.mdD tasks/done/11_fix-broken-scrapers.mdD tasks/done/11_fix-sample-variants-p0.mdD tasks/done/11_innovations-mfr-sku-format-analysis.mdD tasks/done/11_interior-design-tagger.mdD tasks/done/11_phase2-coordonne-specs.mdD tasks/done/11_pm2-health-report.mdD tasks/done/11_thibaut-302-archive.mdD tasks/done/12_12_enrich-marburg.mdD tasks/done/12_12_enrich-nicolettemayer.mdD tasks/done/12_12_sample-weight-fix.mdD tasks/done/12_british-walls-enrich.mdD tasks/done/12_db-maintenance.mdD tasks/done/12_phase2-schumacher-specs.mdD tasks/done/12_pm2-health-audit.mdD tasks/done/12_shop-by-color-phase3.mdD tasks/done/12_social-posts.mdD tasks/done/12_weekly-relink-orphans.mdD tasks/done/12_york-catalog-images-p0.mdD tasks/done/13_13_1838-dedup-check.mdD tasks/done/13_13_draft-zero-image-audit.mdD tasks/done/13_13_enrich-brewster.mdD tasks/done/13_brewster-images-p0.mdD tasks/done/13_git-commit-session109.mdD tasks/done/13_hollywood-hex-extraction.mdD tasks/done/13_innovations-true-bug-discovery.mdD tasks/done/13_phase2-thibaut-specs.mdD tasks/done/13_vendor-standardization-check.mdD tasks/done/13_website-uptime-check.mdD tasks/done/14_14_enrich-jeffreystevens.mdD tasks/done/14_14_retro-walls-cleanup.mdD tasks/done/14_1838-enrichment.mdD tasks/done/14_duplicate-collections-p1.mdD tasks/done/14_innovations-recrawl-and-relink-plan.mdD tasks/done/14_phase2-koroseal-fire-rating.mdD tasks/done/14_shopify-image-audit.mdD tasks/done/14_stout-timorous-width.mdD tasks/done/14_studio-printworks-enrich.mdD tasks/done/14_styles-page-tile-images.mdD tasks/done/15_15_enrich-bespoke.mdD tasks/done/15_15_enrich-newwall.mdD tasks/done/15_15_lincrusta-sku-push.mdD tasks/done/15_database-vacuum-analyze.mdD tasks/done/15_full-system-health.mdD tasks/done/15_legal-violations-audit.mdD tasks/done/15_momentum-sku-investigation.mdD tasks/done/15_novasuede-ultrasuede-enrich.mdD tasks/done/15_phase2-harlequin-specs.mdD tasks/done/15_scrape-repeats-schumacher.mdD tasks/done/16_16_blog-function-leak.mdD tasks/done/16_16_enrich-malibu.mdD tasks/done/16_phase2-leejofa-algolia.mdD tasks/done/16_philippe-romano-legacy.mdD tasks/done/16_scrape-repeats-brewster-york.mdD tasks/done/16_vcc-dashboard-status-fix.mdD tasks/done/17_17_cost-gap-report.mdD tasks/done/17_17_enrich-innovations.mdD tasks/done/17_17_midnight-health-check.mdD tasks/done/17_phase2-sandberg-specs.mdD tasks/done/17_scrape-repeats-versace.mdD tasks/done/17_shop-by-color-integration.mdD tasks/done/17_youtube-oauth-reauth.mdD tasks/done/18_18_as-creation-specs-enrich.mdD tasks/done/18_18_enrich-hygge.mdD tasks/done/18_18_enrich-york.mdD tasks/done/18_kalshi-calendar-panel.mdD tasks/done/18_old-youtube-videos-unlist.mdD tasks/done/18_phase2-clarke-specs.mdD tasks/done/18_push-repeats-to-shopify.mdD tasks/done/19_19_3am-health-check.mdD tasks/done/19_19_enrich-grahambrown.mdD tasks/done/19_check-in-progress-items.mdD tasks/done/19_phase2-nina-shopify-api.mdD tasks/done/19_vcc-stats-refresh.mdD tasks/done/19_zero-repeat-textures.mdD tasks/done/20_20_enrich-maya.mdD tasks/done/20_nora-fix-pipeline-org-scoping.mdD tasks/done/20_phase2-osborne-specs.mdD tasks/done/20_phoebe-videos-dir-fix.mdD tasks/done/20_pm2-health-cleanup.mdD tasks/done/20_vcc-all-catalogs-ready.mdD tasks/done/21_21_enrich-stout.mdD tasks/done/21_knoll-maharam-scraper-investigate.mdD tasks/done/21_nora-fix-contacts-org-scoping.mdD tasks/done/21_phase2-wolfgordon-specs.mdD tasks/done/21_scrape-repeats-thibaut-r2.mdD tasks/done/22_22_enrich-harlequin.mdD tasks/done/22_cron-audit-cleanup.mdD tasks/done/22_nora-audit-all-api-org-scoping.mdD tasks/done/22_phase2-arte-specs.mdD tasks/done/22_scrape-repeats-schumacher-r2.mdD tasks/done/23_23_enrich-catchii.mdD tasks/done/23_nora-ux-audit-per-org.mdD tasks/done/23_phase2-designers-guild.mdD tasks/done/23_scrape-repeats-brewster-york-r2.mdD tasks/done/24_24_enrich-ellipopp.mdD tasks/done/24_nora-sidebar-buttons-fix.mdD tasks/done/24_phase2-bespoke-width.mdD tasks/done/24_push-repeats-to-shopify-r2.mdD tasks/done/25_nora-code-review-and-polish.mdD tasks/done/25_phase2-command54-specs.mdD tasks/done/25_zero-repeat-textures-r2.mdD tasks/done/26_repeat-loop-requeue-r2.mdD tasks/done/27_enrich-jeffrey-stevens-width.mdD tasks/done/27_scrape-match-type-thibaut-r3.mdD tasks/done/28_push-repeat-h-to-shopify-r3.mdD tasks/done/29_texture-classify-remaining-r3.mdD tasks/done/30_glitter-walls-full-monty.mdD tasks/done/31_retro-walls-full-monty.mdD tasks/done/32_command54-shopify-push.mdD tasks/done/33_brands-page-sync.mdD tasks/done/34_spoonflower-push.mdD tasks/done/35_twil-karin-push.mdD tasks/done/36_nina-campbell-full-monty.mdD tasks/done/37_revert-dwcy-skus.mdD tasks/done/41_refresh-as-creation-images.mdD tasks/done/43_scrape-surface-stick.mdD tasks/done/44_scrape-la-walls.mdD tasks/done/45_scrape-paul-montgomery.mdD tasks/done/46_scrape-remaining-missing-image.mdD tasks/done/47_approve-newly-enriched.mdD tasks/done/50_command54-rehome-phase-d-batch-25.mdD tasks/done/50_newmor-verify.mdD tasks/done/51_donghia-alt-source.mdD tasks/done/52_wm-store-code.mdD tasks/done/53_newmor-missing-patterns.mdD tasks/done/AC_collection-merge-dupes.mdD tasks/done/AD_collection-create-tier1.mdD tasks/done/AE_collection-ai-admin-enhance.mdD tasks/done/AF_blog-audit-purge.mdD tasks/done/AG_dw-exclusive-page-update.mdD tasks/done/AJ_kill-wallpaper-word-everywhere.mdD tasks/done/AK_fix-collection-trailing-artifacts.mdD tasks/done/AM_cleanup-errored-pm2.mdD tasks/done/AN_blog-post-goodquestion.mdD tasks/done/AO_tweet-and-bsky.mdD tasks/done/AP_hollywood-abaca-room-settings.mdD tasks/done/AQ_hollywood-imageclean-and-spin.mdD tasks/done/AS_update-dw-exclusive-page.mdD tasks/done/AT_fix-vendor-logo-size-flash.mdD tasks/done/AU_publish-collections-all-channels.mdD tasks/done/AV_hollywood-wc-body-cleanup.mdD tasks/done/AW_hollywood-wc-commercial-rooms.mdD tasks/done/AX_variant-weight-fix-pm2.mdD tasks/done/AY_metafield-push-all-vendors-pm2.mdD tasks/done/AZ_standardize-product-types-pm2.mdD tasks/done/BA_fix-product-type-by-width-pm2.mdD tasks/done/BB_create-youtube-videos-pm2.mdD tasks/done/BC_delete-oversized-hw-rooms.mdD tasks/done/BD_dw-exclusive-page-images.mdD tasks/done/BF_fix-collections-page-empty.mdD tasks/done/BG_rollback-product-types.mdD tasks/done/BH_steve-command-dashboard.mdD tasks/done/GATE.mdD tasks/done/HF1_retire-hollywood-add-pricing.mdD tasks/done/HF2_hunt-mystery-dwhd-pricing-script.mdD tasks/done/HF3_regression-verify-98-fixed-prices.mdD tasks/done/YL1_logo-contamination-cleanup.mdD tasks/done/hawke-monitor.mdD tasks/done/now_1774493279502.mdD tasks/done/repeat-requeue-log.mdD tasks/done/york-bye-bye-overnight.md
Diff
commit 8358fc1b898ef6e7489e65c143b32a21e620934b
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue Jun 9 13:05:25 2026 -0700
untrack logs + done-task archive (approved cleanup)
---
.gitignore | 2 +
logs/yolo-stderr.log | 0
logs/yolo-stdout.log | 11 --
tasks/done/00_1838-final-cleanup.md | 50 -----
tasks/done/00_FREEZE-no-new-vendors.md | 33 ----
tasks/done/00_URGENT-collection-menus.md | 94 ----------
tasks/done/00_URGENT-fix-hero-banner-collection.md | 55 ------
tasks/done/00_URGENT-phillipe-romano-fullmonty.md | 77 --------
tasks/done/00_URGENT-scraper-blitz.md | 61 ------
tasks/done/00_URGENT-update-dw-exclusive-page.md | 101 ----------
tasks/done/00_anna-french-full-monte.md | 67 -------
tasks/done/00_archive-gb-europe-dupes.md | 12 --
tasks/done/00_build-stock-check-agent.md | 51 -----
tasks/done/00_clean-body-specs.md | 19 --
tasks/done/00_cmd54-west-coast-rename.md | 37 ----
tasks/done/00_create-79-missing-collections.md | 88 ---------
tasks/done/00_create-hermosa-beach-impact.md | 23 ---
tasks/done/00_crop-grs-images-to-square.md | 75 --------
tasks/done/00_dwjs-enrich-specs.md | 86 ---------
tasks/done/00_dwpr-remove-specs-from-body.md | 50 -----
tasks/done/00_enrich-thibaut-af.md | 20 --
.../00_fix-fabrics-remove-trim-new-arrivals.md | 54 ------
tasks/done/00_fix-ledger-modal-flash.md | 14 --
tasks/done/00_fix-malibu-body-dedup.md | 31 ---
tasks/done/00_fix-thibaut-af-fabrics.md | 38 ----
tasks/done/00_fix-thibaut-titles-vendor.md | 31 ---
tasks/done/00_full-monte-phase3-all.md | 52 ------
tasks/done/00_hex-color-gemini-blitz.md | 29 ---
tasks/done/00_norma-news-refresh-button.md | 56 ------
tasks/done/00_overnight-status-watch.md | 20 --
.../done/00_push-repeat-metafields-all-vendors.md | 63 -------
...finish_DONE-orchestrator-died-work-succeeded.md | 207 ---------------------
tasks/done/00_set-pattern-names.md | 18 --
tasks/done/00_stock-check-agent.md | 36 ----
tasks/done/00a_brewster-line-manager.md | 20 --
tasks/done/00a_maya-room-settings.md | 43 -----
tasks/done/00b_designtex-scraper-fix.md | 15 --
tasks/done/00b_harvest-dead-links.md | 17 --
tasks/done/00c_logo-hunter-summary.md | 30 ---
tasks/done/01_01_karin-room-settings.md | 1 -
tasks/done/01_01_pm2-health-check.md | 1 -
tasks/done/01_01_silas-violations-report.md | 1 -
tasks/done/01_01_verify-archive-bodyhtml.md | 6 -
tasks/done/01_02_blog-homepage-refinement.md | 18 --
tasks/done/01_1838-cleanup-and-enrich.md | 48 -----
tasks/done/01_ai-enrich-romo.md | 19 --
tasks/done/01_apex-finish-build.md | 77 --------
tasks/done/01_arte-image-enrichment.md | 33 ----
tasks/done/01_audit-and-fix-empty-collections.md | 57 ------
tasks/done/01_blog-post.md | 6 -
tasks/done/01_blog-social-session112.md | 17 --
tasks/done/01_china-seas-width-repeat-update.md | 42 -----
tasks/done/01_cleanup-37-empty-collections.md | 83 ---------
tasks/done/01_dexter-full-scan-cleanup.md | 16 --
tasks/done/01_enrich-prl-direct.md | 14 --
tasks/done/01_farrow-ball-specs-extract.md | 29 ---
tasks/done/01_fix-phillipe-romano-vendors.md | 32 ----
tasks/done/01_fix-vendor-name-duplicates.md | 31 ---
tasks/done/01_health-check-restart.md | 11 --
tasks/done/01_health-check.md | 4 -
tasks/done/01_hire-page-black-redesign.md | 39 ----
tasks/done/01_hire-page-overnight-refinement.md | 54 ------
tasks/done/01_hollywood-color-name-cleanup.md | 83 ---------
tasks/done/01_hollywood-fix-color-names.md | 26 ---
.../01_hollywood-width-backfill-from-gdrive.md | 25 ---
tasks/done/01_interior-design-tags-all-vendors.md | 87 ---------
tasks/done/01_jd-fix-broken-images.md | 44 -----
tasks/done/01_lod-line-followup.md | 43 -----
tasks/done/01_monitor-crawls.md | 35 ----
tasks/done/01_monitor-running-processes.md | 20 --
tasks/done/01_newmor-full-monty-test.md | 63 -------
tasks/done/01_newmor-rescrape-widths.md | 73 --------
tasks/done/01_norma-build-verify.md | 15 --
tasks/done/01_norma_email_center_demo_video.md | 37 ----
tasks/done/01_purge-blog-duplicates.md | 27 ---
tasks/done/01_push-fire-ratings.md | 25 ---
tasks/done/01_push-versace-vi-shopify.md | 29 ---
tasks/done/01_ralphy-boy-continue.md | 16 --
tasks/done/01_raoul-to-anna-french.md | 31 ---
tasks/done/01_resume-pointe-updater.md | 19 --
tasks/done/01_resume-weight-fix.md | 10 -
tasks/done/01_small-vendor-image-sweep.md | 49 -----
tasks/done/01_vendor-width-audit.md | 36 ----
tasks/done/01_verify-dual-docs.md | 22 ---
tasks/done/01_verify-kamism-spins.md | 7 -
tasks/done/02_02_enrich-coordonne.md | 3 -
tasks/done/02_02_fix-room-setting-images.md | 1 -
tasks/done/02_02_hollywood-unmatched-skus.md | 1 -
tasks/done/02_02_investigate-unmatched-patterns.md | 21 ---
tasks/done/02_02_norma-git-commit.md | 5 -
tasks/done/02_02_vendor-catalog-audit.md | 1 -
tasks/done/02_02_versa-remaining-push.md | 9 -
tasks/done/02_100pct-readiness-push.md | 53 ------
tasks/done/02_activate-versace-drafts.md | 59 ------
tasks/done/02_ai-enrich-villa-nova.md | 9 -
tasks/done/02_archive-ncf-fabrics.md | 16 --
tasks/done/02_bespoke-width-inference.md | 47 -----
tasks/done/02_blog-post.md | 12 --
tasks/done/02_build-as-creation-agent.md | 51 -----
tasks/done/02_check-image-scrapers-complete.md | 28 ---
tasks/done/02_china-seas-room-settings.md | 27 ---
tasks/done/02_enrich-remaining-gaps.md | 9 -
tasks/done/02_fix-missing-sample-variants.md | 41 ----
tasks/done/02_fix-out-of-range-skus.md | 31 ---
tasks/done/02_full-monte-enrichment.md | 18 --
tasks/done/02_full-store-collection-audit.md | 76 --------
tasks/done/02_git-backup.md | 5 -
tasks/done/02_hollywood-fix-fire-rating.md | 27 ---
.../done/02_hollywood-shopify-orphan-reconcile.md | 93 ---------
tasks/done/02_jd-room-settings.md | 45 -----
tasks/done/02_low-image-vendors-enrich.md | 30 ---
tasks/done/02_newmor-brands-page.md | 109 -----------
tasks/done/02_newmor-bulk-phase3-ai.md | 89 ---------
tasks/done/02_norma-git-commit.md | 23 ---
tasks/done/02_norma_email_analyzer_demo_video.md | 54 ------
tasks/done/02_p2-09-vendor-standardization.md | 19 --
tasks/done/02_push-color-metafields.md | 22 ---
tasks/done/02_rewrite-blogs-professional.md | 54 ------
tasks/done/02_scraper-status.md | 4 -
tasks/done/02_system-health-check.md | 12 --
tasks/done/02_update-vcc-spec-standards.md | 64 -------
tasks/done/02_verify-archive-duplicates.md | 19 --
tasks/done/02_versa-william-morris-enrich.md | 28 ---
tasks/done/03_03_data-quality-audit.md | 28 ---
tasks/done/03_03_enrich-kravet.md | 3 -
tasks/done/03_03_gls-tag-cleanup.md | 9 -
tasks/done/03_03_reseller-vendor-analysis.md | 1 -
tasks/done/03_03_server-health-check.md | 9 -
tasks/done/03_03_shopify-product-count.md | 1 -
tasks/done/03_ai-enrich-mark-alexander.md | 9 -
tasks/done/03_blog-post.md | 6 -
tasks/done/03_build-html-video-pipeline.md | 109 -----------
tasks/done/03_build-scraper-nobilis.md | 15 --
tasks/done/03_china-seas-metafields-push.md | 23 ---
tasks/done/03_china-seas-width-fix.md | 18 --
tasks/done/03_db-maintenance.md | 7 -
tasks/done/03_fix-44-pricing.md | 18 --
tasks/done/03_fix-remaining-11-images.md | 37 ----
tasks/done/03_fix-spec-gaps.md | 67 -------
tasks/done/03_git-commit.md | 4 -
tasks/done/03_harlequin-post-scrape-enrich.md | 31 ---
tasks/done/03_hollywood-scrape-tower-pricing.md | 36 ----
tasks/done/03_jd-shopify-push.md | 52 ------
tasks/done/03_launch-supervisor-audit-sweep.md | 85 ---------
tasks/done/03_newmor-full-monty-test-1.md | 74 --------
tasks/done/03_orphan-product-audit.md | 105 -----------
tasks/done/03_p2-06-interior-design-tagger.md | 33 ----
tasks/done/03_phillip-jeffries-specs-only.md | 20 --
tasks/done/03_phillipe-romano-shopify-push.md | 18 --
tasks/done/03_pj-browse-hidden-audit.md | 24 ---
tasks/done/03_pm2-health-check.md | 18 --
tasks/done/03_pm2-health-zombie-cleanup.md | 19 --
tasks/done/03_private-label-vendor-audit.md | 26 ---
tasks/done/03_push-hollywood.md | 27 ---
tasks/done/03_run-http-scrapers.md | 18 --
tasks/done/03_server-health-check.md | 17 --
tasks/done/03_vcc-sync-and-report.md | 36 ----
tasks/done/03_zoffany-enrich.md | 18 --
tasks/done/04_04_dead-agent-restart.md | 1 -
tasks/done/04_04_enrich-scalamandre.md | 3 -
tasks/done/04_04_shopify-product-audit.md | 5 -
tasks/done/04_04_shopify-product-quality.md | 25 ---
tasks/done/04_04_thibaut-discontinued-archive.md | 1 -
tasks/done/04_04_vcc-stats-refresh.md | 14 --
tasks/done/04_add-auth-to-all-open-services.md | 79 --------
tasks/done/04_ai-enrich-arte.md | 9 -
tasks/done/04_blog-post-overnight.md | 23 ---
tasks/done/04_brewster-specs-fix-and-push.md | 13 --
tasks/done/04_build-scraper-pierre-frey.md | 15 --
tasks/done/04_catalog-audit.md | 5 -
tasks/done/04_china-seas-hex-colors.md | 32 ----
tasks/done/04_china-seas-quadrille-retry.md | 27 ---
tasks/done/04_enrichment-status-report.md | 37 ----
tasks/done/04_enrichment-tracking-progress.md | 34 ----
tasks/done/04_gemini-texture-classify-17k.md | 46 -----
tasks/done/04_malibu-width-inference.md | 32 ----
tasks/done/04_overall-stats-email.md | 40 ----
tasks/done/04_pm2-health-check.md | 13 --
tasks/done/04_pm2-health-cleanup.md | 6 -
tasks/done/04_push-ascreation.md | 23 ---
tasks/done/04_push-enrichment-to-shopify.md | 15 --
tasks/done/04_push-mf-york.md | 6 -
tasks/done/04_push-specs-pj.md | 16 --
tasks/done/04_recrawl-low-spec-vendors.md | 46 -----
tasks/done/04_run-all-vendor-crawls-high-to-low.md | 86 ---------
tasks/done/04_scrape-repeats-thibaut.md | 11 --
tasks/done/04_session-blog-social.md | 22 ---
tasks/done/04_shop-by-color-page.md | 49 -----
tasks/done/04_shopify-body-html-cleanup.md | 27 ---
tasks/done/04_shopify-product-audit.md | 25 ---
tasks/done/04_vcc-dashboard-fix.md | 16 --
tasks/done/04_verify-body-html-patches.md | 99 ----------
tasks/done/05_05_disk-cleanup.md | 1 -
tasks/done/05_05_dupenny-sancar-push-check.md | 1 -
tasks/done/05_05_enrich-schumacher.md | 3 -
tasks/done/05_05_tag-enrichment.md | 31 ---
tasks/done/05_05_thibaut-monitor.md | 5 -
tasks/done/05_ai-enrich-remaining.md | 10 -
tasks/done/05_blog-recently-viewed-feature.md | 49 -----
tasks/done/05_build-scraper-zoffany.md | 15 --
tasks/done/05_check-running-scrapers.md | 4 -
tasks/done/05_china-seas-interior-tagger.md | 27 ---
tasks/done/05_collection-image-refresh.md | 26 ---
tasks/done/05_crawl-report-for-steve.md | 43 -----
tasks/done/05_db-maintenance.md | 12 --
tasks/done/05_enrichment-gap-report.md | 3 -
tasks/done/05_final-audit-report.md | 38 ----
tasks/done/05_git-commit-session107.md | 22 ---
tasks/done/05_grahambrown-puppeteer-width.md | 26 ---
tasks/done/05_no-image-product-audit.md | 11 --
tasks/done/05_push-color-metafields.md | 15 --
tasks/done/05_push-specs-york-contract.md | 9 -
tasks/done/05_push-wolfgordon.md | 23 ---
tasks/done/05_roberto-cavalli-enrich.md | 15 --
tasks/done/05_sales-channel-audit.md | 19 --
tasks/done/05_schumacher-osborne-enrich.md | 25 ---
tasks/done/05_social-posts.md | 8 -
tasks/done/05_thibaut-discontinued-archive.md | 17 --
tasks/done/05_thibaut-scrape-monitor.md | 19 --
tasks/done/05_upload-youtube-unlist-old.md | 37 ----
tasks/done/05_vcc-dashboard-health.md | 22 ---
tasks/done/06_06_brands-page-pills-fix.md | 15 --
tasks/done/06_06_brewster-specs-fix.md | 1 -
tasks/done/06_06_enrich-thibaut.md | 3 -
tasks/done/06_06_vcc-sync.md | 5 -
tasks/done/06_build-scraper-donghia.md | 15 --
tasks/done/06_china-seas-verify-all.md | 35 ----
tasks/done/06_collections-cleanup-phase1.md | 31 ---
tasks/done/06_db-maintenance.md | 14 --
tasks/done/06_enrich-ascreation.md | 11 --
tasks/done/06_enrich-rl-width.md | 12 --
tasks/done/06_git-backup-push.md | 12 --
tasks/done/06_git-backup.md | 6 -
tasks/done/06_hollywood-gemini-hex.md | 14 --
tasks/done/06_hollywood-hex-extraction.md | 21 ---
tasks/done/06_lincrusta-width-enrichment.md | 19 --
tasks/done/06_missing-images-audit.md | 3 -
tasks/done/06_push-command54.md | 33 ----
tasks/done/06_push-mf-scalamandre.md | 6 -
tasks/done/06_rebuild-astro-site.md | 22 ---
tasks/done/06_vcc-sync-all.md | 11 --
tasks/done/06_wallquest-completion.md | 18 --
tasks/done/06_zoffany-shopify-prep.md | 11 --
tasks/done/07_07_dw-exclusive-page-update.md | 17 --
tasks/done/07_07_enrich-designtex.md | 3 -
tasks/done/07_07_hollywood-hex-cleanup.md | 1 -
tasks/done/07_07_thibaut-discontinued-archive.md | 14 --
tasks/done/07_07_width-enrichment-audit.md | 4 -
tasks/done/07_07_zombie-claude-kill.md | 1 -
tasks/done/07_archive-discontinued-zoffany.md | 19 --
tasks/done/07_build-scraper-glitter.md | 15 --
tasks/done/07_duplicate-sku-check.md | 3 -
tasks/done/07_elli-popp-verify-cleanup.md | 13 --
tasks/done/07_fix-blog-agent-generator.md | 30 ---
tasks/done/07_git-backup.md | 13 --
tasks/done/07_legal-violations-report.md | 18 --
tasks/done/07_pm2-health.md | 6 -
tasks/done/07_push-classification-tags-shopify.md | 61 ------
tasks/done/07_sync-and-final-report.md | 20 --
tasks/done/07_zimmer-rohde-enrich.md | 16 --
tasks/done/08_08_enrich-andrew-martin.md | 3 -
tasks/done/08_08_enrich-fabricut.md | 3 -
tasks/done/08_08_philippe-romano-metafields.md | 1 -
tasks/done/08_08_ssl-cert-audit.md | 1 -
tasks/done/08_blog-post.md | 15 --
tasks/done/08_newwall-enrichment-scrape.md | 18 --
tasks/done/08_nlxl-enrich.md | 17 --
tasks/done/08_phase3-enrichment-run.md | 13 --
tasks/done/08_phoebe-fix-and-dreamteam-health.md | 18 --
tasks/done/08_pm2-health-report.md | 2 -
tasks/done/08_pm2-health.md | 26 ---
tasks/done/08_push-synced-repeats-shopify.md | 56 ------
tasks/done/08_slack-notify-completion.md | 11 --
tasks/done/08_vcc-dashboard-fix.md | 13 --
tasks/done/09_09_blog-post.md | 1 -
tasks/done/09_09_pj-search-exclusion.md | 1 -
tasks/done/09_ai-enrich-zoffany.md | 12 --
tasks/done/09_collections-color-palettes.md | 58 ------
tasks/done/09_pm2-restart-errored.md | 7 -
tasks/done/09_quinn-queue-health.md | 12 --
tasks/done/09_run-vendor-standardization.md | 19 --
tasks/done/10_10_enrich-koroseal.md | 3 -
tasks/done/10_10_pm2-log-cleanup.md | 6 -
tasks/done/10_10_vcc-dashboard-fix.md | 1 -
tasks/done/10_1838-push-prep.md | 11 --
tasks/done/10_build-scraper-york-contract.md | 14 --
tasks/done/10_catalog-spec-fill-audit.md | 12 --
tasks/done/10_collections-fix-excluded.md | 34 ----
tasks/done/10_database-vacuum-analyze.md | 23 ---
tasks/done/10_fix-missing-samples.md | 12 --
tasks/done/10_nobilis-enrich.md | 23 ---
tasks/done/10_phase2-kravet-specs.md | 26 ---
tasks/done/10_phoebe-dreamteam-fix.md | 12 --
tasks/done/10_resume-weight-fix.md | 10 -
.../done/10_scalamandre-mfr-sku-format-analysis.md | 59 ------
tasks/done/10_shopify-imageless-audit.md | 25 ---
tasks/done/11_11_bespoke-vendor-consolidation.md | 1 -
tasks/done/11_11_enrich-kravet.md | 3 -
tasks/done/11_11_enrich-newwall.md | 3 -
tasks/done/11_blog-post-session109.md | 22 ---
tasks/done/11_blog-post.md | 11 --
tasks/done/11_fix-broken-scrapers.md | 16 --
tasks/done/11_fix-sample-variants-p0.md | 17 --
.../done/11_innovations-mfr-sku-format-analysis.md | 26 ---
tasks/done/11_interior-design-tagger.md | 20 --
tasks/done/11_phase2-coordonne-specs.md | 27 ---
tasks/done/11_pm2-health-report.md | 12 --
tasks/done/11_thibaut-302-archive.md | 15 --
tasks/done/12_12_enrich-marburg.md | 4 -
tasks/done/12_12_enrich-nicolettemayer.md | 3 -
tasks/done/12_12_sample-weight-fix.md | 1 -
tasks/done/12_british-walls-enrich.md | 16 --
tasks/done/12_db-maintenance.md | 13 --
tasks/done/12_phase2-schumacher-specs.md | 26 ---
tasks/done/12_pm2-health-audit.md | 14 --
tasks/done/12_shop-by-color-phase3.md | 17 --
tasks/done/12_social-posts.md | 14 --
tasks/done/12_weekly-relink-orphans.md | 46 -----
tasks/done/12_york-catalog-images-p0.md | 14 --
tasks/done/13_13_1838-dedup-check.md | 1 -
tasks/done/13_13_draft-zero-image-audit.md | 2 -
tasks/done/13_13_enrich-brewster.md | 3 -
tasks/done/13_brewster-images-p0.md | 14 --
tasks/done/13_git-commit-session109.md | 21 ---
tasks/done/13_hollywood-hex-extraction.md | 17 --
tasks/done/13_innovations-true-bug-discovery.md | 117 ------------
tasks/done/13_phase2-thibaut-specs.md | 27 ---
tasks/done/13_vendor-standardization-check.md | 11 --
tasks/done/13_website-uptime-check.md | 22 ---
tasks/done/14_14_enrich-jeffreystevens.md | 3 -
tasks/done/14_14_retro-walls-cleanup.md | 1 -
tasks/done/14_1838-enrichment.md | 9 -
tasks/done/14_duplicate-collections-p1.md | 14 --
.../done/14_innovations-recrawl-and-relink-plan.md | 98 ----------
tasks/done/14_phase2-koroseal-fire-rating.md | 27 ---
tasks/done/14_shopify-image-audit.md | 13 --
tasks/done/14_stout-timorous-width.md | 15 --
tasks/done/14_studio-printworks-enrich.md | 13 --
tasks/done/14_styles-page-tile-images.md | 16 --
tasks/done/15_15_enrich-bespoke.md | 3 -
tasks/done/15_15_enrich-newwall.md | 3 -
tasks/done/15_15_lincrusta-sku-push.md | 1 -
tasks/done/15_database-vacuum-analyze.md | 13 --
tasks/done/15_full-system-health.md | 15 --
tasks/done/15_legal-violations-audit.md | 19 --
tasks/done/15_momentum-sku-investigation.md | 18 --
tasks/done/15_novasuede-ultrasuede-enrich.md | 17 --
tasks/done/15_phase2-harlequin-specs.md | 30 ---
tasks/done/15_scrape-repeats-schumacher.md | 41 ----
tasks/done/16_16_blog-function-leak.md | 1 -
tasks/done/16_16_enrich-malibu.md | 3 -
tasks/done/16_phase2-leejofa-algolia.md | 26 ---
tasks/done/16_philippe-romano-legacy.md | 20 --
tasks/done/16_scrape-repeats-brewster-york.md | 51 -----
tasks/done/16_vcc-dashboard-status-fix.md | 15 --
tasks/done/17_17_cost-gap-report.md | 1 -
tasks/done/17_17_enrich-innovations.md | 3 -
tasks/done/17_17_midnight-health-check.md | 10 -
tasks/done/17_phase2-sandberg-specs.md | 26 ---
tasks/done/17_scrape-repeats-versace.md | 30 ---
tasks/done/17_shop-by-color-integration.md | 17 --
tasks/done/17_youtube-oauth-reauth.md | 15 --
tasks/done/18_18_as-creation-specs-enrich.md | 1 -
tasks/done/18_18_enrich-hygge.md | 3 -
tasks/done/18_18_enrich-york.md | 3 -
tasks/done/18_kalshi-calendar-panel.md | 13 --
tasks/done/18_old-youtube-videos-unlist.md | 19 --
tasks/done/18_phase2-clarke-specs.md | 26 ---
tasks/done/18_push-repeats-to-shopify.md | 43 -----
tasks/done/19_19_3am-health-check.md | 8 -
tasks/done/19_19_enrich-grahambrown.md | 3 -
tasks/done/19_check-in-progress-items.md | 19 --
tasks/done/19_phase2-nina-shopify-api.md | 26 ---
tasks/done/19_vcc-stats-refresh.md | 24 ---
tasks/done/19_zero-repeat-textures.md | 50 -----
tasks/done/20_20_enrich-maya.md | 3 -
tasks/done/20_nora-fix-pipeline-org-scoping.md | 38 ----
tasks/done/20_phase2-osborne-specs.md | 27 ---
tasks/done/20_phoebe-videos-dir-fix.md | 11 --
tasks/done/20_pm2-health-cleanup.md | 14 --
tasks/done/20_vcc-all-catalogs-ready.md | 18 --
tasks/done/21_21_enrich-stout.md | 3 -
tasks/done/21_knoll-maharam-scraper-investigate.md | 15 --
tasks/done/21_nora-fix-contacts-org-scoping.md | 34 ----
tasks/done/21_phase2-wolfgordon-specs.md | 27 ---
tasks/done/21_scrape-repeats-thibaut-r2.md | 46 -----
tasks/done/22_22_enrich-harlequin.md | 3 -
tasks/done/22_cron-audit-cleanup.md | 16 --
tasks/done/22_nora-audit-all-api-org-scoping.md | 50 -----
tasks/done/22_phase2-arte-specs.md | 36 ----
tasks/done/22_scrape-repeats-schumacher-r2.md | 41 ----
tasks/done/23_23_enrich-catchii.md | 3 -
tasks/done/23_nora-ux-audit-per-org.md | 87 ---------
tasks/done/23_phase2-designers-guild.md | 28 ---
tasks/done/23_scrape-repeats-brewster-york-r2.md | 51 -----
tasks/done/24_24_enrich-ellipopp.md | 3 -
tasks/done/24_nora-sidebar-buttons-fix.md | 57 ------
tasks/done/24_phase2-bespoke-width.md | 26 ---
tasks/done/24_push-repeats-to-shopify-r2.md | 42 -----
tasks/done/25_nora-code-review-and-polish.md | 50 -----
tasks/done/25_phase2-command54-specs.md | 28 ---
tasks/done/25_zero-repeat-textures-r2.md | 50 -----
tasks/done/26_repeat-loop-requeue-r2.md | 67 -------
tasks/done/27_enrich-jeffrey-stevens-width.md | 38 ----
tasks/done/27_scrape-match-type-thibaut-r3.md | 48 -----
tasks/done/28_push-repeat-h-to-shopify-r3.md | 42 -----
tasks/done/29_texture-classify-remaining-r3.md | 61 ------
tasks/done/30_glitter-walls-full-monty.md | 18 --
tasks/done/31_retro-walls-full-monty.md | 13 --
tasks/done/32_command54-shopify-push.md | 15 --
tasks/done/33_brands-page-sync.md | 13 --
tasks/done/34_spoonflower-push.md | 8 -
tasks/done/35_twil-karin-push.md | 8 -
tasks/done/36_nina-campbell-full-monty.md | 35 ----
tasks/done/37_revert-dwcy-skus.md | 16 --
tasks/done/41_refresh-as-creation-images.md | 44 -----
tasks/done/43_scrape-surface-stick.md | 47 -----
tasks/done/44_scrape-la-walls.md | 25 ---
tasks/done/45_scrape-paul-montgomery.md | 21 ---
tasks/done/46_scrape-remaining-missing-image.md | 40 ----
tasks/done/47_approve-newly-enriched.md | 53 ------
tasks/done/50_command54-rehome-phase-d-batch-25.md | 118 ------------
tasks/done/50_newmor-verify.md | 92 ---------
tasks/done/51_donghia-alt-source.md | 95 ----------
tasks/done/52_wm-store-code.md | 105 -----------
tasks/done/53_newmor-missing-patterns.md | 117 ------------
tasks/done/AC_collection-merge-dupes.md | 27 ---
tasks/done/AD_collection-create-tier1.md | 25 ---
tasks/done/AE_collection-ai-admin-enhance.md | 98 ----------
tasks/done/AF_blog-audit-purge.md | 20 --
tasks/done/AG_dw-exclusive-page-update.md | 86 ---------
tasks/done/AJ_kill-wallpaper-word-everywhere.md | 60 ------
tasks/done/AK_fix-collection-trailing-artifacts.md | 19 --
tasks/done/AM_cleanup-errored-pm2.md | 17 --
tasks/done/AN_blog-post-goodquestion.md | 14 --
tasks/done/AO_tweet-and-bsky.md | 8 -
tasks/done/AP_hollywood-abaca-room-settings.md | 34 ----
tasks/done/AQ_hollywood-imageclean-and-spin.md | 59 ------
tasks/done/AS_update-dw-exclusive-page.md | 79 --------
tasks/done/AT_fix-vendor-logo-size-flash.md | 49 -----
tasks/done/AU_publish-collections-all-channels.md | 62 ------
tasks/done/AV_hollywood-wc-body-cleanup.md | 70 -------
tasks/done/AW_hollywood-wc-commercial-rooms.md | 53 ------
tasks/done/AX_variant-weight-fix-pm2.md | 24 ---
tasks/done/AY_metafield-push-all-vendors-pm2.md | 39 ----
tasks/done/AZ_standardize-product-types-pm2.md | 44 -----
tasks/done/BA_fix-product-type-by-width-pm2.md | 22 ---
tasks/done/BB_create-youtube-videos-pm2.md | 22 ---
tasks/done/BC_delete-oversized-hw-rooms.md | 30 ---
tasks/done/BD_dw-exclusive-page-images.md | 77 --------
tasks/done/BF_fix-collections-page-empty.md | 36 ----
tasks/done/BG_rollback-product-types.md | 49 -----
tasks/done/BH_steve-command-dashboard.md | 127 -------------
tasks/done/GATE.md | 21 ---
tasks/done/HF1_retire-hollywood-add-pricing.md | 55 ------
tasks/done/HF2_hunt-mystery-dwhd-pricing-script.md | 67 -------
.../done/HF3_regression-verify-98-fixed-prices.md | 56 ------
tasks/done/YL1_logo-contamination-cleanup.md | 52 ------
tasks/done/hawke-monitor.md | 18 --
tasks/done/now_1774493279502.md | 1 -
tasks/done/repeat-requeue-log.md | 22 ---
tasks/done/york-bye-bye-overnight.md | 26 ---
462 files changed, 2 insertions(+), 12983 deletions(-)
diff --git a/.gitignore b/.gitignore
index 9fa6d19..84af930 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@ logs/
*.bak.*
*.pre-*
*.orig
+logs/
+tasks/done/
diff --git a/logs/yolo-stderr.log b/logs/yolo-stderr.log
deleted file mode 100644
index e69de29..0000000
diff --git a/logs/yolo-stdout.log b/logs/yolo-stdout.log
deleted file mode 100644
index 8104f35..0000000
--- a/logs/yolo-stdout.log
+++ /dev/null
@@ -1,11 +0,0 @@
-======================================
- YOLO Agent (Yuri) — Port 9670
- Mode: queue
- Cooldown: 30s
- Dashboard: http://45.61.58.125:9670/
-======================================
-[YOLO] YOLO Agent started
-[YOLO] YOLO loop idle — use POST /api/start to begin processing
-[YOLO] SIGTERM received — stopping loop, current task will finish
-[YOLO] SIGTERM received — stopping loop, current task will finish
-[YOLO] SIGTERM received — stopping loop, current task will finish
diff --git a/tasks/done/00_1838-final-cleanup.md b/tasks/done/00_1838-final-cleanup.md
deleted file mode 100644
index 5bbe42f..0000000
--- a/tasks/done/00_1838-final-cleanup.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# 1838 Wallcoverings — Final Cleanup (March 13, 2026)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Table: `w1838_catalog`
-- Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-- Store: `designer-laboratory-sandbox.myshopify.com`
-- 92 old Architectural Wallcoverings products ALREADY archived (done this session)
-
-## Task 1: Archive 2 Duplicate Products
-
-Two duplicate titles on Shopify. Archive the HIGHER ID (keep lower):
-
-```bash
-# Archive Fairhaven duplicate
-curl -s -X PUT "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/7796831551539.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"product":{"id":7796831551539,"status":"archived"}}'
-
-# Archive Verdi duplicate
-curl -s -X PUT "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/7796824309811.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"product":{"id":7796824309811,"status":"archived"}}'
-```
-
-## Task 2: Mark Paste Products Discontinued
-
-```sql
-UPDATE w1838_catalog SET discontinued = true, updated_at = NOW() WHERE mfr_sku IN ('1838-PASTE-5', '1838-PASTE-2.5');
-```
-
-## Task 3: Fix Titles — Replace "Wallpaper" with "Wallcovering"
-
-For ALL active 1838 Wallcoverings products on Shopify, replace "Wallpaper" with "Wallcovering" in the title.
-
-Write a Python script that:
-1. Fetches all active products with vendor "1838 Wallcoverings"
-2. For each product where title contains "Wallpaper", update to "Wallcovering"
-3. Rate limit: 0.5s between API calls
-4. Print progress every 50 products
-
-## Task 4: Verify and Report
-
-After all changes, print:
-- Total active 1838 products on Shopify
-- Count of products still containing "Wallpaper" in title
-- Count of duplicate titles remaining
-- DB: total, on_shopify, discontinued counts
diff --git a/tasks/done/00_FREEZE-no-new-vendors.md b/tasks/done/00_FREEZE-no-new-vendors.md
deleted file mode 100644
index 4ddf242..0000000
--- a/tasks/done/00_FREEZE-no-new-vendors.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# FREEZE: No New Vendor Onboarding Until Core Complete > 50K
-
-## DO NOT ask clarifying questions — proceed autonomously.
-
-## What
-FREEZE all new vendor scraping, new line imports, and new catalog creation.
-Focus 100% on completing existing products through the Full Monte pipeline.
-
-## Actions
-1. Move ALL new vendor onboarding tasks to /root/DW-Agents/yolo-agent/tasks/frozen/:
- - Any task with "scraper", "onboard", "new vendor", "new line" in the filename
- - DO NOT freeze enrichment, push, fix, or completion tasks
-
-2. Create the frozen directory:
-```bash
-mkdir -p /root/DW-Agents/yolo-agent/tasks/frozen
-```
-
-3. Move matching tasks:
-```bash
-cd /root/DW-Agents/yolo-agent/tasks
-for f in *scraper*.md *onboard*.md *new-vendor*.md *new-line*.md; do
- [ -f "$f" ] && mv "$f" frozen/ && echo "FROZEN: $f"
-done
-```
-
-4. Create a gate file that blocks new onboarding:
-```bash
-echo "FROZEN until core_complete > 50,000 (currently 16,108)" > /root/DW-Agents/yolo-agent/tasks/frozen/GATE.md
-echo "Check: SELECT COUNT(*) FROM enrichment_tracking WHERE phase2_specs_at IS NOT NULL AND phase3_ai_at IS NOT NULL AND phase8_shopify_at IS NOT NULL" >> /root/DW-Agents/yolo-agent/tasks/frozen/GATE.md
-```
-
-5. Report what was frozen to Slack
diff --git a/tasks/done/00_URGENT-collection-menus.md b/tasks/done/00_URGENT-collection-menus.md
deleted file mode 100644
index 8b08df4..0000000
--- a/tasks/done/00_URGENT-collection-menus.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# URGENT: Build Visual Collection Navigation with Category Images
-
-## Goal
-Create a VISUAL mega-menu style navigation page AND Shopify menu for top 100 collections. Each category shows a hero image from the NEWEST product in that collection. NOT just text links — full visual experience.
-
-## Two Deliverables
-
-### 1. Visual Collections Page (Shopify Page)
-Create/update a Shopify page at handle `collections-directory` that displays ALL categories as visual cards.
-
-Each category card:
-- **Hero image**: Pulled from the NEWEST product in that category's top collection
-- **Category title**: Bold overlay text
-- **Collection count**: "X Collections" badge
-- **Click**: Links to a section with all collections in that category
-- **Grid**: Same slider system as dw-exclusive page (2-8 columns, default 4)
-
-Each collection within a category:
-- **Image**: From the NEWEST product (sort by created_at DESC, take first image)
-- **Title overlay**: Collection name with dark gradient
-- **Product count badge**
-- **Hover**: Scale + shadow lift
-
-### 2. Shopify Navigation Menu
-Update the main navigation with visual mega-menu links pointing to this page's sections.
-
-## How to Get NEWEST Product Image Per Collection
-
-For each collection, get the newest product's image:
-```
-GET /admin/api/2024-01/products.json?collection_id={ID}&limit=1&order=created_at+desc&fields=id,images,created_at
-```
-Use the first image from that product as the collection card image.
-
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-IMPORTANT: 1 second delay between ALL API calls.
-
-## Categories with Visual Headers
-
-### Shop by Brand
-- Hero: newest product from the largest vendor collection
-- Subcollections: Cole & Son, Thibaut, Schumacher, Ralph Lauren, York, Brewster, Arte, Koroseal, Phillip Jeffries, Maya Romanoff, Innovations, Elitis, Romo, Anna French, Graham & Brown
-
-### Shop by Type
-- Hero: newest grasscloth product image
-- Subcollections: Grasscloth, Vinyl, Glass Bead, Metallic, Murals, Fabric, PVC-Free, Silk, Linen, Cork
-
-### Shop by Style
-- Hero: newest floral product image
-- Subcollections: Floral, Geometric, Stripe, Damask, Toile, Animal, Botanical, Tropical, Modern, Traditional
-
-### Shop by Room
-- Hero: use a room setting image if available (check for products with 4+ images, last image is usually a room setting)
-- Subcollections: Bedroom, Living Room, Dining Room, Bathroom, Office, Nursery
-
-### DW Exclusive
-- Hero: newest Phillipe Romano product
-- Subcollections: Phillipe Romano, Architectural, Bespoke, Fentucci, Retro Walls, Steve Abrams Studios
-
-## Page HTML Structure
-```html
-<style>
-/* Same grid slider as dw-exclusive + category sections */
-.dw-cat-hero { position:relative; height:300px; overflow:hidden; border-radius:12px; margin-bottom:24px; }
-.dw-cat-hero img { width:100%; height:100%; object-fit:cover; }
-.dw-cat-hero-overlay { position:absolute; bottom:0; left:0; right:0; padding:30px; background:linear-gradient(transparent,rgba(0,0,0,0.8)); }
-.dw-cat-hero-title { color:#fff; font-size:32px; font-weight:700; }
-.dw-cat-hero-count { color:rgba(255,255,255,0.8); font-size:14px; margin-top:4px; }
-</style>
-
-<!-- Grid slider control -->
-<!-- Category sections with hero + subcollection grid -->
-<!-- Each subcollection card = newest product image + title overlay + count -->
-```
-
-## Shopify Tokens
-- Products/Collections: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Pages (create/update): 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
-- Menus/Navigation: <redacted:SHOPIFY_ORDERS_TOKEN>
-
-## Page IDs
-- dw-exclusive page: 26778599489 (can update or create new page)
-- For a NEW page: POST /admin/api/2024-01/pages.json with handle "collections-directory"
-
-## CRITICAL
-- VISUAL FIRST — every card must have an image, no text-only links
-- Images from NEWEST products (created_at DESC)
-- 1 second between API calls (store under load)
-- Same font as product pages: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif
-- Light/clean storefront style (white bg, not dark admin theme)
-- All CSS inline in body_html
-- Grid slider: 2-8 columns, default 4
-- Responsive: 2 cols on mobile
-- Start with top 100 collections, skip any with 0 products
diff --git a/tasks/done/00_URGENT-fix-hero-banner-collection.md b/tasks/done/00_URGENT-fix-hero-banner-collection.md
deleted file mode 100644
index b0dcb00..0000000
--- a/tasks/done/00_URGENT-fix-hero-banner-collection.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# URGENT: Fix Hero Banner — Broken Phyllis Morris Collection Link
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-Before making ANY changes, answer these questions by investigating the current state:
-
-1. **What collection handle does the hero slideshow currently link to?**
- - Read settings_data.json for section `home_slideshow_EXmkyD`
- - Confirm the broken URL: `/collections/phyllis-morris-classic-wallpaper-1`
-
-2. **Do Phyllis Morris products still exist in the store?**
- - Query Shopify API: `products.json?vendor=Phyllis+Morris&limit=5`
- - Count total active products for this vendor
- - If YES → create collection. If NO → replace hero slide entirely.
-
-3. **What other hero slides exist and are they all healthy?**
- - Read ALL slides in the hero slideshow section from settings_data.json
- - Verify each slide's link returns HTTP 200
- - Report any other broken slides
-
-4. **Is there an existing collection that could replace this?**
- - Search for any collection with "phyllis" in the handle
- - Check if any related collection exists that we can point to
-
-## What To Do (ONLY after answering above)
-
-### Option A: If Phyllis Morris products exist (28 found in audit)
-1. Create a new smart collection via Shopify API:
- ```
- POST /admin/api/2024-01/smart_collections.json
- {
- "smart_collection": {
- "title": "Phyllis Morris Classic Wallcovering",
- "rules": [{"column": "vendor", "relation": "equals", "condition": "Phyllis Morris"}],
- "published": true,
- "sort_order": "best-selling"
- }
- }
- ```
-2. Update the hero slideshow link to point to the new collection handle
-3. Verify the link works (HTTP 200)
-
-### Option B: If Phyllis Morris products DON'T exist
-1. Replace the hero slide with a working collection (e.g., one of the top sellers)
-2. OR disable the slide entirely in settings_data.json
-
-## Verification (MANDATORY)
-- Curl the homepage and confirm NO broken collection links
-- Curl the new/updated collection URL and confirm HTTP 200
-- Report: collection handle, product count, slide URL
-
-## Credentials
-- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-- Theme ID: 141654589491
diff --git a/tasks/done/00_URGENT-phillipe-romano-fullmonty.md b/tasks/done/00_URGENT-phillipe-romano-fullmonty.md
deleted file mode 100644
index b951acc..0000000
--- a/tasks/done/00_URGENT-phillipe-romano-fullmonty.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# URGENT: Phillipe Romano Full Monty — Specs + Room Settings + Metafields
-
-## Goal
-Run Full Monty pipeline on ALL Phillipe Romano products missing specs or room settings.
-Phillipe Romano = DW private label brand. 8,972 products on Shopify under vendor "Phillipe Romano".
-
-## IMPORTANT: Phillipe Romano is PRIVATE LABEL
-- Real vendor names MUST NEVER appear in public-facing content
-- Use "Phillipe Romano" as brand, not the real manufacturer
-- Check `private_label.real_vendor_name` metafield — if set, it IS private label
-
-## Step 1: Find PR Products Needing Work
-
-Query Shopify for products with <=2 images (no room settings) OR missing metafields:
-```bash
-# Use the vendor-full-monty-chain.js pattern but for Phillipe Romano
-# Or call Full Monty API directly for each product
-```
-
-Products token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Full Monty API: http://127.0.0.1:9851/api/run (POST with {"url": "PRODUCT_ID"})
-Auth: admin / DWSecure2024!
-
-## Step 2: Write a PR Full Monty Runner Script
-
-Create `/root/DW-Agents/vendor-scrapers/phillipe-romano-full-monty.js` following the pattern of:
-- `/root/DW-Agents/vendor-scrapers/vendor-full-monty-chain.js`
-- `/root/DW-Agents/vendor-scrapers/anna-french-full-monty.js`
-
-Key differences for PR:
-1. Vendor name on Shopify: "Phillipe Romano"
-2. Also check "Architectural Wallcoverings" (4 products)
-3. Catalog table: phillipe_romano_catalog (check also phillipe_romano_metals_catalog, phillipe_romano_elegante_catalog)
-4. Skip products already processed (full_monty_at IS NOT NULL in shopify_products)
-5. Skip products with >=3 images (already have room settings)
-
-## Step 3: Run It
-
-```bash
-pm2 start phillipe-romano-full-monty.js --name pr-full-monty
-```
-
-Progress file: /tmp/pr-fullmonty-progress.json
-Rate limit: 1s between Shopify API calls, Full Monty handles its own Gemini rate limiting
-
-## Step 4: Verify
-
-After processing, check:
-1. Metafields populated (custom.width, custom.material, custom.fire_rating, specs.*)
-2. Room settings generated (images count >= 3)
-3. AI rooms disclaimer metafield set: specs.ai_rooms_note = "Room Settings may not be to scale. Please inquire to verify repeats."
-4. Sample variant at $4.25
-5. Spin GIF present
-
-## Full Monty Pipeline (11 steps per product)
-1. Fetch product from Shopify
-2. Image clean (Gemini watermark check)
-3. VCC vendor check
-4. Silas validation
-5. Specs → metafields (DB + cross-namespace sync + Title Case)
-6. Interior design tagger (Gemini)
-7. Room settings (Gemini image gen) + AI disclaimer metafield
-8. Swatch spin GIF
-9. Sample variant ($4.25)
-10. Verify all metafields populated
-11. Report + DB stamp with per-step timestamps
-
-## DB
-postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-## CRITICAL
-- 1 second between ALL Shopify API calls
-- Full Monty agent is shared — other vendors may be using it. Wait if busy.
-- Products with full_monty_at already set get auto-skipped (fast)
-- Products with >=3 images skip room generation (saves Gemini cost)
-- EVERY product with AI room settings MUST get specs.ai_rooms_note metafield
-- Phillipe Romano is PRIVATE LABEL — never expose real vendor name
diff --git a/tasks/done/00_URGENT-scraper-blitz.md b/tasks/done/00_URGENT-scraper-blitz.md
deleted file mode 100644
index 094f961..0000000
--- a/tasks/done/00_URGENT-scraper-blitz.md
+++ /dev/null
@@ -1,61 +0,0 @@
-## URGENT: Full Vendor Scraper Blitz — Load ALL Catalogs
-
-Steve needs ALL vendor products loaded into PostgreSQL ASAP with full info (images, specs, prices).
-
-### CRITICAL RULES:
-- Do NOT create ANY new Shopify products
-- Do NOT push anything to Shopify
-- ONLY populate PostgreSQL catalog tables
-- rwltd_catalog = Reid Witlin (NOT Rebel Walls)
-
-### Phase 1: Run ALL existing scrapers (populate/refresh catalog tables)
-
-Scrapers are at `/root/DW-Agents/vendor-scrapers/scrape-*.js` and `scrape-*.py`.
-
-Run these in batches (5-8 at a time to avoid overloading). Use `timeout 600` for each.
-
-**Priority batch (0% images, high product count):**
-```bash
-cd /root/DW-Agents/vendor-scrapers
-timeout 600 node scrape-malibu.js &
-timeout 600 node scrape-rebelwalls.js &
-timeout 600 node scrape-miltonking.js &
-timeout 600 node scrape-harlequin.js &
-timeout 600 node scrape-bnwalls.js &
-wait
-```
-
-**Batch 2 (low image coverage):**
-```bash
-timeout 600 node scrape-wolfgordon.js &
-timeout 600 node scrape-glitter.js &
-timeout 600 node scrape-fabricut.js &
-timeout 600 node scrape-mindthegap.js &
-timeout 600 node scrape-timorous.js &
-wait
-```
-
-**Batch 3 (refresh existing):**
-```bash
-timeout 600 node scrape-kravet-brand.js &
-timeout 600 node scrape-coordonne.js &
-timeout 600 node scrape-osborne.js &
-timeout 600 node scrape-sandberg.js &
-timeout 600 node scrape-dedar.js &
-wait
-```
-
-### Phase 2: Update vendor_registry counts
-After scrapers finish, update total_products and products_with_images:
-```sql
--- For each catalog table, update the vendor_registry
-UPDATE vendor_registry SET total_products = (SELECT COUNT(*) FROM kravet_catalog) WHERE vendor_code = 'kravet';
--- Repeat for all vendors with catalog tables
-```
-
-### Phase 3: Report
-Query all catalog tables and report:
-- vendor_name, catalog_table, total rows, rows with image_url, rows with width, rows with mfr_sku
-- Sort by total products descending
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/00_URGENT-update-dw-exclusive-page.md b/tasks/done/00_URGENT-update-dw-exclusive-page.md
deleted file mode 100644
index 056160b..0000000
--- a/tasks/done/00_URGENT-update-dw-exclusive-page.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# URGENT: Redesign DW Exclusive Collections Page — Professional Grid with Slider
-
-## Page: https://www.designerwallcoverings.com/pages/dw-exclusive
-## Shopify Page ID: 26778599489
-## Token (pages): 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
-## Token (products): <redacted:SHOPIFY_ADMIN_TOKEN>
-## Priority: DO THIS TODAY — Steve requested 2026-03-21
-
-## Design Requirements
-
-### Grid Slider
-- Default: **4 columns** grid
-- User can slide between **2 columns** (large cards) and **8 columns** (compact grid)
-- Slider control at top right of the page — clean range input
-- Grid transitions smoothly (CSS grid with transition)
-- Responsive: on mobile collapses to 2 columns max regardless of slider
-
-### Each Collection Card
-- **Image**: Collection image or best product image, square aspect ratio, object-fit cover
-- **Title overlay**: Collection name at bottom of card with dark gradient overlay
-- **Product count badge**: Small pill showing "X products" in top right corner
-- **Hover effect**: Subtle scale + shadow lift
-- **Link**: Wraps entire card, links to /collections/{handle}
-
-### Visual Style — Professional & Elegant
-- Background: White/off-white (#f8f7f5) — NOT dark theme (this is storefront, not admin)
-- Cards: White with subtle shadow, rounded corners (8px)
-- Typography: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif
-- Collection title: 16px semibold, dark (#1a1a1a)
-- Product count: 12px, muted (#888), small pill badge
-- Section headers: 28px bold, clean divider line below
-- Spacing: 16px grid gap at 4 columns, adjusts with slider
-
-### Section Layout
-Keep 4 sections:
-1. **DW Private Label Lines** — Phillipe Romano, Architectural, Bespoke, Fentucci, Retro
-2. **Signature Collections** — Exclusive partnerships and curated sets
-3. **Style & Specialty** — Type 2 Commercial, Glass Bead, European Import, etc.
-4. **Named & Statement Collections** — Themed/curated collections
-
-### Slider HTML (embed in page body_html)
-```html
-<div style="display:flex;justify-content:space-between;align-items:center;margin:20px 0;">
- <h2 style="font-size:28px;font-weight:700;color:#1a1a1a;margin:0;">Our Collections</h2>
- <div style="display:flex;align-items:center;gap:10px;">
- <span style="font-size:12px;color:#888;">Grid Size</span>
- <input type="range" id="dw-grid-slider" min="2" max="8" value="4" step="1"
- style="width:120px;accent-color:#1a1a1a;cursor:pointer;"
- oninput="document.querySelectorAll('.dw-grid').forEach(g=>g.style.gridTemplateColumns='repeat('+this.value+',1fr)')">
- </div>
-</div>
-```
-
-### Grid CSS (embed in page body_html)
-```css
-.dw-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; transition: all 0.3s ease; }
-.dw-card { position:relative; border-radius:8px; overflow:hidden; box-shadow:0 2px 8px rgba(0,0,0,0.08); transition: all 0.25s ease; aspect-ratio:1; }
-.dw-card:hover { transform:translateY(-4px); box-shadow:0 8px 24px rgba(0,0,0,0.12); }
-.dw-card img { width:100%; height:100%; object-fit:cover; }
-.dw-card-overlay { position:absolute; bottom:0; left:0; right:0; padding:12px 14px; background:linear-gradient(transparent,rgba(0,0,0,0.7)); }
-.dw-card-title { color:#fff; font-size:15px; font-weight:600; line-height:1.3; }
-.dw-card-count { position:absolute; top:8px; right:8px; background:rgba(255,255,255,0.9); color:#555; font-size:11px; padding:3px 8px; border-radius:12px; font-weight:600; }
-@media(max-width:768px){ .dw-grid{grid-template-columns:repeat(2,1fr)!important;} }
-```
-
-## Steps
-
-1. Get current page HTML:
- `curl -s "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/pages/26778599489.json" -H "X-Shopify-Access-Token: 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"`
-
-2. Get all collections with product counts + images:
- `curl -s "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/custom_collections.json?limit=250&fields=id,title,handle,products_count,image" -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>"`
-
-3. Build the new page HTML:
- - Full-width CSS reset (keep existing)
- - Grid slider control at top
- - 4 sections with section headers
- - Each collection as a .dw-card with image, title overlay, product count badge
- - Only include collections with >0 products
- - Sort within each section by product count (largest first)
-
-4. Update via Shopify API:
- ```
- PUT /admin/api/2024-01/pages/26778599489.json
- { "page": { "id": 26778599489, "body_html": "..." } }
- ```
-
-5. Verify page loads: `curl -sI https://www.designerwallcoverings.com/pages/dw-exclusive`
-
-## Collection Categorization
-- **Private Label**: Any collection with "phillipe romano", "architectural", "bespoke", "fentucci", "retro walls", "steve abrams" in handle
-- **Signature**: "glambeads", "florentina", "graham-and-brown-exclusive", "china-seas", any with "exclusive" or "signature"
-- **Style & Specialty**: "type-2", "commercial", "glass-bead", "european-import", "grasscloth", "metallic"
-- **Named & Statement**: Everything else that has DW-specific curation
-
-## IMPORTANT
-- Keep page handle: dw-exclusive
-- All CSS must be inline in the body_html (Shopify pages don't load external CSS)
-- Images: use Shopify CDN URLs, not external URLs
-- Test that slider JS works in the Shopify page context (no external scripts)
-- Page must look professional — this is the storefront, NOT an admin dashboard
diff --git a/tasks/done/00_anna-french-full-monte.md b/tasks/done/00_anna-french-full-monte.md
deleted file mode 100644
index c496d2c..0000000
--- a/tasks/done/00_anna-french-full-monte.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# Anna French — Full Monte (All 8 Phases)
-
-## Context
-- Catalog: `anna_french_catalog` | 595 products
-- SKU prefix: DWTA- | Range: 65000+
-- Part of Thibaut group — products on thibautdesign.com
-- DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-## Current State
-- 595 products, all have images and width
-- 491/595 have vertical repeat (104 missing)
-- ZERO: AI colors, hex codes, DW SKUs, fire rating, Shopify pushes
-
-## Phase 1: Scrape Missing Specs from Thibaut
-- Thibaut site: https://www.thibautdesign.com/wallpaper?brand=Anna+French
-- For the 104 products missing repeat_v, scrape from thibautdesign.com product pages
-- Also scrape fire_rating for all 595 (currently zero)
-- Use existing Thibaut scraper patterns at /root/DW-Agents/vendor-scrapers/
-
-## Phase 2: Generate DW SKUs
-- Assign DW SKUs to all 595 products: DWTA-65000 through DWTA-65594
-- UPDATE anna_french_catalog SET dw_sku = 'DWTA-' || (65000 + row_number) WHERE dw_sku IS NULL
-
-## Phase 3: AI Enrichment (Gemini Vision)
-- Run: cd /root/DW-Agents/vendor-scrapers && node enrich-ai-tags.js anna_french --limit 595
-- Or write a custom script following the enrich-maya-ai.js pattern:
- - Fetch each image_url → base64 → Gemini 2.0 Flash
- - Extract: dominantColor, hexCode, allColors, styles, patterns, description
- - Also detect imageType (scan_swatch, photo_full, etc.) and physical dimensions
- - Update: ai_colors, ai_background_color, color_hex, ai_styles, ai_patterns
-- Rate limit: 600ms between calls
-- Track in enrichment_tracking table if it exists
-
-## Phase 4: Silas Validation
-- Check FULLPRODUCT requirements: image_url ✓, width ✓, mfr_sku ✓
-- Validate all 595 pass before proceeding
-
-## Phase 5: VCC Approval
-- Mark as VCC-ready (auto-approve for Thibaut/Anna French — established vendor)
-
-## Phase 6: Room Settings
-- Anna French = RESIDENTIAL (decorative, designer brand)
-- Generate 2 rooms per product: Living Room, Bedroom
-- Use Gemini 2.5 Flash Image generation
-- Use image physical dimensions from Phase 3 for correct tile scale
-
-## Phase 7: Spin Viewers
-- Generate spin GIF for each product
-- Use batch-spin-generator.js
-
-## Phase 8: Shopify Push with ALL Metafields
-- Title: {pattern_name} - {color_name} Wallpaper | Anna French
-- vendor: "Anna French"
-- product_type: "Wallpaper"
-- tags: collection, material, AI styles, AI patterns, color names, fire rating
-- Metafields: custom.manufacturer_sku, custom.color_hex, specs.width, specs.repeat_v, specs.material, specs.fire_rating, specs.collection
-- variant SKU: dw_sku, variant barcode: EMPTY (per MEMORY rules)
-- price: "0.00"
-- images: primary image + room_setting_images
-- Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Rate limit: 650ms, handle 429s + daily variant limit
-
-## Important
-- Anna French is NOT private label — use real brand name
-- Scrape newest SKUs first (per MEMORY: scrape newest to oldest)
-- Body HTML = description only, NO specs tables (specs in metafields only)
-- All colors as tags, hex in color_hex + metafield
diff --git a/tasks/done/00_archive-gb-europe-dupes.md b/tasks/done/00_archive-gb-europe-dupes.md
deleted file mode 100644
index 6bd2dc2..0000000
--- a/tasks/done/00_archive-gb-europe-dupes.md
+++ /dev/null
@@ -1,12 +0,0 @@
-DO NOT ask clarifying questions. Proceed autonomously.
-
-Archive all 528 "Graham & Brown Europe" duplicate products. The "Graham & Brown" versions are the correct ones.
-
-Steps:
-1. Search Shopify for all products with vendor = "Graham & Brown Europe" (GraphQL pagination)
-2. For each: set status to "archived" via shopify-queue-client (priority 2)
-3. Log progress to /tmp/gb-europe-archive.log
-4. Do NOT delete — archive only (status: archived)
-5. Count: total found, archived, errors
-
-Use shopify-queue-client at /root/DW-Agents/shared/shopify-queue-client.js
diff --git a/tasks/done/00_build-stock-check-agent.md b/tasks/done/00_build-stock-check-agent.md
deleted file mode 100644
index c0afd36..0000000
--- a/tasks/done/00_build-stock-check-agent.md
+++ /dev/null
@@ -1,51 +0,0 @@
-DO NOT ask clarifying questions. Proceed autonomously.
-
-Build Stock Check Agent — universal vendor trade portal inventory lookup.
-
-REQUIREMENTS:
-1. Create /root/DW-Agents/stock-check-agent/server.js on port 9663
-2. Express + Puppeteer for trade portal login
-3. Auth: admin / DWSecure2024!
-
-ENDPOINTS:
-- GET /health
-- GET /api/check/:mfrSku?vendor=york — check stock for a specific SKU
-- GET /api/vendors — list all vendors with credentials (from vendor_registry WHERE has_credentials=true)
-
-DATABASE:
-- Read credentials from: vendor_registry (login_url, has_credentials columns)
-- Read account creds from: vendor_registry (account_username column) + .env for passwords
-- Create table: stock_checks (id, mfr_sku, vendor, in_stock, quantity, lead_time, price, checked_at, source)
-- Cache results 1 hour — return cached if fresh
-
-VENDOR SUPPORT (Phase 1 — yorkwall.com):
-- York + Brewster share yorkwall.com (NopCommerce)
-- Login: POST to /Login/SignIn with username/password
-- Search: GET /catalogsearch/result/?q={sku} OR /quickviewdata?productId={nopId}
-- Stock info: look for "In Stock", "Out of Stock", quantity text, lead time
-- Jeffrey Stevens is private label through York — MFR SKU maps to York product
-
-VENDOR SUPPORT (Phase 2 — add more):
-- Thibaut: thibautdesign.com
-- Schumacher: fschumacher.com
-- Fabricut: fabricut.com
-- Kravet: kravet.com
-
-INTEGRATION:
-- Add to DW Central Chat: "check stock {sku}" triggers /api/check
-- Show on Shoppy: GET /api/stock/:sku proxies to stock-check-agent
-
-DB connection: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-PM2 name: stock-check-agent
-Port: 9663
-
-SHOPPY WEB PAGE INTEGRATION:
-- Add a "Stock Check" section to Shoppy dashboard (port 9887)
-- Input field: enter MFR SKU or DW SKU
-- Hit Enter → calls stock-check-agent → shows result:
- - In Stock / Out of Stock badge
- - Quantity available
- - Lead time
- - Last checked timestamp
- - Vendor name + portal link
-- Show recent checks history (last 20 from stock_checks table)
diff --git a/tasks/done/00_clean-body-specs.md b/tasks/done/00_clean-body-specs.md
deleted file mode 100644
index 62bd03a..0000000
--- a/tasks/done/00_clean-body-specs.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Clean Specs from Body HTML — ALL Vendors
-
-## Status: PAUSED (waiting for rename-cmd54 to finish, then restart)
-
-## What
-Strip specs tables, specification headers, and structured data from body_html on 322+ products. Body = description ONLY per CLAUDE.md rule #6.
-
-## Monitor
-```bash
-pm2 logs clean-body-specs --lines 20
-```
-
-## Restart After Rename Completes
-```bash
-pm2 restart clean-body-specs
-```
-
-## Script
-`/root/DW-Agents/scripts/clean-body-specs.js`
diff --git a/tasks/done/00_cmd54-west-coast-rename.md b/tasks/done/00_cmd54-west-coast-rename.md
deleted file mode 100644
index d180a33..0000000
--- a/tasks/done/00_cmd54-west-coast-rename.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Command54 Bulk Rename — East Coast → West Coast Beach Names
-
-## Status: IN-PROGRESS (PM2: rename-cmd54)
-
-## What
-Rename 789 Command54/Phillipe Romano products:
-1. Strip "Contact" from all titles
-2. Replace East Coast collection names with West Coast:
- - Amagansett → Malibu
- - Amelia Island → Laguna Beach
- - Bar Harbor → Carmel
- - Block Island → Half Moon Bay
- - Martha's Vineyard → Santa Cruz
- - Montauk → Big Sur
- - Kiawah → Pacifica
- - Kennebunkport → Coronado
- - Cape May → Santa Barbara
- - Hilton Head → Dana Point
- - Chatham → Mendocino
-3. Title format: "{WestCoast} - {Color} Wallcovering | Phillipe Romano"
-4. Strip MFR SKU from titles
-5. Clean body_html (remove Contact, specs, CMD54 references)
-6. Set collection metafield to "Hollywood Vinyls Vol. 1"
-
-## Monitor
-```bash
-pm2 logs rename-cmd54 --lines 20
-```
-
-## Script
-`/root/DW-Agents/scripts/rename-command54.js`
-
-## Rules
-- NEVER show CMD54, Command54, or "Contact" in any public content
-- Pattern name = color name (Apollo, Indigo, etc.)
-- Collection name = West Coast beach city
-- All products go into "Hollywood Vinyls Vol. 1" collection
diff --git a/tasks/done/00_create-79-missing-collections.md b/tasks/done/00_create-79-missing-collections.md
deleted file mode 100644
index b6a0c8f..0000000
--- a/tasks/done/00_create-79-missing-collections.md
+++ /dev/null
@@ -1,88 +0,0 @@
-# Create Smart Collections for 79 Vendors Missing Dedicated Collection Pages
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-1. **Is the collection audit report still current?**
- Read: `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json`
- Verify it has a "missing" array with vendor names and product counts.
-
-2. **Are any of these vendors private label or internal?**
- Skip these — do NOT create public collections for:
- - "Designer Laboratory" (internal)
- - "Steve Abrams Studios" (internal)
- - "DW Home" (internal brand)
- - "Sancar" (distributor — NEVER show distributor names publicly)
- - Any vendor in the private_label system
-
-3. **What's the smart collection rule pattern?**
- Use: `vendor equals "{Vendor Name}"` — this auto-populates with all matching products.
-
-## What To Do
-
-### Step 1: Read the audit report
-```bash
-cat /root/DW-Agents/yolo-agent/logs/collection-audit-report.json
-```
-Extract the "missing" array.
-
-### Step 2: For each vendor (skip internal/private label), create a smart collection:
-```bash
-curl -s -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/smart_collections.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{
- "smart_collection": {
- "title": "{Vendor Name} Wallcoverings",
- "rules": [{"column": "vendor", "relation": "equals", "condition": "{Vendor Name}"}],
- "published": true,
- "sort_order": "best-selling"
- }
- }'
-```
-
-**Title rules:**
-- If vendor sells wallcoverings: "{Vendor} Wallcoverings" (e.g., "Versa Designed Surfaces Wallcoverings")
-- If vendor sells trim: "{Vendor} Trim" (e.g., "Schumacher Trim")
-- If vendor sells fabric: "{Vendor} Fabrics"
-- If unclear: just "{Vendor}" with no suffix
-
-### Step 3: Rate limiting
-- Shopify API: max 2 requests/second
-- Add 600ms delay between each collection creation
-- Log each creation: vendor name, collection ID, product count
-
-### Step 4: Verify
-After creating all collections, verify each returns HTTP 200:
-```bash
-curl -s -o /dev/null -w "%{http_code}" "https://www.designerwallcoverings.com/collections/{handle}"
-```
-
-### Step 5: Save results
-Save to: `/root/DW-Agents/yolo-agent/logs/collections-created-report.json`
-Format:
-```json
-{
- "timestamp": "...",
- "created": [{"vendor": "X", "collectionId": 123, "handle": "x", "products": 50}],
- "skipped": [{"vendor": "Y", "reason": "internal/private label"}],
- "failed": [{"vendor": "Z", "error": "..."}]
-}
-```
-
-### Step 6: Slack notification
-```bash
-curl -X POST -H 'Content-type: application/json' \
- --data '{"text":"✅ *Collections Created*\n• Created: X new collections\n• Skipped: Y (internal/private)\n• Failed: Z\n• Report: /root/DW-Agents/yolo-agent/logs/collections-created-report.json"}' \
- "${SLACK_WEBHOOK_URL}"
-```
-
-## DO NOT
-- Create collections for Sancar, Designer Laboratory, Steve Abrams Studios, DW Home
-- Create collections for vendors with < 3 active products (not worth an SEO page)
-- Delete any existing collections
-- Modify any products
-
-## Credentials
-- Shopify API: <redacted:SHOPIFY_ORDERS_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-- Slack: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/00_create-hermosa-beach-impact.md b/tasks/done/00_create-hermosa-beach-impact.md
deleted file mode 100644
index 318f5b3..0000000
--- a/tasks/done/00_create-hermosa-beach-impact.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Create: Hermosa Beach - Impact Wallcovering | Phillipe Romano
-
-## Status: QUEUED (waiting for daily variant limit to reset)
-
-## Product Details
-- **Title**: Hermosa Beach - Impact Wallcovering | Phillipe Romano
-- **DW SKU**: DWPR-450870
-- **MFR SKU**: CMD54-SATIN-29 (NEVER in public content)
-- **Collection**: Hollywood Vinyls Vol. 1
-- **Width**: 54"
-- **Material**: Type II Vinyl
-- **Image**: https://command54.com/storage/app/uploads/product/medium/IplIVZfBhffuD9FHXZxTrfyFs3fmXNd5crEpiXBn.jpg
-
-## Retry Command
-```bash
-curl -s -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"product":{"title":"Hermosa Beach - Impact Wallcovering | Phillipe Romano","vendor":"Phillipe Romano","product_type":"Commercial","status":"draft","variants":[{"title":"Sample","sku":"DWPR-450870-Sample","price":"4.25","weight":0}],"images":[{"src":"https://command54.com/storage/app/uploads/product/medium/IplIVZfBhffuD9FHXZxTrfyFs3fmXNd5crEpiXBn.jpg"}],"metafields":[{"namespace":"custom","key":"manufacturer_sku","value":"CMD54-SATIN-29","type":"single_line_text_field"},{"namespace":"custom","key":"width","value":"54\"","type":"single_line_text_field"},{"namespace":"custom","key":"collection_name","value":"Hollywood Vinyls Vol. 1","type":"single_line_text_field"},{"namespace":"custom","key":"pattern_name","value":"Hermosa Beach","type":"single_line_text_field"}]}}'
-```
-
-## Why Queued
-Daily variant creation limit hit (1,000/24h rolling). Retry after ~6 hours.
diff --git a/tasks/done/00_crop-grs-images-to-square.md b/tasks/done/00_crop-grs-images-to-square.md
deleted file mode 100644
index 37f1897..0000000
--- a/tasks/done/00_crop-grs-images-to-square.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Crop GRS- Product Images to Largest Square
-
-## DO NOT ask clarifying questions — proceed autonomously.
-
-## What
-GRS- products (grasscloth/Designer Wallcoverings vendor) have manufacturer logos in a strip at the top or bottom of their images. Crop ALL GRS- product images to the largest possible square to remove the logo strip.
-
-## How
-
-### Step 1: Find all GRS products on Shopify
-Search for products with "GRS" in handle, tags, or SKU:
-```bash
-# Use GraphQL to find all products with GRS in handle
-curl -s -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"query":"{ products(first: 250, query: \"tag:grs OR sku:GRS\") { edges { node { id title handle images(first: 5) { edges { node { id src width height } } } } } } }"}'
-```
-
-Also search by vendor "Designer Wallcoverings" with GRS in handle:
-```
-GET /products.json?vendor=Designer+Wallcoverings&limit=250
-```
-Filter for handles containing "grs".
-
-### Step 2: For each product image, crop to square
-1. Download the image
-2. Get dimensions (W x H)
-3. If already square, skip
-4. Crop to the largest square:
- - If W < H (portrait): crop center vertically → (0, (H-W)/2, W, (H-W)/2 + W)
- - If W > H (landscape): crop center horizontally → ((W-H)/2, 0, (W-H)/2 + H, H)
-5. Use ImageMagick or Python Pillow:
-```bash
-# ImageMagick center crop to square
-convert input.jpg -gravity center -crop WxW+0+0 +repage output.jpg
-# Where W = min(width, height)
-```
-
-### Step 3: Upload cropped image back to Shopify
-```bash
-# Delete old image
-DELETE /admin/api/2024-01/products/{product_id}/images/{image_id}.json
-
-# Upload new cropped image
-POST /admin/api/2024-01/products/{product_id}/images.json
-{"image": {"attachment": "BASE64_DATA", "filename": "product.jpg"}}
-```
-
-### Step 4: Rate limiting
-- 500ms between API calls
-- Process in batches of 50
-- Write progress to /tmp/grs-crop-progress.log
-
-## DO NOT
-- Crop images that are already square (aspect ratio ~1.0)
-- Delete the original without uploading the replacement first
-- Process non-GRS products
-
-## Verification
-After processing, spot-check 5 products to verify:
-- Image is square
-- No logo visible
-- Image quality preserved
-
-## Slack Notification
-```bash
-curl -X POST -H 'Content-type: application/json' \
- --data '{"text":"✂️ *GRS Image Crop Complete*\n• Products processed: X\n• Images cropped: Y\n• Already square: Z"}' \
- "${SLACK_WEBHOOK_URL}"
-```
-
-## Credentials
-- Shopify: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
diff --git a/tasks/done/00_dwjs-enrich-specs.md b/tasks/done/00_dwjs-enrich-specs.md
deleted file mode 100644
index 3455b46..0000000
--- a/tasks/done/00_dwjs-enrich-specs.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# Jeffrey Stevens (DWJS) — Push Specs + Scrape Missing Width/Repeat
-
-## DO NOT ask clarifying questions — proceed autonomously.
-
-## Current State (3,165 products)
-- Width: 1,676 (53%) — 1,489 need scraping
-- Repeat: 1,357 (43%) — 1,808 need scraping
-- Fire Rating: 3,165 (100%) — DONE
-- Material: 1,813 (57%) — scrape missing
-- Match Type: 3,165 (100%) — DONE
-- Coverage: 3,165 (100%) — DONE (this is the roll length)
-
-## PHASE 1: Push existing specs to Shopify NOW (1,676+ products already have data)
-
-For ALL products in jeffrey_stevens_catalog, push available specs to Shopify metafields.
-
-### Spec Format for Shopify
-The `global.Width` metafield should be formatted as: `{width}" x {coverage}`
-Example: `27" x 13.5 feet` (width = 27, coverage = 13.5 ft)
-This is the CORRECT format per Steve.
-
-### Metafields to push:
-```javascript
-metafields = [
- // Width as roll size: "{width}" x {coverage}"
- { namespace: 'global', key: 'Width', value: width + '" x ' + coverage },
- { namespace: 'custom', key: 'width', value: width + ' inches' },
- // Repeat
- { namespace: 'global', key: 'Repeat', value: repeat_v ? repeat_v + '"' : 'None' },
- { namespace: 'global', key: 'Vert-Rpt', value: repeat_v || 'None' },
- // Fire Rating — CRITICAL: must be "Class A Fire Rated", NOT "Type II"
- // "Type II" is a DURABILITY standard (commercial wallcovering weight), NOT a fire rating
- // DB has correct value: "Class A Fire Rated" for 3,000 products
- { namespace: 'custom', key: 'fire_rating', value: fire_rating }, // "Class A Fire Rated"
- { namespace: 'global', key: 'Fire-Rating', value: fire_rating },
- // Material
- { namespace: 'custom', key: 'material', value: material },
- { namespace: 'product', key: 'material', value: material },
- // Match Type
- { namespace: 'global', key: 'MATCH', value: match_type || 'Random Match' },
-]
-```
-
-### CRITICAL FIX: Fire Rating for ALL DWJS
-ALL DWJS products: `fire_rating = "Class A Fire Rated"`
-Overwrite any existing `custom.fire_rating = "Type II"` with `"Class A Fire Rated"`.
-
-### Width Classification Rules (DO NOT assign Type II by width alone):
-- 20.5" → Residential wallcovering
-- 27" → Mostly residential wallcovering
-- 36" → Often textile but still a wallcovering (NOT necessarily commercial)
-- 54" → Almost always commercial, but NOT necessarily Type II
-
-Type II is determined by the VENDOR SPEC SHEET, not by width. Do NOT auto-assign Type II.
-Only set Type II if the vendor explicitly states it in their specs.
-```
-```
-
-### How to match DWJS products to Shopify:
-- Search by variant SKU containing "DWJS-{number}"
-- OR by vendor "Jeffrey Stevens"
-- Use Shopify REST: `GET /products.json?vendor=Jeffrey+Stevens&limit=250` (paginate)
-
-### Rate limit: 2 req/s. Process 500 per YOLO run.
-
-## PHASE 2: Scrape missing width/repeat from York trade portal
-
-For products WHERE width IS NULL OR repeat_v IS NULL:
-1. Use York trade portal (yorkwall.com) or construct product URL
-2. Fetch product page, extract specs
-3. Update jeffrey_stevens_catalog
-4. Then push to Shopify (same as Phase 1)
-
-Auth for York: check `/root/DW-Agents/vendor-scrapers/york-cookies.json`
-NopCommerce quickview: `GET https://www.yorkwall.com/quickviewdata?productId={nopId}`
-
-## DB
-- Table: jeffrey_stevens_catalog
-- postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-## Shopify
-- Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-
-## Progress
-Write to /tmp/dwjs-specs-enrich.log
diff --git a/tasks/done/00_dwpr-remove-specs-from-body.md b/tasks/done/00_dwpr-remove-specs-from-body.md
deleted file mode 100644
index a39298d..0000000
--- a/tasks/done/00_dwpr-remove-specs-from-body.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# URGENT: Remove ALL Specs from Body HTML — DWPR (Phillipe Romano) Products
-
-## Rule
-Body HTML = description ONLY. NO specs tables, NO specification lists, NO metafield data in body. EVER.
-
-## Problem
-All ~16,000 Phillipe Romano products have specs tables in body_html like:
-```html
-<table style="width:100%;border-collapse:collapse...">
- <tr><td>Width</td><td>54"</td></tr>
- <tr><td>Material</td><td>Type II Vinyl</td></tr>
- ...
-</table>
-```
-
-## Fix
-For EACH Phillipe Romano product on Shopify:
-
-1. GET product body_html
-2. Extract the description paragraph (text before the <table> tag)
-3. Strip ALL <table>...</table> elements from body_html
-4. Strip ALL <ul>/<ol> spec lists that contain spec labels (Width, Material, Repeat, etc.)
-5. Keep ONLY the description paragraph(s) — the text that describes the product
-6. If no description exists, generate one: "{PatternName} by Phillipe Romano. A premium wallcovering designed for lasting beauty in commercial, hospitality, and residential interiors."
-7. PUT the cleaned body_html back
-
-## Execution
-```bash
-# Use Shopify API to iterate all Phillipe Romano products
-# Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-# Store: designer-laboratory-sandbox.myshopify.com
-# GET /products.json?vendor=Phillipe+Romano&limit=250&fields=id,title,body_html
-# Rate limit: 520ms between updates
-
-# For each product:
-# 1. Parse body_html
-# 2. Remove <table>...</table> blocks
-# 3. Remove <style>...</style> blocks
-# 4. Remove inline styles (dark theme colors like #e0e0e0, #ccc)
-# 5. Keep only <p> and <div> description text
-# 6. Wrap in clean <div class="product-description">
-# 7. PUT updated body_html
-```
-
-## Important
-- Do NOT lose the description text — only remove the specs
-- Process in batches of 250
-- Log progress every 100 products
-- Count how many had specs removed vs already clean
-- Handle 429 rate limits with retry
diff --git a/tasks/done/00_enrich-thibaut-af.md b/tasks/done/00_enrich-thibaut-af.md
deleted file mode 100644
index e8aa048..0000000
--- a/tasks/done/00_enrich-thibaut-af.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# enrich-ai-tags — Thibaut + Anna French
-
-Run AI enrichment on both vendors. Uses enrichment_tracking dedup — zero duplicate calls.
-
-```bash
-cd /root/DW-Agents/vendor-scrapers
-
-# Thibaut first (5,992 products)
-node enrich-ai-tags.js thibaut --limit 6000 --budget 10
-
-# Anna French (595 products)
-node enrich-ai-tags.js anna_french --limit 600 --budget 5
-```
-
-This extracts for EVERY product:
-- ALL colors with hex codes + percentages
-- Background color + hex
-- Styles, patterns, material
-- Image type (scan/photo) + physical dimensions
-- Feeds into color dots, Shop by Color collections, metafields
diff --git a/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md b/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md
deleted file mode 100644
index 653680f..0000000
--- a/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Fix Fabric Titles + Remove Trim from New Arrivals
-
-## DO NOT ask clarifying questions — proceed autonomously.
-
-## Task 1: Remove ALL Trim products from New Arrivals collection
-
-New Arrivals collection ID: 167327760435
-
-1. Get all products in New Arrivals with product_type "Trim" using Shopify REST API pagination
-2. For each Trim product, remove it from the New Arrivals collection using the Collect API:
- - GET /admin/api/2024-01/collects.json?collection_id=167327760435&product_id={id}
- - DELETE /admin/api/2024-01/collects/{collect_id}.json
-3. If it's a smart collection, the rule may auto-include them — check the collection rules:
- - GET /admin/api/2024-01/smart_collections/167327760435.json
- - If the rules don't exclude Trim, add a rule: product_type NOT_EQUALS Trim
-4. Rate limit: 500ms between API calls
-5. Report: how many trim products removed
-
-## Task 2: Fix 80+ Fabric titles with "Fabrics" in the name
-
-1. Search: GET /products.json?product_type=Fabric&limit=250 (paginate)
-2. Filter for titles containing "Fabrics" (the word, not "Fabric")
-3. For each, fix title: replace "Fabrics" with "Fabric"
- - "Akio Fabrics – Black | Thibaut" → "Akio - Black Fabric | Thibaut"
- - "Ralph Lauren Fabrics" in title → keep "Ralph Lauren" as vendor, remove "Fabrics" from pattern name
-4. PUT /products/{id}.json with corrected title
-5. Rate limit: 500ms
-
-## Task 3: Fix 50 Fabric products with "Wallcovering" in title
-
-These are products with product_type "Fabric" but "Wallcovering" in the title — wrong label.
-1. Check if they're actually fabrics or wallcoverings:
- - Wolf Gordon: could be BOTH fabric and wallcovering — check tags
- - Schumacher: check tags for "Fabric" tag
- - If tagged Fabric and product_type is Fabric → replace "Wallcovering" with "Fabric" in title
- - If it's actually a wallcovering miscategorized → change product_type to "Wallcovering"
-2. The Adriano products (Phillipe Romano) with "Wallcovering" in title but product_type "Fabric" are likely wallcoverings — change product_type to "Wallcovering"
-
-## Title Format Rules
-- Pattern: `{PatternName} - {Color} {ProductType} | {Vendor}`
-- ProductType for fabrics: "Fabric" (not "Fabrics")
-- NEVER "Wallcovering" in a Fabric product title
-- NEVER "Fabrics" (plural) — always "Fabric" (singular)
-
-## Verification
-After all fixes:
-- Count products in New Arrivals with product_type Trim (should be 0)
-- Count products store-wide with "Fabrics" in title and product_type Fabric (should be 0)
-- Send Slack notification with results
-
-## Credentials
-- Shopify: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-- Slack: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/00_fix-ledger-modal-flash.md b/tasks/done/00_fix-ledger-modal-flash.md
deleted file mode 100644
index 3c1d0bd..0000000
--- a/tasks/done/00_fix-ledger-modal-flash.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Fix YOLO Ledger Dashboard — Modal Data Flashing
-
-## Problem
-The task ledger at http://45.61.58.125:9670/ledger auto-refreshes and destroys open modal state. When you click a task to see details, the data flashes and disappears on the next refresh cycle.
-
-## Fix
-Edit /root/DW-Agents/yolo-agent/dashboard/task-ledger.html:
-1. Track which task modals are open in a JS variable
-2. On refresh, only update the data content — don't rebuild the entire DOM
-3. If a modal is open, preserve its state and content across refreshes
-4. Use a diff approach: compare new data with existing DOM, only update changed rows
-5. Or simpler: pause auto-refresh while any modal/detail panel is open
-
-The simplest fix: add a flag `var isModalOpen = false;` — when true, skip the refresh interval. Set it true on click, false on close.
diff --git a/tasks/done/00_fix-malibu-body-dedup.md b/tasks/done/00_fix-malibu-body-dedup.md
deleted file mode 100644
index a4c5b21..0000000
--- a/tasks/done/00_fix-malibu-body-dedup.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Fix ALL Malibu Wallcovering Products — Remove Duplicate Descriptions + Specs from Body
-
-## Problem
-Malibu products have body_html with:
-1. Description paragraph
-2. "Pattern - Color by Malibu Wallcovering" line
-3. SAME description duplicated again
-4. Specs table (<table>)
-
-## Fix
-For EACH Malibu Wallpaper product on Shopify:
-1. GET body_html
-2. Extract the FIRST <p> paragraph (the description)
-3. Remove everything else: duplicate paragraphs, specs tables, "by Malibu Wallcovering" line
-4. Wrap in clean: `<div class="product-description"><p>{description}</p></div>`
-5. PUT updated body_html
-
-## Detection
-- Find first <p>...</p> that has 50+ chars (the real description)
-- Strip ALL <table>...</table>
-- Strip duplicate paragraphs (compare text content, keep first only)
-- Strip "{Pattern} by Malibu Wallcovering" lines
-
-## Shopify
-- Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- GET /products.json?vendor=Malibu+Wallpaper&limit=250&fields=id,title,body_html
-- Rate limit: 520ms
-- Also check vendor=Malibu+Wallcovering (might be variant)
-
-## Report
-Count: total products, bodies cleaned, already clean
diff --git a/tasks/done/00_fix-thibaut-af-fabrics.md b/tasks/done/00_fix-thibaut-af-fabrics.md
deleted file mode 100644
index 5839ba7..0000000
--- a/tasks/done/00_fix-thibaut-af-fabrics.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Fix ALL Thibaut & Anna French Fabrics — Body, Tags, SKUs, Packaging
-
-## Rules — MANDATORY
-1. Body = description ONLY. Remove ALL specs tables, spec lists, <table> blocks
-2. Tags: REMOVE "Wallpaper", "Wallcovering", "Trending Wallpaper Collection 2026", "Double Roll", MFR SKUs (AF9861, T12345), "Background Color {X}" prefix
-3. Tags: ADD "Fabric", "Upholstery" and/or "Drapery" where applicable
-4. SKU prefix: AF products → DWAF- (not DWTT-)
-5. Vendor: AF prefix → "Anna French" (not Thibaut)
-6. Product type: "Fabric" (not Wallcovering)
-7. Title: NO MFR SKU, format: "{PatternName} - {Color} Fabric | {Vendor}"
-8. Packaging: NEVER "Double Roll" — fabrics are "Sold Per Yard"
-9. Remove "Thibaut" from tags on Anna French products (keep only "Anna French")
-
-## How to Identify Fabrics
-Products are fabrics if ANY of these are true:
-- Title contains "Fabric", "Fabrics", "Embroidery", "Upholstery", "Drapery", "Trim"
-- product_type already = "Fabric"
-- Tags contain "Fabric", "Upholstery", "Drapery", "Cotton", "Linen", "Embroidery"
-- Width = 54" or 56" (typical fabric width, wallcoverings are usually 20.5"-36")
-
-## Execution
-1. Scan ALL Thibaut vendor products: GET /products.json?vendor=Thibaut&limit=250
-2. Scan ALL Anna French vendor products: GET /products.json?vendor=Anna+French&limit=250
-3. For each product that IS a fabric:
- a. Clean body_html — keep only description paragraphs
- b. Fix tags — remove wallpaper refs, add fabric tags
- c. Fix vendor if AF prefix → Anna French
- d. Fix SKU prefix if AF → DWAF-
- e. Fix product_type → Fabric
- f. Fix title format
-4. Rate limit: 520ms between updates
-
-## Shopify
-- Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-
-## Report
-Count: fabrics found, bodies cleaned, tags fixed, SKUs changed, vendors changed
diff --git a/tasks/done/00_fix-thibaut-titles-vendor.md b/tasks/done/00_fix-thibaut-titles-vendor.md
deleted file mode 100644
index 33c8ed2..0000000
--- a/tasks/done/00_fix-thibaut-titles-vendor.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Fix Thibaut Product Titles — Remove SKUs + Fix Anna French Attribution
-
-## Rules
-1. NEVER put MFR SKU in product title (e.g., "(af9637)", "(T75490)")
-2. AF prefix = Anna French, NOT Thibaut — fix vendor to "Anna French"
-3. Products with "Fabric" or "Embroidery" = product_type "Fabric", not "Wallcovering"
-4. Title format: `{PatternName} - {Color} {ProductType} | {Vendor}`
-
-## Task
-Scan all Thibaut products on Shopify (3,794 total):
-
-1. Find products with MFR SKU in title (regex: parentheses containing alphanumeric pattern numbers)
- - Remove the SKU from the title
-
-2. Find products where MFR SKU starts with "AF" — these are Anna French, not Thibaut
- - Change vendor to "Anna French"
- - Update title to use "| Anna French" instead of "| Thibaut"
-
-3. Find products with "Fabric" or "Embroidery" in title but product_type = "Wallcovering"
- - Change product_type to "Fabric"
- - Remove "Wallcovering" from tags, add "Fabric"
-
-## Shopify API
-- Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-- Rate limit: 650ms between updates
-- GET /products.json?vendor=Thibaut&limit=250&fields=id,title,tags,product_type,variants
-- PUT /products/{id}.json to update
-
-## Report
-Log: how many titles fixed, how many vendor changes, how many type changes
diff --git a/tasks/done/00_full-monte-phase3-all.md b/tasks/done/00_full-monte-phase3-all.md
deleted file mode 100644
index f426e8d..0000000
--- a/tasks/done/00_full-monte-phase3-all.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# Full Monte Phase 3 — AI Enrichment ALL Vendors
-
-Run Phase 3 (Gemini AI vision) on ALL vendors in priority order. Budget: $227 max.
-Uses enrichment_tracking dedup — will NOT re-process already enriched products.
-
-## Execution
-```bash
-cd /root/DW-Agents/full-monte
-
-# Tier 1 first (already on Shopify, high value)
-node full-monte-batch.js --vendor kravet --phase 3 --limit 10000
-node full-monte-batch.js --vendor thibaut --phase 3 --limit 6000
-node full-monte-batch.js --vendor schumacher --phase 3 --limit 5200
-node full-monte-batch.js --vendor phillip_jeffries --phase 3 --limit 4400
-node full-monte-batch.js --vendor cole_son --phase 3 --limit 1000
-
-# Tier 2 (large catalogs)
-node full-monte-batch.js --vendor brewster --phase 3 --limit 9000
-node full-monte-batch.js --vendor york --phase 3 --limit 5500
-node full-monte-batch.js --vendor arte --phase 3 --limit 2000
-node full-monte-batch.js --vendor elitis --phase 3 --limit 1200
-node full-monte-batch.js --vendor romo --phase 3 --limit 2600
-node full-monte-batch.js --vendor koroseal --phase 3 --limit 2600
-
-# Anna French (specifically requested)
-node full-monte-batch.js --vendor anna_french --phase 3 --limit 600
-
-# Tier 3 — remaining large vendors
-node full-monte-batch.js --vendor marburg --phase 3 --limit 9300
-node full-monte-batch.js --vendor as_creation --phase 3 --limit 6200
-node full-monte-batch.js --vendor designtex --phase 3 --limit 2900
-node full-monte-batch.js --vendor holly_hunt --phase 3 --limit 2100
-node full-monte-batch.js --vendor maharam --phase 3 --limit 1600
-node full-monte-batch.js --vendor fabricut --phase 3 --limit 1400
-node full-monte-batch.js --vendor ralph_lauren --phase 3 --limit 3100
-node full-monte-batch.js --vendor graham_brown --phase 3 --limit 3200
-node full-monte-batch.js --vendor andrew_martin --phase 3 --limit 1000
-node full-monte-batch.js --vendor harlequin --phase 3 --limit 800
-node full-monte-batch.js --vendor carlisle --phase 3 --limit 800
-node full-monte-batch.js --vendor sandberg --phase 3 --limit 900
-node full-monte-batch.js --vendor wolf_gordon --phase 3 --limit 300
-node full-monte-batch.js --vendor maya_romanoff --phase 3 --limit 100
-```
-
-## Rules
-- Gemini key: ${GOOGLE_API_KEY}
-- Rate limit: 600ms between calls
-- Dedup via enrichment_tracking — zero duplicate calls
-- Image type detection included (scan_swatch, photo_full, etc.)
-- Run Tier 1 first, then 2, then 3
-- If budget exceeded ($227), stop and report
-- Log all progress to /root/DW-Agents/logs/full-monte-phase3.log
diff --git a/tasks/done/00_hex-color-gemini-blitz.md b/tasks/done/00_hex-color-gemini-blitz.md
deleted file mode 100644
index a565036..0000000
--- a/tasks/done/00_hex-color-gemini-blitz.md
+++ /dev/null
@@ -1,29 +0,0 @@
-## Hex Color Gemini Blitz — Fill Missing color_hex via AI Vision
-
-Many products have images but no color_hex. Use Gemini vision to analyze product images and extract the dominant color as a hex code.
-
-### Priority vendors (most missing hex):
-- Marburg: 7,652 missing (have images)
-- Newwall: 7,602 missing
-- Cowtan & Tout: 5,011 missing (specs only vendor but color is a spec)
-- Schumacher: 3,894 missing
-- Scalamandre: 4,055 missing
-- PJ: 4,294 missing
-- Rebel Walls: 1,934 missing
-- Milton King: 2,464 missing
-
-### Approach:
-1. For each vendor, find products with image_url but no color_hex
-2. Use Gemini 2.0 Flash vision API to analyze each image:
- - Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- - Prompt: "What is the dominant background color of this wallpaper/wallcovering? Reply with ONLY a hex code like #A5B2C3"
- - Pass the image_url as an image part
-3. Update color_hex in the catalog table
-4. Rate limit: 10 req/sec (Gemini allows 60 RPM on free tier)
-5. Start with smallest vendors first for quick wins
-
-### CRITICAL:
-- Use Gemini for ALL image analysis (NEVER Claude vision)
-- API key for analysis: ${GOOGLE_API_KEY}
-- Do NOT push to Shopify
-- If Gemini returns a color name instead of hex, map it using color_hex_map table
diff --git a/tasks/done/00_norma-news-refresh-button.md b/tasks/done/00_norma-news-refresh-button.md
deleted file mode 100644
index c2f8559..0000000
--- a/tasks/done/00_norma-news-refresh-button.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Norma: Add News Refresh Button + Nikki Settings for News Crawl Targets
-
-## Project: /root/Projects/Norma
-## Port: 7400 | PM2: norma-email
-## Auth: admin / DWSecure2024!
-## DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/sdcc
-## Read /root/Projects/Norma/CLAUDE.md for full context first.
-
-## Task 1: Add Refresh Button to News Page
-
-Find the news page component (likely in `app/` or `components/`) and add a visible "Refresh News" button that:
-- Calls `POST /api/cron/ingest-news` with Basic auth (admin/DWSecure2024!)
-- Shows a loading spinner while refreshing
-- Shows toast with results (inserted/skipped count)
-- Use the existing `.btn .btn-primary` classes from globals.css
-- Use `RefreshCw` icon from lucide-react
-
-## Task 2: Nikki Settings — News Crawl Targets
-
-Add a settings section in the Nikki tier that lets org staff configure WHO gets monitored in news crawls. This is for nonprofit staff who want to track mentions of their team members.
-
-Requirements:
-1. Create a `news_watch_people` table in the sdcc database:
- ```sql
- CREATE TABLE IF NOT EXISTS news_watch_people (
- id SERIAL PRIMARY KEY,
- org_id INTEGER REFERENCES organizations(id),
- person_name TEXT NOT NULL,
- title TEXT,
- keywords TEXT[], -- additional search terms
- is_active BOOLEAN DEFAULT true,
- created_at TIMESTAMPTZ DEFAULT NOW()
- );
- ```
-
-2. Add a Settings section in the Nikki UI with:
- - List of people being monitored (name, title, keywords)
- - Add new person form (name, title, optional keywords)
- - Toggle active/inactive
- - Delete button
-
-3. Update the news ingest (`lib/ingest-news.ts`) to also search for these people's names when crawling
-
-4. Example seed data: "Natalia Abrams, President" with keywords ["student debt", "SDCC", "Student Debt Crisis Center"]
-
-## Design
-- Use Norma's dark theme CSS variables (see CLAUDE.md)
-- Card layout with .card class
-- Icons from lucide-react (Users, Plus, Trash2, ToggleLeft/Right)
-- Follow existing component patterns in components/settings/
-
-## IMPORTANT
-- Dark theme ONLY
-- Use CSS variables, never hardcode colors
-- All database changes go through lib/db.ts query function
-- Add audit logging for all changes
diff --git a/tasks/done/00_overnight-status-watch.md b/tasks/done/00_overnight-status-watch.md
deleted file mode 100644
index ae8c492..0000000
--- a/tasks/done/00_overnight-status-watch.md
+++ /dev/null
@@ -1,20 +0,0 @@
-Report current overnight pipeline state.
-
-Check and report these in one concise message:
-
-1. Gemini busyness sweep progress: `tail -3 /tmp/gemini-sweep-full.log`
- Count classified products: `psql ... -c "SELECT COUNT(*) FROM vendor_catalog WHERE busy_gemini IS NOT NULL"`
-
-2. Logo Hunter — for each vendor log in /tmp/logo-hunt-*.log, show last progress line. Report any vendors where logos_found >= 20.
- Query: `SELECT vendor_code, COUNT(*) total, COUNT(*) FILTER (WHERE has_logo) with_logo FROM logo_hunter GROUP BY vendor_code ORDER BY with_logo DESC`
-
-3. Quinn queue: `SELECT source_agent, COUNT(*) FILTER (WHERE status='pending') pending FROM shopify_api_queue GROUP BY source_agent HAVING COUNT(*) FILTER (WHERE status='pending') > 0 ORDER BY 2 DESC`
-
-4. Thibaut revive: `tail -2 /tmp/thibaut-revive.log`
-
-5. Any PM2 processes that are not "online": `pm2 list | grep -v online | head -10`
-
-If anything is crashed or errored, restart it via `pm2 restart <name>`.
-If a log shows consistent errors (>20% error rate), report it clearly.
-
-Keep the report under 200 words. No action needed unless there's a failure.
diff --git a/tasks/done/00_push-repeat-metafields-all-vendors.md b/tasks/done/00_push-repeat-metafields-all-vendors.md
deleted file mode 100644
index ddca39e..0000000
--- a/tasks/done/00_push-repeat-metafields-all-vendors.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# Push Pattern Repeat Metafields to Shopify — ALL Vendors
-Priority: P0
-Status: OPEN
-
-## Context
-~9,000 products on Shopify have repeat data in vendor_catalog.pattern_repeat but it's NOT pushed to Shopify `custom.pattern_repeat` metafield.
-
-Top vendors with repeat data in DB but missing on Shopify:
-- Thibaut: 1,791 products
-- China Seas: 1,460
-- York: 1,135
-- Versa DS: 881
-- Command54: 870
-- Mind the Gap: 761
-- British Walls: 497
-- Anna French: 375
-- Arte: 369
-- Hygge: 324
-
-## Task
-1. Query all on_shopify products with repeat in DB:
-```sql
-SELECT vc.dw_sku, vc.pattern_repeat, vc.vendor_code,
- sp.shopify_product_id
-FROM vendor_catalog vc
-JOIN shopify_products sp ON sp.dw_sku = vc.dw_sku
-WHERE vc.on_shopify = true
- AND vc.pattern_repeat IS NOT NULL AND vc.pattern_repeat != '' AND vc.pattern_repeat != '0'
- AND sp.shopify_product_id IS NOT NULL;
-```
-
-2. For each product, check if `custom.pattern_repeat` metafield already exists on Shopify (to avoid overwriting).
- Use bulkOperationRunQuery for efficiency:
-```graphql
-mutation {
- bulkOperationRunQuery(query: """
- { products(query: "status:active") {
- edges { node { id metafield(namespace: "custom", key: "pattern_repeat") { value } } }
- }}
- """) { bulkOperation { id status } }
-}
-```
-
-3. For products MISSING the metafield, push via batched metafieldsSet:
-```graphql
-mutation($metafields: [MetafieldsSetInput!]!) {
- metafieldsSet(metafields: $metafields) {
- metafields { id }
- userErrors { field message }
- }
-}
-```
- - Max 25 metafields per call
- - Rate limit: 2 req/s (wait 500ms between calls)
- - Use shopify_product_id as ownerId: `gid://shopify/Product/{id}`
-
-4. Log results: total pushed, total skipped (already had value), total failed.
-
-## Rules
-- ALL Shopify writes via GraphQL, batched 25 per call
-- Do NOT overwrite existing `custom.pattern_repeat` values
-- Do NOT touch Phillip Jeffries products (no AI tags rule)
-- Skip vendor_code = 'cowtan_tout' and 'colefax_fowler' (never touch)
diff --git a/tasks/done/00_schumacher-logo-cleanup-finish_DONE-orchestrator-died-work-succeeded.md b/tasks/done/00_schumacher-logo-cleanup-finish_DONE-orchestrator-died-work-succeeded.md
deleted file mode 100644
index 196c346..0000000
--- a/tasks/done/00_schumacher-logo-cleanup-finish_DONE-orchestrator-died-work-succeeded.md
+++ /dev/null
@@ -1,207 +0,0 @@
-# Schumacher logo-header cleanup — finish remaining batches
-
-Steve's session pushed clean images for ~645 of the 886 Schumacher products
-that had SCHUMACHER_WALLPAPER_ branded header images. The gather phase (Phase 1-2)
-finished collecting all 886 rows into `/tmp/schumacher-bulk-data-full.jsonl`.
-Pilot + batches 1/2/3/4 are in flight through Quinn. Your job is to:
-
-1. **Wait for batch 4 to finish draining through Quinn.**
-2. **Run batch 5** on the remaining products (rows 841+) to finish the push.
-3. **Run the verification scan** and report the final state.
-
-Everything you need is already staged in `/tmp`. No discovery or re-gather needed.
-
----
-
-## Context files (all in `/tmp`)
-
-| File | Purpose |
-|---|---|
-| `/tmp/schumacher-bulk-data-full.jsonl` | **886 gathered rows** — full input for enqueuer. DO NOT rebuild. |
-| `/tmp/schumacher-bulk-enqueue.js` | Batched mutation enqueuer — reads `/tmp/schumacher-bulk-data.jsonl` (note: WITHOUT `-full`) and pushes via Quinn |
-| `/tmp/schumacher-verify.js` | Post-run verification scan — counts still-branded products on Shopify |
-| `/tmp/schumacher-branded-list.json` | Original discovery scan output (886 products) |
-
-**IMPORTANT**: The enqueuer hard-codes `/tmp/schumacher-bulk-data.jsonl` as its input. So before running it, you MUST `cp /tmp/schumacher-bulk-data-full.jsonl /tmp/schumacher-bulk-data.jsonl`.
-
----
-
-## Step 1 — Wait for batch 4 to drain
-
-Batch 4 is currently running via process `schumacher-bulk-enqueue.js --live --from 643`.
-It may still have pending Quinn jobs. Poll until none remain:
-
-```bash
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c \
- "SELECT status, COUNT(*) FROM shopify_api_queue WHERE source_agent='schumacher-bulk' GROUP BY status;"
-```
-
-Wait until `pending` + `processing` rows reach 0 for the `schumacher-bulk` source_agent.
-
-**If Quinn gets stuck behind priority 1/3 jobs from other agents**, promote ours to priority 0:
-```bash
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c \
- "UPDATE shopify_api_queue SET priority=0 WHERE source_agent='schumacher-bulk' AND status='pending' AND priority>0;"
-```
-
-Also check batch 4's enqueuer log for completion message:
-```bash
-tail -5 /tmp/schumacher-bulk-enqueue-batch4.log
-```
-Look for `BULK RUN COMPLETE`.
-
----
-
-## Step 2 — Run batch 5 on remaining rows
-
-Target: rows 841 onwards of the eligible list (~43 rows, the ones gathered after batch 4 was enqueued).
-
-```bash
-# Stage the full gather data as the enqueuer input (it hard-codes the filename)
-cp /tmp/schumacher-bulk-data-full.jsonl /tmp/schumacher-bulk-data.jsonl
-
-# Run batch 5 live
-NODE_PATH=/root/DW-Agents/shopify-queue-worker/node_modules \
- timeout 1800 node /tmp/schumacher-bulk-enqueue.js --live --from 841 \
- > /tmp/schumacher-bulk-enqueue-batch5.log 2>&1 &
-```
-
-Immediately after enqueuer finishes enqueuing (within ~30s), promote jobs to priority 0 to jump Quinn's queue:
-```bash
-# Wait a moment for jobs to land, then promote
-sleep 10
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c \
- "UPDATE shopify_api_queue SET priority=0 WHERE source_agent='schumacher-bulk' AND status='pending' AND priority>0;"
-```
-
-Monitor batch 5 until it shows `BULK RUN COMPLETE` in its log. You may need to re-promote priorities after each stage (createMedia → metafieldsSet → productDeleteMedia):
-```bash
-tail -10 /tmp/schumacher-bulk-enqueue-batch5.log
-```
-
-If Quinn is slow, also re-promote:
-```bash
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c \
- "UPDATE shopify_api_queue SET priority=0 WHERE source_agent='schumacher-bulk' AND status='pending' AND priority>0;"
-```
-
-The enqueuer has 3 stages (create, metafieldsSet, delete). Batch 5 is small (~43 products, ~6-8 Quinn jobs total), should finish in under 5 minutes once Quinn picks it up.
-
----
-
-## Step 3 — Verification scan
-
-Once all schumacher-bulk jobs are done (no pending/processing), run the verification:
-
-```bash
-NODE_PATH=/root/DW-Agents/shopify-queue-worker/node_modules \
- timeout 300 node /tmp/schumacher-verify.js 2>&1 | tee /tmp/schumacher-verify.log
-```
-
-This re-scans every active Schumacher product on Shopify and counts how many still
-have `SCHUMACHER_WALLPAPER_` in any image URL. The number should be very small (ideally 0-3).
-
-If there ARE still-branded products, they'll be listed in `/tmp/schumacher-still-branded.json`.
-Common reasons for remaining:
-- Gemini rejected products (1 known — can't fix without a clean CDN image)
-- Products that were already fixed before our scan (1 known)
-- Any creates/deletes that failed silently
-
----
-
-## Step 4 — Report to Steve
-
-Post the final stats in a clear format. Include:
-
-- Total schumacher-bulk Quinn jobs run (success/fail counts):
- ```bash
- PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c \
- "SELECT status, COUNT(*) FROM shopify_api_queue WHERE source_agent='schumacher-bulk' GROUP BY status;"
- ```
-- vendor_catalog drift fix count (sum across all batch logs):
- ```bash
- grep "updated.*vendor_catalog" /tmp/schumacher-bulk-enqueue-batch*.log
- ```
-- Still-branded count from verify scan
-- Gather stats (CDN hits, Gemini successes, rejections):
- ```bash
- tail -20 /tmp/schumacher-bulk-gather-1.log /tmp/schumacher-bulk-gather-2.log | grep -A 10 "GATHER COMPLETE"
- ```
-- Any Quinn jobs that ended in `failed` or `dead` status
-
-Post the report to Slack if possible:
-```bash
-curl -X POST "https://hooks.slack.com/services/<steve-channel>" ...
-```
-Or just print to stdout and trust the YOLO log capture.
-
----
-
-## Rules / don'ts
-
-- **DO NOT re-run the gather** — it already finished and took ~30 minutes.
-- **DO NOT touch products outside the Schumacher vendor** — the enqueuer is scoped.
-- **DO NOT skip the priority promotion** — Quinn has 12k+ priority-3 backlog, our jobs will starve without promotion to 0.
-- **DO NOT modify the enqueuer script mid-run** — state batches are live.
-- **DO NOT hit Shopify REST directly** — all writes go through Quinn (already enforced by the enqueuer).
-- If batch 4 has failed jobs, flag them in the report but don't try to resurrect — just run batch 5 as planned.
-- If the verify scan shows more than 10 still-branded products, STOP and report — something went wrong and Steve needs to look.
-
-## Success criteria
-
-- Verify scan reports ≤ 5 still-branded Schumacher products.
-- 0 failed schumacher-bulk Quinn jobs.
-- Clear summary report posted with all stats.
-
----
-
-## Outcome (added 2026-04-11 post-mortem)
-
-**Status: ✅ WORK SUCCEEDED — orchestrator died, cleanup completed.**
-
-YOLO classified this task as `failed` 3 times (history: 04:42, 05:02, 05:03 UTC)
-and auto-retried it because each YOLO Claude instance ran the verify scan
-**before** waiting for batch 5's slow Stage 3 (productDeleteMedia, 439s) to
-drain through Quinn. They saw a transient 231 still-branded count, hit the
-playbook's "STOP if >10" rule, and exited non-zero. The actual work completed
-cleanly ~9 minutes after the first premature verify.
-
-### Final state (re-verified post-drain by interactive session)
-
-| Metric | Value |
-|---|---|
-| Total active Schumacher products | 1,696 |
-| Still branded | **1** |
-| Clean | 1,695 (99.9%) |
-| Success criteria | ≤ 5 still-branded → **MET** |
-
-### Quinn job results
-
-- 9/9 batch 5 jobs `done` (2 createMedia + 5 metafieldsSet + 2 deleteMedia)
-- 279 total `schumacher-bulk` jobs across all batches, **0 errors**
-- First done: 04:09:17 UTC | Last done: 05:01:46 UTC
-
-### The 1 remaining product
-
-- `pid=4523155554355` / `DWSW-5007380` / "Incandescence Damask Burnished Silver"
-- Skipped at gather time: `aiRejected: "text/logo/watermark covers pattern"`
-- Source CDN image (`cdn-webassets.schumacher.com/catalog/hd/5007380.jpg`)
- has a logo overlay covering the pattern — no clean upstream available
-- **Manual fix required**: source a clean asset (request from Schumacher,
- check sister vendor portals, or commission a re-photograph)
-
-### Evidence files (in /tmp at time of move)
-
-- `/tmp/schumacher-bulk-enqueue-batch5.log` — `BULK RUN COMPLETE` footer
-- `/tmp/schumacher-verify-rerun.log` — post-drain verify output (1 still-branded)
-- `/tmp/schumacher-still-branded.json` — single-row file with the unfixable product
-- `/tmp/schumacher-bulk-data-full.jsonl` — gather data with `ok:false` flag for DWSW-5007380
-
-### Lesson for future YOLO playbooks
-
-When a task includes a "wait for Quinn to drain THEN verify" sequence, the wait
-loop must be bounded by **both** a pending-count check AND a settle period
-(e.g. "0 pending for 60 consecutive seconds") — otherwise an in-flight
-deleteMedia stage can briefly look "done" between job transitions and trigger
-premature verification. Also: tasks that auto-retry should self-detect "the
-work I'm asked to do has already been done" before re-running batches.
diff --git a/tasks/done/00_set-pattern-names.md b/tasks/done/00_set-pattern-names.md
deleted file mode 100644
index bfdd0ea..0000000
--- a/tasks/done/00_set-pattern-names.md
+++ /dev/null
@@ -1,18 +0,0 @@
-DO NOT ask clarifying questions. Proceed autonomously.
-
-Set custom.pattern_name metafield on ALL active products that don't have it.
-
-Strategy:
-1. Check PostgreSQL vendor catalogs first — many have pattern_name column
-2. For products without DB data, parse from Shopify title:
- - "Pattern - Color Type | Vendor" → pattern = "Pattern"
- - "Pattern Color | Vendor" → need AI or manual split
-3. Push via shopify-queue-client at priority 4
-
-Title parsing rules:
-- If title has " - " → everything before it is the pattern
-- If title has " | " but no " - " → need to identify where pattern ends and color begins
-- Common color words to strip: Blue, Red, Green, Gold, Black, White, Grey, Beige, Brown, Cream, Ivory, Coral, Pink, Purple, Silver, etc.
-
-Database: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Queue: /root/DW-Agents/shared/shopify-queue-client.js
diff --git a/tasks/done/00_stock-check-agent.md b/tasks/done/00_stock-check-agent.md
deleted file mode 100644
index f569e1a..0000000
--- a/tasks/done/00_stock-check-agent.md
+++ /dev/null
@@ -1,36 +0,0 @@
-DO NOT ask clarifying questions. Proceed autonomously.
-
-Build a Stock Check Agent that can query live inventory from vendor trade portals.
-
-Phase 1 — MVP:
-1. Create /root/DW-Agents/stock-check-agent/server.js on port 9663
-2. Express server with auth (admin/DWSecure2024!)
-3. Endpoint: GET /api/check/:sku — accepts MFR SKU, returns live stock
-4. Start with York/Brewster (yorkwall.com NopCommerce trade portal)
-5. Use Puppeteer for trade portal login + stock lookup
-6. Store credentials in .env (YORK_USER, YORK_PASS)
-7. Cache results for 1 hour in PostgreSQL table stock_checks
-
-Phase 2 — Multi-vendor:
-8. Add vendor detection — route SKU to correct trade portal
-9. Support: York, Brewster, Thibaut, Schumacher, Fabricut
-10. Fallback: return "manual check required" for unsupported vendors
-
-Phase 3 — Integration:
-11. Add to DW Central Chat: "check stock DWJS-16327" triggers live lookup
-12. Add to VCC: stock badge on product cards
-13. Add to Shoppy: /api/stock/:sku endpoint
-
-DB table:
-CREATE TABLE stock_checks (
- id SERIAL PRIMARY KEY,
- mfr_sku TEXT NOT NULL,
- vendor TEXT NOT NULL,
- in_stock BOOLEAN,
- quantity INT,
- lead_time TEXT,
- checked_at TIMESTAMPTZ DEFAULT NOW(),
- source TEXT DEFAULT 'trade_portal'
-);
-
-PM2: stock-check-agent | Port: 9663
diff --git a/tasks/done/00a_brewster-line-manager.md b/tasks/done/00a_brewster-line-manager.md
deleted file mode 100644
index cc8cae0..0000000
--- a/tasks/done/00a_brewster-line-manager.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## P2-08: Brewster Line Manager
-
-Build a catalog management interface for Brewster products.
-
-### Steps:
-1. Check current Brewster catalog state:
- psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
- SELECT COUNT(*) as total, COUNT(CASE WHEN image_url IS NOT NULL THEN 1 END) as has_img,
- COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width,
- COUNT(DISTINCT collection) as collections
- FROM brewster_catalog;"
-
-2. Check if there's already a Brewster management UI on any port
-3. If not, create a simple Express app with:
- - Dashboard showing catalog stats (total, with images, with specs, collections)
- - Table view of products with search/filter
- - Ability to mark products as discontinued
-4. Use port 9600 (brewster-york-catalog) or find an available port
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
\ No newline at end of file
diff --git a/tasks/done/00a_maya-room-settings.md b/tasks/done/00a_maya-room-settings.md
deleted file mode 100644
index f003d2f..0000000
--- a/tasks/done/00a_maya-room-settings.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Maya Romanoff — Room Settings + Spin Viewers for 224 New Products
-
-## Context
-224 new Maya Romanoff products were just imported into maya_catalog (dw_sku LIKE 'DWMR-8%'). They need room setting images and spin viewer assets generated before Shopify push.
-
-## Task 1: Room Settings (Gemini Image Generation)
-Generate room setting images for the new Maya Romanoff products using Gemini 2.5 Flash Image generation.
-
-For EACH new product (WHERE dw_sku LIKE 'DWMR-8%' AND room_setting_images IS NULL):
-1. Fetch the product image from image_url
-2. Send to Gemini with prompt: "Create a photorealistic interior room visualization showing this wallcovering installed on the main wall. Show a luxurious {room_type} with complementary furniture and decor. The wallcovering should be the focal point covering the full back wall. Photorealistic, professional interior design photography, warm natural lighting."
-3. Generate 3 room types per product: living room, dining room, hotel lobby
-4. Save generated images to /root/DW-Agents/room-settings/maya-romanoff/
-5. Upload to Shopify CDN and store URLs in maya_catalog.room_setting_images as JSON array
-
-API: Gemini 2.5 Flash Image
-- Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent
-- Key: ${GOOGLE_API_KEY}
-- Config: generationConfig: { responseModalities: ['TEXT', 'IMAGE'] }
-- Rate limit: 600ms between calls
-
-Since 224 products × 3 rooms = 672 images is expensive, prioritize:
-- First batch: 50 most popular collections (Ajiro, Beadazzled, Craze, Island Weaves, Fleece Veil)
-- Skip products where image_url returns non-200
-
-## Task 2: Spin Viewers
-For each new product with an image:
-1. Generate 8 rotation frames using CSS transform perspective
-2. Create an HTML spin viewer at /root/DW-Agents/spin-viewers/maya-romanoff/{dw_sku}.html
-3. Each viewer shows the wallcovering pattern on a 3D-perspective wall that rotates
-
-Use the existing spin viewer template at /root/DW-Agents/vendor-command-center/spin-viewer-template.html if it exists, otherwise create a simple CSS 3D transform viewer.
-
-## Task 3: Update Shopify Push Task
-After room settings are generated, the 00_maya-romanoff-shopify-push.md task should include room_setting_images in the Shopify product payload as additional product images.
-
-## DB Connection
-postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-## Important
-- Use Gemini for ALL image generation — never DALL-E or other providers
-- Save images locally first, then reference in DB
-- Log progress every 10 products
diff --git a/tasks/done/00b_designtex-scraper-fix.md b/tasks/done/00b_designtex-scraper-fix.md
deleted file mode 100644
index c36b387..0000000
--- a/tasks/done/00b_designtex-scraper-fix.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## Designtex Scraper Fix — Stuck at 96 Products
-
-Session #79: Designtex scraper stuck at 96 products. Heavy SPA site needs enhanced approach.
-
-### Tasks:
-1. Check current designtex_catalog count: `SELECT COUNT(*) FROM designtex_catalog;`
-2. Read current scraper at `/root/DW-Agents/vendor-scrapers/scrape-designtex.js` (or find it)
-3. Investigate designtex.com — check if it's an SPA (React/Angular/Vue)
-4. Check if there's an API, JSON-LD, Algolia, or other data source
-5. If API found: build HTTP-only scraper (much faster than Puppeteer)
-6. If SPA only: add longer timeouts, waitForSelector, better pagination
-7. Run the improved scraper
-8. Report: old count vs new count, approach used
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/00b_harvest-dead-links.md b/tasks/done/00b_harvest-dead-links.md
deleted file mode 100644
index 8e48273..0000000
--- a/tasks/done/00b_harvest-dead-links.md
+++ /dev/null
@@ -1,17 +0,0 @@
-Run the dead-link harvester to clean up stale shopify_product_id references in catalog tables.
-
-```bash
-cd /root/DW-Agents/dex-dedup-agent
-node harvest-dead-links.js --write 2>&1 | tail -15
-```
-
-Report number of catalog rows NULL'd per vendor.
-
-After harvest, also check that cost-hunter has not queued jobs targeting newly-deleted products:
-```sql
-SELECT COUNT(*) FROM shopify_api_queue
-WHERE source_agent LIKE 'cost-hunter%' AND status='pending'
-AND payload::text ~ '"ownerId":\s*"gid://shopify/Product/[0-9]+"';
-```
-
-Short report (under 150 words) of what was cleaned.
diff --git a/tasks/done/00c_logo-hunter-summary.md b/tasks/done/00c_logo-hunter-summary.md
deleted file mode 100644
index f0375fb..0000000
--- a/tasks/done/00c_logo-hunter-summary.md
+++ /dev/null
@@ -1,30 +0,0 @@
-Generate a Logo Hunter summary across all scanned vendors.
-
-Query:
-```sql
-SELECT vendor_code,
- COUNT(*)::int AS scanned,
- COUNT(*) FILTER (WHERE has_logo)::int AS logos_found,
- ROUND(100.0 * COUNT(*) FILTER (WHERE has_logo) / NULLIF(COUNT(*),0), 1) AS logo_pct,
- ARRAY_AGG(DISTINCT logo_position) FILTER (WHERE has_logo AND logo_position IS NOT NULL) AS positions,
- ARRAY_AGG(DISTINCT suggested_action) FILTER (WHERE has_logo) AS actions
-FROM logo_hunter
-GROUP BY vendor_code
-HAVING COUNT(*) > 20
-ORDER BY logo_pct DESC NULLS LAST;
-```
-
-Report:
-1. Top 5 vendors by logo percentage (these need most cleanup)
-2. Total logos found across all vendors
-3. Most common logo positions (top-left? center? etc.)
-4. Most common suggested actions
-5. Any vendor where 100% of scanned products have logos (systemic problem)
-
-Save findings to memory as `/root/.claude/projects/-root/memory/project_logo_hunter_2026_04_14.md`.
-
-Include:
-- Count per vendor
-- Recommended next step (mass crop, scrape MFR, manual review)
-
-Keep under 300 words.
diff --git a/tasks/done/01_01_karin-room-settings.md b/tasks/done/01_01_karin-room-settings.md
deleted file mode 100644
index abe7029..0000000
--- a/tasks/done/01_01_karin-room-settings.md
+++ /dev/null
@@ -1 +0,0 @@
-Generate room settings for ALL Phillipe Romano / TWIL Karin products (175 products). Query shopify_products for vendor=Phillipe Romano with shopify_product_id > 7808000000000. For each product, call the room-setting-generator skill with 6 rooms: living_room, bedroom, dining_room, office, hotel_lobby, restaurant. These are plain texture grasscloth wallcoverings - use 5x5 tile scale. Prioritize products that do NOT have room_setting_images yet.
\ No newline at end of file
diff --git a/tasks/done/01_01_pm2-health-check.md b/tasks/done/01_01_pm2-health-check.md
deleted file mode 100644
index 2ad750b..0000000
--- a/tasks/done/01_01_pm2-health-check.md
+++ /dev/null
@@ -1 +0,0 @@
-Check PM2 status for all agents. Report any that are errored, stopped, or crash-looping (restart count > 50). Check disk space with df -h and memory with free -h. Report summary to /root/DW-Agents/logs/health-check.log with timestamp.
\ No newline at end of file
diff --git a/tasks/done/01_01_silas-violations-report.md b/tasks/done/01_01_silas-violations-report.md
deleted file mode 100644
index deb7bbe..0000000
--- a/tasks/done/01_01_silas-violations-report.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Read /root/DW-Agents/logs/silas-30day-audit-violations.json. Count violations by vendor and by error type. Report top 10 vendors with most violations. For vendors with ALL products violating (missing images AND missing mfr_sku), these are likely incomplete imports that should be archived on Shopify. Generate a summary report to /root/DW-Agents/logs/silas-action-report.json with: vendor, violation_count, recommended_action (archive/enrich/investigate). Do NOT archive anything yet - just report.
\ No newline at end of file
diff --git a/tasks/done/01_01_verify-archive-bodyhtml.md b/tasks/done/01_01_verify-archive-bodyhtml.md
deleted file mode 100644
index 17dd27e..0000000
--- a/tasks/done/01_01_verify-archive-bodyhtml.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Hollywood catalog maintenance task.
-
-1. Check if the Shopify archive script completed: look at /tmp/claude-0/-root/tasks/b7d92vadb.output for final results. If still running, wait and check again.
-2. Check the body_html updater agent results: the script is at /root/DW-Agents/vendor-scrapers/hollywood-update-body-html.py. Verify by picking 3 random Hollywood products on Shopify and checking their body_html contains a specs table.
-3. Run: psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT COUNT(*) as archived FROM hollywood_catalog WHERE mfr_sku LIKE 'X%' AND discontinued = true AND shopify_product_id IS NOT NULL;"
-4. Report totals: how many archived, how many got body_html updated, any errors.
\ No newline at end of file
diff --git a/tasks/done/01_02_blog-homepage-refinement.md b/tasks/done/01_02_blog-homepage-refinement.md
deleted file mode 100644
index 433d6b9..0000000
--- a/tasks/done/01_02_blog-homepage-refinement.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Refine /root/Projects/goodquestion-ai/src/pages/index.astro and /root/Projects/goodquestion-ai/src/styles/global.css
-
-Match the hire page design system exactly:
-- Font: Inter for body (already set), Playfair Display for headings (already set)
-- Base font-size in global.css body: bump to 19px
-- All text minimum 16px
-- Secondary text: #AAAAAA not #999 or #777
-- Tags: tangerine #E85D26 text on #1A1A1A bg
-- Post cards: #111111 bg, #1A1A1A border (check current values match)
-- Hero title: Playfair Display 900 in tangerine
-- Contact icons: all tangerine #E85D26 (already done but verify)
-- Remove any leftover blue (#58a6ff) references
-- Ensure backdrop-filter on header uses rgba(10,10,10,0.92)
-
-AFTER:
-1. cd /root/Projects/goodquestion-ai && npx astro build
-2. pm2 restart goodquestion-blog
-3. curl -s -o /dev/null -w "%{http_code}" http://localhost:9883/
\ No newline at end of file
diff --git a/tasks/done/01_1838-cleanup-and-enrich.md b/tasks/done/01_1838-cleanup-and-enrich.md
deleted file mode 100644
index dd56dfe..0000000
--- a/tasks/done/01_1838-cleanup-and-enrich.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# 1838 Wallcoverings — Cleanup & Enrichment
-
-## Context
-- DB table: `w1838_catalog` (404 products, 402 on Shopify)
-- DB connection: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-- Shopify store: `designer-laboratory-sandbox.myshopify.com`
-- 92 Architectural Wallcoverings orphans already archived (just completed)
-
-## Task 1: Archive 2 Duplicate Products on Shopify
-
-Two duplicate titles exist. Archive the HIGHER Shopify ID (keep the lower one):
-
-1. "Fairhaven - Grey Wallpaper | 1838 Wallcoverings" — IDs: 7796831486003, 7796831551539 → Archive 7796831551539
-2. "Verdi - Ivory Cream Wallpaper | 1838 Wallcoverings" — IDs: 7796824277043, 7796824309811 → Archive 7796824309811
-
-Use Shopify REST Admin API:
-```
-PUT /admin/api/2024-01/products/{id}.json
-{"product": {"id": ID, "status": "archived"}}
-```
-
-## Task 2: Mark Paste Products as Discontinued in DB
-
-```sql
-UPDATE w1838_catalog
-SET discontinued = true, updated_at = NOW()
-WHERE mfr_sku IN ('1838-PASTE-5', '1838-PASTE-2.5');
-```
-
-## Task 3: Fix Product Titles on Shopify
-
-All 1838 products currently have titles ending in "Wallpaper | 1838 Wallcoverings".
-The correct format per DW rules is: "Pattern Name - Color Wallcovering | 1838 Wallcoverings"
-
-Replace "Wallpaper" with "Wallcovering" in ALL 1838 product titles on Shopify.
-
-Use the Shopify REST API to update each product title. Rate limit: 2 req/s with 0.5s delay between calls.
-
-## Task 4: Verify Final Counts
-
-After all changes, report:
-- Total active 1838 products on Shopify (should be ~460)
-- Total in DB with shopify_product_id
-- Any remaining duplicates
-- Any products missing images or prices
-
-Print a summary table at the end.
diff --git a/tasks/done/01_ai-enrich-romo.md b/tasks/done/01_ai-enrich-romo.md
deleted file mode 100644
index 6725ae1..0000000
--- a/tasks/done/01_ai-enrich-romo.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# AI Enrich — Romo (454 products missing AI colors)
-
-Run Phase 3 AI enrichment on Romo products missing ai_colors in vendor_catalog.
-
-```bash
-cd /root/DW-Agents/full-monte
-node full-monte-batch.js --vendor romo --phase 3 --limit 200
-```
-
-If that script doesn't exist or errors, use the enrich-ai-tags approach:
-1. Query vendor_catalog for romo products where ai_colors IS NULL AND image_url IS NOT NULL
-2. For each product, call Gemini 2.0 Flash vision API with the image
-3. Extract: colors (with hex + percentages), background color, styles, patterns, image type
-4. Update vendor_catalog with results
-5. Mark in enrichment_tracking table
-
-Use Gemini API key: ${GOOGLE_API_KEY}
-Model: gemini-2.0-flash
-Limit to 200 products per run to stay within rate limits.
diff --git a/tasks/done/01_apex-finish-build.md b/tasks/done/01_apex-finish-build.md
deleted file mode 100644
index 32c4e81..0000000
--- a/tasks/done/01_apex-finish-build.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Project Apex — Finish Build (Server + Frontend + Test)
-
-## Context
-Project Apex is at `/root/DW-Agents/apex-agent/` — a competitive SKU completion agent with 4 sub-teams.
-Port 9850, PM2: apex-agent. Auth: admin / DWSecure2024!
-
-The `lib/auditor.js` and `lib/team-engine.js` have ALREADY been updated with:
-- PostgreSQL cross-reference (auditor checks vendor catalog tables for missing Shopify data)
-- Real fix logic (team-engine proposes actual values from DB instead of stubs)
-- Dynamic column detection (handles width/roll_width, repeat_v/vertical_repeat variations)
-- Vendor list endpoint ready in auditor (`getVendorList()`)
-
-## REMAINING WORK — 3 Tasks
-
-### Task 1: Update server.js
-
-Read the current `/root/DW-Agents/apex-agent/server.js` first, then update it:
-
-1. Add `/api/vendors` GET endpoint that calls `auditor.getVendorList()` and caches results for 5 minutes
-2. Update `/api/audit` POST to accept `{ vendor, catalogTable }` from the request body
-3. Pass `catalogTable` to `auditor.auditVendor(vendor, catalogTable)`
-4. Pass `vendorName` to `teamEngine.loadAuditResults(results, vendorName)`
-5. Keep all existing endpoints working
-6. Keep the same auth middleware, SSE, port 9850
-
-### Task 2: Update Frontend
-
-#### index.html (`/root/DW-Agents/apex-agent/public/index.html`)
-Read first, then update:
-- Replace the hardcoded `<select>` with an empty `<select id="vendor-select">` that gets populated dynamically
-- Add a fix summary section below the bar chart showing counts: "X DB fixes available | Y need scraping | Z need room generation | W need sample variants"
-- Add a `<span id="vendor-stats" class="count-badge"></span>` next to the vendor dropdown showing "X on Shopify / Y in DB"
-
-#### app.js (`/root/DW-Agents/apex-agent/public/js/app.js`)
-Read first, then update:
-- Add `loadVendors()` function that fetches `/api/vendors` and populates the dropdown
- - Each option: `<option value="vendorName" data-table="catalogTable">VendorName (X on Shopify)</option>`
-- Call `loadVendors()` in `init()`
-- Update the audit button handler to send both `vendor` and `catalogTable` from the selected option
-- Update `cleanTitle()` to strip any vendor name (not just Franquemont) using the current vendor from state
-- Add `updateFixSummary(data)` function that reads `data.fixSummary` and updates the fix summary section
-- Call `updateFixSummary` inside `updateDashboard`
-- Update product card rendering to show fix availability badges:
- - If `product.fixable` has items, show a green "DB Fix Available" badge
- - If product needs scraping, show orange "Needs Scrape" badge
- - If product needs room generation, show purple "Needs Rooms" badge
-- Update SSE product-updated handler to also show proposals count
-- Store selected vendor name in state for cleanTitle
-
-#### style.css (`/root/DW-Agents/apex-agent/public/css/style.css`)
-Read first, then add:
-- `.fix-summary` — a flex row of stat cards below the chart section
-- `.fix-stat` — individual stat card (icon + count + label), dark card style matching existing design
-- `.fix-badge` — small inline badge on product cards for fix type indicators
-- `.fix-badge-db` — green tint for DB fixes
-- `.fix-badge-scrape` — orange tint for needs scrape
-- `.fix-badge-rooms` — purple tint for needs rooms
-- `.fix-badge-variant` — blue tint for needs variant
-- `.vendor-stats` — small text next to dropdown
-
-### Task 3: Test
-
-1. `pm2 restart apex-agent`
-2. Wait 3 seconds
-3. Test `/api/vendors`: `curl -s -u admin:DWSecure2024! http://45.61.58.125:9850/api/vendors | head -c 2000`
-4. Verify it returns a JSON array of vendors with shopify_products > 0
-5. Check PM2 logs: `timeout 10 pm2 logs apex-agent --lines 20`
-6. If errors, fix them and restart
-
-## RULES
-- Read every file BEFORE editing it
-- Keep existing auth middleware (admin / DWSecure2024!)
-- Keep port 9850
-- Use safe DOM construction (no innerHTML) — match existing app.js patterns
-- All times in Pacific Time
-- Do NOT modify auditor.js or team-engine.js — they are already done
-- PostgreSQL connection: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/01_arte-image-enrichment.md b/tasks/done/01_arte-image-enrichment.md
deleted file mode 100644
index 0c40271..0000000
--- a/tasks/done/01_arte-image-enrichment.md
+++ /dev/null
@@ -1,33 +0,0 @@
-## Arte International Image Enrichment
-
-Arte has 1,991 products but only 1,417 images (71.2%). Their catalog has product_url, packshot_url, and roomshot_url columns.
-
-### Steps:
-1. Check how many Arte products have product_url but no image_url:
- ```sql
- SELECT COUNT(*) as missing_images,
- COUNT(NULLIF(product_url,'')) as has_url,
- COUNT(NULLIF(packshot_url,'')) as has_packshot,
- COUNT(NULLIF(roomshot_url,'')) as has_roomshot
- FROM arte_catalog WHERE image_url IS NULL OR image_url = '';
- ```
-
-2. For products that have packshot_url but no image_url, copy packshot to image_url:
- ```sql
- UPDATE arte_catalog SET image_url = packshot_url
- WHERE (image_url IS NULL OR image_url = '') AND packshot_url IS NOT NULL AND packshot_url != '';
- ```
-
-3. For remaining products with product_url, check if there's an arte-international scraper:
- ```bash
- ls /root/DW-Agents/vendor-scrapers/*arte*.js
- ```
-
-4. If enrichment scraper exists, run it. If not, try fetching a sample URL to understand page structure:
- ```bash
- timeout 15 curl -s "SAMPLE_URL" | grep -o 'https://[^"]*\.\(jpg\|png\|webp\)' | head -10
- ```
-
-5. Update VCC: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-### CRITICAL: Do NOT push to Shopify. Only update PostgreSQL.
diff --git a/tasks/done/01_audit-and-fix-empty-collections.md b/tasks/done/01_audit-and-fix-empty-collections.md
deleted file mode 100644
index 5dc8267..0000000
--- a/tasks/done/01_audit-and-fix-empty-collections.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Audit and Fix Empty/Broken Collections on Homepage
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-Before making ANY changes, investigate and answer:
-
-1. **How many total collection links are on the homepage?**
- - Fetch the homepage HTML: `curl -s https://www.designerwallcoverings.com/`
- - Extract all `/collections/` URLs
- - Count unique collection handles
-
-2. **Which collections have 0 visible products?**
- - For each collection link, check product count via Storefront
- - Known issue: `phillipe-romano-faux-leathers-upholstery-more` has 741 archived (0 visible)
- - Are there others?
-
-3. **Which collections return 404?**
- - HEAD request each collection URL
- - Known issue: `phyllis-morris-classic-wallpaper-1` (handled by task 00)
- - Known issue: `the-original-yeeha-cowboy-wallpaper` (disabled section)
- - Any others?
-
-4. **Are there any collections that redirect unexpectedly?**
- - Follow redirects and check final URL
- - If final URL is `/collections` (the index) that means the collection was deleted
-
-5. **For empty collections — should they be removed from the homepage or repopulated?**
- - Check if the products are archived (can be un-archived)
- - Check if the collection rule is wrong (no matching products)
- - Default: REMOVE empty collection links from homepage, don't un-archive products without Steve's approval
-
-## What To Do
-
-### For each broken/empty collection found:
-1. **404 collections**: Remove the link/button from the homepage section in the theme
-2. **Empty collections (0 products)**: Remove from homepage OR add a note in the section settings
-3. **Redirect-to-index**: Same as 404 — remove from homepage
-
-### How to remove a link from homepage:
-- Read settings_data.json for the theme
-- Find the section containing the broken collection reference
-- Either remove the block or disable it
-- Upload updated settings_data.json
-
-### DO NOT:
-- Delete any collections from Shopify (just remove homepage links)
-- Un-archive products without approval
-- Modify navigation menus (only homepage sections)
-
-## Verification (MANDATORY)
-- After fixes, re-fetch homepage and verify ALL collection links return HTTP 200
-- Report: total links checked, broken found, fixed, remaining issues
-
-## Credentials
-- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-- Theme ID: 141654589491
diff --git a/tasks/done/01_blog-post.md b/tasks/done/01_blog-post.md
deleted file mode 100644
index 48f308d..0000000
--- a/tasks/done/01_blog-post.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Publish Blog Post — goodquestion.ai
-```bash
-cd /root/DW-Agents/blog-agent && node generator.js
-```
-Fallback: `curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:9871/api/generate`
-Verify post published and Astro build succeeded.
diff --git a/tasks/done/01_blog-social-session112.md b/tasks/done/01_blog-social-session112.md
deleted file mode 100644
index d1c3603..0000000
--- a/tasks/done/01_blog-social-session112.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Blog + Social — Session #112
-
-Write and publish a blog post about "Color Intelligence: How AI Analyzes Every Pixel of 173,000 Products"
-
-Key points:
-- AI vision analyzes product images for ALL colors with hex codes and percentages
-- Background color identified separately from pattern colors
-- Colors feed into clickable hex dot swatches on product pages
-- Smart collections auto-populated by AI color tags
-- Dedup tracking prevents re-analysis ($0 wasted)
-- 23,000+ products already enriched, 150K queued
-
-Pipeline: generate → sanitize → publish → build astro → tweet → bluesky
-Run: cd /root/DW-Agents/blog-agent && node generator.js
-Then: cd /root/Projects/goodquestion-ai && node scripts/tweet.cjs && node scripts/bsky-post.cjs
-
-IMPORTANT: No wallcovering/vendor names per MEMORY rules. Frame as "product catalog color analysis."
diff --git a/tasks/done/01_china-seas-width-repeat-update.md b/tasks/done/01_china-seas-width-repeat-update.md
deleted file mode 100644
index f5b514b..0000000
--- a/tasks/done/01_china-seas-width-repeat-update.md
+++ /dev/null
@@ -1,42 +0,0 @@
-## Update China Seas Width & Repeat from Quadrille Catalog
-
-China Seas wallpapers are sold through Quadrille (quadrillefabrics.com). The Quadrille wallpaper page lists specs for each pattern. Update the china_seas_catalog table with width and pattern_repeat data.
-
-Here are the specs extracted from the Quadrille catalog page. Match by pattern name to update ALL colorways of each pattern:
-
-| Pattern | Width | Repeat |
-|---------|-------|--------|
-| Climbing Hydrangea | 19.5" | 21.75" |
-| Spring Blossom | 36.5" | 24" |
-| Fern Cliff | 27" | 36" |
-| Henriot Floral | 26" | 18.5" |
-| Martine | 27" | 26" |
-| Camino | 27" | 26" |
-| Palm Garden | 27" | 13.5" |
-| El Cid | 24.5" | 34.5" |
-| Chantilly Stripe | 27" | 18" |
-| Shanghai | 27" | 32" |
-| Kazak | 27" | 30" |
-| Uzbek | 27" | 44" |
-| Tulip Multicolor | 26" | 17" |
-| Malaya | 27" | 15.5" |
-| Veneto | 28.5" | 38.25" |
-| Volpi | 27" | 9" |
-| Puccini | 28.5" | all over |
-| Carlo II | 26.5" | 6" |
-| Longfellow | 23" | 31.5" |
-| Bonaparte | 27" | n/a |
-| Cherry Branch | 18" | 27" |
-| Pina | 27" | n/a |
-| Monty | 27" | 29" |
-| Monty On Grasscloth | 32" | 29.25" |
-| Des Gardes | 28" | 21" |
-| Des Gardes Reverse | 28" | 21" |
-
-Run SQL updates like:
-```sql
-UPDATE china_seas_catalog SET width = '27"', pattern_repeat = '26"' WHERE pattern_name ILIKE '%Camino%' AND (width IS NULL OR width = '');
-```
-
-Do this for ALL patterns above. Report how many rows were updated total.
-Also set match_type where known (e.g., Shanghai has "32"v 2"h" suggesting half drop).
diff --git a/tasks/done/01_cleanup-37-empty-collections.md b/tasks/done/01_cleanup-37-empty-collections.md
deleted file mode 100644
index 3b2507b..0000000
--- a/tasks/done/01_cleanup-37-empty-collections.md
+++ /dev/null
@@ -1,83 +0,0 @@
-# Clean Up 37 Empty Collections (0 Active Products)
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-1. **Which collections are empty and why?**
- Read: `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json`
- The "empty" array has collections with 0 active products. For each:
- - Are ALL products archived? (vendor discontinued)
- - Is the collection rule wrong? (no matching products)
- - Was the vendor removed from the store?
-
-2. **Are any of these collections linked from the homepage or navigation?**
- Check: `curl -s https://www.designerwallcoverings.com/ | grep "{collection_handle}"`
- If linked from homepage/nav, DON'T delete — flag for Steve.
-
-3. **Should empty collections be unpublished or deleted?**
- - **Unpublish** = keeps the collection but removes from storefront/search (reversible)
- - **Delete** = permanent removal
- - Default: **UNPUBLISH** (safer — can re-publish if products come back)
-
-## What To Do
-
-### Step 1: Read the audit report
-```bash
-cat /root/DW-Agents/yolo-agent/logs/collection-audit-report.json
-```
-Extract the "empty" array with collection IDs and handles.
-
-### Step 2: For each empty collection, check if it's linked anywhere important
-```bash
-# Check homepage
-curl -s "https://www.designerwallcoverings.com/" | grep -i "{handle}"
-# Check navigation menus via API
-curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/menus.json"
-```
-
-### Step 3: Unpublish empty collections (NOT delete)
-```bash
-# For smart collections:
-curl -s -X PUT "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/smart_collections/{id}.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"smart_collection": {"published": false}}'
-
-# For custom collections:
-curl -s -X PUT "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/custom_collections/{id}.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"custom_collection": {"published": false}}'
-```
-
-### Step 4: Rate limiting
-- 600ms between API calls
-- Log each action
-
-### Step 5: Save results
-Save to: `/root/DW-Agents/yolo-agent/logs/empty-collections-cleanup-report.json`
-```json
-{
- "timestamp": "...",
- "unpublished": [{"id": 123, "handle": "x", "title": "Y", "reason": "0 active products"}],
- "skippedLinked": [{"id": 456, "handle": "z", "linkedFrom": "homepage"}],
- "failed": []
-}
-```
-
-### Step 6: Slack notification
-```bash
-curl -X POST -H 'Content-type: application/json' \
- --data '{"text":"🧹 *Empty Collections Cleanup*\n• Unpublished: X collections\n• Skipped (linked): Y\n• Failed: Z\n• Report: /root/DW-Agents/yolo-agent/logs/empty-collections-cleanup-report.json"}' \
- "${SLACK_WEBHOOK_URL}"
-```
-
-## DO NOT
-- DELETE any collections (unpublish only — reversible)
-- Remove collections that are linked from homepage or main navigation
-- Modify any products or product data
-
-## Credentials
-- Shopify API: <redacted:SHOPIFY_ORDERS_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-- Slack: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/01_dexter-full-scan-cleanup.md b/tasks/done/01_dexter-full-scan-cleanup.md
deleted file mode 100644
index 28cc886..0000000
--- a/tasks/done/01_dexter-full-scan-cleanup.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Dexter Full Duplicate Scan & Cleanup
-
-Run Dexter (port 9676) to scan ALL catalog tables for duplicates and clean them up.
-
-### Steps:
-1. Trigger a fresh scan: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9676/api/scan`
-2. Wait for scan to complete (check status every 30s): `curl -s -u admin:DWSecure2024! http://localhost:9676/api/status`
-3. Report total duplicates found per table
-4. If there are pending duplicates, trigger cleanup: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9676/api/clean`
-5. Verify cleanup completed
-
-### CRITICAL RULES:
-- Do NOT create any Shopify products
-- Do NOT push anything to Shopify
-- Only clean up PostgreSQL duplicates
-- Report final counts per vendor table
diff --git a/tasks/done/01_enrich-prl-direct.md b/tasks/done/01_enrich-prl-direct.md
deleted file mode 100644
index dd038d6..0000000
--- a/tasks/done/01_enrich-prl-direct.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# AI Enrich — PRL Products (186 missing in vendor_catalog)
-
-PRL vendor_code has 186 products in vendor_catalog with images but no ai_colors. There's no dedicated PRL catalog table.
-
-Run Gemini Vision directly on each product:
-1. Query: `SELECT id, mfr_sku, image_url FROM vendor_catalog WHERE vendor_code = 'PRL' AND ai_colors IS NULL AND image_url IS NOT NULL LIMIT 100`
-2. For each, call Gemini 2.0 Flash with the image_url
-3. Extract: colors (name, hex, percentage), background_color, styles, patterns, image_type
-4. UPDATE vendor_catalog SET ai_colors, ai_background_color, ai_styles, ai_patterns, ai_tags, ai_description
-5. Track in enrichment_tracking
-
-Gemini key: ${GOOGLE_API_KEY}
-Model: gemini-2.0-flash
-Limit: 100 per run, 500ms between calls
diff --git a/tasks/done/01_farrow-ball-specs-extract.md b/tasks/done/01_farrow-ball-specs-extract.md
deleted file mode 100644
index a512663..0000000
--- a/tasks/done/01_farrow-ball-specs-extract.md
+++ /dev/null
@@ -1,29 +0,0 @@
-## Farrow & Ball — Extract Specs from JSON to Columns
-
-The `farrow_ball_catalog` table has a `specs` JSONB column with data like:
-`{"roll_width": "53cm / 21\"", "roll_length": "10m", "pattern_repeat": "16cm / 6 1/4\""}`
-
-But the VCC reports 0 width and 0 repeat because it checks dedicated columns, not the JSON.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check if `farrow_ball_catalog` has `width` and `repeat_v` columns. If not, add them:
- ```sql
- ALTER TABLE farrow_ball_catalog ADD COLUMN IF NOT EXISTS width TEXT;
- ALTER TABLE farrow_ball_catalog ADD COLUMN IF NOT EXISTS repeat_v TEXT;
- ALTER TABLE farrow_ball_catalog ADD COLUMN IF NOT EXISTS repeat_h TEXT;
- ALTER TABLE farrow_ball_catalog ADD COLUMN IF NOT EXISTS material TEXT;
- ALTER TABLE farrow_ball_catalog ADD COLUMN IF NOT EXISTS roll_length TEXT;
- ```
-3. Extract from specs JSON into the new columns:
- ```sql
- UPDATE farrow_ball_catalog SET
- width = specs->>'roll_width',
- repeat_v = specs->>'pattern_repeat',
- roll_length = specs->>'roll_length'
- WHERE specs IS NOT NULL AND specs != '{}';
- ```
-4. Verify: `SELECT COUNT(*) FROM farrow_ball_catalog WHERE width IS NOT NULL AND width != '';`
-5. Report results.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/01_fix-phillipe-romano-vendors.md b/tasks/done/01_fix-phillipe-romano-vendors.md
deleted file mode 100644
index 3c68df6..0000000
--- a/tasks/done/01_fix-phillipe-romano-vendors.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# URGENT: Fix Phillipe Romano Vendor Name Duplicates
-
-## Problem
-Phillipe Romano products exist under 5+ vendor name variants:
-- "Phillipe Romano" — 8,541 products (CANONICAL)
-- "Phillipe Romano Wallpaper" — 971 products (WRONG)
-- "Phillipe Romano European Prints" — 175 products (WRONG)
-- "Phillipe Romano Fabrics" — 87 products (WRONG)
-- "Phillipe Romano Vinyls" — unknown count (WRONG, has old Benedetta dupes)
-
-## Task
-1. For ALL products under "Phillipe Romano Wallpaper", "Phillipe Romano European Prints", "Phillipe Romano Fabrics", "Phillipe Romano Vinyls":
- - Check if a duplicate exists under "Phillipe Romano" (match by barcode/mfr_sku)
- - If duplicate: ARCHIVE the variant-name version
- - If NOT duplicate: UPDATE vendor to "Phillipe Romano" (canonical name)
-
-2. Use GraphQL bulkOperationRunMutation for speed — REST is too slow for 1,233 products
-
-3. Rate limit: respect Shopify throttle. Use `metafieldsSet` mutation for vendor updates.
-
-4. DO NOT create new products. DO NOT create new SKUs. Update only.
-
-5. Report: total checked, vendor names fixed, duplicates archived, errors
-
-## GraphQL to update vendor:
-```graphql
-mutation { productUpdate(input: { id: "gid://shopify/Product/ID", vendor: "Phillipe Romano" }) { product { id vendor } userErrors { field message } } }
-```
-
-## Important
-- Phillipe Romano is PRIVATE LABEL — never expose real manufacturer
-- The XXX series (54" extra wide) already exist in FMPro master CSV — match by mfr_sku, don't create new
diff --git a/tasks/done/01_fix-vendor-name-duplicates.md b/tasks/done/01_fix-vendor-name-duplicates.md
deleted file mode 100644
index 35a8773..0000000
--- a/tasks/done/01_fix-vendor-name-duplicates.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# URGENT: Fix Vendor Name Duplicates on Shopify
-
-13 vendors have multiple name variants causing duplicate products on the store.
-
-## Duplicate Vendor Names Found:
-- China Seas / China Seas Fabrics / China Seas Wallpaper
-- Scalamandre / Scalamandre Fabrics / Scalamandre Wallpaper
-- Graham & Brown / Graham & Brown Europe
-- Coordonné / Coordonné Europe
-- Ralph Lauren / Ralph Lauren Fabric / Ralph Lauren Wallpaper
-- Phillipe Romano / Phillipe Romano European Prints / Phillipe Romano Fabrics / Phillipe Romano Wallpaper
-- Nicolette Mayer / Nicolette Mayer Fabrics / Nicolette Mayer Wallpaper
-- Osborne & Little / Osborne & Little Fabrics / Osborne & Little Wallpaper
-- Nina Campbell / Nina Campbell Fabrics / Nina Campbell Wallcoverings
-- Architectural Fabrics / Architectural Wallcoverings
-- Missoni Fabric / Missoni Wallpaper
-- Roberto Cavalli / Roberto Cavalli Wallpaper
-- Edge Wallcovering / Edge Wallpaper
-
-## Task:
-1. For EACH vendor group above, identify the CANONICAL vendor name (use the base name, e.g., "Scalamandre" not "Scalamandre Wallpaper")
-2. Find ALL active products under variant vendor names
-3. Check if a duplicate exists under the canonical name (same mfr_sku/barcode)
-4. If duplicate: ARCHIVE the variant-name version (keep the canonical one)
-5. If no duplicate: UPDATE the vendor name to the canonical version
-6. DO NOT delete any products — archive only
-7. Report: total duplicates found, archived, vendor names standardized
-
-Use GraphQL for speed. Rate limit: 2 req/sec.
-
-IMPORTANT: Scalamandre FABRICS are NOT wallcoverings — those may be legitimate fabric products. Check product_type before archiving.
diff --git a/tasks/done/01_health-check-restart.md b/tasks/done/01_health-check-restart.md
deleted file mode 100644
index 2a7521e..0000000
--- a/tasks/done/01_health-check-restart.md
+++ /dev/null
@@ -1,11 +0,0 @@
-Run a full health check of the DW-Agents infrastructure:
-
-1. Run `pm2 list` and identify any agents with status "errored" or "stopped"
-2. Restart any crashed agents with `pm2 restart <name>`
-3. Check disk space with `df -h /` — warn if >85%
-4. Check memory with `free -h` — warn if available <2GB
-5. Check for zombie Claude processes older than 12 hours: `ps aux | grep claude | grep -v grep` — kill any with `kill -9`
-6. Check the 6 production websites are responding (ports 9888, 3200, 8080, 7123, 8100, 3011) with `curl -s -o /dev/null -w "%{http_code}" http://localhost:PORT`
-7. Report a summary of findings
-
-Working directory: /root/DW-Agents
diff --git a/tasks/done/01_health-check.md b/tasks/done/01_health-check.md
deleted file mode 100644
index 9b77b65..0000000
--- a/tasks/done/01_health-check.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Check all PM2 processes and report any that are errored, stopped, or crash-looping.
-Run: pm2 list | grep -E "errored|stopped"
-Check disk space with df -h / and memory with free -h.
-Report a brief status summary.
\ No newline at end of file
diff --git a/tasks/done/01_hire-page-black-redesign.md b/tasks/done/01_hire-page-black-redesign.md
deleted file mode 100644
index 3373e98..0000000
--- a/tasks/done/01_hire-page-black-redesign.md
+++ /dev/null
@@ -1,39 +0,0 @@
-CRITICAL TASK: Complete redesign of /root/Projects/goodquestion-ai/src/pages/hire.astro
-
-DESIGN RULES — STRICT:
-- ONLY 3 COLORS: #0A0A0A (black background), #FFFFFF (white text), #E85D26 (tangerine accent)
-- NO other colors. No terracotta, no forest green, no warm stone, no linen
-- Black background on EVERY section
-- White text for all headings and body
-- Tangerine (#E85D26) for accents only: labels, borders, badges, hover states
-- Cards: #111111 background, 1px #1A1A1A border
-- Secondary text: #777777 (gray on black)
-
-LAYOUT CHANGES:
-- Remove ALL SVG graphic illustrations from claude-card elements
-- Replace with real screenshot images from /images/tools/ directory:
- - color-search.jpg, paint-visualizer.jpg, coordinate-builder.jpg
- - room-setting.jpg (skip - its a login screen), blog-agent.jpg, vendor-command-center.jpg
-- Screenshot images should be full-width within card, 200px height, object-fit: cover
-- Core Skills section: ALL cards same style - black (#111111) background, white text, tangerine accent bar
-- Remove alternating white/dark/black card styles — everything is dark
-- Symmetrical 3-column grid throughout
-- Reduce padding: 60px sections, 40px gaps
-
-CSS VARIABLE OVERHAUL:
-- --bg: #0A0A0A
-- --text: #FFFFFF
-- --text-secondary: #777777
-- --brass: #E85D26 (this is the ONLY accent color)
-- --forest: DELETE or change to #E85D26
-- --terracotta: DELETE or change to #E85D26
-- --stone: #777777
-- --white: #FFFFFF
-- --black: #0A0A0A
-
-AFTER EDITING:
-1. Run: npx astro build
-2. Run: pm2 restart goodquestion-blog
-3. Verify: curl -s -o /dev/null -w "%{http_code}" http://localhost:9883/hire/
-
-The page must look like a premium black portfolio site. Clean, symmetrical, 3 colors only.
\ No newline at end of file
diff --git a/tasks/done/01_hire-page-overnight-refinement.md b/tasks/done/01_hire-page-overnight-refinement.md
deleted file mode 100644
index 51b658b..0000000
--- a/tasks/done/01_hire-page-overnight-refinement.md
+++ /dev/null
@@ -1,54 +0,0 @@
-OVERNIGHT REFINEMENT TASK — Run continuously until morning.
-
-File: /root/Projects/goodquestion-ai/src/pages/hire.astro
-
-Do ALL of the following:
-
-1. ACCESSIBILITY PASS
-- Ensure ALL font sizes are minimum 19px (1rem at 19px base). Search for any remaining font-size below 1rem and fix
-- Secondary text color must be #AAAAAA (not #777 or #999) for WCAG AA on black
-- All buttons minimum 48px height touch targets
-- Add aria-hidden=true to ALL decorative elements (SVGs, placeholders)
-- Canvas needs role=img aria-label
-- Add <main> landmark wrapper
-- Fix hamburger menu: add aria-expanded toggle
-
-2. DEAD CSS CLEANUP
-- Remove any CSS selectors that have no matching HTML (decor-square, decor-circles, hero-stats-card, stats-pill, etc)
-- Remove --forest, --terracotta, --stone variables if still present
-- Ensure only 3 colors used: #0A0A0A, #FFFFFF, #E85D26 (plus #111111 cards, #1A1A1A borders, #AAAAAA secondary text)
-
-3. TOOL CARDS CONSISTENCY
-- All 13 cards must have identical structure
-- Cards WITHOUT screenshots need better placeholders — use a dark gradient (#111 to #181818) with the tool name in tangerine
-- Ensure all card descriptions are roughly same length (2 lines max)
-
-4. SHOPIFY SECTION
-- Move Shopify section ABOVE the services section (closer to tools)
-- Numbered circles must all be #E85D26
-- All card descriptions use Inter font, #AAAAAA color
-
-5. SERVICES SECTION
-- Full Build card border must be #E85D26
-- POPULAR badge must be #E85D26 background
-- All three cards same height
-
-6. FORM / CTA
-- The inquiry form must have visible field borders (#333 not #1A1A1A)
-- Submit button must be #E85D26 with white text, 48px height
-- Add placeholder text in lighter color
-
-7. THREE.JS
-- Add defer to Three.js script tag
-- Check prefers-reduced-motion in JS — stop animation if reduced motion preferred
-- Add IntersectionObserver — only animate when canvas is visible
-
-8. RESPONSIVE
-- Test all breakpoints still work (1024px, 768px, 480px)
-- Mobile: canvas height 250px, stats row wraps
-- Cards go 2-col at 1024, 1-col at 768
-
-AFTER ALL CHANGES:
-1. cd /root/Projects/goodquestion-ai && npx astro build
-2. pm2 restart goodquestion-blog
-3. Verify: curl -s -o /dev/null -w "%{http_code}" http://localhost:9883/hire/
\ No newline at end of file
diff --git a/tasks/done/01_hollywood-color-name-cleanup.md b/tasks/done/01_hollywood-color-name-cleanup.md
deleted file mode 100644
index c1e6eb4..0000000
--- a/tasks/done/01_hollywood-color-name-cleanup.md
+++ /dev/null
@@ -1,83 +0,0 @@
-# Task A: Hollywood color_name cleanup
-
-You're cleaning up corrupted `color_name` data in `vendor_catalog` for Hollywood products, then writing clean values to Shopify metafields. This is a standalone background task — act autonomously.
-
-## Context
-- **Database**: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- **Shopify product token**: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-- **Shopify store**: `designer-laboratory-sandbox.myshopify.com`
-- **Shopify API**: `2024-10` GraphQL
-- **Quinn queue table**: `shopify_api_queue` (insert with `method='POST', endpoint='/graphql.json', token_alias='product', priority=3, source_agent='hollywood-color-cleanup', max_retries=3`)
-- **Rule #14 (private label)**: Hollywood is a DW PL brand. Public brand = "Hollywood Wallcoverings". Real manufacturer must stay hidden.
-- **Rule #23**: ALL Shopify writes via Quinn GraphQL, batched 25 per call
-
-## The problem
-
-`vendor_catalog.color_name` for 2,684 Hollywood products contains JSON blobs like `{"hex":"#F2E7D9","pct":45,"name":"Beige"}` instead of plain color names like `"Beige"` or `"Atmospheric"`. These are AI-extracted color objects that leaked into the name column during Phase 3 enrichment.
-
-Shopify product titles for the SAME products have the correct color name in their title format: `"{Pattern Name} - {Color Name} {Product Type} | Hollywood Wallcoverings"`. For example: `"Green Drakes - Atmospheric Wallcovering | Hollywood Wallcoverings"` → color is `"Atmospheric"`.
-
-## Your job — three steps
-
-### Step 1: Query the corrupted rows
-```sql
-SELECT vc.id, vc.dw_sku, vc.mfr_sku, vc.color_name AS corrupted,
- vc.shopify_product_id, vc.product_type
-FROM vendor_catalog vc
-WHERE vc.vendor_code = 'hollywood'
- AND vc.on_shopify = true
- AND vc.shopify_product_id IS NOT NULL
- AND (vc.color_name LIKE '{%' OR vc.color_name LIKE '[%')
-ORDER BY vc.id;
-```
-Expected: ~2,684 rows.
-
-### Step 2: Fetch their Shopify titles in batches
-Shopify GraphQL `products(first: 250)` with `query: "id:7816095989811 OR id:..."` lets you batch up to 250 IDs. Or use `bulkOperationRunQuery` if the set is too big. For each product:
-- Parse the title: `{Pattern} - {Color} {ProductType} | {Vendor}`
-- Regex suggestion: `^(.+?)\s-\s(.+?)\s+(Wallcovering|Commercial Wallcovering|Mural|Fabric|Wallpaper|Type\s+\w+)\s+\|\s+.+$`
-- Group 2 is the color name. Trim whitespace.
-- If title doesn't match the format, SKIP that product (log it for a follow-up list)
-
-### Step 3: Write clean color back to both places
-For each resolved `{dw_sku, clean_color}`:
-
-**3a.** Update `vendor_catalog`:
-```sql
-UPDATE vendor_catalog SET color_name = $1 WHERE id = $2;
-```
-
-**3b.** Write to Shopify `custom.color` and `dwc.color` via Quinn. Pack 12 products × 2 metafields = 24 entries per `metafieldsSet` call (Shopify limit is 25). Insert one Quinn job per batch:
-
-```javascript
-const payload = {
- query: `mutation($metafields: [MetafieldsSetInput!]!) {
- metafieldsSet(metafields: $metafields) {
- metafields { id }
- userErrors { field message }
- }
- }`,
- variables: { metafields: [...] } // array of {ownerId, namespace, key, type, value}
-};
-// INSERT INTO shopify_api_queue (method, endpoint, payload, token_alias, priority, status, source_agent, max_retries)
-// VALUES ('POST', '/graphql.json', $1::jsonb, 'product', 3, 'pending', 'hollywood-color-cleanup', 3)
-```
-
-## Safety
-
-- **DO NOT** touch products whose title doesn't parse cleanly — log and skip
-- **DO NOT** write an empty string or "null" — if parse fails, skip
-- **DO NOT** touch rows outside `vendor_code='hollywood'` — this is scoped to Hollywood only
-- **DO NOT** bump job priority above 3 — let Quinn's normal pacing handle it
-- Write the script to `/root/DW-Agents/scripts/yolo-hollywood-color-cleanup.js` so it's reusable
-- Use `node --check` on your script before running it
-
-## Deliverables
-
-1. Script file saved at `/root/DW-Agents/scripts/yolo-hollywood-color-cleanup.js`
-2. Count of rows matched, clean colors extracted, and rows skipped
-3. Count of Quinn jobs enqueued
-4. A list of up to 20 skipped products with their title format (for follow-up)
-5. A final SQL verification showing that no `vendor_catalog` rows for Hollywood still have JSON-blob color_name
-
-Report these deliverables in a terse markdown summary at the end. Under 400 words.
diff --git a/tasks/done/01_hollywood-fix-color-names.md b/tasks/done/01_hollywood-fix-color-names.md
deleted file mode 100644
index 5403254..0000000
--- a/tasks/done/01_hollywood-fix-color-names.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Fix Hollywood Color Names from AI Data
-
-## Context
-3,011 Hollywood Wallcoverings products in vendor_catalog have color_name = "Hollywood Wallcoverings" (wrong).
-All 2,852 of these already have ai_colors extracted via Gemini Vision with real color names.
-
-## Task
-1. Update vendor_catalog: set color_name to the first AI color name for products where color_name = 'Hollywood Wallcoverings'
-
-```sql
-UPDATE vendor_catalog
-SET color_name = (
- CASE
- WHEN ai_colors IS NOT NULL AND ai_colors::text ~ '^\[' THEN
- TRIM(BOTH '"' FROM (ai_colors::jsonb->0)::text)
- ELSE color_name
- END
-),
-updated_at = NOW()
-WHERE vendor_code = 'hollywood'
- AND (color_name IS NULL OR color_name = '' OR color_name = 'Hollywood Wallcoverings')
- AND ai_colors IS NOT NULL;
-```
-
-2. Verify: count how many now have real color names
-3. Report: how many fixed, sample 5 results showing old vs new
diff --git a/tasks/done/01_hollywood-width-backfill-from-gdrive.md b/tasks/done/01_hollywood-width-backfill-from-gdrive.md
deleted file mode 100644
index 09a09d3..0000000
--- a/tasks/done/01_hollywood-width-backfill-from-gdrive.md
+++ /dev/null
@@ -1,25 +0,0 @@
-Hollywood Wallcoverings — Backfill Width from Google Drive Specs
-
-Hollywood is a DW PRIVATE LABEL line (real manufacturer: Momentum Textiles / MDC).
-363 Hollywood products on our DB have NO width, blocking Shopify push.
-94% of existing Hollywood products have 54" width.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: hollywood_catalog
-
-## Steps:
-
-1. Check rclone config: `rclone listremotes` and `cat /root/.config/rclone/rclone.conf`
-2. List files in Google Drive looking for Hollywood/Momentum spec sheets:
- `rclone ls gdrive: --include "*hollywood*" --include "*momentum*" --include "*MDC*" 2>/dev/null | head -30`
-3. Also try: `rclone ls gdrive: | grep -i "hollywood\|momentum\|MDC\|width\|spec" | head -30`
-4. If spec files found (CSV/Excel/PDF), download and parse for width data
-5. If NO Drive files found, use product_type inference:
- - "Commercial Wallcoverings" → 54" (94% of existing Hollywood products are 54")
- - "Wallcoverings" → 54"
- - "Wallcoverings, Wallpapers, Walls" → varies
-6. Also try scraping width from the Momentum website for unmatched products:
- - The original scraper is at /root/DW-Agents/vendor-scrapers/momentum-colorway-scraper.js
-7. After backfill, also assign DW SKUs to products missing them using VCC:
- `curl -u admin:DWSecure2024! -X POST http://localhost:9660/api/vendors/hollywood/assign-skus`
-8. Report: how many got width, how many got DW SKU, how many are now ready for Shopify push
diff --git a/tasks/done/01_interior-design-tags-all-vendors.md b/tasks/done/01_interior-design-tags-all-vendors.md
deleted file mode 100644
index 85a86d3..0000000
--- a/tasks/done/01_interior-design-tags-all-vendors.md
+++ /dev/null
@@ -1,87 +0,0 @@
-# Interior Design Tags — PostgreSQL First, Then Shopify
-
-Run Gemini AI analysis on ALL products in PostgreSQL catalog tables. Save tags + hex codes to DB.
-Then push to Shopify only for products already on Shopify.
-
-## FLOW: PostgreSQL → Gemini → PostgreSQL → Shopify
-
-## TAG RULES
-- **NO "Background Color" prefix** — just the color name directly
-- **ALL colors detected** — tag every visible color
-- **Hex code** → save to `color_hex` column in catalog table + `global.color_hex` metafield on Shopify
-- **NEVER use "wallpaper"** — always "wallcovering"
-
-## Step 1: Analyze ALL catalog tables in PostgreSQL
-
-For each vendor catalog table that has an `image_url` column:
-
-```sql
-SELECT id, mfr_sku, pattern_name, color_name, image_url, material, collection
-FROM {catalog_table}
-WHERE image_url IS NOT NULL AND image_url <> ''
-AND (color_hex IS NULL OR color_hex = '')
-ORDER BY id
-```
-
-### For EACH product with image:
-
-1. **Gemini analysis** — send image URL, get colors + hex + styles + patterns
-```
-POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}
-```
-Prompt: "Analyze this wallcovering. Return ONLY valid JSON: { dominantColor, hexCode (#XXXXXX), allColors (ALL visible), styles, patterns, material }"
-
-2. **Save to PostgreSQL** — update the catalog row:
-```sql
-UPDATE {catalog_table} SET
- color_hex = '{hexCode}',
- ai_colors = '{allColors as JSON array}',
- ai_styles = '{styles as JSON array}',
- ai_patterns = '{patterns as JSON array}',
- ai_background_color = '{dominantColor}',
- ai_tags = '{computed tags as JSON array}',
- updated_at = NOW()
-WHERE id = {id}
-```
-
-Add missing columns if needed:
-```sql
-ALTER TABLE {table} ADD COLUMN IF NOT EXISTS color_hex VARCHAR(7);
-ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_colors JSONB;
-ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_styles JSONB;
-ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_patterns JSONB;
-ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_background_color VARCHAR(100);
-ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_tags JSONB;
-```
-
-## Step 2: Push to Shopify (only products already on Shopify)
-
-For products with `shopify_product_id IS NOT NULL`:
-
-1. Build tag string from ai_tags + brand + pattern + material + "Wallcovering" + "Commercial" + "Architectural"
-2. Push hex to `global.color_hex` metafield via GraphQL
-3. Update Shopify product tags via REST
-4. Publish to all 14 channels if not already
-
-## Vendor catalog tables to process (in order of priority):
-1. versace_catalog (136 products)
-2. black_edition_catalog (191)
-3. kirkby_catalog (152)
-4. zinc_catalog (100)
-5. villa_nova_catalog (387)
-6. arte_catalog (1991)
-7. romo_catalog (2595)
-8. scalamandre_catalog (4819)
-9. thibaut_catalog (5703)
-10. All other catalogs with image_url
-
-## Rate limits
-- Gemini: 1 req/sec (max 15 RPM on free tier — use batch API for 100+ products)
-- Shopify: 2 req/sec
-- Log progress every 25 products per vendor
-
-## CRITICAL
-- PostgreSQL FIRST, Shopify SECOND
-- Save ALL Gemini results to DB before touching Shopify
-- color_hex must be valid hex format (#XXXXXX)
-- Preserve display_variant tag on Shopify
diff --git a/tasks/done/01_jd-fix-broken-images.md b/tasks/done/01_jd-fix-broken-images.md
deleted file mode 100644
index 5d0b2ed..0000000
--- a/tasks/done/01_jd-fix-broken-images.md
+++ /dev/null
@@ -1,44 +0,0 @@
-Fix 605 Justin David products with broken/missing image URLs in justindavid_catalog.
-
-## Problem
-- 504 products have image_url pointing to old /colors/ path (all return 404)
-- 101 products have NULL image_url
-- Working images use /uploads/2023/04/ or /uploads/2025/12/ dated paths
-
-## Steps
-
-### Step 1: Scrape current image URLs from justindavid.com
-```bash
-cd /root/DW-Agents
-# Get the 605 broken SKUs
-timeout 30 psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
- SELECT mfr_sku, product_url, image_url
- FROM justindavid_catalog
- WHERE ai_colors IS NULL
- ORDER BY mfr_sku
- LIMIT 10;"
-```
-
-For each product with a product_url, fetch the page and extract the current main image URL.
-Use a Node.js script with axios to fetch product pages and cheerio to parse image src.
-
-### Step 2: Update image_url in catalog
-After finding new image URLs, update justindavid_catalog.image_url for each product.
-
-### Step 3: Run AI enrichment on fixed images
-```bash
-node /root/DW-Agents/vendor-scrapers/enrich-ai-tags.js justin_david --limit 605
-```
-This will pick up the products that have images but no ai_colors.
-
-### Step 4: Update enrichment tracking P3
-```sql
-UPDATE enrichment_tracking SET phase3_ai_at = NOW()
-WHERE vendor_code = 'justin_david' AND mfr_sku IN (
- SELECT mfr_sku FROM justindavid_catalog WHERE ai_colors IS NOT NULL
-) AND phase3_ai_at IS NULL;
-```
-
-## Success Criteria
-- 0 products with NULL ai_colors that have valid images
-- P3 completion > 95%
diff --git a/tasks/done/01_lod-line-followup.md b/tasks/done/01_lod-line-followup.md
deleted file mode 100644
index a6ec921..0000000
--- a/tasks/done/01_lod-line-followup.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# LOD Line Follow-up — Enrich 3 Active Products
-
-## Context
-Session on 2026-03-26 updated 3 LOD products with fresh images from York CDN and new pricing.
-63 discontinued LOD products were archived. Only 3 remain active:
-
-- LOD-43335 (Shopify ID: 1498182746224) — The Birches Silver — MFR: LL4757 — $102.94
-- LOD-43351 (Shopify ID: 1498188284016) — Aspen Charcoal — MFR: LL4785 — $98.04
-- LOD-43352 (Shopify ID: 1498188611696) — Aspen Teal — MFR: LL4786 — $102.94
-
-## Tasks
-
-### 1. Update MFR SKU metafield
-For each of the 3 active LOD products, set `custom.manufacturer_sku` metafield to the York MFR SKU:
-- LOD-43335 → LL4757
-- LOD-43351 → LL4785
-- LOD-43352 → LL4786
-
-### 2. Run AI enrichment (Gemini Vision)
-Use the enrich-ai-tags workflow on each product's primary image:
-- Extract colors with hex codes and percentages
-- Identify background color + hex
-- Classify styles, patterns, material
-- Set `custom.color_hex` and `custom.color_details` metafields
-
-### 3. Update body_html
-Rewrite body_html for each product as 3-sentence description only (no specs):
-- Sentence 1: What it is (pattern/color/vendor)
-- Sentence 2: Style/mood
-- Sentence 3: Recommended use
-
-### 4. Add MFR SKU as tag
-Add the manufacturer SKU as a Shopify tag on each product for searchability.
-
-### 5. Verify archive cleanup
-Check if any Shopify collections associated with Lodge Living have <10 products remaining.
-If so, archive those collections too per the archive-collection-cleanup rule.
-
-## Important Rules
-- ALL specs go in metafields, NEVER in body_html
-- This is a private label line (Architectural Wallcoverings / Malibu Wallpaper) — NEVER expose "York" as vendor in public content
-- Use Shopify product token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- PostgreSQL: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
diff --git a/tasks/done/01_monitor-crawls.md b/tasks/done/01_monitor-crawls.md
deleted file mode 100644
index 7ec51d9..0000000
--- a/tasks/done/01_monitor-crawls.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Monitor Active Crawls — Nadia (Newwall) + Marco (Marburg)
-
-Check the status of all three vendor crawl agents and report:
-
-1. **Sara (Sancar)** — port 9649. Should be complete at 3,416 products. Verify with:
- ```
- curl -s -u admin:DWSecure2024! http://127.0.0.1:9649/api/status
- ```
-
-2. **Nadia (Newwall)** — port 9654. Crawling 8,574 handles. Check progress:
- ```
- curl -s -u admin:DWSecure2024! http://127.0.0.1:9654/api/status
- ```
-
-3. **Marco (Marburg)** — port 9655. Crawling ~13,600 products via WooCommerce Store API. Check:
- ```
- curl -s -u admin:DWSecure2024! http://127.0.0.1:9655/api/status
- ```
-
-Also check PM2 status for all three:
-```
-pm2 list | grep -E 'sara|nadia|marco'
-```
-
-And check DB catalog counts:
-```
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
-SELECT 'sancar' as vendor, COUNT(*) as total FROM sancar_catalog
-UNION ALL SELECT 'newwall', COUNT(*) FROM newwall_catalog
-UNION ALL SELECT 'marburg', COUNT(*) FROM marburg_catalog;"
-```
-
-If any agent has crashed or stopped, restart it with `pm2 restart <name>` and re-trigger its crawl.
-
-Report all findings clearly.
diff --git a/tasks/done/01_monitor-running-processes.md b/tasks/done/01_monitor-running-processes.md
deleted file mode 100644
index b289d9a..0000000
--- a/tasks/done/01_monitor-running-processes.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## Monitor Running Session #107 Processes
-
-Check status of all 5 running processes and report results.
-
-### Processes to check:
-1. `pm2 logs fix-sample-weights --lines 3 --nostream` — weight fix progress (PM2 #381)
-2. `pm2 logs fix-sample-variants --lines 5 --nostream` — sample variant batch (PM2 #383)
-3. `pm2 logs york-image-scraper --lines 3 --nostream` — York images (PM2 #382)
-4. `pm2 logs brewster-image-scraper --lines 3 --nostream` — Brewster images (PM2 #385)
-5. `pm2 logs specs-bulk-update --lines 3 --nostream` — body_html specs (PM2 #387)
-
-### If any process has stopped/errored:
-- Check the error log: `pm2 logs {name} --lines 20 --nostream`
-- If it completed normally, note the final result
-- If it crashed, restart it: `pm2 restart {name}`
-
-### Report format:
-For each process, state: name, status (running/stopped/done), progress, and any issues.
-
-IMPORTANT: Do NOT create any new Shopify products. Updates to existing products only.
diff --git a/tasks/done/01_newmor-full-monty-test.md b/tasks/done/01_newmor-full-monty-test.md
deleted file mode 100644
index 1cfe695..0000000
--- a/tasks/done/01_newmor-full-monty-test.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# NEWMOR Full Monty — Test 1 Product
-
-## Context
-NEWMOR has 1,285 products in `newmor_catalog` PostgreSQL table. 653 have width. Zero have AI enrichment. Zero on Shopify.
-Steve wants a 1-product Full Monty test to validate the pipeline before running the full batch.
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-## Steps
-
-### Step 1: Pick a good test product
-Query `newmor_catalog` for a product with image_url, width, and mfr_sku populated. Prefer one with material and fire_rating too. Good candidate: mfr_sku = 'CHARCOAL' (pattern: Mid-Century Modern Floral Geo, width: 70cm, material: Non-Woven 460gsm, fire_rating: Type II).
-
-### Step 2: Run Phase 3 (AI Tags) on 1 product
-```bash
-cd /root/DW-Agents/vendor-scrapers
-node enrich-ai-tags.js newmor --limit 1
-```
-This runs Gemini Vision on the product image to extract colors, hex codes, styles, patterns, background color, and image type.
-
-### Step 3: Verify AI enrichment results
-Query the database to confirm AI data was written:
-```sql
-SELECT mfr_sku, pattern_name, color_name, ai_colors, ai_styles, ai_patterns, ai_background_color, ai_tags, color_hex
-FROM newmor_catalog
-WHERE ai_colors IS NOT NULL
-LIMIT 5;
-```
-
-Also check enrichment_tracking:
-```sql
-SELECT * FROM enrichment_tracking WHERE vendor_code = 'newmor' AND phase3_ai_at IS NOT NULL LIMIT 5;
-```
-
-### Step 4: Run Phase 4 (Silas Validation) on the enriched product
-```bash
-curl -s -u admin:DWSecure2024! http://localhost:9674/api/validate/newmor | head -100
-```
-
-### Step 5: Generate body_html description (3 sentences max, NO specs)
-Write a 3-sentence product description for the test product. Format:
-- Sentence 1: What it is (pattern/color/vendor)
-- Sentence 2: Style/mood
-- Sentence 3: Recommended use
-Store in the `body_html` column of `newmor_catalog`.
-
-### Step 6: Report Results
-Print a clear summary of what was found:
-- Product tested (SKU, pattern, color)
-- AI colors detected (with hex codes)
-- AI styles and patterns
-- Background color
-- Image type classification
-- Body HTML generated
-- Whether the product passes FULLPRODUCT gate (image + width + mfr_sku)
-
-### IMPORTANT RULES
-- ALL specs go in metafields, NEVER in body_html
-- body_html = description ONLY, 3 sentences max
-- Use Gemini analysis key: ${GOOGLE_API_KEY}
-- Track costs via /root/DW-Agents/shared/gemini-cost-tracker.js
-- Do NOT push to Shopify yet — this is Phase 3 test only
diff --git a/tasks/done/01_newmor-rescrape-widths.md b/tasks/done/01_newmor-rescrape-widths.md
deleted file mode 100644
index c2a2d9c..0000000
--- a/tasks/done/01_newmor-rescrape-widths.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# NEWMOR — Re-scrape Missing Widths (632 products)
-
-## Context
-NEWMOR has 1,285 products in `newmor_catalog` but only 653 have width populated. 632 are missing width, which is a FULLPRODUCT gate requirement. Re-run the enrichment scraper to backfill widths from individual product pages.
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-## Steps
-
-### Step 1: Check current width coverage
-```sql
-SELECT
- COUNT(*) as total,
- COUNT(CASE WHEN width IS NOT NULL AND length(width) > 0 THEN 1 END) as has_width,
- COUNT(CASE WHEN width IS NULL OR length(width) = 0 THEN 1 END) as missing_width
-FROM newmor_catalog;
-```
-
-### Step 2: Run the NEWMOR enrichment scraper
-The Python enricher crawls individual product pages to extract specs including width:
-```bash
-cd /root/DW-Agents/vendor-scrapers
-timeout 1800 python3 enrich-newmor.py 2>&1
-```
-
-If the Python enricher fails or doesn't exist, try the JS scraper:
-```bash
-cd /root/DW-Agents/vendor-scrapers
-timeout 1800 node scrape-newmor.js 2>&1
-```
-
-### Step 3: If scrapers don't fill widths, try direct product page scraping
-For products missing width, visit their product URLs and extract width from the page:
-- Newmor products typically show width in specs section
-- Common widths: 70cm, 100cm, 127cm, 130cm, 137cm
-- Check the product URL pattern from existing products in the DB
-
-Query products missing width:
-```sql
-SELECT mfr_sku, pattern_name, color_name, image_url
-FROM newmor_catalog
-WHERE width IS NULL OR length(width) = 0
-LIMIT 20;
-```
-
-If the scraper can't get widths from product pages (many Newmor products may not list width on their page), check if there's a default width for Newmor. Common commercial wallcovering widths from Newmor:
-- Standard: 130cm (51")
-- Wide format: 137cm (54")
-- Narrow: 70cm (27.5")
-
-### Step 4: Verify improvement
-```sql
-SELECT
- COUNT(*) as total,
- COUNT(CASE WHEN width IS NOT NULL AND length(width) > 0 THEN 1 END) as has_width,
- COUNT(CASE WHEN width IS NULL OR length(width) = 0 THEN 1 END) as still_missing
-FROM newmor_catalog;
-```
-
-### Step 5: Report results
-Print a clear summary:
-- Width coverage before and after
-- How many products were updated
-- Any errors encountered
-- Whether all products now pass the width gate
-
-## IMPORTANT
-- Do NOT push anything to Shopify
-- Do NOT run AI enrichment — that's the next task
-- Only focus on backfilling width (and any other missing specs like material, repeat, fire_rating)
-- Use timeout for long-running commands
-- If the scraper takes too long, it's OK to report partial results
diff --git a/tasks/done/01_norma-build-verify.md b/tasks/done/01_norma-build-verify.md
deleted file mode 100644
index fd48980..0000000
--- a/tasks/done/01_norma-build-verify.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Rebuild and verify the Norma project at /root/Projects/Norma/.
-
-Steps:
-1. cd /root/Projects/Norma && npx next build --webpack 2>&1 | tail -50
-2. If build fails, read the error messages and fix the TypeScript issues. Common fixes:
- - Replace `user` with `auth.username` in API routes that use requireRole
- - Fix any missing imports for `requireRole` from `@/lib/require-role`
- - Fix Next.js 16 async params: use `Promise<{ id: string }>` for route params
-3. After fixes, rebuild until it passes
-4. pm2 restart norma-email
-5. Verify the app loads: curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:7400/login
-6. Test login: curl -s -X POST http://127.0.0.1:7400/api/auth/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"DWSecure2024!"}' | head -1
-7. Test staff login: curl -s -X POST http://127.0.0.1:7400/api/auth/login -H 'Content-Type: application/json' -d '{"username":"SDCC","password":"SDCC1999"}' | head -1
-8. Test role gating: use the admin cookie to access /api/settings/tier-credentials (should work), then try with SDCC cookie (should get 403)
-9. Report results including: build status, login test results, role gating test results
diff --git a/tasks/done/01_norma_email_center_demo_video.md b/tasks/done/01_norma_email_center_demo_video.md
deleted file mode 100644
index f2172ee..0000000
--- a/tasks/done/01_norma_email_center_demo_video.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Record Demo Video: Norma Email & Outreach Center
-
-**Goal:** Produce a polished screen recording that walks through every layout and feature of the Norma Email & Outreach Center at http://45.61.58.125:7400/, then uses the system for ~3 minutes with real data.
-
-## What to record (in order)
-
-1. **Login** — admin / DWSecure2024!
-2. **Navigation to Email & Outreach Center** — show the sidebar entry
-3. **All layout modes** — 1-column, 2-column, 3-column, 4-column. Show each one, point out that drag-drop persists, that panels can be collapsed.
-4. **Original Email panel** — paste a real SDCC email (pull from the "Old Emails from SDC" panel)
-5. **Rewrite Controls** — change tone (professional → urgent → empathetic), change paragraph count
-6. **Rewrite Chat panel** — send an instruction like "make the ask clearer and add a specific donation link"
-7. **Version Timeline** — show stars/favorites toggle, click "Favs first" sort, expand a version, demo "Ref: project" and "Ref: universal" buttons
-8. **Score & Feedback panel** — show score, suggestions, click "apply"
-9. **Version Comparison (pros/cons)** — compare two versions side-by-side
-10. **Subject Line Maker** — generate 8 subjects, favorite one, add a learning note
-11. **Reference Library** + **Auto-Suggested References** — demonstrate both panels
-12. **Saved Analyses** panel — show past sessions, open one
-13. **Settings → Universal Email References** — create a universal reference, toggle active/off, search
-14. **Search Saved Emails** panel — trigram search, favorites filter, open a result
-
-## Real-data requirement
-- Use an actual SDCC email from the "Old Emails from SDC" panel (connected via natalia@studentdebtcrisis.org Gmail)
-- Run a real rewrite cycle for ~3 minutes, producing 3-4 distinct versions
-
-## Deliverable
-- MP4, 1920x1080, 30fps, narrated
-- Save to `/root/Projects/Norma/demos/email-center-YYYY-MM-DD.mp4`
-- Also save a shorter 60-second highlight reel to `/root/Projects/Norma/demos/email-center-highlights-YYYY-MM-DD.mp4`
-
-## Tools available
-- ffmpeg for recording/editing
-- Chrome DevTools screen recording via Claude-in-Chrome MCP tools
-- GIF recorder: mcp__claude-in-chrome__gif_creator for short clips if full-video tooling isn't ready
-
-## If blocked
-Log what's missing (e.g., "no narration audio tooling") and produce a silent version plus a written transcript, so a human can overdub later.
diff --git a/tasks/done/01_purge-blog-duplicates.md b/tasks/done/01_purge-blog-duplicates.md
deleted file mode 100644
index 3ea9826..0000000
--- a/tasks/done/01_purge-blog-duplicates.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Purge Duplicate Blog Posts
-
-Working directory: /root/Projects/goodquestion-ai/src/content/posts/
-
-## KEEP ONLY these 18 files:
-- 2026-02-19-zone-culling-nearly-killed-my-3d-app.md
-- 2026-02-20-building-a-skill-system-for-claude-code.md
-- 2026-02-21-the-animation-loop-that-wouldnt-animate.md
-- 2026-02-22-parallel-agents-74-processes-one-server.md
-- 2026-02-23-when-your-ai-writes-a-blog.md
-- 2026-02-24-remote-control-claude-code-from-your-phone.md
-- 2026-02-24-shipping-day-and-the-identity-split.md
-- 2026-02-24-tracking-progress-compact-sessions-and-context-windows.md
-- 2026-02-25-100-portfolios-paper-trading-at-scale.md
-- 2026-02-25-building-a-vendor-onboarding-machine.md
-- 2026-02-25-building-a-video-sanitizer-with-ocr-and-opencv.md
-- 2026-02-25-the-content-sanitizer-that-guards-every-channel.md
-- 2026-02-25-the-scraper-blitz.md
-- 2026-02-25-parallel-agents-at-scale.md
-- 2026-03-14-how-one-ai-system-manages-60000-products-while-you-sleep.md
-- 2026-03-16-data-quality-automation-that-actually-ships.md
-- 2026-03-19-three-tier-auth-system-one-founder-no-dev-team.md
-- prompt-caching-is-everything.md
-
-## DELETE every other .md file in that directory.
-
-Then count remaining (should be 18). List them to confirm.
diff --git a/tasks/done/01_push-fire-ratings.md b/tasks/done/01_push-fire-ratings.md
deleted file mode 100644
index 63feace..0000000
--- a/tasks/done/01_push-fire-ratings.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## Push Fire Rating Metafields to Shopify
-
-6,104 products in vendor_catalog just had fire_rating defaults stamped. Push these to Shopify.
-
-### Steps:
-1. Query vendor_catalog for all on_shopify=true products that have fire_rating set AND shopify_product_id
-2. For each batch of 25, build a GraphQL `metafieldsSet` mutation:
-```graphql
-mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
- metafieldsSet(metafields: $metafields) {
- metafields { key namespace value }
- userErrors { field message }
- }
-}
-```
-3. Each metafield input: `{ ownerId: "gid://shopify/Product/{id}", namespace: "custom", key: "fire_rating", type: "single_line_text_field", value: "{fire_rating}" }`
-4. ALL writes go through Quinn queue: `POST http://127.0.0.1:9660/api/shopify-queue` with `{ method: 'POST', endpoint: '/graphql.json', payload: {query, variables} }`
-5. If Quinn is not available, queue via: `INSERT INTO shopify_queue (method, endpoint, payload, status) VALUES ('POST', '/graphql.json', $1::jsonb, 'pending')`
-
-### Constraints:
-- GraphQL ONLY — never REST
-- Batch 25 metafields per call
-- Use Quinn queue — never direct Shopify API
-- Log progress every 100 products
-- Report final count when done
diff --git a/tasks/done/01_push-versace-vi-shopify.md b/tasks/done/01_push-versace-vi-shopify.md
deleted file mode 100644
index 32043c2..0000000
--- a/tasks/done/01_push-versace-vi-shopify.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Launch Versace VI on Shopify — 136 Products
-
-## Pre-launch Checks
-1. Verify all 136 products have DW SKUs: `SELECT COUNT(*) FROM versace_catalog WHERE dw_sku IS NOT NULL`
-2. Verify all have width: `SELECT COUNT(*) FROM versace_catalog WHERE width IS NOT NULL AND width <> ''`
-3. Check for duplicate mfr_sku on Shopify already: Query Shopify by barcode for each mfr_sku
-
-## Push to Shopify
-For each product in versace_catalog:
-1. Call Silas validate-push: `POST http://127.0.0.1:9674/api/validate-push`
-2. If approved, create on Shopify as ACTIVE with:
- - Title: `{pattern_name} - {color_name} Wallcovering | Versace`
- - vendor: "Versace"
- - product_type: "Wallcovering"
- - tags: "Versace, Versace VI, {pattern_name}, Luxury, Italian, A.S. Création"
- - variant SKU: DW SKU + "-Sample" at $4.25
- - variant barcode: mfr_sku
- - Metafields (global.*): width, manufacturer_sku, Brand, Collection, Contents, application
- - Width: 53 cm (20.9 in)
- - Material: Non-woven
- - Collection: Versace VI
- - Brand: Versace
- - Application: Paste the wall
-
-## IMPORTANT
-- DO NOT push without Silas validation
-- Use `push-metafields-vendor.py versace_catalog` for metafields AFTER product creation
-- Check for existing Versace products on Shopify first to avoid duplicates
-- Produced by A.S. Création — note this in description but vendor = "Versace"
diff --git a/tasks/done/01_ralphy-boy-continue.md b/tasks/done/01_ralphy-boy-continue.md
deleted file mode 100644
index 8c418ea..0000000
--- a/tasks/done/01_ralphy-boy-continue.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Continue Ralphy Boy
-
-Check Ralph's current status and continue autonomous feature development.
-
-```bash
-# Check status
-curl -s http://45.61.58.125:7300/api/ralph/status 2>/dev/null | python3 -c "import json,sys;print(json.dumps(json.load(sys.stdin),indent=2))"
-
-# Continue
-curl -X POST http://45.61.58.125:7300/api/ralph/continue 2>/dev/null
-
-# Monitor for 2 minutes
-timeout 120 tail -f /tmp/ralph-output.log 2>/dev/null
-```
-
-If Ralph is idle or stopped, check /root/Projects/Letsbegin/ for pending tasks and restart.
diff --git a/tasks/done/01_raoul-to-anna-french.md b/tasks/done/01_raoul-to-anna-french.md
deleted file mode 100644
index dbb36e5..0000000
--- a/tasks/done/01_raoul-to-anna-french.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Fix Raoul Textiles → Anna French (93 products)
-
-## Context
-93 live Shopify products have vendor="Raoul Textiles" but should be "Anna French". These are DWAT-prefix SKUs (Anna French / Thibaut catalog). PG mirror is already correct. There are also 93 pending Quinn jobs (source_agent='raoul-vendor-fix') that should be deleted after this direct fix.
-
-## Steps
-
-1. Connect to Shopify Admin GraphQL at `https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json` with token `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`.
-
-2. Pull ALL products with vendor='Raoul Textiles' (paginate with cursor). Expect ~93.
-
-3. Batch-update them using aliased GraphQL mutations (25 per call, 4 batches). Each mutation:
-```graphql
-mutation { p0: productUpdate(input: {id: "gid://...", vendor: "Anna French"}) { product { id vendor } userErrors { field message } } ... }
-```
-
-4. After all batches complete, verify: query `productsCount(query: "vendor:'Raoul Textiles'")` should return 0.
-
-5. Delete the now-redundant Quinn queue entries:
-```sql
-DELETE FROM shopify_api_queue WHERE source_agent = 'raoul-vendor-fix' AND status = 'pending';
-```
-Use pg connection: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-6. Print final summary: how many updated, how many errors, final Raoul count on Shopify.
-
-## Important
-- Use `fetch()` for Shopify API calls (Node.js built-in)
-- Use `/root/DW-Agents/node_modules/pg` for PostgreSQL
-- Rate limit: max 2 requests/second to Shopify
-- If any userErrors, log them but continue with remaining batches
diff --git a/tasks/done/01_resume-pointe-updater.md b/tasks/done/01_resume-pointe-updater.md
deleted file mode 100644
index b1c4f54..0000000
--- a/tasks/done/01_resume-pointe-updater.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Resume Pointe Updater — Remaining 690 Products
-
-**Priority**: P0
-**Estimated time**: ~6 hours
-**Type**: Data pipeline
-
-## Description
-Resume the Pointe Fabrics private label updater for the remaining ~690 products that still need DW names.
-
-## Steps
-1. Run `python3 /root/DW-Agents/pointe-updater/updater.py` — picks up where it left off (products without dw_color)
-2. Monitor progress via `cat /root/DW-Agents/pointe-updater/progress.json`
-3. After completion, run `python3 /root/DW-Agents/pointe-updater/handle-updater.py` for newly updated products
-4. Verify: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT COUNT(*) FROM phillipe_romano_catalog WHERE mfr_sku LIKE 'PI-%' AND (dw_color IS NULL OR dw_color = '') AND shopify_product_id IS NOT NULL"`
-
-## Success criteria
-- All Pointe products on Shopify have DW pattern names, colors, tags
-- All handles reformatted to {pattern}-{color}-{sku}
-- All old URLs have 301 redirects
diff --git a/tasks/done/01_resume-weight-fix.md b/tasks/done/01_resume-weight-fix.md
deleted file mode 100644
index 4b41201..0000000
--- a/tasks/done/01_resume-weight-fix.md
+++ /dev/null
@@ -1,10 +0,0 @@
-## Resume Weight Fix (remaining variants)
-
-Run the weight fix script. It's idempotent — re-scans all active products and fixes any variant with non-zero weight.
-
-```bash
-cd /root/DW-Agents/scripts
-python3 fix-weights.py 2>&1 | tail -20
-```
-
-Report how many variants were fixed.
diff --git a/tasks/done/01_small-vendor-image-sweep.md b/tasks/done/01_small-vendor-image-sweep.md
deleted file mode 100644
index fbb5a25..0000000
--- a/tasks/done/01_small-vendor-image-sweep.md
+++ /dev/null
@@ -1,49 +0,0 @@
-## Small Vendor Image Sweep — Final Push to 100%
-
-Hit ALL remaining vendors with <50 missing images. Use existing scrapers or try product URLs.
-
-### Steps:
-1. For each vendor below, check if enrichment scraper exists and run it:
-
-**Maya Romanoff (44 missing)**:
-- Check: `ls /root/DW-Agents/vendor-scrapers/*maya*`
-- Products have no product_url — try constructing from pattern name on mayaromanoff.com
-- CDN: assets.mayaromanoff.com
-
-**Arte International (30 missing)**:
-- Check if packshot_url can fill image_url:
- ```sql
- UPDATE arte_catalog SET image_url = packshot_url WHERE (image_url IS NULL OR image_url = '') AND packshot_url IS NOT NULL AND packshot_url != '';
- ```
-
-**Franquemont (26 missing)** and **Jim Dultz (24 missing)**:
-- Check if scraper exists: `ls /root/DW-Agents/vendor-scrapers/*franque* /root/DW-Agents/vendor-scrapers/*dultz*`
-- Run if available
-
-**Thibaut (21 missing)**:
-- Scraper exists: `ls /root/DW-Agents/vendor-scrapers/*thibaut*`
-- Run: `cd /root/DW-Agents/vendor-scrapers && timeout 300 node enrich-thibaut-specs.js`
-
-**Koroseal (20 missing)**:
-- Check: `ls /root/DW-Agents/vendor-scrapers/*koroseal*`
-- Run if available
-
-**William Morris (14 missing)**, **Cole & Son (13 missing)**, **BN Walls (10 missing)**, **Stout (8 missing)**:
-- Check for enrichment scrapers and run them
-
-2. After all runs, recount:
-```sql
-DO $$
-DECLARE v RECORD; tc INT; ic INT;
-BEGIN
- FOR v IN SELECT vendor_code, catalog_table FROM vendor_registry WHERE catalog_table IS NOT NULL LOOP
- BEGIN
- EXECUTE format('SELECT COUNT(*), COUNT(NULLIF(image_url, '''')) FROM %I', v.catalog_table) INTO tc, ic;
- UPDATE vendor_registry SET total_products = tc, products_with_images = ic WHERE vendor_code = v.vendor_code;
- EXCEPTION WHEN OTHERS THEN NULL;
- END;
- END LOOP;
-END $$;
-```
-
-3. Report final stats
diff --git a/tasks/done/01_vendor-width-audit.md b/tasks/done/01_vendor-width-audit.md
deleted file mode 100644
index f0ec409..0000000
--- a/tasks/done/01_vendor-width-audit.md
+++ /dev/null
@@ -1,36 +0,0 @@
-## Vendor Width Audit — Find and Fix Missing Widths
-
-Width is REQUIRED for Shopify import. Audit all non-private-label vendors with missing widths and run enrichment scrapers where available.
-
-### Steps:
-1. Query vendors with low width coverage:
- ```sql
- SELECT v.vendor_name, v.vendor_code, v.total_products,
- (SELECT COUNT(*) FROM information_schema.columns WHERE table_name = v.catalog_table AND column_name = 'width') as has_width_col
- FROM vendor_registry v
- WHERE v.total_products > 50 AND v.private_label = false
- ORDER BY v.total_products DESC;
- ```
- DB: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified"`
-
-2. For each vendor with a width column, check coverage:
- ```sql
- SELECT COUNT(*) as total, COUNT(NULLIF(width,'')) as has_width FROM {catalog_table};
- ```
-
-3. Check for existing enrichment scrapers:
- ```bash
- ls /root/DW-Agents/vendor-scrapers/enrich-*-specs.js
- ```
-
-4. For vendors below 50% width coverage that have enrichment scrapers, run them:
- ```bash
- cd /root/DW-Agents/vendor-scrapers && timeout 600 node enrich-{vendor}-specs.js
- ```
-
-5. Report final width coverage per vendor
-
-### CRITICAL RULES:
-- Do NOT push to Shopify
-- Do NOT create new SKUs
-- Only enrich existing catalog data in PostgreSQL
diff --git a/tasks/done/01_verify-dual-docs.md b/tasks/done/01_verify-dual-docs.md
deleted file mode 100644
index be95da8..0000000
--- a/tasks/done/01_verify-dual-docs.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Verify Daily Notes Dual-Doc System
-
-The daily notes system was just updated to push to TWO Google Docs:
-1. **Original doc** (DW-only): `11eQo3qspd5wfyuTLp7tjGnDzp0q1XoNwL2V84WzZKJI` — filters out non-DW entries
-2. **Steve's Daily Update** (all projects): `1PX9v2U5XzJLl2ARed_ZctXiHHI2w9RnP1w7gaJRnLfU` — no filters
-
-## Tasks
-
-1. Read `/root/DW-Agents/shared/update-daily-notes.js` and confirm it has both `DOC_ID` and `STEVE_DOC_ID` constants and the `isDWRelated()` filter function.
-
-2. Verify the filter works by running the state through it — count total, DW-only, and filtered entries.
-
-3. Check the heartbeat cron is active: `crontab -l | grep heartbeat`
-
-4. Verify both Google Docs exist and were recently updated by checking their modification times via the Drive API (use the googleapis module from `/root/DW-Agents/gmail-agent/node_modules`).
-
-5. Trigger a test update by running:
- ```
- NODE_PATH=/root/DW-Agents/gmail-agent/node_modules node /root/DW-Agents/shared/update-daily-notes.js --title "YOLO Verification" --details "Dual doc system verified|Both docs updating correctly"
- ```
-
-6. Report final status: entry counts, filter accuracy, both docs accessible.
diff --git a/tasks/done/01_verify-kamism-spins.md b/tasks/done/01_verify-kamism-spins.md
deleted file mode 100644
index c7a26cf..0000000
--- a/tasks/done/01_verify-kamism-spins.md
+++ /dev/null
@@ -1,7 +0,0 @@
-Check if the Kamism spin generation is complete. The script is running at PID 86071 (or may have finished).
-
-1. Check: `tail -20 /tmp/kamism-spins-v2.log`
-2. If complete, verify output: `ls /root/DW-Agents/vendor-scrapers/kamism-spins/*.gif | wc -l` — should be 45
-3. Verify a sample GIF is circular (not rectangular): `identify /root/DW-Agents/vendor-scrapers/kamism-spins/KPU134-swatch-spin.gif | head -1` — should be 350x350
-4. Check file sizes: `ls -lhS /root/DW-Agents/vendor-scrapers/kamism-spins/*.gif | head -5` — should be 1.5-4MB each
-5. Report the final count and any errors from the log
diff --git a/tasks/done/02_02_enrich-coordonne.md b/tasks/done/02_02_enrich-coordonne.md
deleted file mode 100644
index 3549611..0000000
--- a/tasks/done/02_02_enrich-coordonne.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-coordonne-specs.js
-
-Enrich Coordonné catalog with all specs.
\ No newline at end of file
diff --git a/tasks/done/02_02_fix-room-setting-images.md b/tasks/done/02_02_fix-room-setting-images.md
deleted file mode 100644
index 3ad3b47..0000000
--- a/tasks/done/02_02_fix-room-setting-images.md
+++ /dev/null
@@ -1 +0,0 @@
-7 Karin products have room setting photos as their PRIMARY image instead of product swatches. Fix these by finding proper swatch images via Bing search (SKU + grasscloth wallcovering). Products: DWPW-430005 (HBD9020T), DWPW-430020 (WP7594), DWPW-430035 (SH1519T), DWPW-430109 (ZG1502), DWPW-430160 (TW68063), DWPW-430181 (TW68089), DWPW-430201 (TSJ7549). For each, search Bing, find a swatch image, update the Shopify product image.
\ No newline at end of file
diff --git a/tasks/done/02_02_hollywood-unmatched-skus.md b/tasks/done/02_02_hollywood-unmatched-skus.md
deleted file mode 100644
index 18e212d..0000000
--- a/tasks/done/02_02_hollywood-unmatched-skus.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Find Hollywood (X-prefix) products whose momentum_sku pattern name does not match any pattern in momentum_colorways table. The momentum_sku field uses format PatternName_InternalSKU. Split on underscore to get pattern name. Count unmatched, group by pattern. Save report to /root/DW-Agents/logs/hollywood-unmatched-report.json
\ No newline at end of file
diff --git a/tasks/done/02_02_investigate-unmatched-patterns.md b/tasks/done/02_02_investigate-unmatched-patterns.md
deleted file mode 100644
index 6d4c060..0000000
--- a/tasks/done/02_02_investigate-unmatched-patterns.md
+++ /dev/null
@@ -1,21 +0,0 @@
-Hollywood catalog — investigate 85 unmatched Momentum-format patterns.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-1. Get the 85 distinct unmatched patterns:
- SELECT DISTINCT SPLIT_PART(momentum_sku, '_', 1) as pattern, COUNT(*) as cnt FROM hollywood_catalog WHERE mfr_sku LIKE 'X%' AND momentum_sku LIKE '%\_%' AND momentum_sku != mfr_sku AND price_retail IS NULL GROUP BY 1 ORDER BY cnt DESC;
-
-2. For each pattern, check the momentum_sku format — the part after underscore often indicates the source manufacturer:
- - T2-XX = Momentum Type II
- - L2-XX = Momentum Luxury
- - ACS/ACU = Momentum Acoustical
- - sg/a/bk prefix = possibly GenOn or Koroseal
- Look at the SKU codes and categorize which MDC supplier line each likely comes from.
-
-3. Try searching the Momentum website via cloudscraper to see if any of these patterns still exist:
- pip3 install cloudscraper (if needed)
- Use cloudscraper to fetch https://www.momentumtextilesandwalls.com/search?q=PATTERN_NAME for a sample of 5 patterns.
-
-4. Update the vendor_source field in hollywood_catalog with your findings (e.g. 'momentum_discontinued', 'mdc_other', 'unknown').
-
-5. Report findings: how many are likely discontinued Momentum vs other MDC suppliers.
\ No newline at end of file
diff --git a/tasks/done/02_02_norma-git-commit.md b/tasks/done/02_02_norma-git-commit.md
deleted file mode 100644
index f653de5..0000000
--- a/tasks/done/02_02_norma-git-commit.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Commit all three-tier UI work in /root/Projects/Norma/.
-1. cd /root/Projects/Norma && git add components/ lib/ app/ middleware.ts docs/ db/ -A
-2. git status to verify
-3. Commit with message: feat: complete three-tier UI implementation (Steps 11-18) - Sidebar filtering, ManageTabsModal, OrgProvider auto-set, 201 API routes gated, Credentials CRUD, Pulse Config UI, Middleware, Pulse Activity/Tracked/Profile pages, Personalized feed
-4. Report commit hash
\ No newline at end of file
diff --git a/tasks/done/02_02_vendor-catalog-audit.md b/tasks/done/02_02_vendor-catalog-audit.md
deleted file mode 100644
index 1655e16..0000000
--- a/tasks/done/02_02_vendor-catalog-audit.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified and run: SELECT table_name, (SELECT count(*) FROM information_schema.tables t2 WHERE t2.table_name = t.table_name) as exists_flag FROM vendor_registry ORDER BY vendor_name. Then for each vendor, count products in their catalog table. Report vendors with 0 products vs vendors with products. Save report to /root/DW-Agents/logs/vendor-audit.log
\ No newline at end of file
diff --git a/tasks/done/02_02_versa-remaining-push.md b/tasks/done/02_02_versa-remaining-push.md
deleted file mode 100644
index 3832ed2..0000000
--- a/tasks/done/02_02_versa-remaining-push.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Check how many Versa products still need pushing to Shopify:
-
-```bash
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT COUNT(*) FILTER (WHERE shopify_product_id IS NOT NULL) as on_shopify, COUNT(*) FILTER (WHERE shopify_product_id IS NULL AND dw_sku IS NOT NULL AND image_url IS NOT NULL AND width IS NOT NULL AND mfr_sku IS NOT NULL) as remaining FROM versa_catalog"
-```
-
-If remaining > 0, run: `cd /root/DW-Agents/vendor-scrapers && timeout 900 node push-versa-to-shopify.js 400`
-
-Report results.
\ No newline at end of file
diff --git a/tasks/done/02_100pct-readiness-push.md b/tasks/done/02_100pct-readiness-push.md
deleted file mode 100644
index 6c47ea2..0000000
--- a/tasks/done/02_100pct-readiness-push.md
+++ /dev/null
@@ -1,53 +0,0 @@
-Push all products to 100% readiness by classifying unknowns and re-stamping ready_at.
-
-## Current State (2026-04-01 — updated)
-- Total live: 43,040
-- Ready (with ready_at): 40,948 (95.1%)
-- Unknowns: 1,481
-- Classifiers running: York Contract, Graham Brown still active
-
-## Steps
-
-### Step 1: Check remaining unknowns by vendor
-```bash
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
- SELECT vendor_code, COUNT(*) as unknowns
- FROM enrichment_tracking
- WHERE phase8_shopify_at IS NOT NULL AND repeat_type = 'unknown'
- GROUP BY vendor_code ORDER BY unknowns DESC LIMIT 20;
-"
-```
-
-### Step 2: Run classifiers for remaining unknowns
-```bash
-cd /root/DW-Agents
-for v in $(psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -t -A -c "
- SELECT vendor_code FROM enrichment_tracking
- WHERE phase8_shopify_at IS NOT NULL AND repeat_type = 'unknown'
- GROUP BY vendor_code HAVING COUNT(*) > 5 ORDER BY COUNT(*) DESC;
-"); do
- cnt=$(psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -t -A -c "
- SELECT COUNT(*) FROM enrichment_tracking WHERE vendor_code='$v' AND repeat_type='unknown' AND phase8_shopify_at IS NOT NULL;
- ")
- nohup node vendor-scrapers/classify-repeat-type.js --vendor "$v" --limit "$cnt" > logs/classify-yolo-$v.log 2>&1 &
- echo "Started $v ($cnt unknown)"
-done
-```
-
-### Step 3: Re-stamp ready_at after classification
-```bash
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
- UPDATE enrichment_tracking SET ready_at = NOW(), ready_score = 8
- WHERE phase8_shopify_at IS NOT NULL AND repeat_type IN ('texture','pattern') AND ready_at IS NULL;
-"
-```
-
-### Step 4: For vendors with dead image URLs (like Ralph Lauren), use name-based heuristics
-- Pattern keywords: Damask, Stripe, Plaid, Floral, Toile, Paisley, Trellis, Lattice, Check, Dot, etc.
-- Texture keywords: Linen, Grasscloth, Suede, Leather, Canvas, Stucco, Weave, Burlap, etc.
-- Default remaining to 'texture' (safer default)
-
-### Success Criteria
-- Unknown count = 0
-- Ready percentage = 100%
-- All enrichment_tracking rows have ready_at set
diff --git a/tasks/done/02_activate-versace-drafts.md b/tasks/done/02_activate-versace-drafts.md
deleted file mode 100644
index e4aaf39..0000000
--- a/tasks/done/02_activate-versace-drafts.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Activate 136 Versace Draft Products on Shopify
-
-136 Versace VI products are on Shopify as DRAFT. They need enrichment + activation.
-
-## Steps for EACH product:
-
-### 1. Find all Versace drafts
-```sql
-SELECT mfr_sku, dw_sku, pattern_name, color_name, shopify_product_id, width, material, collection, image_url
-FROM versace_catalog WHERE shopify_product_id IS NOT NULL
-```
-
-### 2. Generate Gemini AI description
-For each product with an image, call Gemini to generate a 2-3 sentence luxury wallcovering description.
-API key: ${GOOGLE_API_KEY}
-Model: gemini-2.0-flash
-Push result to Shopify body_html (description only, NO specs table).
-
-### 3. Push metafields via GraphQL metafieldsSet
-For each product, push ALL available specs to global.* namespace:
-- global.width (from DB width column)
-- global.manufacturer_sku (from mfr_sku)
-- global.Brand = "Versace"
-- global.Collection = "Versace VI"
-- global.Contents = "Non-woven"
-- global.application = "Paste the wall"
-- global.repeat (if pattern, not texture — check pattern_name, textures have no repeat)
-
-Use GraphQL metafieldsSet mutation — ONE call per product (not REST).
-Endpoint: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-### 4. Silas validate-activation
-POST http://127.0.0.1:9674/api/validate-activation
-Body: { shopify_product_id }
-Only activate if Silas approves.
-
-### 5. Activate
-PUT product status to 'active' via REST API.
-Only if Silas approved AND metafields are confirmed.
-
-### 6. Skip products without images
-67 of 136 have images. Products without images CANNOT be activated (Silas will block).
-Log skipped products.
-
-## Rate limits
-- Gemini: 1 req/sec
-- Shopify GraphQL: 2 req/sec
-- Shopify REST: 2 req/sec
-- Silas: no limit (local)
-
-## Report
-Total drafts, enriched with description, metafields pushed, activated, blocked by Silas, skipped (no image).
-
-## CRITICAL
-- Body HTML = description ONLY, no specs tables
-- All specs in metafields exclusively (global.* namespace)
-- NEVER use word "wallpaper" — always "wallcovering"
-- Use Gemini for image analysis, NEVER Claude Vision
diff --git a/tasks/done/02_ai-enrich-villa-nova.md b/tasks/done/02_ai-enrich-villa-nova.md
deleted file mode 100644
index d189bab..0000000
--- a/tasks/done/02_ai-enrich-villa-nova.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# AI Enrich — Villa Nova (357 products missing AI colors)
-
-Same as Romo task but for villa_nova vendor_code. Limit 200 per run.
-
-```bash
-cd /root/DW-Agents/full-monte
-node full-monte-batch.js --vendor villa_nova --phase 3 --limit 200
-```
-Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/02_archive-ncf-fabrics.md b/tasks/done/02_archive-ncf-fabrics.md
deleted file mode 100644
index 57a350d..0000000
--- a/tasks/done/02_archive-ncf-fabrics.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Archive 114 Nina Campbell Fabric Orphans
-
-The DWC legacy audit found 114 products under vendor "Nina Campbell Wallcoverings" that have NCF- prefix SKUs. These are FABRICS, not wallcoverings. They should be archived.
-
-### Steps:
-1. Query Shopify API for active products where vendor = "Nina Campbell Wallcoverings"
-2. Filter to those with variant SKU containing "NCF-" or barcode containing "NCF-"
-3. Archive each one: PUT /admin/api/2024-01/products/{id}.json with {"product":{"status":"archived"}}
-4. Rate limit: 0.5 req/s, check x-shopify-shop-api-call-limit header
-5. Report how many archived
-
-Shop: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-API version: 2024-01
-
-IMPORTANT: Only archive products with NCF- prefix. Do NOT archive NCW- products (those are wallcoverings).
\ No newline at end of file
diff --git a/tasks/done/02_bespoke-width-inference.md b/tasks/done/02_bespoke-width-inference.md
deleted file mode 100644
index f890cdc..0000000
--- a/tasks/done/02_bespoke-width-inference.md
+++ /dev/null
@@ -1,47 +0,0 @@
-Fill Bespoke width gaps via pattern-family inference — 4,584 wallcoverings missing width, no external site to scrape.
-
-Bespoke is DW's private label. No vendor website. But 1,678 products DO have width. Many share the same pattern_name or collection — siblings should have the same width.
-
-Steps:
-1. Check how many can be inferred from siblings:
-```sql
-SELECT COUNT(*) FROM bespoke_catalog b
-WHERE b.width IS NULL
- AND EXISTS (SELECT 1 FROM bespoke_catalog s WHERE s.pattern_name = b.pattern_name AND s.width IS NOT NULL);
-```
-
-2. Run the inference:
-```sql
-UPDATE bespoke_catalog b
-SET width = s.width
-FROM (
- SELECT DISTINCT pattern_name, width
- FROM bespoke_catalog
- WHERE width IS NOT NULL AND pattern_name IS NOT NULL
-) s
-WHERE b.pattern_name = s.pattern_name AND b.width IS NULL;
-```
-
-3. For remaining gaps, try collection-level inference:
-```sql
-UPDATE bespoke_catalog b
-SET width = s.mode_width
-FROM (
- SELECT collection, MODE() WITHIN GROUP (ORDER BY width) AS mode_width
- FROM bespoke_catalog
- WHERE width IS NOT NULL AND collection IS NOT NULL
- GROUP BY collection
-) s
-WHERE b.collection = s.collection AND b.width IS NULL;
-```
-
-4. For STILL remaining gaps, try product_type-level heuristic:
-- "Type II Vinyl" → "54"
-- "Grasscloth" → "36"
-- "Mural" → varies, skip
-- Standard wallcovering → "20.5"
-
-5. Report: how many filled at each step, how many still missing.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: bespoke_catalog
diff --git a/tasks/done/02_blog-post.md b/tasks/done/02_blog-post.md
deleted file mode 100644
index 545ea36..0000000
--- a/tasks/done/02_blog-post.md
+++ /dev/null
@@ -1,12 +0,0 @@
-## Publish Blog Post — Data Quality Automation
-
-Generate and publish a blog post to goodquestion.ai about the latest infrastructure work:
-- Autonomous YOLO agent processing 80+ tasks
-- Specs pushed to 6+ vendors (PJ, York, RL, Schumacher)
-- Zero-price fixes, NCF archival
-- Width enrichment campaigns
-
-Follow the blog pipeline: Generate → Sanitize → Publish → Build Astro → Push Git.
-Use the blog-agent at port 9871 or run the pipeline directly.
-Sanitize per MEMORY.md rules — NO vendor names, NO internal data.
-Target audience: entrepreneurs and small business owners.
diff --git a/tasks/done/02_build-as-creation-agent.md b/tasks/done/02_build-as-creation-agent.md
deleted file mode 100644
index 494d9eb..0000000
--- a/tasks/done/02_build-as-creation-agent.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Build AS Creation Agent — Ace
-
-Build a scraper agent for AS Creation at https://products.as-creation.com/en/Collections/
-
-## Key Facts
-- **Platform**: Shopware 6 (NOT Shopify, NOT WooCommerce)
-- **Codename**: Ace (A = AS Creation)
-- **Port**: 9656
-- **Directory**: /root/DW-Agents/ace-agent/
-- **Sitemap**: https://products.as-creation.com/sitemap.xml (contains gzipped product sitemap)
-- **API**: Shopware Store API at /store-api/product requires `sw-access-key` header
-- **US Distribution**: Sancar is US distributor. Astek may also rep their lines.
-
-## Find the sw-access-key
-The access key is usually in the page source HTML. Look for it:
-```bash
-curl -sk 'https://products.as-creation.com/en/' | grep -i 'access.key\|sw-access\|salesChannel'
-```
-Or check the JavaScript files for the key.
-
-## Build Steps
-1. Create /root/DW-Agents/ace-agent/ directory
-2. Create package.json with express and pg dependencies
-3. Build server.js following the pattern of marco-agent (/root/DW-Agents/marco-agent/server.js) but adapted for Shopware:
- - POST to /store-api/product with sw-access-key header
- - Paginate with `page` and `limit` params
- - Extract all product attributes/properties for specs
-4. Create as_creation_catalog table with ALL spec columns including:
- - us_distributor (default: 'Sancar Wallcoverings')
- - showroom_locations
- - All standard spec columns (width, length, repeat_v, repeat_h, material, fire_rating, finish, etc.)
-5. npm install
-6. Register in vendor_registry
-7. Open firewall: `sudo ufw allow from 76.33.146.135 to any port 9656 proto tcp`
-8. Start with PM2: `pm2 start server.js --name ace-agent`
-9. Trigger 3h continuous crawl
-
-## If Store API doesn't work:
-Fall back to sitemap approach:
-1. Download the gzipped sitemap
-2. Extract product URLs
-3. Curl each product page and parse the HTML for specs
-4. Use execFileSync('curl', ['-sk', url]) to avoid SSL issues
-
-## CRITICAL RULE
-**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
-
-## Slack Notification — REQUIRED
-```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"AS CREATION AGENT BUILT: Ace on port 9656. [X] products crawled. US distributor: Sancar."}' "${SLACK_WEBHOOK_URL}"
-```
diff --git a/tasks/done/02_check-image-scrapers-complete.md b/tasks/done/02_check-image-scrapers-complete.md
deleted file mode 100644
index 0cd2aeb..0000000
--- a/tasks/done/02_check-image-scrapers-complete.md
+++ /dev/null
@@ -1,28 +0,0 @@
-## Check Image Scrapers Completion & Report
-
-After the York and Brewster image scrapers finish, generate a completion report.
-
-### Steps:
-1. Check `pm2 show york-image-scraper` — if stopped, read final log:
- `tail -30 /root/.pm2/logs/york-image-scraper-out.log`
-2. Check `pm2 show brewster-image-scraper` — if stopped, read final log:
- `tail -30 /root/.pm2/logs/brewster-image-scraper-out.log`
-3. Query DB for updated coverage:
- ```sql
- SELECT 'York' as vendor, COUNT(*) as total,
- COUNT(NULLIF(image_url,'')) as has_image,
- ROUND(100.0 * COUNT(NULLIF(image_url,'')) / COUNT(*), 1) as pct
- FROM york_catalog
- UNION ALL
- SELECT 'Brewster', COUNT(*),
- COUNT(NULLIF(image_url,'')),
- ROUND(100.0 * COUNT(NULLIF(image_url,'')) / COUNT(*), 1)
- FROM brewster_catalog;
- ```
-4. DB connection: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-### If scrapers are still running:
-- Report current progress and ETA
-- Skip this task — it will run again in the next cycle
-
-IMPORTANT: Do NOT push any products to Shopify. This is DB-only work.
diff --git a/tasks/done/02_china-seas-room-settings.md b/tasks/done/02_china-seas-room-settings.md
deleted file mode 100644
index bef1458..0000000
--- a/tasks/done/02_china-seas-room-settings.md
+++ /dev/null
@@ -1,27 +0,0 @@
-## Fetch Room Setting Photos for China Seas Wallpapers
-
-Room setting images from Quadrille's S3 bucket need to be saved to the `rooms` column in china_seas_catalog.
-
-Update these patterns with room setting URLs (apply to ALL colorways of each pattern):
-
-```sql
-UPDATE china_seas_catalog SET rooms = '["https://quadrillefabrics.s3.us-east-2.amazonaws.com/Editorial-Images/Climbing-Hydrangea-wallpaper-Summer-Thornton-photographer-Bjorn-Wallander-House-Beautiful-January-2019-OT1215806-1000.jpg"]'
-WHERE pattern_name ILIKE '%Climbing Hydrangea%';
-
-UPDATE china_seas_catalog SET rooms = '["https://quadrillefabrics.s3.us-east-2.amazonaws.com/Editorial-Images/Fern-Cliff-wallpaper-drapery-Simms-Hayes-Design-Kips-Bay-Showhouse-November-2023-1000.jpg"]'
-WHERE pattern_name ILIKE '%Fern Cliff%';
-
-UPDATE china_seas_catalog SET rooms = '["https://quadrillefabrics.s3.us-east-2.amazonaws.com/Editorial-Images/Palm-Garden-wallpaper-Elizabeth-Malmo-Interior-Design-Photography-Sarah-Rossi-Selavie-Photography1-1000.jpg"]'
-WHERE pattern_name ILIKE '%Palm Garden%';
-```
-
-Then for each product that has a room setting AND a shopify_product_id, push the room setting image to Shopify as an additional product image using the REST API:
-```
-POST /admin/api/2024-01/products/{id}/images.json
-{"image": {"src": "URL", "alt": "Room setting"}}
-```
-
-Use token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Report how many room settings were added to DB and how many pushed to Shopify.
diff --git a/tasks/done/02_enrich-remaining-gaps.md b/tasks/done/02_enrich-remaining-gaps.md
deleted file mode 100644
index ef16a19..0000000
--- a/tasks/done/02_enrich-remaining-gaps.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# AI Enrich — Remaining Gaps (Arte 9, Black Edition 4)
-
-13 products still missing ai_colors in vendor_catalog.
-
-For Arte (9 missing): Check arte_catalog for matching products and sync. If not in arte_catalog, run Gemini directly.
-For Black Edition (4 missing, SKUs W924/01-04): Not in black_edition_catalog. Run Gemini Vision directly.
-
-Gemini key: ${GOOGLE_API_KEY}
-Model: gemini-2.0-flash
diff --git a/tasks/done/02_fix-missing-sample-variants.md b/tasks/done/02_fix-missing-sample-variants.md
deleted file mode 100644
index dd7a875..0000000
--- a/tasks/done/02_fix-missing-sample-variants.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Fix Missing Sample Variants — Bulk Audit & Repair
-
-## Objective
-Find ALL active Shopify products that are missing a $4.25 Sample variant, then add one.
-
-## Steps
-
-1. **Audit**: Use GraphQL bulk query to fetch all active products with their variants. Identify any product where NO variant has:
- - SKU ending in `-Sample`, OR
- - price = `4.25`, OR
- - title = `Sample`
-
-2. **Report count** of affected products before making changes
-
-3. **For each missing product**, create a Sample variant:
- - `option1`: "Sample"
- - `price`: "4.25"
- - `sku`: "{existing_sku}-Sample"
- - `weight`: 0
- - `weight_unit`: "lb"
- - `inventory_management`: "shopify"
- - `requires_shipping`: true
-
-4. After creating each sample variant, set its inventory to 2026 at location 5795643504
-
-5. **Rate limit awareness**:
- - Daily variant creation limit is ~1,000/24h rolling window
- - If you hit 429 "Daily variant creation limit reached", STOP and log how many remain
- - API call rate: stay under 2 req/s (use 500ms delay between calls)
-
-6. Also check: any existing variant with inventory_quantity = 0 that should have 2026. Fix those too.
-
-## Known affected (from manual audit):
-- Zoffany products (3 confirmed missing) — DWZF-187057, DWZF-187058, DWZF-187321
-- Run full scan across all 58K active products
-
-## Token
-Use `<redacted:SHOPIFY_ADMIN_TOKEN>` for product operations.
-
-## Output
-Print summary: total scanned, total missing sample, total fixed, total blocked by rate limit
diff --git a/tasks/done/02_fix-out-of-range-skus.md b/tasks/done/02_fix-out-of-range-skus.md
deleted file mode 100644
index 9ae755a..0000000
--- a/tasks/done/02_fix-out-of-range-skus.md
+++ /dev/null
@@ -1,31 +0,0 @@
-Fix 6 vendors with SKU ranges exceeding 999999. ALL DW SKUs must be between 100000-999999.
-
-## Out-of-range vendors (current state)
-- Pierre Frey: DWPF-1000001 to 1000030 (30 products)
-- Zoffany: DWZF-1010001 to 1010372 (372 products)
-- Milton & King: DWMK-1020001 to 1023742 (3,742 products)
-- Andrew Martin: DWAM-1030001 to 1030010 (10 products)
-- Donghia: DWDH-1040001 to 1040167 (167 products)
-- Sancar: DWWP-1045000 to 1048377 (some products, mixed with DWSN-345000)
-
-## DB Connection
-postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-## Steps for EACH vendor:
-1. Find an unused 5000-block gap in the 100000-999999 range
- - Query: `SELECT sku_prefix, sku_range_start FROM vendor_registry WHERE sku_range_start IS NOT NULL AND sku_range_start BETWEEN 100000 AND 999999 ORDER BY sku_range_start`
- - Look for gaps >= 5000 between consecutive vendors
-2. Update vendor_registry: `UPDATE vendor_registry SET sku_range_start = {NEW_START} WHERE vendor_code = '{code}'`
-3. Reassign DW SKUs in the catalog table:
- - For each product with dw_sku starting with the vendor prefix and number >= 1000000:
- - Assign new sequential SKU starting at the new range
- - Example: `UPDATE {table} SET dw_sku = 'DWPF-' || ({new_start} + row_number - 1) ...`
-4. Verify no SKU exceeds 999999 after reassignment
-5. Verify no SKU collisions with other vendors
-
-## CRITICAL RULES
-- NEVER assign SKUs outside 100000-999999
-- NEVER collide with existing vendor ranges
-- Keep each vendor's products sequential within their new block
-- Update BOTH vendor_registry AND the catalog table
-- Report before/after for each vendor
diff --git a/tasks/done/02_full-monte-enrichment.md b/tasks/done/02_full-monte-enrichment.md
deleted file mode 100644
index 0e23f24..0000000
--- a/tasks/done/02_full-monte-enrichment.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Full Monte Enrichment — Overnight Batch
-
-**Priority**: P1
-**Estimated time**: ~4 hours
-**Type**: AI enrichment pipeline
-
-## Description
-Run Full Monte phases on pending vendors overnight.
-
-## Steps
-1. Check status: `node /root/DW-Agents/full-monte/full-monte-batch.js --status`
-2. Run Phase 3 (AI enrichment): `node /root/DW-Agents/full-monte/full-monte-batch.js --vendor all --phase 3 --tier 1 --limit 500`
-3. Run Phase 8 (Shopify push): `node /root/DW-Agents/full-monte/full-monte-batch.js --vendor all --phase 8 --tier 1 --limit 800`
-4. Log results to `/root/DW-Agents/logs/full-monte-yolo.log`
-
-## Success criteria
-- Phase 3: 500+ products enriched with AI color/style tags
-- Phase 8: 800+ products pushed to Shopify with metafields
diff --git a/tasks/done/02_full-store-collection-audit.md b/tasks/done/02_full-store-collection-audit.md
deleted file mode 100644
index d86c5c3..0000000
--- a/tasks/done/02_full-store-collection-audit.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Full Store-Wide Collection Audit — Every Vendor Has a Collection
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-1. **How many unique vendors exist in the store?**
- ```bash
- curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"query":"{ shop { productVendors(first: 250) { edges { node } } } }"}' \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json"
- ```
- List all vendor names. Count them.
-
-2. **How many custom + smart collections exist?**
- - Count custom_collections and smart_collections via API
- - This gives us the denominator for "% of vendors with collections"
-
-3. **What's the mapping strategy?**
- - Most vendor collections use the handle pattern: `vendor-name-lowercase-dashed`
- - Some use alternate handles (e.g., `arte` not `arte-international`)
- - Check for both exact and partial matches
-
-## What To Do
-
-### Phase 1: Build the vendor→collection map
-1. Fetch ALL unique vendors via GraphQL (shop.productVendors)
-2. Fetch ALL collections (custom + smart) via REST API with pagination
-3. For each vendor, check if a collection exists where:
- - Collection title contains the vendor name, OR
- - Collection handle matches the vendor handle pattern, OR
- - Collection has a rule `vendor equals {vendor_name}`
-
-### Phase 2: Identify gaps
-Create a report with three categories:
-- **MATCHED**: Vendor has a collection (with handle and product count)
-- **MISSING**: Vendor has active products but NO collection
-- **EMPTY**: Vendor has a collection but 0 visible products
-
-### Phase 3: Save the report
-Save to: `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json`
-Also save a human-readable summary to: `/root/DW-Agents/yolo-agent/logs/collection-audit-summary.md`
-
-Format for JSON:
-```json
-{
- "timestamp": "2026-03-23T...",
- "totalVendors": 250,
- "matched": [{"vendor": "Arte", "collection": "arte", "products": 150}],
- "missing": [{"vendor": "Phyllis Morris", "activeProducts": 28}],
- "empty": [{"vendor": "Phillipe Romano", "collection": "phillipe-romano-faux-leathers", "reason": "all archived"}]
-}
-```
-
-### Phase 4: Send Slack notification
-```bash
-curl -X POST -H 'Content-type: application/json' \
- --data '{"text":"📊 *Collection Audit Complete*\n• Total vendors: X\n• Matched: X\n• Missing collection: X\n• Empty collection: X\n• Report: /root/DW-Agents/yolo-agent/logs/collection-audit-summary.md"}' \
- "${SLACK_WEBHOOK_URL}"
-```
-
-## DO NOT
-- Create any new collections (just report — Steve will review)
-- Delete any collections
-- Modify any products
-- This task is AUDIT ONLY — read operations only
-
-## Verification
-- Report file exists and has data
-- Total vendors matches GraphQL count
-- matched + missing + empty = total vendors
-- Slack notification sent
-
-## Credentials
-- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
-- GraphQL endpoint: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json
-- Slack webhook: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/02_git-backup.md b/tasks/done/02_git-backup.md
deleted file mode 100644
index ea5400b..0000000
--- a/tasks/done/02_git-backup.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Git Backup — Commit & Push
-```bash
-cd /root/DW-Agents && git add -A && git commit -m "auto-backup $(date +%Y-%m-%d-%H%M)" && git push origin main 2>&1 || echo "nothing to commit"
-cd /root/Projects/goodquestion-ai && git add -A && git commit -m "auto-backup $(date +%Y-%m-%d-%H%M)" && git push origin main 2>&1 || echo "nothing to commit"
-```
diff --git a/tasks/done/02_hollywood-fix-fire-rating.md b/tasks/done/02_hollywood-fix-fire-rating.md
deleted file mode 100644
index 2f1ccdd..0000000
--- a/tasks/done/02_hollywood-fix-fire-rating.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Fix Hollywood Fire Ratings — Default for Type II Vinyl
-
-## Context
-3,011 Hollywood Wallcoverings products in vendor_catalog have no fire_rating.
-These are all "Type II Vinyl" commercial wallcoverings which are inherently Class A rated per ASTM E-84.
-
-## Task
-1. Update vendor_catalog: set fire_rating for Hollywood products that are missing it.
- - Pattern names containing "Type II" → fire_rating = "Class A - ASTM E-84, Type II Commercial"
- - Pattern names containing "Durable Vinyl" → fire_rating = "Class A - ASTM E-84, Durable Vinyl"
- - All others → fire_rating = "Class A - ASTM E-84"
-
-```sql
-UPDATE vendor_catalog
-SET fire_rating = CASE
- WHEN pattern_name ILIKE '%Type II%' THEN 'Class A - ASTM E-84, Type II Commercial'
- WHEN pattern_name ILIKE '%Durable Vinyl%' THEN 'Class A - ASTM E-84, Durable Vinyl'
- ELSE 'Class A - ASTM E-84'
- END,
- updated_at = NOW()
-WHERE vendor_code = 'hollywood'
- AND (fire_rating IS NULL OR fire_rating = '')
- AND on_shopify IS NOT TRUE;
-```
-
-2. Also sync the fire_rating to enrichment_tracking for these products.
-3. Verify and report count of updated products.
diff --git a/tasks/done/02_hollywood-shopify-orphan-reconcile.md b/tasks/done/02_hollywood-shopify-orphan-reconcile.md
deleted file mode 100644
index 3818e2d..0000000
--- a/tasks/done/02_hollywood-shopify-orphan-reconcile.md
+++ /dev/null
@@ -1,93 +0,0 @@
-# Task B: Hollywood Shopify orphan reconciliation
-
-Shopify has ~4,890 products with `vendor="Hollywood Wallcoverings"`, but `vendor_catalog` only tracks 3,011 as `on_shopify=true`. The ~1,879 gap is "orphans" — products live on Shopify whose provenance link back to PostgreSQL is lost. Your job is to match them up and restore the link.
-
-## Context
-- **Database**: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- **Shopify product token**: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-- **Shopify store**: `designer-laboratory-sandbox.myshopify.com`
-- **Shopify API**: `2024-10` GraphQL
-- **Rule #14**: Hollywood is DW PL. Real manufacturer stays hidden.
-- **Legacy DW SKU prefixes for Hollywood**: `DWHD-`, `DWHW-` (both valid per memory file `feedback_never_change_existing_dw_skus.md`)
-
-## The problem
-
-A vendor_catalog "link" means `vendor_catalog.shopify_product_id` is populated and `on_shopify=true`. Shopify has ~4,890 Hollywood products, but only 3,011 have a matching DB row. That's 1,879 orphans — either:
-1. The product was pushed before `on_shopify` tracking was added
-2. The push code failed to write the ID back (we fixed one such bug this week — launch-pad-followup)
-3. The DB row is in a different table (hollywood_catalog, not vendor_catalog)
-4. The product was created manually or by a retired script
-
-## Your job — four steps
-
-### Step 1: Fetch ALL Hollywood products from Shopify
-Use GraphQL with pagination. Collect: `id, title, handle, variants[0].sku, metafields(custom.dw_sku, custom.manufacturer_sku, private_label.manufacturer_sku)`. Expect ~4,890 results.
-
-```graphql
-{
- products(first: 250, after: $cursor, query: "vendor:\"Hollywood Wallcoverings\"") {
- edges {
- cursor
- node {
- id
- title
- handle
- variants(first: 1) { nodes { sku } }
- metafield_dwsku: metafield(namespace: "custom", key: "dw_sku") { value }
- metafield_cust_mfr: metafield(namespace: "custom", key: "manufacturer_sku") { value }
- metafield_pl_mfr: metafield(namespace: "private_label", key: "manufacturer_sku") { value }
- }
- }
- pageInfo { hasNextPage endCursor }
- }
-}
-```
-
-### Step 2: Identify orphans
-For each Shopify product, check if `vendor_catalog.shopify_product_id` matches:
-```sql
-SELECT COUNT(*) FROM vendor_catalog
-WHERE shopify_product_id = $1;
-```
-If zero, it's an orphan.
-
-### Step 3: Resolve each orphan using 5-tier matching
-For each orphan, try these strategies in order, stop at the first hit:
-
-**Tier 1 — DW SKU from metafield**: If `custom.dw_sku` metafield is set, find `vendor_catalog` by `dw_sku = metafield_value`.
-
-**Tier 2 — DW SKU from variant SKU**: If variant SKU looks like `DWHD-######` or `DWHW-######` (strip `-Sample` if present), match `vendor_catalog.dw_sku`.
-
-**Tier 3 — Real MFR SKU from metafield**: If `private_label.manufacturer_sku` or `custom.manufacturer_sku` is set, find `vendor_catalog` by `vendor_code='hollywood' AND mfr_sku = metafield_value`.
-
-**Tier 4 — Title parse**: Extract `{pattern_name}` from the title (everything before ` - `). Find `vendor_catalog` by `vendor_code='hollywood' AND pattern_name ILIKE '$1%'`. Only accept if exactly 1 match.
-
-**Tier 5 — Handle parse**: Shopify handles are slugified titles. Use as a last resort.
-
-### Step 4: Update vendor_catalog
-For each resolved match:
-```sql
-UPDATE vendor_catalog
-SET shopify_product_id = $1, on_shopify = true, shopify_synced_at = NOW()
-WHERE id = $2 AND (shopify_product_id IS NULL OR shopify_product_id <> $1);
-```
-
-## Safety
-
-- **DO NOT** set `on_shopify=true` on a row whose `vendor_code != 'hollywood'` — reject cross-vendor matches
-- **DO NOT** overwrite an existing `shopify_product_id` that differs from the orphan — log as conflict instead
-- **DO NOT** accept Tier 4 matches with more than 1 candidate row — too ambiguous, skip
-- **DO NOT** write to Shopify in this task — this is a DB-side reconciliation only
-- Write the script to `/root/DW-Agents/scripts/yolo-hollywood-orphan-reconcile.js`
-- Use `node --check` before running
-
-## Deliverables
-
-Terse markdown report, under 400 words:
-1. Total Hollywood products on Shopify (actual count)
-2. Orphan count before
-3. Resolved count by tier (T1/T2/T3/T4/T5)
-4. Unresolved count with reason breakdown
-5. Conflicts found (same Shopify product matched to different DB rows)
-6. Final orphan count after reconciliation
-7. Any cross-vendor contamination detected
diff --git a/tasks/done/02_jd-room-settings.md b/tasks/done/02_jd-room-settings.md
deleted file mode 100644
index 5a233d2..0000000
--- a/tasks/done/02_jd-room-settings.md
+++ /dev/null
@@ -1,45 +0,0 @@
-Generate room settings for 4,379 Justin David products missing P6.
-
-## Current State
-- P6 Rooms: 341/4,720 done (7%)
-- All products are private label (Phillipe Romano branding)
-- Products have AI color data for matching room palettes
-
-## Steps
-
-### Step 1: Check room setting agent is available
-```bash
-timeout 10 curl -s http://localhost:9896/health
-```
-
-### Step 2: Generate room settings in parallel batches
-Use the room-setting-agent (port 9896) to generate rooms for JD products.
-These are private label commercial/contract products — use RESIDENTIAL room types (bedroom, living room, dining room, office, bathroom).
-
-Query products needing rooms:
-```sql
-SELECT c.mfr_sku, c.image_url, c.pattern_name, c.color_name, c.width,
- c.dw_sku, c.tropical_name, c.tropical_color
-FROM justindavid_catalog c
-JOIN enrichment_tracking et ON et.vendor_code = 'justin_david' AND et.mfr_sku = c.mfr_sku
-WHERE et.phase6_rooms_at IS NULL AND c.image_url IS NOT NULL AND c.ai_colors IS NOT NULL
-ORDER BY c.mfr_sku
-LIMIT 100;
-```
-
-Process in batches of 3 concurrent (per CLAUDE.md rule #18).
-
-### Step 3: Update tracking after each batch
-```sql
-UPDATE enrichment_tracking SET phase6_rooms_at = NOW(), phase6_room_count = [count]
-WHERE vendor_code = 'justin_david' AND mfr_sku = '[sku]';
-```
-
-## Important
-- Room settings MUST run in parallel batches of 3 (rule #18)
-- Use Gemini image generation (gemini-2.5-flash-image)
-- Track Gemini costs via shared/gemini-cost-tracker.js
-- Default tile scale: 5"x5" for swatches (per memory feedback_room_settings_scale.md)
-
-## Success Criteria
-- P6 > 80% (3,776+ products with room settings)
diff --git a/tasks/done/02_low-image-vendors-enrich.md b/tasks/done/02_low-image-vendors-enrich.md
deleted file mode 100644
index 1164d2f..0000000
--- a/tasks/done/02_low-image-vendors-enrich.md
+++ /dev/null
@@ -1,30 +0,0 @@
-## Enrich Low-Image Vendors
-
-Run enrichment scrapers for non-private-label vendors below 80% image coverage.
-
-### Steps:
-1. Find vendors needing images:
- ```sql
- SELECT vendor_name, vendor_code, total_products, products_with_images,
- ROUND(products_with_images::numeric / NULLIF(total_products,0) * 100, 1) as img_pct
- FROM vendor_registry
- WHERE total_products > 50 AND private_label = false
- AND (products_with_images::numeric / NULLIF(total_products,0) * 100) < 80
- ORDER BY total_products DESC;
- ```
- DB: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified"`
-
-2. Check for enrichment scrapers:
- ```bash
- ls /root/DW-Agents/vendor-scrapers/enrich-*-images.js /root/DW-Agents/vendor-scrapers/enrich-*-shopify.js
- ```
-
-3. Run enrichment scrapers for vendors that have them, one at a time with 600s timeout each.
-
-4. Update VCC stats after: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-### CRITICAL RULES:
-- Do NOT push to Shopify
-- Do NOT create new products or SKUs
-- Only update PostgreSQL catalog tables with images
-- Skip private label vendors entirely
diff --git a/tasks/done/02_newmor-brands-page.md b/tasks/done/02_newmor-brands-page.md
deleted file mode 100644
index bc09ee1..0000000
--- a/tasks/done/02_newmor-brands-page.md
+++ /dev/null
@@ -1,109 +0,0 @@
-# Add Newmor to Brands Page
-
-## Context
-The Brands page on Designer Wallcoverings lists all vendor brands with logos in a CSS grid. Newmor is NOT currently on the page. Steve wants it added.
-
-## Key Files
-- **Brands HTML template**: `/root/Projects/Designer-Wallcoverings/vendor-logos/_brands-page.html`
-- **Brands JSON data**: `/root/Projects/Designer-Wallcoverings/brand-router/brands-data.json`
-- **Shopify Page ID**: 79444574259 (handle: "brands")
-- **Brands agent**: `/root/DW-Agents/brands-agent/`
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-## Steps
-
-### Step 1: Check if Newmor collection exists on Shopify
-```bash
-curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/smart_collections.json?title=Newmor" | python3 -m json.tool | head -30
-```
-Also check custom collections:
-```bash
-curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/custom_collections.json?title=Newmor" | python3 -m json.tool | head -30
-```
-
-### Step 2: Create Newmor smart collection if it doesn't exist
-Create a smart collection with rules:
-- vendor equals "Newmor" OR title contains "Newmor" OR tag equals "Newmor"
-- disjunctive: true
-
-Use the Shopify queue worker (Quinn) for the API call:
-```bash
-# Check Quinn queue worker is running
-curl -s http://localhost:9660/api/queue-status 2>/dev/null || echo "Check shopify-queue-worker directly"
-```
-
-If Quinn isn't available, create directly:
-```bash
-curl -s -X POST \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/smart_collections.json" \
- -d '{
- "smart_collection": {
- "title": "Newmor",
- "rules": [
- {"column": "vendor", "relation": "equals", "condition": "Newmor"},
- {"column": "title", "relation": "contains", "condition": "Newmor"},
- {"column": "tag", "relation": "equals", "condition": "Newmor"}
- ],
- "disjunctive": true,
- "published": true
- }
- }'
-```
-
-### Step 3: Check for Newmor logo
-Look for a logo file:
-```bash
-ls /root/Projects/Designer-Wallcoverings/vendor-logos/ | grep -i newmor
-```
-If no logo exists, check the Newmor website for a logo at newmor.com. Download it to vendor-logos/ directory.
-
-### Step 4: Read the current brands-data.json
-Read `/root/Projects/Designer-Wallcoverings/brand-router/brands-data.json` and add a Newmor entry:
-```json
-{
- "name": "Newmor",
- "href": "/collections/newmor",
- "type": "collection",
- "vendorQuery": "Newmor",
- "logo": "[CDN URL or local path to logo]",
- "collectionHandle": "newmor"
-}
-```
-Add it in alphabetical order among the existing brands.
-
-### Step 5: Update the brands HTML template
-Read `/root/Projects/Designer-Wallcoverings/vendor-logos/_brands-page.html` and add a Newmor card in the grid. Follow the existing card format:
-```html
-<a class="dw-bv7-card" href="/collections/newmor">
- <img src="[LOGO_URL]" alt="Newmor" loading="lazy">
- <span>Newmor</span>
-</a>
-```
-Place it alphabetically among other brand cards.
-
-### Step 6: Push updated HTML to Shopify page
-Use the pages content token to update the Shopify page:
-```bash
-curl -s -X PUT \
- -H "X-Shopify-Access-Token: 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" \
- -H "Content-Type: application/json" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/pages/79444574259.json" \
- -d '{"page": {"id": 79444574259, "body_html": "UPDATED_HTML_HERE"}}'
-```
-
-### Step 7: Verify
-- Check the brands page loads: `curl -s -o /dev/null -w "%{http_code}" https://www.designerwallcoverings.com/pages/brands`
-- Confirm Newmor appears in the HTML response
-- Report the collection URL and brands page status
-
-### IMPORTANT RULES
-- Use pages/content token (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) for page updates
-- Use product token (<redacted:SHOPIFY_ADMIN_TOKEN>) for collection operations
-- Do NOT modify the LIVE theme (141654589491)
-- If no Newmor logo found, use a text-only card or download from newmor.com
diff --git a/tasks/done/02_newmor-bulk-phase3-ai.md b/tasks/done/02_newmor-bulk-phase3-ai.md
deleted file mode 100644
index 18e1ce3..0000000
--- a/tasks/done/02_newmor-bulk-phase3-ai.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# NEWMOR — Bulk Phase 3 AI Enrichment (All Products)
-
-## Context
-After width re-scraping, run Gemini Vision AI enrichment on ALL NEWMOR products. The test run on CHARCOAL validated the pipeline. Now run the full batch.
-
-Currently ~3 products already enriched from the test run. ~1,282 remaining.
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-## Steps
-
-### Step 1: Check how many still need AI enrichment
-```sql
-SELECT
- COUNT(*) as total,
- COUNT(CASE WHEN ai_colors IS NOT NULL THEN 1 END) as has_ai,
- COUNT(CASE WHEN ai_colors IS NULL THEN 1 END) as needs_ai,
- COUNT(CASE WHEN width IS NOT NULL AND length(width) > 0 THEN 1 END) as has_width
-FROM newmor_catalog;
-```
-
-### Step 2: Run Phase 3 (AI Tags) via Full Monte CLI
-```bash
-cd /root/DW-Agents/full-monte
-node full-monte-batch.js --vendor newmor --phase 3 --limit 1300
-```
-
-This calls `enrich-ai-tags.js` which:
-- Queries newmor_catalog for products with images but no AI tags
-- Checks enrichment_tracking to prevent duplicate Gemini calls
-- Sends each product image to Gemini Vision for analysis
-- Extracts: colors (with hex + percentages), background color, styles, patterns, image type, tags
-- Writes results to newmor_catalog and marks enrichment_tracking
-
-### Step 3: Monitor progress (check periodically)
-```sql
-SELECT
- COUNT(*) as total,
- COUNT(CASE WHEN ai_colors IS NOT NULL THEN 1 END) as enriched,
- COUNT(CASE WHEN ai_colors IS NULL THEN 1 END) as remaining
-FROM newmor_catalog;
-```
-
-### Step 4: Check enrichment tracking
-```sql
-SELECT COUNT(*) as phase3_done
-FROM enrichment_tracking
-WHERE vendor_code = 'newmor' AND phase3_ai_at IS NOT NULL;
-```
-
-### Step 5: Check for errors
-```sql
-SELECT COUNT(*) as with_errors, SUM(error_count) as total_errors
-FROM enrichment_tracking
-WHERE vendor_code = 'newmor' AND error_count > 0;
-```
-
-### Step 6: Generate body_html for all enriched products
-For products that have AI data but no body_html, generate 3-sentence descriptions:
-- Sentence 1: What it is (pattern/color/vendor)
-- Sentence 2: Style/mood
-- Sentence 3: Recommended use
-NEVER include specs in body_html.
-
-### Step 7: Final Full Monte status
-```bash
-cd /root/DW-Agents/full-monte
-node full-monte-batch.js --status --vendor newmor
-```
-
-### Step 8: Report results
-Print summary:
-- Total products enriched
-- Sample of 5 products with their AI colors and tags
-- Error count
-- Cost estimate (products * $0.0006)
-- Updated Full Monte status table
-- Next steps needed before Shopify push
-
-## IMPORTANT RULES
-- Use Gemini analysis key: ${GOOGLE_API_KEY}
-- Track costs via /root/DW-Agents/shared/gemini-cost-tracker.js
-- Check enrichment_tracking BEFORE every Gemini call (dedup)
-- Do NOT push to Shopify — that requires VCC approval (Phase 5)
-- body_html = description ONLY, 3 sentences max, NO specs
-- ALL specs go in metafields exclusively
-- If rate limited by Gemini, back off and retry
-- This may take 15-30 minutes for 1,200+ products
diff --git a/tasks/done/02_norma-git-commit.md b/tasks/done/02_norma-git-commit.md
deleted file mode 100644
index 114735c..0000000
--- a/tasks/done/02_norma-git-commit.md
+++ /dev/null
@@ -1,23 +0,0 @@
-Commit all the new three-tier UI work in the Norma project.
-
-Steps:
-1. cd /root/Projects/Norma
-2. git status to see all changes
-3. Stage the important files (components, lib, app/api, middleware.ts, docs) but NOT node_modules or .next
-4. Create a commit with this message:
-
-feat: complete three-tier UI implementation (Steps 11-18)
-
-- Step 11: Sidebar role-based filtering + hide/show toggle + ManageTabsModal
-- Step 12: OrgProvider auto-set org for staff/pulse roles
-- Step 13: API route role gating (201 routes with requireRole)
-- Step 14: Credentials Management CRUD UI in Settings
-- Step 15: Pulse Config UI (toggles, branding, featured count)
-- Step 16: Middleware auth gate + Pulse layout with auth-aware nav
-- Step 17: Pulse Activity, Tracked, Profile pages + APIs
-- Step 18: Personalized Pulse feed with category affinity
-- Build fix: --webpack flag for Turbopack race condition
-
-Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
-
-5. Report the commit hash
diff --git a/tasks/done/02_norma_email_analyzer_demo_video.md b/tasks/done/02_norma_email_analyzer_demo_video.md
deleted file mode 100644
index ba0277f..0000000
--- a/tasks/done/02_norma_email_analyzer_demo_video.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Record Demo Video: Norma Email Analyzer (Round 1 + 2 features)
-
-**Goal:** Produce a polished screen recording specifically of the Email Analyzer tab at http://45.61.58.125:7400/, highlighting the newly shipped Round 1/2 features, then using the system with real SDCC data for ~3 minutes.
-
-## Features to demonstrate (in order)
-
-### Layout & Panels
-1. Layout toolbar: 1/2/3/4-column toggle — show that panels redistribute row-major instead of stacking
-2. Default panel order: alphabetical + row-major on fresh load
-3. Drag-drop a panel; refresh the page; confirm the drop persisted (sendBeacon + local-wins rehydrate)
-4. Collapse / expand panels
-5. "Reset layout" button
-
-### New Panels (Round 2)
-6. **Old Emails from SDC** — search "SAVE plan", click a result, confirm it loads into Original Email
-7. **Search Saved Emails** — trigram search across all saved analyses, favorites-only filter, "Open" a result, "Universal" to save to universal refs
-8. **Subject Line Maker** — generate 8 subjects, compare styles (curiosity/urgency/etc), favorite one, add a note → confirm it's logged as a learning
-
-### Version Timeline (Round 1)
-9. Star a version → favorites indicator in header activates
-10. Click "Sort ★ / Favs first" to reorder
-11. Expand a version → show score badge, reading time, rewrite instruction
-12. Click "Ref: project" → confirm it appears in Reference Library
-13. Click "Ref: universal" → confirm toast "Added to Universal References"
-
-### Settings → Universal References
-14. Open Settings → Universal Email References
-15. Add a new reference manually ("High-converting fundraising opener")
-16. Toggle Active/Off → confirm `X active / Y total` count updates
-17. Search across references
-
-### Prompt integration proof
-18. After adding an active universal reference, run a rewrite in the analyzer
-19. Inspect /api/email-analyzer/rewrite request — confirm the universal ref is in the prompt (use DevTools Network tab)
-
-## Real-data requirement
-Use one actual SDCC email loaded from the "Old Emails from SDC" panel. Run at least:
-- 3 AI rewrites (different instructions)
-- 1 subject line generation batch
-- 2 universal references saved
-- 1 cross-session search
-
-Total interactive demo time: ~3 minutes.
-
-## Deliverable
-- MP4, 1920x1080, 30fps, narrated
-- Save to `/root/Projects/Norma/demos/email-analyzer-YYYY-MM-DD.mp4`
-- 60-second highlight reel: `/root/Projects/Norma/demos/email-analyzer-highlights-YYYY-MM-DD.mp4`
-
-## Auth
-admin / DWSecure2024!
-
-## If blocked
-Log what's missing and produce silent video + written transcript. The video is more valuable than perfect narration.
diff --git a/tasks/done/02_p2-09-vendor-standardization.md b/tasks/done/02_p2-09-vendor-standardization.md
deleted file mode 100644
index 3158b95..0000000
--- a/tasks/done/02_p2-09-vendor-standardization.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# P2-09: Vendor Name Standardization
-
-The vendor standardization engine is ready to run. It normalizes inconsistent vendor names across Shopify products.
-
-## What to do
-
-1. Check current status: `curl -s http://127.0.0.1:9660/api/standardize/status -u admin:DWSecure2024!`
-2. If not running, start the bulk analysis:
-```
-curl -s -X POST http://127.0.0.1:9660/api/standardize/bulk -u admin:DWSecure2024! -H "Content-Type: application/json" -d '{"batchSize": 500, "apply": true}'
-```
-3. Monitor progress via the VCC dashboard at http://127.0.0.1:9660
-4. Report: how many vendors standardized, any unmapped names found
-
-## Context
-- 0 unmapped vendors as of Session #108 (was 30+)
-- 238 vendor names scanned
-- PR sub-brands confirmed as private label under phillipe_romano
-- Vendor exclusions: Cowtan & Tout (specs only), Scalamandre fabrics (not wallcoverings), Bespoke (DW brand)
diff --git a/tasks/done/02_push-color-metafields.md b/tasks/done/02_push-color-metafields.md
deleted file mode 100644
index fe2079a..0000000
--- a/tasks/done/02_push-color-metafields.md
+++ /dev/null
@@ -1,22 +0,0 @@
-## Push Color Metafields to Shopify
-
-52,160+ on_shopify products have dominant_color_hex in vendor_catalog DB. Push color data to Shopify metafields.
-
-### Fields to push:
-- `custom.color_hex` (single_line_text_field) — dominant_color_hex from vendor_catalog
-- `custom.color_details` (json) — ai_colors JSON array from vendor_catalog (for color dots on product page)
-
-### Steps:
-1. Query: `SELECT shopify_product_id, dominant_color_hex, ai_colors::text FROM vendor_catalog WHERE on_shopify = true AND shopify_product_id IS NOT NULL AND dominant_color_hex IS NOT NULL AND dominant_color_hex != ''`
-2. Batch 25 products per GraphQL `metafieldsSet` call (each product gets 2 metafields = 50 metafield inputs per call)
-3. Build mutation with metafields array containing both color_hex and color_details for each product
-4. Queue ALL calls through Quinn: POST to shopify-queue-worker
-5. color_details value = the ai_colors JSON string (already JSON in DB)
-
-### Constraints:
-- GraphQL ONLY via Quinn queue
-- Batch 25 products (50 metafield inputs) per call
-- Skip products where shopify_product_id is null
-- Log progress every 500 products
-- This is a large job (52K+ products, ~2,100 GraphQL calls) — will take time through Quinn's 2 req/s rate limit
-- Report estimated completion time at start
diff --git a/tasks/done/02_rewrite-blogs-professional.md b/tasks/done/02_rewrite-blogs-professional.md
deleted file mode 100644
index 6e9c9d5..0000000
--- a/tasks/done/02_rewrite-blogs-professional.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Rewrite All 18 Blog Posts — Professional, Less Jargon
-
-Working directory: /root/Projects/goodquestion-ai/src/content/posts/
-
-## Goal
-Make every blog post more professional. Less developer jargon. Write for entrepreneurs and founders, not programmers. The reader should understand the VALUE of what was built without needing to know what PM2, PostgreSQL, or ffmpeg are.
-
-## Rules for EVERY post:
-
-### Tone
-- Professional, authoritative, warm
-- Write like a founder sharing lessons, not an engineer showing off
-- No "I did X with Y framework" — instead "I built a system that does Z"
-- Remove unnecessary code blocks. Keep ONLY 1-2 small code snippets max per post IF they illustrate a concept even non-developers can appreciate
-- Replace jargon with plain language: "process manager" not "PM2", "database" not "PostgreSQL", "background jobs" not "cron"
-
-### Structure
-- Hook in the first sentence — a problem the reader relates to
-- Clear sections with descriptive headers
-- End with a "What This Means For Your Business" or "The Takeaway" section aimed at founders
-- Include the numbers table (people love metrics)
-- Keep the CTA section at the bottom
-
-### Fix these specific issues:
-- Remove ALL video embed sections that reference non-existent videos (we're remaking all videos)
-- Remove duplicate YouTube subscribe lines
-- Keep the "Let's Connect" section with social links
-- Keep the "Advisory & Board" paragraph on longer posts
-- Fix any broken image references
-- Ensure ALL content passes sanitization (no vendor names, no wallpaper/wallcovering, no internal codenames)
-
-### Description field
-- Rewrite every description to be compelling and jargon-free
-- Max 160 characters for SEO
-- Focus on the outcome/result, not the technology
-
-### Tags
-- Simplify tags: use broad categories like "automation", "ai", "infrastructure", "security", "scaling"
-- Remove overly specific tags like "pm2", "three.js", "postgresql"
-
-## Process
-Read each of the 18 files. Rewrite them in place. Do all 18.
-
-## SANITIZATION — CRITICAL
-NEVER include in any post:
-- wallcoverings, wallpaper, wall covering
-- Specific pricing
-- Internal DB fields
-- IP addresses, ports, credentials
-- Vendor names (Elli Popp, Thibaut, Brewster, etc.)
-- Company name: Designer Wallcoverings, DW, DW-Agents
-- Shopify, Algolia
-- SDCC, Student Debt Crisis Center, student debt, Norma (use "nonprofit platform")
-- Agent codenames (Parker, Annie, Silas, etc.)
diff --git a/tasks/done/02_scraper-status.md b/tasks/done/02_scraper-status.md
deleted file mode 100644
index 54f6676..0000000
--- a/tasks/done/02_scraper-status.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Check all running vendor scrapers and their progress.
-Run: ps aux | grep "node scrape-" | grep -v grep
-Check catalog counts: psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT relname, n_live_tup FROM pg_stat_user_tables WHERE relname LIKE '%_catalog' ORDER BY n_live_tup DESC LIMIT 20;"
-Report which scrapers are running and current product counts.
\ No newline at end of file
diff --git a/tasks/done/02_system-health-check.md b/tasks/done/02_system-health-check.md
deleted file mode 100644
index 64d01a1..0000000
--- a/tasks/done/02_system-health-check.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# System Health Check
-
-Run a full system health check:
-
-1. Check PM2 status — report any errored or stopped agents
-2. Restart any crashed agents
-3. Check disk space: `df -h /`
-4. Check memory: `free -h`
-5. Check for zombie Claude processes older than 12 hours: `ps aux | grep claude | grep -v grep`
-6. Kill any zombie processes found (log PID and task before killing)
-7. Check PostgreSQL is responding: `timeout 5 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT 1"`
-8. Report summary of all findings
diff --git a/tasks/done/02_update-vcc-spec-standards.md b/tasks/done/02_update-vcc-spec-standards.md
deleted file mode 100644
index 8d813ee..0000000
--- a/tasks/done/02_update-vcc-spec-standards.md
+++ /dev/null
@@ -1,64 +0,0 @@
-# Update Vendor Command Center — Enforce ALL Specs Standard
-
-Read the Vendor Command Center (Victor) at `/root/DW-Agents/vendor-command-center/server.js` (port 9660).
-
-Update it to enforce that ALL vendor agents must capture these fields:
-
-## Required Catalog Columns (ALL vendors)
-- `mfr_sku` — manufacturer SKU (UNIQUE)
-- `dw_sku` — Designer Wallcoverings internal SKU
-- `pattern_name` — pattern/design name
-- `color_name` — colorway name
-- `collection` — collection name
-- `product_type` — wallpaper, wallcovering, fabric, etc.
-- `width` — roll width
-- `length` — roll length
-- `repeat_v` — vertical repeat
-- `repeat_h` — horizontal repeat
-- `match_type` — straight, offset, random, seamless
-- `material` — vinyl, non-woven, grasscloth, etc.
-- `finish` — matte, satin, textured, etc.
-- `application` — paste-the-wall, paste-the-paper, peel-and-stick
-- `coverage` — square footage per roll
-- `features` — washable, strippable, breathable, etc.
-- `fire_rating` — ASTM E84 Class A, Class 1, etc.
-- `design` — floral, geometric, stripe, etc.
-- `rooms` — bedroom, living room, bathroom, etc.
-- `color_primary` — primary color
-- `color_secondary` — secondary color
-- `price_retail` — retail price
-- `price_currency` — USD, EUR, GBP
-- `image_url` — primary image
-- `all_images` — ALL images pipe-separated (|)
-- `product_url` — source product page URL
-- `in_stock` — boolean
-- `vendor_name` — vendor name
-- `brand` — brand if different from vendor
-- `tags` — tags/categories
-- `body_html` — full product description HTML
-- `short_description` — short description
-- `about_vendor` — vendor company info (about us)
-- `us_distributor` — US distribution info
-- `showroom_locations` — physical showroom locations
-
-## What to Add to VCC Dashboard
-1. Add a "Spec Completeness" column to the vendor table showing % of required fields filled
-2. Add a "Missing Specs" alert for vendors below 50% spec fill rate
-3. Add `about_vendor`, `us_distributor`, and `showroom_locations` columns to all catalog tables
-
-## Important
-- Do NOT restart the VCC if it's currently running well — just update the code and restart after
-- Test the dashboard still loads after changes
-- Auth: admin / DWSecure2024!
-
-
-## CRITICAL RULE
-**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY. Downloading product data FROM other vendors' Shopify stores for catalog data is fine and expected. product-scheduler and schedule-engine are STOPPED intentionally. Do NOT restart them.**
-
-
-## Slack Notification — REQUIRED
-When this task is complete, send a Slack message to Steve with the results summary:
-```bash
-curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "${SLACK_WEBHOOK_URL}"
-```
-Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/02_verify-archive-duplicates.md b/tasks/done/02_verify-archive-duplicates.md
deleted file mode 100644
index 057c5fb..0000000
--- a/tasks/done/02_verify-archive-duplicates.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Verify Duplicate Shopify Products Archived
-
-An archive script is running at /tmp/archive-duplicates.log archiving DWHW2-/DWPR- duplicate Shopify products.
-
-### Steps:
-1. Check if the script finished: `cat /tmp/archive-duplicates.log | tail -5`
-2. Count remaining active DWHW2/DWPR products in Shopify:
- ```bash
- curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/count.json?created_at_min=2026-03-13T00:00:00-07:00&status=active"
- ```
-3. If count > 0, check which ones survived and report their vendors/SKUs
-4. Verify the VCC (port 9660) shows correct product counts after cleanup
-5. Run VCC sync: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-### CRITICAL RULES:
-- Do NOT create any Shopify products
-- Do NOT push anything to Shopify
-- Only verify and report
diff --git a/tasks/done/02_versa-william-morris-enrich.md b/tasks/done/02_versa-william-morris-enrich.md
deleted file mode 100644
index 1ff2b1c..0000000
--- a/tasks/done/02_versa-william-morris-enrich.md
+++ /dev/null
@@ -1,28 +0,0 @@
-## Versa + William Morris Image Enrichment
-
-Two small vendors close to completion:
-- Versa Designed Surfaces: 147 products, 30 images (20.4%) — versadesignedsurfaces.com
-- William Morris: 243 products, 193 images (79.4%) — sanderson.design
-
-### Steps:
-1. Check Versa products missing images:
- ```sql
- SELECT COUNT(*) as missing, COUNT(NULLIF(product_url,'')) as has_url
- FROM versa_catalog WHERE image_url IS NULL OR image_url = '';
- ```
-
-2. For Versa products with product_url, fetch and extract images:
- - Sample URL: https://www.versadesignedsurfaces.com/north-america/products/product-detail/mandolin-a181-usa/a181-777-280
- - Try: `timeout 15 curl -s -L "SAMPLE_URL" | grep -o 'https://[^"]*\.\(jpg\|png\|webp\)' | head -10`
- - Look for og:image meta tag
-
-3. For William Morris, check sanderson.design structure:
- ```sql
- SELECT product_url FROM william_morris_catalog WHERE (image_url IS NULL OR image_url = '') AND product_url != '' LIMIT 3;
- ```
-
-4. Build quick inline enrichment for each (curl + update SQL) if no dedicated scraper exists
-
-5. Update VCC after: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-### CRITICAL: Do NOT push to Shopify.
diff --git a/tasks/done/03_03_data-quality-audit.md b/tasks/done/03_03_data-quality-audit.md
deleted file mode 100644
index 450ab23..0000000
--- a/tasks/done/03_03_data-quality-audit.md
+++ /dev/null
@@ -1,28 +0,0 @@
-Hollywood catalog data quality audit.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: hollywood_catalog
-
-1. Run comprehensive quality check on ALL Hollywood products (not just X-prefix):
- SELECT COUNT(*) as total,
- COUNT(CASE WHEN mfr_sku LIKE 'X%' THEN 1 END) as x_prefix,
- COUNT(CASE WHEN mfr_sku NOT LIKE 'X%' THEN 1 END) as non_x,
- COUNT(CASE WHEN image_url IS NOT NULL AND image_url != '' THEN 1 END) as has_image,
- COUNT(CASE WHEN dw_sku IS NOT NULL AND dw_sku != '' THEN 1 END) as has_dw_sku,
- COUNT(CASE WHEN product_type IS NOT NULL AND product_type != '' THEN 1 END) as has_type,
- COUNT(CASE WHEN collection IS NOT NULL AND collection != '' THEN 1 END) as has_collection
- FROM hollywood_catalog;
-
-2. Check non-X-prefix products — what are they? Sample 20.
-
-3. Check for duplicate mfr_skus.
-
-4. Check for products missing images that are on Shopify.
-
-5. Check product_type distribution — are types consistent?
-
-6. Check tag coverage for Shopify products.
-
-7. Identify any products with shopify_product_id that are NOT discontinued but have no price — these are live with no pricing.
-
-8. Generate a summary report and log it.
\ No newline at end of file
diff --git a/tasks/done/03_03_enrich-kravet.md b/tasks/done/03_03_enrich-kravet.md
deleted file mode 100644
index 6d11e1f..0000000
--- a/tasks/done/03_03_enrich-kravet.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-kravet-full.js 500
-
-Enrich Kravet catalog — up to 500 products.
\ No newline at end of file
diff --git a/tasks/done/03_03_gls-tag-cleanup.md b/tasks/done/03_03_gls-tag-cleanup.md
deleted file mode 100644
index bd16713..0000000
--- a/tasks/done/03_03_gls-tag-cleanup.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Find ALL products on Shopify with GLS in the SKU (across ALL vendors — Glitter Walls, Glass Beaded, Phillipe Romano). For each:
-1. Remove tags: brand vinyl, composition vinyl, Vinyl (the tag, not material)
-2. Run interior design tagger via Gemini Vision to generate proper style/color/pattern tags
-3. Add a 2-3 sentence product description if body_html is empty
-4. Crop any text/logos from the primary product image
-
-Shopify API: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Gemini key: ${GOOGLE_API_KEY}
\ No newline at end of file
diff --git a/tasks/done/03_03_reseller-vendor-analysis.md b/tasks/done/03_03_reseller-vendor-analysis.md
deleted file mode 100644
index 93216af..0000000
--- a/tasks/done/03_03_reseller-vendor-analysis.md
+++ /dev/null
@@ -1 +0,0 @@
-Build detailed reseller vendor analysis. For each of the 13 TWIL Karin reseller sites (wallquest.com, sterlingdecor.com, walfab.net, hdwalls.com, twill-us.com, grasscloth.twenty2.net, texaspaint.com, therollingwallpaperstudio.com, lanebrothers.net, winfieldthybony.com, supplyshowroom.com, hartmannforbes.com, wallcoveringsmart.com): scrape their full brand/vendor list, cross-reference with DW catalog (shopify_products table), identify opportunities (lines DW does not carry). Add to spreadsheet at /root/DW-Agents/data/Thibaut-Clients.xlsx. Include websites and contact emails for each brand.
\ No newline at end of file
diff --git a/tasks/done/03_03_server-health-check.md b/tasks/done/03_03_server-health-check.md
deleted file mode 100644
index 6d85b57..0000000
--- a/tasks/done/03_03_server-health-check.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Full server health check.
-1. df -h / (warn if >85%)
-2. free -h
-3. pm2 list - report errored/stopped agents, restart crashed ones
-4. Check zombie Chrome processes - kill orphans older than 2h
-5. Check zombie Claude processes older than 12h
-6. Check SSL certs for glassbeadedwallpaper.com, flockedwallpaper.com, grassclothwallcoverings.com
-7. Check PM2 log sizes - rotate if over 500MB
-8. Report summary
\ No newline at end of file
diff --git a/tasks/done/03_03_shopify-product-count.md b/tasks/done/03_03_shopify-product-count.md
deleted file mode 100644
index ed034c5..0000000
--- a/tasks/done/03_03_shopify-product-count.md
+++ /dev/null
@@ -1 +0,0 @@
-Use curl to query Shopify API: curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/count.json". Also get counts by status (active, draft, archived). Report total products, active vs archived. Save to /root/DW-Agents/logs/shopify-count.log with timestamp.
\ No newline at end of file
diff --git a/tasks/done/03_ai-enrich-mark-alexander.md b/tasks/done/03_ai-enrich-mark-alexander.md
deleted file mode 100644
index c4a2f3c..0000000
--- a/tasks/done/03_ai-enrich-mark-alexander.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# AI Enrich — Mark Alexander (260 products missing AI colors)
-
-Same as Romo task but for mark_alexander vendor_code. Limit 200 per run.
-
-```bash
-cd /root/DW-Agents/full-monte
-node full-monte-batch.js --vendor mark_alexander --phase 3 --limit 200
-```
-Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/03_blog-post.md b/tasks/done/03_blog-post.md
deleted file mode 100644
index 08ed3b1..0000000
--- a/tasks/done/03_blog-post.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Publish Blog Post — goodquestion.ai
-
-```bash
-cd /root/DW-Agents/blog-agent && node generator.js
-```
-Or: `curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:9871/api/generate`
diff --git a/tasks/done/03_build-html-video-pipeline.md b/tasks/done/03_build-html-video-pipeline.md
deleted file mode 100644
index dc96f44..0000000
--- a/tasks/done/03_build-html-video-pipeline.md
+++ /dev/null
@@ -1,109 +0,0 @@
-# Build HTML Video Slide Pipeline
-
-Working directory: /root/Projects/goodquestion-ai/video-html/
-
-Create a complete HTML-to-video pipeline with animated slides captured by Puppeteer.
-
-## Directory Structure
-```
-video-html/
- templates/ — HTML template files per slide type
- capture.js — Puppeteer 30fps frame capture
- assemble.sh — ffmpeg PNG→MP4 assembly
- generate-video.js — Master pipeline script
- package.json
-```
-
-## Install dependencies
-```bash
-cd /root/Projects/goodquestion-ai/video-html/
-npm init -y
-npm install puppeteer
-```
-
-## Design System
-```
-Background: #0B0B0F
-Primary: #00D4FF (cyan)
-Accent: #FF6B35 (orange)
-Success: #00E68C (green)
-Text: #E8E8ED
-Muted: #6B7280
-Font: system-ui, -apple-system, sans-serif
-```
-
-## 5 Slide Templates (each is a self-contained HTML file)
-
-### 1. hook.html — Full-screen title
-- Giant headline (80px bold), subtext below (32px muted)
-- Animated gradient background shift
-- CSS fadeInUp animation on text
-
-### 2. big-number.html — 1-3 animated counters
-- Numbers tick up from 0 to final value (CSS counter animation or JS)
-- Colored accent bar below each
-- Labels in muted text
-
-### 3. feature-list.html — Title + staggered bullet points
-- Title in accent color at top
-- 3-5 bullets fade-in-up with 0.3s stagger delay
-- Colored dot indicators
-
-### 4. comparison.html — Before/After metrics
-- Left: "before" (muted, smaller)
-- Center: arrow/divider
-- Right: "after" (large, accent)
-- Percentage change badge
-
-### 5. cta.html — Closing slide
-- Brand name large centered with subtle pulse
-- @AgentAbrams handle below
-- goodquestion.ai link
-- Tagline
-
-## capture.js
-```
-Usage: node capture.js <slide.html> <output-dir> [--duration 5] [--fps 30] [--width 1920] [--height 1080]
-```
-- Launches headless Chrome at exact dimensions
-- Waits 200ms for CSS init
-- Screenshots every 1000/fps ms
-- Outputs frame-0001.png, frame-0002.png, etc.
-
-## assemble.sh
-```
-Usage: ./assemble.sh <frames-dir> <narration.mp3> <output.mp4> [--fps 30]
-```
-- PNG sequence → h264 video
-- Overlay narration audio
-- AAC audio codec
-
-## generate-video.js — Master script
-Takes a JSON config file as input:
-```json
-{
- "title": "Video Title",
- "slides": [
- {"template": "hook", "data": {...}, "narration": "text", "duration": 6, "avatar": true},
- {"template": "big-number", "data": {...}, "narration": "text", "duration": 8}
- ],
- "avatar": {
- "image": "/root/Projects/goodquestion-ai/dashboard-captures/avatar/avatar-front.png",
- "maxDuration": 3,
- "position": "bottom-right",
- "size": 200
- }
-}
-```
-
-Pipeline:
-1. Generate HTML from template + data
-2. Capture frames with Puppeteer
-3. Generate narration with: edge-tts --voice "en-US-AndrewNeural" --rate="-10%"
-4. Avatar overlay on FIRST slide only, 3 seconds max, then fade out (use ffmpeg overlay filter)
-5. Concat all segments with narration
-6. Output landscape (1920x1080)
-7. Output vertical (1080x1920) — letterbox or crop with centered content
-
-## Test
-After building everything, create a test slide and capture 5 seconds at 30fps to verify the pipeline works.
diff --git a/tasks/done/03_build-scraper-nobilis.md b/tasks/done/03_build-scraper-nobilis.md
deleted file mode 100644
index 0a15fa9..0000000
--- a/tasks/done/03_build-scraper-nobilis.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Build a vendor scraper for Nobilis wallcoverings.
-
-Directory: /root/DW-Agents/vendor-scrapers/
-Output file: scrape-nobilis.js
-DB table: nobilis_catalog
-
-Use scraper-utils.js upsertProduct() pattern. Follow scrape-hermes.js as a template (similar French luxury brand).
-
-1. Visit nobilis.fr and find their wallcovering products
-2. Extract: pattern_name, collection, color, mfr_sku, image_url, product_url, all_images, description, specs
-3. Check for API endpoints or structured data
-4. Upsert to nobilis_catalog using scraper-utils
-5. Test with a small batch first (10 products)
-
-CRITICAL: Use mfr_sku field. Include all_images array.
\ No newline at end of file
diff --git a/tasks/done/03_china-seas-metafields-push.md b/tasks/done/03_china-seas-metafields-push.md
deleted file mode 100644
index f81a816..0000000
--- a/tasks/done/03_china-seas-metafields-push.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Push Width & Repeat Metafields to Shopify — China Seas
-
-Push width and pattern_repeat from china_seas_catalog to Shopify metafields for all products that have:
-- shopify_product_id IS NOT NULL
-- width IS NOT NULL AND width != ''
-
-Use namespace `global` with keys: `width`, `repeat_v`, `match_type`
-
-Connect to PostgreSQL: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-For each product:
-```bash
-curl -s -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/{SHOPIFY_ID}/metafields.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"metafield":{"namespace":"global","key":"width","value":"27 inches","type":"single_line_text_field"}}'
-```
-
-Note: shopify_product_id in china_seas_catalog uses GraphQL format (gid://shopify/Product/XXXXXXX). Extract the numeric ID.
-
-Rate limit: 2 req/sec (500ms delay). Report total metafields pushed.
diff --git a/tasks/done/03_china-seas-width-fix.md b/tasks/done/03_china-seas-width-fix.md
deleted file mode 100644
index dfa6fed..0000000
--- a/tasks/done/03_china-seas-width-fix.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Fix China Seas width extraction — 2,024 products missing width. All on quadrillefabrics.com.
-
-The existing scraper `/root/DW-Agents/vendor-scrapers/enrich-chinaseas-width.js` fetches the Wallpaper.html listing page and parses pattern groups. It found 143 widths last run but missed 2,024.
-
-Steps:
-1. Read the scraper to understand current parsing logic
-2. Fetch the listing page: `curl -s "https://www.quadrillefabrics.com/Wallpaper.html" | head -200`
-3. Also check if there's a Fabric.html or other listing pages with more products
-4. Check the HTML structure — maybe the parser misses patterns where width is in a different format
-5. Common formats to look for: "Width: 36\"", "36\" Wide", "W: 54\"", "54\" width"
-6. Update the regex/parser to catch all width formats
-7. Also check if product pages have width: try fetching a sample product URL from the DB
-8. Run updated scraper
-9. Report before/after
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: china_seas_catalog
-Scraper: /root/DW-Agents/vendor-scrapers/enrich-chinaseas-width.js
diff --git a/tasks/done/03_db-maintenance.md b/tasks/done/03_db-maintenance.md
deleted file mode 100644
index 0e8bc0b..0000000
--- a/tasks/done/03_db-maintenance.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Database Maintenance
-```bash
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "VACUUM ANALYZE vendor_catalog; VACUUM ANALYZE shopify_api_queue;"
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "DELETE FROM shopify_api_queue WHERE status = 'done' AND completed_at < NOW() - INTERVAL '3 days';"
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "DELETE FROM shopify_api_queue WHERE status = 'dead' AND completed_at < NOW() - INTERVAL '7 days';"
-```
-Report rows deleted and DB size.
diff --git a/tasks/done/03_fix-44-pricing.md b/tasks/done/03_fix-44-pricing.md
deleted file mode 100644
index 2452fb8..0000000
--- a/tasks/done/03_fix-44-pricing.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Fix 44 Single-Variant Products with DB Pricing
-
-The single-variant audit found 44 products that have pricing in the DB but $0 on Shopify.
-- 41 Sancar products at $170.00 retail (sancar_catalog.price_retail)
-- 3 Phillipe Romano products at $196.50 retail (phillipe_romano_catalog.price_retail)
-
-### Steps:
-1. Read the report: /root/DW-Agents/reports/single-variant-audit.json
-2. Extract the "can_fix" array — these have shopify_id and db_price
-3. For each product, update the variant price:
- - GET /admin/api/2024-01/products/{shopify_id}.json?fields=id,variants
- - PUT /admin/api/2024-01/variants/{variant_id}.json with {"variant":{"price":"170.00"}} (or 196.50 for PR)
-4. Rate limit: 0.5 req/s
-
-Shop: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-Do NOT create new variants. Only update the existing variant price.
\ No newline at end of file
diff --git a/tasks/done/03_fix-remaining-11-images.md b/tasks/done/03_fix-remaining-11-images.md
deleted file mode 100644
index a4a2f68..0000000
--- a/tasks/done/03_fix-remaining-11-images.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Fix 11 Remaining Products Missing Images (Novasuede, Phillipe Romano, Hollywood)
-
-## Context
-11 active Shopify products across 3 vendors have no images. These are the last stragglers from a 135-product no-image audit.
-
-## Products
-
-### Novasuede (5) — DWCC prefix, novasuede_catalog uses DWNS prefix
-- DWCC-490004 | mfr: Novasuede™ - BROWN T3F6W | novasuede™-brown
-- DWCC-490007 | mfr: Novasuede™ - CYPRESS T3PHW | novasuede™-cypress
-- DWCC-109200 | mfr: NOVASUEDE™-AFRICAN-VIOLET | novasuede™-fabric-wallcovering-pistachio
-- DWCC-110800 | mfr: NOVASUEDE™-AFRICAN-VIOLET | novasuede™-fabric-wallcovering-rosewater
-- DWCC-115800 | mfr: NOVASUEDE™-AFRICAN-VIOLET | novasuede™-fabric-wallcovering-storm
-
-### Phillipe Romano (3) — DWPW prefix
-- DWPW-430001 | mfr: C3542T | canvas-textile-wallcovering-seafoam-wallcovering-phillipe-romano
-- DWPW-430135 | mfr: SG2231 | cinque-terre-seagrass-woven-umber-wallcovering-phillipe-romano
-- DWPW-430198 | mfr: SG5605T | cinque-terre-seagrass-wheat-straw-wallcovering-phillipe-romano
-
-### Hollywood Wallcoverings (3) — DWHD/DWHW2 prefix
-- DWHD-506273 | mfr: MCV-SM-02 | hammond-point-serene-dream-commercial-wallcovering-hollywood-wallcoverings
-- DWHD-505817 | mfr: MCV-RS-01 | anchor-bay-jubilee-commercial-wallcovering-hollywood-wallcoverings
-- DWHW2-383906 | mfr: Capulet_L2-CT-13 | canal-texture-durable-tan-wallcovering-hollywood-wallcoverings
-
-## Steps
-1. Connect to PG (postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified).
-2. For Novasuede: query novasuede_catalog by mfr_sku patterns (strip ™ symbol). Try scraping novasuede.com product pages.
-3. For Phillipe Romano DWPW: these may be from justindavid_catalog or relativity_textiles_catalog. Query by mfr_sku (C3542T, SG2231, SG5605T). Also check gdrive: `rclone lsjson "gdrive:" --include "*C3542T*" -R --max-depth 4`.
-4. For Hollywood: query command54_catalog and hollywood_catalog by mfr_sku (MCV-SM-02, MCV-RS-01, Capulet_L2-CT-13).
-5. For any found image_url: download, stagedUploadsCreate on Shopify, productCreateMedia.
-6. If catalog URLs are dead, try Google Image Search pattern or vendor website scrape.
-7. Shopify GraphQL: designer-laboratory-sandbox.myshopify.com, token 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.
-8. Report: fixed count, still-missing list with reasons.
-
-## Important
-- NODE_PATH=/root/Projects/dw-showroom/node_modules for pg
-- NEVER expose source vendor names publicly (Hollywood = public, Command54 = hidden source)
diff --git a/tasks/done/03_fix-spec-gaps.md b/tasks/done/03_fix-spec-gaps.md
deleted file mode 100644
index 87e5181..0000000
--- a/tasks/done/03_fix-spec-gaps.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# Fix Spec Gaps — Extract specs from body_html for all vendors
-
-Many vendors have body_html but empty spec columns. Parse body_html to extract specs.
-
-## Vendors with 0% specs that have body_html:
-Run this query to find them:
-```sql
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
-SELECT relname, n_live_tup FROM pg_stat_user_tables
-WHERE relname LIKE '%_catalog' AND n_live_tup > 100
-ORDER BY n_live_tup DESC;"
-```
-
-For EACH catalog table with > 100 products:
-1. Check how many have body_html populated
-2. Check how many have empty width, repeat_v, material
-3. If body_html has data but specs are empty, extract with regex patterns:
-
-### Common spec patterns in body_html:
-```sql
--- Width patterns
-UPDATE xxx_catalog SET width = TRIM(match[1])
-FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Width|Roll Width|Dimensions)[:\s]*([^<,]+)', 'i') as match FROM xxx_catalog WHERE (width IS NULL OR width = '') AND body_html <> '') sub
-WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
-
--- Repeat patterns
-UPDATE xxx_catalog SET repeat_v = TRIM(match[1])
-FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Repeat|Pattern Repeat|Vertical Repeat)[:\s]*([^<,]+)', 'i') as match FROM xxx_catalog WHERE (repeat_v IS NULL OR repeat_v = '') AND body_html <> '') sub
-WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
-
--- Material patterns
-UPDATE xxx_catalog SET material = TRIM(match[1])
-FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Material|Substrate|Composition)[:\s]*([^<,]+)', 'i') as match FROM xxx_catalog WHERE (material IS NULL OR material = '') AND body_html <> '') sub
-WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
-
--- Fire rating patterns
-UPDATE xxx_catalog SET fire_rating = TRIM(match[1])
-FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Fire|Flame|ASTM|Class\s*[A1]|NFPA)[:\s]*([^<]+)', 'i') as match FROM xxx_catalog WHERE body_html <> '') sub
-WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
-```
-
-4. Add missing columns first:
-```sql
-ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS fire_rating VARCHAR(255) DEFAULT '';
-ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS finish VARCHAR(255) DEFAULT '';
-ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS application VARCHAR(255) DEFAULT '';
-ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS match_type VARCHAR(100) DEFAULT '';
-ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS all_images TEXT DEFAULT '';
-```
-
-## Priority order (most products first):
-1. kravet_catalog (9,780)
-2. brewster_catalog (8,715)
-3. cowtan_tout_catalog (8,466)
-4. bespoke_catalog (6,262)
-5. thibaut_catalog (5,557)
-6. york_catalog (5,304)
-7. schumacher_catalog (5,182) — already has 3,144 fire ratings
-8. pj_catalog (4,376)
-
-## CRITICAL RULE
-**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
-
-## Slack Notification — REQUIRED
-```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"SPEC GAPS FIXED: Extracted specs from body_html for [X] vendors. [summary]"}' "${SLACK_WEBHOOK_URL}"
-```
diff --git a/tasks/done/03_git-commit.md b/tasks/done/03_git-commit.md
deleted file mode 100644
index b989d0b..0000000
--- a/tasks/done/03_git-commit.md
+++ /dev/null
@@ -1,4 +0,0 @@
-## Git Commit — All Recent Changes
-
-Commit all changes in /root/DW-Agents/ with a descriptive message summarizing recent work.
-Check git status first. Stage relevant files. Do NOT push to remote.
diff --git a/tasks/done/03_harlequin-post-scrape-enrich.md b/tasks/done/03_harlequin-post-scrape-enrich.md
deleted file mode 100644
index 455b693..0000000
--- a/tasks/done/03_harlequin-post-scrape-enrich.md
+++ /dev/null
@@ -1,31 +0,0 @@
-## Harlequin Post-Scrape Enrichment
-
-Harlequin catalog was emptied by Dexter cleanup and is being re-scraped right now. After the scrape finishes, enrich with images.
-
-### Steps:
-1. Check if Harlequin scrape completed:
- ```bash
- tail -10 /tmp/scrape-harlequin-r2.log
- ```
- ```sql
- SELECT COUNT(*) FROM harlequin_catalog;
- ```
-
-2. If scrape is done (should have ~800-900 products), run image enrichment:
- ```bash
- cd /root/DW-Agents/vendor-scrapers && timeout 600 node enrich-harlequin-images.js
- ```
-
-3. Check results:
- ```sql
- SELECT COUNT(*) as total, COUNT(NULLIF(image_url,'')) as with_images FROM harlequin_catalog;
- ```
-
-4. Also check for duplicate prevention — make sure Dexter doesn't flag these again:
- ```bash
- curl -s -u admin:DWSecure2024! http://localhost:9676/api/status
- ```
-
-5. Update VCC: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-### CRITICAL: Do NOT push to Shopify.
diff --git a/tasks/done/03_hollywood-scrape-tower-pricing.md b/tasks/done/03_hollywood-scrape-tower-pricing.md
deleted file mode 100644
index 48bfae1..0000000
--- a/tasks/done/03_hollywood-scrape-tower-pricing.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Scrape Tower Wallcovering for Hollywood Pricing
-
-## Context
-3,011 Hollywood Wallcoverings products in vendor_catalog have no pricing.
-These products likely originate from towerwallcovering.com (MDC distributor).
-They have numeric mfr_sku codes (e.g., 48128, 72146, 48391).
-
-## Task
-1. First, check if towerwallcovering.com is reachable:
- ```bash
- timeout 10 curl -sI https://www.towerwallcovering.com/ | head -5
- ```
-
-2. If reachable, write a Node.js script to:
- - Query vendor_catalog for Hollywood products missing pricing (vendor_code='hollywood', specs->>'retail_price' IS NULL)
- - For each product, try to find it on towerwallcovering.com using the mfr_sku
- - Extract list_price from the product page
- - Calculate hw_price using DW formula: list_price * 0.80 / 0.65 / 0.85
- - Update vendor_catalog specs with list_price, hw_price, and set retail_price = hw_price
- - Use rate limiting: max 1 request per 2 seconds
- - Save script to /root/DW-Agents/vendor-scrapers/tower-pricing-backfill.js
-
-3. If NOT reachable, try mdcwall.com as alternative.
-
-4. If neither is reachable, try searching momentumtextilesandwalls.com with the numeric SKU codes.
-
-5. Report:
- - How many products were priced
- - Average price per yard
- - Any SKUs that couldn't be found
-
-## IMPORTANT
-- DB connection: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-- NEVER use sudo ufw allow PORT/tcp (security rule)
-- Use timeout 30 for any potentially long commands
-- Save all scripts, don't delete after running
diff --git a/tasks/done/03_jd-shopify-push.md b/tasks/done/03_jd-shopify-push.md
deleted file mode 100644
index 6a51416..0000000
--- a/tasks/done/03_jd-shopify-push.md
+++ /dev/null
@@ -1,52 +0,0 @@
-Push Justin David products to Shopify (P8) via Quinn queue.
-
-## Prerequisites
-- P3 AI enrichment should be mostly complete (>90%)
-- P4 Silas + P5 VCC already done (4,720/4,720)
-- Products need P6 room settings (separate task handles this)
-
-## Steps
-
-### Step 1: Check how many are ready for push
-```sql
-SELECT count(*) as ready
-FROM enrichment_tracking et
-JOIN justindavid_catalog c ON c.mfr_sku = et.mfr_sku
-WHERE et.vendor_code = 'justin_david'
- AND et.phase8_shopify_at IS NULL
- AND et.phase3_ai_at IS NOT NULL
- AND c.image_url IS NOT NULL
- AND c.width IS NOT NULL;
-```
-
-### Step 2: Push via Full Monte Phase 8
-Use the full-monte batch script or direct Quinn queue insertion.
-
-CRITICAL RULES:
-- ALL Shopify writes go through shopify-queue-worker (Quinn)
-- Title format: {TropicalName} - {TropicalColor} Wallcovering | Phillipe Romano
-- Body = 3 sentences ONLY, NO specs
-- Vendor = "Phillipe Romano" (private label, NEVER "Justin David")
-- SKU = DW SKU (DWPR-XXXXX format)
-- MFR SKU in custom.manufacturer_sku metafield only
-- ALL metafields from catalog data
-- Sample variant at $4.25, weight 0
-- Priced variants weight = 2 lbs
-- Inventory = 2026 units
-- Color data in custom.color_hex + custom.color_details metafields
-
-### Step 3: Update enrichment tracking
-```sql
-UPDATE enrichment_tracking SET phase8_shopify_at = NOW(), phase8_shopify_id = [shopify_id]
-WHERE vendor_code = 'justin_david' AND mfr_sku = '[sku]';
-```
-
-## Important
-- Shopify daily variant limit: ~1,000/day rolling window
-- At 2 products/minute, 4,115 products = ~34 hours
-- Will need to run across multiple days
-- Monitor queue: SELECT status, count(*) FROM shopify_api_queue WHERE created_at > now() - interval '24h' GROUP BY status;
-
-## Success Criteria
-- P8 > 0% after first batch
-- All pushed products have correct private label branding
diff --git a/tasks/done/03_launch-supervisor-audit-sweep.md b/tasks/done/03_launch-supervisor-audit-sweep.md
deleted file mode 100644
index 0a8ce4f..0000000
--- a/tasks/done/03_launch-supervisor-audit-sweep.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# Task C: Launch supervisor audit sweep — prioritized fix list
-
-Read-only task. Pick 500 random products already on Shopify, run them through launch-supervisor's audit endpoint, aggregate the v2_* gate failures by gate type and vendor, and produce a prioritized fix list.
-
-## Context
-- **Database**: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- **launch-supervisor**: running at `http://127.0.0.1:9843`
-- **Auth**: `admin` / `DWSecure2024!` (HTTP basic)
-- **Audit endpoint**: `POST /api/audit/:shopifyProductId`
-- **Audit log table**: `launch_audit_log` (has `shopify_product_id, audit_type, passed, issues::jsonb, audited_at`)
-
-## What launch-supervisor checks (as of today)
-
-Seven audit types run per product:
-1. `title` — format, vendor rules, MFR SKU not in title
-2. `image` — Gemini vision quality check
-3. `data` — inventory, pricing, basic data
-4. `tag` — tag quality
-5. `v2_variants` — Sample + Roll variants + READY image (Session 1 new gate)
-6. `v2_metafields` — 9 required custom.* metafields + PL rules (Session 1 new gate)
-7. `v2_spin` — enrichment_tracking.phase7_spin_url presence (Session 1 new gate)
-
-The v2_* gates are in **observe-only mode** — they log failures but don't draft products. You're collecting data for a prioritization conversation, not taking action.
-
-## Your job — four steps
-
-### Step 1: Pick 500 random on-shopify products
-```sql
-SELECT shopify_product_id, vendor_code, dw_sku
-FROM vendor_catalog
-WHERE on_shopify = true AND shopify_product_id IS NOT NULL
-ORDER BY RANDOM()
-LIMIT 500;
-```
-
-### Step 2: Hit the audit endpoint for each
-```bash
-curl -sS -u admin:DWSecure2024! -X POST http://127.0.0.1:9843/api/audit/{numeric_id}
-```
-Wait ~200ms between calls to avoid overwhelming the supervisor. Each call also triggers a Shopify GraphQL fetch and some DB writes, so pace yourself.
-
-**Track results in memory**: for each product, record `{shopify_id, vendor_code, dw_sku, results: [...]}` from the API response.
-
-**CRITICAL**: don't rely on the response alone — also read `launch_audit_log` after each call since that's the durable source of truth.
-
-### Step 3: Aggregate failures
-Build three aggregations:
-
-**A. By gate type** — how many of the 500 products fail each gate:
-```
-v2_variants: X/500 (Y%)
-v2_metafields: X/500 (Y%)
-v2_spin: X/500 (Y%)
-title: X/500
-...
-```
-
-**B. By specific issue text** — the top 15 most common issue strings across all gates (from `issues` JSONB array). Dedupe per product.
-
-**C. By vendor × gate** — pivot table showing which vendors have the most v2_metafields failures, v2_spin failures, etc.
-
-### Step 4: Prioritized fix list
-Rank the top 10 fixes by impact (number of products affected). For each fix, include:
-- What the fix is (e.g., "backfill `custom.dw_sku` metafield on {vendor}")
-- How many products would stop failing that gate
-- Estimated scope (how many Quinn jobs needed)
-- Which vendor(s) are most affected
-
-## Safety
-
-- **Read-only** — no UPDATE, no INSERT beyond launch_audit_log rows created by the supervisor itself
-- **DO NOT** flip any gates to enforce mode — leave observe-only alone
-- **DO NOT** touch Quinn or any Shopify write path
-- Save script to `/root/DW-Agents/scripts/yolo-audit-sweep.js`
-- Save the final report to `/root/DW-Agents/logs/audit-sweep-report-$(date +%Y-%m-%d).md`
-
-## Deliverables
-
-Terse markdown report, under 600 words, saved to the log file AND returned:
-1. Sample size (actual, since some lookups may fail)
-2. Gate failure rate table (A above)
-3. Top 15 issue strings (B above)
-4. Top 10 vendors × gate heat map (C above)
-5. **Top 10 prioritized fixes** with scope estimates
-6. Any products that errored during audit (shopify_id + error)
diff --git a/tasks/done/03_newmor-full-monty-test-1.md b/tasks/done/03_newmor-full-monty-test-1.md
deleted file mode 100644
index 3356d6b..0000000
--- a/tasks/done/03_newmor-full-monty-test-1.md
+++ /dev/null
@@ -1,74 +0,0 @@
-# NEWMOR Full Monty — Single Product Test (ALACHA-BLUE)
-
-Run the COMPLETE Full Monty pipeline for exactly 1 NEWMOR product: **ALACHA-BLUE** (Alacha pattern, Blue colorway).
-
-This is a test run. Report PASS/FAIL for each phase and show the final Shopify product URL.
-
-## Test Product
-- **MFR SKU**: ALACHA-BLUE
-- **Pattern**: Alacha
-- **Color**: Alacha Blue
-- **Width**: 130cm
-- **Material**: Non-Woven 460gsm
-- **Image**: https://newmor.com/wp-content/uploads/2016/11/Screen-Shot-2016-11-01-at-14.22.46.png
-- **Source**: https://newmor.com/product/alacha/
-
-## Pipeline — ALL 8 Phases Required
-
-### Phase 1: Scrape ✅ (ALREADY DONE)
-Product is already in `newmor_catalog` with full data. Verify the row exists.
-
-### Phase 2: Images ✅ (ALREADY DONE)
-Image URL present. Download the image to verify it's accessible (HTTP 200). Report dimensions.
-
-### Phase 3: AI Enrichment ✅ (ALREADY DONE)
-AI fields already populated (ai_colors, ai_styles, ai_patterns, ai_tags). Verify they exist and are non-empty.
-
-### Phase 4: Silas Validation
-Query the `newmor_catalog` row and validate ALL FULLPRODUCT requirements:
-- image_url present and accessible
-- width present
-- mfr_sku present
-- AI enrichment complete
-Report PASS/FAIL for each check.
-
-### Phase 5: VCC Approval
-For this test, auto-approve since Steve requested it. Log that VCC approval was bypassed for test.
-
-### Phase 6: Room Settings
-Generate room setting images for this product using the room-setting pipeline.
-Use Gemini image generation (`gemini-2.5-flash-image`) to create at least 1 room setting.
-Save to `/root/DW-Agents/shared/room-settings/newmor/ALACHA-BLUE/`.
-The wallpaper tile scale should be based on image physical size (default 5"x5" for swatches), NEVER roll width.
-
-### Phase 7: Spin Viewer
-Skip for this test — spin viewers are for 3D products only. Log as N/A.
-
-### Phase 8: Shopify Push
-Push this single product to Shopify via the shopify-queue-worker (NEVER hit Shopify API directly).
-Use these mappings:
-- **Title**: `Alacha - Blue Wallcovering | Newmor`
-- **Vendor**: `Newmor`
-- **product_type**: `Wallcovering`
-- **variant.sku**: Generate DW SKU format `DWNM-ALACHA-BLUE`
-- **variant.barcode**: EMPTY (mfr_sku goes in metafield only)
-- **Metafields**: manufacturer_sku, width, material, ai_colors, ai_styles, ai_tags, specs
-- **Tags**: Auto-generate from material, AI styles, AI colors, collection
-- **Images**: Primary image + any room settings from Phase 6
-- **body_html**: Description only (NO specs table in body — specs go in metafields exclusively)
-- **Status**: `draft` (this is a test — do NOT publish)
-
-## Final Report
-Print a summary table:
-| Phase | Status | Notes |
-|-------|--------|-------|
-| 1. Scrape | ✅ | Pre-existing |
-| 2. Images | ? | ... |
-| 3. AI Enrichment | ? | ... |
-| 4. Silas Validation | ? | ... |
-| 5. VCC Approval | ? | ... |
-| 6. Room Settings | ? | ... |
-| 7. Spin Viewer | N/A | Not applicable |
-| 8. Shopify Push | ? | ... |
-
-Include the Shopify admin URL for the created product so Steve can review it.
diff --git a/tasks/done/03_orphan-product-audit.md b/tasks/done/03_orphan-product-audit.md
deleted file mode 100644
index b032ca7..0000000
--- a/tasks/done/03_orphan-product-audit.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# Orphan Product Audit — Every Active Product Has a Browsable Collection
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-1. **How many total active products are in the store?**
- ```bash
- curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"query":"{ productsCount(query: \"status:active\") { count } }"}' \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json"
- ```
-
-2. **Is the collection audit (task 02) done?**
- - Check if `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json` exists
- - If not, this task depends on it — use the report to know which vendors lack collections
- - If the file doesn't exist, use the "missing" vendors from the audit as a starting point
-
-3. **What does "orphan" mean in this context?**
- - An active product whose vendor has NO collection at all
- - OR an active product that isn't included in ANY collection (custom or smart)
- - The second definition is harder to check — start with the first (vendor-level)
-
-## What To Do
-
-### Phase 1: Vendor-level orphan detection (fast)
-Using the collection audit report (task 02), for each vendor in the "missing" list:
-1. Count active products for that vendor via GraphQL
-2. List up to 10 example products (title, handle, SKU)
-3. This gives us "X products from Y vendors have no collection"
-
-### Phase 2: Product-level orphan check (thorough)
-For a more thorough check:
-1. Get all collection IDs and their product lists via Shopify Collect API
-2. Get all active product IDs
-3. Find product IDs that don't appear in ANY collection
-4. Group orphaned products by vendor for the report
-
-NOTE: This can be expensive API-wise. Use GraphQL bulk operations if product count > 10,000:
-```graphql
-{
- products(first: 250, query: "status:active") {
- edges {
- node {
- id
- title
- vendor
- handle
- collections(first: 10) {
- edges { node { id title } }
- }
- }
- }
- pageInfo { hasNextPage endCursor }
- }
-}
-```
-Paginate through all products. Any product with collections.edges = [] is an orphan.
-
-### Phase 3: Save report
-Save to: `/root/DW-Agents/yolo-agent/logs/orphan-product-report.json`
-Summary to: `/root/DW-Agents/yolo-agent/logs/orphan-product-summary.md`
-
-Format:
-```json
-{
- "timestamp": "2026-03-23T...",
- "totalActiveProducts": 50000,
- "totalOrphans": 500,
- "orphansByVendor": [
- {"vendor": "Phyllis Morris", "count": 28, "examples": ["product-1", "product-2"]},
- {"vendor": "Another Vendor", "count": 15, "examples": ["product-a"]}
- ],
- "percentOrphaned": "1.0%"
-}
-```
-
-### Phase 4: Slack notification
-```bash
-curl -X POST -H 'Content-type: application/json' \
- --data '{"text":"🔍 *Orphan Product Audit Complete*\n• Total active products: X\n• Products with NO collection: X (Y%)\n• Vendors with orphans: Z\n• Top 5 vendors: ...\n• Report: /root/DW-Agents/yolo-agent/logs/orphan-product-summary.md"}' \
- "${SLACK_WEBHOOK_URL}"
-```
-
-## DO NOT
-- Create collections (audit only — Steve reviews the report)
-- Archive or delete any products
-- Modify any product data
-- This is READ-ONLY
-
-## Verification
-- Report files exist
-- orphansByVendor entries have non-zero counts
-- totalOrphans + products-in-collections ≈ totalActiveProducts
-- Slack notification sent
-
-## Credentials
-- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
-- GraphQL: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json
-- Slack: ${SLACK_WEBHOOK_URL}
-
-## Rate Limiting
-- GraphQL: 1000 points/second on standard plan
-- products(first:250) costs ~252 points per call
-- For 50K products = ~200 pages = ~200 calls = ~4 points/sec = safe
-- Add 500ms delay between pages to be conservative
diff --git a/tasks/done/03_p2-06-interior-design-tagger.md b/tasks/done/03_p2-06-interior-design-tagger.md
deleted file mode 100644
index 52e59af..0000000
--- a/tasks/done/03_p2-06-interior-design-tagger.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# P2-06: Interior Design Tagger Automation
-
-Run the Gemini-based interior design tagger on products that are missing AI tags.
-
-## What to do
-
-1. Find products missing AI tags:
-```sql
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
-SELECT vendor, COUNT(*) as total,
- COUNT(CASE WHEN ai_tags IS NOT NULL AND ai_tags != '' THEN 1 END) as has_tags
-FROM shopify_products
-WHERE UPPER(status) = 'ACTIVE'
-GROUP BY vendor
-HAVING COUNT(CASE WHEN ai_tags IS NULL OR ai_tags = '' THEN 1 END) > 50
-ORDER BY total DESC
-LIMIT 15;"
-```
-
-2. For the top vendors missing tags, run the tagger script:
-```
-python3 /root/DW-Agents/scripts/tag-interior-design.py --vendor <VENDOR_CODE> --limit 200
-```
-
-3. Focus on vendors that already went through Full Monty (they got tags via Gemini but shopify_products table may not be updated)
-
-4. Report: tags added per vendor
-
-## Key
-- Uses Gemini 2.0 Flash for vision analysis
-- API key: ${GOOGLE_API_KEY}
-- Adds style tags (Modern, Traditional), color tags, pattern tags, room application tags
-- Tags go on Shopify product tags field
diff --git a/tasks/done/03_phillip-jeffries-specs-only.md b/tasks/done/03_phillip-jeffries-specs-only.md
deleted file mode 100644
index 4a8785b..0000000
--- a/tasks/done/03_phillip-jeffries-specs-only.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## Phillip Jeffries — Specs Only (No Images)
-
-PJ has 4,376 products with 0 images. Rule: PJ and Cowtan & Tout — NEVER pull images. Specs only.
-
-### Steps:
-1. Check what specs PJ catalog already has:
- ```sql
- SELECT COUNT(*) as total,
- COUNT(NULLIF(width,'')) as has_width,
- COUNT(NULLIF(material,'')) as has_material,
- COUNT(NULLIF(repeat_v,'')) as has_repeat
- FROM pj_catalog;
- ```
- (Table might be phillip_jeffries_catalog — check: `SELECT catalog_table FROM vendor_registry WHERE vendor_code = 'phillip_jeffries';`)
-
-2. If they have product_url, check if specs can be scraped from phillipjeffries.com
-3. Focus on width, material, repeat, match_type, fire_rating — these are needed for Shopify body_html
-4. Do NOT download or save any images
-
-### CRITICAL: No images for PJ. Specs only. Do NOT push to Shopify.
diff --git a/tasks/done/03_phillipe-romano-shopify-push.md b/tasks/done/03_phillipe-romano-shopify-push.md
deleted file mode 100644
index 0ae0f75..0000000
--- a/tasks/done/03_phillipe-romano-shopify-push.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Retry pushing Phillipe Romano products to Shopify. The daily variant creation limit may have reset.
-
-## Script
-cd /root/DW-Agents/vendor-scrapers && node push-phillipe-romano-to-shopify.js --limit 500
-
-## Expected behavior
-- Creates new Shopify products from phillipe_romano_catalog entries that have no shopify_product_id
-- Each product gets: title, body_html with specs table, tags, images, metafields
-- Rate limited to ~1.8 calls/sec
-- Links back to catalog by setting shopify_product_id
-
-## If daily limit error persists
-- Log the error and report it
-- Do NOT retry in a loop — just report that the limit is still active
-- The cron job at 9 UTC will retry automatically
-
-## DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-## Shopify: designer-laboratory-sandbox.myshopify.com with token <redacted:SHOPIFY_ADMIN_TOKEN>
diff --git a/tasks/done/03_pj-browse-hidden-audit.md b/tasks/done/03_pj-browse-hidden-audit.md
deleted file mode 100644
index 837ce8c..0000000
--- a/tasks/done/03_pj-browse-hidden-audit.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# PJ Browse-Hidden Audit — Fix 3 Failed Products
-
-**Priority**: P1
-**Estimated time**: ~10 minutes
-**Type**: Data fix
-
-## Description
-The PJ browse-hidden fix completed with 3 failures. Find and fix them.
-
-## Steps
-1. Check which 3 failed: Review `/tmp/pj-fix.log` for FAIL entries
-2. For each failed product, manually add browse-hidden tag via Shopify API:
- ```
- curl -X PUT "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/{ID}.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- -d '{"product": {"tags": "EXISTING_TAGS, browse-hidden"}}'
- ```
-3. Verify no PJ products appear in collection browsing:
- `curl -s "https://www.designerwallcoverings.com/collections/fine-paper/products.json?limit=50&page=5" | python3 -c "...filter for PJ without browse-hidden"`
-
-## Success criteria
-- All 4,377 PJ products have browse-hidden tag
-- Zero PJ products visible in collection browsing
diff --git a/tasks/done/03_pm2-health-check.md b/tasks/done/03_pm2-health-check.md
deleted file mode 100644
index a4c7fcd..0000000
--- a/tasks/done/03_pm2-health-check.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## PM2 Health Check — Restart Crashed Agents
-
-### Steps:
-1. Run `pm2 jlist` and parse for errored/stopped processes
-2. Check which agents have high restart counts (>100)
-3. Restart any errored agents: `pm2 restart {name}`
-4. Check disk usage: `df -h /`
-5. Check memory: `free -h`
-6. Check for zombie Claude processes: `ps aux | grep claude | grep -v grep`
- - Kill any Claude processes running >12 hours
-7. Truncate any PM2 logs over 100MB: `find /root/.pm2/logs -size +100M -exec truncate -s 0 {} \;`
-
-### Report:
-- List any agents that were down and restarted
-- Current disk/memory status
-- Any zombie processes killed
-
-IMPORTANT: Do NOT start product-scheduler (PM2 #44) — it is intentionally stopped.
diff --git a/tasks/done/03_pm2-health-zombie-cleanup.md b/tasks/done/03_pm2-health-zombie-cleanup.md
deleted file mode 100644
index 9e588b9..0000000
--- a/tasks/done/03_pm2-health-zombie-cleanup.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## PM2 Health Audit + Zombie Cleanup
-
-Recurring issue across many sessions: zombie Claude processes, crashed agents, disk/memory pressure.
-
-### Tasks:
-1. Run `pm2 list` and report any agents in errored/stopped state — restart them
-2. Check for zombie Claude processes older than 12 hours: `ps aux | grep claude | grep -v grep`
- - Log their PID and how long they've been running before killing
-3. Check disk space (`df -h /`) and memory (`free -h`)
-4. Check if any ports have conflicts: `ss -tlnp | grep node | sort -t: -k2 -n`
-5. Verify all production websites are responding:
- - glassbeadedwallpaper.com (9888)
- - flockedwallpaper.com (3200)
- - grassclothwallcoverings.com (8080)
- - grassclothwallpaper.com (7123)
- - novasuede.com (8100)
- - bubbe.ai (3011)
-6. Check crontab for any stale/broken cron jobs
-7. Report summary: agents up/down, disk %, memory %, zombie count, website status
diff --git a/tasks/done/03_private-label-vendor-audit.md b/tasks/done/03_private-label-vendor-audit.md
deleted file mode 100644
index 55abf77..0000000
--- a/tasks/done/03_private-label-vendor-audit.md
+++ /dev/null
@@ -1,26 +0,0 @@
-## Private Label Vendor Audit
-
-15 vendors are flagged as private_label in vendor_registry. These are DW's own brands that already exist in Shopify. They should NEVER get new SKU assignments or be pushed as "new" products.
-
-### Steps:
-1. List all private label vendors:
- ```sql
- SELECT vendor_name, vendor_code, sku_prefix, total_products, private_label, skip_catalog_scrape
- FROM vendor_registry WHERE private_label = true ORDER BY total_products DESC;
- ```
- DB: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified"`
-
-2. For each private label vendor, verify their catalog table does NOT have newly-assigned DW SKUs that conflict with existing Shopify SKUs. Check for old SKU prefixes in Shopify (like XJA- for Hollywood).
-
-3. Check that the VCC assign-skus endpoint rejects private label vendors:
- ```bash
- curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/vendors/hollywood/assign-skus
- ```
- Should return an error about private label being blocked.
-
-4. Report any private label vendors that have products in PostgreSQL but should be enrichment-only (images/specs from real manufacturer sites).
-
-### CRITICAL RULES:
-- Do NOT create any Shopify products
-- Do NOT assign any new SKUs
-- Only audit and report
diff --git a/tasks/done/03_push-hollywood.md b/tasks/done/03_push-hollywood.md
deleted file mode 100644
index c3896b5..0000000
--- a/tasks/done/03_push-hollywood.md
+++ /dev/null
@@ -1,27 +0,0 @@
-## Push Hollywood Wallcoverings to Shopify — Full Monte Phase 8
-
-8,091 push-ready Hollywood products. Push through Full Monte Phase 8.
-
-### Product Rules:
-- Vendor display name: "Hollywood Wallcoverings"
-- Commercial vendor — default fire_rating: "Class A Fire Rated : Passes ASTM E84"
-- Title format: {PatternName} - {Color} Wallcovering | Hollywood Wallcoverings
-- Body HTML: 3-sentence description ONLY from ai_description. NO specs, NO tables.
-- ALL specs in metafields: custom.width, custom.fire_rating, custom.material, custom.pattern_name, custom.color, custom.manufacturer_sku, custom.color_hex, custom.color_details, dwc.ai_generated_description
-- MFR SKU as a Shopify tag
-- Sample variant: $4.25, SKU = {DW_SKU}-Sample, weight = 0
-- Roll variant: price from DB or $0 (quote model), weight = 2 lbs
-- inventory_quantity: 2026 for all variants
-- NEVER expose source vendor name in any public content
-
-### Steps:
-1. Query vendor_catalog for vendor_code='hollywood' with on_shopify=false, has image+width+mfr_sku+ai_colors
-2. Use Quinn queue for ALL Shopify writes (GraphQL productCreate batched, then metafieldsSet)
-3. Batch 25 per GraphQL call
-4. Mark on_shopify=true and set shopify_product_id in vendor_catalog after successful push
-5. Update enrichment_tracking phase8_shopify date
-6. Cap at 25 products per batch through Launch Pad gate — Steve reviews before push
-
-### IMPORTANT:
-- These go to Launch Pad first for Steve's review — do NOT auto-push all 8,091
-- Queue the first 25 for review, report the rest as "queued for Launch Pad"
diff --git a/tasks/done/03_run-http-scrapers.md b/tasks/done/03_run-http-scrapers.md
deleted file mode 100644
index 415815c..0000000
--- a/tasks/done/03_run-http-scrapers.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Run all HTTP-based vendor scrapers that haven't been refreshed today.
-Execute these in sequence (they're fast, 5-30s each):
-
-cd /root/DW-Agents/vendor-scrapers
-node scrape-hollyhunt.js
-node scrape-brunschwig.js
-node scrape-leejofa.js
-node scrape-gpbaker.js
-node scrape-threads.js
-node scrape-mulberry.js
-node scrape-bakerlifestyle.js
-node scrape-nina.js
-node scrape-spoonflower.js
-node scrape-hollywood.js
-node scrape-wolfgordon.js
-node scrape-flavorpaper.js
-
-Report results for each.
\ No newline at end of file
diff --git a/tasks/done/03_server-health-check.md b/tasks/done/03_server-health-check.md
deleted file mode 100644
index 5ecfc88..0000000
--- a/tasks/done/03_server-health-check.md
+++ /dev/null
@@ -1,17 +0,0 @@
-Full server health check and cleanup.
-
-Steps:
-1. Check disk space: df -h / (warn if >85%)
-2. Check memory: free -h
-3. Check PM2 status: pm2 list — report any errored/stopped agents
-4. Restart any crashed agents (not intentionally stopped ones)
-5. Check for zombie Chrome/Puppeteer processes: ps aux | grep -E "chrome|chromium|puppeteer" | grep -v grep | wc -l
- - If more than 5, kill orphaned ones older than 2 hours
-6. Check for zombie Claude processes: ps aux | grep claude | grep -v grep
- - Any older than 12 hours should be noted (PID, runtime, command)
-7. Check SSL certs expiry for production sites:
- - echo | openssl s_client -servername glassbeadedwallpaper.com -connect glassbeadedwallpaper.com:443 2>/dev/null | openssl x509 -noout -dates
- - echo | openssl s_client -servername flockedwallpaper.com -connect flockedwallpaper.com:443 2>/dev/null | openssl x509 -noout -dates
- - echo | openssl s_client -servername grassclothwallcoverings.com -connect grassclothwallcoverings.com:443 2>/dev/null | openssl x509 -noout -dates
-8. Check PM2 log sizes: du -sh ~/.pm2/logs/ — rotate if over 500MB
-9. Report summary of all findings
diff --git a/tasks/done/03_vcc-sync-and-report.md b/tasks/done/03_vcc-sync-and-report.md
deleted file mode 100644
index 8fdb2a7..0000000
--- a/tasks/done/03_vcc-sync-and-report.md
+++ /dev/null
@@ -1,36 +0,0 @@
-## VCC Full Sync and Status Report
-
-Force a VCC sync and generate a comprehensive status report.
-
-### Steps:
-1. Trigger full sync: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-2. Wait 30s for sync to complete
-
-3. Generate report:
- ```sql
- SELECT
- COUNT(*) as total_vendors,
- COUNT(CASE WHEN total_products > 0 THEN 1 END) as vendors_with_products,
- COUNT(CASE WHEN private_label = true THEN 1 END) as private_label,
- SUM(total_products) as total_products,
- SUM(products_with_images) as total_with_images,
- ROUND(SUM(products_with_images)::numeric / NULLIF(SUM(total_products),0) * 100, 1) as overall_img_pct,
- SUM(shopify_count) as total_shopify
- FROM vendor_registry;
- ```
-
-4. Top 10 vendors by catalog size:
- ```sql
- SELECT vendor_name, total_products, products_with_images, shopify_count,
- CASE WHEN private_label THEN 'PL' ELSE '' END as pl
- FROM vendor_registry ORDER BY total_products DESC LIMIT 10;
- ```
-
-5. Report vendors with 0 products (need scraping or inquiry pages)
-
-6. Email Steve a summary: Use George (port 9850):
- ```bash
- curl -s -u admin:DWSecure2024! -X POST http://localhost:9850/api/send \
- -H 'Content-Type: application/json' \
- -d '{"to":"steve@designerwallcoverings.com","subject":"VCC Status Report - Mar 14","body":"<report here>"}'
- ```
diff --git a/tasks/done/03_zoffany-enrich.md b/tasks/done/03_zoffany-enrich.md
deleted file mode 100644
index 02d8edc..0000000
--- a/tasks/done/03_zoffany-enrich.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Zoffany — Scrape Product Pages for Width & Repeat
-
-373 products in `zoffany_catalog`. Have `product_url` column but `width` and `repeat_v` are empty. Need to scrape product pages.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Sample: `SELECT product_url FROM zoffany_catalog WHERE (width IS NULL OR width = '') AND product_url IS NOT NULL LIMIT 3`
-3. Fetch a sample product page with curl. Find where width/repeat are displayed on the page.
-4. Write a Node.js script at `/root/DW-Agents/vendor-scrapers/enrich-zoffany.js` that:
- - Queries products with missing width: `SELECT id, product_url FROM zoffany_catalog WHERE (width IS NULL OR width = '') AND product_url IS NOT NULL LIMIT 400`
- - Fetches each product_url (2s delay between requests)
- - Parses width, repeat_v, repeat_h, material, match_type from the HTML
- - Updates the row in the DB
-5. Run: `timeout 900 node enrich-zoffany.js`
-6. Report enrichment count.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-Zoffany site: https://www.zoffany.com/
diff --git a/tasks/done/04_04_dead-agent-restart.md b/tasks/done/04_04_dead-agent-restart.md
deleted file mode 100644
index 10f5bc7..0000000
--- a/tasks/done/04_04_dead-agent-restart.md
+++ /dev/null
@@ -1 +0,0 @@
-Run: pm2 list | grep -E "errored|stopped". For any errored or stopped agents, restart them with pm2 restart <name>. Also check for agents with restart count > 100 (crash looping) and log their names. Save results to /root/DW-Agents/logs/agent-restarts.log with timestamp.
\ No newline at end of file
diff --git a/tasks/done/04_04_enrich-scalamandre.md b/tasks/done/04_04_enrich-scalamandre.md
deleted file mode 100644
index c7683b3..0000000
--- a/tasks/done/04_04_enrich-scalamandre.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-scalamandre-width.js
-
-Enrich Scalamandre catalog with width specs.
\ No newline at end of file
diff --git a/tasks/done/04_04_shopify-product-audit.md b/tasks/done/04_04_shopify-product-audit.md
deleted file mode 100644
index c371567..0000000
--- a/tasks/done/04_04_shopify-product-audit.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Audit Shopify product counts. Token: <redacted:SHOPIFY_ADMIN_TOKEN>, Store: designer-laboratory-sandbox.myshopify.com
-1. Get total product count, active/draft/archived breakdown
-2. Report top vendors by product count
-3. Check for zero-image products
-4. Report summary
\ No newline at end of file
diff --git a/tasks/done/04_04_shopify-product-quality.md b/tasks/done/04_04_shopify-product-quality.md
deleted file mode 100644
index be44783..0000000
--- a/tasks/done/04_04_shopify-product-quality.md
+++ /dev/null
@@ -1,25 +0,0 @@
-Hollywood Shopify product quality verification.
-
-Shopify: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-API: 2024-01
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-1. Get count of ACTIVE (non-archived) Hollywood products on Shopify. Use the Shopify API to count products by vendor='Hollywood Wallcoverings'.
-
-2. Sample 10 active products and verify:
- - body_html contains specs table (not just placeholder text)
- - Has at least 1 image
- - Has product_type set
- - Has tags set
- - Has correct variant structure (Sample + Sold Per Yard if priced)
-
-3. For any products missing images on Shopify, check if image_url exists in the DB and attempt to re-push.
-
-4. Check if product_type is set correctly — should reflect actual type (Wallcovering, Acoustical, etc.), NOT hardcoded 'Fabric'.
-
-5. Ensure tags include: material type, fire rating (if available), application, collection name.
-
-6. Report: how many products pass quality check, how many need fixes, what the most common issues are.
-
-IMPORTANT: Rate limit Shopify API calls — max 2 per second. Use 0.5s sleep between calls.
\ No newline at end of file
diff --git a/tasks/done/04_04_thibaut-discontinued-archive.md b/tasks/done/04_04_thibaut-discontinued-archive.md
deleted file mode 100644
index abe974c..0000000
--- a/tasks/done/04_04_thibaut-discontinued-archive.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Find Thibaut products that are discontinued (returning 301/302 redirects from vendor site). Query thibaut_catalog or thibaut table for products with discontinued=true or status containing redirect. Cross-reference with Shopify: for each discontinued product that has a shopify_id, archive it on Shopify using PUT /admin/api/2024-01/products/{id}.json with status=archived. Use token <redacted:SHOPIFY_ADMIN_TOKEN>. Rate limit to 2 req/sec. Log results to /root/DW-Agents/logs/thibaut-archive.log
\ No newline at end of file
diff --git a/tasks/done/04_04_vcc-stats-refresh.md b/tasks/done/04_04_vcc-stats-refresh.md
deleted file mode 100644
index 561b328..0000000
--- a/tasks/done/04_04_vcc-stats-refresh.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Refresh vendor_registry stats for ALL vendors with catalog tables.
-
-For each vendor, update:
-```sql
-UPDATE vendor_registry SET
- catalog_count = (SELECT COUNT(*) FROM {table}),
- catalog_with_images = (SELECT COUNT(*) FROM {table} WHERE image_url IS NOT NULL AND image_url != ''),
- catalog_with_width = (SELECT COUNT(*) FROM {table} WHERE width IS NOT NULL AND width != ''),
- catalog_on_shopify = (SELECT COUNT(*) FROM {table} WHERE shopify_product_id IS NOT NULL),
- updated_at = NOW()
-WHERE catalog_table = '{table}';
-```
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
\ No newline at end of file
diff --git a/tasks/done/04_add-auth-to-all-open-services.md b/tasks/done/04_add-auth-to-all-open-services.md
deleted file mode 100644
index d60b111..0000000
--- a/tasks/done/04_add-auth-to-all-open-services.md
+++ /dev/null
@@ -1,79 +0,0 @@
-# Add Basic Auth to ALL Open Services
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-1. **Which services are currently missing auth?**
- Run: `ss -tlnp | grep node` and test each port with `curl -s -o /dev/null -w "%{http_code}" http://localhost:PORT/`
- A response of 200 without auth credentials means NO auth.
- Compare against these known auth-protected services: 7240, 9501, 9607 (already have auth).
-
-2. **What framework does each service use?**
- - Next.js → needs middleware.ts
- - Express.js → needs express-basic-auth or custom middleware
- - Plain node HTTP → needs manual auth check
- Check package.json for each service.
-
-3. **Are there any services that SHOULD be public?**
- - Production websites (grassclothwallcoverings.com etc.) should NOT get auth — they're customer-facing
- - Blog (goodquestion.ai on port 9883) — this is a static Astro site, public by design
- - API endpoints that other agents call — may need API key auth instead of basic auth
-
-## What To Do
-
-### Standard credentials for ALL services:
-- Username: `admin`
-- Password: `DWSecure2024!`
-
-### For Express.js services (most DW-Agents):
-Add this to the top of server.js/index.js:
-```javascript
-// Basic auth middleware
-app.use((req, res, next) => {
- // Skip auth for health checks
- if (req.path === '/health' || req.path === '/api/health') return next();
-
- const auth = req.headers.authorization;
- if (!auth || !auth.startsWith('Basic ')) {
- res.setHeader('WWW-Authenticate', 'Basic realm="DW Agent"');
- return res.status(401).json({ error: 'Authentication required' });
- }
-
- const decoded = Buffer.from(auth.slice(6), 'base64').toString();
- const [user, pass] = decoded.split(':');
- if (user === 'admin' && pass === 'DWSecure2024!') return next();
-
- res.setHeader('WWW-Authenticate', 'Basic realm="DW Agent"');
- res.status(401).json({ error: 'Invalid credentials' });
-});
-```
-
-### For Next.js services:
-Create `middleware.ts` or `src/middleware.ts` with Basic Auth check (see ClawCoder at /root/Projects/ClawCoder/src/middleware.ts as reference).
-
-### Priority order:
-1. YOLO agent (9670) — has API auth but dashboard is open
-2. DW Master Hub (9893) — central dashboard, HIGH priority
-3. Shopify Expert Agent (9887)
-4. Product Agent (9604)
-5. Room Setting Agent (9896)
-6. George Gmail (9850)
-7. Remaining services
-
-### SKIP these (public or special):
-- goodquestion-blog (9883) — public blog, no auth needed
-- grassclothwallpaper (7123) — production website
-- Port 8891 — temporary preview server (will be killed)
-
-## DO NOT
-- Add auth to production customer-facing websites
-- Change existing auth on services that already have it
-- Break inter-agent API calls (check if agents call each other)
-
-## Verification
-After each service is updated:
-1. `curl -s -o /dev/null -w "%{http_code}" http://localhost:PORT/` should return 401 (not 200)
-2. `curl -s -u admin:DWSecure2024! -o /dev/null -w "%{http_code}" http://localhost:PORT/` should return 200
-3. `pm2 restart SERVICE_NAME` and verify it stays up
-
-## Credentials
-- Auth: admin / DWSecure2024!
diff --git a/tasks/done/04_ai-enrich-arte.md b/tasks/done/04_ai-enrich-arte.md
deleted file mode 100644
index 6a64571..0000000
--- a/tasks/done/04_ai-enrich-arte.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# AI Enrich — Arte (204 products missing AI colors)
-
-Same approach for arte vendor_code. Limit 200 per run.
-
-```bash
-cd /root/DW-Agents/full-monte
-node full-monte-batch.js --vendor arte --phase 3 --limit 200
-```
-Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/04_blog-post-overnight.md b/tasks/done/04_blog-post-overnight.md
deleted file mode 100644
index 3e7b533..0000000
--- a/tasks/done/04_blog-post-overnight.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Publish Blog Post to goodquestion.ai
-
-Generate and publish a blog post to the Agent Abrams blog.
-
-### Topic Ideas (pick ONE):
-- "How One AI System Manages 60,000+ Products While You Sleep"
-- "The Architecture of Autonomous Data Pipelines"
-- "Building Self-Healing Infrastructure with AI Agents"
-
-### Pipeline:
-1. Generate post content (target: entrepreneurs, founders, small business owners)
-2. Run through sanitizer: `node /root/DW-Agents/blog-agent/sanitizer.js`
-3. Build Astro: `cd /root/Projects/goodquestion-ai && npm run build`
-4. Push to git
-5. Post to Twitter (@agentabrams): `node /root/Projects/goodquestion-ai/scripts/tweet.cjs`
-6. Post to Bluesky: `node /root/Projects/goodquestion-ai/scripts/bsky-post.cjs`
-
-### CRITICAL SANITIZATION RULES:
-- NEVER mention: wallcoverings, wallpaper, Designer Wallcoverings, DW, DW-Agents
-- NEVER mention: specific pricing, vendor names, IP addresses, ports
-- NEVER mention: Shopify, Algolia, student debt
-- See /root/.claude/projects/-root/memory/MEMORY.md for full blocklist
-- Tone: Practical, direct, shows real results. End with board seat mention.
diff --git a/tasks/done/04_brewster-specs-fix-and-push.md b/tasks/done/04_brewster-specs-fix-and-push.md
deleted file mode 100644
index 3e00a9b..0000000
--- a/tasks/done/04_brewster-specs-fix-and-push.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Fix Brewster specs detection and push specs to Shopify.
-
-From Session #105: "Brewster specs in ul not table, not detected as product-specs."
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Shopify product token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-1. Read /root/DW-Agents/scripts/add-specs-bulk.py — understand how it detects existing specs
-2. Read /root/DW-Agents/vendor-scrapers/enrich-brewster-specs.js — understand Brewster's UL format
-3. The add-specs-bulk.py now detects UL-based specs for upgrade (as confirmed by dry run)
-4. Run: `cd /root/DW-Agents/scripts && timeout 600 python3 add-specs-bulk.py --live --vendor Brewster 2>&1`
-5. If it's already running from another agent, check the output and report status
-6. Report: total Brewster products processed, specs added, errors
diff --git a/tasks/done/04_build-scraper-pierre-frey.md b/tasks/done/04_build-scraper-pierre-frey.md
deleted file mode 100644
index 35baab1..0000000
--- a/tasks/done/04_build-scraper-pierre-frey.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Build a vendor scraper for Pierre Frey wallcoverings.
-
-Directory: /root/DW-Agents/vendor-scrapers/
-Output file: scrape-pierrefrey.js
-DB table: pierre_frey_catalog
-
-Use scraper-utils.js upsertProduct() pattern. Follow scrape-hermes.js as a template.
-
-1. Visit pierrefrey.com and find their wallpaper/wallcovering products
-2. Extract: pattern_name, collection, color, mfr_sku, image_url, product_url, all_images, description, specs
-3. French luxury brand - likely has clean product pages
-4. Upsert to pierre_frey_catalog using scraper-utils
-5. Test with a small batch first (10 products)
-
-CRITICAL: Use mfr_sku field. Include all_images array.
\ No newline at end of file
diff --git a/tasks/done/04_catalog-audit.md b/tasks/done/04_catalog-audit.md
deleted file mode 100644
index 0fe2a7c..0000000
--- a/tasks/done/04_catalog-audit.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Run a comprehensive catalog audit. Connect to PostgreSQL and get counts:
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT relname, n_live_tup FROM pg_stat_user_tables WHERE relname LIKE '%_catalog' ORDER BY n_live_tup DESC;"
-
-Then calculate the total product count across all catalogs.
-Report which vendors have the most products and which have suspiciously low counts (under 50).
\ No newline at end of file
diff --git a/tasks/done/04_china-seas-hex-colors.md b/tasks/done/04_china-seas-hex-colors.md
deleted file mode 100644
index e2620ed..0000000
--- a/tasks/done/04_china-seas-hex-colors.md
+++ /dev/null
@@ -1,32 +0,0 @@
-## Extract Hex Colors for China Seas via Gemini Vision
-
-Use Gemini 2.0 Flash to analyze China Seas wallpaper product images and extract dominant hex colors.
-
-Query china_seas_catalog for all wallpaper products (mfr_sku LIKE '%WP%') that have image_url but no color_hex:
-```sql
-SELECT id, mfr_sku, pattern_name, color_name, image_url
-FROM china_seas_catalog
-WHERE image_url IS NOT NULL AND image_url != ''
-AND (color_hex IS NULL OR color_hex = '')
-AND mfr_sku LIKE '%WP%'
-LIMIT 200;
-```
-
-For each product, call Gemini:
-```bash
-curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}" \
- -H "Content-Type: application/json" \
- -d '{
- "contents": [{"parts": [
- {"text": "Analyze this wallpaper image. Return ONLY a single hex color code (e.g. #8B4513) representing the dominant/primary color. Just the hex code, nothing else."},
- {"inlineData": {"mimeType": "image/jpeg", "data": "BASE64_IMAGE"}}
- ]}]
- }'
-```
-
-OR if image is a URL, use fileUri approach. Update the DB:
-```sql
-UPDATE china_seas_catalog SET color_hex = '#XXXXXX' WHERE id = {id};
-```
-
-Process in batches of 50. Report total updated.
diff --git a/tasks/done/04_china-seas-quadrille-retry.md b/tasks/done/04_china-seas-quadrille-retry.md
deleted file mode 100644
index 9d956e9..0000000
--- a/tasks/done/04_china-seas-quadrille-retry.md
+++ /dev/null
@@ -1,27 +0,0 @@
-## China Seas / Quadrille — Image Retry
-
-China Seas has 2,650 products but only 842 images (31.8%). The previous enrichment scraper tried quadrillefabrics.com listing pages but couldn't find images for 1,808 products.
-
-### Steps:
-1. Check if China Seas products have usable source_url or product_url:
- ```sql
- SELECT column_name FROM information_schema.columns WHERE table_name = 'china_seas_catalog' AND column_name LIKE '%url%';
- SELECT source_url FROM china_seas_catalog WHERE (image_url IS NULL OR image_url = '') AND source_url IS NOT NULL LIMIT 5;
- ```
-
-2. Try the Quadrille website product pages directly:
- ```bash
- timeout 15 curl -s -L "https://www.quadrillefabrics.com/Wallpaper.html" | head -200
- ```
-
-3. Check if products are on kravet.com too (Kravet distributes Quadrille):
- ```sql
- SELECT mfr_sku FROM china_seas_catalog WHERE image_url IS NULL OR image_url = '' LIMIT 5;
- ```
- Then try: `curl -s "https://www.kravet.com/SKUHERE"`
-
-4. If any approach works, build a quick enrichment loop
-
-5. Update VCC after: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-### CRITICAL: Do NOT push to Shopify. China Seas = Quadrille, NOT Kravet.
diff --git a/tasks/done/04_enrichment-status-report.md b/tasks/done/04_enrichment-status-report.md
deleted file mode 100644
index 81c975b..0000000
--- a/tasks/done/04_enrichment-status-report.md
+++ /dev/null
@@ -1,37 +0,0 @@
-## Enrichment Scraper Status Report
-
-Check results from overnight enrichment scrapers and report which vendors still need work.
-
-### Steps:
-1. Check overall catalog stats:
- ```sql
- SELECT vendor_name, total_products, products_with_images,
- ROUND(products_with_images::numeric / NULLIF(total_products,0) * 100, 1) as img_pct
- FROM vendor_registry
- WHERE total_products > 50 AND private_label = false
- ORDER BY img_pct ASC NULLS FIRST;
- ```
- DB: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified"`
-
-2. Check enrichment logs for results:
- - `tail -10 /tmp/enrich-malibu-images.log 2>/dev/null`
- - `tail -10 /tmp/enrich-chinaseas-images.log 2>/dev/null`
- - `tail -10 /tmp/enrich-harlequin-images.log 2>/dev/null`
-
-3. Check Marburg width enrichment:
- ```sql
- SELECT COUNT(*) as total, COUNT(NULLIF(width,'')) as has_width FROM marburg_catalog;
- ```
-
-4. Check Newwall width enrichment:
- ```sql
- SELECT COUNT(*) as total, COUNT(NULLIF(width,'')) as has_width FROM newwall_catalog;
- ```
-
-5. Update VCC stats: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-6. Report top 10 vendors by missing images (non-private-label only)
-
-### CRITICAL RULES:
-- Do NOT push to Shopify
-- Only query and report
diff --git a/tasks/done/04_enrichment-tracking-progress.md b/tasks/done/04_enrichment-tracking-progress.md
deleted file mode 100644
index be87207..0000000
--- a/tasks/done/04_enrichment-tracking-progress.md
+++ /dev/null
@@ -1,34 +0,0 @@
-## Enrichment Tracking Progress Report
-
-Check the enrichment_tracking table to see overall pipeline progress across all 8 Full Monte phases.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Run these queries:
-
-```sql
--- Phase completion by phase
-SELECT phase, COUNT(*) as completed
-FROM enrichment_tracking
-WHERE is_processed = true
-GROUP BY phase ORDER BY phase;
-
--- Vendors with lowest phase 3 (AI enrichment) coverage
-SELECT vendor,
- COUNT(*) FILTER (WHERE phase = 'phase_3' AND is_processed) as enriched,
- COUNT(DISTINCT sku) as total_skus
-FROM enrichment_tracking
-GROUP BY vendor
-HAVING COUNT(DISTINCT sku) > 50
-ORDER BY (COUNT(*) FILTER (WHERE phase = 'phase_3' AND is_processed))::float / NULLIF(COUNT(DISTINCT sku), 0) ASC
-LIMIT 15;
-
--- Recent enrichment activity (last 7 days)
-SELECT DATE(processed_at) as dt, phase, COUNT(*) as processed
-FROM enrichment_tracking
-WHERE processed_at > NOW() - INTERVAL '7 days'
-GROUP BY dt, phase ORDER BY dt DESC, phase;
-```
-
-### Output:
-Report only. Write results to `/root/DW-Agents/logs/yolo-enrichment-progress.log` with timestamp.
diff --git a/tasks/done/04_gemini-texture-classify-17k.md b/tasks/done/04_gemini-texture-classify-17k.md
deleted file mode 100644
index 80522b8..0000000
--- a/tasks/done/04_gemini-texture-classify-17k.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Gemini AI Texture Classification — Unclassified Products
-
-## Goal
-Classify products with no `repeat_classification` using Gemini Vision.
-Textures don't need repeat data. Patterns do. This closes the classification gap.
-
-## Step 1: Write and run a Node.js script
-
-Create `/root/DW-Agents/scripts/gemini-texture-classify.js` that does:
-
-1. Query DB for unclassified products:
-```sql
-SELECT id, vendor_code, mfr_sku, image_url
-FROM vendor_catalog
-WHERE on_shopify = true
- AND (repeat_classification IS NULL OR repeat_classification = '')
- AND image_url IS NOT NULL AND image_url != ''
-ORDER BY id DESC
-LIMIT 5000;
-```
-
-2. For each product, call Gemini 2.0 Flash:
- - API key: `${GOOGLE_API_KEY}`
- - Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=API_KEY`
- - Send image via URL in the request (download image, base64 encode, send as inline_data)
- - Prompt: "Classify this wallcovering image. TEXTURE = solid color, natural fiber, faux finish, no repeating decorative pattern. PATTERN = has a decorative design that repeats (florals, stripes, geometric, damask, etc). Respond with ONLY one word: texture or pattern"
-
-3. Parse response, update DB:
-```sql
-UPDATE vendor_catalog SET repeat_classification = $1, updated_at = NOW() WHERE id = $2;
-```
-
-4. Rate limit: max 10 requests/second, exponential backoff on 429s
-5. Log progress every 100 products
-6. Batch size: process 5000 per run (YOLO can re-queue for more)
-7. If Gemini returns anything other than "texture" or "pattern", mark as "unknown"
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-## Run
-```bash
-cd /root/DW-Agents/scripts && timeout 900 node gemini-texture-classify.js
-```
-
-Report: total classified, texture count, pattern count, errors.
diff --git a/tasks/done/04_malibu-width-inference.md b/tasks/done/04_malibu-width-inference.md
deleted file mode 100644
index a2838df..0000000
--- a/tasks/done/04_malibu-width-inference.md
+++ /dev/null
@@ -1,32 +0,0 @@
-Fill Malibu width gaps via inference — 1,657 products missing width. No external vendor site (URLs point to DW storefront).
-
-All 1,657 have specs JSONB with fire_rating, substrate, traffic — but no width. 1,227 products DO have width.
-
-Steps:
-1. Check what widths exist and their distribution:
-```sql
-SELECT width, COUNT(*) FROM malibu_catalog WHERE width IS NOT NULL GROUP BY width ORDER BY 2 DESC LIMIT 20;
-```
-
-2. Pattern-name inference (siblings with same pattern should share width):
-```sql
-UPDATE malibu_catalog m SET width = s.width
-FROM (SELECT DISTINCT pattern_name, width FROM malibu_catalog WHERE width IS NOT NULL AND pattern_name IS NOT NULL) s
-WHERE m.pattern_name = s.pattern_name AND m.width IS NULL;
-```
-
-3. Collection-level inference:
-```sql
-UPDATE malibu_catalog m SET width = s.mode_width
-FROM (SELECT collection, MODE() WITHIN GROUP (ORDER BY width) AS mode_width FROM malibu_catalog WHERE width IS NOT NULL AND collection IS NOT NULL GROUP BY collection) s
-WHERE m.collection = s.collection AND m.width IS NULL;
-```
-
-4. Check Shopify metafields for width on products that have shopify_product_id:
-- Fetch global.width or specs.width metafield from Shopify for each product
-- Update DB if found
-
-5. Report before/after counts at each step.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: malibu_catalog
diff --git a/tasks/done/04_overall-stats-email.md b/tasks/done/04_overall-stats-email.md
deleted file mode 100644
index a4e33f4..0000000
--- a/tasks/done/04_overall-stats-email.md
+++ /dev/null
@@ -1,40 +0,0 @@
-## Overall Stats Update and Email Steve
-
-### Steps:
-1. Force VCC sync: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-2. Generate comprehensive stats:
- ```sql
- SELECT
- COUNT(*) as total_vendors,
- SUM(total_products) as total_products,
- SUM(products_with_images) as with_images,
- ROUND(SUM(products_with_images)::numeric / NULLIF(SUM(total_products),0) * 100, 1) as img_pct,
- SUM(shopify_count) as shopify_total
- FROM vendor_registry WHERE total_products > 0;
- ```
-
-3. Top 5 vendors improved today:
- Compare current products_with_images to see who gained the most
-
-4. Vendors still below 80%:
- ```sql
- SELECT vendor_name, total_products, products_with_images,
- ROUND(products_with_images::numeric / NULLIF(total_products,0) * 100, 1) as pct
- FROM vendor_registry WHERE total_products > 50 AND is_private_label = false
- AND (products_with_images::numeric / NULLIF(total_products,0) * 100) < 80
- ORDER BY total_products DESC;
- ```
-
-5. Email Steve via George (port 9850):
- ```bash
- curl -s -u admin:DWSecure2024! -X POST http://localhost:9850/api/send \
- -H 'Content-Type: application/json' \
- -d '{"to":"steve@designerwallcoverings.com","subject":"Catalog Enrichment Progress - Mar 15","body":"<stats>"}'
- ```
-
-### Include in email:
-- Total products and image coverage percentage
-- Vendors that improved today
-- Vendors still needing work
-- Private label vendor count
diff --git a/tasks/done/04_pm2-health-check.md b/tasks/done/04_pm2-health-check.md
deleted file mode 100644
index 47047ee..0000000
--- a/tasks/done/04_pm2-health-check.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## PM2 Health Check and Cleanup
-
-Check all PM2 processes, restart any crashed agents, and clean up zombies.
-
-### Steps:
-1. Get PM2 status: `pm2 jlist` — parse JSON for errored/stopped processes
-2. Restart any errored agents: `pm2 restart <name>`
-3. Check for zombie Claude processes (>12h): `ps aux | grep claude | grep -v grep`
-4. Kill any zombie Claude processes older than 12 hours (log PID first)
-5. Check disk space: `df -h /`
-6. Check memory: `free -h`
-7. Check if any port conflicts exist: `ss -tlnp | grep node | sort -t: -k2 -n`
-8. Report summary of agent health
diff --git a/tasks/done/04_pm2-health-cleanup.md b/tasks/done/04_pm2-health-cleanup.md
deleted file mode 100644
index 1fbe08d..0000000
--- a/tasks/done/04_pm2-health-cleanup.md
+++ /dev/null
@@ -1,6 +0,0 @@
-## PM2 Health Check & Cleanup
-
-1. Check for errored/stopped PM2 processes and restart them
-2. Check disk space and memory usage
-3. Kill any orphaned Chrome/Puppeteer/Playwright processes
-4. Report system health summary
diff --git a/tasks/done/04_push-ascreation.md b/tasks/done/04_push-ascreation.md
deleted file mode 100644
index 547eb9d..0000000
--- a/tasks/done/04_push-ascreation.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Push AS Creation to Shopify — Full Monte Phase 8
-
-6,114 push-ready AS Creation products.
-
-### Product Rules:
-- Vendor display name: "AS Creation"
-- Residential vendor — default fire_rating: "Inquire for more Information"
-- Title format: {PatternName} - {Color} Wallcovering | AS Creation
-- Body HTML: 3-sentence ai_description ONLY. NO specs.
-- ALL specs in metafields exclusively
-- MFR SKU as tag
-- Sample variant: $4.25, weight 0
-- Roll variant: price from DB or $0, weight 2 lbs
-- inventory_quantity: 2026
-
-### Steps:
-1. Query vendor_catalog for vendor_code='as_creation' with on_shopify=false, has image+width+mfr_sku+ai_colors
-2. Queue through Quinn (GraphQL batched 25 per call)
-3. Mark on_shopify=true after push
-4. Update enrichment_tracking phase8
-
-### IMPORTANT:
-- Launch Pad gate — queue first 25 for Steve's review, rest stays in pipeline
diff --git a/tasks/done/04_push-enrichment-to-shopify.md b/tasks/done/04_push-enrichment-to-shopify.md
deleted file mode 100644
index 197812f..0000000
--- a/tasks/done/04_push-enrichment-to-shopify.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Push AI Enrichment Data to Shopify Metafields
-
-Find products in vendor_catalog that have ai_colors but haven't had their color data pushed to Shopify metafields yet.
-
-For each product with shopify_product_id AND ai_colors:
-1. Parse ai_colors for dominant color hex
-2. Queue via shopify_api_queue (INSERT INTO):
- - `custom.color_hex` = dominant hex
- - `custom.color_details` = full JSON array of colors with hex + percentages
-3. Use GraphQL metafieldsSet for efficiency
-
-Token alias: product
-Source agent: enrichment-push
-Priority: 2
-Limit: 500 products per run
diff --git a/tasks/done/04_push-mf-york.md b/tasks/done/04_push-mf-york.md
deleted file mode 100644
index e03ed3b..0000000
--- a/tasks/done/04_push-mf-york.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Push Metafields — York (5,304 products)
-
-Run: `python3 /root/DW-Agents/scripts/push-metafields-vendor.py york_catalog`
-
-Pushes dw.width, dw.manufacturer_sku, dw.repeat, dw.pattern_name, dw.color, dw.contents + global.* legacy.
-Report results.
diff --git a/tasks/done/04_push-specs-pj.md b/tasks/done/04_push-specs-pj.md
deleted file mode 100644
index 966168c..0000000
--- a/tasks/done/04_push-specs-pj.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Push Specs to Shopify body_html — Phillip Jeffries (4,376 products)
-
-PJ has 4,376 products linked to Shopify via shopify_product_id, with 100% width coverage.
-
-### Steps:
-1. Run the specs pusher script:
- python3 /root/DW-Agents/scripts/push-specs-vendor.py pj_catalog --limit 5000
-2. Monitor progress — it logs every 50 products pushed
-3. Report final count when complete
-
-The script handles rate limiting (429 retries with 4s sleep).
-It skips products that already have "product-specs" in body_html.
-It builds HTML spec tables from DB columns: width, material, fire_rating, match_type, collection, design.
-
-NOTE: The Shopify API is congested by ~200 PM2 agents. The script will be slow (~1 product/2-3s).
-If rate limiting is excessive, consider stopping non-essential PM2 agents first.
\ No newline at end of file
diff --git a/tasks/done/04_recrawl-low-spec-vendors.md b/tasks/done/04_recrawl-low-spec-vendors.md
deleted file mode 100644
index 3690ddf..0000000
--- a/tasks/done/04_recrawl-low-spec-vendors.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Re-crawl Low Spec Vendors — Trigger fresh scans
-
-After fixing image and spec columns, re-trigger scans on vendors that need fresh data.
-
-## Re-scan these agents (check they're online first):
-```bash
-# Check each agent, restart if needed, then trigger scan
-for agent in "elise:9623" "dara:9632" "theo:9645" "cleo:9622" "artie:9624" \
- "sasha:9620" "pj:9627" "maya:9626" "oscar:9640" "ines:9628" \
- "knox:9621" "kira:9625" "rex:9635" "etta:9631" \
- "dex:9633" "faye:9634" "graham:9617" \
- "bjorn:9638" "milo:9639" "hank:9637" "beau:9629" \
- "wendy:9642" "kurt:9636" "stu:9641"; do
- IFS=: read name port <<< "$agent"
- status=$(curl -s -u admin:DWSecure2024! http://127.0.0.1:$port/health 2>/dev/null)
- if [ -n "$status" ]; then
- echo "Scanning $name on $port..."
- curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:$port/api/scan
- sleep 5 # small gap between triggers
- else
- echo "OFFLINE: $name on $port"
- fi
-done
-```
-
-Wait for all to complete, then run a final audit:
-```sql
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
-SELECT relname as catalog,
- n_live_tup as products
-FROM pg_stat_user_tables
-WHERE relname LIKE '%_catalog' AND n_live_tup > 0
-ORDER BY n_live_tup DESC;"
-```
-
-## CRITICAL RULE
-**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
-
-## Slack Notification — REQUIRED
-```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"VENDOR RE-CRAWL COMPLETE: [X] vendors re-scanned. Total catalog: [Y] products."}' "${SLACK_WEBHOOK_URL}"
-```
-
-## EXCLUSIONS — DO NOT TOUCH IMAGES
-- **Phillip Jeffries (PJ, port 9627)** — Do NOT pull images. Specs only.
-- **Cowtan & Tout** — Do NOT pull images. Skip image fields entirely.
diff --git a/tasks/done/04_run-all-vendor-crawls-high-to-low.md b/tasks/done/04_run-all-vendor-crawls-high-to-low.md
deleted file mode 100644
index c7eb67e..0000000
--- a/tasks/done/04_run-all-vendor-crawls-high-to-low.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# Run Vendor Command Center Crawls — High-End to Low-End
-
-Trigger vendor crawls through the Vendor Command Center (Victor, port 9660) in order from highest-end luxury vendors down to mass market. For each vendor, trigger a scan and wait for completion before moving to the next.
-
-## Vendor Order (High-End → Low-End)
-
-### Tier 1 — Ultra Luxury
-1. **Elitis** (Elise, port 9623) — French luxury wallcoverings
-2. **Dedar** (Dara, port 9632) — Italian luxury
-3. **Timorous Beasties** (Theo, port 9645) — Scottish luxury
-4. **Cole & Son** (Cleo, port 9622) — Heritage British luxury
-5. **Arte International** (Artie, port 9624) — Belgian luxury
-
-### Tier 2 — Premium
-6. **Schumacher** (Sasha, port 9620) — American heritage luxury
-7. **Phillip Jeffries** (PJ, port 9627) — Natural materials specialist
-8. **Maya Romanoff** (Maya, port 9626) — Artisan handcrafted
-9. **Osborne & Little** (Oscar, port 9640) — British premium
-10. **Innovations** (Ines, port 9628) — Contract/commercial premium
-
-### Tier 3 — Upper Mid
-11. **Kravet** (Knox, port 9621) — Large design house
-12. **Koroseal** (Kira, port 9625) — Commercial wallcovering
-13. **Romo** (Rex, port 9635) — British design
-14. **1838 Wallcoverings** (Etta, port 9631) — Heritage prints
-15. **Ralph Lauren** (Ralph, port 9608) — Lifestyle luxury
-
-### Tier 4 — Mid Market
-16. **Thibaut** (Thibaut, port 9603) — American classic
-17. **York** (York Contract, port 9618) — American traditional
-18. **Designtex** (Dex, port 9633) — Contract/commercial
-19. **Fabricut** (Faye, port 9634) — Multi-category
-20. **Graham & Brown** (Graham, port 9617) — British modern
-
-### Tier 5 — Specialty & Niche
-21. **BN Walls** (Bjorn, port 9638) — Dutch wallcovering
-22. **Mind the Gap** (Milo, port 9639) — Eclectic design
-23. **Hygge & West** (Hank, port 9637) — Modern artisan
-24. **Bespoke** (Beau, port 9629) — Custom wallcovering
-25. **WallQuest** (Wendy, port 9642) — Decorative
-26. **Knoll** (Kurt, port 9636) — Contract furniture/wall
-27. **Stout Textiles** (Stu, port 9641) — Textile specialist
-
-### Tier 6 — Mass Market / Other
-28. **Brewster** (Brewster, port 9600) — Mass market
-29. **Contrado** (Carlo, port 9643) — Print-on-demand
-30. **Mural Source** (Murray, port 9644) — Custom murals
-31. **Arteriors** (Ari, port 9646) — Home accessories
-32. **Folia Fabrics** (Flora, port 9647) — Fabric specialist
-
-## How to Run Each
-For each vendor, check if the agent is online first:
-```bash
-pm2 list | grep <pm2-name>
-curl -s -u admin:DWSecure2024! http://127.0.0.1:<port>/api/status
-```
-
-If agent is online, trigger a scan:
-```bash
-curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:<port>/api/scan
-```
-
-Or via VCC:
-```bash
-curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:9660/api/vendors/<vendor_code>/scan
-```
-
-Wait for each to complete (check /api/status until scan_running=false) before starting the next.
-
-**ALL agents must capture: ALL specs, ALL images (pipe-separated in all_images), ALL catalog data.**
-
-Skip any vendors that are offline or erroring — just note them and move on.
-
-Report a summary at the end: vendor name, products crawled, spec completeness %, images captured.
-
-
-## CRITICAL RULE
-**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY. Downloading product data FROM other vendors' Shopify stores for catalog data is fine and expected. product-scheduler and schedule-engine are STOPPED intentionally. Do NOT restart them.**
-
-
-## Slack Notification — REQUIRED
-When this task is complete, send a Slack message to Steve with the results summary:
-```bash
-curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "${SLACK_WEBHOOK_URL}"
-```
-Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/04_scrape-repeats-thibaut.md b/tasks/done/04_scrape-repeats-thibaut.md
deleted file mode 100644
index bc70520..0000000
--- a/tasks/done/04_scrape-repeats-thibaut.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Scrape Missing Repeat Measurements — Thibaut
-
-Thibaut has products on Shopify missing vertical/horizontal repeat data.
-
-1. Find Thibaut products in vendor_catalog or thibaut_catalog missing repeat data
-2. For each, visit the product_url and extract repeat measurements
-3. Update the catalog table with repeat_v and repeat_h
-4. If match_type is available, derive repeat_h: Straight=width, Half Drop=repeat_v/2, Free=0
-
-Use `timeout 30` for curl calls. Limit 200 products per run.
-Scraper at: /root/DW-Agents/vendor-scrapers/scrape-thibaut.js
diff --git a/tasks/done/04_session-blog-social.md b/tasks/done/04_session-blog-social.md
deleted file mode 100644
index d8c45ab..0000000
--- a/tasks/done/04_session-blog-social.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Session Checklist — Blog + Social Posts
-
-## Blog Post
-Write and publish a blog post to goodquestion.ai about "The Full Monte: How One AI Agent Enriched 823 Products in 20 Minutes".
-
-Key points:
-- Discovered 224 new products by diffing vendor website against our catalog
-- Scraped 85 collection pages for specs (width, repeat, fire rating, material)
-- Found 44 missing images via CDN URL pattern discovery
-- AI vision (Gemini) analyzed 779 product images for color, hex, style tags
-- All done in parallel — specs + images + AI enrichment running simultaneously
-- Total time: ~20 minutes for complete catalog enrichment
-
-Target audience: entrepreneurs and founders. Angle: showing what 1 person + AI can do.
-Use the blog-agent pipeline: generate → sanitize → publish → build astro → tweet → bluesky.
-Run: cd /root/DW-Agents/blog-agent && node generator.js
-Then: cd /root/Projects/goodquestion-ai && node scripts/tweet.cjs && node scripts/bsky-post.cjs
-
-## IMPORTANT
-- Run content through sanitizer before publishing
-- No wallcovering/wallpaper/vendor names in public content per MEMORY.md rules
-- Frame as "product catalog enrichment" generically
diff --git a/tasks/done/04_shop-by-color-page.md b/tasks/done/04_shop-by-color-page.md
deleted file mode 100644
index 985d0fb..0000000
--- a/tasks/done/04_shop-by-color-page.md
+++ /dev/null
@@ -1,49 +0,0 @@
-Build a "Shop by Color" feature for the Designer Wallcoverings Shopify store.
-
-## Overview
-Create a page where customers can enter a color (Hex, Pantone, Sherwin-Williams SW####, Dunn-Edwards DE####, Behr PPU##-##, or Frazee code) and find matching wallcoverings from our catalog.
-
-## Technical Approach
-1. Build a Node.js service that:
- - Classifies input type (hex vs paint code patterns)
- - Converts all colors to LAB color space
- - Uses CIEDE2000 (ΔE00) distance for perceptual matching
- - Queries our Shopify products by dominant color
-
-2. Data sources for paint-to-hex mapping:
- - Sherwin-Williams: Parse their published color PDF for RGB/Hex values
- - Dunn-Edwards: Per-color pages include Hex/RGB (e.g., /colors/browser/DE6210/)
- - Behr: Consumer color pages + downloadable .ase palette files
- - Pantone: Defer (licensed) — implement as pluggable provider
- - Frazee: Legacy — map through Sherwin-Williams system where possible
-
-3. For our wallcovering products:
- - Extract dominant colors from product images using sharp/Jimp
- - Store as LAB values in PostgreSQL
- - Index for fast nearest-neighbor search
-
-## Implementation Plan
-1. Create `/root/Projects/Designer-Wallcoverings/shop-by-color/` directory
-2. Build `color-service.js` — color parsing, conversion, and matching engine
-3. Build `color-catalog-builder.js` — extract dominant colors from all Shopify product images
-4. Build `server.js` — Express API with endpoints:
- - POST /api/color/search — accepts color input, returns matching products
- - GET /api/color/popular — popular color searches
-5. Build a simple frontend page (HTML/CSS/JS) for the color picker UI
-6. Register as PM2 service on an available port (check with `ss -tlnp | grep node`)
-
-## Color Matching Thresholds (ΔE00)
-- ≤ 2: "Very close match"
-- 2-5: "Close match"
-- 5-10: "Approximate match"
-- > 10: "Distant — show as suggestions only"
-
-## DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-## Auth: admin / DWSecure2024!
-## Port rules: Pick unused port, lock to Steve's IP (76.33.146.135) via UFW
-
-## IMPORTANT
-- Do NOT use Pantone data without a license — implement as disabled provider
-- Generate your own color tiles — do NOT scrape brand chip images
-- Add disclaimers that screen colors are approximations
-- All new ports: `sudo ufw allow from 76.33.146.135 to any port PORT proto tcp`
diff --git a/tasks/done/04_shopify-body-html-cleanup.md b/tasks/done/04_shopify-body-html-cleanup.md
deleted file mode 100644
index d9c75be..0000000
--- a/tasks/done/04_shopify-body-html-cleanup.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Shopify Body HTML Cleanup — Remove Specs Tables
-
-**Priority**: P2
-**Estimated time**: ~3 hours
-**Type**: Data cleanup
-
-## Description
-Scan ALL Shopify products for body_html containing specs tables. Rewrite to 3-sentence description only.
-
-## Steps
-1. Paginate through all products: `GET /admin/api/2024-01/products.json?limit=250&fields=id,title,body_html,vendor`
-2. Flag any product where body_html contains: `<table`, `<ul><li>Width`, `Specifications`, `Material:`, `Collection:`, `Design:`
-3. For each flagged product, rewrite body_html:
- - Sentence 1: What it is (pattern/color/vendor)
- - Sentence 2: Style/mood
- - Sentence 3: Recommended use
-4. Update via PUT API
-5. Log all changes
-
-## Rules
-- NEVER include specs in body. All specs in metafields ONLY.
-- Rate limit: 2 req/s, use 0.5s delay between calls
-- Skip Phillip Jeffries products (browse-hidden, no changes)
-
-## Success criteria
-- Zero products with specs tables in body_html
-- All body_html follows 3-sentence description format
diff --git a/tasks/done/04_shopify-product-audit.md b/tasks/done/04_shopify-product-audit.md
deleted file mode 100644
index ef43083..0000000
--- a/tasks/done/04_shopify-product-audit.md
+++ /dev/null
@@ -1,25 +0,0 @@
-Audit current Shopify product counts and health.
-
-Use token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Steps:
-1. Get total product count:
- curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/count.json"
-
-2. Get count by status (active, draft, archived):
- curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/count.json?status=active"
- (repeat for draft, archived)
-
-3. Get top 10 vendors by product count using GraphQL:
- curl -s -X POST -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -H "Content-Type: application/json" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json" \
- -d '{"query": "{ shop { productCount } }"}'
-
-4. Check for products with zero images:
- Query first 50 products and count how many have images.count = 0
-
-5. Report: total products, active/draft/archived breakdown, top vendors, zero-image count
diff --git a/tasks/done/04_vcc-dashboard-fix.md b/tasks/done/04_vcc-dashboard-fix.md
deleted file mode 100644
index 1978a37..0000000
--- a/tasks/done/04_vcc-dashboard-fix.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## VCC Dashboard Status API Fix
-
-Session #98 noted that VCC (Vendor Command Center) at port 9660 has `/api/status` returning 404, causing the dashboard to show 0 catalog / 0 on Shopify.
-
-### Tasks:
-1. Read the VCC server code at `/root/DW-Agents/vendor-command-center/server.js`
-2. Check if `/api/status` endpoint exists — if not, check what the correct endpoint is
-3. Check the dashboard HTML/JS to see what endpoint it calls
-4. Fix the mismatch — either add the missing endpoint or fix the dashboard to call the correct one
-5. The status endpoint should return: total catalog count, total on Shopify, vendor breakdown
-6. Test with: `curl -u admin:DWSecure2024! http://localhost:9660/api/status`
-7. Restart PM2 process if server.js was modified: `pm2 restart vendor-command-center`
-
-VCC location: `/root/DW-Agents/vendor-command-center/`
-Port: 9660
-Auth: admin / DWSecure2024!
diff --git a/tasks/done/04_verify-body-html-patches.md b/tasks/done/04_verify-body-html-patches.md
deleted file mode 100644
index fcc5d92..0000000
--- a/tasks/done/04_verify-body-html-patches.md
+++ /dev/null
@@ -1,99 +0,0 @@
-# Task D: Verify body_html patches + spot-check live Shopify bodies
-
-Two-part verification. First, static check that the patched `buildDescription` (product-agent) and `buildBodyHtml` (update-agent) can't emit banned Rule #6 patterns. Second, spot-check 25 recently-pushed Shopify products to confirm their live body_html is compliant.
-
-## Context
-- **Database**: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- **Shopify product token**: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-- **Shopify store**: `designer-laboratory-sandbox.myshopify.com`
-- **Rule #6**: body_html is 3 sentences of description ONLY. NO `<table>`, `<ul>`, `<dl>`, `Specifications`, `Width:`, `Material:`, `Collection:`, `Design:`, `Fire Rating:`, `Repeat:`, `Pattern:`, `Style:`, `Colors:`, `SKU:`, `MFR:`.
-- **Patched files**:
- - `/root/DW-Agents/product-agent/server.js` — `buildDescription(row, ai, vendorName)` at ~line 1008, body_html assignment at ~line 1239
- - `/root/DW-Agents/update-agent/server.js` — `buildBodyHtml(...)` at ~line 333, body_html use at ~line 457
-- **Quinn's hardened validator** in `/root/DW-Agents/shopify-queue-worker/worker.js` hard-blocks any GraphQL productSet/productUpdate/productCreate whose descriptionHtml contains banned patterns (see `BODY_RULE_6_PATTERNS` const)
-
-## Part 1: Static code verification
-
-### Step 1a: Grep the patched functions for banned patterns
-For each of the two files, extract the patched function body (line range around `buildDescription` / `buildBodyHtml`) and verify:
-- No literal strings: `<table`, `<ul>`, `<li>`, `<dl>`, `<h3>`, `<h4>`, `Specifications`, `Width:`, `Material:`, `Collection:`, `Design:`, `Fire Rating:`, `Repeat:`, `Pattern:`, `Style:`, `Colors:`, `SKU:`, `MFR:` inside any string literal being RETURNED
-- Only allowed wrapper is `<p>...</p>`
-
-**Important caveat**: grep will also hit the safety-net regex literals (the patches have defense-in-depth regex filters). Those are OK — they're rejection logic, not emission logic. Distinguish by checking if the match is inside a regex (`/.../`, `new RegExp(...)`) vs inside a template/string literal that gets returned/concatenated into the HTML. Report any that are in emission positions.
-
-### Step 1b: Static check on the Quinn validator
-Verify `worker.js` has `BODY_RULE_6_PATTERNS`, `validateBodyHtml`, and `collectBodyStringsFromVariables` exported/used, and that `processJob` hard-blocks jobs whose `gateIssues` start with `BODY_RULE_6:`. Confirm by locating the `[GATE-BLOCK]` log line.
-
-## Part 2: Live Shopify spot-check
-
-### Step 2a: Pick 25 recently-pushed products
-```sql
-SELECT dw_sku, shopify_product_id
-FROM vendor_catalog
-WHERE on_shopify = true
- AND shopify_product_id IS NOT NULL
- AND shopify_synced_at > NOW() - INTERVAL '72 hours'
-ORDER BY shopify_synced_at DESC
-LIMIT 25;
-```
-
-### Step 2b: Fetch their bodyHtml from Shopify
-```graphql
-{ product(id: "gid://shopify/Product/{id}") { id title bodyHtml } }
-```
-
-### Step 2c: Classify each body
-For each product's bodyHtml, determine:
-- `clean` — 1-3 sentences in `<p>...</p>`, no banned patterns
-- `short` — fewer than 3 sentences but no banned patterns (acceptable but not ideal per Rule #6)
-- `empty` — empty string
-- `violation` — contains any banned pattern (record which)
-
-Report counts for each bucket, plus per-vendor breakdown.
-
-## Part 3: Quinn gate live test
-
-### Step 3a: Queue a deliberately-bad test job
-Insert a test Quinn job whose descriptionHtml has a `<table>` and confirm it gets marked `dead` with error starting `Rule #6 body_html violation`. Use a fake productId (any valid GID is fine — the gate runs BEFORE the Shopify call).
-
-```sql
-INSERT INTO shopify_api_queue (method, endpoint, payload, token_alias, priority, status, source_agent, max_retries)
-VALUES (
- 'POST', '/graphql.json',
- $$ {"query":"mutation ProductSet($input: ProductSetInput!) { productSet(input: $input) { product { id } userErrors { field message } } }","variables":{"input":{"id":"gid://shopify/Product/1","descriptionHtml":"<table><tr><td>Width:</td><td>52</td></tr></table>"}}} $$::jsonb,
- 'product', 0, 'pending', 'body-html-verify-test', 3
-);
-```
-
-Wait ~5 sec and confirm:
-```sql
-SELECT id, status, error FROM shopify_api_queue
-WHERE source_agent = 'body-html-verify-test';
-```
-
-Expected: `status='dead'`, `error` starts with `Rule #6 body_html violation`. Then **DELETE the test job** so it doesn't clutter the queue.
-
-## Safety
-
-- **Read-only on code and live Shopify** — no writes to live products
-- The only write is the synthetic test job in Part 3, which you MUST delete afterward
-- **DO NOT** restart any PM2 services — Yuri can't test service restarts safely
-- Save any working scripts to `/root/DW-Agents/scripts/yolo-verify-rule6.js`
-
-## Deliverables
-
-Terse markdown report, under 500 words:
-
-**Part 1 — Static:**
-- product-agent `buildDescription`: PASS/FAIL with specific violations if any
-- update-agent `buildBodyHtml`: PASS/FAIL with specific violations if any
-- Quinn validator presence: CONFIRMED / MISSING (which pieces)
-
-**Part 2 — Live:**
-- Sample size, clean/short/empty/violation counts
-- Per-vendor breakdown if any violations
-- 3 sample clean bodies and 3 sample violations (if any) with product IDs
-
-**Part 3 — Quinn gate live test:**
-- Test job id, final status, error text
-- Confirmation that test job was deleted
diff --git a/tasks/done/05_05_disk-cleanup.md b/tasks/done/05_05_disk-cleanup.md
deleted file mode 100644
index 117a03d..0000000
--- a/tasks/done/05_05_disk-cleanup.md
+++ /dev/null
@@ -1 +0,0 @@
-Check disk usage with df -h /root. Find large log files over 100MB using find command. Find old PM2 logs and list the 20 largest in ~/.pm2/logs/. Truncate any PM2 logs over 50MB. Report space recovered to /root/DW-Agents/logs/disk-cleanup.log.
\ No newline at end of file
diff --git a/tasks/done/05_05_dupenny-sancar-push-check.md b/tasks/done/05_05_dupenny-sancar-push-check.md
deleted file mode 100644
index f41b066..0000000
--- a/tasks/done/05_05_dupenny-sancar-push-check.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Check dupenny and sancar vendor tables. For each: count total products, count products with shopify_id (already pushed), count products without shopify_id (need push). Check if they pass FULLPRODUCT gates: must have image_url, width, mfr_sku. Report readiness to /root/DW-Agents/logs/dupenny-sancar-readiness.json. Do NOT push to Shopify - just report readiness.
\ No newline at end of file
diff --git a/tasks/done/05_05_enrich-schumacher.md b/tasks/done/05_05_enrich-schumacher.md
deleted file mode 100644
index fb859fa..0000000
--- a/tasks/done/05_05_enrich-schumacher.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-schumacher-width.js
-
-Enrich Schumacher catalog with width.
\ No newline at end of file
diff --git a/tasks/done/05_05_tag-enrichment.md b/tasks/done/05_05_tag-enrichment.md
deleted file mode 100644
index 22e33ad..0000000
--- a/tasks/done/05_05_tag-enrichment.md
+++ /dev/null
@@ -1,31 +0,0 @@
-Hollywood Shopify product tag enrichment.
-
-Shopify: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-API: 2024-01
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-For all ACTIVE (non-discontinued) Hollywood products on Shopify:
-
-1. Query the DB for all active products with their specs:
- SELECT shopify_product_id, pattern_name, color_name, collection, product_type, material, fire_rating, application, features, design, width, match_type
- FROM hollywood_catalog
- WHERE mfr_sku LIKE 'X%' AND discontinued = false AND shopify_product_id IS NOT NULL;
-
-2. Build tags for each product from available data:
- - Collection name (if not empty)
- - Material type (e.g. 'Type II Vinyl', 'Acoustical')
- - Fire rating (e.g. 'ASTM E84 Class A', 'ACT Compliant')
- - Application (e.g. 'Commercial', 'Hospitality')
- - Design style from pattern_name (e.g. 'Grasscloth', 'Texture', 'Stripe')
- - Width category
- - 'Hollywood Wallcoverings' vendor tag
- - Do NOT include any Momentum/MDC references in tags
-
-3. Update each Shopify product with the enriched tags via PUT /products/{id}.json
- - MERGE with existing tags, do not replace
- - Rate limit: 0.5s sleep between API calls
-
-4. Report: how many products updated, sample of tag sets generated.
-
-IMPORTANT: This is a PRIVATE LABEL brand. Never mention Momentum, MDC, or real manufacturer in tags.
\ No newline at end of file
diff --git a/tasks/done/05_05_thibaut-monitor.md b/tasks/done/05_05_thibaut-monitor.md
deleted file mode 100644
index 53e7054..0000000
--- a/tasks/done/05_05_thibaut-monitor.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Check Thibaut scraper progress.
-1. ps aux | grep thibaut | grep -v grep
-2. tail -30 /root/DW-Agents/logs/thibaut-full-scrape.log
-3. Check DB: timeout 10 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width, COUNT(CASE WHEN image_url IS NOT NULL THEN 1 END) as has_image FROM thibaut_catalog;"
-4. Report status
\ No newline at end of file
diff --git a/tasks/done/05_ai-enrich-remaining.md b/tasks/done/05_ai-enrich-remaining.md
deleted file mode 100644
index fa19224..0000000
--- a/tasks/done/05_ai-enrich-remaining.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# AI Enrich — Remaining Vendors (Black Edition 195, PRL 186, Zinc 100, Kirkby 76)
-
-Run Phase 3 enrichment for these 4 smaller vendors. Process all of them in sequence.
-
-For each vendor_code in [black_edition, PRL, zinc_textile, kirkby_design]:
-```bash
-cd /root/DW-Agents/full-monte
-node full-monte-batch.js --vendor {vendor} --phase 3 --limit 200
-```
-Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/05_blog-recently-viewed-feature.md b/tasks/done/05_blog-recently-viewed-feature.md
deleted file mode 100644
index 99397f4..0000000
--- a/tasks/done/05_blog-recently-viewed-feature.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Blog Post: Building a Recently Viewed Carousel with 6 AI Agents in Parallel
-
-## Ralph Clarifying Questions (Self-Check Before Executing)
-
-1. **Has a similar post been published today?**
- Check: `ls -lt /root/Projects/goodquestion-ai/src/content/posts/ | head -5`
- If "recently viewed" or "carousel" already exists, skip this task.
-
-2. **Is the blog agent running?**
- Check: `pm2 status blog-agent`
-
-## What To Do
-
-Write and publish a blog post about building the Recently Viewed product carousel using 6 parallel AI agents.
-
-### Post Content Direction:
-- **Title angle**: "6 AI Agents Built a Shopify Feature in 12 Minutes — Here's the Playbook"
-- **Hook**: One person, 6 parallel agents, a production Shopify feature from zero to deployed
-- **Key beats**:
- 1. The Ralph pattern: break features into user stories, each small enough for one agent
- 2. Parallel execution: design + data layer built simultaneously
- 3. Cross-file validation: test agent caught 4 integration bugs between files built by different agents
- 4. Production deployment via Shopify Theme Asset API — no manual upload
- 5. SEO built-in: JSON-LD structured data, semantic HTML, lazy loading
-- **Takeaway**: Small business owners can ship features that used to require a team
-- **End with**: Board seat / advisory mention per MEMORY.md rules
-
-### CRITICAL — Sanitization Rules:
-- NO mention of wallcoverings, wallpaper, or the industry
-- NO mention of Designer Wallcoverings, DW, or specific products
-- NO vendor names, pricing, SKUs, or internal details
-- NO IP addresses, ports, credentials
-- Frame as "e-commerce store" or "Shopify store" generically
-- Run through sanitizer: `node /root/DW-Agents/blog-agent/sanitizer.js`
-
-### Publishing Pipeline:
-1. Write the post markdown
-2. Run through sanitizer
-3. Save to `/root/Projects/goodquestion-ai/src/content/posts/`
-4. Build Astro: `cd /root/Projects/goodquestion-ai && npm run build`
-5. Tweet via `/root/Projects/goodquestion-ai/scripts/tweet.cjs`
-6. Bluesky via `/root/Projects/goodquestion-ai/scripts/bsky-post.cjs`
-7. Slack notify
-
-## Credentials
-- Blog dir: /root/Projects/goodquestion-ai/
-- Tweet script: scripts/tweet.cjs
-- Bluesky script: scripts/bsky-post.cjs
-- Sanitizer: /root/DW-Agents/blog-agent/sanitizer.js
diff --git a/tasks/done/05_build-scraper-zoffany.md b/tasks/done/05_build-scraper-zoffany.md
deleted file mode 100644
index 34e00b3..0000000
--- a/tasks/done/05_build-scraper-zoffany.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Build a vendor scraper for Zoffany wallcoverings.
-
-Directory: /root/DW-Agents/vendor-scrapers/
-Output file: scrape-zoffany.js
-DB table: zoffany_catalog
-
-Use scraper-utils.js upsertProduct() pattern. Follow scrape-harlequin.js as a template (same parent company - Sanderson Design Group).
-
-1. Visit zoffany.com and find their wallpaper/wallcovering products
-2. Extract: pattern_name, collection, color, mfr_sku, image_url, product_url, all_images, description, specs
-3. Sanderson Design Group brand - check for shared API patterns with Harlequin
-4. Upsert to zoffany_catalog using scraper-utils
-5. Test with a small batch first (10 products)
-
-CRITICAL: Use mfr_sku field. Include all_images array.
\ No newline at end of file
diff --git a/tasks/done/05_check-running-scrapers.md b/tasks/done/05_check-running-scrapers.md
deleted file mode 100644
index 01ff916..0000000
--- a/tasks/done/05_check-running-scrapers.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Check all running vendor scrapers and report their progress.
-Run: ps aux | grep "node scrape-" | grep -v grep
-For each running scraper, check its log file in /tmp/ for the latest progress line.
-Report a summary of what's running and estimated completion.
\ No newline at end of file
diff --git a/tasks/done/05_china-seas-interior-tagger.md b/tasks/done/05_china-seas-interior-tagger.md
deleted file mode 100644
index e91e213..0000000
--- a/tasks/done/05_china-seas-interior-tagger.md
+++ /dev/null
@@ -1,27 +0,0 @@
-## Run Interior Design Tagger on China Seas Wallpapers
-
-Analyze all China Seas wallpaper products and tag them with interior design terminology.
-
-For each wallpaper product on Shopify (china_seas_catalog WHERE shopify_product_id IS NOT NULL AND mfr_sku LIKE '%WP%'):
-
-Use Gemini 2.0 Flash (key: ${GOOGLE_API_KEY}) to analyze the product image and generate tags.
-
-Prompt for Gemini:
-"Analyze this wallpaper image as an interior designer. Provide:
-1. Style period (e.g., Mid-Century Modern, Art Deco, Contemporary, Traditional, Chinoiserie, Botanical)
-2. Pattern type (e.g., Floral, Geometric, Damask, Toile, Animal Print, Stripe, Abstract)
-3. Color family (e.g., Warm Neutrals, Cool Blues, Earth Tones, Jewel Tones)
-4. Recommended rooms (e.g., Living Room, Bedroom, Dining Room, Powder Room, Hallway)
-5. Design mood (e.g., Elegant, Playful, Dramatic, Serene, Bold)
-Return as JSON: {style, pattern, colorFamily, rooms, mood}"
-
-Save results to the `design` column and update Shopify tags to include the new design tags.
-
-Use Shopify REST API to update tags:
-PUT /admin/api/2024-01/products/{id}.json
-{"product":{"id":ID,"tags":"existing,tags,New Style Tag,New Pattern Tag"}}
-
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Process first 100 products. Report results.
diff --git a/tasks/done/05_collection-image-refresh.md b/tasks/done/05_collection-image-refresh.md
deleted file mode 100644
index 2cbd7b1..0000000
--- a/tasks/done/05_collection-image-refresh.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Collection Image Refresh — Update to Room Settings
-
-**Priority**: P2
-**Estimated time**: ~2 hours
-**Type**: Store optimization
-
-## Description
-Update collection cover images to use room setting photos instead of product swatches.
-
-## Steps
-1. Get all smart collections: `GET /admin/api/2024-01/smart_collections.json?limit=250`
-2. For each collection, check if image is a swatch (small, square, no room context)
-3. Find a room setting image from a product in that collection:
- - Get products: `GET /collections/{handle}/products.json?limit=10`
- - Look for product images at position 2+ (room settings) that are >= 800px wide
-4. Update collection image: `PUT /admin/api/2024-01/smart_collections/{id}.json`
-5. Use Gemini vision to verify the replacement image shows a room (not just a swatch)
-
-## Rules
-- Only update collections with swatch-only images
-- Prefer room settings with wallcovering clearly visible
-- Skip collections with < 5 products
-
-## Success criteria
-- All major collections have room setting cover images
-- No collection shows a raw swatch as its cover
diff --git a/tasks/done/05_crawl-report-for-steve.md b/tasks/done/05_crawl-report-for-steve.md
deleted file mode 100644
index 02e6765..0000000
--- a/tasks/done/05_crawl-report-for-steve.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Morning Report for Steve — Crawl Results Summary
-
-Generate a comprehensive report of ALL overnight crawl activity. Steve is waking up and wants to see results.
-
-## Check ALL catalog tables
-```sql
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
-SELECT table_name, n_live_tup as row_count
-FROM pg_stat_user_tables
-WHERE table_name LIKE '%_catalog'
-ORDER BY n_live_tup DESC;"
-```
-
-## For each catalog, report:
-- Total products
-- Products with images (all_images not empty)
-- Products with width
-- Products with repeat_v
-- Products with material
-- Products with fire_rating
-- Price range
-
-## Also check:
-1. Are Sara, Nadia, Marco still running or completed?
-2. Any PM2 agents that crashed overnight?
-3. Disk space and memory usage
-4. Any errors in logs?
-
-## Format as a clean summary table that's easy to scan.
-
-Save the report to `/root/DW-Agents/logs/overnight-crawl-report.md`
-
-
-## CRITICAL RULE
-**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY. Downloading product data FROM other vendors' Shopify stores for catalog data is fine and expected. product-scheduler and schedule-engine are STOPPED intentionally. Do NOT restart them.**
-
-
-## Slack Notification — REQUIRED
-When this task is complete, send a Slack message to Steve with the results summary:
-```bash
-curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "${SLACK_WEBHOOK_URL}"
-```
-Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/05_db-maintenance.md b/tasks/done/05_db-maintenance.md
deleted file mode 100644
index 01f1a6a..0000000
--- a/tasks/done/05_db-maintenance.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Database Maintenance
-
-```sql
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
-VACUUM ANALYZE vendor_catalog;
-VACUUM ANALYZE shopify_api_queue;
-VACUUM ANALYZE enrichment_tracking;
-DELETE FROM shopify_api_queue WHERE status = 'dead' AND completed_at < NOW() - INTERVAL '7 days';
-DELETE FROM shopify_api_queue WHERE status = 'done' AND completed_at < NOW() - INTERVAL '3 days';
-"
-```
-Report table sizes and DB size.
diff --git a/tasks/done/05_enrichment-gap-report.md b/tasks/done/05_enrichment-gap-report.md
deleted file mode 100644
index 5d47de0..0000000
--- a/tasks/done/05_enrichment-gap-report.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Enrichment Gap Report
-Query enrichment_tracking in dw_unified DB to find vendors with the biggest gaps between phases. For each vendor, count products at each phase. Output a report to /root/DW-Agents/logs/enrichment-gaps.json with vendor_code, total, and phase counts. Sort by total DESC. This helps prioritize which vendors need attention.
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
diff --git a/tasks/done/05_final-audit-report.md b/tasks/done/05_final-audit-report.md
deleted file mode 100644
index 90c6c48..0000000
--- a/tasks/done/05_final-audit-report.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Final Audit Report — Full Catalog Status
-
-Generate a comprehensive audit of ALL catalog tables and send to Steve via Slack.
-
-## Run this audit:
-```sql
-PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
-SELECT relname as catalog, n_live_tup as products
-FROM pg_stat_user_tables
-WHERE relname LIKE '%_catalog' AND n_live_tup > 0
-ORDER BY n_live_tup DESC;"
-```
-
-## For the top 20 catalogs, check spec fill rates:
-For each table, run:
-```sql
-SELECT
- COUNT(*) as total,
- COUNT(NULLIF(image_url,'')) as has_image,
- COUNT(NULLIF(all_images,'')) as has_all_images,
- COUNT(NULLIF(width,'')) as has_width,
- COUNT(NULLIF(repeat_v,'')) as has_repeat,
- COUNT(NULLIF(material,'')) as has_material,
- COUNT(NULLIF(fire_rating,'')) as has_fire_rating,
- COUNT(NULLIF(body_html,'')) as has_body_html
-FROM xxx_catalog;
-```
-
-## Save report to:
-`/root/DW-Agents/logs/morning-audit-report.md`
-
-## Slack Notification — REQUIRED (send FULL summary)
-```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"MORNING AUDIT COMPLETE\n\nTotal catalogs: [X]\nTotal products: [Y]\nWith images: [Z]%\nWith specs: [W]%\nWith fire ratings: [F]%\n\nFull report: /root/DW-Agents/logs/morning-audit-report.md"}' "${SLACK_WEBHOOK_URL}"
-```
-
-## CRITICAL RULE
-**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
diff --git a/tasks/done/05_git-commit-session107.md b/tasks/done/05_git-commit-session107.md
deleted file mode 100644
index 9e7e907..0000000
--- a/tasks/done/05_git-commit-session107.md
+++ /dev/null
@@ -1,22 +0,0 @@
-## Git Commit — Session #107 Changes
-
-Commit all changes from Session #107 to the DW-Agents repo.
-
-### Steps:
-1. `cd /root/DW-Agents`
-2. `git status` — review changes
-3. Stage new/modified files (be specific, don't use `git add .`):
- - `vendor-scrapers/scrape-york-images.js` — new York image scraper
- - `vendor-scrapers/scrape-brewster-images-v2.js` — new Brewster v2 scraper
- - `scripts/fix-all-sample-variants.sh` — batch sample variant fix
- - `scripts/cleanup-duplicate-wallpaper-collections.js` — collection dedup
- - `DW-TASK-LEDGER.md` — updated task ledger
- - Any other modified files from this session
-4. Commit with message: "Session #107: York/Brewster scrapers, sample variant fixes, collection cleanup"
-5. Push to GitHub: `git push origin`
-
-### Do NOT commit:
-- .env files
-- Node modules
-- Log files
-- Credentials
diff --git a/tasks/done/05_grahambrown-puppeteer-width.md b/tasks/done/05_grahambrown-puppeteer-width.md
deleted file mode 100644
index 5273deb..0000000
--- a/tasks/done/05_grahambrown-puppeteer-width.md
+++ /dev/null
@@ -1,26 +0,0 @@
-Build Puppeteer scraper for Graham & Brown width extraction — 1,493 missing width, 784 have product URLs on grahambrown.com.
-
-grahambrown.com is a Next.js SPA — specs render client-side. Plain HTTP scraping gets scaffolding HTML with no specs. Need Puppeteer.
-
-Steps:
-1. Check if Puppeteer is installed: `which puppeteer` or `node -e "require('puppeteer')"`
-2. If not: `cd /root/DW-Agents/vendor-scrapers && npm install puppeteer`
-3. Get a sample URL: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT product_url FROM graham_brown_catalog WHERE width IS NULL AND product_url IS NOT NULL LIMIT 1;"`
-4. Test with Puppeteer — visit the URL, wait for page load, extract specs:
-```javascript
-const browser = await puppeteer.launch({headless: true, args: ['--no-sandbox']});
-const page = await browser.newPage();
-await page.goto(url, {waitUntil: 'networkidle0', timeout: 30000});
-// Look for specs in rendered DOM
-const specs = await page.evaluate(() => document.body.innerText);
-// Extract width from specs text
-```
-5. Identify the CSS selector or text pattern for width on G&B product pages
-6. Build the enrichment scraper: `/root/DW-Agents/vendor-scrapers/enrich-grahambrown-width-puppeteer.js`
-7. Run with batch of 50 first, then scale up
-8. Also check if G&B has a hidden API (check __NEXT_DATA__ in page source)
-9. Report: how many widths extracted
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: graham_brown_catalog
-Rate limit: 2 seconds between page loads (be polite to their server)
diff --git a/tasks/done/05_no-image-product-audit.md b/tasks/done/05_no-image-product-audit.md
deleted file mode 100644
index ebbe0c7..0000000
--- a/tasks/done/05_no-image-product-audit.md
+++ /dev/null
@@ -1,11 +0,0 @@
-Audit active Shopify products that have no images or broken images.
-
-Steps:
-1. Use the Shopify GraphQL API (token: <redacted:SHOPIFY_ADMIN_TOKEN>, store: designer-laboratory-sandbox.myshopify.com) to find active products with 0 images
-2. Query: products(first: 250, query: "status:active") and check featuredImage is null
-3. Page through all results (there are ~42K active products)
-4. For each imageless product, check the catalog tables in PostgreSQL for an image_url
-5. If catalog has an image, queue it for image upload via the product update API
-6. Report: total active products with no image, how many have catalog images available, how many are truly imageless
-
-Working directory: /root/DW-Agents
diff --git a/tasks/done/05_push-color-metafields.md b/tasks/done/05_push-color-metafields.md
deleted file mode 100644
index c39e05b..0000000
--- a/tasks/done/05_push-color-metafields.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Push AI Color Data to Shopify Metafields
-
-Find products that have ai_colors + shopify_product_id but haven't had color metafields pushed yet.
-
-For each product:
-1. Parse ai_colors JSON for dominant color hex and full color list
-2. Queue via shopify_api_queue (INSERT INTO) using GraphQL metafieldsSet:
- - `custom.color_hex` = dominant hex code
- - `custom.color_details` = full JSON array [{name, hex, percentage}]
-3. Use method POST, endpoint /admin/api/2024-01/graphql.json
-
-Token alias: product | Source agent: color-metafield-push | Priority: 2
-Limit: 200 products per run. Use 100ms delay between INSERTs.
-
-DB connection: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
diff --git a/tasks/done/05_push-specs-york-contract.md b/tasks/done/05_push-specs-york-contract.md
deleted file mode 100644
index b2f933c..0000000
--- a/tasks/done/05_push-specs-york-contract.md
+++ /dev/null
@@ -1,9 +0,0 @@
-## Push Specs to Shopify body_html — York Contract (3,284 products)
-
-York Contract has 3,284 products linked to Shopify, 98% width coverage.
-
-### Steps:
-1. Run: python3 /root/DW-Agents/scripts/push-specs-vendor.py york_contract_catalog --limit 4000
-2. Monitor and report results
-
-Same rate limiting caveats as PJ task above.
\ No newline at end of file
diff --git a/tasks/done/05_push-wolfgordon.md b/tasks/done/05_push-wolfgordon.md
deleted file mode 100644
index 1a237bb..0000000
--- a/tasks/done/05_push-wolfgordon.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Push Wolf Gordon to Shopify — Full Monte Phase 8
-
-3,841 push-ready Wolf Gordon products (commercial vendor, just completed enrichment).
-
-### Product Rules:
-- Vendor display name: "Wolf Gordon"
-- Commercial vendor — fire_rating: "Class A Fire Rated : Passes ASTM E84"
-- Title format: {PatternName} - {Color} Wallcovering | Wolf Gordon
-- Body HTML: 3-sentence ai_description ONLY. NO specs, NO tables.
-- ALL specs in metafields: custom.width, custom.fire_rating, custom.material, custom.pattern_name, custom.color, custom.manufacturer_sku, custom.color_hex, custom.color_details, dwc.ai_generated_description
-- MFR SKU as tag
-- Sample variant: $4.25, weight 0
-- Roll variant: $0 (commercial = quote model), weight 2 lbs
-- inventory_quantity: 2026
-
-### Steps:
-1. Query vendor_catalog for vendor_code='wolf_gordon' with on_shopify=false, has image+width+mfr_sku+ai_colors
-2. Queue through Quinn (GraphQL productCreate + metafieldsSet, batched 25)
-3. Mark on_shopify=true, set shopify_product_id
-4. Update enrichment_tracking phase8
-
-### IMPORTANT:
-- Launch Pad gate — first 25 for Steve's review
diff --git a/tasks/done/05_roberto-cavalli-enrich.md b/tasks/done/05_roberto-cavalli-enrich.md
deleted file mode 100644
index a3b97cd..0000000
--- a/tasks/done/05_roberto-cavalli-enrich.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## Roberto Cavalli — Scrape for Width & Repeat
-
-81 products in `roberto_cavalli_catalog`. Missing width and repeat. Have product_url. Small catalog, should be quick.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check schema: `SELECT column_name FROM information_schema.columns WHERE table_name = 'roberto_cavalli_catalog' ORDER BY ordinal_position;`
-3. Add missing columns if needed (width, repeat_v, repeat_h, material, fire_rating).
-4. Sample: `SELECT product_url FROM roberto_cavalli_catalog WHERE product_url IS NOT NULL LIMIT 2`
-5. Fetch sample page and identify specs in the HTML.
-6. Write enrichment script at `/root/DW-Agents/vendor-scrapers/enrich-roberto-cavalli.js`
-7. Run: `timeout 300 node enrich-roberto-cavalli.js`
-8. Report how many enriched.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/05_sales-channel-audit.md b/tasks/done/05_sales-channel-audit.md
deleted file mode 100644
index 149f210..0000000
--- a/tasks/done/05_sales-channel-audit.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Sales Channel Publication Audit
-
-Per DW hard rule: every active Shopify product must be published to ALL installed sales channels. Find products that are NOT published to all channels.
-
-### Task:
-1. Source env from /root/.env (SHOPIFY_ADMIN_TOKEN, SHOPIFY_STORE).
-2. First, list all publications (sales channels):
-```graphql
-{ publications(first: 20) { nodes { id name } } }
-```
-3. Then sample 500 random active products and check their publication status:
-```graphql
-{ products(first: 50, query: "status:active") { nodes { id handle vendor publishedOnPublication(publicationId: "gid://shopify/Publication/XXX") } } }
-```
-4. Or use `resourcePublications` on each product to see which channels it's on.
-5. Report any products NOT on all channels, grouped by vendor.
-
-### Output:
-Report only — DO NOT publish anything. Write results to `/root/DW-Agents/logs/yolo-sales-channel-audit.log` with timestamp. Include: total channels found, sample size, products missing from 1+ channels.
diff --git a/tasks/done/05_schumacher-osborne-enrich.md b/tasks/done/05_schumacher-osborne-enrich.md
deleted file mode 100644
index 0267b18..0000000
--- a/tasks/done/05_schumacher-osborne-enrich.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## Schumacher + Osborne & Little Image Enrichment
-
-- Schumacher: 5,182 products, 4,474 images (86.3%) — 708 missing
-- Osborne & Little: 787 products, 651 images (82.7%) — 136 missing
-
-### Steps:
-1. Check if products have source URLs:
- ```sql
- SELECT COUNT(*) FILTER (WHERE (image_url IS NULL OR image_url = '') AND product_url IS NOT NULL AND product_url != '') as has_url
- FROM schumacher_catalog;
- ```
- Do same for osborne_catalog.
-
-2. Check for existing enrichment scrapers:
- ```bash
- ls /root/DW-Agents/vendor-scrapers/*schumacher* /root/DW-Agents/vendor-scrapers/*osborne*
- ```
-
-3. If scrapers exist, run them. If not, check if products are on fschumacher.com or osborneandlittle.com
-
-4. For Schumacher, try their Shopify-based site: append .json to product URLs
-
-5. Update VCC: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-
-### CRITICAL: Do NOT push to Shopify.
diff --git a/tasks/done/05_social-posts.md b/tasks/done/05_social-posts.md
deleted file mode 100644
index b2028c3..0000000
--- a/tasks/done/05_social-posts.md
+++ /dev/null
@@ -1,8 +0,0 @@
-## Post to Social Media — Agent Abrams
-
-Post about autonomous AI infrastructure to X/Twitter and Bluesky.
-- X: Use /root/Projects/goodquestion-ai/scripts/tweet.cjs (check if billing works first)
-- Bluesky: Use /root/Projects/goodquestion-ai/scripts/bsky-post.cjs
-- Topic: How one person + AI runs production infrastructure that processes thousands of products autonomously
-- Sanitize per MEMORY.md rules
-- If X returns 402, skip it and note billing needs renewal
diff --git a/tasks/done/05_thibaut-discontinued-archive.md b/tasks/done/05_thibaut-discontinued-archive.md
deleted file mode 100644
index 1418481..0000000
--- a/tasks/done/05_thibaut-discontinued-archive.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Thibaut Discontinued Products Archive
-
-Session #102 noted 302 Thibaut products returning 301 redirects (discontinued). These need to be archived on Shopify.
-
-### Tasks:
-1. Query `thibaut_catalog` for products marked as discontinued or where the product URL returns a 301/404
-2. Cross-reference with Shopify to find which ones are still active
-3. Archive discontinued products on Shopify (set status to 'archived')
-4. Update `thibaut_catalog` to mark them as `discontinued = true`
-5. Report: how many products were archived, total remaining active
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-Store: `designer-laboratory-sandbox.myshopify.com`
-
-Rate limit: 2 req/s Shopify API. Use delays between requests.
-Do NOT delete products — only archive (status: 'archived').
diff --git a/tasks/done/05_thibaut-scrape-monitor.md b/tasks/done/05_thibaut-scrape-monitor.md
deleted file mode 100644
index 5fe037c..0000000
--- a/tasks/done/05_thibaut-scrape-monitor.md
+++ /dev/null
@@ -1,19 +0,0 @@
-Check on the running Thibaut scraper and report progress.
-
-Steps:
-1. Check if the scraper is still running: ps aux | grep thibaut | grep -v grep
-2. Check the log: tail -30 /root/DW-Agents/logs/thibaut-full-scrape.log
-3. Check the database for Thibaut product counts:
- timeout 10 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "
- SELECT COUNT(*) as total,
- COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width,
- COUNT(CASE WHEN image_url IS NOT NULL THEN 1 END) as has_image,
- COUNT(CASE WHEN mfr_sku IS NOT NULL THEN 1 END) as has_sku
- FROM thibaut_catalog;"
-
-4. If the scraper finished or crashed:
- - Check exit status in the log
- - Report final counts
- - If crashed, note the error
-
-5. Report: scraper status (running/finished/crashed), product counts, enrichment coverage
diff --git a/tasks/done/05_upload-youtube-unlist-old.md b/tasks/done/05_upload-youtube-unlist-old.md
deleted file mode 100644
index 2286dd0..0000000
--- a/tasks/done/05_upload-youtube-unlist-old.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Upload New Videos to YouTube + Unlist Old Ones
-
-Working directory: /root/Projects/goodquestion-ai/
-
-## Step 1: Unlist ALL old videos
-Use the youtube API scripts to unlist these old videos:
-- ddX59d79z7A (Parallel Agents - old)
-- jZ5LCtiNMa8 (Command Center - old)
-- 9a5HAA5SYa8 (Autonomous Infra - old)
-- icNSntDDH_E (Blog Pipeline - old)
-- dkNB_4cThoM (Data Pipeline - old)
-- 6iCCuuk7dDM (Silent video 1)
-- mT2LZBrajas (Silent video 2)
-- OZY5D0NgQ_g (Silent video 3)
-- 5un2PCEhtmw (Silent video 4)
-
-Use: node scripts/youtube-privatize-all.cjs or equivalent to set privacy to "unlisted"
-
-## Step 2: Upload all 18 new LANDSCAPE videos
-Use: node scripts/youtube-upload.cjs <path> --title "Title" --description "Desc" --tags "tags"
-
-Upload each landscape video from /root/Projects/goodquestion-ai/videos/ with:
-- Professional titles (not jargon)
-- Description includes goodquestion.ai link and social links
-- Tags: "AI automation", "Claude Code", "Agent Abrams", "founder", "solo developer"
-- Privacy: public
-
-## Step 3: Report
-List all uploaded video IDs with their titles and URLs.
-
-## SANITIZATION
-NEVER include in titles, descriptions, or tags:
-- wallcoverings, wallpaper
-- Vendor names
-- Designer Wallcoverings, DW
-- Internal codenames
-- IP addresses or ports
diff --git a/tasks/done/05_vcc-dashboard-health.md b/tasks/done/05_vcc-dashboard-health.md
deleted file mode 100644
index 5a3b887..0000000
--- a/tasks/done/05_vcc-dashboard-health.md
+++ /dev/null
@@ -1,22 +0,0 @@
-## VCC Dashboard Health Check
-
-Verify the Vendor Command Center (port 9660) is displaying accurate data.
-
-### Steps:
-1. Hit VCC health: `curl -s -u admin:DWSecure2024! http://localhost:9660/health`
-2. Get vendor count summary: `curl -s -u admin:DWSecure2024! http://localhost:9660/api/vendors/summary`
-3. Check for any vendors with wildly wrong counts (compare DB vs VCC):
- ```sql
- SELECT vendor_name, total_products, products_with_images,
- CASE WHEN total_products > 0 THEN ROUND(products_with_images::numeric / total_products * 100, 1) ELSE 0 END as img_pct
- FROM vendor_registry
- WHERE total_products > 0
- ORDER BY total_products DESC
- LIMIT 30;
- ```
-4. Force a full sync: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-5. Report any discrepancies
-
-### CRITICAL RULES:
-- Do NOT modify Shopify data
-- Only sync PostgreSQL counts and report
diff --git a/tasks/done/06_06_brands-page-pills-fix.md b/tasks/done/06_06_brands-page-pills-fix.md
deleted file mode 100644
index bbfea71..0000000
--- a/tasks/done/06_06_brands-page-pills-fix.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Fix the brands page on the Shopify store (designerwallcoverings.com). The collection pills/badges on each brand card need to be:
-1. All the SAME SIZE (uniform width/height)
-2. Positioned at the TOP CENTER of each card
-
-Access the theme:
-- Token with theme access: <redacted:SHOPIFY_ORDERS_TOKEN>
-- Live theme ID: 141654589491
-- Dev theme ID: 141935312947 (push here first)
-
-Check the collection-list section and snippets for the card template. Add CSS to:
-- Make all pills/badges uniform size: min-width, fixed height, text-align center
-- Position at top center of each card
-- Use the DW warm brown style (#8b7355)
-
-Push CSS changes to the dev theme first.
\ No newline at end of file
diff --git a/tasks/done/06_06_brewster-specs-fix.md b/tasks/done/06_06_brewster-specs-fix.md
deleted file mode 100644
index 3ece610..0000000
--- a/tasks/done/06_06_brewster-specs-fix.md
+++ /dev/null
@@ -1 +0,0 @@
-The Brewster specs enricher at /root/DW-Agents/vendor-scrapers/enrich-brewster-specs.js was not detecting specs because Brewster puts them in ul elements, not tables. Read the enricher script. Check if it has been updated to handle ul-based specs. If not, check the Brewster product pages to see the actual HTML structure of specs (use the quickview API: GET /quickviewdata?productId={nopId}). Report findings and what needs to change to /root/DW-Agents/logs/brewster-specs-analysis.log
\ No newline at end of file
diff --git a/tasks/done/06_06_enrich-thibaut.md b/tasks/done/06_06_enrich-thibaut.md
deleted file mode 100644
index 6ea94ba..0000000
--- a/tasks/done/06_06_enrich-thibaut.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-thibaut-specs.js
-
-Enrich Thibaut catalog with all available specs.
\ No newline at end of file
diff --git a/tasks/done/06_06_vcc-sync.md b/tasks/done/06_06_vcc-sync.md
deleted file mode 100644
index 7dd9171..0000000
--- a/tasks/done/06_06_vcc-sync.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Sync VCC vendor stats.
-1. curl -s -u admin:DWSecure2024! http://127.0.0.1:9660/api/health
-2. curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:9660/api/vendors/refresh-stats
-3. Wait 10s then get summary: curl -s -u admin:DWSecure2024! http://127.0.0.1:9660/api/vendors/summary
-4. Report totals
\ No newline at end of file
diff --git a/tasks/done/06_build-scraper-donghia.md b/tasks/done/06_build-scraper-donghia.md
deleted file mode 100644
index d2f0dca..0000000
--- a/tasks/done/06_build-scraper-donghia.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Build a vendor scraper for Donghia wallcoverings.
-
-Directory: /root/DW-Agents/vendor-scrapers/
-Output file: scrape-donghia.js
-DB table: donghia_catalog
-
-Use scraper-utils.js upsertProduct() pattern. Follow scrape-scalamandre.js as a template (similar luxury brand structure).
-
-1. Visit donghia.com and find their wallcovering products
-2. Extract: pattern_name, collection, color, mfr_sku, image_url, product_url, all_images, description, specs
-3. Donghia is a Kravet brand - may share API patterns
-4. Upsert to donghia_catalog using scraper-utils
-5. Test with small batch first
-
-CRITICAL: Use mfr_sku field. Include all_images array.
\ No newline at end of file
diff --git a/tasks/done/06_china-seas-verify-all.md b/tasks/done/06_china-seas-verify-all.md
deleted file mode 100644
index 7cf8fb4..0000000
--- a/tasks/done/06_china-seas-verify-all.md
+++ /dev/null
@@ -1,35 +0,0 @@
-## Verify China Seas Pipeline — Ralph Checkpoint
-
-This is the verification step. Check ALL prior tasks completed successfully.
-
-Run these verification queries against PostgreSQL (postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified):
-
-```sql
--- Overall stats
-SELECT
- COUNT(*) as total,
- COUNT(CASE WHEN width IS NOT NULL AND width != '' THEN 1 END) as has_width,
- COUNT(CASE WHEN pattern_repeat IS NOT NULL AND pattern_repeat != '' THEN 1 END) as has_repeat,
- COUNT(CASE WHEN color_hex IS NOT NULL AND color_hex != '' THEN 1 END) as has_hex,
- COUNT(CASE WHEN rooms IS NOT NULL AND rooms != '' AND rooms != '[]' THEN 1 END) as has_rooms,
- COUNT(CASE WHEN design IS NOT NULL AND design != '' THEN 1 END) as has_design_tags
-FROM china_seas_catalog
-WHERE mfr_sku LIKE '%WP%';
-```
-
-Also verify Shopify metafields by spot-checking 3 random products:
-```bash
-curl -s "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/products/{ID}/metafields.json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" | python3 -m json.tool
-```
-
-Report a summary table comparing BEFORE vs AFTER counts for each metric.
-
-BEFORE counts:
-- Width: 222
-- Repeat: 215
-- Hex: 11
-- Rooms: 0
-- Design tags: 0
-
-If any metric didn't improve, flag it as NEEDS ATTENTION.
diff --git a/tasks/done/06_collections-cleanup-phase1.md b/tasks/done/06_collections-cleanup-phase1.md
deleted file mode 100644
index a44c7a7..0000000
--- a/tasks/done/06_collections-cleanup-phase1.md
+++ /dev/null
@@ -1,31 +0,0 @@
-COLLECTIONS CLEANUP — Phase 1: Delete empty, merge duplicates, fix excluded
-
-## Step 1: Delete 57 empty collections (0 products)
-```bash
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified"
-```
-Use Shopify GraphQL to delete all collections with 0 products:
-```graphql
-mutation { collectionDelete(input: {id: "gid://shopify/Collection/ID"}) { deletedCollectionId } }
-```
-Shopify store: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-Get the list first: fetch all collections via `/api/collections` on port 9501, filter for productCount === 0, then delete each one. Rate limit 0.5s.
-
-## Step 2: Merge 32 duplicate-titled collections
-For each duplicate title, keep the one with the MOST products. Delete the others.
-Special cases:
-- "Emma J Shipley Collection" x3 — keep the one with 179 products
-- "Modern Originals" x2 — keep the one with 30,584
-- "Geometric Wallpaper" x2 — keep 13,839
-- "Phillipe Romano Fine Textures" x2 — keep the non-empty one
-- "Hermes Wallpaper" x2 — both empty, delete both
-
-## Step 3: Review tiny collections (1-9 products) — 983 total
-For each:
-- If it's a smart collection with valid rules, leave it (may grow as products are added)
-- If it's a manual collection with <5 products, check if the title matches a pattern_name or collection in the DB — if so, convert to smart collection with tag-based rules
-- Collections named after a specific wallpaper pattern with <5 products are OK (pattern-level collections)
-
-Report: how many deleted, merged, converted. Don't delete pattern-level collections.
diff --git a/tasks/done/06_db-maintenance.md b/tasks/done/06_db-maintenance.md
deleted file mode 100644
index 92acb1f..0000000
--- a/tasks/done/06_db-maintenance.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Database Maintenance — Nightly Cleanup
-
-1. VACUUM ANALYZE on key tables:
-```sql
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "VACUUM ANALYZE vendor_catalog; VACUUM ANALYZE shopify_api_queue; VACUUM ANALYZE enrichment_tracking; VACUUM ANALYZE w1838_catalog;"
-```
-
-2. Clean dead Quinn queue entries older than 7 days:
-```sql
-DELETE FROM shopify_api_queue WHERE status = 'dead' AND completed_at < NOW() - INTERVAL '7 days';
-DELETE FROM shopify_api_queue WHERE status = 'done' AND completed_at < NOW() - INTERVAL '3 days';
-```
-
-3. Report DB size and table sizes.
diff --git a/tasks/done/06_enrich-ascreation.md b/tasks/done/06_enrich-ascreation.md
deleted file mode 100644
index ab0c03d..0000000
--- a/tasks/done/06_enrich-ascreation.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Enrich AS Creation Specs
-
-Run this command directly:
-
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node enrich-ascreation-width.js
-```
-
-If the script doesn't exist, check `ls /root/DW-Agents/vendor-scrapers/enrich-ascreation*` for the correct filename and run that instead.
-
-Report the final count of products enriched.
diff --git a/tasks/done/06_enrich-rl-width.md b/tasks/done/06_enrich-rl-width.md
deleted file mode 100644
index 9058ce7..0000000
--- a/tasks/done/06_enrich-rl-width.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Ralph Lauren Width Enrichment (82 with URL, 481 without)
-
-82 RL products have source_url but no width. 481 have no URL at all.
-
-## Task
-1. Check for existing enrichment scripts in /root/DW-Agents/vendor-scrapers/ for RL/Ralph Lauren
-2. If `enrich-rl-images.js` exists, check if it also extracts width
-3. Run the enrichment script for the 82 products with URLs
-4. For the 481 without URLs, check if they can be constructed from mfr_sku
-5. Report results: how many widths were filled
-
-Table: rl_catalog. URL column: source_url. Width column: width.
diff --git a/tasks/done/06_git-backup-push.md b/tasks/done/06_git-backup-push.md
deleted file mode 100644
index 7c44c6f..0000000
--- a/tasks/done/06_git-backup-push.md
+++ /dev/null
@@ -1,12 +0,0 @@
-Commit all uncommitted changes in DW-Agents and push to GitHub backup.
-
-Steps:
-1. cd /root/DW-Agents
-2. Run `git status` to see what's changed
-3. Add all changed files (but NOT .env files or credentials): `git add -A && git reset HEAD '*.env' '.env*' '*credentials*' '*secret*'`
-4. Create a descriptive commit message summarizing the changes
-5. Push to origin (Stevemdr/DW-Agents private repo): `git push origin main`
-6. Also check /root/Projects/goodquestion-ai/ for uncommitted changes and push those too
-7. Report what was committed and pushed
-
-Working directory: /root/DW-Agents
diff --git a/tasks/done/06_git-backup.md b/tasks/done/06_git-backup.md
deleted file mode 100644
index 47f8be9..0000000
--- a/tasks/done/06_git-backup.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Git Backup
-```bash
-cd /root/DW-Agents && git add -A && git commit -m "auto-backup $(date +%Y-%m-%d)" && git push origin main
-cd /root/Projects/goodquestion-ai && git add -A && git commit -m "auto-backup $(date +%Y-%m-%d)" && git push origin main
-```
-Skip if nothing to commit.
diff --git a/tasks/done/06_hollywood-gemini-hex.md b/tasks/done/06_hollywood-gemini-hex.md
deleted file mode 100644
index f444f9c..0000000
--- a/tasks/done/06_hollywood-gemini-hex.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Hollywood Gemini hex color extraction — same pattern as DWPR.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Gemini key: ${GOOGLE_API_KEY}
-Model: gemini-2.0-flash
-
-1. Check how many Hollywood products have image_url but no hex color data:
- SELECT COUNT(*) FROM hollywood_catalog WHERE image_url IS NOT NULL AND image_url != '' AND (color_primary IS NULL OR color_primary = '');
-2. Find the DWPR Gemini hex extraction script in /root/DW-Agents/vendor-scrapers/ (gemini-color*.py or similar)
-3. Adapt it for hollywood_catalog — send each product image to Gemini vision, extract dominant hex color
-4. Save hex to color_primary column
-5. Use Gemini Batch API if >100 products (50% cheaper)
-6. Rate limit: max 15 req/min for sync Gemini calls
-7. Report how many products got hex colors.
diff --git a/tasks/done/06_hollywood-hex-extraction.md b/tasks/done/06_hollywood-hex-extraction.md
deleted file mode 100644
index 8c6320d..0000000
--- a/tasks/done/06_hollywood-hex-extraction.md
+++ /dev/null
@@ -1,21 +0,0 @@
-## Hollywood Gemini Hex Extraction
-
-Sessions #99 and #102 noted that Hollywood Wallcoverings (4,770 products) need Gemini hex color extraction — same process that was completed for Phillipe Romano (1,622 products).
-
-### Tasks:
-1. Check how many Hollywood products already have hex data in `product_colors` table
-2. Query `hollywood_catalog` for active products with images (image_url IS NOT NULL)
-3. Use Gemini 2.0 Flash to analyze product images and extract dominant hex color
-4. Store results in `product_colors` table (same schema as DWPR products)
-5. Generate color tags from the hex values
-6. Report: how many products processed, hex codes found, any errors
-
-Gemini API key: `${GOOGLE_API_KEY}`
-Model: `gemini-2.0-flash`
-Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={key}`
-
-Reference script: Look at how DWPR hex extraction was done in session #99
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-IMPORTANT: This is a large batch (4,770 products). Use rate limiting (max 10 req/s to Gemini).
-If batch is >100 products, consider using Gemini Batch API for 50% cost savings.
diff --git a/tasks/done/06_lincrusta-width-enrichment.md b/tasks/done/06_lincrusta-width-enrichment.md
deleted file mode 100644
index 997b22e..0000000
--- a/tasks/done/06_lincrusta-width-enrichment.md
+++ /dev/null
@@ -1,19 +0,0 @@
-Enrich 5 Lincrusta products missing width, then push to Shopify.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: lincrusta_catalog
-Shopify product token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-From Lincrusta push agent: 5 products were skipped because they have no width:
-- DWLI-890028 William Morris Willow Boughs Dado
-- DWLI-890029 Gothic Dado
-- DWLI-890030 Seville
-- DWLI-890031 Art Nouveau
-- DWLI-890032 Edwardian
-
-1. Check Lincrusta website for width specs: https://www.lincrusta.com/
-2. Use the scraper at /root/DW-Agents/vendor-scrapers/scrape-lincrusta-full.js to re-scrape those 5 products
-3. If scraper doesn't get width, check: Lincrusta dado panels are typically 533mm (21") wide
-4. Update DB with correct width
-5. Then push the 5 products to Shopify using the same pattern as the Lincrusta push script
-6. Report: widths found, products pushed
diff --git a/tasks/done/06_missing-images-audit.md b/tasks/done/06_missing-images-audit.md
deleted file mode 100644
index 6529659..0000000
--- a/tasks/done/06_missing-images-audit.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Missing Images Audit
-Query all vendor catalog tables in dw_unified DB to find products where image_url IS NULL or empty. Group by vendor/catalog table. Output to /root/DW-Agents/logs/missing-images.json. Include: catalog_table, count_missing, count_total, percentage_missing.
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
diff --git a/tasks/done/06_push-command54.md b/tasks/done/06_push-command54.md
deleted file mode 100644
index ca8305b..0000000
--- a/tasks/done/06_push-command54.md
+++ /dev/null
@@ -1,33 +0,0 @@
-## Push Command54 as Phillipe Romano — Full Monte Phase 8
-
-1,945 push-ready Command54 products. PRIVATE LABEL — all public-facing content uses "Phillipe Romano".
-
-### CRITICAL PRIVATE LABEL RULES:
-- Public vendor name: "Phillipe Romano" — NEVER "Command54"
-- Public SKU: DW SKU only (DWPR-prefix) — NEVER CMD54- SKUs
-- Collection: "Hollywood Vinyls Vol. 1"
-- private_label.real_vendor_name metafield: "Command54" (hidden, internal only)
-- Strip "Contact" from all pattern names
-- NEVER show CMD54-, "Command54", or real pattern names in titles, tags, body_html
-
-### Product Rules:
-- Commercial vendor — fire_rating: "Class A Fire Rated : Passes ASTM E84"
-- Title format: {PatternName} - {Color} Wallcovering | Phillipe Romano
-- Body HTML: 3-sentence ai_description ONLY. NO specs.
-- ALL specs in metafields
-- MFR SKU tag uses DW SKU (DWPR-), NOT manufacturer CMD54- number
-- Sample variant: $4.25, weight 0
-- Roll variant: $0 (commercial = quote), weight 2 lbs
-- inventory_quantity: 2026
-- Commercial room settings REQUIRED (hotel lobby, office, healthcare, restaurant, retail, conference)
-
-### Steps:
-1. Query vendor_catalog for vendor_code='command54' with on_shopify=false, has image+width+mfr_sku+ai_colors
-2. Apply private label transforms BEFORE push
-3. Queue through Quinn (GraphQL, batched 25)
-4. Add to "Hollywood Vinyls Vol. 1" collection after push
-5. Mark on_shopify=true, update enrichment_tracking phase8
-
-### IMPORTANT:
-- Launch Pad gate — first 25 for Steve's review
-- Double-check NO Command54 references leak into public content
diff --git a/tasks/done/06_push-mf-scalamandre.md b/tasks/done/06_push-mf-scalamandre.md
deleted file mode 100644
index 11915cc..0000000
--- a/tasks/done/06_push-mf-scalamandre.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Push Metafields — Scalamandre (4,819 products)
-
-Run: `python3 /root/DW-Agents/scripts/push-metafields-vendor.py scalamandre_catalog`
-
-Pushes dw.width, dw.manufacturer_sku, dw.repeat, dw.pattern_name, dw.color, dw.contents + global.* legacy.
-Report results.
diff --git a/tasks/done/06_rebuild-astro-site.md b/tasks/done/06_rebuild-astro-site.md
deleted file mode 100644
index feb6597..0000000
--- a/tasks/done/06_rebuild-astro-site.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Rebuild Astro Site After Blog Cleanup
-
-Working directory: /root/Projects/goodquestion-ai/
-
-After blog posts are purged and rewritten, rebuild the Astro static site and restart.
-
-```bash
-cd /root/Projects/goodquestion-ai
-npm run build
-pm2 restart goodquestion-blog
-```
-
-Verify the site is up:
-```bash
-curl -s -o /dev/null -w "%{http_code}" http://localhost:9883/
-```
-
-Should return 200. If build fails, check for broken markdown frontmatter or missing image references and fix them.
-
-Also check that all 18 posts are accessible:
-- curl each post URL pattern: http://localhost:9883/posts/[slug]/
-- Report any 404s
diff --git a/tasks/done/06_vcc-sync-all.md b/tasks/done/06_vcc-sync-all.md
deleted file mode 100644
index 13c4428..0000000
--- a/tasks/done/06_vcc-sync-all.md
+++ /dev/null
@@ -1,11 +0,0 @@
-Run VCC (Vendor Command Center) sync to update all vendor stats.
-
-Steps:
-1. Check VCC is running: curl -s -u admin:DWSecure2024! http://127.0.0.1:9660/api/health | head -1
-2. If not running, start it: pm2 restart vendor-command-center
-3. Trigger a full vendor stats refresh:
- curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:9660/api/vendors/refresh-stats 2>/dev/null | head -5
-4. Wait 10 seconds for it to process
-5. Get the updated vendor summary:
- curl -s -u admin:DWSecure2024! http://127.0.0.1:9660/api/vendors/summary 2>/dev/null | python3 -m json.tool | head -40
-6. Report: total vendors, total products across all vendors, vendors with 0 products
diff --git a/tasks/done/06_wallquest-completion.md b/tasks/done/06_wallquest-completion.md
deleted file mode 100644
index 6b8172e..0000000
--- a/tasks/done/06_wallquest-completion.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## WallQuest Image Completion
-
-WallQuest: 465 products, 384 images (82.6%) — 81 missing
-
-### Steps:
-1. Check what data missing-image products have:
- ```sql
- SELECT mfr_sku, pattern_name, product_url FROM wallquest_catalog
- WHERE (image_url IS NULL OR image_url = '') LIMIT 10;
- ```
-
-2. Check for existing scraper: `ls /root/DW-Agents/vendor-scrapers/*wallquest*`
-
-3. Try wallquest.com product pages for images
-
-4. Update VCC after
-
-### CRITICAL: Do NOT push to Shopify.
diff --git a/tasks/done/06_zoffany-shopify-prep.md b/tasks/done/06_zoffany-shopify-prep.md
deleted file mode 100644
index 29872ea..0000000
--- a/tasks/done/06_zoffany-shopify-prep.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Zoffany — Prepare 16 Unpushed Products for Shopify
-
-16 Zoffany products in zoffany_catalog have ZOW-format SKUs, are on the live website, but haven't been pushed to Shopify.
-
-1. Query: `SELECT * FROM zoffany_catalog WHERE shopify_product_id IS NULL AND mfr_sku LIKE 'ZOW%' LIMIT 20`
-2. Validate each has: image_url, width, mfr_sku (FULLPRODUCT gate)
-3. Generate DW SKUs using DWZF- prefix (check vendor_registry for sku_range_start)
-4. For products passing validation, mark them as ready for VCC approval
-5. Report: how many pass, how many fail, what's missing
-
-Do NOT push to Shopify directly. Just prepare and validate.
diff --git a/tasks/done/07_07_dw-exclusive-page-update.md b/tasks/done/07_07_dw-exclusive-page-update.md
deleted file mode 100644
index 3ac683a..0000000
--- a/tasks/done/07_07_dw-exclusive-page-update.md
+++ /dev/null
@@ -1,17 +0,0 @@
-Update the DW-Exclusive page on the Shopify store to be much more robust. Include a SEPARATE SECTION for Thibaut collections.
-
-Shopify Admin API:
-- Token: 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 (pages)
-- Product token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Theme token: <redacted:SHOPIFY_ORDERS_TOKEN>
-- Dev theme: 141935312947
-
-1. Find the DW-Exclusive page: check pages API for handle containing exclusive or dw-exclusive
-2. Get Thibaut collections from Shopify
-3. Create a robust page with:
- - Hero section with DW branding
- - Separate Thibaut Collections section with collection cards (image + title + product count)
- - Other exclusive brands section
- - Each section should be visually distinct with headers
-4. Use the DW styling: warm browns, clean modern look
-5. Push to dev theme first
\ No newline at end of file
diff --git a/tasks/done/07_07_enrich-designtex.md b/tasks/done/07_07_enrich-designtex.md
deleted file mode 100644
index a2d7a9a..0000000
--- a/tasks/done/07_07_enrich-designtex.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-designtex-repeat.js
-
-Enrich Designtex catalog with repeat data.
\ No newline at end of file
diff --git a/tasks/done/07_07_hollywood-hex-cleanup.md b/tasks/done/07_07_hollywood-hex-cleanup.md
deleted file mode 100644
index 3fa63a9..0000000
--- a/tasks/done/07_07_hollywood-hex-cleanup.md
+++ /dev/null
@@ -1 +0,0 @@
-Run the Hollywood Gemini hex extraction script to process the remaining ~171 products that have color names instead of hex codes. Script: cd /root/DW-Agents/vendor-scrapers && python3 hollywood-gemini-hex.py --limit 200. This will re-process products where color_primary does not start with #. Should take about 12 minutes. Log results. Also sync any missing entries to product_colors table if there is a sync script.
\ No newline at end of file
diff --git a/tasks/done/07_07_thibaut-discontinued-archive.md b/tasks/done/07_07_thibaut-discontinued-archive.md
deleted file mode 100644
index e1e0aad..0000000
--- a/tasks/done/07_07_thibaut-discontinued-archive.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Thibaut discontinued product archival.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: thibaut_catalog
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-From Session #102: 302 Thibaut products returning 301 redirects need archival.
-
-1. Check thibaut_catalog for products marked discontinued or with redirect URLs
-2. Find which ones are on Shopify (have shopify_product_id)
-3. Archive them on Shopify: PUT /products/{id}.json {status: archived}
-4. Rate limit 0.5s between calls
-5. Report results.
\ No newline at end of file
diff --git a/tasks/done/07_07_width-enrichment-audit.md b/tasks/done/07_07_width-enrichment-audit.md
deleted file mode 100644
index 4f71f01..0000000
--- a/tasks/done/07_07_width-enrichment-audit.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Check which vendors have low width coverage in PostgreSQL. Run:
-timeout 30 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT table_name, total, has_width, ROUND(100.0*has_width/NULLIF(total,0),1) as pct FROM (SELECT tablename as table_name, (xpath(chr(47)||chr(47)||chr(114)||chr(111)||chr(119), xml_count))[1]::text::int as total, 0 as has_width FROM pg_tables WHERE tablename LIKE chr(37)||chr(95)||chr(99)||chr(97)||chr(116)||chr(97)||chr(108)||chr(111)||chr(103)) x ORDER BY pct ASC LIMIT 20;"
-
-Actually simpler: list all _catalog tables and for each check COUNT(*) and COUNT where width IS NOT NULL. Report the 10 worst vendors by width coverage percentage. Use timeout 30 on psql.
\ No newline at end of file
diff --git a/tasks/done/07_07_zombie-claude-kill.md b/tasks/done/07_07_zombie-claude-kill.md
deleted file mode 100644
index 018c428..0000000
--- a/tasks/done/07_07_zombie-claude-kill.md
+++ /dev/null
@@ -1 +0,0 @@
-Find any zombie Claude processes older than 12 hours: ps aux | grep claude | grep -v grep. For each process, check how long it has been running. If over 12 hours, log the PID and command, then kill it. Report to /root/DW-Agents/logs/zombie-cleanup.log with timestamp.
\ No newline at end of file
diff --git a/tasks/done/07_archive-discontinued-zoffany.md b/tasks/done/07_archive-discontinued-zoffany.md
deleted file mode 100644
index ca0d435..0000000
--- a/tasks/done/07_archive-discontinued-zoffany.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Archive 6 Discontinued Zoffany Products
-
-These 6 Zoffany products return 404 on the vendor site and should be archived on Shopify:
-
-Find their shopify_product_id in zoffany_catalog for MFR SKUs:
-- ZOW0025-03 (Renaissance Damask Russet)
-- ZOW0040-02 (Metallo)
-- ZOW0061-03 (Evelyn)
-- ZOW0068-01 (Taisho Lotus Panel A)
-- ZOW0069-01 (Taisho Lotus Panel B)
-- ZOW0079-10 (Ormonde)
-
-For each with a shopify_product_id, queue an archive via shopify_api_queue:
-```sql
-INSERT INTO shopify_api_queue (method, endpoint, payload, token_alias, priority, status, source_agent, max_retries)
-VALUES ('PUT', '/admin/api/2024-01/products/{ID}.json', '{"product":{"status":"archived"}}', 'product', 2, 'pending', 'zoffany-archive', 3);
-```
-
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
diff --git a/tasks/done/07_build-scraper-glitter.md b/tasks/done/07_build-scraper-glitter.md
deleted file mode 100644
index e01a2c9..0000000
--- a/tasks/done/07_build-scraper-glitter.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Build a vendor scraper for Glitter wallcovering (glitterwallcovering.com).
-
-Directory: /root/DW-Agents/vendor-scrapers/
-Output file: scrape-glitter.js
-DB table: glitter_catalog
-
-Use scraper-utils.js upsertProduct() pattern.
-
-1. Visit glitterwallcovering.com (or similar URL) and find their products
-2. Extract: pattern_name, collection, color, mfr_sku, image_url, product_url, all_images, description, specs
-3. Check if it is a Shopify store (use Shopify products.json API if so)
-4. Upsert to glitter_catalog using scraper-utils
-5. Test with small batch first
-
-CRITICAL: Use mfr_sku field. Include all_images array.
\ No newline at end of file
diff --git a/tasks/done/07_duplicate-sku-check.md b/tasks/done/07_duplicate-sku-check.md
deleted file mode 100644
index 2afecc2..0000000
--- a/tasks/done/07_duplicate-sku-check.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Duplicate SKU Check
-Query enrichment_tracking in dw_unified to find mfr_sku values that appear in multiple vendor_code entries. This indicates potential duplicate products from different vendors. Output to /root/DW-Agents/logs/duplicate-skus.json with mfr_sku, vendor_codes array, and count.
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
diff --git a/tasks/done/07_elli-popp-verify-cleanup.md b/tasks/done/07_elli-popp-verify-cleanup.md
deleted file mode 100644
index fbd89f8..0000000
--- a/tasks/done/07_elli-popp-verify-cleanup.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Verify the Elli Popp products we updated earlier are correct on Shopify.
-
-Steps:
-1. Use Shopify REST API (token: <redacted:SHOPIFY_ORDERS_TOKEN>, store: designer-laboratory-sandbox) to pull all Elli Popp products (vendor="Elli Popp")
-2. Verify ALL 123 products have:
- - A "Sample" variant at $4.25 with 0 weight
- - A non-sample variant (Roll/Sold Per Roll) with proper pricing ($96-$239 range)
- - Option name is "Size" (not "Title")
-3. Check if any Elli Popp products are still in DRAFT status — they should be moved to ACTIVE if they have images and pricing
-4. Verify the body_html follows the 3-sentence description format (no specs tables)
-5. Report: how many passed verification, how many need fixes, how many are still in draft
-
-Working directory: /root/DW-Agents
diff --git a/tasks/done/07_fix-blog-agent-generator.md b/tasks/done/07_fix-blog-agent-generator.md
deleted file mode 100644
index 31a15ff..0000000
--- a/tasks/done/07_fix-blog-agent-generator.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Fix Blog Agent Generator — Prevent Future Spam
-
-Working directory: /root/DW-Agents/blog-agent/
-
-## Problem
-The blog agent cron (every 2h) was generating duplicate posts. 69 copies of "Notes From the Build" and 10+ copies of other titles. The generator has no deduplication check.
-
-## Fix Required
-
-### 1. Add title deduplication
-Before generating a new post, check existing posts in /root/Projects/goodquestion-ai/src/content/posts/ for:
-- Exact title match
-- Similar title (fuzzy match — strip numbers and compare)
-If a post with the same or very similar title exists, SKIP generation.
-
-### 2. Add daily limit
-Max 1 post per day. Check if a post was already generated today before creating another.
-
-### 3. Improve topic diversity
-Read the generator.js file. The topic selection is probably pulling from a small static list. Expand it or make it dynamic based on recent SESSION-LEARNINGS.md entries.
-
-### 4. Improve quality
-- Descriptions must be unique and relevant (not the "Mobile bottom sheets" placeholder)
-- Titles should be specific and compelling
-- Target audience: entrepreneurs and founders, NOT developers
-
-### 5. Add sanitization check
-Every generated post MUST pass through the sanitizer before being written to disk. If it fails sanitization, don't publish — log the failure.
-
-Read generator.js, understand the current logic, then fix all 5 issues above.
diff --git a/tasks/done/07_git-backup.md b/tasks/done/07_git-backup.md
deleted file mode 100644
index 9a5cda8..0000000
--- a/tasks/done/07_git-backup.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Git Backup — Commit & Push All Repos
-
-1. DW-Agents:
-```bash
-cd /root/DW-Agents && git add -A && git commit -m "nightly auto-backup $(date +%Y-%m-%d)" && git push origin main
-```
-
-2. goodquestion-ai:
-```bash
-cd /root/Projects/goodquestion-ai && git add -A && git commit -m "nightly auto-backup $(date +%Y-%m-%d)" && git push origin main
-```
-
-Skip if nothing to commit.
diff --git a/tasks/done/07_legal-violations-report.md b/tasks/done/07_legal-violations-report.md
deleted file mode 100644
index a38b3d5..0000000
--- a/tasks/done/07_legal-violations-report.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Legal Violations Status Report
-
-Sessions #84-86 flagged 48 HIGH-risk + 176 MEDIUM-risk legal violations that need Steve's review. The legal review page is at http://45.61.58.125:9878/review.
-
-### Tasks:
-1. Check if the legal agent (Judge) is running: `pm2 status` for legal-related processes
-2. Query the legal violations database to get current counts by status (pending, cleared, removed)
-3. Check if the nightly legal scan cron (2 AM PT) is still running correctly
-4. Generate a summary report:
- - Total violations by risk level (HIGH/MEDIUM/LOW)
- - How many have been reviewed vs pending
- - Top vendors with violations
- - Any new violations since March 4
-5. If the legal agent is down, restart it
-6. Print the report to stdout
-
-Legal agent location: `/root/DW-Agents/legal-agent/` or search PM2 for "legal" or "judge"
-Review page: http://45.61.58.125:9878/review
diff --git a/tasks/done/07_pm2-health.md b/tasks/done/07_pm2-health.md
deleted file mode 100644
index df58c81..0000000
--- a/tasks/done/07_pm2-health.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# PM2 Health Check
-1. List errored/stopped processes, restart them
-2. Check memory usage (flag >500MB)
-3. Check disk (df -h)
-4. Kill any zombie Claude processes >12h old
-5. Check Quinn queue depth and processing rate
diff --git a/tasks/done/07_push-classification-tags-shopify.md b/tasks/done/07_push-classification-tags-shopify.md
deleted file mode 100644
index 7f51877..0000000
--- a/tasks/done/07_push-classification-tags-shopify.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Push Classification Tags to Shopify via Quinn Queue
-
-## Goal
-Add product_category and repeat_classification as Shopify tags for all classified products.
-MUST use Quinn queue — NEVER call Shopify API directly.
-
-## Step 1: Write and run a Node.js script
-
-Create `/root/DW-Agents/scripts/push-classification-tags.js` that does:
-
-1. Query products that need tags:
-```sql
-SELECT id, shopify_product_id, product_category, repeat_classification
-FROM vendor_catalog
-WHERE on_shopify = true
- AND shopify_product_id IS NOT NULL
- AND (product_category IS NOT NULL OR repeat_classification IS NOT NULL)
-ORDER BY id DESC;
-```
-
-2. Build batched GraphQL mutations (10 products per call) using `tagsAdd`:
-```graphql
-mutation {
- p0: tagsAdd(id: "gid://shopify/Product/123", tags: ["Wallcovering", "Texture"]) {
- node { id }
- userErrors { message }
- }
- p1: tagsAdd(id: "gid://shopify/Product/456", tags: ["Fabric"]) {
- node { id }
- userErrors { message }
- }
-}
-```
-
-3. Queue each batch through Quinn:
-```javascript
-const ShopifyQueueClient = require('/root/DW-Agents/shared/shopify-queue-client.js');
-const quinn = new ShopifyQueueClient('classification-tagger');
-
-// For each batch of 10 products:
-await quinn.queueWrite('POST', '/graphql.json', {
- query: batchedMutation,
- variables: {}
-}, 3, 'product');
-```
-
-4. Fire and forget — don't wait for Quinn responses
-5. If shopify_product_id is numeric, prepend `gid://shopify/Product/`
-6. Capitalize tags: "Wallcovering", "Texture", "Pattern", "Fabric", "Mural"
-7. Skip products with NULL repeat_classification (only add product_category tag for those)
-8. Log progress every 500 products
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-## Run
-```bash
-cd /root/DW-Agents/scripts && timeout 600 node push-classification-tags.js
-```
-
-Report: total queued, by category, by classification.
diff --git a/tasks/done/07_sync-and-final-report.md b/tasks/done/07_sync-and-final-report.md
deleted file mode 100644
index 4890370..0000000
--- a/tasks/done/07_sync-and-final-report.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## Final Sync and Report
-
-### Steps:
-1. Force VCC sync: `curl -s -u admin:DWSecure2024! -X POST http://localhost:9660/api/sync`
-2. Wait 30s
-
-3. Full stats:
- ```sql
- SELECT vendor_name, total_products, products_with_images,
- ROUND(products_with_images::numeric / NULLIF(total_products,0) * 100, 1) as pct
- FROM vendor_registry WHERE total_products > 50 AND is_private_label = false
- ORDER BY pct ASC NULLS FIRST LIMIT 15;
- ```
-
-4. Overall:
- ```sql
- SELECT SUM(total_products) as total, SUM(products_with_images) as imgs,
- ROUND(SUM(products_with_images)::numeric / NULLIF(SUM(total_products),0) * 100, 1) as pct
- FROM vendor_registry WHERE total_products > 0;
- ```
diff --git a/tasks/done/07_zimmer-rohde-enrich.md b/tasks/done/07_zimmer-rohde-enrich.md
deleted file mode 100644
index d0668f5..0000000
--- a/tasks/done/07_zimmer-rohde-enrich.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Zimmer + Rohde — Scrape for Width & Repeat
-
-93 products in `zimmer_rohde_catalog`. Missing width and repeat. Have product_url. Small catalog.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check schema: `SELECT column_name FROM information_schema.columns WHERE table_name = 'zimmer_rohde_catalog' ORDER BY ordinal_position;`
-3. Add missing columns if needed (width, repeat_v, repeat_h, material).
-4. Check if specs JSON has data: `SELECT LEFT(specs::text, 300) FROM zimmer_rohde_catalog WHERE specs IS NOT NULL LIMIT 3;`
-5. If specs has width/repeat, extract. Otherwise scrape product_url.
-6. Write enrichment script at `/root/DW-Agents/vendor-scrapers/enrich-zimmer-rohde.js`
-7. Run: `timeout 300 node enrich-zimmer-rohde.js`
-8. Report.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-Zimmer + Rohde site: https://www.zimmer-rohde.com/
diff --git a/tasks/done/08_08_enrich-andrew-martin.md b/tasks/done/08_08_enrich-andrew-martin.md
deleted file mode 100644
index 5e0317d..0000000
--- a/tasks/done/08_08_enrich-andrew-martin.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Check Andrew Martin catalog in PostgreSQL. Run:
-timeout 10 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width, COUNT(CASE WHEN image_url IS NOT NULL THEN 1 END) as has_image, COUNT(CASE WHEN repeat_v IS NOT NULL THEN 1 END) as has_repeat FROM andrew_martin_catalog;"
-If width coverage is low, check if there is an enrichment script in /root/DW-Agents/vendor-scrapers/ for andrew_martin. If found, run it. If not, report what is needed.
\ No newline at end of file
diff --git a/tasks/done/08_08_enrich-fabricut.md b/tasks/done/08_08_enrich-fabricut.md
deleted file mode 100644
index 7eb6a96..0000000
--- a/tasks/done/08_08_enrich-fabricut.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-fabricut-width.js
-
-Enrich Fabricut catalog with width.
\ No newline at end of file
diff --git a/tasks/done/08_08_philippe-romano-metafields.md b/tasks/done/08_08_philippe-romano-metafields.md
deleted file mode 100644
index a807637..0000000
--- a/tasks/done/08_08_philippe-romano-metafields.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Find Philippe Romano products that have data in phillipe_romano catalog table but zero metafields on Shopify. Query: SELECT p.shopify_id, p.mfr_sku, p.width, p.material, p.repeat_v, p.repeat_h, p.match_type, p.fire_rating, p.collection FROM phillipe_romano_catalog p WHERE p.shopify_id IS NOT NULL AND p.width IS NOT NULL. For each product with a shopify_id, push specs as body_html specs table to Shopify using PUT /admin/api/2024-01/products/{shopify_id}.json with token <redacted:SHOPIFY_ADMIN_TOKEN>. Rate limit 2/sec. Log count pushed to /root/DW-Agents/logs/pr-metafield-push.log
\ No newline at end of file
diff --git a/tasks/done/08_08_ssl-cert-audit.md b/tasks/done/08_08_ssl-cert-audit.md
deleted file mode 100644
index 514388d..0000000
--- a/tasks/done/08_08_ssl-cert-audit.md
+++ /dev/null
@@ -1 +0,0 @@
-Check all SSL certificates on the server for expiry: for cert in /etc/letsencrypt/live/*/fullchain.pem; do domain=$(echo $cert | cut -d/ -f5); expiry=$(openssl x509 -enddate -noout -in $cert 2>/dev/null | cut -d= -f2); echo "$domain: $expiry"; done. Flag any expired or expiring within 30 days. Save to /root/DW-Agents/logs/ssl-audit.log.
\ No newline at end of file
diff --git a/tasks/done/08_blog-post.md b/tasks/done/08_blog-post.md
deleted file mode 100644
index 09dcd23..0000000
--- a/tasks/done/08_blog-post.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Publish Blog Post — goodquestion.ai
-
-Generate and publish a new blog post for goodquestion.ai using the blog-agent pipeline.
-
-```bash
-cd /root/DW-Agents/blog-agent
-node generator.js
-```
-
-If that fails, trigger via API:
-```bash
-curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:9871/api/generate
-```
-
-Wait for completion, verify the post was published, and confirm Astro build succeeded.
diff --git a/tasks/done/08_newwall-enrichment-scrape.md b/tasks/done/08_newwall-enrichment-scrape.md
deleted file mode 100644
index b6906f3..0000000
--- a/tasks/done/08_newwall-enrichment-scrape.md
+++ /dev/null
@@ -1,18 +0,0 @@
-Enrich Newwall catalog with product-page level specs. Only 20/8,713 products have width.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Table: newwall_catalog
-
-Current state: 8,713 products. 20 have width (0.2%), 681 have material (8%).
-
-1. Check if a Newwall scraper exists: `ls /root/DW-Agents/vendor-scrapers/*newwall*`
-2. Check the table schema: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "\d newwall_catalog"`
-3. Check product URLs: `psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT product_url FROM newwall_catalog WHERE product_url IS NOT NULL AND product_url != '' LIMIT 5;"`
-4. If product_url exists, build a Puppeteer product-page enrichment scraper:
- - Visit each product URL on newwall.com
- - Extract: width, material, repeat (vertical/horizontal), fire rating, any other specs
- - Update newwall_catalog with found specs
-5. Rate limit: 1 req/sec, batch of 200 per run
-6. Report: products processed, specs found, errors
-
-Priority: WIDTH is the #1 FULLPRODUCT gate. Get width for as many as possible.
diff --git a/tasks/done/08_nlxl-enrich.md b/tasks/done/08_nlxl-enrich.md
deleted file mode 100644
index 5533803..0000000
--- a/tasks/done/08_nlxl-enrich.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## NLXL — Scrape for Width & Repeat
-
-97 products in `nlxl_catalog`. Missing width and repeat. Have product_url. Small catalog.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check schema: `SELECT column_name FROM information_schema.columns WHERE table_name = 'nlxl_catalog' ORDER BY ordinal_position;`
-3. Add missing columns if needed.
-4. Check specs JSON: `SELECT LEFT(specs::text, 300) FROM nlxl_catalog WHERE specs IS NOT NULL AND specs::text != '{}' LIMIT 3;`
-5. Sample URL: `SELECT product_url FROM nlxl_catalog WHERE product_url IS NOT NULL LIMIT 2`
-6. Fetch sample page, find width/repeat/material in HTML.
-7. Write enrichment script at `/root/DW-Agents/vendor-scrapers/enrich-nlxl.js`
-8. Run: `timeout 300 node enrich-nlxl.js`
-9. Report.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-NLXL site: https://www.nlxl.com/
diff --git a/tasks/done/08_phase3-enrichment-run.md b/tasks/done/08_phase3-enrichment-run.md
deleted file mode 100644
index 9481378..0000000
--- a/tasks/done/08_phase3-enrichment-run.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Run Phase 3 AI enrichment on products that have images but no AI color data.
-
-Connect to PostgreSQL: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-1. Find the catalog tables: SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_name LIKE '%catalog%' ORDER BY table_name;
-2. For each catalog table, find products with image_url but NULL ai_colors. Limit to 20 products per batch.
-3. For each product image, call Gemini Vision API:
- curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}" \
- -H "Content-Type: application/json" \
- -d '{"contents":[{"parts":[{"text":"Analyze this wallcovering image. Return JSON with: colors (array of {name, hex, percentage}), background_color ({name, hex}), styles (array of strings like Traditional, Modern, etc), patterns (array of strings), image_type (scan_swatch, photo_full, etc). Be precise with hex codes."},{"inline_data":{"mime_type":"image/jpeg","data":"BASE64_HERE"}}]}]}'
-4. Download each image with curl, base64 encode it, send to Gemini
-5. Update the catalog table with the AI results
-6. Report how many products were enriched
diff --git a/tasks/done/08_phoebe-fix-and-dreamteam-health.md b/tasks/done/08_phoebe-fix-and-dreamteam-health.md
deleted file mode 100644
index 9be1a05..0000000
--- a/tasks/done/08_phoebe-fix-and-dreamteam-health.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Phoebe Fix + Dream Team Health Check
-
-Session #96 noted Phoebe's home route crashes due to missing `videos` directory. Also need to verify all Dream Team agents are healthy.
-
-### Tasks:
-1. Fix Phoebe (port 9668): Create missing `videos` directory in Phoebe's working directory
-2. Restart Phoebe via PM2
-3. Verify all Dream Team agents are responding:
- - Sandy (9652) — SEO
- - Sherman (9650) — Frontend
- - Sheldon (9651) — Performance
- - Shane (9653) — Product Strategy
- - Phoebe (9668) — QA
-4. For each agent, curl the home endpoint and report status
-5. Check if Dream Team telemetry pipeline is working (Sheldon should have recent perf snapshots)
-6. Report: which agents are up, any errors found
-
-Auth for all: admin / DWSecure2024!
diff --git a/tasks/done/08_pm2-health-report.md b/tasks/done/08_pm2-health-report.md
deleted file mode 100644
index 64f0919..0000000
--- a/tasks/done/08_pm2-health-report.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# PM2 Health Report
-Run pm2 jlist and analyze the output. Find: processes that are erroring (restart count > 5), processes using excessive memory (>200MB), processes that are stopped. Output a health report to /root/DW-Agents/logs/pm2-health.json with timestamp, total_processes, healthy, erroring, stopped, high_memory (list names + details).
diff --git a/tasks/done/08_pm2-health.md b/tasks/done/08_pm2-health.md
deleted file mode 100644
index 53edc2e..0000000
--- a/tasks/done/08_pm2-health.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# PM2 Health Check
-
-Run these commands and report results:
-
-```bash
-# 1. Check for errored/stopped processes
-pm2 jlist 2>/dev/null | python3 -c "
-import sys,json
-procs=json.load(sys.stdin)
-for p in procs:
- s=p['pm2_env']['status']
- if s != 'online':
- print(f' ISSUE: {p[\"name\"]} is {s}')
-if all(p['pm2_env']['status']=='online' for p in procs):
- print(f' ALL {len(procs)} processes online')
-"
-
-# 2. Disk usage
-df -h / | tail -1
-
-# 3. Memory
-free -h | head -2
-```
-
-If any process is errored, restart it with `pm2 restart {name}`.
-Do NOT kill any Claude processes — just report them.
diff --git a/tasks/done/08_push-synced-repeats-shopify.md b/tasks/done/08_push-synced-repeats-shopify.md
deleted file mode 100644
index 55fc885..0000000
--- a/tasks/done/08_push-synced-repeats-shopify.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Push Synced Repeat Data to Shopify via Quinn Queue
-
-## Goal
-Push pattern_repeat values from vendor_catalog to Shopify `custom.pattern_repeat` metafield.
-MUST use Quinn queue — NEVER call Shopify API directly.
-
-## Step 1: Write and run a Node.js script
-
-Create `/root/DW-Agents/scripts/push-synced-repeats.js` that does:
-
-1. Query products with repeat data to push:
-```sql
-SELECT id, shopify_product_id, pattern_repeat
-FROM vendor_catalog
-WHERE on_shopify = true
- AND shopify_product_id IS NOT NULL
- AND pattern_repeat IS NOT NULL AND pattern_repeat != '';
-```
-
-2. Build batched GraphQL mutations using `metafieldsSet` (max 25 metafields per call):
-```graphql
-mutation {
- metafieldsSet(metafields: [
- {ownerId: "gid://shopify/Product/123", namespace: "custom", key: "pattern_repeat", value: "21\"", type: "single_line_text_field"},
- {ownerId: "gid://shopify/Product/456", namespace: "custom", key: "pattern_repeat", value: "25.2\"", type: "single_line_text_field"}
- ]) {
- metafields { id }
- userErrors { message }
- }
-}
-```
-
-3. Queue each batch through Quinn:
-```javascript
-const ShopifyQueueClient = require('/root/DW-Agents/shared/shopify-queue-client.js');
-const quinn = new ShopifyQueueClient('repeat-pusher');
-
-await quinn.queueWrite('POST', '/graphql.json', {
- query: batchedMutation,
- variables: {}
-}, 3, 'product');
-```
-
-4. Fire and forget — don't wait for Quinn responses
-5. If shopify_product_id is numeric, prepend `gid://shopify/Product/`
-6. Log progress every 500 products
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-## Run
-```bash
-cd /root/DW-Agents/scripts && timeout 600 node push-synced-repeats.js
-```
-
-Report: total queued, sample of values pushed.
diff --git a/tasks/done/08_slack-notify-completion.md b/tasks/done/08_slack-notify-completion.md
deleted file mode 100644
index 9ca1e09..0000000
--- a/tasks/done/08_slack-notify-completion.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Send Completion Report to Slack
-
-After all previous tasks are done, send a summary to Slack.
-
-```bash
-curl -s -X POST '${SLACK_WEBHOOK_URL}' \
- -H 'Content-Type: application/json' \
- -d '{
- "text": "✅ *Agent Abrams Content Overhaul — COMPLETE*\n\n*Blog:*\n• 134 junk posts deleted\n• 18 posts rewritten (professional, no jargon)\n• Astro site rebuilt and live\n• Blog generator fixed (dedup + daily limit + sanitization)\n\n*Videos:*\n• HTML video pipeline built (Puppeteer + CSS animations)\n• 18 new videos created (landscape + vertical)\n• All uploaded to YouTube\n• Old videos unlisted\n\n*All content sanitized. Zero leaks.*\n\nCheck goodquestion.ai and youtube.com/@AgentAbrams"
- }'
-```
diff --git a/tasks/done/08_vcc-dashboard-fix.md b/tasks/done/08_vcc-dashboard-fix.md
deleted file mode 100644
index e0777b6..0000000
--- a/tasks/done/08_vcc-dashboard-fix.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Fix VCC dashboard — port 9660 /api/status returns 404.
-
-VCC server: /root/DW-Agents/vendor-command-center/server.js
-Port: 9660, PM2: vendor-command-center
-Auth: admin / DWSecure2024!
-
-1. Check pm2 status of vendor-command-center
-2. Read server.js and find the /api/status route — is it missing or broken?
-3. Check what routes exist: curl -u admin:DWSecure2024! http://localhost:9660/api/vendors
-4. Fix the /api/status endpoint if missing
-5. Also check: dashboard showing 0 catalog/0 on Shopify — likely a DB query issue
-6. Restart via pm2 restart vendor-command-center after fix
-7. Verify: curl -u admin:DWSecure2024! http://localhost:9660/api/status
diff --git a/tasks/done/09_09_blog-post.md b/tasks/done/09_09_blog-post.md
deleted file mode 100644
index a5c2e88..0000000
--- a/tasks/done/09_09_blog-post.md
+++ /dev/null
@@ -1 +0,0 @@
-Write and publish a blog post to goodquestion.ai about building a three-tier authentication system with role-based access control. Target audience: entrepreneurs and small business owners. Angle: how one person with AI built a production auth system (Norma/Nikki/Pulse tiers) that would normally require a full dev team. Follow the blog pipeline at /root/DW-Agents/blog-agent/. Run the sanitizer before publishing. Do NOT mention wallcoverings, wallpaper, DW, Designer Wallcoverings, SDCC, student debt, or any vendor names.
\ No newline at end of file
diff --git a/tasks/done/09_09_pj-search-exclusion.md b/tasks/done/09_09_pj-search-exclusion.md
deleted file mode 100644
index cccf928..0000000
--- a/tasks/done/09_09_pj-search-exclusion.md
+++ /dev/null
@@ -1 +0,0 @@
-Phillip Jeffries has 2,504 products on Shopify that only have logo placeholder images (no real product photos). These clutter search results. Query Shopify for all products with vendor=Phillip Jeffries using token <redacted:SHOPIFY_ADMIN_TOKEN>. Count how many have images. For products with NO images or only logo images, add the tag exclude-from-search. Log count to /root/DW-Agents/logs/pj-exclusion.log. Rate limit 2/sec.
\ No newline at end of file
diff --git a/tasks/done/09_ai-enrich-zoffany.md b/tasks/done/09_ai-enrich-zoffany.md
deleted file mode 100644
index 6e087cf..0000000
--- a/tasks/done/09_ai-enrich-zoffany.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# AI Enrich — Zoffany (0 of 302 Shopify products enriched)
-
-Zoffany products are in w1838_catalog table (vendor_code w1838 in vendor_registry, but actual catalog table is w1838_catalog).
-Wait — Zoffany has its OWN table. Check:
-```sql
-SELECT table_name FROM information_schema.tables WHERE table_name ILIKE '%zoff%';
-```
-
-Find the Zoffany catalog table and run Phase 3 AI enrichment on all products with images but no ai_colors.
-
-Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
-Limit 200 per run.
diff --git a/tasks/done/09_collections-color-palettes.md b/tasks/done/09_collections-color-palettes.md
deleted file mode 100644
index 8b9560d..0000000
--- a/tasks/done/09_collections-color-palettes.md
+++ /dev/null
@@ -1,58 +0,0 @@
-COLLECTIONS CLEANUP — Phase 4: Create designer color palette collections
-
-Create NEW smart collections for curated designer color palettes that don't exist yet. These are sophisticated color groupings that interior designers actually search for.
-
-Shopify store: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-## New Color Palette Collections to Create
-
-Use smart collection rules based on tags. Each collection uses tag-based rules.
-
-### Warm Neutrals
-1. **Warm Whites & Ivory** — tags: ivory, cream, off-white, warm white, alabaster, pearl
-2. **Camel & Cognac** — tags: camel, cognac, tan, tobacco, butterscotch
-3. **Terracotta & Clay** — tags: terracotta, clay, rust, burnt sienna, adobe
-4. **Warm Taupe** — tags: taupe, greige, mushroom, putty
-
-### Cool Tones
-5. **Slate & Charcoal** — tags: slate, charcoal, graphite, anthracite
-6. **Steel Blue** — tags: steel blue, denim, chambray, french blue
-7. **Sage & Olive** — tags: sage, olive, eucalyptus, moss
-8. **Plum & Aubergine** — tags: plum, aubergine, eggplant, wine, merlot
-
-### Statement Palettes
-9. **Chinoiserie** — tags: chinoiserie, toile, oriental, pagoda, willow
-10. **Art Deco Metallics** — tags: art deco, metallic, gold, silver, bronze, geometric
-11. **Moody Dark** — tags: dark, moody, dramatic, noir, midnight
-12. **Coastal & Nautical** — tags: coastal, nautical, maritime, seagrass, driftwood
-
-### By Room
-13. **Powder Room Statements** — tags: bold, statement, powder room, dramatic
-14. **Primary Suite** — tags: serene, bedroom, linen, silk, muted
-15. **Study & Library** — tags: traditional, library, study, leather, plaid, tartan
-
-For each collection:
-1. Create smart collection with disjunctive tag rules (OR logic)
-2. Verify it pulls >10 products — if not, broaden the tag list
-3. Add a 3-sentence description (same style as Phase 3)
-4. Set the cover image from the best-looking product in the collection
-
-Use Shopify API:
-```
-POST /admin/api/2024-07/smart_collections.json
-{
- "smart_collection": {
- "title": "Collection Name",
- "body_html": "<p>description</p>",
- "rules": [
- {"column": "tag", "relation": "equals", "condition": "tag1"},
- {"column": "tag", "relation": "equals", "condition": "tag2"}
- ],
- "disjunctive": true,
- "published": true
- }
-}
-```
-
-Report: how many created, product counts for each, any that pulled <10 products.
diff --git a/tasks/done/09_pm2-restart-errored.md b/tasks/done/09_pm2-restart-errored.md
deleted file mode 100644
index 9bf57df..0000000
--- a/tasks/done/09_pm2-restart-errored.md
+++ /dev/null
@@ -1,7 +0,0 @@
-Check PM2 for any errored or stopped processes and restart them.
-
-1. Run: pm2 jlist
-2. Parse the JSON output to find processes with pm2_env.status = "errored" or "stopped"
-3. For each errored/stopped process, run: pm2 restart <name>
-4. After restarting, run pm2 list and verify they came back online
-5. Report what was restarted
diff --git a/tasks/done/09_quinn-queue-health.md b/tasks/done/09_quinn-queue-health.md
deleted file mode 100644
index 8491735..0000000
--- a/tasks/done/09_quinn-queue-health.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Quinn Queue Health Check
-
-```bash
-psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
-SELECT source_agent, status, COUNT(*)
-FROM shopify_api_queue
-WHERE status = 'pending'
-GROUP BY 1, 2 ORDER BY 3 DESC LIMIT 10;"
-```
-
-Report: total pending, processing rate (compare done count now vs 10 min ago), estimated completion time.
-If any source_agent has >10,000 pending jobs, flag it.
diff --git a/tasks/done/09_run-vendor-standardization.md b/tasks/done/09_run-vendor-standardization.md
deleted file mode 100644
index 3a27288..0000000
--- a/tasks/done/09_run-vendor-standardization.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Run Vendor Standardization Engine (P2-09)
-
-Session #108 fixed both bugs:
-- Bug #1: Pagination gap — now queries shopify_products directly (238 vendor names vs 91)
-- Bug #2: Vendor mapping — added 34 new codes, 0 unmapped vendors
-
-The standardization engine is ready to run.
-
-### Steps:
-1. Check the script: ls /root/DW-Agents/scripts/run-all-standardization.js
-2. Run in dry-run mode first: node /root/DW-Agents/scripts/run-all-standardization.js --dry-run 2>&1 | tail -30
-3. Review the dry-run output — confirm it finds products to standardize
-4. If dry-run looks good, run live: node /root/DW-Agents/scripts/run-all-standardization.js --limit 500 2>&1
-
-Rate limit: 0.5 req/s on Shopify API
-Shop: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-Report: how many products standardized, which vendors affected, any errors.
\ No newline at end of file
diff --git a/tasks/done/10_10_enrich-koroseal.md b/tasks/done/10_10_enrich-koroseal.md
deleted file mode 100644
index ddf94b8..0000000
--- a/tasks/done/10_10_enrich-koroseal.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-koroseal-specs.js
-
-Enrich Koroseal catalog with specs.
\ No newline at end of file
diff --git a/tasks/done/10_10_pm2-log-cleanup.md b/tasks/done/10_10_pm2-log-cleanup.md
deleted file mode 100644
index 13a4a99..0000000
--- a/tasks/done/10_10_pm2-log-cleanup.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Clean PM2 logs if they are getting large.
-1. du -sh ~/.pm2/logs/
-2. If over 200MB, run: pm2 flush
-3. Check /tmp for files older than 7 days: find /tmp -maxdepth 1 -type f -mtime +7 -delete 2>/dev/null
-4. Check npm cache: du -sh ~/.npm/_npx/ && npm cache clean --force 2>/dev/null
-5. Report disk before and after: df -h /
\ No newline at end of file
diff --git a/tasks/done/10_10_vcc-dashboard-fix.md b/tasks/done/10_10_vcc-dashboard-fix.md
deleted file mode 100644
index bc0ca95..0000000
--- a/tasks/done/10_10_vcc-dashboard-fix.md
+++ /dev/null
@@ -1 +0,0 @@
-The Vendor Command Center dashboard at port 9660 has a broken /api/status endpoint returning 404. Check the VCC server file at /root/DW-Agents/vendor-command-center/server.js. Find the /api/status route or lack thereof. If missing, check what routes exist. Also check if the dashboard HTML references /api/status and what data it expects. Report findings to /root/DW-Agents/logs/vcc-status-fix.log. If the fix is simple (just adding a route that returns aggregate counts from vendor_registry), implement it and restart vendor-command-center via pm2.
\ No newline at end of file
diff --git a/tasks/done/10_1838-push-prep.md b/tasks/done/10_1838-push-prep.md
deleted file mode 100644
index 86a0050..0000000
--- a/tasks/done/10_1838-push-prep.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# 1838 Wallcoverings — Validate Products for Shopify Push
-
-1838 has 404 products in w1838_catalog with repeat_h now populated. Check push readiness:
-
-1. Count products with: image_url, width, mfr_sku, repeat_v, repeat_h (all required)
-2. Count products already on Shopify (shopify_product_id IS NOT NULL)
-3. Count products ready to push (pass validation, not on Shopify)
-4. Report vendor_registry entry for 1838 (sku_prefix should be DWPT-)
-
-Do NOT push. Just validate and report numbers.
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
diff --git a/tasks/done/10_build-scraper-york-contract.md b/tasks/done/10_build-scraper-york-contract.md
deleted file mode 100644
index 86f78f1..0000000
--- a/tasks/done/10_build-scraper-york-contract.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Build a vendor scraper for York Contract wallcoverings (commercial division of York).
-
-Directory: /root/DW-Agents/vendor-scrapers/
-Output file: scrape-yorkcontract.js
-DB table: york_contract_catalog
-
-Use scraper-utils.js upsertProduct() pattern.
-
-1. Visit yorkcontract.com and find their commercial wallcovering products
-2. Extract: pattern_name, collection, color, mfr_sku, image_url, product_url, all_images, description, specs
-3. Upsert to york_contract_catalog using scraper-utils
-4. Test with small batch first
-
-CRITICAL: Use mfr_sku field. Include all_images array.
\ No newline at end of file
diff --git a/tasks/done/10_catalog-spec-fill-audit.md b/tasks/done/10_catalog-spec-fill-audit.md
deleted file mode 100644
index a9b7366..0000000
--- a/tasks/done/10_catalog-spec-fill-audit.md
+++ /dev/null
@@ -1,12 +0,0 @@
-Run a comprehensive spec fill rate audit across all vendor catalog tables.
-
-Working directory: /root/DW-Agents
-
-1. Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-2. Query every table ending in '_catalog' to check fill rates for: image_url, all_images, width, material, repeat_v, fire_rating, body_html, mfr_sku
-3. Calculate fill percentages per vendor per field
-4. Identify the 10 worst-performing catalogs (most empty fields)
-5. Save a markdown report to /root/DW-Agents/logs/catalog-spec-audit-$(date +%Y%m%d).md
-6. Send a Slack notification summary via: curl -X POST -H 'Content-type: application/json' --data '{"text":"Catalog Spec Audit Complete - see logs/catalog-spec-audit report"}' ${SLACK_WEBHOOK_URL}
-
-Focus on actionable insights — which vendors need the most scraper improvements.
diff --git a/tasks/done/10_collections-fix-excluded.md b/tasks/done/10_collections-fix-excluded.md
deleted file mode 100644
index ab90683..0000000
--- a/tasks/done/10_collections-fix-excluded.md
+++ /dev/null
@@ -1,34 +0,0 @@
-COLLECTIONS CLEANUP — Phase 5: Fix excluded collections that should be included
-
-Review all manual collections with 10+ products. Many are "excluded" from the storefront (published_at is null or published is false). Fix them if products are correctly named and the collection rules would pull the right products.
-
-Shopify store: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-## Process
-1. Fetch ALL manual collections with 10+ products
-2. For each, check if it's published:
-```graphql
-query { collection(id: "gid://shopify/Collection/ID") { title publishedOnCurrentPublication productsCount { count } } }
-```
-3. If unpublished AND has 10+ products:
- - Check if the products in it are correctly titled (not "Default Title", not empty)
- - Check if the collection title makes sense (not "test", "temp", "delete me")
- - If valid: publish it via `PUT /admin/api/2024-07/custom_collections/{id}.json` with `published: true`
- - If not valid: leave unpublished
-
-4. Also check smart collections with valid rules that are unpublished — same logic, publish if 10+ products and valid rules.
-
-## Exclusion Rules — DO NOT publish these:
-- Collections with "test", "temp", "old", "delete", "archive" in title
-- Collections for internal use (title contains "internal", "agent", "script")
-- Private label real-vendor collections (would expose vendor names)
-- Collections with <10 products (too small to show)
-
-## Also Fix Product Naming
-For each published collection, spot-check 3 products:
-- Title should NOT be "Default Title" or empty
-- Title should follow format: "Pattern - Color ProductType | Vendor"
-- If products have bad titles, flag the collection but still publish if majority are OK
-
-Report: how many collections published, how many flagged for product naming issues.
diff --git a/tasks/done/10_database-vacuum-analyze.md b/tasks/done/10_database-vacuum-analyze.md
deleted file mode 100644
index cdbde6a..0000000
--- a/tasks/done/10_database-vacuum-analyze.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Database Vacuum & Analyze
-
-Routine maintenance task. With 232K SKUs across 122+ tables, the database needs regular vacuum and analyze.
-
-### Tasks:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check database size: `SELECT pg_size_pretty(pg_database_size('dw_unified'));`
-3. Find tables that haven't been vacuumed recently:
- ```sql
- SELECT schemaname, relname, last_vacuum, last_autovacuum, last_analyze, n_dead_tup
- FROM pg_stat_user_tables
- ORDER BY n_dead_tup DESC NULLS LAST
- LIMIT 20;
- ```
-4. Run `VACUUM ANALYZE` on the top 20 tables with most dead tuples
-5. Check for bloated indexes:
- ```sql
- SELECT schemaname, tablename, indexname, pg_size_pretty(pg_relation_size(indexrelid))
- FROM pg_stat_user_indexes
- ORDER BY pg_relation_size(indexrelid) DESC
- LIMIT 20;
- ```
-6. Report: DB size, dead tuples cleaned, largest tables/indexes
diff --git a/tasks/done/10_fix-missing-samples.md b/tasks/done/10_fix-missing-samples.md
deleted file mode 100644
index bb379b8..0000000
--- a/tasks/done/10_fix-missing-samples.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Fix Missing Sample Variants — All Active Products
-
-Scan ALL active Shopify products. For any product missing a $4.25 Sample variant, add one.
-
-Use GraphQL to batch-read products (250 at a time). For each product:
-- Check if any variant has SKU ending in -Sample OR price=4.25 OR title=Sample
-- If missing, create via REST: POST /admin/api/2024-01/products/{id}/variants.json
- - option1: "Sample", price: "4.25", sku: "{base_sku}-Sample", weight: 0, inventory_management: "shopify"
-- After creating, set inventory to 2026 at location 5795643504
-
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Rate limit: 500ms between calls. If 429 "Daily variant creation limit", STOP and report count remaining.
diff --git a/tasks/done/10_nobilis-enrich.md b/tasks/done/10_nobilis-enrich.md
deleted file mode 100644
index 55fb6dc..0000000
--- a/tasks/done/10_nobilis-enrich.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Nobilis — Enrich Missing Specs
-
-176 products in `nobilis_catalog`. Has 176 width and 119 repeat, but spec completeness is only 54%. Check what other fields are missing.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check what's populated vs missing:
- ```sql
- SELECT
- COUNT(*) as total,
- COUNT(CASE WHEN width IS NOT NULL AND width != '' THEN 1 END) as has_width,
- COUNT(CASE WHEN repeat_v IS NOT NULL AND repeat_v != '' THEN 1 END) as has_repeat,
- COUNT(CASE WHEN material IS NOT NULL AND material != '' THEN 1 END) as has_material,
- COUNT(CASE WHEN fire_rating IS NOT NULL AND fire_rating != '' THEN 1 END) as has_fire,
- COUNT(CASE WHEN match_type IS NOT NULL AND match_type != '' THEN 1 END) as has_match
- FROM nobilis_catalog;
- ```
-3. If specs JSON exists with data, extract to dedicated columns.
-4. For remaining gaps, scrape from product_url. Nobilis site: https://www.nobilis.fr/
-5. Write enrichment script if needed at `/root/DW-Agents/vendor-scrapers/enrich-nobilis.js`
-6. Run and report.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/10_phase2-kravet-specs.md b/tasks/done/10_phase2-kravet-specs.md
deleted file mode 100644
index b765f45..0000000
--- a/tasks/done/10_phase2-kravet-specs.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Phase 2: Kravet Specs Enrichment (811 gap)
-
-## Goal
-Enrich 811 Kravet products missing specs via Algolia API.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 600 node enrich-kravet-algolia.js
-```
-
-After completion, sync results from kravet_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(kc.width, vc.width),
- pattern_repeat = COALESCE(kc.repeat_v, vc.pattern_repeat),
- updated_at = NOW()
-FROM kravet_catalog kc
-WHERE vc.mfr_sku = kc.mfr_sku AND vc.vendor_code = 'kravet'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '')
- AND (kc.width IS NOT NULL OR kc.repeat_v IS NOT NULL);
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/10_phoebe-dreamteam-fix.md b/tasks/done/10_phoebe-dreamteam-fix.md
deleted file mode 100644
index c1bc6b1..0000000
--- a/tasks/done/10_phoebe-dreamteam-fix.md
+++ /dev/null
@@ -1,12 +0,0 @@
-Fix Phoebe (Figma QA agent) and Dream Team health.
-
-Phoebe: port 9668, PM2: figma-agent
-
-1. Find Phoebe path: pm2 show figma-agent | grep "script path"
-2. Create missing videos directory that crashes Phoebe home route
-3. Restart Phoebe: pm2 restart figma-agent
-4. Verify all Dream Team agents are healthy:
- - Sandy 9652, Sherman 9650, Sheldon 9651, Shane 9653, Phoebe 9668
- - curl -u admin:DWSecure2024! http://localhost:PORT/health for each
-5. Fix any that are down
-6. Report status of all 5
diff --git a/tasks/done/10_resume-weight-fix.md b/tasks/done/10_resume-weight-fix.md
deleted file mode 100644
index a51acdc..0000000
--- a/tasks/done/10_resume-weight-fix.md
+++ /dev/null
@@ -1,10 +0,0 @@
-## Resume Weight Fix (10,217 variants remaining)
-
-The weight fix was at 32,650/42,867 (76%). It needs to be restarted. The script is idempotent — it re-scans all active products and fixes any variant with non-zero weight.
-
-### Steps:
-1. Check API bucket: curl -sI -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/shop.json" | grep call-limit
-2. If bucket < 40/80, start the fix:
- cd /root/DW-Agents && pm2 start scripts/fix-sample-weights.js --name fix-sample-weights --no-autorestart
-3. Wait 60 seconds, check progress: pm2 logs fix-sample-weights --lines 3 --nostream
-4. Report progress and estimated completion time
\ No newline at end of file
diff --git a/tasks/done/10_scalamandre-mfr-sku-format-analysis.md b/tasks/done/10_scalamandre-mfr-sku-format-analysis.md
deleted file mode 100644
index d013074..0000000
--- a/tasks/done/10_scalamandre-mfr-sku-format-analysis.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Scalamandre mfr_sku Format Mismatch Investigation
-
-**Context**: The drift audit (commit dff14da82) found 313 Scalamandre Wallpaper orphans on Shopify. The scalamandre_catalog table has 4,819 rows with mfr_sku populated, but only 16 rows have shopify_product_id set. That means 4,803 catalog entries exist but aren't linked to any Shopify product, and 313 Shopify products can't find their catalog row by mfr_sku.
-
-This is NOT a missing-catalog problem. It's a **mfr_sku format mismatch** — the SKUs on Shopify don't normalize to the same form as the SKUs in scalamandre_catalog.
-
-**Goal**: Figure out the format difference so we can write a targeted relink script for Scalamandre.
-
-**Read-only investigation task. NO WRITES.**
-
-## Steps
-
-1. Query 10 sample rows from scalamandre_catalog showing mfr_sku, pattern_name, color_name
- ```sql
- SELECT mfr_sku, pattern_name, color_name, collection
- FROM scalamandre_catalog WHERE mfr_sku IS NOT NULL ORDER BY random() LIMIT 10;
- ```
-
-2. Query 10 sample Shopify Scalamandre orphans by joining gap_audit with vendor_catalog absence:
- - Get shopify_product_id from gap_audit WHERE vendor='Scalamandre Wallpaper'
- - Exclude ones that link to vendor_catalog or scalamandre_catalog
- - Get their `custom.manufacturer_sku` metafield from Shopify via GraphQL
- - Show mfr_sku + title side by side
-
-3. Compare the two samples. Look for:
- - Case sensitivity (ABC-123 vs abc-123)
- - Prefix differences (SC-ABC-123 vs ABC-123)
- - Hyphen/space placement
- - Color suffix (ABC-123 vs ABC-123-Blue)
- - Leading zeros
-
-4. If you find a transformation rule (e.g., "PG strips the SC- prefix that Shopify has"), test it:
- - Pick 5 Shopify orphan mfr_skus
- - Apply the transformation
- - Query scalamandre_catalog with the transformed key
- - Report: how many of 5 now match?
-
-5. Write findings to `/root/DW-Agents/logs/scalamandre-format-analysis.md` with:
- - Sample data from both sides
- - Format difference hypothesis
- - Transformation rule (if found)
- - Test results (hit rate on 5 samples)
- - Recommendation: "can we auto-relink with this rule?" — yes/no + confidence
-
-## Success Criteria
-- Investigation report file exists at `/root/DW-Agents/logs/scalamandre-format-analysis.md`
-- Contains at least one concrete format difference hypothesis
-- Uses real data, no speculation
-- Does NOT modify any database or Shopify data
-
-## References
-- Drift audit code: `/root/DW-Agents/scripts/drift-audit.js`
-- Relink script: `/root/DW-Agents/scripts/relink-orphans.js`
-- Triage bucket 2: `/root/DW-Agents/logs/orphan-triage.json`
-- Previous session notes: `/root/.claude/SESSION-LEARNINGS.md` (search "Scalamandre")
-- DB connection: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-
-**Time budget**: 15 minutes max. Report partial findings rather than running long.
diff --git a/tasks/done/10_shopify-imageless-audit.md b/tasks/done/10_shopify-imageless-audit.md
deleted file mode 100644
index f477479..0000000
--- a/tasks/done/10_shopify-imageless-audit.md
+++ /dev/null
@@ -1,25 +0,0 @@
-Find active Shopify products with zero images.
-
-Use Shopify GraphQL API:
-- Store: designer-laboratory-sandbox.myshopify.com
-- Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-Query:
-{
- products(first: 50, query: "status:active") {
- edges {
- node {
- id
- title
- vendor
- featuredImage { url }
- totalInventory
- }
- }
- }
-}
-
-1. Page through results looking for products where featuredImage is null
-2. Count total imageless products per vendor
-3. Write results to /root/DW-Agents/yolo-agent/reports/imageless-products.json
-4. Report: total imageless active products, top 5 vendors with most imageless
diff --git a/tasks/done/11_11_bespoke-vendor-consolidation.md b/tasks/done/11_11_bespoke-vendor-consolidation.md
deleted file mode 100644
index 93bc588..0000000
--- a/tasks/done/11_11_bespoke-vendor-consolidation.md
+++ /dev/null
@@ -1 +0,0 @@
-On Shopify there are 3 vendor names for the same brand: Bespoke, DW Bespoke Studios, and DW Bespoke Studios to go. They should be consolidated under one name. Using token <redacted:SHOPIFY_ADMIN_TOKEN>, count products per vendor name. Report which has the most products. The plan is to keep one name and archive the others. Do NOT archive yet - just report counts and which products belong to which vendor name. Save to /root/DW-Agents/logs/bespoke-consolidation-report.json
\ No newline at end of file
diff --git a/tasks/done/11_11_enrich-kravet.md b/tasks/done/11_11_enrich-kravet.md
deleted file mode 100644
index d5eb3be..0000000
--- a/tasks/done/11_11_enrich-kravet.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Check Kravet catalog width coverage:
-timeout 10 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width FROM kravet_catalog;"
-If low, look for enrichment scripts: ls /root/DW-Agents/vendor-scrapers/*kravet* and run the appropriate one.
\ No newline at end of file
diff --git a/tasks/done/11_11_enrich-newwall.md b/tasks/done/11_11_enrich-newwall.md
deleted file mode 100644
index c19ebe1..0000000
--- a/tasks/done/11_11_enrich-newwall.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-newwall-specs.js
-
-Enrich Newwall catalog with specs.
\ No newline at end of file
diff --git a/tasks/done/11_blog-post-session109.md b/tasks/done/11_blog-post-session109.md
deleted file mode 100644
index 2a7bf14..0000000
--- a/tasks/done/11_blog-post-session109.md
+++ /dev/null
@@ -1,22 +0,0 @@
-## Publish Blog Post — Session #109 Results
-
-Generate and publish a blog post to goodquestion.ai about data quality automation.
-
-### Topic: "How We Fixed 20,000 Product Pages in One Session"
-
-Key points to cover (sanitized — NO vendor names, NO product counts, NO internal details):
-- AI agents autonomously audited and enriched a 60K+ product catalog
-- 1,099 dead products identified and archived via automated scan
-- Image coverage improved from 0% to 72% for an entire vendor in under 10 minutes
-- Spec data assessment revealed 20K+ products ready for enrichment
-- 5 parallel AI agents completed research tasks simultaneously
-
-### Pipeline:
-1. cd /root/Projects/goodquestion-ai
-2. Generate post using blog-agent: curl -s -u admin:DWSecure2024! -X POST http://45.61.58.125:9871/api/generate -H 'Content-Type: application/json' -d '{"topic":"autonomous data quality pipelines","angle":"one person + AI agents fixing 20K products"}'
-3. If blog-agent not responding, create post manually in /root/Projects/goodquestion-ai/src/content/posts/
-4. Run sanitizer: node /root/DW-Agents/blog-agent/sanitizer.js on the post content
-5. Build: cd /root/Projects/goodquestion-ai && npm run build
-6. Git commit and push to deploy
-
-CRITICAL: Run ALL content through the sanitizer. Never mention wallcoverings, vendor names, pricing, or internal details.
\ No newline at end of file
diff --git a/tasks/done/11_blog-post.md b/tasks/done/11_blog-post.md
deleted file mode 100644
index 648e49a..0000000
--- a/tasks/done/11_blog-post.md
+++ /dev/null
@@ -1,11 +0,0 @@
-Generate and publish a blog post on goodquestion.ai.
-
-Topic: "How One Person Runs 150+ Autonomous Agents on a Single $80/Month Server"
-
-The blog targets entrepreneurs and founders. Tone: practical, direct, no-nonsense.
-
-Steps:
-1. Check the blog agent: curl -s -u admin:DWSecure2024! http://45.61.58.125:9871/api/status
-2. Trigger a blog post: curl -s -u admin:DWSecure2024! -X POST http://45.61.58.125:9871/api/generate -H "Content-Type: application/json" -d '{"topic":"How One Person Runs 150+ Autonomous Agents on a Single $80/Month Server","angle":"Show real PM2 process counts, real costs, real infrastructure. No theory - just what works. Mention Claude Code, Gemini for vision tasks, PostgreSQL for pipeline state, PM2 for process management. End with the board seat CTA."}'
-3. Wait 30 seconds then check: curl -s -u admin:DWSecure2024! http://45.61.58.125:9871/api/recent
-4. Report the published URL
diff --git a/tasks/done/11_fix-broken-scrapers.md b/tasks/done/11_fix-broken-scrapers.md
deleted file mode 100644
index 921deb9..0000000
--- a/tasks/done/11_fix-broken-scrapers.md
+++ /dev/null
@@ -1,16 +0,0 @@
-Audit and fix broken vendor scrapers in /root/DW-Agents/vendor-scrapers/.
-
-Working directory: /root/DW-Agents
-
-1. List all scrape-*.js files in vendor-scrapers/
-2. Check each one for common issues:
- - Corrupted shebangs (\! instead of !)
- - Missing scraper-utils.js import
- - Using vendor_sku instead of mfr_sku
- - Missing all_images extraction
- - Missing room_setting_images extraction
- - Hardcoded timeouts under 60000ms (should be 300000+ for Puppeteer scrapers)
-3. Fix any issues found (edit the files directly)
-4. Log all fixes to /root/DW-Agents/logs/scraper-fixes-$(date +%Y%m%d).md
-
-DO NOT run the scrapers — just audit and fix the code. Be conservative — only fix clear bugs.
diff --git a/tasks/done/11_fix-sample-variants-p0.md b/tasks/done/11_fix-sample-variants-p0.md
deleted file mode 100644
index 7145c67..0000000
--- a/tasks/done/11_fix-sample-variants-p0.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Fix ~6,166 Sample Variant Issues (P0-01)
-
-3,933 products missing Sample variants + 2,233 with wrong Sample price ($4.25 expected).
-
-### Tasks:
-1. Query Shopify for all products missing a "Sample" variant
-2. Query Shopify for all products where Sample variant price != $4.25
-3. For missing samples: create a "Sample" variant at $4.25, weight 0.3 lb
-4. For wrong-price samples: update price to $4.25
-5. Use VCC fix endpoint if available: POST http://45.61.58.125:9660/api/fix/sample-variants
-6. Report: total fixed, errors, products still needing attention
-
-Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-Store: `designer-laboratory-sandbox.myshopify.com`
-API version: 2024-01
-
-IMPORTANT: Respect Shopify rate limits (2 req/s, 80 bucket). Add 500ms delays between calls.
diff --git a/tasks/done/11_innovations-mfr-sku-format-analysis.md b/tasks/done/11_innovations-mfr-sku-format-analysis.md
deleted file mode 100644
index dd8aec5..0000000
--- a/tasks/done/11_innovations-mfr-sku-format-analysis.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Innovations USA mfr_sku Format Mismatch Investigation
-
-**Context**: The orphan triage (commit 4423ead47) found 125 Innovations USA orphans on Shopify in Bucket 2 (catalog exists, mfr_sku format mismatch). The innovations_catalog table exists and has data, but 125 Shopify Innovations products can't find their catalog row by mfr_sku.
-
-**Goal**: Identify the SKU format difference so we can write a targeted relink rule.
-
-**Read-only investigation task. NO WRITES.**
-
-## Steps
-
-1. Query 10 sample rows from innovations_catalog showing mfr_sku, pattern_name, color_name, collection
-2. Query 10 sample Innovations USA Shopify orphans via gap_audit ⋂ innovations_catalog absence, fetch their custom.manufacturer_sku metafields from Shopify
-3. Compare the two samples — look for case, prefix, hyphen placement, color suffix, leading zero differences
-4. Test any transformation hypothesis on 5 samples — report hit rate
-5. Write findings to `/root/DW-Agents/logs/innovations-format-analysis.md`
-
-## Success Criteria
-- Report file exists with sample data from both sides, concrete hypothesis, and test results
-- Does NOT modify any database or Shopify data
-
-## References
-- Triage report: `/root/DW-Agents/logs/orphan-triage.json`
-- DB connection: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-
-**Time budget**: 15 minutes max.
diff --git a/tasks/done/11_interior-design-tagger.md b/tasks/done/11_interior-design-tagger.md
deleted file mode 100644
index d735639..0000000
--- a/tasks/done/11_interior-design-tagger.md
+++ /dev/null
@@ -1,20 +0,0 @@
-Interior design tagger automation — build and run on justindavid + phillipe_romano catalogs.
-
-From Session 101: The interior-design-tagger skill exists at /root/.claude/skills/interior-design-tagger/SKILL.md but NO automated pipeline runs it.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Tables: justindavid_catalog, phillipe_romano_catalog (~9,392 products total)
-Gemini key: ${GOOGLE_API_KEY}
-Model: gemini-2.0-flash (vision)
-
-1. Read the skill file to understand the tagging logic
-2. Count products needing tagging in both tables (interior_design_status IS NULL or != 'done')
-3. Build a Python script /root/DW-Agents/vendor-scrapers/interior-design-tagger.py that:
- - Queries products with image_url but no interior_design_status
- - Sends image to Gemini vision with the skill prompt
- - Parses response for design tags (style, room type, color palette, pattern)
- - Updates DB with tags and sets interior_design_status = 'done'
- - Rate limits to 15 req/min
-4. Run on a small batch first (--limit 20)
-5. If working, run full batch (use Gemini Batch API for 50% savings if >100)
-6. Report: how many tagged, sample output
diff --git a/tasks/done/11_phase2-coordonne-specs.md b/tasks/done/11_phase2-coordonne-specs.md
deleted file mode 100644
index 12c895d..0000000
--- a/tasks/done/11_phase2-coordonne-specs.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Phase 2: Coordonne Specs Enrichment (1,291 gap)
-
-## Goal
-Scrape specs from coordonne.com for 1,291 products missing width/repeat/material.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node enrich-coordonne-specs.js
-```
-
-After completion, sync from coordonne_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(cc.width, vc.width),
- pattern_repeat = COALESCE(cc.repeat_v, vc.pattern_repeat),
- composition = COALESCE(cc.material, vc.composition),
- fire_rating = COALESCE(cc.fire_rating, vc.fire_rating),
- updated_at = NOW()
-FROM coordonne_catalog cc
-WHERE vc.mfr_sku = cc.mfr_sku AND vc.vendor_code = 'coordonne'
- AND (vc.width IS NULL OR vc.width = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/11_pm2-health-report.md b/tasks/done/11_pm2-health-report.md
deleted file mode 100644
index 51ec6b6..0000000
--- a/tasks/done/11_pm2-health-report.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# PM2 Health Report
-
-Check all PM2 processes. Report:
-1. Any errored/stopped processes
-2. Any process using >500MB memory
-3. Restart any errored processes
-4. Check disk usage (df -h)
-5. Check if any Claude processes are zombies (>12h old)
-
-```bash
-pm2 jlist | python3 -c "import sys,json; [print(f'{p[\"name\"]}: {p[\"pm2_env\"][\"status\"]}') for p in json.load(sys.stdin) if p['pm2_env']['status'] != 'online']"
-```
diff --git a/tasks/done/11_thibaut-302-archive.md b/tasks/done/11_thibaut-302-archive.md
deleted file mode 100644
index dbb3b81..0000000
--- a/tasks/done/11_thibaut-302-archive.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## Thibaut 302 Discontinued Products — Archive on Shopify
-
-Session #102 found 302 Thibaut products returning 301 redirects (discontinued on vendor site).
-
-### Tasks:
-1. Query `thibaut_catalog` for products where `discontinued = true` OR `product_url` returns 301/404
-2. Cross-reference with Shopify: find active products matching these SKUs
-3. Archive them on Shopify (set status to 'archived' via Admin API)
-4. Update DB: set `discontinued = true` where not already set
-5. Report: count archived, count already archived, count not on Shopify
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-Store: `designer-laboratory-sandbox.myshopify.com`
-Rate limit: 2 req/s. Use delays.
diff --git a/tasks/done/12_12_enrich-marburg.md b/tasks/done/12_12_enrich-marburg.md
deleted file mode 100644
index 387f654..0000000
--- a/tasks/done/12_12_enrich-marburg.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Check Marburg catalog:
-timeout 10 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width, COUNT(CASE WHEN image_url IS NOT NULL THEN 1 END) as has_image FROM marburg_catalog;"
-Look for enrichment scripts: ls /root/DW-Agents/vendor-scrapers/*marburg*
-Run if found. Report results.
\ No newline at end of file
diff --git a/tasks/done/12_12_enrich-nicolettemayer.md b/tasks/done/12_12_enrich-nicolettemayer.md
deleted file mode 100644
index c0a4be8..0000000
--- a/tasks/done/12_12_enrich-nicolettemayer.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-nicolettemayer-specs.js
-
-Enrich Nicolette Mayer catalog.
\ No newline at end of file
diff --git a/tasks/done/12_12_sample-weight-fix.md b/tasks/done/12_12_sample-weight-fix.md
deleted file mode 100644
index 4ddf98e..0000000
--- a/tasks/done/12_12_sample-weight-fix.md
+++ /dev/null
@@ -1 +0,0 @@
-There are 294 products on Shopify with non-zero sample variant weights. Samples should weigh 0 for free shipping calculation. Using token <redacted:SHOPIFY_ADMIN_TOKEN>, find all variants where option1 contains Sample and weight > 0. For each, update the variant weight to 0 using PUT /admin/api/2024-01/variants/{variant_id}.json. Rate limit 2/sec. Log count fixed to /root/DW-Agents/logs/sample-weight-fix.log
\ No newline at end of file
diff --git a/tasks/done/12_british-walls-enrich.md b/tasks/done/12_british-walls-enrich.md
deleted file mode 100644
index 7a49a46..0000000
--- a/tasks/done/12_british-walls-enrich.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## British Walls — Scrape for Width, Repeat & Images
-
-506 products in `british_walls_catalog`. Missing width (0), repeat (0), and images (0!). Need full enrichment.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check schema: `SELECT column_name FROM information_schema.columns WHERE table_name = 'british_walls_catalog' ORDER BY ordinal_position;`
-3. Add missing columns if needed.
-4. Check specs: `SELECT LEFT(specs::text, 300) FROM british_walls_catalog WHERE specs IS NOT NULL AND specs::text != '{}' LIMIT 3;`
-5. Sample URL: `SELECT product_url FROM british_walls_catalog WHERE product_url IS NOT NULL LIMIT 3`
-6. Fetch sample, parse for width/repeat/material/images.
-7. Write enrichment script at `/root/DW-Agents/vendor-scrapers/enrich-british-walls.js` — limit 200 products.
-8. Run: `timeout 600 node enrich-british-walls.js`
-9. Report.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/12_db-maintenance.md b/tasks/done/12_db-maintenance.md
deleted file mode 100644
index 9ec00dc..0000000
--- a/tasks/done/12_db-maintenance.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Run PostgreSQL maintenance on the dw_unified database.
-
-Connect: psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-1. Run ANALYZE on the most important tables:
- ANALYZE enrichment_tracking;
- ANALYZE vendor_registry;
-2. Check table sizes:
- SELECT relname, pg_size_pretty(pg_total_relation_size(oid)) FROM pg_class WHERE relkind='r' ORDER BY pg_total_relation_size(oid) DESC LIMIT 20;
-3. Check for bloated tables (dead tuples):
- SELECT relname, n_dead_tup, n_live_tup, round(n_dead_tup::numeric/greatest(n_live_tup,1)*100, 1) as dead_pct FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY n_dead_tup DESC LIMIT 10;
-4. If any table has >20% dead tuples, run VACUUM ANALYZE on it
-5. Report table sizes and any maintenance performed
diff --git a/tasks/done/12_phase2-schumacher-specs.md b/tasks/done/12_phase2-schumacher-specs.md
deleted file mode 100644
index 7068e40..0000000
--- a/tasks/done/12_phase2-schumacher-specs.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Phase 2: Schumacher Specs Enrichment (1,290 gap)
-
-## Goal
-Enrich 1,290 Schumacher products missing width. An existing width script exists.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node enrich-schumacher-width.js
-```
-
-After completion, sync from schumacher_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(sc.width, vc.width),
- pattern_repeat = COALESCE(sc.repeat_v, vc.pattern_repeat),
- composition = COALESCE(sc.material, vc.composition),
- updated_at = NOW()
-FROM schumacher_catalog sc
-WHERE vc.mfr_sku = sc.mfr_sku AND vc.vendor_code = 'schumacher'
- AND (vc.width IS NULL OR vc.width = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/12_pm2-health-audit.md b/tasks/done/12_pm2-health-audit.md
deleted file mode 100644
index 37862f9..0000000
--- a/tasks/done/12_pm2-health-audit.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Run a comprehensive PM2 health audit and fix any issues.
-
-Working directory: /root/DW-Agents
-
-1. Run pm2 list and check for any agents with status "errored" or "stopped"
-2. For any errored agents, check their error logs (pm2 logs <name> --err --lines 20)
-3. Restart any crashed agents
-4. Check for zombie Claude processes older than 12 hours: ps aux | grep claude | grep -v grep
-5. Kill any zombie processes (log PID and task first)
-6. Check disk space (df -h) and memory (free -m)
-7. Check if any PM2 process is using >500MB memory
-8. Save full health report to /root/DW-Agents/logs/pm2-health-$(date +%Y%m%d).md
-
-If disk space is below 10GB free, identify largest files that can be cleaned up.
diff --git a/tasks/done/12_shop-by-color-phase3.md b/tasks/done/12_shop-by-color-phase3.md
deleted file mode 100644
index 50726d9..0000000
--- a/tasks/done/12_shop-by-color-phase3.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Shop by Color Phase 3 — CSS Conic-Gradient Wheel + Lazy Loading
-
-Sessions #92-96 planned Phase 3 improvements for the Shop by Color page.
-
-### Tasks:
-1. Read the current Shop by Color page from Shopify (page ID 79213035571) using Pages API
-2. Add CSS conic-gradient color wheel replacement (currently canvas-based)
-3. Add lazy loading for product images (IntersectionObserver-based)
-4. Add mobile hue strip for small screens (horizontal gradient bar alternative to color wheel)
-5. Push updated page to Shopify
-6. Report: what was changed, new page size
-
-Shopify content token: `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`
-Store: `designer-laboratory-sandbox.myshopify.com`
-Page ID: 79213035571
-
-Keep all existing functionality intact. Only ADD Phase 3 features.
diff --git a/tasks/done/12_social-posts.md b/tasks/done/12_social-posts.md
deleted file mode 100644
index d20b651..0000000
--- a/tasks/done/12_social-posts.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## Post to Social Media — Agent Abrams
-
-Post about data quality automation to all social channels.
-
-### Twitter/X (@agentabrams):
-cd /root/Projects/goodquestion-ai && node scripts/tweet.cjs --text "Just ran 5 AI agents in parallel to audit 75,000 products. Found and archived 1,099 dead listings, enriched 5,400 product records to 100% data completeness, and pushed 418 images — all in one session. The future of e-commerce ops is autonomous. #AIAutomation #ClaudeCode"
-
-### Bluesky (@agentabrams.bsky.social):
-cd /root/Projects/goodquestion-ai && node scripts/bsky-post.cjs --text "5 AI agents. 75,000 products audited. 1,099 dead listings archived. 5,400 records enriched to 100%. 418 images pushed. One session. One person. This is what autonomous e-commerce operations looks like." --url "https://goodquestion.ai" --link-text "Read more on goodquestion.ai"
-
-### Notes:
-- Check if Twitter API billing is active first (was 402 in previous session)
-- If Twitter fails with 402, skip and note the error
-- Bluesky should work (credentials in .env: BSKY_HANDLE, BSKY_PASSWORD)
\ No newline at end of file
diff --git a/tasks/done/12_weekly-relink-orphans.md b/tasks/done/12_weekly-relink-orphans.md
deleted file mode 100644
index ed03954..0000000
--- a/tasks/done/12_weekly-relink-orphans.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Weekly Relink Orphans Run
-
-**Context**: The `relink-orphans.js` script matches Shopify orphan products to their vendor catalog rows by mfr_sku. It should run weekly to catch new drift before it accumulates. The last manual LIVE run (2026-04-10) relinked 3,979 rows.
-
-**Goal**: Run the relink script in LIVE mode, report the results, log to Slack if significant changes.
-
-## Steps
-
-1. Run the relink script LIVE:
- ```bash
- cd /root/DW-Agents && timeout 600 node scripts/relink-orphans.js --live 2>&1 | tee /root/DW-Agents/logs/relink-weekly-$(date +%Y%m%d).log
- ```
-
-2. Parse the output to extract:
- - Total orphans processed
- - Can auto-relink count
- - Updated catalog rows count
- - Failures
-
-3. Run the drift audit to see the post-relink state:
- ```bash
- timeout 60 node /root/DW-Agents/scripts/drift-audit.js
- ```
-
-4. If orphan count dropped by >10 since last run, post a Slack notification via:
- ```
- curl -X POST ${SLACK_WEBHOOK_URL} \
- -H 'Content-Type: application/json' \
- -d '{"text": "Weekly relink: relinked N rows, drift now M.MM%"}'
- ```
-
-5. Log result to `/root/DW-Agents/logs/relink-weekly.log` with date and counts.
-
-## Success Criteria
-- Script runs to completion
-- drift_audit_log has a fresh row
-- Slack notification sent (if threshold met)
-- Log file updated
-
-## References
-- Relink script: `/root/DW-Agents/scripts/relink-orphans.js`
-- Drift audit: `/root/DW-Agents/scripts/drift-audit.js`
-- Previous session commits: fa093bd59, dff14da82, ac1481da7, 4423ead47, 6538f96fd
-- Current state (2026-04-10): 1,690 orphans (3.27%), target <5%
-
-**Time budget**: 10 minutes.
diff --git a/tasks/done/12_york-catalog-images-p0.md b/tasks/done/12_york-catalog-images-p0.md
deleted file mode 100644
index a998d1f..0000000
--- a/tasks/done/12_york-catalog-images-p0.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## York Catalog Images — 5,304 Products at 0% (P0-02)
-
-York Wallcoverings has 5,304 products on Shopify with ZERO images. York uses the same NopCommerce/CDN platform as Brewster (bhf-cdn.azureedge.net).
-
-### Tasks:
-1. Check if `/root/DW-Agents/vendor-scrapers/scrape-york-images.js` exists — if so, run it
-2. If not, check the Brewster image scraper pattern at `/root/DW-Agents/vendor-scrapers/scrape-brewster-images.js` and adapt for York
-3. York product pages: `yorkwallcoverings.com/product/{sku}` — extract all CDN image URLs
-4. York quickview API (same as Brewster): `GET /quickviewdata?productId={nopId}` — returns images without Puppeteer
-5. Save images to york_catalog or w_york table in PostgreSQL
-6. Report: images found, images missing, sample product URLs
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-Check existing tables: `\dt *york*`
diff --git a/tasks/done/13_13_1838-dedup-check.md b/tasks/done/13_13_1838-dedup-check.md
deleted file mode 100644
index fba48bb..0000000
--- a/tasks/done/13_13_1838-dedup-check.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Check the 1838 catalog table for duplicate products. Query: SELECT mfr_sku, pattern_name, COUNT(*) as cnt FROM catalog_1838 GROUP BY mfr_sku, pattern_name HAVING COUNT(*) > 1. Also check the Aurora sub-collection which reportedly has 49 products (9 extra from a prior session). Report duplicates to /root/DW-Agents/logs/1838-dedup-report.json
\ No newline at end of file
diff --git a/tasks/done/13_13_draft-zero-image-audit.md b/tasks/done/13_13_draft-zero-image-audit.md
deleted file mode 100644
index 41c0af5..0000000
--- a/tasks/done/13_13_draft-zero-image-audit.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Find draft products with zero images on Shopify. Use token <redacted:SHOPIFY_ADMIN_TOKEN> store designer-laboratory-sandbox.myshopify.com.
-Get 50 draft products and check which have 0 images. Report: vendor breakdown of zero-image drafts, how many total, and recommend whether to archive or enrich them.
\ No newline at end of file
diff --git a/tasks/done/13_13_enrich-brewster.md b/tasks/done/13_13_enrich-brewster.md
deleted file mode 100644
index c51ac30..0000000
--- a/tasks/done/13_13_enrich-brewster.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-brewster-specs.js
-
-Enrich Brewster catalog with specs.
\ No newline at end of file
diff --git a/tasks/done/13_brewster-images-p0.md b/tasks/done/13_brewster-images-p0.md
deleted file mode 100644
index 9bc20b1..0000000
--- a/tasks/done/13_brewster-images-p0.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## Brewster Images — 21.6% Coverage (P0-03)
-
-6,834 Brewster products missing images on Shopify. Brewster uses NopCommerce with CDN at bhf-cdn.azureedge.net.
-
-### Tasks:
-1. Check existing scraper: `/root/DW-Agents/vendor-scrapers/scrape-brewster-images.js`
-2. Check enrichment scraper: `/root/DW-Agents/vendor-scrapers/enrich-brewster-specs.js`
-3. Use the quickview trick: `GET /quickviewdata?productId={nopId}` — returns all images without Puppeteer
-4. Query DB for Brewster products missing image_url: `SELECT COUNT(*) FROM brewster_catalog WHERE image_url IS NULL OR image_url = ''`
-5. Run the image scraper if it exists, or build one using the quickview API
-6. Report: total products, images found, images still missing
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-Image CDN: `bhf-cdn.azureedge.net/bhf-blob-prod/`
diff --git a/tasks/done/13_git-commit-session109.md b/tasks/done/13_git-commit-session109.md
deleted file mode 100644
index 831af94..0000000
--- a/tasks/done/13_git-commit-session109.md
+++ /dev/null
@@ -1,21 +0,0 @@
-## Git Commit — Session #109 Changes
-
-Commit all changes from Session #109.
-
-### Steps:
-1. cd /root/DW-Agents
-2. git status — review changes
-3. Stage new/modified files:
- - scripts/push-brewster-images.js
- - scripts/push-specs-vendor.py
- - scripts/push-vendor-specs.js
- - reports/hollywood-enrichment-report.txt
- - reports/spec-data-availability.txt
- - reports/single-variant-audit.json
- - reports/dwc-legacy-audit.json
- - reports/dwc-audit.py
- - reports/dwc-audit-refine.py
- - reports/dwc-audit-fix-arte.py
- - DW-TASK-LEDGER.md
-4. Commit: git commit -m "Session #109: Brewster images, Hollywood enrichment, specs assessment, 1099 products archived, DWC audit, blog links fixed"
-5. Push: git push origin main
\ No newline at end of file
diff --git a/tasks/done/13_hollywood-hex-extraction.md b/tasks/done/13_hollywood-hex-extraction.md
deleted file mode 100644
index c17c05e..0000000
--- a/tasks/done/13_hollywood-hex-extraction.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Hollywood Wallcoverings Gemini Hex Extraction
-
-Sessions #99 and #102: Hollywood's 4,770 products need Gemini hex color extraction (same as completed for Phillipe Romano).
-
-### Tasks:
-1. Check `product_colors` table for existing Hollywood entries
-2. Query `hollywood_catalog` for active products with `image_url IS NOT NULL`
-3. For each product image, call Gemini 2.0 Flash to extract dominant hex color
-4. Insert into `product_colors` table with vendor='Hollywood Wallcoverings'
-5. Generate color tags from hex values
-6. Use rate limiting: max 10 req/s to Gemini API
-7. For batches >100, use Gemini Batch API (50% cheaper)
-8. Report: processed count, hex codes found, errors
-
-Gemini API key: `${GOOGLE_API_KEY}`
-Model: `gemini-2.0-flash`
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/13_innovations-true-bug-discovery.md b/tasks/done/13_innovations-true-bug-discovery.md
deleted file mode 100644
index 2789054..0000000
--- a/tasks/done/13_innovations-true-bug-discovery.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# Innovations USA "TRUE" Metafield Bug — Discovery & Fix Generator
-
-**Context**: The orphan investigation (2026-04-10 session) found that 234 Innovations USA products on Shopify have `custom.manufacturer_sku = "TRUE"` — literally the string "TRUE" instead of a real SKU. This is a data corruption bug: some earlier script wrote a boolean check result instead of the actual SKU. The real SKU lives in `dwc.manufacturer_sku` or in product tags.
-
-**This is a DISCOVERY + SCRIPT-GENERATION task. NO SHOPIFY WRITES.** The goal is to survey the full scope, extract real SKUs, and generate a ready-to-execute fix script that a human will review and run manually.
-
-## Goal
-
-1. Find all Innovations USA products with `custom.manufacturer_sku = "TRUE"` (should be ~234)
-2. For each, determine whether a real SKU can be recovered from:
- - `dwc.manufacturer_sku` metafield (highest priority)
- - Product tags (second priority — often formatted like `innovations-USA-ABC-001` or just `ABC-001`)
- - Product title (third priority — parse from "Pattern | Innovations USA" format)
-3. Generate a comprehensive plan report + a ready-to-run fix script (DRY-RUN by default)
-4. Do NOT modify any Shopify data
-
-## Steps
-
-### Step 1: Query Shopify via GraphQL
-Get all Innovations products with full data. Batch 25 per query. Need per product:
-- `id`, `title`, `tags`
-- `metafield(namespace:"custom", key:"manufacturer_sku")`
-- `metafield(namespace:"dwc", key:"manufacturer_sku")`
-- First variant's `sku` (DW SKU like DWIN-)
-
-Query by `vendor:"Innovations USA"` AND `status:active`. Expect around 500-1100 total products based on catalog size (innovations_catalog has ~1100 rows).
-
-### Step 2: Classify each product into buckets
-
-- **Bucket A**: `custom.manufacturer_sku = "TRUE"` (the bug) — these are the 234 we need to fix
-- **Bucket B**: `custom.manufacturer_sku` is populated correctly with a real SKU — leave alone
-- **Bucket C**: `custom.manufacturer_sku` is NULL/missing — different problem, log separately
-
-### Step 3: For Bucket A, extract the real SKU
-
-**CRITICAL: Innovations SKUs are SHORT.** Typical format is 4-10 characters total:
-- 3-4 letters + hyphen + 1-3 digits: `ASR-001`, `CRD-04`, `VIT-12`
-- 4 lowercase letters + hyphen + 1-2 digits: `gami-9`, `arin-12`, `sion-6`
-- Occasionally 2-letter or single-word prefixes: `J603`, `ORI21`, `RS8502`
-
-Anything longer than ~12 characters is almost certainly NOT an Innovations mfr_sku. And "TRUE" itself is 4 chars, so length alone won't distinguish — the extraction MUST validate by looking up the candidate in `innovations_catalog`.
-
-For each "TRUE" product, try extraction in this priority order:
-
-1. **`dwc.manufacturer_sku`** — if present, short (≤12 chars), looks SKU-like (contains digits, has a hyphen or letter+digit pattern), and is NOT the string "TRUE"/"FALSE"/"null". This is the highest-confidence source.
-
-2. **Product tags** — iterate through tags, filter to ones that match SHORT SKU patterns:
- - Regex candidates: `^[A-Za-z]{2,4}-?\d{1,4}$`, `^[a-z]{4}-\d{1,3}$`, `^[A-Z]{1}\d{3,4}$`
- - Length must be ≤12 chars
- - Skip words like "wallcovering", "texture", "innovations", "usa", category/color words
- - Skip anything that's clearly a vendor name, collection name, or price
- - For each candidate tag, VALIDATE by querying innovations_catalog — only accept if the SKU exists
-
-3. **Product title** — format is usually "Pattern Name | Innovations USA". Extract the pattern name (everything before " | "), then query:
- ```sql
- SELECT mfr_sku FROM innovations_catalog
- WHERE pattern_name ILIKE $1
- ORDER BY CHAR_LENGTH(mfr_sku) ASC
- LIMIT 5
- ```
- If exactly one result → use it. If multiple → low confidence, log for manual review.
-
-**Rule of thumb**: if your "extracted SKU" is longer than 12 characters or doesn't match `innovations_catalog` when queried, discard it and move to the next source.
-
-### Step 4: Validate against innovations_catalog
-
-For each extracted SKU, query innovations_catalog to verify it exists:
-```sql
-SELECT mfr_sku, pattern_name FROM innovations_catalog WHERE mfr_sku = $1
-```
-
-Classify each "TRUE" product into:
-- **FIXABLE**: extracted SKU exists in innovations_catalog (can write to custom.manufacturer_sku)
-- **EXTRACTED_NO_CATALOG**: found a SKU but it's not in innovations_catalog (flag for recrawl)
-- **NO_EXTRACTION**: couldn't find a real SKU anywhere (needs manual entry)
-
-### Step 5: Write the plan report
-
-Create `/root/DW-Agents/logs/innovations-true-bug-plan.md` containing:
-- Summary counts (FIXABLE / EXTRACTED_NO_CATALOG / NO_EXTRACTION / TOTAL)
-- For FIXABLE: full table (DW SKU, old mfr_sku="TRUE", new mfr_sku, extraction source, pattern name)
-- For EXTRACTED_NO_CATALOG: list of SKUs that need recrawl
-- For NO_EXTRACTION: list of products needing manual review (shopify_id + title)
-- Risk assessment: how many are high-confidence matches vs fuzzy
-
-### Step 6: Generate the fix script
-
-Create `/root/DW-Agents/scripts/fix-innovations-true-bug.js` that:
-- Loads the FIXABLE list from the plan report (as a JSON file `/root/DW-Agents/logs/innovations-true-bug-fixable.json`)
-- Accepts `--dry-run` (default) and `--live` flags
-- In dry-run mode: prints what WOULD be updated, no API calls
-- In live mode: uses `metafieldsSet` GraphQL mutation, batched 25 per call, with the canonical type helper at `/root/DW-Agents/shared/shopify-metafield-types.js`
-- Rate-limits to ~4 req/s
-- Logs results to `/root/DW-Agents/logs/fix-innovations-true-bug-YYYYMMDD.log`
-- Uses token `<redacted:SHOPIFY_ADMIN_TOKEN>`
-
-Also save the FIXABLE list as a standalone JSON file for the script to consume.
-
-## Success Criteria
-
-- `/root/DW-Agents/logs/innovations-true-bug-plan.md` exists with counts + detail tables
-- `/root/DW-Agents/logs/innovations-true-bug-fixable.json` exists with the list of products to fix
-- `/root/DW-Agents/scripts/fix-innovations-true-bug.js` exists, runs cleanly in `--dry-run` mode, and reports the expected count
-- ZERO writes to Shopify happened during this task (the fix script is generated but not executed in live mode)
-- Report explicitly states "READY FOR HUMAN REVIEW — do not run --live until approved"
-
-## References
-
-- DB connection: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-- Store: `designer-laboratory-sandbox.myshopify.com`
-- Type helper: `/root/DW-Agents/shared/shopify-metafield-types.js` (use `typeFor('custom','manufacturer_sku')`)
-- Previous analysis: `/root/DW-Agents/logs/innovations-format-analysis.md`
-- Memory: `feedback_no_barcodes_use_mfr_metafield.md` — mfr_sku belongs in `custom.manufacturer_sku`, NOT variant.barcode
-- Rule: per CLAUDE.md Rule 23 — "MANDATORY GraphQL, NEVER REST" + batch 25 per call
-
-**Time budget**: 20 minutes max. Report partial findings if running long.
diff --git a/tasks/done/13_phase2-thibaut-specs.md b/tasks/done/13_phase2-thibaut-specs.md
deleted file mode 100644
index 011ee3a..0000000
--- a/tasks/done/13_phase2-thibaut-specs.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Phase 2: Thibaut Fire Rating + Material (4,410 gap)
-
-## Goal
-Thibaut has width/repeat done (only 8 gap) but 4,410 missing fire_rating and 4,423 missing material.
-Enrich via existing specs script.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node enrich-thibaut-specs.js
-```
-
-After completion, sync from thibaut_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET fire_rating = COALESCE(tc.fire_rating, vc.fire_rating),
- composition = COALESCE(tc.material, vc.composition),
- updated_at = NOW()
-FROM thibaut_catalog tc
-WHERE vc.mfr_sku = tc.mfr_sku AND vc.vendor_code = 'thibaut'
- AND (vc.fire_rating IS NULL OR vc.fire_rating = '' OR vc.composition IS NULL OR vc.composition = '')
- AND (tc.fire_rating IS NOT NULL OR tc.material IS NOT NULL);
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/13_vendor-standardization-check.md b/tasks/done/13_vendor-standardization-check.md
deleted file mode 100644
index 42df25b..0000000
--- a/tasks/done/13_vendor-standardization-check.md
+++ /dev/null
@@ -1,11 +0,0 @@
-Vendor standardization completion check.
-
-From Session 100: Vendor standardization was running (~12K/70K done). Check if completed.
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-1. Check if standardization script is still running: ps aux | grep -i standard
-2. Check /tmp/ for any standardization logs
-3. Sample 50 Shopify products and check vendor names for duplicates (e.g. Schumacher vs F. Schumacher)
-4. Report: current state of vendor standardization, any remaining inconsistencies
diff --git a/tasks/done/13_website-uptime-check.md b/tasks/done/13_website-uptime-check.md
deleted file mode 100644
index 1d36052..0000000
--- a/tasks/done/13_website-uptime-check.md
+++ /dev/null
@@ -1,22 +0,0 @@
-Check all production websites and fix any that are down.
-
-Working directory: /root/DW-Agents
-
-Production sites to check:
-- glassbeadedwallpaper.com (port 9888)
-- flockedwallpaper.com (port 3200)
-- grassclothwallcoverings.com (port 8080)
-- grassclothwallpaper.com (port 7123)
-- novasuede.com (port 8100)
-- bubbe.ai (port 3011)
-- goodquestion.ai (port 9883)
-
-For each site:
-1. curl -sL -o /dev/null -w "%{http_code}" http://localhost:<port> — check response code
-2. If 502 or no response, check pm2 status for that process
-3. Restart any down processes
-4. Verify they come back up
-
-Also check the Nora app on port 7400.
-
-Save results to /root/DW-Agents/logs/website-check-$(date +%Y%m%d).md
diff --git a/tasks/done/14_14_enrich-jeffreystevens.md b/tasks/done/14_14_enrich-jeffreystevens.md
deleted file mode 100644
index 3394b41..0000000
--- a/tasks/done/14_14_enrich-jeffreystevens.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-jeffreystevens-width.js
-
-Enrich Jeffrey Stevens catalog.
\ No newline at end of file
diff --git a/tasks/done/14_14_retro-walls-cleanup.md b/tasks/done/14_14_retro-walls-cleanup.md
deleted file mode 100644
index 249a490..0000000
--- a/tasks/done/14_14_retro-walls-cleanup.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Check Retro Walls catalog for non-wallcovering products (furniture, accessories, etc). Query the product_type or category field. Count how many are wallpaper vs non-wallpaper. For non-wallcovering products that have shopify_id, check if they should be archived. Report to /root/DW-Agents/logs/retro-walls-cleanup.json with product counts by type.
\ No newline at end of file
diff --git a/tasks/done/14_1838-enrichment.md b/tasks/done/14_1838-enrichment.md
deleted file mode 100644
index 16794b4..0000000
--- a/tasks/done/14_1838-enrichment.md
+++ /dev/null
@@ -1,9 +0,0 @@
-1838 Wallcoverings enrichment completion.
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-1. Find the 1838 table: psql ... -c "\dt *1838*"
-2. Check enrichment status - how many have width, specs
-3. Find any remaining products needing enrichment
-4. Enrich them if possible
-5. Report status
diff --git a/tasks/done/14_duplicate-collections-p1.md b/tasks/done/14_duplicate-collections-p1.md
deleted file mode 100644
index 5cf40c8..0000000
--- a/tasks/done/14_duplicate-collections-p1.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## Fix 261 Duplicate "Wallpaper Collection" Collections (P1-01)
-
-261 Shopify smart collections have generic "Wallpaper Collection" or similar duplicate names that confuse customers and hurt SEO.
-
-### Tasks:
-1. Query Shopify for all smart collections: GET /admin/api/2024-01/smart_collections.json?limit=250
-2. Find collections with duplicate or generic titles ("Wallpaper Collection", "Wallcovering Collection")
-3. For each duplicate: check what rules/products are in it — is it a vendor collection? pattern collection?
-4. Identify which duplicates can be safely deleted (no unique products)
-5. Report: list of duplicate collections with product counts, recommended action (delete/rename/merge)
-6. Do NOT delete without reporting first — this is an audit task
-
-Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-Store: `designer-laboratory-sandbox.myshopify.com`
diff --git a/tasks/done/14_innovations-recrawl-and-relink-plan.md b/tasks/done/14_innovations-recrawl-and-relink-plan.md
deleted file mode 100644
index 5754960..0000000
--- a/tasks/done/14_innovations-recrawl-and-relink-plan.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# Innovations USA — Full Recrawl + Broken Row Relink Plan
-
-**Context**: Session 2026-04-11 discovered `innovations_catalog` has **81 broken tail-4 rows** (mfr_skus like `atra-9`, `gami-12`, `lyph-7` across 15 patterns). All 81 have `shopify_product_id` populated (linked to real Shopify products) but have empty `product_url` and `color_name` — they're phantom catalog entries pointing to real products. Image reverse-match failed (0/81 shared URLs with proper-format catalog rows).
-
-**Root cause**: An earlier import script tail-4'd the pattern name and wrote it as mfr_sku (e.g., "Sumatra"→"atra", "Origami"→"gami", "Vandal"→"ndal") and inserted ghost catalog rows for each colorway.
-
-**Goal**: Fresh-crawl innovationsusa.com to ensure the canonical catalog has all real SKUs, then generate a mapping that migrates each broken row's `shopify_product_id` onto its real catalog row.
-
-**THIS IS A DISCOVERY + PLAN GENERATION TASK. NO DELETIONS, NO SHOPIFY WRITES.** All destructive operations are gated on human review of the generated plan.
-
-## Steps
-
-### Step 1: Inventory current catalog state (5 min)
-
-Query `innovations_catalog` for baseline counts:
-- Total rows, rows with product_url, rows with image_url, rows with shopify_product_id
-- 81 tail-4 broken rows (regex: `^[a-z]{4}-[0-9]{1,3}$`) + their patterns
-- Proper-format rows (regex: `^[A-Z]{2,4}-[0-9]{1,3}$`)
-
-Write findings to `/root/DW-Agents/logs/innovations-recrawl-step1-baseline.json`.
-
-### Step 2: Run the URL populator (5-10 min)
-
-```bash
-cd /root/DW-Agents && timeout 540 node vendor-scrapers/populate-innovations-urls.js 2>&1 | tee /root/DW-Agents/logs/innovations-recrawl-populate.log
-```
-
-This script paginates innovationsusa.com's `/product/all-wallcovering` to discover ALL current product URLs and matches them to `innovations_catalog.mfr_sku`. It populates `product_url` for catalog rows that are missing it.
-
-Report: how many URLs populated, how many catalog rows still have NULL product_url after the run.
-
-### Step 3: Build broken-row → proper-row mapping (5-10 min)
-
-For each of the 81 broken tail-4 rows with `shopify_product_id` populated:
-
-1. Fetch the Shopify product via GraphQL using `shopify_product_id`:
- - Get: title, tags, first variant SKU, image URLs, metafields (custom.color, custom.pattern_name, dwc.manufacturer_sku)
-2. Extract candidate "real SKU" by priority:
- - `dwc.manufacturer_sku` if it matches a proper format (`[A-Z]{2,4}-\d+`) AND exists in `innovations_catalog`
- - Tags containing a proper-format SKU that exists in catalog
- - Title's pattern name + color → search catalog WHERE pattern_name ILIKE $pattern AND color_name ILIKE $color
-3. Validate the candidate — it must:
- - Match `[A-Z]{2,4}-\d+` or `^[A-Z]{3,4}[0-9]+$` pattern
- - Exist in `innovations_catalog` (WHERE mfr_sku = $candidate)
- - Have the same pattern_name as the broken row
- - NOT already be claimed by another Shopify product (check for existing shopify_product_id on the target row)
-
-Bucket each broken row into:
-- **DIRECT_MATCH**: single unambiguous proper SKU found
-- **MULTIPLE_CANDIDATES**: pattern matches but color/colorway is ambiguous
-- **NO_MATCH**: couldn't find a proper SKU for this product
-
-### Step 4: Write the plan report
-
-Create `/root/DW-Agents/logs/innovations-recrawl-plan.md`:
-- Summary counts (DIRECT_MATCH / MULTIPLE_CANDIDATES / NO_MATCH)
-- For DIRECT_MATCH: table of (old broken mfr_sku → new proper mfr_sku, shopify_product_id, confidence source)
-- For MULTIPLE_CANDIDATES: table of candidates for human review
-- For NO_MATCH: list of products needing recrawl or manual entry
-- Explicit banner: "READY FOR HUMAN REVIEW — do not execute migration until approved"
-
-Also write the DIRECT_MATCH list to `/root/DW-Agents/logs/innovations-relink-plan.json` as structured data.
-
-### Step 5: Generate (but do NOT run) the migration script
-
-Create `/root/DW-Agents/scripts/migrate-innovations-broken-links.js` that:
-- Loads `innovations-relink-plan.json`
-- Has `--dry-run` (default) and `--live` flags
-- For each DIRECT_MATCH: (in a transaction per row)
- 1. Verify source row still has shopify_product_id + is still a broken tail-4 format
- 2. Verify target row still exists + has NULL shopify_product_id (or log collision)
- 3. UPDATE target row: SET shopify_product_id = source.shopify_product_id, on_shopify = true
- 4. UPDATE source row: SET shopify_product_id = NULL, on_shopify = false (don't delete — keep for audit)
-- Logs every operation to `/root/DW-Agents/logs/migrate-innovations-broken-links-YYYYMMDD.log`
-- **Never deletes rows** — only transfers the shopify_product_id pointer
-
-## Success Criteria
-
-- `/root/DW-Agents/logs/innovations-recrawl-plan.md` exists with counts + detail
-- `/root/DW-Agents/logs/innovations-relink-plan.json` exists with DIRECT_MATCH list
-- `/root/DW-Agents/scripts/migrate-innovations-broken-links.js` exists and runs cleanly in `--dry-run`
-- ZERO writes to Shopify (entire task is PG-read + PG-URL-populate + file generation)
-- Report explicitly states "AWAITING HUMAN REVIEW"
-
-## References
-
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Shopify token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
-- URL populator: `/root/DW-Agents/vendor-scrapers/populate-innovations-urls.js`
-- Spec enricher: `/root/DW-Agents/vendor-scrapers/enrich-innovations-specs.js`
-- Vendor config: `/root/DW-Agents/vendor-agent-core/vendors/innovations.json`
-- Quarantined bad fix: `/root/DW-Agents/scripts/quarantine/fix-innovations-true-bug.js.BROKEN-DO-NOT-RUN`
-- Memory context: `feedback_au_series_keep.md`, `project_catalog_orphans_backlog.md`
-
-**Broken tail-4 patterns to fix** (81 rows total):
-eode (Geode), lyph (Glyph), sion (Immersion), inth (Labyrinth), tice (Lattice), arin (Mazarin), tley (Motley), ilus (Nautilus), gami (Origami), palt (Spalt), rift (Spindrift), lice (Splice), atra (Sumatra), ndal (Vandal), ctor (Vector)
-
-**Time budget**: 18 minutes max (leave buffer under YOLO 20-min cap).
diff --git a/tasks/done/14_phase2-koroseal-fire-rating.md b/tasks/done/14_phase2-koroseal-fire-rating.md
deleted file mode 100644
index aa02579..0000000
--- a/tasks/done/14_phase2-koroseal-fire-rating.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Phase 2: Koroseal Fire Rating + Material (2,193 gap)
-
-## Goal
-Koroseal has width done (only 676 gap) but 2,193 missing fire_rating and 695 missing material.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node enrich-koroseal-specs.js
-```
-
-After completion, sync from koroseal_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET fire_rating = COALESCE(kc.fire_rating, vc.fire_rating),
- composition = COALESCE(kc.material, vc.composition),
- width = COALESCE(kc.width, vc.width),
- pattern_repeat = COALESCE(kc.repeat_v, vc.pattern_repeat),
- updated_at = NOW()
-FROM koroseal_catalog kc
-WHERE vc.mfr_sku = kc.mfr_sku AND vc.vendor_code = 'koroseal'
- AND (vc.fire_rating IS NULL OR vc.fire_rating = '' OR vc.width IS NULL OR vc.width = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/14_shopify-image-audit.md b/tasks/done/14_shopify-image-audit.md
deleted file mode 100644
index 3660ed7..0000000
--- a/tasks/done/14_shopify-image-audit.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Audit Shopify products for missing images and generate a priority fix list.
-
-Working directory: /root/DW-Agents
-
-1. Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-2. Query shopify_products for products with NULL or empty image_url
-3. Group by vendor — which vendors have the most missing images?
-4. Cross-reference with vendor catalog tables — do the catalogs HAVE images that Shopify is missing?
-5. Generate a priority list: vendors where catalog has images but Shopify doesn't
-6. Count total products, products with images, products without images
-7. Save report to /root/DW-Agents/logs/shopify-image-audit-$(date +%Y%m%d).md
-
-This is READ-ONLY — do NOT modify any Shopify products. Just audit and report.
diff --git a/tasks/done/14_stout-timorous-width.md b/tasks/done/14_stout-timorous-width.md
deleted file mode 100644
index c117754..0000000
--- a/tasks/done/14_stout-timorous-width.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## P1-06: Stout/Timorous Width Enrichment
-
-Stout and Timorous Beasties products need width data scraped from vendor websites.
-
-### Steps:
-1. Check current state:
- psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL AND width != '' THEN 1 END) as has_width FROM stout_catalog;"
- psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL AND width != '' THEN 1 END) as has_width FROM timorous_catalog;"
-
-2. For Stout: Check if product pages have width in specs. Sample a product_url and curl it to find width pattern.
-3. For Timorous: Same approach — find width on vendor product pages.
-4. Build a simple enrichment script that visits each product_url and extracts width via regex.
-5. Update the catalog table with scraped widths.
-
-Use timeout 30 for all curl/psql commands. This is DB-only work, no Shopify API needed.
\ No newline at end of file
diff --git a/tasks/done/14_studio-printworks-enrich.md b/tasks/done/14_studio-printworks-enrich.md
deleted file mode 100644
index 79caae0..0000000
--- a/tasks/done/14_studio-printworks-enrich.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## Studio Printworks — Scrape for Width & Repeat
-
-383 products in `studio_printworks_catalog`. Missing width and repeat. Have product_url.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check schema and specs JSON.
-3. Sample product_url and fetch to find specs.
-4. Write enrichment script at `/root/DW-Agents/vendor-scrapers/enrich-studio-printworks.js`
-5. Run: `timeout 600 node enrich-studio-printworks.js`
-6. Report.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/14_styles-page-tile-images.md b/tasks/done/14_styles-page-tile-images.md
deleted file mode 100644
index 42d8fb5..0000000
--- a/tasks/done/14_styles-page-tile-images.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Styles Page — Replace Bad Tile Images
-
-Session #91: Multiple styles page tiles need better images. The page is at Shopify page ID 176660709427.
-
-### Tasks:
-1. Read the current styles page from Shopify Pages API
-2. Identify tiles for: Florals & Flowers, French Country, Animal Print, Chevron, Botanicals & Florals
-3. For each, query Shopify collections for products with room_setting_images or attractive product images
-4. Pick the best image URL for each tile (prefer room settings over flat samples)
-5. Replace the image URLs in the page HTML
-6. Push updated page to Shopify
-7. Report: which tiles were updated, old vs new image URLs
-
-Shopify content token: `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`
-Store: `designer-laboratory-sandbox.myshopify.com`
-Page ID: 176660709427
diff --git a/tasks/done/15_15_enrich-bespoke.md b/tasks/done/15_15_enrich-bespoke.md
deleted file mode 100644
index 38b8e7e..0000000
--- a/tasks/done/15_15_enrich-bespoke.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-bespoke-width.js
-
-Enrich DW Bespoke Studio catalog.
\ No newline at end of file
diff --git a/tasks/done/15_15_enrich-newwall.md b/tasks/done/15_15_enrich-newwall.md
deleted file mode 100644
index 3746824..0000000
--- a/tasks/done/15_15_enrich-newwall.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Check NewWall catalog coverage:
-timeout 10 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width, COUNT(CASE WHEN image_url IS NOT NULL THEN 1 END) as has_image, COUNT(CASE WHEN shopify_id IS NOT NULL THEN 1 END) as on_shopify FROM newwall_catalog;"
-Report readiness for Shopify push.
\ No newline at end of file
diff --git a/tasks/done/15_15_lincrusta-sku-push.md b/tasks/done/15_15_lincrusta-sku-push.md
deleted file mode 100644
index dcb6072..0000000
--- a/tasks/done/15_15_lincrusta-sku-push.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Check Lincrusta vendor status: SELECT * FROM vendor_registry WHERE vendor_name ILIKE %lincrusta%. Check if Lincrusta has a catalog table and how many products it has. Check if DW SKUs have been assigned (sku_prefix in vendor_registry). Check if any have shopify_id. Report readiness for Shopify push to /root/DW-Agents/logs/lincrusta-readiness.json
\ No newline at end of file
diff --git a/tasks/done/15_database-vacuum-analyze.md b/tasks/done/15_database-vacuum-analyze.md
deleted file mode 100644
index c7161a8..0000000
--- a/tasks/done/15_database-vacuum-analyze.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Run database maintenance on the dw_unified PostgreSQL database.
-
-Working directory: /root/DW-Agents
-
-1. Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-2. Check database size: SELECT pg_size_pretty(pg_database_size('dw_unified'));
-3. Find the 10 largest tables: SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;
-4. Check for tables with dead tuples needing vacuum: SELECT relname, n_dead_tup, last_vacuum, last_autovacuum FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY n_dead_tup DESC LIMIT 10;
-5. Run VACUUM ANALYZE on the top 5 tables with most dead tuples
-6. Check for unused indexes
-7. Save report to /root/DW-Agents/logs/db-maintenance-$(date +%Y%m%d).md
-
-Be careful — only run VACUUM ANALYZE (not VACUUM FULL which locks tables).
diff --git a/tasks/done/15_full-system-health.md b/tasks/done/15_full-system-health.md
deleted file mode 100644
index 260fe9b..0000000
--- a/tasks/done/15_full-system-health.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Full system health check — all agents, all services.
-
-1. Run: pm2 list and check for any errored/stopped processes
-2. Restart any crashed agents
-3. Check disk space: df -h /
-4. Check memory: free -h
-5. Check for zombie Claude processes >12h: ps aux | grep claude | grep -v grep
-6. Kill any zombies (log PID and uptime first)
-7. Check all production websites respond:
- curl -sI https://glassbeadedwallpaper.com | head -1
- curl -sI https://flockedwallpaper.com | head -1
- curl -sI https://grassclothwallcoverings.com | head -1
- curl -sI https://grassclothwallpaper.com | head -1
- curl -sI https://novasuede.com | head -1
-8. Report: total agents up/down, disk/memory usage, any issues found
diff --git a/tasks/done/15_legal-violations-audit.md b/tasks/done/15_legal-violations-audit.md
deleted file mode 100644
index 153b40b..0000000
--- a/tasks/done/15_legal-violations-audit.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Legal Violations Comprehensive Audit
-
-Sessions #84-86: 48 HIGH-risk + 176 MEDIUM-risk legal violations flagged but never fully reviewed.
-
-### Tasks:
-1. Check if legal agent (Judge) is running on port 9878
-2. If down, restart via PM2
-3. Query the legal violations database for current status breakdown:
- - By risk level (HIGH/MEDIUM/LOW)
- - By review status (pending/cleared/removed)
- - By vendor (top 10)
-4. Check if nightly scan cron (2 AM PT = 10 AM UTC) is in crontab
-5. Run a fresh scan if last scan was >7 days ago
-6. Generate summary report with actionable items
-7. Report findings to stdout
-
-Legal agent: `/root/DW-Agents/legal-agent/` or search PM2 for "legal"/"judge"
-Port: 9878
-Review page: http://45.61.58.125:9878/review
diff --git a/tasks/done/15_momentum-sku-investigation.md b/tasks/done/15_momentum-sku-investigation.md
deleted file mode 100644
index 8750266..0000000
--- a/tasks/done/15_momentum-sku-investigation.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## P1-08: Investigate 1,059 Unmatched Momentum SKUs
-
-Hollywood catalog has 1,059 products with Momentum-format momentum_sku values that don't match any scraped Momentum colorway.
-
-### Steps:
-1. Query the unmatched set:
- psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
- SELECT SPLIT_PART(h.momentum_sku, '_', 1) as pattern, COUNT(*) as cnt
- FROM hollywood_catalog h
- LEFT JOIN momentum_colorways m ON SPLIT_PART(h.momentum_sku, '_', 1) = m.pattern_name
- WHERE h.momentum_sku IS NOT NULL AND h.momentum_sku != '' AND m.pattern_name IS NULL
- GROUP BY 1 ORDER BY cnt DESC LIMIT 20;"
-
-2. Check if these are from non-Momentum MDC supplier lines (other brands under MDC umbrella)
-3. Check if any of these patterns exist on the Momentum website but weren't scraped
-4. Report findings: how many are genuinely discontinued vs missing from scrape
-
-This is READ-ONLY investigation. No modifications.
\ No newline at end of file
diff --git a/tasks/done/15_novasuede-ultrasuede-enrich.md b/tasks/done/15_novasuede-ultrasuede-enrich.md
deleted file mode 100644
index 5bc5ce2..0000000
--- a/tasks/done/15_novasuede-ultrasuede-enrich.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Novasuede + Ultrasuede — Enrich Specs
-
-Novasuede: 140 products in `novasuede_catalog`. Missing width+repeat.
-Ultrasuede: 111 products in `ultrasuede_catalog`. Missing width+repeat.
-
-Both are suede/textile wallcoverings — typically 54" width, no repeat (solid textures).
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. Check each table schema and what data exists.
-3. Check specs JSON for embedded data.
-4. For Novasuede, check if product pages have width data: `SELECT product_url FROM novasuede_catalog LIMIT 3`
-5. For Ultrasuede, same check.
-6. If scraping needed, write scripts. If standard widths can be inferred from product type, update in bulk.
-7. Report results.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/15_phase2-harlequin-specs.md b/tasks/done/15_phase2-harlequin-specs.md
deleted file mode 100644
index 6e95626..0000000
--- a/tasks/done/15_phase2-harlequin-specs.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Phase 2: Harlequin Specs via Internal API (448 gap)
-
-## Goal
-Harlequin exposes a JSON API at harlequin.design with width, repeat, fire_rating, match_type.
-The existing enrichment script at enrich-harlequin-shopify.js can pull specs.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node enrich-harlequin-shopify.js
-```
-
-If that doesn't enrich specs specifically, the Magento GraphQL API used by the Phase 1 scraper (scrape-harlequin.js) can be queried for individual product specs. Check the scraper code for the API endpoint pattern.
-
-After completion, sync from harlequin_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(hc.width, vc.width),
- pattern_repeat = COALESCE(hc.repeat_v, vc.pattern_repeat),
- fire_rating = COALESCE(hc.fire_rating, vc.fire_rating),
- composition = COALESCE(hc.material, vc.composition),
- updated_at = NOW()
-FROM harlequin_catalog hc
-WHERE vc.mfr_sku = hc.mfr_sku AND vc.vendor_code = 'harlequin'
- AND (vc.width IS NULL OR vc.width = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/15_scrape-repeats-schumacher.md b/tasks/done/15_scrape-repeats-schumacher.md
deleted file mode 100644
index 57b2121..0000000
--- a/tasks/done/15_scrape-repeats-schumacher.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Scrape Missing Repeat Measurements — Schumacher (400 on Shopify missing repeat_v)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Table: `schumacher_catalog`
-- 400 products on Shopify missing repeat_v, 409 have product_url to scrape
-- Schumacher product pages: fschumacher.com
-
-## Task
-Write and run a Node.js scraper that:
-
-1. Query all Shopify products missing repeats with a product_url:
-```sql
-SELECT id, sku, name, product_url FROM schumacher_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0'))
- AND product_url IS NOT NULL AND product_url != ''
- AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
-ORDER BY id DESC;
-```
-
-2. For each product_url, fetch with `node-fetch` (1 req/s)
-3. Parse HTML for repeat info — Schumacher shows it in product details:
- - "Repeat", "Vertical Repeat", "V Repeat" → repeat_v
- - "Horizontal Repeat" / "H Repeat" → repeat_h
- - "Match", "Match Type", "Pattern Match" → match_type (values: "Straight", "Drop", "Half Drop", "Random", "Free Match")
- - If "Half Drop" → match_type = 'Half Drop'
- - If "Straight" → match_type = 'Straight'
- - If "Random" or "Free Match" → match_type = 'Random'
-4. Update DB with ALL THREE:
-```sql
-UPDATE schumacher_catalog SET repeat_v = $1, repeat_h = $2, match_type = $3, updated_at = NOW() WHERE id = $4;
-```
-5. Log all progress
-6. Report final stats
-
-## Important
-- Rate limit 1 req/s
-- Do NOT hit Shopify API
-- Save scraper to `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-schumacher.js`
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/16_16_blog-function-leak.md b/tasks/done/16_16_blog-function-leak.md
deleted file mode 100644
index 68c238a..0000000
--- a/tasks/done/16_16_blog-function-leak.md
+++ /dev/null
@@ -1 +0,0 @@
-Two blog posts on goodquestion.ai contain the function name upsertProduct() which is a code leak from the DW codebase. Search all blog post files in /root/Projects/goodquestion-ai/src/content/posts/ for upsertProduct. If found, remove or replace the function name with a generic description. Rebuild the blog with cd /root/Projects/goodquestion-ai && npm run build. Report which posts were fixed to /root/DW-Agents/logs/blog-leak-fix.log
\ No newline at end of file
diff --git a/tasks/done/16_16_enrich-malibu.md b/tasks/done/16_16_enrich-malibu.md
deleted file mode 100644
index a480afb..0000000
--- a/tasks/done/16_16_enrich-malibu.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-malibu-width.js
-
-Enrich Malibu Wallpaper catalog.
\ No newline at end of file
diff --git a/tasks/done/16_phase2-leejofa-algolia.md b/tasks/done/16_phase2-leejofa-algolia.md
deleted file mode 100644
index 67ad383..0000000
--- a/tasks/done/16_phase2-leejofa-algolia.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Phase 2: Lee Jofa Specs via Kravet Algolia API (343 gap)
-
-## Goal
-Lee Jofa is a Kravet brand. The scraper at scrape-leejofa.js uses Kravet's Algolia API which returns width, repeat_vertical, repeat_horizontal, product_composition in a single API call.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 600 node scrape-leejofa.js
-```
-
-After completion, sync from lee_jofa_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(lj.width, vc.width),
- pattern_repeat = COALESCE(lj.repeat_v, lj.repeat_vertical, vc.pattern_repeat),
- composition = COALESCE(lj.material, lj.product_composition, vc.composition),
- updated_at = NOW()
-FROM lee_jofa_catalog lj
-WHERE vc.mfr_sku = lj.mfr_sku AND vc.vendor_code = 'lee_jofa'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/16_philippe-romano-legacy.md b/tasks/done/16_philippe-romano-legacy.md
deleted file mode 100644
index b47d9b2..0000000
--- a/tasks/done/16_philippe-romano-legacy.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## P1-10: Philippe Romano 1,721 Legacy DWKK Products
-
-1,721 Philippe Romano products on Shopify have DWKK- prefix SKUs (legacy) instead of the correct vendor prefix. Session #106 fixed 55 products via metafield backfill but 1,721 remain.
-
-### Steps:
-1. Count current state:
- psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
- SELECT COUNT(*) FROM shopify_products WHERE sku LIKE 'DWKK-%' AND vendor ILIKE '%romano%' AND status='active';"
-
-2. Check if phillipe_romano_catalog has these products with correct dw_sku:
- psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
- SELECT COUNT(*) as total,
- COUNT(CASE WHEN dw_sku IS NOT NULL AND dw_sku NOT LIKE 'DWKK%' THEN 1 END) as has_new_sku
- FROM phillipe_romano_catalog WHERE shopify_product_id IS NOT NULL;"
-
-3. If new SKUs exist, build a script to update Shopify variant SKUs from DWKK- to the correct prefix
-4. Rate limit: 0.5 req/s on Shopify API
-
-Shop: designer-laboratory-sandbox.myshopify.com
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
\ No newline at end of file
diff --git a/tasks/done/16_scrape-repeats-brewster-york.md b/tasks/done/16_scrape-repeats-brewster-york.md
deleted file mode 100644
index 2d52930..0000000
--- a/tasks/done/16_scrape-repeats-brewster-york.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Scrape Missing Repeat/Match Data — Brewster & York (6,353 + 4,525 need data)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Tables: `brewster_catalog` (6,353 with URLs need data), `york_catalog` (4,525 with URLs need data)
-- Both use yorkwall.com trade portal
-- Scrape ALL products with URLs, not just Shopify ones — we need this data for future pushes too
-
-## Task
-Write and run a Node.js scraper that handles BOTH vendors:
-
-### Part 1: Brewster (prioritize Shopify products first, then all others)
-```sql
-SELECT id, sku, name, product_url FROM brewster_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0')
- OR (match_type IS NULL OR match_type = ''))
- AND product_url IS NOT NULL AND product_url != ''
-ORDER BY (shopify_product_id IS NOT NULL AND shopify_product_id != '') DESC, id DESC
-LIMIT 500;
-```
-
-### Part 2: York (prioritize Shopify products first, then all others)
-```sql
-SELECT id, sku, name, product_url FROM york_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0')
- OR (match_type IS NULL OR match_type = ''))
- AND product_url IS NOT NULL AND product_url != ''
-ORDER BY (shopify_product_id IS NOT NULL AND shopify_product_id != '') DESC, id DESC
-LIMIT 500;
-```
-
-For each product_url:
-1. Fetch with `node-fetch` (1 req/s — yorkwall.com is a trade portal)
-2. Parse for ALL rendering data:
- - "Vertical Repeat", "Pattern Repeat", "Repeat" → repeat_v
- - "Horizontal Repeat", "H. Repeat" → repeat_h
- - "Match", "Match Type", "Pattern Match" → match_type (values: "Straight", "Drop", "Half Drop", "Random", "Free Match")
- - If "Half Drop" → match_type = 'Half Drop'
- - If "Straight" → match_type = 'Straight'
- - If "Random" or "Free Match" → match_type = 'Random'
-3. Update the respective catalog table with ALL found values (repeat_v, repeat_h, match_type)
-4. Log progress and report stats per vendor
-
-## Important
-- Rate limit 1 req/s (both share yorkwall.com infrastructure)
-- If 403/login wall, log it and skip — some URLs may require auth
-- Do NOT hit Shopify API
-- Save to `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-brewster-york.js`
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/16_vcc-dashboard-status-fix.md b/tasks/done/16_vcc-dashboard-status-fix.md
deleted file mode 100644
index 0b8ca8a..0000000
--- a/tasks/done/16_vcc-dashboard-status-fix.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## VCC Dashboard — Fix /api/status 404
-
-Session #98: Vendor Command Center (port 9660) has /api/status returning 404. Dashboard shows 0 catalog / 0 on Shopify.
-
-### Tasks:
-1. Read VCC server at `/root/DW-Agents/vendor-command-center/server.js`
-2. Find what endpoints exist (grep for app.get, app.post, router)
-3. Check dashboard HTML/JS for which endpoint it calls for status data
-4. Fix the mismatch — add missing endpoint or fix dashboard call
-5. The status endpoint should aggregate: total catalog count across all vendor tables, total on Shopify, vendor breakdown
-6. Test: `curl -u admin:DWSecure2024! http://localhost:9660/api/status`
-7. Restart PM2: `pm2 restart vendor-command-center`
-
-VCC location: `/root/DW-Agents/vendor-command-center/`
-Port: 9660
diff --git a/tasks/done/17_17_cost-gap-report.md b/tasks/done/17_17_cost-gap-report.md
deleted file mode 100644
index 4e09cb9..0000000
--- a/tasks/done/17_17_cost-gap-report.md
+++ /dev/null
@@ -1 +0,0 @@
-Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified. Generate a report of products missing cost data. For each vendor in vendor_registry, count products with and without cost fields (us_msrp, master_cost, your_cost). Calculate percentage with cost. Sort by gap size (most missing first). Top vendors with cost gaps from Session 74: G and B (1,598), Thibaut (1,519), Koroseal (566), Bespoke (375), Phillipe Romano (309), Romo (191). Check if these have improved. Save to /root/DW-Agents/logs/cost-gap-report.json
\ No newline at end of file
diff --git a/tasks/done/17_17_enrich-innovations.md b/tasks/done/17_17_enrich-innovations.md
deleted file mode 100644
index 06eba52..0000000
--- a/tasks/done/17_17_enrich-innovations.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-innovations-specs.js
-
-Enrich Innovations USA catalog.
\ No newline at end of file
diff --git a/tasks/done/17_17_midnight-health-check.md b/tasks/done/17_17_midnight-health-check.md
deleted file mode 100644
index e0baab0..0000000
--- a/tasks/done/17_17_midnight-health-check.md
+++ /dev/null
@@ -1,10 +0,0 @@
-Server health check.
-1. df -h /
-2. free -h
-3. pm2 list | grep -E "errored|stopped" and restart any crashed agents
-4. Check zombie Chrome: ps aux | grep chrome | grep -v grep | wc -l — kill if >5
-5. Check the DWTT metafield job: ps aux | grep dwtt-body | grep -v grep — report if still running
-6. Check the spin job: ps aux | grep swatch-spin | grep -v grep — report if still running
-7. tail -5 /tmp/dwtt-metafields.log
-8. tail -5 /tmp/dwtt-spins.log
-9. Report summary
\ No newline at end of file
diff --git a/tasks/done/17_phase2-sandberg-specs.md b/tasks/done/17_phase2-sandberg-specs.md
deleted file mode 100644
index a269cce..0000000
--- a/tasks/done/17_phase2-sandberg-specs.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Phase 2: Sandberg Specs via Sitemap Scraper (540 gap)
-
-## Goal
-Sandberg scraper (scrape-sandberg.js) crawls sitemap.xml and individual product pages for width, repeat, specs.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node scrape-sandberg.js
-```
-
-After completion, sync from sandberg_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(sc.width, vc.width),
- pattern_repeat = COALESCE(sc.repeat_v, vc.pattern_repeat),
- composition = COALESCE(sc.material, vc.composition),
- updated_at = NOW()
-FROM sandberg_catalog sc
-WHERE vc.mfr_sku = sc.mfr_sku AND vc.vendor_code = 'sandberg'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/17_scrape-repeats-versace.md b/tasks/done/17_scrape-repeats-versace.md
deleted file mode 100644
index a907dd1..0000000
--- a/tasks/done/17_scrape-repeats-versace.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Scrape Missing Repeat Measurements — Versace (23 missing in DB, 18 have URLs)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Table: `versace_catalog`
-- Only 23 products missing repeat_v, 18 have product_url — quick job
-
-## Task
-Write and run a Node.js scraper:
-
-1. Query:
-```sql
-SELECT id, sku, name, product_url FROM versace_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0'))
- AND product_url IS NOT NULL AND product_url != ''
-ORDER BY id DESC;
-```
-
-2. Fetch each URL (1 req/s), parse for BOTH repeat values:
- - "Vertical Repeat", "Pattern Repeat", "Repeat" → repeat_v
- - "Horizontal Repeat", "H. Repeat" → repeat_h
- - "Match", "Match Type", "Pattern Match" → match_type ("Straight", "Drop", "Half Drop", "Random")
-3. Versace specs are often in a specifications table or JSON-LD on the page
-4. Update DB with ALL THREE: `UPDATE versace_catalog SET repeat_v = $1, repeat_h = $2, match_type = $3, updated_at = NOW() WHERE id = $4`
-5. Report all results — this should complete in under 30 seconds
-
-## Important
-- Save to `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-versace.js`
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/17_shop-by-color-integration.md b/tasks/done/17_shop-by-color-integration.md
deleted file mode 100644
index 71e0b7d..0000000
--- a/tasks/done/17_shop-by-color-integration.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## P2-07: Shop by Color Shopify Integration
-
-Build the storefront page for Shop by Color. The API exists but needs a Shopify page.
-
-### Steps:
-1. Check if the Shop by Color API is running:
- curl -s http://127.0.0.1:9501/api/colors 2>/dev/null | head -c 200
- (or check the correct port from PM2 list)
-
-2. Check if a Shopify page already exists:
- curl -s -H "X-Shopify-Access-Token: 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" \
- "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/pages.json?limit=250" | python3 -c "import json,sys;[print(p['title']) for p in json.load(sys.stdin).get('pages',[]) if 'color' in p['title'].lower()]"
-
-3. If no page exists, create one with embedded JS that calls the Shop by Color API
-4. The page should show color swatches that link to filtered collections
-
-Content token for pages: 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
\ No newline at end of file
diff --git a/tasks/done/17_youtube-oauth-reauth.md b/tasks/done/17_youtube-oauth-reauth.md
deleted file mode 100644
index 6def935..0000000
--- a/tasks/done/17_youtube-oauth-reauth.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## YouTube OAuth Re-authentication
-
-Session #85: YouTube uploads blocked by expired OAuth tokens. `invalid_grant` error.
-
-### Tasks:
-1. Check current OAuth token at `/root/Projects/goodquestion-ai/.youtube-token.json`
-2. Check if the token has expired (look at expiry_date field)
-3. Try refreshing the token using the refresh_token
-4. If refresh fails, report that manual re-auth is needed (user must visit Google OAuth URL in browser)
-5. Check YouTube upload script at `/root/Projects/goodquestion-ai/scripts/youtube-upload.cjs`
-6. List any recent upload attempts in logs
-7. Report: token status, what's needed to fix
-
-OAuth client: `1014965852542-23fjv7gsva6ifgg8vsei0ii9796ddjld`
-Token file: `/root/Projects/goodquestion-ai/.youtube-token.json`
diff --git a/tasks/done/18_18_as-creation-specs-enrich.md b/tasks/done/18_18_as-creation-specs-enrich.md
deleted file mode 100644
index ee9c010..0000000
--- a/tasks/done/18_18_as-creation-specs-enrich.md
+++ /dev/null
@@ -1 +0,0 @@
-AS Creation has 6,166 products in as_creation_catalog table (postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified). The width, material, repeat, match_type, and features are populated but finish and application columns are empty strings. The product pages at products.as-creation.com have this data in the properties table. Sample URL pattern: https://products.as-creation.com/en/A.S.-Creation-Wallpaper-designdrop/AS784911-0. The page has Schema.org itemprop metadata (width in mm, weight in kg, gtin13, depth) plus a properties table with Application, Material, Pattern matching, Removal, Water resistance, Light fastness, Room, Style, Features, CE marking. Write a Node.js enrichment script at /root/DW-Agents/vendor-scrapers/enrich-as-creation-specs.js that: 1) Reads products from as_creation_catalog where product_url IS NOT NULL, 2) Fetches each product page, 3) Extracts finish (Surface field or from highlights), application (Application field), coverage (roll size from highlights), and any other missing specs from the properties table, 4) Updates the DB row. Use simple HTTP fetch (not Puppeteer - the site is server-rendered). Rate limit 1 req/sec. Support --limit N and --dry-run flags. Run it with --limit 50 --dry-run first to test, then with --limit 100 to start enriching. Log to /root/DW-Agents/logs/as-creation-enrich.log
\ No newline at end of file
diff --git a/tasks/done/18_18_enrich-hygge.md b/tasks/done/18_18_enrich-hygge.md
deleted file mode 100644
index 70dae91..0000000
--- a/tasks/done/18_18_enrich-hygge.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-hygge-specs.js
-
-Enrich Hygge & West catalog.
\ No newline at end of file
diff --git a/tasks/done/18_18_enrich-york.md b/tasks/done/18_18_enrich-york.md
deleted file mode 100644
index a68c035..0000000
--- a/tasks/done/18_18_enrich-york.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Check York catalog:
-timeout 10 psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified -c "SELECT COUNT(*) as total, COUNT(CASE WHEN width IS NOT NULL THEN 1 END) as has_width, COUNT(CASE WHEN image_url IS NOT NULL THEN 1 END) as has_image FROM york_catalog;"
-Report coverage and Shopify push readiness.
\ No newline at end of file
diff --git a/tasks/done/18_kalshi-calendar-panel.md b/tasks/done/18_kalshi-calendar-panel.md
deleted file mode 100644
index 47a28f7..0000000
--- a/tasks/done/18_kalshi-calendar-panel.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## P2-01: Kalshi Calendar Live Panel
-
-Add a live calendar panel to the Ken Kalshi trading agent dashboard.
-
-### Steps:
-1. Check Ken agent status: curl -s -u admin:DWSecure2024! http://45.61.58.125:9816/api/status
-2. Check if there's already a calendar view in the dashboard
-3. If not, add a panel showing upcoming Kalshi events/contracts with expiry dates
-4. Use the Kalshi API to fetch active markets
-5. Display in a simple table or calendar grid on the existing dashboard
-
-Ken agent port: 9816
-Ken CLAUDE.md: /root/DW-Agents/ken/CLAUDE.md
\ No newline at end of file
diff --git a/tasks/done/18_old-youtube-videos-unlist.md b/tasks/done/18_old-youtube-videos-unlist.md
deleted file mode 100644
index b1fcb6e..0000000
--- a/tasks/done/18_old-youtube-videos-unlist.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Unlist Old Silent YouTube Videos
-
-Session #76: 4 old silent videos should be unlisted from YouTube.
-
-### Tasks:
-1. Check if YouTube OAuth tokens are valid (see task 17)
-2. If tokens work, unlist these video IDs:
- - 6iCCuuk7dDM
- - mT2LZBrajas
- - OZY5D0NgQ_g
- - 5un2PCEhtmw
-3. Use YouTube Data API v3 to set privacy status to 'unlisted'
-4. Also check old channel (@steveabramsdw9839) for duplicate videos
-5. Report: which videos were unlisted, any errors
-
-YouTube upload script: `/root/Projects/goodquestion-ai/scripts/youtube-upload.cjs`
-Token: `/root/Projects/goodquestion-ai/.youtube-token.json`
-
-NOTE: If OAuth tokens are expired, just report what needs to be done manually. Don't block on this.
diff --git a/tasks/done/18_phase2-clarke-specs.md b/tasks/done/18_phase2-clarke-specs.md
deleted file mode 100644
index c9e15ff..0000000
--- a/tasks/done/18_phase2-clarke-specs.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Phase 2: Clarke & Clarke Specs via JSON-LD Crawler (328 gap)
-
-## Goal
-Clarke & Clarke scraper (scrape-clarke.js) crawls clarkeandclarke.design and parses JSON-LD structured data from product pages.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node scrape-clarke.js
-```
-
-After completion, sync from clarke_clarke_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(cc.width, vc.width),
- pattern_repeat = COALESCE(cc.repeat_v, vc.pattern_repeat),
- composition = COALESCE(cc.material, vc.composition),
- updated_at = NOW()
-FROM clarke_clarke_catalog cc
-WHERE vc.mfr_sku = cc.mfr_sku AND vc.vendor_code = 'clarke_clarke'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/18_push-repeats-to-shopify.md b/tasks/done/18_push-repeats-to-shopify.md
deleted file mode 100644
index 54d1c07..0000000
--- a/tasks/done/18_push-repeats-to-shopify.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Push Newly Found Repeats to Shopify via Quinn Queue
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- After scraper tasks 13-17 run, catalog tables will have new repeat_v, repeat_h, AND match_type values
-- These ALL need to be pushed to Shopify metafields via Quinn (shopify_api_queue)
-
-## Task
-For each vendor catalog with repeat data that isn't on Shopify yet:
-
-1. Find products where DB has repeat but Shopify metafield doesn't:
-```sql
--- For each vendor table, find products with repeat data and shopify_product_id
--- Then check apex_audit_cache to see which ones had missing repeats
-SELECT ac.shopify_id, ct.repeat_v, ct.repeat_h, ct.match_type
-FROM {catalog_table} ct
-JOIN apex_audit_cache ac ON ac.shopify_id::text = ct.shopify_product_id
-WHERE ((ct.repeat_v IS NOT NULL AND ct.repeat_v != '' AND ac.has_repeat_v = false)
- OR (ct.repeat_h IS NOT NULL AND ct.repeat_h != '' AND ac.has_repeat_h = false)
- OR (ct.match_type IS NOT NULL AND ct.match_type != ''));
-```
-
-2. Queue metafield pushes through Quinn for each:
-```sql
-INSERT INTO shopify_api_queue (method, endpoint, payload, token_alias, priority, source_agent)
-VALUES ('POST', '/graphql.json', $payload, 'product', 3, 'apex-repeat-push');
-```
-
-Where payload is a `metafieldsSet` mutation pushing ALL available fields in ONE call:
-- `specs.repeat_v` = vertical repeat value (if found)
-- `specs.repeat_h` = horizontal repeat value (if found)
-- `specs.match_type` = match type: Straight, Drop, Half Drop, Random (if found)
-
-Batch all three metafields into a single `metafieldsSet` call per product to save API calls.
-
-3. Run for these tables: `versace_catalog`, `thibaut_catalog`, `kravet_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_catalog`
-
-4. Report: how many repeat values pushed per vendor
-
-## Important
-- Do NOT duplicate — check shopify_api_queue for existing pending entries first
-- Use priority 3 (lower than Apex's priority 2 fixes)
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/19_19_3am-health-check.md b/tasks/done/19_19_3am-health-check.md
deleted file mode 100644
index 047d54b..0000000
--- a/tasks/done/19_19_3am-health-check.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Late night health check.
-1. df -h / — report disk
-2. free -h — report memory
-3. Check DWTT jobs still running: ps aux | grep -E "dwtt-body|swatch-spin" | grep -v grep
-4. tail -3 /tmp/dwtt-metafields.log
-5. tail -3 /tmp/dwtt-spins.log
-6. pm2 list | grep -c online — report how many agents online
-7. Report summary for Steve in the morning
\ No newline at end of file
diff --git a/tasks/done/19_19_enrich-grahambrown.md b/tasks/done/19_19_enrich-grahambrown.md
deleted file mode 100644
index 5f87cad..0000000
--- a/tasks/done/19_19_enrich-grahambrown.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-grahambrown-width.js
-
-Enrich Graham & Brown catalog.
\ No newline at end of file
diff --git a/tasks/done/19_check-in-progress-items.md b/tasks/done/19_check-in-progress-items.md
deleted file mode 100644
index a880284..0000000
--- a/tasks/done/19_check-in-progress-items.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Check In-Progress Items from Task Ledger
-
-Several items were marked IN-PROGRESS in the task ledger. Verify their status.
-
-### Tasks:
-1. Check PM2 process `fix-sample-weights` (id 381) — is it still running? Did it complete successfully?
- - Run: `pm2 info fix-sample-weights`
- - Check logs: `timeout 10 pm2 logs fix-sample-weights --lines 50 --nostream`
- - If completed: report final count of variants fixed
- - If errored: report the error
-
-2. Check if DWC legacy cross-reference audit (106-17) completed
- - Look for output/results in /root/DW-Agents/ or /tmp/
-
-3. Check if Nora Run 91 push (106-13) completed
- - Check script: `/root/DW-Agents/nora-agent/push-run91.js`
- - Check PM2 or logs for completion status
-
-4. Report status of all in-progress items so the task ledger can be updated
diff --git a/tasks/done/19_phase2-nina-shopify-api.md b/tasks/done/19_phase2-nina-shopify-api.md
deleted file mode 100644
index d353b89..0000000
--- a/tasks/done/19_phase2-nina-shopify-api.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Phase 2: Nina Campbell Specs via Shopify API (448 gap)
-
-## Goal
-Nina Campbell runs on Shopify. The scraper (scrape-nina.js) pulls products.json which may include specs in metafields or description.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 600 node scrape-nina.js
-```
-
-After completion, sync from nina_campbell_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(nc.width, vc.width),
- pattern_repeat = COALESCE(nc.repeat_v, vc.pattern_repeat),
- composition = COALESCE(nc.material, vc.composition),
- updated_at = NOW()
-FROM nina_campbell_catalog nc
-WHERE vc.mfr_sku = nc.mfr_sku AND vc.vendor_code = 'nina_campbell'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/19_vcc-stats-refresh.md b/tasks/done/19_vcc-stats-refresh.md
deleted file mode 100644
index 3a9699c..0000000
--- a/tasks/done/19_vcc-stats-refresh.md
+++ /dev/null
@@ -1,24 +0,0 @@
-## VCC — Refresh Vendor Registry Stats
-
-After overnight enrichment, refresh the vendor_registry stats for all vendors that were enriched.
-
-### Task:
-1. Connect to PostgreSQL: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-2. For each vendor with a catalog_table, update the stats:
- ```sql
- -- Template for each vendor:
- UPDATE vendor_registry SET
- catalog_count = (SELECT COUNT(*) FROM {table}),
- catalog_with_images = (SELECT COUNT(*) FROM {table} WHERE image_url IS NOT NULL AND image_url != ''),
- catalog_with_width = (SELECT COUNT(*) FROM {table} WHERE width IS NOT NULL AND width != ''),
- catalog_with_repeat = (SELECT COUNT(*) FROM {table} WHERE repeat_v IS NOT NULL AND repeat_v != ''),
- catalog_on_shopify = (SELECT COUNT(*) FROM {table} WHERE shopify_product_id IS NOT NULL),
- catalog_not_on_shopify = (SELECT COUNT(*) FROM {table} WHERE shopify_product_id IS NULL),
- shopify_count = (SELECT COUNT(*) FROM {table} WHERE shopify_product_id IS NOT NULL),
- updated_at = NOW()
- WHERE catalog_table = '{table}';
- ```
-3. Run this for: farrow_ball_catalog, andrew_martin_catalog, zoffany_catalog, donghia_catalog, roberto_cavalli_catalog, versace_catalog, zimmer_rohde_catalog, nlxl_catalog, kirkby_catalog, villa_nova_catalog, black_edition_catalog, zinc_catalog, nobilis_catalog, designtex_catalog, british_walls_catalog, cmo_paris_catalog, studio_printworks_catalog, novasuede_catalog, ultrasuede_catalog, versa_catalog
-4. Report how many vendors were updated.
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/19_zero-repeat-textures.md b/tasks/done/19_zero-repeat-textures.md
deleted file mode 100644
index 3453364..0000000
--- a/tasks/done/19_zero-repeat-textures.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Set Repeat = 0 for Texture Products (No Pattern = No Repeat)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Many products missing repeat aren't patterns — they're TEXTURES (grasscloth, linen, stucco, metallic, plain, solid)
-- Textures have zero repeat. If a scraper can't find a repeat, AND the image confirms it's a texture, set repeat_v = '0' and repeat_h = '0'
-
-## Task
-For each vendor catalog table that has products still missing repeats AFTER the scraper tasks run:
-
-### Step 1: Identify likely textures from names/descriptions
-```sql
--- Products missing repeat that are likely textures based on name/description
-SELECT id, sku, name, product_url, image_url
-FROM {catalog_table}
-WHERE (repeat_v IS NULL OR repeat_v = '' )
- AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
- AND (
- LOWER(name) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave|burlap|faux |jute|suede|cork|hemp|raffia|sand|stone|concrete|plaster|canvas)'
- OR LOWER(COALESCE(description, '')) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave)'
- );
-```
-
-For these, set repeat_v = '0' and repeat_h = '0' directly — name match is sufficient.
-
-### Step 2: For remaining unknown products, use Gemini Vision
-For products that still have no repeat AND didn't match the name pattern:
-
-1. Send the product's `image_url` to Gemini 2.0 Flash vision:
- - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- - Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
-
-2. If Gemini says TEXTURE → set repeat_v = '0', repeat_h = '0'
-3. If Gemini says PATTERN → leave repeat as NULL (needs scraping or manual entry)
-4. Log all Gemini calls and track cost via `require('/root/DW-Agents/shared/gemini-cost-tracker.js')`
-
-### Step 3: Update DB
-```sql
-UPDATE {catalog_table} SET repeat_v = '0', repeat_h = '0', updated_at = NOW() WHERE id = $1;
-```
-
-Run for: `versace_catalog`, `thibaut_catalog`, `kravet_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_catalog`
-
-## Important
-- Run AFTER tasks 13-17 (scraping) complete — this handles the leftovers
-- Gemini analysis key: `${GOOGLE_API_KEY}`
-- Rate limit Gemini to 10 req/s (flash is generous)
-- Track Gemini costs with the shared tracker
-- Report per vendor: how many set to zero by name, how many by Gemini, how many still unknown
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/20_20_enrich-maya.md b/tasks/done/20_20_enrich-maya.md
deleted file mode 100644
index 3663a31..0000000
--- a/tasks/done/20_20_enrich-maya.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-maya-specs.js
-
-Enrich Maya Romanoff catalog.
\ No newline at end of file
diff --git a/tasks/done/20_nora-fix-pipeline-org-scoping.md b/tasks/done/20_nora-fix-pipeline-org-scoping.md
deleted file mode 100644
index 3e89665..0000000
--- a/tasks/done/20_nora-fix-pipeline-org-scoping.md
+++ /dev/null
@@ -1,38 +0,0 @@
-Fix CRITICAL org-scoping bug in Nora pipeline API.
-
-Project: /root/Projects/Nora
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/nora
-Port: 7400 | PM2: nora-email
-
-## Problem
-`/api/pipeline/route.ts` GET returns ALL pipeline items globally — no org_id filter.
-`/api/pipeline/[id]/route.ts` GET/PUT/DELETE can manipulate any item without org ownership check.
-This means switching between nonprofit orgs (Feeding America, NAACP, Sierra Club, United Way) shows the SAME pipeline data for everyone.
-
-## How other routes do it correctly
-Look at `/api/drafts/route.ts` or `/api/petitions/route.ts` — they use:
-```
-import { getOrgId } from '@/lib/orgId';
-const orgId = getOrgId(request);
-// Then: WHERE org_id = $1
-```
-
-## Fix Required
-1. Open `app/api/pipeline/route.ts`
-2. Import `getOrgId` from `@/lib/orgId`
-3. Add `const orgId = getOrgId(request);` at the start of GET handler
-4. Add `WHERE org_id = $1` (or AND org_id = $1) to the SQL query, passing orgId as parameter
-5. For POST handler — ensure org_id is set from the header when creating new items
-6. Open `app/api/pipeline/[id]/route.ts`
-7. Add org_id ownership check to GET, PUT, DELETE — ensure the item belongs to the requesting org
-8. After fixing, rebuild: `cd /root/Projects/Nora && npx next build`
-9. Restart: `pm2 restart nora-email`
-10. Test with curl:
-```bash
-# Should return only items for this org
-curl -s -b "nora-auth=$(node -e "const{createSession}=require('/root/Projects/Nora/lib/auth');console.log(createSession('admin'))")" \
- -H "X-Org-Id: 5bab29c7-f24f-47cf-8556-277de5255c1e" \
- http://localhost:7400/api/pipeline | python3 -m json.tool | head -20
-```
-
-Report: what was changed, how many lines, test results.
diff --git a/tasks/done/20_phase2-osborne-specs.md b/tasks/done/20_phase2-osborne-specs.md
deleted file mode 100644
index 0c5fb27..0000000
--- a/tasks/done/20_phase2-osborne-specs.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Phase 2: Osborne & Little Specs (872 gap)
-
-## Goal
-Osborne has a Phase 1 scraper (scrape-osborne.js) with dual strategy: trade API + Puppeteer.
-The trade API may return specs. Image enrichment script also exists.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node scrape-osborne.js
-```
-
-After completion, sync from osborne_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(oc.width, vc.width),
- pattern_repeat = COALESCE(oc.repeat_v, vc.pattern_repeat),
- composition = COALESCE(oc.material, vc.composition),
- updated_at = NOW()
-FROM osborne_catalog oc
-WHERE vc.mfr_sku = oc.mfr_sku AND vc.vendor_code = 'osborne'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/20_phoebe-videos-dir-fix.md b/tasks/done/20_phoebe-videos-dir-fix.md
deleted file mode 100644
index 1e15543..0000000
--- a/tasks/done/20_phoebe-videos-dir-fix.md
+++ /dev/null
@@ -1,11 +0,0 @@
-## Phoebe Agent — Fix Missing Videos Directory
-
-Session #96: Phoebe's home route crashes due to missing `videos` directory.
-
-### Tasks:
-1. Find Phoebe's working directory (PM2 name: figma-agent, port 9668)
-2. Create the missing `videos` directory
-3. Also check for other missing directories (reports, screenshots, gifs)
-4. Restart Phoebe: `pm2 restart figma-agent`
-5. Verify home route works: `curl -u admin:DWSecure2024! http://localhost:9668/`
-6. Report: what was fixed, Phoebe status after restart
diff --git a/tasks/done/20_pm2-health-cleanup.md b/tasks/done/20_pm2-health-cleanup.md
deleted file mode 100644
index 7488996..0000000
--- a/tasks/done/20_pm2-health-cleanup.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## PM2 Health Check & Cleanup
-
-Clean up stale/dead PM2 processes and check system health.
-
-### Steps:
-1. List all errored/stopped processes: pm2 jlist | python3 -c "import json,sys;d=json.load(sys.stdin);[print(f'{p[\"pm_id\"]} {p[\"name\"]} {p[\"pm2_env\"][\"status\"]} restarts={p[\"pm2_env\"].get(\"restart_time\",0)}') for p in d if p['pm2_env']['status'] in ('errored','stopped')]"
-2. Delete processes that are stopped and not needed (old one-off scripts)
-3. Check for high restart count processes (>100 restarts = crash loop)
-4. Check disk: df -h /
-5. Check memory: free -h
-6. Check for zombie Claude processes: ps aux | grep claude | grep -v grep
-7. Kill any Claude process running >12 hours
-8. Truncate PM2 logs over 50MB: find /root/.pm2/logs -name "*.log" -size +50M -exec truncate -s 0 {} \;
-9. Report system health summary
\ No newline at end of file
diff --git a/tasks/done/20_vcc-all-catalogs-ready.md b/tasks/done/20_vcc-all-catalogs-ready.md
deleted file mode 100644
index bd9b4ab..0000000
--- a/tasks/done/20_vcc-all-catalogs-ready.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## VCC — All Catalogs Ready (URGENT)
-
-Vendor Command Center (port 9660) needs ALL vendor catalogs verified and ready. This is the highest priority.
-
-### Tasks:
-1. Query vendor_registry for ALL vendors: `SELECT vendor_name, vendor_code, catalog_table, total_products, sku_prefix FROM vendor_registry ORDER BY vendor_name`
-2. For each vendor with a catalog_table: verify the table exists and has products
-3. Check catalog_count accuracy — update total_products in vendor_registry to match actual row counts
-4. Report vendors with 0 products (candidates for inquiry pages)
-5. Report vendors missing catalog_table entries
-6. Update VCC dashboard data: POST http://localhost:9660/api/refresh-stats (if endpoint exists)
-7. Final report: vendor name, catalog table, product count, has_images %, has_specs %, has_dw_sku %
-
-CRITICAL: Do NOT create any new Shopify products. Only audit and report.
-CRITICAL: rwltd_catalog = Reid Witlin (NOT Rebel Walls)
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-VCC: http://localhost:9660
diff --git a/tasks/done/21_21_enrich-stout.md b/tasks/done/21_21_enrich-stout.md
deleted file mode 100644
index 6a3fa05..0000000
--- a/tasks/done/21_21_enrich-stout.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-stout-width.js
-
-Enrich Stout Textiles catalog.
\ No newline at end of file
diff --git a/tasks/done/21_knoll-maharam-scraper-investigate.md b/tasks/done/21_knoll-maharam-scraper-investigate.md
deleted file mode 100644
index 02c4263..0000000
--- a/tasks/done/21_knoll-maharam-scraper-investigate.md
+++ /dev/null
@@ -1,15 +0,0 @@
-## Knoll/Maharam Scraper Investigation
-
-Session #79: Knoll scraper broken — maharam.com times out completely.
-
-### Tasks:
-1. Check current knoll_catalog count: `SELECT COUNT(*) FROM knoll_catalog;`
-2. Read current scraper at `/root/DW-Agents/vendor-scrapers/scrape-knoll.js` (or find it)
-3. Test if maharam.com is accessible: `curl -sI https://www.maharam.com/ | head -10`
-4. Check if they have an API, sitemap.xml, or CDN endpoint
-5. Check if Cloudflare/bot protection is blocking
-6. Try alternative approaches: sitemap parsing, Google cache, Wayback Machine
-7. Report findings: is the site accessible, what's blocking, recommended approach
-
-DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-NOTE: This is investigative — don't force a scrape if the site actively blocks.
diff --git a/tasks/done/21_nora-fix-contacts-org-scoping.md b/tasks/done/21_nora-fix-contacts-org-scoping.md
deleted file mode 100644
index 1d76cbe..0000000
--- a/tasks/done/21_nora-fix-contacts-org-scoping.md
+++ /dev/null
@@ -1,34 +0,0 @@
-Fix CRITICAL org-scoping bug in Nora contacts API.
-
-Project: /root/Projects/Nora
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/nora
-Port: 7400 | PM2: nora-email
-
-## Problem
-`/api/contacts/route.ts` GET returns ALL contacts globally — no org_id filter.
-Switching between nonprofit orgs shows the same contacts for everyone.
-
-## How other routes do it correctly
-Look at `/api/drafts/route.ts` — they use:
-```
-import { getOrgId } from '@/lib/orgId';
-const orgId = getOrgId(request);
-// Then: WHERE org_id = $1
-```
-
-## Fix Required
-1. Open `app/api/contacts/route.ts`
-2. Import `getOrgId` from `@/lib/orgId`
-3. Add org_id filtering to GET query
-4. Add org_id to POST (creating contacts)
-5. Check `app/api/contacts/[id]/route.ts` if it exists — add org ownership checks
-6. Check the `contacts` table schema: `psql postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/nora -c "\d contacts"`
-7. If `contacts` table lacks an `org_id` column, ADD it:
- ```sql
- ALTER TABLE contacts ADD COLUMN IF NOT EXISTS org_id UUID REFERENCES nonprofit_accounts(id);
- ```
-8. After fixing, rebuild: `cd /root/Projects/Nora && npx next build`
-9. Restart: `pm2 restart nora-email`
-10. Test with curl for different org IDs to verify isolation
-
-Report: schema changes, code changes, test results.
diff --git a/tasks/done/21_phase2-wolfgordon-specs.md b/tasks/done/21_phase2-wolfgordon-specs.md
deleted file mode 100644
index 5acec15..0000000
--- a/tasks/done/21_phase2-wolfgordon-specs.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Phase 2: Wolf Gordon Specs via Sitemap Scraper (871 gap)
-
-## Goal
-Wolf Gordon has a scraper (scrape-wolfgordon.js) that crawls via sitemap and individual product pages with colorway thumbnails. Also has image enrichment script.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node scrape-wolfgordon.js
-```
-
-After completion, sync from wolf_gordon_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(wg.width, vc.width),
- pattern_repeat = COALESCE(wg.repeat_v, vc.pattern_repeat),
- composition = COALESCE(wg.material, vc.composition),
- fire_rating = COALESCE(wg.fire_rating, vc.fire_rating),
- updated_at = NOW()
-FROM wolf_gordon_catalog wg
-WHERE vc.mfr_sku = wg.mfr_sku AND vc.vendor_code = 'wolf_gordon'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/21_scrape-repeats-thibaut-r2.md b/tasks/done/21_scrape-repeats-thibaut-r2.md
deleted file mode 100644
index 592bd53..0000000
--- a/tasks/done/21_scrape-repeats-thibaut-r2.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Scrape Missing Repeat Measurements — Thibaut Round 2 (2,071 remaining)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Table: `thibaut_catalog`
-- Round 1 scraped ~489 products. 2,071 still missing repeat_v or repeat_h with shopify_product_id
-- The WHERE clause naturally skips already-scraped products
-
-## Task
-Write and run a Node.js scraper (or reuse `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-thibaut.js` if it exists):
-
-1. Query products missing EITHER repeat that have a product_url:
-```sql
-SELECT id, sku, name, product_url FROM thibaut_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0'))
- AND product_url IS NOT NULL AND product_url != ''
- AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
-ORDER BY id DESC
-LIMIT 500;
-```
-
-2. For each product_url, fetch the page with `node-fetch` (respect 1 req/s rate limit)
-3. Parse the HTML for BOTH vertical AND horizontal repeat info — look for:
- - "Vertical Repeat", "V. Repeat", "Pattern Repeat", "Repeat" -> repeat_v
- - "Horizontal Repeat", "H. Repeat", "Half Drop" -> repeat_h
- - "Match", "Match Type", "Pattern Match" -> match_type (values: "Straight", "Drop", "Half Drop", "Random", "Free Match")
- - Common formats: `27"`, `27 inches`, `68.6 cm`, `9.5 in (24.13 cm)`
- - If page says "Half Drop" or "Half Drop Match" -> set match_type = 'Half Drop'
- - If page says "Straight Match" or "Straight Across" -> set match_type = 'Straight'
- - If page says "Random" or "Random Match" or "Free Match" -> set match_type = 'Random'
- - If page says "Drop Match" -> set match_type = 'Drop'
-4. Update the DB with ALL values:
-```sql
-UPDATE thibaut_catalog SET repeat_v = $1, repeat_h = $2, match_type = $3, updated_at = NOW() WHERE id = $4;
-```
-5. Log progress every 50 products
-6. Report final stats: how many V found, how many H found, how many still missing each
-
-## Important
-- Use `timeout 30` on any shell commands
-- Rate limit to 1 request per second (Thibaut CDN)
-- If a page returns 404/301, skip it and mark in logs
-- Do NOT hit Shopify API — this is DB-only work
-- Save the scraper to `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-thibaut.js`
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/22_22_enrich-harlequin.md b/tasks/done/22_22_enrich-harlequin.md
deleted file mode 100644
index 0993fce..0000000
--- a/tasks/done/22_22_enrich-harlequin.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-harlequin-images.js
-
-Enrich Harlequin images and specs.
\ No newline at end of file
diff --git a/tasks/done/22_cron-audit-cleanup.md b/tasks/done/22_cron-audit-cleanup.md
deleted file mode 100644
index 9027077..0000000
--- a/tasks/done/22_cron-audit-cleanup.md
+++ /dev/null
@@ -1,16 +0,0 @@
-## Cron Job Audit & Cleanup
-
-Multiple sessions mentioned stale/broken cron jobs and timezone issues.
-
-### Tasks:
-1. List all crontab entries: `crontab -l`
-2. Check each cron job:
- - Is the script file still there?
- - Is the timezone correct? (server is UTC, store is Pacific)
- - Is the schedule reasonable?
-3. Identify duplicates or conflicting jobs (especially Shopify push scripts that share daily limits)
-4. Check agent-cronjob (port 9602) for its managed jobs
-5. Report: total cron jobs, any broken/stale, timezone issues, conflicts
-6. Fix any obvious issues (missing scripts, wrong timezone offsets)
-
-DO NOT delete cron jobs — only fix and report. Leave deletion for Steve.
diff --git a/tasks/done/22_nora-audit-all-api-org-scoping.md b/tasks/done/22_nora-audit-all-api-org-scoping.md
deleted file mode 100644
index d2d21e5..0000000
--- a/tasks/done/22_nora-audit-all-api-org-scoping.md
+++ /dev/null
@@ -1,50 +0,0 @@
-Audit ALL Nora API routes for org-scoping and fix any that leak data across organizations.
-
-Project: /root/Projects/Nora
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/nora
-Port: 7400 | PM2: nora-email
-
-## Context
-Nora is a multi-tenant nonprofit platform with 5 orgs (Feeding America, NAACP, Sierra Club, United Way, etc.).
-Each org should only see its own data. The org filtering pattern uses:
-```typescript
-import { getOrgId } from '@/lib/orgId';
-const orgId = getOrgId(request);
-// SQL: WHERE org_id = $1
-```
-
-## Already Fixed (skip these)
-- /api/drafts ✅
-- /api/petitions ✅
-- /api/sessions ✅
-- /api/news ✅
-- /api/grants ✅
-- /api/statements ✅
-- /api/pipeline ✅ (fixed in task 20)
-- /api/contacts ✅ (fixed in task 21)
-
-## Audit These Routes
-Scan EVERY route file under `app/api/` and check if it:
-1. Reads from a table that has an `org_id` column
-2. Actually filters by org_id using getOrgId()
-
-Routes to audit (likely need fixing):
-- /api/intelligence/* (power-intel, journalists, community)
-- /api/pulse/* (topics, feed, heatmap)
-- /api/geo/* (hotspots, zip-report, state-heatmap)
-- /api/donations/route.ts
-- /api/donation-insights/route.ts
-- /api/email-campaigns/route.ts
-- /api/collaborations/route.ts
-- /api/community/route.ts
-- /api/journalists/route.ts
-- /api/outreach/route.ts
-- /api/library/route.ts
-
-## Steps
-1. `find /root/Projects/Nora/app/api -name "route.ts" | sort` — list all routes
-2. For each: grep for `getOrgId` — if missing, check if table has org_id column
-3. Fix any that should be org-scoped but aren't
-4. Check database tables: which ones have org_id? `psql ... -c "SELECT table_name, column_name FROM information_schema.columns WHERE column_name='org_id' ORDER BY table_name;"`
-5. After all fixes: rebuild and restart
-6. Report: list of routes fixed, routes that are correctly global (accounts, settings, etc)
diff --git a/tasks/done/22_phase2-arte-specs.md b/tasks/done/22_phase2-arte-specs.md
deleted file mode 100644
index dfc66ba..0000000
--- a/tasks/done/22_phase2-arte-specs.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Phase 2: Arte International Specs (455 gap)
-
-## Goal
-Arte has a JSON API similar to Harlequin (both Sanderson Design Group). Config shows Cloudflare protection.
-Image enrichment script exists. Need to extract specs from product pages or API.
-
-## Run
-First check what specs exist in arte_catalog already:
-```sql
-SELECT COUNT(*) AS total,
- COUNT(width) AS have_width,
- COUNT(repeat_v) AS have_repeat
-FROM arte_catalog;
-```
-
-If arte_catalog has specs, sync to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(ac.width, vc.width),
- pattern_repeat = COALESCE(ac.repeat_v, vc.pattern_repeat),
- composition = COALESCE(ac.material, vc.composition),
- updated_at = NOW()
-FROM arte_catalog ac
-WHERE vc.mfr_sku = ac.mfr_sku AND vc.vendor_code = 'arte'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-If arte_catalog is empty on specs, check for a scraper or run the v2 agent:
-```bash
-cd /root/DW-Agents/vendor-scrapers && ls *arte* && timeout 600 node scrape-arte.js 2>/dev/null || echo "No scrape-arte.js found"
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched or gap remaining.
diff --git a/tasks/done/22_scrape-repeats-schumacher-r2.md b/tasks/done/22_scrape-repeats-schumacher-r2.md
deleted file mode 100644
index c85f279..0000000
--- a/tasks/done/22_scrape-repeats-schumacher-r2.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Scrape Missing Repeat Measurements — Schumacher Round 2 (79 remaining)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Table: `schumacher_catalog`
-- Round 1 handled most. Only 79 products on Shopify still missing repeat_v or repeat_h
-- Schumacher product pages: fschumacher.com
-
-## Task
-Reuse or write a Node.js scraper (check `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-schumacher.js`):
-
-1. Query all remaining Shopify products missing repeats with a product_url:
-```sql
-SELECT id, sku, name, product_url FROM schumacher_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0'))
- AND product_url IS NOT NULL AND product_url != ''
- AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
-ORDER BY id DESC;
-```
-
-2. For each product_url, fetch with `node-fetch` (1 req/s)
-3. Parse HTML for repeat info — Schumacher shows it in product details:
- - "Repeat", "Vertical Repeat", "V Repeat" -> repeat_v
- - "Horizontal Repeat" / "H Repeat" -> repeat_h
- - "Match", "Match Type", "Pattern Match" -> match_type
- - If "Half Drop" -> match_type = 'Half Drop'
- - If "Straight" -> match_type = 'Straight'
- - If "Random" or "Free Match" -> match_type = 'Random'
-4. Update DB with ALL THREE:
-```sql
-UPDATE schumacher_catalog SET repeat_v = $1, repeat_h = $2, match_type = $3, updated_at = NOW() WHERE id = $4;
-```
-5. Log all progress
-6. Report final stats
-
-## Important
-- Rate limit 1 req/s
-- Do NOT hit Shopify API
-- Save scraper to `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-schumacher.js`
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/23_23_enrich-catchii.md b/tasks/done/23_23_enrich-catchii.md
deleted file mode 100644
index 335df04..0000000
--- a/tasks/done/23_23_enrich-catchii.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-catchii-specs.js
-
-Enrich Catchii catalog.
\ No newline at end of file
diff --git a/tasks/done/23_nora-ux-audit-per-org.md b/tasks/done/23_nora-ux-audit-per-org.md
deleted file mode 100644
index 35465f4..0000000
--- a/tasks/done/23_nora-ux-audit-per-org.md
+++ /dev/null
@@ -1,87 +0,0 @@
-Rigorous UX audit of Nora dashboard — click through EVERY page for EACH nonprofit org and fix broken UI.
-
-Project: /root/Projects/Nora
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/nora
-Port: 7400 | PM2: nora-email
-
-## Context
-Nora has 5 nonprofit accounts. Every button, link, and action on the dashboard must work correctly when scoped to each org. The admin login is admin / DWSecure2024!
-
-## Orgs to test
-1. Feeding America (FA) — 5bab29c7-f24f-47cf-8556-277de5255c1e
-2. NAACP — b326b9c0-4fd2-40e0-982c-3b2a71eeda0d
-3. Sierra Club — a9276267-8623-429e-b376-effe950b6c25
-4. Org 4 — 53e2a0e7-c2b3-47c7-a292-9aeda183f934
-5. United Way Worldwide (UWW) — 4fa9d85c-0203-49eb-a1eb-fde9fae8011e
-
-## For EACH org, test these pages via API:
-
-### 1. Dashboard (/)
-- Does it show the correct org name?
-- Are metrics (Active Petitions, In Pipeline, Topics Tracked) org-specific?
-
-### 2. Drafts (/drafts)
-- Can you create a new session?
-- Does the session list only show drafts for this org?
-- Does the 3-lane editor (Action/Policy/Resources) work?
-
-### 3. News (/news)
-- Does news feed load?
-- Are news items org-specific?
-
-### 4. Petitions
-- Can you list petitions for this org?
-- Can you create a petition?
-- Does filtering work?
-
-### 5. Grants
-- Does grant list load per org?
-- Can you create/edit grants?
-
-### 6. Pipeline
-- After task 20 fix — does pipeline filter by org now?
-- Can you drag items between stages?
-
-### 7. Statements
-- Does statement list load per org?
-- Can you generate a new statement?
-
-### 8. Intelligence section (10 sub-pages)
-- Power Intel, Journalists, Contacts, Community, Topic Radar, Petition Ideas, etc.
-- Do they load? Do they show org-specific data?
-
-### 9. Sidebar navigation
-- Do all sidebar links navigate to the correct pages?
-- Do badge counts update per org?
-- Does the org switcher dropdown work?
-
-## How to test
-Use curl with auth cookie and X-Org-Id header:
-```bash
-# Generate auth cookie
-AUTH=$(cd /root/Projects/Nora && node -e "
-const crypto = require('crypto');
-const ts = Date.now().toString();
-const sig = crypto.createHash('sha256').update('admin:' + ts + ':nora-fallback-secret').digest('hex');
-console.log(Buffer.from('admin:' + ts + ':' + sig).toString('base64'));
-")
-
-# Test each org
-for ORG_ID in "5bab29c7-f24f-47cf-8556-277de5255c1e" "b326b9c0-4fd2-40e0-982c-3b2a71eeda0d" "a9276267-8623-429e-b376-effe950b6c25" "53e2a0e7-c2b3-47c7-a292-9aeda183f934" "4fa9d85c-0203-49eb-a1eb-fde9fae8011e"; do
- echo "=== Testing org: $ORG_ID ==="
- curl -s -b "nora-auth=$AUTH" -H "X-Org-Id: $ORG_ID" http://localhost:7400/api/drafts | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' Drafts: {len(d) if isinstance(d,list) else d}')"
- curl -s -b "nora-auth=$AUTH" -H "X-Org-Id: $ORG_ID" http://localhost:7400/api/petitions | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' Petitions: {len(d) if isinstance(d,list) else d}')"
- curl -s -b "nora-auth=$AUTH" -H "X-Org-Id: $ORG_ID" http://localhost:7400/api/pipeline | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' Pipeline: {len(d) if isinstance(d,list) else d}')"
- curl -s -b "nora-auth=$AUTH" -H "X-Org-Id: $ORG_ID" http://localhost:7400/api/grants | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' Grants: {len(d) if isinstance(d,list) else d}')"
- curl -s -b "nora-auth=$AUTH" -H "X-Org-Id: $ORG_ID" http://localhost:7400/api/news | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' News: {len(d) if isinstance(d,list) else d}')"
-done
-```
-
-## Fix anything broken:
-- 404 pages → create the missing page component
-- Empty states → add "No data yet" UI with proper messaging
-- Broken buttons → wire up the onClick handler to the correct API
-- Missing org filtering → add getOrgId() to the API route
-- After all fixes: `cd /root/Projects/Nora && npx next build && pm2 restart nora-email`
-
-Report: for each org, which pages worked, which were broken, what was fixed.
diff --git a/tasks/done/23_phase2-designers-guild.md b/tasks/done/23_phase2-designers-guild.md
deleted file mode 100644
index 4168385..0000000
--- a/tasks/done/23_phase2-designers-guild.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Phase 2: Designers Guild Specs (746 gap)
-
-## Goal
-Designers Guild has a Phase 1 scraper (scrape-designers-guild.js) that crawls designersguild.com.
-The Phase 1 scraper already extracts width and composition from HTML/JSON-LD.
-Re-run it to populate specs, then sync.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node scrape-designers-guild.js
-```
-
-After completion, sync from designers_guild_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(dg.width, vc.width),
- pattern_repeat = COALESCE(dg.repeat_v, vc.pattern_repeat),
- composition = COALESCE(dg.composition, dg.material, vc.composition),
- updated_at = NOW()
-FROM designers_guild_catalog dg
-WHERE vc.mfr_sku = dg.mfr_sku AND vc.vendor_code = 'designers_guild'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/23_scrape-repeats-brewster-york-r2.md b/tasks/done/23_scrape-repeats-brewster-york-r2.md
deleted file mode 100644
index f64ef7c..0000000
--- a/tasks/done/23_scrape-repeats-brewster-york-r2.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Scrape Missing Repeat/Match Data — Brewster & York Round 2 (265 + 289 remaining)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Tables: `brewster_catalog` (265 remaining), `york_catalog` (289 remaining)
-- Both use yorkwall.com trade portal
-- Round 1 scraped initial batch. This handles the next 500 per vendor.
-
-## Task
-Reuse or write a Node.js scraper (check `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-brewster-york.js`):
-
-### Part 1: Brewster (prioritize Shopify products first)
-```sql
-SELECT id, sku, name, product_url FROM brewster_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0')
- OR (match_type IS NULL OR match_type = ''))
- AND product_url IS NOT NULL AND product_url != ''
-ORDER BY (shopify_product_id IS NOT NULL AND shopify_product_id != '') DESC, id DESC
-LIMIT 500;
-```
-
-### Part 2: York (prioritize Shopify products first)
-```sql
-SELECT id, sku, name, product_url FROM york_catalog
-WHERE ((repeat_v IS NULL OR repeat_v = '' OR repeat_v = '0')
- OR (repeat_h IS NULL OR repeat_h = '' OR repeat_h = '0')
- OR (match_type IS NULL OR match_type = ''))
- AND product_url IS NOT NULL AND product_url != ''
-ORDER BY (shopify_product_id IS NOT NULL AND shopify_product_id != '') DESC, id DESC
-LIMIT 500;
-```
-
-For each product_url:
-1. Fetch with `node-fetch` (1 req/s — yorkwall.com is a trade portal)
-2. Parse for ALL rendering data:
- - "Vertical Repeat", "Pattern Repeat", "Repeat" -> repeat_v
- - "Horizontal Repeat", "H. Repeat" -> repeat_h
- - "Match", "Match Type", "Pattern Match" -> match_type (values: "Straight", "Drop", "Half Drop", "Random", "Free Match")
- - If "Half Drop" -> match_type = 'Half Drop'
- - If "Straight" -> match_type = 'Straight'
- - If "Random" or "Free Match" -> match_type = 'Random'
-3. Update the respective catalog table with ALL found values (repeat_v, repeat_h, match_type)
-4. Log progress and report stats per vendor
-
-## Important
-- Rate limit 1 req/s (both share yorkwall.com infrastructure)
-- If 403/login wall, log it and skip — some URLs may require auth
-- Do NOT hit Shopify API
-- Save to `/root/DW-Agents/apex-agent/scrapers/scrape-repeats-brewster-york.js`
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/24_24_enrich-ellipopp.md b/tasks/done/24_24_enrich-ellipopp.md
deleted file mode 100644
index e4cc103..0000000
--- a/tasks/done/24_24_enrich-ellipopp.md
+++ /dev/null
@@ -1,3 +0,0 @@
-cd /root/DW-Agents/vendor-scrapers && node enrich-ellipopp-specs.js
-
-Enrich Elli Popp catalog.
\ No newline at end of file
diff --git a/tasks/done/24_nora-sidebar-buttons-fix.md b/tasks/done/24_nora-sidebar-buttons-fix.md
deleted file mode 100644
index 4e45b26..0000000
--- a/tasks/done/24_nora-sidebar-buttons-fix.md
+++ /dev/null
@@ -1,57 +0,0 @@
-Fix ALL sidebar navigation items and buttons in Nora to ensure they work correctly per org.
-
-Project: /root/Projects/Nora
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/nora
-Port: 7400 | PM2: nora-email
-
-## Problem
-The sidebar has 62+ navigation items across multiple sections. Some may not navigate properly, some badge counts may not be org-specific, and the "2 more" collapsed section needs to expand correctly.
-
-## Sidebar sections to verify (from Sidebar.tsx)
-
-### N WORKSPACE
-- Dashboard, Drafts, News, Petitions, Grants, Pipeline, Statements, Donations
-- Each should navigate to the correct page
-- Badge counts (D, P, L, etc.) should reflect the ACTIVE org's data
-
-### INTELLIGENCE (10 items)
-- Power Intel, Journalists, Contacts, Community, Topic Radar, Petition Ideas, etc.
-- Count badge should show items for active org
-
-### GEO INTELLIGENCE (9 items)
-- Zip Report, Hotspot Map, Advocacy Targets, State Heatmap, etc.
-
-### PULSE (6 items)
-- Feed, Globe, Mind Map, Data Sources, Onboard, Heatmap
-
-### CONGRESS (2 items)
-- Congress, Opportunity Map
-
-### NEXUS (9 items)
-- Explorer, Power Map, Viz Hub, 3D Network, Sunburst, etc.
-
-### AI AGENTS (5 items)
-- Petition Builder, Grant Finder, Advocacy Kit, Onboarding
-
-### IMPACT METRICS (bottom of sidebar)
-- Active Petitions, In Pipeline, Topics Tracked — must be per-org
-
-## Steps
-1. Read `components/Sidebar.tsx` to understand the full nav structure
-2. Check each navigation item — does it link to a real page under `app/`?
-3. For any missing pages, check if a component exists but isn't routed
-4. Fix badge counts: ensure they query with org_id filter
-5. Fix the "2 more" collapse/expand functionality
-6. Ensure org switcher dropdown in sidebar header works — switching should:
- - Update localStorage `nora-active-org`
- - Reload all badge counts
- - Clear/reload the current page data
-7. Fix the IMPACT METRICS section at bottom of sidebar — ensure counts are org-filtered
-
-## Also check:
-- Top bar: Search (⌘K), grid icon, admin name, Sign out — all should work
-- Chat panel (floating button bottom-left) — should it be org-aware?
-- Keyboard shortcuts for sidebar navigation
-
-After fixes: rebuild and restart.
-Report: which items were broken, what was fixed, final state.
diff --git a/tasks/done/24_phase2-bespoke-width.md b/tasks/done/24_phase2-bespoke-width.md
deleted file mode 100644
index d8fae21..0000000
--- a/tasks/done/24_phase2-bespoke-width.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Phase 2: Bespoke Width Enrichment (684 gap)
-
-## Goal
-Bespoke is DW's own private label brand. Width enrichment script exists (enrich-bespoke-width.js).
-It pulls width from Shopify metafields via GraphQL + catalog cross-reference.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 600 node enrich-bespoke-width.js
-```
-
-After completion, sync from bespoke_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(bc.width, vc.width),
- pattern_repeat = COALESCE(bc.repeat_v, vc.pattern_repeat),
- updated_at = NOW()
-FROM bespoke_catalog bc
-WHERE vc.mfr_sku = bc.mfr_sku AND vc.vendor_code = 'bespoke'
- AND (vc.width IS NULL OR vc.width = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced.
diff --git a/tasks/done/24_push-repeats-to-shopify-r2.md b/tasks/done/24_push-repeats-to-shopify-r2.md
deleted file mode 100644
index a2fac79..0000000
--- a/tasks/done/24_push-repeats-to-shopify-r2.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Push Newly Found Repeats to Shopify via Quinn Queue — Round 2
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- After scraper tasks 21-23 run, catalog tables will have new repeat_v, repeat_h, AND match_type values
-- These ALL need to be pushed to Shopify metafields via Quinn (shopify_api_queue)
-
-## Task
-For each vendor catalog with repeat data that isn't on Shopify yet:
-
-1. Find products where DB has repeat but Shopify metafield doesn't:
-```sql
-SELECT ac.shopify_id, ct.repeat_v, ct.repeat_h, ct.match_type
-FROM {catalog_table} ct
-JOIN apex_audit_cache ac ON ac.shopify_id::text = ct.shopify_product_id
-WHERE ((ct.repeat_v IS NOT NULL AND ct.repeat_v != '' AND ac.has_repeat_v = false)
- OR (ct.repeat_h IS NOT NULL AND ct.repeat_h != '' AND ac.has_repeat_h = false)
- OR (ct.match_type IS NOT NULL AND ct.match_type != ''));
-```
-
-2. Queue metafield pushes through Quinn for each:
-```sql
-INSERT INTO shopify_api_queue (method, endpoint, payload, token_alias, priority, source_agent)
-VALUES ('POST', '/graphql.json', $payload, 'product', 3, 'apex-repeat-push-r2');
-```
-
-Where payload is a `metafieldsSet` mutation pushing ALL available fields in ONE call:
-- `specs.repeat_v` = vertical repeat value (if found)
-- `specs.repeat_h` = horizontal repeat value (if found)
-- `specs.match_type` = match type: Straight, Drop, Half Drop, Random (if found)
-
-Batch all three metafields into a single `metafieldsSet` call per product to save API calls.
-
-3. Run for these tables: `thibaut_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_catalog`
- (Skip kravet_catalog and versace_catalog — they completed in round 1)
-
-4. Report: how many repeat values pushed per vendor
-
-## Important
-- Do NOT duplicate — check shopify_api_queue for existing pending entries first
-- Use priority 3 (lower than Apex's priority 2 fixes)
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/25_nora-code-review-and-polish.md b/tasks/done/25_nora-code-review-and-polish.md
deleted file mode 100644
index 1cec424..0000000
--- a/tasks/done/25_nora-code-review-and-polish.md
+++ /dev/null
@@ -1,50 +0,0 @@
-Final code review and UX polish for the Nora nonprofit platform.
-
-Project: /root/Projects/Nora
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/nora
-Port: 7400 | PM2: nora-email
-
-## Code Review Checklist
-
-### Security
-1. Verify ALL API routes under `app/api/` require authentication (check for `verifyAuth()` calls)
-2. Verify org isolation — no route should return data from another org
-3. Check for SQL injection — all queries should use parameterized queries ($1, $2)
-4. Check for XSS in any user-generated content — ensure content is sanitized before rendering
-5. Verify rate limiting on sensitive endpoints (login, create, delete)
-
-### Data Integrity
-1. All mutation endpoints (POST/PUT/DELETE) should include org_id validation
-2. CASCADE constraints — deleting an org should not orphan data
-3. All foreign keys should reference the correct parent table
-
-### UX Polish
-1. Loading states — every data-fetching component should show a spinner/skeleton
-2. Error states — failed API calls should show a user-friendly error, not a blank screen
-3. Empty states — pages with no data should show a helpful "Get started" message
-4. Toast notifications — create/update/delete operations should show success/error toasts
-5. Responsive layout — sidebar should collapse on small screens
-
-### Component Quality
-1. No console.log statements left in production code (except intentional logging)
-2. No unused imports
-3. TypeScript types — no `any` type without justification
-4. Key props on all list items (no array index keys)
-5. useEffect cleanup — all subscriptions and listeners properly cleaned up
-
-### Performance
-1. No N+1 queries — check for loops that make individual DB calls
-2. API responses should be reasonably fast (under 500ms)
-3. Large lists should have pagination or virtual scrolling
-4. Images should have proper dimensions to avoid layout shift
-
-## Steps
-1. Run a sweep of all `app/api/` routes for the checklist above
-2. Run TypeScript compiler: `cd /root/Projects/Nora && npx tsc --noEmit 2>&1 | head -50`
-3. Check for console.logs: `grep -rn "console.log" /root/Projects/Nora/app/ /root/Projects/Nora/components/ | grep -v node_modules | grep -v ".next"`
-4. Fix any critical issues found
-5. Final rebuild: `cd /root/Projects/Nora && npx next build`
-6. Restart: `pm2 restart nora-email`
-7. Final smoke test: curl all main endpoints
-
-Report: issues found, issues fixed, remaining tech debt.
diff --git a/tasks/done/25_phase2-command54-specs.md b/tasks/done/25_phase2-command54-specs.md
deleted file mode 100644
index 8170c9c..0000000
--- a/tasks/done/25_phase2-command54-specs.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Phase 2: Command54 Specs Scrape (1,084 need repeat)
-
-## Goal
-Command54 has a scraper (scrape-command54.js) that crawls command54.com/designs.
-Re-run to pull specs from individual product pages.
-
-## Run
-```bash
-cd /root/DW-Agents/vendor-scrapers && timeout 900 node scrape-command54.js
-```
-
-After completion, sync from command54_catalog to vendor_catalog:
-```sql
-UPDATE vendor_catalog vc
-SET width = COALESCE(cc.width, vc.width),
- pattern_repeat = COALESCE(cc.repeat_v, vc.pattern_repeat),
- composition = COALESCE(cc.material, vc.composition),
- fire_rating = COALESCE(cc.fire_rating, vc.fire_rating),
- updated_at = NOW()
-FROM command54_catalog cc
-WHERE vc.mfr_sku = cc.mfr_sku AND vc.vendor_code = 'command54'
- AND (vc.width IS NULL OR vc.width = '' OR vc.pattern_repeat IS NULL OR vc.pattern_repeat = '');
-```
-
-## DB Connection
-`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-
-Report: count enriched, count synced. NEVER mention Command54 in any public content.
diff --git a/tasks/done/25_zero-repeat-textures-r2.md b/tasks/done/25_zero-repeat-textures-r2.md
deleted file mode 100644
index 5f1efef..0000000
--- a/tasks/done/25_zero-repeat-textures-r2.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Set Repeat = 0 for Texture Products — Round 2 (No Pattern = No Repeat)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Many products missing repeat aren't patterns — they're TEXTURES (grasscloth, linen, stucco, metallic, plain, solid)
-- Textures have zero repeat. If a scraper can't find a repeat, AND the image confirms it's a texture, set repeat_v = '0' and repeat_h = '0'
-
-## Task
-For each vendor catalog table that has products still missing repeats AFTER the R2 scraper tasks run:
-
-### Step 1: Identify likely textures from names/descriptions
-```sql
-SELECT id, sku, name, product_url, image_url
-FROM {catalog_table}
-WHERE (repeat_v IS NULL OR repeat_v = '' )
- AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
- AND (
- LOWER(name) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave|burlap|faux |jute|suede|cork|hemp|raffia|sand|stone|concrete|plaster|canvas)'
- OR LOWER(COALESCE(description, '')) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave)'
- );
-```
-
-For these, set repeat_v = '0' and repeat_h = '0' directly — name match is sufficient.
-
-### Step 2: For remaining unknown products, use Gemini Vision
-For products that still have no repeat AND didn't match the name pattern:
-
-1. Send the product's `image_url` to Gemini 2.0 Flash vision:
- - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- - Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
-
-2. If Gemini says TEXTURE -> set repeat_v = '0', repeat_h = '0'
-3. If Gemini says PATTERN -> leave repeat as NULL (needs scraping or manual entry)
-4. Log all Gemini calls and track cost via `require('/root/DW-Agents/shared/gemini-cost-tracker.js')`
-
-### Step 3: Update DB
-```sql
-UPDATE {catalog_table} SET repeat_v = '0', repeat_h = '0', updated_at = NOW() WHERE id = $1;
-```
-
-Run for: `thibaut_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_catalog`
-(Skip kravet_catalog and versace_catalog — completed in round 1)
-
-## Important
-- Run AFTER tasks 21-23 (scraping) complete — this handles the leftovers
-- Gemini analysis key: `${GOOGLE_API_KEY}`
-- Rate limit Gemini to 10 req/s (flash is generous)
-- Track Gemini costs with the shared tracker
-- Report per vendor: how many set to zero by name, how many by Gemini, how many still unknown
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/26_repeat-loop-requeue-r2.md b/tasks/done/26_repeat-loop-requeue-r2.md
deleted file mode 100644
index 391e93c..0000000
--- a/tasks/done/26_repeat-loop-requeue-r2.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# Repeat Scraper — Check Progress & Requeue Next Batch (Round 3)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- This task runs AFTER tasks 21-25 complete
-- It checks remaining missing repeats and creates the next round of scraper tasks
-
-## Task
-
-### Step 1: Check remaining missing repeats per vendor
-```sql
-SELECT 'thibaut' as vendor,
- COUNT(*) FILTER (WHERE ((repeat_v IS NULL OR repeat_v = '') OR (repeat_h IS NULL OR repeat_h = '')) AND product_url IS NOT NULL AND shopify_product_id IS NOT NULL) as remaining
-FROM thibaut_catalog
-UNION ALL
-SELECT 'kravet',
- COUNT(*) FILTER (WHERE ((repeat_v IS NULL OR repeat_v = '') OR (repeat_h IS NULL OR repeat_h = '')) AND product_url IS NOT NULL AND shopify_product_id IS NOT NULL)
-FROM kravet_catalog
-UNION ALL
-SELECT 'schumacher',
- COUNT(*) FILTER (WHERE ((repeat_v IS NULL OR repeat_v = '') OR (repeat_h IS NULL OR repeat_h = '')) AND product_url IS NOT NULL AND shopify_product_id IS NOT NULL)
-FROM schumacher_catalog
-UNION ALL
-SELECT 'brewster',
- COUNT(*) FILTER (WHERE ((repeat_v IS NULL OR repeat_v = '') OR (repeat_h IS NULL OR repeat_h = '')) AND product_url IS NOT NULL AND shopify_product_id IS NOT NULL)
-FROM brewster_catalog
-UNION ALL
-SELECT 'york',
- COUNT(*) FILTER (WHERE ((repeat_v IS NULL OR repeat_v = '') OR (repeat_h IS NULL OR repeat_h = '')) AND product_url IS NOT NULL AND shopify_product_id IS NOT NULL)
-FROM york_catalog
-UNION ALL
-SELECT 'versace',
- COUNT(*) FILTER (WHERE ((repeat_v IS NULL OR repeat_v = '') OR (repeat_h IS NULL OR repeat_h = '')) AND product_url IS NOT NULL AND shopify_product_id IS NOT NULL)
-FROM versace_catalog;
-```
-
-### Step 2: For each vendor with remaining > 10, create a new scraper task
-Write a new task file to `/root/DW-Agents/yolo-agent/tasks/` for each vendor that still has missing repeats. Use the SAME format as tasks 21-23 but increment the round number to r3.
-
-Only create tasks for vendors where remaining > 10 (skip if nearly done).
-
-### Step 3: Re-create this requeue task
-If ANY vendor still has remaining > 0, copy this task pattern back into the queue with incremented task numbers and round number r3.
-
-If ALL vendors have 0 remaining, log "ALL REPEATS COMPLETE" and do NOT requeue.
-
-### Step 4: Also create the push + texture tasks for the new round
-- Create a push task (same as task 24) to push newly found repeats to Shopify
-- Create a texture task (same as task 25) to zero-out textures via Gemini
-
-### Step 5: Report
-Log a summary:
-```
-REPEAT SCRAPER ROUND 2 COMPLETE
-- Thibaut: X remaining (was 2071)
-- Kravet: 0 (done round 1)
-- Schumacher: X remaining (was 79)
-- Brewster: X remaining (was 265)
-- York: X remaining (was 289)
-- Versace: 0 (done round 1)
-Next round queued: [yes/no]
-```
-
-## Important
-- This creates an autonomous loop: scrape -> push -> texture -> check -> requeue -> scrape...
-- The loop stops naturally when all repeats are found or confirmed as textures
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/27_enrich-jeffrey-stevens-width.md b/tasks/done/27_enrich-jeffrey-stevens-width.md
deleted file mode 100644
index 2f82e74..0000000
--- a/tasks/done/27_enrich-jeffrey-stevens-width.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Jeffrey Stevens Width Enrichment (3,165 missing widths — PRIVATE LABEL)
-
-Jeffrey Stevens is a PRIVATE LABEL vendor. 3,165 of 3,165 products missing width.
-Also 3 missing images.
-
-## PRIVATE LABEL RULES
-- Jeffrey Stevens is private label — real manufacturer identity must stay hidden
-- Never expose real MFR data in public-facing content
-- DW SKU in public, real MFR SKU in hidden metafields only
-
-## Task
-
-1. Check `jeffrey_stevens_catalog` columns and product_url availability:
- ```sql
- SELECT count(*) filter (where product_url is not null and product_url != '') as has_url,
- count(*) as total
- FROM jeffrey_stevens_catalog;
- ```
-
-2. If product_urls exist, write `/root/DW-Agents/vendor-scrapers/enrich-jeffreystevens-width.js`
-3. If no product_urls, check if there's a Shopify store or other source
-4. Extract: width, material, repeat_v, repeat_h, roll_length, match_type
-5. Also fix the 3 missing images if product pages have image URLs
-
-### Connection
-```
-postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-```
-
-### Template
-Follow `/root/DW-Agents/vendor-scrapers/enrich-thibaut-width.js` pattern.
-
-### Rules
-- Rate limit: 1 req/sec
-- Only update NULL/empty fields
-- Batch: 300 max first run
-- This is private label — be aware but doesn't affect spec scraping
-- Print summary at end
diff --git a/tasks/done/27_scrape-match-type-thibaut-r3.md b/tasks/done/27_scrape-match-type-thibaut-r3.md
deleted file mode 100644
index 64769b8..0000000
--- a/tasks/done/27_scrape-match-type-thibaut-r3.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Scrape Match Type for Remaining Thibaut Products (446 missing repeat_h)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- Table: `thibaut_catalog`
-- Round 3: 446 products still missing repeat_h — ALL have repeat_v filled, ALL have NULL match_type
-- These were scraped in R1 but match_type wasn't found — try again with improved parsing
-- The existing scraper is at `/root/DW-Agents/apex-agent/scrapers/scrape-repeats.js`
-
-## Task
-Reuse or update the scraper at `/root/DW-Agents/apex-agent/scrapers/scrape-repeats.js`:
-
-1. Query products missing repeat_h with NULL match_type:
-```sql
-SELECT id, name, product_url FROM thibaut_catalog
-WHERE (repeat_h IS NULL OR repeat_h = '')
- AND (match_type IS NULL OR match_type = '')
- AND product_url IS NOT NULL AND product_url != ''
- AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
-ORDER BY id DESC
-LIMIT 500;
-```
-
-2. For each product_url, fetch the page and look for match info:
- - Search for: "match", "Match Type", "Pattern Match", "Straight", "Drop", "Half Drop", "Random", "Free Match"
- - Also look in JSON-LD data, structured data, or product specs sections
- - Thibaut Hydrogen pages may have match info in `__INITIAL_STATE__` or product JSON
- - Common patterns in HTML: `"match":"Straight"`, `match_type`, `pattern_match`
-
-3. If match_type found, derive repeat_h:
- - Straight/Random/Free → repeat_h = '0'
- - Half Drop → repeat_h = numeric_part_of_repeat_v / 2
- - Drop → repeat_h = numeric_part_of_repeat_v / 2
- - Quarter Drop → repeat_h = numeric_part_of_repeat_v / 4
-
-4. Update DB:
-```sql
-UPDATE thibaut_catalog SET match_type = $1, repeat_h = $2, updated_at = NOW() WHERE id = $3;
-```
-
-5. For products where NO match info found on page, set match_type = 'Unknown' (do NOT set repeat_h yet — texture task will handle)
-
-6. Report: found match for X, still unknown for Y
-
-## Important
-- Rate limit to 1 req/s
-- Save scraper as `/root/DW-Agents/apex-agent/scrapers/scrape-match-type-thibaut-r3.js`
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/28_push-repeat-h-to-shopify-r3.md b/tasks/done/28_push-repeat-h-to-shopify-r3.md
deleted file mode 100644
index 9fe6ed0..0000000
--- a/tasks/done/28_push-repeat-h-to-shopify-r3.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Push 2,233+ Newly Derived repeat_h Values to Shopify
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- R2 requeue derived repeat_h from match_type for 2,233 products across all vendors
-- These new repeat_h values need to be pushed to Shopify metafields
-- Vendors completed: Schumacher (79), York (289), Kravet (0), Versace (0)
-- Vendors with new data: Thibaut (1,625), Brewster (251), York (289), Schumacher (79)
-
-## Task
-For each vendor catalog, push repeat_h (and match_type if available) to Shopify metafields via Quinn queue:
-
-1. Find products where repeat_h was recently derived (updated today):
-```sql
-SELECT ct.shopify_product_id, ct.repeat_h, ct.match_type
-FROM {catalog_table} ct
-WHERE ct.repeat_h IS NOT NULL AND ct.repeat_h != ''
- AND ct.shopify_product_id IS NOT NULL AND ct.shopify_product_id != ''
- AND ct.updated_at > NOW() - interval '1 day';
-```
-
-2. Queue metafield pushes through Quinn:
-```sql
-INSERT INTO shopify_api_queue (method, endpoint, payload, token_alias, priority, source_agent)
-VALUES ('POST', '/graphql.json', $payload, 'product', 3, 'repeat-h-push-r3');
-```
-
-Where payload is a `metafieldsSet` mutation:
-- `specs.repeat_h` = horizontal repeat value
-- `specs.match_type` = match type (Straight, Drop, Half Drop, etc.) if available
-
-3. Run for tables: `thibaut_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_catalog`
- (Skip kravet and versace — already at 0 remaining)
-
-4. Check shopify_api_queue for existing pending entries first — do NOT duplicate
-
-5. Report: how many repeat_h values queued per vendor
-
-## Important
-- Use priority 3 (lower than Apex's priority 2 fixes)
-- Batch into `metafieldsSet` calls — one per product
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/29_texture-classify-remaining-r3.md b/tasks/done/29_texture-classify-remaining-r3.md
deleted file mode 100644
index 670be04..0000000
--- a/tasks/done/29_texture-classify-remaining-r3.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Classify Remaining Unknown Products as Texture or Pattern (446 thibaut + 13 brewster)
-
-## Context
-- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
-- After R3 match_type scraping, some products will STILL have NULL match_type
-- These need Gemini Vision classification: TEXTURE → repeat_h = '0', PATTERN → leave for manual review
-- Also handles 13 brewster products with NULL match_type
-
-## Task
-
-### Step 1: Quick name-based classification
-```sql
--- Products missing repeat_h that are likely textures based on name
-UPDATE {catalog_table}
-SET repeat_h = '0', match_type = COALESCE(match_type, 'Texture'), updated_at = NOW()
-WHERE (repeat_h IS NULL OR repeat_h = '')
- AND shopify_product_id IS NOT NULL
- AND (
- LOWER(name) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave|burlap|faux |jute|suede|cork|hemp|raffia|sand|stone|concrete|plaster|canvas)'
- OR LOWER(COALESCE(description, '')) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave)'
- );
-```
-
-Run for: `thibaut_catalog`, `brewster_catalog`
-
-### Step 2: Gemini Vision for remaining unknowns
-For products that still have no repeat_h AND didn't match the name pattern:
-
-1. Query remaining:
-```sql
-SELECT id, name, image_url FROM {catalog_table}
-WHERE (repeat_h IS NULL OR repeat_h = '')
- AND shopify_product_id IS NOT NULL
- AND image_url IS NOT NULL AND image_url != ''
-ORDER BY id;
-```
-
-2. Send image to Gemini 2.0 Flash:
- - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- - Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
-
-3. If TEXTURE → `UPDATE SET repeat_h = '0', match_type = 'Texture'`
-4. If PATTERN → `UPDATE SET match_type = 'Unknown Pattern'` (leave repeat_h NULL for manual)
-
-### Step 3: Final cleanup — assume Straight for remaining
-Any products STILL without repeat_h after Gemini classification:
-```sql
-UPDATE {catalog_table}
-SET repeat_h = '0', match_type = COALESCE(match_type, 'Assumed Straight'), updated_at = NOW()
-WHERE (repeat_h IS NULL OR repeat_h = '')
- AND shopify_product_id IS NOT NULL;
-```
-
-This ensures 100% completion. Products tagged 'Unknown Pattern' or 'Assumed Straight' can be manually reviewed later.
-
-## Important
-- Run AFTER task 27 (match_type scraping) completes
-- Gemini rate limit: 10 req/s
-- Track costs via `require('/root/DW-Agents/shared/gemini-cost-tracker.js')`
-- Report: how many by name, how many by Gemini (texture vs pattern), how many assumed
-- Do NOT ask questions — run autonomously
diff --git a/tasks/done/30_glitter-walls-full-monty.md b/tasks/done/30_glitter-walls-full-monty.md
deleted file mode 100644
index a5f02c1..0000000
--- a/tasks/done/30_glitter-walls-full-monty.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Glitter Walls Full Monty (456 products)
-Priority: P1
-Status: IN_PROGRESS
-Started: 2026-04-06
-
-## Task
-Run full monty on all 456 Glitter Walls products:
-- Migrate global.* → custom.* specs
-- AI color analysis (Gemini) → unique designer color names
-- 2-paragraph body rewrite
-- Title: "Pattern - Color | Glitter Walls"
-- Set full_monty_date
-- Image alt tags: "{SKU} {Pattern} {Color} designer-wallcoverings-los-angeles"
-- NO room settings
-
-## Script
-`/root/Projects/Letsbegin/glitter_full_monty.js`
-Log: `/tmp/glitter_monty.log`
diff --git a/tasks/done/31_retro-walls-full-monty.md b/tasks/done/31_retro-walls-full-monty.md
deleted file mode 100644
index 8543c49..0000000
--- a/tasks/done/31_retro-walls-full-monty.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Retro Walls Full Monty (1,203 products)
-Priority: P1
-Status: IN_PROGRESS
-Started: 2026-04-06
-
-## Task
-Run full monty on all 1,203 Retro Walls products:
-- Same pipeline as Glitter Walls
-- Vendor: "Retro Walls"
-
-## Script
-`/root/Projects/Letsbegin/retro_full_monty.js`
-Log: `/tmp/retro_monty.log`
diff --git a/tasks/done/32_command54-shopify-push.md b/tasks/done/32_command54-shopify-push.md
deleted file mode 100644
index 7a9a98b..0000000
--- a/tasks/done/32_command54-shopify-push.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Command54 → Shopify Push (1,242 products)
-Priority: P1
-Status: AUDIT
-Started: 2026-04-06
-
-## Task
-Push 1,242 Command54 products to Shopify as "Phillipe Romano" (PRIVATE LABEL).
-- Collection: "Hollywood Vinyls Vol. 1"
-- NEVER mention "Command54" in any public content
-- DW SKU prefix: DWHW
-- Audit data quality first (image, width, mfr_sku gates)
-
-## Notes
-- Audit report: `/tmp/command54_audit.txt`
-- FULLPRODUCT gate required before push
diff --git a/tasks/done/33_brands-page-sync.md b/tasks/done/33_brands-page-sync.md
deleted file mode 100644
index 624e814..0000000
--- a/tasks/done/33_brands-page-sync.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Brands Page Sync (VCC → Shopify)
-Priority: P2
-Status: IN_PROGRESS
-Started: 2026-04-06
-
-## Task
-Build and run sync from VCC (vendor_registry.show_on_brands_page) to Shopify /pages/brands.
-- Read approved vendors from PostgreSQL
-- Generate HTML with alphabetized brand links + logos
-- Push to Shopify page via content API
-
-## Script
-`/root/Projects/Letsbegin/sync_brands_page.js`
diff --git a/tasks/done/34_spoonflower-push.md b/tasks/done/34_spoonflower-push.md
deleted file mode 100644
index 0e461d8..0000000
--- a/tasks/done/34_spoonflower-push.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Spoonflower → Shopify Push (573 products)
-Priority: P2
-Status: OPEN
-
-## Task
-Push 573 Spoonflower products from PostgreSQL to Shopify.
-- Audit data quality first
-- Full monty after push
diff --git a/tasks/done/35_twil-karin-push.md b/tasks/done/35_twil-karin-push.md
deleted file mode 100644
index b83885a..0000000
--- a/tasks/done/35_twil-karin-push.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# TWIL Karin → Shopify Push (210 products)
-Priority: P2
-Status: OPEN
-
-## Task
-Push 210 TWIL Karin products from PostgreSQL to Shopify as Phillipe Romano (private label).
-- DW SKU prefix: DWPW
-- Audit data quality first
diff --git a/tasks/done/36_nina-campbell-full-monty.md b/tasks/done/36_nina-campbell-full-monty.md
deleted file mode 100644
index 1ece974..0000000
--- a/tasks/done/36_nina-campbell-full-monty.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Nina Campbell Full Monty (759 products)
-Priority: P0
-Status: READY
-Started: 2026-04-07
-
-## CRITICAL RULES
-- **NEVER change existing DW SKUs** — leave DWC-XXXXXXX as-is
-- **MFR SKU from image filename** — extract NCW/NCF code from Shopify image URL filename
-- **Crop from BOTTOM UP** — not top. Only crop if clear repeating pattern exists
-- **DO NOT crop murals, panels, scenics** — use Gemini to check if unsure
-- **Real pattern names from nina_campbell_catalog** (238 entries in PostgreSQL)
-- **AI color if no catalog color** — use designer terms (Ivory, Sage, Cobalt, etc.)
-
-## Full Monty Steps (NO room settings)
-1. Set `custom.manufacturer_sku` from image filename (NCW/NCF code)
-2. Match to nina_campbell_catalog for real pattern_name + color_name
-3. If no catalog match → AI color analysis via Gemini on image
-4. Crop image from BOTTOM to square (width × width) IF clear repeat pattern exists
-5. Upload cropped image, set as position 1, delete old tall image
-6. Title: "{Pattern Name} - {Color} Wallcovering | Nina Campbell"
-7. Body: 2 paragraphs (editorial + AI description)
-8. Metafields: manufacturer_sku, color_name, pattern_name, brand, width, full_monty_date
-9. Image alt tags: "{DW SKU} {Pattern} {Color} designer-wallcoverings-los-angeles"
-10. Migrate global.* → custom.* specs
-
-## Data Sources
-- PostgreSQL: nina_campbell_catalog (238 entries with mfr_sku, pattern_name, color_name)
-- Shopify: 759 active products, vendor "Nina Campbell"
-- Image filenames contain NCW/NCF manufacturer codes
-
-## Script Location
-`/root/Projects/Letsbegin/nina_names_colors.js` (needs update with crop logic)
-
-## Log
-`/tmp/nina_monty.log`
diff --git a/tasks/done/37_revert-dwcy-skus.md b/tasks/done/37_revert-dwcy-skus.md
deleted file mode 100644
index c3f61bc..0000000
--- a/tasks/done/37_revert-dwcy-skus.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# REVERT DWCY SKUs back to DWC (712 products)
-Priority: P0 URGENT
-Status: OPEN
-
-## Task
-712 Nina Campbell products had their SKUs wrongly changed from DWC-XXXXXXX to DWCY-900xxx.
-REVERT ALL back to original DWC SKUs.
-
-## How
-The original SKU can be reconstructed from the MFR code in the image filename:
-- Image: ncw4300-01.jpg → MFR: NCW4300-01 → original SKU was DWC-NCW4300-01
-- For DWC1001xxx format products, check the product's global.js_number_internal or dwc.manufacturer_sku
-
-## CRITICAL
-- NEVER create new SKU series
-- Restore EXACT original SKU format
diff --git a/tasks/done/41_refresh-as-creation-images.md b/tasks/done/41_refresh-as-creation-images.md
deleted file mode 100644
index 4f684f3..0000000
--- a/tasks/done/41_refresh-as-creation-images.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Refresh AS Creation Images
-
-**Context:** 51 AS Creation products in `vendor_catalog` (not on Shopify) have stale `cdn.as-creation.com` URLs. Root site `www.as-creation.com` responds with HTTP 200 but specific media paths 404. On-Shopify AS Creation products (already live) have working URLs, so the CDN is not fully dead — just specific older paths.
-
-**Goal:** Get fresh working image URLs for 51 stale-URL AS Creation products.
-
-## Approach
-
-1. **Target set:**
- ```sql
- SELECT id, mfr_sku, pattern_name, image_url
- FROM vendor_catalog
- WHERE vendor_code='as_creation'
- AND shopify_product_id IS NULL
- AND image_url LIKE '%cdn.as-creation.com%'
- ORDER BY mfr_sku;
- ```
-
-2. **Check existing scrapers:**
- ```bash
- ls /root/DW-Agents/vendor-scrapers/ | grep -i ascreation
- ```
- Existing: `enrich-ascreation-ai-tags.js`, `enrich-ascreation-width.js` (no dedicated image scraper)
-
-3. **Build or run a scraper:**
- - Check if `www.as-creation.com` has a products.json or API endpoint
- - If not, scrape product pages via puppeteer
- - Extract current image URLs
-
-4. **After fresh URLs:** run enrichment
- ```bash
- cd /root/DW-Agents/vendor-scrapers && timeout 900 node enrich-ai-tags.js as_creation --limit 51
- ```
-
-## Report
-
-- URL refresh count
-- Enrichment success count
-- Any non-recoverable products
-
-## Safe to stop if
-
-- Site blocks scraping
-- No clear scrape path exists
diff --git a/tasks/done/43_scrape-surface-stick.md b/tasks/done/43_scrape-surface-stick.md
deleted file mode 100644
index 636bca7..0000000
--- a/tasks/done/43_scrape-surface-stick.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Scrape Surface Stick — Biggest Single Unlock (745 products)
-
-**Context:** 745 Surface Stick products in `vendor_catalog` have NO `image_url` at all. Surface Stick uses native SUR- SKUs and is registered in `vendor_registry` with `catalog_table='surface_stick_catalog'`. The `surface-stick.json` vendor config exists at `/root/DW-Agents/vendor-agent-core/vendors/surface-stick.json`.
-
-**Goal:** Run or build a scraper for Surface Stick to populate image URLs for 745 products — the biggest single re-scrape unlock in the catalog.
-
-## Approach
-
-1. **Target set:**
- ```sql
- SELECT COUNT(*) FROM vendor_catalog
- WHERE vendor_code='surface_stick' AND shopify_product_id IS NULL AND (image_url IS NULL OR image_url='');
- ```
-
-2. **Check vendor config:**
- ```bash
- cat /root/DW-Agents/vendor-agent-core/vendors/surface-stick.json
- ```
- Look for `scraper.phase_scripts` — which script does it call?
-
-3. **Find existing scraper:**
- ```bash
- ls /root/DW-Agents/vendor-scrapers/ | grep -i surface
- ```
-
-4. **Run scraper or build one:**
- - If a scraper exists and works, run it
- - If not, scope: what's Surface Stick's website? Is it a Shopify store (use `/products.json`)? WordPress? Custom?
-
-5. **After scrape:** run full-monty pipeline stages:
- - Phase 2 enrichment (individual product pages for specs)
- - Phase 3 AI enrichment (Gemini)
- - Silas validation
- - VCC approval (if passes gate)
-
-## Report
-
-- Products scraped
-- Images populated
-- Enrichment count
-- Any blockers (site down, scraping blocked, different SKU format, etc.)
-
-## Safe to defer
-
-- Scraper doesn't exist and building one is a day-long task
-- Site blocks automated access
-- Vendor is discontinued
diff --git a/tasks/done/44_scrape-la-walls.md b/tasks/done/44_scrape-la-walls.md
deleted file mode 100644
index d7a2503..0000000
--- a/tasks/done/44_scrape-la-walls.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Scrape LA Walls (728 products)
-
-**Context:** 728 LA Walls products in `vendor_catalog` have NO `image_url`. Vendor config exists at `/root/DW-Agents/vendor-agent-core/vendors/la-walls.json`.
-
-**Goal:** Populate image URLs for 728 LA Walls products.
-
-## Approach
-
-1. **Target set:**
- ```sql
- SELECT COUNT(*) FROM vendor_catalog
- WHERE vendor_code='la_walls' AND shopify_product_id IS NULL AND (image_url IS NULL OR image_url='');
- ```
-
-2. **Check config + scraper:**
- ```bash
- cat /root/DW-Agents/vendor-agent-core/vendors/la-walls.json
- ls /root/DW-Agents/vendor-scrapers/ | grep -iE "la.?walls|lawalls"
- ```
-
-3. **Run or build scraper** — try existing first. If none, check lawalls.com / la-walls.com structure.
-
-4. **Follow with enrichment pipeline (Phases 2-5).**
-
-## Report results and blockers.
diff --git a/tasks/done/45_scrape-paul-montgomery.md b/tasks/done/45_scrape-paul-montgomery.md
deleted file mode 100644
index 7e2dc36..0000000
--- a/tasks/done/45_scrape-paul-montgomery.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Scrape Paul Montgomery (714 products)
-
-**Context:** 714 Paul Montgomery products in `vendor_catalog` have NO `image_url`. Vendor config exists at `/root/DW-Agents/vendor-agent-core/vendors/paul-montgomery.json`.
-
-## Approach
-
-1. Target set:
- ```sql
- SELECT COUNT(*) FROM vendor_catalog
- WHERE vendor_code='paul_montgomery' AND shopify_product_id IS NULL AND (image_url IS NULL OR image_url='');
- ```
-
-2. Check config + scraper:
- ```bash
- cat /root/DW-Agents/vendor-agent-core/vendors/paul-montgomery.json
- ls /root/DW-Agents/vendor-scrapers/ | grep -iE "paul|montgomery"
- ```
-
-3. Run or build scraper. Follow with enrichment pipeline.
-
-## Report results and blockers.
diff --git a/tasks/done/46_scrape-remaining-missing-image.md b/tasks/done/46_scrape-remaining-missing-image.md
deleted file mode 100644
index b655c86..0000000
--- a/tasks/done/46_scrape-remaining-missing-image.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Scrape Remaining MISSING-Image Vendors (~1,000 products)
-
-**Context:** Several smaller-count vendors have products in `vendor_catalog` with no `image_url`. Tackle them in one sweep since each is a small job.
-
-## Targets
-
-| Vendor | Products | Config |
-|--------|---------|--------|
-| York (Jeffrey Stevens) | 298 | via yorkwall.com trade portal |
-| Novasuede | 139 | novasuede.com |
-| Leed Walls | 129 | |
-| Wolf Gordon | 24 | wolf-gordon.com |
-| Versa DS | 21 | versagroup.com |
-| Command54 | 10 | private label — route via Phillipe Romano scraper |
-| BN Walls | 10 | bnwallcoverings.com |
-| Marburg | 6 | marburg.com |
-
-Skip these (investigated separately):
-- Fentucci 232 (misbucketed — needs reassignment, not scrape)
-- Glitter Walls 440 (orphan SKUs from dead source)
-- Primo Leathers 134 (leather, excluded)
-- Ultrasuede 111 (leather-ish, excluded)
-
-## Approach
-
-For each vendor:
-1. Query current gap: `SELECT COUNT(*) FROM vendor_catalog WHERE vendor_code=$1 AND shopify_product_id IS NULL AND (image_url IS NULL OR image_url='')`
-2. Check for existing scraper: `ls /root/DW-Agents/vendor-scrapers/ | grep -i {vendor}`
-3. Run scraper if exists
-4. Sync refreshed URLs into `vendor_catalog`
-5. Run `enrich-ai-tags.js {vendor_code}`
-6. Report
-
-## Query when done
-
-```sql
-SELECT * FROM stale_image_urls WHERE status='MISSING' AND resolution='needs-rescrape';
-```
-
-Update the `stale_image_urls` tracking table as each vendor is resolved.
diff --git a/tasks/done/47_approve-newly-enriched.md b/tasks/done/47_approve-newly-enriched.md
deleted file mode 100644
index d64bb16..0000000
--- a/tasks/done/47_approve-newly-enriched.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# VCC-Approve Newly-Enriched Products
-
-**Context:** After any enrichment campaign completes, newly-enriched products that pass the FULLPRODUCT gate need to be marked `phase5_approved=true` in `enrichment_tracking` so they appear in Launch Pad for Steve's manual review.
-
-**Run this AFTER tasks 40-46 finish.**
-
-## Approach
-
-Run the same bulk approval pattern used earlier in the 2026-04-11 session. Exclude banned vendors, fabrics, trim, leather.
-
-```sql
-BEGIN;
-
-CREATE TEMP TABLE to_approve AS
-SELECT DISTINCT vc.mfr_sku, vc.vendor_code, vr.catalog_table
-FROM vendor_catalog vc
-JOIN vendor_registry vr ON vr.vendor_code = vc.vendor_code
-LEFT JOIN enrichment_tracking et ON et.mfr_sku = vc.mfr_sku AND et.vendor_code = vc.vendor_code
-WHERE vc.shopify_product_id IS NULL
- AND vc.vendor_code NOT IN ('cowtan_tout','cowtan_and_tout','cowtan','colefax_fowler','connie_our','connie_competitor','jf_fabrics','holly_hunt_leather','bespoke','scalamandre_fabrics','phillip_jeffries')
- AND (vc.product_type IS NULL OR vc.product_type NOT ILIKE '%fabric%')
- AND (vc.product_type IS NULL OR vc.product_type NOT ILIKE '%trim%')
- AND (vc.product_type IS NULL OR vc.product_type NOT ILIKE '%leather%')
- AND vc.image_url IS NOT NULL AND vc.image_url != ''
- AND vc.mfr_sku IS NOT NULL AND vc.mfr_sku != ''
- AND vc.pattern_name IS NOT NULL AND vc.pattern_name != '' AND vc.pattern_name != '-'
- AND vc.color_name IS NOT NULL AND vc.color_name != '' AND vc.color_name != '-'
- AND vc.ai_colors IS NOT NULL
- AND vc.ai_styles IS NOT NULL AND jsonb_array_length(vc.ai_styles) > 0
- AND vc.fire_rating IS NOT NULL AND vc.fire_rating != ''
- AND (vc.pattern_repeat IS NOT NULL AND vc.pattern_repeat != '' OR (vc.ai_patterns::text ILIKE '%solid%' OR vc.ai_patterns::text ILIKE '%texture%'))
- AND vc.pattern_name !~ '[\u0900-\u097F]'
- AND vc.color_name !~ '[\u0900-\u097F]'
- AND (et.phase5_approved IS NOT TRUE OR et.phase5_approved IS NULL);
-
-SELECT COUNT(*) FROM to_approve;
-
-UPDATE enrichment_tracking et
-SET phase5_approved = true, phase5_vcc_at = COALESCE(phase5_vcc_at, NOW()), updated_at = NOW()
-FROM to_approve ta
-WHERE et.mfr_sku = ta.mfr_sku AND et.vendor_code = ta.vendor_code;
-
-INSERT INTO enrichment_tracking (mfr_sku, vendor_code, catalog_table, phase5_approved, phase5_vcc_at, created_at, updated_at)
-SELECT ta.mfr_sku, ta.vendor_code, ta.catalog_table, true, NOW(), NOW(), NOW()
-FROM to_approve ta
-LEFT JOIN enrichment_tracking et ON et.mfr_sku = ta.mfr_sku AND et.vendor_code = ta.vendor_code
-WHERE et.mfr_sku IS NULL
-ON CONFLICT DO NOTHING;
-
-COMMIT;
-```
-
-Report how many newly approved.
diff --git a/tasks/done/50_command54-rehome-phase-d-batch-25.md b/tasks/done/50_command54-rehome-phase-d-batch-25.md
deleted file mode 100644
index 5d939ff..0000000
--- a/tasks/done/50_command54-rehome-phase-d-batch-25.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# Command54 / Phillipe Romano → Hollywood Wallcoverings Rehome — Phase D (25-product batch)
-
-## Status as of handoff
-- Phase A (discovery) — DONE. Artifacts: `/tmp/cmd54_rehome_diff.json` (41 MB, 8,089 products with current/proposed per-product diff)
-- Phase B (build test script) — DONE. Script: `/root/DW-Agents/scripts/command54-rehome-test.js`
-- Phase C (single-product live test) — DONE. Test product **7791124381747** (Crossroads Reef - Sand Phthalate Free Vinyl) successfully rehomed. Quinn jobs 800871/800872/800873 all completed HTTP 200, zero userErrors. Verified on live Shopify. Before/after: `/tmp/rehome_test_before_7791124381747.json` / `/tmp/rehome_test_after_7791124381747.json`. Audit log table: `brand_backfill_audit_log` + `rehome_audit_log`.
-- **Phase D (this task)** — Run the rehome on a 25-product batch, verify each one end-to-end, report. STOP before Phase E (full 8,089 push).
-
-## Your mandate
-Execute Phase D only. Do NOT proceed to Phase E without a separate task file.
-
-**Hard cap: maximum 25 products touched in this task.** If anything goes wrong, STOP and report; do not retry past the cap.
-
-## Decisions already made by Steve (do not re-ask)
-- **Scope**: All 8,089 products with Shopify `vendor="Phillipe Romano"` (Q8b). Phase D picks 25 from this pool as a representative batch.
-- **Sub-brand tags**: Rename (Q9a) — e.g., `Phillipe Romano Naturals` → `Hollywood Wallcoverings Naturals`.
-- **Product Type**: Normalize to `Wallcovering` (Q10a).
-- **Phillip Jeffries**: Skip for ALL bulk updates (in `feedback_skip_pj.md`). Not relevant here since we're only touching PR products, but apply the pattern to any future scripts you write.
-- **HDDW/DWHQ SKU**: Variant SKUs get renamed `DWPR-XXXXXX` → `DWHQ-XXXXXX` (preserve number). `custom.series` metafield = `"DWHQ"`.
-- **Target brand**: `custom.brand = "Hollywood Wallcoverings"`.
-- **Target collection_name**: `custom.collection_name = "Hollywood Wallcoverings Vol. 2"`.
-
-## Unresolved decisions — USE THESE DEFAULTS for Phase D only
-1. **D4 (source book preservation)**: Before overwriting `custom.collection_name`, save the OLD value into `custom.source_book` (new metafield, `single_line_text_field`). This is reversible and preserves the upstream book name (Virtual, Galleria, Tekloom, Aire SI, etc.). If the existing `custom.collection_name` is null or empty, skip the source_book write.
-2. **D3 (descriptor tags)**: Do NOT strip the `Fabric` tag even though productType is changing to `Wallcovering`. Tags are orthogonal. Only remove tags matching `/phillipe romano/i`.
-3. **D2 (priority)**: Enqueue at priority 2. If you see >60s of Quinn starvation (jobs pending with no processing), bump priority to 0 via direct SQL: `UPDATE shopify_api_queue SET priority = 0 WHERE source_agent='command54-rehome-batch' AND status='pending';`
-4. **D1 (collection objects)**: Do NOT create new "Hollywood Wallcoverings Vol. 1/2" Shopify Collection objects. The existing `Hollywood` smart collection (id `gid://shopify/Collection/164060463155`) already auto-absorbs rehomed products via its vendor/title/tag rules. The `custom.collection_name` metafield provides the Vol. 2 semantic label. Phase F (separate future task) handles collection renames if Steve decides to.
-
-## Candidate selection (25 products)
-
-Pick 25 products from `/tmp/cmd54_rehome_diff.json` meeting these criteria (in priority order):
-
-- **Exclude** the 27 products already tested in Phase C (test product 7791124381747 — already rehomed)
-- **Exclude** the 47 products already partially branded "Hollywood Wallcoverings" in `current.metafields.brand` — those need separate handling
-- **Include variety**: aim for 10 Fabric type (tests productType normalization), 10 Wallcovering type (no type change needed), 5 Natural Wallcovering type
-- **Include**: both 1-variant (sample-only) and 2-variant (sample+priced) products
-- **Include**: products with different upstream sources (`current.metafields.pl_real_vendor` = Pointe Fabrics, Pointe International, Astek, etc.) to test multi-source coverage
-- **Prefer**: products whose variant SKUs start with `DWPR-` (tests the SKU rename path). If fewer than 25 meet this, include some with non-DW prefixes (test the skip-rename path)
-- **Avoid**: products with >50 tags (harder to diff), and anything with `Phillip Jeffries` anywhere in title/tags/vendor
-
-Dump the selected 25 to `/tmp/phase_d_batch_25.json` before starting any writes.
-
-## Execution steps
-
-1. **Read** `/root/DW-Agents/scripts/command54-rehome-test.js` — it has the reference patterns for: fetching live state, computing the rehome diff, building the 3 GraphQL mutations, enqueueing to Quinn (`shopify_api_queue`), waiting for completion, and verifying via re-fetch. Reuse those functions.
-
-2. **Write** `/root/DW-Agents/scripts/command54-rehome-batch.js` by extending the test script's logic with:
- - `--batch-size=25` (hard-capped at 25 for this run)
- - `--source-file=/tmp/phase_d_batch_25.json` (list of product IDs)
- - `--dry-run` (default) / `--push`
- - Per-product: fetch live state, compute rehome, build 3 mutations + 1 extra for source_book preservation (D4), enqueue, log to `rehome_audit_log`
- - Batched wait: enqueue all ~100 Quinn jobs first, then poll until all complete (not sequential per-product)
- - Post-push verification: re-fetch each of the 25 products via a single `nodes()` GraphQL call (batch 25 IDs per call — Rule 23), compare to expected state, count mismatches
- - Output: `/tmp/phase_d_results.json` with per-product `{before, after, status, mismatches}`
-
-3. **Dry-run first**: `node /root/DW-Agents/scripts/command54-rehome-batch.js --source-file=/tmp/phase_d_batch_25.json`. Verify payload shapes look correct. Inspect the generated GraphQL mutations manually — spot-check 3 products' worth.
-
-4. **Push**: `node /root/DW-Agents/scripts/command54-rehome-batch.js --source-file=/tmp/phase_d_batch_25.json --push`. Expected: 25 × 4 Quinn jobs = 100 jobs enqueued. At 2 req/s that's ~50s processing + queue wait. Budget 5 minutes for queue drain.
-
-5. **Verify on live Shopify** — for each of the 25 products, confirm:
- - `vendor == "Hollywood Wallcoverings"`
- - `title` no longer contains "Phillipe Romano" (case-insensitive)
- - `productType == "Wallcovering"`
- - `custom.brand.value == "Hollywood Wallcoverings"`
- - `custom.collection_name.value == "Hollywood Wallcoverings Vol. 2"`
- - `custom.series.value == "DWHQ"`
- - `custom.source_book.value` matches the OLD `custom.collection_name` value (for products that had one)
- - Any DWPR-prefixed variant SKU is now DWHQ- with preserved suffix
- - No PR-tagged smart collections in the `collections` list
-
-6. **Report** to stdout:
- - Number processed, number successful, number failed
- - Any GraphQL userErrors from Quinn response bodies (`SELECT response_body FROM shopify_api_queue WHERE source_agent='command54-rehome-batch-d'`)
- - First 3 sample product diffs
- - Specific blockers or anomalies (rate limit hits, missing metafields, etc.)
- - The literal line: `PHASE D STATUS: [GREEN|YELLOW|RED] — [one-sentence summary]`
-
-7. **Do NOT auto-proceed to Phase E.** After reporting, exit. Steve will read the report and decide.
-
-## Hard rules (from CLAUDE.md + memory)
-- **Rule 23**: Batch 25 mutations per GraphQL call. Use aliases (`p0:`, `p1:`, ...) for productUpdate / productVariantsBulkUpdate. Use `metafieldsSet` with 25-input array.
-- **Rule 14**: NEVER write "Command54", "CMD54-", or any command54 real pattern name to a public-visible field. Use "Hollywood Wallcoverings" as the brand, "DWHQ" as the series tag.
-- **Rule 27**: Metafield namespace must be `custom.*` not `specs.*`. Brand key = `custom.brand`. Collection key = `custom.collection_name`. Series = `custom.series`. New source book = `custom.source_book`.
-- **Use Quinn queue exclusively** — no direct Shopify REST or GraphQL writes. All writes go through `INSERT INTO shopify_api_queue ... RETURNING id;`. Reads are fine directly.
-- **Three-state safety** (from `hollywood-drift-sync.js` lesson): if a product can't be verified pre or post, SKIP it — never mutate unverified state.
-- **Never change existing DW SKUs on other vendors** — this rehome specifically renames DWPR- → DWHQ- under explicit Steve approval. Do NOT touch other prefixes.
-
-## If something goes wrong
-- **Quinn rejects jobs (userErrors)**: Report the first 5 errors verbatim, stop, don't retry until diagnosed.
-- **Shopify rate limit**: Quinn handles this — don't override. If Quinn itself is throttled, report and stop.
-- **productVariantsBulkUpdate fails because inventoryItem is missing**: Older API versions use `variants: [{id, sku}]`, newer use `variants: [{id, inventoryItem: {sku}}]`. We're on 2024-10 — use the inventoryItem form. If the form is wrong, test one variant rename manually via curl first to find the right shape.
-- **Priority starvation**: Bump source_agent='command54-rehome-batch-d' jobs to priority=0 via direct SQL. This is authorized.
-- **Anything else unexpected**: STOP. Append findings to the report with `PHASE D STATUS: RED`.
-
-## Do NOT do
-- Do not touch more than 25 products in this task.
-- Do not proceed to Phase E (full 8,089) or Phase F (vendor_registry / CLAUDE.md updates).
-- Do not update MEMORY.md, CLAUDE.md, or vendor_registry.
-- Do not commit to git (The Scribe auto-commits on its 30-min cycle).
-- Do not post to Slack / blog / Twitter / etc.
-- Do not pause schumacher-bulk or other agents.
-
-## Source material
-- Full discovery: `/tmp/cmd54_rehome_diff.json` (8,089 product entries)
-- Phase C test script (reference): `/root/DW-Agents/scripts/command54-rehome-test.js`
-- Phase C before/after (reference state): `/tmp/rehome_test_before_7791124381747.json` and `/tmp/rehome_test_after_7791124381747.json`
-- Audit tables: `brand_backfill_audit_log`, `rehome_audit_log`
-- Shopify tokens: see `/root/DW-Agents/CLAUDE.md` (product token)
-- Quinn queue table: `shopify_api_queue`
-
-## Completion criterion
-Task is considered complete when:
-1. 25 products have been processed through Quinn
-2. Post-push verification has been run against live Shopify
-3. A clear `PHASE D STATUS: GREEN|YELLOW|RED` line has been printed
-4. `/tmp/phase_d_results.json` exists with full per-product results
-
-Exit 0 = all 25 fully rehomed and verified. Exit non-zero = something's off, stay in queue for retry after human review.
diff --git a/tasks/done/50_newmor-verify.md b/tasks/done/50_newmor-verify.md
deleted file mode 100644
index 4208a67..0000000
--- a/tasks/done/50_newmor-verify.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Newmor 98-product image fix — Phase 2a verification
-
-**Type**: READ-ONLY (no DB writes, no Shopify writes, no Quinn enqueues)
-**Max turns**: 30
-**Output**: `/tmp/yolo_newmor_verify_result.json`
-
-## Context
-
-A previous session dispatched a Newmor image-fix batch via Quinn queue. The
-goal was to replace the "NEWMOR Wallcovering Direct" logo covers on 98
-wallcovering products with real newmor.com swatch images, by looking up each
-product's `mfr_sku` in `newmor_catalog`. The push was enqueued as 4 Quinn
-jobs under `source_agent = 'newmor-image-fix-phase2a'`, priority 1. The 4
-jobs contain 25 + 25 + 25 + 23 aliased GraphQL mutations (productCreateMedia
-+ productDeleteMedia).
-
-Your job is to verify the push landed cleanly. Nothing else.
-
-## Required steps
-
-1. **Wait for Quinn drain** (up to 20 minutes).
- Poll every 60 seconds:
- ```
- PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -t -A -c \
- "SELECT status, COUNT(*) FROM shopify_api_queue \
- WHERE source_agent='newmor-image-fix-phase2a' GROUP BY status;"
- ```
- Stop polling when there are 0 rows in status `pending` or `processing`.
- If 20 minutes pass and jobs are still pending, write the blocked result
- (see "Blocked outcome" below) and exit 0.
-
-2. **Assert success of all 4 jobs**:
- ```
- PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c \
- "SELECT id, status, response_status, LEFT(COALESCE(error,''),120) AS err \
- FROM shopify_api_queue WHERE source_agent='newmor-image-fix-phase2a' ORDER BY id;"
- ```
- All 4 rows must have `status='done'` and `response_status=200`.
- If any row fails, record its id, error, and response_body snippet.
-
-3. **Spot-check 5 products via live Shopify GraphQL**.
- Pick 5 random `shopify_id` values from `/tmp/newmor_119_plan.json`'s `targets[]`.
- For each, fetch current media:
- ```
- curl -sS -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json" \
- -H "Content-Type: application/json" \
- -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
- -d '{"query":"{ product(id: \"<gid>\") { title media(first:20) { edges { node { id alt ... on MediaImage { image { url } } } } } } }"}'
- ```
- For each product assert:
- - At least 1 media present.
- - At least 1 media URL contains `newmor.com/wp-content/uploads/` AND `-swatch-` (e.g. `arona-swatch-AR104-1.jpg`).
- - The Shopify logo URL (`NEWMOR_*.jpg`) is NO LONGER present among the media.
-
-## Output format
-
-Write `/tmp/yolo_newmor_verify_result.json`:
-```json
-{
- "status": "pass" | "partial" | "fail",
- "quinn_drain": {
- "all_done": true,
- "job_ids": [801162, 801163, 801164, 801165],
- "responses": [{"id": 801162, "response_status": 200, "error": null}, ...]
- },
- "spot_check": {
- "sampled": 5,
- "new_swatch_present": 5,
- "old_logo_removed": 5,
- "details": [
- {"shopify_id": "gid://shopify/Product/...", "mfr_sku": "AR-104", "new_swatch_found": true, "old_logo_removed": true, "current_media_urls": [...]}
- ]
- },
- "verdict": "one-line summary"
-}
-```
-
-Print the JSON body at the end of your output. That's it.
-
-## Stop conditions
-
-- **All pass** → status=`pass`, exit 0.
-- **Quinn drain timeout** → status=`partial`, record pending count, exit 0.
-- **Any Quinn job has response_status != 200** → status=`fail`, include error details, exit 0.
-- **Spot-check fails on 1+ products** → status=`partial`, list which, exit 0.
-
-## Do NOT
-
-- Do NOT write to the database, Shopify, or Quinn queue.
-- Do NOT try to fix failures. Just report.
-- Do NOT expand scope to the 21 BST- skipped products or the 178 fuzzy-match cohort.
-- Do NOT delete media, reorder media, or update any product.
diff --git a/tasks/done/51_donghia-alt-source.md b/tasks/done/51_donghia-alt-source.md
deleted file mode 100644
index 97ba689..0000000
--- a/tasks/done/51_donghia-alt-source.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# Donghia 32-product — alternate image source investigation
-
-**Type**: READ-ONLY (no DB writes, no Shopify writes, no Quinn enqueues, no scraping of vendor sites beyond minimal HEAD/GET probes)
-**Max turns**: 30
-**Output**: `/tmp/yolo_donghia_alt_source_result.json`
-
-## Context
-
-32 Donghia products on our Shopify store currently show Kravet's
-`placeholder-thumb-kk` image as their cover instead of a real product photo.
-Donghia is a Kravet brand, and our existing donghia scraper
-(`/root/DW-Agents/vendor-scrapers/scrape-donghia.js`) reads from Kravet's
-Algolia index. BUT: the current `donghia_catalog` also stores the placeholder
-image for all 32 SKUs — meaning Kravet's Algolia itself returns placeholders
-for these specific products. Re-running the scraper will NOT fix this cohort.
-Also, `donghia.com` times out when accessed from this server (likely
-geoblocking).
-
-Your job is to find an alternate image source for these 32 products.
-Report what you found. Do NOT fix anything.
-
-## Required steps
-
-1. **Load the 32 SKUs**:
- ```
- PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -t -A -F'|' -c \
- "SELECT shopify_id, mfr_sku, title FROM shopify_products \
- WHERE vendor ILIKE '%donghia%' AND status='ACTIVE' \
- AND image_url ILIKE '%placeholder%' ORDER BY mfr_sku;"
- ```
-
-2. **Source A: kravet.com direct product page**.
- For each SKU, try `https://www.kravet.com/{normalized-sku}` and
- `https://www.kravet.com/products/{normalized-sku}` with a realistic
- User-Agent, timeout 10s. If any returns HTTP 200 and the HTML contains
- an `img` tag with a non-placeholder CDN URL, record it. Do NOT pull
- every image — just the primary/cover.
-
-3. **Source B: Wayback Machine (web.archive.org)** for donghia.com.
- Try `https://archive.org/wayback/available?url=donghia.com/products/{slug}`
- where slug is derived from the product title. If there's a snapshot
- timestamp, fetch the archived HTML and look for the product cover image.
- Record the archive URL and the image URL it contains.
-
-4. **Source C: other DB tables**.
- Check whether any of these tables have a row matching the 32 mfr_sku values
- with a non-placeholder image:
- - `kravet_catalog`
- - `master_fmpro`
- - `vendor_catalog` (filter vendor_name ILIKE '%donghia%')
- - `fmpro_master`
- For any match, record the source table and the image URL.
-
-5. **Summarize** which of the 32 SKUs now have at least one viable alternate
- image source and which still have nothing.
-
-## Output format
-
-Write `/tmp/yolo_donghia_alt_source_result.json`:
-```json
-{
- "total_skus": 32,
- "sources_tried": ["kravet_direct", "wayback", "db_tables"],
- "per_sku": [
- {
- "mfr_sku": "DG-09705.008",
- "shopify_id": "gid://shopify/Product/...",
- "title": "Linen Texture II - Natural...",
- "kravet_direct": {"status": 200, "image_url": "https://...", "notes": "..."} | null,
- "wayback": {"snapshot_ts": "20231014...", "image_url": "https://..."} | null,
- "db_tables": {"source": "vendor_catalog", "image_url": "https://..."} | null,
- "best_candidate": "kravet_direct" | "wayback" | "db_tables" | "none"
- }
- ],
- "summary": {
- "with_alternate_source": 0,
- "without_alternate_source": 0,
- "recommended_next_step": "one-line human-readable recommendation"
- }
-}
-```
-
-## Stop conditions
-
-- **Found alternates for ≥ 80%** → exit 0, recommend "re-scrape script with new sources".
-- **Found alternates for < 80%** → exit 0, recommend "this cohort needs manual curation or escalation".
-- **All three sources blocked** → exit 0, recommend "escalate — no viable image source exists".
-
-## Do NOT
-
-- Do NOT scrape kravet.com aggressively — limit to 1 request per SKU, sleep 500ms between.
-- Do NOT modify shopify_products, donghia_catalog, or any other table.
-- Do NOT enqueue Quinn jobs.
-- Do NOT attempt the fix. Scope ends at "best source identified per SKU".
-- Do NOT spend more than ~5 minutes on Wayback for any single SKU.
diff --git a/tasks/done/52_wm-store-code.md b/tasks/done/52_wm-store-code.md
deleted file mode 100644
index a46b6bd..0000000
--- a/tasks/done/52_wm-store-code.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# William Morris — Sanderson Magento GraphQL store code discovery
-
-**Type**: READ-ONLY (no DB writes, no Shopify writes, no Quinn enqueues)
-**Max turns**: 25
-**Output**: `/tmp/yolo_wm_store_code_result.json`
-
-## Context
-
-We want to re-scrape ~175 William Morris wallcovering products whose Shopify
-cover images are "Morris & Co" logo placeholders instead of real product
-photos. William Morris is a Sanderson Design Group brand. Our existing
-`rescrape-harlequin-api.js` script queries the Sanderson Magento GraphQL
-at `https://www.mage.sandersondesigngroup.com/graphql` with the header
-`Store: harlequin_us` and successfully pulls Harlequin products.
-
-A previous session tested the same endpoint with 4 William Morris store
-header candidates (`william_morris_us`, `morris_us`, `morrisandco_us`,
-`mandc_us`) and all 4 returned a maintenance HTML page — meaning either the
-WM store is genuinely offline OR the correct store code is something else.
-
-Your job is to find the correct WM store header, if it exists. Report what
-you found. Do NOT scrape.
-
-## Required steps
-
-1. **Read the working Harlequin pattern** from
- `/root/DW-Agents/vendor-scrapers/rescrape-harlequin-api.js`.
- Extract: exact GraphQL URL, exact Store header value, exact query shape
- used for schema introspection AND for product fetch. Note any other
- headers or query params used.
-
-2. **Sanity-check harlequin_us still works** with the same query shape used
- below, so we have a known-good baseline:
- ```
- curl -sS -X POST "https://www.mage.sandersondesigngroup.com/graphql" \
- -H "Content-Type: application/json" \
- -H "Store: harlequin_us" \
- -d '{"query":"{ products(filter:{sku:{match:\"HAW\"}} pageSize:1) { total_count items { sku name } } }"}'
- ```
- Record the response (JSON or HTML).
-
-3. **Probe candidate WM store header values**. Try each with the same query
- shape above but substituting a pattern filter that matches likely WM SKUs
- (e.g., `"DWMO"`, `"Morris"`, `"WM"`, or just `""` for all).
- Candidates to try (one request each, 500ms apart):
- - `william_morris_us`
- - `williammorris_us`
- - `morris_us`
- - `morrisco_us`
- - `morris_and_co_us`
- - `morrisandco_us`
- - `morris_co_us`
- - `m_and_co_us`
- - `mandc_us`
- - `mc_us`
- - `williamandmorris_us`
-
- For each, record: HTTP status, content-type, first 200 chars of body, and
- whether it returned JSON (`{"data":...}`) or HTML (`<!DOCTYPE html>...`).
-
-4. **If a candidate returns JSON with products**, try one more query to fetch
- 5 products with image data to confirm the store is actually populated:
- ```
- '{"query":"{ products(pageSize:5) { total_count items { sku name url_key small_image { url } media_gallery { url } } } }"}'
- ```
- Record the total_count and the first 5 SKUs returned.
-
-5. **If NO candidate returns JSON**, check whether Harlequin still returns
- JSON (step 2). If Harlequin also stopped returning JSON, the endpoint is
- globally down — not a WM-specific issue.
-
-## Output format
-
-Write `/tmp/yolo_wm_store_code_result.json`:
-```json
-{
- "harlequin_baseline": {
- "returned_json": true,
- "total_count_for_HAW_filter": 0,
- "sample_response": "first 200 chars"
- },
- "wm_probes": [
- {"store_code": "william_morris_us", "http_status": 200, "content_type": "text/html; charset=UTF-8", "is_json": false, "body_snippet": "<!DOCTYPE html>..."}
- ],
- "match_found": false,
- "matched_store_code": null,
- "wm_product_sample": [{"sku": "...", "name": "..."}],
- "wm_total_count": null,
- "verdict": "one-line: 'correct store code is X and returns N products' OR 'no WM store code responds; endpoint may not host WM'"
-}
-```
-
-## Stop conditions
-
-- **Match found** → record it, exit 0. Next human step: build rescrape script from harlequin pattern.
-- **No match, Harlequin still works** → exit 0 with verdict "WM is not hosted on this endpoint; try alternate source (direct WM site, distributor feed)".
-- **No match, Harlequin also failing** → exit 0 with verdict "Sanderson endpoint is globally down; retry later".
-
-## Do NOT
-
-- Do NOT spider the Sanderson site.
-- Do NOT try to scrape WM products even if the correct store code is found. Report only.
-- Do NOT modify any database or enqueue jobs.
-- Do NOT attempt to guess passwords, API keys, or bypass authentication.
-- Do NOT send more than ~15 requests total to the Sanderson endpoint.
diff --git a/tasks/done/53_newmor-missing-patterns.md b/tasks/done/53_newmor-missing-patterns.md
deleted file mode 100644
index cc3fbd5..0000000
--- a/tasks/done/53_newmor-missing-patterns.md
+++ /dev/null
@@ -1,117 +0,0 @@
-# Newmor — enumerate 18 patterns missing from catalog
-
-**Type**: READ-ONLY (no DB writes, no Shopify writes, no Quinn enqueues)
-**Max turns**: 25
-**Output**: `/tmp/yolo_newmor_missing_patterns_result.json`
-
-## Context
-
-Earlier analysis found that only 14 of 32 distinct Newmor pattern prefixes
-present on our Shopify store (vendor='Newmor Wallcoverings', status=ACTIVE)
-have any row in `newmor_catalog`. The remaining 18 pattern prefixes account
-for roughly 225 Shopify products that currently show a "NEWMOR Wallcovering
-Direct" logo instead of a real product image, and cannot be fixed from the
-current catalog.
-
-Your job is to produce a ranked target list of those 18 missing patterns, so
-a future scraper task can be built from it. Do NOT scrape any pages.
-
-## Required steps
-
-1. **Query the missing patterns** using the prefix-extraction logic
- (pattern prefix = `upper(mfr_sku)` with trailing `-NNN` stripped):
- ```
- PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
- WITH sp AS (
- SELECT regexp_replace(upper(trim(mfr_sku)),'-[0-9]+\$','') AS prefix,
- pattern_name, mfr_sku, title
- FROM shopify_products
- WHERE vendor='Newmor Wallcoverings' AND status='ACTIVE'
- AND mfr_sku IS NOT NULL AND mfr_sku<>''
- ),
- cp AS (
- SELECT DISTINCT regexp_replace(upper(trim(mfr_sku)),'-[0-9]+\$','') AS prefix
- FROM newmor_catalog WHERE mfr_sku IS NOT NULL AND mfr_sku<>''
- )
- SELECT sp.prefix,
- COUNT(*) AS shopify_colorways,
- MIN(sp.pattern_name) AS pattern_name_sample,
- MIN(sp.title) AS title_sample
- FROM sp LEFT JOIN cp USING (prefix)
- WHERE cp.prefix IS NULL
- GROUP BY sp.prefix
- ORDER BY 2 DESC;
- "
- ```
- Record all rows.
-
-2. **For each missing prefix**, derive a likely newmor.com product URL. The
- standard newmor.com product URL shape is
- `https://newmor.com/product/{pattern-slug}/` where `{pattern-slug}` is
- the pattern name lowercased with spaces/special chars replaced by dashes.
- Examples:
- - prefix "SHO", pattern_name "Shoreline" → `https://newmor.com/product/shoreline/`
- - prefix "KOT", pattern_name "Kota Silk" → `https://newmor.com/product/kota-silk/`
-
-3. **HEAD-check each derived URL** with a 10-second timeout and realistic
- User-Agent. Record:
- - HTTP status code
- - content-type
- - Whether the page exists (200/301/302 = likely yes, 404 = no)
-
-4. **Spot-check the WordPress uploads folder for recent pattern images**.
- For each confirmed prefix, try:
- ```
- curl -sSI "https://newmor.com/wp-content/uploads/2024/{MM}/" --max-time 10
- curl -sSI "https://newmor.com/wp-content/uploads/2025/{MM}/" --max-time 10
- ```
- for a few recent months (2024 H2, 2025). If any return 200, note that
- recent uploads exist. (If the server returns a directory listing, do
- NOT crawl it — just note.)
-
-5. **Rank** the missing patterns by `shopify_colorways DESC` (highest-volume
- fix target first). Break ties alphabetically.
-
-## Output format
-
-Write `/tmp/yolo_newmor_missing_patterns_result.json`:
-```json
-{
- "total_missing_prefixes": 18,
- "total_shopify_products_affected": 225,
- "missing_patterns": [
- {
- "prefix": "SHO",
- "pattern_name_sample": "Shoreline",
- "shopify_colorways": 15,
- "title_sample": "Shoreline Commercial Wallcovering",
- "derived_url": "https://newmor.com/product/shoreline/",
- "head_status": 200,
- "url_reachable": true
- }
- ],
- "summary": {
- "reachable_prefixes": 0,
- "unreachable_prefixes": 0,
- "ready_for_rescrape": [ "SHO", "COR", ... ],
- "needs_manual_lookup": [ "XYZ" ],
- "recommended_next_step": "one-line recommendation"
- }
-}
-```
-
-## Stop conditions
-
-- **All 18 HEAD-checked** → exit 0 with summary.
-- **newmor.com unreachable** (timeout or blocked from this server) → exit 0
- with verdict "newmor.com not reachable; mark all 18 as needs_manual_lookup".
-
-## Do NOT
-
-- Do NOT scrape product pages. HEAD-only.
-- Do NOT fetch image files, only URL reachability.
-- Do NOT insert anything into newmor_catalog or any other table.
-- Do NOT enqueue Quinn jobs.
-- Do NOT spend more than ~10 seconds per URL.
-- Do NOT expand scope to the 21 BST- stale URLs from Phase 2a — that's a
- separate future task.
diff --git a/tasks/done/AC_collection-merge-dupes.md b/tasks/done/AC_collection-merge-dupes.md
deleted file mode 100644
index 3f7210f..0000000
--- a/tasks/done/AC_collection-merge-dupes.md
+++ /dev/null
@@ -1,27 +0,0 @@
-COLLECTION CONSOLIDATION — Phase 3: Merge duplicate collections
-
-Find and merge near-duplicate collection pairs.
-
-## Finding duplicates
-Fetch all collections from http://localhost:9501/api/collections (auth: admin/DWSecure2024!)
-
-A pair is a duplicate if ANY of:
-1. Exact same title (case-insensitive)
-2. Same title after removing " | Designer Wallcoverings"
-3. Identical product counts AND >80% character overlap in titles
-4. Known pairs to merge:
- - Any "VENDOR" + "VENDOR Collection" → keep "VENDOR"
- - Any "VENDOR" + "VENDOR Wallpaper" → keep "VENDOR"
-
-## Merge process
-For each pair:
-1. Keep the one with MORE products
-2. Delete the smaller one:
- - Create redirect: /collections/{loser-handle} → /collections/{winner-handle}
- POST redirects.json with content token: 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
- - Delete: DELETE smart_collections/{id}.json or custom_collections/{id}.json
- Product token: <redacted:SHOPIFY_ADMIN_TOKEN>
-3. Rate limit: 0.6s
-
-Store: designer-laboratory-sandbox.myshopify.com
-Report: total merged, redirects created.
diff --git a/tasks/done/AD_collection-create-tier1.md b/tasks/done/AD_collection-create-tier1.md
deleted file mode 100644
index 749ea00..0000000
--- a/tasks/done/AD_collection-create-tier1.md
+++ /dev/null
@@ -1,25 +0,0 @@
-COLLECTION CONSOLIDATION — Phase 4: Create missing Tier 1 navigation collections
-
-Ensure these 10 primary navigation collections exist. Check by searching titles. Create missing ones.
-
-Target Tier 1:
-1. Commercial & Contract — tags: commercial, contract, type II, type ii, hospitality, healthcare
-2. Residential — tags: residential
-3. Murals & Scenics — tags: mural, scenic, panoramic, landscape
-4. Grasscloth & Natural Fiber — tags: grasscloth, sisal, jute, seagrass, abaca, arrowroot, natural fiber
-5. Textures & Solids — tags: textured, solid, texture, plain, tone on tone
-6. Peel & Stick — tags: peel and stick, peel & stick, self adhesive, removable
-7. Sustainable & Eco — tags: sustainable, eco-friendly, recycled, FSC, GREENGUARD, low VOC
-8. Stripes & Geometrics — tags: stripe, geometric, chevron, herringbone, trellis
-9. Florals & Botanicals — tags: floral, botanical, flower, vine, leaf, garden
-10. Damask & Traditional — tags: damask, toile, medallion, scroll, traditional
-
-For each missing one:
-POST https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-07/smart_collections.json
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Body: { "smart_collection": { "title": "NAME", "body_html": "<p>3-sentence description</p>", "rules": [tag rules], "disjunctive": true, "published": true } }
-
-Write descriptions in interior design trade language. No "elevate", "stunning", "transform".
-After creation, verify product count > 0.
-
-Also set cover image for each using newest product room setting.
diff --git a/tasks/done/AE_collection-ai-admin-enhance.md b/tasks/done/AE_collection-ai-admin-enhance.md
deleted file mode 100644
index 87815b4..0000000
--- a/tasks/done/AE_collection-ai-admin-enhance.md
+++ /dev/null
@@ -1,98 +0,0 @@
-COLLECTION CONSOLIDATION — Phase 5: Add AI health scores to admin page
-
-Add a health score endpoint and display health grades on each collection card in the admin UI at /admin on port 9501.
-
-## Backend: Add to /root/DW-Agents/collections-agent/server.js
-
-Find "app.post('/api/collections/delete'" and add BEFORE it:
-
-```javascript
-// Health score — compute locally without AI for speed
-app.post('/api/collections/health-score', async (req, res) => {
- const { ids } = req.body;
- if (!ids) return res.status(400).json({ error: 'ids required' });
-
- const cached = collectionsCache || [];
- const scores = [];
- for (const id of ids) {
- const numId = id.includes('/') ? id.split('/').pop() : id;
- const c = cached.find(x => x.id.includes(numId));
- if (!c) continue;
-
- let score = 0;
- const issues = [];
- // Product count scoring
- if (c.productCount === 0) { score -= 3; issues.push('Empty'); }
- else if (c.productCount < 10) { score -= 1; issues.push('< 10 products'); }
- else if (c.productCount >= 50) score += 1;
- else if (c.productCount >= 500) score += 2;
-
- // Title quality
- const t = c.title.toLowerCase();
- if (t.includes('wallpaper') || t.includes('wallcovering') || t.includes('fabric')) { score -= 1; issues.push('Industry term in title'); }
- if (t.length < 5) { score -= 1; issues.push('Title too short'); }
- if (t.includes('test') || t.includes('temp')) { score -= 2; issues.push('Test/temp collection'); }
-
- // Description
- if (c.description && c.description.length > 20) score += 1;
- else { issues.push('No description'); }
-
- // Cover image
- if (c.imageUrl) score += 1;
- else { issues.push('No cover image'); }
-
- // Grade
- let grade;
- if (score >= 3) grade = 'A';
- else if (score >= 2) grade = 'B';
- else if (score >= 0) grade = 'C';
- else if (score >= -1) grade = 'D';
- else grade = 'F';
-
- scores.push({ id: c.id, grade, score, issues });
- }
- res.json({ scores });
-});
-```
-
-## Frontend: Add health badges to grid cards
-
-In the admin page JS, find the function that renders grid cards. After the count-badge element, add a health grade badge.
-
-Find where cards are built (look for "count-badge" in the renderGrid or renderGridView function). After appending the count badge to imgWrap, add:
-
-```javascript
-// Health grade badge
-var gradeEl = document.createElement('div');
-gradeEl.style.cssText = 'position:absolute;top:8px;left:8px;width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.65rem;font-weight:800;z-index:5';
-var grade = getHealthGrade(c);
-var gradeColors = { A: '#22c55e', B: '#3b82f6', C: '#eab308', D: '#f97316', F: '#ef4444' };
-gradeEl.style.background = gradeColors[grade] || '#6b7280';
-gradeEl.style.color = '#fff';
-gradeEl.textContent = grade;
-gradeEl.title = 'Health: ' + grade;
-imgWrap.appendChild(gradeEl);
-```
-
-Add the getHealthGrade function:
-```javascript
-function getHealthGrade(c) {
- var score = 0;
- if (c.productCount === 0) score -= 3;
- else if (c.productCount < 10) score -= 1;
- else if (c.productCount >= 500) score += 2;
- else if (c.productCount >= 50) score += 1;
- var t = c.title.toLowerCase();
- if (t.indexOf('wallpaper') !== -1 || t.indexOf('wallcovering') !== -1) score -= 1;
- if (c.description && c.description.length > 20) score += 1;
- if (c.imageUrl) score += 1;
- if (score >= 3) return 'A';
- if (score >= 2) return 'B';
- if (score >= 0) return 'C';
- if (score >= -1) return 'D';
- return 'F';
-}
-```
-
-After changes: pm2 restart collections-agent
-Verify at http://45.61.58.125:9501/admin — each card should show a colored health grade circle.
diff --git a/tasks/done/AF_blog-audit-purge.md b/tasks/done/AF_blog-audit-purge.md
deleted file mode 100644
index 006a959..0000000
--- a/tasks/done/AF_blog-audit-purge.md
+++ /dev/null
@@ -1,20 +0,0 @@
-BLOG AUDIT AND PURGE — Critical review of all blog posts
-
-Review ALL blog posts on goodquestion.ai. Delete low quality or rule-violating posts.
-
-## goodquestion.ai Blog
-Location: /root/Projects/goodquestion-ai/src/content/posts/
-Port: 9883 | PM2: goodquestion-blog
-
-1. List all posts: ls /root/Projects/goodquestion-ai/src/content/posts/
-2. For EACH post file, read content and check:
- - Violates MEMORY.md? (mentions wallcoverings, wallpaper, DW, Designer Wallcoverings, specific pricing, vendor names like Thibaut/Kravet/Schumacher, internal tools, port numbers, IP addresses)
- - Quality: is it AI slop? Generic filler? Or genuine actionable insights?
- - Brand voice: practical, direct, for entrepreneurs/founders?
-3. DELETE posts that violate sanitization rules or are low quality
-4. For good posts, verify: title, meta description, clean slug
-
-After deletions: rebuild the Astro site:
-cd /root/Projects/goodquestion-ai && npm run build && pm2 restart goodquestion-blog
-
-Report: total reviewed, deleted (with reasons), kept.
diff --git a/tasks/done/AG_dw-exclusive-page-update.md b/tasks/done/AG_dw-exclusive-page-update.md
deleted file mode 100644
index 76e294f..0000000
--- a/tasks/done/AG_dw-exclusive-page-update.md
+++ /dev/null
@@ -1,86 +0,0 @@
-UPDATE THE DW-EXCLUSIVE PAGE on the Shopify store with comprehensive content.
-
-The DW-Exclusive page should showcase everything unique about Designer Wallcoverings — private label brands, exclusive collections, curated palettes, and the new designer collections.
-
-## Step 1: Find or create the DW-Exclusive page
-Check if it exists:
-```bash
-curl -s "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-07/pages.json" \
- -H "X-Shopify-Access-Token: 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" | python3 -c "
-import json,sys
-pages = json.load(sys.stdin)['pages']
-for p in pages:
- if 'exclusive' in p['title'].lower() or 'exclusive' in p.get('handle','').lower():
- print(f'Found: {p[\"id\"]} | {p[\"title\"]} | handle: {p[\"handle\"]}')
-"
-```
-
-If not found, create it:
-```bash
-curl -s -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-07/pages.json" \
- -H "X-Shopify-Access-Token: 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" \
- -H "Content-Type: application/json" \
- -d '{"page": {"title": "DW Exclusive", "published": true, "body_html": "..."}}'
-```
-
-## Step 2: Build the page content
-
-The body_html should include these sections (use clean HTML with inline styles matching the DW dark luxury aesthetic):
-
-### Section 1: Hero
-"DW Exclusive — Curated Collections You Won't Find Anywhere Else"
-Subtitle: "Private label lines, designer palettes, and trade-exclusive selections from the world's finest mills."
-
-### Section 2: Private Label Brands
-Query PostgreSQL for private label vendors:
-```sql
-SELECT vendor_name, private_label_name, sku_prefix FROM vendor_registry WHERE is_private_label = true;
-```
-List each with description. Key brands:
-- Architectural Wallcoverings (formerly Phillipe Romano)
-- Architectural Fabrics
-- DW Bespoke Studio
-- Hollywood Wallcoverings
-
-### Section 3: New Designer Palettes (15 collections created this session)
-List with links:
-- Warm Whites & Ivory, Camel & Cognac, Terracotta & Clay, Warm Taupe
-- Slate & Charcoal, Steel Blue, Sage & Olive, Plum & Aubergine
-- Chinoiserie, Art Deco Metallics, Moody Dark, Coastal & Nautical
-- Powder Room Statements, Primary Suite, Study & Library
-
-For each, get the collection handle and product count from Shopify.
-Link format: /collections/{handle}
-
-### Section 4: Thibaut Exclusive Collections
-Query Shopify for all collections containing "Thibaut" in title:
-```graphql
-{ collections(first: 20, query: "title:Thibaut") { edges { node { id title handle productsCount { count } } } } }
-```
-List each Thibaut collection with product count and link.
-
-### Section 5: Featured Commercial Lines
-- Versa Designed Surfaces (884 products)
-- Koroseal
-- Designtex
-- Wolf Gordon
-- Maharam
-Link to their vendor collection pages.
-
-### Section 6: Trade Benefits CTA
-"Trade pricing available for designers, architects, and specifiers. Contact us for a trade account."
-Button: "Apply for Trade Access" → link to contact page
-
-## Style Guidelines
-- Use inline CSS with dark background (#0C0A09), gold accents (#C4995C)
-- Font: system-ui for body, serif for headings
-- Clean, minimal layout — no clutter
-- Each section has a subtle border separator
-- Collection links as styled buttons/pills
-- DO NOT use "wallpaper" or "wallcovering" — use "collections", "patterns", "designs"
-
-## Step 3: Update the page
-PUT /admin/api/2024-07/pages/{id}.json with the new body_html
-Token: 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
-
-Report: page URL, sections included, collection links working.
diff --git a/tasks/done/AJ_kill-wallpaper-word-everywhere.md b/tasks/done/AJ_kill-wallpaper-word-everywhere.md
deleted file mode 100644
index 8746f84..0000000
--- a/tasks/done/AJ_kill-wallpaper-word-everywhere.md
+++ /dev/null
@@ -1,60 +0,0 @@
-KILL THE WORD "WALLPAPER" EVERYWHERE — Replace with "Wallcovering"
-
-ABSOLUTE RULE: The word "wallpaper" must NOT appear anywhere customer-facing. It is always "wallcovering".
-
-## Scope — fix ALL of these:
-
-### 1. Product Titles on Shopify
-Find all products with "wallpaper" in the title:
-```graphql
-{ products(first: 250, query: "title:wallpaper") { edges { node { id title } } pageInfo { hasNextPage } } }
-```
-For each: replace "Wallpaper" → "Wallcovering", "wallpaper" → "wallcovering", "Wallpapers" → "Wallcoverings"
-Use: `mutation { productUpdate(input: { id: "ID", title: "new title" }) { product { id } userErrors { message } } }`
-
-### 2. Product Tags on Shopify
-Find products tagged with any wallpaper-related tag:
-- Remove tags: "Wallpaper", "wallpaper", "Custom Wallpaper", "wallpaper collection"
-- Add replacement: "Wallcovering" if not already present
-Use GraphQL tagsAdd/tagsRemove mutations.
-
-### 3. Collection Titles on Shopify (already in task AB but reinforce)
-Any collection with "Wallpaper" in title → rename to "Wallcovering"
-"Blue Wallpaper Collection" → "Blue"
-"Grey Wallpaper Collections" → "Grey"
-Use the /api/collections/rename endpoint on port 9501.
-
-### 4. Product body_html on Shopify
-For products where body_html contains "wallpaper":
-Replace "wallpaper" → "wallcovering" (case-insensitive)
-Use: productUpdate mutation with updated body_html
-CAREFUL: only replace the word, not partial matches (e.g. don't touch URLs)
-
-### 5. PostgreSQL catalog tables
-For ALL *_catalog tables, update:
-```sql
-UPDATE {table}_catalog SET product_type = REPLACE(REPLACE(product_type, 'Wallpaper', 'Wallcovering'), 'wallpaper', 'wallcovering')
-WHERE product_type ILIKE '%wallpaper%';
-```
-
-### 6. Metafield descriptions
-Check specs.description and custom.* metafields for "wallpaper" references.
-
-## Implementation Order
-1. Product types (task AI handles this)
-2. Product titles (this task — highest visibility)
-3. Tags
-4. Collection titles
-5. body_html (careful, large content)
-6. PostgreSQL (backend alignment)
-
-## Rate Limits
-- GraphQL mutations: 0.3s between calls
-- Paginate 250 at a time
-- For body_html updates: 0.5s (larger payloads)
-
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-
-Report: total replacements by category (titles, tags, collections, body_html, DB).
diff --git a/tasks/done/AK_fix-collection-trailing-artifacts.md b/tasks/done/AK_fix-collection-trailing-artifacts.md
deleted file mode 100644
index c33f4ed..0000000
--- a/tasks/done/AK_fix-collection-trailing-artifacts.md
+++ /dev/null
@@ -1,19 +0,0 @@
-Fix trailing artifacts left by the collection rename task (AB).
-
-Many Shopify collections now have titles ending with trailing fragments like:
-- Trailing ` -` (e.g., "Temperley London x Romo -")
-- Trailing ` and` (e.g., "Made in Italy Wallpaper and")
-- Trailing ` |` or ` | Designer` fragments
-- Trailing ` &`
-
-Steps:
-1. Use the Shopify Admin REST API to list all smart collections and custom collections
-2. Find any with titles ending in ` -`, ` and`, ` |`, ` &`, or similar trailing fragments
-3. Strip the trailing artifact and update the title
-4. Report how many were fixed
-
-Use token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-API version: 2024-01
-
-Rate limit: max 2 requests/second. Add 500ms delay between calls.
diff --git a/tasks/done/AM_cleanup-errored-pm2.md b/tasks/done/AM_cleanup-errored-pm2.md
deleted file mode 100644
index 557da89..0000000
--- a/tasks/done/AM_cleanup-errored-pm2.md
+++ /dev/null
@@ -1,17 +0,0 @@
-Clean up errored PM2 processes and trim idle ones.
-
-1. List all PM2 processes with `pm2 jlist`
-2. Delete these errored processes (they've been broken for days):
- - zendesk-agent
- - tasha-agent
- - abby-agenda
- - test-agent
-
- Use `pm2 delete <name>` for each.
-
-3. Find all online processes using 0% CPU and <50MB memory that are NOT in this protected list:
- - Any process with "website", "blog", "goodquestion", "bubbe", "norma", "yolo", "commit-agent", "agent-cronjob", "timeout-agent", "dw-master-hub", "dw-dashboard-monitor", "vendor-command-center", "silas", "george", "schedule-engine", "server-agent", "web-uptime", "grasscloth", "flockedwallpaper", "glassbeadedwallpaper", "novasuede", "exec-", "boardroom", "dw-central", "restart-crashed", "ken", "enrich-romo", "audit-body-fix", "letsbegin", "af-full-monty", "showroom"
-
- Stop (don't delete) any idle process NOT on the protected list.
-
-4. Report: how many deleted, how many stopped, RAM before/after.
diff --git a/tasks/done/AN_blog-post-goodquestion.md b/tasks/done/AN_blog-post-goodquestion.md
deleted file mode 100644
index 8ab4787..0000000
--- a/tasks/done/AN_blog-post-goodquestion.md
+++ /dev/null
@@ -1,14 +0,0 @@
-Generate and publish a new blog post to goodquestion.ai.
-
-Follow the existing blog pipeline:
-
-1. Read the blog generator at /root/DW-Agents/blog-agent/generator.js to understand the current pipeline
-2. Generate a new blog post about a relevant AI operations topic for entrepreneurs.
- Ideas: "How One Person Can Replace a Dev Team with AI Agents", "Autonomous Infrastructure: Set It and Forget It", "The Economics of AI-First Operations"
-3. Run the content through the sanitizer at /root/DW-Agents/blog-agent/sanitizer.js
-4. NEVER mention: wallcoverings, wallpaper, Designer Wallcoverings, DW, Shopify, specific vendor names, IP addresses, ports, credentials, or any item from the sanitizer blocklist
-5. Target audience: entrepreneurs and small business owners
-6. End with a note about seeking advisory/board roles in AI-driven operations
-7. Save the post as an Astro markdown file in /root/Projects/goodquestion-ai/src/content/posts/
-8. Build: cd /root/Projects/goodquestion-ai && npm run build
-9. Report the post title and URL slug
diff --git a/tasks/done/AO_tweet-and-bsky.md b/tasks/done/AO_tweet-and-bsky.md
deleted file mode 100644
index de26df7..0000000
--- a/tasks/done/AO_tweet-and-bsky.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Post to Twitter/X and Bluesky for @agentabrams.
-
-1. Check the latest blog post at /root/Projects/goodquestion-ai/src/content/posts/ (most recently modified .md file)
-2. Extract the title and create a compelling tweet (under 280 chars) that promotes the post
-3. Include the URL: https://goodquestion.ai/posts/{slug}/
-4. Post to Twitter/X using: cd /root/Projects/goodquestion-ai && node scripts/tweet.cjs --text "YOUR TWEET TEXT"
-5. Post to Bluesky using: cd /root/Projects/goodquestion-ai && node scripts/bsky-post.cjs --text "YOUR POST TEXT" --url "https://goodquestion.ai/posts/{slug}/" --link-text "Read more"
-6. Report success/failure for both platforms
diff --git a/tasks/done/AP_hollywood-abaca-room-settings.md b/tasks/done/AP_hollywood-abaca-room-settings.md
deleted file mode 100644
index 4a8dcf4..0000000
--- a/tasks/done/AP_hollywood-abaca-room-settings.md
+++ /dev/null
@@ -1,34 +0,0 @@
-Generate room settings for ALL 75 Hollywood Abaca Grasscloth products with corrected scale.
-
-CRITICAL CONTEXT: These are flatbed-scanned grasscloth textures. The images represent ~7-9" of physical material, NOT the full 36" roll width. You MUST use patternWidthInches=8 and patternHeightInches=10 when calling the room app, NOT the product's width metafield (36").
-
-Product IDs are in /tmp/hollywood-ids.txt (76 total, skip 1496353341552 which is already done with 7 images).
-
-Write a Node.js script at /root/DW-Agents/scripts/hollywood-abaca-rooms.js that:
-
-1. Reads /tmp/hollywood-ids.txt, filters out 1496353341552
-2. For each product ID:
- a. Fetch product from Shopify REST API (GET /admin/api/2024-01/products/{id}.json)
- b. Skip if product already has 3+ images (room settings likely exist)
- c. Download the primary image as base64
- d. Call the room app at http://127.0.0.1:3075/api/generate-room for 5 rooms:
- - bedroom (12x9 ft wall), living_room (16x9), dining_room (14x9), office (12x9), bathroom (10x9)
- - Use patternWidthInches: 8, patternHeightInches: 10 (NOT 36!)
- - angle: 'straight_on', halfDrop: false, patternType: 'wallpaper'
- - Auth: Basic admin:DWSecure2024!
- - Timeout: 90 seconds per room
- e. Upload each generated room image to Shopify:
- - POST /admin/api/2024-01/products/{id}/images.json
- - Strip "data:image/...;base64," prefix from base64 if present
- - Alt text: "{product title} - {room} room setting"
- f. Wait 2s between rooms, 3s between products (rate limiting)
-3. Log progress: "Product X/75: {title} — {n} rooms uploaded"
-4. At the end, report total: X products processed, Y rooms uploaded, Z errors
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-After writing the script, start it with PM2:
-pm2 start /root/DW-Agents/scripts/hollywood-abaca-rooms.js --name hollywood-rooms --no-autorestart
-
-Then report the PM2 name so we can monitor it.
diff --git a/tasks/done/AQ_hollywood-imageclean-and-spin.md b/tasks/done/AQ_hollywood-imageclean-and-spin.md
deleted file mode 100644
index 353d015..0000000
--- a/tasks/done/AQ_hollywood-imageclean-and-spin.md
+++ /dev/null
@@ -1,59 +0,0 @@
-Run image cleaning (crop text/logos) and swatch spin generation for ALL 75 Hollywood Abaca Grasscloth products.
-
-The room settings script (hollywood-rooms) is already running but it ONLY does rooms. This task handles the 2 missing steps: imageClean + spin.
-
-Product IDs are in /tmp/hollywood-ids.txt (76 total, skip 1496353341552 which already has both).
-
-Write a Node.js script at /root/DW-Agents/scripts/hollywood-abaca-clean-spin.js that processes each product:
-
-## STEP 1: Image Clean (crop text/logos from swatch)
-
-For each product:
-1. Fetch product from Shopify: GET /admin/api/2024-01/products/{id}.json
-2. Download the PRIMARY image (position 1, the original swatch) as base64
-3. Call Gemini 2.0 Flash vision to check if image has text/logos/watermarks:
- - Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}
- - Prompt: "Does this image contain text, logos, watermarks, rulers, or website URLs? Answer YES or NO."
- - mimeType: auto-detect from magic bytes (0xFF 0xD8 = image/jpeg, 0x89 0x50 = image/png)
- - Strip any "data:image/...;base64," prefix before sending to Gemini
-4. If DIRTY (YES):
- a. Save image to /tmp/clean_in_{id}.jpg
- b. Use ImageMagick to crop: bottom 15%, top 5%, sides 2%:
- - convert input.jpg -gravity South -chop 0x15% out1.jpg
- - convert out1.jpg -gravity North -chop 0x5% out2.jpg
- - convert out2.jpg -gravity West -chop 2%x0 out3.jpg
- - convert out3.jpg -gravity East -chop 2%x0 -quality 92 final.jpg
- c. Re-check with Gemini — if still dirty, crop more aggressively (bottom 25%, top 10%)
- d. Upload cleaned image to Shopify as NEW image at position 1 (replacing old primary)
- - PUT /admin/api/2024-01/products/{id}/images/{imageId}.json with { image: { attachment: base64 } }
- e. Clean up /tmp files
-5. If CLEAN (NO): skip, log "clean"
-
-## STEP 2: Swatch Spin
-
-For each product (after clean step):
-1. Check if product already has a spin image (alt text contains "Spin" or "Swatch" or src contains "spin")
-2. If no spin exists, run the spin script:
- - node /root/Projects/Designer-Wallcoverings/scripts/shopify-swatch-spin.js --product-id {id}
- - Timeout: 120 seconds
-3. Log result
-
-## Rate Limiting
-- 500ms between Shopify API calls
-- 2s between Gemini calls
-- 3s between products
-- 15s timeout for each ImageMagick command
-
-## Logging
-- Log: "Product X/75: {title} — clean:{dirty|clean|skipped} spin:{done|exists|error}"
-- At end: "COMPLETE: X cleaned, Y spins generated, Z errors"
-
-## Config
-- Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Store: designer-laboratory-sandbox.myshopify.com
-- Gemini key: ${GOOGLE_API_KEY}
-
-After writing the script, start it with PM2:
-pm2 start /root/DW-Agents/scripts/hollywood-abaca-clean-spin.js --name hollywood-clean-spin --no-autorestart
-
-Report the PM2 name so it can be monitored.
diff --git a/tasks/done/AS_update-dw-exclusive-page.md b/tasks/done/AS_update-dw-exclusive-page.md
deleted file mode 100644
index 82e69e3..0000000
--- a/tasks/done/AS_update-dw-exclusive-page.md
+++ /dev/null
@@ -1,79 +0,0 @@
-Redesign the "DW Exclusive" Shopify page (ID: 181094645811, handle: dw-exclusive-1) with an organized collection browse experience.
-
-## Design Requirements
-
-Create a beautiful, responsive HTML page (inline CSS only — Shopify pages don't support external stylesheets) that organizes collections into browseable categories. The design should be:
-
-- Clean, luxury aesthetic (think Net-a-Porter / RH / 1stDibs)
-- Light background (#FAF9F6), dark text, gold accents (#C4995C)
-- Font: Georgia for headings, system-ui for body
-- Responsive grid: 3 columns desktop, 2 tablet, 1 mobile
-- Each collection card links to /collections/{handle}
-- Include collection image if available, fallback to a solid color block
-
-## Page Sections (in order)
-
-### 1. Hero Header
-- "Browse Our Collections" main heading
-- Subtitle: "Curated wallcoverings for designers, architects, and specifiers"
-- Subtle separator line
-
-### 2. Shop By Category (Tier-1 Navigation)
-These are the 10 tier-1 collections created in task AD. Show as large cards in a 2-column grid:
-- Commercial & Contract → /collections/commercial-contract
-- Grasscloth & Natural Fiber → /collections/grasscloth-natural-fiber
-- Murals & Scenics → /collections/murals-scenics
-- Textures & Solids → /collections/textures-solids
-- Florals & Botanicals → /collections/florals-botanicals
-- Damask & Traditional → /collections/damask-traditional
-- Peel & Stick → /collections/peel-stick
-- Sustainable & Eco → /collections/sustainable-eco
-- Residential → /collections/residential
-- Kids & Nursery → /collections/kids-nursery
-
-### 3. Shop By Color
-Show 15 color palette collections as circular swatches with the color name below:
-- Blue, Green, Gold, Red, Pink, Purple, Orange, Yellow, Black, White, Gray, Brown, Beige, Silver, Metallic
-- Link each to its collection handle (e.g., /collections/blue-wallcoverings)
-
-### 4. Private Label Exclusives
-DW's own brands in a premium section:
-- Architectural Wallcoverings → /collections/architectural-wallcoverings
-- Phillipe Romano → /collections/phillipe-romano
-- Retro Walls → /collections/retro-walls
-- Malibu → /collections/malibu
-
-### 5. Popular Brands (A-Z)
-Query Shopify for the top 50 vendor-name collections with the most products. Show as a 4-column alphabetical grid of text links. Each links to /collections/{handle}.
-
-To get the top collections: use the Shopify REST API to list smart collections, then for each get product count via /admin/api/2024-01/smart_collections/{id}/products/count.json. Pick collections whose titles match known vendor names.
-
-Actually — to keep it simple and avoid excessive API calls, just hardcode the known top brands from the vendor registry. Query PostgreSQL:
-```sql
-SELECT vendor_name, LOWER(REPLACE(REPLACE(vendor_name, ' ', '-'), '&', 'and')) as handle
-FROM vendor_registry
-WHERE status = 'active'
-ORDER BY vendor_name
-LIMIT 60;
-```
-
-### 6. Shop By Style
-Text link grid:
-- Art Deco, Mid Century Modern, Contemporary, Traditional, Tropical, Coastal, Bohemian, Geometric, Abstract, Chinoiserie, Toile, Damask, Ikat, Stripe, Plaid, Paisley, Lattice, Trellis
-
-### 7. Shop By Material
-Text link grid:
-- Grasscloth, Vinyl (Type II), Non-Woven, Paper, Silk, Sisal, Jute, Cork, Linen, Mica, Foil, Fabric-Backed
-
-## Implementation
-
-1. Query PostgreSQL for active vendor names
-2. Build the HTML with all sections
-3. Update the page via Shopify REST API:
- PUT /admin/api/2024-01/pages/181094645811.json
- Body: { page: { id: 181094645811, body_html: "..." } }
- Token: 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 (Pages/content token)
-
-4. Verify the update: curl the page and confirm body_html was saved
-
-DB connection: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
diff --git a/tasks/done/AT_fix-vendor-logo-size-flash.md b/tasks/done/AT_fix-vendor-logo-size-flash.md
deleted file mode 100644
index 618891a..0000000
--- a/tasks/done/AT_fix-vendor-logo-size-flash.md
+++ /dev/null
@@ -1,49 +0,0 @@
-Fix vendor logo/name on product pages — it flashes large then shrinks to small. It should STAY large.
-
-## Problem
-On product pages like https://www.designerwallcoverings.com/products/hollywood-abaca-grasscloth-grs-32003, the vendor logo or vendor name initially renders at a large size, then JavaScript or CSS transitions shrink it down to a smaller size. This creates an ugly flash/revert effect. The logo/name should stay at the large size.
-
-## Steps
-
-1. First, read the Shopify theme code. The theme files are at /root/Projects/Designer-Wallcoverings/
- - Check `sections/product-template.liquid` or `sections/main-product.liquid`
- - Check `snippets/product-vendor.liquid` or similar vendor display snippet
- - Check `assets/theme.js` or `assets/product.js` for JS that resizes vendor elements
- - Check `assets/theme.css` or `assets/theme.scss.liquid` for CSS transitions on vendor elements
-
-2. Identify the element that displays the vendor logo/name. Look for:
- - `.product-vendor`, `.vendor-logo`, `.brand-logo`, `.product__vendor` class names
- - Any CSS transitions or animations that change `font-size`, `height`, `width`, `transform`, or `max-height`
- - Any JavaScript that adds/removes CSS classes after page load (like `loaded`, `small`, `collapsed`)
- - Any `setTimeout` or `requestAnimationFrame` that modifies vendor element styles
-
-3. Fix the issue:
- - If CSS transition: remove the transition and set the element to the LARGE state permanently
- - If JS resize: remove or disable the JS that shrinks the vendor element
- - If class toggle: keep the initial large class, prevent the small class from being added
- - Make sure the fix works on both desktop and mobile
-
-4. Test by checking the Liquid template renders correctly without the flash
-
-5. After fixing, rebuild/deploy:
- - If theme is deployed via git: commit the fix
- - If using Shopify CLI: note the change for manual deploy
-
-## Important
-- Do NOT change the vendor logo image itself
-- Do NOT change the logo's position/layout — only prevent the size change
-- The logo should display at whatever its INITIAL large size is, permanently
-- Look in BOTH the Liquid template AND any JavaScript files that run on product pages
-
-## Theme location
-/root/Projects/Designer-Wallcoverings/
-
-If the theme files aren't there, check:
-- /root/Projects/Designer-Wallcoverings/theme/
-- /root/Projects/Designer-Wallcoverings/shopify-theme/
-
-Or use the Shopify Admin API to read theme assets:
-GET /admin/api/2024-01/themes.json (get active theme ID)
-GET /admin/api/2024-01/themes/{id}/assets.json?asset[key]=sections/product-template.liquid
-Token: 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
-Store: designer-laboratory-sandbox.myshopify.com
diff --git a/tasks/done/AU_publish-collections-all-channels.md b/tasks/done/AU_publish-collections-all-channels.md
deleted file mode 100644
index b477fd0..0000000
--- a/tasks/done/AU_publish-collections-all-channels.md
+++ /dev/null
@@ -1,62 +0,0 @@
-Ensure ALL collections are published to ALL 13 Shopify sales channels.
-
-## Problem
-Collections may only be published to some channels (e.g., Online Store) but not all 13. They need to be visible across every channel.
-
-## Steps
-
-1. First, get all sales channels (publications):
-```graphql
-{
- publications(first: 20) {
- edges {
- node {
- id
- name
- supportsFuturePublishing
- }
- }
- }
-}
-```
-Use GraphQL Admin API:
-POST https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json
-Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-
-2. Get ALL collection IDs (smart + custom). Paginate through all ~1,983 (or however many remain after cull task AR runs).
-
-3. For each collection, publish to ALL channels using the publishablePublish mutation:
-```graphql
-mutation publishCollection($id: ID!, $input: [PublicationInput!]!) {
- publishablePublish(id: $id, input: $input) {
- publishable { ... on Collection { id title } }
- userErrors { field message }
- }
-}
-```
-Where $input is an array of ALL publication IDs:
-```json
-[
- { "publicationId": "gid://shopify/Publication/XXXXX" },
- { "publicationId": "gid://shopify/Publication/YYYYY" },
- ...for all 13 channels
-]
-```
-
-4. Rate limit: 500ms between GraphQL mutations (Shopify GraphQL cost limit)
-
-5. Log progress: "Collection X/total: {title} — published to {n} channels"
-
-6. At the end report:
- - Total collections processed
- - How many were already on all channels
- - How many had channels added
- - Any errors
-
-Write a Node.js script at /root/DW-Agents/scripts/publish-collections-all-channels.js
-
-After writing, start with PM2:
-pm2 start /root/DW-Agents/scripts/publish-collections-all-channels.js --name publish-collections-channels --no-autorestart
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
diff --git a/tasks/done/AV_hollywood-wc-body-cleanup.md b/tasks/done/AV_hollywood-wc-body-cleanup.md
deleted file mode 100644
index 104786d..0000000
--- a/tasks/done/AV_hollywood-wc-body-cleanup.md
+++ /dev/null
@@ -1,70 +0,0 @@
-Clean up body_html for ALL 2,956 Hollywood Wallcoverings products. Remove ALL specs from body — specs go in metafields ONLY.
-
-## The Problem
-Hollywood Wallcoverings product pages have messy body_html with:
-1. Duplicate spec tables (one styled table + one raw table)
-2. Specs that should ONLY be in metafields (width, material, collection, product type)
-3. Mixed fonts and inconsistent styling
-4. Redundant "Product Specifications" header
-
-Example of CURRENT bad body (product ID 3740354314305):
-- Styled spec table with "Product Specifications" header
-- Raw spec table with Collection, Product Type, Width, Material
-- Pattern name repeated as bold text
-- "Professional Installation Recommended" box
-
-## What the body_html SHOULD look like
-A CLEAN, single-font description paragraph ONLY. No specs, no tables. Example:
-
-```html
-<div style="font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333;line-height:1.7;max-width:700px;">
-<p>Amazing Vinyl is a premium Type II vinyl wallcovering by Hollywood Wallcoverings. Commercial-grade durability meets elegant design, ideal for high-traffic commercial, hospitality, and residential environments.</p>
-<p style="font-size:13px;color:#777;margin-top:16px;">Professional installation recommended. Available by the yard. Contact us for trade pricing and samples.</p>
-</div>
-```
-
-That's IT. No tables. No specs. No "Product Specifications" header. Just the description text.
-
-## What MUST be in metafields (verify these exist, create if missing)
-For each product, ensure these metafields are set from the body data before removing it:
-- specs.width (e.g., "54\"")
-- specs.material (e.g., "Vinyl")
-- specs.collection (e.g., "Hollywood Wallcoverings")
-- custom.width, custom.material, custom.collection_name (duplicate in custom namespace)
-
-## Implementation
-
-Write a script at /root/DW-Agents/scripts/hollywood-wc-body-cleanup.js:
-
-1. Paginate through ALL Hollywood Wallcoverings products using GraphQL:
- ```graphql
- products(first: 50, query: "vendor:\"Hollywood Wallcoverings\" status:active", after: $cursor) {
- edges { node { id title bodyHtml metafields(first: 20) { edges { node { namespace key value } } } } }
- pageInfo { hasNextPage endCursor }
- }
- ```
-
-2. For each product:
- a. Parse body_html — extract any spec values (width, material, collection) from tables
- b. Check if metafields already have these values. If not, create them via:
- ```graphql
- mutation { productUpdate(input: { id: $id, metafields: [...] }) { ... } }
- ```
- c. Rebuild body_html as CLEAN description only:
- - Extract the first paragraph (the actual description text)
- - Keep "Professional installation recommended" note
- - Strip ALL tables, ALL spec rows, ALL duplicate pattern name headers
- - Single font: Helvetica Neue, no mixed sizes
- d. Update product with new body_html via REST:
- PUT /admin/api/2024-01/products/{id}.json
- Body: { product: { id, body_html: cleanHtml } }
-
-3. Rate limit: 500ms between mutations
-4. Log: "Product X/2956: {title} — specs extracted: {n}, body cleaned"
-5. Start with product 3740354314305 (Amazing Vinyl) as first product to verify
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Start with PM2:
-pm2 start /root/DW-Agents/scripts/hollywood-wc-body-cleanup.js --name hw-body-cleanup --no-autorestart
diff --git a/tasks/done/AW_hollywood-wc-commercial-rooms.md b/tasks/done/AW_hollywood-wc-commercial-rooms.md
deleted file mode 100644
index d7ad085..0000000
--- a/tasks/done/AW_hollywood-wc-commercial-rooms.md
+++ /dev/null
@@ -1,53 +0,0 @@
-Generate COMMERCIAL room settings for ALL 2,956 Hollywood Wallcoverings products.
-
-CRITICAL: Hollywood Wallcoverings is a COMMERCIAL/CONTRACT line (Type II vinyl, 54" wide). Room settings must be COMMERCIAL environments, NOT residential.
-
-## Commercial Room Types (6 rooms per product)
-1. Hotel Lobby (20x12 ft wall)
-2. Corporate Office (16x9 ft wall)
-3. Healthcare Waiting Room (14x9 ft wall)
-4. Restaurant Dining (18x10 ft wall)
-5. Retail Showroom (20x10 ft wall)
-6. Conference Room (16x9 ft wall)
-
-## Scale
-These are 54" wide commercial vinyls. The product images are manufacturer photos (NOT flatbed scans), so use patternWidthInches: 54 and patternHeightInches based on the repeat metafield (default to 54 if no repeat).
-
-## Implementation
-
-Write a script at /root/DW-Agents/scripts/hollywood-wc-commercial-rooms.js:
-
-1. Get all Hollywood Wallcoverings product IDs. Use GraphQL pagination:
- ```graphql
- products(first: 50, query: "vendor:\"Hollywood Wallcoverings\" status:active", after: $cursor) {
- edges { node { id title handle images(first: 1) { edges { node { src } } } metafields(first: 10, keys: ["specs.width","custom.width","specs.repeat_v","custom.pattern_repeat"]) { edges { node { key value } } } } }
- pageInfo { hasNextPage endCursor }
- }
- ```
-
-2. For each product:
- a. Skip if product already has 3+ images (room settings likely exist)
- b. Download primary image as base64
- c. Get width from metafields (default 54") and repeat (default 54")
- d. Call room app at http://127.0.0.1:3075/api/generate-room for each of 6 commercial rooms:
- - Auth: Basic admin:DWSecure2024!
- - Body: { patternBase64, roomType, angle: 'straight_on', wallWidthFeet, wallHeightFeet, patternWidthInches, patternHeightInches, halfDrop: false, patternType: 'wallpaper' }
- - Room types map to the room app's types: use 'living_room' for hotel lobby (20x12), 'office' for corporate (16x9), 'bedroom' for healthcare (14x9), 'dining_room' for restaurant (18x10), 'bathroom' for retail (20x10), 'office' with different size for conference (16x9)
- - Actually the room app may only support: bedroom, living_room, dining_room, office, bathroom
- - Use these 5 rooms with commercial wall sizes: living_room (20x12), office (16x9), dining_room (18x10), bedroom (14x9), bathroom (14x9)
- - Timeout: 90 seconds per room
- e. Strip "data:image/...;base64," prefix from response
- f. Upload each room to Shopify as product image:
- - POST /admin/api/2024-01/products/{id}/images.json
- - Alt text: "{product title} - {room type} commercial room setting"
- g. Wait 2s between rooms, 3s between products
-
-3. Start with product 3740354314305 (Amazing Vinyl HWC-61190) FIRST
-4. Log: "Product X/2956: {title} — {n}/5 rooms uploaded"
-5. Final report: total processed, rooms uploaded, errors
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-After writing, start with PM2:
-pm2 start /root/DW-Agents/scripts/hollywood-wc-commercial-rooms.js --name hw-commercial-rooms --no-autorestart
diff --git a/tasks/done/AX_variant-weight-fix-pm2.md b/tasks/done/AX_variant-weight-fix-pm2.md
deleted file mode 100644
index cece7a8..0000000
--- a/tasks/done/AX_variant-weight-fix-pm2.md
+++ /dev/null
@@ -1,24 +0,0 @@
-Run the variant physical weight fix as a long-running PM2 script.
-
-There is already a script at /root/DW-Agents/scripts/fix-variant-weights.js — check if it exists and works.
-
-If it exists, start it:
-pm2 start /root/DW-Agents/scripts/fix-variant-weights.js --name fix-variant-weights --no-autorestart
-
-If it doesn't exist or errors, write a new one that:
-1. Queries Shopify for ALL active products using GraphQL pagination
-2. For each product, check all variants
-3. If variant price > $0 AND title does NOT contain "Sample" AND weight = 0: set weight to 2.0 lbs
-4. If variant title contains "Sample" OR price = $0: set weight to 0
-5. Use GraphQL inventoryItem weight mutation:
- ```graphql
- mutation { inventoryItemUpdate(id: $inventoryItemId, input: { measurement: { weight: { value: 2.0, unit: POUNDS } } }) { inventoryItem { id } userErrors { message } } }
- ```
-6. Rate limit: 500ms between mutations
-7. Log: "Product X: {title} — {n} variants fixed"
-8. Final report: total variants fixed
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Start with PM2 --no-autorestart after writing/verifying.
diff --git a/tasks/done/AY_metafield-push-all-vendors-pm2.md b/tasks/done/AY_metafield-push-all-vendors-pm2.md
deleted file mode 100644
index 02ead5d..0000000
--- a/tasks/done/AY_metafield-push-all-vendors-pm2.md
+++ /dev/null
@@ -1,39 +0,0 @@
-Push metafields from PostgreSQL to Shopify for ALL vendors as a long-running PM2 script.
-
-Write a script at /root/DW-Agents/scripts/push-metafields-all-vendors.js that:
-
-1. Query PostgreSQL for all vendors with catalog tables:
- ```sql
- SELECT table_name FROM information_schema.tables
- WHERE table_schema = 'public' AND table_name LIKE 'catalog_%'
- ORDER BY table_name;
- ```
-
-2. For each catalog table, find products that have a shopify_id but might be missing metafields:
- ```sql
- SELECT shopify_id, pattern_name, color_name, width, material, repeat_v, repeat_h,
- collection, mfr_sku, fire_rating, match_type, coverage, finish, application
- FROM catalog_{vendor}
- WHERE shopify_id IS NOT NULL AND width IS NOT NULL
- ```
-
-3. For each product, push specs to Shopify metafields using REST API:
- PUT /admin/api/2024-01/products/{shopify_id}.json
- Body: { product: { id, metafields: [
- { namespace: "specs", key: "width", value: width, type: "single_line_text_field" },
- { namespace: "specs", key: "material", value: material, type: "single_line_text_field" },
- { namespace: "custom", key: "width", value: width, type: "single_line_text_field" },
- ... etc for all available fields
- ]}}
-
-4. Skip fields that are NULL/empty in DB
-5. Rate limit: 500ms between API calls, 2s between vendors
-6. Log: "Vendor {name}: {X}/{Y} products updated, {Z} metafields pushed"
-7. Final summary: vendors processed, total products, total metafields
-
-DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Start with PM2:
-pm2 start /root/DW-Agents/scripts/push-metafields-all-vendors.js --name push-metafields-all --no-autorestart
diff --git a/tasks/done/AZ_standardize-product-types-pm2.md b/tasks/done/AZ_standardize-product-types-pm2.md
deleted file mode 100644
index 9517857..0000000
--- a/tasks/done/AZ_standardize-product-types-pm2.md
+++ /dev/null
@@ -1,44 +0,0 @@
-Standardize product_type across ALL Shopify products as a long-running PM2 script.
-
-Write a script at /root/DW-Agents/scripts/standardize-product-types.js that:
-
-1. Query ALL active products from Shopify using GraphQL pagination:
- ```graphql
- products(first: 50, query: "status:active", after: $cursor) {
- edges { node { id title productType vendor tags metafields(keys: ["specs.width","custom.width","global.width"]) { edges { node { key value } } } } }
- pageInfo { hasNextPage endCursor }
- }
- ```
-
-2. Apply standardization rules based on width:
- - Width >= 48" (or "48" in string): product_type = "Commercial Wallcoverings"
- - Width >= 20" and < 48": product_type = "Wallcovering"
- - Width < 20" or contains "fabric": product_type = "Fabric"
- - No width available: keep existing product_type
-
-3. Also fix by vendor type (override width rule if needed):
- - Known commercial vendors (Hollywood Wallcoverings, Versa, Koroseal, Designtex, Wolf Gordon): "Commercial Wallcoverings"
- - Known fabric vendors: "Fabric"
-
-4. Normalize inconsistent types:
- - "Wallpaper" → "Wallcovering"
- - "wallpaper" → "Wallcovering"
- - "Wall Covering" → "Wallcovering"
- - "Type II Vinyl" → "Commercial Wallcoverings"
- - "Grasscloth" → "Wallcovering" (unless commercial width)
-
-5. Update via GraphQL mutation:
- ```graphql
- mutation { productUpdate(input: { id: $id, productType: $type }) { product { id } userErrors { message } } }
- ```
-
-6. Rate limit: 500ms between mutations
-7. Log: "Product X: {old_type} → {new_type} ({title})"
-8. Skip products already correct
-9. Final: total scanned, total changed, breakdown by type
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Start with PM2:
-pm2 start /root/DW-Agents/scripts/standardize-product-types.js --name standardize-types --no-autorestart
diff --git a/tasks/done/BA_fix-product-type-by-width-pm2.md b/tasks/done/BA_fix-product-type-by-width-pm2.md
deleted file mode 100644
index 3caed2a..0000000
--- a/tasks/done/BA_fix-product-type-by-width-pm2.md
+++ /dev/null
@@ -1,22 +0,0 @@
-Fix product types specifically for products where width contradicts their current product_type. Long-running PM2 script.
-
-Write a script at /root/DW-Agents/scripts/fix-product-type-by-width.js that:
-
-1. Query ALL active products with width metafields from Shopify using GraphQL pagination
-2. For each product:
- - Parse width from metafields (specs.width, custom.width, or global.width)
- - Compare against current productType
- - Flag mismatches:
- a. Width >= 48" but type is NOT "Commercial Wallcoverings" → fix to "Commercial Wallcoverings"
- b. Width 20-47" but type is "Commercial Wallcoverings" → fix to "Wallcovering" (unless vendor is known commercial)
- c. Width < 20" but type is "Wallcovering" → might be fabric, check vendor
-3. Update mismatched products via GraphQL productUpdate mutation
-4. Rate limit: 500ms between mutations
-5. Log each fix: "{title}: {width} — {old_type} → {new_type}"
-6. Final report: total scanned, mismatches found, fixed
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Start with PM2:
-pm2 start /root/DW-Agents/scripts/fix-product-type-by-width.js --name fix-types-by-width --no-autorestart
diff --git a/tasks/done/BB_create-youtube-videos-pm2.md b/tasks/done/BB_create-youtube-videos-pm2.md
deleted file mode 100644
index ceed1e5..0000000
--- a/tasks/done/BB_create-youtube-videos-pm2.md
+++ /dev/null
@@ -1,22 +0,0 @@
-Create and upload narrated YouTube videos for the Agent Abrams channel.
-
-Use the existing video pipeline:
-1. edge-tts for narration (voice: en-US-AndrewNeural, rate: -10%)
-2. ffmpeg for video assembly (slides + audio)
-3. youtube-upload.cjs for uploading
-
-Create 2-3 videos about recent work:
-- "How AI Agents Manage 3,000 Products Autonomously" — about the Hollywood Wallcoverings batch processing (body cleanup, commercial rooms, metafields)
-- "Collection Intelligence: From 2,000 to 500 Smart Collections" — about the collection cull, merge, rename, tier-1 creation
-
-For each video:
-1. Write a script (300-500 words) that's engaging and practical for entrepreneurs
-2. Generate narration: edge-tts --voice "en-US-AndrewNeural" --rate="-10%" --text "script" --write-media /tmp/narration.mp3
-3. Create 5-8 slide images using node-canvas or sharp (text + simple graphics)
-4. Assemble with ffmpeg: slides + narration → mp4
-5. Upload to YouTube via: cd /root/Projects/goodquestion-ai && node scripts/youtube-upload.cjs --title "Title" --description "desc" --file /tmp/video.mp4
-
-NEVER mention in videos: wallcoverings, wallpaper, Designer Wallcoverings, DW, Shopify, vendor names, IP addresses, or any blocked term from the sanitizer.
-
-YouTube OAuth tokens: /root/Projects/goodquestion-ai/.youtube-token.json
-Channel: @AgentAbrams (UCUyOiEMZPSUGUr3HCc1vE1w)
diff --git a/tasks/done/BC_delete-oversized-hw-rooms.md b/tasks/done/BC_delete-oversized-hw-rooms.md
deleted file mode 100644
index 4ab03d1..0000000
--- a/tasks/done/BC_delete-oversized-hw-rooms.md
+++ /dev/null
@@ -1,30 +0,0 @@
-Delete oversized room setting images from the first ~991 Hollywood Wallcoverings products.
-
-The hw-commercial-rooms script ran with patternWidth=54 (wrong — should be 5). This made textures appear 10x too large on walls. Need to delete these room images so they can be regenerated at correct scale.
-
-Write a script at /root/DW-Agents/scripts/delete-oversized-hw-rooms.js that:
-
-1. Query Hollywood Wallcoverings products using GraphQL pagination:
- ```graphql
- products(first: 50, query: "vendor:\"Hollywood Wallcoverings\" status:active", after: $cursor) {
- edges { node { id title images(first: 20) { edges { node { id alt src } } } } }
- pageInfo { hasNextPage endCursor }
- }
- ```
-
-2. For each product, find images with alt text containing "room setting" or "commercial room"
-3. Delete those images via REST:
- DELETE /admin/api/2024-01/products/{productId}/images/{imageId}.json
-4. Keep the original swatch image (position 1) and any spin GIF
-5. Rate limit: 500ms between deletes
-6. Log: "Product {title}: deleted {n} room images"
-7. Final: total products cleaned, total images deleted
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-After the delete script finishes, restart the fixed rooms script:
-pm2 restart hw-commercial-rooms
-
-Start with PM2:
-pm2 start /root/DW-Agents/scripts/delete-oversized-hw-rooms.js --name delete-oversized-rooms --no-autorestart
diff --git a/tasks/done/BD_dw-exclusive-page-images.md b/tasks/done/BD_dw-exclusive-page-images.md
deleted file mode 100644
index 779bb99..0000000
--- a/tasks/done/BD_dw-exclusive-page-images.md
+++ /dev/null
@@ -1,77 +0,0 @@
-Upgrade the DW Exclusive page (/pages/dw-exclusive, Shopify page ID 26778599489) with real product images, vendor logos, and room settings in every section.
-
-## Current Problem
-The page has all the right sections but uses solid color blocks and text-only links. It needs REAL images from the store.
-
-## Section-by-Section Requirements
-
-### 1. Shop by Category — Use PATTERN IMAGES
-For each of the 10 tier-1 category cards, fetch a representative product image from that collection:
-- Query Shopify: GET /admin/api/2024-01/collections/{handle}/products.json?limit=1&fields=image
-- Use the collection's own image if it has one, otherwise use the first product image
-- Replace the solid color gradient backgrounds with the product image as a CSS background
-- Keep the text overlay (category name + subtitle) with a dark gradient overlay for readability
-- Style: `background: linear-gradient(rgba(0,0,0,0.3), rgba(0,0,0,0.5)), url(IMAGE_URL); background-size: cover;`
-
-Collections to get images for:
-- commercial-contract, grasscloth-natural-fiber, murals-scenics, textures-solids
-- florals-botanicals, damask-traditional, peel-stick, sustainable-eco, residential, kids-nursery
-
-### 2. Shop by Color — KEEP AS-IS
-The color circle swatches look good. No changes needed.
-
-### 3. DW Exclusive Brands — Use BRAND LOGOS or PRODUCT IMAGES
-For each DW Exclusive brand (Architectural Wallcoverings, Phillipe Romano, Retro Walls, Malibu):
-- Check if the brand has a logo image in the Shopify collection image
-- If no logo, use a signature product image from that brand's collection
-- Display as cards with the image, brand name overlay, and subtitle
-- Dark background section stays (#1a1a2e)
-
-### 4. Popular Brands — Use VENDOR LOGOS
-Transform the text-only grid into a visual brand grid with logos:
-- Check the Shopify brands page (/pages/brands, page ID 79444574259) for existing brand logo images
-- For each of the ~40 vendors listed, try to get their logo from:
- a. The vendor's Shopify collection image
- b. The brands page HTML (which already has logo cards)
- c. If no logo available, show the brand name in a styled text card with a subtle product image background
-- Display as a 4-column grid of logo cards (image on top, brand name below)
-- Each links to /collections/{handle}
-- Logo images should be consistent size (200x100 or similar, contain/white-space)
-
-### 5. Shop by Style — Use ROOM SETTING IMAGES
-For each interior design style, show a room setting image from a product in that style's collection:
-- Styles: Art Deco, Mid Century Modern, Contemporary, Traditional, Tropical, Coastal, Bohemian, Geometric, Abstract, Chinoiserie, Toile, Damask, Ikat, Stripe, Plaid, Paisley, Lattice, Trellis
-- For each style, query Shopify for a product tagged with that style that HAS a room setting image (alt text contains "room setting")
-- If no room setting found, use a product image from that style collection
-- Display as a 3-column card grid with image + style name overlay
-- Style: same dark gradient overlay as category cards
-
-### 6. Shop by Material — Use TEXTURE CLOSE-UP IMAGES
-For each material type, show a close-up texture/pattern image:
-- Materials: Grasscloth, Vinyl (Type II), Non-Woven, Paper, Silk, Sisal, Jute, Cork, Linen, Mica, Foil, Fabric-Backed
-- Query products with that material tag, pick one with a good swatch image
-- Display as a 3 or 4 column card grid with image + material name
-
-## Implementation
-
-Write a Node.js script at /root/DW-Agents/scripts/dw-exclusive-with-images.js that:
-
-1. Fetch images for each section from Shopify API
-2. Fetch the brands page HTML to extract existing logo URLs
-3. Build the complete page HTML with real images embedded as CDN URLs (not base64)
-4. Update the Shopify page via PUT /admin/api/2024-01/pages/26778599489.json
-
-IMPORTANT:
-- Use Shopify CDN URLs for all images (https://cdn.shopify.com/...)
-- Keep the existing design aesthetic (#FAF9F6 background, Georgia headings, gold accents #C4995C)
-- Maintain responsive design (2-col desktop, 1-col mobile for categories)
-- NEVER say "Private Label" — use "DW Exclusive"
-- All brand names are OK to show (this is NOT public content — this is the storefront)
-- Keep the "View all 300+ brands" link
-
-Shopify tokens:
-- Products: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Pages: 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
-Store: designer-laboratory-sandbox.myshopify.com
-
-After running, verify by fetching the page and checking image count in the HTML.
diff --git a/tasks/done/BF_fix-collections-page-empty.md b/tasks/done/BF_fix-collections-page-empty.md
deleted file mode 100644
index 0646a31..0000000
--- a/tasks/done/BF_fix-collections-page-empty.md
+++ /dev/null
@@ -1,36 +0,0 @@
-URGENT: The /collections page on designerwallcoverings.com is EMPTY. Only shows color swatches, no collection grid below.
-
-The HTML shows 2 empty collection items with blank titles and href="". The collections exist (501 total, 0 unpublished) but the theme template isn't rendering them.
-
-## Diagnosis
-The Shopify /collections page uses a theme section (list-collections template) that either:
-1. Has specific collection IDs configured in the section settings (and those IDs were deleted by the cull task)
-2. Or the section is misconfigured
-
-## Fix Steps
-
-1. Check the theme's list-collections template. Use the Shopify Admin API:
- GET /admin/api/2024-01/themes/{theme_id}/assets.json?asset[key]=templates/list-collections.json
- or
- GET /admin/api/2024-01/themes/{theme_id}/assets.json?asset[key]=sections/list-collections-template.liquid
-
- First get the active theme ID:
- GET /admin/api/2024-01/themes.json (look for role: "main")
-
-2. Check what the section settings contain — look for collection IDs that may reference deleted collections
-
-3. Fix the section settings to either:
- a. Use "all" collections mode (show all published collections)
- b. Or configure the specific collection IDs for the top collections
-
-4. If can't access theme API, use an alternative approach:
- - Check /root/Projects/Designer-Wallcoverings/ for theme files
- - Look for templates/list-collections.json or sections/list-collections-template.liquid
- - Fix the section configuration
-
-5. Verify the page shows collections after fix
-
-Shopify tokens:
-- Products: <redacted:SHOPIFY_ADMIN_TOKEN>
-- Pages/Content: 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
-Store: designer-laboratory-sandbox.myshopify.com
diff --git a/tasks/done/BG_rollback-product-types.md b/tasks/done/BG_rollback-product-types.md
deleted file mode 100644
index 4cc37e3..0000000
--- a/tasks/done/BG_rollback-product-types.md
+++ /dev/null
@@ -1,49 +0,0 @@
-URGENT: Rollback incorrect product_type changes made by standardize-types and fix-types-by-width scripts.
-
-## What Went Wrong
-The standardize-types script changed 14,449 products with WRONG rules:
-- Width < 20" → set to "Fabric" (WRONG — many wallcoverings are under 20")
-- Width >= 48" → set to "Commercial Wallcoverings" (WRONG — 54" can be fabric upholstery too)
-- Removed "Peel & Stick" and "Mural" types (correct per Steve)
-
-## Correct Product Type Taxonomy (ONLY these 5):
-1. **Wallcovering** — residential wall products of any width
-2. **Commercial** — Type II/III vinyl, contract grade. Determined by VENDOR and MATERIAL, not width alone. Most commercial AND upholstery fabrics are 54".
-3. **Naturals** — Grasscloth, sisal, jute, cork, abaca, seagrass, hemp, linen
-4. **Fabric** — Actual upholstery/drapery fabric. NOT wallcovering.
-5. **Trim** — Borders, trims, accessories
-
-## Rules for Classification:
-- Width alone NEVER determines type
-- 54" wide = could be Commercial wallcovering OR Fabric upholstery — check vendor/material
-- Known commercial VENDORS (Koroseal, Designtex, Wolf Gordon, Versa, Hollywood WC, Carnegie, Arc-Com): → "Commercial"
-- Known fabric VENDORS (Kravet Fabrics, Lee Jofa Fabrics): → "Fabric"
-- Material says "Vinyl", "Type II", "PVC", "Polyurethane": → "Commercial"
-- Material says "Grasscloth", "Sisal", "Jute", "Cork", "Abaca", "Seagrass": → "Naturals"
-- "Peel & Stick" is NOT a type — those are just "Wallcovering"
-- "Mural" is NOT a type — those are just "Wallcovering"
-
-## Fix Script
-
-Write /root/DW-Agents/scripts/rollback-product-types.js that:
-
-1. Query ALL active products from Shopify with their current product_type
-2. For products that were changed to "Fabric" incorrectly (was "Wallcoverings" or "Wallcovering"):
- - If vendor is in fabric vendor list → keep as "Fabric"
- - Otherwise → set back to "Wallcovering"
-3. For products set to "Commercial Wallcoverings":
- - If vendor is in commercial vendor list OR material contains vinyl/Type II → change to "Commercial"
- - If vendor is NOT commercial → set to "Wallcovering"
-4. For "Natural Wallcovering" → change to "Naturals"
-5. For "Mural" → change to "Wallcovering"
-6. For "Peel & Stick" → change to "Wallcovering"
-7. For "Accessory" → change to "Trim"
-8. Rate limit 500ms between mutations
-9. Log every change
-10. Report summary
-
-Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Store: designer-laboratory-sandbox.myshopify.com
-
-Start with PM2:
-pm2 start /root/DW-Agents/scripts/rollback-product-types.js --name rollback-types --no-autorestart
diff --git a/tasks/done/BH_steve-command-dashboard.md b/tasks/done/BH_steve-command-dashboard.md
deleted file mode 100644
index 44c1048..0000000
--- a/tasks/done/BH_steve-command-dashboard.md
+++ /dev/null
@@ -1,127 +0,0 @@
-Build Steve's Command Center — a real-time monitoring dashboard at port 9700.
-
-## Stack
-- Single file: /root/DW-Agents/steve-command/server.js
-- Express + better-sqlite3 (for task history)
-- Chart.js via CDN (no build step)
-- Auto-refresh every 5 seconds
-- Auth: admin / DWSecure2024!
-- PM2 name: steve-command
-
-## Design — ADHD-Friendly Dark Dashboard
-- Background: #0a0a0f
-- Cards: #111118 with subtle #1a1a2a borders
-- Gold accent: #C4995C
-- Large text (16-18px base), high contrast
-- Status dots: green=running, gold=queued, red=failed, gray=stopped
-- NO clutter, NO sidebars — single scrollable page
-- Font: system-ui (fast loading)
-
-## Page Layout (top to bottom)
-
-### 1. Header Bar
-- "Steve's Command Center" title (gold, 28px)
-- Server health pills inline: CPU load, RAM used/total, Disk used/total, PM2 online count
-- All from /api/health endpoint
-
-### 2. Command Input
-- Large textarea (18px, gold border, 60px tall): "What do you need done?"
-- Submit button "GO" (gold background)
-- POST to /api/task → saves to SQLite, queues in YOLO agent
-
-### 3. Active Jobs (live cards)
-- Pull from PM2 jlist — show only ONLINE processes that match active work patterns
-- Each card: name, uptime, last log line, restart count
-- Progress bar if log contains "X/Y" pattern (parse it)
-- Color-coded left border by status
-- Auto-refresh every 5 seconds via fetch
-
-### 4. YOLO Queue & History
-- Current task (highlighted gold border)
-- Queue list below
-- Last 10 completed tasks with status badge (success/failed) and duration
-- Pull from http://127.0.0.1:9670/api/status and /api/history (auth: admin:DWSecure2024!)
-
-### 5. Task Timeline Chart
-- Chart.js line chart
-- X axis: time (last 24 hours)
-- Y axis: tasks completed
-- Two lines: success (green) and failed (red)
-- Data from YOLO history timestamps
-
-### 6. Server Health Gauges
-- 3 donut/gauge charts (Chart.js doughnut):
- - CPU load (0-100%, from os.loadavg)
- - RAM usage (used/total, from os.freemem/totalmem)
- - Disk usage (used/total, from df)
-- Color: green < 60%, gold 60-80%, red > 80%
-
-### 7. Claude Sessions
-- List active Claude CLI processes (from ps aux | grep claude)
-- Show PID, start time, what they're doing (args)
-- Kill button for each (POST /api/kill-claude/:pid)
-
-## API Endpoints
-
-```
-GET / — Dashboard HTML
-GET /api/health — { cpu, ram, disk, pm2Online, uptime }
-GET /api/active-jobs — Active PM2 jobs with last log line
-GET /api/yolo — YOLO status + queue + last 20 history
-GET /api/claude-sessions — Active Claude CLI processes
-POST /api/task — Submit task { prompt } → queue in YOLO
-POST /api/kill-claude/:pid — Kill a Claude process
-GET /api/chart-data — Task completion data for charts (last 24h)
-```
-
-## Implementation Details
-
-### /api/health
-```javascript
-const os = require('os');
-const { execSync } = require('child_process');
-// CPU: os.loadavg()[0] / os.cpus().length * 100
-// RAM: (os.totalmem() - os.freemem()) / os.totalmem() * 100
-// Disk: parse `df -h / | tail -1`
-// PM2: parse `pm2 jlist` count online
-```
-
-### /api/active-jobs
-```javascript
-// Run: pm2 jlist (parse JSON)
-// Filter: status === 'online' AND name matches work patterns
-// For each: get last log line via pm2 logs --nostream --lines 1
-// Parse progress: match /(\d+)\/(\d+)/ in log line
-```
-
-### /api/yolo
-```javascript
-// Fetch: http://127.0.0.1:9670/api/status (with auth header)
-// Fetch: http://127.0.0.1:9670/api/history
-// Return combined
-```
-
-### Dashboard HTML
-- ALL HTML/CSS/JS inline in server.js as template string
-- Chart.js loaded from CDN: https://cdn.jsdelivr.net/npm/chart.js@4
-- Use fetch() with setInterval(5000) for auto-refresh
-- DOM updates via getElementById, NOT innerHTML (XSS safe)
-- Use textContent for all user-generated content
-
-### SQLite (tasks.db)
-```sql
-CREATE TABLE IF NOT EXISTS tasks (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- prompt TEXT NOT NULL,
- status TEXT DEFAULT 'queued',
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
- yolo_task_name TEXT
-);
-```
-
-## After Building
-1. Install deps: cd /root/DW-Agents/steve-command && npm init -y && npm install express better-sqlite3
-2. Start: pm2 start server.js --name steve-command
-3. Firewall: sudo ufw allow from 76.33.146.135 to any port 9700 proto tcp
-4. Verify: curl -u admin:DWSecure2024! http://localhost:9700/api/health
-5. Open: http://45.61.58.125:9700/
diff --git a/tasks/done/GATE.md b/tasks/done/GATE.md
deleted file mode 100644
index cbb176a..0000000
--- a/tasks/done/GATE.md
+++ /dev/null
@@ -1,21 +0,0 @@
-FROZEN until core_complete > 50,000 (currently 16,108 as of 2026-03-24)
-
-Check progress:
-```sql
-SELECT COUNT(*) FROM enrichment_tracking WHERE phase2_specs_at IS NOT NULL AND phase3_ai_at IS NOT NULL AND phase8_shopify_at IS NOT NULL;
-```
-
-## Frozen Tasks
-- 00a_brewster-line-manager.md — New line catalog manager
-- 00b_designtex-scraper-fix.md — New vendor scraper fix
-- 21_knoll-maharam-scraper-investigate.md — New vendor scraper investigation
-- 00_FREEZE-no-new-vendors.md — Original freeze directive
-
-## What's NOT frozen
-- Enrichment tasks (specs, AI, hex colors)
-- Push-to-Shopify tasks
-- Fix/completion tasks
-- Room settings, blog, social
-
-## Unfreeze when
-core_complete > 50,000
diff --git a/tasks/done/HF1_retire-hollywood-add-pricing.md b/tasks/done/HF1_retire-hollywood-add-pricing.md
deleted file mode 100644
index 4f2a72d..0000000
--- a/tasks/done/HF1_retire-hollywood-add-pricing.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# HF1 — Retire `hollywood-add-pricing.py` (replace with error stub)
-
-**Context**: On 2026-04-11 we discovered that 98 active Hollywood Wallcoverings DWHD products on live Shopify were underpriced. The root cause investigation found `/root/DW-Agents/vendor-scrapers/hollywood-add-pricing.py` has a bug on line 84 — it uses `price_trade` verbatim as the selling price despite the docstring claiming to apply a markup formula `(momentum * 0.80) / 0.65 / 0.85`. The script also violates **Rule 23** (mandatory GraphQL via Quinn queue) by hitting Shopify REST directly.
-
-Although this specific script didn't set the DWHD $40.55 defaults (it filters `WHERE mfr_sku LIKE 'X%'` which doesn't match DWHD), it is the same bug family and should not be run again.
-
-**Goal**: Retire the script so nobody accidentally re-runs it.
-
----
-
-## Steps (dry-run first, manual approval gate)
-
-1. **Read** `/root/DW-Agents/vendor-scrapers/hollywood-add-pricing.py` and confirm line 84 still has:
- ```python
- yard_price = f"{float(price_trade):.2f}"
- ```
- If line 84 has changed, STOP and report — something has already been modified.
-
-2. **Dry-run**: Print the planned actions without touching files:
- - Move `/root/DW-Agents/vendor-scrapers/hollywood-add-pricing.py` → `/root/DW-Agents/vendor-scrapers/_archived/hollywood-add-pricing-DEPRECATED.py`
- - Create new stub file `/root/DW-Agents/vendor-scrapers/hollywood-add-pricing.py` with content:
- ```python
- #!/usr/bin/env python3
- """DEPRECATED — DO NOT USE.
-
- This script used Shopify REST directly (Rule 23 violation) and had a
- list-vs-retail pricing bug on the original line 84 that caused 98 live
- Hollywood products to be underpriced. Archived 2026-04-11.
-
- Use scripts/hollywood-fix-yard-prices.js as the reference pattern for
- Quinn+GraphQL variant price updates.
- """
- import sys
- print("ERROR: hollywood-add-pricing.py is RETIRED. See docstring.", file=sys.stderr)
- sys.exit(1)
- ```
-
-3. **Stop and wait for manual approval**. Output a summary of proposed file operations and the literal diff. Do NOT actually move the file or write the stub.
-
-4. When re-queued with the token `--execute` present in the task content, perform the move and the stub write.
-
-5. **Verify**: After execution, run `python3 /root/DW-Agents/vendor-scrapers/hollywood-add-pricing.py` — should exit non-zero with the error message.
-
-## Success criteria
-- Original script moved to `_archived/` with `DEPRECATED` suffix
-- Stub in place, errors on import
-- Archived copy still readable for historical reference
-
-## Rollback plan
-If anything goes wrong, `git checkout HEAD -- vendor-scrapers/hollywood-add-pricing.py` restores the original. The archived copy can be deleted.
-
-## DO NOT
-- Touch any other file in vendor-scrapers/
-- Modify the archived copy
-- Run git commit — that's for the commit agent (Scribe) to handle
diff --git a/tasks/done/HF2_hunt-mystery-dwhd-pricing-script.md b/tasks/done/HF2_hunt-mystery-dwhd-pricing-script.md
deleted file mode 100644
index 0726dc8..0000000
--- a/tasks/done/HF2_hunt-mystery-dwhd-pricing-script.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# HF2 — Hunt the script that set DWHD $40.55 per-yard defaults
-
-**Context**: On 2026-04-11 we audited 109 Hollywood DWHD multi-variant products on live Shopify and found that 98 had the wrong per-yard price. The pattern: **36 products are stuck at exactly $40.55**, and others cluster at wrong values that match Momentum LIST prices (wholesale) instead of RETAIL.
-
-`hollywood-add-pricing.py` has a similar bug but filters `WHERE mfr_sku LIKE 'X%'` — it couldn't have set the DWHD prices. **Another script is still out there** that set these defaults. Finding it is critical because it could re-run and undo the fix we just shipped.
-
-**Goal**: Read-only hunt. Find the script. Report findings. Do NOT modify anything.
-
----
-
-## Investigation steps
-
-1. **Grep 1**: Find all files that create or update Shopify variants with a hardcoded `40.55` or any variant of that number:
- ```
- grep -rE "40\.55|40_55|4055" /root/DW-Agents --include="*.js" --include="*.py" \
- --exclude-dir=node_modules --exclude-dir=_archived --exclude-dir=logs
- ```
-
-2. **Grep 2**: Find all files that touch DWHD products with variant creation/update:
- ```
- grep -rE "DWHD.*productVariant|DWHD.*variant.*price|productVariant.*DWHD" \
- /root/DW-Agents --include="*.js" --include="*.py" \
- --exclude-dir=node_modules --exclude-dir=_archived --exclude-dir=logs
- ```
-
-3. **Grep 3**: Find scripts that read `hollywood_catalog.price_trade` or similar:
- ```
- grep -rE "hollywood_catalog|price_trade.*SELECT|SELECT.*price_trade" \
- /root/DW-Agents --include="*.js" --include="*.py" \
- --exclude-dir=node_modules --exclude-dir=_archived
- ```
-
-4. **Grep 4**: Find scripts that call productVariantsBulkCreate or Sold Per Yard creation for DWHD:
- ```
- grep -rE "productVariantsBulkCreate|Sold Per Yard|Per Yard.*yard_price" \
- /root/DW-Agents --include="*.js" --include="*.py" \
- --exclude-dir=node_modules --exclude-dir=_archived
- ```
-
-5. **DB check**: Query `shopify_api_queue` for any historical completed jobs that touched these product IDs. Look for `source_agent` values and approximate dates:
- ```sql
- SELECT source_agent, COUNT(*), MIN(created_at), MAX(created_at)
- FROM shopify_api_queue
- WHERE payload::text LIKE '%DWHD-502872%' OR payload::text LIKE '%Asilomar%'
- GROUP BY source_agent ORDER BY COUNT(*) DESC;
- ```
-
-6. **Git blame**: For any suspicious file found, run `git log --oneline -- <file>` to see when it was last touched. The $40.55 default must have been set recently enough to affect live products.
-
-## Deliverable
-
-Write a markdown report to `/root/DW-Agents/yolo-agent/logs/hf2-mystery-script-report.md` with:
-- List of candidate files (file + line numbers)
-- For each candidate, a 1-sentence verdict: "YES this set the prices", "MAYBE", or "NO (why)"
-- If a confirmed culprit is found, paste the offending line + the shebang/module header
-- Recommendation: next action (retire, fix, or leave alone)
-
-## DO NOT
-- Modify any file
-- Run the suspicious scripts
-- Hit any live Shopify endpoint
-- Touch the database (read-only queries OK)
-
-## Success criteria
-- Report file exists with at least 3 candidate files examined
-- At least one confirmed verdict
-- No writes to anything
diff --git a/tasks/done/HF3_regression-verify-98-fixed-prices.md b/tasks/done/HF3_regression-verify-98-fixed-prices.md
deleted file mode 100644
index 4c224fb..0000000
--- a/tasks/done/HF3_regression-verify-98-fixed-prices.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# HF3 — Regression: verify the 98 Hollywood fixed prices are still correct
-
-**Context**: On 2026-04-11, Quinn jobs 794072-794076 corrected the per-yard prices of 98 Hollywood DWHD products from the Momentum LIST price to the RETAIL price. All 5 jobs returned HTTP 200 with zero userErrors, and 5 spot-checks on live Shopify confirmed the updates.
-
-This task verifies **nothing has regressed**. If an unknown script re-ran and reset the prices, we need to catch it fast.
-
-**Run this task 24h after the original fix, then weekly thereafter.**
-
----
-
-## Steps
-
-1. **Read** `/root/.claude/projects/-root/memory/` and `/root/DW-Agents/scripts/hollywood-fix-yard-prices.js` to understand the fix context.
-
-2. **Query** the `hollywood_price_audit_log` table for all rows from the 2026-04-11 fix:
- ```sql
- SELECT dw_sku, product_id, variant_id, new_price, mm_pattern, mm_alt_sku
- FROM hollywood_price_audit_log
- WHERE source_script = 'hollywood-fix-yard-prices.js'
- AND action = 'update'
- ORDER BY dw_sku;
- ```
- This gives you the expected state: {dw_sku → expected new_price}.
-
-3. **Verify against live Shopify** — for each audit row, query the variant on live Shopify and confirm the price is still the expected value (within $0.01 tolerance):
- - Use GraphQL (NOT REST). Batch 20 variants per `nodes(...)` query.
- - Product token: `<redacted:SHOPIFY_ADMIN_TOKEN>`
- - Endpoint: `https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json`
-
-4. **Categorize** each product:
- - **OK**: live price matches audit log
- - **REGRESSED**: live price differs (report: dw_sku, expected, actual, delta)
- - **MISSING**: variant no longer exists on live Shopify (report: dw_sku, note)
- - **ARCHIVED**: product status is ARCHIVED (expected for discontinued — not a regression)
-
-5. **Report**: Write `/root/DW-Agents/yolo-agent/logs/hf3-regression-report-YYYY-MM-DD.md` with:
- - Total count of each category
- - Full list of REGRESSED products (critical)
- - If any REGRESSED, recommend: "Investigate what script re-ran and corrupted the prices. See HF2 hunt task."
-
-6. **Alert**: If the REGRESSED count is > 0, append a line to `/root/DW-Agents/logs/alerts.log`:
- ```
- YYYY-MM-DD HH:MM PT | HOLLYWOOD PRICE REGRESSION | N products reverted
- ```
-
-## DO NOT
-- Modify any prices
-- Hit Shopify via REST
-- Write to vendor_catalog or any product table
-- Push to Quinn queue (this is a read-only verification task)
-
-## Success criteria
-- Report file exists with counts for OK/REGRESSED/MISSING/ARCHIVED
-- Live Shopify was queried (not the stale `shopify_products` table)
-- Any regression is flagged in `/root/DW-Agents/logs/alerts.log`
-- 0 writes to any table
diff --git a/tasks/done/YL1_logo-contamination-cleanup.md b/tasks/done/YL1_logo-contamination-cleanup.md
deleted file mode 100644
index bec7668..0000000
--- a/tasks/done/YL1_logo-contamination-cleanup.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# YL1 — Logo-contamination image cleanup (Newmor, PJ, William Morris, et al.)
-
-**Source**: YOLO Overnight Vendor Audit Phase 4 (2026-04-11) — Gemini cover-vs-title audit on 62/80 vendors.
-
-**Finding**: Three vendors have brand-logo thumbnails instead of actual product images, in direct violation of rule `feedback_gemini_reject_logos.md` (Phase 3 AI should have rejected these at import).
-
-| Vendor | Mismatch rate | Sample count | Nature |
-|---|---|---|---|
-| Newmor Wallcoverings | **93.3%** | 14/15 | All sampled commercial wallcoverings show the Newmor logo instead of the pattern |
-| Phillip Jeffries | 86.7% | 13/15 | Brand text "PHILLIP JEFFRIES" in brown serif instead of the pattern (PJ is browse-hidden but search still shows thumbnail) |
-| William Morris | 33.3% | 5/15 | Morris & Co logo instead of the pattern on Snakeshead, Wandle, Pimpernel, Golden Lily, Arbutus |
-
-MED risk (also worth investigating): Donghia 13.3%, Surface Stick 13.3%, AS Creation 12.5%, Brand McKenzie 13.3%.
-
-**Caveat**: Phase 4 stopped at vendor [62/80] (stuck on Ralph Lauren in `do_select` syscall, probably a hung image fetch). Vendors 63-80 haven't been audited yet — Schumacher, Kravet, Arte International, Koroseal, etc. may have similar contamination.
-
-## Steps
-
-1. **Identify affected products per vendor** via GraphQL (read-only):
- - Pull each vendor's product list with cover image URLs
- - Filter products where the cover image URL contains words like `logo`, `brand`, `header`, `banner`, or where the image is below a size threshold (real pattern swatches are usually >500KB; logos are usually tiny)
- - Run each candidate through Gemini Vision with the prompt: `"Does this image show a wallcovering product pattern, or is it a brand logo / text / banner? Respond JSON: {\"is_logo\": true|false, \"why\": \"...\"}"`
-
-2. **For each confirmed logo-contaminated product**, look up the original product URL and re-scrape the image from the vendor site:
- - Newmor: https://www.newmor.com (native manufacturer site)
- - Phillip Jeffries: scrape blocked per vendor rules, use PJ dealer portal if available
- - William Morris: https://www.williammorris.com or https://william-morris.co.uk
-
-3. **Push replacement images** via Quinn queue using `productCreateMedia` + `productDeleteMedia` (GraphQL batched 25 per call, Rule 23).
- - Tag each updated product with `image-fix-2026` so we can verify later.
-
-4. **Re-run Phase 4** on the fixed vendors to confirm mismatch rate drops below 5%.
-
-## DO NOT
-
-- Hit Shopify via REST. Use Quinn + GraphQL only (Rule 23).
-- Delete the old logo image before the replacement is confirmed uploaded — avoid empty-media products.
-- Scrape Phillip Jeffries (vendor rules prohibit it). PJ may need manual image provision from Steve.
-- Re-enrich PJ with AI tags (rule: PJ gets no AI tags).
-- Mass-update more than 100 products without Steve's review — this is a quality cleanup, not a bulk push.
-
-## Success criteria
-
-- Newmor, PJ, William Morris each re-audit at <5% logo-contamination rate
-- Artifacts: a per-vendor fix log at `/root/DW-Agents/yolo-agent/logs/yl1-logo-fix-{vendor}.jsonl`
-- Alert to Steve via Slack when each vendor completes
-
-## Related
-
-- Rule: `feedback_gemini_reject_logos.md` — Phase 3 AI must detect and reject logo images at import time (this task fixes the existing damage; a separate effort should harden the Phase 3 validator)
-- Memory: `project_yolo_vendor_audit_2026_04_11.md` — original audit context and Phase 4 findings
-- Artifact: `/tmp/yolo_phase4_cover_audit.jsonl` — the 62-vendor raw audit data
diff --git a/tasks/done/hawke-monitor.md b/tasks/done/hawke-monitor.md
deleted file mode 100644
index 8393d91..0000000
--- a/tasks/done/hawke-monitor.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Hawke High-Low Agent — Monitor & Maintain
-
-Check the Hawke price intelligence agent (port 9800, PM2: hawke).
-
-## Steps
-
-1. **Health**: `timeout 10 pm2 list 2>&1 | grep hawke` — must show online. If not: `pm2 restart hawke`
-2. **Memory**: If mem >500MB, `pm2 restart hawke` (Chromium leak)
-3. **Workers**: `timeout 10 curl -sS -u admin:DWSecure2024! http://localhost:9800/api/workers` — 10 workers, check errors
-4. **Samples**: `timeout 10 psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "SELECT COUNT(*) AS samples, COUNT(DISTINCT dw_sku) AS skus, COUNT(DISTINCT competitor_id) AS competitors FROM hawke_price_samples;"`
-5. **Queue**: `timeout 10 curl -sS -u admin:DWSecure2024! http://localhost:9800/api/queue-stats`
-6. **Backfill mfr_sku from fmprocsv** if >50 missing:
- ```sql
- WITH fm AS (SELECT q.dw_sku, f.mfr_sku AS m FROM hawke_crawl_queue q JOIN master_fmpro f ON REPLACE(q.dw_sku,'-','') ILIKE f.sku||'%' WHERE (q.mfr_sku IS NULL OR q.mfr_sku='') AND f.mfr_sku IS NOT NULL AND f.mfr_sku!='') UPDATE hawke_crawl_queue q SET mfr_sku=fm.m FROM fm WHERE q.dw_sku=fm.dw_sku;
- ```
-7. **Report**: `[Hawke] samples=N skus=N competitors=N queue=N workers=N/10 errors=N`
-
-Use `timeout 30` on all commands. Never modify crawler code.
diff --git a/tasks/done/now_1774493279502.md b/tasks/done/now_1774493279502.md
deleted file mode 100644
index c8e5cf9..0000000
--- a/tasks/done/now_1774493279502.md
+++ /dev/null
@@ -1 +0,0 @@
-Run all 5 pending tasks in /root/DW-Agents/yolo-agent/tasks/pending/ sequentially. Start with 01_resume-pointe-updater.md (P0), then 02, 03, 04, 05. Move each to done/ when complete. Log progress to /root/DW-Agents/logs/yolo-overnight.log
\ No newline at end of file
diff --git a/tasks/done/repeat-requeue-log.md b/tasks/done/repeat-requeue-log.md
deleted file mode 100644
index 47f9041..0000000
--- a/tasks/done/repeat-requeue-log.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-## Round 1 → Round 2 Requeue (2026-04-01)
-
-### Remaining Missing Repeats:
-| Vendor | Remaining | Total on Shopify | Status |
-|--------|-----------|------------------|--------|
-| Thibaut | 2,071 | 4,108 | R2 queued (task 21) |
-| Kravet | 0 | 5,565 | ✅ COMPLETE |
-| Schumacher | 79 | 2,282 | R2 queued (task 22) |
-| Brewster | 264 | 655 | R2 queued (task 23) |
-| York | 289 | 691 | R2 queued (task 23) |
-| Versace | 0 | 136 | ✅ COMPLETE |
-
-### R2 Tasks Created:
-- 21_scrape-repeats-thibaut-r2.md (LIMIT 500)
-- 22_scrape-repeats-schumacher-r2.md (all 79)
-- 23_scrape-repeats-brewster-york-r2.md (500 each)
-- 24_push-repeats-to-shopify-r2.md
-- 25_zero-repeat-textures-r2.md (Gemini vision for unknowns)
-- 26_repeat-loop-requeue-r2.md (auto-requeue for R3)
-
-Next round queued: YES (4 vendors with remaining > 10)
diff --git a/tasks/done/york-bye-bye-overnight.md b/tasks/done/york-bye-bye-overnight.md
deleted file mode 100644
index b3414d5..0000000
--- a/tasks/done/york-bye-bye-overnight.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# YorkByeBye — Overnight York Product Discontinuation Check
-
-## Task
-Monitor the running YorkByeBye checker (PID 957202) that's crawling 9,448 York products on yorkwall.com. When it completes, generate a summary report and commit results.
-
-## What's Already Running
-- Checker: `node /root/DW-Agents/skills/york-bye-bye/checker.js --limit 9448 --offset 0`
-- PID: 957202
-- Dashboard: http://45.61.58.125:9760
-- Results CSV: /root/DW-Agents/skills/york-bye-bye/results.csv
-- Log: /root/DW-Agents/skills/york-bye-bye/checker.log
-
-## Steps
-1. Check if PID 957202 is still running: `ps -p 957202 -o pid,cmd --no-headers`
-2. If still running, check progress: `tail -5 /root/DW-Agents/skills/york-bye-bye/checker.log`
-3. If finished, generate summary:
- - Count CURRENT vs DISCONTINUED vs NOT_FOUND vs ERROR from results.csv
- - List all Shopify products that were archived
- - Calculate total checked, time taken
-4. Save summary to `/root/DW-Agents/skills/york-bye-bye/SUMMARY.md`
-5. Git commit results: `cd /root/DW-Agents && git add skills/york-bye-bye/ && git commit -m "YorkByeBye: overnight run complete — [X] discontinued, [Y] archived"`
-
-## Do NOT
-- Restart the checker if it's still running
-- Modify the checker script while it's running
-- Ask questions — this is autonomous
← 02bfcb0 chore: untrack stray .bak/.pre-* backups + add gitignore pat
·
back to Yolo Agent
·
chore: macstudio3 migration — reconcile from mac2 + repoint 2b7ab10 →