Skip to content

Releases: context-labs/loupe

v0.13.0 — review several directories as one system

Choose a tag to compare

@github-actions github-actions released this 13 Sep 23:37
3ba9176

dir takes a list

Systems that span folders can be reviewed as one. The monorepo's inference/ platform routes inferences through elixir_engine/; a PR often changes both, and a reviewer that sees only one side misses the contract between them.

{ "dir": ["inference", "elixir_engine"] }
  • Only changed files under a listed directory are in scope.
  • Convention docs (AGENTS.md, CLAUDE.md, …) are read from each directory.
  • The harness runs at the repo root so the agent reads both sides of a change in one review; the on-disk diff and the file tree carry both.
  • Per-reviewer dir (string or list) overrides the top level, so one reviewer can cover the engine alone while another covers both.
  • The Action input dir and the CLI --dir accept a comma-separated list.

Not a breaking change. dir: "inference" validates and behaves exactly as before: cwd inside the directory, the path-mapping note, prefixed convention paths. Existing configs need no edits.

Until now the only way to span folders was to drop dir, run from the repo root, and hand-write include globs and explicit convention paths.

Also in this line since v0.12.0

  • v0.12.1: a clean review may omit the findings array (was falling back to headless).
  • v0.12.2: @loupe review edits its ack into a per-reviewer completion line.
  • v0.12.3: call-site discovery understands Elixir (def/defmacro, .ex/.exs, mix.exs as package root).

v0.12.3 — Elixir call sites

Choose a tag to compare

@github-actions github-actions released this 13 Sep 23:31
d8adb1c

Call-site discovery understands Elixir

Consumers that review several languages in one config (the monorepo now reviews inference/ TypeScript and elixir_engine/ Elixir together, since the platform routes inferences through the engine) got no "Call sites of changed exports" section for Elixir changes. Discovery only knew export function declarations, .ts/.js sources, and package.json as the package boundary.

  • def and defmacro names count as changed exports. defp is private and skipped.
  • .ex and .exs files are searched.
  • mix.exs, go.mod, Cargo.toml, and pyproject.toml mark a package root alongside package.json.

A def resolve/1 → resolve/2 change in lib/router.ex now lists its caller in lib/handler.ex, scoped to the Mix project.

v0.12.2 — @loupe review reports completion

Choose a tag to compare

@github-actions github-actions released this 13 Sep 23:25
b86edb3

@loupe review now tells you when it is done

Each reviewer keeps one summary comment per PR and updates it in place. That is what makes incremental review work, but it made a forced re-review invisible: @loupe review posted "🔍 On it", the summaries higher up the thread changed silently, and nothing new appeared. On a PR outside the config's dir, every reviewer found zero files, posted nothing, and the ack stayed forever.

The ack is now edited into a completion line when the run finishes:

✅ Re-review of `c61996e` done.

- code: ✅ no issues
- zdr: 🟡 1
- docs: ✅ no issues

Each reviewer's summary comment above was updated in place.

or, when no changed file is under the config's dir:

Nothing to review: this loupe config covers `inference/` and no changed file is under it.

A reviewer that failed shows ⚠️ failed (see the failure comment).

Internals

  • runReviews returns one outcome per reviewer instead of a boolean.
  • postIssueComment returns the comment id; new updateIssueComment.
  • Docs: docs/how-it-works/chat.md.

v0.12.1 — accept clean reviews without a findings array

Choose a tag to compare

@github-actions github-actions released this 13 Sep 17:25
63a0628

Fix

Clean reviews that omit findings no longer trigger the headless fallback. v0.12.0 tightened the parser so {} or {"status":"done"} could not pass as an empty clean review. The check also required a findings array, and glm-5.3 routinely answers a clean diff with {"summary": "… No defects found."} and nothing else. That valid answer was rejected, the agentic result discarded, and the review re-run headless with a ⚠️ degraded run marker. Three production reviews on the monorepo hit this in the first day.

Now a review needs a string summary. findings and concerns may be omitted and must be arrays when present. {}, {"status":"done"}, and {"summary":"s","findings":"none"} still reject.

First-day report for v0.12.0 on the monorepo

From 67 summaries posted after v0 moved:

  • 64 agentic, 3 headless fallbacks, all three with the cause above. No other fallback cause observed.
  • 29 incremental (delta) runs, 38 full. No unknown-history runs.
  • Thread resolution confirmed: PR 6413 has three loupe threads resolved across three pushes, PR 6416 one. History intact.
  • Verification: 2 passed, 1 invalid (the model answered in prose; all findings kept, as designed), the rest skipped because there was nothing to verify.
  • Summaries state which callers were checked in 40 of 67 runs, which is the review procedure taking effect.
  • Findings posted were substantive: fail-open retention defaults for zero-retention teams, an ACCESS EXCLUSIVE lock note on a trigger drop, an unpinned-deployment resolution change. No blockers, no false positives reported.

v0.12.0 — find the caller, keep the history, show the run

Choose a tag to compare

@github-actions github-actions released this 12 Sep 14:22
7eb73ef

Why this release

A bug that Cursor Bugbot caught on context-labs/monorepo#6377 and loupe missed. Replaying that PR's pre-fix commit with the consumer's real config (glm-5.3, maxTurns: 5, low reasoning) showed loupe stopping one file short of the bug on every run. This release is the set of changes that makes the same replay find it, plus the customer-requested comment-history change and the observability every run was missing. Tracked as INF-5289 (INF-5290 through INF-5295).

Replay scorecard, same PR, same config, no hint in the PR text:

loupe Runs Found the spinner-over-prompt bug
v0.11.1 1 no
v0.12.0 2 yes, 2 of 2, as a blocker that survived verification

What changed for reviews

Callers of changed code are found for the agent, not by the agent. Before the harness starts, loupe finds every exported value the diff touches (declaration lines, hunk headers, and hunk context), greps its callers within the same package, follows the enclosing function of each call up to three hops, and injects the list into the prompt with four lines of preceding context per site. Test files, strings, comments, and vendored directories are skipped. A five-turn agent now sees withProgress( wrapping ensureSession() wrapping login() without spending a single tool call to get there.

An always-on review procedure. Custom reviewer prompts replace loupe's default guidance, so procedural rules never reached them. A short procedure block is now appended regardless: read the listed callers before judging, follow thin wrappers one more hop, and treat a newly interactive or blocking call inside a spinner or other terminal-owning wrapper as a defect rather than a cosmetic note. Turn it off with procedure: false at the top level or per reviewer.

Incremental runs see the whole PR. Every in-scope patch is written to the on-disk diff on every run. On an incremental run the prompt names the files to reassess and labels the rest as context. Findings anchored on context files are dropped and counted, so they cannot duplicate a prior thread that was deliberately kept. A history lookup that fails, a compare that errors, or a compare at GitHub's 300-file cap means "unknown": full review, nothing cleaned up.

Reasoning effort reaches the harness. reasoning is passed natively: whip defaultEffort in the materialized WHIP_HOME, claude --effort, codex -c model_reasoning_effort. It also applies to the verify pass, chat, and fix. Unset means the harness's own default and no reasoning sentence in the prompt. Known limit: whip without a whip config block keeps its own default.

Verification fails open on evidence outside the diff. The verifier may reject a finding only when the diff itself contradicts it or it duplicates another. A finding grounded in a checkout file is no longer thrown away as "code not shown". Verification output must carry exactly one verdict per finding, otherwise it is ignored and every finding is kept.

Markdown bodies. The output contract and default guidance no longer cap findings at one or two sentences. Summaries, concern details, and finding bodies are GitHub Markdown with paragraphs and fenced code. Off-diff notes and concerns render as blocks, so fences survive.

What changed on the PR

Prior inline comments are resolved, not deleted. New priorComments setting, top level, per reviewer, Action input prior-comments, CLI --prior-comments:

Policy Effect
resolve (default) The thread is resolved via GraphQL. History stays visible under "Show resolved". Means "superseded by a newer review", not "proven fixed".
delete Previous behavior.
keep Nothing is touched. New comments accumulate. Useful for side-by-side evaluations.

Only threads rooted by loupe's own login with this reviewer's marker qualify, scoped to the reassessed files on an incremental run and to in-scope files on a full run. Threads the token cannot resolve are left open with a warning. Cleanup now runs after the new review and summary post, from a snapshot taken before, so a failed post never leaves a PR with old comments gone and nothing in their place.

Run details on every summary. A collapsed block reports the review mode (agentic, headless, or headless fallback after an agentic failure), verification status (passed, skipped, invalid, failed), scope (full, delta, unknown), and counts of findings dropped as malformed, out of scope, below the noise profile, or rejected by verification. The stat line shows ⚠️ degraded run when any of that lost something.

Failures are visible. A reviewer that throws posts a ⚠️ loupe · <name> could not complete this review comment (no marker, no SHA, so it never advances incremental state) and the job exits 1. The other reviewers keep running. The parser now rejects objects that are not a review ({}, {"status":"done"}) instead of treating them as a clean pass, and an agentic run whose output is not a review gets the same single headless retry a crashed run gets.

Path mapping under dir. When the harness runs inside a subdirectory, the prompt says so and explains that listed paths are repository-relative. This alone had been costing consumers a wasted turn per run.

Behavior changes for @main and @v0 consumers

  • Prior inline threads are resolved on re-review. Set priorComments: "delete" to keep the old behavior.
  • reasoning unset no longer injects "Reasoning effort: low" into the prompt.
  • Every summary gains a Run details block. Custom reviewer prompts gain the procedure block unless procedure: false.
  • maxTurns default is unchanged at 10. Consumers running at 5 should expect the call-site list to do more of the work, not more turns.

Docs

  • New docs/how-it-works/: triggers, review run, what the agent sees, GitHub objects, first vs incremental runs, chat. Mermaid diagrams throughout.
  • docs/configuration.md, docs/guide.md, docs/github-action.md, and README.md updated for every new field, flag, and default.
  • loupe reviews its own PRs with a new docs-currency reviewer (.loupe/reviewers/docs.md).

Tests

122 tests. New end-to-end runReview coverage with an injected Octokit and a scripted harness: incremental scope and cleanup ordering, unknown history, headless fallback, the compare cap, dry run, and the call-site section. Call-site discovery has its own unit tests for hunk parsing, package scoping, string and comment filtering, and the three-hop chain.

Review process for this release

Plan and tickets critiqued by gpt-6-astra before coding. Adversarial pass on the diff: 3 raised, 1 fixed, 1 deferred and documented, 1 refuted. loupe's own code and docs reviewers ran on every push and found four real items that were fixed before merge.

v0.11.1

Choose a tag to compare

@github-actions github-actions released this 03 Sep 23:23
7915d10

What's Changed

  • chore: track the latest whip release by @atbe in #8
  • fix(harness): tolerate a whip that predates -cache-key by @atbe in #9

Full Changelog: v0.11.0...v0.11.1

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 03 Sep 21:20
58aaba1

What's Changed

  • feat: agentic reviews explore the diff (tree, not inline) + stable cache key by @atbe in #7

Full Changelog: v0.10.2...v0.11.0

v0.10.2

Choose a tag to compare

@github-actions github-actions released this 01 Sep 18:37
e76dcd1

What's Changed

  • perf: run reviewers concurrently instead of sequentially by @francescov1 in #5
  • ci: real CI + loupe self-review; docs: release/versioning by @atbe in #6

New Contributors

Full Changelog: v0.10.1...v0.10.2