Skip to content

TRP-83 feat(economics): embed digest with plain-language copy + spend sparkline - #22

Merged
ripgrim merged 4 commits into
mainfrom
feat/economics-digest-copy
Jul 27, 2026
Merged

TRP-83 feat(economics): embed digest with plain-language copy + spend sparkline#22
ripgrim merged 4 commits into
mainfrom
feat/economics-digest-copy

Conversation

@ripgrim

@ripgrim ripgrim commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Reworks the daily economics digest from a cryptic one-liner into a structured Discord embed with a generated spend-trend thumbnail, and cleans up the monthly report copy. Three commits, each shippable:

1. Plain-language copy

The digest read like a telemetry dump (runs 14 (8 AI) · metered $0.0256 · drift 26.6% OK) and hardcoded "OK" on the drift line even on days the alert fired. Rewrote the formatters into plain sentences (later folded into the embed), honoring the file's copy rules: no em dashes, sentence case.

2. Discord embed

buildDigestEmbed (pure, unit-tested) returns a structured embed: a 2×3 field grid (Runs / Billed / OpenRouter · Credits / Railway / Review model), a green border on a calm day and red with an Alerts field on a breach. Adds the REVIEW_MODEL constant (x-ai/grok-4.5, $2/M in, $6/M out) as a field — cost math never reads it (metered cost is provider-reported per call), so a stale price only misprints a label.

3. Spend sparkline thumbnail

The last 14 days of billed (blurple) vs actual (yellow) as overlaid sparklines, attached to the embed thumbnail. Built with no image dependency and our spend figures never leave our infra:

  • sparkline-png.ts (@tripwire/utils): a dependency-free PNG encoder (hand-assembled chunks + CRC32, IDAT via node:zlib) + a pixel-plotted renderer. Unit-tested by parsing the PNG back.
  • guardedPostMultipart (@tripwire/utils): multipart POST through the same SSRF guard as guardedPost (extracted guardedResolve; the existing 62-test guard suite passes unchanged).
  • getRecentDailyCosts (@tripwire/db): the 14-day range query.
  • A day with no invoice yet falls back to its billed figure so the actual line stays continuous; under two points, no chart and the embed posts as plain JSON.

Verification

Worker + utils + db suites green; all four packages typecheck. Verified end to end against the real prod Jul 24 row via economics-trigger digest --day … [--dry-run]. Cost math, thresholds, and cron schedule untouched; deploys with the next worker release.

🤖 Generated with Claude Code

ripgrim and others added 3 commits July 25, 2026 16:45
…port

The daily digest read like a telemetry dump ("runs 14 (8 AI) · metered
$0.0256 · drift 26.6% OK") and, worse, hardcoded "OK" on the drift line
even on days the drift alert fired. Rewrote all three pure formatters into
plain sentences that say what the numbers mean, honoring the file's copy
rules (no em dashes, short declarative lines, sentence case).

- formatDigest: full sentences; "metered" -> "we billed", "pulled/OR" ->
  "OpenRouter charged", "drift" -> the gap with an under/over direction.
  Drops the hardcoded "OK" — the digest states the gap, [ALERT] lines judge.
- buildAlerts: each alert explains what tripped and what to check, keeping
  the [ALERT] prefix for filtering.
- formatMonthlyReport: middot-separated fragments become sentences; figures
  unchanged.
- tests updated to the new copy; a new case locks the honest over/under
  wording so "OK" can never reappear on a breaching day.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the plain-text daily digest with a structured embed: a 2x3 field
grid (runs, billed, OpenRouter+drift / credits, Railway, review model), a
green border on a calm day and a red border with an Alerts field on any
breach. The fields state the numbers; the color and the Alerts field judge.

- buildDigestEmbed(totals, thresholds): pure, returns the embed object, so
  it stays unit-tested against fixtures like the text renderer it replaces.
- REVIEW_MODEL constant (x-ai/grok-4.5, $2/M in, $6/M out) surfaced as a
  field. Cost math never reads it — metered cost is provider-reported per
  call — so a stale price only misprints a label, never the drift.
- post() now sends a { content?, embeds? } payload; the monthly report
  stays plain markdown content. dry-run prints the full JSON payload.
- embed carries an optional thumbnail slot for a generated chart (follow-up).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…igest

Adds a generated spend-trend chart to the digest embed's thumbnail: the last
14 days of billed (blurple) vs actual (yellow) as overlaid sparklines, so a
reader sees at a glance whether today's drift is a blip or a trend.

Built without any image dependency and without our spend figures leaving our
infra:
- sparkline-png.ts (@tripwire/utils): a dependency-free PNG encoder (hand-
  assembled chunks + CRC32, IDAT via node:zlib) and a pixel-plotted sparkline
  renderer. Pure, unit-tested by parsing the PNG back (signature, IHDR dims,
  chunk order).
- guardedPostMultipart (@tripwire/utils): posts multipart/form-data through
  the SAME SSRF guard as guardedPost. Extracted guardedResolve so the guard is
  single-sourced; the existing guard test suite still passes unchanged.
- getRecentDailyCosts (@tripwire/db): the 14-day billed/actual range query.
- economicsDigest attaches the PNG as files[0] and points the embed thumbnail
  at attachment://econ.png; a day with no invoice yet falls back to its billed
  figure so the actual line stays continuous. Under two points, no chart and
  the embed posts as plain JSON. dry-run prints the payload and the attachment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ripgrim ripgrim changed the title TRP-83 feat(economics): plain-language digest, alerts, and monthly report TRP-83 feat(economics): embed digest with plain-language copy + spend sparkline Jul 26, 2026
Swaps the plain line sparkline for an ordered-dither AREA chart matching the
dither-kit house style, and moves it from the embed thumbnail to a full-width
image below the fields.

- renderDitherChart (@tripwire/utils): ports the dither-kit's exact Bayer 4x4
  ordered-dither into the server PNG renderer. Each column fills from its value
  line to the floor with the Bayer scatter (dense at the floor, dissolving up to
  a soft value-line border), varying only alpha. Painted into a low-res backing
  buffer then nearest-neighbor upscaled, so the cells stay chunky and pixelated —
  same pipeline as the web charts. Actual paints behind, billed rides on top
  thinned so the layer behind reads through.
- embed.image (was embed.thumbnail): the chart renders large under the grid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ripgrim
ripgrim merged commit b6f0da6 into main Jul 27, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant