fix(T-31): stop dropping failure alerts, and stop saving blank KPI summaries - #24
Merged
Merged
Conversation
…mmaries
Two bugs found by a full-codebase scan, both confirmed against the live APIs.
notifyError built an HTML message but never escaped the error text, and never
checked whether Telegram accepted it. Telegram's HTML mode rejects any tag
outside its whitelist with a 400, so an error carrying markup lost the alert
entirely and left no trace — verified live: a Tavily gateway 502 (HTML body)
and a <think> tag from the curator retry path both returned
400 Bad Request: can't parse entities: Unsupported start tag "html"
The error is now escaped, and a rejected alert is logged rather than
discarded. The raw text is cut before escaping, never after: escaping a cut
string is safe, but cutting an escaped one can split an entity ("&qu") and
Telegram rejects that too. 600 raw chars stay inside the 4096 limit even at
the sixfold worst case. The alert links to LangSmith for the full trace.
github_summary was empty on every GitHub-only KPI day. The tool hardcodes
summary: '' and GITHUB_PROMPT tells the agent to relay the output verbatim,
so nothing ever filled it — confirmed end-to-end against the live GitHub API
and LLM: 4 commits and 6 PRs stored with "". The summary is now built from
the counts. Payloads that never carried the arrays stay blank, which keeps a
broken payload distinguishable from a genuine zero-activity day.
Closes #21
Closes #23
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F5CmYPdQcMeLzzJc3iDQ2u
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.
Closes #21
Closes #23
Two bugs from a full-codebase scan. Both were confirmed against the live APIs, not just reasoned about.
1. Failure alerts were silently dropped (#21)
notifyErrorbuilt an HTML message but never escaped the error text, and never checked whether Telegram accepted it. Telegram's HTML mode rejects any tag outside its whitelist with a 400, so an error carrying markup lost the alert entirely — and becauseresponse.okwas never read, nothing was logged either. Every other Telegram builder in the repo already escapes; this one, the one handling arbitrary third-party text, did not.Confirmed against the real bot. Both of these reach
notifyErrorfrom live code paths — a Tavily gateway 502 returns an HTML body (ai-news-search.tool.ts:53), and the default reasoning model emits<think>into the curator retry error (curator.graph.ts:40):The same errors after this change, plus the worst cases:
200 ok=true<think>from curator retry200 ok=true200 ok=true&and<>in a DB error200 ok=trueOrdering matters here. The raw text is cut before escaping, never after — escaping a cut string is always safe, but cutting an escaped one can split an entity into
&qu, which Telegram also rejects. 600 raw chars stay inside the 4096 limit even if every character expands sixfold ("→& quot;). The alert carries a LangSmith link for the full trace.A rejected alert is now logged instead of discarded. It is the last line of defence, so there is nowhere left to escalate to.
2.
github_summarywas always empty (#23)Every GitHub-only KPI day wrote a blank summary.
github.tool.ts:56hardcodessummary: '', andGITHUB_PROMPTtells the agent to "return the full result as-is. Do not summarize or modify the data." — so nothing ever filled it.Confirmed end-to-end against the live GitHub API and LLM, on a real day:
The summary is now built from the counts:
"4 commits, 6 PRs on GitHub".One judgement call worth reviewing. Payloads that never carried the arrays (unparseable, or
{}) stay blank rather than getting synthesized text.kpi-record.test.ts:58argues the summary is the only thing separating a real quiet day from a broken payload, and describing both would destroy that. So a genuine zero day reads"0 commits, 0 PRs on GitHub", a broken one stays"".Also worth knowing
The LangSmith link appears on every alert, but only 3 of the 14
notifyErrorcall sites are LangChain runs it can trace — the other 11 arefetchandpgerrors with no trace to point at. The full error is in the job logs either way. Showing the line only for agent contexts is a reasonable follow-up if it proves annoying.Verification
pnpm tsc,pnpm format:check,pnpm test,pnpm test:coverage— allexit=0kpi-record.tsat 100% coverage🤖 Generated with Claude Code