Generate a Change Overview¶
lgtmaybe can post a change overview on a pull request. It says what the change is, what is risky about it, and what it touches. It is one comment, updated in place, for a reviewer to read before the diff.
The overview is separate from the review. The review finds bugs in the lines it reads. The overview describes the change as a whole.
Contents¶
- What You Get
- High Impact Areas
- Structure and Sequence
- On GitHub:
/diagramandauto_diagram - Locally:
lgtmaybe diagram - Why Mermaid (and What the ASCII Is For)
- See Also
What You Get¶
One comment holds three model calls, run concurrently, plus one deterministic section (worked out by code, with no model call). In reading order:
- The description head (
auto_describe): a suggested title, the change type, and a short summary. - Risk of Change (
risk): the change'slowtocriticalverdict and every reason behind it, computed without a model call. See Risk of Change. - High Impact Areas
(
high_impact): the changes a reviewer must not miss. See High Impact Areas below. - The description detail (
auto_describe): a per-file walkthrough, and a "does it do what it says?" check when the PR states an intent. - Up to two diagrams: a Mermaid flowchart of the structure and, when the change alters one, a Mermaid sequence diagram of the run-time flow. Mermaid is a text format for diagrams that GitHub draws for you. Each diagram also comes as plain text. A terminal shows that text, and it is the fallback if the Mermaid can't be rendered.
The description, High Impact Areas and diagrams are three calls rather than one. A single prompt doing three jobs does all three worse. The calls run at the same time, so the overview takes about as long as its slowest call.
Each section has its own switch. The description and High Impact Areas are best-effort: if one of those calls fails, its slot says so and the rest of the comment still posts.
GitHub renders both Mermaid diagrams natively inside the comment, with no image and no external hosting.
The flowchart is deliberately sparse:
- at most six components;
- short relationship labels;
- Mermaid's automatic layout.
Changed components are marked (new) or (changed) in their labels.
GitHub's renderer offers zoom buttons but no full screen. So each diagram is followed by an ⛶ Open full screen link. It opens the same diagram alone on mermaid.live, with pan and zoom. The diagram source travels compressed in the URL fragment and is decoded in your browser. Nothing is sent anywhere until you click.
The diff is only a slice of the codebase. So a note under the diagrams calls out any relationship the model inferred rather than saw in the diff.
Here is the posted comment for a PR that puts a Redis cache in front of a user service. The Mermaid renders in place. Each text rendering sits in a collapsible "Text version" underneath:
Cache user lookups in Redis¶
Change type: feature
User reads now check Redis before PostgreSQL, and successful database reads populate the cache for later requests.
Risk of Change: Critical¶
- touches infrastructure:
infra/redis.tf- touches backup and recovery:
ops/backup_retention.yamlHigh Impact Areas¶
- Infrastructure: New ElastiCache cluster (
infra/redis.tf). Adds a managed dependency to the production VPC. Check: security group and failover settings.- Availability: Cache on the user read path (
api/users.py). User reads now depend on Redis. A Redis outage takes reads down with it. Check: confirm a Redis error falls back to the database.- Backup and recovery: touched
ops/backup_retention.yaml(not assessed by the model)Walkthrough¶
File Change api/users.pyReads through the Redis cache before querying PostgreSQL infra/redis.tfAdds an ElastiCache Redis cluster ops/backup_retention.yamlExcludes the cache from nightly backups Structure¶
flowchart LR
n0["Client"]
n1["User API<br/>Python<br/>serves user reads<br/>(changed)"]
n2["Redis cache<br/>Redis<br/>caches user rows<br/>(new)"]
n3["User DB<br/>PostgreSQL"]
n0 -->|"requests"| n1
n1 -->|"checks"| n2
n1 -->|"on miss"| n3
⛶ Open full screen (the real link carries the diagram source compressed into the URL)
Text version
[Client] --requests--> [User API Python (changed)]
[User API Python (changed)] --checks--> [Redis cache Redis (new)]
[User API Python (changed)] --on miss--> [User DB PostgreSQL]
Sequence¶
sequenceDiagram
participant n0 as Client
participant n1 as User API (changed)
participant n2 as Redis cache (new)
participant n3 as User DB
n0->>n1: GET /users/{id}
n1->>n2: read cached row
n2-->>n1: miss
n1->>n3: SELECT user
n1->>n2: store row, 60s TTL
n1-->>n0: user JSON
Text version
1. [Client] -> [User API (changed)]: GET /users/{id}
2. [User API (changed)] -> [Redis cache (new)]: read cached row
3. [Redis cache (new)] --> [User API (changed)]: miss
4. [User API (changed)] -> [User DB]: SELECT user
5. [User API (changed)] -> [Redis cache (new)]: store row, 60s TTL
6. [User API (changed)] --> [Client]: user JSON
The User DB link is inferred from an import, not shown in the diff.
High Impact Areas¶
A review reads the changed lines and tells you what is wrong in them. It cannot tell you that halving a node count removes your peak headroom. Nor can it tell you that the PR quietly shortened a backup retention policy.
High Impact Areas asks a different question: what could this change break beyond itself? It answers in one section near the top of the overview.
Ten areas are checked:
| Area | What it covers |
|---|---|
| Infrastructure | IaC, Kubernetes/Helm, container images, CI/CD pipelines, deploy scripts, networking, DNS, load balancers, resource sizing |
| Security | Authentication, authorisation, IAM or permission scope, cryptography, secret handling, removed input validation, CORS/CSP/TLS, CI workflow permissions |
| Availability | Anything that could cause a production outage: startup and config defaults, timeouts, retries, circuit breakers, connection pools, rate limits, health checks, removed error handling, concurrency and locking, runtime or major dependency upgrades, hot-path performance, rollback and deploy ordering |
| Data migration | Schema or data migrations, destructive or irreversible data operations, backfills |
| Backup and recovery | Backup jobs, retention and lifecycle policies, snapshots, restore paths, disaster recovery, failover, replication |
| Compatibility | Breaking an external contract: HTTP APIs, event or message schemas, CLI flags, SDK surface, wire formats, feature-flag removal |
| Observability | Logs, metrics, alerts, dashboards, tracing, audit trails or runbooks being removed, silenced or changed |
| Dependencies | Supply chain: new dependencies, major bumps, loosened pins, lockfiles, base images, build toolchain |
| Cost | Autoscaling limits, instance types, provisioned capacity, storage classes, quotas |
| Compliance | PII or privacy handling, audit trails, data residency, licence changes |
Each call-out names:
- the area;
- a short headline;
- the files involved;
- the blast radius (what else the change can affect);
- the one thing a reviewer should verify.
A call-out the model rates medium or critical shows that severity next to
the area name. high is the unmarked default. Files the model names that the
PR never changed are dropped.
The Path Floor¶
The model is not the only source. Before the call, lgtmaybe matches the PR's
changed paths against fixed patterns per area. Examples are *.tf,
.github/workflows/, alembic/, anything named for backups or retention, and
lockfiles. Those matches do two jobs:
- They are sent to the model as untrusted hints, saying where to look (never that anything is wrong).
- They floor the output. Say the model said nothing about a matched file under
that area. The file is still listed, marked
(not assessed by the model). A weak model cannot make your Terraform change invisible.
The floor is also the fallback. If the call fails or returns nothing usable, the section shows the path matches alone, under a "Model assessment unavailable" note. The section degrades, but it never disappears.
When nothing qualifies, the section says so and names what it checked:
High Impact Areas¶
None detected. Checked: infrastructure, security posture, availability, data migrations, backups and recovery, compatibility, observability, dependencies, cost, compliance.
Turning It Off¶
The section is on by default. To drop it and its model call:
high_impact: false
The description has its own switch, auto_describe: false. Risk of Change has
risk: {enabled: false}. With all three off, the comment holds only the
diagrams and costs one call.
Structure and Sequence¶
The two diagrams answer different questions, which is why lgtmaybe draws both:
| Answers | Best on | |
|---|---|---|
| Flowchart (structure) | What does this change touch, and what talks to what? | Structural PRs: a component added, a dependency introduced, a service split |
| Sequence | What happens at run time, in what order, and where did that change? | Behavioural PRs: a retry added, an ordering fixed, a signal now handled |
Most pull requests are behavioural, and structure alone under-describes them.
Take a PR that fixes terminal restoration on SIGTERM. It might touch two files
and draw three boxes. Its sequence diagram (signal, flag set, loop exit, guard
dropped, terminal restored) is the explanation of the fix.
A PR that puts a cache in front of a service is the opposite. The new box is the headline, and the flow shows how a request now reaches it.
Some changes have no meaningful run-time flow, such as documentation, configuration or formatting. For those, the sequence view is omitted rather than invented. Both diagrams come from one model call and share the same six-component budget. The sequence view adds at most eight ordered steps.
On GitHub: /diagram and auto_diagram¶
Comment /diagram on a pull request to post the change overview, or update
it in place. Re-running edits the same comment instead of stacking new ones.
(/describe still posts the description alone as its own comment, for when
that is all you want.)
auto_diagram is on by default, with no workflow input or .lgtmaybe.yml
needed. The overview posts when a PR is opened or reopened. It refreshes after
each push, so every section tracks the head commit. To opt out of the whole
comment, set the input in your workflow:
- uses: MattJColes/lgtmaybe@v2
with:
provider: anthropic
model: claude-sonnet-4-6
api_key: ${{ secrets.ANTHROPIC_API_KEY }}
auto_diagram: "false"
or in .lgtmaybe.yml:
auto_diagram: false
The automatic overview stands back when the pull request already diagrams the
change. That means a fenced mermaid block in either:
- the PR description;
- any comment that is not this setup's own overview. That covers yours, a teammate's, another review bot's walkthrough, or another lgtmaybe setup's overview.
The review still runs, and its summary says where the existing diagram lives.
Once lgtmaybe has posted its own overview on a PR, it keeps refreshing that one
so it never goes stale. Comment /diagram to post the overview anyway: slash
commands skip the check.
Gitea runs the same check through the shared Action entrypoint. The
gitlab-ci command posts no automatic overview yet.
The description and High Impact Areas are best-effort. A failure is logged and leaves a visible note in its slot. It never blocks the rest. The diagram is a required completion step. So a failed diagram leaves the commit unfinished, and the next run redoes it rather than skipping it.
Locally: lgtmaybe diagram¶
lgtmaybe diagram prints the change overview of your local changes, with no
GitHub involved:
$ lgtmaybe diagram --provider ollama --model llama3
It diffs your branch against the base, using the same base resolution as
lgtmaybe review:
--baseoverrides the base;--workingincludes uncommitted edits;--uncommitteduses only the working-tree edits.
The output is the same body the /diagram comment carries. In order, that is
the description, Risk of Change, High Impact Areas and walkthrough. Then come
each diagram's Mermaid source, full-screen link and text rendering. There is one
change: a terminal renders no HTML. So the collapsible "Text version" wrapper
becomes a plain labelled section. An excerpt:
$ lgtmaybe diagram --provider ollama --model llama3
...
### Sequence
```mermaid
sequenceDiagram
participant n0 as Order API (changed)
participant n1 as Payments
n0->>n1: POST /charge
n1-->>n0: 503 Unavailable
n0->>n0: backs off 200ms
```
[⛶ Open full screen](https://mermaid.live/view#pako:…)
Text version:
```
1. [Order API (changed)] -> [Payments]: POST /charge
2. [Payments] --> [Order API (changed)]: 503 Unavailable
3. [Order API (changed)] -> [Order API (changed)]: backs off 200ms
```
The Mermaid source stays in the output on purpose. Your terminal can't draw it, but you can paste it into a GitHub comment, mermaid.live, or a Markdown file to render it.
Why Mermaid (and What the ASCII Is For)¶
GitHub renders Mermaid natively in comments and Markdown, both flowcharts
and sequence diagrams. So a mermaid fence renders in the comment, with no
image to generate or host.
That matters for a pull_request_target reviewer. Hosting an image would mean
committing a file or calling an external service. Neither fits a fork-safe
comment that is updated in place.
A terminal can't render Mermaid, which is why each diagram also comes as plain text. In the GitHub comment, that text sits in a collapsible "Text version" block, out of the way of the rendered diagram. In the terminal, the same block becomes a labelled section, because HTML tags are noise in a shell. Either way, the text is what you read without a renderer. It is also the fallback if a diagram fails to render.
The model never writes Mermaid. It returns typed graph data: components, relationships and ordered steps. lgtmaybe renders the syntax itself and escapes every label. A diff that tries to smuggle diagram source or markup into a label can't reach the fence.
D2 isn't used because GitHub doesn't render it in Markdown, so it would show as source.