Repository navigation
fix(cli): the guard that watches plugin files parses what it guards - #974
Merged
Merged
Conversation
The scripts guard is the only one of the two that runs on a change confined to plugins, and it checks substrings. The cli test parses, and no filter runs it for those paths. So the structural assertions move to the one that sees them. Neither filter is widened: plugins/** would run 6800 cli tests on a prose edit, and a narrow path list is the duplication that caused this, since those paths moved last week. Source is the local artefact: GitHub returns HTTP 500 on issue creation, so no number exists to declare yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
…rching it The guard over the two skills that teach backlog-link.json looked for the three field names as substrings. A taught example that kept "backlog", "written_at" and "written_by" while ceasing to be JSON stayed green, and that case fell between this guard and the cli/ one. Recorded: with the asset's syntax broken the guard passed 3 of 3. The guard now parses the asset and the fenced block in 04-plan.md and requires each field to be a non-empty string, failing with the file and the field named. Proven by mutation: broken syntax, an emptied written_by, a broken fence in the plan and a renamed asset each turn this test red for their own reason. Neither the scripts-tests nor the cli filter was widened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The fence match took the first json block, so a second one added above the taught example would be parsed in its place, and the guard could pass while the example it guards was broken. That is the defect this guard was just fixed for, one level up. The markdown form must now carry exactly one json fence. Proven by mutation: a second fence added before the taught one fails with "found 2". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
GitHub answered HTTP 500 on issue creation while this was framed, so the folder carried a source and no declaration, which backlog-link.md calls a normal state. The issue is #972. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
It guarded a case nobody has produced: no teaching file carries two json fences. It did change behaviour, which is why it was not obviously dead, but it would have failed a contributor adding an unrelated json block to 04-plan.md with a message about a rule they never read. The guard goes back to matching the first json fence. The known weakness is accepted and stated: a json fence added above the taught example would be parsed in its place. Simplicity over a hypothetical. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
A reviewer measured two things against the previous two attempts. The mutation claimed as proof, a renamed asset, proved another test: readFileSync was already there, so origin/next failed it too. The mutation that discriminates is the fence markers stripped with the JSON left as prose, which origin/next passes and this fails. And dropping the one-fence rule did not remove the failure it was dropped for. An unrelated fence above the taught one still failed, now claiming the file does not teach `backlog` when it does. Selecting the first fence whose parse carries `backlog` answers both, and closes the weakness the last commit accepted: an unrelated fence above a broken taught example now fails instead of passing. Proven by mutation: an unrelated fence above passes; the same with the taught example broken fails; the fences stripped fails; an emptied field fails naming it. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The plan reaches implemented and the phase done. The phase gains the task the review produced: the taught example is found by what it declares, not by where it sits. The acceptance criteria are rewritten around the mutations that discriminate. A renamed asset was not one of them: readFileSync was already there, so it failed on origin/next too. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
A reviewer measured that selecting the fence by content made one case worse. A fence without `backlog` above the taught example left this guard green while `task-backlog-skill-shape.integration.test.ts`, which reads the first fence by position, went red. No gate runs that test for plugins, so the edit commits locally and lands red on next: the harm #972 was opened for. Both guards now read the first json fence. The message says what that fence must carry and how many were read, and the parse error is reported instead of swallowed, which was missing for the markdown form. And the claim that the last commit closed the accepted weakness was wrong. That weakness is a valid decoy carrying `backlog` above a broken example, and it is still green. What content selection changed was the message for a fence lacking `backlog`. Measured: a fence above the example fails here and in the sibling; a broken taught example reports its parse error; the fences stripped fails; null and an array each fail naming the shape; an emptied field fails naming it. Suite 550/550. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
Mine was unanchored where cli/tests/.../task-backlog-skill-shape reads /^[ \t]*```json.../mu. So a fence opened after text on the same line counted here and not there, and a valid decoy carrying backlog placed inline above a broken taught example left this guard green while the sibling went red. No gate runs the sibling for plugins, so that edit commits locally and lands red on next: the harm #972 was opened for, reached a second way. The regex is now copied character for character, close included. Measured on three decoy shapes, both guards agreeing each time: inline carrying backlog over a broken example, both red; inline without backlog, both green; line-start without backlog, both red. Suite 550/550. The plan records the three decisions it was missing, including why the cli test pinned value is left alone. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
It lived only in a commit body, and this repository squashes with COMMIT_MESSAGES. A valid decoy carrying backlog at line start, above a broken taught example, is read as the example by both guards. They agree, so nothing lands red on next, and closing it here alone would reinstate the rule this branch already added and removed. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The docblocks had grown by accretion over four rounds. Gone: that the two skills decide what gets written, that they teach the same three names, that the file is read rather than trusted, and the restated field count. Each is in the code a line below. Kept, because none of it is derivable: why the field list is restated instead of imported, which the third case keeps honest; that a drifting shape already produced two unreadable files; why the example is parsed and not searched; and which fence the sibling reads. 23 comment lines to 15. Suite 550/550. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The comment cut kept that two guards reading different fences leave one green and the other red, and dropped the consequence: no gate runs the sibling on plugins, so that red lands on next. That consequence is why the pattern is copied rather than improved, and it is what reverted content selection. The phase records the comment pass and replaces a criterion that pointed at a commit body with the measurement itself. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
The plan said the cli test pinned value "belongs to its own issue" while no such issue existed, which is a silent TODO. It is #973. Refs #972 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎯 What & why
A cli test reads plugin markdown that no gate watches, so a change confined to those plugin files could land it red on
next. The structural assertions move to the guard whose glob already coversplugins/**.🛠️ How it works
a-backlog-link-the-reader-can-read.test.jschecked that the three field names appeared as substrings. It parses the taught example now, and requires each field as a non-empty string — the same predicate the reader applies attask-backlog-adapter.ts:12. A taught value that is not a plain object is refused by the predicatejson-file.ts:5uses, so nothing the reader accepts is rejected here.The fence rule took three attempts, and the history is worth one line. Requiring exactly one json fence failed a case nobody has produced. Selecting the fence by content then left this guard green on a decoy above the example while its
cli/sibling went red — reopening the very harm this issue names, since no gate runs that sibling onplugins/**. What ships copies the sibling's own pattern,gadded formatchAll, so the two cannot disagree about which fence they read.One case stays green on both, knowingly. A valid decoy carrying
backlogat line start, above a broken taught example, is read as the example by both guards. They agree, so nothing lands red onnext, and closing it here alone would make this guard red where the sibling is green. Recorded in the plan's Decisions.🧪 How to verify
written_byinplugins/aidd-dev/skills/01-plan/actions/04-plan.md, stage that file alone, and runpnpm exec lefthook run pre-commit:scripts-testsexits 1. Do not pass--all-files --force, which bypasses the globs and so proves nothing about the local gate.null,[1], and an emptied field each fail naming the file, the shape or the field.node scripts/check-tests-leave-git-alone.js -- node --test 'scripts/__tests__/**/*.test.js'→ 550 pass.next.review.mdin the task folder is round 1's record and readschanges-requestedagainst a head three rounds old. Rounds 2 to 4 each found one item, each fixed here; the last returned no contract violation.🔗 Linked issue
Closes #972
✅ I certify
🤖 Generated with Claude Code
https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z