TRP-83 feat(economics): embed digest with plain-language copy + spend sparkline - #22
Merged
Conversation
…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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 theREVIEW_MODELconstant (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 vianode:zlib) + a pixel-plotted renderer. Unit-tested by parsing the PNG back.guardedPostMultipart(@tripwire/utils): multipart POST through the same SSRF guard asguardedPost(extractedguardedResolve; the existing 62-test guard suite passes unchanged).getRecentDailyCosts(@tripwire/db): the 14-day range query.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