Entwurf · Issue 5a95366

Die Kette wird am Merge geprüft

Entwurf zu Issue 5a95366, Meilenstein 5 von docs/plans/5a95366-chain-gate.md. Jedes Urteil unten wird beim Rendern von judgeChain erzeugt, keiner der Sätze ist abgeschrieben. Ändert sich eine Zurückweisung im Code, ändert sich diese Seite mit — oder der Build bricht.

AGENTS.md beschreibt eine Reihenfolge: erst das Issue, dann der Plan, dann der Entwurf, dann die Freigabe, dann der Code. Bitten war bisher alles, was daran hing. Ein Runner, der die Schritte überspringt, merkt nichts davon — und ein Runner, der sich daran hält, kann es nicht belegen. Beides ist derselbe Mangel: nichts an dieser Kette ist prüfbar.

Dieses Kapitel entwirft die Prüfung. Nicht die Regeln — die stehen schon im Kontrakt — sondern die Sätze, mit denen sie zurückweisen, und die Stelle, an der sie zurückweisen können.

01Die sieben Glieder

Ein Urteil meldet immer alle sieben, auch die, die es nicht prüfen konnte. Ein Bericht, der beim ersten Fehler abbricht, verbirgt die übrigen sechs — und wer eine Kette repariert, will die ganze Form auf einmal sehen.

  • patch — die Arbeit fügt genau einen Plan hinzu. Der Plan, den ein Patch schreibt, benennt sein Issue; einen bereits gemergten Plan zu ändern, beansprucht dessen Issue nicht.
  • issue — die Id aus dem Front Matter liegt im COB-Store. Diese Frage stellt just plan-check bewusst nicht: sie braucht rad und einen laufenden Node, und eine Prüfung, die offline rot wird, wird abgeschaltet.
  • plan — die Datei hat die Form eines Plans. Dieselbe Prüfung wie lokal, an der Stelle, an der sie zählt.
  • draft — das Kapitel liegt im Baum. Zwei Zeiger, genau einer (entschieden in ecdea18): draft: fügt ein Kapitel hinzu, settles: nennt das bestehende, das die Änderung wahr hält. In plan-spec.ts optional, hier verpflichtend: der Entwurf ist die Stufe, auf der eine Entscheidung noch billig ist.
  • approval — eine Unterschrift auf dem Token deckt genau diesen Plan. Siehe 03.
  • settled — der Plan trägt ## What the work settled. Das wird nach der Arbeit geschrieben, also kann keine Prüfung, die vorher läuft, es je verlangen.
  • proves — jedes **Ends when** trägt ein Kommando. Das ist der Unterschied zwischen „die Arbeit hängt an einem freigegebenen Plan" und „der freigegebene Plan wurde geliefert". Ausgeführt werden die Kommandos hier, nicht im Gate: sie gehören zur Werkzeugkette dieses Repos, und keine davon liegt auf dem Broker-Image. Das Gate hält, dass es sie gibt und dass sie im Digest der Freigabe stehen — ein nachträglich ausgetauschtes Kommando bricht die Signatur.
vollständige Kette
okpatchnames its issue through docs/plans/5a95366-chain-gate.md
okissueissue 5a95366a19f5f8d0a3fbb9cb2faeb8c73093a466 exists
okplandocs/plans/5a95366-chain-gate.md is well-formed, 1 milestone(s)
okdraftdraft /docs/design/chain-gate is in the tree
okapprovalapproved by did:key:z6MkrNYyVNngbG1UVmt5xbB3wye4RQtVUgZUi4cvJ5v88v5N, on the token
oksettledthe plan records what the work settled
okproves1 of 1 milestone command(s) ran green

chain complete

02Drei Arten, nein zu sagen

Ein Gate, das „chain check failed" sagt, schickt jemanden dazu, den Prüfer zu lesen. Jeder Satz benennt deshalb das Glied, was erwartet wurde und was gefunden wurde — der nächste Schritt soll aus der Zurückweisung allein hervorgehen. Die Tests prüfen diese Sätze, nicht Booleans: der Satz ist der Teil, der weiter funktionieren muss.

freigegeben, dann geändert — die Unterschrift ist echt und deckt etwas anderes
okpatchnames its issue through docs/plans/5a95366-chain-gate.md
okissueissue 5a95366a19f5f8d0a3fbb9cb2faeb8c73093a466 exists
okplandocs/plans/5a95366-chain-gate.md is well-formed, 1 milestone(s)
okdraftdraft /docs/design/chain-gate is in the tree
NOapprovaldid:key:z6MkrNYyVNngbG1UVmt5xbB3wye4RQtVUgZUi4cvJ5v88v5N approved something else: planDigest changed since — a plan changed after approval is a plan nobody approved
oksettledthe plan records what the work settled
okproves1 of 1 milestone command(s) ran green

chain broken — the work does not hang on an approved plan

Das ist die Zurückweisung, wegen der es den Schritt überhaupt gibt. „Signatur ungültig" würde jemanden zur falschen Reparatur schicken — das Feld, das sich bewegt hat, steht deshalb im Satz.

mit einem Schlüssel unterschrieben, den ein Agent erreichen kann
okpatchnames its issue through docs/plans/5a95366-chain-gate.md
okissueissue 5a95366a19f5f8d0a3fbb9cb2faeb8c73093a466 exists
okplandocs/plans/5a95366-chain-gate.md is well-formed, 1 milestone(s)
okdraftdraft /docs/design/chain-gate is in the tree
NOapproval.chain/approvals/5a95366a19f5f8d0a3fbb9cb2faeb8c73093a466.sig verifies against no signer in .chain/allowed_signers, in namespace plan-approval@tessera.at — an approval is made on the token, and no key an agent can reach counts
oksettledthe plan records what the work settled
okproves1 of 1 milestone command(s) ran green

chain broken — the work does not hang on an approved plan

der COB-Store war nicht lesbar — nicht geprüft ist nicht abgelehnt
okpatchnames its issue through docs/plans/5a95366-chain-gate.md
--issuenot checked — could not read the COB store to look up 5a95366a19f5f8d0a3fbb9cb2faeb8c73093a466
okplandocs/plans/5a95366-chain-gate.md is well-formed, 1 milestone(s)
okdraftdraft /docs/design/chain-gate is in the tree
okapprovalapproved by did:key:z6MkrNYyVNngbG1UVmt5xbB3wye4RQtVUgZUi4cvJ5v88v5N, on the token
oksettledthe plan records what the work settled
okproves1 of 1 milestone command(s) ran green

chain broken — the work does not hang on an approved plan

Ein Faktum, das nicht ermittelt werden konnte, ist null, nie false. Ein Gate, das „ich konnte den COB-Store nicht erreichen" als „das Issue existiert nicht" liest, weist gute Arbeit zurück; umgekehrt winkt es schlechte durch. null besteht nie — es sagt nur, dass niemand nachgesehen hat.

Genau dieser Fehler ist beim Bauen passiert: rad issue show <id> ist die naheliegende Schreibweise und eine Falle. Der Befehl endet mit 1, sobald stdout kein Terminal ist — für ein Issue, das offensichtlich existiert, und ohne ein Wort dazu. Das Gate las das als „nicht im Store" und wies korrekte Arbeit zurück. rad cob list antwortet ohne Terminal und meint es.

03Was eine Freigabe unterschreibt

Eine Unterschrift unter „Issue 5a95366 ist freigegeben" ist wertlos: sie verifiziert gegen jeden Patch, der dieses Issue nennt, auch gegen Arbeit, die der Freigebende nie gesehen hat. Unterschrieben wird deshalb ein Fingerabdruck dessen, was tatsächlich gelesen wurde.

chain-statement v1
issue=5a95366a19f5f8d0a3fbb9cb2faeb8c73093a466
plan-path=docs/plans/5a95366-chain-gate.md
plan-digest=sha256:e4413b0b81d2aad54eacf5c19aa8ca2bf6dc4c17263ca26004fe82fc869c4e2e
draft-path=app/docs/design/chain-gate
draft-tree=89abcdef0123456789abcdef0123456789abcdef

Nicht gebunden sind der Commit und der Arbeitsbaum, und das ist Absicht. Die Freigabe fällt, bevor der Code existiert — genau darum geht die Reihenfolge so. Einen Commit zu binden hieße entweder, die Freigabe ans Ende zu verschieben, was den Prozess umdreht, oder nach jedem Commit neu freizugeben, was allen beibringt, durchzuklicken. Die Kette garantiert also, dass Arbeit an einem freigegebenen Plan hängt; dass die Arbeit dem Plan entspricht, kann sie nicht behaupten. Das ist Review, und das ist die Aufgabe eines Menschen.

plan-digest deckt die freigegebene Substanz, nicht die Bytes der Datei: Front Matter, Approach, jeden Meilenstein mit seinen Arbeitspaketen, seinem Endzustand und seinen Kommandos. Ausgenommen sind die Abschnitte, die es gibt, um hinterher festzuhalten, was geschah — ## What the work settled verlangt der Kontrakt bis zum Merge, und ein Digest, den diese eine Pflichtänderung zerstört, ist ein Digest, den niemand grün hält. Ein umbrochener Absatz ist derselbe Plan; ein geänderter Approach ist es nicht.

