Skip to content

ci(triage): issue-triage automation (stale + on-open label/dedup + backfill) - #2224

Open
BenTaylorDev wants to merge 5 commits into
mainfrom
oss-triage-automation
Open

ci(triage): issue-triage automation (stale + on-open label/dedup + backfill)#2224
BenTaylorDev wants to merge 5 commits into
mainfrom
oss-triage-automation

Conversation

@BenTaylorDev

@BenTaylorDev BenTaylorDev commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

What

Port of the CopilotKit issue-triage automation (CopilotKit/CopilotKit#6094), adapted to ag-ui's label taxonomy. Three GitHub Actions workflows sharing one analysis module. This is the issues-first slice of the OSS backlog maintenance plan; PR automation is a later, deferred arc.

Everything is advisory, high-confidence, and reopen-friendly — nothing closes an issue on an LLM's say-so.

Workflow Trigger LLM? Does
triage-stale.yml daily cron no Marks needs-info issues stale after 14d of silence, closes at +7d. Scoped to needs-info only so it never touches active issues. Exempts Roadmap,proposal. PRs untouched.
triage-on-open.yml issues: opened yes One combined classify + dedup pass. Applies allow-listed labels (conf ≥ 0.75) and flags likely duplicates with an advisory comment (conf ≥ 0.8). Never closes.
triage-backfill.yml manual yes Same analysis over the existing open backlog. Dry-run by default (previews in the job summary), capped by max_issues.

scripts/triage/analyze.js is the single source of truth for the LLM logic (candidate search → one combined Anthropic call → structured proposals). It's repo-agnostic (context.repo), so it's identical to the CopilotKit copy.

ag-ui-specific tailoring

  • Label allow-list holds ag-ui's real content labels only: bug, enhancement, documentation, question, Integration, SDK, framework, Agent Framework. enhancement is this repo's canonical feature type (78 open issues); the rarely-used Feature Request synonym is deliberately omitted so the classifier can't waffle between the two. Curation/disposition labels (proposal, Roadmap, good first issue, help wanted…) are excluded — human calls.
  • Stale exemptions are Roadmap,proposal (the roadmap-track labels).

Rollout

  • triage-stale needs a needs-info label created before it does anything — this repo doesn't have one yet. Until then it's a clean no-op.
  • The two LLM workflows are clean no-ops until a model provider is configured (they log and exit, no failures). Provider is pluggable via env:
    • Azure OpenAI / Foundry (preferred — uses shared credits): secret AZURE_OPENAI_API_KEY + repo Variables AZURE_OPENAI_ENDPOINT / AZURE_OPENAI_DEPLOYMENT.
    • Anthropic (fallback): secret ANTHROPIC_API_KEY.
    • TRIAGE_PROVIDER forces one; otherwise inferred from present creds (Azure wins if both).
  • First LLM run should be triage-backfill with dry-run on + a small max_issues to eyeball proposals before anything applies.

Safety model

  • Constrained action space — the LLM never posts free text; it returns structured JSON, workflow applies validated labels + templated comments only.
  • Default-deny label allow-list — only curated content labels can be applied.
  • Confidence gates — labels ≥ 0.75, dedup ≥ 0.8 (dup target must be one of the candidates actually searched).
  • Flag, never close — humans close.
  • Spam / low-signal gate — already-flagged or empty-body-from-outsider issues skip the LLM call entirely (cost guard).
  • SHA-pinned actions with persist-credentials: false; job-scoped issues: write; inputs via env (no template injection) — passes zizmor/GHAS.
  • Provider-agnostic — one small callModel() speaks Anthropic or any Azure OpenAI-compatible deployment; keys are secrets, endpoint/deployment are non-secret Variables.

Testing

  • node --check scripts/triage/analyze.js → syntax OK.
  • Workflow structure validated; no CopilotKit-specific tokens leaked into the ported files.
  • Provider selection logic unit-checked locally (none → skip; anthropic-only; azure partial → skip; azure full; both → azure; forced anthropic) — all pass.
  • Runtime behavior to be validated via the dry-run backfill once a provider is configured (first rollout step above).

🤖 Generated with Claude Code

…+ backfill)

Port of the CopilotKit issue-triage automation, adapted to ag-ui's label
taxonomy. Three workflows sharing one analysis module (scripts/triage/analyze.js;
repo-agnostic via context.repo):

- triage-stale.yml       daily cron, no LLM. Marks needs-info issues stale after
                         14d silence, closes at +7d. Exempts Roadmap/proposal;
                         PRs untouched. (Needs a needs-info label created to act.)
- triage-on-open.yml     issues:opened. One combined LLM call classifies + flags
                         duplicates. Applies allow-listed labels (>=0.75) and an
                         advisory dup comment (>=0.8). Never closes.
- triage-backfill.yml    manual, dry-run by default. Same analysis over the
                         existing backlog, capped by max_issues.

Allow-list holds ag-ui's real content labels only: bug, enhancement,
documentation, question, Integration, SDK, framework, Agent Framework.
Curation/disposition labels (proposal, Roadmap, good first issue, help wanted…)
excluded. Safety: default-deny allow-list, confidence gates, flag-never-close,
spam/low-signal cost gate, SHA-pinned actions w/ persist-credentials:false,
clean-skip when ANTHROPIC_API_KEY absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@BenTaylorDev
BenTaylorDev requested a review from a team as a code owner July 21, 2026 16:16
Comment thread .github/workflows/triage-backfill.yml Fixed
Comment thread .github/workflows/triage-on-open.yml Fixed
Comment thread .github/workflows/triage-stale.yml Fixed
Comment thread .github/workflows/triage-backfill.yml Fixed
Comment thread .github/workflows/triage-on-open.yml Fixed
Comment thread .github/workflows/triage-backfill.yml Fixed
Comment thread .github/workflows/triage-on-open.yml Fixed
Comment thread .github/workflows/triage-on-open.yml Fixed
Comment thread .github/workflows/triage-stale.yml Fixed
Comment thread .github/workflows/triage-stale.yml Fixed
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Python Preview Packages

