Zum Hauptinhalt springen
Eine Mauer trennt einen Builder mit leuchtenden Bausteinen von einem Prüfer, der durch einen Schlitz eine Karte erhält.
Blog·

Verifier-Harness für Coding Agents: Builder und Prüfer trennen

Ein Verifier-Harness trennt Code-Erzeugung und Abnahme. Bun und SeatRace zeigen, wie Claims, Orakel, Blind-Gates und Selbsttests zusammenspielen.

Porträt von Antonio Agudo

Geschrieben von

Antonio Agudo

Inhouse-Enablement für Entwicklerteams · davor zwei Jahre KI in Produktion · Köln

Im Mai 2026 ließ Bun große Teile seiner Runtime von Coding Agents von Zig nach Rust portieren. Der zusammengeführte Diff umfasste mehr als eine Million neue Rust-Zeilen. Eine vollständige menschliche Zeile-für-Zeile-Prüfung ist nicht dokumentiert. Am 20. August erschien die neue Runtime mit Bun 1.4 als stabile Version.

Entscheidend war dabei nicht nur die Zahl der Agenten. Bun hatte schon vor der ersten Rust-Zeile eine sprachunabhängige Testsuite, die an die alte Zig- und die neue Rust-Implementierung dieselben Verhaltenserwartungen anlegte. Darin steckt die eigentliche Frage dieses Artikels: Wenn Menschen den neu erzeugten Code nicht vollständig lesen, woran erkennen wir dann, dass er gut genug für die Freigabe ist?

SeatRace, ein Experiment, das ich für diesen Artikel aufgesetzt habe, geht einen Schritt weiter. Dort sind Builder und Prüfer voneinander getrennt: Der Builder kennt die Anforderungen, aber nicht die verborgene Prüflogik. Doch auch ein solcher Prüfer kann blind sein. In einem Lauf blieben 6.814 zufällig erzeugte Prüfschritte ohne Befund, obwohl ein absichtlich eingebauter Fehler negative Kontostände erlaubte. Die Prüfung funktionierte grundsätzlich, sie erzeugte nur nie die Situation, in der der Fehler sichtbar geworden wäre.

Damit verschiebt sich die Frage noch einmal: Nicht nur der Build muss geprüft werden. Auch der Prüfer selbst muss zeigen, dass er bekannte Fehler tatsächlich erkennt.

Kurz gefasst: Ein Verifier-Harness ist eine vom Builder getrennte Abnahmeumgebung. Der Builder kennt die Regeln, nach denen sein Ergebnis beurteilt wird, aber nicht die verborgenen Testfälle. Vertrauen verdient der Prüfer erst, wenn er eine bekannte korrekte Referenz akzeptiert und absichtlich fehlerhafte Varianten zuverlässig zurückweist.

Warum Bun den Anstoß gab

Übertragbar ist an diesem Rewrite nicht die Zahl der Agenten, sondern die Reihenfolge. Die sprachunabhängige Testsuite existierte, bevor die erste Rust-Zeile entstand. Sie legte an die alte Zig- und die neue Rust-Implementierung dieselben Verhaltenserwartungen an, ohne selbst von der Implementierungssprache abzuhängen. Auf Debian umfasste sie rund 1,39 Millionen einzelne Prüfaussagen, sogenannte Assertions, die genaue Zahl hängt von der Plattform ab. Ausgangspunkt waren 535.496 Zig-Zeilen, der Port lief elf Tage im Mai 2026. Die kritischen Gegenprüfer in getrennten Kontextfenstern hatten dabei nicht die Aufgabe, das Ergebnis zu bestätigen, sondern Gegenbelege zu finden.

Die meistzitierten Kennzahlen des Sprints, als Größenordnung, nicht als Beleg für Korrektheit:

  • rund 6.778 Commits
  • zeitweise bis zu etwa 64 parallele Agenten-Instanzen, verteilt auf rund 50 dynamische Workflows
  • ein auf Basis der API-Preise hochgerechneter Betrag von etwa 165.000 Dollar (Rechenwert zu API-Preisen, keine bestätigte Rechnung)

Andrew Kelley, der Erfinder der Programmiersprache Zig, bezweifelte, dass dieser Maßstab für mehr als eine Million Zeilen ungelesenen Code ausreicht. Er nannte das Ergebnis „unreviewed slop“, also ungeprüften, maschinell erzeugten Code, und fragte rhetorisch, warum eine Suite Fehler in einer Million Zeilen davon finden solle, wenn sie die Fehler im handgeschriebenen Zig nicht gefunden habe (im Originalbeitrag von Andrew Kelley und laut The Register, 14. Juli 2026). Den Einwand sollten Sie nicht abschwächen. Buns eigene 19 behobene Regressionen zeigen, dass „kein Test übersprungen“ nicht dasselbe ist wie „kein Fehler entstanden“.

Aggregierte Telemetriedaten zeigen, dass die Sorge nicht aus der Luft gegriffen ist: Der Engpass hat sich bei hoher KI-Nutzung messbar vom Schreiben zur Prüfung und Freigabe verschoben, die Zahlen dazu stehen im Nachbarartikel zum Reviewengpass. Bun ist demgegenüber ein einzelner dokumentierter Fall.

Wichtig ist die Grenze des Vergleichs. Bun beschreibt keine Prüflogik, die dem Builder verborgen blieb und dem SeatRace-Muster entsprach. Bun ist der Anlass und das Produktionsbeispiel für einen Prüfmaßstab, der bereits vor dem Rewrite existierte. Das SeatRace-Experiment ist der kleinere, strengere Versuch zur Trennung und Selbstprüfung. Diesen Aufbau zeige ich im Folgenden in acht Schritten, mit SeatRace als durchgehendem Beispiel.

Fünf Begriffe, bevor es technisch wird

Der Builder erstellt die Anwendung. Der Prüfer beobachtet sie von außen und vergleicht ihr Verhalten mit Regeln, die schon vor dem Build feststanden. Fünf Begriffe beschreiben, wie diese Prüfung funktioniert:

BegriffEinfach gesagtBeispiel aus SeatRace
Vertrag (Contract)Die öffentlich bekannten Regeln und Schnittstellen, die vor dem Build feststehen.Ein Kontostand darf nie negativ werden.
ClaimEine einzelne Produktzusage, die sich von außen prüfen lässt.NO-NEG-BALANCE
ProbeEine Aktion, die das System gezielt in eine riskante Situation bringt.Mehrere Käufe leeren ein Konto bis an die zulässige Grenze.
Orakel (Oracle)Eine unabhängige Regel oder Referenz, die entscheidet, ob das beobachtete Ergebnis korrekt ist.Referenzmodell und Datenbankinvariante prüfen den Kontostand.
Meta-PrüfungEin Test des Prüfers selbst.Eine absichtlich fehlerhafte Variante erlaubt negative Kontostände, der Prüfer muss sie erkennen.

Eine Probe fällt noch kein Urteil. Sie erzeugt nur die Situation, in der ein Fehler sichtbar werden kann. Erst das Orakel entscheidet, ob der Claim erfüllt oder verletzt ist. Die Meta-Prüfung beurteilt wiederum nicht die Anwendung, sondern die Zuverlässigkeit des Prüfers.

Diese Rollen sind keine nacheinander ausgeführten Teststufen. Sie beschreiben, welche Aufgabe ein Baustein hat und worüber er urteilen darf. Wann die Prüfungen ausgeführt werden, regeln später die Stufen fast, behavioral, system und final.

Was Sie Schritt für Schritt entwickeln

Sie entwickeln zwei getrennte Repositories, einen öffentlichen Vertrag, eine verborgene Prüflogik, einen begrenzten Rückkanal, einen selbst getesteten Prüfer und ein abschließendes Blind-Gate. Das Blind-Gate prüft mit Testdaten, die der Builder während der Entwicklung nie gesehen hat. Der Aufbau sieht so aus:

Verifier-Repo
├── public/                 sichtbar
   ├── API-Vertrag
   ├── Claims              prüfbare Produktzusagen
   └── Failure-Card-Schema Format der Fehlerrückmeldung
├── src/                    verborgen
   ├── Referenzmodell      unabhängiges Sollverhalten
   ├── Invarianten         Regeln, die immer gelten müssen
   └── Runner              führt Prüfungen aus, sammelt Urteile
├── hidden/                 verborgen
   ├── Seed-Banken         reproduzierbare Testeingaben
   └── Defektkandidaten    absichtlich fehlerhafte Varianten
└── preregistration/        vorab festgelegt
    ├── Stopping Rules      Abbruchregeln des Versuchs
    └── Hash der Final-Seeds Fingerabdruck der Schlussdaten

          public/ wird kopiert
                   

Builder-Repo
├── public-spec/            sichtbar
├── App-Code
└── Deployment-Dateien

Verifier  laufende App
Builder  Failure Cards + evidence.json

Ein Seed ist dabei ein gespeicherter Startwert, aus dem der Runner dieselbe Folge zufälliger Testaktionen jederzeit erneut erzeugen kann.

Wer welche Information sieht, entscheidet über die Aussagekraft des ganzen Aufbaus:

InformationBuilder sieht siePrüfer sieht sie
Öffentliche Anforderungenjaja
Claim-Namen und Schweregradejaja
Orakel-Implementierungneinja
Feedback-Seedsindirekt über Kartenja
Final-Seedsneinja
App-Quellcodejanein
Laufendes Verhaltenjaja
Schreibgeschützte Prüfsichtennur das vertragliche Schemaja

Der Leitfaden zu KI-Coding-Agents ordnet diesen Aufbau in die größere Arbeitsteilung zwischen Erzeugung, Review und Freigabe ein.

Schritt 1: Wählen Sie einen riskanten, von außen prüfbaren Funktionsbereich

Ein Verifier-Harness, im Folgenden kurz Prüfer, lohnt sich nicht überall. Er lohnt sich dort, wo ein Build plausibel wirkt, aber unbemerkt fehlerhaft sein kann und ein Rollback den entstandenen Schaden nicht mehr beheben kann. Als durchgehendes Beispiel dient SeatRace, eine Ticketreservierungsanwendung, so benannt im Product Requirements Document.

SeatRace bündelt bewusst mehrere Risikofaktoren, und genau diese sind die Auswahlkriterien für Ihren eigenen Funktionsbereich:

  • Nebenläufig: Viele gleichzeitige Anfragen greifen auf dieselben 50 Sitze von A1 bis A50 zu, beim Andrang konkurrieren viele Kunden um denselben Platz.
  • Finanziell kritisch: Jeder Kunde startet mit 100 Euro Guthaben, ein Ticket kostet 25 Euro, Käufe und Rückerstattungen verschieben Beträge zwischen Kundenkonten und Veranstalterkonto.
  • Zustandsbehaftet: Eine Reservierung läuft nach exakt 30 Sekunden ab, und der Zustand muss über mehrere Prozesse und über Neustarts hinweg konsistent bleiben.
  • Von außen prüfbar: Jede harte Regel lässt sich als beobachtbare Eigenschaft des laufenden Systems formulieren, nicht als Frage nach der inneren Struktur.

Wann Sie nicht mit einem vollständigen Harness beginnen sollten: bei stark subjektiven oder rein visuellen Ergebnissen ohne klar beschreibbares Sollverhalten, bei einmaligen kleinen Änderungen mit lokaler Reichweite und überall dort, wo ein Mensch den gesamten Diff ohnehin vollständig prüft. Für alles außerhalb des riskanten Funktionsbereichs sind starke Property-Tests und ein zweiter Reviewer mit einer unabhängigen Prüfmethode oft die verhältnismäßige Alternative.

Schritt 2: Formulieren Sie jede Produktzusage als prüfbare Aussage

Bevor Sie Prüfcode schreiben, zerlegen Sie die Produktanforderungen in einzelne Aussagen, die sich am laufenden System beobachten lassen. Eine solche Aussage heißt im Harness Claim.

„Kein Kontostand wird negativ“ ist ein Claim. „Die Anwendung verwendet eine Methode namens checkBalance“ wäre dagegen keiner: Das beschreibt die interne Umsetzung, nicht das von außen sichtbare Verhalten. Das ist das Herzstück des Aufbaus, und am klarsten zeigt es ein vollständig ausgearbeitetes Beispiel.

Am Claim NO-NEG-BALANCE, der Zusage „Kein Kontostand wird je negativ“, lässt sich die gesamte Kette von der Anforderung bis zur Rückmeldung zeigen:

ElementKonkrete Ausprägung
ProduktzusageKein Kontostand wird negativ
Öffentlicher ClaimNO-NEG-BALANCE
Auslösender ZustandDas Guthaben liegt unter dem Ticketpreis
AktionKaufversuch oder gezielte Abfolge mehrerer Käufe
BeobachtungKontostand über kontrollierte schreibgeschützte Prüfsicht
OrakelReferenzmodell plus SQL-Invariante
Baseline-KandidatSimulator mit aktiver Guthabenprüfung
Bekannter Defektneg_balance_ok
SelbsttestBaseline-Kandidat PASS, Defektkandidat FAIL
Gefundene Lücke6.814 zufällige Schritte erreichten den Zustand nicht
Korrekturdeterministisches Drain-Szenario
Builder-FeedbackErwartung, Beobachtung und minimierte Schrittfolge

Für jede harte Regel durchlaufen Sie dieselbe Kette: Produktanforderung, Claim, Auslöser, Beobachtung, Urteil, bekannter Defekt und Rückmeldung. Dabei gehören Claims und Defekte in getrennte Spalten, denn ein Defekt ist kein Claim, sondern der Testfall, mit dem Sie die Trennschärfe des Claims prüfen.

ProduktzusageÖffentlicher ClaimPrüfmethodeBekannter Defekt
Kein Kontostand wird negativNO-NEG-BALANCEReferenzmodell, SQL-Invariante, deterministisches Drain-Szenarioneg_balance_ok
Geld wird weder erzeugt noch vernichtetMONEY-CONSERVSQL-Bilanzinvariante gegen Vorab-SnapshotBilanzsumme verletzt
Ein Sitz gehört höchstens einer PersonSEAT-UNIQparallel ausgelöste Reservierungs- und Kaufversuche, DB-InvarianteSitzsperre abgeschaltet
Kauf ist idempotentKauf-Idempotenzwiederholte Anfragen an das Modell und die laufende Appdouble_debit
Rückerstattung ist idempotentRefund-Idempotenzwiederholte Anfragen an das Modell und die laufende Apprefund_nonidem
Nur der Halter darf kaufen oder freigebenAutorisierungs-Claimgezielte Angriffsversuche gegen fremde Reservierungenauthz_off
Testpfade existieren nicht im Live-SystemPROD-ISOLATIONextern gestartetes Produktionsdeployment, alle Methoden und Auth-Zuständelab_in_prod

double_debit und refund_nonidem in der letzten Spalte sind die zugehörigen Defekte, nicht die Claim-Namen. Insgesamt umfasst der Prüfer 23 solcher Claims, 16 davon kritisch. Erst wenn feststeht, was gilt und woran Sie es messen, ergibt der Rest des Aufbaus Sinn.

Schritt 3: Frieren Sie den öffentlichen Vertrag ein

Vor dem Start des Builders habe ich den öffentlichen Vertrag und die Prüflogik auf zwei getrennten Ebenen eingefroren. Der öffentliche Vertrag in public/ enthält API, Claims, Schweregrade, Feedback-Schema und alle normativen Auslegungen. Das Versuchsprotokoll in preregistration/ hält Budget, Abbruchregeln und den Hash der Final-Seeds fest. Versteckt bleiben Orakel, Prüfdaten und Angriffssequenzen, nicht die geltenden Anforderungen. Acht Spezifikationswidersprüche habe ich vorab in disagreements.md aufgelöst, statt sie mitten im Lauf umzudeuten.

Daraus folgt eine wichtige Regel, die der technischen Trennung vorausgeht: Jede normative Auslegung gehört vor dem Build in den öffentlichen Vertrag. Eine versteckte Entscheidung über die Auslegung einer mehrdeutigen Anforderung ist keine saubere Trennung, sondern ein unfaires Gate. Verborgen bleiben die Prüflogik, die Seed-Auswahl und die interne Angriffsstrategie, nicht die Anforderung selbst. Der Builder muss wissen, was gilt, er darf nur nicht wissen, wie geprüft wird. Warum normative Auslegungen vor dem Build feststehen und öffentlich dokumentiert sein müssen, zeigt auch Spec-Driven Development mit Coding Agents.

Daraus ergibt sich eine saubere Dreiteilung:

  • Öffentlich und normativ: API-Vertrag, Claim-Namen und Schweregrade, Feedback-Schema, aufgelöste Widersprüche.
  • Vorab festgelegt, aber nicht öffentlich: Budget, Abbruchregeln, Hash der Final-Seeds.
  • Verborgen und prüfungsspezifisch: Orakel-Implementierung, Referenzsimulator, Seed-Banken, Defektkandidaten, konkrete Angriffssequenzen.

Nicht jedes vorab festgelegte Artefakt muss für den Builder sichtbar sein. Fertig ist dieser Schritt, wenn Claims, API-Vertrag, Feedback-Schema und aufgelöste Widersprüche eingecheckt sind und unverändert bleiben, es sei denn, die Anforderung selbst ändert sich.

Schritt 4: Trennen Sie Repositories, Sitzungen und Zugriffsrechte

Die Struktur oben zeigt bereits zwei getrennte Repositories. Entscheidend ist, dass diese Trennung über das Dateisystem und die Ausführungsumgebung erzwungen wird, nicht per Prompt. Eine Prompt-Zeile, die den Agenten bittet, nicht ins Prüfer-Verzeichnis zu schauen, bleibt eine Bitte. Erst die Umgebung macht daraus eine Grenze.

Nur public/ aus dem Prüfer-Repo wird byte-identisch als public-spec/ in das Builder-Repo kopiert. Orakel, Referenzsimulator und Seed-Banken bleiben außerhalb des Builder-Repositories. Sie technisch abzuriegeln heißt mehr als ein chmod 700 hidden/. chmod 700 schützt nur gegenüber anderen Unix-Identitäten, nicht gegen denselben Benutzer, privilegierte Prozesse, Root oder falsch gesetzte Container-Mounts. Es taugt als zusätzliche lokale Maßnahme, nicht als tragende Grenze. Die tragende Grenze ist das Bedrohungsmodell der Ausführungsumgebung:

  • Der Builder läuft unter einer getrennten Betriebssystem- oder CI-Identität.
  • Der Container mountet nur das Builder-Repo und die öffentliche Spezifikation. hidden/ und das Prüfer-Repo werden nicht gemountet.
  • Der Builder erhält weder den Docker-Socket noch Zugriff auf das Host-Dateisystem.
  • Der Prüfer erreicht die App nur über das vorgesehene Netzwerkinterface.
  • Der Datenbankzugriff läuft nur über definierte schreibgeschützte Views. Bei SeatRace sind das vier verify.*-Views.
  • Builder und Prüfer haben getrennte Secrets und Credentials.
  • Der Prüfer kann weder das Container-Image noch Source Maps oder Build-Artefakte inspizieren.

