← back to Gmc Titlefix
TK-11847 add merge-recheck script; verified PASS — all 6 overrides merged, no longer legal_restrictions-disapproved
c36bba28ce9be0054f3d9e381aa2d1c880117e2e · 2026-09-24 13:10:58 -0700 · Steve
Confirms the --apply run cleared all 6 offers within ~10 minutes: color+material
attributes merged and none still legal_restrictions_policy_violation-disapproved.
Launchd durable-cron memo superseded (no longer needed) — moved to _done/.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sb592So8iWsxtsmVsiTMfq
Files touched
A data/tk11847-merge-recheck-state.jsonA tk11847-merge-recheck.mjs
Diff
commit c36bba28ce9be0054f3d9e381aa2d1c880117e2e
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Sep 24 13:10:58 2026 -0700
TK-11847 add merge-recheck script; verified PASS — all 6 overrides merged, no longer legal_restrictions-disapproved
Confirms the --apply run cleared all 6 offers within ~10 minutes: color+material
attributes merged and none still legal_restrictions_policy_violation-disapproved.
Launchd durable-cron memo superseded (no longer needed) — moved to _done/.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sb592So8iWsxtsmVsiTMfq
---
data/tk11847-merge-recheck-state.json | 36 +++++++++++
tk11847-merge-recheck.mjs | 115 ++++++++++++++++++++++++++++++++++
2 files changed, 151 insertions(+)
diff --git a/data/tk11847-merge-recheck-state.json b/data/tk11847-merge-recheck-state.json
new file mode 100644
index 0000000..9b7f35a
--- /dev/null
+++ b/data/tk11847-merge-recheck-state.json
@@ -0,0 +1,36 @@
+{
+ "started_at": "2026-09-24T20:10:08.803Z",
+ "stalled_logged": false,
+ "offers": {
+ "44724797538355": {
+ "merged": true,
+ "stillLR": false,
+ "error": null
+ },
+ "44724798226483": {
+ "merged": true,
+ "stillLR": false,
+ "error": null
+ },
+ "44724798193715": {
+ "merged": true,
+ "stillLR": false,
+ "error": null
+ },
+ "40977437818931": {
+ "merged": true,
+ "stillLR": false,
+ "error": null
+ },
+ "44508742418483": {
+ "merged": true,
+ "stillLR": false,
+ "error": null
+ },
+ "44508735209523": {
+ "merged": true,
+ "stillLR": false,
+ "error": null
+ }
+ }
+}
\ No newline at end of file
diff --git a/tk11847-merge-recheck.mjs b/tk11847-merge-recheck.mjs
new file mode 100644
index 0000000..fa373f9
--- /dev/null
+++ b/tk11847-merge-recheck.mjs
@@ -0,0 +1,115 @@
+// TK-11847 — READ-ONLY durable merge-status recheck for the 6 colorway-override offers.
+// Designed to run under launchd every 2h. NEVER writes to GMC/Shopify. $0.
+//
+// On each run: reads the APPLY artifact for the 6 offers + their pushed color/material,
+// fetches live product state, compares to persisted state, and:
+// - all 6 merged (color+material match) AND none still legal_restrictions-disapproved
+// -> logs PASS to TK-11847, self-uninstalls the launchd job, exits.
+// - now >= 72h deadline and any offer still LR-disapproved
+// -> logs APPEAL NEEDED with the offending offerIds, self-uninstalls, exits.
+// - now >= 12h and none have merged yet (never logged before)
+// -> logs a WARN (possible naming/rule problem), keeps looping.
+// - per-offer state changed since last run -> logs the diff, keeps looping.
+// - nothing changed -> prints one quiet line, no ticket write.
+import fs from 'fs';
+import { execSync } from 'child_process';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const mc = require('./_mc-read-v1.js');
+
+const DIR = '/Users/macstudio3/Projects/gmc-titlefix';
+const APPLY_ARTIFACT = `${DIR}/data/tk11847-colorway-override-APPLY-2026-09-24T19-59-41.json`;
+const STATE_FILE = `${DIR}/data/tk11847-merge-recheck-state.json`;
+const TICKET = 'TK-11847';
+const LAUNCHD_LABEL = 'com.steve.gmc-tk11847-merge-recheck';
+const LAUNCHD_PLIST = `${process.env.HOME}/Library/LaunchAgents/${LAUNCHD_LABEL}.plist`;
+const DEADLINE_MS = 72 * 3600 * 1000;
+const STALLED_MS = 12 * 3600 * 1000;
+
+function tkLog(msg) {
+ try {
+ execSync(`~/Projects/ticket-system/tk comment ${TICKET} ${JSON.stringify(msg)}`, {
+ env: { ...process.env, TK_AGENT: 'gmc-merge-recheck-cron' },
+ stdio: 'pipe',
+ });
+ } catch (e) {
+ console.error('tk comment failed:', e.message);
+ }
+}
+
+function selfUninstall() {
+ try { execSync(`launchctl bootout gui/${process.getuid()} ${LAUNCHD_LABEL}`, { stdio: 'pipe' }); } catch (e) { /* may already be gone */ }
+ try { fs.unlinkSync(LAUNCHD_PLIST); } catch (e) { /* already removed */ }
+ console.log('self-uninstalled launchd job', LAUNCHD_LABEL);
+}
+
+const apply = JSON.parse(fs.readFileSync(APPLY_ARTIFACT, 'utf8'));
+const targets = apply.rows.map(r => ({
+ offerId: r.offerId,
+ title: r.title,
+ expectColor: r.productAttributes.color,
+ expectMaterial: r.productAttributes.material,
+}));
+
+let state = fs.existsSync(STATE_FILE) ? JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')) : null;
+const now = Date.now();
+if (!state) {
+ state = { started_at: new Date(now).toISOString(), stalled_logged: false, offers: {} };
+}
+const deadlineAt = new Date(state.started_at).getTime() + DEADLINE_MS;
+const stalledAt = new Date(state.started_at).getTime() + STALLED_MS;
+
+const results = [];
+for (const t of targets) {
+ try {
+ const live = await mc.getProduct('online:en:US:' + t.offerId);
+ const a = live._v1.productAttributes || {};
+ const merged = a.color === t.expectColor && a.material === t.expectMaterial;
+ const stillLR = live.itemLevelIssues.some(i => i.code === 'legal_restrictions_policy_violation' && i.servability === 'disapproved');
+ results.push({ offerId: t.offerId, title: t.title, merged, stillLR, liveColor: a.color || null, liveMaterial: a.material || null });
+ } catch (e) {
+ results.push({ offerId: t.offerId, title: t.title, error: String(e).slice(0, 140) });
+ }
+ await new Promise(z => setTimeout(z, 300));
+}
+
+const changed = results.filter(r => {
+ const prev = state.offers[r.offerId];
+ return !prev || prev.merged !== r.merged || prev.stillLR !== r.stillLR || !!prev.error !== !!r.error;
+});
+for (const r of results) state.offers[r.offerId] = { merged: r.merged, stillLR: r.stillLR, error: r.error || null };
+
+const allMerged = results.every(r => r.merged && !r.error);
+const anyStillLR = results.some(r => r.stillLR);
+const anyMerged = results.some(r => r.merged);
+
+console.log(new Date().toISOString(), JSON.stringify(results.map(r => ({ o: r.offerId, m: r.merged, lr: r.stillLR, e: r.error || undefined }))));
+
+if (allMerged && !anyStillLR) {
+ tkLog(`Merge recheck PASS: all 6 offers show merged color/material, none still legal_restrictions-disapproved. ${results.map(r => r.offerId).join(', ')}`);
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 1));
+ selfUninstall();
+ process.exit(0);
+}
+
+if (now >= deadlineAt) {
+ const stillBad = results.filter(r => r.stillLR || !r.merged || r.error);
+ tkLog(`Merge recheck 72h DEADLINE reached. APPEAL NEEDED (console) for: ${stillBad.map(r => `${r.offerId} (${r.title}) merged=${r.merged} stillLR=${r.stillLR}${r.error ? ' err=' + r.error : ''}`).join('; ')}`);
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 1));
+ selfUninstall();
+ process.exit(0);
+}
+
+if (now >= stalledAt && !anyMerged && !state.stalled_logged) {
+ tkLog(`Merge recheck WARN: 12h elapsed, no offer has merged the new color/material yet. Possible naming/rule/datasource-link problem. Offers: ${results.map(r => r.offerId).join(', ')}`);
+ state.stalled_logged = true;
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 1));
+ process.exit(0);
+}
+
+if (changed.length) {
+ tkLog(`Merge recheck state change: ${changed.map(r => `${r.offerId} (${r.title}) merged=${r.merged} stillLR=${r.stillLR}${r.error ? ' err=' + r.error : ''}`).join('; ')}`);
+}
+
+fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 1));
+console.log(changed.length ? 'logged change to ticket' : 'no change, quiet');
← 7b93cd7 TK-11847 fix double Response-body read in APPLY path
·
back to Gmc Titlefix
·
chore: lint, refactor (session close) + track TK-11847 APPLY 2164b4c →