Naar de inhoud
Koen Holman
← Terug naar de projecten

AI-projecten · de laag eronder

Losse projecten, één engineeringstandaard

Elk project dat ik bouw heeft z'n eigen repo, eigen issues, eigen architectuur en eigen workflow, en is los van de rest te ontwikkelen. deSchouwVloot is de laag daarboven: het legt vast hoe er gebouwd, gecontroleerd en vrijgegeven wordt — dezelfde poorten, dezelfde checks, en per project een keuze in hoeveel er zonder mij doorgaat. Elke bewering hieronder is na te trekken in de publieke repo.

Hoe het op elkaar staat

Vier repo's draaien erop, en er is geen regel gedeelde logica geforkt. Deze site is er zelf één van.

overkoepelenddeSchouwVlootgedeelde workflows · één contract per project · per project instelbaar hoeveel er zonder mij doorgaat
  • BiohackOS

    Flutter-app, privérepo, eigen constitution.md en een eigen testset waar de checks op leunen.

    eigen repoeigen issueseigen workflow
  • koenholman.nl

    Deze site. Andere taal, andere stack, geen eigen server — en toch dezelfde poort en dezelfde checks.

    eigen repoeigen issueseigen workflow
  • Een privéproject

    Privé, en van een heel andere vorm dan waarvoor de vloot ooit gebouwd is. Aangehaakt zonder uitzondering in het contract.

    eigen repoeigen issueseigen workflow
De vierde repo is de vloot zelf: die haalt z'n eigen wijzigingen door dezelfde poort als de rest. Wat een project deelt is de vorm — wat het bouwt, en wat daar wel en niet mag, bepaalt het project zelf.

De route van issue tot live

Zes stappen, en er is geen centrale inbox waar ze doorheen moeten. Wat een project deelt is de vólgorde en de poorten — niet de plek waar het werk binnenkomt.

01Een issue begint waar het thuishoort

Werk ontstaat in de repo van het project dat het raakt: daar staat de code, daar staan de grenzen van dat project, en daar is te beoordelen of een idee er überhaupt in past. Eén zin is genoeg om te beginnen.

02Het project levert de context

Het issue wordt behandeld met de .fleet.yml van dát project: eigen lanes, eigen poorten, eigen labelnamen, eigen budgetten. De gedeelde workflows kennen die waarden niet — ze krijgen ze binnen als input van de aanroeper.

03Triage en plan gaan vooraf aan code

Triage bepaalt wat voor werk het is; plannen toetst het aan de vastgelegde grenzen van het project en bakent de scope af. Pas daarna mag er gebouwd worden.

04Bouwen op een eigen machine, met een limiet

De agent voert het plan uit op een runner van het project zelf, met een turn-budget en een allowlist per commando. Loopt hij vast, dan stopt hij vanzelf in plaats van het budget op te maken.

05Zeven bewakingen moeten groen zijn

De tests draaien over de volledige rekenkern van het project, niet alleen over het stukje dat net veranderde. Alleen de tests en de deterministische checks kunnen zelf tegenhouden.

06Live, en nooit half

Pas als een epic in zijn geheel binnen is, gaat het naar buiten — met mijn goedkeuring op de release-knop van dát project, op dát project z’n eigen moment.

Zelfstandig, maar niet apart geregeld

Een project houdt alles wat het eigen maakt in eigen hand: eigen doelen, eigen grenzen in constitution.md, eigen tests, eigen releasecadans. Wat het deelt is de vorm eromheen — per station een caller van een paar regels naar de gedeelde workflows, en verder niets. Zo draait BiohackOS op dezelfde standaard als deze site, zonder dat het ene project iets van het andere hoeft te weten.

Het dragende mechanisme is dat een reusable workflow draait in de context van de aanroeper: runs-on resolvet tegen de runnerpool van de aanroepende repo, github.repository is de aanroepende repo, en secrets: inherit geeft diens secrets door. De vloot levert de logica, het project levert de hardware en de secrets. Het contract tussen beide is één bestand: .fleet.yml — lanes, poorten, budgetten, labelnamen en de spine.

