Auto-apply high-confidence frontend-triage results and report them to Slack - #6439
Open
msujaws wants to merge 3 commits into
Open
Auto-apply high-confidence frontend-triage results and report them to Slack#6439msujaws wants to merge 3 commits into
msujaws wants to merge 3 commits into
Conversation
frontend-triage produces a root-cause analysis and fix plan and records it as
Bugzilla actions, but nothing applied them: a human had to click Apply in the
hackbot UI. Close that loop for the results the agent is confident about.
`AgentSpec` gains `auto_apply_confidence`, the set of `findings.confidence`
levels whose actions may be applied unattended, layered on top of the existing
`auto_apply_actions` switch. frontend-triage opts in at `{"high"}` only, so
widening the policy later — to also accept "medium" — is an edit to that set
rather than to the applier.
The decision is a pure predicate over (spec, run) so the policy is testable on
its own, and it fails closed: a run whose findings carry no usable confidence
never qualifies. Because `confidence` is parsed out of the agent's free-form
JSON block, it is compared case- and whitespace-insensitively, so "High" doesn't
silently mean "never apply".
Medium and low results are unchanged from today: still recorded, still visible
in the UI, still appliable by hand. So are all the other agents.
`confidence` used to be advisory metadata a human read off the run. Now that a high-confidence run's actions are applied to Bugzilla unreviewed, it is a control input — and the agent was being asked to self-report it without being told what it does, which invites grading on a curve. Also record why `bugzilla.update_bug` depends on `editbugs` for the apply account, and the coalescing behaviour that makes losing that privilege take the analysis comment down with the field change, so the coupling isn't rediscovered the hard way.
Nothing told the team a triage had happened. A high-confidence result now
reaches a real bug unattended, and a medium/low one sits in the UI waiting for
someone who has no reason to look — so both are worth a line in a channel.
Routed per component, because a channel belongs to the team that owns the
component: `notification_slack_emails` maps `"<Product> :: <Component>"` to an
address, with an optional `default` for components that have no channel of their
own. A component that matches neither sends nothing, since posting one team's
triage into another team's channel is worse than silence.
The component comes from Bugzilla rather than from the run — the agent doesn't
report it, and Bugzilla is authoritative anyway, so a bug moved between
components mid-triage reaches whoever owns it now. That also keeps this
self-contained: no new agent input, no notification concern in the agent's
schema, and it works for runs from the already-deployed agent image.
Delivered as email to a Slack channel address ("Email to channel"), which is why
this is an email module rather than a Slack client: Slack renders an inbound
message's subject as the title, so the one-line summary goes there and the links
go in the body. Sent via SendGrid because hackbot-api runs on Cloud Run, which
has no local MTA and blocks outbound port 25 — and because the project already
holds a `sendgrid-api-key` secret and a verified sender for the pulse listener,
so this adds no new credentials.
It reports what actually happened rather than what was decided: deciding to
apply and Bugzilla accepting it are different things, so a rejected PUT reads as
failed, not posted.
Placed at the end of `on_run_completed` rather than on its own `run.completed`
subscription, because it has to know whether Bugzilla was written and a second
consumer would race the applier. `Run.notified_at` makes it at-most-once, since
Pub/Sub is at-least-once and an email — unlike an action row — is not idempotent.
Both blocking calls (the Bugzilla read and the SendGrid send) go through
`asyncio.to_thread`, as `pubsub.py` does, so neither stalls the event loop.
Channel addresses are deployment config, not committed values: anyone holding one
can post to that channel. With `SENDGRID_API_KEY`, `NOTIFICATION_SENDER` or
`NOTIFICATION_SLACK_EMAILS` unset the whole thing is a logged no-op, so this is
safe to land before the Cloud Run config and `alembic upgrade head`.
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.
frontend-triageproduces a root-cause analysis and fix plan and records it as Bugzilla actions — but nothing applied them, so every triage needed a human to click Apply in the hackbot UI, and nothing told the team a triage had happened at all.This closes that loop for the results the agent is confident about. Three commits, each standing alone.
1. Auto-apply, gated on confidence
AgentSpecgainsauto_apply_confidence: the set offindings.confidencelevels whose actions may be applied unattended, layered on top of the existingauto_apply_actionsswitch.frontend-triageopts in at{"high"}only, so widening the policy later — to also acceptmedium— is an edit to that set rather than to the applier.The decision is a pure predicate over
(spec, run)so the policy is testable on its own, and it fails closed: a run whose findings carry no usable confidence never qualifies. Becauseconfidenceis parsed out of the agent's free-form JSON block, it's compared case- and whitespace-insensitively, so"High"doesn't silently mean "never apply".Medium and low results are unchanged from today: still recorded, still visible in the UI, still appliable by hand. So are all the other agents — there's a test asserting that.
2. Tell the agent what its confidence rating now causes
confidenceused to be advisory metadata a human read off the run. Now it's a control input that decides whether a comment reaches a real bug unreviewed — and the agent was being asked to self-report it without being told what it does, which invites grading on a curve.3. Report each outcome to its team's Slack channel
Routed per component, because a channel belongs to the team that owns the component:
notification_slack_emailsmaps"<Product> :: <Component>"to an address, with an optionaldefaultfor components that have no channel of their own. A component matching neither sends nothing — posting one team's triage into another team's channel is worse than silence.The component comes from Bugzilla rather than from the run: the agent doesn't report it, Bugzilla is authoritative (a bug moved mid-triage reaches whoever owns it now), and it keeps this self-contained — no new agent input, and it works for runs from the already-deployed agent image.
Delivered as email to a Slack channel address ("Email to channel"), which is why this is an email module rather than a Slack client: Slack renders an inbound message's subject as the title, so the one-line summary goes there and the links go in the body. Via SendGrid because Cloud Run has no local MTA and blocks outbound port 25 — and because the project already holds a
sendgrid-api-keysecret and a verified sender for the pulse listener, so this adds no new credentials.It reports what actually happened rather than what was decided: deciding to apply and Bugzilla accepting it are different, so a rejected PUT reads as failed, not posted.
It sits at the end of
on_run_completedrather than on its ownrun.completedsubscription, because it has to know whether Bugzilla was written and a second consumer would race the applier.Run.notified_atmakes it at-most-once, since Pub/Sub is at-least-once and an email — unlike an action row — is not idempotent. Both blocking calls (the Bugzilla read and the SendGrid send) go throughasyncio.to_thread, aspubsub.pydoes.Deploying
Safe to land before any config: with
SENDGRID_API_KEY,NOTIFICATION_SENDERorNOTIFICATION_SLACK_EMAILSunset, the notification is a logged no-op. To turn it on:SENDGRID_API_KEYandNOTIFICATION_SENDER— mount the existingsendgrid-api-keysecret and reuse the pulse listener's verified senderNOTIFICATION_SLACK_EMAILS— JSON, e.g.{"Firefox :: New Tab Page": "…@mozilla.org.slack.com"}alembic upgrade headforruns.notified_atBUGZILLA_API_KEYis already set on the service and working ([email protected]has been applying comments), so nothing new is needed for the apply path.Testing
124 passedinservices/hackbot-api. The 8 errors intest_list_runs_api.py/test_create_run_api.py(fixture 'client' not found) are pre-existing on master — verified by running master's suite unmodified in a clean worktree (80 passed, 8 errors).requestsandsendgridare declared explicitly rather than relied on transitively, since the workspace venv otherwise hides whatuv sync --package hackbot-apiactually installs in the image.Follow-up
mozilla/bugbot will start triggering these runs automatically for newly filed
Firefox :: New Tab Pagebugs from staff — land this first, so a triggered run auto-applies and reports.