← back to Sample Followup Sweep
Centralize sample follow-up compliance enforcement
984ab8ef961919b74d5ca23ec0c6781d4af09f59 · 2026-08-28 14:36:13 -0700 · Steve Abrams
Files touched
M bin/run.jsA lib/george-transport.jsM lib/pre-send-gate.jsM scripts/make-drafts.jsM scripts/scheduled-run.mjsM scripts/send-drafts.jsM scripts/send-reconciled.mjsM server.jsA test/george-transport.test.jsM test/pre-send-gate.test.jsA test/sendable-callsite-coverage.test.jsA verification/e2e-proof.json
Diff
commit 984ab8ef961919b74d5ca23ec0c6781d4af09f59
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Aug 28 14:36:13 2026 -0700
Centralize sample follow-up compliance enforcement
---
bin/run.js | 3 +-
lib/george-transport.js | 40 ++++++++++++++++++++++++++
lib/pre-send-gate.js | 28 ++++++++++++++++--
scripts/make-drafts.js | 4 ++-
scripts/scheduled-run.mjs | 24 ++++------------
scripts/send-drafts.js | 22 ++++----------
scripts/send-reconciled.mjs | 13 +++++++--
server.js | 19 ++++--------
test/george-transport.test.js | 42 +++++++++++++++++++++++++++
test/pre-send-gate.test.js | 9 ++++++
test/sendable-callsite-coverage.test.js | 37 ++++++++++++++++++++++++
verification/e2e-proof.json | 51 +++++++++++++++++++++++++++++++++
12 files changed, 234 insertions(+), 58 deletions(-)
diff --git a/bin/run.js b/bin/run.js
index 2ce56f1..72b3609 100644
--- a/bin/run.js
+++ b/bin/run.js
@@ -7,6 +7,7 @@ const fs = require('fs');
const path = require('path');
const { sweep } = require('../lib/sweep');
const { compose } = require('../lib/compose');
+const { createGmailDraftArtifact } = require('../lib/george-transport');
const slug = process.argv[2] || 'osborne-little';
const root = path.join(__dirname, '..');
@@ -28,7 +29,7 @@ const draft = compose(vendor, followUp);
const outDir = path.join(root, 'out');
fs.mkdirSync(outDir, { recursive: true });
if (missing.length === 0) {
- const payload = { account: 'info', to: draft.to, subject: draft.subject, body: draft.html };
+ const payload = createGmailDraftArtifact({ account: 'info', to: draft.to, subject: draft.subject, body: draft.html });
fs.writeFileSync(path.join(outDir, `${slug}.draft.json`), JSON.stringify(payload, null, 2));
}
fs.writeFileSync(path.join(outDir, `${slug}.preview.html`), preview(vendor, draft, followUp, escalation, skipped));
diff --git a/lib/george-transport.js b/lib/george-transport.js
new file mode 100644
index 0000000..c32aabd
--- /dev/null
+++ b/lib/george-transport.js
@@ -0,0 +1,40 @@
+'use strict';
+
+const http = require('node:http');
+const { assertPreSendCompliance, REQUIRED_FROM } = require('./pre-send-gate');
+
+const SENDABLE_PATHS = new Set(['/api/send', '/api/drafts']);
+
+function assertSendablePayload(payload) {
+ assertPreSendCompliance({
+ from: payload?.account === 'info' ? REQUIRED_FROM : '',
+ subject: payload?.subject,
+ body: payload?.body,
+ });
+ return payload;
+}
+
+function createGmailDraftArtifact(payload) {
+ return assertSendablePayload(payload);
+}
+
+async function georgeRequest({ path, payload, headers = {}, requestImpl = http.request }) {
+ if (!SENDABLE_PATHS.has(path)) throw new Error(`Unsupported sendable George path: ${path}`);
+ assertSendablePayload(payload); // Must run before serialization, credential reads, or network setup.
+ const body = JSON.stringify(payload);
+ return new Promise((resolve) => {
+ const req = requestImpl({
+ host: '127.0.0.1', port: 9850, path, method: 'POST',
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), ...headers },
+ }, (response) => {
+ let responseBody = '';
+ response.on('data', (chunk) => { responseBody += chunk; });
+ response.on('end', () => resolve({ statusCode: response.statusCode || 0, body: responseBody }));
+ });
+ req.on('error', (error) => resolve({ statusCode: 0, body: error.message }));
+ req.write(body);
+ req.end();
+ });
+}
+
+module.exports = { assertSendablePayload, createGmailDraftArtifact, georgeRequest, SENDABLE_PATHS };
diff --git a/lib/pre-send-gate.js b/lib/pre-send-gate.js
index 257ce45..9dda896 100644
--- a/lib/pre-send-gate.js
+++ b/lib/pre-send-gate.js
@@ -2,16 +2,38 @@
const REQUIRED_FROM = 'info@designerwallcoverings.com';
const ADDRESS_PATTERN = /15442\s+Ventura\s+Blvd(?:\.|\s)*(?:#|Suite)?\s*102[\s\S]*Sherman\s+Oaks,?\s*CA\s+91403/i;
-const UNSUBSCRIBE_PATTERN = /(?:mailto:|https?:\/\/)[^"'\s>]*(?:unsubscribe|opt[-_ ]?out)|(?:unsubscribe|opt\s*out)[\s\S]{0,160}(?:mailto:|https?:\/\/)/i;
const MISLEADING_SUBJECT_PATTERN = /\b(?:re|fw|fwd)\s*:/i;
+function stripHtmlComments(value) {
+ return String(value || '').replace(/<!--[\s\S]*?-->/g, '');
+}
+
+function hasWorkingOptOutLink(body) {
+ const visible = stripHtmlComments(body);
+ const hrefs = [...visible.matchAll(/<a\b[^>]*\bhref\s*=\s*(["'])(.*?)\1[^>]*>/gi)].map((match) => match[2].trim());
+ return hrefs.some((href) => {
+ try {
+ const url = new URL(href);
+ const optOutSignal = `${url.pathname} ${url.search} ${url.hash}`;
+ if (!/(?:unsubscribe|opt[-_ ]?out)/i.test(optOutSignal)) return false;
+ if (url.protocol === 'mailto:') return url.pathname.toLowerCase() === REQUIRED_FROM;
+ if (url.protocol !== 'https:') return false;
+ const host = url.hostname.toLowerCase();
+ if (!host.includes('.') || host === 'invalid.example' || host.endsWith('.invalid') || host.endsWith('.example') || host.endsWith('.test') || host === 'localhost') return false;
+ return true;
+ } catch {
+ return false;
+ }
+ });
+}
+
function assertPreSendCompliance({ from, subject, body }) {
const errors = [];
if (String(from || '').trim().toLowerCase() !== REQUIRED_FROM) errors.push(`From must be ${REQUIRED_FROM}`);
if (!String(subject || '').trim()) errors.push('Subject is required');
if (MISLEADING_SUBJECT_PATTERN.test(String(subject || ''))) errors.push('Subject must not imply a reply or forward');
if (!ADDRESS_PATTERN.test(String(body || ''))) errors.push('Body must include the DW physical postal address');
- if (!UNSUBSCRIBE_PATTERN.test(String(body || ''))) errors.push('Body must include a working unsubscribe or opt-out link');
+ if (!hasWorkingOptOutLink(body)) errors.push('Body must include a working unsubscribe or opt-out link');
if (errors.length) {
const error = new Error(`PRE-SEND COMPLIANCE GATE BLOCKED: ${errors.join('; ')}`);
error.code = 'PRE_SEND_COMPLIANCE_BLOCKED';
@@ -21,4 +43,4 @@ function assertPreSendCompliance({ from, subject, body }) {
return true;
}
-module.exports = { assertPreSendCompliance, REQUIRED_FROM };
+module.exports = { assertPreSendCompliance, hasWorkingOptOutLink, REQUIRED_FROM };
diff --git a/scripts/make-drafts.js b/scripts/make-drafts.js
index c9d3641..9116814 100644
--- a/scripts/make-drafts.js
+++ b/scripts/make-drafts.js
@@ -4,6 +4,7 @@
const fs = require('fs');
const path = require('path');
const { compose } = require('../lib/compose');
+const { createGmailDraftArtifact } = require('../lib/george-transport');
const SHIP_TO = { name: 'Designer Wallcoverings', line1: '15442 Ventura Blvd. #102', city_state_zip: 'Sherman Oaks, CA 91403', phone: '1-888-373-4564' };
const root = path.join(__dirname, '..');
@@ -34,7 +35,8 @@ for (const key of Object.keys(groups)) {
if (!rows.length) continue; // nothing real to chase
const vendor = { name: g.name, account_number: g.account, sample_email: g.to, ship_to: SHIP_TO };
const d = compose(vendor, rows);
- drafts.push({ to: d.to, subject: d.subject, body: d.html, slugs: g.slugs, name: g.name, items: rows.length });
+ const payload = createGmailDraftArtifact({ account: 'info', to: d.to, subject: d.subject, body: d.html });
+ drafts.push({ ...payload, slugs: g.slugs, name: g.name, items: rows.length });
}
drafts.sort((a, b) => b.items - a.items);
diff --git a/scripts/scheduled-run.mjs b/scripts/scheduled-run.mjs
index 7de4ce1..412c573 100644
--- a/scripts/scheduled-run.mjs
+++ b/scripts/scheduled-run.mjs
@@ -27,7 +27,7 @@ const require = createRequire(import.meta.url);
const __dir = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dir, '..');
const { compose } = require(join(ROOT, 'lib', 'compose.js'));
-const { assertPreSendCompliance, REQUIRED_FROM } = require(join(ROOT, 'lib', 'pre-send-gate.js'));
+const { georgeRequest } = require(join(ROOT, 'lib', 'george-transport.js'));
const SEND = process.argv.includes('--send');
// Steve 8/20: DRAFT mode — create a Gmail draft per vendor in info@ Drafts for human review,
@@ -109,29 +109,15 @@ function georgeCreds() {
return { auth: 'Basic ' + Buffer.from(auth).toString('base64'), token: g('GEORGE_EXTERNAL_SEND_TOKEN') };
}
function georgeSend(payload) {
- assertPreSendCompliance({ from: payload.account === 'info' ? REQUIRED_FROM : '', subject: payload.subject, body: payload.body });
const { auth, token } = georgeCreds();
- const body = JSON.stringify(payload);
- return new Promise((resolve) => {
- const req = http.request({ host: '127.0.0.1', port: 9850, path: '/api/send', method: 'POST',
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), Authorization: auth, 'X-Send-Approval': token } },
- (r) => { let b = ''; r.on('data', (d) => b += d); r.on('end', () => { try { const j = JSON.parse(b); resolve({ ok: !!j.success, id: j.messageId || '', detail: b.slice(0, 200) }); } catch { resolve({ ok: false, detail: b.slice(0, 200) }); } }); });
- req.on('error', (e) => resolve({ ok: false, detail: e.message }));
- req.write(body); req.end();
- });
+ return georgeRequest({ path: '/api/send', payload, headers: { Authorization: auth, 'X-Send-Approval': token } })
+ .then(({ body }) => { try { const j = JSON.parse(body); return { ok: !!j.success, id: j.messageId || '', detail: body.slice(0, 200) }; } catch { return { ok: false, detail: body.slice(0, 200) }; } });
}
// Create a Gmail draft in info@ Drafts (POST /api/drafts — no send-approval token needed).
function georgeDraft(payload) {
- assertPreSendCompliance({ from: payload.account === 'info' ? REQUIRED_FROM : '', subject: payload.subject, body: payload.body });
const { auth } = georgeCreds();
- const body = JSON.stringify(payload);
- return new Promise((resolve) => {
- const req = http.request({ host: '127.0.0.1', port: 9850, path: '/api/drafts', method: 'POST',
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), Authorization: auth } },
- (r) => { let b = ''; r.on('data', (d) => b += d); r.on('end', () => { try { const j = JSON.parse(b); resolve({ ok: !!j.success, id: j.draftId || '', detail: b.slice(0, 200) }); } catch { resolve({ ok: false, detail: b.slice(0, 200) }); } }); });
- req.on('error', (e) => resolve({ ok: false, detail: e.message }));
- req.write(body); req.end();
- });
+ return georgeRequest({ path: '/api/drafts', payload, headers: { Authorization: auth } })
+ .then(({ body }) => { try { const j = JSON.parse(body); return { ok: !!j.success, id: j.draftId || '', detail: body.slice(0, 200) }; } catch { return { ok: false, detail: body.slice(0, 200) }; } });
}
// Live suppression source: recipients the team already emailed a "New Sample Request" to recently
// (harvested straight from info@ Sent via George). Self-updating — no manual list to maintain.
diff --git a/scripts/send-drafts.js b/scripts/send-drafts.js
index ea798c2..e1051cb 100644
--- a/scripts/send-drafts.js
+++ b/scripts/send-drafts.js
@@ -3,9 +3,8 @@
// carrying the human-approval token (Steve-approved batch, 2026-08-14).
// Secrets are read from george-gmail/.env at runtime and never printed.
const fs = require('fs');
-const http = require('http');
const path = require('path');
-const { assertPreSendCompliance, REQUIRED_FROM } = require('../lib/pre-send-gate');
+const { georgeRequest } = require('../lib/george-transport');
const GENV = '/Users/macstudio3/Projects/george-gmail/.env';
function env(k) { try { const m = fs.readFileSync(GENV, 'utf8').match(new RegExp('^' + k + '=(.+)$', 'm')); return m ? m[1].trim() : ''; } catch (e) { return ''; } }
@@ -19,21 +18,10 @@ const basic = 'Basic ' + Buffer.from(auth).toString('base64');
const drafts = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'out', 'all-drafts.json'), 'utf8'));
function send(d) {
- return new Promise((res) => {
- const payload = { account: 'info', to: d.to, subject: d.subject, body: d.body };
- try {
- assertPreSendCompliance({ from: payload.account === 'info' ? REQUIRED_FROM : '', subject: payload.subject, body: payload.body });
- } catch (error) {
- return res({ code: 0, body: JSON.stringify({ success: false, error: error.message }) });
- }
- const wirePayload = JSON.stringify(payload);
- const req = http.request({
- host: '127.0.0.1', port: 9850, path: '/api/send', method: 'POST',
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(wirePayload), Authorization: basic, 'X-Send-Approval': token },
- }, r => { let b = ''; r.on('data', x => b += x); r.on('end', () => res({ code: r.statusCode, body: b })); });
- req.on('error', e => res({ code: 0, body: e.message }));
- req.write(wirePayload); req.end();
- });
+ const payload = { account: 'info', to: d.to, subject: d.subject, body: d.body };
+ return georgeRequest({ path: '/api/send', payload, headers: { Authorization: basic, 'X-Send-Approval': token } })
+ .then((result) => ({ code: result.statusCode, body: result.body }))
+ .catch((error) => ({ code: 0, body: JSON.stringify({ success: false, error: error.message }) }));
}
(async () => {
diff --git a/scripts/send-reconciled.mjs b/scripts/send-reconciled.mjs
index 2d91764..044929e 100644
--- a/scripts/send-reconciled.mjs
+++ b/scripts/send-reconciled.mjs
@@ -4,8 +4,10 @@
// letter per vendor into info@ Drafts (NEVER auto-sends). Steve reviews + sends from Drafts.
// Letter = intro + "Attn / processed by: <vendor processor>" + Ref#·Date Ordered·Manufacturer# table.
// WIN env overrides the FileMaker date range (default = today's fill-in window).
-import { readFileSync, writeFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; import http from 'node:http';
+import { readFileSync, writeFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; import { createRequire } from 'node:module';
+const require = createRequire(import.meta.url);
const ROOT = join(homedir(), 'Projects/sample-followup-sweep');
+const { georgeRequest } = require(join(ROOT, 'lib/george-transport.js'));
// Rolling window: memos that crossed the 10-day follow-up floor within the last CATCHUP days (default 7),
// so a daily scheduled run always covers any gap. Env WIN overrides for a manual one-off. (Draft-dedup
// below stops the same memo being drafted twice across runs.)
@@ -61,13 +63,18 @@ ${tbl}
</table>
<p>Ship to:<br>${esc(SHIP.name)}<br>${esc(SHIP.line1)}<br>${esc(SHIP.csz)}<br>${esc(SHIP.phone)}</p>
<p><strong>Sidemark: Samples ASAP</strong></p>
-<p>Thank you!<br>Showroom Manager</p></div>`;
+<p>Thank you!<br>Showroom Manager</p>
+<p style="font-size:11px;color:#666">Designer Wallcoverings · 15442 Ventura Blvd. #102 · Sherman Oaks, CA 91403<br>
+To stop receiving sample follow-up emails, <a href="mailto:info@designerwallcoverings.com?subject=Unsubscribe%20from%20sample%20follow-ups">unsubscribe here</a>.</p></div>`;
}
// George draft (account=info, /api/drafts — no send-approval token)
const genv = k => { for (const f of ['/Users/macstudio3/Projects/Designer-Wallcoverings/DW-MCP/.env', '/Users/macstudio3/Projects/george-gmail/.env']) { try { const m = readFileSync(f, 'utf8').match(new RegExp('^' + k + '=(.+)$', 'm')); if (m) return m[1].trim(); } catch {} } return ''; };
let auth = genv('GEORGE_BASIC_AUTH'); if (!auth.includes(':')) auth = 'admin:' + genv('GEORGE_BASIC_AUTH_PASS');
auth = 'Basic ' + Buffer.from(auth).toString('base64');
-function draft(to, subject, body) { return new Promise(res => { const b = JSON.stringify({ account: 'info', to, subject, body }); const rq = http.request({ host: '127.0.0.1', port: 9850, path: '/api/drafts', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(b), Authorization: auth } }, r => { let d = ''; r.on('data', x => d += x); r.on('end', () => { try { const j = JSON.parse(d); res(j.success ? 'ok' : d.slice(0, 80)); } catch { res(d.slice(0, 80)); } }); }); rq.on('error', e => res(e.message)); rq.write(b); rq.end(); }); }
+async function draft(to, subject, body) {
+ const result = await georgeRequest({ path: '/api/drafts', payload: { account: 'info', to, subject, body }, headers: { Authorization: auth } });
+ try { const parsed = JSON.parse(result.body); return parsed.success ? 'ok' : result.body.slice(0, 80); } catch { return result.body.slice(0, 80); }
+}
const DO = process.argv.includes('--draft');
// Draft-dedup: never re-draft a memo (keyed by vendor+combo-sku) that a prior run already drafted,
diff --git a/server.js b/server.js
index de69a0f..530e479 100644
--- a/server.js
+++ b/server.js
@@ -9,7 +9,7 @@ const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
const { compose } = require('./lib/compose');
-const { assertPreSendCompliance, REQUIRED_FROM } = require('./lib/pre-send-gate');
+const { createGmailDraftArtifact, georgeRequest } = require('./lib/george-transport');
const ROOT = __dirname;
const AUTH = 'Basic ' + Buffer.from('admin:DW2024!').toString('base64');
@@ -136,11 +136,6 @@ const server = http.createServer(async (req, res) => {
const addrs = String(payload.to).split(',').map(a => a.trim().toLowerCase()).filter(a => a.includes('@'));
if (addrs.length && addrs.every(a => be[a])) return send(res, 409, { error: 'already sent to this vendor — click again to force-resend', to: payload.to, alreadySent: true });
}
- try {
- assertPreSendCompliance({ from: payload.account === 'info' ? REQUIRED_FROM : '', subject: payload.subject, body: payload.body });
- } catch (error) {
- return send(res, 422, { error: error.message, reasons: error.reasons || [] });
- }
// George's canonical creds live in the DW-MCP .env (the file George's server loads into `creds`);
// GEORGE_EXTERNAL_SEND_TOKEN lives in george-gmail/.env. Search both, canonical first. (2026-08-15 fix:
// george-gmail/.env has no GEORGE_BASIC_AUTH, so the old single-file read sent an empty pass → 401.)
@@ -149,9 +144,7 @@ const server = http.createServer(async (req, res) => {
let gauth = genv('GEORGE_BASIC_AUTH'); if (!gauth.includes(':')) gauth = 'admin:' + genv('GEORGE_BASIC_AUTH_PASS');
const token = genv('GEORGE_EXTERNAL_SEND_TOKEN');
if (!token) return send(res, 500, { error: 'GEORGE_EXTERNAL_SEND_TOKEN not configured' });
- const gp = JSON.stringify(payload);
- const greq = http.request({ host: '127.0.0.1', port: 9850, path: '/api/send', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(gp), Authorization: 'Basic ' + Buffer.from(gauth).toString('base64'), 'X-Send-Approval': token } }, gr => {
- let gb = ''; gr.on('data', x => gb += x); gr.on('end', () => {
+ georgeRequest({ path: '/api/send', payload, headers: { Authorization: 'Basic ' + Buffer.from(gauth).toString('base64'), 'X-Send-Approval': token } }).then(({ body: gb }) => {
let ok = false, mid = ''; try { const j = JSON.parse(gb); ok = !!j.success; mid = j.messageId || ''; } catch (e) {}
if (ok) {
try { const s = sent(); s.byEmail = s.byEmail || {}; const iso = new Date().toISOString(); for (let a of String(payload.to).split(',')) { a = a.trim().toLowerCase(); if (!a.includes('@')) continue; const cur = s.byEmail[a]; if (!cur) s.byEmail[a] = { lastSent: iso, count: 1 }; else { cur.count++; cur.lastSent = iso; } } fs.writeFileSync(p('data', 'sent.json'), JSON.stringify(s, null, 2)); } catch (e) {}
@@ -159,10 +152,7 @@ const server = http.createServer(async (req, res) => {
return send(res, 200, { ok: true, messageId: mid, to: payload.to });
}
return send(res, 502, { error: 'George send blocked/failed', detail: gb.slice(0, 200) });
- });
- });
- greq.on('error', e => send(res, 502, { error: e.message }));
- greq.write(gp); greq.end();
+ }).catch((error) => send(res, error.code === 'PRE_SEND_COMPLIANCE_BLOCKED' ? 422 : 502, { error: error.message, reasons: error.reasons || [] }));
return;
}
@@ -172,7 +162,8 @@ const server = http.createServer(async (req, res) => {
const queue = picked.map(v => {
const cc = c[v.slug] || {}; const d = draftFor(v, c);
const missing = []; if (!cc.sample_email) missing.push('sample_email'); if (!cc.account_number) missing.push('account_number');
- return { slug: v.slug, name: cc.name || v.name, to: d.to, subject: d.subject, body: d.html, items: v.items.length, ready: missing.length === 0, missing };
+ const sendable = missing.length === 0 ? createGmailDraftArtifact({ account: 'info', to: d.to, subject: d.subject, body: d.html }) : { to: d.to, subject: d.subject, body: d.html };
+ return { slug: v.slug, name: cc.name || v.name, ...sendable, items: v.items.length, ready: missing.length === 0, missing };
});
fs.mkdirSync(p('out'), { recursive: true });
fs.writeFileSync(p('out', 'send-queue.json'), JSON.stringify({ generated: new Date().toISOString(), queue }, null, 2));
diff --git a/test/george-transport.test.js b/test/george-transport.test.js
new file mode 100644
index 0000000..7b895c0
--- /dev/null
+++ b/test/george-transport.test.js
@@ -0,0 +1,42 @@
+'use strict';
+
+const assert = require('node:assert/strict');
+const { EventEmitter } = require('node:events');
+const { compose } = require('../lib/compose');
+const { georgeRequest, createGmailDraftArtifact } = require('../lib/george-transport');
+
+const composed = compose({
+ name: 'Test Vendor', account_number: '123', sample_email: 'samples@vendor.com',
+ ship_to: { name: 'Designer Wallcoverings', line1: '15442 Ventura Blvd. #102', city_state_zip: 'Sherman Oaks, CA 91403', phone: '1-888-373-4564' },
+}, [{ sku: 'DW-1', requested: '08/01/2026', mfr: 'ABC' }]);
+const valid = { account: 'info', to: composed.to, subject: composed.subject, body: composed.html };
+const invalid = { ...valid, body: valid.body.replace(/<a href="mailto:info@designerwallcoverings\.com\?subject=Unsubscribe[\s\S]*?<\/a>/, '<a href="https://invalid.example/unsubscribe">unsubscribe</a>') };
+
+let requests = 0;
+const requestImpl = (_options, callback) => {
+ requests += 1;
+ const req = new EventEmitter();
+ req.write = () => {};
+ req.end = () => {
+ const response = new EventEmitter();
+ response.statusCode = 200;
+ callback(response);
+ response.emit('data', Buffer.from('{"success":true}'));
+ response.emit('end');
+ };
+ return req;
+};
+
+(async () => {
+ for (const path of ['/api/send', '/api/drafts']) {
+ await assert.rejects(Promise.resolve().then(() => georgeRequest({ path, payload: invalid, requestImpl })), { code: 'PRE_SEND_COMPLIANCE_BLOCKED' });
+ }
+ assert.equal(requests, 0, 'blocked payloads must make zero requests');
+ assert.throws(() => createGmailDraftArtifact(invalid), { code: 'PRE_SEND_COMPLIANCE_BLOCKED' });
+ assert.equal(createGmailDraftArtifact(valid), valid);
+
+ const result = await georgeRequest({ path: '/api/drafts', payload: valid, requestImpl });
+ assert.equal(result.statusCode, 200);
+ assert.equal(requests, 1, 'compliant payload should reach the injected transport once');
+ console.log('george transport fail-closed boundary: PASS');
+})().catch((error) => { console.error(error); process.exitCode = 1; });
diff --git a/test/pre-send-gate.test.js b/test/pre-send-gate.test.js
index 423093b..4921f82 100644
--- a/test/pre-send-gate.test.js
+++ b/test/pre-send-gate.test.js
@@ -27,4 +27,13 @@ for (const mutation of [
assert.throws(() => assertPreSendCompliance(mutation), { code: 'PRE_SEND_COMPLIANCE_BLOCKED' });
}
+const bodyWithoutLink = compliant.html.replace(/<a href="mailto:info@designerwallcoverings\.com\?subject=Unsubscribe[\s\S]*?<\/a>/, 'unsubscribe here');
+for (const fakeLink of [
+ '<!-- <a href="mailto:info@designerwallcoverings.com?subject=Unsubscribe">unsubscribe</a> -->',
+ 'mailto:info@designerwallcoverings.com?subject=Unsubscribe',
+ '<a href="https://invalid.example/unsubscribe">unsubscribe</a>',
+]) {
+ assert.throws(() => assertPreSendCompliance({ from: REQUIRED_FROM, subject: compliant.subject, body: bodyWithoutLink + fakeLink }), { code: 'PRE_SEND_COMPLIANCE_BLOCKED' });
+}
+
console.log('pre-send compliance gate: PASS');
diff --git a/test/sendable-callsite-coverage.test.js b/test/sendable-callsite-coverage.test.js
new file mode 100644
index 0000000..8891b2e
--- /dev/null
+++ b/test/sendable-callsite-coverage.test.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const path = require('node:path');
+
+const root = path.join(__dirname, '..');
+const allowedTransport = path.join(root, 'lib', 'george-transport.js');
+const files = [];
+function walk(dir) {
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ if (['.git', 'node_modules', 'out', 'verification'].includes(entry.name)) continue;
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) walk(full);
+ else if (/\.(?:js|mjs|cjs)$/.test(entry.name) && !full.startsWith(path.join(root, 'test'))) files.push(full);
+ }
+}
+walk(root);
+
+const directGeorgeCalls = [];
+for (const file of files) {
+ if (file === allowedTransport) continue;
+ const source = fs.readFileSync(file, 'utf8');
+ if (/http\.request\([\s\S]{0,500}?path\s*:\s*['"]\/api\/(?:send|drafts)['"]/.test(source)) directGeorgeCalls.push(path.relative(root, file));
+}
+assert.deepEqual(directGeorgeCalls, [], `sendable George calls must route through lib/george-transport.js: ${directGeorgeCalls.join(', ')}`);
+
+const artifactWriters = files.filter((file) => {
+ const source = fs.readFileSync(file, 'utf8');
+ const names = ["'all-drafts.json'", '"all-drafts.json"', "'send-queue.json'", '"send-queue.json"', '`${slug}.draft.json`'];
+ return [...source.matchAll(/writeFileSync\(([\s\S]{0,300})/g)].some((match) => names.some((name) => match[1].includes(name)));
+});
+for (const file of artifactWriters) {
+ assert.match(fs.readFileSync(file, 'utf8'), /createGmailDraftArtifact\s*\(/, `${path.relative(root, file)} must gate sendable draft artifacts`);
+}
+assert.ok(artifactWriters.length > 0, 'coverage test must find at least one sendable artifact writer');
+console.log('repository sendable call-site coverage: PASS');
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
new file mode 100644
index 0000000..b01e050
--- /dev/null
+++ b/verification/e2e-proof.json
@@ -0,0 +1,51 @@
+{
+ "intent": "Centralize fail-closed compliance enforcement for all sendable George transports and Gmail draft artifacts",
+ "risk_tier": "R3",
+ "environment": "local repository only; injected HTTP spy; no George/Gmail/FileMaker/production network calls",
+ "ticket": "TK-10940-centralize-sample-follow-up-pre-send-com",
+ "baseline": "Cycle 1 commit c74ebf7 had an ungated /api/drafts call, an ungated gmail_create_draft artifact, and accepted hidden/comment/plain-text/reserved-domain opt-out tokens",
+ "commands": [
+ "for f in test/*.test.js; do node \"$f\"; done",
+ "node --check on every changed JavaScript and MJS entry point",
+ "git diff --check"
+ ],
+ "assertions": [
+ {
+ "boundary": "compliance parser",
+ "verdict": "PASS",
+ "evidence": "test/pre-send-gate.test.js rejects HTML comments, plain-text mailto tokens, and https://invalid.example/unsubscribe while accepting lib/compose.js output"
+ },
+ {
+ "boundary": "George /api/send and /api/drafts",
+ "verdict": "PASS",
+ "evidence": "test/george-transport.test.js invokes both endpoints with a blocked payload and asserts the injected request function remains at zero calls"
+ },
+ {
+ "boundary": "gmail_create_draft artifacts",
+ "verdict": "PASS",
+ "evidence": "bin/run.js, scripts/make-drafts.js, and server.js queue creation call createGmailDraftArtifact before writing sendable payloads"
+ },
+ {
+ "boundary": "repository call-site coverage",
+ "verdict": "PASS",
+ "evidence": "test/sendable-callsite-coverage.test.js rejects direct /api/send or /api/drafts HTTP calls outside lib/george-transport.js and ungated artifact writers"
+ },
+ {
+ "boundary": "external side effects",
+ "verdict": "PASS",
+ "evidence": "No real transport, credential mutation, email/draft/send, FileMaker write, deployment, schedule change, or remote push was executed"
+ }
+ ],
+ "negative_checks": [
+ "wrong From",
+ "misleading reply subject",
+ "missing postal address",
+ "missing opt-out anchor",
+ "opt-out anchor hidden in HTML comment",
+ "plain-text mailto token",
+ "reserved invalid.example unsubscribe URL",
+ "blocked transport makes zero requests"
+ ],
+ "cleanup": "No external or persistent test state created; repository verification artifact intentionally retained",
+ "verdict": "PASS"
+}
← c70d49a auto-data-snapshot: 2026-08-28T14:34:59 (1 data files) — REA
·
back to Sample Followup Sweep
·
Require visible owned compliance content 297d26a →