[object Object]

← back to Designer Wallcoverings

lint-inventory-write-guard: sentinel matches 2026 as a quantity, not the current year in dates

8bf7c210652682ff5e2cabc662fc11cb04402de1 · 2026-09-20 18:21:28 -0700 · Steve Abrams

TK-11857 review finding: /\\b2026\\b matched any 2026 (ISO dates/timestamps/filenames) →
false-positive risk on any inventory-writer carrying a 2026 date, the 'noisy linter gets
switched off' failure the header warns against. Sentinel now excludes date/number contexts;
detection refactored into isOffender() + a --test negative/positive self-test (4/4). Real scan clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011xoYjhg4XpLjXwhLbhBndb

Files touched

Diff

commit 8bf7c210652682ff5e2cabc662fc11cb04402de1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Sep 20 18:21:28 2026 -0700

    lint-inventory-write-guard: sentinel matches 2026 as a quantity, not the current year in dates
    
    TK-11857 review finding: /\\b2026\\b matched any 2026 (ISO dates/timestamps/filenames) →
    false-positive risk on any inventory-writer carrying a 2026 date, the 'noisy linter gets
    switched off' failure the header warns against. Sentinel now excludes date/number contexts;
    detection refactored into isOffender() + a --test negative/positive self-test (4/4). Real scan clean.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_011xoYjhg4XpLjXwhLbhBndb
---
 shopify/scripts/lib/lint-inventory-write-guard.mjs | 120 +++++++++++++++++++++
 1 file changed, 120 insertions(+)

