Skip to content

Design: feedback channel from cicd-squabbler's diagnosed gate-deadlock classes to hypatia's ruleset #567

Description

@hyperpolymath

Summary

Design proposal for a feedback loop: cicd-squabblerhypatia. Raised while stewarding the estate's stuck-PR backlog (2026-07-01) and extending squabbler with real gh-backed gate-fetching (hyperpolymath/cicd-squabbler#6).

The gap: squabbler's Move enum now models several recognized gate-deadlock classes (ReconcileRequiredContext, LicencePolicyDrift, FlagNonFunctionalGate, etc.) discovered by resolving real stuck PRs. Some of these classes are genuinely things hypatia's scanner could catch proactively — before they ever produce a stuck gate — if it had a rule for them. Right now there's no channel from "squabbler diagnosed this class of problem on a real PR" to "hypatia's Elixir ruleset gained a rule for it."

Concrete example that motivated this: Move::FlagNonFunctionalGate was mined from hypatia#566 (a required CI check whose script never consulted its own exemption file, so it failed unconditionally). A hypatia rule that flags "a required-status-check script with no code path reading its own baseline/exemption file" would catch the next instance of this class before it reaches a stuck PR. Today, that connection only exists in a human's head (mine, right now) — nothing structural carries it from squabbler's diagnosis to hypatia's rule authors.

Where this would land in hypatia (confirmed by reading the code)

lib/rules/rules.ex (Hypatia.Rules) is the registration facade — it aliases and calls specific rule submodules from scan_file. There's already a live precedent for deferred wiring: # alias Hypatia.Rules.ResearchExtensions # wired in follow-up after PR #325 merges. A squabbler-sourced candidate would follow the same pattern: land as a new module under lib/rules/, then get aliased + wired into scan_file once reviewed — a human decision each time, not an automated edit.

Proposed shape (not implemented — this issue is the design proposal)

  1. Emission side (squabbler, cheap to add): when a Diagnosis resolves to a "systemic, hypatia-catchable" class (starting with FlagNonFunctionalGate and LicencePolicyDrift's HeaderDrift), the host (squabble-cli/squabble-app) emits a structured "rule candidate" record: {move_kind, repo, pr, evidence, suggested_pattern}. Where this record goes (a file, a hypatia-facing issue via gh issue create, an API call) is an open question — simplest v0 is probably just filing/updating a tracking issue here, mirroring what I'm doing manually right now.
  2. Consumption side (hypatia, this repo, needs a maintainer decision): a lightweight ingestion point — could be as simple as "candidates land as issues labelled from:squabbler, a human triages into a real rule module + wires it into rules.ex" — no auto-authored Elixir, ever. This mirrors the existing ResearchExtensions-deferred pattern exactly.

Why not build the automated version

Auto-writing Elixir rule modules from squabbler's output would be exactly the kind of "fan-out without fan-in, but now the fan-in is also unreviewed" mistake the estate's own 2026-06-24 issue-backlog RCA flagged as the master cause of the 564-issue backlog (project_estate_issue_backlog_rca_2026_06_24). The value here is connecting two systems that don't currently talk to each other; the judgment about whether a given pattern is worth a permanent rule stays human.

Next step

Owner decision needed on: where candidate records land (issue vs. file vs. something else), and whether squabble-cli/squabble-app is the right place to emit them (vs. e.g. squabble-core gaining an optional "escalation" outcome type). Not blocking anything — this is a proposal, not a P0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cicdCI/CD: workflows, actions, lockfiles, pins, runners, release gatesdesignDesign of an interface, protocol, grammar, or type theoryenhancementNew capability or improvement to existing behaviour

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions