← back to Rebel Walls Push
TK-11248 prep: revalidate + gated executor + rollback for TK-11238 (Aries title) + TK-10405 (12 RW Default-Title deletes)
3111ca518ea364d6e1fbf3f17eaff8259caddde8 · 2026-09-04 11:00:57 -0700 · Steve Abrams
Read-only revalidation confirms both live targets exactly match characterization
(Aries ACTIVE, old title verbatim; 12 products each 3 variants -> 1 Default-Title
+ distinct Mural + Sample survivors). Executor is DRY-RUN by default and hard-gated:
--apply requires --approval=TK-11248 + a sentinel token file written only after
Steve's explicit approval on TK-11248 (proven: premature --apply exits 3, no write).
Restore maps written for one-command rollback. NO Shopify writes fired.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HzpfrKkjFqGRUrMEQGhFFW
Files touched
A data/TK-10405-restore-map.jsonA data/TK-11238-restore.jsonA data/TK11248-revalidation-2026-09-04T17-58-27-571Z.jsonA scripts/execute-TK11248.jsA scripts/revalidate-TK11248.jsA scripts/rollback-TK11248.js
Diff
commit 3111ca518ea364d6e1fbf3f17eaff8259caddde8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Sep 4 11:00:57 2026 -0700
TK-11248 prep: revalidate + gated executor + rollback for TK-11238 (Aries title) + TK-10405 (12 RW Default-Title deletes)
Read-only revalidation confirms both live targets exactly match characterization
(Aries ACTIVE, old title verbatim; 12 products each 3 variants -> 1 Default-Title
+ distinct Mural + Sample survivors). Executor is DRY-RUN by default and hard-gated:
--apply requires --approval=TK-11248 + a sentinel token file written only after
Steve's explicit approval on TK-11248 (proven: premature --apply exits 3, no write).
Restore maps written for one-command rollback. NO Shopify writes fired.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HzpfrKkjFqGRUrMEQGhFFW
---
data/TK-10405-restore-map.json | 176 +++++
data/TK-11238-restore.json | 9 +
...1248-revalidation-2026-09-04T17-58-27-571Z.json | 750 +++++++++++++++++++++
scripts/execute-TK11248.js | 149 ++++
scripts/revalidate-TK11248.js | 133 ++++
scripts/rollback-TK11248.js | 57 ++
6 files changed, 1274 insertions(+)
diff --git a/data/TK-10405-restore-map.json b/data/TK-10405-restore-map.json
new file mode 100644
index 0000000..669c0ce
--- /dev/null
+++ b/data/TK-10405-restore-map.json
@@ -0,0 +1,176 @@
+{
+ "ticket": "TK-10405",
+ "ts": "2026-09-04T17:59:09.927Z",
+ "store": "designer-laboratory-sandbox.myshopify.com",
+ "note": "Restore = productVariantsBulkCreate each entry back onto its product. Shopify assigns a NEW variant id on recreate; SKU/price/options are restored.",
+ "variants": [
+ {
+ "pid": "6679729078323",
+ "product_gid": "gid://shopify/Product/6679729078323",
+ "label": "A City Rises (Class 2 $80.48 anomaly)",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453186830387",
+ "sku": "DWRW-76016-Sample",
+ "price": "80.48",
+ "options": [
+ {
+ "name": "Size",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "6679739629619",
+ "product_gid": "gid://shopify/Product/6679739629619",
+ "label": "A Priori",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453188206643",
+ "sku": "DWRW-76264-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Size",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164303411",
+ "product_gid": "gid://shopify/Product/7851164303411",
+ "label": "3D Art - Multi",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453185060915",
+ "sku": "DWRW-452034-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164336179",
+ "product_gid": "gid://shopify/Product/7851164336179",
+ "label": "3D Boxes - Gray",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453185093683",
+ "sku": "DWRW-451853-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164368947",
+ "product_gid": "gid://shopify/Product/7851164368947",
+ "label": "3D Wave - Gray",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453185126451",
+ "sku": "DWRW-451205-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164401715",
+ "product_gid": "gid://shopify/Product/7851164401715",
+ "label": "3D Wave - Marble",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453185159219",
+ "sku": "DWRW-451203-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164434483",
+ "product_gid": "gid://shopify/Product/7851164434483",
+ "label": "3D Wave - Sand",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453185191987",
+ "sku": "DWRW-451204-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164500019",
+ "product_gid": "gid://shopify/Product/7851164500019",
+ "label": "3D Wave - White",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453185224755",
+ "sku": "DWRW-451206-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164532787",
+ "product_gid": "gid://shopify/Product/7851164532787",
+ "label": "70S Retro - Beige",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453186633779",
+ "sku": "DWRW-452709-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164565555",
+ "product_gid": "gid://shopify/Product/7851164565555",
+ "label": "A City Rises - Dark Blue",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453186764851",
+ "sku": "DWRW-450400-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164598323",
+ "product_gid": "gid://shopify/Product/7851164598323",
+ "label": "A Priori - Taupe",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453188075571",
+ "sku": "DWRW-360008-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ },
+ {
+ "pid": "7851164631091",
+ "product_gid": "gid://shopify/Product/7851164631091",
+ "label": "A Priori - Soft",
+ "delete_variant_gid": "gid://shopify/ProductVariant/44453188042803",
+ "sku": "DWRW-452814-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/data/TK-11238-restore.json b/data/TK-11238-restore.json
new file mode 100644
index 0000000..2e068eb
--- /dev/null
+++ b/data/TK-11238-restore.json
@@ -0,0 +1,9 @@
+{
+ "ticket": "TK-11238",
+ "ts": "2026-09-04T17:59:09.975Z",
+ "store": "designer-laboratory-sandbox.myshopify.com",
+ "product_id": "7800009752627",
+ "product_gid": "gid://shopify/Product/7800009752627",
+ "restore_title": "Aries - Single Color Commercial Wallcovering | DW Commercial Surfaces",
+ "proposed_title": "Aries - Akoya Type II Vinyl Wallcovering | Hollywood Wallcoverings"
+}
\ No newline at end of file
diff --git a/data/TK11248-revalidation-2026-09-04T17-58-27-571Z.json b/data/TK11248-revalidation-2026-09-04T17-58-27-571Z.json
new file mode 100644
index 0000000..e51c0d6
--- /dev/null
+++ b/data/TK11248-revalidation-2026-09-04T17-58-27-571Z.json
@@ -0,0 +1,750 @@
+{
+ "ts": "2026-09-04T17-58-27-571Z",
+ "store": "designer-laboratory-sandbox.myshopify.com",
+ "tk11238": {
+ "id": "7800009752627",
+ "gid": "gid://shopify/Product/7800009752627",
+ "handle": "aries-pvc-free-wallcovering",
+ "vendor": "Hollywood Wallcoverings",
+ "status": "ACTIVE",
+ "current_title": "Aries - Single Color Commercial Wallcovering | DW Commercial Surfaces",
+ "expected_old_title": "Aries - Single Color Commercial Wallcovering | DW Commercial Surfaces",
+ "proposed_new_title": "Aries - Akoya Type II Vinyl Wallcovering | Hollywood Wallcoverings",
+ "title_matches_expected": true,
+ "is_active": true,
+ "undo": {
+ "field": "title",
+ "restore_to": "Aries - Single Color Commercial Wallcovering | DW Commercial Surfaces"
+ }
+ },
+ "tk10405": {
+ "products": [
+ {
+ "pid": "6679729078323",
+ "label": "A City Rises (Class 2 $80.48 anomaly)",
+ "product_status": "ACTIVE",
+ "handle": "dwrw-76016",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453186830387",
+ "legacyId": "44453186830387",
+ "sku": "DWRW-76016-Sample",
+ "price": "80.48",
+ "options": [
+ {
+ "name": "Size",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44486915063859",
+ "sku": "DWRW-76016",
+ "price": "117.47"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44453186830387",
+ "sku": "DWRW-76016-Sample",
+ "price": "80.48"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453186830387",
+ "title": "Default Title",
+ "sku": "DWRW-76016-Sample",
+ "price": "80.48"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44486915063859",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-76016",
+ "price": "117.47"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/40347630403635",
+ "title": "Sample",
+ "sku": "DWRW-76016-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "6679739629619",
+ "label": "A Priori",
+ "product_status": "ACTIVE",
+ "handle": "dwrw-76264",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453188206643",
+ "legacyId": "44453188206643",
+ "sku": "DWRW-76264-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Size",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44490271719475",
+ "sku": "DWRW-76264",
+ "price": "117.47"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44453188206643",
+ "sku": "DWRW-76264-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453188206643",
+ "title": "Default Title",
+ "sku": "DWRW-76264-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44490271719475",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-76264",
+ "price": "117.47"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/40347693940787",
+ "title": "Sample",
+ "sku": "DWRW-76264-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164303411",
+ "label": "3D Art - Multi",
+ "product_status": "ACTIVE",
+ "handle": "3d-art-multi-dwrw-360000",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453185060915",
+ "legacyId": "44453185060915",
+ "sku": "DWRW-452034-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405346467891",
+ "sku": "DWRW-452034",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405346500659",
+ "sku": "DWRW-452034-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405346467891",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-452034",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405346500659",
+ "title": "Sample",
+ "sku": "DWRW-452034-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453185060915",
+ "title": "Default Title",
+ "sku": "DWRW-452034-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164336179",
+ "label": "3D Boxes - Gray",
+ "product_status": "ACTIVE",
+ "handle": "3d-boxes-gray-dwrw-360001",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453185093683",
+ "legacyId": "44453185093683",
+ "sku": "DWRW-451853-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405346533427",
+ "sku": "DWRW-451853",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405346566195",
+ "sku": "DWRW-451853-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405346533427",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-451853",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405346566195",
+ "title": "Sample",
+ "sku": "DWRW-451853-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453185093683",
+ "title": "Default Title",
+ "sku": "DWRW-451853-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164368947",
+ "label": "3D Wave - Gray",
+ "product_status": "ACTIVE",
+ "handle": "3d-wave-gray-dwrw-360002",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453185126451",
+ "legacyId": "44453185126451",
+ "sku": "DWRW-451205-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405346598963",
+ "sku": "DWRW-451205",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405346631731",
+ "sku": "DWRW-451205-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405346598963",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-451205",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405346631731",
+ "title": "Sample",
+ "sku": "DWRW-451205-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453185126451",
+ "title": "Default Title",
+ "sku": "DWRW-451205-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164401715",
+ "label": "3D Wave - Marble",
+ "product_status": "ACTIVE",
+ "handle": "3d-wave-marble-dwrw-360003",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453185159219",
+ "legacyId": "44453185159219",
+ "sku": "DWRW-451203-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405346664499",
+ "sku": "DWRW-451203",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405346697267",
+ "sku": "DWRW-451203-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405346664499",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-451203",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405346697267",
+ "title": "Sample",
+ "sku": "DWRW-451203-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453185159219",
+ "title": "Default Title",
+ "sku": "DWRW-451203-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164434483",
+ "label": "3D Wave - Sand",
+ "product_status": "ACTIVE",
+ "handle": "3d-wave-sand-dwrw-360004",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453185191987",
+ "legacyId": "44453185191987",
+ "sku": "DWRW-451204-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405346730035",
+ "sku": "DWRW-451204",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405346762803",
+ "sku": "DWRW-451204-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405346730035",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-451204",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405346762803",
+ "title": "Sample",
+ "sku": "DWRW-451204-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453185191987",
+ "title": "Default Title",
+ "sku": "DWRW-451204-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164500019",
+ "label": "3D Wave - White",
+ "product_status": "ACTIVE",
+ "handle": "3d-wave-white-dwrw-360005",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453185224755",
+ "legacyId": "44453185224755",
+ "sku": "DWRW-451206-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405347090483",
+ "sku": "DWRW-451206",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405347123251",
+ "sku": "DWRW-451206-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405347090483",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-451206",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405347123251",
+ "title": "Sample",
+ "sku": "DWRW-451206-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453185224755",
+ "title": "Default Title",
+ "sku": "DWRW-451206-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164532787",
+ "label": "70S Retro - Beige",
+ "product_status": "ACTIVE",
+ "handle": "70s-retro-beige-dwrw-360006",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453186633779",
+ "legacyId": "44453186633779",
+ "sku": "DWRW-452709-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405347156019",
+ "sku": "DWRW-452709",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405347188787",
+ "sku": "DWRW-452709-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405347156019",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-452709",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405347188787",
+ "title": "Sample",
+ "sku": "DWRW-452709-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453186633779",
+ "title": "Default Title",
+ "sku": "DWRW-452709-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164565555",
+ "label": "A City Rises - Dark Blue",
+ "product_status": "ACTIVE",
+ "handle": "a-city-rises-dwrw-360007",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453186764851",
+ "legacyId": "44453186764851",
+ "sku": "DWRW-450400-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405347221555",
+ "sku": "DWRW-450400",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405347254323",
+ "sku": "DWRW-450400-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405347221555",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-450400",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405347254323",
+ "title": "Sample",
+ "sku": "DWRW-450400-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453186764851",
+ "title": "Default Title",
+ "sku": "DWRW-450400-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164598323",
+ "label": "A Priori - Taupe",
+ "product_status": "ACTIVE",
+ "handle": "a-priori-dwrw-360008",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453188075571",
+ "legacyId": "44453188075571",
+ "sku": "DWRW-360008-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405347287091",
+ "sku": "DWRW-360008",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405347319859",
+ "sku": "DWRW-360008-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405347287091",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-360008",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405347319859",
+ "title": "Sample",
+ "sku": "DWRW-360008-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453188075571",
+ "title": "Default Title",
+ "sku": "DWRW-360008-Sample",
+ "price": "4.25"
+ }
+ ]
+ },
+ {
+ "pid": "7851164631091",
+ "label": "A Priori - Soft",
+ "product_status": "ACTIVE",
+ "handle": "a-priori-soft-dwrw-360009",
+ "variant_count": 3,
+ "default_title_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44453188042803",
+ "legacyId": "44453188042803",
+ "sku": "DWRW-452814-Sample",
+ "price": "4.25",
+ "options": [
+ {
+ "name": "Title",
+ "value": "Default Title"
+ }
+ ]
+ }
+ ],
+ "has_mural_survivor": true,
+ "mural": {
+ "id": "gid://shopify/ProductVariant/44405347352627",
+ "sku": "DWRW-452814",
+ "price": "191.00"
+ },
+ "has_sample_survivor": true,
+ "sample": {
+ "id": "gid://shopify/ProductVariant/44405347385395",
+ "sku": "DWRW-452814-Sample",
+ "price": "4.25"
+ },
+ "all_variants": [
+ {
+ "id": "gid://shopify/ProductVariant/44405347352627",
+ "title": "Mural (per m²)",
+ "sku": "DWRW-452814",
+ "price": "191.00"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44405347385395",
+ "title": "Sample",
+ "sku": "DWRW-452814-Sample",
+ "price": "4.25"
+ },
+ {
+ "id": "gid://shopify/ProductVariant/44453188042803",
+ "title": "Default Title",
+ "sku": "DWRW-452814-Sample",
+ "price": "4.25"
+ }
+ ]
+ }
+ ],
+ "per_product_safe": [
+ {
+ "pid": "6679729078323",
+ "label": "A City Rises (Class 2 $80.48 anomaly)",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "6679739629619",
+ "label": "A Priori",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164303411",
+ "label": "3D Art - Multi",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164336179",
+ "label": "3D Boxes - Gray",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164368947",
+ "label": "3D Wave - Gray",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164401715",
+ "label": "3D Wave - Marble",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164434483",
+ "label": "3D Wave - Sand",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164500019",
+ "label": "3D Wave - White",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164532787",
+ "label": "70S Retro - Beige",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164565555",
+ "label": "A City Rises - Dark Blue",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164598323",
+ "label": "A Priori - Taupe",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ },
+ {
+ "pid": "7851164631091",
+ "label": "A Priori - Soft",
+ "ok": true,
+ "dt_count": 1,
+ "variant_count": 3
+ }
+ ]
+ },
+ "gate": {
+ "ready_11238": true,
+ "ready_10405": true
+ }
+}
\ No newline at end of file
diff --git a/scripts/execute-TK11248.js b/scripts/execute-TK11248.js
new file mode 100644
index 0000000..f0281cd
--- /dev/null
+++ b/scripts/execute-TK11248.js
@@ -0,0 +1,149 @@
+'use strict';
+/* GATED executor for the TK-11248 batch. Serializes:
+ * Step 1 = TK-11238 Aries title correction (productUpdate)
+ * Step 2 = TK-10405 delete 12 Rebel Walls Default-Title variants (productVariantsBulkDelete)
+ *
+ * SAFETY:
+ * - DRY-RUN by default. Prints the exact diff and exits without writing.
+ * - To write, ALL of these must hold:
+ * --apply
+ * --approval=TK-11248 (must match the recorded approval ticket)
+ * env RW_APPROVAL_TOKEN must equal the token written into the approval
+ * sentinel file data/TK-11248-APPROVAL.json by the operator AFTER Steve's
+ * explicit exact approval is logged on TK-11248.
+ * - Re-revalidates each target live immediately before writing (title still
+ * matches old; product still has exactly the 1 Default-Title variant + Mural
+ * + Sample survivors). Refuses the individual write if drift is detected.
+ * - One product at a time; verifies userErrors empty + post-state after each.
+ * - Logts every write to ~/.claude/yolo-queue/executed-reversible/ledger.jsonl.
+ *
+ * Usage:
+ * node execute-TK11248.js # dry-run both steps
+ * node execute-TK11248.js --step=11238 # dry-run only the title
+ * node execute-TK11248.js --step=10405 # dry-run only the deletes
+ * node execute-TK11248.js --apply --approval=TK-11248 # LIVE (needs sentinel)
+ */
+const fs = require('fs');
+const path = require('path');
+const { execFileSync } = require('child_process');
+const { gqlRetry, sleep, DOMAIN } = require('./_shop');
+
+const DATA = path.join(__dirname, '..', 'data');
+const argv = process.argv.slice(2);
+const has = f => argv.includes(f);
+const val = k => { const a = argv.find(x => x.startsWith(k + '=')); return a ? a.split('=')[1] : null; };
+const APPLY = has('--apply');
+const STEP = val('--step'); // '11238' | '10405' | null(both)
+const APPROVAL = val('--approval');
+const LEDGER = '/Users/macstudio3/.claude/yolo-queue/executed-reversible/ledger.jsonl';
+const SENTINEL = path.join(DATA, 'TK-11248-APPROVAL.json');
+
+const ARIES_GID = 'gid://shopify/Product/7800009752627';
+const ARIES_OLD = 'Aries - Single Color Commercial Wallcovering | DW Commercial Surfaces';
+const ARIES_NEW = 'Aries - Akoya Type II Vinyl Wallcovering | Hollywood Wallcoverings';
+
+const PRODUCT_Q = `query($id: ID!) { product(id:$id){ id legacyResourceId title status vendor
+ variants(first:20){ nodes{ id title sku price } } } }`;
+const PRODUCT_UPDATE = `mutation($input: ProductInput!){ productUpdate(input:$input){
+ product{ id title } userErrors{ field message } } }`;
+const VARIANTS_DELETE = `mutation($productId: ID!, $variantsIds: [ID!]!){
+ productVariantsBulkDelete(productId:$productId, variantsIds:$variantsIds){
+ product{ id variants(first:20){ nodes{ id title sku } } } userErrors{ field message } } }`;
+
+function gateOrDie() {
+ if (!APPLY) return; // dry-run always allowed
+ if (APPROVAL !== 'TK-11248') { console.error('REFUSED: --apply requires --approval=TK-11248'); process.exit(3); }
+ if (!fs.existsSync(SENTINEL)) {
+ console.error(`REFUSED: approval sentinel missing: ${SENTINEL}`);
+ console.error(' Operator writes this file ONLY after Steve\'s explicit exact approval is logged on TK-11248.');
+ process.exit(3);
+ }
+ const s = JSON.parse(fs.readFileSync(SENTINEL, 'utf8'));
+ if (!s.token || process.env.RW_APPROVAL_TOKEN !== s.token) {
+ console.error('REFUSED: env RW_APPROVAL_TOKEN does not match the sentinel token.');
+ process.exit(3);
+ }
+ if (!s.approves || !s.approves.includes(STEP || 'both')) {
+ // sentinel should list which steps Steve approved, e.g. approves:["11238","10405"]
+ console.error(`REFUSED: sentinel does not approve step "${STEP || 'both'}". sentinel.approves=${JSON.stringify(s.approves)}`);
+ process.exit(3);
+ }
+}
+
+function ledger(entry) {
+ try { fs.appendFileSync(LEDGER, JSON.stringify(entry) + '\n'); } catch (e) { /* best-effort */ }
+}
+
+async function step11238() {
+ console.log('\n=== STEP 1 — TK-11238 Aries title correction ===');
+ const r = await gqlRetry(PRODUCT_Q, { id: ARIES_GID }, 'aries-pre');
+ const p = r.json.data.product;
+ console.log(` product ${p.legacyResourceId} status=${p.status} vendor="${p.vendor}"`);
+ console.log(` FROM: "${p.title}"`);
+ console.log(` TO : "${ARIES_NEW}"`);
+ if (p.title !== ARIES_OLD) { console.error(` DRIFT: live title != expected old title. Refusing.`); return { ok: false, drift: true }; }
+ if (p.status !== 'ACTIVE') { console.error(` DRIFT: product not ACTIVE. Refusing.`); return { ok: false, drift: true }; }
+ if (!APPLY) { console.log(' [dry-run] no write.'); return { ok: true, dry: true }; }
+ const w = await gqlRetry(PRODUCT_UPDATE, { input: { id: ARIES_GID, title: ARIES_NEW } }, 'aries-update');
+ const ue = w.json.data.productUpdate.userErrors;
+ if (ue && ue.length) { console.error(' userErrors:', JSON.stringify(ue)); return { ok: false }; }
+ const after = w.json.data.productUpdate.product.title;
+ console.log(` WROTE. live title now: "${after}"`);
+ ledger({ ts: new Date().toISOString(), agent: 'shopify-repairs', ticket: 'TK-11238', action: 'productUpdate title',
+ target: '7800009752627', blast_radius: 1, old: ARIES_OLD, new: ARIES_NEW,
+ undo_cmd: `node execute-TK11248.js rollback --step=11238 (restores old title from TK-11238-restore.json)`,
+ verify: `product 7800009752627 title == "${ARIES_NEW}"` });
+ return { ok: after === ARIES_NEW };
+}
+
+async function step10405() {
+ console.log('\n=== STEP 2 — TK-10405 delete 12 Default-Title variants ===');
+ const map = JSON.parse(fs.readFileSync(path.join(DATA, 'TK-10405-restore-map.json'), 'utf8'));
+ let done = 0;
+ for (const v of map.variants) {
+ const pre = await gqlRetry(PRODUCT_Q, { id: v.product_gid }, `rw-pre:${v.pid}`);
+ const pp = pre.json.data.product;
+ const vs = pp.variants.nodes;
+ const dt = vs.filter(x => x.title === 'Default Title');
+ const mural = vs.find(x => /Mural/.test(x.title));
+ const sampleSurvivor = vs.find(x => x.title === 'Sample' && (!dt[0] || x.id !== dt[0].id));
+ const okPre = dt.length === 1 && dt[0].id === v.delete_variant_gid && mural && sampleSurvivor && vs.length === 3;
+ console.log(` ${v.pid} ${v.label.padEnd(30)} del=${v.sku}@$${v.price} pre-ok=${okPre} (variants=${vs.length} dt=${dt.length})`);
+ if (!okPre) { console.error(` DRIFT on ${v.pid}: refusing this delete.`); continue; }
+ if (!APPLY) { console.log(' [dry-run] no write.'); continue; }
+ const w = await gqlRetry(VARIANTS_DELETE, { productId: v.product_gid, variantsIds: [v.delete_variant_gid] }, `rw-del:${v.pid}`);
+ const ue = w.json.data.productVariantsBulkDelete.userErrors;
+ if (ue && ue.length) { console.error(` userErrors:`, JSON.stringify(ue)); continue; }
+ const post = w.json.data.productVariantsBulkDelete.product.variants.nodes;
+ const stillDT = post.some(x => x.title === 'Default Title');
+ const stillMural = post.some(x => /Mural/.test(x.title));
+ const stillSample = post.some(x => x.title === 'Sample');
+ const okPost = post.length === 2 && !stillDT && stillMural && stillSample;
+ console.log(` WROTE. now ${post.length} variants DT-gone=${!stillDT} mural=${stillMural} sample=${stillSample} post-ok=${okPost}`);
+ ledger({ ts: new Date().toISOString(), agent: 'shopify-repairs', ticket: 'TK-10405',
+ action: 'productVariantsBulkDelete Default-Title', target: v.pid, deleted_variant: v.delete_variant_gid,
+ sku: v.sku, price: v.price, blast_radius: 1,
+ undo_cmd: `productVariantsBulkCreate from TK-10405-restore-map.json entry pid=${v.pid}`,
+ verify: `product ${v.pid} has 2 variants (Mural + Sample), no Default Title` });
+ if (okPost) done++;
+ await sleep(400);
+ }
+ console.log(` deleted ${done}/12`);
+ return { ok: !APPLY || done === 12 };
+}
+
+async function main() {
+ console.log(`\n### execute-TK11248 APPLY=${APPLY} STEP=${STEP || 'both'} store=${DOMAIN}`);
+ gateOrDie();
+ if (!APPLY) console.log('MODE: DRY-RUN (no writes). Pass --apply --approval=TK-11248 + sentinel to fire.');
+ let r1 = { ok: true, skipped: true }, r2 = { ok: true, skipped: true };
+ if (!STEP || STEP === '11238') r1 = await step11238();
+ // serialize: only proceed to deletes if title step did not hit drift/error under --apply
+ if (!STEP || STEP === '10405') {
+ if (APPLY && !STEP && !r1.ok) { console.error('\nABORT: step 1 failed under --apply; not proceeding to step 2.'); process.exit(4); }
+ r2 = await step10405();
+ }
+ console.log(`\nRESULT: step1_ok=${r1.ok} step2_ok=${r2.ok}`);
+ console.log(APPLY ? 'LIVE run complete.' : 'DRY-RUN complete. NO WRITES FIRED.');
+}
+main().catch(e => { console.error('FATAL', e); process.exit(2); });
diff --git a/scripts/revalidate-TK11248.js b/scripts/revalidate-TK11248.js
new file mode 100644
index 0000000..cb2d9dd
--- /dev/null
+++ b/scripts/revalidate-TK11248.js
@@ -0,0 +1,133 @@
+'use strict';
+/* READ-ONLY revalidation for the TK-11248 batch (TK-11238 + TK-10405).
+ * Confirms both live targets still match their characterization and builds
+ * restore maps. NO WRITES. $0. Exit 0 only if BOTH targets are exactly as
+ * expected and safe to execute once approval is logged.
+ *
+ * Usage: node revalidate-TK11248.js
+ * Writes: data/TK11248-revalidation-<ts>.json (evidence + restore maps)
+ */
+const fs = require('fs');
+const path = require('path');
+const { gqlRetry, DOMAIN } = require('./_shop');
+
+// ---- TK-11238: Aries title fix ------------------------------------------------
+const ARIES_ID = '7800009752627';
+const ARIES_GID = `gid://shopify/Product/${ARIES_ID}`;
+const ARIES_OLD_TITLE = 'Aries - Single Color Commercial Wallcovering | DW Commercial Surfaces';
+const ARIES_NEW_TITLE = 'Aries - Akoya Type II Vinyl Wallcovering | Hollywood Wallcoverings';
+
+// ---- TK-10405: 12 Rebel Walls Default-Title variant deletes -------------------
+const RW12 = [
+ ['6679729078323', 'A City Rises (Class 2 $80.48 anomaly)'],
+ ['6679739629619', 'A Priori'],
+ ['7851164303411', '3D Art - Multi'],
+ ['7851164336179', '3D Boxes - Gray'],
+ ['7851164368947', '3D Wave - Gray'],
+ ['7851164401715', '3D Wave - Marble'],
+ ['7851164434483', '3D Wave - Sand'],
+ ['7851164500019', '3D Wave - White'],
+ ['7851164532787', '70S Retro - Beige'],
+ ['7851164565555', 'A City Rises - Dark Blue'],
+ ['7851164598323', 'A Priori - Taupe'],
+ ['7851164631091', 'A Priori - Soft'],
+];
+
+const PRODUCT_Q = `query($id: ID!) {
+ product(id: $id) {
+ id legacyResourceId title handle status vendor
+ variants(first: 20) {
+ nodes { id legacyResourceId title sku price selectedOptions { name value } }
+ }
+ }
+}`;
+
+async function main() {
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
+ const report = { ts, store: DOMAIN, tk11238: null, tk10405: null, gate: { ready_11238: false, ready_10405: false } };
+
+ // ---- TK-11238 revalidation ----
+ const ar = await gqlRetry(PRODUCT_Q, { id: ARIES_GID }, 'aries');
+ const ap = ar.json && ar.json.data && ar.json.data.product;
+ if (!ap) {
+ report.tk11238 = { error: 'product not found', raw: ar.json };
+ } else {
+ const titleMatches = ap.title === ARIES_OLD_TITLE;
+ const isActive = ap.status === 'ACTIVE';
+ report.tk11238 = {
+ id: ap.legacyResourceId, gid: ap.id, handle: ap.handle, vendor: ap.vendor, status: ap.status,
+ current_title: ap.title,
+ expected_old_title: ARIES_OLD_TITLE,
+ proposed_new_title: ARIES_NEW_TITLE,
+ title_matches_expected: titleMatches,
+ is_active: isActive,
+ undo: { field: 'title', restore_to: ap.title },
+ };
+ report.gate.ready_11238 = titleMatches && isActive;
+ }
+
+ // ---- TK-10405 revalidation ----
+ const rows = [];
+ for (const [pid, label] of RW12) {
+ const r = await gqlRetry(PRODUCT_Q, { id: `gid://shopify/Product/${pid}` }, `rw:${pid}`);
+ const p = r.json && r.json.data && r.json.data.product;
+ if (!p) { rows.push({ pid, label, error: 'not found', raw: r.json }); continue; }
+ const vs = p.variants.nodes;
+ const byTitle = t => vs.filter(v => v.title === t);
+ const dt = byTitle('Default Title');
+ const mural = vs.find(v => /Mural \(per m²\)|Mural \(per m2\)/.test(v.title));
+ const sample = vs.find(v => v.title === 'Sample' || /-Sample$/i.test(v.sku || ''));
+ rows.push({
+ pid, label, product_status: p.status, handle: p.handle,
+ variant_count: vs.length,
+ default_title_variants: dt.map(v => ({ id: v.id, legacyId: v.legacyResourceId, sku: v.sku, price: v.price,
+ options: v.selectedOptions })),
+ has_mural_survivor: !!mural,
+ mural: mural ? { id: mural.id, sku: mural.sku, price: mural.price } : null,
+ has_sample_survivor: !!sample,
+ sample: sample ? { id: sample.id, sku: sample.sku, price: sample.price } : null,
+ all_variants: vs.map(v => ({ id: v.id, title: v.title, sku: v.sku, price: v.price })),
+ });
+ }
+ // safe iff: each product has exactly one Default Title variant to delete, AND both
+ // Mural + Sample survivors remain (so the delete cannot orphan the sellable/sample).
+ const perProductSafe = rows.map(row => ({
+ pid: row.pid, label: row.label,
+ ok: !row.error && Array.isArray(row.default_title_variants) && row.default_title_variants.length === 1 &&
+ row.has_mural_survivor && row.has_sample_survivor && row.variant_count === 3,
+ dt_count: row.default_title_variants ? row.default_title_variants.length : null,
+ variant_count: row.variant_count,
+ }));
+ report.tk10405 = { products: rows, per_product_safe: perProductSafe };
+ report.gate.ready_10405 = perProductSafe.length === 12 && perProductSafe.every(x => x.ok);
+
+ const outDir = path.join(__dirname, '..', 'data');
+ const outFile = path.join(outDir, `TK11248-revalidation-${ts}.json`);
+ fs.writeFileSync(outFile, JSON.stringify(report, null, 2));
+
+ // ---- console summary ----
+ console.log(`\n=== TK-11248 REVALIDATION (READ-ONLY, $0) @ ${ts} ===`);
+ console.log(`store: ${DOMAIN}`);
+ console.log(`\n[TK-11238 Aries title]`);
+ if (report.tk11238.error) console.log(` ERROR: ${report.tk11238.error}`);
+ else {
+ console.log(` product ${report.tk11238.id} status=${report.tk11238.status} handle=${report.tk11238.handle} vendor="${report.tk11238.vendor}"`);
+ console.log(` current title : "${report.tk11238.current_title}"`);
+ console.log(` title matches expected old? ${report.tk11238.title_matches_expected}`);
+ console.log(` is active? ${report.tk11238.is_active}`);
+ console.log(` -> proposed new title: "${report.tk11238.proposed_new_title}"`);
+ console.log(` READY_11238=${report.gate.ready_11238}`);
+ }
+ console.log(`\n[TK-10405 Rebel Walls 12 Default-Title deletes]`);
+ for (const s of perProductSafe) {
+ const row = rows.find(r => r.pid === s.pid);
+ const dt = row && row.default_title_variants && row.default_title_variants[0];
+ console.log(` ${s.pid} ${s.ok ? 'OK ' : 'HOLD'} variants=${s.variant_count} dt=${s.dt_count} ${dt ? `[del ${dt.sku} @ $${dt.price}]` : ''} mural=${row&&row.has_mural_survivor} sample=${row&&row.has_sample_survivor} ${s.label}`);
+ }
+ console.log(` READY_10405=${report.gate.ready_10405} (${perProductSafe.filter(x=>x.ok).length}/12 safe)`);
+ console.log(`\nEvidence -> ${outFile}`);
+ console.log(`\nGATE: ready_11238=${report.gate.ready_11238} ready_10405=${report.gate.ready_10405}`);
+ console.log(`NO WRITES FIRED. Awaiting explicit exact approval logged on TK-11248.\n`);
+}
+
+main().catch(e => { console.error('FATAL', e); process.exit(2); });
diff --git a/scripts/rollback-TK11248.js b/scripts/rollback-TK11248.js
new file mode 100644
index 0000000..4d6c719
--- /dev/null
+++ b/scripts/rollback-TK11248.js
@@ -0,0 +1,57 @@
+'use strict';
+/* Rollback for the TK-11248 batch. Reverses whichever step was applied.
+ * --step=11238 restores Aries title from TK-11238-restore.json
+ * --step=10405 recreates the 12 deleted Default-Title variants from TK-10405-restore-map.json
+ * (Shopify assigns NEW variant ids; SKU/price/options restored)
+ * DRY-RUN by default; --apply to fire. Rollback is the undo of a recorded write,
+ * so it only needs --apply (no sentinel) — but it prints exactly what it will do first.
+ *
+ * Usage:
+ * node rollback-TK11248.js --step=11238 # dry-run
+ * node rollback-TK11248.js --step=11238 --apply # restore title
+ * node rollback-TK11248.js --step=10405 --apply # recreate 12 variants
+ */
+const fs = require('fs');
+const path = require('path');
+const { gqlRetry, sleep } = require('./_shop');
+const DATA = path.join(__dirname, '..', 'data');
+const argv = process.argv.slice(2);
+const APPLY = argv.includes('--apply');
+const STEP = (argv.find(x => x.startsWith('--step=')) || '').split('=')[1];
+
+const PRODUCT_UPDATE = `mutation($input: ProductInput!){ productUpdate(input:$input){ product{ id title } userErrors{ field message } } }`;
+const VARIANTS_CREATE = `mutation($productId: ID!, $variants:[ProductVariantsBulkInput!]!){
+ productVariantsBulkCreate(productId:$productId, variants:$variants){
+ productVariants{ id title sku price } userErrors{ field message } } }`;
+
+async function rb11238() {
+ const r = JSON.parse(fs.readFileSync(path.join(DATA, 'TK-11238-restore.json'), 'utf8'));
+ console.log(`ROLLBACK TK-11238: product ${r.product_id} title -> "${r.restore_title}"`);
+ if (!APPLY) { console.log('[dry-run]'); return; }
+ const w = await gqlRetry(PRODUCT_UPDATE, { input: { id: r.product_gid, title: r.restore_title } }, 'rb-title');
+ const ue = w.json.data.productUpdate.userErrors;
+ console.log(ue && ue.length ? `userErrors ${JSON.stringify(ue)}` : `restored: "${w.json.data.productUpdate.product.title}"`);
+}
+async function rb10405() {
+ const m = JSON.parse(fs.readFileSync(path.join(DATA, 'TK-10405-restore-map.json'), 'utf8'));
+ console.log(`ROLLBACK TK-10405: recreate ${m.variants.length} Default-Title variants`);
+ for (const v of m.variants) {
+ // optionValues must map to the product's option; Default Title = single default option value.
+ const optionValues = (v.options && v.options.length)
+ ? v.options.map(o => ({ optionName: o.name, name: o.value })) : [{ optionName: 'Title', name: 'Default Title' }];
+ console.log(` ${v.pid} recreate ${v.sku}@$${v.price}`);
+ if (!APPLY) { console.log(' [dry-run]'); continue; }
+ const w = await gqlRetry(VARIANTS_CREATE, { productId: v.product_gid,
+ variants: [{ price: v.price, inventoryItem: { sku: v.sku }, optionValues }] }, `rb-create:${v.pid}`);
+ const ue = w.json.data.productVariantsBulkCreate.userErrors;
+ console.log(ue && ue.length ? ` userErrors ${JSON.stringify(ue)}` : ` recreated ${w.json.data.productVariantsBulkCreate.productVariants.map(x=>x.id).join(',')}`);
+ await sleep(400);
+ }
+}
+(async () => {
+ console.log(`### rollback-TK11248 STEP=${STEP} APPLY=${APPLY}`);
+ if (STEP === '11238') await rb11238();
+ else if (STEP === '10405') await rb10405();
+ else { console.error('specify --step=11238 or --step=10405'); process.exit(1); }
+ console.log(APPLY ? 'rollback complete.' : 'dry-run complete, no writes.');
+})().catch(e => { console.error('FATAL', e); process.exit(2); });
← 5b92c13 auto-data-snapshot: 2026-09-03T14:29:24 (1 data files) — dat
·
back to Rebel Walls Push
·
auto-data-snapshot: 2026-09-04T12:10:54 (2 data files) — dat 3fbf2ca →