Repository navigation
Releases
v3.12.5
Added
- Configurable multi-repo root limit (#2237).
ctx_multi_repostill admits 16 roots by
default;max_rootsinmulti-repo.tomlorLEAN_CTX_MAX_ROOTS(which wins) raises it,
up to a hard ceiling of 256. An invalid value is ignored. Roots a savedmulti-repo.toml
or--rootlists always load (up to the ceiling), so a process without the env cannot
drop them and then overwrite the file. Theadd_rooterror names both
settings,list_rootsshows the limit, andsave_configkeepsmax_rootsandrrf_k
that are already in the file.
Fixed
-
A knowledge category can be listed whole through
recall(#2248).ctx_knowledge action=recall category=<c>showed at mostrecall_facts_limitfacts (showing 10/41) with
no way to reach the rest, and only listed facts are refreshed against decay. The call now
takeslimit(up to a hard ceiling of 200) andoffset; the header names the window
(showing 11-20/41) and, while facts remain, the next offset.lean-ctx knowledge recall --category <c>takes--limitand--offset. Every fact a window returns counts as
recalled, as before.memory.knowledge.recall_facts_limitin a project's.lean-ctx.toml
sets that project's default window; a regression test now covers it. Query and bare recall
are unchanged. Reported by @antojk. -
ctx_multi_repo searchacross several roots (#2244). With two or more roots every
search failed with "search source authority changed or is unavailable": one source view
was held for the whole search while each root's index re-bound the request to its own
project. Each root is now searched under its own project's view and content policy; a root
withheld by its policy, or whose authority cannot be resolved, is named in a warning and
the others still answer. A requesting project under an active policy still refuses the
whole search, checked before and again after the roots are read.list_rootssays "index loads on first search" instead of
"pending". -
Cockpit starts on Windows with a shared
%TEMP%(#2203). The Cockpit's private copy
was staged under%TEMP%\leanctx-runtime-private; when%TEMP%or a folder above it grants
other users write access (C:\Temp, managed profiles) the private-directory check refused
it ("DACL grants an untrusted principal write or ownership access") andlean-ctx cockpit
answered 503. The copy is now staged beside the installed runtime, whose private ACL the
install already established;%TEMP%is no longer used. -
Shell gate reads
config.tomlfor directory-service accounts on musl (#2236). The
staticx86_64-unknown-linux-muslbuild has no NSS, so an account served by SSSD, LDAP,
NIS or winbind had no home in the account database: the release gate never read the
user'sconfig.tomland ran on the built-in defaults. Whengetpwuid_rfinds nothing,
Linux builds now askgetent passwd <uid>(only/usr/bin/getentor/bin/getent,
root-owned and not group/world-writable, empty environment, 3 s timeout) and accept the
home only when the line names this uid and an absolute directory.$HOMEis still never
consulted. The lookup is resolved once per process, a failed or timed-out one included: a
restart retries it. When no home can be found, the block
message says that no config location exists instead of naming~/.lean-ctx/config.toml,
andlean-ctx doctorreports the shell allowlist as running on defaults. -
ctx_globwalks only as far as the pattern reaches (#2250, thanks @dkiaulakis). A
pattern with a/and no**(src/*.rs,.tmp/prefix*) now descends only through its
literal leading directories and no deeper than its own component count; a leading./
anchors a name to the directory's own entries (./prefix*). Recursive walks (**, or a
slash-free name, which still matches at any depth) stop after 200,000 visited entries and
saycomplete=false (walk budget 200000 entries reached); a walk cut short by
max_resultssayscomplete=false (result cap N reached). Ignore rules, confinement and
secret-path redaction are unchanged.
Security
-
Rule-file backups and the
replace_symboltemp file no longer follow a planted symlink
(GHSA-x992-2945-crwx).lean-ctx init --agentanddoctor --fixwrote the backups
AGENTS.md.bak,.cursorrules.bakand.claude/rules/lean-ctx.bakwith a plain copy, so a
cloned repository that shipped such a name as a symlink had the repository's own rule file
written outside the project (for example to~/.bash_aliases). The backups now go through
the confined write, which refuses a symlinked component, and keep the source's permission
bits.lean-ctx uninstallnow writes its<file>.lean-ctx.bakbackups of the project's
AGENTS.mdand.hermes.mdthe same way. The headlessctx_patch replace_symbolpath
staged its edit in a fixed-name temp file and now uses the shared atomic writer with a
fresh, unpredictable temp file. Reported and fixed by @kasparovabi. -
quinn-udp 0.5.16 (RUSTSEC-2026-0538). The indirect dependency
quinn-udpmoves from
0.5.15 to the patched 0.5.16; its receive loop could spin after a truncated datagram. -
hono 4.13.18 and @hono/node-server 2.1.8 in the
pi-lean-ctxpackage lockfile
(from 4.13.13 and 2.1.5; Dependabot #2246, #2243).
Upgrade
lean-ctx update # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx # or
npm update -g lean-ctx-bin # or
brew upgrade lean-ctxNote: After upgrading via cargo/npm/brew, run
lean-ctx setupto refresh shell aliases.lean-ctx updatedoes this automatically.
Full Changelog: v3.12.4...v3.12.5
v3.12.4
Added
-
Policy enforcement acknowledged to Hive (D4). The bus hello now names the features
policy_advancedandpolicy_applied, so Hive delivers itspolicy.advancednudge (it
sends that nudge only to Instances that name the feature). Once per version and digest of a
verified policy that is in force and that Hive acknowledged, the Instance sends one
policy.appliedframe:version,digest,key_id,applied_atandsessions(the live
app sessions enforcing it). It sends the frame only when Hive'shello.ack.featuresnames
policy_applied. An expired, outdated or unverified policy is never reported. Local Read
policy.sessionsaddsinstalled.enforcement(applied_version,acknowledged_at,
sessions,reported_at), additively. -
ECCP L/I/G change classifier (task 156).
scripts/change-class <base> <head>
printsL(no public Rust item changed),I(public items only added) orG
(public item changed or removed, Cargo features or dependency tables,
Cargo.lock,build.rs, Cargo config, toolchain pin, CI configuration or a
generated registry) from the git objects alone, without a build or network,
and serves as$ECCP_CLASSIFIERforscripts/eccp-envelope. -
ECCP change envelope in git notes (task 157).
scripts/eccp-envelopewrites,
shows and verifies aleanctx.change-envelope/v1JSON note under
refs/notes/eccpon a merge request head: intent, base, class, read set,
derivations and check evidence (docs/contracts/change-envelope-v1.md). -
Audit entries v2 bind each tool call to its session (ent3-1). Every new
audit-trail entry carriesv: 2and a chainedv2_hashover its timestamp
and, for a call of an MCP session, thesession_id, a per-sessionseq,
the decision and an output digest, so an edited timestamp, seq or fact fails
verify_chainandverify_chain_fullat that entry. Input and output digests
are HMAC-SHA256 under the engine-local key (hive/receipts.hmac-key, never
uploaded). The v1entry_hashis unchanged: older engines and
leanctx-verifystill verify a mixed trail, and an entry of a newer version
counts as forward-compatible, not as tampering. -
Managed configuration for MDM-provisioned machines (task 169). An organisation can
preconfigure an Instance through a macOS configuration profile, the Windows policy key
HKLM\SOFTWARE\Policies\LeanCTX\Managedor/etc/lean-ctx/managed.toml
(leanctx.managed-config/v1,docs/contracts/managed-config-v1.md). Managed
hive.urlandhive.root_keywin over[hive]andconfig seton them is refused;
hive.orgrefuses an enrollment for another org;hive.requiremakes tools refuse
until the machine is signed in and has a policy floor. A carrier that is malformed,
a symlink, or not root-owned (it or its directory) fails closed; it is checked and
read through one open handle.hive statusand the cockpithive status --json
(managed, since 1.7) show the carrier and each member. -
Unit claims warn before two agents edit the same code. A
ctx_patchorctx_edit
write claims the functions, types and Markdown sections it touches for the editing agent
(10-minute lease, renewed on each edit). When another agent holds a claim on a touched
unit, the write goes ahead and the result names the holder (LEAN_CTX_CLAIM_MODE=warn,
the default).strictrefuses the write before anything is written, andoffturns the
check off.ctx_agent action=claimslists the live claims.action=plan_claimsplans a
task against them, through an optional local planner (LEAN_CTX_CLAIM_PLANNER) or a
built-in fallback without network, and claims the free units. Claims are released when
the agent reportsstatus=finishedand when the server shuts down.
Security
The first five fixes below were reported by @dkiaulakis.
-
A redirection written another way cannot write past the gate (GHSA-4gm5-2mgw-9r9r). A
redirection with a file-descriptor prefix, an&>/>|/<>operator, a quoted or escaped
target, or no space before its target was missed, so an agent shell could write lean-ctx's
own state or an unlisted file. The gate now reads every redirection in its canonical form
before any other check. -
A command inside
( … )or{ …; }is checked like any other (GHSA-9jrf-q94p-frhr). A
subshell or group was one segment to the gate, whose first word ((eval,(rm) matched no
rule, and a redirection after the group was dropped. Every group is now opened into its
commands for the checks, and a redirection after it is checked too; a command in acase
arm or afterthen,do,elseorcoprocis judged as its own command. The line that
runs is unchanged. -
The state guard follows the working directory and the values a line builds
(GHSA-cfmv-qg29-3rvp). Repeated or conditionalcd .., acdin a loop, function or trap,
CDPATH/HOME/OLDPWDassignments, variables assigned or read at run time (also
${d:=…}andset --arguments, also inside a longer word), the shell's own
$PWD/$OLDPWD/$_and~+/~-/~N, brace and parameter expansions, function
arguments,aliasbodies,GIT_DIR-style variables, versioned interpreter names
(python3.12,node@20),deno eval/bun -e, awksystem()/pipes/output redirection
(also in-e/--sourceprograms; a program read from stdin with-f -or-f /dev/stdin
is refused) and in-place writers (sed,perl -i,sort -o,gzip,
patch,split,tar/unzipextraction) could reach lean-ctx's own state unseen. A write
whose target cannot be placed is now refused (fail closed); read-only commands are unchanged. -
A read-only git subcommand cannot write lean-ctx's state or run a program
(GHSA-fv44-pm58-9c8p). When git runs at or above lean-ctx's state,log/diff/show/grep
and the other read subcommands were allowed whatever their options, so--output=…,
--ext-diffwith-c diff.external=…, a-c/--config-envpager, fsmonitor or alias,
--paginate,--textconvorgrep -Ocould write a file or run a program. Those options are
now refused there; plain reads such aslog --onelineanddiff --statstill pass. -
Context admission never delivers a value the secret detector flagged
(GHSA-xmx8-69qv-7f86). The detector and the redactor read different spans of one
comparison or key/value expression: the operand with or without the rest of its operator
(==,:=), with or without a trailing)or:. The redactor could judge a flagged value
benign and leave it unchanged, and admission then delivered it asAllowRedacted, because it
only re-checked text it had rewritten. Both layers now judge the same operand, so a
comparison operand is masked exactly when itskey=<value>form is. Unchanged text is
re-checked after a reported value too and is withheld (redaction.incomplete) when the value
is still there. A benign comparison such asif (token==unit_reference):is no longer withheld. -
Memory compaction, context bundles and rule exports do not write through a symlink
(GHSA-mq5c-rjv9-2fc3). Reported by @kasparovabi.ctx_compress_memorywrote its backup
(<name>.original.md) and the compacted file, andctx_pack/lean-ctx packwrote the
bundle file, with a plain write after the path jail check. The jail leaves a dangling link
unresolved, so a symlink a cloned repository shipped could redirect the write to a file
outside the project. These writes now go through the confined write, which refuses a
symlinked component below the project root, and a memory file that is itself a symlink is
refused before it is read. The compacted file and its backup keep the original's permissions.
The same applies tolean-ctx export-rules(AGENTS.md,CLAUDE.md,CODEBUDDY.md,
.cursor/rules/…) and to the artifact registry thatctx_artifacts action=removerewrites.
A rule file that links to another file, such asCLAUDE.md→AGENTS.md, is now refused
with an error instead of written through, and a new bundle file is readable only by its
owner (0600).
Changed
- Library crates carry new versions.
lean-ctx-protocol0.7.0 adds the Hive model catalog,
three routing members of the security policy and themodels.subscriptionright, which
changes its public types;lean-ctx-ocla7.0.0,lean-ctx-workgraph0.3.0 and
lean-ctx-tasks0.2.0 expose those types and move with it.
Fixed
- A PowerShell-shaped command with a non-ASCII character no longer panics the shell gate.
The verb-prefix check sliced the command name at a byte that could fall inside a
multi-byte character (A-bᲳ); it now compares bytes, so such a name is no match. - The cockpit's proof no longer pins a core per session on a large audit trail (#2240).
lean-ctx value --sessionread the whole segmented trail into memory and the savings
ledger twice; it now streams both once, skips segments sealed before the session began
and lines that cannot name the session. The cockpit proves only in a session whose
sidebar was drawn (background spare sessions never draw one), backs off up to an hour
after a failed or timed-out proof, and/cockpitstill proves at once. Thanks
@jenseinar for the report.
Upgrade
lean-ctx update # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx # or
npm update -g lean-ctx-bin # or
brew upgrade lean-ctxNote: After upgrading via cargo/npm/brew, run
lean-ctx setupto refresh shell aliases.lean-ctx updatedoes this automatically.
Full Changelog: v3.12.3...v3.12.4
v3.12.3
Added
-
Process v1 G6 and checker ≠ author on Hive tasks. Submitting a task that carries
processnow runs the G6 gate in the Instance: the locked tests must be unchanged against
spec_commitand must actually run and pass in a clean rerun, else the submit is refused
with the rule and the paths, and nothing is sent. A verify is refused for the session that
claimed or submitted the result. Tasks withoutprocesskeep today's path, and no Hive
serves process tasks yet. Known gap: code under test runs as the agent's Unix user and
could forge a passing report; details and the planned fix are in the module documentation
ofrust/crates/lean-ctx-hive/src/tasks/process.rs.
G6 also asks the compiler which locked tests exist atspec_commit(one extra build in a
private copy; no test ofspec_commitruns), so a locked test the parser cannot name
(macro-generated,#[cfg_attr(test, test)]) must still pass in the judged run. This can
roughly double a G6 run's time.
G6 runs on Linux, macOS and Windows. On macOS a kqueue watch notices a renamed or removed
ancestor of the run tree (as inotify does on Linux). On Windows the run uses Git for
Windows'sh, an environment built from the known folders and a job object; there the
tree's ancestors have no rename protection and locked files are pinned by content,
attributes and write time (details intasks/gate_run.rs). -
P4 tripwires: new crate
lean-ctx-tripwires. A pure, deterministic diff analyzer that flags the documented reward-hacking tricks (touched tests or graders, removed or weakened asserts, new skips, a falling test count, fixture constants, branches on test inputs, time/hash/assert overrides, swallowed errors, mock-only tests, edit cycles, plusunparsable) and returns aleanctx.process.tripwires/v1report; a hit routes to a person, never to automatic acceptance. Not wired into the gate yet. -
Each app session is its own Hive worker (E48). An agent registered with
LEAN_CTX_SESSION_IDset gets the role<role>-s<tag>, where the tag is 8 base32
characters of a blake3 hash of the session id. It is stable across MCP restarts, so every Hive
Desktop session appears on Hive and in the roster as its own worker (coder-s…). A reviewer
session on the same Instance can now pass Hive'sself_verify, while a subagent of the same
session is still the same worker and is refused. Without a session id the shared
(host, role)worker is kept as before. One key (attach::agent_worker_key) now drives task
links, attach, roster, friction and worker accounting. The session id comes from the
environment, so this separates sessions as hygiene, not as a trust boundary: the boundary
stays the Instance. Upgrade note: a claim taken before the upgrade by the shared worker
belongs to a differentwkr_, so let it expire or release it and claim again. -
Meta Muse Code agent target (#2198).
lean-ctx init --agent museandsetupregister the
lean-ctx MCP server in$XDG_CONFIG_HOME/muse/settings.json(default
~/.config/muse/settings.json). The writer adds the mandatory"schema_version": 1when it is
missing, keeps every other key and server, and merges into the root the file already uses —
canonicalmcpServersor legacymcp_servers— because Muse disables MCP when both appear;
a file that already mixes them is left untouched with an error. Re-runs are no-ops,uninstall
removes only thelean-ctxentry, anddoctorchecks the entry. Muse Code is MCP-only: its
PreToolUse output schema is closed and anallownever skips approval, so shell rewrites would
add prompts. Guidance reaches it through the projectAGENTS.mdit auto-loads. -
Task priority: the boards carry the order of work. A task's priority (
high|
normal) and labels (up to 16 of[a-z0-9-]{1,32}) are set with
lean-ctx hive tasks priority <n> high|normal/labels <n> a,b|--clear(C0 2.2 E47
PATCHat the task'sfields_version, read again and resent once onstale_base), from
Hive Desktop over/v1/local/hive/tasks/{n}/priority|labels(tasks.ack), and by a Queen
session withctx_tasks action=prioritize. Local tasks take the same verbs
(lean-ctx tasks priority|labels,/v1/local/tasks/{n}/priority|labels, event
fields). Lists show high first, then by number, an older high task included (the high
tasks are fetched beside Hive's newest-100 page); a list a full Hive answer may have cut
is markedpartial(JSONpartial: true, a(partial: …)line in text). JSON
rows carrypriority,labelsandfields_version. A wrong value is refused before any
request. Only a person (the app, the person's own CLI) and Queen sessions the app
registered over the local socket (/v1/local/queens, socket only) set them; workers read
them. A self-declared bus role is never enough. -
The bus refuses approvals in a person's name (process v1 §7).
ctx_agent postand
sendrefuse an agent's message, with a named rule and nothing rewritten, when it
speaks for a person (on_behalf_of,400 invalid_field: theon_behalf_offield or
"on behalf of …" / "in the name of …" in its text), states or hands off a person's
decision (approval_on_behalf,403 forbidden_right: decision fields with a person as
decider, a handoff of an Inbox item, consent, G2/G9/G10 approval, release, deploy,
secret or rights decision, or a sentence such as "the owner approved …"; questions,
negations and "waiting for" never match), or passes on what was refused for the sender
(denied_forwarded,403 forbidden_right: a place Hive denied, a publish Hive refused,
a vetoed lock or handoff). These three are always enforced. The checks cover questions,
answers, offers and handoffs;status,progressandfindingposts are not checked.
The relay applies the same rules to older queued posts and keeps a post that breaks
them local (counted aswithheld). Publishes the Hive refuses are remembered per sender
in the newbus/refused.json(7 days, at most 1000), so no agent passes them on. Only a
registered Queen session appears as the Queen: under the new[hive] queen_identity
(warn|enforce, envLEAN_CTX_HIVE_QUEEN_IDENTITY, which wins) the defaultwarn
passes a sender presenting as the Queen (rolequeen, the name "Queen", a "From the
Queen:" label) that is no registered Queen session, puts
[unverified Queen sender: <name>, agent <id>, role <role>]in front of its text and
logs it in the newbus/queen-warnings.json;enforcerefuses it, and any other value
counts asenforce. Switch toenforceonly oncequeen-sessions.jsonlists every
Queen session: Queens on another machine are not registered with the host yet. Not
covered yet: app session messages (hive_message_session, an app tool, not the bus). -
Rights per session (process v1 §9). The launcher (Hive Desktop) writes one rights file
per session (leanctx.session-rights/v1) and starts the session with the new
LEAN_CTX_RIGHTS_FILE=<absolute path>. It narrows what that session may write, below the
Instance's own path jail: writes only inside the role's write scopes, such as its own
worktree (write_scope); after G2 no role, the Spec Writer included, writes a locked path
(the feature list, a locked test, a directory or hard link holding one;
feature_list_locked); adeny_writeclass refuses (deny_write:<class>); the rights
file itself is never written (rights_file);denied_toolsrefuses a tool, aliases
included (denied_tools). It applies toctx_shell/shell(a command whose writes
cannot be judged is refused:unanalysable),ctx_execute, LeanCTX's own writes, the new
lean-ctx hook rightsfor nativeEdit/Write/MultiEdit/NotebookEdit/Bash/
apply_patch(an unknown native tool is refused:unknown_tool), and calls forwarded to
the daemon, which carry the file as the new/v1/tools/callfieldrightsFile(local
socket only, else400 rights_file_refused; a profiled session may not use the daemon
socket itself:daemon_socket). Every refusal names its rule. Reads are not narrowed. A
rights file that is set but missing, unreadable, insecure or invalid refuses every tool
call and every write; it never falls back to the Instance's rules. No rights file set
means no change: the Instance's rules apply as before. This is a best-effort first line;
the guarantee is the G6 locked-path check, and the known limits are listed in the module
doc (rust/src/core/session_rights.rs). -
Local Read / Local Service contract for LeanCTX | Hive Desktop. Every Local Read kind
is a versioned JSON document on the CLI and a/v1/localroute of the daemon socket,
released with one receipt for the bytes sent, and listed in onecapabilitiesdocument
with every verb and the transports that answer it (docs/contracts/local-read-v1.md,
local-service-v1.md): session stats and cost perLEAN_CTX_SESSION_ID, gain/spend
rollups (daily and per project), the agent bus (bus list|read|post|leases|claim|release),
presence list, knowledge writes, evidence verification and export, providers, effective
policies with the organization policy's signature state,agents, and the new
lean-ctx daemon status --json(running,pid,socket,autostart,service_name). -
Sync and policy evidence on the local service.
hive.sync,policy.sessionsand
policy.session, CLI-only before, are served on the daemon socket at/v1/local/hive/sync,
/v1/local/hive/policy/sessionsand/v1/local/hive/policy/sessions/{id}, released through
the same release point (one receipt) and listed in capabilities.policy.sessions
installedaddsvaults: one row per vault the installed policy reaches on this machine
(vault_id,ns,name, linkedroots) and which policy applies (vault,default, or
`unver...
v3.12.2
Added
- LeanCTX | Hive is open.
lean-ctx hive connectandhive linkconnect an instance to
hive.leanctx.com (or a self-hosted Hive), andhiveis back in help, suggestions and
completions; 3.12.0 and 3.12.1 shipped the connector switched off.
Security
- Write destinations stay inside the project without a Workspace (GHSA-483j-9vcj-2gxx).
ctx_package save,ctx_pack export, thectx_edit/ctx_patchbackups,ctx_checkpoint restoreand thectx_knowledgeexport wrote to the path the model passed, so an absolute or
../path left the project in the default configuration. A write destination now goes
through the path jail with or without a Workspace; reads are unchanged. Reported by
kasparovabi.
Fixed
- A directory granted with
lean-ctx allow-pathis walkable (#2190).ctx_search,
ctx_treeandctx_globrefused a granted folder below a protected home location such as
~/Libraryalthoughctx_readread its files. A walk inside a granted directory now runs;
a grant never makes home, a filesystem root or a privacy-guarded home folder walkable. - A dogfood or release-candidate build updates to its release. The update check read
3.12.1-dogfood.9as 3.12.9 and answered "already up to date"; a prerelease now sorts
before its release, as in SemVer. - Updates no longer switch an existing setup to the governor, and 3.12.1's switch is
undone. The 3.12.1 update moved every unpinned Claude Code, CodeBuddy, Cursor, Codex,
Gemini and Copilot CLI setup to the governor without the lean-ctx MCP server. An update or
setup refresh now keeps the MCP, hybrid or replace setup it finds. A host that 3.12.1 switched
automatically (a*.pre-governor-<version>.bakbackup or an interrupted migration, and no
governor pin) gets its previous mode back on the nextlean-ctx setup,lean-ctx updateor
init --agent <host>. The mode is read from the backup and pinned.init --agent <host> --mode governor|governor-mcpnow pins that choice too, so the repair leaves it alone. - New Claude Code installs get
governor-mcp. Governor hooks plus the lean-ctx MCP server
with deferred tools, so Hive, the agent bus and knowledge work from the first session. New
installs of the other hosts get the standard MCP setup. - Claude Code always keeps a search tool. The standard setup denied native Grep/Glob even
when~/.claude.jsonhad no lean-ctx MCP server. That happened after the 3.12.1 migration,
when a server or daemon of the previous build re-rendered the standard hooks, and sessions
then had neither native search norctx_search. The deny is now written only together with
the MCP entry, and a refresh removes it when the entry is missing.lean-ctx doctorreports
a pinned standard mode or a native-tool deny without the MCP server, with the repair command. - An older binary no longer overwrites a newer setup. Every explicit setup and init records
its version. The silent hook refresh of a server or daemon still running from before an update
leaves the setup alone when a newer build wrote it. - A host pin selects the mode of later refreshes.
[governor] pinned.<host>(written by
init --agent <host> --mode <m>) is honoured by every setup and refresh path, not only by the
governor default. gainand the cockpit count the governor's applied savings. Applied R1.bash, R2 and R4
decisions are recorded asgovernor:<rule>savings; shadow decisions still count nothing.
R1.read already did.- Test processes of every LeanCTX crate stay off production endpoints. The guard that
sends test traffic to a loopback sink recognised only the Engine's own unit tests and
processes cargo launched for the Engine orlean-ctxpackage. Since the crate split, the
unit tests of the server, Hive, hooks and proxy crates, and the binaries they spawn, sent
telemetry to production, each as a new installation. Any process of alean-ctx…or
leanctx…package, and every sandboxed unit-test harness, now gets the loopback sink; an
installed binary keeps its configured endpoint.
Upgrade
lean-ctx update # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx # or
npm update -g lean-ctx-bin # or
brew upgrade lean-ctxNote: After upgrading via cargo/npm/brew, run
lean-ctx setupto refresh shell aliases.lean-ctx updatedoes this automatically.
Full Changelog: v3.12.1...v3.12.2
v3.12.1
LeanCTX | OSS 3.12.1
A security and governance release. Shell, file and Hive paths now fail closed: the shell gate checks every command it lets through, its policy can no longer be changed from an agent's own environment, and native Read/Grep follow the secret-path rule. LeanCTX on a machine is now an instance, and it speaks Hive-Protocol 2.2 with LeanCTX | Hive.
Full list: CHANGELOG.md.
Highlights
- Governor setup by default on Claude Code. New installs get native tools plus two hooks (
hook govern,hook rewrite-enforce) and no MCP server: about 86 tokens of fixed prefix per turn instead of ~2,950. Measured ×0.88 against native Claude Code on long conversations. Existing setups switch once onupdate, with backups.lean-ctx init --agent claude --mode hybridkeeps thectx_*tools, and that choice is pinned. CodeBuddy, Cursor, Codex CLI, Gemini CLI and Copilot CLI get the same default. - Governor rule R4 groups large Bash output whose lines differ only in numbers, timestamps or ids. The exact original stays in the archive (
ctx_expand). Errors, tracebacks and diffs stay verbatim. Shadow by default. ctx_read symbol="name"reads one function or class. A read without a range returns files up to 300 lines whole and larger files as an outline capped at ~1,500 tokens.ctx_shellasks before refusing an unlisted program: allow once, for the session, always, or deny, in your AI tool's approval dialog.- Opt-in: long-session elision in the proxy. Once a Claude Code conversation passes ~180 kB (~50k tokens), the proxy can replace old tool outputs (over 2 KB, older than the last 8 messages) with a short note that names the archived full text. It is off by default: measured on Claude Code with Haiku 5.5 over three runs (44 long tasks per arm), it cut cost to ×0.67–0.92 against native but solved 37 of 44 tasks against 42 of 44, when the model answered without reading back an elided early output. Turn it on with
[proxy] long_session_elide = trueorLEAN_CTX_PROXY_LONG_SESSION_ELIDE=on. lean-ctx hive docs import --markdownturns docs into LeanCTX | Hive pages, one section per heading, re-revising only what changed.- Hive-Protocol 2.2. Agents check each other's task results (
ctx_tasks action=verify). A connected instance now carries corrections, supersedes and withdrawals of its facts to the vault. - emspace spaces work without setup.
ctx_readnames the page that documents a file, andctx_providerreads one page section. - Knowledge has no fact limit. Facts leave memory only by quality. The knowledge store keeps a change journal (
ctx_knowledge action=changes).
Security
- Shell gate: every command the hooks let run without the
lean-ctx -cwrap is now checked: heredocs,lean-ctx …prefixes,&&chains with reads, Mistral Vibe, Codex, Copilot and PowerShell. Release builds take shell-security settings only from the built-in defaults and~/…/config.toml, never fromLEAN_CTX_SHELL_*,HOMEorXDG_*set by the agent. lean-ctx calls from an agent's shell follow an allowlist. Over-long or too deeply nested commands are refused instead of crashing the hook. - Approvals mirror your AI tool. The hooks no longer answer "allow" for every call. They allow only what the host's own permission settings would have run without asking.
- Native Read and Grep follow the secret-path rule on Claude Code, CodeBuddy, Cursor, Copilot CLI and Gemini CLI.
- PathJail writes stay in the active project by default (
path_jail_scope = projects).ctx_multi_repo add_rootpasses the jail. Project policies can no longer drop the built-in exclusions for.env*, keys and credentials. - Signatures are verified against a trusted key: context capsules, every signed audit-trail entry, context packages, addons and
compliance verify.--allow-untrusted-signerrestores the old check where it applies. - Hive:
hive connectsends nothing before your yes and lists exactly what it shares. A broken screening rule pauses Hive content instead of letting it through. Fencing tokens are no longer printed. - Installers: the skill's
curl | bashinstaller verifiesSHA256SUMS. A Cockpit bundle with two checksum entries is refused.npm install lean-ctx-bin@Xinstalls engine X. The Hermes plugin pinslean-ctx-client. - Secrets off command lines:
serve --auth-token-file,serve --a2a-signing-key-file,cockpit --auth-token-file. The signing keychain password goes over stdin. - Local surfaces: dashboard requests that name a project are answered for that project or refused. The Windows daemon pipe and the daemon socket are owner-only.
report-issueredacts secrets. Role, policy and workflow refusals are tool errors (isError: true).
Upgrade notes
- Behaviour changes: native reads of secret-like files (for example
*.pemtest certificates,.npmrc) are denied under the default role. Allow them throughio.allow_secret_paths. Shell-security options belong inconfig.toml, becauseLEAN_CTX_SHELL_*andLEAN_CTX_PROFILE=… lean-ctx …from an agent's shell are ignored or refused.lean-ctx config set path_jail_scope homerestores the 3.11 jail. See the upgrade guide: docs/guides/upgrade-3.12.md. - Claude Code switches to the governor setup on the next
update, once per version, after backing upsettings.json,~/.claude.json,CLAUDE.mdand the config. Pin another mode withlean-ctx init --agent claude --mode hybrid|replace|mcp. - LeanCTX | Hive: this release speaks only the instance naming (C0 2.2), and no earlier release connects to Hive. Hive's TOML org policy is no longer read.
- New crates on crates.io: the engine is split into
lean-ctx-engine,lean-ctx-hooks,lean-ctx-hive,lean-ctx-serverandlean-ctx-proxy, with the CLI cratelean-ctxon top (all 3.12.1). Also new:lean-ctx-compress,lean-ctx-fs,lean-ctx-patterns,lean-ctx-syntax,lean-ctx-workgraphandlean-ctx-doc(behindctx_doc), all 0.1.0.leanctx-native-storagestays unchanged at 0.1.1. Breaking for direct users:lean-ctx-protocol0.5.0 andlean-ctx-ocla5.0.0.cargo install lean-ctxworks as before. - Removed: Hive swarms and Task Compute, sealed vaults, human review of Hive knowledge, episodic and procedural memory, the optional LLM enhancement layer and learned
automodes.lean-ctx autopilot policyandautopilot evidence --learnare deprecated no-ops. Details are in the changelog under Removed and Deprecated.
v3.12.0
🚀 LeanCTX 3.12.0: meet the Cockpit
The new Cockpit, Savings view. Our own dogfooding numbers since Oct 17: every figure is measured from the signed savings ledger on this machine.
3.12.0 brings the LeanCTX Cockpit: one local place for everything LeanCTX does for your agents. What it kept out of the context, what it cost, what it protected and what it can prove. It is free for everyone, runs only on 127.0.0.1, needs no account, and installs itself.
lean-ctx update # then start any agent session; the Cockpit installs itself
lean-ctx cockpit # opens it in your browser✨ The Cockpit
- One place for savings, spend and proof.
lean-ctx cockpit(orlean-ctx dashboard) starts in the background, reuses a running instance, picks a free port and stops withlean-ctx cockpit stop. - Your familiar commands open it. On a terminal,
lean-ctx gain,gain --web,gain --live,watch,wrappedandcockpit --exportopen the matching view. Scripts, CI,--jsonand--terminalkeep their text output exactly as before. - Every page, free, no plan checks.
- Cockpit shows protection and open findings side by side.
- Savings, Spend and the Ledger of every call.
- Report.
- Numbers explained, with every formula and price source.
- Security, Context, Sources, Agents, Proof, Decisions.
- Memory, Packages, Inbox and Improvements.
- Honest numbers. Every figure carries a trust label: measured, estimated, evidenced or derived.
- Installs itself, signed and verified.
- A fresh install with install.sh, PowerShell, npm or Homebrew ships it.
- An update from 3.11 and AUR's
lean-ctx-binget it at the next agent session, without a command. - Every package is checked against the LeanCTX signing keys before it runs.
- Opt out with
LEAN_CTX_NO_COCKPIT=1.
- Light and dark, desktop to phone.
📚 Knowledge from your repository
lean-ctx import docsturnsAGENTS.md,CLAUDE.md,.cursorrules,.cursor/rulesanddocs/into project facts, each anchored to its file and section.lean-ctx import gitlearns decisions, fixes and dead ends from your commit history. It reads no diffs and no authors.- Every fact passes the same secret and personal-data screening as any other import.
lean-ctx knowledge export-agents-md --writewrites them back as a marked section ofAGENTS.md.
🔒 Security: eight advisories fixed
- GHSA-rxv5-4c3x-g4q6:
ctx_executecode in languages other than shell now needs your approval, in the same dialog asctx_shell. - GHSA-rrww-7924-w72f: project provider configs load only in trusted workspaces.
- GHSA-h693-v7jv-vmv9: the SSRF guard blocks NAT64, 6to4 and Teredo IPv6 forms.
- GHSA-53h5-q4m8-vg3x: gateway-held provider keys need the lean-ctx token.
- GHSA-pv78-9chr-hj3c: proof and knowledge export paths stay inside the project.
- GHSA-gvg8-285q-m62g: signed handoffs prove who signed, not only that nothing changed.
- GHSA-jhf9-3pv4-m469: "no web fetches" policy packs also close provider egress.
- GHSA-79fg-7cj5-4xmm: an untrusted project config cannot widen write paths.
Thank you to everyone who reported them responsibly.
🧭 Focus
3.12.0 keeps what selects, controls and proves context, and drops what measured no benefit.
- 36 rarely used MCP tools are removed. Calling one names its successor.
- The hosted services that needed an account close. Personal Cloud ended today: its routes answer
410 personal_cloud_ended, your local data is unaffected, and the stored data is deleted 30 days after release. Existing hosted team servers keep running until their teams move to LeanCTX Hive with 3.12.1. - The local Engine and the whole Cockpit stay free.
- LeanCTX Hive, shared knowledge for teams and your own machines, follows with 3.12.1.
- Upgrade guide: docs/guides/upgrade-3.12.md. Focus changes: docs/migration/focus-2026-10.md.
🪟 Also in this release
- Windows: checkpoint packages, imports and source re-checks work again, and so do file locks and atomic file replacement.
- Semantic search without ONNX Runtime: the new built-in
potion-codemodel runs in pure Rust. - Pi and Oh My Pi are recognised as AI clients.
- You choose which AI tools
setupconfigures, and updates stay inside that choice (--dry-runshows the plan). - Dependencies: zip 9.0.5 (critical advisory GHSA-m665-v7pm-7grj), rustls 0.23.46 and @hono/node-server 2.1.5.
Full changelog (every change, breaking changes, removed features)
Focus release. The local Engine and the whole Cockpit are free and local, with no
account and no plan checks; the hosted services that needed an account close. LeanCTX
Hive (shared knowledge for teams and your own machines) follows with 3.12.1. LeanCTX also keeps what
selects, controls and proves context and drops what measured no benefit. 3.12.0 includes
every change prepared for 3.11.5, which is not released separately. Upgrade notes:
docs/guides/upgrade-3.12.md (retired services) and
docs/migration/focus-2026-10.md (focus changes,
successor tools and restore lines).
Breaking changes at a glance: Personal Cloud, the ctxpkg.com registry and the hosted
per-customer team servers close on the 3.12.0 release day (writes stop, the services answer
410) and their stored data is deleted 30 days after release, without migration; the
engine's cloud client, the login, register, account, sync, cloud, team and
enterprise commands, remote pack install|update|publish and addon add ns/name are
removed; hosted Wrapped publication, the leaderboard, the README badge and the billing CLI
are gone; the capability registry is schema 3 (price classes hive and retired); the
built-in personal-data detectors are email, phone and payment cards. From the focus work:
36 MCP tools are removed (34 retired plus ctx_intent and ctx_task; a call names the
successor); the Autonomy Drivers, Workflow Evidence Ledger, Intent Route,
invocation-context-binding, invocation-evidence-manifest and tokenizer-translation-driver
contracts are withdrawn; ctx_work_graph action="execute" and lean-ctx pair are removed
(the host starts agents with action="start"); cognitive_mode and the read-path autonomy
drivers are retired; role budgets, SLOs and anomaly detection are gone; domain and
compliance packs moved to Pro/Enterprise; .lean-ctx.toml write and read-only keys apply
only in a trusted workspace; decision receipts are schema_version: 2 with keyed object
digests, so SDKs before 3.12.0 reject them (engine and SDK ship together); the engine's
built-in dashboard UI is replaced by the Cockpit. The SDK Agent Tools v1 contract is
unchanged. Telemetry is unchanged. lean-ctx-protocol renames hive::project to
hive::vault (VaultV1, VaultRefV1, vault_id); the published crates need Rust 1.87.
Added
-
ctx_execute code needs your approval (GHSA-rxv5-4c3x-g4q6). Code in any language other
than shell runs only after approval in the same dialog as ctx_shell — once, for this
session, always (global execute_allowed_languages) or deny, per language; a batch asks once
per language and runs nothing if one is denied. Without the dialog (client without
elicitation, shell_approval=off) the code is refused — list the language in
execute_allowed_languages to run it without asking. -
Knowledge from the repository itself.
lean-ctx import docs [<file|dir>…]turns
AGENTS.md,CLAUDE.md,.cursorrules,.cursor/rulesanddocs/into project facts:
each bullet or paragraph is one fact, categorised by its nearest heading (conventions,
procedures, decisions, architecture, gotchas, dead ends, experiences) and anchored to its file
and section.lean-ctx import git [--limit N]mines the local history: reverts become dead
ends, fixes experiences, and commits that decide, switch or replace something decisions, each
anchored to its commit; trailers and lines naming a person by email are dropped first, and no
diff or author is read. Every file and every fact passes the same admission as any other import
(path rules, project policy, secret and PII detection): a masked value stays masked, a withheld
one yields nothing and is counted. A re-import updates the fact at the same anchor instead of
adding a second one. Each fact under a heading carries a code anchor on its section, so an edit
there marks it to verify; a fact whose section (or file) is gone on re-import is marked to
verify (source_removed) and becomes current again when the section returns.
lean-ctx knowledge export-agents-md [--write]writes these categories as a marked section of
AGENTS.md(only public facts; the rest of the file is kept), and importing that section yields
the same facts again. -
The whole Cockpit is free and ships with official installs. Improvements, Spend
history, Ledger filters and export, Report, Delivery insights, Memory Care, Package
Studio, recipes and compar...
v3.11.4 — LeanCTX Engine: more reliable agent tool calls
A bugfix release: tool calls that an agent gets almost right are now served instead of refused, edits survive indentation slips, and the Claude Code plugin wakes the agent only once per finished background job. Agent Tools protocol and data formats are unchanged.
Fixed
- Searching a broad directory searches the project instead of failing.
ctx_search,ctx_globandctx_treecalled on~,/or another directory that contains the session's project now walk the project and say so in the first line of the result. Before, they answered "refusing to scan" and agents repeated the same call. Without a bound project the refusal now names the fix: pass the project directory aspath. - Agents can read their own instruction files. Skills, commands, sub-agent definitions, rules and plans under
~/.claude,~/.codex/skillsand~/.codex/prompts,~/.agents/skills,~/.cursor,~/.gemini/commandsand OpenCode's~/.config/opencode, plus the globalCLAUDE.md,AGENTS.mdandGEMINI.md, open read-only on first use. Credentials and settings beside them, such as~/.codex/auth.jsonor~/.claude/.credentials.json, stay closed, and LeanCTX tools cannot write there. A skill linked in from elsewhere below~is followed read-only; a link into a protected location or outside~is not, and links inside dependency folders such asnode_modulesare never followed. - A missing file comes with the file that was meant.
ctx_read,ctx_search,ctx_globandctx_treename the existing path when the requested one has a typo, another extension, or was written relative to the wrong directory (src/x.rsforrust/src/x.rs). - Paths with spaces work when written shell-style. On macOS and Linux a path such as
My\ Project/notes.mdor'/Users/me/My Project'turned intoMy/ Projectand was reported as missing. Backslash escapes and surrounding quotes are now removed. - Common argument spellings are accepted.
file_path,filePath,fileandfilenamework aspath,cmdascommand(for MCP calls andlean-ctx call), apathinside actx_searchqueries[]entry scopes that one query,ctx_globacceptsglobforpattern, andctx_patchreplace_allacceptsold_string/new_string. A replacement must still be named explicitly, so a missing one is never taken as "delete every match"; two spellings with different values are rejected. - An edit whose indentation is off still applies. When
old_stringmatches exactly one place in the file at a uniformly different indentation,ctx_editandctx_patch(replace_unique) apply it there and shiftnew_stringby the same amount, and say so. Uneven or ambiguous matches still fail with the exact-match error. - Read modes and glob patterns with small typos work.
lines:72:92,lines:72..92,lines 72-92and a capitalisedFullare read as the mode they spell;**.rsandsrc**are read as**/*.rsandsrc*/**. ctx_readno longer fails with "per-file lock contention". When an earlier read of the same file holds its lock for more than 10 seconds, the read proceeds without it.- No more "Retry the read" after LeanCTX opened a dependency folder. When a path in a dependency cache or an agent folder is still refused after it was opened read-only, the error now gives the actual reason.
lean-ctx callresolves these paths the same way as the MCP server. - The Claude Code plugin wakes the agent once per finished job. When the agent itself had already read a finished background job with
ctx_shell(background_action="status"or"cancel"), the plugin announced the same result again on the next turn, costing a whole extra turn.
Changed
- The token-saving curve in the Claude Code cockpit is drawn true to time and to scale. The x axis now runs from the session's first lean-ctx call to now, the y axis starts at zero, the raw total sits behind "kept out" as a dim envelope, moments in which tokens were kept out are lit, and an axis line shows the session span and the gain of the last ten minutes. Before, one column per sample scaled from the window's lowest value made a single saving look like a cliff.
Upgrade
lean-ctx update # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx # or
npm update -g lean-ctx-bin # or
brew upgrade lean-ctxNote: After upgrading via cargo/npm/brew, run
lean-ctx setupto refresh shell aliases.lean-ctx updatedoes this automatically.
Full Changelog: v3.11.3...v3.11.4
v3.11.3
PathJail opens the home directory by default, the gateway keeps downstream MCP sessions alive between calls, Windows updates and CUDA 13 GPU embeddings work, and Claude Code gets a gateway cockpit. Agent Tools protocol and data formats are unchanged.
Changed
- PathJail opens your whole home directory by default. The default
path_jail_scope = homenow admits everything below~for reading and writing; before, it admitted only paths inside a project, read-only outside the session's project. Protected locations stay closed: every top-level dot entry (~/.ssh,~/.aws,~/.config, shell rc files, other agents'~/.claude,~/.codex, …),~/Library,~/AppDataand~/snap, and everything outside~. The previous behaviour is the new scopeprojects(lean-ctx config set path_jail_scope projects), the strictest one staysproject. A scope you set yourself is kept;homeset explicitly now means the wider scope too. Every installation without its own setting shows a one-time notice on its next interactive command.
Fixed
- Windows: a second update or
enable-gpuno longer fails (#2048). Each update moves the runninglean-ctx.exeaside tolean-ctx.old.exe. The process that does this is still running from that file, so it cannot delete it, and the next update refused to start withstale Windows rollback binary exists. The next update now deletes the leftover file. If an old lean-ctx process (an editor's MCP server, a daemon) still runs from it, the file is renamed aside and removed by a later update once that process has exited; only when even that rename fails does the update stop and say which process to close.lean-ctx dev-installuses the same cleanup. lean-ctx enable-gpuon the CUDA build it would install says so (#2048) instead of downloading and reinstalling the same binary.lean-ctx enable-gpu --forcestill reinstalls it.- GPU embeddings work with CUDA 13 runtimes (#2049).
onnxruntime-gpuon PyPI is built for CUDA 13 since 1.27, andpip install "onnxruntime-gpu[cuda,cudnn]"installs the CUDA 13 wheels, which put the DLLs intosite-packages\nvidia\cu13\bin\x86_64(Linux:nvidia/cu13/lib). LeanCTX looked only for CUDA 12 libraries (cudart64_12.dll,libcudart.so.12) in the CUDA 12 wheel directories, solean-ctx embeddings statusreportedCUDA runtime: not loadablealthough everything was installed. LeanCTX now reads which CUDA major the selected ONNX Runtime's CUDA provider was built for from the provider's import table and searches and preloads the matching CUDA 12 or CUDA 13 libraries, including the new wheel layout andCUDA_PATH_V13_*toolkits. - Windows: the CUDA check no longer fails on every machine (#2049). The check loads
onnxruntime_providers_cuda.dlldirectly; outside ONNX Runtime its initialisation refuses to run, which Windows reports as error 1114 after every dependency has already loaded (Linux reports the same case as a missingProvider_GetHost, which was already handled).embeddings statustherefore saidnot loadableand indexing warned about a CPU fallback while ONNX Runtime was in fact using the GPU, and GPU-sized embedding batches were never used. Error 1114 now counts as loaded; other loader errors show their Windows error code and meaning instead of a bareLoadLibraryExW failed. CUDA runtime: OKmeans the GPU is really used (#2049). ONNX Runtime loads cudart and cuDNN by name only when a session starts, so a provider DLL that loads (for example through a CUDA toolkit onPATH) could still fall back to CPU without a word.embeddings statusnow also requires the CUDA runtime and cuDNN to resolve, preloads them before the provider is registered, and names the library that is missing otherwise.- Clearer CUDA diagnostics on Windows (#2048, #2049). Instead of a bare
LoadLibraryExW failed,lean-ctx embeddings statuslists the missing DLLs and every directory it searched, says when no NVIDIA driver (nvcuda.dll) is installed, and states the CUDA major the provider needs instead of the CUDA version of the ONNX Runtime LeanCTX was compiled against. - ONNX Runtime and CUDA libraries from more Windows Python installs are found (#2048): every
python.exeonPATH, system-wide installs under%ProgramFiles%\Python3*andC:\Python3*, the Python install manager's%LOCALAPPDATA%\Python\pythoncore-*(the default for Python 3.14) and the Microsoft Store Python, in addition to the per-user installs and active virtual or conda environments. lean-ctx embeddings statussays which setting chose the embedding model (#2049):LEAN_CTX_EMBEDDING_MODEL(which overrides the config),[embedding] modelin the namedconfig.toml, or the default, plus how to change it.- Every second gateway call no longer fails with
Transport closed(#2040, reported by @smollgreymouse).ctx_tools(find, list, call) and gateway compression addons each built their own Tokio runtime per call and dropped it afterwards. A pooled downstream MCP session's transport lives on the runtime that opened it, so the next call got a session whose connection was already gone. All gateway I/O now runs on one runtime that lives as long as the process, so a downstream server is started once and reused across calls. - The status line shows this conversation's numbers. When several agents run LeanCTX in the same project, the Claude Code status line showed whichever session had written last, so its figures jumped between conversations or stood still. Each session now records the process of the AI client it serves, and the status line reads the session that belongs to its own client.
- Polling a background shell job no longer inflates the session counts. Every
ctx_shellstatus or cancel call for a background job counted as one more tool call and one more command in the session statistics; a single long build polled by an agent could add hundreds. These polls no longer count.
Tool calls that failed although the agent's request was sound, found in the usage telemetry of 3.11.1 and 3.11.2:
ctx_readanswers an empty file, an SVG and a directory. An empty file returnedFile is emptyas an error; it now reads as<path>: empty file (0 bytes). SVG files were rejected as binary; they are XML text and read like source (a real binary behind any extension is still caught by its content). A directory failed with the OS errorIs a directory; it now lists its entries.ctx_searchsearches an invalid regex literally. Code text such asfoo(,items[0orfn run(&selfis not valid regex and failed the call; it is now searched verbatim, with a one-line note.action=symbolalso takes the symbol name fromsymbol,queryorpatternwhennameis missing.ctx_patchinfersreplace_unique. An edit with old and new text but noopfailed withmissing 'op'; such an edit can only bereplace_unique, at the top level and insideops[].- An edit whose result already holds is not an error.
ctx_edit(andctx_patchreplace_unique) with identical old and new text, where that text is in the file, reportsNo change:instead of failing. Text that is absent still fails. - Dependency source reads on the first call. Reading a file in a language cache registered the cache as a read-only root but still failed the call with "Retry the read"; it now resolves in the same call. The Rust toolchain source (
~/.rustup/toolchains), Cargo git dependencies (~/.cargo/git/checkouts) and the Bun, Dart pub and Ruby gem caches are recognized alongside the existing ones. Credential and configuration directories (~/.claude,~/.codex,~/.config,~/.cargo/credentials.toml) stay jailed, and writes into any cache stay denied. - Usage telemetry counts each tool call once. The daily total of tool calls and failures, and the latency histogram, counted every call twice: once where the call is dispatched and again when its lifecycle finished. The per-tool counts were already right; the totals now match them.
- A failed inline shell command is one error, filed as a failed command. The shell worker also recorded every non-zero exit of a command that finished in the foreground as a LeanCTX-internal error, next to the pipeline's own
commandentry, and did so even forexit 1with output (grep, diff, test), which is not an error. The worker now records only background jobs, ascommandortimeout; inline runs are classified once by the call pipeline. - Telemetry opt-outs and CI detection reach MCP servers started by Codex. Codex starts MCP servers with only
HOME,PATH,USER,LANGand a few more variables, so a server it started could not seeDO_NOT_TRACK=1,LEAN_CTX_TELEMETRY=offor the CI markers set in the shell or job around it: an opt-out did not reach the server, and a CI job counted as a local machine. On Linux the server now also reads these few keys, and the runtime-environment markers, from its nearest ancestor processes. Nothing else from their environment is read or kept. - Containers under cgroup v2 report as
container. A container that has no/.dockerenv(containerd, Kubernetes, Podman) and runs under cgroup v2 reported as a local machine. Its overlay root filesystem, thecontainervariable and the Kubernetes service-account directory now mark it too.
Added
- Notices from the LeanCTX team reach the installations they concern. The acknowledgement of a telemetry send can carry up to three short notices, for example "the fix for this
ctx_readfailure is in 3.11.3". The server picks them by what the batch already reports (version, AI client, OS, runtime environment, a tool that failed), so nothing extra is sent and nobody needs to know who you are. A notice is shown once, on the terminal of the next interactivelean-ctxcommand, and never in an MCP response: text from a server does not reach the AI model. Only plain text up to 280 characters ...
v3.11.2
Updater and security hotfix for 3.11.1. Agent Tools protocol, configuration and data formats are unchanged.
Upgrade notes
- Updating from 3.11.0 or 3.11.1 needs one manual step. Their built-in updater stops after downloading and verifying a release with
Failed to complete durable update transaction: prepared transaction is missing its integrity digest, and scheduled automatic updates fail the same way. Nothing is changed or damaged: the installed binary keeps running. Install this release once through the channel you installed with —curl -fsSL https://leanctx.com/install.sh | sh,brew upgrade lean-ctx,npm i -g lean-ctx-bin@latest,cargo install lean-ctx --forceor your AUR helper — andlean-ctx updateand automatic updates work again from then on. - Windows on 3.11.0 (#2039): the 3.11.0 updater also needs the external
cosigntool and looks for it under that exact name; installing cosign does not help, because its update then stops at the error above. Extract the Windows ZIP of this release over yourlean-ctx.exe, or update through npm. From 3.11.1 on, no external tool is needed.
Security
- The
homejail scope opens projects, not your home directory. 3.11.1 admitted every path below~for reading, so a prompt-injected agent could pull loose personal files (~/Documents/taxes.pdf,~/Downloads,~/Desktop) into the model context. A path is now admitted only when a folder between it and~holds a project marker (.git,Cargo.toml,package.json,go.mod,pyproject.toml,Makefile, …). Every repository below~stays readable; anything else needslean-ctx allow-path <dir>, and the refusal says so. redirect_excludefrom an untrusted workspace is withheld (#2034). Paths inredirect_excludeskip the native-read hook redirect and with it the redaction that path applies. In 3.11.1 a repository's own.lean-ctx.tomlcould extend the list even when the workspace was not trusted, so a cloned repository could opt its reads out (for example with["**"]) on hosts where the read redirect is active. Such a list is now ignored with a[SECURITY]warning until you runlean-ctx trust; the global config andLEAN_CTX_HOOK_EXCLUDEstill apply.
Fixed
- Codex threads recorded while ChatGPT routing was on stay resumable. Codex stamps
model_provider = "leanctx-chatgpt"into every such thread and refuses to resume it once that provider is missing (Model provider 'leanctx-chatgpt' not found).lean-ctx doctor --fix,proxyruns and stale-proxy cleanup deleted the[model_providers.leanctx-chatgpt]block by name, including the direct one users restored by hand. Cleanup now only touches a block that targets the local proxy, and repoints it athttps://chatgpt.com/backend-api/codexinstead of deleting it; a block aimed anywhere else is kept verbatim and no longer reported as routed or broken. ctx_multi_repo action=searchwithout a query says so. While a content policy was active, the call was refused with the cross-project policy message instead ofquery is required for search.- No downgrade as "update". Without an explicit version,
lean-ctx updateand scheduled updates only install a release newer than the running build; a build ahead of GitHub's latest release was offered, and on a schedule installed, the older release.lean-ctx update <version>and--pinstill install any version on purpose. - GitHub API rate limit (#2037): the updater now sends
GITHUB_TOKEN,GH_TOKENorLEAN_CTX_GITHUB_TOKENwhen set (only to api.github.com, without following redirects), raising GitHub's limit from 60 requests per hour per IP address to 5000. An exhausted quota now says so, with the reset time and the fix, instead ofhttp status: 403; a rejected token reports401 Bad credentials. The background version check uses the same client. lean-ctx updateandlean-ctx update --rollbackcomplete again. The updater sealed each prepared transaction with its integrity digest when writing it to disk, but then executed the unsealed copy, which the integrity check rejected every time. The updater now executes exactly the sealed transaction it persisted. A new test runs the real prepare → execute → recover path on a stand-in binary.- macOS: the update signature is applied before the swap. The updater re-signed the installed binary after moving it into place (#356, so the TCC grant survives), which changed its bytes after the transaction had recorded them; the update then stopped with "active binary does not match prepared target". The staged binary is now signed first, and the receipt records exactly the bytes that are installed; the release manifest and archive digests still record its provenance.
- A reinstall no longer blocks later updates. A failed 3.11.0/3.11.1 run leaves a prepared transaction behind; after a manual reinstall the updater refused every update with "active binary matches neither prepared state". Recovery now recognizes a transaction that a build at least as new as its target has superseded and removes its staged files without touching the binary. Likewise, an update receipt from a different, earlier version no longer refuses updates ("current binary differs from the active receipt"); the updater starts a new receipt from the running binary. A binary that differs from a receipt of the same version is still refused.
Upgrade
lean-ctx update # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx # or
npm update -g lean-ctx-bin # or
brew upgrade lean-ctxNote: After upgrading via cargo/npm/brew, run
lean-ctx setupto refresh shell aliases.lean-ctx updatedoes this automatically.
Full Changelog: v3.11.1...v3.11.2
v3.11.1
Highlights
- Agents can read across all your projects at once: the path jail now admits every project below your home directory for reading by default, while writes stay in the session's own project and
~/.ssh,~/.aws,~/.config, other top-level dot directories,~/Library,~/AppDataand~/snapstay closed. - Each agent session binds to the project it was started in. Setup no longer pins one project into an agent's global MCP config, and existing pins are removed.
Upgrade notes
- Path jail scope: the new
path_jail_scopesetting defaults to"home": reads anywhere below your home directory outside the protected zones; writes only in the session's project, host-declared roots and allow entries. Setlean-ctx config set path_jail_scope projectto keep the previous single-project boundary. The setting is global-only; a project-local.lean-ctx.tomlcannot change it. With an implausible$HOME(/, a single path component, not owned by you) the scope falls back toproject.path_jail = falsestill disables the jail. - Protected zones are a hard deny in both scopes: a project root or allow entry that only contains a zone (for example a dotfiles repository at
~) no longer opens~/.sshand the like; add an entry inside the zone (lean-ctx allow-path ~/.config/myapp) if a tool must reach it. - Project pins: earlier
lean-ctx setup/doctor --fixruns and MCP-start hook refreshes copied the installing session'sLEAN_CTX_PROJECT_ROOTandLEAN_CTX_EXTRA_ROOTSinto user-global agent configs (~/.codex/config.toml,~/.grok/config.toml, global JSON MCP entries), so every later session of that agent opened the same project. The Codex and Grok entries are cleaned on the next agent refresh;lean-ctx doctorreports remaining pins under "Project binding" andlean-ctx doctor --fixremoves them line by line, keeping key order and comments (a pin that shares a line with other keys is listed for a manual edit).LEAN_CTX_EXTRA_ROOTSvalues move intoextra_roots; per-project entries in~/.claude.jsonare left alone. Restart the agent once afterwards. - Telemetry: the daily aggregates report more (see Changed), and setup no longer asks: setup and the first interactive command after this update print a three-line notice instead. Turn telemetry off any time with
lean-ctx telemetry off,DO_NOT_TRACK=1orLEAN_CTX_TELEMETRY=off; an explicit opt-out made since 3.11.0 stays in effect, andlean-ctx telemetry showprints the exact payload. What the server keeps, including a keyed network hash and the network operator's public name derived from the connection (never the IP address), and how long, is described at leanctx.com/privacy.
Security
- Agent shells can no longer loosen lean-ctx itself.
lean-ctxis on the default shell allowlist, so an agent could runlean-ctx yolo --yes,lean-ctx allow <cmd>,lean-ctx allow-path <dir>,lean-ctx trust,lean-ctx security open,lean-ctx security secrets offorlean-ctx config set <security key>through ctx_shell (or a hook-rewritten Bash call) and then do what the jail or the allowlist had just refused. Those subcommands are now blocked in agent shells with a message to ask the user; listing (--list,status,config show), narrowing (--remove) and tightening (secure,untrust,security secrets on) still run. Run the blocked commands in your own terminal. - Path jail writes: the new
homescope opens other projects for reading only; writes outside the session's project need an explicitlean-ctx allow-path, so an agent in one repository cannot plant another repository's Git hooks or a binary on yourPATH.
Added
lean-ctx allow-path <dir>admits one directory for reading and writing — outside your home directory, a sibling project to edit, or one protected location — effective on the next tool call without a restart.--listshows the jail scope and added directories;--removetakes one out. It refuses/, the home directory and any directory containing it.lean-ctx security statusshows the active jail scope, andlean-ctx doctorreports global project pins.
Changed
- A refused path names the one command that admits it (
lean-ctx allow-path <dir>, offered for the enclosing project) for the user to run in their terminal. The previous hints to open a new IDE window or set an env var that the running server cannot see are gone. - When a stale
LEAN_CTX_PROJECT_ROOTpin is still set and the MCP server starts inside a different real project, the session binds to that project and logs a warning. Host-specific roots (CLAUDE_PROJECT_DIR, workspace folders) keep their precedence. - Telemetry reports where and how long LeanCTX runs, as coarse ranges. The daily heartbeat adds the runtime environment (local, container, Codespaces, Gitpod, Replit, cloud agent, CI), the installation age (under an hour … 30+ days) and the number of active days in the last 30 (1 … 15+), each as a closed value. They let usage numbers separate people from short-lived agent sandboxes. No machine, account, network or path information is sent. The disclosure lists the new category, so every installation sees the one-time notice again;
DO_NOT_TRACK=1,LEAN_CTX_TELEMETRY=offandlean-ctx telemetry offstill turn telemetry off. - The install channel is detected. It was
unknownfor every installation because no build set it; it now follows the location of the running executable (npm, Homebrew, cargo, PyPI, AUR, release binary). Only the channel name is sent. - Telemetry reports token savings and per-tool latency. The daily tool aggregate adds the day's total tool-output tokens before and after compression, and each built-in tool's summed latency. Counts only; no content.
- Telemetry includes the daily usage record and why tools fail. The batch carries the last 90 days of the installation's own
lean-ctx gainrecord (operations and tokens before/after compression per day, lifetime totals, month of first use), so usage through shell hooks and before telemetry is counted. Failed tool calls are reported by class (invalid input, not found, permission, policy, timeout, edit conflict, too large, unavailable), together with the five most frequent error messages per tool and day as templates: the client replaces every quoted text, path, file name, number, identifier, URL and e-mail address with a placeholder before anything is sent, and drops a message entirely when no safe wording remains. Shell commands that exit non-zero are now reported ascommandinstead ofinternal. - Upgrades turn on telemetry that was only off by the old default. Before 3.11 telemetry was opt-in, so
telemetry.enabled = falsewithout a recorded choice is re-enabled once on upgrade, and the one-time notice explains it. An opt-out made since 3.11 (lean-ctx telemetry off, declining in setup, and now alsolean-ctx config set telemetry.enabled false) is recorded as explicit and never overridden;DO_NOT_TRACK=1andLEAN_CTX_TELEMETRY=offalways win, and a read-only config is left untouched. - Telemetry counts which LeanCTX features are used. A daily
feature_aggregatereports how often each CLI command runs (lean-ctx pack export→cli.pack.export,graph build,index build-full,telemetry off…) and every graph, BM25 and semantic index build with its failures. Commands and verbs come from a fixed registry; arguments, paths and queries never become part of a code, and hot-path commands (shell hooks,-c,read,grep, statusline) are not counted. - Telemetry names 21 more AI clients. Cline, Roo Code, Kilo Code, Continue, OpenCode, Goose, Amp, Augment, JetBrains, Warp, Trae, Qwen Code, Crush, Claude Desktop, ChatGPT, LM Studio, Copilot CLI, Visual Studio, Neovim, Emacs and Factory are reported as their own family, and
noneseparates installations used only through the CLI and shell hooks from an unrecognised MCP client (other). The client's name is never sent, and what LeanCTX offers each client over MCP is unchanged. - Setup no longer asks about telemetry. Setup and the first interactive command after an install or update show a three-line notice instead: telemetry is on, how to turn it off (
lean-ctx telemetry off, orenabled = falseunder[telemetry]in theconfig.tomlit names) and where to see what is sent. The full list stays onlean-ctx telemetry on|offand the payload onlean-ctx telemetry show. An earlier explicit choice is kept.
Fixed
- ctx_shell output capture works in your project. Redirects and
tee(> build.log,>> notes.txt,| tee out.txt) were refused everywhere except/tmp,$TMPDIRandwrite_allow_paths; the project root stayed refused even when listed, andallow_paths/extra_rootswere not consulted. Capture may now also go to the session's project and the jail's allow entries./,~, its ancestors andread_only_rootsare never capture targets; downloads (curl -o,wget,dd of=) keep the scratch-only rule. - ctx_shell no longer relocates, queues or throttles your builds. Three multi-agent build settings were on by default:
agents.shared_cargo_targetpointed everycargo build/testrun through ctx_shell at<data dir>/build-cache/cargo-target, so./targetkept a stale binary;agents.serialize_build_commandsmade each build wait for builds from other sessions on the machine;agents.cargo_build_jobs = 3capped cargo at three jobs. All three are now opt-in (false,false,0) and documented in the config reference under[agents]. Set them again inconfig.tomlif you relied on the shared cache. - ctx_shell walk hint judges each invocation on its own (#2027). A scoped
grep -r … tarifffollowed by a stdin-readinggrepin the same command line (…; echo x | grep -c x,| grep -v _test) was treated as a recursive walk of the working directory, so the hint named `.claude/work...
