[object Object]

← back to Dw Signup Fulfillment

Retire obsolete theme commands that could undo designer signup

dc1323fb12536ab29ccd276a2f909b574fdcc742 · 2026-09-05 00:23:03 -0700 · Steve Abrams

Files touched

Diff

commit dc1323fb12536ab29ccd276a2f909b574fdcc742
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Sep 5 00:23:03 2026 -0700

    Retire obsolete theme commands that could undo designer signup
---
 scripts/gate2-theme-put.mjs                        |  36 +--
 scripts/gate2-theme-rollback.mjs                   |  25 +--
 verification/tk11185-reconcile/RETIRED-COMMANDS.md |  29 +++
 .../tk11185-reconcile/retired-boundaries.cjs       |  35 +++
 verification/tk11185-reconcile/retired-check.cjs   | 106 +++++++++
 .../tk11185-reconcile/retired-e2e-proof.json       | 249 +++++++++++++++++++++
 6 files changed, 439 insertions(+), 41 deletions(-)

diff --git a/scripts/gate2-theme-put.mjs b/scripts/gate2-theme-put.mjs
index 78c0eed..7c9637f 100644
--- a/scripts/gate2-theme-put.mjs
+++ b/scripts/gate2-theme-put.mjs
@@ -1,26 +1,10 @@
-// TK-11185 GATE 2 — live theme PUT of the patched trade-apply success handler.
-// Confirms 145121607731 is the live/main theme before writing (aborts on mismatch).
-// cwd-independent (absolute paths); token read inside Node (no shell quoting).
-import fs from "fs";
-import os from "os";
-const ROOT = os.homedir() + "/Projects/dw-signup-fulfillment";
-const env = fs.readFileSync(os.homedir() + "/Projects/secrets-manager/.env", "utf8");
-const TT = (env.match(/^SHOPIFY_THEME_TOKEN=(.*)$/m) || [])[1].replace(/^"|"$/g, "").trim();
-const DOMAIN = "designer-laboratory-sandbox.myshopify.com", VER = "2024-10", THEME = "145121607731";
-const H = { "X-Shopify-Access-Token": TT };
-
-const tj = await (await fetch(`https://${DOMAIN}/admin/api/${VER}/themes.json`, { headers: H })).json();
-const main = (tj.themes || []).find(t => t.role === "main");
-console.log("live/main theme:", main && main.id, main && main.name);
-if (!main || String(main.id) !== THEME) {
-  console.error("ABORT: main theme is", main && main.id, "expected", THEME, "— do not PUT.");
-  process.exit(1);
-}
-const value = fs.readFileSync(ROOT + "/theme-proposals/loggedin-trade-entry/dw-signin-modal.PATCHED-tk11185.liquid", "utf8");
-const r = await fetch(`https://${DOMAIN}/admin/api/${VER}/themes/${THEME}/assets.json`, {
-  method: "PUT",
-  headers: { ...H, "Content-Type": "application/json" },
-  body: JSON.stringify({ asset: { key: "snippets/dw-signin-modal.liquid", value } }),
-});
-const j = await r.json();
-console.log("HTTP", r.status, j.asset ? ("PUT OK " + j.asset.key + " @ " + j.asset.updated_at) : JSON.stringify(j));
+// Retired TK-11185 command. Historical source: git show 5e609e8:scripts/gate2-theme-put.mjs
+// Keep inert: no imports, credential reads, network, or forwarding.
+console.error(`SUPERSEDED: TK-11185 theme PUT is retired (exit 78).
+TK-11283 published the required designer form and account/login flow. This old command would overwrite it.
+Read-only Shopify status (run from any directory):
+  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status
+For the current guarded deploy/rollback procedure and approval requirements, read:
+  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md
+No credentials were read and no Shopify request was made.`);
+process.exitCode = 78;
diff --git a/scripts/gate2-theme-rollback.mjs b/scripts/gate2-theme-rollback.mjs
index a35570c..ece8233 100644
--- a/scripts/gate2-theme-rollback.mjs
+++ b/scripts/gate2-theme-rollback.mjs
@@ -1,15 +1,10 @@
-// TK-11185 GATE 2 ROLLBACK — re-PUT the pre-change live snapshot of dw-signin-modal.liquid.
-import fs from "fs";
-import os from "os";
-const ROOT = os.homedir() + "/Projects/dw-signup-fulfillment";
-const env = fs.readFileSync(os.homedir() + "/Projects/secrets-manager/.env", "utf8");
-const TT = (env.match(/^SHOPIFY_THEME_TOKEN=(.*)$/m) || [])[1].replace(/^"|"$/g, "").trim();
-const DOMAIN = "designer-laboratory-sandbox.myshopify.com", VER = "2024-10", THEME = "145121607731";
-const value = fs.readFileSync(ROOT + "/theme-backups/live-snapshots/snippets__dw-signin-modal.liquid.20260903T181955.bak", "utf8");
-const r = await fetch(`https://${DOMAIN}/admin/api/${VER}/themes/${THEME}/assets.json`, {
-  method: "PUT",
-  headers: { "X-Shopify-Access-Token": TT, "Content-Type": "application/json" },
-  body: JSON.stringify({ asset: { key: "snippets/dw-signin-modal.liquid", value } }),
-});
-const j = await r.json();
-console.log("HTTP", r.status, j.asset ? ("ROLLBACK OK " + j.asset.updated_at) : JSON.stringify(j));
+// Retired TK-11185 command. Historical source: git show 5e609e8:scripts/gate2-theme-rollback.mjs
+// Keep inert: no imports, credential reads, network, or forwarding.
+console.error(`SUPERSEDED: TK-11185 theme rollback is retired (exit 78).
+TK-11283 published the required designer form and account/login flow. This old command would overwrite it.
+Read-only Shopify status (run from any directory):
+  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status
+For the current guarded deploy/rollback procedure and approval requirements, read:
+  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md
+No credentials were read and no Shopify request was made.`);
+process.exitCode = 78;
diff --git a/verification/tk11185-reconcile/RETIRED-COMMANDS.md b/verification/tk11185-reconcile/RETIRED-COMMANDS.md
new file mode 100644
index 0000000..d88f829
--- /dev/null
+++ b/verification/tk11185-reconcile/RETIRED-COMMANDS.md
@@ -0,0 +1,29 @@
+# Retired TK-11185 theme commands
+
+`scripts/gate2-theme-put.mjs` and `scripts/gate2-theme-rollback.mjs` are intentionally retired. Both print a superseded message and exit 78 before imports, credential reads, network requests, or forwarding. Extra flags do not reactivate them.
+
+TK-11283 published the required full designer application and account/login flow. The former PUT checked only the main theme ID; the former rollback checked neither the main theme nor the current asset. Both could replace newer content with a historical snippet. Original files remain recoverable through `git show 5e609e8:scripts/gate2-theme-put.mjs` and the corresponding rollback path for historical review. Do not execute recovered historical scripts.
+
+## Current supported procedure
+
+Read current Shopify values using the guarded tool from any directory:
+
+```sh
+node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status
+```
+
+This mode performs Shopify GETs and saves current snapshots locally. Retired commands display this command; they never invoke it. Review [TK-11283 evidence](../tk11283/REVIEW.md), its preserved baselines, and current hashes before considering any change.
+
+The unchanged `verification/tk11283/publish-theme.cjs` owns the current two-asset deploy/rollback workflow. Its `--apply` and `--rollback` modes mutate the live theme and require explicit authorization for the exact action. Preserve its baseline, theme identity, content-hash, review-evidence and post-write checks. Retirement is not permission to run either mode.
+
+## Verification and limits
+
+```sh
+node --experimental-vm-modules verification/tk11185-reconcile/retired-check.cjs
+```
+
+The runner loads actual historical modules from Git in an isolated VM with synthetic `fs`/`os` imports and mocked Shopify responses. It starts with the current proposed designer-flow body and proves the historical commands overwrite it without inspecting its current value. No real credential or API is used.
+
+It then invokes each real retired entrypoint with Node, including `--apply` and `--rollback`, from the repository and `/private/tmp`. A preload blocks and records application file/credential and network attempts; plain invocations verify normal startup. Assertions require exit78, a superseded message, zero app-file/network attempts, and an unchanged guarded tool. Results are in [retired-e2e-proof.json](retired-e2e-proof.json).
+
+A repository-wide `rg` search found no callers of these exact filenames before retirement. This is local inventory only; external pasted commands or automation are not exhaustively inventoried. Exit78 deliberately reports failure to those callers and should not be retried. No customer/account/OTP journey changed. TK-11185's separate retained production smoke-record cleanup remains with the parent ticket; this report does not claim the whole ticket complete.
diff --git a/verification/tk11185-reconcile/retired-boundaries.cjs b/verification/tk11185-reconcile/retired-boundaries.cjs
new file mode 100644
index 0000000..0f153e3
--- /dev/null
+++ b/verification/tk11185-reconcile/retired-boundaries.cjs
@@ -0,0 +1,35 @@
+'use strict';
+// Preload the real entrypoint; reject and count all app file/network attempts.
+const fs = require('node:fs'), path = require('node:path');
+const { fileURLToPath } = require('node:url');
+const { syncBuiltinESMExports } = require('node:module');
+const entry = path.resolve(process.env.TK_RETIRE_ALLOWED_ENTRY);
+const audit = { entryLoads: 0, appFileAttempts: [], networkAttempts: [] };
+function checkFile(value) {
+  const filename = value instanceof URL ? fileURLToPath(value) : String(value);
+  if (path.resolve(filename) === entry) { audit.entryLoads++; return; }
+  audit.appFileAttempts.push(filename);
+  throw new Error('Unexpected application file access');
+}
+for (const name of ['readFileSync', 'readFile', 'openSync', 'open', 'createReadStream']) {
+  const original = fs[name];
+  fs[name] = function (filename, ...args) { checkFile(filename); return original.call(this, filename, ...args); };
+}
+for (const name of ['readFile', 'open']) {
+  const original = fs.promises[name];
+  fs.promises[name] = function (filename, ...args) { checkFile(filename); return original.call(this, filename, ...args); };
+}
+function blockNetwork(name) {
+  return function () { audit.networkAttempts.push(name); throw new Error('Network is forbidden in retirement proof'); };
+}
+globalThis.fetch = blockNetwork('fetch');
+for (const [moduleName, methods] of Object.entries({
+  'node:http': ['request', 'get'], 'node:https': ['request', 'get'],
+  'node:net': ['connect', 'createConnection'], 'node:tls': ['connect'],
+  'node:dns': ['lookup', 'resolve'],
+})) {
+  const target = require(moduleName);
+  for (const name of methods) target[name] = blockNetwork(moduleName + '.' + name);
+}
+syncBuiltinESMExports();
+process.on('exit', () => process.stderr.write('\nRETIREMENT_BOUNDARY_AUDIT=' + JSON.stringify(audit) + '\n'));
diff --git a/verification/tk11185-reconcile/retired-check.cjs b/verification/tk11185-reconcile/retired-check.cjs
new file mode 100644
index 0000000..13cd301
--- /dev/null
+++ b/verification/tk11185-reconcile/retired-check.cjs
@@ -0,0 +1,106 @@
+'use strict';
+// Historical modules use synthetic imports; real current commands deny access.
+const fs = require('node:fs'), path = require('node:path'), vm = require('node:vm');
+const crypto = require('node:crypto'), assert = require('node:assert/strict');
+const { execFileSync, spawnSync } = require('node:child_process');
+const root = path.resolve(__dirname, '../..');
+const sha = value => crypto.createHash('sha256').update(value).digest('hex');
+const history = '5e609e8';
+const git = (...args) => execFileSync('git', args, { cwd: root, encoding: 'utf8' });
+const report = {
+  intent: 'Retire obsolete theme writes and preserve the current guarded tool',
+  risk: 'R1 local command; R3 external boundary tested with denied/mock APIs',
+  environment: { node: process.version, platform: process.platform, root },
+  timestamp: new Date().toISOString(), baselineCommit: history, buildBase: git('rev-parse', 'HEAD').trim(),
+  command: 'node --experimental-vm-modules verification/tk11185-reconcile/retired-check.cjs',
+  baseline: [], invocations: [], assertions: [],
+  cleanup: 'No real API calls, customers, emails, or production mutations. No temporary records.',
+  scope: 'Retirement only; TK11185 production smoke-record cleanup remains with the parent.',
+};
+async function reproduce(name, fixtureRelative) {
+  const file = 'scripts/' + name, source = git('show', history + ':' + file);
+  const historical = fs.readFileSync(path.join(root, fixtureRelative), 'utf8');
+  const published = fs.readFileSync(path.join(root, 'theme-proposals/designer-signin-tk11283/snippets/dw-signin-modal.liquid'), 'utf8');
+  assert.notEqual(sha(historical), sha(published));
+  let live = published;
+  const reads = [], calls = [], output = [], fakeHome = '/retirement-fixture';
+  const fakeFS = { readFileSync(filename) {
+    reads.push(filename);
+    if (filename === fakeHome + '/Projects/secrets-manager/.env') return 'SHOPIFY_THEME_TOKEN=dummy-test-token\n';
+    if (filename === fakeHome + '/Projects/dw-signup-fulfillment/' + fixtureRelative) return historical;
+    throw new Error('Unallowed fixture file: ' + filename);
+  } };
+  const context = vm.createContext({
+    console: { log: (...args) => output.push(args.join(' ')), error: (...args) => output.push(args.join(' ')) },
+    process: { exit(code) { throw new Error('Unexpected process.exit ' + code); } },
+    fetch: async (url, options = {}) => {
+      const method = options.method || 'GET', call = { method, url };
+      calls.push(call);
+      if (url.endsWith('/themes.json') && method === 'GET') return { json: async () => ({ themes: [{ role: 'main', id: 145121607731, name: 'fixture-main' }] }) };
+      if (url.endsWith('/assets.json') && method === 'PUT') {
+        const { asset } = JSON.parse(options.body);
+        assert.equal(asset.key, 'snippets/dw-signin-modal.liquid');
+        call.valueSHA256 = sha(asset.value);
+        live = asset.value;
+        return { status: 200, json: async () => ({ asset: { key: asset.key, updated_at: 'fixture-only' } }) };
+      }
+      throw new Error('Unexpected mock API access');
+    },
+  });
+  const module = new vm.SourceTextModule(source, { context, identifier: file });
+  await module.link(specifier => {
+    assert.ok(['fs', 'os'].includes(specifier), 'Only synthetic imports');
+    const value = specifier === 'fs' ? fakeFS : { homedir: () => fakeHome };
+    return new vm.SyntheticModule(['default'], function () { this.setExport('default', value); }, { context });
+  });
+  await module.evaluate();
+  assert.equal(live, historical, 'Original overwrites the newer published body');
+  assert.equal(calls.filter(x => x.method === 'PUT').length, 1);
+  assert.equal(calls.filter(x => x.method === 'GET' && x.url.includes('/assets')).length, 0, 'No current-content check');
+  report.baseline.push({ file, sourceSHA256: sha(source), publishedBeforeSHA256: sha(published), staleAfterSHA256: sha(live), reads, calls, output, reproduced: true });
+}
+async function main() {
+  await reproduce('gate2-theme-put.mjs', 'theme-proposals/loggedin-trade-entry/dw-signin-modal.PATCHED-tk11185.liquid');
+  await reproduce('gate2-theme-rollback.mjs', 'theme-backups/live-snapshots/snippets__dw-signin-modal.liquid.20260903T181955.bak');
+  const guardPath = 'verification/tk11283/publish-theme.cjs';
+  assert.equal(fs.readFileSync(path.join(root, guardPath), 'utf8'), git('show', 'HEAD:' + guardPath), 'Supported tool has no edits');
+  report.guardedToolSHA256 = sha(fs.readFileSync(path.join(root, guardPath)));
+  for (const name of ['gate2-theme-put.mjs', 'gate2-theme-rollback.mjs']) {
+    const entry = path.join(root, 'scripts', name);
+    for (const args of [[], ['--apply'], ['--rollback']]) {
+      const cwd = args.length ? '/private/tmp' : root;
+      const command = ['--require', path.join(__dirname, 'retired-boundaries.cjs'), entry, ...args];
+      const result = spawnSync(process.execPath, command, { cwd, encoding: 'utf8', timeout: 5000,
+        env: { PATH: process.env.PATH, TK_RETIRE_ALLOWED_ENTRY: entry } });
+      assert.ifError(result.error);
+      assert.equal(result.status, 78, name + ' must exit78');
+      assert.match(result.stderr, /SUPERSEDED/);
+      assert.match(result.stderr, /TK-11283/);
+      assert.match(result.stderr, /publish-theme\.cjs --status/);
+      assert.equal(result.stdout, '');
+      const auditLine = result.stderr.split('\n').find(line => line.startsWith('RETIREMENT_BOUNDARY_AUDIT='));
+      assert.ok(auditLine, 'Preload audit actually ran');
+      const audit = JSON.parse(auditLine.slice('RETIREMENT_BOUNDARY_AUDIT='.length));
+      assert.deepEqual(audit.appFileAttempts, [], 'No credential or application file reads');
+      assert.deepEqual(audit.networkAttempts, [], 'No network attempts');
+      report.invocations.push({ entry, args, cwd, exitCode: result.status, signal: result.signal,
+        command: [process.execPath, ...command], stderr: result.stderr, audit, sourceSHA256: sha(fs.readFileSync(entry)), verdict: 'PASS' });
+    }
+    const plain = spawnSync(process.execPath, [entry], { cwd: '/private/tmp', encoding: 'utf8', timeout: 5000, env: { PATH: process.env.PATH } });
+    assert.equal(plain.status, 78);
+    assert.match(plain.stderr, /SUPERSEDED/);
+    report.invocations.push({ entry, mode: 'plain direct Node, no preload', cwd: '/private/tmp', exitCode: plain.status, stderr: plain.stderr, verdict: 'PASS' });
+  }
+  report.assertions = [
+    { boundary: 'Historical fs/API', verdict: 'PASS', reason: 'Actual original sources read dummy credentials then overwrite newer body without checking current hash.' },
+    { boundary: 'Real CLI process', verdict: 'PASS', reason: 'Both exit78 from repository or unrelated cwd, even with apply/rollback flags.' },
+    { boundary: 'Credentials/files/network', verdict: 'PASS', reason: 'Six instrumented invocations report zero app file/network attempts.' },
+    { boundary: 'Supported tooling', verdict: 'PASS', reason: 'TK11283 guarded deploy/rollback tool byte-identical to HEAD.' },
+    { boundary: 'UI', verdict: 'N/A', reason: 'No storefront/UI changes.' },
+  ];
+  report.verdict = 'PASS';
+  fs.writeFileSync(path.join(__dirname, 'retired-e2e-proof.json'), JSON.stringify(report, null, 2) + '\n');
+  console.log(JSON.stringify({ verdict: report.verdict, historicalReproductions: report.baseline.length, directInvocations: report.invocations.length, networkCalls: 0,
+    evidence: path.join(__dirname, 'retired-e2e-proof.json') }, null, 2));
+}
+main().catch(error => { console.error(error); process.exitCode = 1; });
diff --git a/verification/tk11185-reconcile/retired-e2e-proof.json b/verification/tk11185-reconcile/retired-e2e-proof.json
new file mode 100644
index 0000000..5ef958f
--- /dev/null
+++ b/verification/tk11185-reconcile/retired-e2e-proof.json
@@ -0,0 +1,249 @@
+{
+  "intent": "Retire obsolete theme writes and preserve the current guarded tool",
+  "risk": "R1 local command; R3 external boundary tested with denied/mock APIs",
+  "environment": {
+    "node": "v26.4.0",
+    "platform": "darwin",
+    "root": "/Users/macstudio3/Projects/dw-signup-fulfillment"
+  },
+  "timestamp": "2026-09-05T07:22:49.286Z",
+  "baselineCommit": "5e609e8",
+  "buildBase": "8a877d4ed47ea559582c0944a702e60a8a6d927a",
+  "command": "node --experimental-vm-modules verification/tk11185-reconcile/retired-check.cjs",
+  "baseline": [
+    {
+      "file": "scripts/gate2-theme-put.mjs",
+      "sourceSHA256": "ef79df61e957697626f8b52b727ec23e4ea497563325c9ca0e62777e2362e758",
+      "publishedBeforeSHA256": "42e67a0e45ca602a2e23fd482ac2ecaefc17ab458d6440d9bfe4a206332999a0",
+      "staleAfterSHA256": "6108eff129c244b9c96b831e14479b5626b26a8ce32a4b3d79fca36ee560f3a6",
+      "reads": [
+        "/retirement-fixture/Projects/secrets-manager/.env",
+        "/retirement-fixture/Projects/dw-signup-fulfillment/theme-proposals/loggedin-trade-entry/dw-signin-modal.PATCHED-tk11185.liquid"
+      ],
+      "calls": [
+        {
+          "method": "GET",
+          "url": "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/themes.json"
+        },
+        {
+          "method": "PUT",
+          "url": "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/themes/145121607731/assets.json",
+          "valueSHA256": "6108eff129c244b9c96b831e14479b5626b26a8ce32a4b3d79fca36ee560f3a6"
+        }
+      ],
+      "output": [
+        "live/main theme: 145121607731 fixture-main",
+        "HTTP 200 PUT OK snippets/dw-signin-modal.liquid @ fixture-only"
+      ],
+      "reproduced": true
+    },
+    {
+      "file": "scripts/gate2-theme-rollback.mjs",
+      "sourceSHA256": "39f2ff000e502a18447bfee5d8d97fd11d4eb51f5d4732af7e92953ec7c313e0",
+      "publishedBeforeSHA256": "42e67a0e45ca602a2e23fd482ac2ecaefc17ab458d6440d9bfe4a206332999a0",
+      "staleAfterSHA256": "b69ff27a64730532a22e30a4e0f30eb116da0a7317e60d7052e1cb1657b52305",
+      "reads": [
+        "/retirement-fixture/Projects/secrets-manager/.env",
+        "/retirement-fixture/Projects/dw-signup-fulfillment/theme-backups/live-snapshots/snippets__dw-signin-modal.liquid.20260903T181955.bak"
+      ],
+      "calls": [
+        {
+          "method": "PUT",
+          "url": "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/themes/145121607731/assets.json",
+          "valueSHA256": "b69ff27a64730532a22e30a4e0f30eb116da0a7317e60d7052e1cb1657b52305"
+        }
+      ],
+      "output": [
+        "HTTP 200 ROLLBACK OK fixture-only"
+      ],
+      "reproduced": true
+    }
+  ],
+  "invocations": [
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-put.mjs",
+      "args": [],
+      "cwd": "/Users/macstudio3/Projects/dw-signup-fulfillment",
+      "exitCode": 78,
+      "signal": null,
+      "command": [
+        "/opt/homebrew/Cellar/node/26.4.0/bin/node",
+        "--require",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/retired-boundaries.cjs",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-put.mjs"
+      ],
+      "stderr": "SUPERSEDED: TK-11185 theme PUT is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n\nRETIREMENT_BOUNDARY_AUDIT={\"entryLoads\":2,\"appFileAttempts\":[],\"networkAttempts\":[]}\n",
+      "audit": {
+        "entryLoads": 2,
+        "appFileAttempts": [],
+        "networkAttempts": []
+      },
+      "sourceSHA256": "a2a84c428210606345fb1115ecb7bb3122f221471cedf136959d0eafa55c054c",
+      "verdict": "PASS"
+    },
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-put.mjs",
+      "args": [
+        "--apply"
+      ],
+      "cwd": "/private/tmp",
+      "exitCode": 78,
+      "signal": null,
+      "command": [
+        "/opt/homebrew/Cellar/node/26.4.0/bin/node",
+        "--require",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/retired-boundaries.cjs",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-put.mjs",
+        "--apply"
+      ],
+      "stderr": "SUPERSEDED: TK-11185 theme PUT is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n\nRETIREMENT_BOUNDARY_AUDIT={\"entryLoads\":2,\"appFileAttempts\":[],\"networkAttempts\":[]}\n",
+      "audit": {
+        "entryLoads": 2,
+        "appFileAttempts": [],
+        "networkAttempts": []
+      },
+      "sourceSHA256": "a2a84c428210606345fb1115ecb7bb3122f221471cedf136959d0eafa55c054c",
+      "verdict": "PASS"
+    },
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-put.mjs",
+      "args": [
+        "--rollback"
+      ],
+      "cwd": "/private/tmp",
+      "exitCode": 78,
+      "signal": null,
+      "command": [
+        "/opt/homebrew/Cellar/node/26.4.0/bin/node",
+        "--require",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/retired-boundaries.cjs",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-put.mjs",
+        "--rollback"
+      ],
+      "stderr": "SUPERSEDED: TK-11185 theme PUT is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n\nRETIREMENT_BOUNDARY_AUDIT={\"entryLoads\":2,\"appFileAttempts\":[],\"networkAttempts\":[]}\n",
+      "audit": {
+        "entryLoads": 2,
+        "appFileAttempts": [],
+        "networkAttempts": []
+      },
+      "sourceSHA256": "a2a84c428210606345fb1115ecb7bb3122f221471cedf136959d0eafa55c054c",
+      "verdict": "PASS"
+    },
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-put.mjs",
+      "mode": "plain direct Node, no preload",
+      "cwd": "/private/tmp",
+      "exitCode": 78,
+      "stderr": "SUPERSEDED: TK-11185 theme PUT is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n",
+      "verdict": "PASS"
+    },
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-rollback.mjs",
+      "args": [],
+      "cwd": "/Users/macstudio3/Projects/dw-signup-fulfillment",
+      "exitCode": 78,
+      "signal": null,
+      "command": [
+        "/opt/homebrew/Cellar/node/26.4.0/bin/node",
+        "--require",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/retired-boundaries.cjs",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-rollback.mjs"
+      ],
+      "stderr": "SUPERSEDED: TK-11185 theme rollback is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n\nRETIREMENT_BOUNDARY_AUDIT={\"entryLoads\":2,\"appFileAttempts\":[],\"networkAttempts\":[]}\n",
+      "audit": {
+        "entryLoads": 2,
+        "appFileAttempts": [],
+        "networkAttempts": []
+      },
+      "sourceSHA256": "d5d1ed66c9092f51895d829e92e4fed19c6949b23a4c2f0cbb93e9f3170e6153",
+      "verdict": "PASS"
+    },
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-rollback.mjs",
+      "args": [
+        "--apply"
+      ],
+      "cwd": "/private/tmp",
+      "exitCode": 78,
+      "signal": null,
+      "command": [
+        "/opt/homebrew/Cellar/node/26.4.0/bin/node",
+        "--require",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/retired-boundaries.cjs",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-rollback.mjs",
+        "--apply"
+      ],
+      "stderr": "SUPERSEDED: TK-11185 theme rollback is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n\nRETIREMENT_BOUNDARY_AUDIT={\"entryLoads\":2,\"appFileAttempts\":[],\"networkAttempts\":[]}\n",
+      "audit": {
+        "entryLoads": 2,
+        "appFileAttempts": [],
+        "networkAttempts": []
+      },
+      "sourceSHA256": "d5d1ed66c9092f51895d829e92e4fed19c6949b23a4c2f0cbb93e9f3170e6153",
+      "verdict": "PASS"
+    },
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-rollback.mjs",
+      "args": [
+        "--rollback"
+      ],
+      "cwd": "/private/tmp",
+      "exitCode": 78,
+      "signal": null,
+      "command": [
+        "/opt/homebrew/Cellar/node/26.4.0/bin/node",
+        "--require",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/retired-boundaries.cjs",
+        "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-rollback.mjs",
+        "--rollback"
+      ],
+      "stderr": "SUPERSEDED: TK-11185 theme rollback is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n\nRETIREMENT_BOUNDARY_AUDIT={\"entryLoads\":2,\"appFileAttempts\":[],\"networkAttempts\":[]}\n",
+      "audit": {
+        "entryLoads": 2,
+        "appFileAttempts": [],
+        "networkAttempts": []
+      },
+      "sourceSHA256": "d5d1ed66c9092f51895d829e92e4fed19c6949b23a4c2f0cbb93e9f3170e6153",
+      "verdict": "PASS"
+    },
+    {
+      "entry": "/Users/macstudio3/Projects/dw-signup-fulfillment/scripts/gate2-theme-rollback.mjs",
+      "mode": "plain direct Node, no preload",
+      "cwd": "/private/tmp",
+      "exitCode": 78,
+      "stderr": "SUPERSEDED: TK-11185 theme rollback is retired (exit 78).\nTK-11283 published the required designer form and account/login flow. This old command would overwrite it.\nRead-only Shopify status (run from any directory):\n  node ~/Projects/dw-signup-fulfillment/verification/tk11283/publish-theme.cjs --status\nFor the current guarded deploy/rollback procedure and approval requirements, read:\n  ~/Projects/dw-signup-fulfillment/verification/tk11185-reconcile/RETIRED-COMMANDS.md\nNo credentials were read and no Shopify request was made.\n",
+      "verdict": "PASS"
+    }
+  ],
+  "assertions": [
+    {
+      "boundary": "Historical fs/API",
+      "verdict": "PASS",
+      "reason": "Actual original sources read dummy credentials then overwrite newer body without checking current hash."
+    },
+    {
+      "boundary": "Real CLI process",
+      "verdict": "PASS",
+      "reason": "Both exit78 from repository or unrelated cwd, even with apply/rollback flags."
+    },
+    {
+      "boundary": "Credentials/files/network",
+      "verdict": "PASS",
+      "reason": "Six instrumented invocations report zero app file/network attempts."
+    },
+    {
+      "boundary": "Supported tooling",
+      "verdict": "PASS",
+      "reason": "TK11283 guarded deploy/rollback tool byte-identical to HEAD."
+    },
+    {
+      "boundary": "UI",
+      "verdict": "N/A",
+      "reason": "No storefront/UI changes."
+    }
+  ],
+  "cleanup": "No real API calls, customers, emails, or production mutations. No temporary records.",
+  "scope": "Retirement only; TK11185 production smoke-record cleanup remains with the parent.",
+  "guardedToolSHA256": "2f37dc836fb0ddba07270d3608b584ccd29c917f0455363e0aaba1562fc68797",
+  "verdict": "PASS"
+}

← 116fc35 Serialize trade rejection with approval and preserve concurr  ·  back to Dw Signup Fulfillment  ·  auto-data-snapshot: 2026-09-05T01:06:29 (1 data files) — ver b8ee89a →