Vision Stand 2026-08-28 03:30 UTC

Autopilot

Wer nachts die Tickets abarbeitet – und was ihn daran hindert

Die Systemkarte. Was existiert, was tut es, wo liegt es. Erhoben am 31.07.2026 aus dem laufenden Code, nicht aus dem Gedächtnis. Warum etwas so gebaut ist (Entscheidungen, Fehlschläge, Lehren): design.md. Diese Datei wiederholt das nicht – sie beantwortet „was ist da".

In einem Satz

Ein Prozess auf dem Host arbeitet Tickets aus TODO.md selbstständig ab: er wählt eins, lässt einen frischen claude -p-Lauf daran arbeiten, nimmt das Ergebnis von Prüf-Agenten abnehmen und entscheidet dann, ob es erledigt ist oder zu Leon geht.

Die Grundidee: das Ticket-System ist das Gedächtnis, nicht der Chat. Jeder Lauf startet mit leerem Kontext – was er wissen muss, steht im Ticket, in kapitel.json und in den Doku-Dateien.


1. Starten, anhalten, übernehmen

Ich will …WieWas passiert
startenKnopf Start auf #/autopilotschreibt einen Wunsch; der Cron-Wachposten (autopilot_watch.py --wache, jede Minute) macht daraus einen Prozess
nach dem laufenden Ticket anhalten◔ Nach diesem Ticket anhaltenMarke archive/autopilot/auslaufen; das laufende Ticket wird samt Abnahme fertig. Nochmal drücken nimmt es zurück
sofort stoppenNot-Ausarchive/autopilot.stop; bricht mitten im Schritt ab und rollt zurück, wo möglich
selbst weitermachenÜbernehmenhält an, gibt [wip] frei und zeigt claude --resume <session-id> – du sitzt danach im selben Chat

Wichtig: ändert sich scripts/autopilot.py, während er läuft, beendet er sich nach dem aktuellen Ticket selbst (Python lädt sein Modul nur beim Start). Dann ist ein Start nötig.


2. Ein Durchgang, Schritt für Schritt

 1  Not-Aus / Auslaufen?      → sauber beenden
 2  Ressourcen ok?            → RAM ≥ MIN_RAM_MB, Swap ≥ MIN_SWAP_MB, sonst warten
 3  Kontingent ok?            → sonst Konto wechseln, sonst warten  (Abschnitt 5)
 4  Ticket wählen             → ein Sonnet-Lauf wählt frei aus allen offenen (2–3 min)
 5  Schnappschuss             → tar der berührten Zone nach archive/autopilot/snap/
 6  Modus lesen               → Detailzeile „Modus: auto|plan"
 7  [wip] setzen              → über /hq/api/todo/status
 8  Lauf                      → claude -p, eigene Session-ID, Log als jsonl
 9  Abnahme                   → agent_loop.py, VIER Rollen (Abschnitt 6)
10  Urteil                    → done · nachbessern · test · leerlauf · freigabe · abbruch
11  Bericht + Journal         → ein Abschlussbericht ans Ticket, Ereignis ins Journal
→ nächstes Ticket


3. Welches Ticket er nimmt

