← back to Claude Webdev Accelerator
chore: session-close hardening — atomic writeProcessed, (no timestamp) fallback, oneLine note in list
0f1dbb8002dd59e754ca3ce33da0666455ae1073 · 2026-08-12 06:57:40 -0700 · steve@designerwallcoverings.com
Files touched
M scripts/bounce-intake.js
Diff
commit 0f1dbb8002dd59e754ca3ce33da0666455ae1073
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Wed Aug 12 06:57:40 2026 -0700
chore: session-close hardening — atomic writeProcessed, (no timestamp) fallback, oneLine note in list
---
scripts/bounce-intake.js | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/scripts/bounce-intake.js b/scripts/bounce-intake.js
index bd50434..69fdd3e 100644
--- a/scripts/bounce-intake.js
+++ b/scripts/bounce-intake.js
@@ -63,7 +63,12 @@ function readProcessed() {
}
function writeProcessed(map) {
if (!fs.existsSync(CLIENTS)) fs.mkdirSync(CLIENTS, { recursive: true });
- fs.writeFileSync(PROCESSED, JSON.stringify(map, null, 2) + '\n');
+ // atomic write: a kill mid-write would otherwise corrupt the marker, and the
+ // readProcessed catch would then return {} → every lead re-scaffolds (dup
+ // folders + dup gated replies). tmp + rename is atomic on POSIX.
+ const tmp = PROCESSED + '.tmp';
+ fs.writeFileSync(tmp, JSON.stringify(map, null, 2) + '\n');
+ fs.renameSync(tmp, PROCESSED);
}
// Collapse untrusted free text to a single safe line before embedding it in a
@@ -95,8 +100,10 @@ function slugFor(lead, taken) {
}
function fmtDate(iso) {
- const d = iso ? new Date(iso) : new Date('2026-01-01T00:00:00Z');
- return d.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
+ // explicit placeholder over a fake sentinel date — a missing ts should read as
+ // "(no timestamp)" in the BRIEF, not a misleading "Jan 1, 2026".
+ if (!iso) return '(no timestamp)';
+ return new Date(iso).toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
}
// ---------- brief + reply templates ----------
@@ -209,7 +216,7 @@ function cmdList(showAll) {
const ref = extractRef(lead);
console.log(` ${flag} #${idx} ${lead.email || '(no email)'} [${lead.budget || '—'}] ${fmtDate(lead.ts)}`);
if (ref) console.log(` ref: ${ref}`);
- if (lead.note) console.log(` note: ${String(lead.note).slice(0, 120)}`);
+ if (lead.note) console.log(` note: ${oneLine(lead.note).slice(0, 120)}`);
});
console.log('\nTriage one: node scripts/bounce-intake.js scaffold <n|email>');
}
← e9af9dd bounce: intake engine (waitlist -> engagement + gated reply)
·
back to Claude Webdev Accelerator
·
preview-tunnel: repoint from dead dw-followup (0e1edf5a) to 2476d9a →