chore: import upstream snapshot with attribution
Harness Compat / harness compat (push) Failing after 0s
CI / test on 3.12 (standard) (push) Has been cancelled
CI / test on 3.13 (standard) (push) Has been cancelled
CI / test on 3.14 (standard) (push) Has been cancelled
CI / test on 3.10 (all-extras) (push) Has been cancelled
CI / test on 3.11 (all-extras) (push) Has been cancelled
CI / test on 3.12 (all-extras) (push) Has been cancelled
CI / test on 3.14 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.10 (pydantic-evals) (push) Has been cancelled
CI / test on 3.11 (pydantic-evals) (push) Has been cancelled
CI / test on 3.12 (pydantic-evals) (push) Has been cancelled
CI / deploy-docs-preview (push) Has been cancelled
CI / build release artifacts (push) Has been cancelled
CI / publish to PyPI (push) Has been cancelled
CI / Send tweet (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / mypy (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / test on 3.10 (standard) (push) Has been cancelled
CI / test on 3.11 (standard) (push) Has been cancelled
CI / test on 3.13 (all-extras) (push) Has been cancelled
CI / test on 3.14 (all-extras) (push) Has been cancelled
CI / test on 3.10 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.11 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.12 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-evals) (push) Has been cancelled
CI / test on 3.14 (pydantic-evals) (push) Has been cancelled
CI / test on 3.10 (lowest-versions) (push) Has been cancelled
CI / test on 3.11 (lowest-versions) (push) Has been cancelled
CI / test on 3.12 (lowest-versions) (push) Has been cancelled
CI / test on 3.13 (lowest-versions) (push) Has been cancelled
CI / test on 3.14 (lowest-versions) (push) Has been cancelled
CI / test examples on 3.11 (push) Has been cancelled
CI / test examples on 3.12 (push) Has been cancelled
CI / test examples on 3.13 (push) Has been cancelled
CI / test examples on 3.14 (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / check (push) Has been cancelled
CI / deploy-docs (push) Has been cancelled
Harness Compat / harness compat (push) Failing after 0s
CI / test on 3.12 (standard) (push) Has been cancelled
CI / test on 3.13 (standard) (push) Has been cancelled
CI / test on 3.14 (standard) (push) Has been cancelled
CI / test on 3.10 (all-extras) (push) Has been cancelled
CI / test on 3.11 (all-extras) (push) Has been cancelled
CI / test on 3.12 (all-extras) (push) Has been cancelled
CI / test on 3.14 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.10 (pydantic-evals) (push) Has been cancelled
CI / test on 3.11 (pydantic-evals) (push) Has been cancelled
CI / test on 3.12 (pydantic-evals) (push) Has been cancelled
CI / deploy-docs-preview (push) Has been cancelled
CI / build release artifacts (push) Has been cancelled
CI / publish to PyPI (push) Has been cancelled
CI / Send tweet (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / mypy (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / test on 3.10 (standard) (push) Has been cancelled
CI / test on 3.11 (standard) (push) Has been cancelled
CI / test on 3.13 (all-extras) (push) Has been cancelled
CI / test on 3.14 (all-extras) (push) Has been cancelled
CI / test on 3.10 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.11 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.12 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-evals) (push) Has been cancelled
CI / test on 3.14 (pydantic-evals) (push) Has been cancelled
CI / test on 3.10 (lowest-versions) (push) Has been cancelled
CI / test on 3.11 (lowest-versions) (push) Has been cancelled
CI / test on 3.12 (lowest-versions) (push) Has been cancelled
CI / test on 3.13 (lowest-versions) (push) Has been cancelled
CI / test on 3.14 (lowest-versions) (push) Has been cancelled
CI / test examples on 3.11 (push) Has been cancelled
CI / test examples on 3.12 (push) Has been cancelled
CI / test examples on 3.13 (push) Has been cancelled
CI / test examples on 3.14 (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / check (push) Has been cancelled
CI / deploy-docs (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
---
|
||||
# Shared adversarial validity gate for bug/behavior-filing gh-aw sweeps.
|
||||
# gh-aw imports this file; the markdown below (after the closing ---) is
|
||||
# appended to the agent's task prompt at runtime via {{#runtime-import}}.
|
||||
#
|
||||
# Encodes the concrete failure modes that made past sweep issues get closed as
|
||||
# not-a-bug / by-design / duplicate. Every create-issue must clear this gate.
|
||||
---
|
||||
|
||||
## Adversarial validity gate — mandatory before `create_issue`
|
||||
|
||||
Put the finding through this validity gate before filing, reviewing it as a
|
||||
skeptical maintainer would — treat **"this is NOT a bug"** as the default and
|
||||
file **only** if it survives every check below; otherwise call
|
||||
`mcp__safeoutputs__noop`. Most runs should noop — a false or by-design report
|
||||
costs more maintainer time than a missed one.
|
||||
|
||||
Record the results as an **`## Adversarial review`** section in the issue body.
|
||||
An issue that omits it is incomplete — noop instead.
|
||||
|
||||
1. **Reproduced on current `main`, for real.** You must have *executed* code this
|
||||
run — a source-level snippet via `uv run python -c …`, a tiny script, or a
|
||||
single `uv run pytest -k …` — and **observed** the failure. Paste the exact
|
||||
command and its actual output. A claim you only reasoned about (e.g. "this
|
||||
*would* fail") is not a bug and is the most common reason past reports were
|
||||
rejected as a false premise.
|
||||
|
||||
2. **Existing tests don't already bless the behavior.** Grep the suite for the
|
||||
symbol / code path and **read** the nearest tests. If a passing test already
|
||||
asserts the current behavior, the behavior is intentional → noop. (Past
|
||||
reports proposed one-line "fixes" that broke a dozen adapter/serialization
|
||||
tests asserting the opposite on purpose.) A fix that would merely require
|
||||
*updating* tests is not, by itself, proof of intent — real fixes often update
|
||||
a stale test — but it raises the bar: read every test the fix would touch and
|
||||
noop if any of them asserts the current behavior deliberately (an explicit
|
||||
comment/docstring, or the same expectation repeated across several tests).
|
||||
|
||||
3. **Ruled out "by design."** Check for: a nearby comment/docstring explaining
|
||||
the choice, the provider profile, a maintainer decision in a linked issue/PR,
|
||||
and whether other providers/adapters deliberately do the same thing.
|
||||
Programmatic-only fields (`metadata`, `conversation_id`) excluded from wire /
|
||||
UI protocols, and request-only parts absent from a *response* union (or vice
|
||||
versa), are intentional — not bugs.
|
||||
|
||||
4. **No cross-provider false equivalence** *(provider-specific findings only)*.
|
||||
If the finding concerns a provider's request/response payload or SDK shape,
|
||||
verify the real type for **that** provider from its own types or docs — never
|
||||
infer a bug by analogy to a different provider. For provider-agnostic findings
|
||||
(core serialization/round-trip, streaming lifecycle, message plumbing), this
|
||||
check does not apply — skip it.
|
||||
|
||||
5. **Not already tracked.** Re-confirm the dedup above — label-filtered where
|
||||
this sweep has a dedicated label, otherwise the full open-issue scan —
|
||||
returned nothing covering this exact finding.
|
||||
|
||||
If any *applicable* check fails or is genuinely inconclusive,
|
||||
`mcp__safeoutputs__noop`. A check that doesn't apply (e.g. the provider check for
|
||||
a core finding) is not a failure — skip it. One issue that clears every
|
||||
applicable check beats five that don't.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
# Full git history for all pydantic-ai gh-aw workflows.
|
||||
# Needed for git log / git diff in the agent container.
|
||||
checkout:
|
||||
fetch-depth: 0
|
||||
---
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
# Shared runtime + engine config for the Pydantic AI gh-aw shim (MiniMax backend).
|
||||
#
|
||||
# Registers as the built-in `claude` engine and only overrides `command`, so
|
||||
# gh-aw runs its full Claude proxy + credential-injection machinery.
|
||||
#
|
||||
# ANTHROPIC_BASE_URL MUST be a compile-time literal (not a ${{ vars.* }}
|
||||
# expression): gh-aw derives the api-proxy target host AND the
|
||||
# `--anthropic-api-base-path` from its parsed URL path at compile time. With a
|
||||
# vars expression the path can't be parsed, so the proxy drops the `/anthropic`
|
||||
# prefix and the upstream returns 404. Only ANTHROPIC_API_KEY stays a secret
|
||||
# (injected by the AWF api-proxy, excluded from the agent container).
|
||||
# MiniMax exposes an Anthropic-compatible API at https://api.minimax.io/anthropic.
|
||||
#
|
||||
# The checked-out workspace is mounted no-exec in the AWF sandbox, so a
|
||||
# pre-step stages a launcher in gh-aw's exec-able /tmp/gh-aw/bin that runs
|
||||
# `uv run --script` against the workspace harness.
|
||||
#
|
||||
# Required repo variable:
|
||||
# GH_AW_MODEL — model name forwarded as `--model <name>` to the harness.
|
||||
# Required secret:
|
||||
# MINIMAX_API_KEY — API key injected by the AWF api-proxy.
|
||||
#
|
||||
# Usage:
|
||||
# imports:
|
||||
# - shared/engine-minimax.md
|
||||
runtimes:
|
||||
uv: {}
|
||||
engine:
|
||||
id: claude
|
||||
model: ${{ vars.GH_AW_MODEL }}
|
||||
command: /tmp/gh-aw/bin/pydantic-ai-runner-launch
|
||||
env:
|
||||
ANTHROPIC_BASE_URL: https://api.minimax.io/anthropic
|
||||
ANTHROPIC_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
||||
---
|
||||
@@ -0,0 +1,118 @@
|
||||
---
|
||||
# Shared network allowlist for provider SDK/API hosts and vendor docs.
|
||||
#
|
||||
# This list is intended for research/review workflows that may need to consult:
|
||||
# - provider API endpoints
|
||||
# - provider SDK/docs/console pages
|
||||
# - provider model catalogs and related reference docs
|
||||
#
|
||||
# Sources:
|
||||
# - Existing allowlist from pydantic-ai-pr-review.md
|
||||
# - Domains referenced under docs/models/
|
||||
#
|
||||
# Keep this list host-only (no schemes/paths) and deduplicated.
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- python
|
||||
- github
|
||||
- chrome
|
||||
|
||||
# Core Pydantic docs
|
||||
- ai.pydantic.dev
|
||||
- pydantic.dev
|
||||
|
||||
# Anthropic
|
||||
- anthropic.com
|
||||
- console.anthropic.com
|
||||
- docs.anthropic.com
|
||||
- platform.claude.com
|
||||
- api.minimax.io
|
||||
|
||||
# OpenAI-compatible ecosystem
|
||||
- api.openai.com
|
||||
- platform.openai.com
|
||||
- developers.openai.com
|
||||
- api.deepseek.com
|
||||
- api-docs.deepseek.com
|
||||
- deepseek.com
|
||||
- openrouter.ai
|
||||
- api.perplexity.ai
|
||||
- docs.perplexity.ai
|
||||
- api.studio.nebius.com
|
||||
- studio.nebius.com
|
||||
- dashscope-intl.aliyuncs.com
|
||||
- dashscope.aliyuncs.com
|
||||
- www.alibabacloud.com
|
||||
- api.fireworks.ai
|
||||
- fireworks.ai
|
||||
- api.together.xyz
|
||||
- www.together.ai
|
||||
- api.sambanova.ai
|
||||
- cloud.sambanova.ai
|
||||
- docs.sambanova.ai
|
||||
- api.cerebras.ai
|
||||
- cerebras.ai
|
||||
- cloud.cerebras.ai
|
||||
- inference-docs.cerebras.ai
|
||||
|
||||
# xAI
|
||||
- api.x.ai
|
||||
- x.ai
|
||||
- console.x.ai
|
||||
- docs.x.ai
|
||||
|
||||
# Groq
|
||||
- api.groq.com
|
||||
- groq.com
|
||||
- console.groq.com
|
||||
|
||||
# Mistral
|
||||
- api.mistral.ai
|
||||
- mistral.ai
|
||||
- console.mistral.ai
|
||||
|
||||
# Cohere
|
||||
- api.cohere.com
|
||||
- cohere.com
|
||||
- dashboard.cohere.com
|
||||
|
||||
# Google Gemini / Vertex
|
||||
- ai.google.dev
|
||||
|
||||
# AWS Bedrock
|
||||
- amazonaws.com
|
||||
- aws.amazon.com
|
||||
- docs.aws.amazon.com
|
||||
- boto3.amazonaws.com
|
||||
|
||||
# GitHub Models
|
||||
- models.github.ai
|
||||
|
||||
# Hugging Face
|
||||
- huggingface.co
|
||||
- hf.co
|
||||
|
||||
# Azure / Microsoft Foundry
|
||||
- ai.azure.com
|
||||
- learn.microsoft.com
|
||||
|
||||
# Additional provider docs/endpoints used in models docs
|
||||
- vercel.com
|
||||
- ollama.com
|
||||
- platform.moonshot.ai
|
||||
- ovh.com
|
||||
- endpoints.ai.cloud.ovh.net
|
||||
|
||||
# Logfire ingestion/tenant endpoints
|
||||
- logfire-api.pydantic.dev
|
||||
- logfire-eu.pydantic.dev
|
||||
- logfire-us.pydantic.dev
|
||||
- logfire-api.pydantic.info
|
||||
- logfire-eu.pydantic.info
|
||||
- logfire-us.pydantic.info
|
||||
|
||||
# UI adapter protocol specs (Vercel AI SDK, AG-UI)
|
||||
- ai-sdk.dev
|
||||
- docs.ag-ui.com
|
||||
---
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
# Logfire OTLP observability shared import
|
||||
# Exports gh-aw distributed traces (agent GenAI spans, setup/conclusion spans)
|
||||
# to Pydantic Logfire via OTLP/HTTP.
|
||||
#
|
||||
# gh-aw POSTs OTLP/HTTP JSON to {endpoint}/v1/traces, so this endpoint must be
|
||||
# the bare Logfire ingest base URL (no /v1/traces path).
|
||||
#
|
||||
# Use vars.LOGFIRE_URL to avoid hardcoding endpoints in each workflow. Keep
|
||||
# network allowlists in sync with the possible hosts for this variable.
|
||||
#
|
||||
# Required secret:
|
||||
# LOGFIRE_TOKEN — a Logfire project write token. Used as the Authorization
|
||||
# header value for OTLP ingest and passed directly to the agent container so
|
||||
# the Logfire Python SDK can also use it natively.
|
||||
#
|
||||
# Usage:
|
||||
# imports:
|
||||
# - shared/otel-logfire.md
|
||||
observability:
|
||||
otlp:
|
||||
endpoint: ${{ vars.LOGFIRE_URL || 'https://logfire-api.pydantic.dev' }}
|
||||
headers:
|
||||
Authorization: ${{ secrets.LOGFIRE_TOKEN }}
|
||||
if-missing: warn
|
||||
---
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
# Shared pre-agent-steps for the Pydantic AI gh-aw shim.
|
||||
#
|
||||
# These steps run after checkout but before the agent container starts, so
|
||||
# they still have open-network access.
|
||||
#
|
||||
# Stage launcher: gh-aw's repository checkout happens between pre-steps and
|
||||
# pre-agent-steps, so this step reads from .github/scripts/ in the workspace.
|
||||
# The launcher is staged into gh-aw's exec-able /tmp/gh-aw/bin/ path.
|
||||
#
|
||||
# Pre-warm: warms the harness's uv script environment on the open network so
|
||||
# the firewalled agent reuses a warm cache. Non-fatal on failure.
|
||||
#
|
||||
# Usage:
|
||||
# imports:
|
||||
# - shared/pre-agent-steps.md
|
||||
pre-agent-steps:
|
||||
- name: Stage Pydantic AI gh-aw shim launcher
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/bin
|
||||
install -m 755 .github/scripts/pydantic-ai-runner-launch.sh /tmp/gh-aw/bin/pydantic-ai-runner-launch
|
||||
# Install ripgrep and expose uv+rg inside the AWF chroot.
|
||||
# AWF auto-merges /opt/hostedtoolcache/**/bin into the container PATH
|
||||
# and also reads $GITHUB_PATH entries added before the engine step.
|
||||
- name: Install tools for AWF sandbox (ripgrep)
|
||||
run: bash .github/scripts/install-sandbox-tools.sh
|
||||
- name: Pre-warm Pydantic AI gh-aw shim uv environment
|
||||
run: bash .github/scripts/prewarm-pydantic-ai-runner.sh
|
||||
---
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
# Shared pre-steps for the Pydantic AI gh-aw shim.
|
||||
#
|
||||
# Setting engine.command (in engine-minimax.md) makes gh-aw skip ALL engine
|
||||
# installation steps, which also drops the bundled AWF firewall binary install.
|
||||
# This step re-runs gh-aw's own installer so the firewall binary is present.
|
||||
#
|
||||
# Usage:
|
||||
# imports:
|
||||
# - shared/pre-steps.md
|
||||
pre-steps:
|
||||
- name: Install AWF firewall binary (skipped by custom engine.command)
|
||||
run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.46
|
||||
---
|
||||
@@ -0,0 +1,128 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Bug Hunter agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is used verbatim only as the fallback
|
||||
when the Logfire managed variable `gh_aw_pydantic_ai_bug_hunter_prompt` is
|
||||
unset or unreachable. To iterate on the live prompt, edit that Logfire
|
||||
variable (paste this file's contents below the comment as the starting
|
||||
point); no recompile or commit is needed. Keep this file in sync as the
|
||||
reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Bug Hunter
|
||||
|
||||
## Objective
|
||||
|
||||
Find a single reproducible, user-impacting bug that can be covered by a minimal
|
||||
failing test. Not a number field accepting `"ABC"` — a real, impactful bug.
|
||||
|
||||
**The bar is high: you must actually reproduce the bug before filing.** Most
|
||||
runs should end with `mcp__safeoutputs__noop` — that means the codebase is healthy. Filing a
|
||||
weak or speculative issue is worse than filing nothing.
|
||||
|
||||
### Data Gathering
|
||||
|
||||
1. Review recent changes: run `git log --since="28 days ago" --stat` and
|
||||
identify candidates with user-facing impact. Read the diffs and related
|
||||
files for each candidate.
|
||||
2. Investigate from multiple angles — different subsystems (model providers,
|
||||
the agent loop, tools, output handling, message history), different bug
|
||||
categories (logic errors, type-safety gaps, async edge cases), and
|
||||
different recent commits.
|
||||
3. Reproduce locally — **mandatory, not optional**:
|
||||
- Write a **new** minimal reproduction: a small script or test that directly
|
||||
triggers the specific bug you identified. Do **not** run the existing
|
||||
suite (`make test`, `pytest`) and report its failures — if you did not
|
||||
write the test, a failure is not your finding.
|
||||
- Capture the exact steps and output from your reproduction.
|
||||
- If you cannot write a concrete reproduction that fails due to the bug, do
|
||||
**not** file it. Call `mcp__safeoutputs__noop` instead.
|
||||
|
||||
### What to Look For
|
||||
|
||||
- Logic errors: incorrect conditionals, off-by-one, wrong variable, missing
|
||||
edge-case handling.
|
||||
- Clear user impact: wrong output, raised/swallowed exception, broken agent
|
||||
run, incorrect tool dispatch, mis-serialized message history.
|
||||
- Deterministic reproduction (not flaky) that you trigger yourself.
|
||||
- Expressible as a minimal failing test (unit or integration).
|
||||
|
||||
### What to Skip
|
||||
|
||||
- Theoretical concerns without a reproduction — no "this looks like it could break."
|
||||
- Code that "looks wrong" but works correctly in practice.
|
||||
- Existing test-suite failures you did not cause.
|
||||
- Edge cases needing unusual or undocumented inputs.
|
||||
- Issues requiring large refactors or design changes.
|
||||
- Behavior already tracked by an open issue.
|
||||
- **By-design behavior.** Check for nearby comments explaining the choice,
|
||||
consistent patterns across the codebase, and recent PRs/commits for context.
|
||||
If the "bug" requires assuming an error despite an established pattern, it is
|
||||
probably by-design.
|
||||
- **Cross-provider comparisons.** Different providers have different semantics
|
||||
by design. Do not assume one provider's behavior is the "correct" reference
|
||||
for another. Only flag a bug if the behavior contradicts the provider's own
|
||||
documented API contract.
|
||||
|
||||
### Deduplication — mandatory BEFORE filing an issue
|
||||
|
||||
Search for existing issues that might overlap
|
||||
your run's scope. List open issues through the proxied `gh` CLI and filter
|
||||
locally — the `/search/issues` endpoint is blocked by the firewall proxy and
|
||||
there are no `mcp__github__*` tools:
|
||||
|
||||
```
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
Scan the titles for the sweep prefixes (`[bug-hunter]`,
|
||||
`[provider-mapping-sweep]`, `[streaming-resilience-sweep]`, `[roundtrip-sweep]`)
|
||||
and for keywords related to whatever subsystem you're investigating.
|
||||
If a matching issue already covers the same root cause, call
|
||||
`mcp__safeoutputs__noop` immediately — do NOT file a duplicate, even to
|
||||
"independently confirm" the bug. Confirming is not value-add.
|
||||
|
||||
### Sandbox notes
|
||||
|
||||
- Read files in large ranges (500+ lines per call). Do NOT read 30–80 lines at a time.
|
||||
- Use the native `Grep` and `Glob` tools for codebase search.
|
||||
|
||||
### Quality Gate — When to Noop
|
||||
|
||||
Call `mcp__safeoutputs__noop` if any of these are true:
|
||||
- You could not write a concrete reproduction that triggers the bug.
|
||||
- Your only evidence is an existing test failure you did not cause.
|
||||
- The bug is speculative — inferred from reading code, not triggered.
|
||||
- A similar issue is already open.
|
||||
- The impact is cosmetic or low-severity (e.g., a typo in a log message).
|
||||
- The bug is already fixed in a recently merged PR (search before filing).
|
||||
|
||||
### Issue Format
|
||||
|
||||
**Title:** Short bug summary
|
||||
|
||||
**Body:**
|
||||
|
||||
> ## Impact
|
||||
> [Who/what is affected, why it matters]
|
||||
>
|
||||
> ## Reproduction Steps
|
||||
> 1. [Exact commands you ran, including the new test or script you wrote]
|
||||
>
|
||||
> ## Expected vs Actual
|
||||
> **Expected:** ...
|
||||
> **Actual:** ... [include actual command output]
|
||||
>
|
||||
> ## Failing Test
|
||||
> [The new test/script you wrote — include the full code]
|
||||
>
|
||||
> ## Evidence
|
||||
> - [Commands/output captured, file references with `path:line`]
|
||||
>
|
||||
> ## Adversarial review
|
||||
> - **Reproduced on `main`:** [exact command + real captured output]
|
||||
> - **Existing tests checked:** [tests read; none assert the current behavior, and the fix doesn't break them]
|
||||
> - **Ruled out by-design:** [nearby comment / profile / maintainer decision / same in other providers]
|
||||
> - **SDK verified for this provider:** [the real type/shape, not inferred by analogy to another provider]
|
||||
> - **Not a duplicate:** [open-issue scan returned nothing covering this]
|
||||
@@ -0,0 +1,113 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Docs Drift agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is used verbatim only as the fallback
|
||||
when the Logfire managed variable `gh_aw_pydantic_ai_docs_drift_prompt` is
|
||||
unset or unreachable. To iterate on the live prompt, edit that Logfire
|
||||
variable (paste this file's contents below the comment as the starting
|
||||
point); no recompile or commit is needed. Keep this file in sync as the
|
||||
reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Docs Drift
|
||||
|
||||
Documentation lives in `docs/`
|
||||
(built with `mkdocs`, configured in `mkdocs.yml`), plus `README.md`,
|
||||
`CONTRIBUTING.md`, and per-package `AGENTS.md` files. Doc code examples are
|
||||
tested by `tests/test_examples.py`.
|
||||
|
||||
## Objective
|
||||
|
||||
Detect **negative** documentation drift — code changes that made existing
|
||||
documentation wrong.
|
||||
|
||||
**Noop is the expected outcome most days.** Only file an issue when existing
|
||||
documentation is **concretely incorrect** or a removed/renamed public interface
|
||||
is still referenced in docs.
|
||||
|
||||
Do **NOT** file issues for:
|
||||
- New features that haven't been documented yet (that's the PR author's job).
|
||||
- Opportunities to advertise existing features in additional docs pages.
|
||||
- Minor wording that could be improved but isn't factually wrong.
|
||||
|
||||
### Data Gathering
|
||||
|
||||
1. Run `git log --since="7 days ago" --oneline --stat` for a summary of recent
|
||||
commits. If there are no commits in the window, call `mcp__safeoutputs__noop` and stop.
|
||||
2. Inventory documentation: scan `docs/`, `mkdocs.yml`, `README.md`,
|
||||
`CONTRIBUTING.md`, and `AGENTS.md` files. Do not assume a fixed structure.
|
||||
|
||||
### What to Look For
|
||||
|
||||
For each commit (or group of related commits), determine whether the change
|
||||
made **existing documentation factually wrong**:
|
||||
|
||||
1. **Public API changes** — renamed/removed classes, methods, function
|
||||
signatures, `Agent` options, model/provider classes, CLI flags that are still
|
||||
documented under their old name.
|
||||
2. **Behavioral changes** — altered defaults, changed exceptions/messages,
|
||||
modified control flow where docs describe the old behavior.
|
||||
3. **Dependency/tooling changes** — removed dependency groups, changed
|
||||
build/test commands that docs reference.
|
||||
4. **Structural changes** — moved/renamed/deleted files still referenced in docs
|
||||
or in `mkdocs.yml` nav.
|
||||
5. **Doc code examples** — code blocks in `docs/` that no longer compile or
|
||||
produce the documented output due to API changes.
|
||||
|
||||
### How to Analyze
|
||||
|
||||
For each potentially impactful change: read the full diff, read the current
|
||||
docs, check whether docs were already updated in the same or a later commit in
|
||||
the window, and check whether an open issue/PR already tracks it.
|
||||
|
||||
### Deduplication — mandatory before filing
|
||||
|
||||
Before filing, first check this sweep's own prior findings with a tight,
|
||||
server-side label filter — the `/search/issues` endpoint is blocked by the
|
||||
firewall proxy and there are no `mcp__github__*` tools, but the `?labels=`
|
||||
filter on the issue-list endpoint is allowed:
|
||||
|
||||
```bash
|
||||
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=docs-drift&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title}'
|
||||
```
|
||||
|
||||
Only if that is inconclusive, widen to a full open-issue scan and grep locally
|
||||
for keywords from your finding:
|
||||
|
||||
```bash
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
If a matching issue exists, call `mcp__safeoutputs__noop`. Do NOT file duplicates.
|
||||
|
||||
### What to Skip
|
||||
|
||||
- Purely internal refactors with no user-facing impact.
|
||||
- Changes where docs were already updated in the same/later commit.
|
||||
- Changes already tracked by an open issue or PR.
|
||||
- Test-only changes.
|
||||
- Minor changes where existing docs are still substantially correct.
|
||||
- **New features without documentation** — these are NOT drift. The PR author
|
||||
or a separate docs PR will add them. Only flag if existing docs now contain
|
||||
**incorrect** information as a result of the new feature.
|
||||
|
||||
### Issue Format
|
||||
|
||||
**Title:** Brief summary (e.g., "Update agent.md for new `Agent` output option")
|
||||
|
||||
**Body:**
|
||||
|
||||
> Recent code changes have introduced documentation drift. The following
|
||||
> changes need corresponding documentation updates.
|
||||
>
|
||||
> ## Changes Requiring Documentation Updates
|
||||
>
|
||||
> ### 1. [Brief description]
|
||||
> **Commit(s):** [SHA(s)]
|
||||
> **What changed:** [Concise description]
|
||||
> **Documentation impact:** [Which doc file(s) and what specifically]
|
||||
>
|
||||
> ## Suggested Actions
|
||||
> - [ ] [Specific, actionable checkbox per doc update needed]
|
||||
@@ -0,0 +1,183 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI PR Review agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is the verbatim fallback when the
|
||||
Logfire managed variable `gh_aw_pydantic_ai_pr_review_prompt` is unset or
|
||||
unreachable. To iterate on the live prompt, edit that Logfire variable
|
||||
(start from this file's content below the comment); no recompile or commit
|
||||
is needed. Keep this file in sync as the reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI PR Review
|
||||
|
||||
You are reviewing PR **#${{ github.event.pull_request.number }}** in
|
||||
[${{ github.repository }}](https://github.com/${{ github.repository }}) —
|
||||
*${{ github.event.pull_request.title }}*.
|
||||
|
||||
**Pydantic AI** ([ai.pydantic.dev](https://ai.pydantic.dev/)) is a
|
||||
provider-agnostic GenAI agent framework for Python. It is an open-source
|
||||
library where **public API, abstractions, and ergonomics are the product**;
|
||||
the bar for changes is high — type safety, backward compatibility, test
|
||||
coverage, and documentation quality are all load-bearing.
|
||||
|
||||
## Constraints
|
||||
|
||||
This workflow is **read-only** for the codebase. Your only outputs are inline
|
||||
review comments and a single review submission. Do not modify files.
|
||||
|
||||
## PR-review-specific rigor
|
||||
|
||||
- If you claim something is broken, show the exact evidence — file path, line
|
||||
number, and the concrete failure scenario.
|
||||
- Before posting any finding, re-read it as a skeptical maintainer. Ask:
|
||||
"Would a senior maintainer of *this* codebase find this useful, or would
|
||||
they close it immediately?" If "close", drop it.
|
||||
|
||||
## Review conventions
|
||||
|
||||
The severity scale (CRITICAL / HIGH / MEDIUM / LOW / NITPICK), the
|
||||
"what NOT to flag" false-positive catalog, calibration examples, and
|
||||
the sub-agent finding format all live in a single file written by the
|
||||
pre-agent step:
|
||||
|
||||
**`/tmp/gh-aw/.review-context/review-instructions.md`** — read this
|
||||
once before reviewing. It is the source of truth; do not re-derive
|
||||
severity bands or false-positive rules from your own priors.
|
||||
|
||||
**Verdict mapping:** any HIGH or CRITICAL finding → `REQUEST_CHANGES`.
|
||||
MEDIUM-only or below → `APPROVE` (post the comments anyway).
|
||||
No findings → `APPROVE`. **Cap inline comments at 30 per run** — if
|
||||
more findings survive, keep the highest-severity 30 inline and list
|
||||
the rest briefly in the review body.
|
||||
|
||||
## Review process
|
||||
|
||||
### Step 1 — Orient
|
||||
|
||||
1. Read `/tmp/gh-aw/.review-context/review-instructions.md` —
|
||||
severity scale, false-positive catalog, calibration examples, and
|
||||
sub-agent finding format. Treat it as binding.
|
||||
2. Read `/tmp/gh-aw/.review-context/pr-details.json` and `pr-size.txt`.
|
||||
3. Read `pr-comments.txt`, `related-issues.txt`, and the relevant
|
||||
`agents-md.txt` sections.
|
||||
4. Skim `review-comments.txt` for prior threads (note the most recent
|
||||
review from this bot — you'll compare verdicts at the end).
|
||||
5. Read repo-root `CLAUDE.md` / `AGENTS.md` for project-wide conventions.
|
||||
|
||||
### Step 2 — Pick a strategy from PR size
|
||||
|
||||
Read `pr-size.txt`. Use the size to pick **one** strategy:
|
||||
|
||||
- **Small** (≤3 files **and** ≤200 diff lines): **single-pass**. Skip
|
||||
Step 3's fan-out; review every changed file yourself in Step 4.
|
||||
- **Medium** (4–10 files, or ≤1000 diff lines): **fan out 2 sub-agents**
|
||||
— one with the `az.txt` ordering, one with `largest.txt`.
|
||||
- **Large** (>10 files **or** >1000 diff lines): **fan out 3 sub-agents**
|
||||
— one each for `az.txt`, `za.txt`, and `largest.txt`.
|
||||
|
||||
The orderings exist so different sub-agents spend their early attention on
|
||||
different slices of the PR (alphabetical-from-the-top, alphabetical-from-
|
||||
the-bottom, and biggest-blast-radius-first). Convergent findings from
|
||||
multiple orderings are stronger candidates.
|
||||
|
||||
### Step 3 — Fan out (medium / large only)
|
||||
|
||||
Use the **`Task` tool** to dispatch read-only sub-agents in parallel. Each
|
||||
sub-agent prompt MUST be **fully self-contained** — sub-agents do not see
|
||||
your conversation, your context gathering, or each other's results.
|
||||
|
||||
For each sub-agent, include in its prompt:
|
||||
|
||||
1. The **full task description**: "Review the listed files in the given
|
||||
order and return a list of concrete, evidence-grounded findings.
|
||||
Return an empty list if you find nothing."
|
||||
2. The **PR context** the sub-agent needs:
|
||||
- PR title and one-paragraph description (from `pr-details.json`).
|
||||
- The relevant `AGENTS.md` excerpts (from `agents-md.txt`).
|
||||
- An explicit instruction to **`Read`
|
||||
`/tmp/gh-aw/.review-context/review-instructions.md` first** —
|
||||
that file holds the severity scale, false-positive catalog,
|
||||
calibration examples, and finding format. **Do not copy those
|
||||
sections into the sub-agent prompt** (the file is the single
|
||||
source of truth; copying drifts and bloats every prompt).
|
||||
3. The **assigned file list** (in the assigned ordering) and instructions
|
||||
to:
|
||||
- Read each `/tmp/gh-aw/.review-context/diff/<path>.diff` for changes.
|
||||
- Read the **full file** from the workspace for surrounding context
|
||||
(full files are checked out — use `Read`).
|
||||
- Check `/tmp/gh-aw/.review-context/review-comments.txt` for existing
|
||||
threads on these files; skip duplicates per the rules above.
|
||||
|
||||
Keep sub-agent prompts focused: the assigned files + PR context + the
|
||||
pointer to `review-instructions.md`. **Wait for all sub-agents to
|
||||
return** before proceeding.
|
||||
|
||||
**Merge findings:** keep findings flagged by multiple sub-agents with the
|
||||
strongest evidence; for a finding flagged by only one, scrutinize harder
|
||||
before keeping it. Then run Step 4 yourself as the quality gate.
|
||||
|
||||
### Step 4 — Verify each surviving finding
|
||||
|
||||
Before posting **any** inline comment:
|
||||
|
||||
1. **Read surrounding code** — open the full file via `Read`, not just the
|
||||
diff hunk. Confirm the failure scenario.
|
||||
2. **Construct a concrete trigger** — what specific input or state makes
|
||||
it fail? If you can't describe one, drop it.
|
||||
3. **Apply the false-positive catalog** from
|
||||
`/tmp/gh-aw/.review-context/review-instructions.md`. If the finding
|
||||
matches a "what NOT to flag" pattern, drop it.
|
||||
4. **Check existing threads** for the same `path:line` and apply the
|
||||
thread-handling rules above.
|
||||
5. **Confirm the line is commentable** — open
|
||||
`/tmp/gh-aw/.review-context/diff/<file>.diff` and check the target line
|
||||
has an `NL:<n>` prefix. If not, move the finding into the review body.
|
||||
|
||||
### Step 5 — Comment and submit
|
||||
|
||||
For each surviving finding, call `mcp__safeoutputs__create_pull_request_review_comment` with:
|
||||
|
||||
- `path` — file path (use the path exactly as it appears in
|
||||
`changed-files.txt`).
|
||||
- `line` — the `NL:` line number from the diff (right side, new code).
|
||||
- `body` — concise problem statement + concrete fix suggestion. Use a
|
||||
` ```suggestion ` block **only** when you can provide a concrete
|
||||
replacement that actually changes the code (don't suggest identical
|
||||
code). One issue per comment; group comments per file before moving on.
|
||||
|
||||
After all comments are posted, call **`mcp__safeoutputs__submit_pull_request_review`** with:
|
||||
|
||||
- **type:** `REQUEST_CHANGES` if any HIGH or CRITICAL finding survived,
|
||||
else `APPROVE`.
|
||||
- **body:** If you are approving, you should most often provide an empty
|
||||
body. For `REQUEST_CHANGES`,
|
||||
include only the verdict + any cross-cutting feedback that can't be
|
||||
expressed inline (e.g. "the new module duplicates logic in `agent.py`
|
||||
— consider unifying"). Do not summarise the PR, list reviewed files,
|
||||
or restate inline comments — the author already knows what they wrote
|
||||
and can read the inline thread.
|
||||
|
||||
**Skip if redundant:** if you have **zero new findings** and your verdict
|
||||
matches the most recent review from this bot (visible in
|
||||
`review-comments.txt`), call `mcp__safeoutputs__noop` with a short reason like
|
||||
"No new findings — prior review still applies" instead of submitting a
|
||||
redundant review.
|
||||
|
||||
**Bot-authored PRs:** GitHub forbids `APPROVE` / `REQUEST_CHANGES` from a
|
||||
bot reviewing another bot's PR. If the PR author is a bot, submit a
|
||||
`COMMENT` review with the verdict in the body.
|
||||
|
||||
## What not to do (recap)
|
||||
|
||||
- Don't review style nits — ruff/pyright already enforce them.
|
||||
- Don't restate the diff or summarise what the PR does — the author wrote
|
||||
it.
|
||||
- Don't post speculative "this might break" findings without a concrete
|
||||
trigger.
|
||||
- Don't flag coverage-gate or `# pragma: no cover` outcomes — the
|
||||
`fail_under = 100` CI job reports uncovered lines (and wrongly-placed
|
||||
pragmas) deterministically; predicting them is noise.
|
||||
- Don't comment on lines without an `NL:` prefix in the per-file diff.
|
||||
- Don't write to the workspace — every output is a safe-output call.
|
||||
- Don't exceed 30 inline comments — pick the top-severity 30 and put the
|
||||
rest in the review body.
|
||||
@@ -0,0 +1,129 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Provider Mapping Sweep agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is the verbatim fallback when the
|
||||
Logfire managed variable `gh_aw_pydantic_ai_provider_mapping_sweep_prompt`
|
||||
is unset or unreachable. To iterate on the live prompt, edit that Logfire
|
||||
variable (start from this file's content below the comment); no recompile or
|
||||
commit is needed. Keep this file in sync as the reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Provider Mapping Sweep
|
||||
|
||||
Model integrations live in
|
||||
`pydantic_ai_slim/pydantic_ai/models/` and providers in
|
||||
`pydantic_ai_slim/pydantic_ai/providers/`.
|
||||
|
||||
## Objective
|
||||
|
||||
This is a **rotating, single-provider sweep**. Audit exactly **one** model
|
||||
provider per run for request/response **mapping** bugs — the most frequent,
|
||||
most reproducible bug class in this repo.
|
||||
|
||||
1. Pick one provider to focus on this run. Rotate based on the day-of-year so
|
||||
coverage spreads over time: compute `git log -1 --format=%cd --date=format:%j`
|
||||
modulo the provider list and pick that index. Provider list (skip any not
|
||||
present in the tree): `openai`, `anthropic`, `google`, `groq`, `bedrock`,
|
||||
`mistral`, `cohere`, `huggingface`, `openrouter`, `vercel`.
|
||||
2. State the chosen provider up front.
|
||||
|
||||
## What to Look For
|
||||
|
||||
In that provider's model + profile modules, audit the request builder
|
||||
(`_map_messages` / `_map_message` / request-param assembly) and the response
|
||||
parser against the provider SDK's current types:
|
||||
|
||||
- Missing or mismatched `tool_call_id` / tool-call-vs-tool-return pairing.
|
||||
- Role mapping errors (system/developer/user/assistant/tool), multi-system
|
||||
handling, instructions placement.
|
||||
- Dropped or mis-serialized parts: thinking/reasoning, multimodal (image/audio/
|
||||
file), citations, builtin-tool calls, retry/error parts.
|
||||
- Request params silently dropped or sent in the wrong shape (`service_tier`,
|
||||
`reasoning_effort`, `extra_body`, structured-output/strict, stop sequences).
|
||||
- Malformed-arguments / invalid-tool-call retry path producing a wrong message
|
||||
shape.
|
||||
- Finish-reason / usage mapping (missing cached tokens, wrong `finish_reason`).
|
||||
|
||||
## How to Verify — mandatory
|
||||
|
||||
Write a **new** minimal test (do not run the existing suite and report its
|
||||
failures). Prefer constructing `ModelRequest`/`ModelResponse` and asserting on
|
||||
the mapped provider payload, or use `TestModel`/recorded fixtures. The bug must
|
||||
be triggered by code you wrote and observed to fail.
|
||||
|
||||
## What to Skip
|
||||
|
||||
- Speculation without a concrete failing reproduction.
|
||||
- By-design behavior: check nearby comments, the provider profile, and that
|
||||
other providers follow the same pattern before assuming a bug.
|
||||
- Behavior already tracked by an open issue — **search issues first**.
|
||||
- Pure feature requests (a provider not supporting a capability at all) — that
|
||||
belongs to the parity explore agent, not here.
|
||||
|
||||
## Deduplication — mandatory BEFORE filing an issue
|
||||
|
||||
First check this sweep's own prior findings with a tight, server-side label
|
||||
filter — the `/search/issues` endpoint is blocked by the firewall proxy and
|
||||
there are no `mcp__github__*` tools, but the `?labels=` filter on the
|
||||
issue-list endpoint is allowed:
|
||||
|
||||
```
|
||||
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=provider-mapping-sweep&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title}'
|
||||
```
|
||||
|
||||
Only if that is inconclusive, widen to a full open-issue scan and grep locally
|
||||
for your chosen provider name (and the `[provider-mapping-sweep]` and
|
||||
`[bug-hunter]` prefixes — the latter also files provider bugs):
|
||||
|
||||
```
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
If a matching issue covers the same mapping bug, call `mcp__safeoutputs__noop`.
|
||||
|
||||
## Sandbox notes
|
||||
|
||||
- Read the provider file in full (or large ranges). Model files are typically
|
||||
800–1500 lines — read them in 1–2 calls.
|
||||
- Do NOT spend time trying to import provider SDK type stubs (`mypy_boto3_*`,
|
||||
etc.) — they are not installed. Instead, grep the raw
|
||||
`botocore/data/*/service-2.json` or use `WebFetch` on the provider's API docs.
|
||||
- Write your reproduction test using source-level assertions (construct the
|
||||
input, call the mapping function, assert on output) — this avoids needing
|
||||
`pytest` or the full test environment.
|
||||
|
||||
## Quality Gate — When to Noop
|
||||
|
||||
`mcp__safeoutputs__noop` is the expected outcome most runs. Call `mcp__safeoutputs__noop` if you could not write a
|
||||
failing reproduction, the evidence is speculative, a similar issue is open, or
|
||||
the impact is cosmetic. One well-evidenced issue beats several weak ones.
|
||||
|
||||
## Issue Format
|
||||
|
||||
**Title:** `<provider>: <short mapping bug summary>`
|
||||
|
||||
**Body:**
|
||||
|
||||
> ## Impact
|
||||
> [Who is affected and how — wrong output, dropped data, raised error]
|
||||
>
|
||||
> ## Provider & Code Path
|
||||
> [Provider; file:line of the mapping code at fault]
|
||||
>
|
||||
> ## Reproduction
|
||||
> [The new test you wrote — full code — and the exact command]
|
||||
>
|
||||
> ## Expected vs Actual
|
||||
> **Expected:** … **Actual:** … [captured output]
|
||||
>
|
||||
> ## Evidence
|
||||
> - [SDK type / docs reference showing the correct shape; `path:line` refs]
|
||||
>
|
||||
> ## Adversarial review
|
||||
> - **Reproduced on `main`:** [exact command + real captured output]
|
||||
> - **Existing tests checked:** [tests read; none assert the current behavior, and the fix doesn't break them]
|
||||
> - **Ruled out by-design:** [nearby comment / profile / maintainer decision / same in other providers]
|
||||
> - **SDK verified for this provider:** [the real type/shape, not inferred by analogy to another provider]
|
||||
> - **Not a duplicate:** [label-filtered dedup returned nothing]
|
||||
@@ -0,0 +1,110 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Provider Parity Explore agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is the verbatim fallback when the
|
||||
Logfire managed variable `gh_aw_pydantic_ai_provider_parity_explore_prompt`
|
||||
is unset or unreachable. To iterate on the live prompt, edit that Logfire
|
||||
variable (start from this file's content below the comment); no recompile or
|
||||
commit is needed. Keep this file in sync as the reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Provider Parity Explore
|
||||
|
||||
Model integrations live in
|
||||
`pydantic_ai_slim/pydantic_ai/models/` and `…/providers/`.
|
||||
|
||||
## Objective
|
||||
|
||||
This is an **explore**, not a bug hunt. Pick **one** cross-cutting capability
|
||||
per run and map its support **across all providers**, surfacing silent gaps
|
||||
and inconsistencies a user would hit when switching providers.
|
||||
|
||||
Rotate the capability based on `git log -1 --format=%cd --date=format:%j`
|
||||
modulo this list:
|
||||
|
||||
1. Thinking / reasoning (`reasoning_effort`, thinking parts, budgets).
|
||||
2. Builtin tools (web search, web fetch, code execution) — presence & version.
|
||||
3. Usage & cost accounting (cached tokens, reasoning tokens, request counts).
|
||||
4. Structured output (native JSON schema / strict mode / tool-output mode).
|
||||
5. Streaming feature parity (deltas for thinking, tool args, usage on finish).
|
||||
6. Multimodal inputs (image / audio / document) per provider.
|
||||
|
||||
## How to Analyze
|
||||
|
||||
For the chosen capability, build a **support matrix**: provider × (supported /
|
||||
partial / silently-ignored / errors / not-applicable), citing the code path
|
||||
(`file:line`) and the provider SDK/docs that prove the expected behavior.
|
||||
Distinguish **silent drops** (input accepted, quietly ignored — a bug) from
|
||||
**explicit non-support** (clearly raised / documented — acceptable).
|
||||
|
||||
## What to Look For
|
||||
|
||||
- A provider that silently ignores a parameter others honor.
|
||||
- Stale provider SDK pinning that omits a now-standard capability.
|
||||
- Inconsistent defaults or types for the same conceptual feature.
|
||||
- A capability documented as general but missing for a major provider.
|
||||
|
||||
## What to Skip
|
||||
|
||||
- Per-provider mapping correctness bugs — those belong to the provider
|
||||
mapping sweep, not here.
|
||||
- Speculative "would be nice" features with no user impact.
|
||||
- Gaps already tracked by an open issue — **search issues first**.
|
||||
|
||||
## Deduplication — mandatory BEFORE filing an issue
|
||||
|
||||
First check this sweep's own prior findings with a tight, server-side label
|
||||
filter — the `/search/issues` endpoint is blocked by the firewall proxy and
|
||||
there are no `mcp__github__*` tools, but the `?labels=` filter on the
|
||||
issue-list endpoint is allowed:
|
||||
|
||||
```
|
||||
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=provider-parity-explore&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title}'
|
||||
```
|
||||
|
||||
Only if that is inconclusive, widen to a full open-issue scan and grep locally
|
||||
for "parity" and the capability you're auditing:
|
||||
|
||||
```
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
If a matching issue exists, call `mcp__safeoutputs__noop` immediately.
|
||||
|
||||
## Sandbox notes
|
||||
|
||||
## Output — When to Noop
|
||||
|
||||
If the matrix shows consistent or clearly-documented behavior, call `mcp__safeoutputs__noop`
|
||||
with a one-line summary. Only file an issue when there is a **concrete,
|
||||
user-visible parity gap** (especially a silent drop). At most one issue per
|
||||
run.
|
||||
|
||||
## Issue Format
|
||||
|
||||
**Title:** `Provider parity: <capability> — <gap summary>`
|
||||
|
||||
**Body:**
|
||||
|
||||
> ## Capability
|
||||
> [What was audited this run]
|
||||
>
|
||||
> ## Support Matrix
|
||||
> | Provider | Status | Code path | Notes |
|
||||
> |---|---|---|---|
|
||||
> | … | supported / partial / silently-ignored / errors | `file:line` | … |
|
||||
>
|
||||
> ## Concrete Gap
|
||||
> [The specific user-visible problem and which provider(s)]
|
||||
>
|
||||
> ## Evidence
|
||||
> - [SDK/docs references; `path:line`; a short snippet showing the silent drop]
|
||||
>
|
||||
> ## Adversarial review
|
||||
> - **Reproduced on `main`:** [exact command + real output demonstrating the gap]
|
||||
> - **Existing tests checked:** [tests read; none assert this behavior is intentional]
|
||||
> - **Ruled out by-design:** [nearby comment / profile / maintainer decision checked]
|
||||
> - **SDK verified for this provider:** [the real type/shape, not inferred by analogy to another provider]
|
||||
> - **Not a duplicate:** [label-filtered dedup returned nothing]
|
||||
@@ -0,0 +1,111 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Regression Detector agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is the verbatim fallback when the
|
||||
Logfire managed variable `gh_aw_pydantic_ai_regression_detector_prompt` is
|
||||
unset or unreachable. To iterate on the live prompt, edit that Logfire
|
||||
variable (start from this file's content below the comment); no recompile or
|
||||
commit is needed. Keep this file in sync as the reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Regression Detector
|
||||
|
||||
## Objective
|
||||
|
||||
Find one **behavioral regression** — something that worked in a recent
|
||||
released version and broke in a later one. Regressions are the most
|
||||
upgrade-blocking bug class for users.
|
||||
|
||||
## Data Gathering
|
||||
|
||||
1. Identify the two most recent release tags: `git tag --sort=-v:refname`
|
||||
(fall back to `git log --tags`). Call them `OLD` and `NEW`.
|
||||
2. `git log --oneline OLD..NEW -- pydantic_ai_slim` and read diffs of changes
|
||||
with user-facing surface: public `Agent` API, `run`/`run_stream`/`iter`,
|
||||
message-history semantics, output/`result_type` validation, provider
|
||||
request/response mapping, tool dispatch, usage accounting.
|
||||
3. Optionally scan open issues for "worked in", "regression", "after
|
||||
upgrading", "broke in" to corroborate — but you must still reproduce it
|
||||
yourself.
|
||||
|
||||
## How to Verify — mandatory
|
||||
|
||||
Write a **new** minimal test that exercises the behavior. Demonstrate it
|
||||
**passes on `OLD` and fails on `NEW`** — e.g. `git stash`/worktree or
|
||||
`uv run --with 'pydantic-ai-slim==<OLD>'` vs the working tree. A change that
|
||||
only "looks risky" in the diff is not a finding.
|
||||
|
||||
## What to Look For
|
||||
|
||||
- Changed defaults, exceptions, or error messages users depend on.
|
||||
- Output/validation behavior change for the same inputs.
|
||||
- Message-history / `new_messages()` shape or re-feed semantics changing.
|
||||
- Provider mapping that regressed for a previously-working call.
|
||||
- Behavioral/semantic changes that break documented usage patterns.
|
||||
|
||||
## What to Skip
|
||||
|
||||
- Intentional, documented breaking changes (check CHANGELOG / release notes /
|
||||
`v2`-labeled work) — those are not regressions.
|
||||
- Speculation without an old-passes/new-fails reproduction.
|
||||
- Behavior already tracked by an open issue — **search issues first**.
|
||||
|
||||
## Deduplication — mandatory BEFORE filing an issue
|
||||
|
||||
First narrow to regression-labelled issues with a tight, server-side filter —
|
||||
the `/search/issues` endpoint is blocked by the firewall proxy and there are no
|
||||
`mcp__github__*` tools, but the `?labels=` filter on the issue-list endpoint is
|
||||
allowed. This covers both prior `[regression-detector]` findings and human-filed
|
||||
regression reports:
|
||||
|
||||
```
|
||||
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=regression&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title}'
|
||||
```
|
||||
|
||||
Only if that is inconclusive, widen to a full open-issue scan and grep locally
|
||||
for "regression" and the affected symbol:
|
||||
|
||||
```
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
If a matching issue exists, call `mcp__safeoutputs__noop` immediately.
|
||||
|
||||
## Sandbox notes
|
||||
|
||||
- Use the native `Grep` tool for codebase search.
|
||||
|
||||
## Quality Gate — When to Noop
|
||||
|
||||
`mcp__safeoutputs__noop` is the expected outcome most runs. Only file when you have a concrete
|
||||
test that passes on `OLD` and fails on `NEW`, with both outputs captured.
|
||||
|
||||
## Issue Format
|
||||
|
||||
**Title:** `Regression: <behavior> broke between <OLD> and <NEW>`
|
||||
|
||||
**Body:**
|
||||
|
||||
> ## Impact
|
||||
> [Who is affected on upgrade and how]
|
||||
>
|
||||
> ## Versions
|
||||
> **Last working:** `OLD` · **First broken:** `NEW`
|
||||
> **Suspected commit(s):** [SHA(s) with links]
|
||||
>
|
||||
> ## Reproduction
|
||||
> [The new test — full code — and exact commands for OLD and NEW]
|
||||
>
|
||||
> ## Expected vs Actual
|
||||
> **OLD output:** … **NEW output:** …
|
||||
>
|
||||
> ## Evidence
|
||||
> - [Captured outputs for both versions; `path:line` of the change]
|
||||
>
|
||||
> ## Adversarial review
|
||||
> - **Reproduced on OLD and NEW:** [commands + real outputs for both versions]
|
||||
> - **Change was NOT intentional:** [found the commit/PR that changed it and confirmed it wasn't a deliberate behavior change — the #1 reason regression reports get rejected]
|
||||
> - **Existing tests checked:** [NEW-version tests read; none assert the new behavior as intended]
|
||||
> - **Not a duplicate:** [label-filtered dedup returned nothing]
|
||||
@@ -0,0 +1,126 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Round-Trip Sweep agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is the verbatim fallback when the
|
||||
Logfire managed variable `gh_aw_pydantic_ai_roundtrip_sweep_prompt` is unset
|
||||
or unreachable. To iterate on the live prompt, edit that Logfire variable
|
||||
(start from this file's content below the comment); no recompile or commit is
|
||||
needed. Keep this file in sync as the reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Round-Trip Sweep
|
||||
|
||||
## Objective
|
||||
|
||||
Find one concrete **state-loss bug across a serialize → deserialize
|
||||
boundary** — the highest-density reproducible cluster in this repo. Pick
|
||||
**one** boundary per run and audit it deeply:
|
||||
|
||||
- `ModelMessagesTypeAdapter` / `to_jsonable_python` ↔ `ModelMessage` round-trip.
|
||||
- `Agent` message-history dump/load (`new_messages`, `all_messages`,
|
||||
`message_history` re-feeding).
|
||||
- AG-UI adapter and Vercel AI adapter request/response conversion.
|
||||
- Temporal / durable-exec serialization (`value_to_type`, activity payloads).
|
||||
- Deferred-tool / tool-approval round-trip across a run boundary.
|
||||
|
||||
## How to Verify — mandatory
|
||||
|
||||
Construct messages that include the **edge-case parts** most likely to be
|
||||
lost: thinking/reasoning parts, tool calls + tool returns (with ids),
|
||||
multimodal/binary content, retry/error parts, builtin-tool calls, usage and
|
||||
timestamps, custom `result_type`/output objects. Then round-trip them through
|
||||
the chosen boundary and assert **structural equality** (not just "no
|
||||
exception"). Write this as a **new** minimal test; do not run and report the
|
||||
existing suite. The bug must be one you triggered and observed.
|
||||
|
||||
## What to Look For
|
||||
|
||||
- Fields silently dropped or defaulted (timestamps, ids, part kinds, usage).
|
||||
- Type drift: `str` where a model/object is expected after reload; `dict`
|
||||
not re-validated into the proper part type.
|
||||
- Ordering changes (tool call/return pairing broken after reload).
|
||||
- Asymmetric adapters (encode then decode ≠ identity).
|
||||
- Re-fed `message_history` changing run behavior vs the original run.
|
||||
|
||||
## What to Skip
|
||||
|
||||
- Speculation without a failing reproduction.
|
||||
- By-design lossy fields explicitly documented as such.
|
||||
- Behavior already tracked by an open issue or fixed by an open PR — **search both first**.
|
||||
|
||||
## Deduplication — mandatory BEFORE filing an issue
|
||||
|
||||
The gap may already be tracked by an open **issue** or already fixed by an
|
||||
open **PR** — check both. List them through the proxied `gh` CLI and filter
|
||||
locally — the `/search/issues` endpoint is blocked by the firewall proxy and
|
||||
there are no `mcp__github__*` tools.
|
||||
|
||||
**(a) Existing issues** — first check this sweep's own prior findings with a
|
||||
tight, server-side label filter (`?labels=` on the issue-list endpoint is
|
||||
allowed even though `/search/issues` is not):
|
||||
|
||||
```
|
||||
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=roundtrip-sweep&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title}'
|
||||
```
|
||||
|
||||
Only if that is inconclusive, widen to a full open-issue scan and grep locally
|
||||
for "round-trip", "serialize", and the boundary/function you investigated:
|
||||
|
||||
```
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
**(b) Existing PRs** — a fix may already be open (and even approved). List
|
||||
open PRs and scan for one touching the failing symbol or file:
|
||||
|
||||
```
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/pulls?state=open&per_page=100' \
|
||||
--jq '.[] | {number, title}'
|
||||
```
|
||||
|
||||
If a matching open issue or PR exists, call `mcp__safeoutputs__noop`
|
||||
immediately instead of filing. If a PR looks related but you cannot confirm it
|
||||
covers this exact gap, still file but fill in the optional **`Possibly
|
||||
addressed by #<N>`** row at the top of the body template (see Issue Format),
|
||||
linking that PR.
|
||||
|
||||
## Sandbox notes
|
||||
|
||||
- Read files in large ranges (500+ lines per call). Do NOT read in 30–80 line chunks.
|
||||
- Use the native `Grep` and `Glob` tools for codebase search.
|
||||
|
||||
## Quality Gate — When to Noop
|
||||
|
||||
`mcp__safeoutputs__noop` is the expected outcome most runs. Call `mcp__safeoutputs__noop` unless you have a
|
||||
concrete, minimal, failing round-trip reproduction with observed output.
|
||||
|
||||
## Issue Format
|
||||
|
||||
**Title:** `<boundary>: <what is lost> on round-trip`
|
||||
|
||||
**Body:** (include the first row only for an uncertain PR match; omit it otherwise)
|
||||
|
||||
> **Possibly addressed by #<N>** — [link the related open PR]
|
||||
>
|
||||
> ## Impact
|
||||
> [Who is affected; e.g. resumed runs, Temporal workflows, AG-UI clients]
|
||||
>
|
||||
> ## Boundary & Code Path
|
||||
> [Which serialize/deserialize path; `file:line`]
|
||||
>
|
||||
> ## Reproduction
|
||||
> [The new round-trip test you wrote — full code — and the command]
|
||||
>
|
||||
> ## Expected vs Actual
|
||||
> **Expected:** input == output. **Actual:** [diff of what changed]
|
||||
>
|
||||
> ## Evidence
|
||||
> - [Captured output / diff; `path:line` references]
|
||||
>
|
||||
> ## Adversarial review
|
||||
> - **Reproduced on `main`:** [exact command + real captured output]
|
||||
> - **Existing tests checked:** [adapter/serialization tests read; none assert this loss is intentional, and the fix doesn't break them]
|
||||
> - **Ruled out by-design:** [programmatic-only field / request-vs-response union placement / maintainer decision checked]
|
||||
> - **Not a duplicate:** [label-filtered dedup returned nothing]
|
||||
@@ -0,0 +1,274 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Stale Issues Finder agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is used verbatim only as the fallback
|
||||
when the Logfire managed variable `gh_aw_pydantic_ai_stale_issues_finder_prompt`
|
||||
is unset or unreachable. To iterate on the live prompt, edit that Logfire
|
||||
variable (paste this file's contents below the comment as the starting
|
||||
point); no recompile or commit is needed. Keep this file in sync as the
|
||||
reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Stale Issues Finder
|
||||
|
||||
## Objective
|
||||
|
||||
Review the entire open-issues corpus every run and identify issues that are
|
||||
**very likely already resolved, no longer relevant, duplicates, or tied to
|
||||
deprecated/removed features**. Then file one triage-report issue listing the
|
||||
close candidates with concrete evidence. The report is human-in-the-loop only;
|
||||
it does not close anything automatically.
|
||||
|
||||
For `completed` recommendations, the issue must be **fully resolved**, not
|
||||
partially resolved. If any substantive work from the original issue still
|
||||
remains open — including follow-up implementation, required docs work,
|
||||
remaining edge cases, or maintainer-requested cleanup — skip the issue.
|
||||
|
||||
**Do NOT add labels, comment on issues, or close issues.** Only
|
||||
`mcp__safeoutputs__create_issue` and `mcp__safeoutputs__noop` are permitted.
|
||||
|
||||
**The bar is high: only include issues where you are confident.** False
|
||||
positives waste maintainer time and erode trust. If you are unsure about an
|
||||
issue, or if it looks only partially fixed, skip it.
|
||||
|
||||
---
|
||||
|
||||
### Data Gathering
|
||||
|
||||
0. **Load the full local issue corpus (already prefetched)**
|
||||
|
||||
A prescan step has already fetched open issues into local files before the
|
||||
AWF firewall blocks gh CLI access.
|
||||
|
||||
Available local inputs:
|
||||
- `/tmp/gh-aw/agent/open-issues.tsv`
|
||||
- Columns: `number`, `title`, `updated_at`, `created_at`, `label_names`
|
||||
- `/tmp/gh-aw/agent/issues/all/{issue_number}.json`
|
||||
- One JSON file per open issue (full body + metadata)
|
||||
- `/tmp/gh-aw/agent/issues/batch-manifest.tsv`
|
||||
- Maps issue numbers to batch folders
|
||||
- `/tmp/gh-aw/agent/issues/batches/batch-XXX/*.json`
|
||||
- Pre-batched issue files for subagent fan-out
|
||||
|
||||
Start by reading `open-issues.tsv` and `batch-manifest.tsv`.
|
||||
|
||||
1. **Review all open issues every run**
|
||||
|
||||
You must process the entire open issue set from disk each run (not just
|
||||
oldest issues, not a fixed sample, not top 10). Age is a prioritization
|
||||
hint, not a scope limiter.
|
||||
|
||||
2. **Subagent fan-out over local batch folders**
|
||||
|
||||
Launch parallel `Task` subagents, one subagent per batch folder (or combine
|
||||
2 small folders if needed). Use local files only for first-pass triage.
|
||||
|
||||
Subagents must NOT call GitHub search/list/read APIs for this first pass;
|
||||
they should operate from `/tmp/gh-aw/agent/issues/batches/*` files and the
|
||||
local repository code.
|
||||
|
||||
Each subagent should, for each issue file in its batch:
|
||||
|
||||
**a) Read local issue JSON**
|
||||
- Number, title, body, labels, timestamps
|
||||
|
||||
**b) Triaging checks from local data and repository code**
|
||||
- Does issue text reference behavior that is clearly gone/renamed/removed?
|
||||
- Does issue describe behavior that is now implemented in current code?
|
||||
- Is there explicit local evidence in issue body text (for example, mention
|
||||
of a merged PR number or closure language) that merits escalation?
|
||||
- Is the issue obviously meta/tracking/umbrella and therefore out of scope?
|
||||
|
||||
**c) Return structured JSON for the whole batch**
|
||||
- Do not write files. `Task` subagents are read-only.
|
||||
- Return one compact JSON object for the batch using this schema:
|
||||
```json
|
||||
{
|
||||
"batch_name": "batch-001",
|
||||
"summary": {
|
||||
"candidate_count": 3,
|
||||
"skip_count": 21,
|
||||
"needs_comment_check_count": 1
|
||||
},
|
||||
"verdicts": [
|
||||
{
|
||||
"issue": 1234,
|
||||
"verdict": "CANDIDATE",
|
||||
"confidence": "high",
|
||||
"reason": "short reason",
|
||||
"evidence": ["bullet 1", "bullet 2"],
|
||||
"recommended_close_reason": "completed",
|
||||
"linked_pr_numbers": [1111, 2222]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**d) Keep the response compact**
|
||||
- Include all non-`SKIP` verdicts in full
|
||||
- For `SKIP` issues, include only enough entries for accurate coverage
|
||||
accounting and dedupe-free processing
|
||||
- Output valid JSON only; no prose before or after
|
||||
|
||||
3. **Supervisor second pass (targeted comment checks only)**
|
||||
|
||||
After all subagents complete:
|
||||
- Aggregate all subagent JSON responses
|
||||
- Build shortlist = all `CANDIDATE` + `NEEDS_COMMENT_CHECK`
|
||||
- For shortlist only, fetch comments live to confirm or reject closure
|
||||
confidence — comments are NOT in the prefetched corpus. Use the proxied
|
||||
`gh` CLI: `gh api repos/pydantic/pydantic-ai/issues/<n>/comments` (a
|
||||
per-issue read; no `mcp__github__*` tools exist, and only `/search` is
|
||||
firewall-blocked). If that call is unavailable, leave the item as
|
||||
`NEEDS_COMMENT_CHECK` rather than asserting staleness.
|
||||
- If comment evidence weakens confidence, downgrade to `SKIP`
|
||||
|
||||
4. **Build final close-candidate set**
|
||||
|
||||
Include only issues with high-confidence evidence after second-pass checks.
|
||||
Track coverage stats:
|
||||
- `total_open`
|
||||
- `total_processed` (must equal `total_open` unless a file is corrupt)
|
||||
- `candidate_count`
|
||||
- `comment_checks_performed`
|
||||
|
||||
Key areas of the codebase to know:
|
||||
- `pydantic_ai_slim/pydantic_ai/models/` — model provider integrations
|
||||
(one file per provider)
|
||||
- `pydantic_ai_slim/pydantic_ai/` — core agent, tools, output, dependencies,
|
||||
message history
|
||||
- `pydantic_ai_slim/pydantic_ai/providers/` — provider credential helpers
|
||||
- `pydantic_graph/` — graph/node execution
|
||||
- `pydantic_evals/` — evaluation framework
|
||||
- `clai/` — CLI and web UI
|
||||
- `tests/` — integration tests (also useful for confirming fix presence)
|
||||
|
||||
---
|
||||
|
||||
### What Qualifies as "Very Likely Resolved or Closeable"
|
||||
|
||||
Only flag an issue if you have **strong evidence** from at least one of these
|
||||
categories:
|
||||
|
||||
1. **Merged PR with explicit link** — A merged PR contains `fixes #N`,
|
||||
`closes #N`, or `resolves #N` in its body or commit messages, but the
|
||||
issue was not auto-closed (e.g., PR targeted a non-default branch)
|
||||
|
||||
2. **Code evidence** — The specific bug, missing feature, or requested change
|
||||
described in the issue is verifiably addressed in the current codebase. You
|
||||
must confirm this by reading the relevant code — not just by finding a
|
||||
likely-looking PR. Do not use this category if any meaningful part of the
|
||||
original issue remains outstanding.
|
||||
|
||||
3. **Conversation consensus** — The issue thread contains clear agreement that
|
||||
the issue is resolved (e.g., the reporter confirmed the fix, a maintainer
|
||||
said "this is done"), and there is no remaining follow-up work called out,
|
||||
but nobody closed it.
|
||||
|
||||
4. **Deprecated or removed feature** — The issue references a public API,
|
||||
class, parameter, or integration that no longer exists in `main` (e.g., a
|
||||
provider that was renamed or dropped, a kwarg that was removed). Confirm by
|
||||
reading the codebase or changelog.
|
||||
|
||||
5. **Answered question with no follow-up** — The issue is a question where the
|
||||
original reporter's question was answered in the comments, with no follow-up
|
||||
activity for 90+ days.
|
||||
|
||||
---
|
||||
|
||||
### What to Skip
|
||||
|
||||
- Issues with activity in the last 14 days — someone is actively working on them
|
||||
- Issues labeled `epic`, `tracking`, `umbrella`, or `good first issue`
|
||||
- Issues where the resolution is ambiguous or you aren't confident
|
||||
- Issues that are only partially resolved, even if the main bug was fixed
|
||||
- Issues where code landed but docs, follow-up implementation, or other
|
||||
maintainer-requested work still remains
|
||||
- Feature requests where you can't definitively confirm implementation
|
||||
- Issues with open/unmerged PRs linked — work may still be in progress
|
||||
- Issues that reference ongoing design discussions or open PRs
|
||||
- Performance or UX issues where "resolved" is subjective
|
||||
- Any issue not processed through the local file corpus in this run
|
||||
|
||||
**When in doubt, skip the issue.** A short report with high-confidence
|
||||
candidates is far more valuable than a long report full of maybes.
|
||||
|
||||
---
|
||||
|
||||
### Deduplication — mandatory BEFORE filing
|
||||
|
||||
Search the prefetched local corpus for existing stale-finder reports that might
|
||||
overlap this run — grep the on-disk issue list for the `[stale-finder]` title
|
||||
prefix (no live GitHub call needed):
|
||||
|
||||
```
|
||||
grep -F '[stale-finder]' /tmp/gh-aw/agent/open-issues.tsv
|
||||
```
|
||||
|
||||
Do not skip this run just because a previous report exists. You are reviewing
|
||||
the full corpus every run. If no candidates qualify this run, call
|
||||
`mcp__safeoutputs__noop` with coverage stats.
|
||||
|
||||
---
|
||||
|
||||
### Sandbox notes
|
||||
|
||||
- Read files in large ranges (500+ lines per call). Do NOT read 30–80 lines at
|
||||
a time.
|
||||
- Use the native `Grep` and `Glob` tools for codebase search.
|
||||
- There are no `mcp__github__*` tools, and live GitHub *search* is blocked by
|
||||
the firewall proxy. Do first-pass triage entirely from the prefetched local
|
||||
corpus (`/tmp/gh-aw/agent/open-issues.tsv` and `issues/all/{number}.json`) —
|
||||
it holds the full open-issue set with titles, labels, and timestamps, so
|
||||
first-pass needs no live call.
|
||||
- The corpus does NOT include comments. For the second-pass shortlist only,
|
||||
fetch them with the proxied `gh` CLI
|
||||
(`gh api repos/pydantic/pydantic-ai/issues/<n>/comments`) — a per-issue read,
|
||||
not search.
|
||||
|
||||
---
|
||||
|
||||
### Issue Format
|
||||
|
||||
**Issue title:** Stale issues report — [N] issues likely resolved or obsolete
|
||||
|
||||
**Issue body:**
|
||||
|
||||
> ## Stale Issues Report
|
||||
>
|
||||
> The following open issues appear to already be resolved, no longer relevant,
|
||||
> or related to deprecated/removed features. Each entry includes the evidence
|
||||
> supporting closure.
|
||||
>
|
||||
> Reviewed {total_processed} of {total_open} open issues this run.
|
||||
> Performed {comment_checks_performed} targeted comment checks.
|
||||
> ({total_open} total open issues).
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> ### 1. #{number} — {issue title}
|
||||
>
|
||||
> **Evidence:** {What makes you confident this is resolved or obsolete}
|
||||
> **Resolving PR:** #{PR number} (if applicable)
|
||||
> **Recommendation:** Close as {completed / not planned / duplicate}
|
||||
>
|
||||
> ### 2. #{number} — {issue title}
|
||||
> ...
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> ## Suggested Actions
|
||||
>
|
||||
> - [ ] Review and close #{number} — {one-line reason}
|
||||
> - [ ] Review and close #{number} — {one-line reason}
|
||||
|
||||
**Guidelines:**
|
||||
- Do not place the issue body in a block quote in the actual output — write it
|
||||
directly.
|
||||
- Do not cap to 10. Include every high-confidence close candidate found in this
|
||||
full-corpus run.
|
||||
- Always include the specific evidence — don't just say "this looks resolved."
|
||||
- Link to the resolving PR, commit, or code line when possible.
|
||||
- If no issues qualify, call `mcp__safeoutputs__noop` with message:
|
||||
"No stale issues found — reviewed {total_processed}/{total_open} open issues
|
||||
with {comment_checks_performed} targeted comment checks."
|
||||
@@ -0,0 +1,114 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI Streaming Resilience Sweep agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is the verbatim fallback when the
|
||||
Logfire managed variable
|
||||
`gh_aw_pydantic_ai_streaming_resilience_sweep_prompt` is unset or
|
||||
unreachable. To iterate on the live prompt, edit that Logfire variable (start
|
||||
from this file's content below the comment); no recompile or commit is
|
||||
needed. Keep this file in sync as the reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI Streaming Resilience Sweep
|
||||
|
||||
The streaming/agent-loop code is in `pydantic_ai_slim/pydantic_ai/` (`_agent_graph`, `result`, `messages`,
|
||||
`run`) and the AG-UI / Vercel adapters.
|
||||
|
||||
## Objective
|
||||
|
||||
Find one concrete bug in the **streaming state machine**. Streaming is the
|
||||
largest topical cluster and harbors hard-to-spot ordering and lifecycle bugs.
|
||||
Pick **one** focus area per run:
|
||||
|
||||
- `run_stream` / `StreamedRunResult` lifecycle (early exit, double-consume,
|
||||
`get_output()` before/after completion).
|
||||
- `agent.iter` / `_next_node` graph node ordering and assertions.
|
||||
- `event_stream_handler` event sequence (start → deltas → end), including for
|
||||
tool calls and thinking parts.
|
||||
- Partial / aborted / cancelled streams (network drop, `break`, timeout) and
|
||||
cleanup.
|
||||
- AG-UI / Vercel adapter event ordering and terminal events.
|
||||
- Usage / final message assembly when the stream ends or errors mid-way.
|
||||
|
||||
## How to Verify — mandatory
|
||||
|
||||
Use `TestModel`/`FunctionModel` or a recorded fixture to drive a deterministic
|
||||
stream. Write a **new** minimal test asserting the **event/None sequence and
|
||||
final message** — e.g. no deltas after the final part, tool-call/return
|
||||
pairing intact, `usage` populated on completion, no `StopAsyncIteration`/
|
||||
assertion leakage on early exit. Do not run and report the existing suite.
|
||||
|
||||
## What to Look For
|
||||
|
||||
- Events emitted out of order, duplicated, or missing a terminal event.
|
||||
- State leaking between consumption attempts; `get_output()` returning stale
|
||||
or partial data.
|
||||
- Exceptions/asserts surfacing to the user on normal early termination.
|
||||
- Final `ModelResponse`/usage missing parts that were streamed.
|
||||
- Cancellation not cleaning up the underlying provider stream.
|
||||
|
||||
## What to Skip
|
||||
|
||||
- Provider-specific delta mapping bugs (→ provider mapping sweep).
|
||||
- Speculation without a deterministic failing reproduction.
|
||||
- Behavior already tracked by an open issue — **search issues first**.
|
||||
|
||||
## Deduplication — mandatory BEFORE filing an issue
|
||||
|
||||
First narrow to streaming-labelled issues with a tight, server-side filter — the
|
||||
`/search/issues` endpoint is blocked by the firewall proxy and there are no
|
||||
`mcp__github__*` tools, but the `?labels=` filter on the issue-list endpoint is
|
||||
allowed. This covers both prior `[streaming-resilience-sweep]` findings and
|
||||
human-filed streaming issues:
|
||||
|
||||
```
|
||||
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=streaming&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title}'
|
||||
```
|
||||
|
||||
Only if that is inconclusive, widen to a full open-issue scan and grep locally
|
||||
for "stream_output" / "stream_text":
|
||||
|
||||
```
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
If a matching issue exists, call `mcp__safeoutputs__noop` immediately.
|
||||
|
||||
## Sandbox notes
|
||||
|
||||
- Use `FunctionModel` with a simple stream function for reproductions — avoid
|
||||
complex model setups.
|
||||
|
||||
## Quality Gate — When to Noop
|
||||
|
||||
`mcp__safeoutputs__noop` is the expected outcome most runs. Only file with a deterministic,
|
||||
minimal, failing streaming reproduction and captured event trace.
|
||||
|
||||
## Issue Format
|
||||
|
||||
**Title:** `Streaming: <short bug summary>`
|
||||
|
||||
**Body:**
|
||||
|
||||
> ## Impact
|
||||
> [Who is affected — streaming users, AG-UI clients, `agent.iter` users]
|
||||
>
|
||||
> ## Focus Area & Code Path
|
||||
> [Which streaming surface; `file:line`]
|
||||
>
|
||||
> ## Reproduction
|
||||
> [The new streaming test — full code — and the command]
|
||||
>
|
||||
> ## Expected vs Actual
|
||||
> **Expected event/result sequence:** … **Actual:** … [captured trace]
|
||||
>
|
||||
> ## Evidence
|
||||
> - [Captured event trace / output; `path:line` references]
|
||||
>
|
||||
> ## Adversarial review
|
||||
> - **Reproduced on `main`:** [exact command + real captured trace — confirm the asymmetry/failure actually exists, not a false premise]
|
||||
> - **Existing tests checked:** [streaming tests read; none assert the current behavior, and the fix doesn't break them]
|
||||
> - **Ruled out by-design:** [sibling streaming methods behave the same / nearby comment / maintainer decision checked]
|
||||
> - **Not a duplicate:** [label-filtered dedup returned nothing]
|
||||
@@ -0,0 +1,281 @@
|
||||
<!--
|
||||
Default/seed prompt for the Pydantic AI UI Security Review agent.
|
||||
|
||||
This file is the COMPLETE prompt. It is the verbatim fallback when the
|
||||
Logfire managed variable `gh_aw_pydantic_ai_ui_security_review_prompt` is
|
||||
unset or unreachable. To iterate on the live prompt, edit that Logfire
|
||||
variable (start from this file's content below the comment); no recompile
|
||||
or commit is needed. Keep this file in sync as the reviewed default.
|
||||
-->
|
||||
|
||||
# Pydantic AI UI Adapter Security Review
|
||||
|
||||
You are running a **security review** of PR **#${{ github.event.pull_request.number }}**
|
||||
in [${{ github.repository }}](https://github.com/${{ github.repository }}) —
|
||||
*${{ github.event.pull_request.title }}*.
|
||||
|
||||
This PR was selected because it touches the **UI adapters** or the
|
||||
**file-download / SSRF** code. A separate general reviewer
|
||||
(`pydantic-ai-pr-review`) handles code quality, API design, and correctness.
|
||||
**You are not that reviewer.** Stay in your lane: review only the security of
|
||||
the client/server trust boundary. Say nothing about style, naming, typing, or
|
||||
test coverage unless it has a concrete security consequence.
|
||||
|
||||
This workflow is **non-voting**. GitHub identifies both this bot and the
|
||||
general `pydantic-ai-pr-review` bot as `github-actions[bot]`, so submitting
|
||||
an `APPROVE` or `REQUEST_CHANGES` verdict here would silently overwrite the
|
||||
other bot's verdict on the merge gate. Your review submission is always
|
||||
`COMMENT`-type (see Step 5); the security outcome lives in the body header
|
||||
and the inline findings. The merge gate stays with `pydantic-ai-pr-review`
|
||||
until check-runs support lands in gh-aw.
|
||||
|
||||
## Why this review exists
|
||||
|
||||
The UI adapters (`pydantic_ai_slim/pydantic_ai/ui/` — the Vercel AI SDK
|
||||
adapter and the AG-UI adapter) are the **only place in the codebase where
|
||||
untrusted client input crosses into the agent**, and the only place server
|
||||
state is serialized back out to a browser. Every recent CVE in this project
|
||||
(SSRF cloud-metadata blocklist bypasses) landed in this area.
|
||||
|
||||
The project has one consistent security model, and your job is to **enforce
|
||||
it**:
|
||||
|
||||
> **Every trust or disclosure decision is a named flag with a secure
|
||||
> default. Default-deny. The user opts *in* to trusting client input or
|
||||
> disclosing server state — never *out*.**
|
||||
|
||||
Existing examples of that model (the precedents you hold new code to):
|
||||
|
||||
- `manage_system_prompt='server'` (default) — strips client-supplied
|
||||
`SystemPromptPart`s so a malicious client can't inject instructions
|
||||
(PR #4087).
|
||||
- `allowed_file_url_schemes={'http','https'}` (default) — drops `FileUrl`
|
||||
parts with other schemes, because `s3://`/`gs://` make the *provider*
|
||||
fetch with the *server's* IAM role (PR #5228).
|
||||
- client-submitted `FileUrl.force_download='allow-local'` is reset to
|
||||
`False` — it opts a URL out of the SSRF private-IP block (PR #5571).
|
||||
- `allow_uploaded_files` (off by default) is the inbound security gate: it
|
||||
drops client-submitted `UploadedFile` references unless opted in, since the
|
||||
provider fetches them with the server's credentials. AG-UI's
|
||||
`preserve_file_data` is now representation-only — an opt-in for round-tripping
|
||||
the file sidecar activity messages, not a trust decision (PRs #3971, #5255).
|
||||
- `instructions` was removed from the Vercel `UIMessage.metadata` dump
|
||||
entirely — never sent to the client, never read back (PR #5279).
|
||||
|
||||
## Your mandate — two directions
|
||||
|
||||
Audit the diff in **both** directions. They are different threat models;
|
||||
review them separately.
|
||||
|
||||
### Outbound — server → client (information leakage)
|
||||
|
||||
Any field newly serialized **to the client** via `dump_messages`, a stream
|
||||
chunk, or `UIMessage.metadata` / AG-UI events.
|
||||
|
||||
**The risk:** server-internal information reaching a browser. Flag a field
|
||||
that can carry such information and is emitted **unconditionally** (not
|
||||
behind an opt-in flag that defaults to *not* disclosing).
|
||||
|
||||
Field sensitivity reference:
|
||||
|
||||
- **Safe to emit:** `timestamp`.
|
||||
- **Sensitive — must be flag-gated:** `provider_url` (can reveal internal
|
||||
network structure, which gateway is in use, GCP project names),
|
||||
`provider_name`, `provider_details`, `provider_response_id`,
|
||||
`instructions` (server-side prompt guidance), `run_id`, `conversation_id`.
|
||||
- Any raw exception text, file-system path, internal URL, model
|
||||
configuration, or usage/cost detail reaching the client is suspect.
|
||||
|
||||
### Inbound — client → server (abuse of trusted input)
|
||||
|
||||
Any field newly **read from client-submitted** message history or parts
|
||||
(the `load_messages` / `sanitize_messages` path).
|
||||
|
||||
**The risk:** a forged value changing server behavior or granting access.
|
||||
Flag a client-controlled field that is consumed without validation.
|
||||
|
||||
Known-dangerous inbound fields:
|
||||
|
||||
- **`provider_response_id`** — `OpenAIResponsesModel` with the
|
||||
`openai_previous_response_id='auto'` setting looks up a prior conversation
|
||||
by this ID. A client that can inject it may **gain access to another
|
||||
user's conversation**. This is the highest-severity inbound vector.
|
||||
- **`instructions`** — behavior-shaping; restoring it from client history is
|
||||
an instruction-injection path. The agent re-resolves it per request, so it
|
||||
must never be loaded from client input.
|
||||
- **`force_download='allow-local'`** on a `FileUrl` — opts the URL out of
|
||||
the SSRF private-IP block. Must be reset on client-submitted parts.
|
||||
- **Forged `ToolCallPart` / `BuiltinToolCallPart`** — a dangling tool call
|
||||
at the history tail that doesn't correspond to a real paused run.
|
||||
- **Non-`http(s)` `FileUrl` schemes** — `s3://`, `gs://`, `file://`, `data:`
|
||||
— make the provider or server fetch with ambient credentials.
|
||||
- **Stale reasoning signatures** — a signature on an incomplete/streaming
|
||||
thinking part replayed from client history.
|
||||
- **`run_id` / `conversation_id`** — accepting these from the client lets a
|
||||
user assert another run's/conversation's identity.
|
||||
|
||||
### The chokepoint rule
|
||||
|
||||
Inbound sanitization belongs in **`UIAdapter.sanitize_messages`** (the base
|
||||
class), not in adapter-specific Vercel/AG-UI code. `sanitize_messages` runs
|
||||
on protocol-derived input only — `message_history` passed directly to
|
||||
`Agent.run` is server-authored and trusted by design. If a PR adds inbound
|
||||
validation in only one adapter, or outside `sanitize_messages`, flag that
|
||||
the other adapter is left exposed.
|
||||
|
||||
## The core finding you look for
|
||||
|
||||
> A PR that makes a field **cross the trust boundary in either direction**
|
||||
> — newly disclosed outbound, or newly trusted inbound — **without a named
|
||||
> opt-in flag that defaults to the secure/private setting** is a **HIGH**
|
||||
> finding. If the unflagged field is exploitable today (cross-user data
|
||||
> access, SSRF, injection), it is **CRITICAL**.
|
||||
|
||||
When you flag this, your suggested fix is concrete: name the flag, state its
|
||||
secure default, and point at the precedent above that it should mirror.
|
||||
|
||||
## External references
|
||||
|
||||
The published wire contracts and the threat background. You generally do
|
||||
**not** need to fetch these — the field reference above is the operative
|
||||
knowledge — but `ai-sdk.dev` and `docs.ag-ui.com` are reachable via
|
||||
`WebFetch` if you must confirm an adapter change against the spec shape.
|
||||
|
||||
- Vercel AI SDK stream protocol — <https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol#data-stream-protocol>
|
||||
- Vercel AI SDK `UIMessage.metadata` — <https://ai-sdk.dev/docs/ai-sdk-ui/message-metadata>
|
||||
- AG-UI message types — <https://docs.ag-ui.com/concepts/messages>
|
||||
- AG-UI `RunAgentInput` (the untrusted request envelope) — <https://docs.ag-ui.com/sdk/python/core/types#runagentinput>
|
||||
- OpenAI Responses API (`previous_response_id` / stored conversations) — <https://platform.openai.com/docs/api-reference/responses>
|
||||
- SSRF advisories — <https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-cg7w-rg45-pc59>, <https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-cqp8-fcvh-x7r3>
|
||||
|
||||
## Security-specific rigor
|
||||
|
||||
- If you claim something is exploitable, show the **attack**: which field a
|
||||
client controls, the exact call path from `load_messages` /
|
||||
`sanitize_messages` / `dump_messages` to the sink, and the concrete
|
||||
consequence. No attack path → no finding.
|
||||
- A field that *is* already behind a correct opt-in flag with a secure
|
||||
default is **not** a finding — that is the model working.
|
||||
- The server-side `message_history` path (passed directly to `Agent.run`)
|
||||
is trusted by design. Do not flag it.
|
||||
- Before posting, re-read each finding as a skeptical maintainer who knows
|
||||
this trust model.
|
||||
|
||||
## Review process
|
||||
|
||||
### Step 1 — Orient
|
||||
|
||||
1. Read `pr-details.json`, `pr-size.txt`, `pr-comments.txt`, and
|
||||
`related-issues.txt`.
|
||||
2. Read the `ui/` `AGENTS.md` / `CLAUDE.md` excerpts in `agents-md.txt`, and
|
||||
`docs/ui/overview.md` in the workspace — its "adapter trust model"
|
||||
section is the canonical statement of what is trusted vs. sanitized.
|
||||
3. Skim `review-comments.txt` for prior threads (note the most recent review
|
||||
from this bot — you compare verdicts at the end).
|
||||
4. From `changed-files.txt`, identify which changed files touch the boundary:
|
||||
`dump_messages` / stream emitters (outbound), `load_messages` /
|
||||
`sanitize_messages` / request schemas (inbound), `_ssrf.py` /
|
||||
`web_fetch.py` / `FileUrl` (download surface).
|
||||
|
||||
### Step 2 — Pick a strategy from PR size
|
||||
|
||||
Read `pr-size.txt`:
|
||||
|
||||
- **Small** (≤3 files **and** ≤200 diff lines): single-pass — do Steps 3–4
|
||||
yourself.
|
||||
- **Larger**: fan out **2 sub-agents by direction** (Step 3).
|
||||
|
||||
### Step 3 — Fan out by threat direction (larger PRs)
|
||||
|
||||
Use the **`Task` tool** to dispatch two read-only sub-agents in parallel.
|
||||
Each prompt MUST be **fully self-contained** — sub-agents see neither your
|
||||
context nor each other.
|
||||
|
||||
- **Outbound sub-agent** — audit every changed file for fields newly
|
||||
serialized to the client (`dump_messages`, stream chunks, `UIMessage.metadata`,
|
||||
AG-UI events). Apply the outbound field-sensitivity reference.
|
||||
- **Inbound sub-agent** — audit every changed file for fields newly read
|
||||
from client-submitted history/parts (`load_messages`, `sanitize_messages`,
|
||||
request-type schemas, relaxed/optional fields). Apply the inbound
|
||||
known-dangerous list and the chokepoint rule.
|
||||
|
||||
Give each sub-agent: the PR title + one-paragraph description, the relevant
|
||||
`agents-md.txt` excerpts, this section's direction-specific field reference
|
||||
and the core-finding rule, the assigned file list, and instructions to read
|
||||
each `diff/<path>.diff` plus the full file from the workspace, and to check
|
||||
`review-comments.txt` for existing threads. **Wait for both** before Step 4.
|
||||
|
||||
### Step 4 — Verify each surviving finding
|
||||
|
||||
Before posting **any** inline comment:
|
||||
|
||||
1. **Trace the path.** Open the full file via `Read`. Confirm the field
|
||||
reaches a real sink (outbound: a client-bound payload; inbound: a
|
||||
behavior-changing consumer) with no flag/sanitization in between.
|
||||
2. **State the attack.** Name the client-controlled input and the concrete
|
||||
consequence. If you cannot, drop the finding.
|
||||
3. **Check for an existing flag.** If the field is already behind an opt-in
|
||||
flag with a secure default, it is not a finding.
|
||||
4. **Check existing threads** for the same `path:line`.
|
||||
5. **Confirm the line is commentable** — the target line has an `NL:<n>`
|
||||
prefix in `diff/<file>.diff`. If not, move the finding to the review body.
|
||||
|
||||
### Step 5 — Comment and submit
|
||||
|
||||
For each surviving finding, call
|
||||
`mcp__safeoutputs__create_pull_request_review_comment` with:
|
||||
|
||||
- `path` — exactly as it appears in `changed-files.txt`.
|
||||
- `line` — the `NL:` line number from the diff.
|
||||
- `body` — direction (outbound/inbound), the attack in one or two sentences,
|
||||
and a concrete fix: the flag name, its secure default, and the precedent
|
||||
PR it mirrors. Use a ` ```suggestion ` block only when you can give a real
|
||||
replacement. One issue per comment.
|
||||
|
||||
Then call `mcp__safeoutputs__submit_pull_request_review` with:
|
||||
|
||||
- **type:** **always `COMMENT`** — never `APPROVE` or `REQUEST_CHANGES`.
|
||||
This workflow is informational (see intro); the general
|
||||
`pydantic-ai-pr-review` workflow owns the merge-gate verdict, and both
|
||||
bots post as `github-actions[bot]`, so a verdict from here would
|
||||
overwrite that one.
|
||||
- **body:** open with a single-line security-outcome header so a reviewer
|
||||
scanning the PR sees the result at a glance:
|
||||
- no findings → `SECURITY: PASS`
|
||||
- any HIGH or CRITICAL surviving → `SECURITY: REQUEST_CHANGES (N high, M critical)`
|
||||
After the header, include only cross-cutting concerns that can't be
|
||||
inlined (e.g. "inbound validation added to the Vercel adapter only —
|
||||
AG-UI inherits nothing"). Do not summarize the PR or restate inline
|
||||
findings.
|
||||
|
||||
**Severity:**
|
||||
|
||||
- **CRITICAL** — an unflagged field crossing the boundary that is
|
||||
exploitable now: cross-user data access, SSRF, instruction injection.
|
||||
- **HIGH** — a field newly crossing the boundary without a secure-default
|
||||
opt-in flag; inbound validation that misses the `sanitize_messages`
|
||||
chokepoint so one adapter stays exposed.
|
||||
- **MEDIUM** — a real but bounded weakening (e.g. a sensitive field gated by
|
||||
a flag whose *default* is the insecure setting).
|
||||
- **LOW** — defense-in-depth gap with no concrete attack path.
|
||||
|
||||
HIGH and CRITICAL drive the `SECURITY: REQUEST_CHANGES` body header. The
|
||||
review submission itself is always `COMMENT`-type — see above.
|
||||
|
||||
**Skip if redundant:** if you have zero new findings and the most recent
|
||||
review from this bot (in `review-comments.txt`) was also `SECURITY: PASS`,
|
||||
call `mcp__safeoutputs__noop` with a short reason instead of a redundant
|
||||
review.
|
||||
|
||||
## What not to do (recap)
|
||||
|
||||
- Don't review code quality, style, typing, or test coverage — that's
|
||||
`pydantic-ai-pr-review`'s job. Security consequences only.
|
||||
- Don't flag a field that is already behind a correct secure-default flag.
|
||||
- Don't flag the server-side `message_history` path — it is trusted by
|
||||
design.
|
||||
- Don't post a finding without a concrete client-controlled attack path.
|
||||
- Don't comment on lines without an `NL:` prefix in the per-file diff.
|
||||
- Don't write to the workspace — every output is a safe-output call.
|
||||
- Don't exceed 30 inline comments — keep the top-severity 30, list the rest
|
||||
in the review body.
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
# Shared repository context for Pydantic AI gh-aw prompts.
|
||||
# gh-aw imports this file; the markdown below (after the closing ---) is
|
||||
# appended to the agent's task prompt at runtime via {{#runtime-import}}.
|
||||
---
|
||||
|
||||
You are working in the **Pydantic AI** repository
|
||||
([ai.pydantic.dev](https://ai.pydantic.dev/)), a provider-agnostic GenAI agent
|
||||
framework for Python. It is a `uv` workspace: `pydantic_ai_slim/` (the agent
|
||||
framework), `pydantic_graph/`, `pydantic_evals/`, `clai/`, with tests in
|
||||
`tests/`.
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
# Shared pre-gathered context and review-thread handling for gh-aw review prompts.
|
||||
# gh-aw imports this file; the markdown below (after the closing ---) is
|
||||
# appended to the agent's task prompt at runtime via {{#runtime-import}}.
|
||||
---
|
||||
|
||||
## Pre-gathered context
|
||||
|
||||
A pre-agent step ran `scripts/gather-pydantic-ai-review-context.sh` and
|
||||
wrote everything you need to `/tmp/gh-aw/.review-context/`. **Read these
|
||||
files instead of calling the GitHub API.**
|
||||
|
||||
- `pr-details.json` — title, body, author, branches, labels, draft/state.
|
||||
- `pr-size.txt` — `{N} files, {M} diff lines`.
|
||||
- `changed-files.txt` — paths in this PR with `+N -M` change counts and the
|
||||
matching `diff/<path>.diff` filename.
|
||||
- `file-orderings/az.txt`, `file-orderings/za.txt`,
|
||||
`file-orderings/largest.txt` — the same file list in three orderings.
|
||||
- `diff/<path>.diff` — per-file diffs with function context, annotated
|
||||
with `NL:<n>` for new-side and `OL:<n>` for old-side line numbers.
|
||||
**Inline comments require an `NL:` line.**
|
||||
- `pr-comments.txt` — issue-style PR discussion.
|
||||
- `review-comments.txt` — inline review threads with diff hunks and
|
||||
per-thread `RESOLVED` / `UNRESOLVED` / `OUTDATED` state.
|
||||
- `related-issues.txt` — linked issues referenced by the PR body.
|
||||
- `agents-md.txt` — `AGENTS.md` excerpts for directories the PR touches.
|
||||
|
||||
The annotated diffs are the **source of truth** for what changed.
|
||||
|
||||
**If a file is missing** (the pre-agent step may have warned), fall back to
|
||||
`gh pr view` / `gh pr diff` for that piece — but only that piece. Don't
|
||||
re-fetch what is already on disk.
|
||||
|
||||
## Handling existing review threads
|
||||
|
||||
For each thread in `review-comments.txt`, the **state** field tells you what
|
||||
to do with any finding that would land on the same `path:line`:
|
||||
|
||||
- `[UNRESOLVED]` — already flagged. **Do not duplicate.**
|
||||
- `[RESOLVED]` with a reviewer reply (e.g. "intentional", "won't fix") —
|
||||
decision is final. **Do not re-flag.**
|
||||
- `[RESOLVED]` without a reply — author likely fixed it. **Do not re-raise**
|
||||
unless your reading shows the fix introduced a new problem.
|
||||
- `[OUTDATED]` — the code has shifted under the comment. Only re-flag if
|
||||
the issue still applies to the *current* diff.
|
||||
|
||||
When in doubt, do not duplicate. Redundant comments erode trust.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
# Shared evidence and accuracy bar for gh-aw prompts.
|
||||
# gh-aw imports this file; the markdown below (after the closing ---) is
|
||||
# appended to the agent's task prompt at runtime via {{#runtime-import}}.
|
||||
---
|
||||
|
||||
## Rigor
|
||||
|
||||
- Prefer concrete evidence over speculation. Ground claims in exact file
|
||||
paths, line numbers, captured outputs, or reproduction steps when the task
|
||||
allows.
|
||||
- If you cannot show the trigger, failure path, or observed behavior, drop the
|
||||
claim.
|
||||
- "I don't know" beats a wrong answer. `mcp__safeoutputs__noop` beats a weak
|
||||
or speculative issue or review.
|
||||
- If you need to hedge with "might", "could", or "possibly", it is not
|
||||
ready.
|
||||
|
||||
## Adversarial self-review
|
||||
|
||||
- Before emitting any issue, discussion, or review, switch sides: assume your
|
||||
finding is WRONG and try your hardest to refute it. Emit it only if it
|
||||
survives your own strongest counter-argument.
|
||||
- A false or by-design report costs maintainers more than a missed one.
|
||||
Precision beats recall — when in doubt, `mcp__safeoutputs__noop`.
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
# Shared tool-calling and sandbox environment hints.
|
||||
# gh-aw imports this file; the markdown below (after the closing ---) is
|
||||
# appended to the agent's task prompt at runtime via {{#runtime-import}}.
|
||||
# Update INSTRUCTIONS in pydantic_ai_gh_aw_shim/cli.py to match.
|
||||
---
|
||||
|
||||
## Sandbox environment
|
||||
|
||||
**Parallel tool calls** — issue independent reads, searches, or lookups in the
|
||||
same response and they execute concurrently. Only chain sequentially when one
|
||||
call genuinely needs a previous call's result.
|
||||
|
||||
**File reading** — read files in large ranges (500+ lines per call). Most Python
|
||||
source files fit in one or two calls. Avoid reading 30–80 lines at a time.
|
||||
|
||||
**Search tools** — use the native `Grep` and `Glob` tools for codebase search.
|
||||
`rg` and `uv` are also available as plain commands via `Bash`.
|
||||
|
||||
**Dev environment** — the repo is checked out at `$GITHUB_WORKSPACE`. Dev
|
||||
dependencies are **not** pre-installed; run `make install` once before using
|
||||
`pytest`, `ruff`, or `pyright`. Prefer `uv run pytest <test_file>` over a bare
|
||||
`pytest` call.
|
||||
|
||||
**GitHub issue search** — this workflow runs the GitHub toolset in `gh-proxy`
|
||||
mode, so there are **no `mcp__github__*` tools**, and the `/search/issues`
|
||||
endpoint (`gh issue list --search`, `gh search issues`) returns HTTP 403 via the
|
||||
AWF firewall proxy. The issue-**list** endpoint **is** allowed through the
|
||||
proxied `gh` CLI, including its server-side `?labels=` filter. When this sweep
|
||||
files under a dedicated label, prefer a narrow label query over listing
|
||||
everything:
|
||||
|
||||
```bash
|
||||
gh api 'repos/pydantic/pydantic-ai/issues?state=open&labels=<label>&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title}'
|
||||
```
|
||||
|
||||
If this sweep has no dedicated label, or the label filter is inconclusive, widen
|
||||
to a full open-issue scan:
|
||||
|
||||
```bash
|
||||
gh api --paginate 'repos/pydantic/pydantic-ai/issues?state=open&per_page=100' \
|
||||
--jq '.[] | select(.pull_request == null) | {number, title, labels: [.labels[].name]}'
|
||||
```
|
||||
|
||||
`select(.pull_request == null)` drops PRs, which the issues endpoint also
|
||||
returns.
|
||||
Reference in New Issue
Block a user