Releases: matthewsinclair/intent
Intent v2.11.5
[2.11.5] - 2026-05-05
Behavioural patch fixing three latent bugs surfaced by a Conflab session 2026-05-05. All three were shipped-as-broken; the first two silently produced output that looked plausible while dropping load-bearing content; the third silently regressed a project's recorded version stamp.
Fixed
-
intent treeindexreported "empty response from Claude" for every directory when run inside any v2.10.0+ Intent project. Root cause: the spawnedclaude -psession inherits the project'sUserPromptSubmithooks; the strict gate (require-in-session.sh) fires on the first prompt, sees no/in-sessionsentinel for the ephemeral session_id, and exits 2; the non-bareclaude -pswallows the hook's stderr and exits 0 with empty stdout. Treeindex saw empty stdout and reported per-directory failures. The treeindex tool was fine; the gate was the silent killer. -
intent agents generateproduced a stripped AGENTS.md (empty project name, no language scaffolding, no installed-skill enumeration, no conditional resource links) when invoked directly via the dispatcher. Root cause: thegeneratedispatch path did not callload_intent_config, leavingPROJECT_ROOTempty so every per-project detection (mix.exs,Cargo.toml,.claude/skills/,CLAUDE.md,usage-rules.md) silently failed.intent agents syncwas unaffected because it pre-loads config. Latent since the dispatcher was first added 2025-08-20; surfaced today whengeneratewas invoked standalone for a diff repro. -
migrate_v2_10_x_to_v2_11_0hard-coded the target stamp to"2.11.0"instead of the live Intent target. A project walked up from v2.10.x through the migration path would land withintent_version = "2.11.0"regardless of which v2.11.x patch was current. Field impact was muted becauseneeds_v2_11_0_upgradeshort-circuits projects already carrying thelanguagesfield, but the bug existed and would have stamped v2.11.5 projects as "2.11.0". Fix stampsget_intent_version.
Changed
-
require-in-session.shacceptsINTENT_SKIP_IN_SESSION_GATE=1as an explicit bypass. Non-interactive automation has no chat surface for/in-sessionto run in, so the gate has no UX affordance for those sessions. The env var is the opt-out wrappers set; the gate short-circuits to exit 0 before any other check. Interactive sessions and untagged automation continue through the normal sentinel-based gate. -
bin/intent_treeindexsetsINTENT_SKIP_IN_SESSION_GATE=1on itsclaude -pinvocation. Treeindex is automation by definition; the bypass is unconditional. -
intent_agents_generate_contentself-loads project context. The fix moves theload_intent_config+require_project_rootguard from the dispatcher branches into the function itself, so any caller (dispatcher,init,sync, future automation) gets a consistent project context without duplicating the load preamble. Highlander applied: one source of truth for "this function needsPROJECT_ROOT."
Documentation
intent/docs/working-with-llms.mdD7 documents theINTENT_SKIP_IN_SESSION_GATEbypass and adds it to the FAQ fix list.intent help treeindexlists the env var under a newENVIRONMENTsection so futureclaude -pwrapper authors can replicate the convention.
Tests
tests/unit/require_in_session_gate.batscovers the bypass branch and the existing slash-command / sentinel pass-throughs as regression smokes.tests/unit/intent_agents.batsgains a regression case assertingintent agents generatepopulates project name, language detection, and installed-skill enumeration when invoked withPROJECT_ROOTunset.tests/unit/intent_upgrade_dispatcher.batsgains a regression case asserting that a v2.10.x project upgraded via the migration path lands with the live target stamp (read fromVERSION), not a hard-coded literal.
Intent v2.11.4
[2.11.4] - 2026-04-30
Docs-only patch following v2.11.3's field verification.
Documentation
- Critic runner code locality —
intent/docs/critics.md"Headless runner" section gains a note clarifying that the runner (bin/intent_critic+critic_runner.sh) and the canon rule library load from$INTENT_HOME(the Intent install on$PATH), not from each project's plugin tree. A fix to the runner or a canon rule applies to every Intent project the moment Intent itself updates. Per-project canon refresh (intent claude upgrade) keepsintent/llm/RULES*.mdand.claude/skills/copies in sync with the Intent version, but is not a prerequisite for gate behaviour to change. The behaviour itself is unchanged from v2.11.3 — only the docs are clarified.
Verification
- v2.11.3's strict-proxy fix smoke-tested in Conflab (the canonical field witness) on 2026-04-30. Previously-misfiring patterns clear:
IN-EX-CODE-004no longer flags single-stepcase ... do;IN-EX-TEST-003no longer flags compliantuse ExUnit.Case, async: true. Gate still produces signal on real violations (e.g.IN-EX-TEST-001weak assertions,IN-EX-TEST-005control flow in tests,IN-EX-TEST-007). No stderrnote: skippingdiagnostics — expected, since the stripped rules no longer carry proxy blocks for the runner to refuse.
v2.11.3
ST0039 ships: pre-commit critic gate stops emitting findings derived from Greppable proxy regexes the headless runner cannot honour. Defect fix to behaviour shipped as broken in v2.11.0; no new feature surface, no schema change.
Fixed
-
Pre-commit gate false positives on
IN-EX-CODE-004(with-for-railway). Field report from a Conflab session post-upgrade-to-v2.11.0: the gate flagged everycase ... doline in two LiveView files (22 false positives in a 3-file diff), forcing back-to-back--no-verifycommits. Root cause:bin/intent_critic's parser extracted only the first quoted regex from a multi-line proxy block and ran it asgrep -nE, silently dropping the| wc -lqualifier that made the line a counter heuristic rather than a detector. The rule's actual detection — "two or more nested fallible calls withoutwith" — is body-confirmation territory and not expressible as a single-file regex. -
Pre-commit gate false positive on
IN-EX-TEST-003(async-by-default). Same Conflab session: the gate flagged the compliantuse ExUnit.Case, async: trueline. Root cause: the documented proxy usesgrep -rnL ... | xargs grep -l ...(find files lacking, async: true); the runner extracted the first quoted argument and ran it forward asgrep -nE, matching the compliant form. -
Runner silently degraded complex grep proxies (pipes,
xargs,grep -L,grep -v, awk, multi-line continuations).critic_pattern_from_grep_commandconsumed the first single-quoted argument from any proxy bash block and emitted findings as if the runner had executed the full pipeline. Replaced with a strict-proxy contract.
Changed
-
bin/intent_criticrunner contract is now strict.critic_runner.shaccepts only proxy lines of the formgrep [-r|-n|-E|-rn|-rE|-nE|-rnE|--include=GLOB ...] '<pattern>' [<path>...]: single grep invocation, no pipes, noxargs, no-L/-v/-B/-A/-l/-c/-o/-w/-x. Multi-line proxy blocks are accepted as a union of simple lines (findings deduped on(line, content)). Lines the runner cannot honour are refused with a once-per-rule stderr diagnosticnote: skipping <rule_id> (proxy not headless-runnable). Loud > silent. -
intent/docs/critics.md"Mechanical subset only" paragraph rewritten to document the strict-proxy contract and the stderr diagnostic.
Removed
-
Greppable proxy blocks stripped from 8 Elixir rules whose detection cannot be expressed as a simple single-file regex. The rules themselves are unchanged in prose and still apply via
/in-review(LLMcritic-elixirsubagent does the body confirmation):IN-EX-TEST-003(async-by-default) — inverse semantics.IN-EX-CODE-003(impl-true-on-callbacks) — line continuation + negative filter.IN-EX-LV-001(two-phase-mount) —-B5context + negative filter.IN-EX-LV-003(thin-liveviews) — awk state machine for line counting.IN-EX-PHX-001(thin-controllers) — awk state machine for line counting.IN-EX-ASH-001(code-interfaces-only) — callsite scope cannot be inferred per-file.IN-EX-ASH-002(actor-on-query) — proxy was inverted (fired on compliant code).IN-EX-TEST-004(start-supervised) — requiredgrep -v start_supervisedfilter.
-
IN-EX-CODE-004(with-for-railway) counter line — thecase.*do$ | wc -lheuristic is gone; the rule now ships only the legitimateerror -> errorforwarder detector. Single-stepcaseblocks are no longer mechanically flagged. -
critic_pattern_from_grep_commandfunction incritic_runner.sh. Replaced bycritic_proxy_is_simplepredicate +critic_patterns_from_grep_blockwalker. No back-compat shim (fail-forward).
Intent v2.11.2
Second hotfix following v2.11.0/v2.11.1.
Fixed
intent upgradefailed withError: Unknown version: 2.11.0when applied to a project already at v2.11.x. The dispatcher inbin/intent_upgradehad cases for every released source version up to v2.10.1 but none for v2.11.0; an in-flight project at v2.11.0 attempting to upgrade to v2.11.1 fell into the*) error "Unknown version: $VERSION"arm. Added a"2.11.*"case that runs the idempotent v2.11.x migration (no-op on field-already-present and stamp-already-current); a glob match rather than a literal so future patches in the v2.11 line don't need a fresh case each time.
Intent v2.11.1
[2.11.1] - 2026-04-28
CI hotfix following v2.11.0.
Fixed
- Pre-commit hook errors on
set -u+ emptyLANGSarray under some bash versions (CI macOS runner). v2.11.0 introduced the empty-array path (a project withlanguages: []declares zero critics), but the existingfor lang in "${LANGS[@]}"; doloop expansion erred as "unbound variable" before the loop body ran. Length-guarded the loop with an explicitif [ "${#LANGS[@]}" -gt 0 ]; thencheck. Local installs need to re-runintent claude upgrade --applyto pick up the corrected hook template; new installs from v2.11.1 onward get the fix automatically.
Intent v2.11.0
[2.11.0] - 2026-04-28
ST0037 ships: languages-in-use becomes an explicit per-project configuration field, replacing four sites of filesystem-marker probing. The probe-based detection was a regression against design intent (filesystem presence is unreliable evidence; a vendored example or a one-off script can flip the wrong switch). Schema change is automatic via migration; existing fleet projects need no user action beyond intent upgrade.
Added
languages: []field inintent/.config/config.json. Array of canonical language names (elixir,rust,swift,lua,shell). Array order is the explicit declaration; the first entry is the primary where a primary is needed. Empty array is a valid state.intent lang remove <lang> [<lang> ...]-- new verb. Reversesintent lang init: removes the entry from the agnosticRULES.mdLanguage Packs marker block, deletesintent/llm/RULES-<lang>.mdandintent/llm/ARCHITECTURE-<lang>.md, removes the language fromintent/.config/config.json. Idempotent: a never-installed language emitsnoop:and returns 0.get_project_languages()helper inbin/intent_helpers. Reads the field viajq, returns one language per line, returns 0 lines when the array is empty or the field is absent. Used by the pre-commit critic gate.add_project_language()/remove_project_language()helpers inbin/intent_helpers. Atomic config-field mutation via tempfile +mv.migrate_v2_10_x_to_v2_11_0inbin/intent_helpers. Adds thelanguagesfield with back-fill from existingintent/llm/RULES-<lang>.mdpresence (alphabetical for determinism). When the back-fill set is empty AND a pre-commit hook is installed, falls back to["shell"]to preserve current "shell-always" gate behaviour. Idempotent: if the field is already present, only stamps the version.- BATS coverage for the migration (
tests/unit/migrate_v2_10_x_to_v2_11_0.bats), the newintent lang initconfig writes and the newintent lang removeverb (tests/unit/intent_lang.bats), the config-driven critic dispatch (tests/unit/critic_dispatch.bats), the config-driven pre-commit gate (tests/unit/pre_commit_hook.bats), and the regression guards on/in-sessionSKILL.md (no filesystem probes, no phantom skill refs).
Changed
/in-sessionSKILL.md -- detection table replaced with config-driven flow. The skill reads(.languages // []) | .[]fromintent/.config/config.jsonand invokes any matching essentials skill. Currently only/in-elixir-essentials(and/in-elixir-testing) are real per-language essentials skills; rust/swift/lua/shell coding rules ship via the rule library atintent/plugins/claude/rules/<lang>/plus thecritic-<lang>subagent applied on demand./in-reviewSKILL.md -- stage-2 dispatch table replaced with config-driven flow. Reads thelanguagesarray, dispatches one critic per language listed./in-tca-auditSKILL.md -- critic-selection table replaced with the same config-driven dispatch.lib/templates/hooks/pre-commit.sh-- theLANGS+=(elixir)etc. probe block is gone. The hook readslanguagesfrom config; an empty array means no language critics run, mirroring the explicit-config contract.bin/intent_init-- fresh projects get"languages": []in their initial config. The existing--lang <lang>flag still seeds the array viaintent lang init.intent/docs/working-with-llms.md-- "Skills and /in-session auto-load" section rewritten to describe the config-driven flow; the four phantom skill references (/in-rust-essentials,/in-swift-essentials,/in-lua-essentials,/in-shell-essentials) are gone.
Removed
- Filesystem-probe-based language detection at four canon sites (
in-session/SKILL.md,in-review/SKILL.md,in-tca-audit/SKILL.md,lib/templates/hooks/pre-commit.sh). File presence is no longer treated as evidence of language-in-use. - Phantom skill references to
/in-rust-essentials,/in-swift-essentials,/in-lua-essentials,/in-shell-essentials. Those skills were promised in WP06/WP12 ("ships in WPNN") in the v2.10.x SKILL.md but never authored. The rule-pack + critic-subagent path is the working mechanism for those four languages and the prose now reflects that.
Fixed
create_v2_directory_structure()inbin/intent_helpers-- previously created an empty top-level.intent/unconditionally duringintent upgrade, even on projects already on the v2.10 layout (intent/.config/present). The next phase (migrate_v2_9_0_to_v2_10_0) then refused to proceed because both.intent/andintent/.config/existed. Skips the.intent/creation whenintent/.config/is in place. Pre-existing latent bug, surfaced when chaining the v2.9.0 → v2.10.0 → v2.11.0 migration sequence.- critic-elixir false positives on canonical OTP/Mix idioms (ST0038). Three rules misfired in the headless pre-commit gate against correct code (Lamplight ST0163/WP-01 commit attempt):
IN-EX-TEST-002(no-process-sleep) fired onProcess.sleep(:infinity)in aMix.Task.run/1body inlib/. The rule's frontmatter declaredapplies_to: ["test/**/*_test.exs"]but the runner ignored the field. Fixed by addingapplies_tohonoring incritic_apply_rule(intent/plugins/claude/lib/critic_runner.sh); globs are matched with suffix anchoring so umbrella layouts (apps/<app>/lib/...,apps/<app>/test/...) resolve correctly.IN-EX-CODE-002(tagged-tuple-returns) fired on every publicdef name(args) dobecause the greppable proxy was too coarse to express "fallible function returns bare nil/false" as a per-file regex. Greppable proxy stripped; the rule remains active for the LLM-drivencritic-elixirsubagent via/in-review, where the body and call sites can be read.IN-EX-CODE-006(module-highlander) fired on every publicdef name(...)for the same reason -- and the rule's actual concern (cross-module duplication) is fundamentally not a per-file scan. Greppable proxy stripped; subagent-applied via/in-review.- New BATS coverage in
tests/unit/critic_runner_applies_to.bats(15 tests) verifies glob-to-regex translation, umbrella-layout matching, the absence of greppable proxies on the two stripped rules, and the presence of the proxy onIN-EX-TEST-002.tests/unit/pre_commit_hook.batsupdated to stage fixtures undertest/<name>_test.exsso they matchIN-EX-TEST-001'sapplies_to.
Migration notes
intent upgradefrom any v2.10.x project runsmigrate_v2_10_x_to_v2_11_0automatically. The migration is atomic, idempotent, and cannot lose user data.- Polyglot projects: declare languages in primacy order with
intent lang init <primary> <secondary> .... The first entry is the primary; later entries follow. To change primacy,intent lang remove <lang>and re-init in the desired order.
Intent v2.10.1
[2.10.1] - 2026-04-28
v2.10.x polish line. Two new pieces of maintainer infrastructure (a release script and a intent doctor migration-leftover warning), the gate-firing fix that surfaced post-v2.10.0 dogfood, and three pre-existing v2.10.0 dogfood-journal follow-ups that needed closing.
Added
scripts/release-- maintainer release orchestrator. Single-invocation cut: pre-flight (clean tree, doctor, tests, gh auth), version bump (--patch / --minor / --major / vX.Y.Z), CHANGELOG date finalisation, sidecar sync (VERSION + AGENTS.md), commit, idempotent tag, push to both remotes (local + upstream), GitHub release publication. Modelled on Conflab's release pattern, pared back to Intent's surface (single repo, two remotes, no native binary, no Homebrew tap).--dry-runpreviews every step with no side effects.intent doctorcheck 4d -- warning (not error) when a stale top-level.intent/directory remains after a v2.9 -> v2.10 migration. Auto-staging is intentionally NOT done: the user runsgit rm -rf .intent/themselves so the cleanup is visible in the commit.
Fixed
/in-sessionUserPromptSubmit gate-firing loop. The SKILL.md inlined an awk pipeline whose positional-field expansion was silently emptied by Claude Code's skill renderer, producing a malformed project_key that prevented the per-session sentinel from being written. The waterfall is now inintent/plugins/claude/skills/in-session/scripts/release-gate.sh, invoked by the SKILL by path; the renderer never sees the pipeline.intent claude upgrade --dry-runUX -- distinguish three states for theconfig.jsonpre-flight (canonical, legacy.intent/, absent) so a pre-relocation project no longer reports its expected-missing config as a hard problem. The legacy-location case now points the user atintent upgradefor the relocation step.IN-RS-CODE-005(lifetime-elision-first) carve-out -- explicit "Does Not Apply" entry for teaching examples inintent/plugins/claude/rules/**andtests/fixtures/critics/rust/**. Closes the false-positive that fired on a clean fixture during ST0034 WP07 verification.
Changed
- Diogenes test-spec handoff -- the four critic agent.md files (
critic-{elixir,rust,swift,lua}) now uniformly suppress the Diogenes RECOMMENDATION for targets undertests/fixtures/critics/. Critic self-test fixtures are not real test code; the handoff was firing inconsistently across critics post-WP07.
Intent v2.10.0
[2.10.0] - 2026-04-27
Retargeted from v2.9.1 mid-development to bundle ST0036 (directory relocation, breaking change) into the same release. Version bump reflects the semver-breaking directory move; LLM canon work (originally scoped as v2.9.1) ships alongside.
Two steel threads landed in this release:
- ST0035 -- Canonical LLM Config + Fleet Rollout. Three-file root canon (
AGENTS.md+CLAUDE.md+usage-rules.md), session hooks (SessionStart + UserPromptSubmit strict gate + Stop), pre-commit critic gate viabin/intent_critic,.intent_critic.ymlper-project config, theworking-with-llms.mdcanon narrative, and per-language canon (intent lang init). - ST0036 -- Directory relocation
.intent/->intent/.config/. Breaking change. Intent's per-project metadata directory moves from a separate top-level.intent/to a nestedintent/.config/, eliminating the "two top-level dirs" smell. Migration handled atomically bymigrate_v2_9_0_to_v2_10_0onintent upgrade.
Fleet rollout: 14 in-scope projects (Intent self + 8 canary + 5 user-manual; Pplr OOS) all on v2.10.0 canon. Canary discipline surfaced and resolved three canon-installer rough edges (MIGRATE_LEGACY_PRE_COMMIT, CHAIN_PRE_COMMIT auto-insert, NORMALIZE_GITIGNORE) before fleet sweep. See intent/st/ST0035/WP/15/canary-summary.md, WP/16/fleet-summary.md, WP/17/feedback-report.md, and WP/17/dogfood-journal.md.
Added
- ST0035 (Canonical LLM Config + Fleet Rollout).
- ST0036 (Directory relocation:
.intent/->intent/.config/). Breaking change. Intent's per-project metadata directory moves from top-level.intent/to nestedintent/.config/, eliminating the "two top-level dirs" smell. Migration handled atomically bymigrate_v2_9_0_to_v2_10_0onintent upgrade. intent langcommand (ST0035/WP-19) for per-language canon installation. Subcommands:list,show,init.intent lang init <lang> [<lang> ...]is idempotent and multi-language; copiesintent/plugins/agents/templates/<lang>/{RULES,ARCHITECTURE}.mdintointent/llm/{RULES,ARCHITECTURE}-<lang>.mdand appends a marker-bracketed entry to the agnosticintent/llm/RULES.mdLanguage Packs section. Replaces the rejected auto-language-detection approach (real projects are polyglot; explicit user choice via--langis more honest). Available canon languages:elixir,rust,swift,lua,shell. New stub templates ship for the four newer languages.intent init --lang <list>flag invokesintent lang initfor each named language post-init. Comma- or space-separated list. Equals form (--lang=elixir) also accepted.- Agnostic
_defaultcanon now includes RULES.md + ARCHITECTURE.md in freshintent init. Previously onlyMODULES.md+DECISION_TREE.mdwere laid down; canon-installer's_defaulttemplates were only seen viaintent claude upgrade --apply. Nowintent initproduces a v2.10.0-complete baseline including the Language Packs anchor thatintent lang initwrites into.
Changed
bin/intent_helpers:migrate_v2_9_0_to_v2_10_0()replaces the earliermigrate_v2_9_0_to_v2_9_1()stub. Bundles version stamp + ST0036 directory relocation. Canon-apply logic still lands in ST0035/WP11 viaintent claude upgrade --apply(separate step).bin/intent_upgrade: chain extended to v2.10.0 (new gateneeds_v2_10_0_upgrade, new case, new chain tail).- Root
VERSIONbumped to2.10.0. - Treeindex ignore canonicalised (ST0036/WP-06): new
lib/templates/_treeindexignoretemplate is the single source of truth.bin/intent_treeindex::ensure_treeindexignorereads from the template instead of an inline heredoc (Highlander cleanup per CLAUDE.md project rule #6).intent claude upgrade --applyinstalls the file when absent (newINSTALL_TREEINDEXIGNOREaction; existing files left alone). Granularity flipped from blanket.intent/tointent/.config/cache/+intent/.config/backup/soconfig.jsonstays indexed. - Pre-commit hook template (ST0036/WP-04):
lib/templates/hooks/pre-commit.shnow probesintent/.config/config.jsoninstead of.intent/config.jsonwhen deciding whether to skip the critic gate (fail-open in non-Intent repos). Newly-installed gates and any project that re-runsintent claude upgrade --applyafter v2.10.0 pick up the corrected probe.
Breaking
- Per-project metadata directory relocated:
.intent/config.json→intent/.config/config.json. Same for.intent/backup/→intent/.config/backup/. Anything scripting against.intent/(CI, editor plugins, ad-hocjq) breaks on upgrade; update tointent/.config/. Migration is fail-forward: old location is pruned, no backwards-compat symlink. Full migration guide including recovery from interrupted upgrades:intent/docs/migration-v2.10.0.md.
Removed
intent/usr/*.mdretired (ST0035/WP-18). The three hand-authored user docs (user_guide.md,reference_guide.md,deployment_guide.md) were stamped atintent_version: 2.6.0(2026-03-05), seven minor versions behind the v2.10.0 canon, and substantially duplicated byREADME.md,intent/docs/working-with-llms.md,intent help <cmd>, andAGENTS.md. Per fail-forward (no preservation, prune actively): all three deleted; theintent/usr/directory is gone. Cross-references updated:README.mdDocumentation and Getting Help sections now point atintent/docs/working-with-llms.md(canon narrative) +intent help(commands) +intent/docs/migration-v2.10.0.md(upgrade path);docs/blog/0005-getting-started-with-intent.mdIntent Documentation section refreshed;intent/docs/migration-v2.10.0.md"unchanged subdirectories" list trimmed.- ST0010 (Anthropic MCP Integration, v2.0.0-era) cancelled — superseded by v2.9.0 skills / subagents / extensions. Moved to
intent/st/CANCELLED/with deprecation annotation. - ST0015 (Enhanced Steel Thread Templates, v2.0.0-era) cancelled — superseded by v2.9.0 tooling. Moved to
intent/st/CANCELLED/with deprecation annotation.
Intent v2.9.0 — Agentic Software Engineering Suite
Intent v2.9.0 Release Notes
Release Date: 2026-04-23
Overview
Intent v2.9.0 ships the Agentic Software Engineering Suite (ST0034). Three things change at once and they were designed to land together:
- Rules become first-class citizens. Coding standards live in
intent/plugins/claude/rules/<lang>/<category>/<slug>/RULE.mdas atomic, cite-able files with stableIN-*IDs, structured frontmatter, Detection heuristics, and bad/good examples. - Critic subagents enforce rules mechanically. A new
critic-<lang>family (critic-elixir,critic-rust,critic-swift,critic-lua,critic-shell) reads the rule library, applies each rule's Detection heuristic to target files, and emits a stable severity-grouped report. - A user extension system at
~/.intent/ext/lets you ship your own subagents, skills, or rule packs without forking Intent. Discovery is layered: canon is the default; user extensions override by name with a visible shadow warning. The reference extension isworker-bee, relocated from canon to demonstrate the mechanism end-to-end.
These three pieces unlock the same workflow loop: a rule file says what good looks like, a critic enforces it on real code, and an extension lets you customise either without touching upstream.
Rules library
A rule is one atomic standard. It has a stable ID (IN-EX-CODE-006, IN-AG-HIGHLANDER-001, etc.), a Detection heuristic, bad/good examples, and required Markdown sections. Skills cite rules by ID; the rule file owns the prose.
The library ships with packs for agnostic, elixir, rust, swift, lua, and shell. The schema is intentionally compatible with iautom8things/elixir-test-critic, so upstream rules drop into Intent's discovery unchanged.
The intent claude rules command surface — list, show, validate, index — operates against the library. validate is the canonical authoring gate; index regenerates a deterministic, sorted index.json.
Authoring guide: intent/docs/rules.md. Schema reference: intent/plugins/claude/rules/_schema/rule-schema.md.
Critic subagents
Critics are thin orchestrators. On invocation, a critic re-reads the rule library (no caching), applies each rule's Detection heuristic to the target source files, and emits a parse-stable severity-grouped report:
## Critic Report: critic-elixir code lib/myapp/accounts.ex
CRITICAL
- IN-EX-CODE-005 (no-silent-failures) lib/myapp/accounts.ex:42
rescue _ -> :ok swallows the lookup failure silently.
Surface the error tuple or let it raise.
WARNING
- IN-EX-CODE-002 (tagged-tuple-returns) lib/myapp/accounts.ex:18
Function returns bare nil on not-found.
Return {:ok, value} | {:error, reason} so callers can pattern-match.
Summary: 1 critical, 1 warning, 0 recommendation, 0 style.
Rules applied: 4 agnostic, 12 language-specific.
Modes are code and test (critic-shell is code only). Each rule's applies_to glob narrows the file set further. Per-project config lives in .intent_critic.yml at the project root for disabling rules and adjusting severity thresholds.
Critics never autofix, never shell out to external linters, never invent rule IDs. Every finding cites a real RULE.md you can open directly.
The full contract — invocation, modes, ambiguity handling, report format, .intent_critic.yml schema, Diogenes/Socrates handoffs, and the registration-freeze operational note — lives at intent/docs/critics.md.
User extensions (~/.intent/ext/)
Extensions are content-only directories that contribute subagents, skills, or rule packs. Each extension has an extension.json manifest declaring its contributions and Intent compatibility bounds. Discovery is layered: canon stays the default; user extensions override by name; extension rules override canon rules with the same ID. Every shadow is logged — no silent overrides.
Author your own:
intent ext new my-agent --subagent
intent ext new my-skill --skill
intent ext new my-rules --rule-pack
intent ext validate my-agentAuthoring guide: intent/docs/writing-extensions.md. Manifest schema: intent/plugins/claude/ext-schema/extension.schema.json.
The reference extension is worker-bee. In v2.8.x it was a canon subagent; in v2.9.0 it is the worked example for the extension mechanism. The migration seeds it from lib/templates/ext-seeds/worker-bee/ to ~/.intent/ext/worker-bee/ on first run. If worker-bee can live as an extension end-to-end, any user-authored subagent can.
Breaking changes
elixirsubagent deleted. Usecritic-elixirinstead. The migration prunes installed copies on upgrade.worker-beemoved out of canon. Re-install from the extension after upgrade:intent claude subagents install worker-bee.
Both are aggressive, fail-forward changes — there are no compatibility shims and no deprecation period inside the migration. The migration prunes the installed copies of both and seeds the new worker-bee extension. Run the upgrade, restart your session, re-install worker-bee from the extension if you want it.
Upgrade
intent upgrade --applyThe migrate_v2_8_2_to_v2_9_0 step (in bin/intent_helpers) does four things:
- Stamps
.intent/config.jsonwithintent_version: 2.9.0. - Bootstraps
~/.intent/ext/with a README on first run. - Seeds
~/.intent/ext/worker-bee/fromlib/templates/ext-seeds/worker-bee/(skipped if already present — never overwrites user state). - Prunes installed copies of the deleted
elixirsubagent and the relocatedworker-beefrom~/.claude/agents/and~/.intent/agents/installed-agents.json.
The migration is idempotent — running the upgrade twice is safe.
To verify post-upgrade state:
intent doctor # general health check
cat .intent/config.json | jq .intent_version # should print "2.9.0"
intent ext list # should show worker-bee
intent claude subagents list | grep critic- # should show 5 critic-* entriesRestart Claude Code after the upgrade. Mid-session subagent installs are not visible to Task() until the next session starts. If you try to invoke critic-elixir (or any other critic) without restarting, you'll get "subagent not found". This is a Claude Code constraint, not an Intent behaviour.
Post-release fixes (tag updated 2026-04-23)
The v2.9.0 tag was force-updated from d1b0fe1 to 0de89cd to include the first real-world dogfood pass of critic-shell against Intent's own bash codebase (WP12 dogfood Entry 1). Three follow-up commits resolve every CRITICAL finding and most WARNING findings the new critic surfaced in production code:
a9ee349— P0/P1 fixes. Seven CRITICAL plus three WARNING resolved acrossintent_st,intent_audit,intent_doctor,intent_plugin, andtests/run_tests.sh. Bugs included unquoted command substitutions in path construction,for x in $(find ...)patterns that swallowed find's exit code underset -e, an unsafefind | xargspipeline missing-print0, and aget_terminal_width()shadow that silently overrode the canonical helper.60dfcd6— Patches the dogfood journal commit reference.0de89cd— P2 sweep (Highlander pass).warning()andinfo()lifted intobin/intent_helpersas canonical helpers (the latter QUIET-aware); pure-shadow duplicates stripped from four files. P2a (set -eadoption) shipped only fortests/run_tests.sh; rolled back from six production scripts after surfacing real conflicts with the[ -d ... ] || error "not found"pattern they use — recorded as follow-on work in the dogfood journal.
Full triage and rationale: intent/st/COMPLETED/ST0034/WP/12/dogfood-journal.md Entry 1.
If you already pulled v2.9.0 at d1b0fe1, re-fetch the tag (git fetch --tags --force) to get the dogfooded version. The migration step and all behavioural surfaces remain unchanged — these are bug fixes in Intent's own bash, not new features.
Acknowledgements
The rule schema, the borrowed rule principles, and the _attribution/elixir-test-critic.md MIT notice all credit iautom8things/elixir-test-critic (MIT, copyright 2026 Manuel Zubieta), pinned at commit 1d9aa40700dab7370b4abd338ce11b922e914b14. The compatibility was deliberate: upstream rules drop into Intent's discovery unchanged, and Intent's critic-elixir recognises upstream's elixir-test-critic plugin if it is installed at ~/.claude/plugins/elixir-test-critic/.
Migration notes for fleet projects
- The migration is gated on
needs_v2_9_0_upgrade(returns true for< 2.9.0, recognises2.9.x,2.10.x, and3.xas already-migrated). - The migration chain in
bin/intent_upgradecovers every prior starting version (16 chain-tails); a project on any v2.x release upgrades cleanly through to v2.9.0. - Worker-bee's
intent_compat.minin the seed manifest is2.8.2— projects on the new minimum or higher load the extension successfully.
v2.8.2 -- cwd-resilient dispatch + Credo wiring
Fixed
- ST0033: cwd-resilient dispatch.
intentsubcommands now work from any directory inside an Intent project, not only from the project root. The dispatcher (bin/intent) exportsINTENT_ORIG_CWDandcds to$PROJECT_ROOTbeforeexec'ing the subcommand, so every subcommand runs with a known-correct cwd. Outside any project, commands fail cleanly with "not in an Intent project" and no longer create stray.intent/orintent/directories at the invoker's cwd.intent treeindexandintent fileindexconsultINTENT_ORIG_CWDwhen resolving relative path arguments. - Upgrade chain completed through 2.8.2.
bin/intent_upgrade's case statement previously halted at 2.6.0 for any starting version <= 2.5.0 and had no entry for 2.6.0/2.7.0 at all, leaving projects stuck mid-chain. Every starting-version case now chains throughmigrate_v2_6_0_to_v2_8_0(new, pure version stamp),migrate_v2_8_0_to_v2_8_1, andmigrate_v2_8_1_to_v2_8_2. The pre-v2 fallback chain is extended to match.needs_v2_8_2_upgradeaccepts 2.6.0 and 2.7.0 as starting points. - ST0032: Credo custom checks wired into
.credo.exs.intent st zero(D5a) andintent auditnow use a standalonelib/scripts/configure_credo.exsto programmatically patch.credo.exs, replacing the earlier wrong hint aboutelixirc_pathsinmix.exsand theintent audit --checks-dirworkaround. Removed 2 broken check templates (boolean_operators,dependency_graph), fixed 4 buggy ones (map_get_on_struct,missing_impl_annotation,debug_artifacts,thick_coordinator), and addedbracket_access_on_struct. Existing projects that went throughst zerocan re-run D5a to pick up the wiring.