• v0.32.1 1e7cf2c751

    code-index v0.32.1
    All checks were successful
    CI / cargo fmt (push) Successful in 51s
    CI / OSS corpus tier-3 scale (nightly) (push) Has been skipped
    CI / Grammar rebuild from source (nightly) (push) Has been skipped
    CI / guest crates (fmt, clippy, doc) (push) Successful in 1m24s
    CI / CI lane wall-clock headroom (push) Successful in 1m29s
    CI / cargo doc (intra-doc links) (push) Successful in 6m4s
    CI / cargo deny (push) Successful in 7m14s
    CI / cargo test (abi, 32-bit + wasm32) (push) Successful in 7m47s
    CI / cargo clippy (push) Successful in 7m48s
    CI / cargo check (MSRV 1.98) (push) Successful in 8m11s
    CI / cargo check (windows-gnu) (push) Successful in 8m22s
    CI / OSS corpus (tier 1) (push) Successful in 36m32s
    CI / cargo test (push) Successful in 49m47s
    CI (Windows) / fmt + clippy + build + test (windows) (push) Successful in 1h1m16s
    CI / cargo test (daemon transport) (push) Successful in 22m6s
    CI / Plugin path cost + pool throughput (nightly) (push) Has been skipped
    Release Build / Generate Version (push) Successful in 29s
    Release Build / Required CI green (push) Successful in 1m5s
    Release Build / Build linux-aarch64 (push) Successful in 12m51s
    Release Build / Build linux-x86_64 (push) Successful in 15m40s
    Release Build / Build linux-x86_64-musl (push) Successful in 15m59s
    Release Build / Pack the Ruby language package (push) Successful in 50s
    Release Build / Pack the XAML reference package (push) Successful in 1m2s
    Release Build / Pack the TimeLine package (push) Successful in 1m11s
    Release Build / Pack the Svelte language package (push) Successful in 1m20s
    Release Build / Build windows-x86_64 (push) Successful in 23m57s
    Release Build / Windows archive smoke (msvc) (push) Successful in 26s
    Release Build / Create Forgejo Release (push) Successful in 5m50s
    Stable

    buildagent released this 2026-09-26 08:08:16 +02:00 | 94 commits to master since this release

    code-index v0.32.1

    Build: v0.32.1+908

    Installer: install.sh and checksum. Both come from this release commit. To pin the installed binaries too, run it with --tag v0.32.1.

    Windows installer: install.ps1 and checksum, from the same release commit. It installs AND updates — the same code does both — verifies the archive against its published .sha256 before unpacking, refuses hostile archive members (absolute paths, .. escapes, symlinks and reparse points, which Expand-Archive does not protect you from), and grants nothing when it installs a package: the bytes go into the machine's store and the approving line is PRINTED for you to run from inside a project. -Help lists the flags; -Tag v0.32.1 pins the binaries.

    Four-binary release: code-index-mcp (MCP stdio bridge — the usual entry), code-index-daemon (long-lived watcher + RPC server, auto-spawned), code-index-plugin-host (bounded worker for plugin packages, spawned by the daemon), and code-index (CLI: init, index, watch, doctor, link, plugin).

    Changes

    v0.32.1 fixes the precision and recall defects that two independent reviews found
    in v0.32.0. Binds were measured one by one against v0.32.0 on nine pinned repositories.

    • pytest fixtures bind again. v0.32.0 correctly stopped fixture parameters
      from binding as ordinary names, but that also removed every fixture edge. For
      example, Flask's conftest.py::client fell from 305 refs to 0. A parameter
      of a test or fixture now binds the @pytest.fixture / @fixture it names,
      including name= aliases. The lookup order is the consumer's class, its module,
      then the nearest conftest.py in the same or an ancestor directory, as pytest
      does. A fixture never resolves to itself. Two definitions at the deciding
      level refuse to bind. Calling the parameter binds nothing. Names supplied by
      @pytest.mark.parametrize are not treated as fixtures, unless they are routed
      back to a fixture with indirect=. Flask gains 786 fixture edges, and 237
      parameter uses that had bound unrelated class members now bind their fixture.
      Not modelled: pytestmark parametrization, pytest_plugins, and built-in
      fixtures.
    • Fewer wrong binds.
      • A name's own import decides a use only when that import's target was
        proven, is a free symbol rather than a member, and is visible at the use.
        Rust inline modules do not inherit the parent's use, and a Python nested
        import shadows the outer one.

      • Aliased-import and local-scope refusals follow the scope they occur in.

      • JavaScript/TypeScript body bindings are function-scoped. Arrow and one-line
        parameters belong to their function, and a generic constraint's parameters
        are no longer read as the function's own.

      • Every Rust parameter is local.

      • Measured against v0.32.0:

        Repository Wrong binds removed Correct binds lost Correct binds restored
        rust-analyzer 92 7 (glob re-exports) 0
        Zod 25 1 0
        Django 9 0 0
        Express 4 0 0

        Django's one correct loss during development, a ProxyModel bind, is
        restored by the innermost-import rule. Flask and Django also lose 8 and
        1 wrong binds from calls of a fixture's value and a mock-injected
        parameter. Guzzle,
        Sinatra and Dapper are unchanged. No wrong bind was added in any
        repository. Every change was read at source.

    • Python scope is decided at extraction. The resolver no longer re-parses
      every Python file on every pass; Django's local-scope step went from 4.4 s to
      0.4 s. Comprehensions, walrus inside comprehensions, match captures and
      PEP 695 type parameters now have their own scopes. A column mismatch that
      broke lines containing non-ASCII text is fixed.
    • Cost. The resolver statements added in v0.32.0 were rewritten. Every
      pinned repository is within +2.0% SQLite VM steps of the previous baseline
      (Flask −2.4%).
    • Clearer failures.
      • An index created by a newer build is refused by name (project_not_available
        with schema_skew, and the repair).
      • A daemon's fatal error now also reaches daemon.log.
      • code-index query exits 1 only when every batch entry refused. It warns
        when the answering server or daemon is a different build or came from
        PATH, and its --log level now reaches the server it spawns.
    • review_diff grades deleting a crate- or package-internal item as
      low internal_symbol_deleted, not an API break, and untested_change now says
      which shapes it cannot see.

    Upgrade

    Schema 71 marks every code file for one reparse. That takes about a minute on a
    3,000-file repository and a few seconds on a small one. Upgrading from 0.31.x
    or older runs schemas 70 and 71 in the same single reparse. Text files keep
    their cache.

    After upgrading, run code-index index once in each project, or let an MCP
    session's daemon finish its reconcile. Answers are incomplete until it does.
    Without a daemon, a one-shot code-index query answers warming_up with a
    reconcile_pending block (converges_on_retry: false). Retrying alone does not
    finish the reconcile, because resolution is one transaction. Making it
    resumable is tracked in #301.

    Known limits

    • #300: six wrong-bind shapes that predate this release, found by the final review.
    • 21 Django refs whose value comes from apps.get_model(...) or import_module
      stay unresolved. The one narrow rule we tried added 12 wrong binds, so it
      was not shipped.

    Validation

    The release tree passed CI on Linux (fmt, clippy, MSRV, windows-gnu check,
    32-bit and wasm32 ABI tests, cargo deny, guest crates, strict rustdoc, the
    workspace suite, the daemon-transport suite and the tier-1 OSS corpus job) and
    on native Windows. Locally the same tree passed 4,172 workspace tests, 876
    daemon-transport tests, all 14 CI corpus suites (including corpus_mutation,
    which deletes and renames definitions and requires 0 rebinds across 7,850
    sites), the tier-3 ratchet, and seven precision fixtures with 0 phantoms.

    Every fix was reviewed independently before release, and every new test's
    mutation was run: each fails when its fix is removed. The corpus change against
    v0.32.0 was measured bind for bind on all nine pinned repositories.

    Linux ARM64 is cross-built, and no native ARM64 runtime test is available.
    This release does not provide macOS or Windows ARM64 archives.

    Downloads

    Four platforms are built for every release. The table below says which ones THIS release published.

    Platform Archive
    Linux x86_64 (glibc) code-index-v0.32.1-linux-x86_64.tar.gz
    Linux x86_64 (static/musl) code-index-v0.32.1-linux-x86_64-musl.tar.gz
    Linux ARM64 code-index-v0.32.1-linux-aarch64.tar.gz
    Windows x64 code-index-v0.32.1-windows-x86_64.zip

    XAML plugin package

    de.h-dv.xaml-0.2.0.cip makes .xaml files carry symbols and references instead of being text-only: x:Class and Click= handlers bind into the paired C# code-behind, x:Name becomes a searchable declaration, and {Binding …} stays unresolved because no bridge can reach it.

    It is NOT installed by installing code-index. Packages are installed into a user-controlled store and approved per project, and installing grants nothing — enable is the grant.

    Download de.h-dv.xaml-0.2.0.cips too and keep the .cips beside the .cip. Packages are signed and an unsigned one is refused (signature_missing) with no flag that accepts it. You do NOT need to anchor us first: this release's publisher key is compiled into the binary, so the install below works on a machine that has anchored nobody. code-index plugin trust list shows it, marked [BUILTIN], and code-index plugin trust remove sha256:1cb03259a8c870b6db02360abd9351e17e67724d1f8c3509d85c4bf6b06fa72c withdraws it — see About that key.

    code-index plugin install de.h-dv.xaml-0.2.0.cip --sha256 sha256:7b572f5cc32ffcd5af550900a451bbef37e255804776d7e3e3e71d2dd7d0aa79
    code-index plugin check   sha256:7b572f5cc32ffcd5af550900a451bbef37e255804776d7e3e3e71d2dd7d0aa79
    code-index plugin enable  sha256:7b572f5cc32ffcd5af550900a451bbef37e255804776d7e3e3e71d2dd7d0aa79 \
        --capabilities bridge_source \
        --bridges 'de.h-dv.xaml/xaml:type->csharp:class,de.h-dv.xaml/xaml:call->csharp:method'
    

    The digest above is the one this release packed and it is what --sha256 pins; de.h-dv.xaml-0.2.0.cip.digest.txt carries it and the extraction identity. A digest that changes between releases means the package changed — re-pin, and read the notes.

    Only .xaml is claimed by this package. The TimeLine package below claims .dataset, .xsql, .shd and .lgd, and the Svelte package below claims .svelte; all other markup remains text-only (searchable, no symbols).

    TimeLine plugin package

    de.h-dv.timeline-0.1.0.cip makes the four TimeLine definition formats carry symbols and references instead of being text-only: .dataset and .xsql through one language, .shd and .lgd through another, both out of a single sandboxed extractor. References resolve INSIDE a definition file; nothing in v1 crosses into the C# that loads it.

    It is NOT installed by installing code-index. Packages are installed into a user-controlled store and approved per project, and installing grants nothing — enable is the grant.

    Download de.h-dv.timeline-0.1.0.cips too and keep the .cips beside the .cip. The same signature rules apply as for the XAML package above, and the same key signs both.

    code-index plugin install de.h-dv.timeline-0.1.0.cip --sha256 sha256:150ceb22ed9757a660d5a2bf58b80c81b60043cc419e5da5dde559d5d6fc9c9f
    code-index plugin check   sha256:150ceb22ed9757a660d5a2bf58b80c81b60043cc419e5da5dde559d5d6fc9c9f
    code-index plugin enable  sha256:150ceb22ed9757a660d5a2bf58b80c81b60043cc419e5da5dde559d5d6fc9c9f \
        --capabilities same_file_candidate,exported_candidate
    

    No --bridges, and that is the package saying so: it declares none, so there is nothing to grant. The digest above is the one this release packed and it is what --sha256 pins; de.h-dv.timeline-0.1.0.cip.digest.txt carries it and the extraction identity.

    The 5 fixtures shipped inside it are synthetic and modelled on the worked examples in the two format specifications. They carry no customer definition.

    Ruby plugin package

    de.h-dv.ruby-0.7.0.cip is the compiled-in Ruby extractor as an EXTERNAL, sandboxed package: tree-sitter-ruby loaded at runtime, the extractor compiled to wasm32-unknown-unknown and byte-reproducible from crates/guest/ruby/, and the same symbols, references, visibility and Rails association names the built-in produces.

    It claims .rbx, not .rb. The compiled-in Ruby plugin owns .rb, .rake, .gemspec, Rakefile and Gemfile, and a package may not claim a file a built-in claims. So installing this changes NOTHING about how your .rb files are indexed — it is the migration proof for running a full language out of a package, published so it can be read and run rather than described. Do not install it expecting to replace built-in Ruby support.

    It is NOT installed by installing code-index. Packages are installed into a user-controlled store and approved per project, and installing grants nothing — enable is the grant.

    Download de.h-dv.ruby-0.7.0.cips too and keep the .cips beside the .cip. The same signature rules apply as for the packages above, and one key signs every package in this release.

    code-index plugin install de.h-dv.ruby-0.7.0.cip --sha256 sha256:5779e34c30d88e213d71091b725b2f3363dfdac53c9f58b85604577db08ad9e3
    code-index plugin check   sha256:5779e34c30d88e213d71091b725b2f3363dfdac53c9f58b85604577db08ad9e3 --derived-names
    code-index plugin enable  sha256:5779e34c30d88e213d71091b725b2f3363dfdac53c9f58b85604577db08ad9e3 --derived-names \
        --capabilities same_file_candidate,exported_candidate,reachability_anchor,qualified_candidate,type_position_candidate,member_candidate
    

    --derived-names is required on check as well as on enable, and without it check REPORTS FAILED. Rails' has_many :posts emits a type reference named Post at the span of the literal :posts, so the name was not copied out of the source it points at; validation is all-or-nothing per file, so the authority is what admits the whole file rather than the one row. Withhold it and you keep the package with every Rails file refused — that is a supported answer, not a broken install.

    No --bridges: Ruby resolves inside its own language, so the package declares none. The six capabilities above are exactly what its manifest requests and enable refuses a grant wider than the request; grant fewer and the index stays searchable but resolves less, because every candidate pool a reference could be admitted to is one of them.

    The digest above is the one this release packed and it is what --sha256 pins; de.h-dv.ruby-0.7.0.cip.digest.txt carries it and the extraction identity.

    The 5 fixtures shipped inside it were generated from the COMPILED-IN Ruby extractor, by a different author from the port they grade, and plugin check compares the package's facts against them exhaustively.

    Svelte plugin package

    de.h-dv.svelte-0.1.0.cip makes .svelte files carry symbols and references instead of being text-only: the TEMPLATE half of a single-file component — markup, mustaches, {#snippet} declarations and {@render} uses — out of one sandboxed extractor over the tree-sitter-svelte-ng grammar.

    It claims .svelte and NOTHING ELSE. .svelte.ts and .svelte.js are deliberately not claimed and this package declares no [[displaces]]: those files are Svelte 5 runes modules, valid TypeScript and valid JavaScript, which the built-in plugins already index correctly. Claiming them would take files away from a producer that reads them in order to read them worse.

    It is NOT installed by installing code-index. Packages are installed into a user-controlled store and approved per project, and installing grants nothing — enable is the grant.

    Download de.h-dv.svelte-0.1.0.cips too and keep the .cips beside the .cip. The same signature rules apply as for the packages above, and one key signs every package in this release.

    code-index plugin install de.h-dv.svelte-0.1.0.cip --sha256 sha256:4b10a478532d67f3694995373e1a64d397611af5c4f2615c085b006628b3322f
    code-index plugin check   sha256:4b10a478532d67f3694995373e1a64d397611af5c4f2615c085b006628b3322f
    code-index plugin enable  sha256:4b10a478532d67f3694995373e1a64d397611af5c4f2615c085b006628b3322f \
        --capabilities same_file_candidate
    

    One capability, and that is the package asking for exactly what it can use. Every symbol this version emits is a {#snippet}, whose visibility is file: reachable by name anywhere in its own component and nowhere else. exported_candidate would admit an empty set, so it is not requested — and enable refuses a grant wider than the manifest asks for, so the line above cannot be padded. No --bridges: this package declares none.

    The digest above is the one this release packed and it is what --sha256 pins; de.h-dv.svelte-0.1.0.cip.digest.txt carries it and the extraction identity.

    The 4 fixtures shipped inside it are synthetic: one positive control in which every rule fires, two that are bait for all of them with an empty expectation, and one malformed source. None carries anybody's component.

    About that key

    sha256:1cb03259a8c870b6db02360abd9351e17e67724d1f8c3509d85c4bf6b06fa72c is a long-lived Ed25519 key held as a secret in this project's CI, used to sign released packages automatically, and it is compiled into the binaries in this release. It signs every package above. That trusts this project's release pipeline, not only its maintainers: anyone who can run a release workflow here can produce a package that verifies under it. It adds nothing you had not already granted — the bytes are inside the program you are running, so forging that anchor means forging the binary — but you are entitled to know it is there before a package installs rather than after.

    There is no expiry — deliberately, because offline installations must not break on a timer — and withdrawal is on your side and works: code-index plugin trust remove sha256:1cb03259a8c870b6db02360abd9351e17e67724d1f8c3509d85c4bf6b06fa72c. A compiled-in key has no file to delete, so that writes one instead, carrying denied = true; the key stops verifying at the next load, plugin trust list shows it marked [DENIED], and deleting that file (the command prints its path) is the only undo. If the key is ever rotated, the new fingerprint is published here and in tests/packages/first-party.fingerprint, and a build that predates the rotation will not trust it.

    You may anchor it yourself instead — code-index plugin trust add code-index-publisher.pub --fingerprint sha256:1cb03259a8c870b6db02360abd9351e17e67724d1f8c3509d85c4bf6b06fa72c --name 'a label you choose', with code-index-publisher.pub from this release's assets. Your file replaces the compiled-in entry, and the --name is a label YOU choose: nothing in the package, the key file or the signature can name its own publisher.

    macOS is not currently built. No x86_64-apple-darwin or aarch64-apple-darwin archive is published — macOS users should build from source (cargo build --release). This is a deliberate deferral, tracked in #59; no date is promised.

    Wire into Claude Code

    { "mcpServers": { "code-index": { "command": "/usr/local/bin/code-index-mcp", "args": ["--root", "/path/to/your/project"] } } }
    

    SHA256 checksums (.sha256 files) available for every archive.

    Downloads