Skip to content

Releases: matthewsinclair/intent

Intent v2.11.5

05 May 13:40

Choose a tag to compare

[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 treeindex reported "empty response from Claude" for every directory when run inside any v2.10.0+ Intent project. Root cause: the spawned claude -p session inherits the project's UserPromptSubmit hooks; the strict gate (require-in-session.sh) fires on the first prompt, sees no /in-session sentinel for the ephemeral session_id, and exits 2; the non-bare claude -p swallows 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 generate produced 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: the generate dispatch path did not call load_intent_config, leaving PROJECT_ROOT empty so every per-project detection (mix.exs, Cargo.toml, .claude/skills/, CLAUDE.md, usage-rules.md) silently failed. intent agents sync was unaffected because it pre-loads config. Latent since the dispatcher was first added 2025-08-20; surfaced today when generate was invoked standalone for a diff repro.

  • migrate_v2_10_x_to_v2_11_0 hard-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 with intent_version = "2.11.0" regardless of which v2.11.x patch was current. Field impact was muted because needs_v2_11_0_upgrade short-circuits projects already carrying the languages field, but the bug existed and would have stamped v2.11.5 projects as "2.11.0". Fix stamps get_intent_version.

Changed

  • require-in-session.sh accepts INTENT_SKIP_IN_SESSION_GATE=1 as an explicit bypass. Non-interactive automation has no chat surface for /in-session to 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_treeindex sets INTENT_SKIP_IN_SESSION_GATE=1 on its claude -p invocation. Treeindex is automation by definition; the bypass is unconditional.

  • intent_agents_generate_content self-loads project context. The fix moves the load_intent_config + require_project_root guard 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 needs PROJECT_ROOT."

Documentation

  • intent/docs/working-with-llms.md D7 documents the INTENT_SKIP_IN_SESSION_GATE bypass and adds it to the FAQ fix list.
  • intent help treeindex lists the env var under a new ENVIRONMENT section so future claude -p wrapper authors can replicate the convention.

Tests

  • tests/unit/require_in_session_gate.bats covers the bypass branch and the existing slash-command / sentinel pass-throughs as regression smokes.
  • tests/unit/intent_agents.bats gains a regression case asserting intent agents generate populates project name, language detection, and installed-skill enumeration when invoked with PROJECT_ROOT unset.
  • tests/unit/intent_upgrade_dispatcher.bats gains a regression case asserting that a v2.10.x project upgraded via the migration path lands with the live target stamp (read from VERSION), not a hard-coded literal.

Intent v2.11.4

30 Apr 11:38

Choose a tag to compare

[2.11.4] - 2026-04-30

Docs-only patch following v2.11.3's field verification.

Documentation

  • Critic runner code localityintent/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) keeps intent/llm/RULES*.md and .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-004 no longer flags single-step case ... do; IN-EX-TEST-003 no longer flags compliant use ExUnit.Case, async: true. Gate still produces signal on real violations (e.g. IN-EX-TEST-001 weak assertions, IN-EX-TEST-005 control flow in tests, IN-EX-TEST-007). No stderr note: skipping diagnostics — expected, since the stripped rules no longer carry proxy blocks for the runner to refuse.

v2.11.3

29 Apr 17:19

Choose a tag to compare

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 every case ... do line in two LiveView files (22 false positives in a 3-file diff), forcing back-to-back --no-verify commits. Root cause: bin/intent_critic's parser extracted only the first quoted regex from a multi-line proxy block and ran it as grep -nE, silently dropping the | wc -l qualifier that made the line a counter heuristic rather than a detector. The rule's actual detection — "two or more nested fallible calls without with" — 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 compliant use ExUnit.Case, async: true line. Root cause: the documented proxy uses grep -rnL ... | xargs grep -l ... (find files lacking , async: true); the runner extracted the first quoted argument and ran it forward as grep -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_command consumed 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_critic runner contract is now strict. critic_runner.sh accepts only proxy lines of the form grep [-r|-n|-E|-rn|-rE|-nE|-rnE|--include=GLOB ...] '<pattern>' [<path>...]: single grep invocation, no pipes, no xargs, 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 diagnostic note: 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 (LLM critic-elixir subagent 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) — -B5 context + 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) — required grep -v start_supervised filter.
  • IN-EX-CODE-004 (with-for-railway) counter line — the case.*do$ | wc -l heuristic is gone; the rule now ships only the legitimate error -> error forwarder detector. Single-step case blocks are no longer mechanically flagged.

  • critic_pattern_from_grep_command function in critic_runner.sh. Replaced by critic_proxy_is_simple predicate + critic_patterns_from_grep_block walker. No back-compat shim (fail-forward).

Intent v2.11.2

28 Apr 18:04

Choose a tag to compare

Second hotfix following v2.11.0/v2.11.1.

Fixed

  • intent upgrade failed with Error: Unknown version: 2.11.0 when applied to a project already at v2.11.x. The dispatcher in bin/intent_upgrade had 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

28 Apr 17:11

Choose a tag to compare

[2.11.1] - 2026-04-28

CI hotfix following v2.11.0.

Fixed

  • Pre-commit hook errors on set -u + empty LANGS array under some bash versions (CI macOS runner). v2.11.0 introduced the empty-array path (a project with languages: [] declares zero critics), but the existing for lang in "${LANGS[@]}"; do loop expansion erred as "unbound variable" before the loop body ran. Length-guarded the loop with an explicit if [ "${#LANGS[@]}" -gt 0 ]; then check. Local installs need to re-run intent claude upgrade --apply to pick up the corrected hook template; new installs from v2.11.1 onward get the fix automatically.

Intent v2.11.0

28 Apr 16:52

Choose a tag to compare

[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 in intent/.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. Reverses intent lang init: removes the entry from the agnostic RULES.md Language Packs marker block, deletes intent/llm/RULES-<lang>.md and intent/llm/ARCHITECTURE-<lang>.md, removes the language from intent/.config/config.json. Idempotent: a never-installed language emits noop: and returns 0.
  • get_project_languages() helper in bin/intent_helpers. Reads the field via jq, 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 in bin/intent_helpers. Atomic config-field mutation via tempfile + mv.
  • migrate_v2_10_x_to_v2_11_0 in bin/intent_helpers. Adds the languages field with back-fill from existing intent/llm/RULES-<lang>.md presence (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 new intent lang init config writes and the new intent lang remove verb (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-session SKILL.md (no filesystem probes, no phantom skill refs).

Changed

  • /in-session SKILL.md -- detection table replaced with config-driven flow. The skill reads (.languages // []) | .[] from intent/.config/config.json and 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 at intent/plugins/claude/rules/<lang>/ plus the critic-<lang> subagent applied on demand.
  • /in-review SKILL.md -- stage-2 dispatch table replaced with config-driven flow. Reads the languages array, dispatches one critic per language listed.
  • /in-tca-audit SKILL.md -- critic-selection table replaced with the same config-driven dispatch.
  • lib/templates/hooks/pre-commit.sh -- the LANGS+=(elixir) etc. probe block is gone. The hook reads languages from 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 via intent 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() in bin/intent_helpers -- previously created an empty top-level .intent/ unconditionally during intent 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/ and intent/.config/ existed. Skips the .intent/ creation when intent/.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 on Process.sleep(:infinity) in a Mix.Task.run/1 body in lib/. The rule's frontmatter declared applies_to: ["test/**/*_test.exs"] but the runner ignored the field. Fixed by adding applies_to honoring in critic_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 public def name(args) do because 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-driven critic-elixir subagent via /in-review, where the body and call sites can be read.
    • IN-EX-CODE-006 (module-highlander) fired on every public def 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 on IN-EX-TEST-002. tests/unit/pre_commit_hook.bats updated to stage fixtures under test/<name>_test.exs so they match IN-EX-TEST-001's applies_to.

Migration notes

  • intent upgrade from any v2.10.x project runs migrate_v2_10_x_to_v2_11_0 automatically. 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

28 Apr 11:06

Choose a tag to compare

[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-run previews every step with no side effects.
  • intent doctor check 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 runs git rm -rf .intent/ themselves so the cleanup is visible in the commit.

Fixed

  • /in-session UserPromptSubmit 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 in intent/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-run UX -- distinguish three states for the config.json pre-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 at intent upgrade for the relocation step.
  • IN-RS-CODE-005 (lifetime-elision-first) carve-out -- explicit "Does Not Apply" entry for teaching examples in intent/plugins/claude/rules/** and tests/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 under tests/fixtures/critics/. Critic self-test fixtures are not real test code; the handoff was firing inconsistently across critics post-WP07.

Intent v2.10.0

27 Apr 22:36

Choose a tag to compare

[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 via bin/intent_critic, .intent_critic.yml per-project config, the working-with-llms.md canon 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 nested intent/.config/, eliminating the "two top-level dirs" smell. Migration handled atomically by migrate_v2_9_0_to_v2_10_0 on intent 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 nested intent/.config/, eliminating the "two top-level dirs" smell. Migration handled atomically by migrate_v2_9_0_to_v2_10_0 on intent upgrade.
  • intent lang command (ST0035/WP-19) for per-language canon installation. Subcommands: list, show, init. intent lang init <lang> [<lang> ...] is idempotent and multi-language; copies intent/plugins/agents/templates/<lang>/{RULES,ARCHITECTURE}.md into intent/llm/{RULES,ARCHITECTURE}-<lang>.md and appends a marker-bracketed entry to the agnostic intent/llm/RULES.md Language Packs section. Replaces the rejected auto-language-detection approach (real projects are polyglot; explicit user choice via --lang is more honest). Available canon languages: elixir, rust, swift, lua, shell. New stub templates ship for the four newer languages.
  • intent init --lang <list> flag invokes intent lang init for each named language post-init. Comma- or space-separated list. Equals form (--lang=elixir) also accepted.
  • Agnostic _default canon now includes RULES.md + ARCHITECTURE.md in fresh intent init. Previously only MODULES.md + DECISION_TREE.md were laid down; canon-installer's _default templates were only seen via intent claude upgrade --apply. Now intent init produces a v2.10.0-complete baseline including the Language Packs anchor that intent lang init writes into.

Changed

  • bin/intent_helpers: migrate_v2_9_0_to_v2_10_0() replaces the earlier migrate_v2_9_0_to_v2_9_1() stub. Bundles version stamp + ST0036 directory relocation. Canon-apply logic still lands in ST0035/WP11 via intent claude upgrade --apply (separate step).
  • bin/intent_upgrade: chain extended to v2.10.0 (new gate needs_v2_10_0_upgrade, new case, new chain tail).
  • Root VERSION bumped to 2.10.0.
  • Treeindex ignore canonicalised (ST0036/WP-06): new lib/templates/_treeindexignore template is the single source of truth. bin/intent_treeindex::ensure_treeindexignore reads from the template instead of an inline heredoc (Highlander cleanup per CLAUDE.md project rule #6). intent claude upgrade --apply installs the file when absent (new INSTALL_TREEINDEXIGNORE action; existing files left alone). Granularity flipped from blanket .intent/ to intent/.config/cache/ + intent/.config/backup/ so config.json stays indexed.
  • Pre-commit hook template (ST0036/WP-04): lib/templates/hooks/pre-commit.sh now probes intent/.config/config.json instead of .intent/config.json when deciding whether to skip the critic gate (fail-open in non-Intent repos). Newly-installed gates and any project that re-runs intent claude upgrade --apply after v2.10.0 pick up the corrected probe.

Breaking

  • Per-project metadata directory relocated: .intent/config.jsonintent/.config/config.json. Same for .intent/backup/intent/.config/backup/. Anything scripting against .intent/ (CI, editor plugins, ad-hoc jq) breaks on upgrade; update to intent/.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/*.md retired (ST0035/WP-18). The three hand-authored user docs (user_guide.md, reference_guide.md, deployment_guide.md) were stamped at intent_version: 2.6.0 (2026-03-05), seven minor versions behind the v2.10.0 canon, and substantially duplicated by README.md, intent/docs/working-with-llms.md, intent help <cmd>, and AGENTS.md. Per fail-forward (no preservation, prune actively): all three deleted; the intent/usr/ directory is gone. Cross-references updated: README.md Documentation and Getting Help sections now point at intent/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.md Intent 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

23 Apr 16:34

Choose a tag to compare

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.md as atomic, cite-able files with stable IN-* 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 is worker-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-agent

Authoring 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

  • elixir subagent deleted. Use critic-elixir instead. The migration prunes installed copies on upgrade.
  • worker-bee moved 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 --apply

The migrate_v2_8_2_to_v2_9_0 step (in bin/intent_helpers) does four things:

  1. Stamps .intent/config.json with intent_version: 2.9.0.
  2. Bootstraps ~/.intent/ext/ with a README on first run.
  3. Seeds ~/.intent/ext/worker-bee/ from lib/templates/ext-seeds/worker-bee/ (skipped if already present — never overwrites user state).
  4. Prunes installed copies of the deleted elixir subagent and the relocated worker-bee from ~/.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-* entries

Restart 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 across intent_st, intent_audit, intent_doctor, intent_plugin, and tests/run_tests.sh. Bugs included unquoted command substitutions in path construction, for x in $(find ...) patterns that swallowed find's exit code under set -e, an unsafe find | xargs pipeline missing -print0, and a get_terminal_width() shadow that silently overrode the canonical helper.
  • 60dfcd6 — Patches the dogfood journal commit reference.
  • 0de89cd — P2 sweep (Highlander pass). warning() and info() lifted into bin/intent_helpers as canonical helpers (the latter QUIET-aware); pure-shadow duplicates stripped from four files. P2a (set -e adoption) shipped only for tests/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, recognises 2.9.x, 2.10.x, and 3.x as already-migrated).
  • The migration chain in bin/intent_upgrade covers 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.min in the seed manifest is 2.8.2 — projects on the new minimum or higher load the extension successfully.

v2.8.2 -- cwd-resilient dispatch + Credo wiring

15 Apr 13:27

Choose a tag to compare

Fixed

  • ST0033: cwd-resilient dispatch. intent subcommands now work from any directory inside an Intent project, not only from the project root. The dispatcher (bin/intent) exports INTENT_ORIG_CWD and cds to $PROJECT_ROOT before exec'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/ or intent/ directories at the invoker's cwd. intent treeindex and intent fileindex consult INTENT_ORIG_CWD when 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 through migrate_v2_6_0_to_v2_8_0 (new, pure version stamp), migrate_v2_8_0_to_v2_8_1, and migrate_v2_8_1_to_v2_8_2. The pre-v2 fallback chain is extended to match. needs_v2_8_2_upgrade accepts 2.6.0 and 2.7.0 as starting points.
  • ST0032: Credo custom checks wired into .credo.exs. intent st zero (D5a) and intent audit now use a standalone lib/scripts/configure_credo.exs to programmatically patch .credo.exs, replacing the earlier wrong hint about elixirc_paths in mix.exs and the intent audit --checks-dir workaround. 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 added bracket_access_on_struct. Existing projects that went through st zero can re-run D5a to pick up the wiring.