Pin the agent instructions, and fix the drift the pin finds - #11210
Conversation
CLAUDE.md and .github/copilot-instructions.md both open with the claim that they stay in sync, and both are read as binding shorthand - CLAUDE.md is loaded into every Claude Code session, copilot-instructions.md into every Copilot review. Nothing checked either claim. tests/unit/test_agent_instructions.py adds four pins that need no database, no network and no fixtures: every backticked repo path in the four agent-facing files resolves (55 of them), every relative link and same-page anchor resolves, every skill the routing table names exists as .claude/skills/<name>/SKILL.md, and the rules that must reach both audiences are present on both sides. The mirroring pin is keywords rather than a text diff, because the two files address different readers and paraphrase each other; requiring byte equality would force false uniformity. It found two things. copilot-instructions.md described the specification and implementation lifecycles but nowhere stated the rule they exist to protect: never merge a pipeline PR by hand, never write the pipeline's files by hand, and put the 'approved' label on the issue rather than the PR. That is the repository's most consequential rule and the one an agent that opens and edits PRs is most able to break. And agentic/docs/project-guide.md linked to /CLAUDE.md, which GitHub resolves as a site-root URL and answers with a 404. Transferred from the sibling repo kurrentschrift. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke
There was a problem hiding this comment.
🟡 Changes recommended
Multiple instruction pins can pass despite invalid anchors, removed companion claims, or reversed workflow rules.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds regression tests to keep agent instructions synchronized and valid.
Changes:
- Pins referenced paths, links, skills, and mirrored rules.
- Adds mandatory pipeline safeguards to Copilot instructions.
- Fixes a project-guide link and updates the changelog.
Review findings:
- Strip fenced code blocks before extracting Markdown headings.
- Match the explicit companion wording rather than filename occurrences.
- Require negative wording for the manual-merge prohibition.
- Align the project guide’s release instructions with the companion guides.
File summaries
| File | Description |
|---|---|
tests/unit/test_agent_instructions.py |
Adds instruction-integrity tests; several assertions require strengthening. |
CHANGELOG.md |
Records the changes. |
agentic/docs/project-guide.md |
Fixes the CLAUDE.md link; release guidance remains inconsistent. |
.github/copilot-instructions.md |
Adds mandatory pipeline safeguards. |
Review details
Suppressed comments (1)
tests/unit/test_agent_instructions.py:209
- This rule pin passes while another file classified above as an agent instruction still says the opposite:
agentic/docs/project-guide.md:1064-1067instructsgh release createto publish the changelog section “verbatim,” whereas both companion guides require a condensed release and also now require bumpingapp/package.json. BecauseCLAUDE.mdlinks agents to that guide, the instructions remain materially inconsistent. Update the project guide to the current release flow (and consider pinning that wording too).
"a release is condensed, never copied": ["condensed, never copied", "agentic/commands/release.md"],
- Files reviewed: 4/4 changed files
- Comments generated: 3
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Copilot review, all three applied. 1. _HEADING ran over raw Markdown, so a '# Install uv' comment inside a fenced shell block counted as a heading - a dead same-page anchor could pass by matching a line GitHub renders as code. Fenced blocks are stripped first; that drops 12 fake anchors in CLAUDE.md, 11 in copilot-instructions.md and 40 in project-guide.md, and no real one. 2. The pipeline rule was pinned on the bare substring 'manually merge', which carries no polarity: both guides could be rewritten to say an agent may do it and the pin would stay green while naming the rule it no longer protects. Both guides are normalised on 'never' (CLAUDE.md's step 3 said 'DO NOT'), and the keywords are now the negative phrases themselves, 'never manually merge' and 'never bypass'. 3. The companion-claim test matched the bare filenames, which appear in both guides in other sections - so it stayed green with both opening claims deleted. It now matches the companion sentence and requires 'Both files MUST stay in sync' on each side. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke
There was a problem hiding this comment.
🟡 Changes recommended
The path heuristic misses extensionless Dockerfiles, and two prose-style nits remain.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
tests/unit/test_agent_instructions.py:12
- Add the serial comma required by the repository documentation style.
Four cheap pins, none of which needs the database, the network or a checkout of
anything but this repository:
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Balanced
Copilot review, both applied. The file-shape heuristic required a known suffix or a trailing slash, so extensionless files fell through it silently: agentic/docs/project-guide.md names api/Dockerfile and app/Dockerfile, and deleting or renaming either would have left the path pin green. A small allowlist of extensionless basenames covers them - matched on the basename, so prose tokens like 'prism/r' and the 'app/app/src' anti-example stay out. The checked-path count goes from 55 to 57. The changelog entry was missing the serial comma the repository's prose style requires. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke
There was a problem hiding this comment.
🟡 Changes recommended
The path check misses documented paths contained in multi-path code spans.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
CLAUDE.md:128
- This edited instruction still uses all-caps and exclamation-mark emphasis, which the repository prose rules prohibit (
.claude/skills/write-docs/SKILL.md:79-80). Use sentence case while retaining the negative phrase required by the pin.
3. NEVER manually merge PRs!
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Balanced
Merge resolves the CHANGELOG conflict by keeping both Added entries (#11205 and #11206 landed on main while this branch was open). Copilot review: _candidate_paths treated a whole code span as one token, so '.claude/commands/ -> ../agentic/commands/' - a sentence about two paths - was not path-shaped and went unchecked as a whole. Spans are now split on whitespace before the shape test; fragments that are not path-shaped (the arrow, a command word, a flag) drop out, which is what makes the split safe. A '../'-relative fragment is skipped rather than resolved against the repository root, where it would mean something else entirely. That leaves the arrow's meaning unpinned, and the sharper failure is the one where somebody replaces the symlink with a real directory: both ends still exist, the guide still reads true, and commands written on either side quietly stop matching the other. So the symlink gets its own test, which follows the link instead of matching its text. (The specific example was in fact already covered, because '.claude/commands/' appears standalone later in the same sentence - but the hole in the heuristic was real.) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke
There was a problem hiding this comment.
🟡 Changes recommended
The mirrored changelog pin does not currently protect the required action.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
tests/unit/test_agent_instructions.py:218
- The test plan in the PR description is stale after this additional test was added: the current parametrization produces 22 tests, not the stated 21. The same description still reports 55 checked paths while the updated changelog reports 57; refresh both verification figures so the PR metadata matches this revision.
def test_the_commands_symlink_points_where_it_says() -> None:
- Files reviewed: 5/5 changed files
- Comments generated: 1
- Review effort level: Balanced
Copilot review, same class as the pipeline-rule finding one round earlier:
the changelog entry was pinned on '[unreleased]' plus 'keep-a-changelog',
which is the rule's subject matter. Both guides could be rewritten to merely
mention the file and the format while dropping the requirement, and the pin
would stay green - reporting a mirrored rule when what is mirrored is a
topic.
It now requires 'every pr updates' as well. The Google-style entry had the
same weakness ('google style' plus a path) and now requires the full 'prose
follows the Google developer documentation style guide'. The dict carries the
rule that keywords must include the obligation, so the next entry added does
not repeat this.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke
There was a problem hiding this comment.
🟢 Approval recommended
The only unresolved comment is a non-blocking documentation-style nit.
Review details
Suppressed comments (1)
CLAUDE.md:128
- Use sentence case here. The repository documentation contract explicitly disallows all-caps and exclamation-mark emphasis; the mirror check is case-insensitive, so this keeps the required negative phrase without violating that rule.
3. NEVER manually merge PRs!
- Files reviewed: 5/5 changed files
- Comments generated: 0 new
- Review effort level: Balanced
CHANGELOG only, resolved as a union: every bullet from both sides kept, main's own order untouched. Added gains this branch's origin-gate entry above the agent-instruction pin; Changed keeps the two gate entries alongside the Node pin; the new Security section is carried over unchanged. The agent-instruction pin test that arrived with #11210 passes against this branch, including the infra/ entries this PR adds to both repository maps. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke
Summary
CLAUDE.mdand.github/copilot-instructions.mdboth open with the claim that they stay in sync, and both are read as binding shorthand — one is loaded into every Claude Code session, the other into every Copilot review. Nothing checked either claim.tests/unit/test_agent_instructions.pyadds four pins that need no database, no network and no fixtures.tests/test_agent_instructions.py, PR [line-basic] matplotlib implementation #484), adapted to this repository's file layout and its own sync claim.What is pinned
CLAUDE.md,.github/copilot-instructions.md,agentic/docs/project-guide.md,agentic/commands/prime.md.claude/skills/<name>/SKILL.md;/update-configis excluded by name because CLAUDE.md itself says it is a harness skill, and slash names that areagentic/commands/*.mdresolve thereA path is only checked when it is multi-segment and file- or directory-shaped, so prose shorthand (
CHANGELOG.md,conftest.py) is not forced to carry a full path — pinning those would make the guides harder to read, which is the opposite of the point.Anchors are matched against real headings: fenced blocks are stripped first, because these guides are full of shell snippets whose
#comments would otherwise pass as headings and let a dead link through (63 such phantoms across the four files).The mirroring pin is keywords, not a text diff. The two files address different readers and paraphrase each other; requiring byte equality would force false uniformity. What it catches is a rule silently living in only one of them. The keywords carry the prohibition, not just its subject —
["never manually merge", "never bypass"], not["manually merge"]— so a guide cannot be rewritten to permit what the pin claims to forbid while staying green.The drift it found
1.
copilot-instructions.mdnever stated the rule the pipeline exists to protect. It documents the specification and implementation lifecycles, the label taxonomy and the quality-threshold cascade — but nowhere says: never merge a spec or implementation PR by hand, never writespecification.md/metadata/*.yamlby hand, and put theapprovedlabel on the issue, not the PR.CLAUDE.mdcarries all of that in a "CRITICAL: Mandatory Workflow" section with a DON'T/DO table. That is the repository's most consequential rule and the one an agent that opens and edits PRs is most able to break, so a compact form of it now sits incopilot-instructions.md's Important Rules, pointing at the full table.2.
agentic/docs/project-guide.mdlinked to/CLAUDE.md. A leading slash reads as repo-root-relative to a human but as site-root to GitHub's renderer, where it 404s. Now../../CLAUDE.md.3. (review round)
CLAUDE.mdstep 3 said "DO NOT manually merge PRs!" whilecopilot-instructions.mdsays "never" — normalised on "never" so the pin can match the negative phrase itself rather than the bare subject.Nothing else moved. The seven mirrored rules are the ones already present in both files (English output, Google style, the changelog rule, the condensed-release rule, never echoing secrets, structural over symptomatic fix) plus the one added above.
What this test is not
It is not a place to introduce a rule. Adding an entry to
MIRRORED_RULESfor something only one guide says makes the suite red until someone writes the rule into both — which is the intended order: write it in both guides first, then pin it.Test plan
uv run pytest tests/unit/test_agent_instructions.py— 21 passed.copilot-instructions.mdunchanged it failed withrule 'never merge a pipeline PR by hand' is missing from: copilot-instructions.md, and the link pin reported/CLAUDE.mdbefore the fix.uv run pytest tests/unit— 1768 passed, 1 skipped (pre-existing local skip: MonoLisa italic not cached).uv run ruff check ./ruff format --check .— clean.Checklist
CHANGELOG.mdupdated under[Unreleased].copilot-instructions.mdmirrors a ruleCLAUDE.mdalready had, so no rule moved out of sync.🤖 Generated with Claude Code
https://claude.ai/code/session_01PBQdMbboxo59sSThGSbfke