github agent-sh/agnix v0.44.0

4 hours ago

Added

  • XP-009: Codex Instruction Chain Exceeds project_doc_max_bytes. Codex's 32 KiB project_doc_max_bytes cap is cumulative across the instruction chain, not per-file - it "stops adding files once the combined size reaches the limit". XP-007 checks each AGENTS.md in isolation, so a project split across several mid-size files was truncated with no diagnostic at all. The new project-level rule builds the chain the way Codex discovers it (root down, at most one file per directory, AGENTS.override.md before AGENTS.md), sums root-first, and reports on the file where the running total crosses the limit - that file and everything deeper is what Codex drops. Rule count 442 -> 443 (closes #1289).
  • args on command hooks. The exec-form field - "When present, command is resolved as an executable and spawned directly with args as the argument vector, with no shell involved" - was absent from the schema, so it was silently ignored and two rules could not tell exec form from shell form.

Removed

  • CC-SK-003 (Context Without Agent). The rule errored when context: fork had no agent field and shipped an unsafe autofix inserting agent: general-purpose. The skills reference states "The agent field specifies which subagent configuration to use... If omitted, uses general-purpose", so the rule demanded that users restate a default the tool already applies. Rule count 443 -> 442.
  • CC-HK-019 (Deprecated Setup Event). The rule warned that the Setup hook event was deprecated in favor of SessionStart and shipped an unsafe autofix that rewrote the event key. Setup is not deprecated: it is documented as a current event that fires on --init-only and on --init/--maintenance in -p mode, with its own matcher set (init, maintenance), exit-code row, and decision-control entry. SessionStart fires on every session begin/resume, so applying the "fix" silently changed when a hook ran. The rule cited no upstream deprecation notice when it was added. Rule count 444 -> 443. Follows the origin-less-rule removal precedent from PR #979.

Fixed

  • COP-005 rejected the documented excludeAgent value and autofixed it wrong. The valid values are code-review and cloud-agent; agnix listed coding-agent, which was never upstream - so the doc's own value errored, and the unsafe autofix rewrote a correct value into the invalid one. coding-agent is retained as a deprecated alias so configs written against agnix's previous wrong advice keep validating, reported at info level rather than accepted silently, and kept out of the autofix candidate list so the fixer can never write it. Fixed across the validator, all 12 locale files, both rules.json copies, and the knowledge-base docs.
  • COP-003 rejected comma-separated applyTo. "You can specify multiple patterns by separating them with commas", with applyTo: "**/*.ts,**/*.tsx" as the doc's own example. The whole string went to glob::Pattern, so every such config errored. A correct brace/bracket-aware splitter already existed with 18 passing tests but was marked #[allow(dead_code)] // reserved for future use and never called.
  • CUR-004 rejected comma-separated globs. Same class as COP-003, same upstream wording ("Separate multiple patterns with commas"). This one was intermittently wrong, which is why it survived: the doc's literal example docs/**/*.md, docs/**/*.mdx happens to parse as one pattern, while src/**, tests/** does not.
  • CUR-010 required version in .cursor/hooks.json. It is documented with a default of 1 and several of the doc's own examples omit it. Now optional, and type-checked as a number (not an integer) only when present.
  • CUR-012 required command on prompt hooks. "Prompt hooks use an LLM to evaluate a natural language condition" and carry prompt instead - the doc's example has only type, prompt, timeout. CUR-018 already checked prompt for those.
  • CUR-016 required install in .cursor/environment.json. The published schema has no required array at all (definitions.common) and required: [] (definitions.container), and the setup page's own snapshot example omits install. Terminal entries now require only command (name/description are optional per both oneOf branches, and the array branch is accepted), and update - absent from the schema, which sets unevaluatedProperties: false - is reported as renamed to install rather than accepted as valid.
  • Cursor hook timeout suggestions said milliseconds. The option is "Execution timeout in seconds".
  • CC-MEM-014 fired on Cursor .mdc files. ClaudeMdValidator is registered for FileType::CursorRule for its generic prose checks, so a .cursor/rules/*.mdc file was reported as CLAUDE.md has N non-empty lines, exceeding the recommended 200 line limit - wrong filename, wrong threshold (Cursor documents 500), wrong tool attribution. Now scoped to Claude memory files.
  • Stale evidence URLs for 18 rules. docs.github.com/en/copilot/customizing-copilot 301-redirects and docs.cline.bot/features/cline-rules/overview 308-redirects; both are updated in rules.json and .github/spec-baselines.json, so those two sentinel sources can match again.
  • AGENTS.override.md was not recognized as an instruction file. Codex checks it before AGENTS.md in each directory, but is_instruction_file() did not list it, so it was invisible to every cross-platform rule. This also made the AGENTS.override.md handling added to XP-007 in the previous release unreachable in practice.
  • CC-SK-009 miscounted dynamic injections in both directions. It counted raw !` occurrences, so inert KEY=!`cmd` forms inflated the count (the doc: a ! following another character "is left as literal text and the command does not run"), while ```! fenced blocks - where every line is a command - counted as zero. Counting now honors both documented forms and ignores plain fences.
  • CC-SK-012/CC-SK-016 recognized only $ARGUMENTS. The substitution table documents four forms; a body using the $N shorthand or a $name declared in arguments was reported as ignoring its own arguments, and $0 escaped CC-SK-016 entirely. The arguments frontmatter field is now parsed so named placeholders can be resolved. The shorthand is matched as a single digit, matching the documented examples, so ${CLAUDE_SKILL_DIR}, x$3y, and prose amounts like $500 are not mistaken for positional arguments.
  • CC-HK-028 flagged the exec form the doc recommends. ${user_config.*} is rejected only in shell form: "Plugin hooks additionally substitute ${user_config.*} values, in exec form only". The rule fired regardless of args, so {"command": "${user_config.formatter}", "args": ["--fix"]} errored.
  • CC-HK-008 skipped scripts named in args. In exec form the script sits in args while command names an interpreter, so exec-form hooks - the form the doc recommends for path placeholders - got no script-existence checking at all.
  • AS-013 only inspected references/ paths. The spec scopes skill resources to "scripts/, references/, or assets/" and uses scripts/extract.py in its own example, so deep scripts/ and assets/ paths were invisible. Git ref paths stay excluded.
  • AS-017 rejected Claude Code display labels. Claude Code decouples name from the directory ("name sets only the display label... the command still comes from the directory or file name"), and a plugin skill's name deliberately replaces the last command segment - the reference's own name: fancy in skills/review/ example errored. Scoped out for Claude Code skills, following the AS-008 precedent; the agentskills.io baseline still requires the match.
  • CC-PL-002 checked four of eight directories. The doc names commands/, agents/, skills/, workflows/, output-styles/, themes/, monitors/, and hooks/ as forbidden inside .claude-plugin/.
  • CC-PL-007/CC-PL-008 checked four of ten path fields. workflows, mcpServers, outputStyles, lspServers, experimental.themes, and experimental.monitors accepted absolute paths, .. traversal, and .claude-plugin/ targets unchecked. Dotted experimental.* keys are now traversed, which a plain field lookup could not do.
  • XP-007 ignored AGENTS.override.md. Codex reads it first in each directory, so it draws on the same project_doc_max_bytes budget; the code comment claiming Codex "reads this file, not local/override variants" was contradicted by the current guide. The cap is cumulative across the root-to-cwd chain while this check remains per-file - noted in the rule docs as a known gap.
  • AGM-006's suggestion contradicted upstream. It advised consolidating files, while Codex documents root-down concatenation with nearest-file-wins precedence and recommends splitting across nested directories to stay under the byte cap.
  • MCP-008 flagged the current protocol revision. 2026-07-28 is now the current MCP revision and the unversioned /specification URL serves it, but agnix compared against a single pinned default of 2025-11-25. With no revision pinned, both are now accepted: the two are behaviorally incompatible rather than sequential (2026-07-28 drops the initialize/initialized handshake and Mcp-Session-Id sessions), so a config written for either is legitimately current and demanding one would only move the false positive onto the other. Pinning [spec_revisions] mcp_protocol still enforces exactly one revision and keeps the autofix; the unpinned diagnostic now names every accepted revision instead of claiming a single one is required. A revision outside the current set is still reported.
  • CC-SK-001's message listed a stale alias set. It advertised only sonnet, opus, haiku, inherit, or claude-* after the accepted list had been widened.
  • codex-cli-agents-md source URL was dead. https://developers.openai.com/codex/guides/agents-md/ 308-redirects to https://learn.chatgpt.com/docs/agent-configuration/agents-md - the doc moved host and site, so the Spec Drift Sentinel baseline for that source could never match again. Updated in spec-baselines.json, in all 20 affected evidence.source_urls entries, and across the knowledge-base docs.
  • Parenthesized tool arguments were shattered by tokenization. tools: Agent(worker, researcher), Read, Bash(npm run test:*) - the sub-agents reference's own verbatim example - reported Unknown tool 'researcher)', 'run', and 'test:*)' under CC-AG-009/010, because the string deserializer split on commas and spaces before parentheses were stripped. The skill validator already had a paren-aware tokenizer; it is now shared as validation::split_tool_list so allowed-tools, tools, and disallowedTools cannot drift apart again.
  • CC-AG-004 rejected auto and manual, and accepted the undocumented delegate. The permission-modes reference documents six modes (default, acceptEdits, plan, auto, dontAsk, bypassPermissions) plus manual as a CLI alias for default (v2.1.200+). delegate appears in neither that reference nor sub-agents and was added without a cited source, so it is no longer accepted.
  • CC-AG-003/CC-SK-001 rejected documented model values. model "Accepts the same values as the --model flag", so default, best, fable, opusplan, sonnet[1m], and opus[1m] are all valid and were all erroring. The alias list is now shared between the agent and skill validators rather than duplicated.
  • MCP-020 warned on two valid capability keys. tasks is in the 2025-11-25 capability-negotiation table - the revision agnix already pins - and extensions was added in 2026-07-28. Note the spec calls capabilities an open set ("any server can define its own, additional capabilities"), so this rule is advisory.
  • CC-MEM-001 rejected absolute and home-directory imports. The memory reference states "Both relative and absolute paths are allowed" and prescribes @~/.claude/my-project-instructions.md as the way to share personal instructions across git worktrees; Claude Code gates external imports behind a one-time approval dialog rather than rejecting them. Existence is still checked, and the path-shape guard is retained for non-memory files under REF-001, where no spec sanctions escaping the project root.
  • CC-MEM-003 allowed two more hops than documented. The doc says "a maximum depth of four hops"; the limit was 5 and the check is depth + 1 > MAX, so the diagnostic first fired at six.
  • .claude/rules/ was only validated one level deep. The doc says "All .md files are discovered recursively, so you can organize rules into subdirectories like frontend/ or backend/", but both the file-type detector and the validator required rules/ to be the immediate parent. Nested rule files got no validation at all - CC-MEM-011/012 never ran on them.
  • CC-HK-025 rejected the documented fork SessionStart matcher. fork covers --fork-session with --resume/--continue, the /fork background copy, and /branch. It was missing from the known-value list, so a valid hook config was flagged. (resume stays valid: before Claude Code v2.1.214 a forked session reported resume.)
  • CC-PL-015 flagged additive and merge-semantics plugin fields as shadowing. The rule warned that a default component folder was ignored whenever the matching manifest key was present, but the plugins reference splits those keys three ways: skills adds to the default (the default skills/ directory is always scanned), while hooks/mcpServers/lspServers have their own merge rules. Neither can shadow, so both skills and hooks produced false warnings on plugins that kept their default folder. The rule now checks only the six documented replace-semantics fields, which also closes a coverage gap: workflows, outputStyles, experimental.themes, and experimental.monitors were never checked. Folder names that differ from their manifest key are handled (outputStyles -> output-styles/, experimental.* -> root-level themes/, monitors/).
  • Spec Drift Sentinel pointed maintainers at 15 rule IDs that do not exist. .github/spec-baselines.json mapped claude-code-subagents to CC-SA-001..007, github-copilot to GH-001..004, and cline-rules to CLINE-001..003 - none of which were ever in this repo; the real rules are CC-AG-*, COP-*/CP-SK-*, and CLN-*/CL-SK-*. It also still listed AS-014, removed in PR #979. Every drift issue rendered these straight into the "rules may need review" list, sending the reader to rules they could not look up. Mappings are now derived from each rule's own evidence URLs, and a new test_spec_baseline_rule_ids_exist parity test fails CI if an unknown ID is ever added.

Don't miss a new agnix release

NewReleases is sending notifications on new releases.