diff --git a/lib/fleet_dispatcher.ex b/lib/fleet_dispatcher.ex index 072c0e49..b92af212 100644 --- a/lib/fleet_dispatcher.ex +++ b/lib/fleet_dispatcher.ex @@ -75,20 +75,22 @@ defmodule Hypatia.FleetDispatcher do }) end - # Dispatch a ProofObligation recipe through the Safety Triangle. - # - # Called by `ProofObligation.obligations_from_patterns/2` and any code - # that constructs `{:proof_obligation, recipe, pattern}` tuples. - # - # Triangle routing for proof obligations: - # - `:eliminate` (auto-provable, confidence >= 0.90) -> - # robot-repo-automaton applies tactic inline - # - `:eliminate` (confidence < 0.90) -> - # echidnabot with eliminate-tier hint - # - `:substitute` -> - # echidnabot with VeriSimDB-recommended prover hint - # - `:control` -> - # sustainabot advisory (sorry/Admitted present, human required) + @doc """ + Dispatch a ProofObligation recipe through the Safety Triangle. + + Called by `ProofObligation.obligations_from_patterns/2` and any code + that constructs `{:proof_obligation, recipe, pattern}` tuples. + + Triangle routing for proof obligations: + - `:eliminate` (auto-provable, confidence >= 0.90) -> + robot-repo-automaton applies tactic inline + - `:eliminate` (confidence < 0.90) -> + echidnabot with eliminate-tier hint + - `:substitute` -> + echidnabot with VeriSimDB-recommended prover hint + - `:control` -> + sustainabot advisory (sorry/Admitted present, human required) + """ def dispatch_routed_action({:proof_obligation, recipe, pattern}) do tier = Map.get(recipe, "triangle_tier", "substitute") claim = Map.get(recipe, "claim", Map.get(pattern, "description", "")) @@ -152,17 +154,19 @@ defmodule Hypatia.FleetDispatcher do end end - # Dispatch a DependabotAlerts recipe through the Safety Triangle. - # - # Called by `DependabotAlerts.fixes_from_alerts/3` and any code that - # constructs `{:dependabot_fix, recipe, pattern}` tuples. - # - # Triangle routing for Dependabot alerts: - # - `:eliminate` + confidence >= 0.95 -> robot-repo-automaton auto-bumps - # (subject to Kin Gate, rate limiter, exclusion registry) - # - `:eliminate` + confidence in [0.85, 0.95) -> rhodibot opens a PR - # - `:substitute` -> rhodibot opens a PR (major bump / breaking change) - # - `:control` -> sustainabot advisory (no auto-fix path) + @doc """ + Dispatch a DependabotAlerts recipe through the Safety Triangle. + + Called by `DependabotAlerts.fixes_from_alerts/3` and any code that + constructs `{:dependabot_fix, recipe, pattern}` tuples. + + Triangle routing for Dependabot alerts: + - `:eliminate` + confidence >= 0.95 -> robot-repo-automaton auto-bumps + (subject to Kin Gate, rate limiter, exclusion registry) + - `:eliminate` + confidence in [0.85, 0.95) -> rhodibot opens a PR + - `:substitute` -> rhodibot opens a PR (major bump / breaking change) + - `:control` -> sustainabot advisory (no auto-fix path) + """ def dispatch_routed_action({:dependabot_fix, recipe, pattern}) do tier = Map.get(recipe, "triangle_tier", "control") confidence = Map.get(recipe, "confidence", 0.5) diff --git a/lib/hypatia/web/api_router.ex b/lib/hypatia/web/api_router.ex index 1f67528e..c73e8f15 100644 --- a/lib/hypatia/web/api_router.ex +++ b/lib/hypatia/web/api_router.ex @@ -64,9 +64,11 @@ defmodule Hypatia.Web.ApiRouter do end end - # GET /api/recipes/:id -- single-recipe drill-down. Returns the same - # shape as one row from `/api/recipes`, plus the recipe definition - # itself when found in the registry. + @doc """ + GET /api/recipes/:id -- single-recipe drill-down. Returns the same + shape as one row from `/api/recipes`, plus the recipe definition + itself when found in the registry. + """ get "/recipes/:id" do health = Hypatia.OutcomeTracker.recipe_health() row = Enum.find(health, &(&1.recipe_id == id)) @@ -79,9 +81,11 @@ defmodule Hypatia.Web.ApiRouter do end end - # GET /api/quarantine -- everything currently auto-quarantined: - # recipes (verification-rate gate) and bots (consecutive-failure / - # FP-rate gate from Hypatia.Safety.Quarantine). + @doc """ + GET /api/quarantine -- everything currently auto-quarantined: + recipes (verification-rate gate) and bots (consecutive-failure / + FP-rate gate from Hypatia.Safety.Quarantine). + """ get "/quarantine" do recipes = Hypatia.OutcomeTracker.recipe_health() @@ -99,9 +103,11 @@ defmodule Hypatia.Web.ApiRouter do }) end - # GET /api/alerts -- Recent threshold-rule alerts emitted by - # Hypatia.Watcher.Alerts (ring buffer, newest first). Powers the - # dashboard alert ribbon and supports manual triage. + @doc """ + GET /api/alerts -- Recent threshold-rule alerts emitted by + Hypatia.Watcher.Alerts (ring buffer, newest first). Powers the + dashboard alert ribbon and supports manual triage. + """ get "/alerts" do rows = case Process.whereis(Hypatia.Watcher.Alerts) do diff --git a/lib/hypatia/web/router.ex b/lib/hypatia/web/router.ex index e2098bc9..c9b30eac 100644 --- a/lib/hypatia/web/router.ex +++ b/lib/hypatia/web/router.ex @@ -28,16 +28,20 @@ defmodule Hypatia.Web.Router do plug(:match) plug(:dispatch) - # GET / -- Single-page live operational dashboard. HTML + vanilla JS, - # polls /api/status and EventSource-streams /api/events. The dashboard - # itself is publicly reachable; the data endpoints it calls are - # loopback-only (gated in ApiRouter), so a non-local browser would - # render the chrome but get 403 from the XHR/SSE calls. + @doc """ + GET / -- Single-page live operational dashboard. HTML + vanilla JS, + polls /api/status and EventSource-streams /api/events. The dashboard + itself is publicly reachable; the data endpoints it calls are + loopback-only (gated in ApiRouter), so a non-local browser would + render the chrome but get 403 from the XHR/SSE calls. + """ get "/" do Hypatia.Web.Dashboard.call(conn, []) end - # GET /health -- Basic health check for the HTTP endpoint. + @doc """ + GET /health -- Basic health check for the HTTP endpoint. + """ get "/health" do health = %{ status: "ok", @@ -50,23 +54,27 @@ defmodule Hypatia.Web.Router do |> send_resp(200, Jason.encode!(health)) end - # GET /metrics -- Prometheus text-format exposition. Publicly - # reachable (NOT loopback-only) because scrapers routinely run on a - # different host; there's no operational data in the metric body - # that isn't already implied by the dashboard's existence. + @doc """ + GET /metrics -- Prometheus text-format exposition. Publicly + reachable (NOT loopback-only) because scrapers routinely run on a + different host; there's no operational data in the metric body + that isn't already implied by the dashboard's existence. + """ get "/metrics" do Hypatia.Web.Metrics.call(conn, []) end - # GET /metrics/snapshot -- Compact JSON snapshot of estate-level - # counters: repos scanned, weak points, dispatched actions, outcomes, - # recipes, average confidence. Consumed by the optional Ada TUI - # (`lib/tui/port.ex`) on its 10s tick, and useful as a single-call - # status read for external dashboards. - # - # Reads from the verisim-data flat-file store via VerisimConnector; - # any failure returns a degraded snapshot with status="degraded" - # rather than 500, so the TUI keeps rendering. + @doc """ + GET /metrics/snapshot -- Compact JSON snapshot of estate-level + counters: repos scanned, weak points, dispatched actions, outcomes, + recipes, average confidence. Consumed by the optional Ada TUI + (`lib/tui/port.ex`) on its 10s tick, and useful as a single-call + status read for external dashboards. + + Reads from the verisim-data flat-file store via VerisimConnector; + any failure returns a degraded snapshot with status="degraded" + rather than 500, so the TUI keeps rendering. + """ get "/metrics/snapshot" do snapshot = Hypatia.Web.MetricsSnapshot.build() @@ -80,12 +88,14 @@ defmodule Hypatia.Web.Router do # reachable for container orchestrators. forward("/api", to: Hypatia.Web.ApiRouter) - # POST /graphql -- GraphQL-shaped query endpoint (M14). - # - # Minimal hand-rolled implementation; no introspection, no schema - # federation, no Absinthe dep. See lib/hypatia/web/graphql.ex for - # the supported field set and limitations. Loopback-only by sharing - # the bearer-auth gate when HYPATIA_API_BEARER_TOKEN is configured. + @doc """ + POST /graphql -- GraphQL-shaped query endpoint (M14). + + Minimal hand-rolled implementation; no introspection, no schema + federation, no Absinthe dep. See lib/hypatia/web/graphql.ex for + the supported field set and limitations. Loopback-only by sharing + the bearer-auth gate when HYPATIA_API_BEARER_TOKEN is configured. + """ post "/graphql" do Hypatia.Web.GraphQL.call(conn, []) end