vier repo's · nul forks

De test of de abstractie draagt

Twee van die vier hebben een compleet andere vorm dan het project waarvoor de vloot ooit gebouwd is — andere taal, andere stack, geen eigen server, alleen door GitHub geleverde machines. Voor geen van beide is één regel gedeelde logica gekopieerd. Dat is de echte toets: niet of een contract denkbaar generiek is, maar of een project dat er niet op ontworpen is er zonder uitzonderingen in past.

Bekijk in repo →

Wat een project meebrengt aan kénnis blijft daar ook. Naast constitution.md en doelen.md heeft een project vaak z'n eigen retrieval: RAG die antwoorden ophaalt uit de eigen documentatie of logs, en een eigen MCP-server die de agent gestructureerde toegang geeft tot de tools en data van dat project. deSchouwVloot blijft daarbuiten — het bevat geen domeinlogica, dus die kennis hoort bij het project, niet bij de machinerie eromheen.

De zijingang voor het losse idee

Soms heb ik alleen een zin en nog geen plek. Daar is één poort voor, die er een bestemming bij zoekt en het issue daarnaartoe verplaatst — en weet hij het niet zeker, dan kiest hij niet maar vraagt hij het. Het meeste werk gaat daar niet langs: dat begint gewoon in de repo waar het thuishoort.

intake.yml is workflow_call-only, net als alle zestien workflows; een guard-script bewijst dat bij elke PR, zodat de poort nooit vanzelf kan starten. De keuze zelf telt hele woorden per consument, en weegt een genoemd bestandspad vijf keer zo zwaar als een trefwoord — een pad zegt iets over de plááts, een woord alleen over het onderwerp. Gelijkspel of nul punten levert geen keuze op maar een label, en het issue blijft staan met een vraag.

Wat er tegenhoudt

Zeven bewakingen moeten groen zijn voordat er iets verdergaat. Menselijke review blijft het inhoudelijke oordeel; alleen de tests en de deterministische checks kunnen zelf tegenhouden. De zeven controles, de nulmeting eronder en de vier keren dat alles groen stond terwijl het stuk was, staan op de ontwerpkeuzes, met een bron per bewering.

impactanalyse · van vinkje naar bewijs

Een bewering die een script kan tegenspreken

Elke feature-PR draagt een klein codeblok: db=geen, net=bestaand, risico=laag — twaalf velden, elk met een gesloten antwoordlijst. Dat verving een checklist van tien vinkjes die alleen toetste of ze waren aangevinkt, niet of ze ook klopten. Nu haalt de check de daadwerkelijke bestandenlijst van de PR op en houdt elk antwoord ertegen: claim je db=geen terwijl er een migratie in de diff zit, dan is dat aantoonbaar onwaar — en gaat de PR rood, zonder dat ik ernaar hoef te kijken.

Bekijk in repo →

Fail-closed is de stand bij twijfel. Miste een project een script dat een station wél eist, dan draaide dat station rood en mergede het per constructie nooit meer — dat was incident I28. De eis wordt nu uit de stationsdefinitie zelf afgeleid in plaats van uit een handlijst die achterloopt.

Hoeveel mag het zelf?

Per project zet ik één knop aan of uit. Staat hij uit, dan wacht nieuw werk op mijn goedkeuring. Staat hij aan, dan gaat werk dat door alle controles komt vanzelf door. Het is een repo-variabele en geen commit — een schakelaar die een merge nodig heeft is geen schakelaar.

Het verschil tussen aan en uit is kleiner dan het klinkt. In allebei de standen kan ik overal ingrijpen. Wat omdraait is alleen wat er gebeurt als ik niets doe: uit betekent dat er gewacht wordt, aan betekent dat er doorgegaan wordt. En als iets niet duidelijk is, wacht het — een configuratiebestand dat niet te lezen is, een ontbrekend onderdeel, een typefout in de stand gaat allemaal terug naar mij, met de reden erbij. Twijfel telt hier niet als groen licht.

gevoeligAlles wat gevoelig ligt

Wachtwoorden, wijzigingen aan de database, en de bouwstraat zelf. Ook op de autonome stand.

noodremEén knop terug naar handmatig

Een variabele die het hele project in één handeling terugzet naar mens-in-de-lus, zonder dat ik eerst iets hoef te begrijpen.

stoplabelEen markering op één voorstel

Precies dat ene stilzetten, zonder de stand van het hele project om te gooien.

breakingElke wijziging die bestaand gedrag breekt

Die wacht altijd, in elke stand — dat staat niet in een variabele maar in code.

De volgorde ligt vast in één resolver, per poort getest: gevoelig pad, noodrem, stoplabel, breaking change, en pas daarna de stand zelf. Dat is geen detail. Ik bouwde er ooit een tweede route naast — werk dat een onafhankelijke controle had doorstaan mocht ook zonder mij door — en die keek niet naar de noodrem en niet naar het stoplabel. Hij deed het juiste, maar de rem die erboven hing werkte er niet op, en dat merk je niet: er gaat niets rood. Er is nu een test die controleert dat béide routes op dezelfde situatie hetzelfde antwoorden.

Waarom er zo weinig bij mij hoeft te komen

Omdat het meeste zichzelf oplost voordat het bij mij komt. Loopt er iets vast, dan wordt dat opgemerkt. Valt een controle om, dan probeert de pijplijn de fout eerst zelf te herstellen. Botst een voorstel met werk dat er intussen bij is gekomen, dan wordt ook dat eerst zelf opgelost. En wat af is, wordt wekelijks opgeruimd — via de PR-historie en niet via een git-ancestor-check, want deze repo's squash-mergen. Zelf herstellen is de regel; bij mij aankloppen de uitzondering.

pr-autofixpr-conflict-solverepic-orchestratorbranch-janitor

Ook de documentatie bewaakt zichzelf. Een overzicht van de code hield ik vroeger met de hand bij, en dat liep ongemerkt achter. Nu wordt het gegenereerd, en loopt het uit de pas met de code, dan houdt het de bouw tegen. Achterstallige documentatie is daarmee een fout geworden in plaats van een gewoonte.

Waar het ophoudt

Bouwen gaat nu uitsluitend autonoom. Zelf meebouwen in een interactieve sessie werkt merkbaar prettiger dan diezelfde stap op een runner, dus er ligt een ontwerp om die bouwstap ook vanuit een chatsessie te kunnen oppakken. Het is een ontwerp en geen station: er staat nog niets van te draaien. Additief, niet vervangend — de autonome route blijft de standaard voor achtergrondwerk.

Waar de checks op kunnen leunen verschilt per project. Eén ervan heeft al een stevige eigen testset; nieuwe projecten groeien daar naartoe zodra ze aanhaken. Tot die tijd bewaakt de poort daar de vórm van een wijziging beter dan de inhoud.

De nachtmeting staat standaard uit. Eén project mag elke nacht aan zichzelf meten hoe het ervoor staat en daar zelf een voorstel over indienen, als ik dat aanzet. Zo'n voorstel legt daarna precies dezelfde weg af als elk ander idee: uitzoeken, plannen, bouwen, controleren, en mijn goedkeuring aan het eind.

Terugkerend werk verschuift naar kleine, lokale modellen. Het grote, gehoste model blijft gereserveerd voor architectuur — de plek waar één groot model nog écht verschil maakt. Eerste voorbeeld draait al: een lokaal model vergelijkt op een self-hosted machine documentclaims met live systeemuitvoer, en bouwt daarbij een geheugenlaag op die dezelfde taak de komende weken scherper maakt.