Skip to content

agents-md.primary: AGENTS.md as the one home of the always-on rules - #75

Merged
llima merged 33 commits into
mainfrom
feat/agents-md-primary
Oct 24, 2026
Merged

llima merged 33 commits into
mainfrom
feat/agents-md-primary

Conversation

@llima

@llima llima commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • Version bumped to 0.36.0 (minor). Merging publishes craftar@0.36.0 to npm after approval in the npm environment.
  • Spec 41 Part A: with the param agents-md.primary: true in a Forge that declares schema: 5, an always-on rule with default targets lives in AGENTS.md alone. Its .claude/rules/ and .kiro/steering/ copies become orphans that sync removes, and every .claude/, .kiro/ and AGENTS.md text that cites the rule follows it.
  • The mode is opt-in: a workspace that does not set it sees no change in bytes, lock, warnings, exit codes or --json. This is pinned in test/pins/agents-md-primary-off.json, committed first and never regenerated.
  • Under the mode:
    • Kiro agent resources name only the steering files the plan writes.
    • A hand-written resource the plan does not write fails sync --check, status and diff --exit-code; a writing sync still writes.
    • A CLAUDE.md that neither imports AGENTS.md nor is absent is warned about. @AGENTS.md / @./AGENTS.md count in the root file, @../AGENTS.md in .claude/CLAUDE.md (measured, Claude Code 2.1.310).
    • doctor gains the checks agents-md-primary and kiro-resources.
    • The words for an orphan the mode made are shared by craftar sync and the sync tool of craftar mcp.
    • drift promote refuses a file the mode reworded (D20).
    • import reads back what the mode laid out, reusing every base.
  • The key agents-md.primary is reserved at every schema. Every craftar before 0.36.0 refuses a schema: 5 Forge. README Upgrading has a to 0.36.0 block.

Test plan

  • npm run typecheck — clean (HEAD 67b97e5, 06:30)
  • npm run build — clean
  • npm test (--maxWorkers=6, test/ci.test.ts excluded from the by-hand run) — 152 files passed, 1 skipped; 3571 tests passed, 11 skipped. Oracle skipped (no fixture on this machine). The byte evidence with the mode off is the off-pin plus the existing pins and goldens; with the mode on it is the golden test/golden/agents-md-primary/.
  • node-cli-reviewer (opus): four rounds over the full range; rounds 1 and 3 should-fixes all fixed; round 4 clean.
  • By hand, synthetic workspace: Claude Code 2.1.310 and kiro-cli 2.29.1 (default agent and a custom agent) each load an always-on rule once.
  • By hand: a CLAUDE.md whose first line is a BOM followed by @AGENTS.md still loads AGENTS.md (Claude Code 2.1.310).
  • By hand: a manual steering file is not loaded through Kiro resources, by glob or by name.
  • CI green on this PR

llima added 30 commits October 24, 2026 06:10
Spec 41 §11.1: bytes, warnings, lock, status --json and exit codes before the mode exists.
Spec 41 §4.1: the schema that reads agents-md.primary; schema 5 with no key or false plans what schema 4 plans.
Spec 41 §4.1: three warnings, none when the key is unset; the key is reserved at every schema.
Spec 41 §4.3: effectiveTargets() narrows 'always + rule + *' to ['agents-md']
when resolution.primary is true. written() and the agents-md emitter use it
to redirect scoped rules.

- Add effectiveTargets function to shared.ts
- Update written() in capabilities.ts to use effectiveTargets
- Update buildRuleLookup to include ruleTargets map
- Update resolveRuleRefs to use lookup.ruleTargets
- Update kiro.ts and agents-md.ts to pass resolution to buildRuleLookup
- Tests verify: effectiveTargets unit tests, written() filtering,
  plan() file paths, and the agents-md.primary invariant
Under Resolution.primary, the claude-code emitter resolves .claude/rules/<x>.md
references through resolveRuleRefs(…, "agents-md"):
- A → unchanged (claude-code writes the rule)
- B → .kiro/steering/<x>.md (kiro writes the rule)
- C → AGENTS.md (rule: <x>) (only AGENTS.md holds the rule)
- D → <x> (rule not in this workspace), reported
- Unknown → unchanged (claude-code is a target)

Files resolved: agent body and frontmatterRaw (or description), command body
and frontmatterRaw (or description), skill SKILL.md and text files of dir
layouts, scoped rule bodies. Copied files (scripts, hooks, binary skill files)
are scanned with unresolvedRuleRefs and hits join the warning.

This adds:
- shared.ts: formatDeadRefs and formatScannedRefs helpers
- types.ts: EmitBase.reportReworded(path)
- sync.ts: Plan.reworded collection via ctx.reportReworded
- claude-code.ts: full rewrite with primary mode support
Under Resolution.primary, the kiro emitter:
- Filters ruleNames and scopedRules to rules kiro writes (effective targets
  admit kiro) so generated agent resources name only planned paths
- Scans non-KIRO_TEXT_EXT files in directory skills for unresolved rule
  references, adding hits to the kiro: warning as <path>:<line> (<name>)
- Reports reworded paths through EmitBase.reportReworded

agents-md emitter:
- Reports AGENTS.md as reworded when any body's resolved text differs from
  mode-off resolved text

Tests in test/agents-md-primary.test.ts cover:
- tests 1-5: claude-code emitter reference resolution
- test 6: kiro copied skill files warned
- test 7a-7b: kiro generated resources under primary mode
- test 8, 8b: Plan.reworded paths
- test 10: unresolvedRuleRefs unit test
Spec 41 §4.5–§4.6: an unplanned entry fails sync --check, status and diff --exit-code; a writing sync still writes.
…S.md

Spec 41 §4.7, §4.9: one warning naming the file, never a line of it; the budget warning says the rules are in no other file.
…ts-md.primary

Spec 41 §4.8: diff header and sync removal line say 'written to AGENTS.md only'.
Spec 41 §4.10: warn when the mode is on and claude-code is a target, warn for unplanned Kiro agent resources.
Measured with Claude Code 2.1.310: from .claude/CLAUDE.md only @../AGENTS.md reaches the root's AGENTS.md; @AGENTS.md there reads nothing and the file still blocks.
…nder agents-md.primary

Spec 41 §4.4 resolves frontmatterRaw when present, else the description; claudeResolves is now the one definition of the files resolved, for the emitter and for import.
Spec 41 §4.10: bases are compared with the claude-code emitter's rewording, a rule held in AGENTS.md alone is not lacked by a recipe, and a profile that does not resolve fails closed with the Forge untouched.
Spec 41 §4.10: D20 before any write; drift show gives the reason, placed after provided by integrations.
Spec 41 §11.3: the always-on rules in AGENTS.md alone, and no reference to a rule file the plan does not write in a .claude/ or .kiro/ text.
Spec 41 §10: what the mode writes, the CLAUDE.md advice and the measured Claude Code and Kiro behaviour, the two doctor checks, and the Upgrading block (its version set at the rebase).
…d.primary

With the mode off import no longer reads recipes/<name>.yaml before writing the owned recipe, so a file there that declares another name is no refusal, as before this branch.
claudeResolves names ./rule.md and /rule.md as the file listed as rule.md, so import's rewording reaches a base declared that way (spec 41 §4.10).
An entry is normalised before it is compared, one that climbs out is not checked, and existence is an lstat — a file an entry names is never read.
… root

Spec 41 §4.7 names every ancestor; the walk stopped one short. The ok case now skips through it.skipIf when a real ancestor of the temp directory holds a CLAUDE.md, instead of passing without an assertion.
One predicate for AGENTS.md-only beside effectiveTargets; each text of a file compared on its own for Plan.reworded; primaryOrphan typed on FileStatus; the unused kiro copy() removed; the right-boundary note restored.
The sync tool of craftar mcp (0.35.0) worded every removal as an orphan the
Forge no longer produces; one orphanReason now words both surfaces.
@llima
llima merged commit 43933c4 into main Oct 24, 2026
11 of 12 checks passed
@llima
llima deleted the feat/agents-md-primary branch October 24, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant