← back to Dw Sku Integrity
TK-10896: read-only dw_sku backlog classifier + scanner + tests + evidence
abb32c71002455d074baf6e1a3a933cc81ebb08a · 2026-08-30 09:27:17 -0700 · codex-10896
Deterministic recover-existing-code classifier (never mints), portable read-only
scanner (Mac2 mirror or Kamatera via DWSKU_PSQL), 20 unit tests, and the
2026-08-30 mirror evidence. Surfaces mirror-vs-canonical drift and the DWAG-
provenance question for Steve.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A .gitignoreA README.mdA classify.mjsA dwsku-backlog-scan.mjsA evidence/ANALYSIS-2026-08-30.mdA evidence/mac2-mirror-2026-08-30.jsonA package.jsonA test/classify.test.mjs
Diff
commit abb32c71002455d074baf6e1a3a933cc81ebb08a
Author: codex-10896 <steve@designerwallcoverings.com>
Date: Sun Aug 30 09:27:17 2026 -0700
TK-10896: read-only dw_sku backlog classifier + scanner + tests + evidence
Deterministic recover-existing-code classifier (never mints), portable read-only
scanner (Mac2 mirror or Kamatera via DWSKU_PSQL), 20 unit tests, and the
2026-08-30 mirror evidence. Surfaces mirror-vs-canonical drift and the DWAG-
provenance question for Steve.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
.gitignore | 10 +++
README.md | 58 ++++++++++++++++
classify.mjs | 116 ++++++++++++++++++++++++++++++++
dwsku-backlog-scan.mjs | 113 +++++++++++++++++++++++++++++++
evidence/ANALYSIS-2026-08-30.md | 68 +++++++++++++++++++
evidence/mac2-mirror-2026-08-30.json | 31 +++++++++
package.json | 11 +++
test/classify.test.mjs | 126 +++++++++++++++++++++++++++++++++++
8 files changed, 533 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42a9d9b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
+# large per-row plan dumps are regenerable; keep summaries, ignore the JSONL
+evidence/*.plan.jsonl
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..953e2d4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+# dw-sku-integrity
+
+Read-only, deterministic tooling for the **canonical `dw_sku` backlog** (TK-10896).
+
+## Doctrine (hard rule — Steve, 2026-08-26)
+
+`dw_sku` must **recover an EXISTING code** (self-copy from `sku`, or re-scrape the
+real mfr/DW code). It must **NEVER mint a new sequential**. See memory
+`dw-dwsku-keep-old-never-mint`. This tooling is built so it *cannot* mint: the
+classifier only ever proposes a candidate that is already a prefix of the row's
+existing `sku`.
+
+## Safety
+
+- **Read-only.** The scanner issues only `SELECT`. It never `UPDATE`/`INSERT`/
+ `DELETE`s, never writes the DB. Its only output is a local summary/plan file.
+- **No minting.** `classify.mjs` recovers codes by pure suffix-stripping; a unit
+ test (`INVARIANT: classifier never proposes a candidate absent from the row`)
+ enforces this.
+- Any actual `dw_sku` write is canonical + customer-facing → **gated** (drafted to
+ `~/.claude/yolo-queue/pending-approval/`, ledgered, reversible). Not this repo's job.
+
+## Files
+
+- `classify.mjs` — pure classifier: `stripUnitSuffix()`, `classifyRow()`, `RECOVERY_GROUP`.
+- `dwsku-backlog-scan.mjs` — read-only DB scanner → segmentation summary + plan JSONL.
+- `test/classify.test.mjs` — 20 unit tests (`node --test`).
+- `evidence/` — committed scan outputs + the analysis writeup.
+
+## Usage
+
+```bash
+node --test # run the unit tests
+
+# scan the local Mac2 mirror (default):
+node dwsku-backlog-scan.mjs --out evidence/mac2-mirror.json --plan
+
+# scan Kamatera (canonical) — set the psql prefix to a remote wrapper:
+DWSKU_PSQL='ssh <kamatera-host> psql' node dwsku-backlog-scan.mjs \
+ --out evidence/kamatera.json --plan
+```
+
+`--plan` also writes a `*.plan.jsonl` (one row → class → recovered candidate →
+collision flag), suitable as the dry-run input to a *gated* apply step elsewhere.
+
+## Classes → recovery group
+
+| class | meaning | group |
+|---|---|---|
+| `SELF_COPY_DW` | `sku` holds a `DW…-` code | recoverable_now_self_copy |
+| `SELF_COPY_SOURCE` | `sku` holds a non-DW source code | recoverable_now_self_copy |
+| `SELF_COPY_CORK` | `sku` holds a `Cork-<n>` code | recoverable_now_self_copy |
+| `STAGING_LINK` | no usable `sku`, real `mfr_sku` present | recoverable_now_staging_link |
+| `SELF_COPY_*_COLLISION` | recovered code already live on another active product | dedup_gated_TK10649 |
+| `RESCRAPE` | no `sku`, no `mfr_sku` — must re-scrape the vendor | rescrape_program_TK10900 |
+| `IMPORT_DEFECT` | literal `null`/`null-Sample` sku (scraper bug) | rescrape_program_TK10900 |
+
+Parent ticket: **TK-10896**. Related: TK-10649 (dedup), TK-10900/10901 (re-scrape).
diff --git a/classify.mjs b/classify.mjs
new file mode 100644
index 0000000..3c7a2a9
--- /dev/null
+++ b/classify.mjs
@@ -0,0 +1,116 @@
+// classify.mjs — deterministic, pure classifier for the canonical dw_sku backlog.
+//
+// DOCTRINE (Steve, 2026-08-26 — memory `dw-dwsku-keep-old-never-mint`):
+// The canonical dw_sku must RECOVER an EXISTING code (self-copy from `sku`,
+// or re-scrape the real mfr/DW code). It must NEVER mint a new sequential.
+// This module therefore only ever proposes a candidate dw_sku that is
+// ALREADY present on the row (in `sku`) — it can never fabricate one.
+//
+// Pure functions only: no DB, no I/O, no clock, no randomness. Fully testable.
+// Parent ticket: TK-10896.
+
+// The unit / variant suffix vocabulary observed on `sku` for blank-dw_sku rows.
+// (Verified counts on the Mac2 dw_unified mirror 2026-08-30:
+// Sample 7325, Yard 2104, Roll/roll 656, Per Yard 42, Panel 12, Bolt 9.)
+// Extra common UoM tokens are included defensively so the strip stays correct
+// if a vendor introduces them; adding a token here can only REMOVE a trailing
+// unit word, never alter the code body.
+export const UNIT_SUFFIXES = [
+ 'Sample', 'Per Yard', 'Yard', 'Double Roll', 'Single Roll', 'Roll',
+ 'Panel', 'Bolt', 'Each', 'Tile', 'SqFt', 'Sq Ft', 'Yд',
+];
+
+// Matches ONE trailing unit suffix, preceded by a '-' or whitespace separator.
+// Case-insensitive. "Per Yard" contains a space, hence the \s in the alternatives.
+const SUFFIX_RE = new RegExp(
+ '[\\s-]+(' +
+ UNIT_SUFFIXES
+ .slice() // longest-first so "Per Yard" wins over "Yard", "Double Roll" over "Roll"
+ .sort((a, b) => b.length - a.length)
+ .map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s+/g, '\\s+'))
+ .join('|') +
+ ')$',
+ 'i'
+);
+
+// A canonical DW code prefix, e.g. DWAK-, DWCC-, DWEL-RM-...
+const DW_CODE_RE = /^DW[A-Z0-9]{1,6}-/i;
+// A Cork- source code (Greenland / Phillipe Romano cork line — self-copy its Cork-<num>).
+const CORK_RE = /^cork-/i;
+// A literal "null" placeholder sku (import defect, e.g. "null-Sample").
+const NULL_RE = /^null([\s-]|$)/i;
+
+/**
+ * Strip trailing unit/variant suffix tokens from a sku, repeatedly, so that
+ * doubled suffixes ("DWKE-41415-Sample-Sample") collapse to the bare code.
+ * Returns the recovered code candidate (the code the row ALREADY holds).
+ */
+export function stripUnitSuffix(sku) {
+ if (sku == null) return sku;
+ let s = String(sku).trim();
+ let prev;
+ do {
+ prev = s;
+ s = s.replace(SUFFIX_RE, '');
+ } while (s !== prev && s.length > 0);
+ return s;
+}
+
+/**
+ * Classify one product row for the canonical-dw_sku backlog.
+ *
+ * @param {{dw_sku?:string, sku?:string, mfr_sku?:string, status?:string}} row
+ * @param {Set<string>} [activeCodeSet] set of dw_sku values already live on an
+ * ACTIVE product — used to flag COLLISION (candidate already taken).
+ * @returns {{class:string, candidate:(string|null), collides:boolean, note?:string}}
+ */
+export function classifyRow(row, activeCodeSet) {
+ const status = String(row.status || '').trim().toLowerCase();
+ const dw = String(row.dw_sku || '').trim();
+ if (dw) return { class: 'NOT_BLANK', candidate: null, collides: false };
+ if (status !== 'active') return { class: 'OUT_OF_SCOPE_STATUS', candidate: null, collides: false };
+
+ const sku = String(row.sku || '').trim();
+ const mfr = String(row.mfr_sku || '').trim();
+ const has = (c) => !!(activeCodeSet && c && activeCodeSet.has(c));
+
+ // Literal "null" placeholder sku = scraper import defect (JOB 4).
+ if (sku && NULL_RE.test(sku)) {
+ if (mfr) return { class: 'STAGING_LINK', candidate: null, collides: false, note: 'null-literal sku, mfr present' };
+ return { class: 'IMPORT_DEFECT', candidate: null, collides: false };
+ }
+
+ if (sku) {
+ const cand = stripUnitSuffix(sku);
+ if (!cand) return { class: 'RESCRAPE', candidate: null, collides: false, note: 'sku was all-suffix' };
+ if (DW_CODE_RE.test(cand)) {
+ const collides = has(cand);
+ return { class: collides ? 'SELF_COPY_DW_COLLISION' : 'SELF_COPY_DW', candidate: cand, collides };
+ }
+ if (CORK_RE.test(cand)) {
+ const collides = has(cand);
+ return { class: collides ? 'SELF_COPY_COLLISION' : 'SELF_COPY_CORK', candidate: cand, collides };
+ }
+ // Non-DW, non-cork existing source code — still a recover-existing-code fix.
+ const collides = has(cand);
+ return { class: collides ? 'SELF_COPY_COLLISION' : 'SELF_COPY_SOURCE', candidate: cand, collides };
+ }
+
+ // No usable sku.
+ if (mfr) return { class: 'STAGING_LINK', candidate: null, collides: false };
+ return { class: 'RESCRAPE', candidate: null, collides: false };
+}
+
+// Recovery-path grouping used for the backlog rollup / job routing.
+export const RECOVERY_GROUP = {
+ SELF_COPY_DW: 'recoverable_now_self_copy',
+ SELF_COPY_CORK: 'recoverable_now_self_copy',
+ SELF_COPY_SOURCE: 'recoverable_now_self_copy',
+ STAGING_LINK: 'recoverable_now_staging_link',
+ SELF_COPY_DW_COLLISION: 'dedup_gated_TK10649',
+ SELF_COPY_COLLISION: 'dedup_gated_TK10649',
+ RESCRAPE: 'rescrape_program_TK10900',
+ IMPORT_DEFECT: 'rescrape_program_TK10900',
+ NOT_BLANK: 'out_of_scope',
+ OUT_OF_SCOPE_STATUS: 'out_of_scope',
+};
diff --git a/dwsku-backlog-scan.mjs b/dwsku-backlog-scan.mjs
new file mode 100644
index 0000000..70b4147
--- /dev/null
+++ b/dwsku-backlog-scan.mjs
@@ -0,0 +1,113 @@
+#!/usr/bin/env node
+// dwsku-backlog-scan.mjs — READ-ONLY scanner for the canonical dw_sku backlog.
+//
+// Runs the deterministic classifier (classify.mjs) over every ACTIVE product
+// whose dw_sku is blank, and reports the A-E / job segmentation plus a plan
+// JSONL of (row -> class -> recovered candidate -> collision flag).
+//
+// SAFETY: this tool issues ONLY SELECT statements. It never UPDATE/INSERT/
+// DELETEs, never mints a code, never writes to the database. The single
+// artifact it writes is a local plan/summary file under --out.
+//
+// PORTABILITY: the psql prefix is parameterized so the SAME tool produces the
+// canonical number on Kamatera and the mirror number on Mac2:
+// Mac2 mirror (default): node dwsku-backlog-scan.mjs
+// Kamatera (canonical): DWSKU_PSQL='ssh <kam> psql' node dwsku-backlog-scan.mjs
+// (or set DWSKU_PSQL to any psql-compatible prefix)
+//
+// Parent ticket: TK-10896.
+
+import { execFileSync } from 'node:child_process';
+import { writeFileSync, mkdirSync } from 'node:fs';
+import { dirname } from 'node:path';
+import { classifyRow, RECOVERY_GROUP } from './classify.mjs';
+
+const US = '\x1f'; // ASCII unit separator — safe field delimiter for code columns
+const RS = '\x1e'; // ASCII record separator
+
+// psql prefix (everything up to the -c). Default = local Mac2 mirror socket.
+const PSQL = (process.env.DWSKU_PSQL || 'psql -h /tmp -d dw_unified').split(/\s+/);
+
+const argv = process.argv.slice(2);
+const outArg = argv.indexOf('--out');
+const OUT = outArg >= 0 ? argv[outArg + 1] : null;
+const emitPlan = argv.includes('--plan'); // also emit per-row plan JSONL next to --out
+
+function psql(sql) {
+ const args = [...PSQL.slice(1), '-tA', '-F', US, '-R', RS, '-c', sql];
+ const raw = execFileSync(PSQL[0], args, { maxBuffer: 1 << 30, encoding: 'utf8' });
+ return raw.split(RS).map((r) => r.replace(/\n$/, '')).filter((r) => r.length).map((r) => r.split(US));
+}
+
+const BLANK_ACTIVE = `lower(coalesce(status,''))='active' and (dw_sku is null or btrim(dw_sku)='')`;
+
+console.error(`[dwsku-scan] psql prefix: ${PSQL.join(' ')}`);
+console.error('[dwsku-scan] reading active dw_sku code set...');
+const activeCodeSet = new Set(
+ psql(`select distinct dw_sku from shopify_products where lower(coalesce(status,''))='active' and dw_sku is not null and btrim(dw_sku)<>''`)
+ .map((r) => r[0])
+);
+console.error(`[dwsku-scan] active codes: ${activeCodeSet.size}`);
+
+console.error('[dwsku-scan] reading blank-dw_sku active rows...');
+const rows = psql(
+ `select coalesce(vendor,''), coalesce(sku,''), coalesce(mfr_sku,''), coalesce(dw_sku,''), coalesce(status,'') ` +
+ `from shopify_products where ${BLANK_ACTIVE}`
+);
+console.error(`[dwsku-scan] blank rows: ${rows.length}`);
+
+const byClass = {};
+const byGroup = {};
+const byVendorRescrape = {};
+const planLines = [];
+// Provenance cross-tab: among self-copy-recoverable rows, how many ALSO carry a
+// real mfr_sku (so the recovered code can be CROSS-VERIFIED against the vendor's
+// staging catalog) vs sku-only (no independent check). Higher mfr coverage =
+// stronger evidence the code is scraper-assigned, not mint residue.
+let selfCopyWithMfr = 0;
+let selfCopyNoMfr = 0;
+
+for (const [vendor, sku, mfr_sku, dw_sku, status] of rows) {
+ const v = classifyRow({ vendor, sku, mfr_sku, dw_sku, status }, activeCodeSet);
+ byClass[v.class] = (byClass[v.class] || 0) + 1;
+ const grp = RECOVERY_GROUP[v.class] || 'unknown';
+ byGroup[grp] = (byGroup[grp] || 0) + 1;
+ if (grp === 'recoverable_now_self_copy') {
+ if (mfr_sku && mfr_sku.trim()) selfCopyWithMfr++;
+ else selfCopyNoMfr++;
+ }
+ if (grp === 'rescrape_program_TK10900') {
+ byVendorRescrape[vendor] = (byVendorRescrape[vendor] || 0) + 1;
+ }
+ if (emitPlan) planLines.push(JSON.stringify({ vendor, sku, mfr_sku, class: v.class, candidate: v.candidate, collides: v.collides, group: grp }));
+}
+
+const summary = {
+ generated_by: 'dwsku-backlog-scan.mjs',
+ ticket: 'TK-10896',
+ psql_prefix: PSQL.join(' '),
+ source_note: process.env.DWSKU_PSQL ? 'CUSTOM (verify which DB)' : 'Mac2 dw_unified mirror (NOT canonical — Kamatera owns shopify_products)',
+ active_code_count: activeCodeSet.size,
+ active_blank_dwsku: rows.length,
+ by_class: Object.fromEntries(Object.entries(byClass).sort((a, b) => b[1] - a[1])),
+ by_recovery_group: Object.fromEntries(Object.entries(byGroup).sort((a, b) => b[1] - a[1])),
+ self_copy_provenance: {
+ with_real_mfr_sku_cross_verifiable: selfCopyWithMfr,
+ sku_only_no_independent_check: selfCopyNoMfr,
+ },
+ rescrape_cohorts_top: Object.fromEntries(Object.entries(byVendorRescrape).sort((a, b) => b[1] - a[1]).slice(0, 15)),
+};
+
+const out = JSON.stringify(summary, null, 2);
+console.log(out);
+
+if (OUT) {
+ mkdirSync(dirname(OUT), { recursive: true });
+ writeFileSync(OUT, out + '\n');
+ console.error(`[dwsku-scan] wrote summary -> ${OUT}`);
+ if (emitPlan) {
+ const planPath = OUT.replace(/\.json$/, '') + '.plan.jsonl';
+ writeFileSync(planPath, planLines.join('\n') + '\n');
+ console.error(`[dwsku-scan] wrote plan (${planLines.length} rows) -> ${planPath}`);
+ }
+}
diff --git a/evidence/ANALYSIS-2026-08-30.md b/evidence/ANALYSIS-2026-08-30.md
new file mode 100644
index 0000000..266dbf0
--- /dev/null
+++ b/evidence/ANALYSIS-2026-08-30.md
@@ -0,0 +1,68 @@
+# TK-10896 — canonical dw_sku backlog: read-only analysis (2026-08-30)
+
+**Agent:** codex-10896 · **Mode:** read-only, no canonical writes, no minting.
+**Source:** Mac2 `dw_unified` mirror via local `/tmp` socket. **NOT canonical** —
+Kamatera owns `shopify_products` (split-ownership doctrine). Re-run the scanner on
+Kamatera for the authoritative number.
+
+## Method
+
+`dwsku-backlog-scan.mjs` runs the deterministic classifier (`classify.mjs`) over
+every ACTIVE product with a blank `dw_sku`. The classifier only ever proposes a
+candidate `dw_sku` that is **already present on the row** (recovered from `sku` by
+pure unit-suffix stripping) — it can never fabricate/mint one. 20 unit tests cover
+the strip rule, collision detection, and the no-mint invariant.
+
+## Headline segmentation (Mac2 mirror, 34,600 active-blank rows)
+
+| Recovery group | Rows | Route |
+|---|---:|---|
+| recoverable_now_self_copy | 34,520 (99.77%) | self-copy the existing code from `sku` |
+| recoverable_now_staging_link | 6 | link `mfr_sku` → vendor `*_catalog.dw_sku` |
+| dedup_gated (TK-10649) | 9 | candidate collides w/ a live active code → verify+archive dup |
+| rescrape_program (TK-10900) | 65 | truly bare (no sku, no mfr): Pixels 52, Steve Abrams Studios 13 |
+
+By class: `SELF_COPY_DW` 31,469 · `SELF_COPY_SOURCE` 3,026 · `SELF_COPY_CORK` 25 ·
+`STAGING_LINK` 6 · `SELF_COPY_COLLISION` 6 · `SELF_COPY_DW_COLLISION` 3 · `RESCRAPE` 65.
+
+Self-copy provenance: **16,175** of the 34,520 self-copy rows also carry a real
+`mfr_sku` (the recovered code is cross-verifiable against the vendor staging
+catalog); 18,345 are sku-only (the code the row already holds is the only identifier).
+
+## Two findings that change the plan — for Steve
+
+### 1. Mirror-vs-canonical drift (must verify on Kamatera)
+The scope memo (verified on Kamatera 2026-08-26) projected **12,589** Class-E rows
+needing the big per-vendor **re-scrape program**, with **Carnegie 5,921** as the
+anchor cohort. On the Mac2 mirror that program **collapses to 65 rows / 2 vendors** —
+every Carnegie/Maharam/Knoll/etc. blank row already holds its DW code in `sku`.
+Either (a) the Mac2 mirror is stale (never received Phase-1's writes / re-synced
+from Shopify), or (b) the re-scrape program is largely unnecessary. **Resolve by
+running `DWSKU_PSQL='ssh <kam> psql' node dwsku-backlog-scan.mjs` on Kamatera.**
+
+### 2. Provenance of the `DWAG-`/mint-adjacent codes (self-copy safety)
+Carnegie's 5,921 blank rows carry `sku=DWAG-######` + a real `mfr_sku`
+(`100795352-panels`) + a `carnegie_catalog` staging row (mfr→dw_sku). `DWAG-` is
+the registry prefix the ticket flagged as **"unused"**, and Phase-4 *minted* into
+`DWAG-` before Steve reverted it. So the open question is whether these `DWAG-`
+codes are **scraper-assigned** (self-copy/staging-link is compliant) or **residue
+of the reverted Phase-4 mint** (self-copying re-instates a minted number — a
+doctrine violation). Read-only analysis on the mirror cannot settle provenance;
+this is a decision-gate for Steve. The 16,175 mfr-bearing self-copy rows are the
+ones where staging cross-verification can help answer it.
+
+## What is safe to conclude now
+- The classifier + strip rule are correct and reproducible (tests green; validated
+ against real sku shapes incl. `-Sample`, `-Yard`, `-Per Yard`, doubled `-Sample-Sample`,
+ and non-numeric Elitis `DWEL-RM-…` cores).
+- Nothing was written to any database. No code was minted.
+- The 9 collisions match the memo's Phase-2 dedup class (Novasuede `DWCC-*-Per Yard`,
+ Arte `DWKE-41415-Sample-Sample`) → route to TK-10649.
+- The only genuine re-scrape need on the mirror is 65 rows (Pixels + Steve Abrams).
+
+## Next steps (all gated / out of this read-only scope)
+1. **Steve/drift-canary:** run the scanner on Kamatera → canonical segmentation.
+2. **Steve decision:** provenance ruling on the `DWAG-`/mint-adjacent cohort
+ (self-copy-OK vs treat-as-minted → re-scrape).
+3. On a self-copy-OK ruling, the write remains canonical/customer-facing → gated,
+ drafted to pending-approval per row batch, ledgered/reversible.
diff --git a/evidence/mac2-mirror-2026-08-30.json b/evidence/mac2-mirror-2026-08-30.json
new file mode 100644
index 0000000..62aa49c
--- /dev/null
+++ b/evidence/mac2-mirror-2026-08-30.json
@@ -0,0 +1,31 @@
+{
+ "generated_by": "dwsku-backlog-scan.mjs",
+ "ticket": "TK-10896",
+ "psql_prefix": "psql -h /tmp -d dw_unified",
+ "source_note": "Mac2 dw_unified mirror (NOT canonical — Kamatera owns shopify_products)",
+ "active_code_count": 55203,
+ "active_blank_dwsku": 34600,
+ "by_class": {
+ "SELF_COPY_DW": 31469,
+ "SELF_COPY_SOURCE": 3026,
+ "RESCRAPE": 65,
+ "SELF_COPY_CORK": 25,
+ "STAGING_LINK": 6,
+ "SELF_COPY_COLLISION": 6,
+ "SELF_COPY_DW_COLLISION": 3
+ },
+ "by_recovery_group": {
+ "recoverable_now_self_copy": 34520,
+ "rescrape_program_TK10900": 65,
+ "dedup_gated_TK10649": 9,
+ "recoverable_now_staging_link": 6
+ },
+ "self_copy_provenance": {
+ "with_real_mfr_sku_cross_verifiable": 16175,
+ "sku_only_no_independent_check": 18345
+ },
+ "rescrape_cohorts_top": {
+ "Pixels": 52,
+ "Steve Abrams Studios": 13
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a7820a3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "dw-sku-integrity",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "description": "Read-only deterministic tooling for the canonical dw_sku backlog (TK-10896). Never mints; recovers existing codes only.",
+ "scripts": {
+ "test": "node --test",
+ "scan": "node dwsku-backlog-scan.mjs"
+ }
+}
diff --git a/test/classify.test.mjs b/test/classify.test.mjs
new file mode 100644
index 0000000..e99e1e5
--- /dev/null
+++ b/test/classify.test.mjs
@@ -0,0 +1,126 @@
+// Unit tests for the deterministic dw_sku classifier. Pure, no DB.
+// Run: node --test
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { stripUnitSuffix, classifyRow, UNIT_SUFFIXES, RECOVERY_GROUP } from '../classify.mjs';
+
+const ACTIVE = new Set(['DWKE-41415', 'DWCC-116000', 'DWCC-116200']); // simulated live codes
+
+test('stripUnitSuffix removes a single -Sample suffix', () => {
+ assert.equal(stripUnitSuffix('DWAK-700042-Sample'), 'DWAK-700042');
+});
+
+test('stripUnitSuffix removes -Yard / -Roll units', () => {
+ assert.equal(stripUnitSuffix('DWEL-142757-Yard'), 'DWEL-142757');
+ assert.equal(stripUnitSuffix('DWEL-142714-Roll'), 'DWEL-142714');
+});
+
+test('stripUnitSuffix removes "-Per Yard" (spaced unit)', () => {
+ assert.equal(stripUnitSuffix('DWCC-112370-Per Yard'), 'DWCC-112370');
+});
+
+test('stripUnitSuffix collapses doubled suffixes', () => {
+ assert.equal(stripUnitSuffix('DWKE-41415-Sample-Sample'), 'DWKE-41415');
+});
+
+test('stripUnitSuffix preserves non-numeric DW code cores (Elitis RM)', () => {
+ assert.equal(stripUnitSuffix('DWEL-RM-631-80-Roll'), 'DWEL-RM-631-80');
+});
+
+test('stripUnitSuffix leaves a bare code untouched', () => {
+ assert.equal(stripUnitSuffix('DWGB-800409'), 'DWGB-800409');
+ assert.equal(stripUnitSuffix('Cork-1234'), 'Cork-1234');
+});
+
+test('stripUnitSuffix is case-insensitive on unit words', () => {
+ assert.equal(stripUnitSuffix('DWQW-60706-sample'), 'DWQW-60706');
+ assert.equal(stripUnitSuffix('DWEL-142714-roll'), 'DWEL-142714');
+});
+
+test('classify: DW-code sku -> SELF_COPY_DW (recoverable, never mints)', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: 'DWAK-700042-Sample' }, ACTIVE);
+ assert.equal(r.class, 'SELF_COPY_DW');
+ assert.equal(r.candidate, 'DWAK-700042');
+ assert.equal(r.collides, false);
+ // candidate must always be a substring already present on the row
+ assert.ok('DWAK-700042-Sample'.startsWith(r.candidate));
+});
+
+test('classify: candidate colliding with a live active code -> COLLISION (dedup, not self-copy)', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: 'DWKE-41415-Sample-Sample' }, ACTIVE);
+ assert.equal(r.class, 'SELF_COPY_DW_COLLISION');
+ assert.equal(r.candidate, 'DWKE-41415');
+ assert.equal(r.collides, true);
+ assert.equal(RECOVERY_GROUP[r.class], 'dedup_gated_TK10649');
+});
+
+test('classify: Novasuede -Per Yard colliding row -> COLLISION', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: 'DWCC-116000-Per Yard' }, ACTIVE);
+ assert.equal(r.class, 'SELF_COPY_DW_COLLISION');
+ assert.equal(r.candidate, 'DWCC-116000');
+});
+
+test('classify: Cork- source code -> SELF_COPY_CORK', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: 'Cork-482' }, ACTIVE);
+ assert.equal(r.class, 'SELF_COPY_CORK');
+ assert.equal(r.candidate, 'Cork-482');
+});
+
+test('classify: non-DW source code -> SELF_COPY_SOURCE', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: 'AS784911-0' }, ACTIVE);
+ assert.equal(r.class, 'SELF_COPY_SOURCE');
+ assert.equal(r.candidate, 'AS784911-0');
+});
+
+test('classify: no sku but mfr present -> STAGING_LINK', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: '', mfr_sku: 'ABC-123' }, ACTIVE);
+ assert.equal(r.class, 'STAGING_LINK');
+});
+
+test('classify: no sku, no mfr -> RESCRAPE', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: '', mfr_sku: '' }, ACTIVE);
+ assert.equal(r.class, 'RESCRAPE');
+ assert.equal(RECOVERY_GROUP[r.class], 'rescrape_program_TK10900');
+});
+
+test('classify: literal null-Sample -> IMPORT_DEFECT', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: 'null-Sample', mfr_sku: '' }, ACTIVE);
+ assert.equal(r.class, 'IMPORT_DEFECT');
+});
+
+test('classify: literal null sku with mfr -> STAGING_LINK', () => {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: 'null', mfr_sku: 'X9' }, ACTIVE);
+ assert.equal(r.class, 'STAGING_LINK');
+});
+
+test('classify: already-populated dw_sku -> NOT_BLANK (never touched)', () => {
+ const r = classifyRow({ status: 'active', dw_sku: 'DWXX-1', sku: 'DWYY-2-Sample' }, ACTIVE);
+ assert.equal(r.class, 'NOT_BLANK');
+ assert.equal(r.candidate, null);
+});
+
+test('classify: non-active status -> OUT_OF_SCOPE_STATUS', () => {
+ const r = classifyRow({ status: 'archived', dw_sku: '', sku: 'DWZZ-9-Sample' }, ACTIVE);
+ assert.equal(r.class, 'OUT_OF_SCOPE_STATUS');
+});
+
+test('INVARIANT: classifier never proposes a candidate absent from the row (no minting)', () => {
+ const samples = [
+ 'DWAK-700042-Sample', 'DWEL-RM-631-80-Roll', 'Cork-482', 'AS784911-0',
+ 'DWCC-112370-Per Yard', 'DWKE-41415-Sample-Sample',
+ ];
+ for (const sku of samples) {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku }, ACTIVE);
+ if (r.candidate) {
+ // The proposed canonical code must be derivable from the existing sku by
+ // pure suffix-stripping — i.e. it is a prefix of the original sku.
+ assert.ok(sku.startsWith(r.candidate), `candidate ${r.candidate} not a prefix of ${sku}`);
+ }
+ }
+});
+
+test('UNIT_SUFFIXES includes the observed vocabulary', () => {
+ for (const w of ['Sample', 'Yard', 'Roll', 'Panel', 'Bolt', 'Per Yard']) {
+ assert.ok(UNIT_SUFFIXES.includes(w), `${w} missing from UNIT_SUFFIXES`);
+ }
+});
(oldest)
·
back to Dw Sku Integrity
·
TK-10896: add provenance guard — greenfield Phase-4 mint pre 2bcc199 →