Woran Sie erkennen, dass der Schritt fertig ist: Der Builder startet in einer neuen Sitzung. Er kennt den öffentlichen Vertrag, erhält aber keinen Zugriff auf die verborgene Prüflogik oder die Seed-Banken. So gelangt kein Wissen aus der vorherigen Prüfersitzung in den Builder-Kontext. Die geltenden Anforderungen bleiben über den eingefrorenen Vertrag verfügbar.

Schritt 5: Trennen Sie Regeln, Auslöser, Urteile und Selbstprüfung

Der Prüfer ist kein einzelnes Skript, sondern rund 8.700 Zeilen Prüfcode über etwa zwei Dutzend Python-Module, dazu die eingefrorenen Vertrags- und Spezifikationsdateien. Er besteht aus vier Arten von Bausteinen, die keine zeitliche Reihenfolge bilden, sondern unterschiedliche Rollen übernehmen: Der Vertrag legt die Regeln fest, Probes erzeugen kritische Situationen, Orakel beurteilen das beobachtete Verhalten, und Meta-Prüfungen testen den Prüfer selbst.

RolleLeitfrageUrteil über die Anwendung?
Vertrag (Contract)Was gilt?Nein
Orakel (Oracle)Ist das beobachtete Verhalten korrekt?Ja
ProbeWie erzeugen wir den kritischen Zustand?Nicht allein
Meta-PrüfungErkennt der Prüfer bekannte Fehler?Nein, sie bewertet den Prüfer

Verträge (Contracts): Was gilt?

Zweck: den Maßstab festlegen, nach dem geurteilt wird. Grundlage: die Produktzusagen aus dem PRD. Ergebnis: die 23 Claims, die Form jeder Rückmeldung, das eingefrorene Protokoll. Typischer Fehler: Prüflogik in der Vertragsschicht unterzubringen, obwohl Contracts selbst nicht urteilen sollen. SeatRace-Beispiel: openapi.yaml mit 13 Operationen, das Schema der Verifikationssicht, der Vertrag für die Laboruhr.

Orakel (Oracles): Ist der beobachtete Zustand korrekt?

Zweck: entscheiden, ob das laufende System korrekt ist. Grundlage: eine Abfolge von Operationen und der beobachtete Zustand. Ergebnis: ein Urteil pro Claim. Typischer Fehler: das Orakel aus derselben Implementierung ableiten, die es bewerten soll. SeatRace-Beispiel: ein reines Python-Referenzmodell ohne HTTP und ohne SQL läuft parallel zur laufenden Fastify-App. Eine Hypothesis-State-Machine treibt beide im Gleichschritt durch dieselbe Abfolge von hold, purchase, refund und advance_time und vergleicht Schritt für Schritt. Zusätzlich prüft der Harness elf SQL-Invarianten direkt in der Datenbank gegen einen zuvor erstellten Snapshot. Der Startsaldo ist dabei nicht hart codiert.

Die drei Verfahren, die hier zusammenkommen, sind Fehlerfinder, keine Korrektheitsbeweise:

TechnikPrinzipFindetFindet nicht
Differential Testingdieselbe Eingabe an zwei unabhängige Implementierungen, die übereinstimmen müsstenAbweichung als starke Evidenz für einen Defektwelche Seite falsch ist, gemeinsame Fehler bleiben unentdeckt
Property-based Testingeine Invariante über einen Eingabebereich, viele Fälle, Gegenbeispiele minimierenVerletzung im StichprobenraumBeweis, dass die Invariante überall hält
Metamorphes TestenBeziehungen zwischen mehreren Läufen, die bei Korrektheit gelten müssenverletzte Relation als DefektsignalKorrektheit bei erfüllter Relation

Ein einziges verdrehtes Zeichen, > statt >= beim Ablaufzeitpunkt einer Reservierung, verursacht einen Defekt, den drei Orakel unabhängig voneinander erkennen und der 14 Claims auf FAIL setzt. Das zeigt, dass der Harness auf genau diesen Defekt anspricht. Es beweist nicht seine allgemeine Vollständigkeit.

Probes: Wie erzeugen Sie den kritischen Zustand?

Zweck: das System in Grenzzustände treiben. Grundlage: Zugriff auf das laufende Deployment von außen. Ergebnis: ein provozierter Zustand, über den ein Orakel richtet. Typischer Fehler: annehmen, die Störung greife im realen Deployment, ohne das zu prüfen. SeatRace-Beispiel: gleichzeitig gesendete Halte- und Kaufanfragen erzeugen Race Conditions, Fault Injection kappt Datenbankverbindungen über pg_terminate_backend. Ob diese Störwerkzeuge im realen Deployment tatsächlich greifen, muss gesondert geprüft werden, und SeatRace deckte später genau an dieser Stelle eine Lücke auf.

Merksatz: Jede Probe braucht ein benanntes Orakel. Die Probe erzeugt den kritischen Zustand, das Orakel entscheidet, ob der beobachtete Ausgang einen Claim verletzt.

  • Gleichzeitige Reservierungen sind die Probe. Die Sitzplatz-Invariante ist das Orakel.
  • Eine gekappte Datenbankverbindung ist die Probe. Datenbankzustand und Audit-Replay liefern die Orakel.

Meta-Prüfung: Erkennt der Prüfer bekannte Fehler?

Zweck: den Prüfer selbst bewerten. Grundlage: ein als Baseline verwendeter Kandidat und bekannte Defektkandidaten. Ergebnis: die Entscheidung, ob der Harness für die Abnahme freigegeben werden kann. Typischer Fehler: diese Schicht ganz weglassen. SeatRace-Beispiel: ein Selbsttest, der weiter unten in einem eigenen Abschnitt beschrieben wird.

Das unterscheidet den Harness von einer Suite, die im selben Repository und Build-Kontext mitwächst und dabei leicht dieselbe Auslegung der Spezifikation und dieselben Hilfsbibliotheken übernimmt. Die Trennung zielt auf diese gemeinsame Herkunft, nicht bloß auf Namen oder Speicherort der Tests. Eine End-to-End-Suite in einem anderen Stack und Repository kann dieselbe Unabhängigkeit erreichen. Wie sich ein Verifier-Harness von einem internen Eval-Set abgrenzt, steht in einem eigenen Beitrag.