diff --git a/shopify/scripts/lib/lint-inventory-write-guard.mjs b/shopify/scripts/lib/lint-inventory-write-guard.mjs
new file mode 100644
index 00000000..26e64b23
--- /dev/null
+++ b/shopify/scripts/lib/lint-inventory-write-guard.mjs
@@ -0,0 +1,120 @@
+#!/usr/bin/env node
+/**
+ * lint-inventory-write-guard.mjs  (TK-11857 / DTD 2026-09-17 — Cody's forcing function)
+ *
+ * Static guard against the $0-checkout-orderable defect class (recurred 6×). It flags any
+ * script that performs a MASS inventory write (inventorySetQuantities / inventoryActivate /
+ * REST inventory_levels) stamping the sentinel quantity 2026 WITHOUT routing the quantity
+ * through the shared guard `lib/inventory-stamp-guard.mjs` (safeStampQuantity). Per-script
+ * inline `price===0` filters are NOT accepted — they miss NaN prices and price-suppressed
+ * lines (the exact holes the shared guard closes), and "we guarded N call sites, the N+1th
+ * hid" (CLAUDE.md TK-11431) is what makes a per-script filter the wrong mechanism.
+ *
+ * Exit 0 = clean, exit 1 = offenders found. Intended for a pre-commit hook / CI step; wiring
+ * it in is a separate (gated) step — this file is only the detector.
+ *
+ *   node lib/lint-inventory-write-guard.mjs          # scan
+ *   node lib/lint-inventory-write-guard.mjs --test   # self-test (negative + positive)
+ */
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dir = path.dirname(fileURLToPath(import.meta.url));
+const SCRIPTS_DIR = path.resolve(__dir, '..');            // shopify/scripts
+const GUARD_MODULE = 'inventory-stamp-guard';             // the shared chokepoint (exempt)
+
+// A write mutation/endpoint that can create orderable stock.
+const WRITE_RE = /inventorySetQuantities|inventoryActivate|inventory_levels\/set|inventory_levels\.json/;
+// The sentinel mass-stamp quantity. Match 2026 ONLY as a bare quantity value — NOT as part
+// of a date (2026-09-17), an ISO timestamp (2026-..T..), a path/filename, a TK id, or a
+// longer number. 2026 is the CURRENT YEAR, so a bare /\b2026\b/ matched every date comment
+// and would false-positive an inventory-writer that merely carries a 2026 date — the exact
+// "noisy linter gets switched off in a day" failure this file's header warns against
+// (TK-11857 review finding). Exclude a leading/trailing digit or date/time punctuation.
+const SENTINEL_RE = /(?<![\d./-])2026(?![\d./:T-])/;
+const GUARD_USE_RE = /safeStampQuantity/;
+
+// Scope: the defect is a $0/NaN/suppressed SELLABLE variant made orderable. Two exemptions keep
+// this REPRESENTATIVE (a linter with mostly-false-positives gets switched off in a day):
+//   1. GUARD files — a *-guard file (or the shared module) is the fix, not an offender.
+//   2. SAMPLE-scoped scripts — a *sample* script stamps the $4.25 Sample variant's inventory,
+//      which is INTENDED (samples are orderable at $4.25) and is not the sellable-$0 class.
+// Assumption (documented, not a security boundary): a mass SELLABLE stamper is not named
+// "*sample*". This guard catches accidental omission (the newest.mjs class), not deliberate
+// evasion. If that ever matters, tighten to per-variant static analysis of the write target.
+const EXEMPT_NAME_RE = /sample|guard/i;
+
+/**
+ * The single decision, extracted so the self-test can exercise the EXACT logic the scan uses
+ * (CLAUDE.md TK-11431 amendment 3 — a detector ships with a test proving it reddens on a fault).
+ * @returns {boolean} true iff `src` is a mass-2026 inventory writer NOT routed through the guard.
+ */
+export function isOffender(base, src) {
+  if (base.includes(GUARD_MODULE)) return false;          // the shared guard itself is exempt
+  if (EXEMPT_NAME_RE.test(base)) return false;            // sample-scoped or a guard file — exempt
+  if (!WRITE_RE.test(src)) return false;                  // not an inventory writer
+  if (!SENTINEL_RE.test(src)) return false;               // not a mass 2026 stamper
+  if (GUARD_USE_RE.test(src)) return false;               // routes through the shared guard — OK
+  return true;
+}
+
+function walk(dir) {
+  const out = [];
+  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
+    const p = path.join(dir, e.name);
+    if (e.isDirectory()) { if (e.name !== 'node_modules') out.push(...walk(p)); }
+    else if (/\.(mjs|cjs|js)$/.test(e.name)) out.push(p);
+  }
+  return out;
+}
+
+// --- self-test: prove the sentinel goes RED on a real 2026-quantity stamp and STAYS GREEN on
+// an inventory writer that only carries a 2026 DATE (the false-positive class this fix closes).
+function runTest() {
+  // NOTE: fixture filenames must NOT contain "sample"/"guard" — EXEMPT_NAME_RE would exempt
+  // them before the sentinel runs, masking what each case is meant to exercise.
+  const cases = [
+    // [name, src, expectedOffender]
+    ['date-only-writer.js',
+      `// updated 2026-09-17\nawait client.inventorySetQuantities({ quantities: [{ inventoryItemId: id, quantity: qty }] });`,
+      false], // carries a 2026 DATE but the quantity is a variable — MUST NOT flag
+    ['bare-2026-quantity-writer.js',
+      `await client.inventorySetQuantities({ quantities: [{ inventoryItemId: id, quantity: 2026 }] });`,
+      true],  // hardcodes 2026 as the quantity, no guard — MUST flag
+    ['routes-through-shared-chokepoint.js',
+      `const q = safeStampQuantity(v, p, 2026);\nawait client.inventorySetQuantities({ quantities: [{ inventoryItemId: id, quantity: q }] });`,
+      false], // routes through safeStampQuantity — OK
+    ['iso-timestamp-writer.js',
+      `const ts = "2026-09-17T11:06:41Z";\nawait fetch(url + "/inventory_levels/set.json", { method: "POST", body });`,
+      false], // ISO timestamp only, variable body — MUST NOT flag
+  ];
+  let pass = 0;
+  for (const [name, src, want] of cases) {
+    const got = isOffender(name, src);
+    const ok = got === want;
+    if (ok) pass++;
+    console.log(`${ok ? 'ok  ' : 'FAIL'}  ${name}: offender=${got} (want ${want})`);
+  }
+  const allOk = pass === cases.length;
+  console.log(allOk ? `\nSELF-TEST PASS (${pass}/${cases.length})` : `\nSELF-TEST FAIL (${pass}/${cases.length})`);
+  process.exit(allOk ? 0 : 1);
+}
+
+if (process.argv.includes('--test')) runTest();
+
+const offenders = [];
+for (const file of walk(SCRIPTS_DIR)) {
+  let src;
+  try { src = fs.readFileSync(file, 'utf8'); } catch { continue; }
+  if (isOffender(path.basename(file), src)) offenders.push(path.relative(SCRIPTS_DIR, file));
+}
+
+if (offenders.length) {
+  console.error(`FAIL: ${offenders.length} inventory-write script(s) stamp 2026 WITHOUT safeStampQuantity:`);
+  for (const o of offenders) console.error(`  - ${o}`);
+  console.error('Fix: import { safeStampQuantity } from "./lib/inventory-stamp-guard.mjs" and gate the quantity through it.');
+  process.exit(1);
+}
+console.log('OK: every mass 2026 inventory writer routes through safeStampQuantity.');
+process.exit(0);

← 32bc93c4 auto-data-snapshot: 2026-09-20T17:51:28 (3 data files) — sho  ·  back to Designer Wallcoverings  ·  auto-data-snapshot: 2026-09-20T18:59:45 (3 data files) — sho a78fdbb5 →