Skip to content

Add AI-assisted triage - #3

Merged
TheJJ merged 28 commits into
ubuntu:mainfrom
renanrodrigo:ai-triage
Jul 8, 2026
Merged

Add AI-assisted triage#3
TheJJ merged 28 commits into
ubuntu:mainfrom
renanrodrigo:ai-triage

Conversation

@renanrodrigo

Copy link
Copy Markdown
Contributor

This adds the whole AI-assisted functionality to startriage. It's full of abstractions and details but it works well.

  • Vibe coded
  • --ai for usual triage second opinion
  • ai-triage for single bugs

@grayson-wolf grayson-wolf left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

looks good, to me, just a few suggestions / nit-questions

Comment thread startriage/data/agents_prompt.md Outdated
Comment thread startriage/data/defaults.toml
Comment thread startriage/data/agents_prompt.md Outdated

@TheJJ TheJJ left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

coming together pretty nicely. the overall architecture is good, i'd say.

one thing that worries me is the implicit assumption this is run in a secure container now:
how about a text-only analysis without funny containers as the first agent, and when it decides we wanna run shell stuff, it spins up a subagent in an lxd container (using the same mechanism we use for git ubuntu deltarebase resolving - we can hopefully create a shared ai lxd containering library so we don't have to duplicate/reinvent stuff)

otherwise many nits and naming issues, you'll see. great work, overall :)

Comment thread startriage/cli.py Outdated
Comment thread startriage/cli.py
Comment thread startriage/cli.py Outdated
Comment thread startriage/cli.py Outdated
Comment thread startriage/cli.py Outdated
Comment thread startriage/ai/provider.py
Comment thread startriage/ai/provider.py Outdated
Comment thread startriage/ai/provider.py Outdated
Comment thread startriage/ai/render.py Outdated
Comment thread tests/test_config.py Outdated
Accept only a bare/#-prefixed integer or a genuine Launchpad bug
URL (.../+bug/<n> or /bugs/<n>) and raise ValueError otherwise.
Plain 'analyze <bug>' now prints the bug metadata (status, tags,
affected targets, description, comments) with no provider or
credentials required; 'analyze --ai <bug>' runs the agent as the old
'ai-triage' command did.
triage --ai no longer writes a separate autotriage-YYYY-MM-DD.md file.
Without --markdown the AI section is printed to stdout after the normal
triage output; with --markdown it is folded into that same file behind
the review notice, giving one cohesive report. Drops the now-unused
report_filename/write_report/resolve_report_dir helpers and their
tests, and updates the prompt and README accordingly.
@renanrodrigo

Copy link
Copy Markdown
Contributor Author

@TheJJ I addressed most of your review points, responded to a couple inline.

I see your point with the two-step agent run, but I would ask you if we could not do this in a future iteration. You mentioned there may be some overlap in the "Run this container and do stuff" tasks - let's go for the shared LXD library then. But the size and scope of this PR make me thing later is better.

For now we can tell users that this is potentially unsafe, and ask them to please containerize (or isolate the environment right) before running AI commands.

WDYT?

@TheJJ TheJJ left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just minor things left, i think for a first version we are nearly ready.

this runs the ai analysis within the snap confinement, but as future work we should add properly containerized ai execution - maybe open an issue for that then :)

Comment thread startriage/cli.py Outdated
Comment on lines +423 to +431
def _build_ai_provider(config: StarTriageConfig) -> Provider | None:
"""Build the AI provider, printing a friendly hint and returning None on misconfig."""
from .ai import build_provider

try:
return build_provider(config.ai)
except AIConfigError as exc:
print(f"error: {exc}", file=sys.stderr)
return None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

shouldn't be part of cli.py, rather ai/provider.py. return value should always be Provider, and if that fails, let the AIConfiError be raised to top for crash.

Comment thread startriage/cli.py Outdated
Comment on lines +434 to +449
def _emit_ai_report(report: str, markdown_path: Path | None) -> None:
"""Emit an AI ``report`` for a ``triage --ai`` run.

With ``--markdown`` the AI section is appended (behind a review notice) to
that same file, so the human triage report and the AI aid live in one
cohesive document. Without ``--markdown`` the report is printed to stdout,
together with the normal triage output that already went there.
"""
from .ai import append_report
from .ai.render import AI_APPEND_NOTICE

if markdown_path is not None:
append_report(markdown_path, report)
print(f"AI triage appended to {markdown_path}")
else:
print("\n" + AI_APPEND_NOTICE + report)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

that should be in ai/render.py

Comment thread startriage/config.py Outdated
return self.github_token or _first_env(COPILOT_TOKEN_ENV_VARS)
return self.openrouter_api_key or _first_env(OPENROUTER_KEY_ENV_VARS)

def require_configured(self) -> None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

    @model_validator(mode='after')
    def check_token(self) -> Self:
        if self.resolve_token() is not None:
            return self
        match self.provider:
            case AIProvider.copilot:
                ...
                raise ValueError('bla')
            case _:
                raise RuntimeError("unhandled provider")

Move the missing-credential check from AIConfig.require_configured() onto a
model validator that only enforces when validated with a require_ai context.
This keeps the [ai] section optional for non-AI commands while build_provider
re-validates with that context so a misconfigured provider fails before a
session starts. AIConfigError is not a ValueError, so pydantic propagates it
unwrapped to the top.
@renanrodrigo

Copy link
Copy Markdown
Contributor Author

@TheJJ issue open

Claude is worried that the model validator will break users who have no [ai] configured when trying to run non-ai commands, so it proposed something in-the-middle.

@TheJJ TheJJ left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

great, now the only missing piece is the discussed --yolo flag to grant/deny execution rights to the agent.

to implement it, we could turn --ai into having a parameter.

--ai=deny (Default)
--ai=always
--ai=ask

and this flag could then be in a parent parser, as mentioned below.

Comment thread startriage/cli.py Outdated
Comment thread startriage/ai/provider.py Outdated
--ai now takes a required level (restricted/full/ask) instead of being a plain
on/off switch, and lives on a parent parser shared by triage and analyze. The
level is mandatory so --ai never swallows a following bug number as its value,
keeping 'analyze --ai 123' unambiguous (it rejects 123 as an invalid level). A
new AIPermission enum carries the level through to build_provider.
build_provider takes the permission level and threads it to CopilotProvider,
which selects the session's on_permission_request handler: full auto-approves
every tool, restricted rejects them all (and appends a text-only note to the
prompt), ask prompts on the terminal per call.
@renanrodrigo

Copy link
Copy Markdown
Contributor Author

Claude iteration done
I just changed the plan to:
a) for clarity, make the permission level mandatory on the CLI
b) changed the level to restricted, full, ask

Then fire the cannons and here we are

@TheJJ TheJJ left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

great, for a first version we can merge and test this for further improvements.

@TheJJ
TheJJ merged commit 0796cf8 into ubuntu:main Jul 8, 2026
@renanrodrigo
renanrodrigo deleted the ai-triage branch July 8, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants