Leite Diagnosezustand im Statuspfad ab
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
852bba6264
commit
4ba3b4ddef
5 changed files with 61 additions and 0 deletions
|
|
@ -199,6 +199,7 @@ Ergaenzt seit dem ersten Geruest:
|
|||
- Backend ergaenzt den Read-Pfad um `received_at` und eine einfache `stale`-Ableitung
|
||||
- Backend bietet zusaetzlich eine kleine Uebersicht aller zuletzt meldenden Screens
|
||||
- Backend validiert den Statuspfad jetzt enger auf erlaubte Lifecycle-/Connectivity-Werte und leitet `stale` aus dem gemeldeten Intervall ab
|
||||
- Backend leitet im Read-Pfad zusaetzlich ein kompaktes `derived_state` fuer Diagnosekonsumenten ab
|
||||
- dateibasierte Agent-Konfiguration zusaetzlich zu Env-Overrides
|
||||
- strukturierte Agent-Logs mit internem Health-Snapshot und signalgesteuertem Shutdown
|
||||
- erster periodischer HTTP-Status-Reporter im Agent
|
||||
|
|
|
|||
|
|
@ -85,10 +85,17 @@ Zusaetzlich fuegt das Backend im Read-Pfad derzeit hinzu:
|
|||
|
||||
- `received_at` als serverseitigen Annahmezeitpunkt des letzten gueltigen Reports
|
||||
- `stale` als einfache serverseitige Einordnung, ob der letzte Report bereits veraltet wirkt
|
||||
- `derived_state` als zusammengefasste Diagnoseeinschaetzung fuer Konsumenten des Read-Pfads
|
||||
|
||||
`stale` ist aktuell bewusst nur eine kleine Diagnosehilfe fuer die Entwicklungsstufe und noch kein vollstaendiges Online-/Offline-Modell fuer spaetere Admin-Oberflaechen.
|
||||
Die Schwelle wird derzeit einfach aus dem gemeldeten `heartbeat_every_seconds` abgeleitet: mehr als zwei Intervalle ohne neuen Report gelten als veraltet.
|
||||
|
||||
`derived_state` wird aktuell bewusst einfach abgeleitet:
|
||||
|
||||
- `offline` bei `stale = true` oder `server_connectivity = offline`
|
||||
- `degraded` bei `server_connectivity = degraded|unknown` oder wenn `status` nicht `running` ist
|
||||
- `online` in den verbleibenden Faellen
|
||||
|
||||
## Abgrenzung
|
||||
|
||||
Noch nicht Teil dieser Stufe:
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ func handleGetLatestPlayerStatus(store playerStatusStore) http.HandlerFunc {
|
|||
}
|
||||
|
||||
record.Stale = isStale(record, store.Now())
|
||||
record.DerivedState = deriveState(record)
|
||||
|
||||
writeJSON(w, http.StatusOK, record)
|
||||
}
|
||||
|
|
@ -133,6 +134,7 @@ func handleListLatestPlayerStatuses(store playerStatusStore) http.HandlerFunc {
|
|||
filtered := make([]playerStatusRecord, 0, len(records))
|
||||
for i := range records {
|
||||
records[i].Stale = isStale(records[i], store.Now())
|
||||
records[i].DerivedState = deriveState(records[i])
|
||||
if wantConnectivity != "" && records[i].ServerConnectivity != wantConnectivity {
|
||||
continue
|
||||
}
|
||||
|
|
@ -183,3 +185,15 @@ func staleThresholdFor(record playerStatusRecord) time.Duration {
|
|||
|
||||
return 2 * time.Minute
|
||||
}
|
||||
|
||||
func deriveState(record playerStatusRecord) string {
|
||||
if record.Stale || record.ServerConnectivity == "offline" {
|
||||
return "offline"
|
||||
}
|
||||
|
||||
if record.ServerConnectivity == "degraded" || record.ServerConnectivity == "unknown" || record.Status != "running" {
|
||||
return "degraded"
|
||||
}
|
||||
|
||||
return "online"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ type playerStatusRecord struct {
|
|||
ServerConnectivity string `json:"server_connectivity,omitempty"`
|
||||
ReceivedAt string `json:"received_at,omitempty"`
|
||||
Stale bool `json:"stale,omitempty"`
|
||||
DerivedState string `json:"derived_state,omitempty"`
|
||||
ServerURL string `json:"server_url,omitempty"`
|
||||
MQTTBroker string `json:"mqtt_broker,omitempty"`
|
||||
HeartbeatEverySeconds int `json:"heartbeat_every_seconds,omitempty"`
|
||||
|
|
|
|||
|
|
@ -295,6 +295,10 @@ func TestHandleGetLatestPlayerStatus(t *testing.T) {
|
|||
if got, want := response.Stale, false; got != want {
|
||||
t.Fatalf("response.Stale = %v, want %v", got, want)
|
||||
}
|
||||
|
||||
if got, want := response.DerivedState, "degraded"; got != want {
|
||||
t.Fatalf("response.DerivedState = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetLatestPlayerStatusMarksStaleRecords(t *testing.T) {
|
||||
|
|
@ -329,6 +333,10 @@ func TestHandleGetLatestPlayerStatusMarksStaleRecords(t *testing.T) {
|
|||
if got, want := response.Stale, true; got != want {
|
||||
t.Fatalf("response.Stale = %v, want %v", got, want)
|
||||
}
|
||||
|
||||
if got, want := response.DerivedState, "offline"; got != want {
|
||||
t.Fatalf("response.DerivedState = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetLatestPlayerStatusUsesHeartbeatIntervalForFreshness(t *testing.T) {
|
||||
|
|
@ -361,6 +369,36 @@ func TestHandleGetLatestPlayerStatusUsesHeartbeatIntervalForFreshness(t *testing
|
|||
}
|
||||
}
|
||||
|
||||
func TestHandleGetLatestPlayerStatusDerivesOnlineState(t *testing.T) {
|
||||
store := newInMemoryPlayerStatusStore()
|
||||
store.now = func() time.Time {
|
||||
return time.Date(2026, 3, 22, 16, 0, 30, 0, time.UTC)
|
||||
}
|
||||
store.Save(playerStatusRecord{
|
||||
ScreenID: "online-screen",
|
||||
Timestamp: "2026-03-22T16:00:00Z",
|
||||
Status: "running",
|
||||
ServerConnectivity: "online",
|
||||
ReceivedAt: "2026-03-22T16:00:00Z",
|
||||
HeartbeatEverySeconds: 30,
|
||||
})
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/online-screen/status", nil)
|
||||
req.SetPathValue("screenId", "online-screen")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handleGetLatestPlayerStatus(store)(w, req)
|
||||
|
||||
var response playerStatusRecord
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("Unmarshal() error = %v", err)
|
||||
}
|
||||
|
||||
if got, want := response.DerivedState, "online"; got != want {
|
||||
t.Fatalf("response.DerivedState = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGetLatestPlayerStatusNotFound(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/screens/missing/status", nil)
|
||||
req.SetPathValue("screenId", "missing")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue