🐛 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.shtmb_normalize_role() using ${1#*:} parameter expansion (strips tmb: / 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:swe dual-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.

Merge request reports

Loading