Skip to content

Claude/charming hamilton 2n l pc#311

Merged
hyperpolymath merged 14 commits into
mainfrom
claude/charming-hamilton-2nLPc
May 24, 2026
Merged

Claude/charming hamilton 2n l pc#311
hyperpolymath merged 14 commits into
mainfrom
claude/charming-hamilton-2nLPc

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

No description provided.

claude and others added 14 commits May 23, 2026 18:57
Recipe matcher rejected every scorecard-source finding (~310 ecosystem-
wide), routing them to :control "no safe fix available" advisories.

Root cause: `lib/recipe_matcher.ex` filtered candidate recipes with
`"*" in langs or language in langs`. Two failure modes:

  1. 12 recipes declared `languages: ["any"]` — never matched, since
     `"any"` is not a sentinel the filter recognises and no repo has
     `"any"` as its primary language.
  2. 8 scorecard / workflow-file recipes declared `languages: ["yaml"]`
     — never matched, since yaml is a workflow-file type, not any
     repo's primary language. So `recipe-pin-dependencies`,
     `recipe-fix-workflow-permissions`, etc. were unreachable for SC013/
     SC018 findings — the exact rule families dominating the daily
     remediation sweep.

Fix:

  - `langs_match?/2` private helper accepts `"*"` and `"any"` as
    synonymous language-agnostic sentinels.
  - `effective_language_for/2` remaps the lookup language to `"yaml"`
    for patterns whose `source` is `"scorecard"` or whose `category`
    names a known workflow-file rule family (DependencyPinning,
    TokenPermissions, DangerousWorkflow, etc.). The repo's primary
    language is irrelevant for workflow-file findings.
  - Applied to `best_recipe/2`, `category_match_recipe/2`, and
    `fuzzy_match_recipe/2`.

Tests pin all three invariants. All 22 scorecard recipe `fix_script`
references already exist on disk in `scripts/fix-scripts/` — the bug
was purely in matcher reachability, not missing fix implementations.

Closes the dispatcher half of the "no security stuff being sorted"
symptom. Remaining M7 work (PAT for cross-repo dispatch, push fixes
to remotes) still needs operator action, but the manifests will now
carry populated fix_script fields for scorecard findings.
The baseline had drifted into pure historical risk: 71 accepted findings
(31 critical, 40 high) generated before the #278 stale-escript fix and
the wave of code_safety/security_errors cleanups landed.

A fresh scan against the current tree finds 35 findings, all
medium-or-lower:
  - 32 low (code_safety hot-path expects, ncl_docker_not_podman,
    workflow_audit missing-workflow, structural_drift, etc.)
  - 3 medium (git_state transient + structural_drift)
  - 0 critical, 0 high

Most old baseline entries are either:
  - fixed in code (e.g. the believe_me at src/abi/RuleEngine.idr is now
    inline-suppressed with a documented `-- hypatia: allow` directive),
  - migrated/refactored (e.g. lib/direct_github_pr.ex no longer exists),
  - or were covered by the new total-Python-ban / scanner-soundness wave.

Net effect: every gate threshold of "fail on critical|high above
baseline" now starts from an empty critical/high ledger — net-new
critical or high findings will stand out, which is what the baseline is
supposed to enable.

Generated with the canonical Elixir escript pipeline against this tree
(no rule changes, just a snapshot refresh). Severity threshold "low" so
the snapshot reflects the full advisory surface, not just gates.
The HYPATIA_DISPATCH_PAT was provisioned with read access to
secret-scanning alerts, code-scanning alerts, and Dependabot alerts.
Only Dependabot was actually being consumed (lib/rules/dependabot_alerts.ex,
DA001-DA004) — the other two alert surfaces were granted but unused.

