← 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
A shopify/scripts/lib/lint-inventory-write-guard.mjs
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 →