← back to Dw Sku Integrity
TK-10896 fail closed on mixed SKU provenance
189503094317db04e8be38ac2ff7da7b30b18ab6 · 2026-08-30 11:26:25 -0700 · codex-10896
Files touched
M README.mdM classify.mjsA evidence/PROVENANCE-HOLD-2026-08-30.mdA evidence/mac2-mirror-provenance-hold-2026-08-30.jsonM test/classify.test.mjsA verification/TK-10896-e2e-proof.jsonA verification/provenance-plan.e2e.mjs
Diff
commit 189503094317db04e8be38ac2ff7da7b30b18ab6
Author: codex-10896 <steve@designerwallcoverings.com>
Date: Sun Aug 30 11:26:25 2026 -0700
TK-10896 fail closed on mixed SKU provenance
---
README.md | 6 ++-
classify.mjs | 23 +++++++++-
evidence/PROVENANCE-HOLD-2026-08-30.md | 36 +++++++++++++++
.../mac2-mirror-provenance-hold-2026-08-30.json | 47 +++++++++++++++++++
test/classify.test.mjs | 20 +++++++-
verification/TK-10896-e2e-proof.json | 51 +++++++++++++++++++++
verification/provenance-plan.e2e.mjs | 53 ++++++++++++++++++++++
7 files changed, 231 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index bf745dd..60a83fd 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,8 @@ existing `sku`.
to no-mint — so the no-mint property holds *provided the guard catches all residue
prefixes*. Greenfield (Bucket B) is fully covered; a small Bucket-A subset (≤~668
rows minted into already-in-use prefixes) needs the exact reverted-mint ledger to
- disambiguate and is flagged, not silently cleared (see `evidence/ANALYSIS`).
+ disambiguate. Those prefixes now fail closed into `PROVENANCE_REVIEW`; they are
+ neither silently cleared nor incorrectly sent to re-scrape (see `evidence/ANALYSIS`).
- **dw_sku is intentionally non-unique.** A pattern's sellable + sample rows share one
canonical `dw_sku`. The scanner reports `within_batch_shared_candidates` (benign
same-pattern groups); an apply step MUST upsert, never assume per-row uniqueness.
@@ -34,7 +35,7 @@ existing `sku`.
- `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`).
+- `test/classify.test.mjs` — 27 unit tests (`node --test`).
- `evidence/` — committed scan outputs + the analysis writeup.
## Usage
@@ -63,6 +64,7 @@ collision flag), suitable as the dry-run input to a *gated* apply step elsewhere
| `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 |
| `MINT_RESIDUE_RESCRAPE` | code sits in a reverted Phase-4 **greenfield** mint prefix (`DWAG/DWAX/DWCX/DWST/DWSC/DWDX/DWWG`) — self-copy would re-mint | rescrape_program_TK10900 |
+| `PROVENANCE_REVIEW` | code uses a mixed native/minted Phase-4 prefix (`DWKN/DWTT/DWRW/DWJS/DWRO/DWCC`) — exact undo ledger required | provenance_review_TK10896 |
| `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 |
diff --git a/classify.mjs b/classify.mjs
index 12e95d7..66e044e 100644
--- a/classify.mjs
+++ b/classify.mjs
@@ -53,6 +53,20 @@ export const GREENFIELD_MINT_PREFIXES = new Set([
'DWWG', // Wolf Gordon
]);
+// Mixed-use Phase-4 prefixes. These prefixes predated the reverted mint, so a
+// prefix match alone cannot say whether a row is scraper-native or mint residue.
+// Fail closed into a dedicated provenance-review class until the retained
+// canonical undo ledger can identify the exact minted numbers. This deliberately
+// quarantines legitimate rows too; it never routes them to re-scrape or collision.
+export const MIXED_USE_MINT_PREFIXES = new Set([
+ 'DWKN', // Knoll
+ 'DWTT', // Thibaut
+ 'DWRW', // Rebel Walls
+ 'DWJS', // Jeffrey Stevens / York legacy shared prefix
+ 'DWRO', // Romo
+ 'DWCC', // Novasuede
+]);
+
function greenfieldPrefixOf(code, prefixSet) {
const m = /^(DW[A-Z0-9]{1,6})-/i.exec(code || '');
if (!m) return null;
@@ -89,11 +103,12 @@ export function stripUnitSuffix(sku) {
* @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).
- * @param {{greenfieldPrefixes?:Set<string>}} [opts] provenance-guard config.
+ * @param {{greenfieldPrefixes?:Set<string>,mixedUsePrefixes?:Set<string>}} [opts] provenance-guard config.
* @returns {{class:string, candidate:(string|null), collides:boolean, note?:string}}
*/
export function classifyRow(row, activeCodeSet, opts = {}) {
const greenfield = opts.greenfieldPrefixes || GREENFIELD_MINT_PREFIXES;
+ const mixedUse = opts.mixedUsePrefixes || MIXED_USE_MINT_PREFIXES;
const status = String(row.status || '').trim().toLowerCase();
const dw = String(row.dw_sku || '').trim();
if (dw) return { class: 'NOT_BLANK', candidate: null, collides: false };
@@ -118,6 +133,11 @@ export function classifyRow(row, activeCodeSet, opts = {}) {
const gf = greenfieldPrefixOf(cand, greenfield);
if (gf) return { class: 'MINT_RESIDUE_RESCRAPE', candidate: null, collides: false, note: `greenfield mint prefix ${gf} (reverted Phase-4) — recover real mfr code, do not self-copy ${cand}` };
const collides = has(cand);
+ const mixed = greenfieldPrefixOf(cand, mixedUse);
+ // A code already owned by another active product is a definite collision;
+ // that stronger fact wins over the unresolved mixed-prefix provenance hold.
+ if (mixed && collides) return { class: 'SELF_COPY_DW_COLLISION', candidate: cand, collides: true };
+ if (mixed) return { class: 'PROVENANCE_REVIEW', candidate: null, collides: false, note: `mixed-use Phase-4 prefix ${mixed} — compare ${cand} with retained undo ledger before self-copy` };
return { class: collides ? 'SELF_COPY_DW_COLLISION' : 'SELF_COPY_DW', candidate: cand, collides };
}
if (CORK_RE.test(cand)) {
@@ -145,6 +165,7 @@ export const RECOVERY_GROUP = {
RESCRAPE: 'rescrape_program_TK10900',
IMPORT_DEFECT: 'rescrape_program_TK10900',
MINT_RESIDUE_RESCRAPE: 'rescrape_program_TK10900',
+ PROVENANCE_REVIEW: 'provenance_review_TK10896',
NOT_BLANK: 'out_of_scope',
OUT_OF_SCOPE_STATUS: 'out_of_scope',
};
diff --git a/evidence/PROVENANCE-HOLD-2026-08-30.md b/evidence/PROVENANCE-HOLD-2026-08-30.md
new file mode 100644
index 0000000..0bbc1b0
--- /dev/null
+++ b/evidence/PROVENANCE-HOLD-2026-08-30.md
@@ -0,0 +1,36 @@
+# TK-10896 — mixed-prefix provenance hold
+
+This phase closes the unsafe default identified by the prior analysis: rows in
+mixed-use Phase-4 prefixes were still routed to self-copy even though the exact
+reverted-mint numbers are not available on the Mac2 mirror.
+
+Canonical ticket evidence establishes that the retained undo source is
+`sku_repair_p4_20260826`, but a read-only `information_schema` lookup found no
+matching table in the local mirror. Canonical/Kamatera access was explicitly out
+of scope, so no remote lookup was attempted.
+
+The classifier now fails closed:
+
+- Greenfield mint prefixes remain `MINT_RESIDUE_RESCRAPE` with no candidate.
+- Mixed native/minted prefixes `DWKN/DWTT/DWRW/DWJS/DWRO/DWCC` become
+ `PROVENANCE_REVIEW` with no candidate until the undo ledger resolves them.
+- A candidate already used by another active product remains a definite collision;
+ that stronger fact routes to TK-10649 rather than being hidden by provenance review.
+- Other self-copy candidates must still be literal prefixes of the row's existing SKU.
+
+Fresh SELECT-only Mac2 mirror scan (`34,614` active blank rows):
+
+- `21,555` recoverable-now self-copy (down from the unsafe `25,623`)
+- `4,082` provenance review / exact-ledger required
+- `8,962` rescrape
+- `9` definite collision/dedup
+- `6` staging-link
+
+The scanner generated a 34,614-row local plan, the E2E verifier checked every row,
+then the plan was removed to avoid retaining a large identifier dump. The compact
+summary remains at `evidence/mac2-mirror-provenance-hold-2026-08-30.json`.
+
+No DB write, canonical/Kamatera access, mint, cleanup of user data, deploy, or
+Shopify action occurred. The safest next action is a separately authorized
+read-only export of the exact retained undo ledger, followed by a local join that
+releases only proven-native rows from `PROVENANCE_REVIEW`.
diff --git a/evidence/mac2-mirror-provenance-hold-2026-08-30.json b/evidence/mac2-mirror-provenance-hold-2026-08-30.json
new file mode 100644
index 0000000..3bca7b3
--- /dev/null
+++ b/evidence/mac2-mirror-provenance-hold-2026-08-30.json
@@ -0,0 +1,47 @@
+{
+ "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": 34614,
+ "by_class": {
+ "SELF_COPY_DW": 18504,
+ "MINT_RESIDUE_RESCRAPE": 8897,
+ "PROVENANCE_REVIEW": 4082,
+ "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": 21555,
+ "rescrape_program_TK10900": 8962,
+ "provenance_review_TK10896": 4082,
+ "dedup_gated_TK10649": 9,
+ "recoverable_now_staging_link": 6
+ },
+ "self_copy_provenance": {
+ "with_real_mfr_sku_cross_verifiable": 9147,
+ "sku_only_no_independent_check": 12408
+ },
+ "within_batch_shared_candidates": {
+ "note": "BENIGN: same-pattern sellable+sample rows resolving to one shared dw_sku (dw_sku is intentionally non-unique per pattern). These are NOT collisions; an apply step must upsert, not assume per-row uniqueness. True cross-product collisions are counted under *_COLLISION classes.",
+ "shared_candidate_groups": 1276,
+ "rows_in_shared_groups": 2552
+ },
+ "rescrape_cohorts_top": {
+ "Carnegie": 5921,
+ "Maharam": 1429,
+ "Wolf Gordon": 419,
+ "Scalamandre": 418,
+ "CMO Paris": 344,
+ "Stout Textiles": 180,
+ "Designtex": 164,
+ "Pixels": 52,
+ "Scalamandre Wallpaper": 22,
+ "Steve Abrams Studios": 13
+ }
+}
diff --git a/test/classify.test.mjs b/test/classify.test.mjs
index ec3cf74..f264b13 100644
--- a/test/classify.test.mjs
+++ b/test/classify.test.mjs
@@ -2,7 +2,7 @@
// Run: node --test
import { test } from 'node:test';
import assert from 'node:assert/strict';
-import { stripUnitSuffix, classifyRow, UNIT_SUFFIXES, RECOVERY_GROUP } from '../classify.mjs';
+import { stripUnitSuffix, classifyRow, UNIT_SUFFIXES, RECOVERY_GROUP, MIXED_USE_MINT_PREFIXES } from '../classify.mjs';
const ACTIVE = new Set(['DWKE-41415', 'DWCC-116000', 'DWCC-116200']); // simulated live codes
@@ -142,11 +142,27 @@ test('PROVENANCE GUARD: a NON-greenfield DW prefix stays SELF_COPY_DW', () => {
test('PROVENANCE GUARD: greenfield set is configurable via opts', () => {
// Override the default set with a custom one — DWAK becomes guarded, DWAG does not.
- const opts = { greenfieldPrefixes: new Set(['DWAK']) };
+ const opts = { greenfieldPrefixes: new Set(['DWAK']), mixedUsePrefixes: new Set() };
assert.equal(classifyRow({ status: 'active', dw_sku: '', sku: 'DWAK-1-Sample' }, new Set(), opts).class, 'MINT_RESIDUE_RESCRAPE');
assert.equal(classifyRow({ status: 'active', dw_sku: '', sku: 'DWAG-1-Sample' }, new Set(), opts).class, 'SELF_COPY_DW');
});
+test('PROVENANCE HOLD: every mixed-use Phase-4 prefix fails closed for ledger review', () => {
+ for (const prefix of MIXED_USE_MINT_PREFIXES) {
+ const r = classifyRow({ status: 'active', dw_sku: '', sku: `${prefix}-12345-Sample` }, new Set());
+ assert.equal(r.class, 'PROVENANCE_REVIEW', `${prefix} must not self-copy without the undo ledger`);
+ assert.equal(r.candidate, null);
+ assert.equal(r.collides, false);
+ assert.equal(RECOVERY_GROUP[r.class], 'provenance_review_TK10896');
+ }
+});
+
+test('PROVENANCE HOLD: mixed-use set is configurable and does not broaden silently', () => {
+ const opts = { mixedUsePrefixes: new Set(['DWAK']), greenfieldPrefixes: new Set() };
+ assert.equal(classifyRow({ status: 'active', dw_sku: '', sku: 'DWAK-1-Sample' }, new Set(), opts).class, 'PROVENANCE_REVIEW');
+ assert.equal(classifyRow({ status: 'active', dw_sku: '', sku: 'DWKN-1-Sample' }, new Set(), opts).class, 'SELF_COPY_DW');
+});
+
test('BENIGN SHARED CODE: a pattern sellable + its -Sample resolve to the SAME candidate and both stay SELF_COPY_DW (not a collision)', () => {
// Same pattern, two rows (sellable + sample). dw_sku is intentionally shared.
const bare = classifyRow({ status: 'active', dw_sku: '', sku: 'DWPP-206465' }, new Set());
diff --git a/verification/TK-10896-e2e-proof.json b/verification/TK-10896-e2e-proof.json
new file mode 100644
index 0000000..eab10c3
--- /dev/null
+++ b/verification/TK-10896-e2e-proof.json
@@ -0,0 +1,51 @@
+{
+ "ticket": "TK-10896",
+ "intent": "Fail closed on mixed native/minted Phase-4 prefixes until exact undo-ledger provenance is available.",
+ "risk_tier": "R1 local classifier and SELECT-only mirror scan",
+ "environment": "Mac2 local repository and local dw_unified mirror; canonical Kamatera intentionally untouched",
+ "baseline_commit": "58d16de75fb6c10a5ca5ca3bde51319382c3fb3a",
+ "timestamp_utc": "2026-08-30T18:25:41Z",
+ "checks": [
+ {
+ "boundary": "unit",
+ "command": "npm test",
+ "assertion": "27/27 pass, including all six mixed-use prefixes, configurable guard sets, definite-collision precedence, and candidate-origin invariants",
+ "verdict": "PASS"
+ },
+ {
+ "boundary": "database read",
+ "command": "node dwsku-backlog-scan.mjs --out evidence/mac2-mirror-provenance-hold-2026-08-30.json --plan",
+ "assertion": "Scanner issued SELECT-only queries against the local mirror and classified 34,614 active blank rows",
+ "verdict": "PASS"
+ },
+ {
+ "boundary": "full dry-run plan",
+ "command": "node verification/provenance-plan.e2e.mjs evidence/mac2-mirror-provenance-hold-2026-08-30.plan.jsonl",
+ "assertions": [
+ "8,897 greenfield residue rows had no candidate and stayed in rescrape",
+ "4,082 mixed-use rows had no candidate and stayed in provenance review",
+ "2 mixed-use rows with independently definite active-code collisions stayed collision-gated",
+ "all 21,555 self-copy candidates were literal prefixes of their row SKU"
+ ],
+ "verdict": "PASS"
+ },
+ {
+ "boundary": "side effects",
+ "assertions": [
+ "no canonical or Kamatera access",
+ "no UPDATE, INSERT, DELETE, mint, Shopify call, deploy, or destructive cleanup",
+ "generated row-level plan removed after verification; compact summary retained"
+ ],
+ "verdict": "PASS"
+ },
+ {
+ "boundary": "diff integrity",
+ "command": "node --check classify.mjs && node --check dwsku-backlog-scan.mjs && git diff --check",
+ "verdict": "PASS"
+ }
+ ],
+ "critical_path_skips": [],
+ "scope_limit": "Exact canonical undo-ledger export is a future separately authorized read-only action; its absence blocks releasing provenance-review rows but does not block this local fail-closed guard.",
+ "cleanup": "The generated 34,614-row plan JSONL was deleted after all-row assertions; no database or external state was created.",
+ "overall": "PASS"
+}
diff --git a/verification/provenance-plan.e2e.mjs b/verification/provenance-plan.e2e.mjs
new file mode 100644
index 0000000..38ce419
--- /dev/null
+++ b/verification/provenance-plan.e2e.mjs
@@ -0,0 +1,53 @@
+#!/usr/bin/env node
+import { readFileSync } from 'node:fs';
+import { GREENFIELD_MINT_PREFIXES, MIXED_USE_MINT_PREFIXES } from '../classify.mjs';
+
+const planPath = process.argv[2];
+if (!planPath) throw new Error('usage: node verification/provenance-plan.e2e.mjs <plan.jsonl>');
+
+const rows = readFileSync(planPath, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse);
+const counts = {};
+let greenfield = 0;
+let mixedHeld = 0;
+let mixedCollision = 0;
+let selfCopy = 0;
+
+function prefixOf(sku) {
+ return (/^(DW[A-Z0-9]{1,6})-/i.exec(sku || '') || [])[1]?.toUpperCase() || null;
+}
+
+for (const row of rows) {
+ counts[row.class] = (counts[row.class] || 0) + 1;
+ const prefix = prefixOf(row.sku);
+
+ if (prefix && GREENFIELD_MINT_PREFIXES.has(prefix)) {
+ if (row.class !== 'MINT_RESIDUE_RESCRAPE' || row.candidate !== null) {
+ throw new Error(`greenfield row escaped no-mint guard: ${row.sku}`);
+ }
+ greenfield += 1;
+ }
+
+ if (prefix && MIXED_USE_MINT_PREFIXES.has(prefix)) {
+ if (row.class === 'PROVENANCE_REVIEW' && row.candidate === null) mixedHeld += 1;
+ else if (/COLLISION$/.test(row.class) && row.collides === true) mixedCollision += 1;
+ else throw new Error(`mixed-use row escaped provenance hold: ${row.sku} (${row.class})`);
+ }
+
+ if (row.group === 'recoverable_now_self_copy') {
+ if (!row.candidate || !String(row.sku).startsWith(row.candidate)) {
+ throw new Error(`self-copy candidate was not recovered from its row: ${row.sku}`);
+ }
+ selfCopy += 1;
+ }
+}
+
+const result = {
+ ok: true,
+ rows: rows.length,
+ greenfield_residue_held_for_rescrape: greenfield,
+ mixed_use_held_for_ledger_review: mixedHeld,
+ mixed_use_definite_collisions: mixedCollision,
+ self_copy_candidates_proven_from_row: selfCopy,
+ by_class: counts,
+};
+process.stdout.write(JSON.stringify(result, null, 2) + '\n');
← 58d16de Revert "TK-10896: record Claude-Codex no-mint comparison pro
·
back to Dw Sku Integrity
·
TK-10896 add exact ledger provenance join harness 9978a12 →