Skip to content

fix(agents-md): resolve .claude/rules references in bodies by the rule they name (0.8.4) - #24

Merged
llima merged 15 commits into
mainfrom
fix/cli-0-8-4
Oct 4, 2026
Merged

llima merged 15 commits into
mainfrom
fix/cli-0-8-4

Conversation

@llima

@llima llima commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

Version bumped to 0.8.4. Merging publishes craftar@0.8.4 to npm after approval in the npm environment.

What changes. Bodies in AGENTS.md (always-on sections and the scoped rules 0.8.3 embeds) used to keep every .claude/rules/<x>.md reference exactly as the Forge holds it. In a workspace without claude-code, those references point at nothing. Each reference is now resolved by the rule it names (approved spec: rule references inside AGENTS.md bodies):

the named rule token link [t](.claude/rules/<x>.md)
claude-code writes it unchanged unchanged
kiro writes .kiro/steering/<x>.md (from the rule or from a steering ingredient) .kiro/steering/<x>.md [t](.kiro/steering/<x>.md), fragment kept
its text is in this AGENTS.md AGENTS.md (rule: <x>) [t](AGENTS.md)
a rule that reaches no target here, or an unknown name without claude-code <x> (rule not in this workspace) t (<x>, rule not in this workspace)
  • One warning at most per AGENTS.md. It names every reference turned into "rule not in this workspace", in AGENTS.md order. Without claude-code, it also names other .claude/ paths (agents, commands, skills, scripts, hooks), which are left as written. It never changes an exit code.
  • The resolver is pure. resolveRuleRefs in src/emitters/shared.ts reads the resolution, never the disk. It shares RULE_NAME_CHARS, ruleWriter and ruleFile with the other emitters.
  • No change elsewhere. The claude-code and kiro emitters are untouched: the reviewer measured them byte-identical to 0.8.3 across sandbox and synthetic Forges. There is no schema, lock, plan() or CLI change.

Which workspaces see update

Only AGENTS.md changes. The user approved the byte change at a checkpoint before the commit, from the before and after measured on 0.8.3 with the spec's Forge.

workspace at the first sync on 0.8.4
every cited rule is written by claude-code (every workspace craftar import produced) no change, no warning
bodies cite no .claude/ path no change
no claude-code; bodies cite sibling rules each reference moves (B, C, or reworded)
claude-code present; a cited rule its own targets keep away from it, or a steering file kiro writes that reference moves
no claude-code; bodies cite only other .claude/ paths no byte change, but the warning appears on every run

An affected workspace shows AGENTS.md as update, and craftar sync --check exits 1 until it syncs.

Tests

  • 17 spec tests, written literally. On 0.8.3, tests 4 and 8 passed, as the spec requires (state A untouched; non-references ignored), and the other 15 failed.
  • Six tests added during review. Each failed before its fix. They cover:
    • link boundaries;
    • a command named like a rule;
    • warning order (two cases);
    • a steering/rule name collision;
    • a token right after a link.
  • Existing tests edited: 3 lines, approved by the user. These are spec-14 tests 1, 2 and 9. Their bodies cite .claude/rules/style.md, a reference spec 14 left for 0.8.4 on purpose. They now expect it resolved: AGENTS.md (rule: style) or .kiro/steering/style.md.

Disclosures

  • 8d68e1d is not a refactor. It is titled refactor(…) but it changed the state precedence when a rule and a steering ingredient share a name. 0994cf4 fixes that, and a test pins it.
  • 61e3b20 only partly fixed warning order. It claimed order by position, which 0d38e9e and 599f437 complete.
  • Two commits are misattributed. The ruleFile("kiro", …) change for state B landed in 0d38e9e, not in 0c019b7. The subject of 0c019b7 names it, but its diff is only a comment.
  • Two commit bodies are imprecise. The bodies of 6d972e2 (ruleWriter described as already used) and 47806d1 (it cites §4.3/§4.5 where §5 and §7 apply) do not match their diffs.
  • Known limits:
    • Link text that is itself a reference. It is resolved as a token too, and the spec does not decide this case.
    • A token immediately before a link. It is resolved correctly, but no dedicated test pins it.
  • The kiro emitter still rewrites every .claude/rules/ to .kiro/steering/. That is 0.8.5's subject (its own spec, with its own byte checkpoint).

Test plan

  • npm run typecheck: exit 0 on 599f437.
  • npm run build: exit 0.
  • vitest without test/ci.test.ts (Linux): 821 passed / 5 skipped.
  • Oracle: skipped. There is no fixture, and the user declined using a client workspace. The byte evidence is:
    • test/golden/** is unchanged;
    • the reviewer measured claude-code and kiro output byte-identical to 417caeb across target sets;
    • the approved before/after probes were re-run after every correction round, with identical results.
  • Reviews:
    • node-cli-reviewer: five rounds; the fifth was clean.
    • docs-author: three rounds; the third was clean.
  • CI green on this PR: 8 of 8 (ubuntu and windows × Node 22 and 24, runs 37241719000, 37241746570).

llima added 15 commits October 4, 2026 17:51
The name-class regex of kiro.ts's referencedRules is now RULE_NAME_CHARS
in shared.ts, so there is one spelling for both emitters.
The inline writers computation in agents-md.ts uses ruleWriter, the
same logic shared.ts already exposes for spec-14 and spec-15 lookups.
…e they name

Spec 15: a reference becomes the file a target writes, the rule's place in
AGENTS.md, or "<x> (rule not in this workspace)", with one warning per AGENTS.md.
Three spec-14 tests now expect their resolved bodies (approved).
…nd what 0.8.4 changes

Adds the new sentence to the agents-md target paragraph and the
### to 0.8.4 upgrading section per spec 15 §4.3 and §4.5.
Bumps version to 0.8.4 for the patch release.
Spec 15 §4.1 puts boundaries around the path, not the link syntax.
A link already bounds its path with ( and ), so no extra checks needed.
Spec 15 §4.2 looks up the name among rules first, then steering only
for state B. A command with the same output name no longer hides the rule.
Spec 15 §4.5 orders entries by position in the body. Collect matches
with their offsets, sort by offset, and dedup keeps first occurrence.
…AME_CHARS

Use ruleWriter for states A and B. Build RuleLookup once per AGENTS.md,
not once per body. Use the ingredient's ref directly for reporting.
…aces see the warning

Clarify that unknown names are left as written when claude-code is a
target, update the No change/no warning cases, and add version numbers.
…t writes

When a rule exists but neither claude-code nor kiro writes it, check for a
steering ingredient of the same name before returning state C or D. This
implements spec 15 §4.2 correctly: B before C/D for .kiro/steering/<x>.md.
…it stops

Clarifies that state-B and state-C references are not reported (only those
turned into "rule not in this workspace"), and that the warning repeats until
the body, the cited rule's targets or the workspace's targets change.
…dedups again

leftBoundary was defined but never called; token pattern captures the boundary
character instead. The emitRefWarning dedup comment now says it is a backstop
for references cited by multiple ingredients.
…al body

Token references were recorded at their offset in the result after link
replacements, not in the original body, causing entries to appear out of order
when links shrank or grew.
…st comment on the warning dedup

The previous commit already routed state B through ruleFile; this commit
updates the dedup comment to accurately describe what it does.
The token offset was calculated from the full match, which includes the
left boundary character. For a token immediately after a link (e.g.
`[y](.claude/rules/u5.md).claude/rules/u6.md`), this caused the overlap
check to wrongly skip it. Now the offset is the position of the actual
token, not the boundary.
@llima
llima merged commit 9328c40 into main Oct 4, 2026
8 checks passed
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