← back to Mfr Review Viewer Corruption
add mark-superseded.mjs — the TK-10922 fallback remediation (Steve-approved)
1bf0022a2e4a5c680513deac65ff94be418e1bb8 · 2026-09-04 09:17:41 -0700 · Steve Abrams
FileMaker DELETE is structurally blocked by [110]. This stamps each broken placeholder
master's existing free-text Mfr Pattern with 'SUPERSEDED - see master <keepRid>' instead
of deleting. Deliberately does NOT write the real code onto the placeholder -- that would
leave two records both carrying gz103 and make the duplicate ambiguity worse (flagged
independently by the first-principles panelist and the contrarian).
Same rails as execute-sweep: dry-run default, snapshot-first with refusal if the snapshot
fails, execute-time re-verify (skuMatch + no sample history + still a placeholder),
keep-worthiness guard, idempotent re-run, write-verified by read-back, ledgered with an
exact undo per record.
Dry-run clean on all 3. The --apply is classifier-blocked for an agent; Steve fires it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5qbBnj5C8dVC83qvX6fV5
Files touched
A scripts/mark-superseded.mjs
Diff
commit 1bf0022a2e4a5c680513deac65ff94be418e1bb8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Sep 4 09:17:41 2026 -0700
add mark-superseded.mjs — the TK-10922 fallback remediation (Steve-approved)
FileMaker DELETE is structurally blocked by [110]. This stamps each broken placeholder
master's existing free-text Mfr Pattern with 'SUPERSEDED - see master <keepRid>' instead
of deleting. Deliberately does NOT write the real code onto the placeholder -- that would
leave two records both carrying gz103 and make the duplicate ambiguity worse (flagged
independently by the first-principles panelist and the contrarian).
Same rails as execute-sweep: dry-run default, snapshot-first with refusal if the snapshot
fails, execute-time re-verify (skuMatch + no sample history + still a placeholder),
keep-worthiness guard, idempotent re-run, write-verified by read-back, ledgered with an
exact undo per record.
Dry-run clean on all 3. The --apply is classifier-blocked for an agent; Steve fires it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5qbBnj5C8dVC83qvX6fV5
---
scripts/mark-superseded.mjs | 158 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 158 insertions(+)
diff --git a/scripts/mark-superseded.mjs b/scripts/mark-superseded.mjs
new file mode 100644
index 0000000..a430c08
--- /dev/null
+++ b/scripts/mark-superseded.mjs
@@ -0,0 +1,158 @@
+#!/usr/bin/env node
+// mark-superseded.mjs — the TK-10922 fallback remediation (DTD dissent, Steve-approved).
+//
+// FileMaker DELETE is structurally blocked by error [110] (a delete-time relationship
+// resolution failure in the WALLPAPER graph). This does NOT delete. It stamps each broken
+// placeholder master's existing free-text `Mfr Pattern` field with:
+//
+// SUPERSEDED - see master <keepRecordId>
+//
+// Why this and not "write the real code onto it": writing gz103 onto the placeholder would
+// leave TWO records both carrying gz103 and make the duplicate-master ambiguity WORSE. Both
+// the first-principles panelist and the contrarian flagged that trap independently.
+//
+// SAFETY RAILS (mirror execute-sweep.mjs):
+// 1. DRY-RUN by default; nothing is written without --apply.
+// 2. Snapshot-first: full record snapshotted to data/restore/ BEFORE any write; refuses
+// to write without a snapshot on disk.
+// 3. Execute-time re-verify: writes ONLY if the record STILL belongs to this dw_sku
+// (skuMatch), STILL has no sample history, and its mfr is STILL a placeholder.
+// 4. Keep-worthiness: the named keep must exist and be keep-worthy, else refuse.
+// 5. Idempotent: a record already marked SUPERSEDED is skipped, not double-stamped.
+// 6. Every write ledgered with an exact undo (restore the prior field value).
+//
+// Usage:
+// node scripts/mark-superseded.mjs --plan data/plans/<plan>.json # DRY-RUN
+// node scripts/mark-superseded.mjs --plan data/plans/<plan>.json --apply # EXECUTE
+
+import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
+import { execFileSync } from 'node:child_process';
+import path from 'node:path';
+import os from 'node:os';
+import { fileURLToPath } from 'node:url';
+
+const __dir = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.join(__dir, '..');
+const RESTORE_DIR = path.join(ROOT, 'data', 'restore');
+const LOG_EXEC = path.join(os.homedir(), '.claude', 'yolo-queue', 'executed-reversible', 'log-exec.mjs');
+const FM_PROJECT = path.join(os.homedir(), 'Projects', 'filemaker-mcp');
+const FM_CLIENT = path.join(FM_PROJECT, 'src', 'fm-client.js');
+const FM_DB = 'WALLPAPER';
+const FM_LAYOUT = '*List Wallpapers - Full View';
+const FM_WRITE_LAYOUT = process.env.FM_WRITE_LAYOUT || 'Basic List of Fields';
+
+const arg = (k) => { const i = process.argv.indexOf('--' + k); return i >= 0 ? process.argv[i + 1] : undefined; };
+const APPLY = process.argv.includes('--apply');
+const PLAN_FILE = arg('plan');
+const TICKET = arg('ticket') || 'TK-10922';
+const AGENT = arg('agent') || process.env.TK_AGENT || 'claude-run-10922';
+
+for (const line of readFileSync(path.join(FM_PROJECT, '.env'), 'utf8').split('\n')) {
+ const m = line.match(/^([A-Z_]+)=(.*)$/); if (m && !process.env[m[1]]) process.env[m[1]] = m[2];
+}
+let _fm = null;
+const fm = async () => (_fm ||= await import('file://' + FM_CLIENT));
+
+const numericTail = (s) => { const m = String(s || '').match(/(\d[\d]*)\s*$/); return m ? m[1] : ''; };
+function parseNoteMfr(note) {
+ let t = String(note || '').trim(); if (!t) return '';
+ t = t.split(/[\r\n]/)[0].trim();
+ const hash = t.match(/#\s*([^\s;,-][^\s;,]*)/); if (hash) return hash[1].replace(/[.,]$/, '').trim();
+ const m = t.match(/^([^\s;]+?)(?:\s*;|\s+-\s+|\s+\$|\s+net\b|$)/i);
+ return m ? m[1].replace(/[.,]$/, '').trim() : t;
+}
+function classify(fd, dwSku) {
+ const clean = String(dwSku || '').trim().replace(/[-_ ]?sample$/i, '');
+ const tail = numericTail(clean);
+ const mfrPattern = String(fd['Mfr Pattern'] || '').trim();
+ const chase = String(fd['Vendor Sample - Where is Memo Send 2nd day'] || '').trim();
+ const noteMfr = parseNoteMfr(mfrPattern || (/#/.test(chase) ? chase : '')) || parseNoteMfr(chase);
+ const sampleOrdered = !!(String(fd['today for client'] || '').trim() || String(fd['Date WP Sample Sent'] || '').trim());
+ const codeDigits = String(noteMfr || mfrPattern || '').replace(/[^0-9]/g, '');
+ const mfrPlaceholder = !/[A-Za-z]/.test(noteMfr || '') && !!tail && codeDigits === tail;
+ const norm = (s) => String(s || '').toUpperCase().replace(/[^A-Z0-9]/g, '');
+ const target = norm(clean);
+ const skuMatch = !!target && (norm(String(fd.Series || '') + String(fd['JS Pattern'] || '')) === target || norm(fd['combo sku']) === target);
+ return { sampleOrdered, mfrPlaceholder, mfrPattern, noteMfr, skuMatch };
+}
+async function snapshot(rid, iso) {
+ const { getRecord } = await fm();
+ const rec = await getRecord(FM_DB, FM_LAYOUT, rid);
+ if (!rec) throw new Error(`record ${rid} not found`);
+ if (!existsSync(RESTORE_DIR)) mkdirSync(RESTORE_DIR, { recursive: true });
+ const f = path.join(RESTORE_DIR, `${rid}-${iso}.json`);
+ writeFileSync(f, JSON.stringify({ recordId: rec.recordId || rid, db: FM_DB, layout: FM_LAYOUT,
+ modId: rec.modId || null, captured_at: new Date().toISOString(), fieldData: rec.fieldData || {} }, null, 2));
+ if (!existsSync(f)) throw new Error(`snapshot write failed for ${rid}`);
+ return f;
+}
+function ledger(action, extra) {
+ try {
+ execFileSync('node', [LOG_EXEC, '--agent', AGENT, '--ticket', TICKET, '--action', action,
+ '--blast', String(extra.blast || 1), '--undo', extra.undo, '--verify', extra.verify], { stdio: 'pipe' });
+ } catch (e) { console.error(' ! ledger failed:', e.message); }
+}
+
+const MARK = /^SUPERSEDED\b/i;
+
+async function main() {
+ if (!PLAN_FILE) { console.error('ERROR: --plan <file.json> required'); process.exit(2); }
+ const plan = JSON.parse(readFileSync(PLAN_FILE, 'utf8'));
+ console.log(`\n=== mark-superseded ${APPLY ? 'APPLY (LIVE canonical writes)' : 'DRY-RUN (no writes)'} ===`);
+ console.log(`plan : ${PLAN_FILE}`);
+ console.log(`writes : ${plan.reduce((n, e) => n + e.deleteRecordIds.length, 0)} field stamps ticket: ${TICKET}\n`);
+
+ const iso = new Date().toISOString().replace(/[:.]/g, '-');
+ let done = 0, skipped = 0, failed = 0;
+ const { getRecord, updateRecord } = await fm();
+
+ for (const e of plan) {
+ console.log(`--- ${e.dw_sku} ---`);
+ const keep = await getRecord(FM_DB, FM_LAYOUT, e.keepRecordId).catch(() => null);
+ if (!keep) { console.log(` GUARD: keep ${e.keepRecordId} not found live — refusing`); skipped += e.deleteRecordIds.length; continue; }
+ const kc = classify(keep.fieldData || {}, e.dw_sku);
+ if (!(kc.sampleOrdered || (!kc.mfrPlaceholder && /[A-Za-z]/.test(kc.noteMfr || kc.mfrPattern || '')))) {
+ console.log(` GUARD: keep ${e.keepRecordId} is NOT keep-worthy — refusing (inverted plan)`); skipped += e.deleteRecordIds.length; continue;
+ }
+ console.log(` keep ${e.keepRecordId} verified keep-worthy (mfr "${kc.mfrPattern}")`);
+
+ for (const rid of e.deleteRecordIds) {
+ const rec = await getRecord(FM_DB, FM_LAYOUT, rid).catch(() => null);
+ if (!rec) { console.log(` SKIP ${rid}: no longer exists`); skipped++; continue; }
+ const c = classify(rec.fieldData || {}, e.dw_sku);
+ const oldVal = String(rec.fieldData['Mfr Pattern'] || '');
+ if (MARK.test(oldVal)) { console.log(` SKIP ${rid}: already marked ("${oldVal}")`); skipped++; continue; }
+ if (!c.skuMatch) { console.log(` SKIP ${rid}: foreign record — does NOT belong to ${e.dw_sku}`); skipped++; continue; }
+ if (c.sampleOrdered) { console.log(` SKIP ${rid}: has SAMPLE history — this is a real master`); skipped++; continue; }
+ if (!c.mfrPlaceholder) { console.log(` SKIP ${rid}: mfr "${oldVal}" is not a placeholder`); skipped++; continue; }
+
+ const newVal = `SUPERSEDED - see master ${e.keepRecordId}`;
+ let snapFile;
+ try { snapFile = await snapshot(rid, iso); }
+ catch (err) { console.log(` ! SNAPSHOT FAILED ${rid} (${err.message}) — REFUSING to write`); failed++; continue; }
+
+ if (!APPLY) { console.log(` ${rid}: WOULD set Mfr Pattern "${oldVal}" -> "${newVal}" (snapshot ${path.relative(ROOT, snapFile)}) (dry-run)`); continue; }
+ try {
+ const r = await updateRecord(FM_DB, FM_WRITE_LAYOUT, rid, { 'Mfr Pattern': newVal }, { dryRun: false });
+ if (!r.committed) { console.log(` ${rid}: no change (${r.note || 'values matched'})`); skipped++; continue; }
+ const back = await getRecord(FM_DB, FM_LAYOUT, rid);
+ const now = String(back?.fieldData?.['Mfr Pattern'] || '');
+ if (now !== newVal) { console.log(` ! ${rid}: WRITE UNVERIFIED — reads back as "${now}"`); failed++; continue; }
+ console.log(` ${rid}: "${oldVal}" -> "${now}" [committed + verified]`);
+ done++;
+ ledger(`TK-10922 mark placeholder master ${rid} (${e.dw_sku}) SUPERSEDED -> keep ${e.keepRecordId}; Mfr Pattern "${oldVal}" -> "${newVal}"`, {
+ blast: 1,
+ undo: `cd ${ROOT} && node -e "import('file://${FM_CLIENT}').then(m=>m.updateRecord('WALLPAPER','${FM_WRITE_LAYOUT}','${rid}',{'Mfr Pattern':'${oldVal}'},{dryRun:false}))"`,
+ verify: `getRecord ${rid}."Mfr Pattern" === "${newVal}"`,
+ });
+ } catch (err) { console.log(` ! WRITE FAILED ${rid}: ${err.message}`); failed++; }
+ }
+ console.log('');
+ }
+ console.log('=== SUMMARY ===');
+ console.log(` stamped : ${done}${APPLY ? '' : ' (dry-run: 0 written)'}`);
+ console.log(` skipped : ${skipped}`);
+ console.log(` failed : ${failed}`);
+ console.log(APPLY ? '\n[APPLIED — live writes committed, verified + ledgered]' : '\n[DRY-RUN — nothing written. Add --apply.]');
+}
+main().catch((e) => { console.error('FATAL:', e.message); process.exit(1); });
← 5b61b4a pre-stage the graph repair: cascade-impact-map parser + Stev
·
back to Mfr Review Viewer Corruption
·
Correct iOS rejection evidence to Apple 5.6 e7e862b →