chameleon 2.22.4
A silent-failure hardening release from a two-pass adversarial audit (24
candidates, 11 confirmed), each fix re-verified across three independent rounds
against live code. The 2.22.3 worktree fix wired the read / advisory / trust
paths but not the enforcement gates; this completes that sweep and closes a set
of independent fail-open gaps. Strictly additive off the affected paths: the
worktree resolver is the identity for every non-worktree layout, so standalone
repos and monorepo workspaces behave byte-identically (full unit suite green).
Fixed
- Enforcement no longer silently no-ops in a linked git worktree. The
PreToolUse secret and banned-import denies, the PostToolUse enforce block, the
Stop turn-end backstop (plus its re-lint, attestation, and correctness-judge
reads), and the per-edit conventions echo all readrepo_root / ".chameleon"
off the raw worktree path. A worktree's profile is gitignored and lives only at
the main worktree, so each gate saw an empty/missing profile and silently fell
through while trust still reported "trusted" (the worst asymmetry). They now
resolve the main worktree's profile through a shared_enf_profile_dir,
keeping the worktree as the identity / archetype root.detect_repo's
production-branch hint resolves the same way. - A broken
uvno longer disables enforcement for the whole session. The
interpreter resolver accepted theuvrung after onlycommand -v uv; a
locked lockfile, an offline first-materialization, or a shadowing non-chameleon
uvthen failed at every hook with only a log line (the no-interpreter
degraded banner never fired). The rung is now probed with its real
uv run --project <mcp> pythonargv under a generous timeout and falls through
to the degraded banner when it fails. - A malformed
config.jsonis now observable instead of silently disabling
the denies and the Stop backstop. The enforcement gates caught the config
parse error in a bareexceptand fell through with no signal. They now record
a degraded check-event (surfaced in the session attestation and
/chameleon-doctor). It stays fail-open by design: failing closed is circular
(the enforcement mode is exactly what could not be parsed) and would wedge
every turn for a config with a stray typo. - The repo-root cache no longer masks an out-of-band
.chameleon. A
no-marker lookup was memoized with no re-stat, so the long-lived daemon served
a stale "no profile here" after agit worktree addor a manual.chameleon.
No-marker results are no longer cached, and positive entries carry a key-dir
mtime stamp that self-heals (mirrors the profile cache). - A sibling clone of the same remote now resolves to the most-recently-used
one._pick_ancestor_or_freshesttie-broke on shortest path string instead
of recency, so two clones sharing one repo_id loaded the wrong clone's profile.
It now keeps the freshest candidate on a descendant-count tie. - repo_id ignores an explicit port on a well-known host. A remote like
https://github.com:443/owner/repoorssh://git@github.com:22/owner/repo
derived a differentrepo_idthan the plain clone, silently losing the trust
grant. The port is now stripped before host matching (IPv6-safe). Such a remote
gets a correctedrepo_idand needs a one-time/chameleon-trustre-grant
(degrades to "untrusted", never data loss). Self-hosted hosts are unchanged.