← back to Dw Signup Fulfillment
verification/tk11285/race.js
75 lines
'use strict';
// TK-11285 preflight repro: does the shipped in-process decision lock protect
// data/trade-applications.jsonl from the OTHER writer, scripts/recover-stuck-apps.js?
// Both sides are REAL production code. Only lib/email + lib/shopify are stubbed
// (zero external calls). Run: node -r ./repro/stubs.js repro/race.js [--control]
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
const ROOT = path.join(__dirname, '..', '..');
const trade = require(path.join(ROOT, 'lib', 'trade'));
const CONTROL = process.argv.includes('--control');
const APPS = trade.APPS_PATH;
const sleep = ms => new Promise(r => setTimeout(r, ms));
const N = 6;
function seed() {
const rows = [];
for (let i = 1; i <= N; i++) rows.push({
id: `TRADE-SEED-${i}`, email: `seed${i}@example.com`, business_name: `Seed ${i}`,
resale_cert: '', phone: '', extra: {}, shopify_customer_id: '5551111',
link_status: 'linked', link_via: 'existing', status: 'pending',
created_at: `2026-09-01T00:00:0${i}.000Z`, decided_at: null, decision: null, assigned_rep: null,
});
fs.mkdirSync(path.dirname(APPS), { recursive: true });
fs.writeFileSync(APPS, rows.map(r => JSON.stringify(r)).join('\n') + '\n');
}
const read = () => fs.readFileSync(APPS, 'utf8').split('\n').filter(Boolean).map(l => JSON.parse(l));
(async () => {
seed();
const before = read();
let child = null;
if (!CONTROL) {
// The OTHER writer: real scripts/recover-stuck-apps.js --send-only, separate PROCESS.
child = spawn(process.execPath, ['-r', path.join(__dirname, 'stubs.js'),
path.join(ROOT, 'scripts', 'recover-stuck-apps.js'),
'--send-only', '--limit', String(N), '--delay', '50'],
{ cwd: ROOT, env: { ...process.env, DRY_RUN: '0', REPRO_SEND_MS: '250' }, stdio: ['ignore', 'pipe', 'pipe'] });
let childOut = '';
const snapshotTaken = new Promise(res => {
child.stdout.on('data', d => { childOut += d; if (/LINKED not-yet-emailed=/.test(childOut)) res(); });
});
child.stderr.on('data', d => process.stderr.write('[child] ' + d));
// Deterministic: wait until the child has ACTUALLY taken its snapshot (it prints the
// target count right after readRows()), so the interleaving is not timing luck.
await snapshotTaken;
console.log('[t] child snapshot taken; live service now acts inside the window');
}
// --- the live service does its work DURING that window ---
const fresh = trade.apply({ email: 'newdesigner@example.com', business_name: 'Arrived During Window' });
const approveRes = await trade.approve('TRADE-SEED-1');
if (child) await new Promise(r => child.on('exit', r));
const after = read();
const newApp = after.find(r => r.id === fresh.id);
const seed1 = after.find(r => r.id === 'TRADE-SEED-1');
const out = {
mode: CONTROL ? 'CONTROL (no second writer)' : 'RACE (recover-stuck-apps.js --send-only concurrent)',
approve_returned_ok: approveRes.ok === true,
approve_status_returned: approveRes.status || null,
rows_before: before.length, rows_after: after.length,
new_intake_id: fresh.id,
new_intake_survived: !!newApp,
seed1_status_after: seed1 ? seed1.status : '<row missing>',
seed1_decision_survived: !!(seed1 && seed1.status === 'approved'),
};
out.VERDICT = (out.new_intake_survived && out.seed1_decision_survived)
? 'NO LOSS' : 'DATA LOSS';
console.log(JSON.stringify(out, null, 2));
fs.writeFileSync(path.join(__dirname, CONTROL ? 'control.json' : 'race.json'), JSON.stringify(out, null, 2) + '\n');
})();