Skip to main content
AcademytutorialHydra leerlijn — Deel 5: Een Hydra-run starten op een echte app

Hydra leerlijn — Deel 5: Een Hydra-run starten op een echte app

Het hele recept van issue tot groene PR, inclusief de HYDRA_LABEL_PREFIX-instelling waarmee meerdere devs gelijktijdig Hydra kunnen draaien zonder elkaars labels te overschrijven. Vijfde van zes korte modules.

TutorialHydraOperationsSetupLabelsTutorial series
12 min read

In de eerste vier delen ging het om concept, pipelines, gates en skills. Tijd om te draaien. In dit deel start je een complete Hydra-run op een doel-app, met aandacht voor de praktische valkuilen: tokens, images, en — als je met meerdere devs aan dezelfde repo's werkt — de HYDRA_LABEL_PREFIX-truc.

Stap 1: clone en bekijk

git clone git@github.com:ConductionNL/hydra.git
cd hydra
cat CLAUDE.md

CLAUDE.md is de canonieke architectuur-doc. Goed om voor de eerste keer doorheen te scrollen. Daarna naar secrets/ — die map zit niet in git en moet je zelf vullen.

Stap 2: credentials.json

Hydra leest één bestand voor alle Claude-OAuth-tokens en alle GitHub-PATs:

{
  "claude_tokens": [
    { "name": "account-1", "token": "sk-ant-oat-…" },
    { "name": "account-2", "token": "sk-ant-oat-…" }
  ],
  "git_tokens": {
    "builder":  "gho_…",
    "reviewer": "gho_…",
    "security": "gho_…"
  }
}

Drie aparte GitHub-PATs. Geen gedeelde org-admin tokens. De reden zag je in deel 1: elk persona heeft zijn eigen scope.

Heb je meerdere Claude Max accounts? Zet ze allemaal in claude_tokens in volgorde van voorkeur. run_container_with_fallback() rouleert automatisch op rate-limit.

Stap 3: secrets/.env

De niet-credential overrides. Cruciaal — verschillende voor productie/CI dan voor je laptop.

# Welke images en tag te gebruiken. Lokaal bouw je vaak met -t localhost/hydra-*:test.
HYDRA_IMAGE_PREFIX=localhost/hydra
HYDRA_IMAGE_TAG=test

# Optioneel: label-namespace zodat je collega's niet op je tenen staat.
HYDRA_LABEL_PREFIX=wilco

# Default review-scope (ADR-020): alleen PR-diff. Zet op 'full' alleen bij onboarding van een repo.
HYDRA_REVIEW_SCOPE=diff

Tip: de defaults zijn ghcr.io/conductionnl/hydra als prefix en latest als tag. Op productie/CI laat je HYDRA_IMAGE_* weg en pakt Hydra die defaults op.

scripts/hydra-supervisor.sh en scripts/orchestrate.sh sourcen dit bestand zelf bij opstart. Eerder moest je het in je shell exporteren — een silly bug die maanden onopgemerkt bleef en gefixt is als onderdeel van het decidesk-44-45 retrospectief (april 2026). Iedere nieuwe long-running entrypoint MOET dit bestand sourcen aan de start; dat is een repo-wide contract.

Stap 4: HYDRA_LABEL_PREFIX — waarom en hoe

Default werkt Hydra met kale labels: build:queued, code-review:running, security-review:pass, enzovoort. Eén supervisor draait, eén pipeline tegelijk per issue. Geen probleem.

Maar: zodra meerdere devs lokaal Hydra draaien tegen dezelfde target-repo's gaat het knetteren. Jouw supervisor schrijft build:queued. De supervisor van een collega ziet datzelfde label, pakt het op, gaat zijn build doen. Resultaat: race-conditions, dubbele PRs, labels die over elkaar pingen.

HYDRA_LABEL_PREFIX is de oplossing. Zet hem in secrets/.env:

HYDRA_LABEL_PREFIX=wilco

Vanaf dat moment prefixed Hydra ELK door hem beheerd label met wilco-:

Default (geen prefix)Met HYDRA_LABEL_PREFIX=wilco
ready-to-buildwilco-ready-to-build
build:queuedwilco-build:queued
build:runningwilco-build:running
code-review:queuedwilco-code-review:queued
security-review:passwilco-security-review:pass
applier:failwilco-applier:fail
needs-inputwilco-needs-input
retry:queued, rebuild:queuedwilco-retry:queued, wilco-rebuild:queued
yolo, openspec (metadata)niet geprefixed — gedeelde metadata

Jouw Hydra leest én schrijft alleen labels in zijn eigen namespace. De supervisor van een collega met HYDRA_LABEL_PREFIX=jan werkt in een andere namespace, dus jullie zien elkaars in-flight werk niet en blokkeren elkaar niet.

Constraints op de waarde

  • Regex: ^[a-z0-9]([a-z0-9-]{0,14}[a-z0-9])?$ — 1 t/m 16 tekens, lowercase a-z 0-9 en koppelteken, niet beginnen of eindigen met een streepje.
  • Empty / unset = default-gedrag (geen prefix). Dat is ook wat productie/CI altijd draait.
  • Wijzigen tijdens een lopende run = NIET DOEN. Eerst pijplijn drainen.

Labels seeden op een target-repo

Voor je eerste run met een nieuwe prefix moet je de labels op de doel-repo's eenmalig aanmaken:

./scripts/create-labels.sh --repo ConductionNL/<app> --prefix wilco

Idempotent — herhaalde aanroepen overschrijven alleen kleuren. Het script seedt ook de juiste kleurcodes (rood/oranje/geel/groen/blauw) zodat het board in één oogopslag leesbaar blijft.

Wanneer wel, wanneer niet

SituatiePrefix gebruiken?
Productie / GitHub Actions / cron-managed deploymentNee. Laat hem leeg. Productie = de canonieke labels.
Lokale dev-werkstation, één Hydra-instanceKan, hoeft niet. Werkt zonder ook.
Lokaal, meerdere devs, gedeelde target-repo'sJa. Iedereen z'n eigen prefix.
Demo / experiment op een target-repo waar productie ook draaitJa, zeker. Anders pakt productie-Hydra je demo-issue op.

Stap 5: build de container images

In CI worden alle images door GitHub Actions gepushd naar GHCR. Lokaal — als je HYDRA_IMAGE_PREFIX=localhost/hydra hebt staan — bouw je ze zelf:

# Base image: Nextcloud + PostgreSQL + OpenRegister (afhankelijkheid van builder)
docker build -t ghcr.io/conductionnl/hydra-nextcloud-test:stable32 \
    -f images/nextcloud-test/Dockerfile .

# De drie pipeline images
docker build -t localhost/hydra-builder:test  -f images/builder/Dockerfile  .
docker build -t localhost/hydra-reviewer:test -f images/reviewer/Dockerfile .
docker build -t localhost/hydra-security:test -f images/security/Dockerfile .

Daarna controleer met docker images | grep hydra of de drie tags er staan. Reviewer en security zijn standalone (geen NC-dependency); de builder leunt op de hydra-nextcloud-test base.

Stap 6: start de supervisor

# Foreground voor je eerste run zodat je live ziet wat gebeurt
./scripts/hydra-supervisor.sh

Wat je verwacht te zien:

[supervisor] loaded HYDRA_LABEL_PREFIX=wilco — restricting to own namespace
[supervisor] image prefix=localhost/hydra tag=test
[supervisor] pool size=5, slots free=5
[supervisor] polling …

In een tweede terminal kun je de logs volgen:

tail -f logs/supervisor.log
ls /tmp/hydra-slots/    # zien welke slots in gebruik zijn

Stap 7: trigger een issue

In de doel-repo open je een issue. Op dat issue zet je:

  • Het trigger-label. Met prefix: wilco-ready-to-build. Zonder prefix: ready-to-build.
  • Optioneel yolo — niet geprefixed, want metadata.
  • Optioneel openspec als je via OpenSpec werkt.

Voorbeeld via gh:

# Met prefix (lokale dev-run):
gh issue edit <N> --repo ConductionNL/<app> --add-label "wilco-ready-to-build"

# Of via de Hydra-helper (synct meteen het board):
./scripts/hydra-label.sh ConductionNL/<app> <N> add ready-to-build

scripts/hydra-label.sh is de voorkeursweg: hij respecteert HYDRA_LABEL_PREFIX automatisch, gebruikt de label-helpers uit scripts/lib/labels.sh, en synct het bijbehorende GitHub Project-bord in real-time. Direct gh issue edit --add-label werkt ook, maar dan blijft het board "in de oude kolom" tot reconcile (om de 10 minuten) het opraapt.

Stap 8: volg de pijplijn

Zodra de supervisor het issue ziet, gebeurt dit (geprefixed of niet, afhankelijk van je config):

ready-to-build → build:queued → build:running → build:pass
              ↳ code-review:queued → :running → :pass / :fail
              ↳ security-review:queued → :running → :pass / :fail
              ↳ applier:queued → :running → :pass / :fail → done / needs-input

Je volgt het op drie plekken:

  1. logs/supervisor.log — wat de supervisor doet.
  2. GitHub-issue — labels veranderen automatisch.
  3. GitHub PR — pipeline-status-comment wordt elke fase bijgewerkt door scripts/lib/pipeline-comment.sh.

Heb je yolo gezet en is alles groen? Dan zie je done op het issue verschijnen, een goedkeuring op de PR, en een auto-merge. Geen yolo? Dan staat de PR klaar voor een mens om te mergen.

Troubleshooting

Eerste-run problemen

Supervisor kiest standaard ghcr.io images terwijl ik lokaal heb gebouwd

Controleer dat secrets/.env daadwerkelijk HYDRA_IMAGE_PREFIX=localhost/hydra en HYDRA_IMAGE_TAG=test bevat — en dat het bestand bestaat in secrets/ en niet in je home-dir. De supervisor en orchestrate.sh sourcen secrets/.env via een vast pad relatief aan de repo-root.

Issue blijft op wilco-ready-to-build staan, supervisor pakt 'm niet op

Heb je ./scripts/create-labels.sh --repo ... --prefix wilco al gedraaid? Zonder de geprefixeerde labels op de doel-repo kan de supervisor wel polled, maar GitHub geeft geen matches terug. Verifieer met gh label list --repo ConductionNL/<app> | grep wilco.

Build start, geen image found error

docker images | grep hydra-builder. Geen treffer? Bouw eerst de base image (hydra-nextcloud-test) en daarna pas de builder. De builder-Dockerfile heeft de base als FROM.

Issue gaat direct naar wilco-needs-input zonder dat ik iets zie

Stale labels van een vorige run blijven hangen. Strip de oude stage-labels met ./scripts/hydra-label.sh ConductionNL/<app> <N> transition ready-to-build — dat zet de issue terug in een definieerde startstate.

HYDRA_LABEL_PREFIX validatie faalt op startup

Regex is ^a-z0-9?$. Geen hoofdletters, geen leestekens, geen underscore, geen leading/trailing streepje. Max 16 tekens.

Test jezelf

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

1. Wat is het probleem dat HYDRA_LABEL_PREFIX oplost, en wanneer hoef je hem expliciet NIET te zetten?

Hint

Wat gebeurt er als twee Hydra-instances tegelijk dezelfde target-repo bekijken? En waarom is dat juist géén probleem in productie?

Antwoord

Probleem dat het oplost: zodra meerdere devs lokaal tegelijk Hydra draaien tegen dezelfde target-repos zien hun supervisors elkaars labels en pakken elkaars werk op. Race-conditions, dubbele PRs, labels die over elkaar pingen. Met een eigen prefix (wilco-build:queued) heeft elke dev zijn eigen namespace.

Wanneer NIET zetten:

  • Productie / GitHub Actions / cron-managed deployment — daar wil je juist de canonieke (kale) labels: één bron van waarheid, geen verdwaalde dev-prefix.
  • Lokale solo-run zonder collega's op dezelfde repos — werkt zonder ook; mag wel maar hoeft niet.

2. Welke labels worden door HYDRA_LABEL_PREFIX WEL en welke NIET geprefixed?

Hint

Wat Hydra zelf doorzet in zijn state-machine wordt geprefixed. Wat over het werk-zélf gaat (eigenschap van het issue, geen pipeline-state) wordt gedeeld.

Antwoord
  • WEL geprefixed: stage-labels (ready-to-build, build:queued/running/pass/fail, code-review:*, security-review:*, applier:*) en herstel-labels (retry:queued, rebuild:queued, needs-input). Allemaal labels die Hydra zelf beheert in de state-machine.
  • NIET geprefixed: metadata die over de aard van het werk gaat, niet over een Hydra-instance: yolo (mag auto-mergen) en openspec (volgt OpenSpec-werkmodel). Twee devs op één issue zijn het immers eens over of het een yolo-issue is — geen reden om dat per-dev op te splitsen.

3. Waarom roep je ./scripts/hydra-label.sh aan in plaats van gh issue edit --add-label bij het triggeren van een run?

Hint

Twee dingen waar het script automatisch voor zorgt en de directe gh-aanroep niet.

Antwoord

Twee redenen:

  1. Respecteert HYDRA_LABEL_PREFIX automatisch — je tikt add ready-to-build, het script maakt er wilco-ready-to-build van. Met gh issue edit --add-label moet je de prefix met de hand meetypen en bij elke verandering aanpassen — recipe voor typos en namespace-drift.
  2. Synct het GitHub Project-bord direct. gh issue edit werkt ook, maar het board blijft dan "in de oude kolom" tot reconcile.sh (om de 10 minuten) het opraapt. Met hydra-label.sh zie je de kolom-overgang meteen.

4. Wat gebeurt er als je HYDRA_LABEL_PREFIX wijzigt terwijl er nog een Hydra-run loopt? En wat is de juiste werkwijze als je toch wilt wijzigen?

Hint

De lopende run kijkt naar labels in de oude namespace, jouw nieuwe supervisor in een andere. Wat blijft er over?

Antwoord

Wat er gebeurt: de lopende run kijkt naar labels in de oude namespace; jouw supervisor met nieuwe prefix kijkt naar een andere set. Lopend werk wordt door niemand meer opgepakt (orphaned), of erger — labels van beide namespaces komen op één issue terecht.

Juiste werkwijze:

  1. Zet geen nieuw werk meer in de oude prefix.
  2. Drain de pijplijn: wacht tot alle issues met de oude prefix op done of needs-input staan.
  3. Pas dán HYDRA_LABEL_PREFIX aanpassen in secrets/.env en de supervisor herstarten.
  4. ./scripts/create-labels.sh --repo ... --prefix <nieuw> om de nieuwe namespace op de doel-repo's te seeden.

Volgende stap

Pijplijn loopt? Mooi. Maar wat doe je als het mis gaat? Deel 6 behandelt herstel- en escalatie-patronen.