Skip to content

chore(agents): auditer ce qui marche, pas seulement ce qui existe - #277

Merged
thierryvm merged 1 commit into
mainfrom
chore/agents-silent-failure-coverage
Jul 27, 2026
Merged

chore(agents): auditer ce qui marche, pas seulement ce qui existe#277
thierryvm merged 1 commit into
mainfrom
chore/agents-silent-failure-coverage

Conversation

@thierryvm

@thierryvm thierryvm commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Le trou

Les 16 agents QA posaient tous, chacun à sa manière, la même question : est-ce présent ?
Aucun ne demandait : est-ce que ça marche, et le saurait-on si ça s'arrêtait ?

Trois incidents en trois mois sont sortis par ce trou. Les trois étaient verts pendant
qu'ils échouaient :

Incident Déguisement Durée d'invisibilité
H3 (#273) Client service_role dégradé en authenticated ; toutes les écritures d'audit refusées. logAuditEvent() avale ses erreurs par conception. 3 mois
purge_audit_log_older_than_12_months() SECURITY DEFINER sur une table FORCE RLS : renvoie 0 qu'elle ait purgé ou été refusée. Depuis avril — jamais appelée
Playwright E2E 214 passed, 173 skipped — tous les parcours connectés dans les 173. Jusqu'à ce que quelqu'un lise la ligne du reporter

La preuve la plus gênante était dans gdpr-compliance-auditor lui-même. Sa checklist
affirmait :

  • « exportUserData() returns a complete JSON bundle » → 7 tables sur 14
  • « executeDeletion() wipes » → aucun appelant depuis avril

Il aurait validé les deux bugs qu'on a passé deux jours à corriger.

Ce que fait cette PR

Un agent créé, trois corrigés — plutôt qu'un agent par symptôme.

silent-failure-auditor (nouveau, Opus)

Question unique : « si ça s'arrêtait cette nuit, qu'est-ce qui serait différent demain
matin ? »
. Si la réponse honnête est « rien d'observable », c'est un constat — même si le
code est correct aujourd'hui.

Six terrains de chasse : écritures à zéro ligne prises pour des succès, chemins privilégiés
refusés en silence, catch qui mangent la preuve, tâches planifiées jamais armées, gates
qui passent à vide, mesures déclarées jamais construites.

Deux règles de méthode : mesurer plutôt que raisonner (il a Bash et la stack locale),
et étiqueter chaque affirmation MEASURED / READ IN CODE / INFERRED / UNVERIFIABLE HERE.
Classement par durée d'invisibilité, pas par gravité — un bug critique qui hurle est
moins dangereux ici qu'un bug moyen qui ne hurlera jamais.

rls-flow-tester — le sens qui manquait

Il ne testait que la direction attaquant. Il teste maintenant aussi la direction
privilégiée : FORCE RLS s'applique au propriétaire de la table, un SECURITY DEFINER
possédé par postgres peut écrire 0 ligne sans erreur, et le postgres hébergé n'est
pas celui de la stack locale. Il doit désormais rapporter des nombres de lignes mesurés,
pas « aucune erreur ».

security-auditor — endpoints non authentifiés + sous-permission

src/app/api/** est exclu du matcher du proxy (src/proxy.ts:139) : pas de session du
tout. Six points bloquants ajoutés (fail-closed, comparaison de digests parce que
timingSafeEqual lève sur des longueurs inégales, pas de secret en URL, sémantique sans
réessai, plafond bruyant…), plus le rappel que la sous-permission est une vulnérabilité :
l'art. 32(1)(b) tombe sans qu'aucune donnée ne fuite.

gdpr-compliance-auditor — déclaré vs implémenté

Les deux affirmations fausses deviennent des questions à poser au code. Nouveau
chapitre : deux des trois constats les plus coûteux n'étaient pas des bugs, mais des
phrases publiées dans cinq locales que le code ne soutenait pas.

Portée et risque

Markdown uniquement — aucun code applicatif, aucune migration, aucune dépendance. Zéro
impact runtime, donc aucun check CI ne peut valider le fond : la revue est humaine.

Deux réserves que je signale plutôt que de les taire :

  1. plan-reviewer n'a pas été invoqué. La règle vise le code > 50 lignes et les chemins
    sensibles (Server Actions, proxy.ts, migrations…). Ce sont des prompts. Il tourne en
    parallèle sur le plan de l'étape 3b, autrement plus conséquent. Arbitrage assumé, à
    contredire si tu préfères la règle littérale.
  2. J'écris les agents qui auditent mon propre travail. C'est structurellement une
    copie corrigée par son auteur. Les trois incidents cités sont mesurés et vérifiables,
    mais le choix de ce qu'on cherche reste le mien.

🤖 Generated with Claude Code

Summary by Sourcery

Introduire un nouvel agent QA axé sur les défaillances silencieuses et renforcer les auditeurs existants afin de vérifier que les protections et les engagements RGPD fonctionnent réellement plutôt que de simplement exister.

Nouvelles fonctionnalités :

  • Ajouter un agent silent-failure-auditor pour détecter les mécanismes pouvant échouer sans impact observable, en couvrant les journaux d’audit, les écritures privilégiées, les tâches en arrière-plan, les garde-fous CI et les tâches de rétention.

Améliorations :

  • Renforcer gdpr-compliance-auditor afin de valider l’exhaustivité des exports de données utilisateur, le comportement d’effacement, ainsi que l’alignement entre les déclarations légales visibles par l’utilisateur et l’implémentation réelle à travers les différentes locales.
  • Étendre rls-flow-tester pour couvrir les chemins RLS privilégiés, en garantissant que les opérations service_role et SECURITY DEFINER ne sont pas bloquées silencieusement et que les résultats sont mesurés via les comptes de lignes.
  • Étendre les recommandations de security-auditor pour les endpoints non authentifiés et planifiés, en imposant un comportement fail-closed, une gestion sûre des secrets, des comparaisons en temps constant correctes, et des vérifications préalables explicites avant les opérations destructrices.
  • Mettre à jour CLAUDE.md pour documenter le nouvel agent QA dédié aux défaillances silencieuses, les responsabilités élargies de rls-flow-tester et le nombre accru d’agents QA.
Original summary in English

Summary by Sourcery

Introduce a new QA agent focused on silent failures and strengthen existing auditors to verify that protections and GDPR promises actually work rather than just exist.

New Features:

  • Add a silent-failure-auditor agent to detect mechanisms that can fail without observable impact, covering audit logs, privileged writes, background jobs, CI gates, and retention tasks.

Enhancements:

  • Tighten the gdpr-compliance-auditor to validate completeness of user data exports, erasure behavior, and alignment between user-facing legal claims and actual implementation across locales.
  • Expand rls-flow-tester to cover privileged RLS paths, ensuring service_role and SECURITY DEFINER operations are not silently blocked and that results are measured via row counts.
  • Extend security-auditor guidance for unauthenticated and scheduled endpoints, enforcing fail-closed behavior, safe secret handling, correct constant-time comparisons, and explicit preflight checks before destructive operations.
  • Update CLAUDE.md to document the new silent-failure QA agent, the expanded responsibilities of rls-flow-tester, and the increased QA agent count.

Les 16 agents QA vérifiaient tous la même chose sous des angles différents :
la présence d'un mécanisme. Aucun ne demandait s'il fonctionne. Trois
incidents en trois mois sont sortis exactement par ce trou, et tous les
trois étaient verts pendant qu'ils échouaient :

  - H3 : un client service_role dégradé en authenticated, toutes les
    écritures d'audit refusées pendant 3 mois sans qu'une seule erreur
    remonte (logAuditEvent avale ses propres échecs par conception)
  - purge_audit_log_older_than_12_months() : SECURITY DEFINER sur une
    table FORCE RLS, renvoie 0 qu'elle ait purgé ou été refusée, jamais
    appelée depuis avril
  - Playwright E2E : 214 passed, 173 skipped, tous les parcours connectés
    dans les 173, `gh pr checks` vert du début à la fin

La preuve la plus gênante était dans gdpr-compliance-auditor lui-même,
dont la checklist affirmait « exportUserData() returns a complete JSON
bundle » (7 tables sur 14) et « executeDeletion() wipes » (aucun
appelant depuis avril). Il aurait validé les deux bugs qu'on a passé
deux jours à corriger.

Un agent créé, trois corrigés — plutôt qu'un agent par symptôme :

  silent-failure-auditor (nouveau, Opus) — question unique : « si ça
  s'arrêtait cette nuit, qu'est-ce qui serait différent demain matin ? ».
  Écritures à zéro ligne prises pour des succès, chemins privilégiés
  refusés en silence, catch qui mangent la preuve, tâches planifiées
  jamais armées, gates qui passent à vide, mesures déclarées jamais
  construites. Classe par durée d'invisibilité, pas par gravité.

  rls-flow-tester — testait uniquement le sens attaquant. Ajoute le sens
  privilégié : FORCE RLS s'applique aussi au propriétaire, un DEFINER
  possédé par postgres peut écrire 0 ligne sans erreur, et le postgres
  hébergé n'est pas celui de la stack locale. Exige des nombres de
  lignes mesurés, pas « aucune erreur ».

  security-auditor — les routes sous src/app/api/** échappent au matcher
  du proxy : pas de session, donc le seul garde-fou est celui qu'on
  écrit. Ajoute fail-closed, comparaison de digests (timingSafeEqual
  lève sur des longueurs inégales), pas de secret en URL, sémantique
  sans réessai, plafond qui doit être bruyant. Plus : la sous-permission
  est une vulnérabilité, art. 32(1)(b) tombe sans perte de données.

  gdpr-compliance-auditor — les deux affirmations fausses corrigées en
  questions à poser au code, et un chapitre « déclaré vs implémenté » :
  deux des trois constats les plus coûteux n'étaient pas des bugs, mais
  des phrases publiées dans cinq locales que le code ne soutenait pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @thierryvm, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ankora Ready Ready Preview, Comment Jul 27, 2026 12:25pm

@github-actions github-actions Bot added status:review-needed Ready for review type:chore Maintenance (deps, CI, tooling) labels Jul 27, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Guide du relecteur

Cette PR affine plusieurs agents d’audit QA et en introduit un nouveau pour se concentrer sur les défaillances silencieuses, en les faisant passer d’assertions de type checklist à des procédures fondées sur des preuves qui mettent l’accent sur le comportement mesuré, les chemins privilégiés et les garanties déclarées vs implémentées, tout en mettant à jour la documentation globale de CLAUDE pour intégrer le nouvel agent et clarifier quand chaque auditeur doit être exécuté.

Diagramme de flux pour la méthode silent-failure-auditor sur un mécanisme

flowchart TD
  A["Enumerate mechanism\n(e.g. audit log purge, cron, RLS path, CI gate)"] --> B["Ask: 'If this stopped tonight,\nwhat changes tomorrow?' "]
  B --> C{"Failure would be observable?"}

  C -- "Yes" --> D["Check implementation\nvia Read / Grep / Bash"]
  D --> E["Label findings\nMEASURED / READ_IN_CODE / INFERRED"]
  E --> F["Record mechanism + evidence\n+ visibility in findings table"]

  C -- "No" --> G["Mark SILENT_FAILURE_POSSIBLE"]
  G --> H["Describe observability gap:\nwhat signal would surface failure?"]
  H --> F

  F --> I["Rank by expected\nduration of invisibility"]
  I --> J["Emit report with verdict\n& observability recommendations"]
Loading

Modifications au niveau des fichiers

Changement Détails Fichiers
Transformer les vérifications de conformité RGPD de simples assertions statiques en questions guidées par les preuves, en vérifiant explicitement la couverture d’export, l’exécution des suppressions, les données persistantes, et les déclarations publiques vs le comportement réel.
  • Remplacement des déclarations en dur concernant exportUserData et executeDeletion par des instructions demandant de compter les tables lues et de vérifier les appelants et effets réels.
  • Ajout de consignes pour auditer les données qui survivent à l’effacement et pour traiter chaque droit RGPD comme une question à tester par rapport au code actuel.
  • Introduction d’une section « Déclaré vs implémenté » qui recoupe les déclarations visibles par l’utilisateur (messages, pages légales, documentation) avec des emplacements de code concrets et leurs bases juridiques, incluant une sortie sous forme de registre des déclarations.
.claude/agents/gdpr-compliance-auditor.md
Étendre le RLS flow tester pour couvrir les directions privilégiées et les refus RLS silencieux, et produire des preuves basées sur le nombre de lignes plutôt que de se reposer sur l’absence d’erreurs.
  • Extension de la description et du rôle de rls-flow-tester pour couvrir à la fois les chemins d’attaquant et les chemins privilégiés, incluant le contexte historique de l’incident H3.
  • Ajout d’une procédure « Direction privilégiée » qui inspecte FORCE RLS, les indicateurs de rôle BYPASSRLS, le comportement SECURITY DEFINER vs INVOKER, ainsi que les restrictions inter-schémas, et impose l’exécution d’écritures réelles et le rapport du nombre de lignes affectées.
  • Mise à jour des exigences de sortie pour inclure un tableau des chemins privilégiés, des exemples de refus silencieux, des remédiations pour la sur- et la sous-application, ainsi qu’un étiquetage explicite des environnements locaux vs hébergés.
.claude/agents/rls-flow-tester.md
Renforcer l’auditeur de sécurité pour les endpoints non authentifiés et planifiés, en mettant l’accent sur un comportement fail-closed, une gestion sûre des secrets, des limites de rayon d’explosion, et la sous-attribution de privilèges en tant que risque de sécurité.
  • Documentation du fait que les routes src/app/api/** contournent la couche proxy/session et ajout de points de checklist explicites pour les endpoints non authentifiés et cron (fail closed, comparaison via digest, absence de secrets dans les URLs, réponses minimales, sémantique de traitement par lots, et signaux forts).
  • Introduction de consignes selon lesquelles le sous-dimensionnement des permissions et les mécanismes de sécurité non exécutés sont des vulnérabilités au sens de l’art. 32(1)(b) du RGPD, et que ces chemins doivent être remis à silent-failure-auditor et rls-flow-tester.
  • Ajout d’une exigence de préflight avant toute opération sortante/destructive qui vérifie le compte CLI actif via npm run preflight.
.claude/agents/security-auditor.md
Introduire l’agent silent-failure-auditor afin de traquer systématiquement les mécanismes qui peuvent échouer tout en semblant réussir, et documenter sa place dans l’écosystème des agents QA.
  • Enregistrement de silent-failure-auditor dans CLAUDE.md en tant que 17ᵉ agent QA, documentation de son mandat, de son modèle, et des cas où il doit être exécuté conjointement avec security-auditor et rls-flow-tester.
  • Création de la spécification de l’agent silent-failure-auditor avec une liste détaillée de cibles (écritures à zéro ligne, chemins privilégiés refusés par RLS, erreurs avalées, tâches planifiées qui ne s’exécutent jamais, barrières CI vacuement réussies, et mesures déclarées mais non implémentées), sa méthodologie et un format de sortie centré sur les lacunes d’observabilité et les étiquettes de preuve.
  • Clarification dans CLAUDE.md des responsabilités mises à jour de rls-flow-tester (vérifications bidirectionnelles avec comptage de lignes) et des cas où silent-failure-auditor est requis pour les mécanismes qui protègent, enregistrent, prouvent ou nettoient.
CLAUDE.md
.claude/agents/silent-failure-auditor.md

Conseils et commandes

Interagir avec Sourcery

  • Déclencher une nouvelle revue : Commentez @sourcery-ai review sur la pull request.
  • Poursuivre les discussions : Répondez directement aux commentaires de revue de Sourcery.
  • Générer un ticket GitHub à partir d’un commentaire de revue : Demandez à Sourcery de créer un ticket à partir d’un commentaire de revue en y répondant. Vous pouvez également répondre à un commentaire de revue avec @sourcery-ai issue pour créer un ticket à partir de celui-ci.
  • Générer un titre de pull request : Écrivez @sourcery-ai n’importe où dans le titre de la pull request pour générer un titre à tout moment. Vous pouvez aussi commenter
    @sourcery-ai title sur la pull request pour (re)générer le titre à tout moment.
  • Générer un résumé de pull request : Écrivez @sourcery-ai summary n’importe où dans le corps de la pull request pour générer un résumé de PR à tout moment exactement là où vous le souhaitez. Vous pouvez également commenter @sourcery-ai summary sur la pull request pour (re)générer le résumé à tout moment.
  • Générer le guide du relecteur : Commentez @sourcery-ai guide sur la pull request pour (re)générer le guide du relecteur à tout moment.
  • Résoudre tous les commentaires Sourcery : Commentez @sourcery-ai resolve sur la pull request pour résoudre tous les commentaires Sourcery. Utile si vous avez déjà traité tous les commentaires et ne voulez plus les voir.
  • Ignorer toutes les revues Sourcery : Commentez @sourcery-ai dismiss sur la pull request pour ignorer toutes les revues Sourcery existantes. Particulièrement utile si vous voulez repartir de zéro avec une nouvelle revue – n’oubliez pas de commenter
    @sourcery-ai review pour déclencher une nouvelle revue !

Personnaliser votre expérience

Accédez à votre tableau de bord pour :

  • Activer ou désactiver des fonctionnalités de revue telles que le résumé de pull request généré par Sourcery, le guide du relecteur, et d’autres.
  • Changer la langue de la revue.
  • Ajouter, supprimer ou modifier des instructions de revue personnalisées.
  • Ajuster d’autres paramètres de revue.

Obtenir de l’aide

Original review guide in English

Reviewer's Guide

This PR refines multiple QA auditing agents and introduces a new one to focus on silent failures, shifting them from checklist-style assertions to evidence-based procedures that emphasize measured behavior, privileged paths, and declared-vs-implemented guarantees, while updating the global CLAUDE documentation to integrate the new agent and clarify when each auditor must run.

Flow diagram for silent-failure-auditor method on a mechanism

flowchart TD
  A["Enumerate mechanism\n(e.g. audit log purge, cron, RLS path, CI gate)"] --> B["Ask: 'If this stopped tonight,\nwhat changes tomorrow?' "]
  B --> C{"Failure would be observable?"}

  C -- "Yes" --> D["Check implementation\nvia Read / Grep / Bash"]
  D --> E["Label findings\nMEASURED / READ_IN_CODE / INFERRED"]
  E --> F["Record mechanism + evidence\n+ visibility in findings table"]

  C -- "No" --> G["Mark SILENT_FAILURE_POSSIBLE"]
  G --> H["Describe observability gap:\nwhat signal would surface failure?"]
  H --> F

  F --> I["Rank by expected\nduration of invisibility"]
  I --> J["Emit report with verdict\n& observability recommendations"]
Loading

File-Level Changes

Change Details Files
Turn GDPR compliance checks from static assertions into evidence-driven questions, explicitly checking export coverage, deletion execution, surviving data, and public claims vs actual behavior.
  • Replaced hard-coded claims about exportUserData and executeDeletion with instructions to count tables read and verify actual callers and effects.
  • Added guidance to audit what data survives erasure and to treat each GDPR right as a question to be tested against current code.
  • Introduced a "Declared vs implemented" section that cross-checks user-facing claims (messages, legal pages, docs) with concrete code locations and legal bases, including a claim ledger output.
.claude/agents/gdpr-compliance-auditor.md
Extend the RLS flow tester to cover privileged directions and silent RLS refusals, and to produce row-count-based evidence instead of relying on absence of errors.
  • Expanded description and role of rls-flow-tester to cover both attacker and privileged paths, including the historical H3 incident context.
  • Added a "Privileged direction" procedure that inspects FORCE RLS, role BYPASSRLS flags, SECURITY DEFINER vs INVOKER behavior, and cross-schema restrictions, and requires executing real writes and reporting affected row counts.
  • Updated output requirements to include a privileged-path table, silent refusal examples, remediation for both over- and under-enforcement, and explicit local vs hosted environment labelling.
.claude/agents/rls-flow-tester.md
Strengthen the security auditor for unauthenticated and scheduled endpoints, emphasizing fail-closed behavior, safe secret handling, blast-radius limits, and under-privilege as a security risk.
  • Documented that src/app/api/** routes bypass the proxy/session layer and added explicit checklist items for unauthenticated and cron endpoints (fail closed, digest comparison, no secrets in URLs, minimal responses, batch processing semantics, and loud caps).
  • Introduced guidance that under-permission and non-running security mechanisms are vulnerabilities under GDPR art. 32(1)(b), and that such paths should be handed to silent-failure-auditor and rls-flow-tester.
  • Added a preflight requirement before any outbound/destructive operation that verifies the active CLI account via npm run preflight.
.claude/agents/security-auditor.md
Introduce the silent-failure-auditor agent to systematically hunt mechanisms that can fail while appearing successful, and document its place in the QA agent ecosystem.
  • Registered silent-failure-auditor in CLAUDE.md as the 17th QA agent, documented its mandate, model, and when it must be run alongside security-auditor and rls-flow-tester.
  • Created the silent-failure-auditor agent spec with a detailed hunt list (zero-row writes, privileged paths refused by RLS, swallowed errors, scheduled work that never runs, vacuous CI gates, and unimplemented declared measures), methodology, and output format focused on observability gaps and evidence labels.
  • Clarified in CLAUDE.md the updated responsibilities of rls-flow-tester (two-direction checks with row counts) and when silent-failure-auditor is required for mechanisms that protect, record, prove, or clean up.
CLAUDE.md
.claude/agents/silent-failure-auditor.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@thierryvm
thierryvm merged commit fc0825b into main Jul 27, 2026
11 checks passed
@thierryvm
thierryvm deleted the chore/agents-silent-failure-coverage branch July 27, 2026 12:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:review-needed Ready for review type:chore Maintenance (deps, CI, tooling)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant