Skip to content

Releases: trendvidia/caliper

Caliper v1.7.0

Choose a tag to compare

@bfrancojr bfrancojr released this 18 Oct 09:27
7a1a643

A load in which no package type-checks can now return an engine instead of an error, through the new EngineOptions.KeepWhenAllFail. Off by default, so existing callers are unchanged.

Added

  • EngineOptions.KeepWhenAllFail: a load in which no package
    type-checks returns an engine instead of the error package errors: …. Packages() is empty and Failed() holds every package with its
    errors. It is for callers that load a few packages of a module and
    want a broken one described as a whole module's load would describe
    it: alidade reloading a worktree's changed packages
    (belluikit/alidade#41). A load none of whose packages has a Go file
    is still an error. Off by default.

Changed

  • Every source file carries its license header:
    SPDX-License-Identifier: MIT and the copyright line. The generated
    protobuf Go gets it from its .proto. Test fixtures under testdata
    are left as they were.
  • The copyright holder in LICENSE is spelled "TrendVidia, LLC.", as in
    the organization's other repositories.

Caliper v1.6.2

Choose a tag to compare

@trendvidia trendvidia released this 13 Oct 18:52
323d6e0

Caliper is now public and MIT-licensed. This release adds the LICENSE file, so a module pinned to this tag carries it, and every release has its binaries attached again. The analysis itself is unchanged since v1.6.1.

Changed

  • Caliper is MIT-licensed, with a LICENSE file at the root. The
    contributor agreement already said so; the repository did not.
  • Install docs no longer assume a private repository: no GOPRIVATE,
    no authenticated release downloads, and brew install trendvidia/tap/caliper without HOMEBREW_GITHUB_API_TOKEN (the tap
    is public too).
  • CI runs on GitHub-hosted Ubuntu and macOS runners with stock Go
    1.27.2, replacing the self-hosted runner and its go-plus check. Stock
    Go and go-plus give the same result locally: 379 tests run, 379 pass,
    none skipped.

Build

  • Every release gets its binaries again. v1.4.0 through v1.6.1
    shipped none, though the install docs promised them. A release
    workflow now runs scripts/build-release.sh when a release is
    published, for macOS / Linux / Windows × amd64 / arm64, with a
    checksums.txt. Binaries are built with Go 1.27.2, the newest Go
    caliper supports, because a binary refuses a newer go command than
    the one it was built with (#35). v1.6.1's binaries were added after
    the fact with the same script.

Caliper v1.6.1

Choose a tag to compare

@trendvidia trendvidia released this 09 Oct 06:06

Go 1.27 compatibility fix: caliper now requires x/tools v0.51.0. On v1.6.0, a program reaching a method with type parameters panicked the call graph, and the export-data load ended the process on go-plus 1.27.2.

Fixed

  • The call graph no longer panics on a method with type parameters
    (#40). Go 1.27 allows methods with type parameters of their own, and
    the standard library has one, math/rand/v2's (*Rand).N. Any
    program that reached such a method made BuildInterProceduralContext
    panic with the type parameter's name (panic: Int) inside x/tools'
    Program.RuntimeTypes. caliper now requires x/tools v0.51.0, which
    skips those methods there; calls to them are edges in the graph.

  • The export-data load works on go-plus 1.27.2. With x/tools
    v0.44.0, DepsFromExportData on a module importing the standard
    library ended the process with internal error: package "fmt" without types was imported from .... The x/tools v0.51.0 bump above fixes it.

  • A panic during call graph construction is returned as an error.
    BuildInterProceduralContext used to let it end the process; the
    next input x/tools cannot handle now fails the build of the context
    instead.

Caliper v1.6.0

Choose a tag to compare

@trendvidia trendvidia released this 06 Oct 04:17

Analysis-engine release: a caller can now see which packages the load dropped and which loaded packages the call graph leaves out, and why.

Added

  • Engine.Failed() returns the packages the load dropped (#38).
    Packages() leaves out every package with errors, and until now
    nothing recorded that they existed, so a caller describing the whole
    module could not tell a package that failed from one that does not
    exist. Failed() returns them with their Errors and whatever
    go/packages kept: the syntax that parsed, and for a partly
    type-checked package, the Types scope and TypesInfo of the
    declarations that resolved. A directory whose files a build
    constraint all excludes is still absent, because ./... does not
    match it.

  • InterProceduralContext.Skipped lists the loaded packages left out of
    the SSA program
    , each with the Cause: the package whose error outside
    a function body put it out of reach. Such a package is in Packages()
    and type-checked cleanly, but imports, possibly transitively, a package
    with a declaration or syntax error, so it has no functions and no
    call-graph edges. Before, that was silent.

Caliper v1.5.0

Choose a tag to compare

@trendvidia trendvidia released this 11 Sep 23:20

Analysis-engine release: a load mode that takes dependency types from export data, three correctness fixes in how packages and findings are classified, and a refusal to run under a toolchain newer than the binary.

Fixed

  • A load refuses a newer toolchain instead of answering wrongly (#35). go/packages asks for complete types for
    every direct import of a package it type-checks from source, and fills
    them from export data; when that read fails it calls log.Fatalf from
    inside its importer, which no caller can recover — an MCP server dies
    with it. A go command from a newer release than the Go this program was
    built with is exactly such a failure. The load now stops before
    starting, with an error naming both versions and what to do about it.
    Measured on steward with a caliper built by go1.26.7 against go1.27.1's
    go list: before, the process exited 1 with internal error: package "math/rand/v2" without types was imported from "google.golang.org/grpc/internal/transport"; now it returns the
    refusal. The default load is refused too, for a different reason: it
    completes under skew, but the older type checker cannot process the
    newer toolchain's sources, so the same run left 15 of steward's 117
    packages in the SSA program and reported no error — a call graph
    missing 87% of the module. Unskewed behaviour is unchanged, 117 of 117
    packages before and after, in both modes. Export data unreadable for
    another reason, a truncated build cache say, can still reach the same
    fatal; only the predictable cause is guarded.

  • context_misuse no longer flags a detached context in a deferred
    cleanup
    (#10). A deferred Rollback, Close or Unlock runs when
    the function returns, including because ctx was cancelled or timed
    out, so passing ctx there skips the cleanup exactly when it is needed
    most — pgx's Rollback returns at once on a done context and leaves
    the connection in a transaction. context.Background() inside a
    defer, whether deferred directly, inside a deferred closure, or as
    the parent of a WithTimeout there, is the recommended idiom and is
    not reported. context.TODO() in a defer still is. Measured on the
    repositories the issue was filed from: gameflow goes from 19 findings
    to 1, and all 9 removed sites are deferred cleanups (four
    tx.Rollback in the trader store, a migration Rollback, a
    conn.Close, an advisory-unlock Exec, two deferred metric
    decrements); govm's 5 and voya's 0 are unchanged.

  • A deferred call is reported once, not twice. The CFG lists a defer
    statement in the block that registers it and again in a BlockDefer
    block where it runs, which is how a path query sees a deferred
    Unlock or Close on every exit; rules that scan nodes to report on
    them saw both copies. On govm, unchecked_error with include_defers
    gave 162 findings of which 8 were exact duplicates; on gameflow,
    context_misuse gave 19 of which 9 were. Both are now 0, with the
    same unique findings (154 and 1 respectively). Affects
    context_misuse, unchecked_error, goroutine_leak and
    sql_injection; lock_violation is unchanged, since its loop feeds
    path queries that need both copies.

  • A dependency with a type error inside a function body no longer
    drops its importers from the SSA program
    (#30). The program was
    built with ssautil.AllPackages, which skips every package go/packages
    marks IllTyped — and IllTyped propagates, so one dependency with
    any error removed every loaded package importing it, transitively,
    from the call graph, silently: the loaded package's own type-check was
    clean and it stayed in Engine.Packages(). On steward, grpc's
    internal/transport fails on go-plus 1.27.1 with undefined: http2.TrailerPrefix inside a function body, and 36 of 117 packages
    had no SSA package and no call-graph nodes. Now a package is skipped
    only for an error outside a function body — a parse error, a type
    error in a declaration, export data that could not be read — or for
    importing such a package; one whose errors are all inside function
    bodies is created from its types alone, so its declarations are there
    for its importers and its unfinished bodies never reach the SSA
    builder. steward: 117 of 117 packages in the SSA program under the
    default load, with the same 13,119 within-module edges as under
    DepsFromExportData, edge for edge. Pinned on the fixture by a
    dependency with a body-only error (importer analysed, calls its clean
    function; the dependency's functions have no bodies) and one with a
    declaration error (importer dropped, as before), under both loads.
    An error is placed by its source position, so //line directives in
    generated code cannot move a declaration error into a body; and the
    notice go/packages adds when caliper was built with an older Go than
    the go command it runs does not count as an error.
    Default-load timing on steward is unchanged (load 1.6–1.7 s, call
    graph 3.8–4.0 s over the now-complete 117 packages; one run of three
    hit 11.4 s on a busy machine).

Added

  • EngineOptions.DepsFromExportData type-checks the loaded packages
    from source and takes every dependency's types from the compiler's
    export data (go list -export) instead of parsing and type-checking
    its source. It drops packages.NeedDeps from the load; under it the
    SSA program is built with the loaded packages from syntax and every
    dependency from types alone (#28, for trendvidia/alidade). Default
    false, everything unchanged.

    Measured on steward (117 packages, 1,293 reached with dependencies,
    warm build cache, three interleaved runs each, median and range):

    Load SSA + CHA call graph
    default 1.93 s (1.4–2.5) 3.46 s (2.5–5.1)
    DepsFromExportData 0.99 s (0.98–1.3) 0.36 s (0.29–0.50)

    The first split load on a tree whose dependencies are not yet in the
    build cache pays their compilation once (3.4 s on steward, where they
    were).

    Limitation: no interprocedural analysis through dependency code. A
    dependency's SSA functions have no bodies, so no call-graph edge leaves
    one (steward: 8,150,734 such edges by default, 0 under the option) and
    no taint flows through one. Calls from a loaded package into a
    dependency are still edges to that function; dynamic calls resolve
    among fewer candidates, since a package reached only through a
    dependency carries just the declarations its importers reference
    (steward: 176,011 edges into dependencies by default, 22,519 under the
    option). A dependency that does not compile on the host's toolchain has
    no export data and is type-checked from source with its bodies skipped;
    it is still bodiless in the SSA program, and the loaded packages that
    import it are analysed. Pinned by TestDepsFromExportData_* on a
    fixture with a third-party dependency (github.com/google/uuid) and a
    local one that type-checks but does not compile: the call-graph edges
    among the loaded packages are identical under both modes, edge for
    edge.

    On steward the within-module graph is not identical but a strict
    superset: 13,119 edges over all 117 packages against 6,366 over 81,
    because grpc's internal/transport has a type error inside a function
    body on this toolchain (undefined: http2.TrailerPrefix), the default
    load sees it and drops the 36 packages importing it from the SSA
    program, and the split load never type-checks that body. The 6,366 are
    all present under the option and every extra edge touches one of the
    36. The default load's drop is #30.

Caliper v1.4.0

Choose a tag to compare

@trendvidia trendvidia released this 11 Sep 11:23

Caliper v1.4.0

One addition, for library consumers that need a graph of a repository to not depend on the machine that built it.

Added

  • EngineOptions.Env appends KEY=VALUE entries to the load's environment, after the process environment and the engine's own settings, and survives the GOWORK=off retry. Its purpose is a build configuration the host is not: GOOS=linux, GOARCH=amd64 load the files behind //go:build constraints a tag cannot satisfy, since GOOS and GOARCH are not tags. Empty by default; DefaultOptions does not touch it. (#25, for belluikit/alidade#43)

CI

  • golangci-lint pinned to v2.13.2, built for the runner's Go 1.27; v2.10.1 panicked reading its export data. (#27)

Caliper v1.3.0

Choose a tag to compare

@trendvidia trendvidia released this 29 Aug 10:15

Caliper v1.3.0

The interprocedural surface could not run against real code. This release fixes that, and adds what a library consumer needs to build on it.

Fixed — call-graph tools work on real code

  • They no longer crash the server. Every interprocedural tool — find_call_sites, find_taint_flow, anything reaching Engine.InterProcedural() — panicked on any real workspace, exiting the MCP server with code 2 rather than returning an error. The packages.Config load mode set NeedDeps but not NeedImports; NeedDeps only propagates the mode into packages already in Imports, and Imports itself is populated by NeedImports. Without it every pkg.Imports was empty, so ssautil.AllPackages never created the dependency packages and prog.Build() panicked on the first unsatisfied import — which is any package importing so much as context. Measured before the fix: 100% of loaded packages had empty .Imports across four repositories. (#11)

  • A program with uncreated dependencies is refused, not built. prog.Build() spawns a goroutine per package, so its panics cannot be recovered by the caller and take the process down. The precondition is now checked up front and reported as an error naming the missing import. (#11)

  • find_call_sites returns results. It reported total: 0 for every input on every repository. FindCallSitesByPattern matched the pattern against function declarations while the pattern compiler only accepts expressions, so the match loop was unreachable for every valid input. It now matches call expressions across the corpus — as the tool's own documented $x.Close() example always implied — then enriches the result with call sites the call graph knows about for each resolved callee. Measured after the fix: 138 hits for $x.Close() on protolsp, 229 for $x.String() on protocompile, 384 for $x.Close() on govm, all previously 0. (#18)

Both defects were invisible to the test suite because every interprocedural fixture was import-free (package testpkg with a bare caller/callee pair). Fixtures now import stdlib packages, so this class of regression fails the suite.

Added — for library consumers

  • EngineOptions.Tests includes each package's test files in the load (default false). Test files are roughly half the Go source in some repositories and are where intent is documented, so a consumer building a picture of a codebase usually wants them while a linter reporting on shipped code does not. Including tests takes protolsp from 1,560 to 4,057 workspace functions in the call graph, and protoregistry from 743 to 1,269.

  • Engine.GeneratedFiles, Engine.IsGeneratedFile, InterProceduralContext.IsGeneratedFunc identify generated code. EngineOptions.SkipGenerated filters AST traversal only — the SSA program and call graph are built from every loaded package and are unaffected by it. That asymmetry was undocumented and is a trap for anything walking the call graph: generated functions are 35% of protolsp's workspace functions and 45% of protoregistry's with SkipGenerated at its true default. Behaviour is unchanged, now documented and pinned by a test, and filterable by consumers that do not want it.

Build

  • golangci-lint excludes SA5011 in test files. The guard is if x == nil { t.Fatal(...) }, and the CI runner's staticcheck does not derive that testing.T.Fatal terminates, so it reported the guarded deref as a nil dereference across 40 sites in 8 files. Scoped to _test.go and that one check.

Install

gh release download v1.3.0 --repo trendvidia/caliper --pattern 'caliper-darwin-arm64'
chmod +x caliper-darwin-arm64

Verify with checksums.txt. caliper-* is the MCP server; caliper-analyze-* is the single-shot CLI.

Caliper v1.2.0

Choose a tag to compare

@trendvidia trendvidia released this 04 Aug 07:05

Caliper v1.2.0

Cross-method lifecycle rules (issue #8), plus the go install version-reporting fix that landed after v1.1.1. Caliper now exposes 26 MCP tools across seven categories.

Added — cross-method lifecycle rules

The bellui pre-v1 audits showed the properties worth checking in a GUI toolkit are cross-method, while all analyses were function-local. Three new capabilities close that gap:

  • check_paired_calls — type-scoped pairing rule: a match of first_pattern in any method of a type requires a match of second_pattern somewhere in the type's method set, or in a companion type its methods construct (the widget→renderer link). Pairs by field identity when both patterns share a metavariable bound to a receiver field, lenient otherwise — $x.Start() on one field is not excused by $x.Stop() on a different one, but an unresolvable binding never produces a false positive.
  • find_stale_capture — capture-at-create vs re-read-at-refresh: exported fields copied in a capture method (CreateRenderer) and never re-read in the re-read method (Refresh) of the type or its constructed companions.
  • find_pattern gains inside / not_inside enclosing-context filters: "go", "defer", "callback_of:<pattern>", "call:<pattern>". Expresses goroutine-context call discipline like "widget mutation inside go/time.AfterFunc contexts must be wrapped in ui.Do(...)" in one call. Context is lexical: code in a named function called from a go statement elsewhere does not count as "inside go".

Both new tools are whole-program analyses and honor //caliper:ignore suppressions and .caliper.pxf severity overrides like every other check.

Fixed

  • go install-ed binaries report their version instead of dev. go install pkg@version builds from the module cache and records no vcs.* settings, so resolveVersion now falls back to Main.Version. Local builds keep reporting dev-<rev>[-dirty].
  • Download instructions that actually work: docs/getting-started.md now documents gh release download (with a raw-curl asset-endpoint equivalent and a checksums.txt verification step) instead of the browser-download URL, which 404s for private repos.

Install

Binaries for macOS / Linux / Windows × amd64 / arm64, built CGO_ENABLED=0 with -trimpath, stamped with -X main.Version=v1.2.0. caliper is the MCP server; caliper-analyze is the single-shot SARIF/JSON CLI.

gh release download v1.2.0 --repo trendvidia/caliper -p caliper-darwin-arm64
chmod +x caliper-darwin-arm64 && sudo mv caliper-darwin-arm64 /usr/local/bin/caliper

Or:

brew install trendvidia/tap/caliper   # macOS
go install github.com/trendvidia/caliper/cmd/caliper-mcp@v1.2.0

Full details in CHANGELOG.md.

v1.1.1

Choose a tag to compare

@trendvidia trendvidia released this 29 Jul 11:19

Caliper v1.1.1

A fix release for v1.1.0, plus the prebuilt binaries the docs have always promised.

Fixed — go install works again

v1.1.0 was tagged at a commit whose go.mod still carried two replace directives, and Go refuses to install any module whose go.mod contains them:

The go.mod file for the module providing named packages contains one or
more replace directives.

So the command documented in the README failed outright, for both @latest and @v1.1.0. Homebrew was never affected — it builds from the release tarball, where the replaces resolved against the in-tree vendor/. v1.0.21 had no replaces and installed fine, so this regressed between there and v1.1.0.

If you are on v1.1.0, there is nothing to reinstall unless you use go install — the binaries Homebrew produced from v1.1.0 are correct.

Added — real release binaries

docs/getting-started.md has always documented downloadable binaries for six platforms, but no release ever carried a single asset and the documented curl URL returned 404. That's fixed: this release ships them.

caliper is the MCP server. caliper-analyze is the single-shot SARIF/JSON CLI added in v1.1.0. Both are built CGO_ENABLED=0 with -trimpath, stamped with -X main.Version=v1.1.1, for macOS / Linux / Windows × amd64 / arm64. checksums.txt accompanies them.

trendvidia/caliper is a private repo, so assets need an authenticated download — a plain curl against a github.com/.../releases/download/... URL returns 404 even with a token, because those URLs authenticate by session cookie rather than by header. Use gh:

gh release download v1.1.1 --repo trendvidia/caliper -p caliper-darwin-arm64
chmod +x caliper-darwin-arm64 && sudo mv caliper-darwin-arm64 /usr/local/bin/caliper

Downloaded binaries report v1.1.1. Binaries obtained via go install still report dev — a separate bug in resolveVersion, fixed on main after this release was cut.

Also

  • caliper-analyze is documented in the README and getting-started install sections. It shipped in v1.1.0, but only caliper-mcp was mentioned.
  • vendor/ is gone from the repo (505 files, 6.2 MB). It existed only because protowire-go was a private module and Homebrew's sandbox couldn't fetch it; that repo is public now. Release tarballs from v1.1.1 onward are no longer self-contained and resolve dependencies from the module proxy.
  • go.mod has zero replace directives. protowire-go is pinned to v1.3.2 and google.golang.org/protobuf to stock upstream v1.36.11.
  • The README's GOPRIVATE note blamed the private protowire dependency. protowire-go is public now; GOPRIVATE is still needed, but only because trendvidia/caliper itself is private.

Install

brew install trendvidia/tap/caliper

Or from source:

GOPRIVATE=github.com/trendvidia/* go install github.com/trendvidia/caliper/cmd/caliper-mcp@v1.1.1
GOPRIVATE=github.com/trendvidia/* go install github.com/trendvidia/caliper/cmd/caliper-analyze@v1.1.1

Full changelog: v1.1.0...v1.1.1

v1.1.0

Choose a tag to compare

@trendvidia trendvidia released this 29 Jul 10:49

Caliper v1.1.0

Three months and 60 commits since v1.0.21. Headline: a new single-shot CLI for CI use, a large noise-reduction pass on find_unchecked_error, cross-reconnect result caching, and a return to stock upstream mcp-go.

New — caliper-analyze, a single-shot CLI

The MCP server is built for LLM-driven interactive use; its session model is pure overhead when a consumer just wants one analysis and a report. caliper-analyze spawns, analyzes, writes SARIF to stdout, and exits — which is what CI gates, reviewdog, and GitHub Code Scanning uploads all actually want.

caliper-analyze -workspace . -format sarif > results.sarif

Flags: -workspace, -rules (comma-separated rule kinds; defaults to the PR-default set), -format (sarif or json), -version.

Analyzer signal

  • find_unchecked_error — noise against the Go standard library dropped 1201 → ~800 hits (33%), via a built-in stdlib infallibility allowlist, a project-local infallible {} config block, a CFG-based cleanup-path detector, and a fmt.State recognizer that stops flagging writes inside Format(s fmt.State, c rune) methods (~50+ stdlib call sites).
  • find_race_condition — now recognizes x++/x-- as writes. Goroutines capturing and incrementing a counter were silently producing no finding at all. Also honors Go 1.22+ per-iteration loop scoping, so loop-variable captures in a Go ≥ 1.22 module are no longer flagged unless the closure itself writes the variable.
  • find_slice_append_race — recognizes mutex-protected appends.
  • find_lock_violation — no longer flags a missing Unlock when the method value is returned, passed, or assigned; the obligation moved to the caller.
  • find_error_swallow — re-implemented on top of find_unchecked_error's engine, inheriting its allowlist, cleanup-path detection and writer-arg transitivity while keeping its own value-use severity escalation. Tool name and input schema are unchanged, but it is stricter than before: bare f.Close() discards are now flagged by default where the old implementation required an explicit opt-in.

Configuration

  • New infallible {} block in .caliper.pxf accepting methods, functions, writers and disable_* counterparts, with * glob support.
  • Config now resolves per analysis path — each tool call walks up from its target looking for .caliper.pxf, instead of a single boot-time load.

Performance

  • Persistent result cache at $TMPDIR/caliper-cache/, keyed on (tool, path, args, source fingerprint), with a 100 MB mtime-LRU cap (CALIPER_CACHE_MAX_BYTES). Repeat calls on unchanged source skip the cold packages.Load — this survives the MCP reconnects that Claude Code's transport causes after every tool batch.
  • Engine cache lock fix — a fan-out of N parallel calls on a cold path no longer triggers N concurrent packages.Load invocations.

Transport

  • mcp-go back on stock upstream v0.57.0, un-patched and un-forked. The interim fork's only patch — panic recovery in the stdio tool-call worker — landed upstream in mark3labs/mcp-go#882, and upstream's version replies INTERNAL_ERROR rather than dropping the message. The replace also leaked through go.work into sibling repos, which were compiling ~v0.49 code under a v0.57.0 label.
  • stderr tee to $TMPDIR/caliper-mcp-YYYY-MM-DD.log with timestamps, for diagnosing MCP disconnects.

Build

  • CI runs gofmt + vet + build + test -race + golangci-lint on a self-hosted runner pinned to the go-plus toolchain.
  • -ldflags "-X main.Version=..." support in both binaries, falling back to dev-<vcs.revision>[-dirty] from runtime/debug.BuildInfo.
  • Migrated from trendvidia/protowire to protowire-go.

Install

brew install trendvidia/tap/caliper

Or from source:

go install github.com/trendvidia/caliper/cmd/caliper-mcp@v1.1.0
go install github.com/trendvidia/caliper/cmd/caliper-analyze@v1.1.0

Full changelog: v1.0.21...v1.1.0