[object Object]

← back to Designerwallcoverings

TK-11644: write-ahead the rollback row before the gated delete

71fc81e518df302fa73623826997c1567da13d75 · 2026-09-16 14:15:58 -0700 · Steve Abrams

The ledger append sat inside the same try as the Shopify delete, so a delete that
SUCCEEDED followed by a failing append would lose the undo record for a completed
live customer-facing write (unrecoverable) and double-count the row as an error
after ok++ had already run. Now the undo row is written BEFORE the delete, in its
own try: a ledger failure aborts that row's delete entirely (fail-safe), and a
write-ahead row for a delete that then fails is harmless because its undo re-sets
the value to what it already is. Also guards the per-row gql() __err shape.

Dry-run re-verified identical: 5 DELETE / 6 LEAVE / 0 skipped, no write fired.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NbiFp4yxwRthCmDhbkwQnu

Files touched

Diff

commit 71fc81e518df302fa73623826997c1567da13d75
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 16 14:15:58 2026 -0700

    TK-11644: write-ahead the rollback row before the gated delete
    
    The ledger append sat inside the same try as the Shopify delete, so a delete that
    SUCCEEDED followed by a failing append would lose the undo record for a completed
    live customer-facing write (unrecoverable) and double-count the row as an error
    after ok++ had already run. Now the undo row is written BEFORE the delete, in its
    own try: a ledger failure aborts that row's delete entirely (fail-safe), and a
    write-ahead row for a delete that then fails is harmless because its undo re-sets
    the value to what it already is. Also guards the per-row gql() __err shape.
    
    Dry-run re-verified identical: 5 DELETE / 6 LEAVE / 0 skipped, no write fired.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01NbiFp4yxwRthCmDhbkwQnu
---
 assets/TK-11644-identity-manifest.json |  2 +-
 scripts/tk11644-identity-fix.mjs       | 16 ++++++++++++----
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/assets/TK-11644-identity-manifest.json b/assets/TK-11644-identity-manifest.json
index 7fd2e56..d4dcd21 100644
--- a/assets/TK-11644-identity-manifest.json
+++ b/assets/TK-11644-identity-manifest.json
@@ -1,5 +1,5 @@
 {
- "generated_at": "2026-09-16T21:09:12.145Z",
+ "generated_at": "2026-09-16T21:15:31.866Z",
  "will_delete": 5,
  "skipped": [],
  "leave": [
diff --git a/scripts/tk11644-identity-fix.mjs b/scripts/tk11644-identity-fix.mjs
index d756fa8..41c8323 100644
--- a/scripts/tk11644-identity-fix.mjs
+++ b/scripts/tk11644-identity-fix.mjs
@@ -74,15 +74,23 @@ const ledger=process.env.HOME+'/.claude/yolo-queue/executed-reversible/ledger.js
 let ok=0,err=0;
 for(const v of verified){
   const q=`mutation { metafieldsDelete(metafields:[{ownerId:${JSON.stringify(v.gid)},namespace:"global",key:"description_tag"}]) { deletedMetafields { key } userErrors { field message } } }`;
+  // WRITE-AHEAD the undo record BEFORE firing the delete. If the ledger append were done
+  // after a successful delete (inside the same try), a failed append would lose the rollback
+  // row for a delete that ALREADY happened — unrecoverable — and would also double-count the
+  // row as an error after ok++ had run. A write-ahead row for a delete that then fails is
+  // harmless by comparison: its undo re-sets the value to what it already is, a no-op.
   try{
-    const r=await gql(q);
-    const ue=r.metafieldsDelete.userErrors;
-    if(ue && ue.length){ err++; console.error('userErrors',v.handle,JSON.stringify(ue)); continue; }
-    ok++;
     fs.appendFileSync(ledger,JSON.stringify({ts:new Date().toISOString(),agent:'claude-run-11644',ticket:'TK-11644',
       action:'delete global.description_tag (identity-contradiction residual)',pid:v.pid,handle:v.handle,blast_radius:1,
       undo_cmd:`re-set global.description_tag=${JSON.stringify(v.old_value)} on product ${v.pid}`,
       verify:`storefront meta description now falls back to body_html`})+'\n');
+  }catch(e){ err++; console.error('LEDGER-FAIL (delete NOT attempted)',v.handle,e.message); continue; }
+  try{
+    const r=await gql(q);
+    if(r && r.__err){ err++; console.error('GraphQL error',v.handle,JSON.stringify(r.__err)); continue; }
+    const ue=r.metafieldsDelete.userErrors;
+    if(ue && ue.length){ err++; console.error('userErrors',v.handle,JSON.stringify(ue)); continue; }
+    ok++;
   }catch(e){ err++; console.error('ERR',v.handle,e.message); }
 }
 console.log(`\nAPPLIED: deleted=${ok} err=${err}`);

← d8ce050 TK-11644: add missing gql() error-object guard to postfix-ve  ·  back to Designerwallcoverings  ·  TK-11357: stop the action-C tripwire fabricating success rec ef068c8 →