check_fds.sh rief last_seen_at des OPs-Agenten nie an - der Agent fiel zehn
Minuten nach seiner Registrierung dauerhaft vom Schreibtisch, obwohl der
Ping-Check normal weiterlief. Das Skript meldet jetzt zu Beginn "working" und
am Ende (auch bei einem fruehen Abbruch, per trap) "idle" per
POST /api/v1/agents/{id}/activity, analog zum Cron-Skript des
Entwickler-Agenten. Fehler dabei werden nur protokolliert, nicht fatal.
Dafuer braucht monitor-api.conf zusaetzlich MONITOR_AGENT_ID:
register-ops-agent.sh schreibt sie bei einer Neuregistrierung mit und
traegt sie bei einer bestehenden, aber noch unmigrierten Datei ohne
Neuregistrierung nach.
Schliesst #75.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bei mehreren Kacheln ueberlagerten sich die fest eingezeichneten
Servernamen im mittleren (Status-)Monitor. Der Name erscheint jetzt
nur noch als SVG-<title>-Tooltip beim Hoover ueber die jeweilige
Kachel, deren gesamte Flaeche als Hoover-Bereich dient.
Schliesst #73
Auf dem Produktionsserver scheiterte "update.sh" an der untracked hosts.conf:
seit Issue #70 schreibt check_fds.sh diese Datei bei jedem Lauf neu, versioniert
ist nur hosts-sample.conf - in .gitignore stand sie aber (anders als
monitor-api.conf und credentials.conf) nicht.
- hosts.conf in .gitignore aufgenommen
- update.sh blockiert nur noch bei Aenderungen an versionierten Dateien und
listet sie auf; unversionierte Dateien werden nur als Hinweis geloggt, denn
der ff-only-Pull laesst sie unangetastet bzw. bricht selbst kontrolliert ab
- neuer Notausgang UPDATE_ALLOW_DIRTY=1 fuer den Fall, dass doch einmal an einer
versionierten Datei gedreht wurde
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBVdYcfaXNq49bMdQQcZue
Wer einen Server aus der Ping-Ueberwachung nehmen wollte, musste sich bisher auf den
Rechner mit check_fds.sh einloggen und die hosts.conf editieren - und die Kachel blieb
danach trotzdem im System-Monitor stehen, weil ein einmal gemeldetes System nirgends
wieder geloescht werden konnte.
Die Liste steht jetzt in der Datenbank und ist ueber ein Zahnrad neben der Ueberschrift
"Systeme" pflegbar, analog zum Zahnrad am PO-Platz (Issue #27). Jeder Host gehoert dem
OPs-Agenten (Issue #66), der ihn anpingt: API und Ping-Check laufen nicht zwangslaeufig
auf demselben Rechner, die API kann die hosts.conf also nicht selbst schreiben.
check_fds.sh holt sie stattdessen bei jedem Lauf mit seinem Agenten-Key ab und schreibt
daraus die lokale Datei; ist die API nicht erreichbar, pingt es unveraendert nach der
zuletzt geschriebenen Liste weiter.
Beim ersten Lauf laedt der Check seine bestehende hosts.conf hoch, damit die bereits
ueberwachten Server nicht von Hand nachgetragen werden muessen. Dass die API das nur
einmal annimmt (Marker in host_config_imports) und "noch nie konfiguriert" per 204 von
"im Frontend geleert" per 200 unterscheidet, ist noetig, damit ein Ping-Rechner mit alter
lokaler Datei die dort geloeschten Hosts nicht wieder anlegt.
Loeschen raeumt mit auf: Ein entfernter Host nimmt seine Kachel mit - beide Ids, denn der
status.txt-Import benennt ihn nach dem Hostnamen, die REST-Meldung nach dessen Slug.
Kacheln ohne zugehoerigen Host (Selbstmelder oder Reste laengst entfernter Hosts) stehen
im selben Dialog unter "Weitere Kacheln im System-Monitor" und sind dort einzeln
loeschbar - dafuer gibt es neu DELETE /api/v1/systems/{id}.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vNjbfr8M3HSfWMee92GTE
Bisher hatte nur der Service sync ein "restart: unless-stopped". Nach einem
Reboot, einem OOM-Kill (512 MiB RAM in der Produktion) oder einem Docker-Neustart
kam deshalb nur sync von allein zurueck, waehrend api (8091) und web (8090)
liegen blieben. "docker compose ps" zeigte dann genau einen laufenden Container
und der Monitor war ueber die Firewall-Weiterleitung nicht mehr erreichbar - ein
Fehlerbild, das nach Firewall/Reverse Proxy aussieht, obwohl schlicht nichts auf
den Ports lauschte.
unless-stopped statt always, damit ein bewusstes "docker compose stop" bestehen
bleibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fuer den Betrieb fehlte bisher eine Rolle: "user" meldet zwar Daten, bleibt in der
Buero-Visualisierung aber unsichtbar, weil sie nie einen Schreibtisch bekommt. Der
Ping-Check aus check_fds.sh (Weg 3 im README) lief deshalb als "user" und tauchte im
Buero nicht auf.
"ops" fuellt die Luecke: die Schreibtischvergabe in AgentRepository::register() greift
automatisch, sobald die Rolle erlaubt ist - nur "user" bleibt ohne Platz.
Der CHECK-Constraint auf agents.role braucht dafuer eine eigene Migration. Ein Edit von
001_initial.sql wuerde nur frische Datenbanken erreichen: bestehende Installationen haben
die Migration laut schema_migrations laengst angewandt und wuerden bei role='ops'
weiterhin abbrechen. SQLite kann CHECK nicht per ALTER TABLE aendern, 004 baut die Tabelle
deshalb neu (Verfahren aus der SQLite-Doku, foreign_keys kurz aus, weil
tickets.assignee_agent_id auf agents zeigt).
Im Frontend bekommt die Rolle Label "OPs", eine Akzentfarbe und ein Zahnrad als
Schreibtisch-Deko - ohne Eintrag in der ROLES-Map rendert der Avatar grau und ohne Label.
Neu ist ausserdem register-ops-agent.sh neben der docker-compose.yml: es liest Admin-Key
und Basis-URL aus der .env, registriert den lokalen OPs-Agenten und schreibt den nur
einmalig ausgegebenen API-Key mit chmod 600 nach monitor-api.conf - also genau die beiden
Schritte aus Weg 3, die hosts.conf und credentials.conf nicht abdecken. Eine vorhandene
monitor-api.conf ueberschreibt es nicht, und ein zweiter Agent gleichen Namens (der einen
weiteren der acht Schreibtische belegen wuerde) wird vorher abgefangen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Laufen check_fds.sh und die API auf verschiedenen Rechnern, erreicht der
Legacy-Import die API nie: SystemController::index() liest MONITOR_STATUS_FILE
als lokale Datei, waehrend status.txt auf dem Ping-Host liegt. Der
LegacyStatusFileReader liefert dann stillschweigend ein leeres Array und der
System-Monitor zeigt dauerhaft "Keine Systeme gemeldet" - ohne Fehlermeldung,
die auf die Ursache hinweisen wuerde.
check_fds.sh meldet die Ping-Ergebnisse jetzt zusaetzlich per
POST /api/v1/systems/{id}/status (online -> ok, offline -> error) und schreibt
status.txt unveraendert weiter. Die Zugangsdaten stehen in monitor-api.conf
(Vorlage: monitor-api-sample.conf); fehlt die Datei, bleibt das Verhalten
unveraendert. Eine unerreichbare API bricht den Check nicht ab, sondern
protokolliert nur eine Warnung.
Die System-ID wird aus dem Hostnamen slugifiziert, weil der Router auf dem
rohen, nicht dekodierten Pfad matcht - "Nextcloud(intern)" landete sonst
prozentkodiert in der Datenbank. Der lesbare Name geht im Feld "name" mit.
Das README beschreibt den kombinierten Weg als "Weg 3" und weist bei Weg 1
darauf hin, dass der Legacy-Import eine lokale Datei erwartet.
Ausserdem zwei Fehler im selben Skript behoben:
- Pfade werden relativ zum Skript statt zum Arbeitsverzeichnis aufgeloest.
- Die letzte Zeile der hosts.conf wurde ohne abschliessenden Zeilenumbruch
stillschweigend verschluckt, ein zuletzt angehaengter Host also nie geprueft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
README erklaert jetzt beide Wege (hosts.conf-Ping-Ueberwachung und
direkte Statusmeldung per REST-API), damit neue Systeme im
System-Monitor auftauchen. hosts-sample.conf bekommt dazu einen
Format-Hinweis; check_fds.sh ueberspringt dafuer Kommentar-/Leerzeilen
in hosts.conf, statt sie faelschlich als Host misszuinterpretieren.
Bislang wurde ein Ticket beim Wechsel von/zu "in_arbeit" sofort an seiner neuen
Spalte gezeichnet, unabhaengig davon, ob der zustaendige Entwickler laut eigenem
gemeldetem state ("walking_to_board") ueberhaupt schon am Board angekommen war -
die Animation wirkte dadurch unvollstaendig/vorgezogen. Analog zu der fuer
Tuer-/PO-Besuche in Issue #55 eingefuehrten Merge-Logik haelt office-render.js
das Ticket nun an seiner alten Stelle, bis der zustaendige Agent laut aktuellstem
Poll das Board erreicht hat (oder nicht mehr feststellbar ist).
detectAgentOwnedTicketChanges() und hasAgentReachedBoard() in office-scene.js
kapseln die dafuer noetige Erkennungs-/Entscheidungslogik pur und testbar.
CodebergSyncService berechnet am Ende jedes Sync-Laufs (periodisch und
webhook-getriggert, auch bei PR-Aenderungen) die Summe der offenen Pull
Requests aller konfigurierten Projekte und speichert sie als KPI
"open_prs" im bestehenden generischen KPI-Repository - sie erscheint
damit automatisch im KPI-Monitor der Wand und im KPI-Modal.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Die per PING ueberwachten lokalen Server (check_fds.sh -> status.txt ->
LegacyStatusFileReader -> SystemRepository) landen bereits als Kacheln im
System-Monitor, dem mittleren der drei Monitore an der Buero-Wand - das ist
exakt der "alte Monitor" aus dem Issue. GET /api/v1/systems liefert
last_check_at dafuer schon mit, das Klick-Modal hat den Wert aber bisher
weder abgeholt noch angezeigt.
buildSystemModalContent() (office-scene.js) formatiert last_check_at jetzt
ueber die neue formatSystemCheckTime() - sie kommt sowohl mit ISO-8601
(API-gemeldete Systeme) als auch mit dem "YYYY-MM-DD HH:MM"-Format der
Legacy-status.txt klar. renderSystemModalBody() (office-render.js) zeigt den
Zeitpunkt nur bei Status "ok" an ("zuletzt erfolgreich geprüft: ..."), da nur
dann Erreichbarkeit UND Ping-Erfolg zusammenfallen - bei "Fehler" bliebe eine
Zeitangabe ohne Aenderung an check_fds.sh irrefuehrend.
Bewusst nicht Teil dieser Aenderung: Die im Issue zusaetzlich genannte
Portscan-Liste (Detailseite fuer weitere, nur manuell zu pruefende Server)
ist als "koennten" markiert und ohne konkrete Vorgabe zu unscharf fuer eine
automatisierte Umsetzung. Die sechs konkret genannten Hosts/IPs gehoeren in
die lokale, nicht versionierte hosts.conf (Analogon zu credentials.conf) und
damit auf den Produktivserver, nicht in dieses oeffentliche Repo.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bisher wurde ein Ticket sofort an seiner neuen Spalte gezeichnet, sobald ein
Statuswechsel erkannt wurde - der Tuerbesuch (Rolle "user") bzw. PO-Besuch
liefen davon unabhaengig erst danach als reine Kosmetik ab. Jetzt haelt
office-render.js das betroffene Ticket an seiner alten Stelle (neue Tickets
bleiben ganz verborgen), bis die zugehoerige Persona am Board angekommen ist
("interacting_with_board"), verschiebt es dann sichtbar und laesst die
Persona danach eine Sekunde stehen, bevor sie den Rueckweg antritt.
applyPendingTicketMoves() in office-scene.js kapselt die dafuer noetige
Merge-Logik pur und testbar; office-render.js reicht die passenden
onArrived-Callbacks an triggerDoorVisit()/triggerPoVisit() durch.
Nach dem Anlegen/Bearbeiten eines Projekts im Konfig-Dialog prueft ein neuer Endpunkt
(POST /api/v1/repo-configs/{id}/check), ob fds2610-bot dort Schreibrechte hat und ein
aktiver Webhook auf diese monitor-Instanz zeigt. Das Ergebnis erscheint als gruener Haken
bzw. rotes Kreuz rechts neben den Bearbeiten-/Loeschen-Symbolen, mit Tooltip zum geprueften
Service bzw. zur Fehlerursache.
Manchmal werden bei Codeberg geloeschte oder anderweitig verschwundene Issues
vom periodischen Sync nicht bereinigt (er upsertet nur, ruehrt fehlende Issues
nie an). Neuer Knopf neben der "Kanban-Board"-Ueberschrift stoesst dafuer einen
synchronen Voll-Resync an (POST /api/v1/tickets/resync, X-Admin-Key): jedes
konfigurierte Projekt wird neu gelesen, Tickets zu inzwischen verschwundenen
Issues werden je Projekt entfernt (auch wenn sie manuell auf eine nicht vom
Sync verwaltete Spalte wie "fertig" bewegt wurden), Tickets zu weiterhin
existierenden Issues bleiben unangetastet.
Der PO geht dabei sichtbar ans Board und steht dort, bis die Anfrage
abgeschlossen ist - anders als beim automatischen Board-Besuch (feste
Animationsdauer je Stage) macht das die tatsaechliche Sync-Dauer sichtbar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Der Agent (fds2610/Agent#9) setzt "help wanted" auf Issues, waehrend er auf
die Erfuellung einer externen Voraussetzung wartet. Solche Issues sollen im
Monitor-Board in der Spalte "QA" auftauchen statt im Backlog zu verschwinden.
"qa" wird dafuer wie "backlog"/"bereit"/"in_arbeit"/"testing"/"done" zur
sync-verwalteten Spalte; nur "fertig" bleibt weiterhin fuer ein zukuenftiges
Label reserviert und rein manuell.
Bislang hat RepoConfigController::delete() nur die Projektkonfiguration
geloescht - die Tickets des Projekts blieben (nur ohne Projektfarbe,
Issue #34) dauerhaft im Kanban sichtbar. Jetzt loescht der Endpunkt
zusaetzlich alle Tickets, deren repo-Feld auf das entfernte Projekt
zeigt.
Lange Meldungen liefen ueber den Monitorrahmen hinaus in die Szene. Jedes
Event belegt jetzt zwei Zeilen (Zeit + System, darunter eingerueckt die
Meldung), beide auf die Innenbreite gekuerzt. Die vollstaendige Meldung
zeigt ein <title>-Tooltip beim Hover ueber das Event.
Die Kuerzung selbst sitzt als truncateToWidth() in office-scene.js und ist
damit ohne DOM testbar; die Zeichenbreite ist an der breitesten der in
Frage kommenden Monospace-Schriften ausgerichtet, damit auf keinem System
etwas ueberlaeuft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der letzte Commit stellte das Test-Skript in package.json auf ein Glob-Muster um,
weil "node --test tests/frontend" seit Node 24 das Verzeichnis nicht mehr als
Testverzeichnis aufloest. Der Test-Container lief aber weiter auf node:20-alpine,
und Node 20 kennt Globs in "node --test" noch nicht - es sucht woertlich nach einer
Datei mit Sternchen im Namen und bricht ab:
Could not find '/app/tests/frontend/**/*.test.js'
Auf dem Entwicklungsrechner (Node 24 auf dem Host) fiel das nicht auf, auf dem
Produktionsserver liefen die Frontend-Tests dadurch gar nicht mehr. Da update.sh
mit "set -e" laeuft, brach es an dieser Stelle ab, noch bevor es die Abschlusszeile
ausgeben konnte - das Update selbst war zu dem Zeitpunkt aber schon vollstaendig
eingespielt, die Tests laufen erst nach dem Neustart der Dienste.
Statt das Glob zurueckzubauen das Image angehoben: node:20 ist seit April 2026
ohnehin ohne Support. Unter node:24-alpine laufen die 76 Frontend-Tests durch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
api/.phpunit.result.cache ist ein reines Laufzeit-Artefakt von PHPUnit
(zuletzt fehlgeschlagene Tests + Laufzeiten fuer die Testreihenfolge). Da
update.sh am Ende die Testsuiten ausfuehrt, schreibt PHPUnit die Datei bei
jedem Update neu - das Arbeitsverzeichnis war danach dirty und der naechste
Lauf von update.sh brach mit "Lokale Aenderungen vorhanden" ab.
Datei aus dem Index entfernt und in .gitignore aufgenommen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Laeuft einem Agenten die Claude-Anmeldung aus, laeuft das Cron-Skript um ihn
herum unbeeindruckt weiter: es meldet "working", ruft claude auf, bekommt nichts
Brauchbares zurueck und meldet am Ende "idle". last_seen_at ist frisch, der
Agent sieht beschaeftigt aus - waehrend Lauf fuer Lauf nichts entsteht. Der
Ausfall fiel bisher erst auf, wenn jemand merkte, dass keine PRs mehr kommen.
Agenten melden den Fall jetzt ueber zwei neue state-Werte (auth_expiring,
auth_expired) am bestehenden Activity-Endpunkt. state ist serverseitig nicht
eingeschraenkt, daher ohne Migration und ohne Aenderung an der API.
Dargestellt in drei Stufen der Aufmerksamkeit:
- Schloss ueber dem Avatar (bei auth_expired pulsierend), entsaettigter Koerper,
eingefaerbter Bildschirm und Namensschild am Platz
- Header-Banner mit den betroffenen Agenten, gelb bei Vorwarnung, rot bei
Ausfall - auch aus einigen Metern Entfernung noch lesbar
- KPI-Kachel "Problem: n", die sich nur einfaerbt, wenn n > 0
Kern ist isAgentVisible(), bewusst getrennt von isAgentPresent(): ein
blockierter Agent bleibt sichtbar, obwohl er sich nicht mehr meldet, zaehlt aber
nicht als "Online" mit. Ohne das verschwaende er nach dem Praesenz-Timeout genau
dann vom Schreibtisch, wenn seine Warnung dort stehen soll.
MAX_KPI_TILES von 4 auf 5, sonst verdraengen Agenten/Online/Problem/Hooks die
gemeldeten KPIs (F7) vollstaendig.
Nebenbei: "npm test" lief seit Node 24 nicht mehr (das Verzeichnis-Argument wird
nicht mehr als Testverzeichnis aufgeloest), jetzt per Glob.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
renderKpiMonitor() hat die Hoehe des KPI-Monitors gleichmaessig unter den Kacheln
aufgeteilt (rowH = (frame.h - 30) / tiles.length, rund 78px pro Zeile bei vier
Kacheln). Der Platz ging dabei in die Sparkline-Kaesten - und genau die
Zaehler-Kacheln "Agenten"/"Online" (Issue #39) und "Hooks" (Issue #38) haben
history: [] und damit gar keine Linie. Zwischen den Werten standen deshalb grosse
leere Kaesten.
Die Zeilen stapeln sich jetzt mit fester Hoehe von oben, und einen Sparkline-Kasten
bekommt nur noch, wer wirklich einen Verlauf hat: eine Zaehler-Kachel braucht damit
24 statt 77,5 Pixel, vier davon zusammen 96 statt 310. Der Kasten ist ausserdem 32px
hoch - die Hoehe, fuer die buildKpiSparklinePoints() die Punkte berechnet; vorher war
er rund 51px hoch und die Linie fuellte ihn nie aus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die im Issue beschriebene Event-Anzeige (neuestes Event oben, aeltere
schieben nach, Ueberschuss wird abgeschnitten) ist bereits seit Phase 4
umgesetzt (buildEventLines()/MAX_EVENT_LINES, F5) und braucht keine
Aenderung.
Neu ist die vierte, immer sichtbare KPI-Kachel ganz rechts im
KPI-Monitor: "Hooks: n" zaehlt die seit dem letzten Start des
api-Containers empfangenen Webhooks. Der Zaehler ist dateibasiert
(Metrics\HookCounter), weil der eingebaute PHP-Server jede Anfrage als
eigene Skriptausfuehrung behandelt; docker/entrypoint.sh setzt die Datei
beim Start des api-Containers (nicht des sync-Containers) zurueck. Neuer
oeffentlicher Endpunkt GET /api/v1/webhooks/stats liefert den Stand,
das Frontend haengt ihn als eigene Kachel an (buildHooksKpi()) und
reserviert ihr dafuer eine feste Kachel statt sie der MAX_KPI_TILES-
Kappung der uebrigen KPIs zu unterwerfen. Der Wert fliesst zusaetzlich
in den SSE-Fingerabdruck (ChangeFeed) ein.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Die KPI-Kacheln "Agenten" (registrierte Agenten) und "Online" (davon
in den letzten 10 Minuten aktiv, siehe AgentRepository::isPresent())
wurden auf der Monitor-Wand nie angezeigt. Statt eine eigene
Meldepflicht dafuer einzufuehren, leitet buildAgentCountKpis() beide
Werte direkt aus der ohnehin geladenen Agentenliste ab und stellt sie
den ueber /api/v1/kpis gemeldeten Kennzahlen voran.
Schliesst #39
deploy-webhook.py laeuft auf dem Host, prueft eingehende Codeberg-Webhooks per
HMAC-Signatur (analog zu POST /api/v1/webhooks/codeberg) und stoesst bei einem
Push auf main - also nach jedem manuell gemergten PR - das bereits vorhandene
update.sh an. README_UPDATE.md dokumentiert die dafuer noetigen manuellen
Vorarbeiten auf dem Server (Checkout, Docker, systemd-Unit, Webhook-Einrichtung).
Schliesst #36
Wenn ein Projekt im Projekte-Modal entfernt oder umbenannt wurde, hat
CodebergSyncService dessen Tickets nicht mehr angefasst - sie blieben
also dauerhaft in der alten Projektfarbe eingefaerbt, obwohl es dafuer
gar kein Projekt mehr gab (Issue #34). sync() setzt die Farbe solcher
Tickets nun nach jedem Lauf auf NULL zurueck, wodurch das Kanban die
Standardfarbe statt der veralteten Projektfarbe zeigt.
Schließt #34