Welle 3 · Lucys Freiheiten je Aktionsart, Rückfragen mit „ja 7“, Lucy untersucht rote Hinweise, Wochenbericht
- services/lucy_autonomie.py: Stufe je Aktionsart (nur lesen, erst fragen, selbst handeln), Start „mehr Freiheit“;
Meldung danach (POST /api/lucy/gehandelt)
- services/rueckfragen.py: nummerierte Rückfragen, nur Knöpfe der Oberfläche (ERLAUBT), „ja“ führt genau die
gespeicherte Aktion aus, 24 h gültig, hinfällig wenn der Hinweis weg ist, Nachprüfung danach
- notify.sh -k: Antworttasten (Antworttastatur über den Direktweg, ohne Eingriff in Hermes)
- services/ermittlung.py: Wächter meldet rote Hinweise an Lucy; Knopf wird Rückfrage, Lucy sieht über den
api_server nach und schickt Ursache und Weg als eine Nachricht
- services/wochenbericht.py + mc2-wochenbericht.timer (So 07:30): feste Sätze, Absatz vom Modell, offene
App-Updates als Rückfragen
- MCP: Werkzeuge fragen die Freiheiten (auf_bitte), neu: freiheiten, rueckfragen, rueckfrage_beantworten,
sicherheitsupdates_einspielen, snapshot_zurueckspielen, snapshots, geraet_logs, dienst_logs
- Ausführer-Aktion journal (nur lesend), POST /api/homelab/ziele/{id}/sicherheitsupdates
- Läufe des Homelabs mit flock (Oberfläche und Steward schreiben beide)
- Skill homelab-orchestrator, SOUL-Abschnitt als Kopie in deploy/nur-box
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
dd23f3fb4c
commit
12cefb427a
@@ -0,0 +1,63 @@
|
||||
"""Der Wächter gibt rote Hinweise an Lucy (seit 25.09.2026, Ausbauplan Welle 3): Die KI-Box antwortet mit der
|
||||
Antwortzeile für den Knopf des Hinweises und ob Lucy nachsieht; der Wächter hängt beides an seine Nachricht."""
|
||||
|
||||
import httpx
|
||||
from services import waechter
|
||||
|
||||
|
||||
class Antwort:
|
||||
status_code = 200
|
||||
|
||||
def __init__(self, daten):
|
||||
self._daten = daten
|
||||
|
||||
def json(self):
|
||||
return self._daten
|
||||
|
||||
|
||||
def _vorbereiten(monkeypatch, tmp_path, post):
|
||||
monkeypatch.setattr(waechter, "LUCY_BEFUND", True)
|
||||
monkeypatch.setattr(waechter, "STORE_PATH", tmp_path / "waechter.json")
|
||||
monkeypatch.setattr(waechter, "ZUSTAND_PATH", tmp_path / "zustand.json")
|
||||
monkeypatch.setattr(waechter, "_stand", {"hinweise": {}, "kandidaten": {}, "verlauf": [], "auto": {}, "stand": 0.0})
|
||||
monkeypatch.setattr(waechter, "_update_laeuft", lambda: False)
|
||||
monkeypatch.setattr(waechter, "FAIL_AFTER", 1)
|
||||
monkeypatch.setattr(waechter, "REMIND_S", 0)
|
||||
monkeypatch.setattr(waechter.httpx, "post", post)
|
||||
befund = waechter.Befund(id="dienst:lucy-stimme", stufe="rot", titel="Lucys Stimme ist gestoppt",
|
||||
text="Die Unit lucy-stimme läuft nicht.", quelle="lucy-stimme",
|
||||
aktionen=[{"id": "neustart", "label": "Starten", "dienst": "lucy-stimme"}])
|
||||
monkeypatch.setattr(waechter, "PRUEFUNGEN", (lambda: [befund],))
|
||||
meldungen: list[tuple[str, list[str] | None]] = []
|
||||
monkeypatch.setattr(waechter, "_telegram", lambda betreff, text, knoepfe=None: meldungen.append((text, knoepfe)))
|
||||
return meldungen
|
||||
|
||||
|
||||
def test_roter_hinweis_mit_antwortzeile(monkeypatch, tmp_path):
|
||||
gesendet = []
|
||||
|
||||
def post(url, timeout=None, json=None):
|
||||
gesendet.append((url, json))
|
||||
return Antwort({"zeilen": ["Antworte „ja 3“, dann starte ich lucy-stimme.",
|
||||
"Ich sehe nach und melde mich mit Ursache und Weg."], "knoepfe": ["ja 3", "nein 3"]})
|
||||
|
||||
meldungen = _vorbereiten(monkeypatch, tmp_path, post)
|
||||
waechter.takt()
|
||||
url, daten = gesendet[0]
|
||||
assert url == "http://127.0.0.1:9/api/lucy/befund" # conftest: MC2 zeigt in Tests ins Leere
|
||||
assert daten["bereich"] == "box" and daten["erinnerung"] is False and daten["aktionen"][0]["id"] == "neustart"
|
||||
assert meldungen == [(("Lucys Stimme ist gestoppt. Die Unit lucy-stimme läuft nicht.\n"
|
||||
"Antworte „ja 3“, dann starte ich lucy-stimme.\n"
|
||||
"Ich sehe nach und melde mich mit Ursache und Weg."), ["ja 3", "nein 3"])]
|
||||
# Die Erinnerung sagt es der Box (dort gibt es dann nur die Antwortzeile wieder, keine zweite Untersuchung).
|
||||
waechter.takt()
|
||||
assert gesendet[1][1]["erinnerung"] is True and meldungen[1][0].startswith("Immer noch: Lucys Stimme")
|
||||
|
||||
|
||||
def test_ohne_box_bleibt_die_meldung_wie_bisher(monkeypatch, tmp_path):
|
||||
def kaputt(*a, **k):
|
||||
raise httpx.ConnectError("weg")
|
||||
|
||||
meldungen = _vorbereiten(monkeypatch, tmp_path, kaputt)
|
||||
waechter.takt()
|
||||
assert meldungen == [("Lucys Stimme ist gestoppt. Die Unit lucy-stimme läuft nicht.", None)]
|
||||
Reference in New Issue
Block a user