Agents That Check Their Work

LSP navigation plus a per-edit quality loop

The gap

Agents could read code like a compiler — but edited like typing in the dark

Amplifier agents already navigated code semantically, yet their edits got no automated lint, type, or format feedback as they wrote. The language-dev bundles close that gap.

First, the proof that the navigation was already real.

Semantic navigation was already there — 14 LSP operations

The base amplifier-bundle-lsp tool exposes definitions, references, and full incoming/outgoing call graphs — what the code-navigator agent calls “something grep cannot do reliably.”

So the story isn’t inventing navigation — it’s what was missing next.

14
LSP operations (down from 17 after 3 dead type-hierarchy ops removed)
3
call-graph ops: prepareCallHierarchy, incomingCalls, outgoingCalls

The language bundles were thin wrappers with zero modules

Per the design doc, the language-LSP bundles were “thin configuration-only wrappers with zero modules.” Only Python had a dev bundle with quality tooling — Rust and TypeScript had nothing.

Navigation was real, but unpaired with any per-edit checks.

Consolidation: 4 dev bundles, each pairing LSP with quality

The design collapses the fragmented repos into 4 comprehensive language-dev bundles. Each uses a two-behavior + composite pattern — an LSP behavior for navigation, a quality behavior for per-edit checks — merged into one composite dev behavior.

Now: how the quality behavior actually fires.

A post-tool hook fires on every edit and injects the results

The quality behavior’s hook triggers on file write/edit events and runs quality checks, injecting feedback straight into the agent’s context when issues are found — with auto_inject on by default.

Feedback is per-edit and in-context, not deferred.

# Triggers on: write_file edit_file Write Edit MultiEdit # then, when issues are found: if self.auto_inject: # inject issues into agent context

For Python, the check orchestrates ruff, pyright, and stub detection

The python-quality behavior wires the hook to run lint, types, and format on every *.py edit. The checker orchestrates ruff format, ruff lint, pyright types, and stub detection.

A real toolchain — not an abstraction. So does it hold up?

On a real TS/JS codebase, the clean file reported zero issues

E2E validation ran against a real codebase with typescript-language-server 5.1.3 and TypeScript 5.9.3. The “zero false positives” criterion PASSED: a clean file (types.ts) reported 0 issues attributed to it.

No noise on clean code — the bar that earns trust.

0
issues on the clean file (types.ts) — zero-false-positives PASS
18
real issues found elsewhere: eslint 13, tsc 1, stubs 4

Navigation plus a self-checking loop — and the thin wrappers retire

Pair precise navigation with an in-context quality loop, and the old thin wrappers become deprecation stubs. The agent that reads code well now also checks its own edits.

Shipped: the thin repos are demoted to forwarding stubs.

Sources

Sources & Research Methodology

Data as of: May 18, 2026  ·  SHIPPED / consolidated, in maintenance

Repos: microsoft/amplifier-bundle-lsp (base) + python-dev, rust-dev, typescript-dev (and parallel ts-dev); thin lsp-python/-rust/-typescript demoted to deprecation stubs.

Research performed:

Primary contributor: Brian Krabach (design doc, LSP absorption, quality-checker core, persistent server lifecycle). OSS governance PRs by sadlilas on typescript-dev.

Gaps: “7 repos” and dead-op counts come from DESIGN.md (a plan, not an audit; its prose counts 5 real repos). Zero-false-positives applies only to the clean-file criterion — overall Tasks 4-6 were 3 PASS, 2 PARTIAL, 1 FAIL. Rust checker toolchain not directly opened. Canonical TS home (typescript-dev vs ts-dev) not definitively resolved.

More Amplifier Stories