Shank Runbook
Shank does not deploy on its own. Every shank change is shipped as part
of a sirloin deploy because the exported HTML is embedded into the
sirloin binary at compile time (//go:embed in
apps/sirloin/internal/pkg/emails/client.go).
Release a template change
1. Edit the template
Work in apps/shank/react-templates/emails/<name>.tsx. Validate visually
with pnpm dev (see shank-local-dev.md).
2. Export to sirloin
From apps/shank/react-templates/:
pnpm install # only if dependencies changedpnpm exportThis writes apps/sirloin/internal/pkg/emails/templates/<name>.html.
3. Verify the diff
git status apps/sirloin/internal/pkg/emails/templates/git diff apps/sirloin/internal/pkg/emails/templates/<name>.htmlSanity-check:
- Placeholder tokens (
{{CHARACTER_NAME}}, etc.) appear unchanged. - No accidental absolute file paths in the HTML.
- No new
<script>tags (gmail strips, others may flag).
4. Update sirloin substitution if needed
If you added or renamed a placeholder, update the matching
strings.ReplaceAll call in apps/sirloin/internal/pkg/emails/client.go
and add or adjust the Send* method signature so callers pass the
new field. Rebuild sirloin (go build ./... from apps/sirloin/) to
catch compile errors.
5. Commit both apps together
A single PR should touch both apps/shank/react-templates/emails/* and
apps/sirloin/internal/pkg/emails/templates/* (and client.go if the
substitution surface changed). This keeps the source TSX and the exported
HTML in lockstep — reviewers can confirm the export is current.
6. Test send (preview-only)
There is no first-class shank “test send” flow. To verify end-to-end:
- Local: run sirloin against a test SMTP (e.g. Mailpit on localhost)
and trigger the worker that calls
SendTrainingDone. Sirloin’s SMTP client readsSIRLOIN_EMAIL_HOST,SIRLOIN_EMAIL_PORT,SIRLOIN_EMAIL_USER,SIRLOIN_EMAIL_PASSWORD,SIRLOIN_EMAIL_SENDER(apps/sirloin/internal/pkg/env/variables.go); point them at the local SMTP. TODO(@zen): pick a canonical local SMTP image (Mailpit / MailHog / etc.) and add it to the sirloin local-dev doc. - Staging: deploy sirloin to staging and use a
@foxy.aitest account (which bypasses some rate limits persecurity-model.md) to trigger a real training-completion event.
7. Deploy sirloin
Follow the sirloin deploy runbook. Shank itself has nothing to deploy.
Rollback
Because shank changes ship inside the sirloin binary, rollback = revert the sirloin commit (or redeploy the previous sirloin image). There is no shank-side rollback knob.
If a bad template is already in production:
- Revert the PR (or the relevant
templates/<name>.htmlchange) onmain. - Re-export from the reverted TSX (
pnpm export) only if the revert did not bring back the previous HTML in lockstep. - Build and redeploy sirloin.
For an urgent stop-gap that does not require a redeploy, sirloin operators
can disable the calling worker path (e.g. skip
SendTrainingDone in checkmediageneration.go) — see sirloin’s runbook.
Observability
Shank emits no telemetry. Delivery success/failure is observable through
sirloin’s logs (log/slog → OTLP) and Sentry (sentry-go). Search for
failed to send email or failed to get user email strings from
apps/sirloin/internal/pkg/emails/client.go. See
docs/src/content/docs/operations/observability.md for the sirloin
logging stack.
There is no per-template delivery counter today —
apps/sirloin/internal/pkg/emails/client.go returns wrapped errors and
the calling worker (apps/sirloin/internal/app/worker/checkmediageneration.go)
emits a failed to send training done email log line on failure, with
no metric. TODO(@zen): decide whether to add a per-template
counter in sirloin’s emails package; it would help post-launch
verification of new templates.
Pre-release checklist
- TSX renders cleanly in
pnpm dev(no console errors). -
pnpm exportproduced exactly the templates you changed. - All
{{TOKEN}}placeholders match aReplaceAllinapps/sirloin/internal/pkg/emails/client.go. - Sirloin compiles (
go build ./...). - PR includes both shank source and sirloin exported HTML.
- Manual preview screenshot attached for design review.