-- SPDX-FileCopyrightText: 2026 Alexandra de Wit
--
-- SPDX-License-Identifier: MIT

{- | The @ecluse.*@ instrument catalogue: which metrics this build emits, and their wire names.

The catalogue is closed and its 'Universe' instance enumerates it, so the runtime creates one
instrument per name and a new metric cannot reach a meter unregistered. What may label an
instrument is in "Ecluse.Core.Telemetry.Metrics". @docs\/architecture\/observability.md@ holds
the catalogue as prose.
-}
module Ecluse.Core.Telemetry.Catalogue (
    MetricName (..),
    metricName,
) where

-- relude's prelude exports a Bounded/Enum-based `universe`. Hide it so the
-- Generic-derived `Data.Universe.Class.universe` is the one in scope here.
import Prelude hiding (universe)

import Data.Universe.Class (Universe (..))
import Data.Universe.Generic (universeGeneric)

{- | The catalogue of metric instruments Écluse emits. Queue backlog and DLQ depth are absent
on purpose: those are cloud-native metrics, not signals Écluse re-emits.
-}
data MetricName
    = -- | @http.server.request.duration@: server request latency (histogram).
      HttpServerRequestDuration
    | -- | @ecluse.serve.decision@: admit\/deny\/unavailable (counter).
      ServeDecision
    | -- | @ecluse.rule.denials@: rule denials by rule and reason class (counter).
      RuleDenials
    | -- | @ecluse.rule.eval.duration@: rule-evaluation latency by tier (histogram).
      RuleEvalDuration
    | -- | @ecluse.rule.effectful.failures@: effectful-rule failures (counter).
      RuleEffectfulFailures
    | -- | @ecluse.rule.breaker.state@: effectful\/mint breaker state by source (gauge).
      RuleBreakerState
    | -- | @ecluse.serve.admission.in_flight@: in-flight metadata parses (up-down counter).
      ServeAdmissionInFlight
    | -- | @ecluse.serve.admission.queued@: admissions that waited for a slot (counter).
      ServeAdmissionQueued
    | -- | @ecluse.serve.admission.memory.budget_bytes@: the metadata memory budget (observable gauge).
      MemoryAdmissionBudgetBytes
    | -- | @ecluse.serve.admission.memory.charged_bytes@: bytes requests hold against the budget (observable gauge).
      MemoryAdmissionChargedBytes
    | -- | @ecluse.serve.admission.memory.brake_level@: 0 calm, 1 holding, 2 braking (observable gauge).
      MemoryAdmissionBrakeLevel
    | -- | @ecluse.serve.admission.memory.waiting@: new requests waiting at the memory gate (observable gauge).
      MemoryAdmissionWaiting
    | -- | @ecluse.serve.admission.memory.paused_now@: started requests paused for memory (observable gauge).
      MemoryAdmissionPausedNow
    | -- | @ecluse.serve.admission.memory.queued@: requests that waited for their entry step (counter).
      MemoryAdmissionQueued
    | -- | @ecluse.serve.admission.memory.shed@: requests shed at the memory gate (counter).
      MemoryAdmissionShed
    | -- | @ecluse.serve.admission.memory.pauses@: times a started request paused for memory (counter).
      MemoryAdmissionPauses
    | -- | @ecluse.serve.admission.memory.overdraws@: steps the token holder took past the budget (counter).
      MemoryAdmissionOverdraws
    | -- | @ecluse.publish.body.in_flight_bytes@: bytes reserved for buffered publish bodies (up-down counter).
      PublishBodyInFlightBytes
    | -- | @ecluse.publish.body.shed@: publishes shed at the body-byte budget (counter).
      PublishBodyShed
    | -- | @ecluse.registry.merge.divergence@: cross-upstream integrity divergences detected in the packument merge (counter).
      MergeDivergence
    | -- | @ecluse.upstream.fetch.duration@: upstream fetch latency (histogram).
      UpstreamFetchDuration
    | -- | @ecluse.upstream.fetch.errors@: upstream fetch errors (counter).
      UpstreamFetchErrors
    | -- | @ecluse.metadata_cache.requests@: full-store hit\/miss\/collapsed (counter).
      MetadataCacheRequests
    | -- | @ecluse.metadata_cache.version.requests@: selected-version request outcomes (counter).
      SingleVersionCacheRequests
    | -- | @ecluse.metadata_cache.assembled.requests@: assembled-response request outcomes (counter).
      AssembledCacheRequests
    | -- | @ecluse.metadata_cache.refused@: capacity refusals and backend failures (counter).
      MetadataCacheRefused
    | -- | @ecluse.metadata_cache.entries@: metadata-cache occupancy (gauge).
      MetadataCacheEntries
    | -- | @ecluse.metadata_cache.resident_bytes@: full-packument cache resident bytes (gauge).
      MetadataCacheResidentBytes
    | -- | @ecluse.metadata_cache.version.resident_bytes@: single-version cache resident bytes (gauge).
      SingleVersionCacheResidentBytes
    | -- | @ecluse.metadata_cache.assembled.resident_bytes@: assembled-representation store resident bytes (gauge).
      AssembledCacheResidentBytes
    | -- | @ecluse.serve.perimeter.faults@: pre-commit handler escapes the request perimeter answered (counter).
      ServePerimeterFaults
    | -- | @ecluse.serve.relay.anomalies@: public relays that were not the admitted artifact (counter).
      ServeRelayAnomalies
    | -- | @ecluse.mirror.enqueued@: mirror jobs enqueued (counter).
      MirrorEnqueued
    | -- | @ecluse.mirror.enqueue.failures@: mirror enqueue failures (counter).
      MirrorEnqueueFailures
    | -- | @ecluse.mirror.jobs.processed@: mirror jobs processed by result (counter).
      MirrorJobsProcessed
    | -- | @ecluse.mirror.publish.duration@: mirror publish latency (histogram).
      MirrorPublishDuration
    | -- | @ecluse.dredger.versions@: versions one sweep cycle disposed of, by target and result (counter).
      DredgerVersions
    | -- | @ecluse.credential.refresh@: credential refreshes by result and provider (counter).
      CredentialRefresh
    | -- | @ecluse.credential.token.ttl.seconds@: remaining token lifetime by provider (gauge).
      CredentialTokenTtlSeconds
    | -- | @ecluse.advisory.sync.attempts@: advisory sync attempts by ecosystem and result (counter).
      AdvisorySyncAttempts
    | -- | @ecluse.advisory.sync.duration@: advisory sync attempt latency by ecosystem and result (histogram).
      AdvisorySyncDuration
    | {- | @ecluse.advisory.database.age.seconds@: seconds since this ecosystem's last swap
      (gauge). It measures this process's own installation, not the data.
      -}
      AdvisoryDatabaseAgeSeconds
    | {- | @ecluse.advisory.source.age.seconds@: seconds since this ecosystem's serving artifact
      was published (gauge). It is the age the CVE-deny path expires on.
      -}
      AdvisorySourceAgeSeconds
    | -- | @ecluse.advisory.compile.accepted@: advisory entries a compile pass accepted (counter).
      AdvisoryCompileAccepted
    | -- | @ecluse.advisory.compile.dropped@: advisory entries a compile pass dropped, by cause (counter).
      AdvisoryCompileDropped
    | -- | @ecluse.advisory.compile.runs@: compile passes by ecosystem and result (counter).
      AdvisoryCompileRuns
    deriving stock (MetricName -> MetricName -> Bool
(MetricName -> MetricName -> Bool)
-> (MetricName -> MetricName -> Bool) -> Eq MetricName
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MetricName -> MetricName -> Bool
== :: MetricName -> MetricName -> Bool
$c/= :: MetricName -> MetricName -> Bool
/= :: MetricName -> MetricName -> Bool
Eq, (forall x. MetricName -> Rep MetricName x)
-> (forall x. Rep MetricName x -> MetricName) -> Generic MetricName
forall x. Rep MetricName x -> MetricName
forall x. MetricName -> Rep MetricName x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. MetricName -> Rep MetricName x
from :: forall x. MetricName -> Rep MetricName x
$cto :: forall x. Rep MetricName x -> MetricName
to :: forall x. Rep MetricName x -> MetricName
Generic, Eq MetricName
Eq MetricName =>
(MetricName -> MetricName -> Ordering)
-> (MetricName -> MetricName -> Bool)
-> (MetricName -> MetricName -> Bool)
-> (MetricName -> MetricName -> Bool)
-> (MetricName -> MetricName -> Bool)
-> (MetricName -> MetricName -> MetricName)
-> (MetricName -> MetricName -> MetricName)
-> Ord MetricName
MetricName -> MetricName -> Bool
MetricName -> MetricName -> Ordering
MetricName -> MetricName -> MetricName
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: MetricName -> MetricName -> Ordering
compare :: MetricName -> MetricName -> Ordering
$c< :: MetricName -> MetricName -> Bool
< :: MetricName -> MetricName -> Bool
$c<= :: MetricName -> MetricName -> Bool
<= :: MetricName -> MetricName -> Bool
$c> :: MetricName -> MetricName -> Bool
> :: MetricName -> MetricName -> Bool
$c>= :: MetricName -> MetricName -> Bool
>= :: MetricName -> MetricName -> Bool
$cmax :: MetricName -> MetricName -> MetricName
max :: MetricName -> MetricName -> MetricName
$cmin :: MetricName -> MetricName -> MetricName
min :: MetricName -> MetricName -> MetricName
Ord, Int -> MetricName -> ShowS
[MetricName] -> ShowS
MetricName -> String
(Int -> MetricName -> ShowS)
-> (MetricName -> String)
-> ([MetricName] -> ShowS)
-> Show MetricName
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MetricName -> ShowS
showsPrec :: Int -> MetricName -> ShowS
$cshow :: MetricName -> String
show :: MetricName -> String
$cshowList :: [MetricName] -> ShowS
showList :: [MetricName] -> ShowS
Show)