04Wo eine Regel binden kann

Nichts in diesem Repository kann einen Runner binden. .radicle/native.yaml, das Justfile, Hooks — alles davon ist von dem editierbar, den es einschränkt. Der Repo-Hook feuert aus einem Worktree ohnehin nie, weil git rev-parse --git-dir dort auf .git/worktrees/<name> zeigt, und alle Arbeit passiert in Worktrees.

  • Im Repo: eine Leitplanke. just chain sagt früh, was das Gate später sagt. Mehr behauptet es nicht — und sie weist genau das zurück, was auch das Gate zurückweist: bei roten Meilenstein-Kommandos warnt sie (Exit 2), statt den Push zu blockieren. Eine Leitplanke, die strenger ist als die Autorität, macht die Autorität nicht stärker, sie blockiert nur.
  • Auf dem Token: die einzige echte Grenze. Der Freigabe­- schlüssel ist ein FIDO2-Schlüssel ohne no-touch-required. Ein Agent unter derselben Kennung kann die Datei lesen und trotzdem nicht unterschreiben — auf dieser Hardware geprüft, nicht aus einer Dokumentation übernommen: fünf unbeaufsichtigte Signaturversuche hintereinander, keiner davon ergab eine Signatur. Der Git-Signing-Key dagegen erzeugt in derselben Namespace ohne jede Berührung eine gültige Signatur — und das Gate weist sie ab.
  • In ci-build: das Gate. Auf dem Broker-Image gebacken, außerhalb der Reichweite des Runners. Es hält das Imagezurück, nicht den Merge: Radicle kann einen Merge nicht verweigern, seine Trigger sind sämtlich Ereignisse, die schon passiert sind.

namespaces= in .chain/allowed_signers heftet den Schlüssel an plan-approval@tessera.at. Git unterschreibt Commits und Tags unter git, also kann dieser Schlüssel keine Commit-Signatur erzeugen und eine Commit-Signatur nie als Freigabe durchgehen — OpenSSH weist die Prüfung ab, statt Bytes zu vergleichen, die zufällig passen.

Das Urteil selbst steht danach als Kommentar am Patch-COB, und was es zu einem Beleg macht, ist sein Autor: die DID des CI-Node, in .chain/gate benannt. Ein Runner kommentiert seinen eigenen Patch als er selbst und kann nicht als das Gate kommentieren. just gate-verdict liest ihn zurück — lokal rot, bis das Gate gelaufen ist, was korrekt ist: die Behauptung handelt von einer Maschine, die der Runner nicht kontrolliert.

Der Principal ist did:key:z6MkrNYyVNngbG1U…, eine DID, und zwar die des Account-Schlüssels, nicht die des Node-Keys dieser Maschine. Beide sind Delegates, „von einem Delegate unterschrieben" ließe einen Runner also seinen eigenen Plan freigeben. Eine E-Mail-Adresse kommt in der Kette nirgends vor: es gibt hier keinen Mailserver, nur DIDs und Handles, und eine Adresse wäre ein zweiter Identitätsraum, den nichts verankert.

05Was gebeten bleibt

Der Wert dieses Abschnitts liegt darin, dass die Linie sichtbar ist. Was das Gate nicht prüft:

  • Ob die Arbeit dem Plan entspricht. Siehe 03 — die Freigabe geht dem Code voraus. Das ist Review.
  • Ob eine Prüfung je rot war. Ein Issue sagt in ## Verify, was das Kommando heute tut; ein Meilenstein wird nur im fertigen Zustand gesehen. Das Gate kann grün verlangen, nicht, dass es je rot war.
  • Ob ein Meilenstein-Kommando grün ist. Auf dem Broker-Image liegt kein bun, kein just, kein rg. Eine Prüfung, die läuft, wenn das Werkzeug zufällig da ist, und sonst überspringt, wäre genau die Manchmal-Durchsetzung, die diese Kette beenden soll. Wer den Plan freigibt, stimmt dem zu, was laufen wird.
  • Ob der Weg der richtige war. Ein grünes Kommando belegt das beobachtbare Ergebnis, nie die Route — der Plan benennt absichtlich das Ziel und nicht die Schritte.
  • Einen bösartigen Runner. Das Gate läuft mit den Zugangsdaten der Registry in seiner Umgebung. Es bindet den, der abkürzt, nicht den, der angreift.