Anatomy of a Teire PR
Every Teire pull request carries its own evidence. It shows why your code is affected, what the source says, what changed, your checks before and after, and what each model call cost.
This page walks through a real pull request: the dry run of golang-jwt/jwt v4.5.2 → v5.3.1 on teire-dev/examples/go-major. It verified in 2 minutes 9 seconds for $0.181.

Title
| Change | Title |
|---|---|
| Package | Upgrade <package> from <version> to <target>, plus and adapt the code when code changed |
| Security | Upgrade <package> from <version> to <target> to fix <GHSA id or N advisories> |
| One model | Replace retired model gpt-4.1-nano with gpt-5.6-luna (or retiring, before the date) |
| Several models | Replace N retired <Provider> models |
| Other API change | Adapt to <change title> |
Body, section by section
The verdict line comes first: Verified., Draft: typecheck only., Draft: checks already failing., Draft: needs live access., Draft: needs a local service., Draft: environment differs., or Needs a human. From the Go run:
Verified.
github.com/golang-jwt/jwt/v4v4.5.2 → github.com/golang-jwt/jwt/v5 v5.3.1, with code changes in 1 file. The repo's checks pass before and after the upgrade (4/4 tests).
Why this repo lists each line that uses what changed, linked to the exact commit, with what changed there (symbol removed, arity changed, …). The heading says how the lines were found: by type, or by name where the ecosystem's search is syntax-level.
github.com/golang-jwt/jwt/v5 v5.3.1 removes or changes APIs this repo calls (resolved by type, not by name):
auth/token.go:22jwt.StandardClaims(symbol removed)auth/token.go:49var ve *jwt.ValidationError(symbol removed)- …
Up to 12 sites per change. Lines whose types merely changed are listed as "Also used, annotated only".
What changed upstream summarises the API diff and quotes the release notes. Each quote comes word for word from a document Teire fetched:
Comparing the public API of v4.5.2 and github.com/golang-jwt/jwt/v5 v5.3.1: removes 34 API(s); adds 6 required propert(ies) to existing types.
Changes lists each file and who changed it. Your package manager changed the manifest and lockfile; Teire changed the imports; Nemotron changed the rest. Then come the agent's own summary and the full diff in a fold:
| File | Change |
|---|---|
go.sum | regenerated by go (+2/−2 lines) |
go.mod | updated by go |
auth/token.go | Teire: imports github.com/golang-jwt/jwt/v4 → github.com/golang-jwt/jwt/v5; edited by NVIDIA Nemotron 3 Ultra |
Verification shows each check's exact command and its result on both sides:
| Check | Before (v4.5.2) | After (v5.3.1) |
|---|---|---|
build go build -o /dev/null ./... | ✓ exit 0 | ✓ exit 0 |
typecheck go vet ./... | ✓ exit 0 | ✓ exit 0 |
test go test -count=1 -json ./... | ✓ exit 0 · 4/4 passed | ✓ exit 0 · 4/4 passed |
Receipt lists every model call with its tokens and cost, and the sandbox time:
| Step | Model | Tokens in / out | Cost |
|---|---|---|---|
| Fix attempt 1: 22 steps → verified | NVIDIA Nemotron 3 Ultra | 276.5k / 3.6k | $0.176 |
| Review of the passing diff: no problems found | NVIDIA Nemotron 3 Ultra | 1.4k / 1.1k | $0.0046 |
| Sandboxes: install + checks, both sides | Cloudflare Containers | — | 27 s |
An upgrade's first run also has a "Read the release notes" row. A run with no code edits has a "Review of the dependency change" row instead of the diff's review, and a run whose install failed has a "Why the install failed" row. This run reused an earlier reading of the same notes, so it paid nothing for them.
Supply chain says what changed about who published the target and what runs when it installs, compared with the version you have. Each registry is read only for what it publishes:
| Registry | Signals |
|---|---|
| npm | a new install script; a different publishing account (unless the target is published with provenance); provenance the installed version had and the target lacks |
| crates.io | a new build script (build.rs); a different publishing account |
| PyPI | a PEP 740 attestation the installed version had and the target lacks, or one from a different repository or workflow; a target with only a source distribution, whose install runs its build backend |
| RubyGems | a new native extension that compiles at install |
| Maven Central | a different signing key (Central requires a signature on every file, so a missing one can't happen) |
| Go | none: the module proxy publishes no publisher or signing data, and the PR says so; go.sum checks content against the checksum database |
PyPI and RubyGems name only today's owners, so owner changes aren't compared.
Risks not covered lists the behaviour changes the release notes describe, each with its quote, the reasons when the evidence is not verified, and anything the review raised. Every package PR ends this section with "Lint, end-to-end and integration suites were not run; dependencies were installed without lifecycle scripts." Model and API PRs end with the network-off and prompt-tuning notes instead.
Rollback says how to undo it. Commands lists @teire rebase · @teire recreate · @teire explain · @teire ignore <package>. A footer links the live run and names the models used, or "deterministic fix, no model call".
A model retirement
Model and API PRs have the same sections. Verification compares the old model with the new one, and the risks add that no request reached the provider. Here is the OpenAI case, fixed by a plain swap with no model call:
zod
examplesteire-dev
- Your codeinstalled version
dependabot-core
Nemotron 3 Ultra22 calls
- SandboxToken Factory
- Your checks4/4 → 4/4
- Review
- Pull request#30
Ready, draft or issue
| Evidence | Opens as | Labels |
|---|---|---|
verified | a ready PR, check run success | teire, the ecosystem (npm, pypi, go, cargo, rubygems, maven) and upgrade, major or security. Model and API PRs get model-retirement or api-change and the provider |
typecheck_only · pre_existing_failure · needs_live_access · needs_local_service · environment_mismatch | a draft PR, check run neutral | the same, plus the evidence, such as typecheck-only |
regression · unverified · timeout | an issue, Teire needs a human: …, with the body and the reasons | teire |
A change from an unconfirmed source (one whose quote Teire couldn't find on the page) is listed on the dashboard as check by hand and never opens an issue. If you ask Teire to try it (@teire fix with its name, on the Dashboard issue), the PR opens as a draft at best, labelled unconfirmed.
A Teire needs a human issue says why a person is needed, your version and the version the change is about, where the code is, how far Teire's search could see (files searched, how uses were resolved, what it couldn't resolve), what changed upstream with its quote, and what to do. For a removed parameter with no replacement that is the call sites to change; for a behaviour change, a short checklist to compare the paths before and after; for an account setting, the step the source names.
A verified PR also says Proven offline and carries the proven-offline label: its checks passed with no network, on Token Factory in microVMs with no network interface, on Cloudflare with every request refused and listed. Before a PR opens, Teire reads back the files it commits with its own lockfile readers, in each root the PR changes: every direct copy of the upgrade and every package moved with it must be locked at the versions the PR names (a Go major at its new module path), and a security fix must clear every advisory it claims, or the run stops and says which. Another root that still locks an affected version is named in the PR, since the PR doesn't change it.
pullRequests.labels in teire.yml adds your own labels.
On GitHub
- Branch.
teire/<group>, such asteire/npm/openai/7.38.0orteire/models/openai. Teire never commits to your default branch and never merges. - Commit. One commit made through GitHub's API as the App, which GitHub signs (very large changes go through the Git Data API instead). Its headline is the PR title, after any
pullRequests.commitMessage.prefix. - Check run "Teire". On the PR's head commit, with a notice annotation on every line the change reaches. Its details link the run page.
- Superseded. When a PR opens for a newer target of the same package, Teire closes the older PR with "Superseded by #N".
- Base moved. A push to the default branch that touches dependency files rebases every open Teire PR. So does GitHub reporting a PR as no longer mergeable. A rebase reruns the fix and the verification.
- CI failed. If your CI fails on a Teire PR, Teire makes one repair attempt per commit. It reads up to 3 failing checks and the last 6,000 characters of their logs. It says so in a comment.
- Your commits. If someone else pushes to a Teire branch, Teire stops updating it and comments on the PR. It never overwrites your commits.
- Closed. Closing marks the change dismissed and merging marks it fixed. Later scans don't reopen it.
The onboarding PR
The first scan of a repository without teire.yml opens Configure Teire. It adds only .github/teire.yml (the defaults). Its body lists:
- what Teire found,
- the upgrades it will open after you merge, and what each means for your code,
- retired models in your code,
- API changes on operations you call,
- runtime notices,
- the inventory per ecosystem.
Merging it turns Teire on for the repository.
How Teire works
Five steps from a notice to a reviewed fix. Teire reads only what your code uses, finds the exact lines, fixes them, proves it with your own checks, and opens a pull request.
The life of a Teire PR
What happens to a Teire pull request after it opens. The base moves, CI fails, a newer version ships, someone pushes a commit or closes it. Each event, what Teire does, and the words it posts.