Phase 4 - Ruleset alignment and agent rules
IMPL 0004: Phase 4 - Ruleset alignment and agent rules
Section titled “IMPL 0004: Phase 4 - Ruleset alignment and agent rules”Status: Completed Author: Donald Gifford Date: 2026-07-09
- Objective
- Scope
- Implementation Phases
- File Changes
- Testing Plan
- Dependencies
- Resolved Decisions
- References
Objective
Section titled “Objective”Close the drift found in
INV-0006:
fix the four rules that produce wrong results against doc-valid input, update
the seven that have stale premises, extend the parsers that block those fixes,
and ship the agent rule package (including model validation and the opt-in
model-policy enforcement rule). At completion the ruleset validates every
artifact kind against the Claude Code docs as of 2026-07-09 (version markers
through v2.1.205).
Implements: INV-0006 Recommendation (PR 1 → Phases 1–2, PR 2 → Phases 3–4, PR 3 → Phase 5).
In Scope
Section titled “In Scope”- Parser changes:
.mcp.jsonmcpServerskey + transport fields, marketplace object sources, hooktype+ per-type fields, comma/space splitting for tool lists, agent frontmatter field extension. KnownToolsandKnownHookEventsrefresh.- Fixes/updates to the 11 existing rules marked Fix/Update in INV-0006.
- New rules:
mcp/url-requiredplus the agent package (agents/model-valid,agents/name-format,agents/tools-known,agents/plugin-ignored-fields,agents/field-enums) and the Phase 5 batch (agents/model-policy,hooks/type-known,hooks/type-fields,mcp/transport-known,mcp/no-secrets-in-headers,mcp/timeout-minimum,marketplace/reserved-name,marketplace/name-format,marketplace/source-path-safety,marketplace/renames-valid,skills/description-length,skills/fork-agent-pairing,claude_md/import-exists). - DESIGN-0005 covering the agent package and the opt-in rule mechanism.
- Ruleset version + fingerprint bumps per phase;
docs/rules/rules.mdand README anchors updated alongside each rule change. - One dogfood pass against
donaldgifford/claude-skillsper shipped PR.
Out of Scope
Section titled “Out of Scope”- Experimental surfaces: monitors, themes, LSP servers,
userConfigschema validation (docs mark these unstable; INV-0006 “deliberately not chasing yet”). - Path-scoped rule files (
.claude/rules/*.md) as a ninth artifact kind. convertsubcommand (original Phase 3 plan, still gated on INV-0001).- Any engine-level scheduling/discovery changes beyond what the opt-in mechanism decision (OQ4) requires.
Implementation Phases
Section titled “Implementation Phases”Phases 1+2 ship together as one PR/release; Phase 4 and Phase 5 are one
PR/release each. Phase 3 is a docs-only PR. Every code phase follows the
house rule-change checklist: rule file + table-driven tests + Register() +
rules.DefaultHelpURI anchor in README + docs/rules/rules.md entry +
fingerprint guardrail acknowledgment + coverage-gate floor (55% per
internal/ package).
Phase 1 — Parser foundations
Section titled “Phase 1 — Parser foundations”Extend the artifact layer so the rule layer can see what the docs define. No rule behavior changes in this phase beyond what parsing unlocks; all new fields carry ranges per the range-emission conventions (pre-parsed field ranges preferred).
-
ParseMCPFile: acceptmcpServersas the primary top-level key; keepserversaccepted but tag the artifact so a deprecation diagnostic can fire (per OQ1 decision). (MCPServer.LegacyServersKeyset for servers{}-keyed files; mcpServers wins when both present) -
MCPServertype: parsetype(defaultstdio),url,headers(string map),headersHelper,timeout(number, ms),alwaysLoad; add ranges fortypeandurl.oauth{}parsed as present/absent only (no field validation this phase). (Transportholds the raw declared value;EffectiveTransport()applies the stdio default so rules can distinguish declared-vs-defaulted) -
ParseMarketplace: parsesourceas string or object; model the four documented object shapes (github{repo,ref,sha},url{url,ref,sha},git-subdir{url,path,ref,sha},npm{package,version,registry}) into a typedMarketplaceSourcewith aKinddiscriminator; keepResolvedsemantics for local string paths. (MarketplacePlugin.SourceInfo; string forms classify aslocal/external-string, unknown object discriminators asinvalid;SourceRangecovers the object span for object forms) -
ParseMarketplace: parse rootowner{name,email}as a distinct field (today folded intoAuthor); parserenames{}map (for Phase 5). (OwnerName/OwnerEmail/OwnerRange+Renamesmap — JSON null targets parse as "" meaning “removed”; legacy mergedAuthorview preserved for existing rules) -
ParseHook: parsetype(defaultcommandwhen absent),url,server,tool,prompt,args(presence = exec-form),async, andshellper hook entry; keepcommand/timeout/matcherextraction as-is. (HookEntry.Typeholds the raw declared value;EffectiveType()applies the command default;ExecFormflags args[] presence so no-unsafe-shell can skip direct-spawn entries) - Tool-list splitting: new shared helper that accepts YAML list, or
comma/whitespace-separated string, and returns entries — used by
command/skill
allowed-tools,disallowed-tools, and agenttools/disallowedTools. Entries preserve permission-rule syntax (Bash(git add:*)) andmcp__*patterns as single tokens. (SplitToolListsplits on commas/whitespace only outside parentheses; YAML list elements pass through verbatim. Skill and commandallowed-tools+ agenttoolsnow use it; thedisallowed-tools/disallowedToolscall sites land with the new-field parsing task below) -
KnownTools: addAgent,Skill; keepTask(documented alias); add a helper that classifiesmcp__<server>,mcp__<server>__*,Agent(...), andTool(args)permission-rule forms as structurally-valid rather than unknown. (IsToolPattern— accepts MCP patterns and permission-rule forms whose base is a known tool or MCP pattern with a non-empty specifier; bare names stayIsKnownTool’s job) -
KnownHookEvents: expand to the full documented event set (~29 events; exact list + exact casing from the hooks reference, recorded in a table in the rules doc). (30 events as of 2026-07 — the reference grew by one since the INV-0006 audit; lifecycle-grouped table added todocs/rules/rules.mdunderhooks/event-name-known, count pinned by test) -
ParseSkill/ParseCommand: parsewhen_to_use,model(command),context,agent,disable-model-invocation,user-invocable,disallowed-tools— fields needed by Phases 2 and 5. (Skill and Command now share the merged frontmatter model;UserInvocableis a*boolso rules can tell declared-false from absent-defaults-true;disallowed-toolsgoes through the shared splitter) - Fixture sweep: add doc-valid testdata files exercising every new
shape (an
mcpServersfile withhttp+stdioservers, a marketplace with all four object source types, a hooks file using new events and all five hook types, skills/commands with string-formallowed-tools). (mcp/marketplace fixtures landed with their parser tasks;all_types.jsongainedSetup+PermissionRequestgroups; newok/skills/deployer.mdandok/commands/commit.mdcover string-form tool lists + merged model fields, wired intoTestFixturesOK)
Success Criteria
Section titled “Success Criteria”- All new fields parse with correct values and ranges in unit tests.
- The doc-valid fixtures parse with zero
schema/parseerrors. - Existing fixtures still parse identically (no behavior change for old
shapes except the
servers-key tagging). just checkgreen; coverage floor holds forinternal/artifact.
Phase 2 — Correctness rule fixes
Section titled “Phase 2 — Correctness rule fixes”Fix the four wrong-today rules, adjust the Update-verdict rules, and add
mcp/url-required. Ships with Phase 1 as one PR (ruleset minor bump,
fingerprint change).
-
hooks/event-name-known: validate against the expanded event list; diagnostic suggests the nearest known event on likely typos (case-insensitive match). (expanded list picked up automatically viaIsKnownHookEvent; newartifact.SuggestHookEventpowers the did-you-mean message; rules.md notes the suggestion behavior) -
commands/allowed-tools-known: consume the shared splitter; accept permission-rule andmcp__*forms; extendAppliesToto run on skillallowed-tools/disallowed-toolstoo (per OQ7 decision). (splitter consumed at parse time; rule acceptsIsToolPatternforms and checks both tool-list keys on command + skill. TheAppliesTochange flipped the fingerprint, so the ruleset minor bump landed here:v1.3.0, fingerprint tracked inexpected_fingerprint.txtper subsequent drift) -
mcp/command-required: fire only whentypeisstdio(or absent). (gates onEffectiveTransport() == "stdio"; message now names the transport requirement) - New
mcp/url-required(schema/error):http/sse/wstransports must declare a non-emptyurl. (range prefersTransportRange; unknown transports deliberately out of scope pendingmcp/transport-knownin Phase 5; rules.md + README rows added, shared prose section withmcp/command-required) -
mcp/command-exists-on-path: skip non-stdio transports. (no fingerprint impact — behavior-only change inside Check) -
marketplace/plugin-source-valid: object sources validate per-type required fields (repo/url/url+path/package); string sources keep the non-empty check;shawhen present must be a 40-char hex string. (one diagnostic per missing requirement, anchored toSourceRange; required-fields table added to rules.md) -
marketplace/external-source-skipped: rework — object sources are now structured; the info diagnostic applies only to sources whose content genuinely can’t be checked locally. (fires on the four remote object kinds + remote string shorthands with a kind-aware locator in the message; absent/invalid sources left to plugin-source-valid to avoid double-reporting) -
marketplace/version-semver: split — missing rootversion→ info; present-but-not-semver → error. (implemented as two rules — the engine assigns one severity per rule (finalizeDiagnosticoverrides per-diagnostic values), so the info half is the newmarketplace/version-missing(style/info) andversion-semverkeeps error for declared-but-malformed versions) -
marketplace/author-required: align with documented requiredowner{name}(per OQ5 decision). (became two rules for the same one-severity-per-rule reason as the version split:marketplace/owner-required(schema/warning, satisfied by owner or legacy author) +marketplace/author-legacy(style/info rename hint when only the legacy string is present)) - Deprecation diagnostic for the legacy
serverskey in.mcp.json(synthesized likeschema/parseor a dedicated rule — pick during build) (per OQ1 decision). (dedicated rulemcp/legacy-servers-key(schema/info) — simpler than engine synthesis sinceLegacyServersKeyis already parsed; identical per-server diagnostics collapse to one per file via the engine’s exact-duplicate dedupe) -
hooks/timeout-present: reword message + rules.md entry around the documented 600 s default (severity per OQ8 decision). (stays warning; message cites the per-type documented default — 600 s command/http/mcp_tool, 30 s prompt, 60 s agent — and frames the nudge as fail-faster-in-CI, not hang prevention) -
hooks/no-unsafe-shell: skip exec-form entries (argspresent) and non-commandhook types. (gates onEffectiveType() == "command" && !ExecForm; rules.md entry corrected — it described eval/unquoted- var smells the rule never actually checked) -
schema/frontmatter-required: reword skill diagnostics to best-practice phrasing without changing behavior (per OQ2 decision). (skill messages name the runtime fallback — directory-name forname, invocation-matching fordescription; command/agent keep the plain form; stricter-than-spec blockquote added to rules.md) -
skills/no-version-field+plugin/manifest-fields: help-text updates citing the current docs (stricter-than-spec note for pluginversion, per OQ3 decision). (no-version-field cites the accepted-but-ignored doc confirmation; manifest-fields’ version message names the git-SHA fallback; rules.md gains the stricter-by-design blockquote and drops its wrong claim thatdescriptionwas checked) - Ruleset version bump + fingerprint ack;
docs/rules/rules.md+ README rule anchors updated for every touched rule. (v1.3.0 landed with the first fingerprint-flipping change; expected_fingerprint.txt re-acked per drift. Catalog is 34 rules; verified every registered ID appears in both the rules.md and README tables; README prose sweep mirrored all reworded entries and both headers now say v1.3) - Update DESIGN-0002 §2.2 (
serversvsmcpServers) to record the resolution; cross-link INV-0006. (resolution blockquote in the MCP artifact section, linking INV-0006 and noting the v1.3.0 transport-field additions; CLAUDE.md’s locked-decision bullet updated from “servers{} — revisit if docs standardize” to the adopted mcpServers{} + legacy-tag state) - Dogfood: run against a
donaldgifford/claude-skillscheckout and the claudelint repo itself (just self-check); triage every new/changed diagnostic. (both clean on 2026-07-10: self-check 0 diagnostics / 3 files; claude-skills@b2d72f1 0 diagnostics / 149 files — nothing to triage.claudelint versionreports ruleset v1.3.0 (6900b22e), catalog 34 rules)
Success Criteria
Section titled “Success Criteria”- The Phase 1 doc-valid fixtures lint with zero false positives
(previously:
mcpServersfiles invisible, object sources erroring, comma-formallowed-toolserroring, new hook events erroring). - Regression fixtures prove old invalid inputs still flag (unknown tool, unknown event, missing stdio command, empty source).
claudelint rules --jsonreflects the new catalog; fingerprint guardrail test updated deliberately.- Dogfood passes clean or with only agreed-expected diagnostics.
just cigreen; release ships as a minor via the label-driven flow.
Phase 3 — DESIGN-0005 for the agent package and opt-in mechanism
Section titled “Phase 3 — DESIGN-0005 for the agent package and opt-in mechanism”Docs-only gate before the agent code. Small by design — INV-0006 already contains the research; the DESIGN settles the contested mechanics.
-
docz create design→ DESIGN-0005 “Agent rules and opt-in rule mechanism”. - Specify the extended
Agentartifact model (which of the 16 documented fields are parsed, their types/ranges; which are noted but unparsed). (all 16 modeled: 12 new typed fields,mcpServers/hooksas presence bools, ranges via the existingFrontmatter.KeyRange; field/enum set re-verified against the sub-agents reference 2026-07-10) - Specify each Phase 4 rule: id, category, severity, options,
diagnostics, range targets, and the shared model-value validator
reused by skill/command
model(values:sonnet/opus/haiku/fable/inherit/full-ID shape). (DESIGN-0005 §4 table + §3IsValidModelRef) - Resolve the opt-in mechanism (OQ4) and write the chosen pattern up
as the house convention, superseding the CLAUDE.md
mcp/server-allowlistnote. (DESIGN-0005 §5 —rules.OptIninterface +HasRuleBlockgate; CLAUDE.md bullet replaced) - Define how plugin-distributed agents are detected (path heuristic:
agent file under a root containing
.claude-plugin/plugin.json) foragents/plugin-ignored-fields. (DESIGN-0005 §2 — discovery setsAgent.PluginDistributed,IndexSkillCompanionsprecedent) -
docz update; PR withdont-releaselabel. (indexes + mkdocs nav regenerated. No separate PR: per the work-loop instruction all IMPL-0004 phases proceed sequentially onfeat/impl-0004-ruleset-alignment, so DESIGN-0005 rides this branch’s PR instead of a standalone dont-release one)
Success Criteria
Section titled “Success Criteria”- DESIGN-0005 status Approved (Donald sign-off on the OQ4 resolution in particular).
- CLAUDE.md opt-in-rules note updated to point at the new convention.
Phase 4 — Agent rule package
Section titled “Phase 4 — Agent rule package”First agent-specific rules. One PR, minor release, fingerprint bump.
- Extend
ParseAgentper DESIGN-0005:model,disallowedTools,permissionMode,maxTurns,skills,mcpServers(presence),hooks(presence),memory,background,effort,isolation,color— with key ranges. (plusinitialPromptfor the full 16-field set; newhas/asInt64markdownDoc helpers; ranges via the existingFrontmatter.KeyRange;PluginDistributedfield added for discovery to set) - New package
internal/rules/agents/with blank-import registration ininternal/rules/all/. -
agents/model-valid(schema/warning): shared validator; also registered for skill + commandmodelfields. (artifact.IsValidModelRef+KnownModelAliases; agent enum sets added alongside for field-enums; message names the full valid-value list per the Phase 4 success criterion) -
agents/name-format(schema/warning): lowercase letters and hyphens only, per the documented constraint. (^[a-z]+(-[a-z]+)*$— also rejects leading/trailing/double hyphens; empty names left toschema/frontmatter-required) -
agents/tools-known(schema/warning): shared splitter + classifier overtoolsanddisallowedTools; diagnostic explains Claude Code silently ignores unknown names. (splitting happens at parse time; the rule reusesIsKnownTool+IsToolPatternlikecommands/allowed-tools-known) -
agents/plugin-ignored-fields(content/warning): plugin-rooted agents declaringmcpServers/hooks/permissionMode(documented as ignored for plugin subagents). (detection viaartifact.MarkPluginDistributed— bounded ancestor walk for.claude-plugin/plugin.json, called from the CLI’s parse wiring with the scan root so the walk can’t escape the repo; one diagnostic per declared key at itsKeyRange) -
agents/field-enums(schema/warning):permissionMode,effort,color,isolation,memoryenum membership. (uses theAgent*enum sets from knowndata.go; isolation is a one-entry set (worktree); messages hardcode the documented value order since map iteration is unstable) - Opt-in mechanism (DESIGN-0005 §5, scheduled here by its rollout
plan):
rules.OptIninterface +rules.IsOptIn; engine skips opt-in rules without arule "<id>"block (Config.HasRuleBlock); fingerprint gainsoptin=component;claudelint rulesshows(opt-in)andrules --jsongainsopt_in;mcp/server-allowlistmigrated (no block → skipped; block withoutallowlist→ loud config error preserved); rules-json-schema.md + rule prose updated. - Fixtures: valid agent exercising all documented fields; invalid
variants per rule; a plugin-rooted agent fixture.
(
ok/agents/full.mdpins all 16 fields viaTestFixturesOK;ok/pluginroot/mirrors a real plugin layout forMarkPluginDistributed; per-rule invalid variants live inline in the table-driven rule tests per house convention, andTestFixtureSweepruns all five rules over the valid fixture (silent) + a kitchen-sink invalid agent (each fires, non-zero ranges)) - Ruleset version bump + fingerprint ack; rules.md + README anchors;
claudelint rules <id>detail entries. (v1.3.0 → v1.4.0 per OQ6; fingerprintb9936acawas acked incrementally with each rule/mechanism commit — version bumps don’t move it. All 39 registered IDs verified present in both doc tables via script; new-rule prose lives in rules.md per this phase’s convention; detail entries come from the registry and were smoke-verified for all five agents rules + server-allowlist) - Coverage: new
internal/rules/agentspackage must clear the 55% floor (plan tests before code). (65.9%;just coverage-gateexits 0. Tests were written alongside each rule — table-driven per rule + the fixture sweep) - Dogfood pass (claude-skills ships plugin agents — prime corpus).
(claude-skills@6c326a3: 7 warnings / 149 files, all
agents/name-formaton colon-prefixed names (name: docz:doc-recommenderetc.) — triaged as TRUE positives: Claude Code auto-prefixes the plugin name, so those declarations render doubled (docz:docz:doc-recommender) while bare-name plugins (go-development, hugo, libtftest) render clean. Fix belongs in claude-skills. No false positives;just self-checkclean at 0/3)
Success Criteria
Section titled “Success Criteria”- All five rules fire on their invalid fixtures with correct ranges and pass their valid fixtures.
- A typo’d
model: sonetproduces a diagnostic naming the valid values. agents/plugin-ignored-fieldsstays silent on project-level (.claude/agents/) definitions declaring the same fields.- Dogfood run on claude-skills triaged; no untriaged false positives.
just cigreen; minor release ships.
Phase 5 — Policy and remaining validation rules
Section titled “Phase 5 — Policy and remaining validation rules”The governance rule plus the remaining validation batch from INV-0006’s proposed tables. One PR, minor release, fingerprint bump.
-
agents/model-policy(error, opt-in per the OQ4/DESIGN-0005 mechanism):require = "inherit"(absent key compliant) orallowlist = [...]option shapes. (first rule on the Phase 4 OptIn mechanism; exactly-one-option enforced with loud config errors incl. allowlist entries failingIsValidModelRef; absentmodelevaluates asinheritunder allowlist mode; diagnostics anchor at the model key, falling back to name key / body — never file-level (0,0)) -
hooks/type-known(schema/error):typein the five documented values; absent =command. (newartifact.KnownHookTypes+IsKnownHookType; anchors atTypeRange) -
hooks/type-fields(schema/error): per-type required fields (commandneedscommand;httpneedsurl;mcp_toolneedsserver+tool;prompt/agentneedprompt). (one diagnostic per missing field; unknown declared types left to type-known; anchor walks the entry’s per-key ranges since the parser leavesEventRangeunset) -
mcp/transport-known(schema/warning):typeinstdio/http/sse/ws;sseadditionally flagged as documented-deprecated (info). (split into two rules — the engine assigns one severity per rule, same precedent as version-semver/version-missing:transport-known(schema/warning, unknown values) +transport-deprecated(schema/info, sse advisory)) -
mcp/no-secrets-in-headers(security/error): reuse theno-secrets-in-envdetector overheadersvalues (or fold into that rule — pick during build, note in rules.md either way). (kept separate so the two surfaces suppress independently; both reusesecurity.MatchesSecret; decision noted in rules.md. Placeholders likeBearer ${API_KEY}pass) -
mcp/timeout-minimum(schema/warning):timeoutbelow 1000 flagged with a seconds-vs-milliseconds hint. (message suggests the ×1000 value; anchors atNameRangesince the parser stores no timeout range; absent (0) passes) -
marketplace/reserved-name(schema/error): the 16 documented reserved names, exact match; impersonation heuristics deliberately NOT attempted (enforced server-side by claude.ai). (list verified against the plugin-marketplaces reference as of v2.1.205 — includesfirst-party-plugins+healthcare; anchors atNameRange) -
marketplace/name-format(style/warning): kebab-case for marketplace name + plugin entry names. (^[a-z0-9]+(-[a-z0-9]+)*$; one diagnostic per offending name at its range; empty names left to marketplace/name + plugin-source-valid) -
marketplace/source-path-safety(security/error): relative sources start with./; no..segments. (parser gainedMarketplace.PluginRoot(metadata.pluginRoot) — bare sources are documented-valid under a pluginRoot, so only the..check applies there; anchors atSourceRange) -
marketplace/renames-valid(schema/error): chains terminate atnullor a listed plugin; no cycles. (one diagnostic per broken link — not per chain crossing it; cycles reported once, rotated to the smallest member for determinism; anchors atNameRangesince the parser stores no renames{} entry ranges) -
skills/description-length(content/warning):description+when_to_usecombined at most 1,536 chars. (max_charsoption, default 1536; anchors at the description key range) -
skills/fork-agent-pairing(schema/warning):agent:withoutcontext: fork. (anchors at the agent key range; fork without agent stays silent — a fork defaults its agent type) -
claude_md/import-exists(content/warning):@pathimports resolve relative to the file (respecting~); flag chains beyond 4 hops; skip code spans/fences per documented parser behavior. (regex with word-boundary guard so emails don’t match; trailing sentence punctuation trimmed; ranges viaartifact.ResolveOffsetRange; chain walk is cycle-safe and capped; filesystem access is deliberate, like command-exists-on-path) - Fixtures for every rule (valid + invalid pairs). (valid+invalid
pairs live inline in each package’s table-driven tests with
range assertions, per the Phase 4 convention. New doc-valid
testdata:
ok/marketplaces/renames/(renames{} + pluginRoot, asserted by a parser test) andok/claudemd/imports.md(resolving import + span/fence literals, swept byTestImportExistsFixture). Thetraditionalmarketplace fixture was renamed off “anthropic-plugins” — that name is now reserved, so it was no longer doc-valid) - Ruleset version bump + fingerprint ack; rules.md + README anchors.
(v1.4.0 → v1.5.0 per OQ6; fingerprint
3247787backed incrementally per rule commit. Catalog is 53 rules — above the ~49 estimate because the engine’s one-severity-per-rule constraint split transport-known/transport-deprecated, on top of the Phase 2 splits. All 53 IDs verified in both doc tables via script; all 13 Phase 5 rules have rules.md prose) - Dogfood pass; add “implemented by IMPL-0004” note to INV-0006 and
flip this doc to Completed. (claude-skills@6c326a3 at v1.5.0:
same 7 known-true-positive
agents/name-formatwarnings as the Phase 4 pass, zero findings from the 14 new Phase 5 rules across 149 files;just self-checkclean 0/3;just cigreen. INV-0006 carries the implemented-by note; status flipped)
Success Criteria
Section titled “Success Criteria”- Every new rule has valid/invalid fixture coverage and correct ranges
(no file-level
(0,0)ranges — per-line suppression markers must work). agents/model-policyunset behaves exactly per the DESIGN-0005 mechanism (no noise on unconfigured repos; enforcement when configured).- Full catalog:
claudelint rules --jsonlists the expanded ruleset (~49 rules) anddocs/rules-json-schema.mdstill validates. - Dogfood clean;
just cigreen; minor release ships. - IMPL-0004 → Completed; INV-0006 cross-referenced as implemented.
File Changes
Section titled “File Changes”| File | Action | Description |
|---|---|---|
internal/artifact/parse_mcp.go | Modify | mcpServers key, transport fields, deprecation tagging |
internal/artifact/parse_marketplace.go | Modify | Object sources, owner{}, renames{} |
internal/artifact/parse_json.go | Modify | Hook type + per-type fields, exec-form detection |
internal/artifact/parse_md_kinds.go | Modify | Agent field extension; skill/command new fields |
internal/artifact/parse_markdown.go | Modify | Shared comma/space tool-list splitter |
internal/artifact/knowndata.go | Modify | KnownTools refresh, KnownHookEvents expansion, model-value + tool-pattern classifiers |
internal/artifact/types.go | Modify | New fields + ranges on MCPServer, MarketplacePlugin, HookEntry, Agent, Skill, Command |
internal/rules/agents/ | Create | Six-rule agent package (Phases 4–5) |
internal/rules/{hooks,mcp,marketplace,skills,claudemd,commands}/ | Modify/Create | Rule fixes + new rules per phase tables |
internal/rules/all/all.go | Modify | Register the agents package |
internal/rules/ version/fingerprint source | Modify | Ruleset version + fingerprint per code phase |
docs/rules/rules.md, README.md | Modify | Catalog + anchors per phase |
docs/design/0005-*.md | Create | Phase 3 output |
docs/design/0002-*.md | Modify | §2.2 resolution note |
testdata/** | Create | Doc-valid + invalid fixtures per phase |
Testing Plan
Section titled “Testing Plan”- Unit (per rule/parser): table-driven tests; every new parser field asserted with value + range; every rule asserted on valid + invalid fixtures.
- Fixture integration: the doc-valid corpus from Phase 1 runs through
claudelint runwith zero unexpected diagnostics; retained as regression fixtures for later phases. - Fingerprint guardrail: deliberate acknowledgment in each code phase; CI fails on unacknowledged drift.
- Coverage gate:
just coverage-gate— 55% floor perinternal/package, including the newinternal/rules/agents. - Dogfood:
just self-checkplus adonaldgifford/claude-skillscheckout after Phases 2, 4, and 5 (cdinto the fixture repo — config discovery walks up from CWD). - Docs:
just lint-md+just docs-checkfor rules.md/README edits.
Dependencies
Section titled “Dependencies”- INV-0006 (merged) — findings and rule tables this implements.
- DESIGN-0005 (Phase 3 output) — gates Phases 4–5.
- DESIGN-0001/0002 — existing architecture constraints (rule shape, range-emission conventions, rules never import the engine).
- Current docs snapshot:
code.claude.com/docs/en/*fetched 2026-07-09; re-verify the event list and enum values at Phase 1 build time in case the docs have moved again.
Resolved Decisions
Section titled “Resolved Decisions”All Open Questions resolved 2026-07-10 — every one on option (a), the recommendation. Task references like “(per OQ1 decision)” point here.
- OQ1 —
.mcp.jsonserverskey: accept both keys;mcpServerswins when both present; info diagnostic onservers(“deprecated key; rename to mcpServers”); dropserverssupport at the next major ruleset rev. - OQ2 — skill
name/descriptionrequiredness: keep both required at current severity; reword diagnostics to best-practice phrasing (“skills should declare X — Claude Code falls back to Y”); document the stricter-than-spec stance in rules.md. - OQ3 — plugin
versionrequirement: keep requiringversionat error; document as stricter-by-design (pinned versions make update semantics explicit for marketplace review). - OQ4 — opt-in mechanism: engine-level config-driven enable. A rule
may declare itself opt-in; the engine runs it only when
.claudelint.hclhas aruleblock for it; no block → fully skipped and shown as “opt-in, disabled” inclaudelint rules.mcp/server-allowlistmigrates to the same mechanism (its loud-error-when-unconfigured behavior preserved for explicit enables without an allowlist). Details specified in DESIGN-0005 (Phase 3); CLAUDE.md convention note updated there. - OQ5 — marketplace
ownervsauthor: repurposemarketplace/author-requiredintomarketplace/owner-requiredchecking rootowner.nameat warning severity (rule-ID rename rides the fingerprint bump); legacyauthorsatisfies the check with an info-level “rename to owner” hint. - OQ6 — release cadence: each code PR ships as its own minor release via the label-driven flow; ruleset version bumps minor each time.
- OQ7 —
allowed-toolsrule placement: extendcommands/allowed-tools-knownAppliesTotocommand, skill; keep the existing rule ID; rules.md notes it covers both kinds. - OQ8 —
hooks/timeout-present: keep at warning with a rewritten message (“no explicit timeout; Claude Code defaults to 600 s — declare one to fail faster in CI”).
References
Section titled “References”- INV-0006 — audit tables this implements, including per-rule verdicts and doc URLs
- DESIGN-0001 — rule interface, range conventions, registration
- DESIGN-0002
— §2.2
.mcp.jsonkey decision being revised - DESIGN-0005 — Phase 3 output (agent package + opt-in mechanism)
- INV-0005 — dogfood-pass precedent
- Claude Code docs: sub-agents, skills, slash-commands, hooks,
hooks-guide, mcp, plugins-reference, plugin-marketplaces, memory
(
code.claude.com/docs/en/*, fetched 2026-07-09)