🐛 fix(hooks): normalize plugin-prefixed role names in 7 hooks + lint
Resolves GL !2925 (local #38) — P1.
Problem
CC's Skill tool input and subagent_type fields can carry a plugin prefix (tmb: or tmb-rc:). 5 plugin hooks compared these against bare role names only, silently skipping on prefixed input. 4 of 5 are safety gates being silently disabled:
| Hook | Risk |
|---|---|
pr-reviewer-no-worktree.sh |
pr-reviewer could spawn with worktree |
require-task-spec.sh |
SWE could spawn without task_id |
require-feature-branch-active.sh |
SWE could spawn off wrong branch |
pr-reviewer-spawn-prompt-shape.sh (Phase B new) |
malformed pr-reviewer spawn prompts pass |
skill-invocation-record.sh |
analytics — caused L6 step 14 fail |
Fix
- New helper
scripts/hooks/lib/normalize-role.sh—tmb_normalize_role()using${1#*:}parameter expansion (stripstmb:/tmb-rc:prefix). - 5 affected hooks source helper + normalize before compare.
- 2 already-correct hooks (
swe-atomic-close.sh,git-push-guard.sh) migrated from\|\| tmb:swedual-check to helper — semantic-equivalent, less drift surface. - New lint
tests/lint/no-bare-role-compare.sh+ fixture — prevents regression. - New L3 test
tests/hooks/normalize-role-prefix.test.sh— verifies both raw + prefixed forms fire gates identically.
Verification
bash tests/run-all.sh exit 0 on this branch. L1 26 lints PASS, L2 423/0, L3 hook tests 55/0 + 6 new assertions PASS, L4 all 5 flows PASS.
Why this wasn't caught
Existing no-hardcoded-plugin-name.sh catches ="tmb" style hardcodes in path construction; it didn't cover role-comparison patterns missing the tmb: variant. The new no-bare-role-compare.sh closes that gap structurally.
pr-reviewer verdict: PASS (validation_attempts id=21).
Held for manual review per Human request — no auto-merge.