feat(classify): fail closed on bracket patterns in risk-paths.yml#107
Merged
Conversation
A '[' in a minimatch pattern is a character class, never a literal bracket — so a SvelteKit-style 'src/routes/[id]/...' entry silently matches nothing, and the CODEOWNERS mirror dies too (GitHub ignores bracket lines). wxa-jake-ai's stream gate was a no-op for six weeks this way (wxa-jake-ai#783). classify.mjs now rejects any bracket in blocked/sensitive/safe_*/trivial AND always_review (consumed by the permissive-by-design codex-gate.mjs, so this is its only fail-closed pass) with an actionable rewrite hint. New selftest bakes in the guard, the '*' rewrite equivalence, and the intentional char-class ban; wired into test_workflow_guards.py so CI enforces it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex reviewno regressions found |
|
Coverage Floor — mode:
|
|
No issues found. Bracket guard logic is correct, always_review coverage is justified, and the 5-case selftest covers the new branches. |
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.
Why
wxa-jake-ai#783 (2026-07-04) uncovered a whole bug class: a
[in a risk-paths pattern is a minimatch character class, never a literal bracket, so a SvelteKit-style entry likesrc/routes/api/chat/[id]/stream/+server.tssilently matches nothing — and GitHub CODEOWNERS drops any line containing brackets, so the lockstep mirror dies the same way. That repo's stream gate was a silent no-op from 2026-05-24 to 2026-07-04.Fleet audit (2026-07-04, same day)
All 43 repos using the pr-classify pipeline were scanned (
.github/risk-paths.yml+ CODEOWNERS, every non-comment line containing[):sensitive: []— benign empty YAML flow arrays, not patterns~/.claude/templates/ci-workflows/) — cleanalways_review:lists includedSo this guard can merge without breaking any caller except wxa-jake-ai (see merge order).
What
classify.mjsnow fails closed on any pattern containing[or]acrossblocked/sensitive/safe_*/trivialandalways_review— the latter becausecodex-gate.mjsconsumes it with the same minimatch semantics but is deliberately permissive on config errors, so this pass is the only fail-closed check that list gets before a dead entry silently skips a required Codex review. The error message gives the concrete rewrite (*for a dynamic segment, parent**glob) and tells the author to mirror the fix into CODEOWNERS.Intentional strictness: legitimate character classes like
*.[jt]sare banned too, with an "enumerate as*.js+*.ts" hint.[id]is syntactically a valid char class, so no check can tell intent apart — a heuristic that guesses wrong recreates the silent dead gate. The fleet audit found zero real char-class uses, so strictness costs nothing today.New
selftest/test_classify_bracket_guard.sh(wired intotest_workflow_guards.py, souv run pytestenforces it on this repo's PRs) bakes in:*rewrite classifies the literal[id]path assensitiveunder the classifier's exact minimatch options (the #783 fix, verified)always_review:fails closedpr-classify.ymlfetchesclassify.mjsfrom this repo's main at run time, so the guard is live fleet-wide the moment this merges. wxa-jake-ai's main still has the bracketed entry until wxa-jake-ai#783 merges — merge #783 first, or every wxa-jake-ai PR'sclassify / Classify PR Riskrequired check goes red (loud, not dangerous, but blocks that repo).Validation
bash selftest/test_classify_bracket_guard.sh— 5/5 ✓uv run pytest -q— 7 passed (new selftest runs via the wrapper)ruff check+ruff format --check— cleanAuto-merge rationale: manual-merge by definition — PRs to ci-workflows are always manual, and this touches the classifier itself (blocked-class fleet-wide surface).
Codex rounds: 4 — R1 char-class false-positive concern → addressed via explicit enumeration hint + selftest case 4; R2
always_reviewcoverage gap → guard extended + selftest case 5; R3, R4 clean.🤖 Generated with Claude Code