← back to Filemaker Mcp
scripts/tk-10083-dryrun.mjs
136 lines
#!/usr/bin/env node
// TK-10083 (iter 2) LIVE-PROOF DRY-RUN — combo-SKU master matching for OP-ART-DECO-WAVES.
//
// Proves the corrected findExistingMaster resolves ALL THREE inconsistent forms of the
// SAME SKU to the ONE genuine master (record 240939) via a REAL, read-only FileMaker
// find — NOT a string simulation. Iteration 1 "passed" a pure-string simulation that
// assumed the stored calc value was already in hand; that assumption is false (FileMaker's
// calc "==" returns 401 for both the invoice and raw forms), which is why iter 1 still
// minted a duplicate. This script exercises the ACTUAL committed findExistingMaster
// against live FileMaker so the proof cannot lie.
//
// invoice form : OPARTDECOWAVES (no separators)
// FM calc form : OP-ARTDECO-WAVES (master's stored `combo sku` calc)
// raw form : OP-ART-DECO-WAVES (the form that minted the duplicate)
// genuine master = recordId 240939 (Series OP-ART, JS Pattern DECO-WAVES)
// duplicates (must NOT be returned) = 538697 / 538698 (Series DWC)
//
// HARD RAILS:
// * FM_READONLY=1 is forced — the fm-client write guard throws on any create/update.
// * Only fm.findRecords (GET/_find) is called; NO create/update/delete path is touched.
// * REQUIRED live proof: if FileMaker creds are absent OR FileMaker is unreachable, the
// script EXITS NON-ZERO (this is a required PASS, not a silent skip).
import { existsSync, readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const __dir = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dir, '..');
// --- load .env (the connector reads Cognito creds + FM_CLOUD_HOST from it) ---
const envPath = join(ROOT, '.env');
if (existsSync(envPath)) {
for (const line of readFileSync(envPath, 'utf8').split('\n')) {
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
if (m && !(m[1] in process.env)) {
let v = m[2];
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
process.env[m[1]] = v;
}
}
}
process.env.FM_READONLY = '1'; // belt-and-suspenders: block any accidental write path
const FORMS = {
invoice: 'OPARTDECOWAVES',
fm_calc: 'OP-ARTDECO-WAVES',
raw: 'OP-ART-DECO-WAVES',
};
const EXPECTED_MASTER = '240939';
const DUPLICATES = new Set(['538697', '538698']);
let failures = 0;
const assert = (cond, msg) => { if (!cond) { failures++; console.log(` ✗ FAIL: ${msg}`); } else { console.log(` ✓ ${msg}`); } };
console.log('=== TK-10083 iter2 LIVE-PROOF master-match dry-run (READ-ONLY, NO WRITES) ===\n');
// ---- REQUIRED: FileMaker creds must be present. Absent => non-zero exit (not a skip). ----
const haveCreds = !!(process.env.FM_CLOUD_HOST && (process.env.FM_CLARIS_EMAIL || process.env.FM_CLOUD_USER) && (process.env.FM_CLARIS_PASSWORD || process.env.FM_CLOUD_PASSWORD));
if (!haveCreds) {
console.log(' ✗ FAIL: FileMaker creds (FM_CLOUD_HOST + FM_CLARIS_EMAIL/PASSWORD) are ABSENT.');
console.log(' Live verification is REQUIRED for this fix — refusing to pass silently.');
console.log('\n=== RESULT: 1 ASSERTION FAILED (creds absent; live proof could not run) ===');
process.exit(1);
}
const MOD = await import(new URL('../lib/wallpaper.js', import.meta.url).href);
const { _internals } = MOD;
const { parseCombo, findExistingMaster, canonicalDashFor } = _internals;
// ---- Verify live reachability up front; unreachable => non-zero exit (not a skip). ----
const fm = await import(new URL('../src/fm-client.js', import.meta.url).href);
try {
await fm.ping();
console.log(' ✓ FileMaker Cloud reachable (Cognito auth + Data API session OK)\n');
} catch (e) {
console.log(` ✗ FAIL: FileMaker unreachable — live proof REQUIRED. (${e.message})`);
console.log('\n=== RESULT: 1 ASSERTION FAILED (FM unreachable; live proof could not run) ===');
process.exit(1);
}
// ---- [1] LIVE: each of the three forms resolves to the genuine master 240939 ----
console.log('[1] LIVE findExistingMaster() — each form must return master ' + EXPECTED_MASTER + ':');
const matchedIds = [];
for (const [label, form] of Object.entries(FORMS)) {
const p = parseCombo(form);
const dash = canonicalDashFor(p.key);
let ex;
try {
ex = await findExistingMaster(form, p, dash);
} catch (e) {
failures++; console.log(` ✗ ${label.padEnd(8)} "${form}" -> THREW ${e.message}`);
continue;
}
console.log(` ${label.padEnd(8)} "${form}" confident=${p.confident} canonicalDash="${dash}" -> id=${ex.id ?? 'null'} vid="${ex.vid || ''}" err=${ex.err ?? 'null'}`);
assert(ex.id === EXPECTED_MASTER, `${label} form resolves to master ${EXPECTED_MASTER} (not a duplicate, not null)`);
assert(!DUPLICATES.has(String(ex.id)), `${label} form did NOT return a duplicate (538697/538698)`);
if (ex.id) matchedIds.push(ex.id);
}
// ---- [2] All three collapse to exactly ONE master id (0 duplicates would be minted) ----
console.log('\n[2] All three forms resolve to exactly ONE master (0 duplicates minted):');
const uniq = [...new Set(matchedIds)];
console.log(` matched ids = ${JSON.stringify(matchedIds)} ; distinct = ${uniq.length}`);
assert(matchedIds.length === 3, 'all three forms returned a master (none fell through to create)');
assert(uniq.length === 1 && uniq[0] === EXPECTED_MASTER, `the single distinct master is ${EXPECTED_MASTER}`);
// ---- [3] resolveWallpaperSource returns the SAME single existing master (no create path) ----
console.log('\n[3] LIVE resolveWallpaperSource() — reuses the existing master, never creates:');
const { resolveWallpaperSource } = MOD;
for (const [label, form] of Object.entries(FORMS)) {
const r = await resolveWallpaperSource(form);
const exId = r._existing ? r._existing.id : null;
console.log(` ${label.padEnd(8)} ok=${r.ok} existingMasterId=${exId ?? 'null'} reason="${r.reason || ''}"`);
assert(exId === EXPECTED_MASTER, `${label}: resolveWallpaperSource sees existing master ${EXPECTED_MASTER}`);
}
// ---- [4] FAIL-CLOSED: a genuinely-ambiguous, non-existent SKU never reaches create ----
// A made-up all-alpha SKU with no master and no confident split must return needs-review.
console.log('\n[4] Fail-closed guard — ambiguous, non-existent SKU routes to review (no create):');
const GHOST = 'ZZQWXNONEXISTENTPATTERN';
{
const p = parseCombo(GHOST);
console.log(` parseCombo("${GHOST}") -> confident=${p.confident} prefix="${p.prefix}"`);
assert(p.confident === false, 'ghost all-alpha SKU parses as NOT confident');
const r = await resolveWallpaperSource(GHOST);
console.log(` resolveWallpaperSource -> ok=${r.ok} reason="${r.reason || ''}"`);
assert(r.ok === false, 'ghost SKU resolve fails (does not proceed toward a create)');
const okReason = /ambiguous-split-needs-review|no mfr number/.test(r.reason || '');
assert(okReason, 'ghost SKU reason is a review/skip reason, not a create');
}
console.log(`\n=== RESULT: ${failures === 0 ? 'ALL ASSERTIONS PASSED (live FileMaker read-only proof)' : failures + ' ASSERTION(S) FAILED'} ===`);
process.exit(failures === 0 ? 0 : 1);