feat(intake): add the submission notification template (#224)

Editable from the settings screen like every other template. reviewUrl is the only required placeholder: a notification with no link in it still sends, still looks fine in the log, and is useless to whoever receives it, which is what the required-placeholder validation exists to catch.

The two signed links are deliberately optional. They are absent whenever INTAKE_ACTION_SECRET is unset, and a template demanding them would leave an unconfigured environment unable to send this at all.

A test asserts the template offers no way to publish. That the email cannot publish is what bounds the risk taken by pricing items on arrival, and it is a property of the copy as much as of the routes — a publish link in the body would be one nobody reviewed.

Where the notification goes is an admin setting rather than an environment variable, for the same reason drafting_model is one: it is changed by whoever runs the shop, not by whoever deploys it. Empty is the default and means do not notify, which is a working configuration.

INTAKE_ACTION_SECRET warns rather than fails at boot, like the drafting key. Being told an item arrived matters far more than being able to discard it in one click.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 15:23:32 -05:00
co-authored by Claude Opus 5
parent acc1b406d8
commit 0517faca60
5 changed files with 110 additions and 3 deletions
+15
View File
@@ -236,4 +236,19 @@ describe('UPLOADS_BASE_URL', () => {
expect(warnings.join(' ')).not.toMatch(/ANTHROPIC_API_KEY/);
});
});
// #224. MINIMAL has no INTAKE_ACTION_SECRET, so it is already the absent case.
describe('the intake action secret', () => {
it('is not required', () => {
expect(validateEnv(MINIMAL).errors).toEqual([]);
});
it('warns when it is absent', () => {
expect(validateEnv(MINIMAL).warnings.join(' ')).toMatch(/INTAKE_ACTION_SECRET/);
});
it('says nothing when it is set', () => {
const { warnings } = validateEnv(withEnv({ INTAKE_ACTION_SECRET: 'a-secret' }));
expect(warnings.join(' ')).not.toMatch(/INTAKE_ACTION_SECRET/);
});
});
});