Skip to content

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).

Work in apps/shank/react-templates/emails/<name>.tsx. Validate visually with pnpm dev (see shank-local-dev.md).

From apps/shank/react-templates/:

Terminal window
pnpm install # only if dependencies changed
pnpm export

This writes apps/sirloin/internal/pkg/emails/templates/<name>.html.

Terminal window
git status apps/sirloin/internal/pkg/emails/templates/
git diff apps/sirloin/internal/pkg/emails/templates/<name>.html

Sanity-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).

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.

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.

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 code path that calls the relevant Send* method. Sirloin’s SMTP client reads SIRLOIN_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(unassigned): 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.ai test account (which bypasses some rate limits per security-model.md) to trigger a real event for that template (for example a manual-review outcome).

Follow the sirloin deploy runbook. Shank itself has nothing to deploy.

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:

  1. Revert the PR (or the relevant templates/<name>.html change) on main.
  2. Re-export from the reverted TSX (pnpm export) only if the revert did not bring back the previous HTML in lockstep.
  3. Build and redeploy sirloin.

There is no runtime switch that stops sends. To stop a template urgently, skip its Send* call (e.g. in striplikenessreviewemail.go), then rebuild and redeploy sirloin.

Shank emits no telemetry. Delivery success/failure is observable through sirloin’s logs (zerolog → OTLP → Axiom). 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 caller (apps/sirloin/internal/app/services/strip/striplikenessreviewemail.go) logs failed to send manual review outcome email on failure, with no metric. TODO(unassigned): decide whether to add a per-template counter in sirloin’s emails package; it would help post-launch verification of new templates.

  • TSX renders cleanly in pnpm dev (no console errors).
  • pnpm export produced exactly the templates you changed.
  • All {{TOKEN}} placeholders match a ReplaceAll in apps/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.