Adds two new rule modules mirroring the DependabotAlerts shape:

  lib/rules/secret_scanning_alerts.ex (SSA001-SSA004)
    SSA001 — Open leaked-secret alerts (always :critical; staleness
             surfaced in the reason for triage prioritisation).
    SSA002 — Repo-level meta-finding when any open alert exists.
    SSA003 — Stale open alerts past the 7-day rotation threshold.
    SSA004 — Resolved alerts with no documented resolution vocabulary
             (anything outside revoked/used_in_tests/pattern_deleted/
             pattern_edited).

  lib/rules/code_scanning_alerts.ex (CSA001-CSA004)
    CSA001 — Open code-scanning alerts (CodeQL + third-party SARIF
             including Hypatia's own `hypatia` category). Severity
             mapped from `security_severity_level`/`severity` onto the
             canonical four-bucket scale.
    CSA002 — Severity summary (any critical, ≥5 high, or ≥10 total).
    CSA003 — Stale open alerts (3/7/30/90 days by severity bucket).
    CSA004 — Dismissed without documented reason.

Wires both into `Hypatia.CLI`:
  - registered in `@all_rule_modules` so the default scan includes them,
  - scan blocks emit normalised findings alongside the rest,
  - `format_module_name/1` gives them display names,
  - usage strings updated to list the new --rules tokens.

Workflow comment in `.github/workflows/hypatia-scan.yml` updated to
note that the existing `security-events: write` grant now covers all
three alert APIs, not just Dependabot. No new permissions needed.

Tests pin token-absent behaviour and the non-GitHub-remote error path
for each module's helpers.
PR #278 documented that the deployed escript had been silently dropping
the Elixir/Erlang/Coq/Lean/Agda/Zig/F*/Ada code_safety pattern families
for days because the binary was stale relative to the rule sources.
"No findings" looks identical whether the code is clean or the rule is
broken — that ambiguity is the soundness gap.

Closes it with the simplest possible mechanism: for every rule the
scanner is supposed to detect, keep a known-bad sample on disk, and
assert in CI that the rule fires on its sample at the expected severity.
A rule that goes silent (regex drift, file pruning, packaging
regression, module rename) breaks the build instead of silently
weakening the estate's security posture.

Layout:
  test/soundness/
    manifest.json                         -- rule -> fixture -> severity
    fixtures/code_safety/
      believe_me.idr                      -- Idris2
      sorry.lean                          -- Lean
      admitted.v                          -- Coq
      unsafe_coerce.hs                    -- Haskell
      obj_magic_ocaml.ml                  -- OCaml
      getexn_on_external.res              -- ReScript
      unwrap_without_check.rs             -- Rust
      transmute.rs                        -- Rust unsafe
      elixir_system_shell.ex              -- THE PR#278 false-negative
      elixir_os_cmd.ex                    -- Elixir os.cmd
      elixir_code_eval.ex                 -- Elixir Code.eval
      shell_download_then_run.sh          -- curl|bash
      agda_postulate.agda                 -- Agda
      zig_ptr_cast.zig                    -- Zig
    README.adoc                           -- how to add a fixture

  test/soundness_test.exs                 -- runner, @moduletag :soundness

Manifest entries cover all the language families PR #278 specifically
called out as having been silently dropped. The runner is data-driven:
adding a rule means dropping a fixture + a manifest entry, no test code
change.

Hand-run against the current tree: 14/14 fixtures fire at the expected
severity. The soundness gate is operational.

Out of scope (next iteration):
  - End-to-end escript-build soundness (build the escript, run it
    against the fixture corpus -- exact PR #278 reproduction). The
    in-process test catches rule-definition regressions, but a
    packaging regression that strips a module would still slip
    through.
  - Fixtures for non-code_safety families (workflow_audit, cicd_rules,
    structural_drift, scorecard, dependabot_alerts, ...).
The OutcomeTracker.verify_fix/3 re-scan mechanism existed but its result
was discarded on the success path: clean re-scans produced no marker,
unclean re-scans were re-recorded as :false_positive without preserving
the "this was verification, not an organic failure" distinction. The
outcomes log had no way to answer "what fraction of this recipe's
'successes' were actually verified clean by post-fix re-scan?"

That's the closed-loop metric this commit adds.

  lib/outcome_tracker.ex
    record_outcome/4,5
      Optional `metadata` map merges into the record (under the canonical
      fields so a caller can't overwrite recipe_id/repo/file/outcome/
      timestamp/bot by accident).
    record_and_verify/5
      Now persists the verification verdict on every branch:
        verified       -> success record with "verification" = "verified"
        still_present  -> success record with "verification" = "still_present"
                          PLUS a follow-up :false_positive record
                          (caused_by = "post_fix_rescan")
        scan_failed    -> success record with "verification" = "scan_failed"
        verify: false  -> outcome record with "verification" = "unverified"
      The distinction between "scan_failed" and "unverified" matters: a
      recipe is not penalised for being run in environments without
      panic-attack.
    verification_rate/2
      For a recipe_id, returns counts {verified, still_present,
      scan_failed, unverified} and a rate = verified / (verified +
      still_present). scan_failed and unverified records are excluded
      from the denominator so a low-verification-attempt environment
      doesn't artificially deflate the rate. Returns :insufficient_data
      below min_attempts.
    recipe_health/1
      Aggregates across every recipe with recorded outcomes. Returns a
      list of maps with dispatches / successes / failures / FPs /
      success_rate / verification breakdown / status, sorted so the
      most actionable rows (quarantine_candidate, degraded) surface
      first. Configurable thresholds.

  lib/mix/tasks/hypatia.recipe_health.ex
    mix hypatia.recipe_health [--format json] [--only-actionable]
    Prints the report in a human-readable table or JSON.

  test/recipe_health_test.exs
    Pins the rate calculation (verified/still_present ratio, scan_failed
    + unverified excluded), the insufficient_data threshold, and the
    healthy/degraded/quarantine_candidate status mapping.

Hand-run against the current outcomes log: 4 recipes found, all flagged
:insufficient_data because the historical log was written before the
verification marker existed. From the next `record_and_verify`-enabled
dispatch onwards, recipes will accumulate verification data and migrate
to :healthy / :degraded / :quarantine_candidate based on real evidence.
…pper

Closes the gap where verification was opt-in only via record_and_verify
with explicit category/repo_path arguments — too inconvenient for the
gitbot-fleet bash runner to use, which is why the verification metric's
denominator was zero in the hand-run earlier.

  OutcomeTracker.record_outcome_for_fix/4,5
    Auto-derives `category` from the recipe registry (target_categories
    field) and `repo_path` from $HYPATIA_REPOS_DIR/<repo>. Always
    attempts re-scan verification on :success. Falls back gracefully
    to "verification = scan_skipped" if the recipe isn't found, so
    the outcome is still recorded and the gap is auditable.

  mix hypatia.record_outcome
    CLI wrapper for the bash dispatch-runner. Exit-code contract:
      0 — recorded, verified-clean or scan_unavailable
      2 — recorded but re-scan still finds the weak point (the runner
          SHOULD treat the batch as failed and consider rollback)
      1 — bad arguments / unrecoverable error
    The non-zero exit on still_present is what lets bash notice a
    false-fix without parsing JSON.

In-tree callers of record_outcome/4,5 stay as-is: learning_scheduler's
fleet-outcome replay and batch_rollback's mark-as-false-positive are
both legitimately unverified paths. The new entry point is for the
external runner; the docstring on record_and_verify now points to it.
Adds end-to-end packaging soundness — the half of the PR #278 fix the
earlier in-process gate didn't cover. Builds the escript fresh, scans
the fixtures tree with the built binary, asserts every manifest entry
fires at the expected severity. Catches the failure mode where rule
sources are correct but the escript build/packaging drops them.

The gate caught a latent bug on its first end-to-end run:

  lib/hypatia/cli.ex:726 @language_extensions was missing .agda, .zig,
  .thy, .fst/.fsti, .adb/.ads. The patterns_for_language/1 dispatcher
  in lib/rules/code_safety.ex defines rules for agda/zig/isabelle/
  fstar/ada — but because the CLI's directory walker had no
  extension-to-language mapping for those files, the rules NEVER FIRED
  on real scans. This is the exact PR #278 class: the engine knows
  the rule, the rule's regex works, but the wiring never gives the
  rule any input.

  Added all 7 missing language → extension mappings. Re-run shows
  agda_postulate and zig_ptr_cast (previously silent) now flag at
  the expected severity.

  test/soundness/run-escript-soundness.sh
    Builds escript, scans test/soundness/fixtures, asserts every
    manifest entry fires. Exits non-zero on the first regression with
    a "PR #278 class" diagnostic message pointing the reader at the
    escript build (mix.exs:escript, hypatia-cli.sh).

  .github/workflows/tests.yml
    New step "Escript packaging soundness" on the e2e-elixir job,
    after the existing E2E test suite. Re-uses the same setup-beam
    + cache + deps.get, no extra runtime cost beyond the escript
    build.

Hand-run on the current tree: 14/14 fixtures fire at expected severity
on the built escript.
Closes the third half of the closed-loop safety net: a recipe whose
post-fix re-scans are failing too often can no longer ship to repos
in :auto_execute mode. The dispatcher now consults the verification
rate before auto-executing and downgrades to :review (rhodibot PR for
human inspection) if the recipe is unhealthy.

  OutcomeTracker.quarantined?/2
    Cheap predicate derived from existing verification data — no new
    GenServer or state. Returns true iff:
      - verification rate < :threshold (default 0.30), AND
      - verifiable-outcomes count >= :min_attempts (default 5)
    Honours HYPATIA_RECIPE_QUARANTINE_DISABLE=true env override (logged
    when used so audit history captures the bypass). Recipes with no
    verification data are NOT quarantined — gating them would create a
    chicken-and-egg where new recipes can never accumulate the data
    needed to leave quarantine.

  fleet_dispatcher.ex
    do_eliminate_dispatch(:auto_execute, ...) now checks quarantined?
    before dispatching to robot-repo-automaton. On quarantine, it
    recurses with :review so rhodibot opens a PR instead. The fix gets
    surfaced for human review; it doesn't get suppressed.

  test/recipe_health_test.exs
    Three new tests pin the invariants:
      - quarantined? returns true on rate < 0.30 with >= 5 verifiable
      - quarantined? returns false on insufficient data (chicken-and-egg)
      - HYPATIA_RECIPE_QUARANTINE_DISABLE env override is honoured

Net effect: once `record_outcome_for_fix` is wired into the dispatch-
runner and verification data starts flowing, any recipe that drifts
toward false fixes silently becomes "review-only" without operator
intervention. The bad recipe can't damage the estate while waiting for
the human-review queue.
Investigation for follow-up #4 (soundness fixtures for non-code_safety
families) revealed each rule family has architectural mismatches that
make the "one fixture file per rule" pattern non-portable. Documenting
honestly so future iteration starts from the right premise instead of
re-discovering each blocker.

Findings:

  cicd_rules/banned_language_file
    Unsuppressable by design (PR #280 "total ban, no exceptions"). A
    .py fixture in-tree produces a real critical finding in every
    scan, polluting the baseline. Needs either a policy carve-out for
    test/soundness/fixtures/ in the unsuppressable clause, or a
    separate scratch-repo scan-root model. Not done in this commit
    because both touch policy invariants that need a deliberate
    decision.

  workflow_audit, structural_drift, root_hygiene
    Operate at scan-root level (look for .github/workflows/, LICENSE,
    SECURITY.md at the root). Whole-tree scanning of the fixtures
    directory can't surface these — workflow_audit doesn't recurse
    into subdirs looking for .github/. Needs per-fixture scan-root.

  git_state, honest_completion
    Transient / git-state dependent — hermetic fixtures are hard.

  dependabot_alerts, secret_scanning_alerts, code_scanning_alerts
    GitHub API queries — no "known-bad sample on disk" model applies.
    Mock-based tests in test/*_alerts_test.exs already cover the
    token-absent + parse paths. Soundness gate doesn't extend here.

README updated with a per-family table and a suggested Phase 2 design
(extend manifest with `scan_root` field, scan per-fixture-directory
instead of one tree-scan). When someone implements that, all the
families above become tractable in one go.

Net effect of #4: no new fixtures, but the README captures the
architectural insight that would otherwise have to be re-discovered.
The code_safety soundness gate from commits 74173ee + 6d40240 remains
operational.
Phase 1, commit 1/4 of the watcher / supervision interface plan.

lib/hypatia/telemetry.ex
  Centralised event-name registry. Eight events covering every
  observable decision in the pipeline:
    :scan, :complete           (duration_ms, finding_count)
    :dispatch, :decision       (strategy, tier, recipe_id, repo)
    :outcome, :recorded        (recipe_id, repo, outcome, verification)
    :verification, :result     (recipe_id, repo, verdict)
    :quarantine, :triggered    (kind, id, reason, level)
    :rate_limit, :exceeded     (bot, scope)
    :neural, :cycle            (duration_ms)
    :soundness, :violation     (rule_module, rule_id)

  Hand-written emit helpers per event (not meta-programmed) so each
  call site shows what it's saying. `:telemetry.execute/3` is wrapped
  in `safe_execute/3` so a missing `:telemetry` module (escript-only
  builds, stripped releases) is a no-op rather than a crash —
  instrumentation must never break the host.

  `all_events/0` exposes the full list so the watcher / Prometheus
  exporter / alerting layer can subscribe via attach_many.

Instrumented sites:
  lib/hypatia/cli.ex          — scan_complete with elapsed time
  lib/fleet_dispatcher.ex     — dispatch_decision on each tier (+
                                quarantine_triggered + downgrade
                                marker when auto_execute degrades
                                to review)
  lib/outcome_tracker.ex      — outcome_recorded on every write,
                                verification_result on every verify_fix,
                                quarantine_triggered when recipe
                                crosses verification-rate threshold

No new runtime dependency: :telemetry is already a transitive dep
of phoenix + bandit. Calls are no-ops when no handler is attached,
so this commit on its own changes nothing about runtime behaviour —
it just makes the upcoming watcher (commit 2/4), JSON API endpoint
(commit 3/4), and `mix hypatia.watch` TUI (commit 4/4) possible.

Refactored OutcomeTracker.verify_fix into a public telemetry-emitting
wrapper around the original `do_verify_fix` private impl, so the
verification event fires regardless of caller and the implementation
stays unchanged.
Phase 1, commit 2/4 of the watcher / supervision interface plan.

lib/hypatia/watcher.ex
  GenServer that subscribes to every event in Hypatia.Telemetry.all_events/0
  and maintains rolling-window counters in ETS so the CLI dashboard /
  JSON API / future alerting layer can read live state without
  re-parsing JSONL.

  Three ETS tables, bucket-keyed for cheap pruning:
    :hypatia_watcher_5m  — 5min window, 5s buckets   (60 buckets)
    :hypatia_watcher_1h  — 1hr window, 1min buckets  (60 buckets)
    :hypatia_watcher_1d  — 1day window, 1hr buckets  (24 buckets)

  Public API:
    Watcher.snapshot/0       full JSON-serialisable state
    Watcher.counts/1         counts per window (cheap ETS read)
    Watcher.recent_events/0  last N events per kind (drilldown)
    Watcher.queue_depths/0   per-GenServer message_queue_len (5s poll)

  Back-pressure: telemetry handler is a captured remote function
  (&__MODULE__.handle_event/4) that casts to the watcher — the
  producer process never pays the watcher's processing cost. If the
  watcher's mailbox exceeds @max_mailbox (1000), incoming events are
  DROPPED and counted as :dropped_events instead of building up.
  This keeps the watcher from becoming a tarpit during sweep storms
  (e.g. the burst of ~20 hypatia-security-alert dispatches we just
  saw in gitbot-fleet).

  Lifecycle: supervised by Hypatia.Application (added at Layer 0.8,
  after the existing Diagnostics.Monitor). ETS tables die with the
  process — live state is ephemeral by design (Phase 1 scope);
  persistence to verisim-data is Phase 3.

lib/application.ex
  Wires Hypatia.Watcher into the supervision tree.

test/watcher_test.exs
  Five tests:
    - scan_complete event increments the m5 counter
    - dispatch_decision event lands in all three windows
    - snapshot/0 returns the documented shape
    - recent_events captures latest with measurements + metadata
    - queue_depths returns a map (content depends on whether
      Hypatia.Supervisor is up)

Hand-smoketested end-to-end: three telemetry calls produced three
counted events across all windows, zero dropped, recent-event tail
populated with the right metadata.
Phase 1, commit 3/4 of the watcher / supervision interface plan.

lib/hypatia/web/api_router.ex (new)
  Forwarded from /api by Hypatia.Web.Router. Three endpoints:

    GET /api/status               full Watcher.snapshot() with event
                                  keys flattened to dotted strings
                                  for JSON-friendliness
    GET /api/counts/:window       cheap event-count read (5m|1h|1d),
                                  bypasses the GenServer and goes
                                  straight to ETS — usable as a
                                  Prometheus polling target
    GET /api/recipes              recipe-health roll-up; optional
                                  ?status=quarantine_candidate,degraded
                                  filter for actionable rows only

  Loopback-only by default — `loopback_only/2` plug returns 403 to
  any non-127.0.0.1 / non-::1 caller. Operational data (queue
  depths, recipe verification rates, recent telemetry events) must
  not leak past the local machine. HYPATIA_API_ALLOW_NONLOCAL=true
  bypasses the gate, logged on every request so audit history
  captures the bypass.

lib/hypatia/web/router.ex
  Forwards /api to ApiRouter. /health stays publicly reachable so
  container orchestrators and load balancers can liveness-probe
  without a tunnel.

test/api_router_test.exs
  Plug.Test-based unit tests covering:
    - loopback gate accepts 127.0.0.1, rejects 10.x with 403
    - env override bypass works
    - /status returns dotted event-key strings (not JSON arrays)
    - /counts/:window accepts 5m/1h/1d, 400s on unknown
    - /recipes returns rows; ?status= filter validates atom names
    - unknown /api/* path returns 404 JSON

No new runtime dep — Plug.Test is part of the existing Plug dep.
Both new modules parse cleanly; compile-check deferred to CI
(local build path doesn't have :plug, but the Mix project does
via the existing dependency declaration).
Phase 1, commit 4/4 of the watcher / supervision interface plan.

lib/mix/tasks/hypatia.watch.ex
  Terminal dashboard backed by Hypatia.Watcher. No external deps —
  uses IO.ANSI for cursor positioning so it works over plain SSH
  with zero terminfo/curses requirements.

  Two operating modes:
    Local      attaches to Watcher in the same BEAM (default)
    Remote     polls http://127.0.0.1:9090/api/status (via --url)

  Modes are interchangeable — same snapshot shape from either source.
  --url makes it possible to watch a remote Hypatia via an SSH
  tunnel without spinning up a local node.

  Display (refresh every 2s by default):
    - Uptime
    - Event counts in the 5min + 1hr windows, sorted by count
    - GenServer queue depths (yellow > 10, red > 100)
    - Dropped-event warning when back-pressure has kicked in
    - Last-updated timestamp

  Flags:
    --interval SECONDS   refresh rate (default 2)
    --url URL            poll HTTP /api/status instead of local
    --once               render once and exit (good for cron)
    --plain              suppress ANSI cursor (pipe-safe)

test/watch_task_test.exs
  Three tests via ExUnit.CaptureIO:
    - render/3 produces dotted-string event names in the output
    - :unavailable snapshot shows the actionable error message
    - dropped_events > 0 fires the back-pressure warning

  render/3 is exposed publicly (was defp) so the test can exercise
  the formatting end-to-end without going through Mix.Task plumbing.

Smoke-tested against a real Watcher with all 5 instrumented event
kinds; output renders cleanly with bold headers, dim dividers,
aligned counters, and the expected colour coding.

This closes Phase 1 of the watcher / supervision plan:
  1/4  ✓ telemetry instrumentation (d0ddd2f)
  2/4  ✓ Watcher GenServer + ETS aggregator (3b4379e)
  3/4  ✓ /api/* loopback-only endpoints (30ced35)
  4/4  ✓ mix hypatia.watch CLI (this commit)

Phase 2 (web dashboard + SSE stream + Prometheus endpoint) and
Phase 3 (alerts + persistence + anomaly detection) remain.
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 32 issues detected

Severity Count
🔴 Critical 0
🟠 High 12
🟡 Medium 20
View findings
[
  {
    "reason": "Js.Dict deprecated -- use Dict (2 occurrences)",
    "type": "deprecated_api",
    "file": "/home/runner/work/hypatia/hypatia/test/soundness/fixtures/code_safety/getexn_on_external.res",
    "action": "module_replace",
    "rule_module": "migration_rules",
    "severity": "high"
  },
  {
    "reason": "Repository has 2 non-main remote branch(es). Policy: single main branch only.",
    "type": "GS007",
    "file": ".",
    "action": "delete_remote_branches",
    "rule_module": "git_state",
    "severity": "medium"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": ".github/workflows/dependabot-automerge.yml",
    "action": "update",
    "rule_module": "code_scanning_alerts",
    "severity": "high"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": ".github/workflows/hypatia-scan.yml",
    "action": "update",
    "rule_module": "code_scanning_alerts",
    "severity": "high"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": "test/soundness/fixtures/code_safety/getexn_on_external.res",
    "action": "update",
    "rule_module": "code_scanning_alerts",
    "severity": "high"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": ".github/workflows/dependabot-automerge.yml",
    "action": "update",
    "rule_module": "code_scanning_alerts",
    "severity": "high"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": "hyperpolymath/hypatia",
    "action": "review",
    "rule_module": "code_scanning_alerts",
    "severity": "medium"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": "adapters/src/main.rs",
    "action": "review",
    "rule_module": "code_scanning_alerts",
    "severity": "medium"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": ".github/workflows/docs.yml",
    "action": "review",
    "rule_module": "code_scanning_alerts",
    "severity": "medium"
  },
  {
    "reason": "Code scanning (Hypatia): hypatia/code_scanning_alerts/CSA001 -- Hypatia code_scanning_alerts: CSA001 -- 0 day(s) old",
    "type": "CSA001",
    "file": ".github/workflows/dependabot-auto-merge.yml",
    "action": "review",
    "rule_module": "code_scanning_alerts",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath merged commit 5b524a7 into main May 24, 2026
34 of 35 checks passed
@hyperpolymath hyperpolymath deleted the claude/charming-hamilton-2nLPc branch May 24, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants