Context
Hypatia.ScorecardReconciler (lib/scorecard_reconciler.ex) plus mix hypatia.reconcile <owner/repo> (lib/mix/tasks/hypatia.reconcile.ex) already implement the full Sense → Classify → Act → Verify → Learn loop that closes GitHub code-scanning alerts (design: #260, #263, #265). The classification core is pure and unit-tested; the write-back dismisses non-actionable findings with rationale and records each by stable fingerprint in ~/.git-private-farm/hypatia-exception-registry.json, so a class adjudicated once is never re-reasoned.
The gap is operational, not code. Nothing runs it on a schedule against the estate. So OpenSSF Scorecard's MaintainedID (the "scorecard staleness" signal) and the other non-actionable / design-exception findings re-accumulate as OPEN on every audit — exactly the recurrence + weekly credit-burn #260 describes. The instrument exists; it just isn't wired to run.
What it would do (grounded in classify/1)
| Scorecard rule |
Action |
MaintainedID, ContributorsID, CITestsID, CIIBestPracticesID |
:dismiss_info — "won't fix" + rationale (activity signal — this is scorecard staleness) |
PinnedDependenciesID (SLSA pin-exempt) |
:dismiss_accept |
SASTID |
:fix (set CodeQL matrix to actions) |
PinnedDependenciesID (other) |
:fix (pin to full SHA) |
BranchProtectionID, CodeReviewID |
:fix_settings (GitHub settings API) |
| else |
:open_escalate (leave open + ensure tracking issue) |
Preconditions to run it
- BEAM host — Elixir/OTP with
mix deps.get (absent from the Claude Code web session; any CI runner or standing host works).
security_events-scoped token (read + write, to dismiss alerts). HYPATIA_DISPATCH_PAT exists but must carry security_events — and administration/repo-settings write only if :fix_settings should auto-apply. Run --conservative to avoid settings mutation entirely.
- Registry persistence — commit
hypatia-exception-registry.json back to .git-private-farm after each run so decisions survive across runs.
Proposed deployment (pick one)
- A — scheduled GitHub Action (in
hypatia or .git-private-farm): weekly cron → erlef/setup-beam → mix deps.get → loop mix hypatia.reconcile <repo> over the estate repo list → commit the registry. Token from secrets. Simplest; no standing host.
- B — existing hypatia host: fold a periodic reconcile pass into the OTP
learning_scheduler (already polls every 5 min) or a dedicated cron GenServer.
Safe rollout
mix hypatia.reconcile <repo> --dry-run across all repos → review the classification (no mutation).
--conservative → dismiss non-actionable findings, escalate settings findings (no automatic branch-protection changes).
- Full-auto once trusted. Run
--verify periodically to catch recurrence defects (reopened-with-no-change).
Acceptance
MaintainedID / staleness + design-exception alerts are dismissed-with-rationale once and stop re-opening across audits.
- The registry records every decision by fingerprint (no re-reasoning, no re-billing).
- Weekly credit-burn from re-accumulated non-actionable findings drops to ~0.
References
Filed as an actionable deployment hand-back. The reconciler code is done and merged; this issue tracks only wiring it to run. Raised from a session that confirmed the task cannot run in the ephemeral web environment (no BEAM; the available token is a proxy placeholder without security_events).
Context
Hypatia.ScorecardReconciler(lib/scorecard_reconciler.ex) plusmix hypatia.reconcile <owner/repo>(lib/mix/tasks/hypatia.reconcile.ex) already implement the full Sense → Classify → Act → Verify → Learn loop that closes GitHub code-scanning alerts (design: #260, #263, #265). The classification core is pure and unit-tested; the write-back dismisses non-actionable findings with rationale and records each by stable fingerprint in~/.git-private-farm/hypatia-exception-registry.json, so a class adjudicated once is never re-reasoned.The gap is operational, not code. Nothing runs it on a schedule against the estate. So OpenSSF Scorecard's
MaintainedID(the "scorecard staleness" signal) and the other non-actionable / design-exception findings re-accumulate as OPEN on every audit — exactly the recurrence + weekly credit-burn #260 describes. The instrument exists; it just isn't wired to run.What it would do (grounded in
classify/1)MaintainedID,ContributorsID,CITestsID,CIIBestPracticesID:dismiss_info— "won't fix" + rationale (activity signal — this is scorecard staleness)PinnedDependenciesID(SLSA pin-exempt):dismiss_acceptSASTID:fix(set CodeQL matrix toactions)PinnedDependenciesID(other):fix(pin to full SHA)BranchProtectionID,CodeReviewID:fix_settings(GitHub settings API):open_escalate(leave open + ensure tracking issue)Preconditions to run it
mix deps.get(absent from the Claude Code web session; any CI runner or standing host works).security_events-scoped token (read + write, to dismiss alerts).HYPATIA_DISPATCH_PATexists but must carrysecurity_events— andadministration/repo-settings write only if:fix_settingsshould auto-apply. Run--conservativeto avoid settings mutation entirely.hypatia-exception-registry.jsonback to.git-private-farmafter each run so decisions survive across runs.Proposed deployment (pick one)
hypatiaor.git-private-farm): weekly cron →erlef/setup-beam→mix deps.get→ loopmix hypatia.reconcile <repo>over the estate repo list → commit the registry. Token fromsecrets. Simplest; no standing host.learning_scheduler(already polls every 5 min) or a dedicated cron GenServer.Safe rollout
mix hypatia.reconcile <repo> --dry-runacross all repos → review the classification (no mutation).--conservative→ dismiss non-actionable findings, escalate settings findings (no automatic branch-protection changes).--verifyperiodically to catch recurrence defects (reopened-with-no-change).Acceptance
MaintainedID/ staleness + design-exception alerts are dismissed-with-rationale once and stop re-opening across audits.References
lib/scorecard_reconciler.ex· task:lib/mix/tasks/hypatia.reconcile.ex:fix_settingspolicy)Filed as an actionable deployment hand-back. The reconciler code is done and merged; this issue tracks only wiring it to run. Raised from a session that confirmed the task cannot run in the ephemeral web environment (no BEAM; the available token is a proxy placeholder without
security_events).