← back to Sample Followup Sweep
scripts/tk12118-vid-backfill.mjs
74 lines
// TK-12118 — backfill the missing `vid` on the 3 VERIFIED outstanding empty-vid records the
// coverage-reconcile canary (TK-12117) flagged as LEAKED (outstanding in FileMaker but dropped
// by the sweep before grouping because vid was blank → never chased, never reported).
//
// Vendors verified LIVE against FileMaker sibling records (not prefix guesses):
// DWTT70793 rec 538622 entered 07/30/2026 -> vid "Thib" (Supplier "Thibaut Wallpaper"; sibling 02/18/2025 carries vid=Thib)
// GLM9116 rec 541395 entered 09/08/2026 -> vid "FTR" (Company "future Textile"; siblings carry vid=FTR = Future Textiles)
// DWPN100994 rec 541415 entered 09/08/2026 -> vid "POI" (Supplier "Pointe at Justin David"; sibling carries vid=POI)
// NOTE: DWPN100994 ARRIVED 09/14/2026 on a twin record — after this backfill the never-false-chase
// guard (TK-12105) will correctly SUPPRESS it (arrived), not chase it. Expected/correct.
//
// NOT included: DWLC1059 (Supplier "LA Walls") — LA Walls has NO vid anywhere in FileMaker or the
// fleet; assigning one is a vendor-registry decision for Steve. Left as WARN, not backfilled.
//
// USAGE:
// node ~/Projects/sample-followup-sweep/scripts/tk12118-vid-backfill.mjs # backfill + write restore-map
// node ~/Projects/sample-followup-sweep/scripts/tk12118-vid-backfill.mjs --rollback # undo (set vid back to '')
// Blast radius = 3 rows. Reversible via the restore-map (data/tk12118-vid-backfill-restore-map.json).
import { readFileSync, writeFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';
const cfg = JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf8'));
const env = cfg?.mcpServers?.filemaker?.env || {};
for (const k of ['FM_CLOUD_HOST', 'FM_CLARIS_EMAIL', 'FM_CLARIS_PASSWORD']) process.env[k] = env[k];
const fm = await import('/Users/macstudio3/Projects/filemaker-mcp/src/fm-client.js');
const DB = 'WALLPAPER', LAYOUT = 'Report for old memo samples', FIELD = 'vid';
// VERIFY layout: 'combo sku' does NOT serialize into fieldData on the write layout (findable but blank
// on read), so the SKU-match guard is verified on 'REPORT ON SAMPLES ORDERED' where it reads correctly
// (same base table WALLPAPER => same recordId space). The vid write still targets the write layout.
const VERIFY_LAYOUT = 'REPORT ON SAMPLES ORDERED';
const MAP = join(homedir(), 'Projects/sample-followup-sweep/data/tk12118-vid-backfill-restore-map.json');
const TARGETS = [
{ sku: 'DWTT70793', recordId: '538622', vid: 'Thib', entered: '07/30/2026' },
{ sku: 'GLM9116', recordId: '541395', vid: 'FTR', entered: '09/08/2026' },
{ sku: 'DWPN100994', recordId: '541415', vid: 'POI', entered: '09/08/2026' },
];
const ROLLBACK = process.argv.includes('--rollback');
// Read a record by its FileMaker recordId on the VERIFY layout (where 'combo sku' serializes).
async function readRec(recordId) {
try { return await fm.getRecord(DB, VERIFY_LAYOUT, recordId); }
catch { return null; }
}
if (ROLLBACK) {
const map = JSON.parse(readFileSync(MAP, 'utf8'));
for (const m of map) {
await fm.updateRecord(DB, LAYOUT, m.recordId, { [FIELD]: m.oldVid }, { dryRun: false });
console.log(`rolled back rec ${m.recordId} (${m.sku}) vid -> "${m.oldVid}"`);
}
console.log('rollback complete.');
process.exit(0);
}
const restore = [];
for (const t of TARGETS) {
// SAFETY: only write if the record still has an EMPTY vid and matches the expected SKU (guard against drift)
const rec = await readRec(t.recordId);
const cur = rec ? (rec.fieldData[FIELD] || '').trim() : null;
const curSku = rec ? (rec.fieldData['combo sku'] || '').trim() : null;
if (!rec) { console.log(`SKIP ${t.sku} rec ${t.recordId}: record not found`); continue; }
if (curSku !== t.sku) { console.log(`SKIP ${t.sku} rec ${t.recordId}: SKU mismatch (found "${curSku}") — aborting this row`); continue; }
if (cur !== '') { console.log(`SKIP ${t.sku} rec ${t.recordId}: vid already "${cur}" (not empty) — not overwriting`); continue; }
restore.push({ sku: t.sku, recordId: t.recordId, oldVid: '' });
await fm.updateRecord(DB, LAYOUT, t.recordId, { [FIELD]: t.vid }, { dryRun: false });
// vid does not serialize into fieldData on either readable layout, so per-row read-back is impossible;
// authoritative verification is the canary (coverage-reconcile.mjs) — leaked must drop by the write count.
console.log(`OK ${t.sku} rec ${t.recordId}: SKU verified on ${VERIFY_LAYOUT}, vid set -> "${t.vid}" (confirm via canary leaked-count)`);
}
writeFileSync(MAP, JSON.stringify(restore, null, 2));
console.log(`\nrestore-map written: ${MAP} (${restore.length} rows). Rollback: node scripts/tk12118-vid-backfill.mjs --rollback`);