{"slug":"NEW-SKU-Viewer","total":7,"limit":100,"offset":0,"since":null,"commits":[{"hash":"d99c7a1","date":"2026-05-19 21:53:18 -0700","author":"Steve Abrams","subject":"fix(server): gate auto-YOLO-mode behind YOLO_MODE_AUTOSTART env var","body":"Previously every pm2 restart unconditionally fired startYoloMode() 60s\nafter boot — a 10-cycle 30-min-interval scan loop hammering\nhttp://45.61.58.125:9830/api/import/auto-scraper with zero operator\nconsent. Combined with the unauthed POST /api/yolo-mode bound to\n0.0.0.0:3030, this meant every restart silently launched a multi-hour\nscrape against prod.\n\nNow gated behind YOLO_MODE_AUTOSTART=1 env var. POST /api/yolo-mode still\nworks for manual on-demand triggering. Default is OFF.\n\nSurfaced by tick 177 session #102 Teal mechanical refactor pass.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"098e8b6","date":"2026-05-19 21:20:29 -0700","author":"Steve Abrams","subject":"public: wire sort <select> + density slider with localStorage persistence","body":"Standing rule — every grid page (admin tools included) must have a sort\nselect + density range slider, both persisted to localStorage, both\nhydrated BEFORE the first API call.\n\n- Sort options: Newest (default) / Most Products / Vendor (SKU prefix)\n  A→Z / Series A→Z / SKU A→Z / Title A→Z / Status (working first).\n- Density slider: 220-520px card-min, driving --card-min CSS var on\n  .vendor-grid's auto-fill minmax.\n- hydrateGridControls() runs before loadVendorData(), and the first\n  fetch is /api/vendors?sort=<saved> so the server returns the\n  pre-sorted list.\n- Removed the hardcoded client-side .sort by products in renderVendors\n  so the server-side sort actually wins.\n\nKeys: newSkuViewer.grid.sort, newSkuViewer.grid.density.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"b85fbce","date":"2026-05-19 21:19:12 -0700","author":"Steve Abrams","subject":"server: 404-guard snapshot paths + server-side sort on /api/vendors","body":"- Middleware before express.static returns 404 for any request whose\n  path matches *.bak / *.bak.* / *.pre-* / *.orig / *.rej / *.swp / ~,\n  so stray editor snapshots never serve from public/.\n- /api/vendors now honors a ?sort= query param (products|newest|vendor|\n  series|sku|title|status) so the grid can be sorted server-side per\n  the standing \"every grid gets sort + density\" rule. Frontend wiring\n  in a follow-up commit.\n\nnode -c server.js passes.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"de911b9","date":"2026-05-19 21:19:00 -0700","author":"Steve Abrams","subject":"public: add rel=noopener noreferrer to product-modal external link","body":"The only target=_blank anchor in the page (product modal \"View Product\"\nlink) was missing rel=noopener noreferrer. Closes window.opener and\nstrips Referer for the popup.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"6c6b864","date":"2026-05-19 21:18:49 -0700","author":"Steve Abrams","subject":"gitignore: broaden snapshot patterns (*.bak.*, *.pre-*, *.orig, *.rej, *.swp, *~)","body":"Existing rule only covered `*.bak`. Broaden so common editor/refactor\nsnapshot files never get committed.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"64c43a4","date":"2026-05-13 08:57:58 -0700","author":"Steve","subject":"snapshot: 1 file(s) changed, ~1 modified","body":""},{"hash":"f79f412","date":"2026-05-06 10:25:05 -0700","author":"Steve Abrams","subject":"initial scaffold (gitify-all 2026-05-06)","body":""}]}