Programmiervorgaben an Claude Code

Wir hatten gestern beim Hub KI über Vibe Coding gesprochen. Ich hatte erzählt, dass ich von Claude (GPT, bzw. Agent - heißt bei Claude Chat & Cowork) Programmiervorgaben schreiben lasse. Diese Programmiervorgaben setzt dann Claude code um.

Da ich es gestern nicht live herzeigen konnte (VPN und 4future Seminar vertragen sich nicht gut) - hier ein Beispiel einer Programmieranweisung:

aufgabe-sweeper-takt.md — Seat-Provisionierung im Fünf-Minuten-Takt statt einmal täglich

Live gefunden 2026-09-02 beim Aufbau der Demofirma Digiwork.

Befund

Der Sweeper hängt vollständig an einem Hook:

add_hook('DailyCronJob', 1, function ($vars) { ... ispcfg3_sweeper_run(); });

DailyCronJob feuert einmal am Tag. Eine Seat-Zuweisung, die ein OrgAdmin im
Portal vornimmt, bleibt deshalb bis zum nächsten Tageslauf auf pending — die
Person sieht in ihrem Profil vier Dienste mit „wird eingerichtet", und es
passiert stundenlang nichts.

Das Portal verspricht etwas anderes. Org\UserController meldet nach der
Zuweisung: „Die Einrichtung läuft (bis zu 5 Minuten)." Diese Zusage war noch
nie gedeckt.

Der WHMCS-System-Cron läuft bereits alle fünf Minuten:

/etc/crontab:
*/5 * * * * www-data /usr/bin/php8.3 -q /var/www/_secure/whmcs4fd/crons/cron.php

AfterCronJob feuert damit alle fĂĽnf Minuten. Der kurze Takt ist also
vorhanden, er wird nur nicht genutzt. Kein Eingriff am Systemcron nötig.

Nicht: den ganzen Sweeper häufiger laufen lassen

Ein Sweeper-Lauf macht sehr viel mehr als Seats — Let’s-Encrypt-Bestellungen,
DMARC-Weiterleitungen, Web-Quota-PrĂĽfungen, Waisen-Abgleich,
WordPress-Umstellungen, Addon-Abgleich und den vollen Kontingent-Abgleich ĂĽber
alle WHMCS-Kunden. Das ist auf einen Lauf pro Tag ausgelegt; im Code steht
ISPCFG_SWEEPER_SSL_BUDGET_PER_RUN = 25 mit dem Kommentar „bewusst weit unter
dem Limit". Bei 288 Läufen am Tag ist dieses Budget bedeutungslos und die
Let’s-Encrypt-Ratenbegrenzung ein realistisches Ziel.

Teil 1 — Zweiter Hook, nur für die wartenden Pfade

Neuer add_hook('AfterCronJob', ...), der ausschlieĂźlich die drei Funktionen
aufruft, auf die ein Mensch wartet:

  • ispcfg3_sweeper_reconcile_seat_provisioning()
  • ispcfg3_sweeper_reconcile_seat_revocations()
  • ispcfg3_sweeper_reconcile_mailbox_unlocks()

ispcfg3_sweeper_run() bleibt unverändert am DailyCronJob und ruft dieselben
drei Funktionen weiterhin mit auf — sie sind idempotent, ein zusätzlicher
Durchlauf im Tageslauf schadet nicht und dient als Netz, falls der kurze Takt
ausfällt.

Der neue Hook braucht denselben require_once- und ISPCFG_SWEEPER_DRY_RUN-
Vorspann wie der bestehende (siehe dortigen Kommentar: der Guard gehört hinter
das require_once, nicht als top-level define()).

Teil 2 — Sperre gegen überlappende Läufe

Bei fünf Minuten Takt können sich zwei Läufe überholen, wenn einer hängt. Die
drei Pfade legen Mailboxen in ISPConfig an und setzen FreeIPA-Gruppen — zweimal
parallel ist nicht harmlos.

Lock-Datei mit flock (nicht blockierend): läuft bereits einer, still
zurückkehren, ohne Logzeile. Zusätzlich eine Altersgrenze, damit ein
abgestürzter Lauf die Sperre nicht dauerhaft hält.

Teil 3 — Nicht ins Log spammen

Der bestehende Lauf beginnt mit ISPConfigLogger::info('Sweeper', 'Start', ...).
288 solcher Zeilen pro Tag machen das Log unbrauchbar, in dem man sonst genau
die Seat-Meldungen sucht.

Der kurze Lauf loggt deshalb nur, wenn es etwas zu tun gab: keine offenen
Zuweisungen, keine Widerrufe, keine Entsperrungen → keine Zeile. Die
bestehenden Meldungen im Erfolgs- und Fehlerfall („Seat provisioniert",
„Seat-Provisionierung übersprungen - …") bleiben unverändert.

Auch die Zusammenfassungsmail (ispcfg3_sweeper_send_summary()) bleibt dem
Tageslauf vorbehalten.

Teil 4 — Verhältnis zu DRY_RUN klären

ISPCFG_SWEEPER_DRY_RUN gatet heute den Addon-Abgleich, DMARC und SSL, aber
nicht den Seat-Pfad. PrĂĽfen, ob das Absicht ist, und die Entscheidung im neuen
Hook kommentieren — sonst provisioniert eine Umgebung, die sich für einen
Trockenlauf hält, munter Mailboxen.

Teil 5 — Bekannte Grenze benennen

Der Kontingent-Abgleich (ispcfg3_sweeper_reconcile_seat_contingent()) bleibt
bewusst im Tageslauf: er geht alle WHMCS-Kunden durch. Folge — eine frisch
gekaufte
Lizenz ist im Portal erst am nächsten Tag als Kontingent sichtbar,
und bis dahin lässt sie sich nicht zuweisen.

Für den Kaufweg ist das die eigentlich störende Wartezeit, und sie bleibt nach
dieser Aufgabe bestehen. Als bekannte Grenze im Code kommentieren, nicht
stillschweigend lassen; die Lösung wäre ein gezielter Abgleich für einen
Kunden direkt nach der Bestellung, und das ist eine eigene Aufgabe.

Akzeptanzkriterien

  1. Eine Seat-Zuweisung im Portal ist spätestens fünf Minuten später
    provisioniert; die Dienste in der Personen-Detailansicht stehen auf
    eingerichtet.
  2. Der Tageslauf verhält sich unverändert — SSL-Budget, DMARC, Quota, Addons,
    Kontingent-Abgleich und Zusammenfassungsmail wie bisher.
  3. Zwei gleichzeitige Läufe des kurzen Hooks sind ausgeschlossen.
  4. Ein Lauf ohne offene Arbeit hinterlässt keine Logzeile.
  5. Die Portal-Meldung „bis zu 5 Minuten" stimmt danach.

Regeln fĂĽr die Umsetzung

  • Branch statt main, kleiner Diff, deutsche Kommentare mit BegrĂĽndung.
  • Kein Eingriff am Systemcron — der FĂĽnf-Minuten-Takt ist bereits da.
  • Testen: WHMCS hat kein Testsystem. Nach dem Deploy eine Zuweisung im Portal
    vornehmen und die Uhr laufen lassen; das Modul-Log zeigt, ob der kurze Hook
    gefeuert hat.
1 „Gefällt mir“