# Contrat de métriques — Suivi des indicateurs Opérations (TECH)

Source de vérité pour le calcul des KPIs tech du sheet « Suivi des indicateurs Opérations ».
Chaque ligne du sheet a une entrée ici : définition, source, formule exacte, fenêtre, mode.

- **Mode `auto`** : calculé par `pysae-ai-tools stats kpi`.
- **Mode `manual`** : saisi par un humain, le skill demande la valeur.
- **Fenêtre « depuis dernière mesure »** : de la date de la dernière colonne remplie (incluse à 00:00 UTC)
  à la date de mesure (exclue). Le snapshot précédent fait foi ; à défaut, demander la date.

Clés JSON : chaque métrique a un identifiant stable (`key`) utilisé dans le snapshot JSON
(`stats kpi --json`) et l'historique. Le futur cockpit consommera ces mêmes clés.

## HEBDO

### Uptime — `uptime_pct`

- **Définition** : % de requêtes API servies sans erreur 5XX (SLI du SLO Datadog « Availability SLO for api service »).
- **Source** : Datadog SLO history — SLO id `c77fb31576955f2f81cee8a79d7d28cf`.
- **Formule** : `GET /api/v1/slo/{id}/history?from_ts={since}&to_ts={until}` → `data.overall.sli_value`.
  Équivalent métrique : `1 − (5xx / total)` sur `trace.fastapi.request{env:prod, service:api*}`.
- **Format sheet** : pourcentage, 3 décimales (ex : `99,996%`).

### Latence APIs — `latency_p95_ms`

- **Définition** : latence **p95** des requêtes API prod sur la fenêtre, pondérée par requête
  (percentile vrai sur la fenêtre entière, pas une moyenne de points par intervalle).
- **Source** : Datadog v2 scalar query, identique au widget « API, P95 ms, semaine passée »
  du dashboard Dev Ops (`dtx-2i8-524`).
- **Formule** : `p95:trace.fastapi.request{env:prod,service:api}` avec `aggregator: percentile`
  (`POST /api/v2/query/scalar`).