Schritt 6: Definieren Sie Runner und Rückmeldeschleife

SeatRace verwendet deshalb einen eigenen Runner. Er bildet fünf Claim-Zustände, nämlich PASS, FAIL, ERROR, SKIPPED und INCONCLUSIVE, dazu Wiederholungen für Quarantänefälle, die Obergrenze von fünf Karten und die mehrstufige Gate-Logik, also die gestufte Freigabeentscheidung, ausdrücklich ab. Dafür bräuchte ein Standard-Testrunner eine zusätzliche Aggregations- und Gate-Schicht. Der Runner läuft in vier kumulativen Stufen:

  • fast: Build und Smoke-Tests.
  • behavioral: State-Machine, Fuzzing, Invarianten, Audit-Replay.
  • system: Races, Fault Injection, Autorisierung.
  • final: alles gegen die zurückgehaltene Seed-Bank, in einer sauberen Umgebung neu gebaut.

Eine vollständige Builder-Verifier-Runde läuft in dieser Reihenfolge ab. Die folgende Liste ist ein Ablaufprotokoll, keine Sammlung realer Repository-Befehle:

  1. Der Builder beginnt mit einem sauberen Ausgangszustand.
  2. Der Builder baut oder korrigiert die App.
  3. App und Infrastruktur werden reproduzierbar gestartet.
  4. Der Prüfer führt zunächst fast aus.
  5. Bei Erfolg folgen behavioral und system.
  6. Der Prüfer schreibt ein vollständiges evidence.json.
  7. Der Builder erhält höchstens fünf Failure Cards.
  8. Der Builder korrigiert den Build, ohne die verborgene Prüflogik zu sehen.
  9. Der Zyklus endet bei PASS oder nach einer vorab definierten Abbruchregel.
  10. final verwendet die zurückgehaltene Seed-Bank nach dem festgelegten Protokoll.

Die Failure Card dokumentiert Symptome, nicht Ursachen

Eine Failure Card ist bewusst knapp. Sie nennt den verletzten Claim, den Seed, ein minimiertes Gegenbeispiel als Schrittfolge aus definierten Operationen wie hold, release, purchase, refund und advance_time, den erwarteten und den beobachteten Zustand und verweist auf die zugehörigen Artefakte in evidence.json. Sie enthält weder einen Dateinamen noch eine Zeilennummer, weder eine Diagnose noch eine Ursachenanalyse oder einen Patch-Vorschlag. Das Schema erzwingt diese Beschränkung mit additionalProperties: false, nicht mit einer höflichen Bitte im Prompt. Pro Runde werden höchstens fünf Karten ausgegeben. So gibt der Prüfer dem Builder keine ungesicherte Ursachenhypothese und keinen Reparaturweg vor.

Was der Builder daraus macht, ist offen. Vier Ergebnisse sind möglich:

  • Der Builder findet die Ursache.
  • Der Builder findet sie nicht.
  • Der Builder behebt nur das Symptom.
  • Die Karte ist unzureichend und muss nach dem festgelegten Protokoll ergänzt werden.

Praxisbeispiel: die einzige Karte aus der zweiten Builder-Runde

In der zweiten Builder-Runde kam genau eine Karte, PROD-ISOLATION. Der folgende gekürzte Ausschnitt gibt die dokumentierten Werte der Karte wieder:

# Redigierter Ausschnitt. Struktur nach public/failure-card.schema.yaml,
# Werte der einzigen Karte aus der zweiten Builder-Runde. Die Felder seed, counterexample
# und artifacts sind hier gekürzt, nicht erfunden.
claim_id: PROD-ISOLATION
severity: critical
status: FAIL
expected: "GET /__lab/clock  ->  404 (Produktionsmodus)"
observed: "GET /__lab/clock mit gültigem Bearer  ->  200 {\"now_ms\": 0}"
seed: <gekürzt>
counterexample: <gekürzt>
artifacts: evidence.json

Damit war ein Debug-Endpunkt in einem als produktiv deklarierten Deployment erreichbar. Die naheliegende Lösung wäre, den Handler zu ändern und die Route zu sperren.

