fix(ai-sre-relay): dedup alert tickets and sanitize llm analysis before filing#65
Merged
Merged
Conversation
…re filing
relates to the AI-SRE relay dedup/formatting bugs.
Three production defects observed in filed tickets:
1. Duplicate tickets: Alertmanager fingerprints hash the full labelset, so
one incident re-firing under a different labelset (e.g. TargetDown per
job) got a fresh fingerprint and sailed past the fingerprint-label dedup
search, filing sibling duplicates. Jira's JQL index is also eventually
consistent, so a search right after a create could miss the new issue.
Fix: layered dedup — serialized upserts with an in-process label->key
registry, fingerprint-label search (reopens Done matches), then an
open-ticket search on a new alertname label that groups refires of the
same alert into one ticket as comments. The dispatcher additionally
coalesces repeat notifications for a fingerprint still under
investigation.
2. Conversational filler in ticket bodies: Holmes returns the backing LLM's
final message verbatim, and chat narration ("Now I have a complete
picture. Let me summarize...") reached ticket descriptions. Fix: a
sanitizer at the Holmes boundary strips leading/trailing conversational
paragraphs while preserving substantive analysis.
3. Raw tool-call dumps as ticket bodies: when the model emitted un-executed
tool-call markup (<tool_call>/<function=...> or JSON tool calls) as its
final message, the relay filed it as the entire ticket description with
zero analysis. Fix: the sanitizer strips tool-call markup and rejects
responses with no prose left; Investigate re-asks once and then fails the
investigation instead of publishing garbage.
Co-Authored-By: Claude Fable 5 <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.
Fixes the three AI-SRE relay bugs observed in tickets it filed (duplicate pairs JDWLABS-151/153 vs 150/152; conversational filler in 151; raw tool-call dump in 153).
Root causes
1. Duplicate tickets (JDWLABS-151 dup of 150, 153 dup of 152)
Dedup was keyed only on the Alertmanager fingerprint label (
amfp-<fp>). Fingerprints hash the full labelset, so the same alert re-firing under a different labelset (e.g.TargetDownfor a different job,KubeAggregatedAPIDownfor a different apiservice set during the same node incident) gets a brand-new fingerprint — the label search legitimately finds nothing and a sibling duplicate is filed. The observed pairs confirm this: each pair shares an alertname but has differentamfp-*labels. A second latent hazard: Jira's JQL index is eventually consistent, so a search issued shortly after a create can miss the fresh issue and duplicate it too.2. Conversational prose in ticket body (JDWLABS-151)
Holmes
/api/chatreturns the backing LLM's final message verbatim inanalysis, and the relay pasted it straight into the Jira description. Weaker models prepend chat narration — 151 opened with "Now I have a complete picture. Let me summarize the investigation findings." — and nothing stripped it.3. Raw tool-call dump as ticket body (JDWLABS-153)
When the model emits an un-executed tool call as its final text (typical when the agent loop hits its iteration cap), Holmes still returns it in
analysis. 153's entire description was a hermes-style<tool_call><function=execute_prometheus_instant_query>...block with zero analysis. The relay had no validation between the LLM response and the ticket create.Fix
jira.go— layered dedup. Upserts are serialized and remembered in an in-processlabel -> issue keyregistry (survives JQL index lag; verified against live status via a strongly-consistent issue GET, not search). Search order: (1) fingerprint label — comments, reopening Done matches as before; (2) newamalert-<alertname>label restricted to open issues — a re-fire of the same alert under a new labelset is grouped into the open ticket as a comment instead of a sibling duplicate (Done issues excluded: a refire after close under a new labelset is new work). New issues carry both labels.dispatcher.go— in-flight coalescing. A repeat notification for a fingerprint still queued/under investigation is acknowledged but not re-investigated (investigations run for minutes; repeats doubled LLM spend and raced the upsert).sanitize.go(new) +holmes.go. All Holmes output passessanitizeAnalysisbefore any output path (Jira, Discord, patch gen) sees it: strips<tool_call>/<function=...>markup (terminated or not), rejects JSON-shaped tool calls and empty/all-filler responses, and trims leading/trailing conversational paragraphs (conservative: only short plain prose with first-person markers; markdown structure always survives).Investigatere-asks once on unusable output, then fails the investigation — the pipeline then sends the existing failure notice instead of filing a garbage ticket.Test evidence
New tests (all following existing
httptestfake-server patterns):TestJiraUpsertGroupsSameAlertnameOpenTicket— replays the 152/153 pair: new fingerprint, open same-alertname ticket → comment, no create.TestJiraUpsertDedupSurvivesSearchIndexLag— create then two refires (same fingerprint, then new fingerprint of same alertname) while search returns empty: exactly 1 create, 2 comments.TestDispatcherCoalescesInFlightFingerprint— repeat while in flight is coalesced; same fingerprint investigable again after completion.sanitize_test.go(9 tests) — preamble strip (151's exact opening line), trailing chat-offer strip, clean-analysis passthrough, tool-call rejection (153's exact markup, unterminated, and JSON forms), empty/all-filler rejection, embedded-tool-call strip keeping prose.TestHolmesInvestigateRetriesOnToolCallResponse,...ErrorsWhenAnalysisStaysUnusable,...StripsConversationalFiller— retry-then-fail behavior at the client boundary.Validation (worktree, Windows):
One existing test updated:
TestDispatcherShutdownDrainsused one fingerprint for 4 alerts, which the new coalescing intentionally collapses — it now uses distinct fingerprints.Notes / limitations
jdwlabs/ai-sre-relay:0.2.0withLITELLM_MODEL=sre-investigator; no cluster changes made (read-only inspection only).🤖 Generated with Claude Code