- **Format sheet** : millisecondes, 2 décimales (ex : `426,24`).
- **Caveats** :
  - La ligne du sheet dit « 99th percentile » mais les valeurs historiques sont du **p95**
    (source : widget Dev Ops). Corriger le label du sheet.
  - Les colonnes historiques étaient relevées à la main sur le widget « semaine passée »
    (fenêtre glissante à l'heure de consultation) ; les fenêtres sont désormais calées à
    minuit UTC → petits écarts possibles avec l'historique, validé exact sur 15/05→22/05.

### Ratio d'erreurs APIs 4XX, 5XX — `error_ratio_4xx_pct` / `error_ratio_5xx_pct`

- **Définition** : part des requêtes en 4XX (resp. 5XX) sur le total.
- **Source** : Datadog query API.
- **Formule** :
  - total : `sum:trace.fastapi.request.hits{env:prod,service:api*}.as_count()`
  - 4XX : idem + `http.status_code:4*` ; 5XX : idem + `http.status_code:5*`
  - ratio = somme des points statut / somme des points total, sur la fenêtre.
- **Format sheet** : `4XX x,xx% · 5XX x,xxx%` (une seule cellule).

### Nombre d'incidents P1 & P2 — `incidents_p1` / `incidents_p2`

- **Définition** : tickets support Airtable de criticité élevée déclarés dans la fenêtre.
  Mapping criticité → priorité (cf. `support-vocabulary.md`) : 4–5 → P1, 3 → P2.
- **Source** : Airtable REST — base « Customer Service » `app79vpJ07qFIOEm1`,
  table « Tickets support » `tblHjCvyOcdq0Kw0z`.
- **Formule** : `filterByFormula = AND({Criticité} >= 3, {Date déclaration} >= since, {Date déclaration} < until)` ;
  P1 = count(Criticité ≥ 4), P2 = count(Criticité = 3).
- **Format sheet** : entier (somme P1+P2), le détail P1/P2 va dans le commentaire de cellule.
- **Caveat** : la règle CS impose de sous-estimer la criticité → biais à la baisse possible
  vs un comptage manuel #cs-tech-support. Validé par recoupement sur les 4 colonnes de mai.

### Error budget burn à date (cible 99,9%) — `error_budget_burn_pct`

- **Définition** : part du budget d'erreur du trimestre en cours déjà consommée.
- **Source** : Datadog SLO history (même SLO), fenêtre = début du trimestre civil → date de mesure.
- **Formule** : `(1 − SLI_trimestre) / (1 − 0,999) × 100`.
- **Format sheet** : pourcentage, 1 décimale. > 100% = budget épuisé.

## MENSUEL

### Uptime — `uptime_pct`

Identique à l'hebdo, fenêtre = mois écoulé (depuis dernière mesure mensuelle).

### Uptime sur trimestre en cours — `uptime_quarter_pct`

Identique, fenêtre = début du trimestre civil → date de mesure.

### Latence APIs — `latency_p95_ms`

Identique à l'hebdo, fenêtre mensuelle.

### Ratio d'erreurs APIs 4XX, 5XX — `error_ratio_4xx_pct` / `error_ratio_5xx_pct`

Identique à l'hebdo, fenêtre mensuelle.

## Incidents (mensuel)

### Nombre d'incidents P1 & P2 — `incidents_p1` / `incidents_p2`

Identique à l'hebdo, fenêtre mensuelle.

### Time to detect (TTD) — `ttd_minutes` · **manual**

- **Définition** : délai moyen survenue → détection, sur les incidents P1/P2 de la fenêtre.
- **Source** : aucune source machine (Airtable n'a que `Date déclaration`).
- **Automatisation future** : ajouter 3 champs date à la table Tickets support
  (survenu à / pris en charge à / résolu à) — décision côté CS.

### Time to acknowledge (TTA) — `tta_minutes` · **manual**

Délai moyen détection → prise en charge. Même limite que TTD.

### Mean Time To Restore (MTTR) — `mttr_minutes` · **manual**

Délai moyen détection → résolution. Même limite que TTD.
Proxy auto disponible (cycle time des MRs fix/revert, cf. `team-productivity`) mais
non comparable à un vrai MTTR : ne pas mélanger.

## Vélocité (mensuel)

Périmètre : groupe GitLab `pysae` (id `918966`), tous projets actifs.

### Poids livré (points) — `weight_delivered`

- **Formule** : somme des `weight` des issues fermées dans la fenêtre.
  Issues sans poids : comptées à 0 et signalées (cf. skill `gitlab-unweighted-issues`).

### Issues fermées — `issues_closed`

- **Formule** : count des issues fermées dans la fenêtre (`state_event=closed`, `closed_at` dans la fenêtre).

### Lead time moyen — `lead_time_days`

- **Définition (celle du sheet)** : temps moyen durant lequel les issues fermées dans la fenêtre
  sont restées ouvertes.
- **Formule** : `avg(closed_at − created_at)` en jours, 1 décimale.
- **Note** : définition v2 plus fine disponible (1er commit lié → fermeture, exclut le bruit
  backlog) — utilisée par `team-productivity`. On garde la définition littérale du sheet
  pour rester comparable aux colonnes existantes.

### Coût IA ($) / Poids livré (points) — `ai_cost_per_point`

- **Définition** : dépense IA de la fenêtre divisée par le poids livré.
- **Source** : Anthropic Admin API `GET /v1/organizations/cost_report` (org console Pysae),
  clé admin requise (`ANTHROPIC_ADMIN_API_KEY`).
- **Formule** : `(coût API usage + sièges abonnement prorata fenêtre) / weight_delivered`.
  Le coût sièges est une constante de config (les abonnements Claude n'apparaissent pas
  dans le cost_report).
- **Mode** : auto si la clé admin résout, sinon le skill demande le montant.

## MENSUEL INTERNE ÉQUIPE

### Fréquence de déploiements — `deploy_frequency`

- **Formule** : count des déploiements `success` sur les environnements `*-prod` du groupe
  dans la fenêtre (API deployments, convention de nommage Pysae : `api-prod`, jamais `production`).
- **Format sheet** : total fenêtre + moyenne / semaine.

### Cycle time — `cycle_time_days`

- **Définition (celle du sheet)** : temps moyen durant lequel les MRs mergées dans la fenêtre
  sont restées ouvertes.
- **Formule** : `avg(merged_at − created_at)` en jours, 1 décimale. Bots exclus (Renovate, etc.).
- **Note** : définition v2 disponible (1er commit → merge), cf. lead time.

### Change failure rate — `change_failure_rate_pct`

- **Définition (proxy)** : part des MRs mergées qui sont des reverts.
- **Formule** : `revert_mrs / mrs_merged × 100` (détection : titre commençant par `Revert` /
  `revert:`), aligné sur `team-productivity`.
- **Note** : le vrai CFR DORA (déploiements ayant causé un incident / déploiements) n'est pas
  mesurable tant que les incidents ne sont pas liés aux déploiements.

### % couverture de tests (modifié / total) — `test_coverage_pct` · **semi-auto**

- **Formule (total)** : couverture du dernier pipeline `success` de la branche par défaut,
  par repo (champ `coverage` de l'API pipelines, nécessite la regex coverage configurée) ;
  agrégat = moyenne simple des repos qui exposent une couverture.
- **« modifié »** (couverture du code modifié dans la fenêtre) : **manual** en v1, aucun
  diff-coverage en place.

### Consommation des ressources — `resource_consumption` · **manual**

Coût infra de la fenêtre (AWS + Atlas + Datadog). Référence : audit finops 2026-03.
Automatisation future : Cost Explorer API + facture Atlas.

### Tech debt : temps vs. backlog estimé — `tech_debt_ratio` · **manual**

Aucune convention de label GitLab pour isoler la dette aujourd'hui.
Automatisation future : label `tech-debt` + poids, ratio poids dette livré / poids backlog dette.

### Comprehension debt : code churn — `comprehension_debt_churn_pct` · **semi-auto**

- **Formule** : % de lignes modifiées dans la fenêtre qui retouchent du code écrit il y a
  moins de 14 jours, sur `api/` et `driver/` (méthode du skill `comprehension-debt-audit`,
  baseline avril 2026 : ~45%).
- **Mode** : nécessite les clones locaux à jour → calculé en local par le skill, pas par la CLI.
