Ga naar hoofdinhoud
AcademytutorialHydra leerlijn — Deel 3: Quality gates

Hydra leerlijn — Deel 3: Quality gates

Wat zijn de mechanische quality gates van Hydra, waarom werken ze juist NIET met AI-oordeel, en wat doe je als een gate per ongeluk fout slaat? Derde van zes korte modules.

TutorialHydraGatesQualityTutorial series
10 min read

In deel 2 zag je dat de drie personas worden aangevuld door mechanische quality gates — checks die deterministisch slagen of falen, zonder AI in de loop. Dit deel legt uit welke gates we hebben, waarom ze mechanisch zijn, en hoe je omgaat met de uitzondering: de fout positief.

Waarom mechanische gates?

AI-review schaalt mee, maar is niet voorspelbaar. Twee runs op precies dezelfde diff kunnen verschillende bevindingen opleveren. En AI is juist niet sterk in het saaie nakijkwerk dat geen oordeel vraagt — bijvoorbeeld: "heeft elke nieuwe PHP-file bovenaan een SPDX-licentie-header staan?". Zo'n check kun je veel sneller en goedkoper met een simpel grep-commando doen.

De stelregel binnen Hydra:

Wat objectief te controleren is, doet een mechanische gate — één script dat per check slaagt of faalt. Pas waar je oordeel nodig hebt, schakelen we een reviewer in.

Concreet betekent dat: voor we Juan Claude of Clyde duur Sonnet-tijd laten verspillen aan "deze functie heet doSomething en dat zou een werkwoord-zelfstandig-naamwoord-paar moeten zijn", draaien we eerst PHPCS. Pas daarna gaan de AI-paar-ogen aan het werk, en die concentreren zich op wat een tool níet kan vangen.

Categorie 1: generieke code-quality tools

Deze checks draaien in scripts/run-quality.sh inside een Docker php:X.Y-cli container, met --keep-server om Nextcloud daarna voor de browser-tests te laten staan:

ToolWat het vangt
lintSyntax-fouten in PHP.
phpcsCoding standard (PSR-12 + Nextcloud-conventie).
phpmdCode-mess detector (te lange methodes, te diepe nesting, dode code).
psalmStatische type-analyse, level 4 baseline.
phpstanTweede statische type-analyser (vangt dingen die Psalm mist en vice versa).
phpmetricsComplexiteits-metriek (cyclomatic, maintainability-index).
composer auditCVE-check op composer.lock-dependencies.
eslintJS-/TS-lint.
stylelintCSS-/SCSS-lint.
npm auditCVE-check op package-lock.json.
PHPUnitUnit + integration tests met een gecontaineriseerd Nextcloud + SQLite.
NewmanAPI-tests tegen de PHP built-in server.

Iedere check is rood of groen. Eén rode check → build:fail (in de pre-review-fase) of code-review:fail / security-review:fail (post-fixes).

Categorie 2: Hydra-specifieke gates

Naast de generieke tools heeft Hydra een eigen reeks hydra-gate-* skills voor zaken die Conduction-specifiek zijn. Die zitten in hydra/.claude/skills/. De huidige set telt 14 skills: één dispatcher die de andere 13 mechanische gates achter elkaar aanroept.

