Summary
claude-security-review.yml documents its paths / paths-file inputs as taking "GitHub Actions paths: filter syntax" (:155-156, :171-172 at c136b27), but :410-449 implements matching with git init + a root-anchored .gitignore + git check-ignore --stdin --no-index. Those are two different specifications.
Not a live behavior defect today — verified empirically below. Filing so the divergence is recorded before a consumer writes a pattern in the divergent set, because on claude-code-plugins this lane's check is REQUIRED, and a silently-non-matching pattern there means a security review that never fires.
Empirical result
I reproduced the workflow's pattern loop (:417-428) verbatim and ran git check-ignore against representative paths. The reproduction script and the anchoring logic are identical to the shipped implementation.
Anchoring works as intended for ordinary globs. Notably **/*.sh DOES match a root-level install.sh (gitignore treats a leading **/ as "any depth including root"), so the most obvious candidate defect does not exist:
FIRES install.sh
FIRES scripts/deploy.sh
FIRES plugins/foo/hooks/x.js
FIRES package.json
FIRES sub/package.json
FIRES .mcp.json
FIRES REVIEW.md
FIRES .github/workflows/ci.yml
NO-REVIEW docs/readme.md
Where the two specifications actually diverge
1. Negation (!) — the material one. Actions paths: supports negation with order-dependent semantics; the official docs state "A matching negative pattern (prefixed with !) after a positive match will exclude the path" and "The order that you define paths patterns matters." The implementation passes ! through to gitignore (:424), where order still matters but the containing-directory rule does not apply the same way. Measured:
patterns: .github/** then !.github/docs/**
FIRES .github/docs/notes.md <-- negation did NOT exclude it
A consumer writing an Actions-valid exclusion gets a pattern that silently does not exclude. On a repo where this check is required, that direction is at least fail-safe (more reviews, not fewer) — but it is not what the consumer wrote.
2. Character classes. [0-9] is undocumented for Actions paths: (the docs describe only * and **) but is fully supported by gitignore. Measured: src/file[0-9].ts matches src/file1.ts and not src/fileA.ts. A consumer could rely on behavior the documented spec does not promise.
3. Trailing-slash directory patterns. scripts/ is a gitignore directory-match idiom; Actions' docs do not address it. Measured as matching scripts/deploy.sh.
4. ? is listed for Actions branch/tag filters but omitted from the paths: description; gitignore supports it.
Live exposure: none today
melodic-software/claude-code-plugins ships the tuned paths file — 26 pattern entries (31 lines including 5 comment/blank). Checked every entry against all four divergent classes:
negation (!): 0
character class ([): 0
trailing-slash only: 0
question mark (?): 0
Every entry is an ordinary * / ** glob, where the two specifications agree. So this is a documentation defect, not a live behavior defect — but the gap is one ! line away from becoming one in the repo where the check is required.
Suggested fix
Correct the input descriptions to describe what is actually implemented. Options, in preference order:
- Document the real matcher — say the patterns are matched as root-anchored gitignore patterns, note that
! negation does not behave as Actions documents it, and drop the "Actions paths: syntax" claim. Cheapest and honest.
- Reject unsupported patterns loudly — fail the
changes job on a ! line rather than silently mismatching. Fail-closed on a spec the implementation cannot honor.
- Implement true Actions
paths: semantics. Most faithful, most work; probably not worth it given zero live usage of the divergent set.
Recommend (1) plus (2): document reality, and make the one materially divergent class loud instead of silent.
Related
No linked issue.
🤖 Generated with Claude Code
https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb
Summary
claude-security-review.ymldocuments itspaths/paths-fileinputs as taking "GitHub Actionspaths:filter syntax" (:155-156,:171-172atc136b27), but:410-449implements matching withgit init+ a root-anchored.gitignore+git check-ignore --stdin --no-index. Those are two different specifications.Not a live behavior defect today — verified empirically below. Filing so the divergence is recorded before a consumer writes a pattern in the divergent set, because on
claude-code-pluginsthis lane's check is REQUIRED, and a silently-non-matching pattern there means a security review that never fires.Empirical result
I reproduced the workflow's pattern loop (
:417-428) verbatim and rangit check-ignoreagainst representative paths. The reproduction script and the anchoring logic are identical to the shipped implementation.Anchoring works as intended for ordinary globs. Notably
**/*.shDOES match a root-levelinstall.sh(gitignore treats a leading**/as "any depth including root"), so the most obvious candidate defect does not exist:Where the two specifications actually diverge
1. Negation (
!) — the material one. Actionspaths:supports negation with order-dependent semantics; the official docs state "A matching negative pattern (prefixed with!) after a positive match will exclude the path" and "The order that you definepathspatterns matters." The implementation passes!through to gitignore (:424), where order still matters but the containing-directory rule does not apply the same way. Measured:A consumer writing an Actions-valid exclusion gets a pattern that silently does not exclude. On a repo where this check is required, that direction is at least fail-safe (more reviews, not fewer) — but it is not what the consumer wrote.
2. Character classes.
[0-9]is undocumented for Actionspaths:(the docs describe only*and**) but is fully supported by gitignore. Measured:src/file[0-9].tsmatchessrc/file1.tsand notsrc/fileA.ts. A consumer could rely on behavior the documented spec does not promise.3. Trailing-slash directory patterns.
scripts/is a gitignore directory-match idiom; Actions' docs do not address it. Measured as matchingscripts/deploy.sh.4.
?is listed for Actions branch/tag filters but omitted from thepaths:description; gitignore supports it.Live exposure: none today
melodic-software/claude-code-pluginsships the tuned paths file — 26 pattern entries (31 lines including 5 comment/blank). Checked every entry against all four divergent classes:Every entry is an ordinary
*/**glob, where the two specifications agree. So this is a documentation defect, not a live behavior defect — but the gap is one!line away from becoming one in the repo where the check is required.Suggested fix
Correct the input descriptions to describe what is actually implemented. Options, in preference order:
!negation does not behave as Actions documents it, and drop the "Actionspaths:syntax" claim. Cheapest and honest.changesjob on a!line rather than silently mismatching. Fail-closed on a spec the implementation cannot honor.paths:semantics. Most faithful, most work; probably not worth it given zero live usage of the divergent set.Recommend (1) plus (2): document reality, and make the one materially divergent class loud instead of silent.
Related
No linked issue.
🤖 Generated with Claude Code
https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb