[object Object]

← back to Tk 10965 Zero Price Analysis

snapshot concurrent zero-price remediation evidence

6696093258b2f5a3f1fc583d2054994546cd021f · 2026-08-30 17:40:45 -0700 · Steve Abrams

Files touched

Diff

commit 6696093258b2f5a3f1fc583d2054994546cd021f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Aug 30 17:40:45 2026 -0700

    snapshot concurrent zero-price remediation evidence
---
 apply-fix.mjs                             | 23 +++++++++++++---
 evidence/credential-scope-2026-08-30.json | 17 ++++++++++++
 evidence/verify-latest.json               |  2 +-
 verification/e2e-proof.json               | 44 +++++++++++++++++++++++++++++++
 4 files changed, 81 insertions(+), 5 deletions(-)

diff --git a/apply-fix.mjs b/apply-fix.mjs
index 2f0539f..7719680 100644
--- a/apply-fix.mjs
+++ b/apply-fix.mjs
@@ -19,6 +19,8 @@ const val = k => (env.match(new RegExp('^' + k + '=(.*)$', 'm')) || [])[1]?.trim
 const DOM = val('SHOPIFY_STORE_DOMAIN'), TOK = val('SHOPIFY_ADMIN_TOKEN');
 const API = `https://${DOM}/admin/api/2024-10/graphql.json`;
 
+if (!DOM || !TOK) throw new Error('Missing SHOPIFY_STORE_DOMAIN or SHOPIFY_ADMIN_TOKEN');
+
 async function gql(q, v) {
   for (let a = 0; a < 8; a++) {
     const r = await fetch(API, { method: 'POST', headers: { 'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: q, variables: v }) });
@@ -58,9 +60,18 @@ async function enumerateBad() {
   return out;
 }
 
-async function setOnHand(inventoryItemId, locationId, quantity) {
+async function requireInventoryWriteScope() {
+  const d = await gql(`query{currentAppInstallation{accessScopes{handle}}}`, {});
+  const scopes = d.currentAppInstallation.accessScopes.map(s => s.handle).sort();
+  if (!scopes.includes('write_inventory')) {
+    throw new Error(`BLOCKED: token lacks write_inventory (present scopes: ${scopes.join(', ')})`);
+  }
+  return scopes;
+}
+
+async function setOnHand(inventoryItemId, locationId, compareQuantity, quantity) {
   const d = await gql(`mutation($input:InventorySetQuantitiesInput!){inventorySetQuantities(input:$input){userErrors{field message code}}}`,
-    { input: { name: 'on_hand', reason: 'correction', ignoreCompareQuantity: true, quantities: [{ inventoryItemId, locationId, quantity }] } });
+    { input: { name: 'on_hand', reason: 'correction', quantities: [{ inventoryItemId, locationId, compareQuantity, quantity }] } });
   const errs = d.inventorySetQuantities.userErrors;
   if (errs && errs.length) throw new Error(JSON.stringify(errs));
 }
@@ -69,8 +80,9 @@ const stamp = () => new Date().toISOString().replace(/[:.]/g, '-');
 const arg = process.argv[2], arg2 = process.argv[3];
 
 if (arg === '--rollback') {
+  await requireInventoryWriteScope();
   const map = JSON.parse(fs.readFileSync(arg2, 'utf8'));
-  let n = 0; for (const it of map.items) for (const l of it.levels) { await setOnHand(it.inventoryItemId, l.locationId, l.onHand); n++; }
+  let n = 0; for (const it of map.items) for (const l of it.levels) { await setOnHand(it.inventoryItemId, l.locationId, 0, l.onHand); n++; }
   console.log(`ROLLBACK: restored ${n} inventory levels from ${arg2}`);
   process.exit(0);
 }
@@ -84,6 +96,9 @@ console.log('by vendor:', JSON.stringify(byV));
 
 if (arg === '--enumerate') { console.log('Enumerate-only. No writes.'); process.exit(0); }
 
+const scopes = await requireInventoryWriteScope();
+console.log(`Inventory write scope verified (${scopes.length} total scopes).`);
+
 const limit = arg === '--canary' ? (Number(arg2) || 50) : bad.length;
 const target = bad.slice(0, limit);
 console.log(`Applying on_hand=0 to ${target.length} products (${arg})...`);
@@ -91,7 +106,7 @@ let done = 0, fail = 0;
 for (const b of target) {
   try {
     if (!b.levels.length) { done++; continue; } // already 0 everywhere
-    for (const l of b.levels) await setOnHand(b.inventoryItemId, l.locationId, 0);
+    for (const l of b.levels) await setOnHand(b.inventoryItemId, l.locationId, l.onHand, 0);
     done++;
     if (done % 100 === 0) console.log(`  ...${done}/${target.length}`);
   } catch (e) { fail++; console.error(`  FAIL ${b.title}: ${e.message}`); }
diff --git a/evidence/credential-scope-2026-08-30.json b/evidence/credential-scope-2026-08-30.json
new file mode 100644
index 0000000..ee12233
--- /dev/null
+++ b/evidence/credential-scope-2026-08-30.json
@@ -0,0 +1,17 @@
+{
+  "ticket": "TK-10963",
+  "checked_at": "2026-08-30T17:37:00-07:00",
+  "shop": "Designer Wallcoverings and Fabrics",
+  "authenticated": true,
+  "api_status": 200,
+  "scopes": [
+    "read_products",
+    "read_publications",
+    "write_products",
+    "write_publications"
+  ],
+  "required_scope": "write_inventory",
+  "can_execute_inventory_remediation": false,
+  "verdict": "BLOCKED",
+  "note": "No secret value is stored in this evidence file. No mutation was attempted."
+}
diff --git a/evidence/verify-latest.json b/evidence/verify-latest.json
index 5f946dd..264dbcb 100644
--- a/evidence/verify-latest.json
+++ b/evidence/verify-latest.json
@@ -1,5 +1,5 @@
 {
-  "ts": "2026-08-30T21:28:04.435Z",
+  "ts": "2026-08-31T00:39:04.098Z",
   "total": 1743,
   "qty2026": 1743,
   "canary_blind": 462,
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
new file mode 100644
index 0000000..2cc7bbd
--- /dev/null
+++ b/verification/e2e-proof.json
@@ -0,0 +1,44 @@
+{
+  "intent": "Revalidate the live zero-price-orderable cohort and execute a restore-map-first CAS canary only when inventory credentials are authorized.",
+  "risk_tier": "R4",
+  "environment": "Designer Wallcoverings Shopify production, read-only preflight",
+  "ticket": "TK-10963",
+  "authorization_ticket": "TK-10979",
+  "timestamp": "2026-08-31T00:39:04.097Z",
+  "baseline": {
+    "affected_total": 1743,
+    "phillipe_romano": 1281,
+    "fentucci_naturals": 462,
+    "quantity_2026": 1743
+  },
+  "checks": [
+    {
+      "boundary": "Shopify authentication and scopes",
+      "verdict": "PASS",
+      "assertion": "Token authenticated with HTTP 200 and scopes were enumerated without exposing the token."
+    },
+    {
+      "boundary": "Inventory authorization",
+      "verdict": "FAIL",
+      "assertion": "Required write_inventory scope is absent; present scopes are read_products, read_publications, write_products, write_publications."
+    },
+    {
+      "boundary": "Live defect cohort",
+      "verdict": "PASS",
+      "assertion": "Read-only verification found exactly 1,743 active zero-price orderable non-sample variants; all 1,743 have quantity 2026."
+    },
+    {
+      "boundary": "Restore-map-first CAS canary",
+      "verdict": "SKIP",
+      "assertion": "Critical path intentionally not attempted because inventory write authorization failed."
+    },
+    {
+      "boundary": "Side effects",
+      "verdict": "PASS",
+      "assertion": "No Shopify mutation, email, paid API, database write, deploy, or schedule change occurred."
+    }
+  ],
+  "overall_verdict": "BLOCKED",
+  "exact_blocker": "SHOPIFY_ADMIN_TOKEN lacks write_inventory.",
+  "safest_next_action": "Provision or route an approved Shopify Admin token with read_products and write_inventory, rerun scope preflight, then run enumerate -> small CAS canary -> read-only verification before any scale-up."
+}

← ae6d884 auto-data-snapshot: 2026-08-30T15:20:13 (1 data files) — tk1  ·  back to Tk 10965 Zero Price Analysis  ·  paginate zero-price canary variants f46558f →