Transformer les analyses d'ingénierie en insights et abonnements
Le dashboard d'analyse d'ingénierie et les outils MCP (pull-requests, workflow-health, pr-lifecycle, engineering-analytics-broken-tests, …)
exécutent du HogQL curé en privé : rien dans l'interface ou les noms de sortie des outils ne nomme les tables sous-jacentes,
et les endpoints ne peuvent pas eux-mêmes être sauvegardés comme insights ou faire l'objet d'un abonnement.
Les données, cependant, sont interrogeables directement, par deux substrats :
- Tables brutes du warehouse —
<prefix>github_pull_requests,<prefix>github_workflow_runs,<prefix>github_workflow_jobs,<prefix>github_reviews, et<prefix>github_teams/<prefix>github_team_members(appartenance aux équipes org — la carte auteur→équipe) — tables ordinaires limitées à l'équipe que tu interroges avec HogQL. - Trois vues curées du warehouse avec des noms fixes —
engineering_analytics_job_costs,engineering_analytics_ci_job_history,engineering_analytics_ci_failures— provisionnées par équipe à partir de la ou des source(s) GitHub connectée(s). Non matérialisées : calculées au moment de la requête, toujours à jour, et elles soutiennent les insights et abonnements comme n'importe quelle table.
Les vues existent pour une seule raison : elles rendent du code produit en SQL — le modèle de coût au niveau runner, la recette de fingerprint des défaillances, les règles d'attribution de commit jobs↔runs — une logique qui s'éloignerait silencieusement si elle était codée manuellement, re-rendue dans la vue de l'équipe chaque fois que le code change. Tout le reste est juste des données de table, et du pur HogQL sur les tables brutes est toujours suffisant : ne crée jamais de vues warehouse supplémentaires pour les données d'analyse d'ingénierie, et ne re-dérive jamais en SQL ce que les trois vues encodent déjà.
| Ce que le produit affiche | Où les données vivent réellement | Peut cela soutenir un insight ? |
|---|---|---|
| Liste de PR, temps de fusion, statut CI, santé du workflow | Tables du warehouse <prefix>github_pull_requests, <prefix>github_workflow_runs |
Oui (SQL insight sur les tables) |
| Révisions et approbations | <prefix>github_reviews |
Oui |
| Métriques de PR au niveau équipe (attribution auteur→équipe) | <prefix>github_team_members semi-jointure contre les auteurs de PR |
Oui (agrégats au niveau équipe uniquement) |
| Durées des tâches, temps d'attente, tiers de runner | <prefix>github_workflow_jobs |
Oui |
| Coût CI (échelle de prix au niveau runner) | Vue engineering_analytics_job_costs |
Oui (interroger la vue, ne jamais recalculer le coût) |
| Historique CI par tâche avec attribution de commit | Vue engineering_analytics_ci_job_history |
Oui |
| Lignes de défaillance CI groupées (fingerprintées) | Vue engineering_analytics_ci_failures (lit le produit Logs, rétention courte) |
Oui, pour des fenêtres récentes courtes |
| Logs de défaillance CI affinés pour une PR ou run | Produit Logs (service_name = 'github-ci-logs') + logique d'affinement |
Non (utilise les outils MCP ad hoc) |
| Classement des tests flaky, triage des tests cassés, santé CI de l'équipe | Spans de trace CI + logique de classement/classification dans le code produit | Non (utilise les outils MCP ad hoc) |
Le travail se divise donc clairement : les métriques soutenues par warehouse (tables brutes ou les trois vues) deviennent des SQL insights (puis dashboards, puis abonnements) ; tout ce qui est calculé par la logique produit au moment de la requête reste sur les outils MCP, livré régulièrement via un abonnement IA si nécessaire.
Étape 1 : découvrir les tables de l'équipe
Les noms des tables du warehouse portent un préfixe choisi par l'utilisateur, donc ne les code jamais en dur.
Appelle l'outil MCP engineering-analytics-sources : chaque source GitHub connectée retourne son id, repo, et prefix.
Les tables sont <prefix>github_pull_requests, <prefix>github_workflow_runs, <prefix>github_workflow_jobs, <prefix>github_reviews, et <prefix>github_teams / <prefix>github_team_members ;
un préfixe vide signifie les noms simples github_*.
Avec plusieurs sources, demande à quel repo l'utilisateur pense ; chaque source est un repo.
Les trois vues engineering_analytics_* n'ont besoin d'aucune découverte : leurs noms sont fixes (sans préfixe), et elles couvrent toutes les sources GitHub de l'équipe à la fois — filtre sur repo_owner / repo_name (job_costs, ci_job_history) ou repo
(ci_failures, qui lit le produit Logs plutôt que les tables par source) pour te limiter à un repo.
Étape 2 : écrire du HogQL qui porte la sémantique curée
D'abord vérifie si l'une des trois vues répond déjà à la question — coût, historique par tâche et attribution de commit, lignes de défaillance fingerprintées — et interroge-la directement ; les règles ci-dessous concernent les tables brutes.
Les tables brutes reçoivent le JSON de GitHub verbatim, donc un SELECT naïf applique mal les règles du domaine.
Copie les sous-requêtes de base depuis references/hogql-recipes.md (elles reflètent les builders curés du produit lui-même) et suis ces règles :
- Les timestamps sont des chaînes. Toujours
parseDateTimeBestEffort(created_at)etc. avant de comparer ou calculer une différence. - Le JSON imbriqué est Nullable.
ifNull(...)-déplie avantJSONExtractArrayRaw/splitByChar: ClickHouse rejette un Array à l'intérieur d'un Nullable. - L'attribution CI ↔ PR se fait par numéro de PR (l'association
pull_requestsde la run), jamais par SHA head : le snapshot de PR garde seulement le head actuel, donc une jointure SHA supprime silencieusement tous les pushes sauf le dernier. Le SHA head est seulement pour le statut CI actuel d'une PR (dernière run par(head_sha, workflow_name)). - Détection de bot :
author_handle LIKE '%[bot]' OR author_handle IN ('dependabot', 'github-actions', 'posthog-bot', 'renovate'). Exclue les bots et les brouillons des métriques de débit / temps de fusion par défaut. - Noms honnêtes.
merged_at - created_atestopen_to_merge_seconds(ça fusionne le temps brouillon et review) ; ne nomme jamais un insight « cycle time » ou « review time ». - Les conclusions peuvent être obsolètes. Les runs synchronisent des repères de limite sur
created_at; une run qui se termine tard peut afficher une conclusion obsolète. Calcule les taux sur les lignesstatus = 'completed'uniquement. - Les reviews se joignent par
pr_number. Dansgithub_reviews,stateestAPPROVED/CHANGES_REQUESTED/COMMENTED/DISMISSED(les brouillons en attente sont supprimés à la synchro), et lepr_numberinjecté se joint aunumberde la table PR. - L'attribution auteur→équipe est une semi-jointure d'appartenance. Filtre
author_handle IN (SELECT member_handle FROM …)contregithub_team_members(voir la recette d'équipe) — la forme que la propre tendance de fusion d'équipe du produit utilise. Une personne peut appartenir à plusieurs équipes GitHub, donc une JOIN simple doublerait le comptage de ses PRs entre équipes ; et seuls les agrégats au niveau équipe quittent la requête, jamais des figures par membre.
Teste la requête avec l'outil MCP execute-sql (ou l'éditeur SQL) avant de sauvegarder quoi que ce soit.
Étape 3 : la sauvegarder comme insight
Utilise insight-create avec un SQL insight :
{
"name": "Weekly PR open→merge time (hours)",
"query": {
"kind": "DataVisualizationNode",
"source": { "kind": "HogQLQuery", "query": "<tested SQL>" }
}
}
display (un ChartDisplayType, p. ex. ActionsLineGraph) et chartSettings (colonnes xAxis / yAxis) transforment la table en graphique ;
quand tu n'es pas sûr, sauvegarde et laisse l'utilisateur choisir la visualisation dans l'interface, en liant l'URL de l'insight retournée.
Intègre une fenêtre relative dans le SQL (>= now() - INTERVAL 90 DAY) ;
note qu'un filtre de date codé en dur signifie que les remplacements de date du dashboard ne s'appliqueront pas à cette tuile.
Bundle plusieurs insights avec dashboard-create.
Étape 4 : s'abonner
Avec l'insight (ou le dashboard) sauvegardé, c'est du territoire d'abonnement standard : suis la skill managing-subscriptions pour le payload subscriptions-create, les canaux, la fréquence, et les options de résumé IA.
Pour « notifie-moi quand X » (une condition, pas une horaire) : les alertes demandent un insights tendances, donc un SQL insight ne peut pas être alerté aujourd'hui. Propose un abonnement planifié à la place, ou une vérification de seuil à l'intérieur d'un abonnement IA de type prompt.
Ce qu'il NE FAUT PAS re-construire en SQL
Le classement des tests flaky, la classification des tests cassés, le rollup de santé CI d'équipe, et l'affinement du log de défaillance sont la logique produit sur des données qu'un insight ne peut pas atteindre (spans de trace CI, corps de logs bruts) ;
les versions SQL codées manuellement s'éloigneront silencieusement de ce que le dashboard affiche.
Pour un rapport récurrent sur ceux-ci, crée un abonnement IA de type prompt (voir la skill creating-ai-subscription) dont le prompt demande la lecture d'analyse d'ingénierie pertinente à chaque période,
ou appelle simplement les outils MCP (engineering-analytics-flaky-tests, engineering-analytics-broken-tests, engineering-analytics-team-ci-health, engineering-analytics-ci-failure-logs, engineering-analytics-run-failure-logs) ad hoc.
Le coût CI n'est pas sur cette liste parce que sa logique produit est rendue dans la
vue engineering_analytics_job_costs (testée en parité contre le modèle du produit lui-même, re-rendue quand le modèle
change), donc les insights de coût sont du SQL simple sur la vue — et les outils de coût MCP (engineering-analytics-pr-cost,
engineering-analytics-workflow-runner-costs) lisent ce même SELECT rendu, donc les chiffres s'accordent. Toujours : ne re-calcule jamais le coût en dollars à partir des labels de runner toi-même.
Mises en garde à porter dans chaque insight
Nomme-les dans la description de l'insight afin que les lecteurs futurs les héritent :
open_to_merge_seconds est brut (brouillon + review fusionnés) ;
les conclusions CI peuvent être en retard jusqu'à ce que le webhook de la run se règle ;
estimated_cost_usd NULL signifie non facturable ou en cours d'exécution, jamais zéro — désambiguïse via provider vs completed_at ;
ci_failures est pytest uniquement et défaillance uniquement, donc ses comptages sont un signal absolu, jamais des taux ;
les bots et les brouillons sont exclus (ou non : dis lequel).
Et ne construis jamais de leaderboards par auteur ou de classements entre auteurs ; la surveillance par développeur est un non-objectif produit explicite.
Quand une répartition au niveau équipe est souhaitée, groupe plutôt par la semi-jointure d'appartenance github_team_members.