Repository navigation
v2.0.0 — realign with Claude Code v2.1.220
ALBA v1.1.0 shipped on 2026-04-30 and was written against Claude Code v2.1.83. Three months of Claude Code releases later, parts of it had stopped working — not loudly, which is the problem. Hooks fail silently by design: a wrong field name, a wrong output shape, a default that flipped underneath you. The process exits 0, nothing is printed, and the feature simply never happens.
This release fixes what was broken, removes what could not be verified, and makes the silent failures detectable.
Why MAJOR: the minimum Claude Code version moves from v2.1.83 to v2.1.218 · the PreToolUse output contract changed and bash-validator.sh had to change with it · examples/*/.claude/ is now generated from templates/ and must not be hand-edited · templates/agents/researcher.md was removed.
Fixed
agent-suggest.shread a field that does not exist. TheUserPromptSubmitpayload carries the prompt in the top-levelpromptfield; the hook parsed.message // .user_prompt, which always resolved to empty. It then exited 0 with no output — the correct behavior for "no suggestion," and indistinguishable from it. The hook was dead in every session since it shipped and nothing ever reported it. It now reads.prompt, with a matching grep fallback for machines withoutjq.agent-suggest.shemitted a shape no event accepts. Its suggestions were printed as{"message": "..."}, which is not part of any hook output contract and would have been injected as literal JSON text. Suggestions are now plain stdout, whichUserPromptSubmitadds to the conversation as visible context — appropriate for a tip the user should also see.bash-validator.shwas lowering the permission floor it claimed to raise. For any command its 20-pattern blocklist did not match, it printed{"decision": "allow"}. OnPreToolUsethat maps topermissionDecision: "allow", whose documented effect is to skip the permission prompt. A hook presented as a security layer was silently auto-approving every command it had never heard of. It now emitsdenyon a match,askif the validator itself fails, and no output at all otherwise, so the normal permission flow is left untouched. This is the reference behavior from the official example:exit 0 # no decision; normal permission flow applies.bash-validator.shpatterns were unanchored and case-insensitive, so they matched their letters anywhere in a command string rather than a command being run.git commit -m "fix reboot handler"was blocked. Patterns are now anchored to a command position (start of string, or after;,&,|,(), matched case-sensitively for shell verbs and case-insensitively only for SQL. A wrapper-absorbing group in the anchor keepssudo rm -rf ~andenv FOO=1 rm -rf /in command position, so absorbing wrappers does not reopen the substring hole.bash-validator.shfail-safe was mislabeled. ItsERRtrap emitted a top-leveldecision: "block", a deprecated field on this event. A broken validator now emitspermissionDecision: "ask"— the user is asked, rather than the validator silently becoming a no-op.- The
settings.jsonsnippet intemplates/hooks/README-hooks.mdwas structurally wrong. It flattened the handler object into the event entry, omitting the mandatory innerhooksarray. That form is accepted without complaint and installs nothing, so anyone who copied it ended up with no hooks and no error message. Corrected, with the requirement called out explicitly. - Relative hook paths (
bash .claude/hooks/x.sh) resolve against the session's working directory and break the moment a session is opened in a subdirectory. All hook commands now use${CLAUDE_PROJECT_DIR}. - The
.envprotection rules never applied.settings.jsoncarrieddeny: Write(.env)andask: Write(**/*.env)— but file permission rules are matched onEdit(path)only, andEditalready covers every file-writing tool. Claude Code accepts aWrite(path)rule, prints a startup warning about it, and then ignores it, so the rule that was supposed to guard credential files had been inert since v1.1.0 introduced it. Both are nowEdit(...), the redundantWrite(memory/**)allow entry was dropped, andtools/doctor.shfails on anyWrite(path)rule. Found by running a real session rather than by reading the file — the warning only appears at startup. /setupcarried its own copy ofsettings.jsonand had drifted from the template. The skill inlined the settings JSON three times, once per scope, so none of the template's fixes reached a freshly set-up project: it still emitted the deadWrite(.env)rules, omittedpermissions.allowentirely, never installedsession-end.sh, and dropped thebashcommand prefix that lets hooks run without an executable bit. Phase 2d now copiestemplates/settings.json.templateand deletes the out-of-scope events, so there is one source again./setupPhase 3 contradictedCLAUDE.md.template. It instructed the wizard to write skills, tools and self-improvement sections into the generatedCLAUDE.md— exactly the tables the template had removed, because skills and MCP tools are discovered by the harness and that content already lives inrules/behavioral.md. A setup following the skill produced aCLAUDE.mdthe template calls wrong. Phase 3 now says fill in the template's sections and add nothing it does not have, with the reasoning attached.- Hook count was wrong everywhere. READMEs and CONTRIBUTING said 6 hooks; the repository has shipped 7 since v1.1.0 added
post-compact.sh, and now ships 8 across 9 event registrations (error-logger.shis wired to bothPostToolUseandPostToolUseFailure). Counts corrected in every document that states one, and made checkable —tools/doctor.shgreps every doc for a stated hook, skill or role count and fails if it disagrees with the files on disk.sync-examples.shcannot catch this class of error, because the example role docs describe the shared config without being copies of it. templates/claude/loop-integration.md.templateandmemory-compatibility.md.templatedid not exist.templates/INDEX.mdlisted both as generated docs, so the index pointed at files nothing produced. Both templates are now present.
Changed
- Minimum Claude Code is v2.1.218 (was v2.1.83); behavior verified against v2.1.220. The floor is set by the
backgroundfrontmatter field, which the two forked skills need. /researchand/reflectnow setbackground: false. Both carrycontext: fork. As of Claude Code v2.1.218, forked skills default tobackground: true— they run detached and their result arrives as a later notification rather than in the turn that invoked them. Both skills predate that release by months and changed behavior without anyone touching a line of them. Asking for research or reflection is a synchronous question, so the answer belongs in the turn that asked it.- All 9 skill descriptions rewritten to the trigger-plus-boundary pattern —
<what it does>. Use when user says "x", "y". Do NOT use for <z>.Thedescriptionis the only text Claude reads when deciding whether to invoke a skill; ALBA's previous descriptions were one-line definitions with no trigger phrases and no negative boundary. - Error log moved from
memory/knowledge/errors_raw.logto.claude/logs/errors_raw.log, with rotation at 500 lines. It is machine-local runtime state, not memory content, and it previously grew unbounded inside the git-tracked memory tree..gitignorenow excludes.claude/logs/instead of the old path. memory-check.shis rate-limited to once per 6 hours via a guard file, and its "session longer than 30 minutes" heuristic was removed. That heuristic read file birth time, which most Linux filesystems report as0— so on those systems the condition was always true and the hook fired on every singleStop, meaning after every response. A reminder that frequent is one users learn to read past. The guard replaces it, and the hook now stays silent — without burning the guard window — when it has nothing concrete to say.CLAUDE.md.templatecut from 149 to 85 lines anddecision-protocol.md.templatefrom 260 to 68. The decision protocol lived in three places with three levels of detail.templates/rules/behavioral.mdis now the single authority on when to ask and when to act — rules files are in context at the moment the decision is made, which a doc you would have to open is not. The doc keeps worked examples and phrasing, and never overrides the rule.examples/*/.claude/is generated fromtemplates/. Every hook script, doc and settings file previously existed in six independent copies, and a fix applied to one skipped the other five. The copies remain on disk so an example is still readable on GitHub, but they are build output now. Role-specific files —CLAUDE.md,README.md,memory/— are never overwritten.templates/hooks/README-hooks.mdsubstantially expanded — see Added.
Added
tools/sync-examples.sh— regenerates the shared parts ofexamples/fromtemplates/.--checkreports drift and exits non-zero without changing anything, so it works as a CI gate.tools/doctor.sh— health check for the failures that produce no error: hook event names that are not among the 30 real events,settings.jsonentries missing the innerhooksarray, hook commands pointing at scripts that do not exist, relative hook paths, aPreToolUsehook that returnsallow, template/example drift, broken repo-relative links, stated counts that no longer match the files, stale version claims, leaked absolute home paths, and removal residue such as strikethrough orDEPRECATEDtombstones. It also shell-parses every script and validates every JSON file. ALBA ships configuration rather than a runnable program, so this stands in for a test suite. Run it before committing and after upgrading Claude Code.templates/settings.json.template— the canonical settings file, previously reconstructed by hand in each example. Includes apermissions.allowlist for read-only operations (git status,git diff,ls, reads undermemory/and.claude/), which ALBA had never used, alongside the existingaskanddenyblocks.session-end.shhook on theSessionEndevent. ALBA's memory layer depends on/endbeing run, and the closing ritual is what users forget. This appends one dated line to today's log so a session that ended without/endis at least visible. It records that a session ran and when — it does not summarize or infer, because content that looks curated but came from a shell script is how a memory system starts lying.templates/rules/verification.md— an auto-loaded rule covering what to report after a change: what was verified and with which command, what was not verified and how to check it, and a three-line close for multi-step runs. "I didn't verify this" is a useful sentence; a wrong "it works" costs a debugging session.templates/hooks/README-hooks.mdnow documents the hook surface properly: all 30 hook events with the decision mechanism each one uses and which ones ALBA ships a hook for; theiffilter with its critical constraint —ifonly works on tool events (PreToolUse,PostToolUse,PostToolUseFailure,PermissionRequest,PermissionDenied), and putting it on any other event makes the hook never run and never report an error; handler types beyondcommand(http,mcp_tool,prompt,agent); and the timeout defaults (60s general, 30sUserPromptSubmit, 10sMessageDisplay).examples/*/.claude/agents/— the example roles now ship the agent template, which previously existed only undertemplates/.
Removed
templates/agents/researcher.md. The/researchskill already runs as a forked subagent doing the same job, so the repository offered two doors to one room with nothing to tell you which to use. The skill is the documented path.maxTurns,disallowedToolsandinitialPromptfrom agent frontmatter. v1.1.0 introduced these as "agent template hardening," but their enforcement could not be confirmed against current documentation — a constraint you cannot verify is not a constraint, and presenting one as a safety property is worse than not having it. For a hard restriction,permissions.denyinsettings.jsonis the mechanism that is actually enforced.- Unverifiable version attributions. Hook comments credited specific releases for specific behaviors (
v2.1.76+,v2.1.105,v2.1.113,v2.1.119). The behaviors are real; the version numbers could not be confirmed. Version numbers now appear only where they were verified, which in this release means v2.1.218 and v2.1.220. memory/knowledge/errors_raw.logfrom.gitignore— superseded by.claude/logs/.
Security
bash-validator.shno longer returnsallow. This is the single most consequential change in the release: the previous behavior removed the permission prompt for every command outside a 20-pattern blocklist, which is strictly worse than having no hook at all.permissions.allowin the shipped settings template is scoped to read-only commands and to paths inside the agent's own workspace. Nothing that writes outsidememory/, nothing that leaves the machine.
Migration from v1.x
Existing setups keep working in the sense that nothing crashes — but the fixes above only take effect once the templates are re-copied. In order:
- Upgrade Claude Code to v2.1.218 or later. On an older version,
background: falseis ignored and/researchand/reflectrun detached. - Re-copy the hook scripts from
templates/hooks/into your.claude/hooks/. Theagent-suggest.shandbash-validator.shfixes are in the scripts themselves; nothing else picks them up. - Rewrite hook paths in
.claude/settings.jsonto${CLAUDE_PROJECT_DIR}. Copytemplates/settings.json.templateif your settings file has no local customizations. Register the newSessionEndhook while you are in there. - Add
.claude/logs/to.gitignoreand remove thememory/knowledge/errors_raw.logline. The old log file can be deleted, or moved to.claude/logs/errors_raw.logif you want to keep its history. It is read by the/endskill and bypre-compact.sh, both of which now look in the new location only. - Add
background: falseto any of your own skills that usecontext: forkand are expected to answer in the same turn. - Run
tools/doctor.shto confirm the wiring.
If you have edited files under examples/, move those edits into templates/ before running tools/sync-examples.sh — it will overwrite them.