{"slug":"dw-rotation-activator","total":39,"limit":100,"offset":0,"since":null,"commits":[{"hash":"90be0b4","date":"2026-09-26 09:22:12 -0700","author":"Steve","subject":"TK-11856 rc-propagation (SHOPIFY): rotation-activator drain.sh exits with activator rc (then prune rc)","body":"Exit-code ONLY, no behavior change (Steve ruling 2026-09-26 Q1/Q2): every step still runs as\nbefore; the wrapper now exits with the first nonzero rc of its side-effecting step(s) instead of\nswallowing it, so cron-fire-canary's exit corroboration can see a failed run. No heartbeat file added.\nRevert: git -C /Users/macstudio3/Projects/dw-rotation-activator revert <this sha>\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01YaiWjfgEuKQLTb5cdDreS3"},{"hash":"f16fc54","date":"2026-09-22 15:44:48 -0700","author":"Steve","subject":"TK-11786: mirror tokiwa/tokiwa usa into leakGuard PL_LEAK_HARD (sync with denylist.json)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01PH1rVLG7Nvp8EcmaARzAv4"},{"hash":"ce4a6ca","date":"2026-09-22 12:27:24 -0700","author":"Steve Abrams","subject":"leakGuard: mirror mdc/mdcwall/mdc wall/as creation denylist terms (TK-12015)","body":"Mirror of the canonical dw-leak-scanner denylist change. mdcwall/mdc wall/as creation\n-> PL_LEAK_HARD (unambiguous, title+vendor); bare 'mdc' -> PL_LEAK_VENDOR_ONLY (short\nacronym, block only when the source vendor is MDC). 'astek' intentionally omitted\n(real customer-facing brand; per-SKU /curate -> Phillipe Romano is Steve-gated).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session"},{"hash":"9f91f6c","date":"2026-09-11 11:55:26 -0700","author":"Steve Abrams","subject":"TK-11471: weight go-live gate on the rotation activator (highest blast radius in the fleet)","body":"This is the biggest unguarded path found in the TK-11414 lineage, and it was missed by\nevery prior sweep — including mine. com.steve.dw-rotation-activator fires HOURLY at :25\nand rotate-activate.js is a GLOBAL DRAFT->ACTIVE funnel: lib/rotation-order.js queries\n`WHERE status = 'DRAFT'` across the whole shopify_products catalog, not scoped to any\nvendor, capped at 500/day, against a queue of ~1,396. `grep -rn weight` over the entire\nrepo returned ZERO hits. Its documented \"5-field gate\" (sample variant, sellable variant,\nprice>0, description, >=2 tags, width) simply had no weight condition.\n\nIt is currently activating ~0/run only because an unrelated mfr-provenance gate is\nblocking 1,393 of the queue. That is not safety — it is a coincidence, and clearing that\nbacklog is the drain's explicit purpose. The moment it clears, this flips thousands of\nproducts ACTIVE with no weight check, independent of every creator this ticket fixed.\n\n- lib/weight-gate.js: self-contained (same decoupling rationale as this repo's\n  private-label leak guard, DTD verdict B 2026-07-21 — a ~/Projects app does not\n  cross-import another app's tree). Constants identical to the canonical\n  designerwallcoverings/scripts/lib/weight-guard.mjs and lib/weight_guard.py.\n  SAMPLES COUNT (43 of the 83 live offenders were samples); the .mjs's sample-filtering\n  zeroWeightBlockers() bug is deliberately not reproduced.\n  FAIL-SAFE: a response that does not carry the weight FIELD is a failure, not a pass —\n  a future query regression that drops the field cannot silently re-open the hole behind\n  a green gate.\n- rotate-activate.js: STATUS_Q now selects inventoryItem measurement weight value+unit\n  (without this the gate would measure nothing and pass 100%), productType added, and\n  weight.ok ANDed into `passes` alongside gate/extra/leak/mfr. HOLDs rather than\n  self-heals, matching every other gate here: this script activates, it is not a creator.\n\ntest/weight-gate.test.js — 16/16 PASS, offline. Includes 4 structural checks that the\ngate cannot be vacuous (query really selects weight; weight.ok really reaches `passes`;\ngate precedes the ACTIVATE call site; constants have not drifted from the canonical .mjs)\nand unit-conversion cases so it is not trigger-happy. Mutation-verified RED on scratch\ncopies: strip weight from STATUS_Q -> 1 failure; drop weight.ok from `passes` -> 1\nfailure; reintroduce the sample-filtering bug -> 2 failures.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01FJHxAzaEMMxado57mFjiCk"},{"hash":"1e149c4","date":"2026-09-10 13:13:43 -0700","author":"Steve Abrams","subject":"fix: remove a NUL byte I introduced in 7e0039b's cache key","body":"7e0039b's vendorScopeFor() cache key was written as `${table}<NUL>${vendor}` — a literal\n0x00 landed where a separator was intended. Node parsed it fine and the function behaved\ncorrectly, so it passed `node --check` and the behavioural test, but the byte made the\nwhole file read as `data` rather than text.\n\nThat is worse than it sounds: `grep` treats a binary file as unmatched and exits 1, so\n`grep -c vendorScopeFor` reported ZERO on a file that plainly contained it. Any future\naudit grepping this file for the scoping fix would have concluded it was missing. The\nprevious committed version was clean UTF-8; the corruption entered with my change.\n\nSeparator is now `::`. Verified after: file is UTF-8 text, 0 NUL bytes, `node --check`\npasses, plain `grep` finds the symbol again, and the behaviour is unchanged — mfr 5016840\nstill resolves Backdrop -> \"Seaglass Wallcovering\" and Schumacher -> \"SEAGLASS\".\n\nAlso re-checked every other file I touched today with binary-safe greps\n(vendor-command-center/server.js, dw-golive-gate-canary/auditor.mjs,\ndw-blank-mfr-visibility-canary/check.py): all clean UTF-8, 0 NULs, and every earlier\nverification claim re-confirms.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"7e0039b","date":"2026-09-10 12:59:40 -0700","author":"Steve Abrams","subject":"TK-11384: scope stagingColorFor() to the vendor on shared catalog tables","body":"stagingColorFor() ran `SELECT color_name FROM ${table} WHERE mfr_sku = … LIMIT 1` with no\nvendor filter and no ORDER BY. For the 29 vendors whose catalog_table is the shared\n`vendor_catalog` — a table holding 157 distinct vendor_codes, in which 11,640 mfr_sku\nvalues are used by more than one vendor — that returned whichever row the seq scan reached\nfirst, i.e. potentially another vendor's colour.\n\nDemonstrated, not inferred. mfr 5016840 exists as schumacher \"sage green\" and backdrop\n\"Seaglass Wallcovering\". Before this change, asking for Backdrop returned Schumacher's\nrow; after it, Backdrop correctly returns \"Seaglass Wallcovering\" (Schumacher resolves to\nits own private schumacher_catalog and is unaffected).\n\nMatters because com.steve.dw-rotation-activator is loaded and runs hourly with --commit.\nImpact today was bounded — the value feeds one placeholder-colour test and only one\nmfr_sku fleet-wide could flip a verdict — but the query was wrong regardless, and the\nblast radius grows with every shared-table vendor added.\n\nFollows this module's fail-open doctrine: a shared table it cannot scope returns null, so\nthe gate fails OPEN on that dimension rather than reading another vendor's row. Added\nORDER BY color_name for determinism.\n\nNOT changed, flagged for follow-up: reusedMfrSet() still COALESCEs down to\nshopify_products.mfr_sku, the column proven polluted with descriptive name strings under\nTK-11384. Correcting that alters gate verdicts on an hourly --commit job and wants its own\nreview.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"0bd5974","date":"2026-09-09 12:24:49 -0700","author":"Steve Abrams","subject":"Record verified 462-product loading and unresolved Google channel exceptions","body":""},{"hash":"0add27a","date":"2026-09-09 12:21:58 -0700","author":"Steve Abrams","subject":"Require confirmed Online Store availability before counting publication","body":""},{"hash":"9720a3e","date":"2026-09-09 11:35:52 -0700","author":"Steve Abrams","subject":"Enable verified one-day activation catch-up within daily allowance","body":""},{"hash":"ea60cf7","date":"2026-09-09 11:15:52 -0700","author":"Steve Abrams","subject":"Keep released runtime worker locks out of repository snapshots","body":""},{"hash":"7396a48","date":"2026-09-09 11:15:15 -0700","author":"Steve Abrams","subject":"Verify full Gemini-free batch and shared hourly activation limit","body":""},{"hash":"c3b8011","date":"2026-09-09 11:10:39 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-09T11:09:48 (1 data files) — .drain.lock/TK-11321-owner.json","body":""},{"hash":"b03419a","date":"2026-09-09 11:09:38 -0700","author":"Steve Abrams","subject":"Apply Steve-authorized Gemini skip and verify first live product","body":""},{"hash":"f9e5125","date":"2026-09-09 10:34:41 -0700","author":"Steve Abrams","subject":"Record successful scheduled activation and remaining image funding block","body":""},{"hash":"c88ff69","date":"2026-09-09 10:20:13 -0700","author":"Steve Abrams","subject":"Restore draft readiness checks and verify live activation canary","body":""},{"hash":"37bb28a","date":"2026-09-03 12:07:07 -0700","author":"steve","subject":"TK-11186 Step D prep: rotation-activator skips showroom vendors (no activate/New Arrival tag/browse publish)","body":""},{"hash":"99013ea","date":"2026-09-01 08:02:16 -0700","author":"Steve Abrams","subject":"feat(golive-gate): mfr_sku provenance gate blocks blank/fabricated codes at DRAFT->ACTIVE","body":"Adds lib/mfr-gate.js (pure, tested) + lib/mfr-gate-resolve.js (dw_unified I/O),\nwired into rotate-activate.js / mdc-activate.js / cmo-activate.js before the flip.\nBlocks: blank/null/unknown mfr, cross-vendor reused counters, and fabricated\n<base>+<seq> runs (Carnegie Grain 6300->63001.. via placeholder 'Color N' staging).\nBlocked drafts tag Needs-MfrSKU + hold; by-colour lines (Novasuede) exempt. TK-11063.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"d9e84e1","date":"2026-08-31 12:42:22 -0700","author":"Steve","subject":"rotation-activator: block $4.25 sample-leak price from passing the activation gate (TK-10456)","body":"The 5-field gate required a sellable variant with price>0, but the universal\n$4.25 memo-sample price is >0, so any draft whose only sellable roll was stuck\nat $4.25 passed the gate and the rotation flipped it DRAFT->ACTIVE — re-leaking\nreverted discontinued/no-price products (5 Thibaut murals + 6 Chloe re-activated\nat $4.25 hours after being reverted). Now a sellable variant priced at exactly\nthe sample-leak $4.25 no longer counts as a real price; a real price on any\nsellable variant still passes. Fail-safe: can only block an activation.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"c4f1747","date":"2026-08-28 21:40:54 -0700","author":"Steve Abrams","subject":"Fail closed on malformed activation fields","body":""},{"hash":"ee136fc","date":"2026-08-28 21:37:35 -0700","author":"Steve Abrams","subject":"Unify pre-activation five-field checks","body":""},{"hash":"3b23d46","date":"2026-08-11 18:50:20 -0700","author":"steve@designerwallcoverings.com","subject":"chore: fail-closed activation guard in drain.sh (session close)","body":""},{"hash":"6074d75","date":"2026-08-11 13:45:39 -0700","author":"steve@designerwallcoverings.com","subject":"rotation drain: pre-flight re-intro guard check — abort activation tick on drift (TK-10474)","body":""},{"hash":"382423f","date":"2026-08-11 13:39:51 -0700","author":"steve@designerwallcoverings.com","subject":"collapse rotation-order.js to single source: symlink activator copy → canonical calendar copy (kills the two-drifting-copies footgun)","body":""},{"hash":"81ca301","date":"2026-08-10 22:13:25 -0700","author":"Steve","subject":"TK-10446 Tier-1: live-verify (892 orphans/48 have-roll/3 inactive) + gated revert script (dry-run default, re-checks live before each revert)","body":""},{"hash":"7f4f53f","date":"2026-08-10 21:36:14 -0700","author":"Steve","subject":"Save TK-10446 Tier-1 cleanup id list (943 rotation-published last-7d)","body":""},{"hash":"0117f7a","date":"2026-08-10 21:22:34 -0700","author":"Steve","subject":"Exclude samples, Gracie/bespoke vendors, and re-introductions from activation queue (TK-10446)","body":""},{"hash":"6ec5f49","date":"2026-07-26 20:21:41 -0700","author":"Steve","subject":"chore: refactor (summary label reflects COMMIT vs DRY mode), session close","body":""},{"hash":"04ef58e","date":"2026-07-26 20:14:52 -0700","author":"Steve","subject":"Add mdc-activate.js: private-label MDC→Phillipe Romano activator (mdc_catalog PID-scoped, settlement+5-field gate, ex-Google, MDC-coinage leak guard, Dublin-Vital hold)","body":""},{"hash":"f8d9655","date":"2026-07-25 12:36:20 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-25T12:36:15 (1 files) — cmo-activate.js","body":""},{"hash":"0b70591","date":"2026-07-23 12:30:51 -0700","author":"Steve Abrams","subject":"rotation gate: quote-only ('quotes' tag) lines exempt from no-sellable-variant too (Koroseal/Vahallan sample-only by design)","body":""},{"hash":"469d6e0","date":"2026-07-22 13:18:30 -0700","author":"Steve Abrams","subject":"leakGuard: add rigo / rigowall / rigo wallcovering (RIGO PL onboard 2026-07-22)","body":""},{"hash":"e06f84c","date":"2026-07-22 11:01:34 -0700","author":"Steve","subject":"drain: prune the activation-calendar cadence after each hourly run","body":"After flipping the per-slot batch DRAFT->ACTIVE, call the calendar's\nprune-active.js so already-live SKUs never linger in the projected cadence\nbetween nightly regens (Steve: cadence = staged items only)."},{"hash":"3466936","date":"2026-07-21 16:16:21 -0700","author":"steve@designerwallcoverings.com","subject":"chore: clarify _HARD_RE/_VENDOR_RE are [token,RegExp] pairs (session close)","body":""},{"hash":"3f4f5ae","date":"2026-07-21 15:41:52 -0700","author":"steve@designerwallcoverings.com","subject":"activator: word-boundary leak matching — fixes 'versa' false-positive on legit Versace brand","body":""},{"hash":"eb84eed","date":"2026-07-21 15:38:11 -0700","author":"steve@designerwallcoverings.com","subject":"activator: align leak guard to canonical dw-leak-scanner denylist (add york/brewster/lillian-august/momentum/versa; drop schumacher); cite source (DTD-B)","body":""},{"hash":"ade810f","date":"2026-07-21 15:28:10 -0700","author":"steve@designerwallcoverings.com","subject":"activator: private-label leak guard — never activate a product with a source name (Greenland/WallQuest/etc) in title or vendor","body":""},{"hash":"329e3f2","date":"2026-07-21 13:26:11 -0700","author":"Steve","subject":"Add per-product settlement gate to rotation activator (texture auto-pass + Gemini vision, fail-closed, dual-key)","body":""},{"hash":"9976a6b","date":"2026-07-21 13:02:12 -0700","author":"Steve","subject":"add hourly drip drain.sh + STAGED launchd plist (not loaded — bootstrap gated on Steve approval)","body":""},{"hash":"fca7d4c","date":"2026-07-21 12:56:54 -0700","author":"Steve","subject":"rotation activator: textures-first + vendor round-robin DRAFT->ACTIVE, all 15,863 drafts, live 5-field re-gate, own daily activation cap","body":""}]}