Der Builder tat das nicht. Er startete zuerst einen Container mit MODE=production und prüfte jeden /__lab/*-Pfad mit allen HTTP-Methoden und Authentifizierungszuständen. Alle Anfragen erhielten die Antwort 404. Sein wörtlicher Befund lautete: „The application was never wrong.“, die Anwendung selbst war also nie fehlerhaft. Das bedeutet: Der Handler-Code war korrekt. Der Build als Ganzes war dennoch falsch konfiguriert.

Der Fehler lag eine Ebene höher, in der docker-compose.yml, die MODE: lab als Literal festschrieb und damit ein exportiertes MODE=production überschrieb. Der Wert 0 war der entscheidende Hinweis: In Produktion wäre now_ms die aktuelle Unix-Zeit in Millisekunden. In diesem Setup belegte der Nullwert, dass eine Lab-Instanz geantwortet hatte. Der Fix war eine Zeile, MODE: ${MODE:-lab}.

Danach fand der Builder den passenden blinden Fleck in seinen eigenen Tests. test_production_mode.py benutzte docker compose run -e MODE=production, und dieses explizite -e überschrieb das Literal. Der Test bestand, während das echte Deployment durchfiel. Er schloss die Lücke mit zwei neuen Tests gegen die von Docker Compose aufgelöste Konfiguration.

In diesem Lauf nutzte der Builder die Offenheit der bewusst knappen Karte, um zuerst den tatsächlichen Systemzustand zu prüfen. Fest steht auch, dass das Gate zu Recht das ausgelieferte System einschließlich Compose-Konfiguration bewertete, nicht nur die Anwendungslogik. Der PROD-ISOLATION-Fall zeigt einen gelungenen Einsatz des Formats. Daraus folgt jedoch nicht, dass das Kartenformat stets zu derselben Diagnosequalität führt.

Schritt 7: Prüfen Sie den Prüfer

Ein Prüfer, der einen fehlerhaften Build bestehen lässt, ist schlimmer als kein Prüfer, denn er weckt unbegründetes Vertrauen. Den Prüfer zu bauen ist die sichtbare Aufgabe. Nachzuweisen, dass er im Fehlerfall anschlägt, wird dagegen häufig übersehen.

Es gibt zwei Fehlerarten, und der Selbsttest muss beide Fehlerarten abdecken. Ein wirkungsloser Check lässt einen fehlerhaften Kandidaten bestehen. Ein überempfindlicher Check lässt einen korrekten Kandidaten fälschlich durchfallen. Die Doppelbedingung lautet deshalb: Der als Baseline verwendete Referenzsimulator muss bestehen, und jeder bekannte Defekt muss bei mindestens einem einschlägigen Claim zu FAIL führen. SeatRace übertrug das Prinzip des Mutation Testing auf den Referenzkandidaten: Verändert wird nicht der Harness, sondern ein bekannter Referenzkandidat. Der Referenzsimulator, eine FastAPI-Anwendung mit einer echten PostgreSQL-Datenbank, enthält 16 eingebaute Defekte, jeder einzeln über BROKEN=<flag> aktiviert.

6.814 Prüfschritte ohne Befund, trotzdem blind

Der Befund zeigte sich bei NO-NEG-BALANCE. Das Flag neg_balance_ok entfernt die Guthabenprüfung komplett, und der Defektkandidat bestand trotzdem. Der Selbsttest zeigte mir, dass in 6.814 zufällig erzeugten Schritten der entscheidende Zustand kein einziges Mal eintrat: Keines der Konten wurde so weit geleert, dass es negativ wurde. Die Kontrolle war intakt und trotzdem wirkungslos, weil sie nie in einen Zustand geriet, in dem sie anschlagen konnte. Das Versuchsprotokoll zeigt: Viele erzeugte Zustandsübergänge sind keine Evidenz dafür, dass jeder Claim mindestens einmal wirksam aktiviert wurde. Behoben wurde die Lücke mit einem deterministischen Drain-Szenario, das ein Konto gezielt in den unzulässigen Zustand bringt, statt auf den Zufall zu hoffen. Genau diese Coverage-Illusion nimmt der Nachbarartikel zur Testgenerierung auseinander.

Der Selbsttest fand noch eine zweite Art blinder Flecken, diesmal bei den Störwerkzeugen. Die App verband sich zunächst als Postgres-Superuser, wodurch ein Teil der über pg_terminate_backend ausgelösten Störungen wirkungslos geblieben wäre. Später zeigte PROD-ISOLATION, dass ein erfolgreicher Selbsttest gegen den Simulator noch keine funktionierende Prüfung der realen Deployment-Topologie garantiert. Daraus folgt: Prüfen Sie nicht nur, ob die Orakel richtig urteilen, sondern auch, ob die Störwerkzeuge im realen Deployment tatsächlich greifen. Ein simulatorbasierter Selbsttest ist notwendig, reicht aber nicht aus. Ergänzend braucht es einen End-to-End-Selbsttest gegen die echte Topologie.

Drei Entscheidungen, die Sie auseinanderhalten müssen

Beim Nachbau werden leicht drei verschiedene Urteile vermischt. Halten Sie diese sprachlich getrennt:

  1. Claim-Urteil: PASS, FAIL, ERROR, SKIPPED oder INCONCLUSIVE für einen einzelnen Claim.
  2. App-Gate: Ist dieser konkrete Kandidat abnahmefähig?
  3. Verifier-Freigabe: Ist der Harness selbst ausreichend geprüft, um überhaupt ein App-Gate auszuführen?

Die Meta-Schicht urteilt nicht über die fachliche Korrektheit der Anwendung. Sie entscheidet, ob dem Harness überhaupt vertraut werden darf. Scheitert sein Selbsttest, etwa weil ein erzwungenes SKIP oder eine ausgelöste Exception unbemerkt bleibt, darf kein bestandenes App-Gate als belastbarer Befund gelten. Ein Absturz darf nie als Bestehen durchgehen.

Schritt 8: Führen Sie das Blind-Gate aus

Im Versuchsprotokoll sind zwei getrennte Seed-Banken mit jeweils 40 Werten festgelegt: 40 Feedback-Seeds, die der Builder während der Entwicklung verwenden darf, und 40 Final-Seeds, die er dabei nie zu sehen bekommt. Der SHA-256-Hash der Final-Bank steht vorab im Manifest, die Kriterien sind vorab eingefroren, und feste Abbruchregeln liegen in stopping-rules.yaml: Abbruch nach zwölf Runden, bei Kosten von 60 Euro pro Lauf oder nach drei identischen Fehlschlägen.

Wussten Sie? Ein Hash in einem privaten Repository belegt nur, dass zwei später verglichene Dateien identisch sind. Er belegt nicht, dass der Hash tatsächlich vor dem Versuch existierte. Für eine belastbare Präregistrierung braucht es einen extern nachvollziehbaren Zeitstempel, etwa einen bereits vor dem Lauf veröffentlichten Commit, ein signiertes Register oder einen unabhängigen Archivdienst.

Das Blind-Gate ist eine einmalige, einseitige Entscheidung. Die Entwicklungsschleife nutzt ausschließlich die Feedback-Bank. Besteht der Kandidat das Feedback-Gate, wird sein Commit eingefroren, ab hier sind keine Code-Änderungen mehr erlaubt. Aus genau diesem Commit wird eine frische Umgebung gebaut, Volumes zerstört, Images entfernt, Build-Cache geleert, Neubau ohne Cache. Gegen diese Umgebung läuft die Final-Bank genau einmal. Das Ergebnis fließt nur in eine Richtung: PASS friert das Ergebnis ein, FAIL macht es zu einem Fehlschlag. Final-Seeds oder Gegenbeispiele bekommt der Builder nie zurück, und es gibt keinen zweiten Versuch. Eine spätere Reparatur ersetzt nie den eingefrorenen Kandidaten.

Hier muss ich zwei Bedeutungen von „Runde“ auseinanderhalten, die sonst einen Scheinwiderspruch ergeben. Die Abbruchregel von zwölf Runden zählt Builder-Runden: eine Builder-Änderung, gefolgt von gestufter Prüfung und zurückgegebenen Karten. Der maßgebliche SeatRace-Lauf brauchte davon genau eine. Das Evidence-Verzeichnis zählt dagegen etwas anderes, nämlich jede einzelne Ausführung des Prüfers, also jeden Aufruf verify run --stage <fast|behavioral|system|final>, fortlaufend über den gesamten Versuch nummeriert. Diese 62 Ausführungen umfassen die Entwicklung mit der Feedback-Bank, die Läufe gegen die Final-Bank und den anschließenden Mutationstest, es sind also keine 62 Builder-Runden.

Der eigentliche Blindtest ist die Ausführung gegen den eingefrorenen Kandidaten in der frisch gebauten Umgebung: bestanden, 16 von 16 kritischen Claims, keine Karte. Zwei frühere Ausführungen der Stufe final liefen noch während der Entwicklung gegen unfertige Stände, danach ging die Entwicklung weiter. Sie sind Probeläufe der Final-Stufe, kein Bestehen des Blind-Gates, und dürfen nicht so gezählt werden. Beruhigend ist allein, dass auch in diesen Probeläufen kein Final-Seed als Karte an den Builder zurückfloss.

Was das Blind-Gate leistet und was nicht, lässt sich genau benennen. Im Lauf beobachtet: Der Build bestand die 40 zurückgehaltenen Final-Seeds, nicht nur die Prüfungen mit den Feedback-Seeds. Das reduziert das Risiko einer gezielten Anpassung an die sichtbaren Feedback-Seeds. Es beweist weder allgemeine Korrektheit noch die vollständige Abdeckung des Zustandsraums. Genau das führt der Lauf mit 6.814 Schritten vor: Ein bestandenes Blind-Gate schützt nicht vor Überanpassung an den Generator, nicht vor gemeinsamen blinden Flecken von Orakel und Implementierung und nicht vor Zuständen, die zufällig nie erreicht wurden. Ein bestandener Lauf gegen die zurückgehaltenen Seeds ist eine Stichprobe, kein Korrektheitsbeweis.

Zwei Ideen aus der empirischen Forschung ordnen das ein. Bei der Präregistrierung werden Hypothese und Auswertungsplan festgeschrieben, bevor Ergebnisse sichtbar sind. Im SeatRace-Versuch wurden Kriterien und Auswertung vor dem Lauf eingefroren, allerdings nur intern, wie der Kasten oben einschränkt. Die Analogie zur Verblindung in klinischen Studien verläuft in umgekehrter Richtung: Nicht der Prüfer ist blind, sondern dem Builder bleibt die Prüflogik verborgen. Damit wird es schwerer, den Build gezielt auf die Prüfung zuzuschneiden. Genau hier liegt das Goodhart-Risiko, benannt nach Charles Goodhart und pointiert von Marilyn Strathern: Ein Maß verliert seine Aussagekraft, sobald es selbst zum Ziel wird.

Im Versuchsprotokoll dokumentiert ist am Ende ein bestandenes Gate ohne Blocker.

Wann der volle Aufbau angemessen ist

Die folgende Matrix ist eine aus dem Experiment abgeleitete Heuristik, keine empirisch belegte ROI-Regel. Eine kontrollierte Studie, die den vollen Harness mit leichteren Varianten vergleicht, liegt nicht vor.

SignalEher voller HarnessEher leichtere Prüfung
Folgen eines FehlersGeldverlust, Rechteverletzung, irreversibler Zustand, schwere betriebliche Auswirkungenleicht rückgängig zu machen, lokal, geringe Reichweite
Verhaltenklare Zustandsübergänge und beobachtbare Invariantenstark subjektiv oder visuell
ParallelitätRaces, Retries, mehrere Prozesselinearer, zustandsarmer Ablauf
Agentenautonomiemehrere autonome Iterationen ohne vollständige Prüfung des Diffskleiner Diff, ein Mensch prüft den gesamten Diff
Orakeleigenständiges Referenzmodell oder klare invariantenbasierte Prüfung möglichkeine eigenständige Beschreibung korrekten Verhaltens möglich
Wiederverwendungviele Builds gegen dieselben Claimseinmalige, kleine Änderung

Für die Gesamtkosten des Harness gibt es keine belastbare Zahl. Dokumentiert sind 78,55 Dollar und knapp drei Stunden für eine einzelne Builder-Sitzung. Davon getrennt galten ein Laufbudget von 60 Euro und ein Gesamtbudget von 250 Euro. Diese Werte lassen weder auf einen typischen Projektpreis schließen noch die Aussage ableiten, ein Harness lasse sich an einem Nachmittag entwickeln. Die 78,55 Dollar und die 165.000 Dollar aus dem Bun-Projekt sind nicht direkt vergleichbar. Hier geht es um eine einzelne Sitzung, dort um einen umfassenden Rewrite eines Großprojekts.

Wichtig für Engineering-Leads: Der Prüfer verlagert die technische Abnahme aus dem Build-Prozess in eine unabhängige Prüfumgebung. Die Freigabeverantwortung bleibt bei der dafür benannten Person oder Rolle. Wie sich formell regeln lässt, wer am Ende freigibt, steht im Nachbarartikel. Beim Vibe Coding vertrauen Sie dem Ergebnis nach Gefühl. Bei agentengestützter Entwicklung mit einem getrennten Prüfer vertrauen Sie einem Verfahren, das Erzeugung und Abnahme voneinander trennt. Ein Verifier-Harness ist die unspektakuläre Architektur, die den Ausschlag geben kann.

Zurück zu Bun

Bun zeigt nicht, dass eine Testsuite jede Form menschlichen Reviews ersetzt. Der bereits vorhandene Verhaltensmaßstab, kritische Gegenreviews und die laufende Steuerung der Workflows waren zentrale Voraussetzungen, um einen Rewrite dieser Größenordnung verantwortbar in den Hauptzweig zu übernehmen. SeatRace ergänzt diesen Befund um die Prüfung des Prüfmaßstabs selbst: Er muss nachweislich zwischen einem Referenzkandidaten, der die Claims erfüllt, und bekannten Defektkandidaten unterscheiden. Ein bestandenes Gate ist erst aussagekräftig, wenn der Prüfer zuvor qualifiziert wurde, wenn also der Referenzkandidat besteht und bekannte Defekte zuverlässig zu FAIL führen. Kelleys Frage, ob ein Prüfer für so viel ungelesenen Code ausreicht, beantwortet daher nicht die Zahl der Assertions, sondern die belegte Trennschärfe des Prüfers.

Häufige Fragen

Was ist ein Verifier-Harness für Coding Agents?
Eine vom erzeugenden Agenten getrennte Abnahmeumgebung, die den laufenden Build von außen gegen vorab festgelegte Claims prüft, statt seinen Code zu lesen. Je nach System kombiniert sie ein Referenzmodell, Invarianten, metamorphe Beziehungen und gezielte Angriffstests. Entscheidend ist, dass der Prüfmaßstab nicht aus der Implementierung abgeleitet ist, die bewertet werden soll, und dass der Builder nur Symptome zurückbekommt, keinen Fix.
Warum sollte der Prüfer den Code des Builders nicht kennen?
Weil Prüfer und Builder dasselbe Missverständnis der Spezifikation übernehmen können, wenn sie aus demselben Kontext stammen. Prüft der Agent in derselben Sitzung, in der er eine Anforderung ausgelegt hat, entsteht eine Bestätigung ohne Beweiskraft. SeatRace setzte die Trennung deshalb auf Ebene des Dateisystems, des Kontexts, der Implementierungssprache und der Zugriffsrechte um, statt sie nur im Prompt zu erbitten. Verborgen bleibt dabei die Prüflogik, nicht die geltende Anforderung. Diese steht im eingefrorenen öffentlichen Vertrag.
Was ist der Unterschied zwischen einem Eval-Set und einem Verifier-Harness?
Ein Eval-Set vergleicht Kandidaten und ordnet Agenten danach, wie gut sie auf einer Aufgabensammlung abschneiden. Ein Verifier-Harness prüft, ob ein einzelner Build abnahmefähig ist, und dient als Gate. Das eine vergleicht, das andere gibt frei.
Was ist eine Failure Card?
Ein bewusst knapper Fehlerbericht des Prüfers: ein verletzter Claim, ein Gegenbeispiel als Schrittfolge, ein Vergleich zwischen erwartetem und beobachtetem Zustand sowie Artefakte. Kein Dateiname, keine Diagnose, kein Fix-Vorschlag. So gibt der Prüfer keine ungesicherte Ursachenhypothese vor. Wo die Ursache liegt, muss der Builder selbst ermitteln, mitunter eine Ebene höher als die App, etwa in der Deployment-Konfiguration.
Braucht man dafür 165.000 Dollar wie beim Bun-Rewrite?
Dafür gibt es noch keinen belastbaren Standardwert. Die 165.000 Dollar sind eine auf API-Preisen basierende Hochrechnung für Buns Rewrite von mehr als einer Million Zeilen. Im SeatRace-Bericht sind 78,55 Dollar für eine einzelne Builder-Sitzung bis zum Commit dokumentiert, nicht für den gesamten Entwurf und die Entwicklung des Harness. Der angemessene Aufwand hängt von Zahl und Kritikalität der Claims sowie von der Verfügbarkeit eines eigenständigen Orakels ab.

Quellen

  • Antonio Agudo, „SeatRace: eine KI baut eine App gegen einen Black-Box-Prüfer“, Experiment für diesen Artikel, dokumentiert in zwei öffentlichen Repositories: seatrace-app (Builder) und seatrace-verification (Prüfer). Eingefrorener Kandidat 50db334, Prüfer-Stand bei Build-Ende f8d93f7, Blind-Gate-Lauf 890a145, 28. August 2026.
  • Jarred Sumner (Bun), „Rewriting Bun in Rust“, bun.com/blog/bun-in-rust, 8. Juli 2026. Zahlen zum Port, 19 bekannte Regressionen, sprachunabhängige Testsuite.
  • „Bun v1.4“, bun.com/blog/bun-v1.4, 20. August 2026. Datum der stabilen Veröffentlichung.
  • Andrew Kelley, „My Thoughts on the Bun Rust Rewrite“, andrewkelley.me, 9. Juli 2026. Prozesskritik am Rewrite.
  • The Register, „Zig creator calls Bun's Rust rewrite 'unreviewed slop'“, theregister.com, 14. Juli 2026.
  • Boris Cherny, „Building Claude Code“, Root Access (Interview), ycrootaccess.com, 27. Juli 2026. Aussage zur Ablation: Löschen großer Teile von System-Prompt, Tools und Harness und schrittweises Zurückbringen dessen, was sich im echten Einsatz wiederholt als nötig erweist.
  • Faros AI, „AI Acceleration Whiplash“, faros.ai, 12. April 2026. Verhältnis von Vorfällen zu Pull Requests, Reviewzeit.
  • Testorakel und nicht-testbare Programme: Elaine Weyuker, „On Testing Non-Testable Programs“, The Computer Journal 25(4), 1982. doi.org/10.1093/comjnl/25.4.465.
  • Property-based Testing: Koen Claessen, John Hughes, „QuickCheck“, ICFP 2000. doi.org/10.1145/351240.351266.
  • Metamorphes Testen: T. Y. Chen, S. C. Cheung, S. M. Yiu, HKUST-CS98-01, 1998, erneut veröffentlicht auf arXiv 2020. arxiv.org/abs/2002.12543.
  • Differential Testing: William McKeeman, „Differential Testing for Software“, Digital Technical Journal 10(1), 1998. Archiviertes PDF.
  • Mutation Testing: R. DeMillo, R. Lipton, F. Sayward, „Hints on Test Data Selection“, Computer 11(4), 1978. doi.org/10.1109/C-M.1978.218136.
  • Fault Injection: M. Hsueh, T. Tsai, R. Iyer, „Fault Injection Techniques and Tools“, IEEE Computer 30(4), 1997. doi.org/10.1109/2.585157.
  • Präregistrierung: B. Nosek, C. Ebersole, A. DeHaven, D. Mellor, „The preregistration revolution“, PNAS 115(11), 2018. doi.org/10.1073/pnas.1708274114.
  • Blinding: K. Schulz, D. Grimes, „Blinding in randomised trials“, The Lancet 359, 2002. doi.org/10.1016/S0140-6736(02)07816-9.
  • Goodharts Gesetz: Charles Goodhart, 1975, Nachdruck in Monetary Theory and Practice, 1984. doi.org/10.1007/978-1-349-17295-5_4. Pointierte Fassung: Marilyn Strathern, European Review 5(3), 1997. doi.org/10.1017/S1062798700002660.

Stand: 28. August 2026.

Weiterlesen

Nächster Schritt

Interesse an einer KI-Schulung für Ihr Entwicklerteam? Coding Agents meistern: Assessment vorab, drei Tage Schulung am eigenen Code, danach 30 Tage Transfer. Nach sechs Wochen sehen Sie an eigenen Daten, was sich geändert hat.

Lesen Sie hier regelmäßig mit? Sie können antonioagudo.com bei Google als bevorzugte Quelle markieren, damit diese Inhalte in Ihren Suchergebnissen sichtbarer bleiben.