Skip to content

v0.4.0

Latest

Choose a tag to compare

@github-actions github-actions released this 07 Sep 19:40
· 8 commits to main since this release
8f3f38a

v0.4.0

v0.4.0 is a minor release, not a patch, by the same rule v0.3.0 and v0.3.1 were versioned under: it adds new public capability (two new MCP tools, a new CLI subcommand, new search modes, new scanner and embedding configuration) and changes on-disk and query behavior in ways an existing user will notice. It also carries the LSP Pass 1 memory and work fix and the scanner exclude fix that were needed to index a 7,000-file repository at all, and restores cross-file Tree-sitter Calls edges for repositories scanned without LSP, which #859 had silently switched off.

Upgrade if you want bounded, role-aware task context from search; structural convergence proofs; repository-scoped scanner excludes that actually match; explicit control over the embedding backend in source builds; or the LSP Pass 1 fix. Read "Behavior changes" first: the graph cache schema moved from 25 to 27, so the first scan after upgrading re-extracts every root.

New capabilities

Bounded, role-aware task context and convergence proofs in search

Before: asking RNA for "the context I need to implement X" returned a ranked list of records under a byte budget with no notion of what role each record played. Whether the result set contained the thing to edit, the tests that constrain it, and the boundary it must not cross was left to the caller to work out, and a tight budget could silently drop the record that mattered. Structural questions such as "where do these two call paths meet before they reach this boundary" had no first-class answer; the caller had to walk neighbors/path results by hand and could not tell an absent edge from an unindexed one.

After: search accepts context_mode: task (and the experimental, non-mutating context_mode: graph-delta-beta). Task mode selects records by role (editable source, constraining tests, boundaries), tracks evidence quality per record, derives obligations from the query rather than from any one language's vocabulary, and degrades deterministically under a budget: metadata is compacted before records are dropped, and what was omitted is accounted for rather than silently missing. Every record carries compact, validated hydration handles so a follow-up fetch is exact.

search gains mode: convergence, and MCP gains a dedicated convergence tool. Given two or more explicitly bound source nodes, optional before boundary, direction, edge_types and depth, it returns deterministically ranked common nodes with one complete witness path from every source and an onward witness to the boundary. It fails closed: unresolved or ambiguous selectors, unknown Calls coverage, an unreachable boundary, or an empty proof produce a not_injectable result with lexical_fallback: disabled — never a lexical guess presented as structure.