GateWat het controleert
hydra-gatesDispatcher: roept de 13 gates hieronder achter elkaar aan en levert één pass/fail-overzicht.
hydra-gate-spdxIedere nieuwe PHP-file heeft een EUPL-1.2 SPDX-header.
hydra-gate-forbidden-patternsGeen dd(, var_dump, dump(, console.log blijft over.
hydra-gate-composer-auditMirror van de generieke composer-audit, voor de reviewer's mandatory block.
hydra-gate-stub-scanGeen lege stub-methods met TODO's blijven over.
hydra-gate-route-authIedere route in appinfo/routes.php heeft expliciete auth-annotaties.
hydra-gate-orphan-authGeen @AuthorizedAdminSetting op een endpoint dat niet bestaat.
hydra-gate-unsafe-auth-resolverAuth resolvers slaan geen claim-bypass.
hydra-gate-semantic-authAuthorisatie-logica klopt semantisch met de route-doel.
hydra-gate-admin-routerAdmin-routes hebben de admin-router en niet de gewone.
hydra-gate-no-admin-idorGeen Insecure Direct Object Reference op admin-endpoints.
hydra-gate-modal-isolationModal components isoleren state correct.
hydra-gate-nc-input-labelsNextcloud <NcTextField>-componenten hebben <label>-koppeling.
hydra-gate-initial-stateGeen initialState lekt naar de client zonder bewuste expose.

Een groot deel hiervan is geboren uit incidenten: een gate ontstaat als reactie op een bug die door alle eerdere mazen heen glipte. hydra-gate-stub-scan bijvoorbeeld kwam uit het decidesk-44-45 retrospectief, waar een builder een methode opleverde die slechts return null; deed met een TODO. PHPCS slikte het, PHPUnit had geen test, code review las het als "in scope", en het brak in productie.

ADR-020: gate-scope is de PR-diff

Een belangrijke regel die je in deel 2 al kort tegenkwam: gates draaien op de PR-diff, niet op de hele repo. Dat staat in ADR-020.

Waarom? Omdat veel van onze repo's een achterstand aan technische schuld dragen. Als je phpcs op de hele repo loslaat krijg je honderden findings die niets met de huidige PR te maken hebben — en wordt elke PR rood. Door de scope te beperken tot de regels die in de PR-diff aangeraakt worden, valt de gate alleen om wat de huidige builder net heeft toegevoegd of gewijzigd.

Override-mechanisme: HYDRA_REVIEW_SCOPE=full in secrets/.env zet de scope om naar de hele repo. Gebruik dat als je een nieuwe repo onboardt of een dedicated tech-debt-sweep doet. Verwacht in alle andere gevallen veel rood.

Fout-positieven herkennen

Mechanische gates zijn deterministisch, maar niet altijd correct. Een klassieker uit eigen huis: hydra-gate-forbidden-patterns zocht naar dd( zonder word-boundary, en sloeg dus ook foutief alarm op een legitieme $builder->add(. Eén grep-flag verkeerd en je hebt een herhaalbare fout-positief.

Hoe je een fout-positief herkent:

  1. De gate slaat steeds op dezelfde regel in elke retry, terwijl de regel zelf onschuldig oogt.
  2. Een handmatige test van de gate-implementatie (open scripts/run-quality.sh of de bijbehorende skill, run hem lokaal) bevestigt het: ja, het patroon matched, maar niet om de bedoelde reden.
  3. Niemand in het team kan uitleggen waarom dit specifieke regel-niveau erover zou moeten klagen.

In dat geval is de fix niet "nog een retry". De fix is: ga naar scripts/run-quality.sh of de gate-skill, en repareer de detectie. Voorbeeld voor hydra-gate-forbidden-patterns: van grep 'dd(' naar grep -wE '(^|[^A-Za-z0-9_])dd\('.

Recheck na reviewer-fixes

Een subtiele maar belangrijke regel: na elke reviewer-fix-cyclus draait de orchestrator opnieuw de mechanische gates op de uiteindelijke PR-staat. Dit is de "quality-recheck"-fase. Reden: een reviewer kan tijdens zijn bounded-fix wel een PHPCS-style fout repareren en daarbij per ongeluk een nieuwe overtreding introduceren.

Slaat de recheck rood, dan gaat het issue naar needs-input. Geen retry. De reviewer is gestopt, de bouwer is gestopt, een mens kijkt.

Test jezelf

Vier korte vragen om te checken of je dit deel begrepen hebt. Vastgelopen? Klik Hint. Curieus naar het antwoord? Klik Antwoord.

1. Waarom heeft Hydra mechanische gates en AI-reviewers, en niet alleen één van de twee?

Hint

Eén soort check is voorspelbaar en goedkoop, de andere is duur maar kan oordelen. Wat is sterk en zwak aan elk?

Antwoord

Ze vullen elkaar precies aan op de plekken waar de ander zwak is.

  • Mechanische gates zijn deterministisch: zelfde input → zelfde uitkomst, slagen of falen. Perfect voor saai, objectief nakijkwerk — bijvoorbeeld "heeft elke nieuwe PHP-file een SPDX-header?". Goedkoop en herhaalbaar.
  • AI-reviewers doen oordeel: "klopt deze authorization-logica semantisch met het route-doel", "is dit in deze context een veiligheidsrisico". Niet voorspelbaar en duurder, dus zet je ze in waar oordeel nodig is.

Alleen-mechanisch mist context-afhankelijke fouten; alleen-AI is duur, niet voorspelbaar, en verspilt Sonnet-tijd aan dingen die een grep kan.

2. Wat zegt ADR-020 over de gate-scope, en wanneer schakel je dat via HYDRA_REVIEW_SCOPE=full uit?

Hint

Denk aan wat er gebeurt als je phpcs op een repo met veel oude technische schuld loslaat. En wanneer wil je dat juist wél?

Antwoord

ADR-020 zegt: gates draaien op de PR-diff, niet op de hele repo.

Reden: veel repo's slepen technische schuld mee. phpcs over alles geeft honderden findings die niets met de huidige PR te maken hebben — elke PR zou rood worden. Door alleen de geraakte regels te checken slaat de gate alleen om op wat de builder net heeft toegevoegd of gewijzigd.

HYDRA_REVIEW_SCOPE=full in secrets/.env schakelt dit uit — gates draaien dan op de hele repo. Gebruik bij:

  • Onboarding van een nieuwe repo in Hydra.
  • Een dedicated tech-debt-sweep.

NIET voor reguliere PRs — verwacht veel rood.

3. Hoe herken je een fout-positieve gate, en wat is de juiste fix? Wat NIET?

Hint

Drie signalen samen wijzen op een fout-positief. En de "verleidelijke maar verkeerde" reflex is hetzelfde nóg een keer doen.

Antwoord

Herkenning — drie signalen samen:

  1. De gate slaat in elke retry op dezelfde regel, terwijl die regel onschuldig oogt.
  2. Handmatig lokaal de gate runnen bevestigt: het patroon matched, maar niet om de bedoelde reden (bijv. grep 'dd(' matcht ook $builder->add().
  3. Niemand in het team kan uitleggen waarom dit specifieke regel-niveau erover zou moeten klagen.

Juiste fix: scherp de gate-detectie aan in scripts/run-quality.sh of de bijbehorende skill. Voorbeeld: grep 'dd('grep -wE '(^|[^A-Za-z0-9_])dd\('.

NIET: nog een keer retry:queued. Dat reproduceert hetzelfde fout-positief en verspilt cycles.

4. Waarom draait Hydra de mechanische gates nogmaals NA de reviewer-fixes (de "quality-recheck"-fase)?

Hint

De reviewers mogen binnen scope zelf fixen. Wat kan daarbij misgaan?

Antwoord

Reviewers (Juan + Clyde) mogen binnen scope mechanische fixes pushen (ADR-021). Daarbij kan een reviewer per ongeluk een nieuwe overtreding introduceren — bijvoorbeeld een PHPCS-style fix die elders een regel breekt.

De quality-recheck draait alle mechanische gates opnieuw op de uiteindelijke PR-staat zodat zeker is dat wat er nu staat ook objectief schoon is. Slaat de recheck rood → needs-input, geen retry. De reviewer is gestopt, de bouwer is gestopt, een mens kijkt. Het is de laatste deterministische gate vóór er een menselijke beslissing valt.

Volgende stap

In deel 4 kijken we naar de skills en commands die de personas tijdens hun werk gebruiken — inclusief hoe je zelf een nieuwe skill kunt toevoegen.