instance Universe MetricName where universe :: [MetricName]
universe = [MetricName]
forall a. (Generic a, GUniverse (Rep a)) => [a]
universeGeneric

-- | The wire name of a 'MetricName'.
metricName :: MetricName -> Text
metricName :: MetricName -> Text
metricName = \case
    MetricName
HttpServerRequestDuration -> Text
"http.server.request.duration"
    MetricName
ServeDecision -> Text
"ecluse.serve.decision"
    MetricName
RuleDenials -> Text
"ecluse.rule.denials"
    MetricName
RuleEvalDuration -> Text
"ecluse.rule.eval.duration"
    MetricName
RuleEffectfulFailures -> Text
"ecluse.rule.effectful.failures"
    MetricName
RuleBreakerState -> Text
"ecluse.rule.breaker.state"
    MetricName
ServeAdmissionInFlight -> Text
"ecluse.serve.admission.in_flight"
    MetricName
ServeAdmissionQueued -> Text
"ecluse.serve.admission.queued"
    MetricName
MemoryAdmissionBudgetBytes -> Text
"ecluse.serve.admission.memory.budget_bytes"
    MetricName
MemoryAdmissionChargedBytes -> Text
"ecluse.serve.admission.memory.charged_bytes"
    MetricName
MemoryAdmissionBrakeLevel -> Text
"ecluse.serve.admission.memory.brake_level"
    MetricName
MemoryAdmissionWaiting -> Text
"ecluse.serve.admission.memory.waiting"
    MetricName
MemoryAdmissionPausedNow -> Text
"ecluse.serve.admission.memory.paused_now"
    MetricName
MemoryAdmissionQueued -> Text
"ecluse.serve.admission.memory.queued"
    MetricName
MemoryAdmissionShed -> Text
"ecluse.serve.admission.memory.shed"
    MetricName
MemoryAdmissionPauses -> Text
"ecluse.serve.admission.memory.pauses"
    MetricName
MemoryAdmissionOverdraws -> Text
"ecluse.serve.admission.memory.overdraws"
    MetricName
PublishBodyInFlightBytes -> Text
"ecluse.publish.body.in_flight_bytes"
    MetricName
PublishBodyShed -> Text
"ecluse.publish.body.shed"
    MetricName
MergeDivergence -> Text
"ecluse.registry.merge.divergence"
    MetricName
UpstreamFetchDuration -> Text
"ecluse.upstream.fetch.duration"
    MetricName
UpstreamFetchErrors -> Text
"ecluse.upstream.fetch.errors"
    MetricName
MetadataCacheRequests -> Text
"ecluse.metadata_cache.requests"
    MetricName
SingleVersionCacheRequests -> Text
"ecluse.metadata_cache.version.requests"
    MetricName
AssembledCacheRequests -> Text
"ecluse.metadata_cache.assembled.requests"
    MetricName
MetadataCacheRefused -> Text
"ecluse.metadata_cache.refused"
    MetricName
MetadataCacheEntries -> Text
"ecluse.metadata_cache.entries"
    MetricName
MetadataCacheResidentBytes -> Text
"ecluse.metadata_cache.resident_bytes"
    MetricName
SingleVersionCacheResidentBytes -> Text
"ecluse.metadata_cache.version.resident_bytes"
    MetricName
AssembledCacheResidentBytes -> Text
"ecluse.metadata_cache.assembled.resident_bytes"
    MetricName
ServePerimeterFaults -> Text
"ecluse.serve.perimeter.faults"
    MetricName
ServeRelayAnomalies -> Text
"ecluse.serve.relay.anomalies"
    MetricName
MirrorEnqueued -> Text
"ecluse.mirror.enqueued"
    MetricName
MirrorEnqueueFailures -> Text
"ecluse.mirror.enqueue.failures"
    MetricName
MirrorJobsProcessed -> Text
"ecluse.mirror.jobs.processed"
    MetricName
MirrorPublishDuration -> Text
"ecluse.mirror.publish.duration"
    MetricName
DredgerVersions -> Text
"ecluse.dredger.versions"
    MetricName
CredentialRefresh -> Text
"ecluse.credential.refresh"
    MetricName
CredentialTokenTtlSeconds -> Text
"ecluse.credential.token.ttl.seconds"
    MetricName
AdvisorySyncAttempts -> Text
"ecluse.advisory.sync.attempts"
    MetricName
AdvisorySyncDuration -> Text
"ecluse.advisory.sync.duration"
    MetricName
AdvisoryDatabaseAgeSeconds -> Text
"ecluse.advisory.database.age.seconds"
    MetricName
AdvisorySourceAgeSeconds -> Text
"ecluse.advisory.source.age.seconds"
    MetricName
AdvisoryCompileAccepted -> Text
"ecluse.advisory.compile.accepted"
    MetricName
AdvisoryCompileDropped -> Text
"ecluse.advisory.compile.dropped"
    MetricName
AdvisoryCompileRuns -> Text
"ecluse.advisory.compile.runs"