Detector spec (from hypatia#333 Pattern 4)
Pattern 4 — Workflow triggers reference branches that don't exist in the repo
Severity: low (dead config, but signals maintenance drift)
Detection (Hypatia.Rules.StructuralDrift):
- For each workflow file, collect
on.push.branches, on.pull_request.branches, on.push.branches-ignore, etc.
- Resolve them against the repo's actual branch list (via the git index or GitHub API).
- Flag any branch name that:
- Doesn't exist as a ref, AND
- Isn't a glob pattern, AND
- Isn't the default branch name (some teams keep
master/main for migration symmetry — exempt those).
Worked examples (this session):
affinescript/.github/workflows/{semgrep,spark-theatre-gate}.yml — branches: [main, master] while the repo uses main exclusively. Fix: affinescript#379 dropped master.
hypatia/.github/workflows/{tests,verify-proofs}.yml — develop and master were dead. Fix: hypatia#331 dropped them.
Remediation guidance to emit:
Drop branch refs in workflow triggers that don't resolve to actual branches in this repo. Confirms intent and avoids ambiguity.
Implementation pointers
- Detection algorithm: Collect
on.push.branches, on.pull_request.branches, on.push.branches-ignore per workflow; resolve against actual repo branches; flag names that aren't refs, aren't globs, and aren't the default branch.
- Real-world example:
affinescript/.github/workflows/{semgrep,spark-theatre-gate}.yml listed master while repo uses main only; hypatia/.github/workflows/{tests,verify-proofs}.yml listed dead develop and master.
- Landed fix (reference): affinescript#379 (dropped
master in affinescript); hypatia#331 (dropped develop/master in hypatia).
- Rule statement: Drop branch refs in workflow triggers that don't resolve to actual branches in this repo. Confirms intent and avoids ambiguity.
Acceptance
Source cohort: hypatia#333.
Detector spec (from hypatia#333 Pattern 4)
Pattern 4 — Workflow triggers reference branches that don't exist in the repo
Severity: low (dead config, but signals maintenance drift)
Detection (
Hypatia.Rules.StructuralDrift):on.push.branches,on.pull_request.branches,on.push.branches-ignore, etc.master/mainfor migration symmetry — exempt those).Worked examples (this session):
affinescript/.github/workflows/{semgrep,spark-theatre-gate}.yml—branches: [main, master]while the repo usesmainexclusively. Fix: affinescript#379 droppedmaster.hypatia/.github/workflows/{tests,verify-proofs}.yml—developandmasterwere dead. Fix: hypatia#331 dropped them.Remediation guidance to emit:
Implementation pointers
on.push.branches,on.pull_request.branches,on.push.branches-ignoreper workflow; resolve against actual repo branches; flag names that aren't refs, aren't globs, and aren't the default branch.affinescript/.github/workflows/{semgrep,spark-theatre-gate}.ymllistedmasterwhile repo usesmainonly;hypatia/.github/workflows/{tests,verify-proofs}.ymllisted deaddevelopandmaster.masterin affinescript); hypatia#331 (droppeddevelop/masterin hypatia).Acceptance
lib/rules/<name>.exif Elixir, or matching the repo's rule DSL)Source cohort: hypatia#333.