Zuerst der Planer (HZ-1044, seit 09.08.2026 – er ist das, was man umgangssprachlich den „Orchestrator" nennt). Ein eigener Lauf auf claude-fable-5 liest das Zielbild und legt eine Queue an: welche Tickets als nächstes drankommen, in welcher Reihenfolge, und welche davon Stärke: schwer bekommen (das hebt sie beim Arbeiten auf Opus). Er darf dabei bis zu PLANER_NEUE_MAX = 3 neue Tickets schneiden. Liegt eine Queue vor, hat sie Vorrang vor der Wahl – der Wähler springt dann gar nicht erst an.

Er läuft ereignisgesteuert, nicht nach Uhr, und das ist Absicht:

GrößeWertwofür
PLANER_LAEUFE_MAX3 je 24 hKontingentschutz – Fable ist teuer, er soll denken, nicht takten
PLANER_QUEUE_MIN2weniger freie Einträge → Plan gilt als „dünn" → neu planen
PLANER_ALTER_H48so alt darf ein Plan höchstens werden, auch ohne Ereignis
PLANER_QUEUE_MAX5so lang wird die Queue höchstens
PLANER_TIMEOUT_S900Fable liest mehr Quellen als der Wähler

Sein Zustand steht in archive/autopilot/plan.json (stand, queue, laeufe_24h, notiz). Eine leere Queue heißt nicht „kaputt": sie heißt entweder „abgearbeitet" oder „Tagesdeckel erreicht, wartet". Wer das verwechselt, sucht einen Fehler, wo eine Regel greift – gemessen 11.08.2026, als drei Läufe vom Vortag den Deckel noch 23,5 h lang belegten.

Dann erst die Wahl. Ein eigener claude -p-Lauf (Sonnet, nur Read Grep Glob) bekommt die Kandidaten und die Vision-Schritte als Maßstab und begründet seine Wahl. Fällt er aus, greift die feste Fabrik-Reihenfolge aus kapitel.json → weg_schritte.

Beides hängt an derselben Datei wie die Vision-Seite. Zeigt ein Schritt dort auf ein Ticket, das es nicht mehr gibt, kann ihn weder Planer noch Wähler greifen – die Reihenfolge sieht voll aus und ist leer. Gemessen 11.08.2026: 10 von 13 System-Schritten waren so unerreichbar (HZ-1117).

Ausgeschlossen ist ein Ticket, wenn (frei_fuer_laeufer()):

GrundFunktion
steht auf [park]–
steht auf [test] und darf nicht nachgearbeitet werdennacharbeit_erlaubt()
steht auf [wip] (ein anderer Chat sitzt dran)–
ein anderer Läufer arbeitet im selben Gebietgebiet_frei()
in den letzten 2 h im selben Modus schon drangewesen (slot-uebergreifend)schon_dran() / archive/autopilot/schon_dran.json
gesperrt (gescheitert / wartet auf Freigabe)gesperrt_bis()
heute schon ANLAUF_MAX mal angefasstzu_oft_versucht()
hat einen offenen Blockeroffener_blocker()
baut am eigenen Code des Läufersbaut_am_eigenen_code()
alle im Ticket genannten Dateien hält eine lebende Fremdsitzungpfade_fremd_belegt()
kostet Geld ohne Freigabekostet_geld() + freigegebene()
ist ein Dach mit Subtickets–
ist ein reiner Leon-Handgriffnur_fuer_leon()
Bereich im HQ abgewähltbereiche_aus()
„nur Vision" an und Ticket nicht auf dem Wegnur_vision() / vision_nummern()

Kernzahl-Vorrang (klon_stillstand() + klon_vorrang(), seit 03.08.2026): wurde seit 24 h kein neuer Gegenstand klon-gemessen, bleiben nur noch Tickets in der Auswahl, die eine Messung erzeugen oder eine gemessene Lücke schließen (klon-luecke:-Marke, Trockenlauf, Artefakt-Takt, ganze Folge). Erkannt wird das über die Aufgabenzeilen (aufgabenzeilen()), nicht über den ganzen Ticketkörper – die angehängte Historie enthält fast immer das Gegenteil („Vision-Bezug: bewegt die Klon-% nicht"). Der Filter endet von selbst mit der nächsten Messung an einem neuen Gegenstand, lässt die volle Liste stehen, wenn er sie leerräumen würde, und verengt bei unlesbarem Ledger gar nicht. Anlass: am 03.08. liefen 53 Läufe über 10,3 h, die Kernzahl stand die ganze Zeit auf dem Vortagswert.

Der Läufer legt nichts mehr zur Abnahme vor (Leon-Entscheid 03.08.2026: „alles automatisch, nur Video zählt"). Ein Lauf endet auf done (grüne Abnahme + Wirkungsbeleg), auf zurueck (Befunde offen oder kein Artefakt → Ticket bleibt in der Arbeit, gesperrt bis zum nächsten Anlauf) oder auf park (3× ohne Dateiänderung – raus aus dem Kreis statt weiterzudrehen). [test] setzt nur noch der Ermittler, wenn er misst, dass wirklich ein Mensch nötig ist: Optik, Geschmack, ein Handgriff außerhalb des Servers. Anlass: von 9 wartenden Tickets wollten 5 nur ein Nicken („sag nur, ob die Zeile reicht"), und 8 von 9 hatte der Ermittler längst auf To-do entschieden – der nächste Lauf setzte sie jedes Mal zurück auf [test].

[test]-Nacharbeit (nacharbeit_erlaubt(), gezählt aus dem Journal, verfällt nicht): 0 Enden = Leon hat es gesetzt → nur anfassen, wenn es einen Zielbild-Schritt blockiert · genau 1 = ein Nacharbeits-Durchgang · ≥ 2 = endgültig beim Menschen.


4. Was er nicht darf (Sperren im Werkzeug, nicht als Bitte)

Durchgesetzt von agent/policy_hook.py, scharf über HERZ_AUTOPILOT=1:

SperreWas frei bleibt
Geld – ApiMart, Higgsfield, HeyGen, YTSeller, letterXpress, --confirm-spend … (PAID_PATTERNS)Preis- und Guthaben-Abfragen
Grundfesten löschen – /opt/herz, /, /home, /etcjedes rm -r innerhalb des Repos (mit automatischem tar davor)
Global-Docker – compose down, system prune, Volumes, Netzedocker stop/rm/up -d <name>
TODO.md als Datei (Bindmount-Inode-Falle)alles über /hq/api/todo/*
Selbst abhaken – /hq/api/todo/done (check_autopilot_abhaken)status, note, add
Fremde Arbeit – zone_guard.py verweigert (exit 2) Schreibzugriff auf Dateien, an denen ein anderer Chat sitzteigene Zone

Nicht gesperrt, aber beobachtet: CLAUDE.md, zones.json, autopilot.py, policy_hook.py darf er ändern – der Läufer vergleicht Prüfsummen und meldet jede Änderung (regeldatei_geaendert), schreibt aber nie zurück.

Messgrundlagen (kapitel.json, registry.json) werden vor/nach jedem Lauf verglichen. Eine Änderung wird nicht verhindert – echter Fortschritt ändert sie zu Recht –, sondern der Abnahme als Frage vorgelegt: sachlich gedeckt oder nur Zahlenkosmetik?


5. Wann er aufhört und wann er weitermacht

Zwei verschiedene Bremsen:

Eigene Zählung (budget_stand()): summiert Ausgabe-Tokens aus dem Journal über BUDGET_FENSTER_S (5 h). Ab BUDGET_SCHWELLE (90 %) der gelernten Grenze wird pausiert. Die Grenze wird bei einem echten Anschlag gelernt (budget_lernen()) – je Konto, weil max und team verschiedene Tarife sind.

Echte Kontozahl (konto_stand()) schlägt die Eigenzählung. Sie kommt aus /hq/api/claude-konten und wird verworfen, wenn ihr session_reset hinter uns liegt – sonst hält eine veraltete Zahl den Läufer endlos in der Pause (der Fehler vom 31.07., HZ-629).

Statt zu schlafen: Konto wechseln (konto_wechseln()). Zwischen zwei Tickets, nie mitten im Lauf. Ein Konto gilt nur als frei, wenn Session und Woche unter der Schwelle liegen. Höchstens WECHSEL_PRO_SITZUNG (2) Wechsel je Sitzung. Welche Konten er nehmen darf, steuern die Chips „Womit er arbeiten darf" (konten_aus in bereiche.json, leer = alle).

Abbruch eines Laufs nur bei: Stille (STILLE_MAX_S = 40 min ohne jedes Ereignis) oder harter Obergrenze (TIMEOUT_S = 4 h). Nicht bei Wiederholung – ein Kriterium, das echte Arbeit tötet, ist schlechter als keins.


6. Abnahme und Urteil

Vier Prüfer laufen parallel (scripts/agent_loop.py, Sonnet, getrennte Aufträge):

RolleSucht
defekteRace Conditions, Fehlerpfade, nicht freigegebene Ressourcen, Sicherheit
vollstaendigkeitwas noch am alten Zustand hängt und still bricht
visionob die Arbeit das Zielbild bewegt – oder nur etwas schaltbar macht
wertejede Zahl und Beschriftung gegen ihre Quelle (die häufigste Fehlerklasse)

Exitcode: 0 sauber · 1 mindestens ein Befund hält auf · 2 Prüfer gescheitert. Aufhaltend ist nur schwere=hoch mit zuversicht hoch|mittel.

Mögliche Urteile:

ErgebnisBedeutung
donegrüne Abnahme und Lauf-Beweis und mindestens eine geänderte Datei
nachbessernBefunde gefunden → derselbe Lauf bekommt sie als Auftrag zurück (eine Runde)
testgeht zu Leon: Befunde bleiben, Prüfer gescheitert, oder kein BEWEIS
leerlaufnichts geändert → zurück auf To-do + Sperre (ab dem 3. Mal doch [test])
freigabean einer bezahlten Stelle gestoppt → Freigabe-Liste im HQ
abbruchStille oder Zeitdeckel → Rollback, Ticket gesperrt
kontingentPlan-Limit → warten oder Konto wechseln

7. Was er dir meldet

Discord (scripts/melden.py, Kanal #alerts, als Embed mit Feldern und HQ-Knopf) – nur was ohne dich nicht weitergeht:

AnlassDrossel
Freigabe nötig (Betrag, Dienst, Zweck)1 h je Ticket
Ticket dreht sich im Kreis1 h je Ticket
Autopilot steht still (eigener Code geändert)1 h
Zielbild hat sich bewegt – Schritt zu, Kernzahl bewegt, erster DNA-Beweis1 Tag
Stillstand – 3 gearbeitete Tage ohne jede Bewegung1 Tag

Gemerkt wird eine Meldung erst, nachdem sie angekommen ist – sonst schluckt die Drossel genau die, die niemand erhalten hat.

HQ (#/autopilot): Kennzahlen-Reihe · „Wo wir im Zielbild stehen" (mit Bedeutung je Zeile) · Freigaben · „Deine Abnahme schließt einen Zielbild-Schritt" · „Wartet auf deinen Test" · Live-Log · Verlauf. Dazu die Chips woran und womit er arbeiten darf.


8. Wo was liegt

DateiRolle
scripts/autopilot.pyder Läufer: Schleife, Wahl, Lauf, Abnahme, Urteil, Journal, Meldungen
scripts/autopilot_watch.pyCron-Wachposten – macht aus dem Start-Knopf einen Prozess
scripts/autopilot-settings.jsonEinstellungen des Kind-Laufs (hängt den Policy-Hook ein)
scripts/agent_loop.pydie vier Prüfer als Befehl
scripts/melden.pyder eine Melde-Weg (Discord, Telegram als Rückfall)
scripts/claude_konto.pyKonten umschalten (vom Läufer als Unterprozess gerufen)
agent/policy_hook.pydie Sperren
dashboard/app.py/hq/api/autopilot/*
dashboard/ui/85_autopilot.jsdie Seite + Cockpit-Band

Zustand in archive/autopilot/:

DateiInhalt
journal.jsonljedes Ereignis (das HQ liest daraus), mit slot und gebiet
laeufer/<slot>.jsonwas dieser Läufer gerade tut, alle 30 s ein Lebenszeichen
laeufer/<slot>.auslaufenMarke: dieser Läufer hört nach seinem Ticket auf
gebiete.jsonwelcher Slot welche Pfade hält (Riegel gegen zwei Läufer im selben Gebiet)
bereiche.jsonabgewählte Bereiche · nur_vision · abgewählte Konten · laeufer_soll
budget.jsongelernte Kontingent-Grenze (global + je Konto), Bremsschwelle, zaehlung_ab
sperren.json · anlaeufe.jsongescheiterte Tickets, Anlauf-Zähler
freigaben.jsonGeld-Freigaben (offen/erteilt/verbraucht)
zielbild_letzt.json · zielbild_bericht.jsonVergleichsstand und abgelegter Bericht
abnahme_letzte.jsonBefunde der letzten Abnahme
snap/ · geloescht/ · logs/Schnappschüsse, Lösch-Sicherungen, Lauf-Logs

Journal-Ereignisse: lauf_start · lauf_ende · start · aus · halt · auslaufen · budget_pause · budget_gelernt · kontingent_warten · konto_gewechselt · neustart_noetig · durchgang_fehler · regeldatei_geaendert · regelbruch · fremdaenderung · messgrundlage_geaendert · doku_geschrieben · freigabe_eingeloest · zielbild_gemeldet · zielbild_stillstand · abnahme_wiederholt


9. Die Stellschrauben

Alle in scripts/autopilot.py, oben beieinander:

KonstanteWertWofür
BUDGET_SCHWELLE0.90ab hier Pause bzw. Kontowechsel
BUDGET_FENSTER_S5 hFenster der Eigenzählung
WECHSEL_PRO_SITZUNG2Deckel gegen das Konto-Karussell
ANLAUF_MAX / ANLAUF_FENSTER3 / 24 hwie oft ein Ticket am Tag drankommt
SPERRE_S2 hSperre nach einem gescheiterten Anlauf
STILLE_MAX_S40 minohne jedes Ereignis → hängt (Stand 06.08.2026, Code: scripts/autopilot.py)
TIMEOUT_S4 hharte Obergrenze je Lauf
LANGLAEUFER_S45 minab hier ein Hinweis ins Journal (kein Abbruch)
_(kein Deckel mehr)_–Denktiefe ist seit 05.08.2026 keine Tagesquote, sondern fest am Modus: plan → xhigh, alles andere → low (denktiefe())
MIN_RAM_MB / MIN_SWAP_MB1200 / 300darunter startet er nicht (7,6-GB-Host)
STILLSTAND_TAGE / _LAEUFE3 / 3wann der Stillstands-Alarm feuert
MELDUNG_DECKEL_S24 hhöchstens eine Zielbild-Meldung pro Tag
MODELL / MODELL_SCHWERsonnet-5 / opus-5Arbeiter: günstiger Standard / teure Ausnahme. Modell und Denk-Aufwand kommen seit HZ-1062 (11.08.2026) aus EINER Tabelle STAFFEL (Stufe → Modell/Effort) über staerke_fuer(t, modus, anlauf); die früheren zwei Funktionen modell_fuer()/effort_fuer() sind gelöscht. Stufen: mechanisch/normal → sonnet/low · schwer → opus/medium · eskalation (ab dem 2. Anlauf) → opus/medium · plan → opus/xhigh. Rangfolge: Eskalation → Modus: plan → Fables Queue-Feld staerke bzw. Ticketzeile Staerke: schwer → Stufe des Wahl-Laufs (stufe, mechanisch\normal) → Standard normal. leicht/mechanisch ist nie ein Veto, nur der Verzicht auf ein Upgrade. Geprüft von scripts/autopilot_selbsttest.py (Test staffel, 15 Prüfungen)
STAFFEL_AUSleer=1 stellt den Stand vom 11.08.2026 her: Effort low außer im Plan-Modus, Modell wie zuvor. Rückfahrkarte für HZ-1062
MODELL_PLANER / MODELL_WAHLfable-5 / sonnet-5Fable ordnet an (was UND wer), Sonnet wählt/ermittelt

10. Bekannte Grenzen

  • Der laufende Prozess kennt nur den Code seines Starts. Jede Änderung an autopilot.py

wirkt erst nach einem Neustart; er beendet sich dafür selbst, aber erst nach dem laufenden Ticket – in einer Pause merkt er es nicht.

  • Fremde Arbeit im selben Zeitfenster geht in die Abnahme ein (geaenderte_dateien() misst

über die Uhrzeit). Folge ist Rauschen, kein Schaden.

  • Medien im Schnappschuss sind ausgeschlossen (82 MB → 14 MB). Löscht ein Lauf ein Bild, holt

der Rollback es nicht zurück.

  • Die Wochen-Grenze bremst nicht. Gebremst wird nach der Session (Leons Entscheid, weil die

Woche über Tage läuft) – bei voller Woche läuft er bis Claude hart stoppt. Die Seite warnt.

  • Erledigt (05.08.2026, HZ-725): der Bericht zeigt die Klon-% als Kernzahl, die DNA-Quote

nur noch als Nebenzahl (zielbild_bericht() → klon_messen() → klon_verlauf.stabiler_stand, also derselbe Median wie HQ und Vision-Seite). Die Zeile nennt Quelle, Alter, n Urteile aus messungen Ledger-Zeilen, die Spanne zwischen den Messungen und die Spanne innerhalb der jüngsten (die vier Linsen untereinander – am 05.08. 60 Punkte, während die Spanne zwischen den Zeilen 0 war), Vorbehalte sowie entwertete Zeilen. Erhoben wird clone_score per Cron (scripts/clone_score_watch.py, jede Minute, ein Lauf zur Zeit per flock, nur Kanäle aus clone_score_watch.json). Vorher stand hier „der Bericht zeigt die DNA-Quote" (Stand 03.08.) – seit HZ-665 falsch.

  • Der Ermittler durfte clone_score.py bis 03.08.2026 nicht aufrufen – er stand in

ERMITTLER_ABGELEHNT. Belegt ist inzwischen: --stand [--json] liest nur und kostet $0. Aufnahme in die Allowlist per scripts/patch_ermittler_clone_score.py --apply (ein Befehl, mit Sicherung und Gegenprobe) – ein unbeaufsichtigter Lauf darf autopilot.py laut Auftragstext nicht selbst ändern, der Policy-Hook hindert ihn seit 29.07.2026 aber nicht mehr daran (AUTOPILOT_RO enthält nur noch TODO.md).


Verwandtes

  • design.md – warum es so gebaut ist: Entscheidungen, Fehlschläge, Lehren je Runde
  • ../architektur.md – Systemkarte des ganzen Hauses
  • ../kb/werkzeuge-rezepte__bauer-pruefer-loop-muster.md – das Prüfer-Muster
  • ../kb/werkzeuge-rezepte__hq-design-standard-nexus.md – wie die HQ-Seite auszusehen hat
  • https://vision.herz-automation.com – das Zielbild, auf das der Läufer hinarbeitet

Quelle im Repo: docs/autopilot/README.md