Consolidate accreted CI gates, ratchets, and hooks into one required aggregator check. Use after audit-gate-logic, or when "clean up our CI checks", "we have three lint jobs", or "make one quality gate". Pipeline cost → audit-cicd.
Installs into .claude/skills of the current project.
Are you the author of Housekeep Gates?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/kensaurus-housekeep-gates)
---
name: housekeep-gates
description: >
Consolidate accreted CI gates, ratchets, and hooks into one required
aggregator check. Use after audit-gate-logic, or when "clean up our CI
checks", "we have three lint jobs", or "make one quality gate". Pipeline
cost → audit-cicd.
license: MIT
effort: high
---
# housekeep-gates — One required check, defined in code
**Degree of freedom: MIXED — T1 is the priority.** Mapping and winner
confirmation `[HIGH freedom]`; aggregator `needs:` + skip-fail, loser
**deletion**, branch-protection, and Phase 4 probes `[LOW freedom — run
exactly]`. **Net enforcement strictly ≥ before.**
Apply-now. `audit-gate-logic` mapped the sprawl and named a winner per
duplicate cluster; this makes the repo match the map. **Target state: one
required check in branch protection, backed by an aggregator job that
`needs:` every real gate — so "what blocks merge" has exactly one answer,
in one file.**
Every retirement is proven safe before deletion. Never weaken enforcement
while consolidating. **Net enforcement strictly ≥ before.**
This is the execution arm of `audit-gate-logic`, the same way
`housekeep-design` executes `plan-uiux-unification`.
## This skill vs neighbors
| Skill | Owns |
|---|---|
| **housekeep-gates** (this) | Consolidate / delete / wire the aggregator |
| `audit-gate-logic` | Read-only map — bypass, ratchets, archaeology |
| `audit-cicd` | Pipeline cost, speed, storage |
| `workflow-quality-gate` | *Runs* the pre-release sweep |
| `enhance-agent-guardrails` | *Installs* new guard classes, not accreted-gate cleanup |
| `workflow-green-repo` | Make the repo green — does not redesign the gate graph |
| `housekeep-backlog` | Parked-work register — not CI gates |
| `housekeep-dead-code` | Owns the dead-code `--max-issues` ratchet that joins this aggregator |
## How to reason
1. **Observe** — archaeology map: cluster, winner, loser, current required checks
2. **Interpret** — would deleting this loser drop unique coverage?
3. **Classify** — port-then-delete / keep / not-yet-mapped
4. **Severity** — deleting the only server-side-enforced gate is a regression
## Worked example
> **Observe:** two lint jobs; old `lint.yml` is required; new `ci.yml` lint
> is not. `paths:` skips `.github/**`.
> **Interpret:** retiring `lint.yml` without porting + aggregator skip-fail
> would let a workflow edit merge unlinted.
> **Classify:** port unique rules, wire winner, delete loser; probe the skip path.
> **Probe:** change matching the old skip filter → aggregator must go red, not skip-green.
---
## Phase 0 — Load the map (or make a quick one) [HIGH freedom; confirm before edits]
Consume the archaeology map from `audit-gate-logic` (duplicate clusters,
winners, losers, ratchet findings). If none exists, run its Phase 0 + 2.5
first — never consolidate gates you have not mapped. Deleting an "obvious
duplicate" that was the only server-side-enforced one *weakens* the repo.
Confirm which winners stand **before touching anything**. Branch-protection
changes always get explicit confirmation.
---
## Phase 1 — Build the aggregator gate [LOW freedom — fail on failed or skipped]
Create (or adopt) the single-gate structure:
- One job — conventionally `gate` / `all-green` — that `needs:` every real
check (lint, typecheck, tests, build, and the audit jobs worth blocking
on) and fails if any dependency failed **or was skipped** (`if: always()`
+ explicit result check). Skipped-counts-as-success is the classic
aggregator bug.
- Make that job the **only** required status check; remove scattered
required entries. Enforcement moves from a settings list nobody audits
to a `needs:` list that lives in code review.
- If a merge queue is (or will be) in use: add `merge_group` to the
workflow triggers and keep check names identical across `pull_request`
and `merge_group`, or queued PRs wait on checks that never start.
- Keep one fast always-runs job as the visible status link if the
aggregator's `needs:` delays its appearance.
---
## Phase 2 — Consolidate per the map [LOW freedom — delete losers, do not disable]
For each duplicate cluster, in this order:
1. **Port unique value** from losers into the winner (a lint rule only the
old config had, a test only the old workflow ran). Diff configs before
deleting — retirement must not drop coverage.
2. **Wire the winner** into the aggregator's `needs:`.
3. **Retire the losers** — delete the workflow/config, do not
disable-and-keep. A disabled gate is comprehension debt and resurrection
bait for the next agent session.
4. **Extract shared logic** into a reusable workflow or composite action
when the same steps exist in 2+ places.
Also restore **hook/CI parity**: local pre-commit / pre-push hooks run the
same commands (ideally the same scripts) as CI's gate jobs — one
definition, two callers — so "passes locally" predicts "passes CI" and
nobody learns `--no-verify`.
---
## Phase 3 — Normalize the ratchets [HIGH freedom]
- **One baseline per metric.** Competing thresholds (jest + codecov + a
committed floor) collapse to one enforcement point; the rest read from
it or die.
- **Auto-tighten upward.** Floors track reality (threshold rises as the
metric improves, small flake tolerance) instead of sitting far below
actual.
- **Resets are reviewed, separate acts.** Baseline regeneration happens in
its own PR with the diff visible. CI should fail if a baseline file
changes in the same PR as a metric regression, where the tooling allows
expressing that.
- **No silent exemption growth.** Ignore-lists and inline-disable counts
become ratcheted metrics themselves where feasible.
---
## Phase 4 — Prove it [LOW freedom — run both probes]
- **Deliberate-violation probe** on a scratch branch: one violation per
consolidated gate class (lint error, failing test, coverage drop,
baseline edit bundled with a regression). Confirm the aggregator goes
red and the PR is unmergeable. A consolidation is not done until each
retired loser's job is demonstrably covered by the winner.
- **Skip-path probe:** push a change matching any `paths:` filter that
previously skipped gates; confirm the aggregator still reports
(skipped-as-success is the failure mode to disprove).
- Run a `workflow-green-repo`-style full pass to confirm the repo is green
under the new single gate.
---
## Definition of Done
- [ ] Archaeology map confirmed with the user before edits
- [ ] Aggregator job exists, fails on any failed **or skipped** dependency, and is the only required check
- [ ] `merge_group` parity in place if a merge queue is used
- [ ] Every cluster: unique value ported, winner wired, losers deleted (not disabled)
- [ ] Shared steps extracted to one reusable definition; hooks and CI call the same commands
- [ ] One baseline per metric; ratchets auto-tighten; resets require a separate reviewed PR; exemption lists cannot silently grow
- [ ] Deliberate-violation + skip-path probes red as expected; repo green under the new gate
- [ ] Net enforcement strictly ≥ before
## Self-critique before claiming done [LOW freedom — do not skip]
1. **Map confirmed** — no "obvious duplicate" deleted unmapped
2. **Skipped ≠ success** — aggregator checks dependency results
3. **Losers deleted** — not disabled
4. **Probes red** — violation + skip-path
5. **Branch protection** — only after explicit confirmation
## Output format
1. **Before/after map** — old gates → new structure (aggregator + needs list)
2. **Per-cluster log** — winner | value ported from losers | losers deleted
3. **Ratchet policy** — metric | enforcement point | floor behavior | reset rule
4. **Probe evidence** — violation | gate response | pass/fail
Applied phase-by-phase. Pause for approval between phases when blast
radius is large. Branch-protection changes always get explicit confirmation.