[object Object]

← back to Dw Kravet Hires

apply-hires: rollback self-scopes by --ticket (shared-ledger over-revert fix)

1ee7e7352e7ca60e41a09d300ee0113491a74b34 · 2026-09-15 17:53:34 -0700 · Steve Abrams

data/apply-ledger.jsonl accumulates every phase's swaps (TK-11658 1546 +
TK-11740 84 + TK-11742 28 = 1658), so the old undo reverted ALL of them.
rollback() now reverses only TICKET's entries by default; --all-tickets restores
the whole-ledger behavior explicitly; baked-in undo_cmd now carries --ticket so
each run's recorded undo self-scopes. Guards exit 2 on an unknown ticket.
Verified dry-run: TK-11740->84, TK-11742->28, --all-tickets->1658, all of 1658.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgXb7b5SZbb9MNZwNZW94q

Files touched

Diff

commit 1ee7e7352e7ca60e41a09d300ee0113491a74b34
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 15 17:53:34 2026 -0700

    apply-hires: rollback self-scopes by --ticket (shared-ledger over-revert fix)
    
    data/apply-ledger.jsonl accumulates every phase's swaps (TK-11658 1546 +
    TK-11740 84 + TK-11742 28 = 1658), so the old undo reverted ALL of them.
    rollback() now reverses only TICKET's entries by default; --all-tickets restores
    the whole-ledger behavior explicitly; baked-in undo_cmd now carries --ticket so
    each run's recorded undo self-scopes. Guards exit 2 on an unknown ticket.
    Verified dry-run: TK-11740->84, TK-11742->28, --all-tickets->1658, all of 1658.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01HgXb7b5SZbb9MNZwNZW94q
---
 scripts/apply-hires.mjs | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/scripts/apply-hires.mjs b/scripts/apply-hires.mjs
index 589534e..6bc29b2 100644
--- a/scripts/apply-hires.mjs
+++ b/scripts/apply-hires.mjs
@@ -54,6 +54,7 @@ const AGENT = process.env.TK_AGENT || 'night-batch1-dwcommerce';
 
 const LIVE = has('--live');
 const ROLLBACK = has('--rollback');
+const ALL_TICKETS = has('--all-tickets'); // rollback: revert EVERY ticket in the shared ledger (old behavior; explicit only)
 const ONLY_SWAPPABLE = has('--only-swappable');
 const BATCH = parseInt(val('--batch', '200'), 10);
 const GAP = parseInt(val('--gap', '90'), 10);      // seconds between batches (live only)
@@ -209,7 +210,7 @@ async function applyLive(rows) {
         appendExecLedger({ ts: rec.ts, agent: AGENT, ticket: TICKET,
           action: `featured-image swap ${r.mfr_sku}: ${oldMediaId} -> ${newMediaId}`,
           blast_radius: 1,
-          undo_cmd: `node scripts/apply-hires.mjs --rollback --ledger ${path.relative(PROJ, LEDGER_PATH)} --live`,
+          undo_cmd: `node scripts/apply-hires.mjs --rollback --ticket ${TICKET} --ledger ${path.relative(PROJ, LEDGER_PATH)} --live`,
           verify: `product ${gid} featured media == ${newMediaId} (hi-res); old ${oldMediaId} retained` });
         applied++;
         console.log(`  ✓ ${r.mfr_sku}  featured -> hi-res (old ${oldMediaId} kept)`);
@@ -228,10 +229,18 @@ async function applyLive(rows) {
 // ------------------------------- rollback ------------------------------------
 async function rollback() {
   if (!fs.existsSync(LEDGER_PATH)) { console.error(`no ledger at ${LEDGER_PATH}`); process.exit(2); }
-  const recs = fs.readFileSync(LEDGER_PATH, 'utf8').trim().split('\n').filter(Boolean).map((l) => JSON.parse(l))
+  const all = fs.readFileSync(LEDGER_PATH, 'utf8').trim().split('\n').filter(Boolean).map((l) => JSON.parse(l))
     .filter((r) => r.old_media_id && r.shopify_id);
+  // SCOPED BY DEFAULT: the shared apply-ledger.jsonl accumulates every ticket's swaps
+  // (TK-11658 + TK-11740 + TK-11742 + …), so an unscoped rollback over-reverts sibling
+  // phases. Reverse only THIS ticket's entries unless --all-tickets is explicitly passed.
+  const recs = ALL_TICKETS ? all : all.filter((r) => r.ticket === TICKET);
+  const scope = ALL_TICKETS ? 'ALL tickets' : `--ticket ${TICKET} only`;
   console.log(`\n=== ${TICKET} apply-hires — ROLLBACK ${LIVE ? '(LIVE)' : '(dry-run)'} ===`);
-  console.log(`ledger: ${LEDGER_PATH}  (${recs.length} swaps to reverse)`);
+  console.log(`ledger: ${LEDGER_PATH}  (scope: ${scope} — ${recs.length} of ${all.length} swaps to reverse)`);
+  if (!ALL_TICKETS && recs.length === 0) {
+    console.error(`no ledger entries for ticket ${TICKET}. Pass --ticket <TK-...> or --all-tickets.`); process.exit(2);
+  }
   if (!LIVE) {
     recs.slice(0, 15).forEach((r) => console.log(`  would re-point ${r.mfr_sku} featured -> old_media_id ${r.old_media_id}`));
     if (recs.length > 15) console.log(`  … +${recs.length - 15} more …`);

← 0c1deb7 TK-11740: post-apply review — scoped rollback ledger (84) +  ·  back to Dw Kravet Hires  ·  TK-11658 cycle-4 ground truth (2026-09-17/18): Phase-1 remai 9caaaac →