Version 0.0.0.dev1784820006 published to TestPyPI.

Warning: These packages are built from contributor code that may not yet have been vetted for correctness or security. Install at your own risk and do not use in production.

Install with uv

Add the TestPyPI index to your pyproject.toml:

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = true

Then install the packages you need:

# Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1784820006' --index testpypi

# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1784820006' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1784820006' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1784820006' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1784820006' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1784820006' --index testpypi

Install with pip

pip install \
  --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  ag-ui-protocol==0.0.0.dev1784820006

Use --extra-index-url https://pypi.org/simple/ so pip can resolve
transitive dependencies (pydantic, fastapi, etc.) from real PyPI.


Commit: 7323f60

@pkg-pr-new

pkg-pr-new Bot commented Jul 21, 2026

Copy link
Copy Markdown

Open in StackBlitz

@ag-ui/a2a-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2a-middleware@2224

@ag-ui/a2ui-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2ui-middleware@2224

@ag-ui/event-throttle-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/event-throttle-middleware@2224

@ag-ui/mcp-apps-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mcp-apps-middleware@2224

@ag-ui/mcp-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mcp-middleware@2224

@ag-ui/a2a

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2a@2224

@ag-ui/adk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/adk@2224

@ag-ui/ag2

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/ag2@2224

@ag-ui/agno

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/agno@2224

@ag-ui/aws-strands

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/aws-strands@2224

@ag-ui/claude-agent-sdk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/claude-agent-sdk@2224

@ag-ui/crewai

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/crewai@2224

@ag-ui/langchain

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/langchain@2224

@ag-ui/langgraph

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/langgraph@2224

@ag-ui/llamaindex

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/llamaindex@2224

@ag-ui/mastra

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mastra@2224

@ag-ui/pydantic-ai

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/pydantic-ai@2224

@ag-ui/vercel-ai-sdk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/vercel-ai-sdk@2224

@ag-ui/watsonx

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/watsonx@2224

@ag-ui/a2ui-toolkit

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2ui-toolkit@2224

create-ag-ui-app

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/create-ag-ui-app@2224

@ag-ui/client

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/client@2224

@ag-ui/core

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/core@2224

@ag-ui/encoder

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/encoder@2224

@ag-ui/proto

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/proto@2224

commit: f633e24

…te injection)

ag-ui runs zizmor via GitHub Advanced Security without CopilotKit's suppression
config, so it surfaces findings CK's check hid. Address them at the source:

- excessive-permissions (error): move `issues: write` from workflow level to the
  job, with an explanatory comment (also clears undocumented-permissions).
- anonymous-definition: give each job a `name:`.
- template-injection: pass workflow_dispatch inputs (dry_run/max_issues) via env
  and read from process.env instead of interpolating ${{ }} into the script.
- secrets-outside-env (warning): inline-ignore with justification — a single
  read-only classification key doesn't warrant a dedicated GH Environment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread .github/workflows/triage-backfill.yml Fixed
Comment thread .github/workflows/triage-on-open.yml Fixed
Comment thread .github/workflows/triage-stale.yml Fixed
…rred, Anthropic fallback)

analyze.js gains a small provider abstraction selected by env:
- Azure OpenAI / Foundry: AZURE_OPENAI_API_KEY (secret) + AZURE_OPENAI_ENDPOINT /
  AZURE_OPENAI_DEPLOYMENT (repo Variables); AZURE_OPENAI_API_VERSION optional.
- Anthropic: ANTHROPIC_API_KEY (secret); ANTHROPIC_MODEL optional.
- TRIAGE_PROVIDER forces one; else inferred from present creds (Azure wins if both).

Both providers get the same prompt and return JSON-as-text, extracted uniformly, so
the whole Foundry catalog (GPT/Llama/…) works. Workflows pass the new env through
(non-secret Azure config via vars); backfill guard now checks providerConfigured().
Lets triage runs draw on shared Azure credits and drops the Anthropic spend-cap item.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…point pin)

From a 3-front security audit (Actions supply-chain, LLM prompt-injection, secrets)
against current best practices. The LLM/output surface was already contained; these
close the runner/cost-layer gaps:

- Harden-Runner (SHA-pinned) as the first step in every job, egress-policy: audit —
  baseline the network, then flip to block with the commented allow-list so the model
  key can only reach GitHub + the model host (the tj-actions lesson: pinning alone
  doesn't stop a bumped/transitive dep from exfiltrating with unrestricted egress).
- Least privilege: top-level `permissions: {}` default + job-scoped
  `contents: read` (needed by checkout) + `issues: write`. Nothing else.
- `timeout-minutes` on every job (a hung model fetch shouldn't burn the 6h default).
- analyze.js refuses to POST the key to any non-*.azure.com host (guards the
  mutable endpoint Variable); Anthropic host is already hardcoded.

Already covered, not re-added: zizmor in CI + Renovate exist in both repos.
Operator follow-ups (documented in README): provider spend cap, flip egress to
block after baseline, confirm Renovate doesn't auto-merge action bumps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread .github/workflows/triage-backfill.yml Fixed
Comment thread .github/workflows/triage-on-open.yml Fixed
Comment thread .github/workflows/triage-stale.yml Fixed
…d-permissions)

Per-scope rationale comments on the job permissions blocks so the GHAS/zizmor
undocumented-permissions notices clear — a truly clean static-analysis pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants