Skip to content

fix: accept pointer-only CLAUDE.md in fm-ensure-agents-md#907

Open
mattwwatson wants to merge 1 commit into
kunchenguid:mainfrom
mattwwatson:up/pointer-only-claude-md
Open

fix: accept pointer-only CLAUDE.md in fm-ensure-agents-md#907
mattwwatson wants to merge 1 commit into
kunchenguid:mainfrom
mattwwatson:up/pointer-only-claude-md

Conversation

@mattwwatson

Copy link
Copy Markdown

Intent

Upstream contribution from the mattwwatson/firstmate fork: stop fm-ensure-agents-md.sh reporting a false conflict for projects whose CLAUDE.md is a deliberate pointer-only file containing an @AGENTS.md include. Detect the pointer-only form (variants tolerated after BOM/CRLF/whitespace strip) inside the both-real-files branch and leave the file byte-for-byte untouched; all other divergent-content cases still refuse. The docs/scripts.md table row was hand-merged onto upstream's current list (upstream's new rows kept, only the fm-ensure-agents-md description updated). Suite passes 14/14 locally on the upstream base.

What Changed

  • bin/fm-ensure-agents-md.sh now recognizes a real CLAUDE.md whose only content is an @AGENTS.md include (after tolerating a BOM, CRLF endings, blank lines, surrounding whitespace, and a missing final newline) inside the both-real-files branch: it proceeds against AGENTS.md and leaves the pointer file byte-for-byte untouched instead of exiting with a false conflict. Any other divergent content — prose, headings, comments, or includes of other files — still triggers the existing refusal, and the conflict message now says "with their own content" to reflect the narrower case.
  • Added 5 tests to tests/fm-ensure-agents-md.test.sh covering the pointer-only acceptance, tolerated byte variants, and continued refusal of pointer-plus-extra-content files (suite now 14/14).
  • Updated docs/architecture.md and the docs/scripts.md table row to describe the CLAUDE.md compatibility pointer as either a symlink or a pointer-only @AGENTS.md include, hand-merged onto upstream's current script list.

Risk Assessment

✅ Low: A small, well-bounded relaxation of one conflict branch with strict fail-closed fallback, correct bash-3.2-safe parsing, five targeted new tests covering accept/refuse edges, and full conformance with the stated intent; the only finding is a test-assertion fidelity nit.

Testing

Ran the fm-ensure-agents-md suite (14/14 pass, both directly and through the project's fm-test-run.sh runner) and a manual before/after CLI demo proving the end-user behavior: the base commit falsely reports a conflict for a pointer-only @AGENTS.md CLAUDE.md, while the target commit accepts it and leaves the file byte-for-byte untouched (sha256-verified), tolerates BOM/CRLF/whitespace variants, and still refuses divergent-content cases without touching either file; no UI surface is involved, so the CLI transcript is the reviewer-visible evidence.

Evidence: Before/after CLI demo transcript (false conflict on base → accepted untouched on target, variants, refusal)

--- BEFORE (base 6bcb381): exit=1 conflict: both AGENTS.md and CLAUDE.md are real files ... --- AFTER (target 0ea99e6): exit=0 unchanged: AGENTS.md with CLAUDE.md including AGENTS.md ... CLAUDE.md sha256 before == after → byte-for-byte untouched: YES (still a regular file, not a symlink) BOM/CRLF/indented/no-final-newline variant: exit=0, untouched: YES Divergent content (@AGENTS.md + extra prose): exit=1 conflict, both files untouched: YES

=== End-to-end demo: pointer-only CLAUDE.md (@AGENTS.md include) ===

--- Project layout ---
$ cat CLAUDE.md
@AGENTS.md
$ head -1 AGENTS.md
# My project agent memory

--- BEFORE (base commit 6bcb381): false conflict ---
$ fm-ensure-agents-md.sh <project>   # base-commit version
exit=1 
conflict: both AGENTS.md and CLAUDE.md are real files in /private/var/folders/mz/9drvc3ms7w9c8vzk0j1gj2vm0000gp/T/tmp.u3D4C5eSNJ/pointer-project; reconcile them manually

--- AFTER (target commit 0ea99e6): accepted, file untouched ---
CLAUDE.md sha256 before: 336cc4fbf19beaada7ccf9986414fa91851a8d7a07dfb3ccbe800a69eed0ab49
$ bin/fm-ensure-agents-md.sh <project>
exit=0
unchanged: AGENTS.md with CLAUDE.md including AGENTS.md in /private/var/folders/mz/9drvc3ms7w9c8vzk0j1gj2vm0000gp/T/tmp.u3D4C5eSNJ/pointer-project
CLAUDE.md sha256 after:  336cc4fbf19beaada7ccf9986414fa91851a8d7a07dfb3ccbe800a69eed0ab49
CLAUDE.md byte-for-byte untouched: YES (and still a regular file, not a symlink)

--- Variant: BOM + CRLF + indented include, no final newline ---
$ bin/fm-ensure-agents-md.sh <variant-project>
exit=0
unchanged: AGENTS.md with CLAUDE.md including AGENTS.md in /private/var/folders/mz/9drvc3ms7w9c8vzk0j1gj2vm0000gp/T/tmp.u3D4C5eSNJ/variant-project
variant CLAUDE.md byte-for-byte untouched: YES

--- Divergent real content still refuses (both files untouched) ---
$ bin/fm-ensure-agents-md.sh <divergent-project>
exit=1
conflict: both AGENTS.md and CLAUDE.md are real files with their own content in /private/var/folders/mz/9drvc3ms7w9c8vzk0j1gj2vm0000gp/T/tmp.u3D4C5eSNJ/divergent-project; reconcile them manually
both files byte-for-byte untouched after refusal: YES

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ℹ️ tests/fm-ensure-agents-md.test.sh:212 - The byte-for-byte-untouched assertion for the pointer-only CLAUDE.md uses [ &#34;$(cat CLAUDE.md)&#34; = &#34;$before_claude&#34; ]; command substitution strips trailing newlines on both sides, so a trailing-newline-only modification of CLAUDE.md would pass undetected. Snapshot the file with cp and compare with cmp/diff (as test_existing_agents_md_with_symlink_gains_self_governance already does for AGENTS.md) to make the assertion truly byte-exact.
✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-ensure-agents-md.test.sh — all 14 tests pass, including the 5 new pointer-only/conflict/variant tests
  • bin/fm-test-run.sh tests/fm-ensure-agents-md.test.sh — project runner: total=1 failed=0, family pure-contract-unit
  • Manual before/after demo: base-commit script (git show 6cb...:bin/fm-ensure-agents-md.sh) reports the false conflict (exit 1); target script exits 0 with unchanged: and CLAUDE.md sha256 identical before/after
  • Manual variant check: BOM + CRLF + indented @AGENTS.md with no final newline accepted and left byte-for-byte untouched
  • Manual refusal check: CLAUDE.md with include plus extra prose exits 1 with conflict: and both files sha256-verified untouched
  • Reviewed diff of docs/scripts.md — only the fm-ensure-agents-md description row changed; upstream rows intact
  • git status --porcelain — working tree clean after testing
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

fm-ensure-agents-md.sh refused a project whose CLAUDE.md is a plain
file containing only an @AGENTS.md include, even though that is a
deliberate, working compatibility layout, so every run reported a
false conflict. Inside the both-real-files branch, detect a
pointer-only CLAUDE.md (@AGENTS.md variants after BOM, CRLF, and
whitespace stripping) and leave it byte-for-byte untouched; every
other divergent-content case still refuses as before.
@mattwwatson
mattwwatson force-pushed the up/pointer-only-claude-md branch from 0ea99e6 to 39f3206 Compare July 24, 2026 19:14
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