Add a custom review lens (BYO skills)¶
lgtmaybe ships nine built-in review lenses (security, correctness, deprecation,
tests, documentation, performance, complexity, intent, and ponytail — the
"lazy senior dev" / write-less-code lens). A custom lens lets you add your
own — a "skill file" that runs alongside the built-ins, fans out as its own
focused model call, and merges its findings into the same review. Use it to bake
in a house style or a team convention the built-in lenses don't cover.
How a lens works¶
Every lens — built-in or custom — is the same shape: a focused instruction set and one worked example, sent as its own system prompt so the model concentrates on a single concern. A custom lens needs:
| Field | Required | What it is |
|---|---|---|
id |
yes | A unique short name. Must not collide with a built-in category. |
instructions |
yes | What to look for, in plain language. This is the lens. |
title |
no | Human-readable heading (falls back to id). |
example_diff + example_finding |
no (together) | A worked example: a hunk and the finding the model should return for it. Optional, but it sharply improves smaller models. |
Inline in .lgtmaybe.yml¶
The quickest way — define the lens directly in your repo config:
provider: ollama
model: qwen3.6:27b
extra_lenses:
- id: house-style
title: House style
instructions: |
Enforce our team conventions on changed code. Flag new public functions
that return bare dicts instead of a typed dataclass, and any logging at
WARNING or above that doesn't include a correlation id.
example_diff: |
--- a/api.py
+++ b/api.py
@@ -10,1 +10,2 @@
def make_user(name):
+ return {"name": name, "active": True}
example_finding:
path: api.py
line: 11
severity: low
title: Public function returns a bare dict
body: House style returns a typed dataclass from public functions, not a dict.
suggestion: " return User(name=name, active=True)"
The Ponytail lens is built in
The "lazy senior dev / write less code" instinct from
Ponytail ships as the built-in
ponytail lens — you don't need a custom lens for it. Reach for extra_lenses
when you want something the nine built-ins don't already cover, like the
house-style rule above.
As reusable skill files¶
To share lenses across repos — or to let an agent harness drop its own lens in —
put each one in its own file and point lens_paths at the file or a directory:
# .lgtmaybe.yml
provider: ollama
model: qwen3.6:27b
lens_paths:
- .lgtmaybe/skills
# .lgtmaybe/skills/house-style.yml
id: house-style
title: House style
instructions: |
Flag new public functions that return bare dicts instead of a typed dataclass,
and WARNING+ logging without a correlation id.
A skill file may hold one lens (a mapping) or several (a list). Lenses loaded from
lens_paths are appended to any inline extra_lenses; ids must be unique across
the whole set.
Bundled lens packs (pack:<name>)¶
lgtmaybe ships a curated, opt-in library of extra lenses — distilled from the
wider engineering-review ecosystem (Ousterhout, Metz, Carmack, the NASA Power of
Ten / TigerStyle, Google's eng-practices, and several skill/rule collections; see
ATTRIBUTION.md).
They are off by default — they are more opinionated than the nine built-ins,
and every lens you enable is another model call per review. Enable a pack by name
with the pack: scheme in lens_paths (this works for a pip install, with no
repo-relative path to point at):
# .lgtmaybe.yml
provider: ollama
model: qwen3.6:27b
lens_paths:
- pack:design # combine as many as you like
- pack:robustness
| Pack | Lenses | Use it when |
|---|---|---|
pack:design |
wrong-abstraction, shallow-module, information-leakage, errors-out-of-existence, hidden-state, naming |
You want taste/structure review (the "right abstraction", deep modules, explicit state). |
pack:robustness |
assertions, bounded, idempotency, migrations, portability, observability |
Operational safety: defensive invariants, bounded work, retry-safe side effects, safe migrations. |
pack:interface |
api-design, type-safety, magic-values, comment-why |
Contracts & clarity: backward compatibility, tight types, named constants, why-not-what comments. |
pack:frontend |
accessibility, i18n |
The diff touches UI / user-facing copy. |
Each pack is a directory of skill files under the package, so you can also browse
or copy any single lens from
src/lgtmaybe/lenses/.
An unknown pack: name fails loudly, listing the packs that exist.
Run it¶
Custom lenses run automatically on every review — CLI or GitHub Action — once they are in your config. On the CLI you'll see findings titled by your lens just like the built-ins:
lgtmaybe review --provider ollama --model qwen3.6:27b --api-base http://localhost:11434
To narrow a run to fewer built-in lenses while keeping your custom ones, set
categories — the two lists are independent.
Security: lenses are trusted input¶
A lens's instructions and example go straight into the model's system prompt, so
treat a lens like code you run: only define lenses in files you control. Keep
them in your committed .lgtmaybe.yml or repo skill files, never source them from
a pull request's contents. On pull_request_target the Action reads config from
the base repository, not the PR head, so a fork PR cannot introduce or alter a
lens. Diff content itself is always treated as untrusted data, separately from
your lenses.
See also¶
- Configure .lgtmaybe.yml — the field reference.
- What gets reviewed — the built-in lenses.