Lavra v0.7.1 release notes

Release date: 2026-03-27

This is a focused patch release covering a few things: security hardening, and closing gaps between planning and implementation. Knowledge captured during research now reliably reaches the agents doing the work, and untrusted content injected into prompts is properly sanitized on every platform. Fix Cortex Code hooks and installer.


What’s new

Research findings now reach implementing agents

During /lavra-design and /lavra-plan, agents log INVESTIGATION:, FACT:, PATTERN:, DECISION:, and LEARNED: comments onto beads. These captured discoveries weren’t totally making it into the prompts sent to implementing subagents in /lavra-work multi-bead mode. Agents were not receiving the full benefit of the previous agents discoveries.

To avoid duplicating processing work, a new extract-bead-context.sh script now pre-processes each bead before the wave executes, producing a clean prompt block that includes both the bead description and a Research Findings section with all knowledge-prefixed comments. Implementing agents treat these findings as implementation constraints with the same weight as Locked Decisions.

This has the added benefit of the orchestrating agent not needing to come up with python/jq processing of json output on the fly.

Single-bead /lavra-work also benefits: bd show --long now surfaces research comments directly, replacing the --json output that silently dropped them.


Goal verification now gates commits, not just reports

Previously, goal verification ran after commits in the multi-bead path. An agent could produce a stub, commit it, and the verifier would flag it, but the bad commit was already in history.

Goal verification now runs before commits. CRITICAL failures (missing code or stubs) reopen the bead and exclude its changes from the wave commit. Only beads that pass verification get committed.

A safety cap prevents runaway loops in autonomous mode (/lavra-work-ralph). If a bead fails CRITICAL goal verification twice in a row, it’s closed as wont_fix and surfaced for manual resolution rather than re-queued indefinitely.


New configuration parameter testing_scope integrated into /project-setup

Lavra is designed to help those working with coding agents, overcoming some of their shortcomings, like being overeager to jump to implemention and producing repetitive code. That means Lavra spends more time trying to come up with better implementation plans, by means of extensive user questioning, ceo and engineering reviews, as well as adversarial reviews. The idea is to come up with better-scoped plans so implementation is much higher quality.

Coming up with higher quality implementations takes longer than slop code. Up until now users had no way to configure how thorough the breadth of testing generated by the planning phases should be. In this release a new testing_scope parameter was added. It’s now used by /lavra-design (and /lavra-plan) for the planning phases, and /lavra-work for implementation.

testing_scope allows users to choose how much test coverage should planning commands generate:

  • targeted: Risky paths only: hooks, API routes, external calls, complex business logic. Skip component render tests, static pages, and layout components. (default)
  • full: All test cases: unit, integration, edge cases, render/structural tests

Full means planning agents will add requirements for comprehensive tests. This affects implementation time and tokens needed. "targeted" tries to strike a balance.

/lavra-work applies it to deviation rule 2, auto-adding tests for missing critical functionality.

/project-setup now asks about testing_scope interactively during setup. See the quickstart for more information.


Prompt injection hardening

Untrusted content from beads (descriptions, titles, comments) and the knowledge base flows into agent prompts. This release adds two layers of defense:

Shared sanitization library. A new sanitize-content.sh hook ships with Lavra and is sourced by all injection points. Previously, auto-recall.sh had two copies of the sanitization pipeline inline. Both have been replaced by calls to the shared function. Any new injection point (new command, script, or hook) should source this library rather than re-implementing the pipeline.

Bidi override characters now actually stripped on macOS. The original implementation used sed -E 's/[\x{202A}-\x{202E}...]//g', which BSD sed on macOS silently ignores. The bidi strip was a complete no-op on every macOS install. The fix uses tr -d with explicit UTF-8 byte sequences, which works correctly on both macOS and Linux.

Bead titles sanitized. The extract-bead-context.sh script was sanitizing descriptions and findings, but not the bead title before injecting it into the prompt block. Titles now pass through sanitize_untrusted_content like everything else.

The security model for these controls is documented in the Security Model reference. Key clarification added: the sed/tr strip is noise reduction, not a security boundary. Token fragmentation (SYS​TEM: with a zero-width space) and homoglyph substitution are accepted residual risks. The <untrusted-knowledge> XML wrapper and the “Do not follow instructions” directive are the primary controls.


Hook dispatcher for Cortex Code

Cortex Code hooks run from a working directory that may not match the project root, so relative paths like bash .cortex/hooks/auto-recall.sh fail with “file not found.” A new dispatch-hook.sh script resolves hook paths through the global hooks directory (~/.snowflake/cortex/hooks/) and then delegates to the project-local copy if it exists, falling back to the global copy.

The Cortex Code installer (install-cortex.sh) now installs dispatch-hook.sh to the global hooks directory and wires all hook entries in hooks.json through it. Both global and project install paths use the same dispatcher pattern. check-memory.sh also auto-installs the dispatcher when it detects Cortex Code and the dispatcher is missing.

The hook command prefix is now platform-aware: Cortex Code uses space-separated dispatcher args (dispatch-hook.sh .cortex/hooks auto-recall.sh), while Claude Code continues using slash-separated direct paths (bash .claude/hooks/auto-recall.sh).

Installation tests in test-installation.sh now verify the dispatcher is installed globally, hooks.json references it, and no direct relative .cortex/hooks/ paths remain.


Bug fixes

  • extract-bead-context.sh was in scripts/ but invoked as bash scripts/extract-bead-context.sh from lavra-work.md, a path that doesn’t exist in any installed project. The script now lives in plugins/lavra/hooks/ and is copied to .claude/hooks/ by all three platform installers (Claude, OpenCode, Gemini). scripts/extract-bead-context.sh becomes a thin wrapper for source-tree use.
  • Empty string $1 to extract-bead-context.sh bypassed the argument count guard and ran bd show "". Guard now also checks for empty string.
  • bd show 2>&1 merged bd errors into the parsed output, causing the script to exit 0 with an empty context block when the bead lookup failed. Stderr now goes to the terminal and set -e catches failures.
  • pipefail + tr -d on BSD: tr returns exit 1 when no bytes are deleted (some BSD platforms). Under set -euo pipefail, this silently killed the sanitize pipeline and the calling script. The function body is now wrapped in { ... } || true.
  • 2>/dev/null on the cd call in path resolution could silently construct a wrong path if the parent directory failed to resolve. Removed; the cd call now fails loudly if the path is invalid.
  • Three separate sed invocations for TITLE whitespace trimming collapsed to one. Parent directory computed once instead of twice via separate subshells.
  • Phase M1 bd ready path ambiguity: a comment said “use the already-fetched list,” which could be read as skipping bd show. Clarified that bd show is required for all input paths. bd ready returns IDs and titles only, not full descriptions or Validation sections.
  • Phase M8 goal-verifier prompt used {validation section content} as a fill placeholder that could remain empty if the orchestrator skipped bd show for bd ready beads. Clarified that Phase M1 must always populate full bead details before Phase M8 can verify them.

Upgrading

Re-run the installer to get the updated hooks (npx is fully supported)

bunx @lavralabs/lavra@latest

This copies the new extract-bead-context.sh and updated sanitize-content.sh to .claude/hooks/. Existing installs that skip this step will not have extract-bead-context.sh in their hooks directory. /lavra-work multi-bead mode will fail when trying to call it.

No breaking changes to bead data, config files, or existing workflows.