Extractors now emit owner-qualified scoped identities (a method is Owner.method, not method) while preserving the lexical name for search, so same-named symbols in one file no longer collide in the graph. (#858, #860 via #859)

Advisory Open Horizons reference resolution

Before: a repository's .oh/ artifacts could name an Open Horizons outcome or capability only by local file path. There was no way to declare a stable cross-repository identity, and nothing in RNA could tell an agent whether such a reference still pointed at something real.

After: Markdown knowledge artifacts may declare rna.relationships[].target.uri values of the form oh://v1/<kind>/<id> alongside their local targets. A new resolve-references CLI subcommand and resolve_references MCP tool resolve them explicitly and read-only against a configured endpoint (OPEN_HORIZONS_RESOLVER_URL, OPEN_HORIZONS_API_KEY), with a small authority-isolated cache, explicit freshness states, and an --offline mode that answers only from that cache. The client sends the canonical identity and optional expected kind and nothing else: no graph records, source, embeddings or outbox data. Ordinary scan, commits, CI and releases never contact Open Horizons, and a missing or unreachable resolver is reported as unresolved/unavailable, not as an error that blocks the scan. Reference discovery is bounded (declaration count, file size, no symlink traversal), and a reference whose declared kind disagrees with its URI is skipped rather than trusted. (#863 via #864)

Repository-scoped scanner excludes

Before: a [scanner] exclude entry containing a /codex-rs/app-server-protocol/schema/, generated/*.json, crates/*/schema.json — never matched anything, because directory patterns were compared against single path components. The only working excludes were bare names such as data/ or *.log, which match at any depth and therefore over-exclude. On a large repository this meant generated projections could not be kept out of the index and the full index was multiplied by them.

After: patterns containing / are matched component-by-component relative to the repository root. * matches within one component and never crosses /. A leading / or ./ is accepted and normalised away; doubled slashes collapse. Unqualified names keep their any-depth behavior. The scanner now warns about patterns that can never match, including empty patterns, //, ./ alone, and ** (which is not supported and is treated as *). Syntax and edge cases are documented in docs/scanner.md. (#875)

Explicit embedding backend configuration, and an optional CUDA build

Before: in a source build with embedding features, the execution device was whatever metal-candle chose. There was no way to force CPU, no way to request an NVIDIA GPU, and no record in the semantic index of which device produced it.

After: [embeddings] in .oh/config.toml (or RNA_EMBEDDING_BACKEND, RNA_CUDA_DEVICE, RNA_EMBEDDING_FALLBACK, RNA_EMBEDDING_BATCH_SIZE) selects auto, cpu, cuda or metal, with a fallback policy of cpu or error. cuda is strict: a missing CUDA 12/cuDNN 9 runtime or a failed device initialisation is a clear error, never a silent CPU fallback. A new cuda Cargo feature builds the ONNX Runtime CUDA execution provider (deliberately separate from fastembed's Candle CUDA). The semantic generation identity records the requested and effective backend, provider, device and material execution options, and serving checks that identity at admission, so an index produced on one device cannot be silently reused as if it were produced on another. list_roots renders an "Embedding runtime" section with the effective backend and any attestation error.

Release binaries are unaffected: as before, they are built without embedding features and do not perform semantic search. See "Known limitations" for the current state of the CUDA build in CI. (#865 via #866)

/record preserves Problem Weave outcome lineage

The record plugin skill can now create or update outcomes, objectives and capabilities (not only outcomes), stores objective and capability artifacts under .oh/outcomes/ where RNA discovery already looks, and preserves S&T lineage, ownership, sufficiency groups and review triggers. Conditional schemas moved to a just-in-time-loaded reference so the skill body stays small. (#861)

Fixes

LSP Pass 1 no longer amplifies work and memory with node count

Before: LSP Pass 1 enrichment rebuilt a per-item view of every node for every work item and cloned full node bodies into each one, so both CPU work and peak memory grew with the square of the node count. A 7,390-file full index was OOM-killed at roughly 20 GiB during this pass.

After: Pass 1 builds one shared Pass1SymbolIndex per run and every work item borrows from it; implementor_at is precomputed per file instead of rescanned. On a 50,000-node real repository the change was validated edge-for-edge identical to the previous output, with a modest reduction in resident memory on that run. The original ~20 GiB failure was not reproduced locally, so this release makes no claim about the exact memory ceiling on that repository; the quadratic term is removed, which is the mechanism that failure pointed at. (#873 via #874)

Cross-file Tree-sitter Calls edges are back for scans without LSP

Before: #859 (merged after v0.3.1, so this never shipped in a release) made import_calls_pass emit a cross-file Calls edge only for functions an extractor had marked scope_bindings_complete, to stop a false edge when a parameter or local shadows an imported name. No extractor set that marker, so a scan --no-lsp produced zero cross-file Tree-sitter Calls edges. The suite's cross-stack fixture check for this edge failed on main and passed on v0.3.1.

After: the Tree-sitter extractor records, per function, every name the function binds anywhere in its body (parameters, let/const/:=/assignment targets, destructuring, for/match/catch bindings, closure parameters, nested definitions), driven by per-language LangConfig tables curated against the vendored grammars for Rust, Python, TypeScript/TSX, JavaScript and Go. import_calls_pass emits the edge when the imported name is not shadowed and suppresses it when it is. Bindings are collected as a deliberate superset, so over-collection can only cost a missing edge, never the false edge #859 removed. A function whose enclosing scope contains syntax the grammar cannot parse withholds the marker and stays LSP-only. Languages without a curated table keep the gate closed. Go functions carry the evidence, but import parsing has no Go branch yet, so Go cross-file calls remain LSP-only. Emitted edges are Confidence::Detected; LSP enrichment still upgrades them to Confirmed. (#877 via #878; follow-up #883)

Also closed in the same change, from the #859 review: two definitions on one source line now resolve calls to their own AST node; an all-stopword task query no longer disables the graph-affinity gate; task-mode scoring tokenizes each body once per request; exact-edge checks use one set instead of rescanning edges per hop; convergence detail compaction binary-searches instead of shrinking one character at a time.

Behavior changes

Read these before upgrading. None requires manual migration; some change what a query returns.

  • Graph cache schema 25 → 27 (26: owner-qualified scoped symbol identities, #859; 27: per-function local-binding scope evidence, #877). The first scan after upgrading re-extracts every root. Persisted LSP work ledgers, embedding generations and operation history keep their schemas.
  • Cross-file Tree-sitter Calls edges are now evidence-gated. Compared with v0.3.1, an imported call whose name is shadowed by a parameter or local anywhere in the calling function no longer produces an edge, and functions in Go or in languages without a curated binding table produce cross-file Calls only through LSP enrichment. Same-file Tree-sitter calls and every LSP-derived edge are unchanged. (#859, #877)
  • Scoped exclude semantics. A pattern that begins with * and contains / (for example */schema.json) is now a scoped pattern matching at exactly that depth; previously it fell through to any-depth suffix matching. ** is treated as * and warns. (#875)
  • MCP tool surface grew from four tools to six: convergence and resolve_references join search, repo_map, outcome_progress, list_roots. Clients that enumerate tools will see the change. (#859, #864)
  • Semantic generation identity now includes backend/provider/device flags. In source builds with embedding features, a generation produced before this release lacks those flags and is treated as a different identity; expect a re-embed on first use rather than reuse. Release binaries have no semantic index and are unaffected. (#866)

Known limitations

  • CUDA feature build is not verified in CI (#865, reopened). The --features cuda build compiles as of #875, but on the GPU-less CI host rna test fails at embed_index: failed to fresh-reopen persisted graph immediately before semantic publication after the expected CUDA unavailable ... libcublasLt.so.12 fallback warning. No release artifact is built with cuda, and no CUDA execution attestation is claimed by this release. Treat --features cuda as a source-build capability for hosts that have the CUDA 12/cuDNN 9 runtime; the fallback path on hosts that do not is the open defect.
  • Intermittent fail-closed abort in enrich immediately after scan --full (#876): LSP work-item recovery failed closed: N required persisted item(s) no longer matched the enrichable node set. This path has existed since v0.3.0 (#800) and was observed on both main and the #874 branch while shipping this release, so it is not new here. Re-running enrich is the workaround. The abort is fail-closed by design; the defect is that a fresh full scan can leave the ledger disagreeing with the graph it just wrote.
  • Python module-scope rebinding of an import (#883). If a Python module imports a name and then rebinds it at module scope with a def or assignment, a call to that name inside a function still emits a Tree-sitter cross-file Calls edge to the import target. Function-scope shadowing is handled; module-scope shadowing is the open case.
  • Review findings on #859 deliberately left standing, each answered on its thread: bounded_paths is re-run per common node when before is supplied; bind_convergence_selector scans per selector; the "typed graph" reason prefix is a string test. All are performance or test-shape items, not data-integrity defects.
  • CI smoke coverage. The smoke job (rna test, CLI and MCP stdio smoke) runs only on manual dispatch, so it did not run automatically on the merged commits in this range. It was run locally against this release candidate's binary: rna test PASS, MCP smoke PASSED (6 tools visible). The full scripts/test-suite.sh was run against a binary built from the release commit: 158 passed, 0 failed, 0 skipped, including the cross-stack cross-file Calls fixture check.

Not in this release

  • No performance or efficacy claim. The SWE-bench harness, its three workflows and its retained LSP toolchain scripts were removed (#872); benchmark/ shrank from 286 to 46 files, of which 33 are result files kept for reproducibility of the paused programme only. They are not qualified evidence and ship in no archive.
  • No change to the Linux or macOS release build feature sets. Linux is still --no-default-features; macOS builds still use default features (no embeddings).

Repository and CI

  • Trusted Rust and Metal CI builds now run on native local runners behind independent repository switches (LOCAL_LINUX_CI_ENABLED, LOCAL_MACOS_CI_ENABLED), with hosted runners retained for untrusted forks. Namespace-specific cache steps are skipped on self-hosted workers. (#867 via #868)
  • recursion_limit raised so the cuda feature closure compiles (E0275 on impl Future: Send). (#875)

Issues addressed

  • #858 — Deliver useful task-relevant search context within budget (#859)
  • #860 — Add structural convergence queries with proof-producing tool guidance (#859)
  • #863 — Resolve advisory Open Horizons references without cloud graph synchronization (#864)
  • #865 — Add native CUDA embedding backend and explicit device configuration (#866; reopened for the CI fallback failure described above)
  • #867 — Move expensive CI builds to native local runners (#868)
  • #871 — Remove SWE-bench machinery and retained artifacts (#872)
  • #873 — LSP Pass 1 is quadratic in node count and clones full node bodies (#874)
  • #877 — Restore cross-file Tree-sitter Calls edges without LSP; disposition #859 review findings (#878)
  • #876 — Intermittent enrich abort after scan --full (filed; known limitation, not fixed here)
  • #883 — Python module-scope import rebinding still emits a cross-file Calls edge (filed; known limitation)
  • #875 — Repository-scoped scanner excludes and CUDA release compile (no separate issue)
  • #861/record preserves S&T outcome lineage (no separate issue)

Install

Download the archive for your platform, extract, and put repo-native-alignment on your PATH.

  • repo-native-alignment-darwin-arm64-fast.tar.gz — Apple Silicon, tuned for M4
  • repo-native-alignment-darwin-arm64.tar.gz — Apple Silicon, M1 baseline
  • repo-native-alignment-linux-x86_64.tar.gz — Linux x86_64

On macOS, if the binary is killed on launch after copying, re-sign it: codesign --force --sign - <path>.