Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions agents/frontend-triage/hackbot_agents/frontend_triage/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from hackbot_runtime.actions import ACTIONS_SERVER_NAME
from hackbot_runtime.actions.claude_sdk import actions_server_for, actions_to_tool_names
from hackbot_runtime.claude import Reporter
from pydantic import BaseModel, ValidationError
from searchfox import AsyncSearchfoxClient

from .config import (
Expand All @@ -49,6 +50,25 @@
_JSON_BLOCK = re.compile(r"```json\s*(\{.*?\})\s*```", re.DOTALL)


class ComponentAssessment(BaseModel):
"""Product/component verification result (see component-verification rules)."""

current: str | None = None # the bug's current "Product :: Component"
correct: bool | None = None # whether the current component is right
suggested_product: str | None = None # correction, if correct is false
suggested_component: str | None = None
confidence: str | None = None # high | medium | low
rationale: str | None = None


class SeverityAssessment(BaseModel):
"""Severity judgment (see severity-assessment rules)."""

suggested: str | None = None # S1 | S2 | S3 | S4
confidence: str | None = None # high | medium | low
rationale: str | None = None


class FrontendTriageResult(HackbotAgentResult):
bug_id: int
# Structured plan (best-effort, parsed from the agent's final message).
Expand All @@ -63,6 +83,9 @@ class FrontendTriageResult(HackbotAgentResult):
relevant_tests: list[str] | None = (
None # existing tests covering the area (verify anchor)
)
# Triage judgments (best-effort, parsed from the agent's final message).
component_assessment: ComponentAssessment | None = None
severity_assessment: SeverityAssessment | None = None
# The agent's full final message, always present as a fallback.
result: str | None = None

Expand Down Expand Up @@ -128,6 +151,14 @@ def _as_list(value):
return [value]
return value if isinstance(value, list) else None

def _as_model(model, value):
if not isinstance(value, dict):
return None
try:
return model.model_validate(value)
except ValidationError:
return None

actionable = data.get("actionable")
if not isinstance(actionable, bool):
actionable = None
Expand All @@ -140,6 +171,12 @@ def _as_list(value):
"actionable": actionable,
"regressor_node": data.get("regressor_node"),
"relevant_tests": _as_list(data.get("relevant_tests")),
"component_assessment": _as_model(
ComponentAssessment, data.get("component_assessment")
),
"severity_assessment": _as_model(
SeverityAssessment, data.get("severity_assessment")
),
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ You are given a bug ID. Your job is to triage it and produce a **proposed fix pl
2. **Read the relevant triage rules** from `{rules_dir}` — Glob the directory and Read only the rulesets that apply to this bug. Do not assume all rules apply to all bugs.
3. **Assess** what the rules say should happen, and whether the bug has open questions in its comments.
4. **Investigate** the source tree (read-only) to localize the cause — delegate deep searches to the `investigator` subagent (see below).
5. **Produce a fix plan**: the likely root cause, the specific files to change, and the approach. Record it as a brief Bugzilla comment.
5. **Verify the product/component** — using the localized file paths and `mots.yaml`, confirm the bug is filed against the right `Product :: Component` and propose a correction if not (see the `component-verification` rules).
6. **Assess severity** — determine an appropriate Mozilla severity (S1–S4) from the user impact (see the `severity-assessment` rules).
7. **Produce a fix plan**: the likely root cause, the specific files to change, and the approach. Record it as a brief Bugzilla comment.

# This agent is READ-ONLY

Expand All @@ -34,6 +36,8 @@ Your working directory is the Firefox source repository. You have Read, Grep, Gl

When you reference a cause or a fix target, cite concrete paths (and ideally functions/selectors), e.g. `browser/components/tabbrowser/content/tabgroup.js`.

The tree also ships **`mots.yaml`** (module-ownership metadata; Glob for `**/mots.yaml`). It maps file-path globs to the owning module and that module's Bugzilla `Product :: Component`. It is your reference for verifying the bug's component — match the paths you localize to a module to find where the bug belongs.

# Code-search & history tools

Your local checkout is **shallow** (no git history), so for anything beyond the current file contents use these network-backed tools. They query Mozilla's live infrastructure and reflect mozilla-central tip (which may differ slightly from the checkout — prefer them for symbol search and history, and local Read/Grep for the exact checked-out bytes).
Expand Down Expand Up @@ -78,7 +82,7 @@ Before calling any action tool, state in your response:
- **What** action you are recording and **why** (cite the specific rule)
- **Your confidence**: high / medium / low

Record exactly one `bugzilla_add_comment` with your fix plan. Only record a `bugzilla_update_bug` (e.g. keyword/severity) when confidence is **high** and a specific triage rule directs it. Never record `status: RESOLVED`.
Record exactly one `bugzilla_add_comment` with your fix plan (which should also state the component-verification and severity conclusions). Only record a `bugzilla_update_bug` when confidence is **high** and a specific triage rule directs it — e.g. a corrected `component`/`product` (per the `component-verification` rules), a `severity` (per the `severity-assessment` rules), or an obvious keyword. You may combine several such fields into one `bugzilla_update_bug`, each justified in the `reasoning`. At medium/low confidence, state the assessment in the comment and structured output but do **not** record a field change. Never record `status: RESOLVED`.

The `reasoning` parameter on every action tool is required and stored alongside the recorded action. Fill it properly.

Expand All @@ -97,7 +101,20 @@ After recording your comment, end your final message with a fenced ```json block
"confidence": "high | medium | low",
"actionable": true,
"regressor_node": "hg node of the introducing changeset, or null",
"relevant_tests": ["browser/.../tests/browser/browser_foo.js"]
"relevant_tests": ["browser/.../tests/browser/browser_foo.js"],
"component_assessment": {{
"current": "Firefox :: New Tab Page",
"correct": true,
"suggested_product": null,
"suggested_component": null,
"confidence": "high | medium | low",
"rationale": "why, citing the mots.yaml module and path evidence"
}},
"severity_assessment": {{
"suggested": "S1 | S2 | S3 | S4",
"confidence": "high | medium | low",
"rationale": "user-impact reasoning"
}}
}}
```

Expand All @@ -106,6 +123,8 @@ Field guidance for the handoff:
- **`actionable`** — `false` when the bug is out of scope or skipped per the scoping rules (meta/tracking, intermittent/test-infra, enhancement/task), or when there is simply nothing to fix-plan; `true` when you produced a real fix plan. The executor uses this to decide whether to act.
- **`regressor_node`** — when the bug is a regression and you identified/confirmed the introducing changeset (via the `mozilla_vcs` tools or `get_blame`), put its hg node here so the executor has a direct pointer; otherwise `null`.
- **`relevant_tests`** — existing tests that cover the affected area (typically browser-chrome mochitests under a component's `tests/browser/` dir, or xpcshell tests). These are the executor's **verification anchor** — it can run them. Use `[]` if you searched and found none (a signal that the executor should add a test).
- **`component_assessment`** — your product/component verification (per the `component-verification` rules). Set `correct: true` and leave the suggestions null when the current component is right; otherwise set `correct: false` and fill `suggested_product` / `suggested_component`. Always give a `rationale`. Set to null only if you could not verify at all.
- **`severity_assessment`** — the severity you judged appropriate (per the `severity-assessment` rules), with `confidence` and a `rationale`. Set to null only if you could not assess it.

If you could not localize a root cause, set `root_cause` to null, keep `confidence` low, set `actionable` accordingly, and have your comment ask the specific open questions that block triage.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Product / component verification

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could do this in bugbot instead, without an LLM, so it'll be cheaper and more deterministic.


Bugs are frequently filed against the wrong `Product :: Component`. Part of triage is
confirming the bug is filed where it belongs and, when it isn't, proposing the correct
`Product :: Component` so it reaches the right team.

Do this **after** you have localized the affected code (you need concrete file paths to
verify ownership), and record the result in the `component_assessment` structured-output
object.

## How to verify

1. **Find the ownership map.** The Firefox source tree ships a module-ownership file,
`mots.yaml` (the maintainer/module metadata; commonly at the repo root or under
`source/`). Glob for it (`**/mots.yaml`) and Read it. Each module lists path globs it
owns (e.g. an `includes:` list) and its Bugzilla product/component (e.g. under a
`meta`/`components` mapping — field names may vary slightly, read the file to see the
shape).
2. **Match your localized paths to a module.** Take the file path(s) you identified as
the root cause and find the module whose path globs own them. That module's Bugzilla
product/component is the _expected_ component for a bug in that code. Use Searchfox to
confirm ownership when a path spans directories or the mapping is ambiguous.
3. **Compare with the bug.** Compare the expected `Product :: Component` against the
bug's current values.

## What to record

- If the current component matches, set `component_assessment.correct` to `true`, leave
the suggestions null, and give a one-line rationale.
- If it clearly belongs elsewhere, set `correct` to `false`, put the corrected values in
`suggested_product` / `suggested_component`, and in the rationale cite **both** the
`mots.yaml` module that owns the path **and** the path evidence.

## Confidence and field changes

- **High** — the localized path is unambiguously owned by one module and the mismatch is
clear. Only then may you record a `bugzilla_update_bug` proposing the corrected
`product` / `component` (see the system prompt's recording rules). Cite the mots.yaml
module and path in the `reasoning`.
- **Medium / low** — the code area is uncertain or the path is owned by multiple modules.
Report the assessment (and your best suggestion) in the comment and structured output,
but do **not** record a field change.

## Interaction with scoping

A wrong-component finding can mean the bug is out of scope for a _frontend fix_ — e.g.
the code you localized is Core (layout, DOM, graphics), not a Firefox frontend
component. In that case, still record the component assessment and the proposed
correction, but do not invent a frontend fix plan: set `actionable` per the scoping
rules and let the comment focus on the re-triage.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ ruleset does not apply — note that and stop.
3. **Write a fix plan**: root cause, the specific files/functions/selectors to
change, and the approach. Prefer a comprehensive fix at the right level over a
spot fix.
4. **Verify the product/component and assess severity.** Using the file paths you
localized, apply the `component-verification` and `severity-assessment` rules to
confirm the bug is filed correctly and to judge its severity. Both belong in your
comment and structured output.

## Comment

Expand All @@ -36,8 +40,9 @@ restate the whole bug. Do not claim the fix is verified — you did not run it.

- **High** (you found the specific code and the cause is clear): record the
plan comment. If a rule or convention clearly applies, you may also record a
`bugzilla_update_bug` for an obviously-correct field (e.g. adding a relevant
keyword). Do not change `status`/`resolution`.
`bugzilla_update_bug` for an obviously-correct field — e.g. adding a relevant
keyword, a corrected `component`/`product` (per `component-verification`), or a
`severity` (per `severity-assessment`). Do not change `status`/`resolution`.
- **Medium** (plausible area, cause not pinned down): record the comment with
your best hypothesis and the open questions that would confirm it.
- **Low** (could not localize): record a comment stating what you checked and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Severity assessment

Assess an appropriate Mozilla severity for the bug and record it in the
`severity_assessment` structured-output object. Base the judgment on **user impact and
reach** as evidenced by the bug report and the code you investigated — how badly the
user is affected, how many users hit it, and whether a workaround exists.

## Severity definitions

- **S1 — catastrophic.** Crash, hang, data loss, security issue, or a bug that blocks
major functionality with **no workaround**. Affects a large number of users.
- **S2 — serious.** Major functionality is broken or a severe UX problem, and the
workaround (if any) is painful or non-obvious. Affects many users.
- **S3 — normal.** Blocks non-critical functionality, or a reasonable workaround exists.
**This is the default for most frontend papercuts.**
- **S4 — minor / trivial.** Cosmetic issues, small polish, or edge cases with negligible
impact.

## Guidance

- Frontend UI/UX papercuts are usually **S3** (or **S4** when purely cosmetic). Reserve
**S1 / S2** for genuine breakage: crashes, data/state loss, or a broken core workflow
with no easy workaround.
- Weigh: is it functional vs cosmetic? Is there a workaround? How frequently and how
broadly is it hit (mainline path vs rare configuration)?
- Do **not downgrade** an existing higher severity unless you have strong evidence the
impact is lower than currently recorded.

## Confidence and field changes

- **High** — impact is clear-cut (clearly cosmetic, or clearly a crash/data-loss). Only
then may you record a `bugzilla_update_bug` proposing the `severity` (see the system
prompt's recording rules), with a `reasoning` citing the impact evidence. Prefer not to
propose a change when the bug already carries a reasonable severity.
- **Medium / low** — suggest a severity in the comment and structured output, but do
**not** record a field change.