Initial: Mesh-Configs vom 03.09.2026 01:23
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
---
|
||||
name: mesh-file-task-queue
|
||||
description: >
|
||||
Multi-Agent-Koordination ueber geteiltes Dateisystem stat.
|
||||
---
|
||||
|
||||
# Mesh File-Based Task Queue
|
||||
|
||||
Kooperative Aufgabenbearbeitung zwischen Hermes-Agents über einen gemeinsamen
|
||||
Datei-Share — der zuverlässigste Kanal, wenn Webhook-Antworten leer bleiben
|
||||
und Nutzer keinen manuellen Relay spielen wollen (Nutzer-Präferenz).
|
||||
|
||||
## Kernidee
|
||||
|
||||
Status = **Ordner**, nicht Feld in der Datei. Verschieben per `mv` ist atomar
|
||||
und konfliktfrei zwischen Agents (auf NFS zuverlässig atomar; über SSH/scp wird
|
||||
der atomare `mv` **remote auf dem Master** ausgeführt und ist ebenso konfliktfrei).
|
||||
Kein Locking-Konflikt, kein Lost Update.
|
||||
|
||||
**Blueprint-v1-Struktur (ab 02.08.2026):**
|
||||
|
||||
```
|
||||
/srv/work/tasks/
|
||||
├── offen/ # offene Aufgaben (JSON, Dateiname = <task_id>.json)
|
||||
├── in_arbeit/ # wird bearbeitet (Dateiname erhält Präfix <C.D>_ , letzte 2 IP-Oktette)
|
||||
├── fertig/ # erledigt, verifiziert
|
||||
├── bugfix/ # Ergebnis passt nicht — NUR Master entscheidet
|
||||
└── archiv/ # wirklich abgeschlossen — NUR Master entscheidet
|
||||
```
|
||||
|
||||
> ⚠️ Ältere Struktur (`todo/inProgress/finished/bugged/completed` mit Markdown)
|
||||
> wurde durch die Blueprint-v1-Struktur abgelöst.
|
||||
|
||||
## Task-Dateiformat (JSON, Blueprint v1)
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "T20260802_1785703372",
|
||||
"ziel_agent": "LaptopHermes",
|
||||
"original_ziel_agent": "LaptopHermes",
|
||||
"aufgabentyp": "html",
|
||||
"prioritaet": "mid",
|
||||
"budget_limit_eur": 1.0,
|
||||
"status": "offen",
|
||||
"ergebnis_pfad": null,
|
||||
"beschreibung": "...",
|
||||
"anlage_ts": "1785703372",
|
||||
"in_arbeit_ts": null,
|
||||
"fertig_ts": null
|
||||
}
|
||||
```
|
||||
|
||||
Pflicht-Felder: `task_id, ziel_agent, aufgabentyp, priorität, budget_limit_eur,
|
||||
status, ergebnis_pfad, original_ziel_agent`.
|
||||
|
||||
## Rollen & Regeln
|
||||
|
||||
### Executor (Sub-Agent)
|
||||
1. **Pollt alle 10s** `tasks/offen/`, zeitversetzt pro Agent (z.B. Sek. 10/20/40/50)
|
||||
2. Greift nur Tasks, deren `ziel_agent` seine Agent-ID/IP trägt
|
||||
3. `offen/` → `in_arbeit/` (vor Bearbeitung; Dateiname erhält Präfix aus den
|
||||
**letzten zwei ZT-IP-Oktetten**, z.B. `34.142_T2026....json`, für Log-Nachvollziehbarkeit)
|
||||
4. Ergebnis schreiben, dann `in_arbeit/` → `fertig/` (Erfolg) oder → `bugfix/`?
|
||||
**NEIN** — Executor archiviert/entscheidet NIE über `bugfix/` oder `archiv/`;
|
||||
nur Master entscheidet final. Bei eigenem Fehler meldet er über die Datei/Log.
|
||||
|
||||
### Master (Hub)
|
||||
- **Polling/Ausfall-Fallback:** **WIEDER AKTIV seit 2026-08-06**, aber im **sanften `nur_alarm`-Modus**
|
||||
(KEIN Auto-Relabel auf `"beliebig"` mehr). Parametrisiert: `VERPASST=180s` (3× Polling-Zyklus 30–60s),
|
||||
**Ping-Check** (Alarm nur wenn Ziel-Agent wirklich `down`, sonst still bei busy/slow), Cron alle 60s
|
||||
(`* * * * * /usr/local/bin/mesh-master-poller.sh`), Alarm → Event-Log `AUSFALL_ALARM`, Master entscheidet
|
||||
manuell. Historie + Begründung: `references/failover-poller-2026-08-06.md`.
|
||||
- **Retry:** Ergebnisse in `fertig/` prüfen; passt nichts → `bugfix/`; wirklich
|
||||
abgeschlossen → `archiv/`.
|
||||
- `bugged/finished/…`-Retry-Counter nach Bedarf (siehe Retry-Logik in blueprint)
|
||||
|
||||
> ⚠️ **Nutzergrundregel (2026-08-06): Bevor Betriebs-Zeitparameter geändert werden (Failover-Fenster,
|
||||
> Polling-Intervalle u.Ä.), IMMER erst den Problemlöser konsultieren** — nicht selber raten/sofort umsetzen.
|
||||
> Nutzer sagte explizit: „zeiten erfragen noch mit problemlöser vorher" und war verärgert, dass der
|
||||
> Master Zeiten eigenmächtig setzen wollte. Ablauf: Brainstorming-Skill `brainstorm` an einen online-
|
||||
> rechenstarken Agenten (UnraidHermes .34.142) senden mit klarer JSON-Abfrage, Ergebnis validieren, DANN
|
||||
> umsetzen. Der 03.08.-Fehler (30s zu aggressiv) passierte genau durch Setzen ohne Abstimmung.
|
||||
|
||||
## Share-Zugriff (SSH/scp — NICHT mehr NFS!)
|
||||
|
||||
**Seit 2026-08-03 ist NFS OBSOLET.** Alle Sub-Agenten laden/bearbeiten/verschieben
|
||||
Tasks über **SSH/scp mit Key-Auth** (verschlüsselt). Grund: NFS überträgt plaintext
|
||||
(Man-in-the-Middle-fähig); SSH ist Ende-zu-Ende verschlüsselt, zeigt nur eigene Tasks
|
||||
(Key-Auth) und ist auditierbar.
|
||||
|
||||
- `/srv/work/tasks/` bleibt die **zentrale Ablage auf dem Master**; kein Mount nötig.
|
||||
- Sub-Agent pullt eigene Tasks per `scp` (`offen/<JahrMonatTag>.<C>.<D>.*.json`),
|
||||
sperrt per remote-`mv` (absolut Pfade), schreibt Ergebnis, push nach `fertig/`.
|
||||
- Jeder Agent hat eigenen ed25519-Key (`/root/.ssh/<agent>_mesh` auf Master, Public-Key in authorized_keys).
|
||||
- **SSH_CMD MUSS den Host enthalten**, sonst `hostname contains invalid characters` → endlose SKIPs.
|
||||
- Daemon: systemd-user-service (Loop alle 10s) — NICHT nohup/Cron-@reboot (stirbt bei SSH-Session-Ende).
|
||||
- **Wichtigster Fix überhaupt:** Task-Dateien mit `chmod 0666` anlegen UND nach jedem `mv` setzen —
|
||||
vom Master (root) erstellte/mit `mv` bewegte Dateien sind sonst für Sub-Agenten (uid 1000) unschreibbar
|
||||
→ `PermissionError [Errno 13]` / Polling „funktioniert nie".
|
||||
- **fail2ban-Whitelist:** `ignoreip = <alle ZT-IPs>` in `[sshd]`-Sektion von jail.local, sonst bannt
|
||||
fail2ban die Mesh-IPs bei scp-Fehlschlägen → „Connection refused" trotz Ping ok.
|
||||
- NFS-Server auf dem Master ist **deaktiviert** (`systemctl disable nfs-kernel-server rpcbind`),
|
||||
NFS-Ports (2049/111/20048) aus UFW entfernt.
|
||||
|
||||
> ⚠️ _Nur falls doch NFS nötig:_ Export auf die **4 bekannten IPs** statt `/16` zur defense-in-depth,
|
||||
> und UFW + Export konsistent halten. Details: `references/ssh-scp-task-polling-2026-08-03.md`.
|
||||
|
||||
## Workflow-Beispiel
|
||||
|
||||
1. Master legt Task in `offen/` ab (JSON) — `mesh-task-create.sh <ziel> <auftrag> <typ> <prio>`
|
||||
2. Sub-Agent pollt alle 10s (systemd-user-service), pullt eigene Task per scp,
|
||||
sperrt per remote-`mv` → `in_arbeit/`, bearbeitet via `hermes chat`, schreibt `result`
|
||||
3. Pusht nach `fertig/` (remote-`mv`)
|
||||
4. Master verifiziert `fertig/`; nur Master entscheidet über `bugfix/` oder `archiv/`
|
||||
5. Optional: Artefakte auf Dokumentations-Website spiegeln (`dokumentationen.perfekt-ohne-en.de`)
|
||||
— `mesh-master-report.sh` generiert die HTML-Doku live aus /srv/work/tasks
|
||||
|
||||
## Brainstorming verteilen (Multi-Perspektive, bewährt 2026-08-04)
|
||||
|
||||
Nutzerliebling-Ansatz: **zwei Rechen-Agenten bekommen DIESELBE Aufgabe aus unterschiedlichen**
|
||||
Perspektiven, Master synthetisiert. Bewährte Kombination (TicTacToe & PersoEnt):
|
||||
|
||||
- **UnraidHermes** (34.142, Entwickler) → `brainstorm`: 2-3 Ausbau-/Lösungs-Optionen + Empfehlung
|
||||
- **SwitchHermes** (20.26, Cockpit) ODER **LaptopHermes** (178.202) → `brainstorm` mit
|
||||
**KRITIK-Perspektive**: Annahmen angreifen, größtes Risiko, robustere MVP-Version
|
||||
|
||||
Abwechselndes Ergebnis ist der Mehrwert: Der „Ausbau"-Agent liefert die naive/ambitionierte
|
||||
Idee, der „KRITIK"-Agent falsifikiert sie gegen reale Beschränkungen (z.B. TicTacToe:
|
||||
Minimax unspielbar → Schwierigkeitsstufen vs. KRITIK: zu viele Stufen schlecht, X/O-Wahl
|
||||
kleiner + risikofrei). Master = Entscheider nach Verifikation.
|
||||
|
||||
## Skripte (Master, auf dem Hub/VPS) — Präfix `mesh-` (seit 2026-08-03 umbenannt von `duo-`)
|
||||
|
||||
- `scripts/master-task-handler.sh` — (älterer) Master-Cron mit Lock, Validierung, Retry
|
||||
- `mesh-task-create.sh <ziel> <auftrag> <typ> <prio>` — legt JSON-Task ab (Dateiname `JahrMonatTag.C.D.Auftrag.json`, setzt 0666)
|
||||
- `mesh-master-poller.sh` — Ausfall-Erkennung; **AKTIV seit 2026-08-06** (`nur_alarm`-Modus, `VERPASST=180s`,
|
||||
Ping-Check; Flag `AUSFALL_FALLBACK_AKTIV=1`), setzt NICHT auf "beliebig"
|
||||
- `mesh-master-report.sh` — generiert Doku-Website live aus den Task-Ordnern
|
||||
- `mesh-agent-poller.sh <meine-ip> <meine-id> [master] [keyfile]` — Sub-Agent-Poller (Pull/scp + remote-mv-Lock + hermes chat + push)
|
||||
- `mesh-poller-daemon.sh` — Loop alle 10s als systemd-user-service (robust, überlebt SSH-Session-Ende)
|
||||
- `mesh-daily-archive.sh` — Tages-Archiv (23:50), `mesh-log.sh` — Ereignis-Log
|
||||
|
||||
## Dokumentation (Nutzer-Präferenz: IMMER sichtbar)
|
||||
|
||||
Der Nutzer verlangt, dass **jede Aktion automatisch dokumentiert und jederzeit
|
||||
einsehbar** ist. Betreibe das Doku-Subsystem aktiv:
|
||||
|
||||
- `mesh-log.sh <aktion> <detail> [agent] [kosten]` bei **jeder** Aktion aufrufen
|
||||
(appendet an `/srv/work/log/events.jsonl`)
|
||||
- Live-Panel: `Dokumentationen.perfekt-ohne-en.de` (Tasks, Agenten, Kosten, 40 Events)
|
||||
- Tages-Archiv: `Archiv.perfekt-ohne-en.de` (täglich 23:50 Zusammenfassung)
|
||||
|
||||
Details, Skripte & Pitfalls: `references/dokumentation-archiv-2026-08-03.md`
|
||||
|
||||
## References
|
||||
|
||||
- `references/file-task-queue-srv-work.md` — ausführliche Doku + Samba/Pitfalls
|
||||
- `references/ssh-scp-task-polling-2026-08-03.md` — 🔑 SSH/scp-Umbau: NFS obsolet, Key-Auth,
|
||||
systemd-daemon, alle Pitfalls (SSH_CMD-Host, chmod 0666, fail2ban-Whitelist, Doppel-Daemon)
|
||||
- `references/failover-poller-2026-08-06.md` — 🔑 Failover-Poller-Reaktivierung (`nur_alarm`, 180s,
|
||||
Ping-Check) + Nutzerregel \"Zeiten erst mit Problemlöser abstimmen\" + Update-Cadence (wöchentlich)
|
||||
- `references/persoent-google-health-2026-08-07.md` — 🔑 PersoEnt: Google Fit REST ist deprecated, Health
|
||||
Connect hat KEINE Server-REST-API → PersoEnt muss native App werden (Android Health-Connect-SDK + iOS
|
||||
HealthKit) statt Website. Entscheidung: erst Android-APK (Nutzer testet am Handy), iOS später. Toolchain-
|
||||
Status auf VPS/LaptopHermes + Baustraßen-Optionen A/B.
|
||||
- Der konkrete APK-Build-Workflow (Flutter-SDK-Setup auf headless VPS, git safe.directory-Fix,
|
||||
AGP-namespace-Pitfall, Webroot-Download) liegt im Skill **`mobile-apk-flutter-build`** (devops).
|
||||
- `references/ssh-key-distribution-master-control.md` — 🔑 SSH-Key-Verteilung an Sub-Agenten (Workaround
|
||||
wenn Tasks nicht ziehen), Master Control Suite (Notfall-Tools + Budget-Report), OS-Update via SSH,
|
||||
Gateway-Auto-Start nach Reboot, Budget-Report-Format (Bullet-Liste für Handy).**Füge ein `# Sub-Agent Task Format: Executable vs Analysis (2026-08-28)`**
|
||||
|
||||
**Critical lesson:** Sub-Agenten fuehren Aufgaben NUR aus, wenn der Task eine einzige,
|
||||
klare Befehls-Instruktion enthaelt. Lange Beschreibungen fuehren zu Analyse statt Aktion.
|
||||
|
||||
| Format | Ergebnis |
|
||||
|--------|----------|
|
||||
| Lange Beschreibung (5+ Saetze, Kontext) | Sub-Agent analysiert, bricht ab ❌ |
|
||||
| Kurzer Befehl mit `befehl`-Feld | Fuehrt aus, meldet Ergebnis ✅ |
|
||||
|
||||
Regel: **Nur `befehl`- und `ansage`-Feld. KEINE Analyse, kein Kontext.**
|
||||
|
||||
### SSH-Key-Verteilung per Task (Henne-Ei-Problem)
|
||||
|
||||
Master braucht SSH zu Sub-Agenten (Kill-Switch, Notfall), hat aber keinen Key dort.
|
||||
Key per Task verteilen scheitert, weil Sub-Agenten kein Terminal fuer `echo pubkey >>
|
||||
authorized_keys` haben (nur `execute_code`/Python).
|
||||
|
||||
**Sofortloesung:** Manuell auf jedem Sub-Agenten ausfuehren:
|
||||
```
|
||||
mkdir -p ~/.ssh && echo '<master-pubkey>' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
## Master Control Suite (Notfall-Tools, 2026-08-28)
|
||||
|
||||
| Skript | Funktion | Cron |
|
||||
|--------|----------|------|
|
||||
| `/usr/local/bin/mesh-kill-all.sh` | Notfall: Alle auf Ollama | Manuell |
|
||||
| `/usr/local/bin/mesh-budget-watch.sh` | Guthaben prüfen, Auto-Switch | `*/30 * * * *` |
|
||||
| `/usr/local/bin/mesh-model-broadcast.sh` | Modell auf allen setzen | Manuell |
|
||||
| `/usr/local/bin/mesh-status-monitor.sh` | Task-Antworten + SSH tracken | `*/30 * * * *` |
|
||||
| `/usr/local/bin/mesh-model-scout.py` | Modelle scouten (alle 3 Tage) | `0 6 */3 * *` |
|
||||
|
||||
**Budget-Schwellen:** <$1 Warnung | <$0.50 Auto-Ollama | <$0.10 Stopp
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Nutzer akzeptiert KEINEN manuellen Relay.** Agents müssen sich über den
|
||||
Share direkt verständigen; der Nutzer ist nur für kostenpflichtige Aktionen
|
||||
(Essen bestellen, Token-Kauf) involviert.
|
||||
- **Task in `todo/` reicht NICHT** ohne Master/Executor-Cron auf der Gegenseite
|
||||
— sonst liegt die Aufgabe ewig (wie Message Bus ohne Poll-Cron).
|
||||
- **Verwaistes Ergebnis-Slot:** Aufgabenliste auf einer Webseite zeigt den
|
||||
Status live aus dem Share; immer den passenden Render-Pfad `/completed/` anlegen.
|
||||
- **`ls /path/*.json` unter `set -euo pipefail` bricht mit Exit 2 ab** bei
|
||||
leerem Glob (kein Match → Skript endet still, Datei wird nie erzeugt).
|
||||
Fix: `shopt -s nullglob` um die Zähl-Loops in `mesh-master-report.sh` und
|
||||
`mesh-daily-archive.sh`. Symptom: curl auf die Site = 403/404, obwohl der
|
||||
vhost existiert.
|
||||
- **Netzwerk konsolidieren:** nur EIN aktives ZeroTier-Netzwerk im Mesh —
|
||||
alte Netzwerk-IDs/Subnetze vollständig aus Skills, Config und UFW entfernen.
|
||||
- **`SSH_CMD` ohne Host** → `hostname contains invalid characters` + leerer Lock-Output
|
||||
(endet als SKIP-Schleife, obwohl der manuelle mv klappt). Host IMMER in `SSH_CMD` einfügen.
|
||||
- **root-eigene Task-Dateien unschreibbar für Sub-Agenten** (uid 1000): `PermissionError [Errno 13]`
|
||||
selbst bei 0777-Dir. Fix: `chmod 0666` bei Task-Erstellung und nach jedem `mv` (in_arbeit + fertig).
|
||||
- **fail2ban bannt Mesh-IPs** → `Connection refused` auf :22 obwohl Ping klappt. Fix: `ignoreip`
|
||||
mit allen ZT-IPs in `[sshd]`-Sektion von `/etc/fail2ban/jail.local`.
|
||||
- **Kontext für Sub-Agenten IMMER in die Task-Beschreibung einbetten — nie als externe Datei.**
|
||||
Agenten sehen per SSH/scp NUR `/srv/work/tasks/`, nicht `/srv/work/` oder `/root/`. Ein
|
||||
„Briefing pfad" wie `/srv/work/briefing_x.md` ist für den Agenten unsichtbar → er weiß den
|
||||
Projektkontext nicht und halluziniert (diagnostiziert 2026-08-04: LaptopHermes erfand „HR-Kausal-KI",
|
||||
weil er nicht wusste, dass PersoEnt eine Gesundheitsplattform ist). Fix: vollen Kontext als
|
||||
Detail-Parameter 6 in die Task-Beschreibung (`mesh-task-create.sh <ziel> <auftrag> <typ> <prio>
|
||||
<budget> \\\"Kontext: ... Aufgabe: ...\\\"`). Erst dann ist ein Verteil-Task zuverlässig.
|
||||
- **Sub-Agent-Review per URL scheitert → IMMER Seiteninhalt einbetten!**
|
||||
URLs allein reichen nicht. Sub-Agenten (besonders SwitchHermes auf ARM64) laden oft keine externen
|
||||
URLs oder stoßen an Tool-Limits. `curl -s "https://domain.de/"` liefert leeres Resultat.
|
||||
**Fix:** HTML + CSS + relevante Textauszüge DIREKT in die Task-Beschreibung. Nie: "Prüfe die URL".
|
||||
Sondern: "Hier der HTML-Code: <...>. Prüfe Kontrast/Lesbarkeit."
|
||||
- **Kurzwort "Briefing" (Nutzerwunsch 2026-08-06):** = das standardisierte Kontext-Übergabe-Dokument
|
||||
(Ziel / Entscheidungen / Begriffe / offene Fragen / Grenzen / nächste Schritte — wie ein Session-Handoff).
|
||||
Ein Wort, große Wirkung. Sagt der Nutzer "Briefing" (z.B. beim Start eines neuen Chats oder als Vorgabe
|
||||
für einen Agenten-Task), IST genau diese Struktur gemeint — als Master jeweils prüfen: vollständig?
|
||||
Dann arbeiten; fehlt Kontext → max. 5 Rückfragen.
|
||||
- **Ziel-Agent offline beim Verteilen → Task auf einen ANDEREN online-Agenten umleiten.**
|
||||
Der Ziel-Agent kann zwischen Task-Anlage und Poll-Takt ausfallen (z.B. Laptop offline,
|
||||
„No route to host"). Der Task bleibt dann ewig in `offen/` (Poller greift nicht, da
|
||||
falsche IP). Fix: Master entfernt den verwaisten `offen/`-Task und legt ihn **manuell neu**
|
||||
für einen online-Agenten an (z.B. Laptop→SwitchHermes). Den Offline-Task VOLLSTÄNDIG löschen
|
||||
(kein Doppel-Artefakt in offen + in_arbeit), Ziel-Agent + Dateinamen-Schema (C.D-Oktette) im
|
||||
neuen Task entsprechend anpassen. Erst das Line-of-Ownership-Log (`mesh-log.sh`) aktualisieren.
|
||||
- **Sub-Agent hängt in Skill/File-Lade-Schleife statt zu antworten (2026-08-08):** Bei komplexen
|
||||
Review-/Brainstorm-Tasks (z.B. „Code-Bremse prüfen", „Städte-Liste gegenprüfen") geraten
|
||||
Rechen-Agenten in einen Loop aus `skill_view(...)`/Dateisuche und stoßen an ihr
|
||||
Iterationslimit („Reached maximum iterations (3). Requesting summary..."), wobei **kein
|
||||
verwertbares `# Ergebnis`** ankommt — nur Reasoning-Spur, der Task sieht fälschlich „fertig"
|
||||
aus. Symptom-Erkennung: `result` enthält die Aufgabe reproduziert/umformuliert statt zu
|
||||
analysieren; ein `# Ergebnis`-Check im Code ist trügerisch, weil der Marker auch im
|
||||
Aufgaben-Prompt-Text vorkommt (echtes Ergebnis = Text NACH dem letzten `# Ergebnis`).
|
||||
**Fix: einfacher + konkreter Prompt, der Skill-Laden explizit verbietet** — z.B.
|
||||
„Beantworte NUR aus deinem Wissen, LADE KEINE Skills/Dateien, antworte sofort. FRAGE: <konkret>.
|
||||
UNTER '# Ergebnis'. Deutsch." Ein solcher fokussierter Prompt lieferte zuverlässig saubere
|
||||
Antworten (2× verifiziert), während der ausführliche „GRUNDGERÜST ... evaluiere ..." die Schleife
|
||||
auslöste. Master-Rechnung/APIs sowieso als Master selbst ausführen, da Sub-Agenten keinen Zugriff
|
||||
auf VPS-Dateien/APIs haben — für deren Bestätigung nur minimale, eindimensionale Fragen stellen.
|
||||
- **Task-Datei-Größen-LLM-Fehlschläge abfangen:** Wenn ein fertiger Task kein echtes Resultat
|
||||
hat, → `bugfix/` und einfacher neu stellen (nicht denselben komplexen Prompt wiederholen).
|
||||
- **Master muss Agenten-Ergebnis GEGEN das echte System verifizieren, bevor er darauf baut.**
|
||||
Sub-Agenten (besonders schwache Modelle) produzieren überzeugend klingende, aber teils
|
||||
halluzinierte Claims (z.B. „85+ Felder / Form-Builder gebaut", „Backend läuft"). Vor der
|
||||
Übernahme/Synthese IMMER gegen reale Quellen prüfen: `curl` auf die Live-Domain (Statuscode),
|
||||
`ls` auf den echten Datei-/Code-Pfad, Schema/Collections per API ansehen, `ss`/`systemctl`
|
||||
für Services. So zeigte sich z.B. „85+ Felder" nie gebaut (Migration hatte nur `health_records`),
|
||||
während „PocketBase läuft" stimmte. Baue auf dem **nachgewiesenen Ist-Stand** auf, nicht auf
|
||||
dem Agenten-Claim. Beide Rechen-Agenten konvergierend = guter Hinweis, aber KEIN Ersatz für
|
||||
die eigene Master-Verifikation.
|
||||
- **GENEREITER, NUTZERGEPRÜFT (2026-08-08): Auch der MASTER-eigene Code/Backend gehört durch
|
||||
Team-Gegenprüfung.** Beim Bau des Flask-Backends lieferten zwar die DATEN vom Team, aber der
|
||||
Code wurde solo gebaut — der Nutzer fragte zu Recht „Habt ihr euch gegengecheckt?" und eine
|
||||
kurze Review-Runde fand sofort 2 echte Bugs (DB-Deep-Link-Falschformat + Einwohner-Platzhalter).
|
||||
**Regel: „Verteilen > Einzelkämpfer" gilt auch für den Master-Selbstbau.** Nach dem Bau einer
|
||||
eigenständigen Komponente (Backend, Skript, Config) EINEN kompakten Review-Task an ein
|
||||
Rechen-Agent schicken (mit eingebettetem Kontext, einfachem Prompt — nicht in die
|
||||
Skill-Lade-Schleife fallen lassen), dann die benannten Fixes prüfen. Spart langfristig mehr,
|
||||
als der eine Review-Aufruf kostet.
|
||||
Signatur: `<ziel> <auftrag> <typ> <prio> <budget> <detail>`. Wer den langen Kontext-Prompt
|
||||
direkt hinter `<prio>` setzt, schreibt ihn in `budget_limit_eur` (Position 5) statt in
|
||||
`beschreibung` → Task-JSON wird **ungültig** (JSONDecodeError beim Parsen, Poller kann ihn
|
||||
nicht bearbeiten bzw. legt den Volltext in das Zahl-Feld). Symptom: `beschreibung` bleibt der
|
||||
reine Auftragsname 2 Zeichen kurz, `budget_limit_eur` enthält den ganzen String. Fix: Budget
|
||||
explizit angeben und Kontext an Position 6: `mesh-task-create.sh <ziel> <auftrag> <typ> <prio>
|
||||
1.0 \"<kompletter Kontext + Aufgabe>\"`. Nach Anlage immer JSON validieren
|
||||
(`python3 -c "import json;json.load(open(...))"`) und `beschreibung`-Länge gegenprüfen.
|
||||
|
||||
## Referenzen
|
||||
|
||||
- `references/file-task-queue-srv-work.md` — ausführliche Doku + Samba/Pitfalls
|
||||
- `references/ssh-scp-task-polling-2026-08-03.md` — 🔑 SSH/scp-Umbau: NFS obsolet, Key-Auth,
|
||||
systemd-daemon, alle Pitfalls (SSH_CMD-Host, chmod 0666, fail2ban-Whitelist, Doppel-Daemon)
|
||||
- `references/failover-poller-2026-08-06.md` — 🔑 Failover-Poller-Reaktivierung (`nur_alarm`, 180s,
|
||||
Ping-Check) + Nutzerregel "Zeiten erst mit Problemlöser abstimmen" + Update-Cadence (wöchentlich)
|
||||
- `references/persoent-google-health-2026-08-07.md` — 🔑 PersoEnt: Google Fit REST ist deprecated, Health
|
||||
Connect hat KEINE Server-REST-API → PersoEnt muss native App werden (Android Health-Connect-SDK + iOS
|
||||
HealthKit) statt Website. Entscheidung: erst Android-APK (Nutzer testet am Handy), iOS später. Toolchain-
|
||||
Status auf VPS/LaptopHermes + Baustraßen-Optionen A/B.
|
||||
- Der konkrete APK-Build-Workflow (Flutter-SDK-Setup auf headless VPS, git safe.directory-Fix,
|
||||
AGP-namespace-Pitfall, Webroot-Download) liegt im Skill **`mobile-apk-flutter-build`** (devops).
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
# Blueprint-v1 Deployment (02.08.2026) — NFS-Pflicht-Setup & Skripte
|
||||
|
||||
Diese Referenz dokumentiert den Übergang auf die Blueprint-v1-Task-Queue
|
||||
(JSON + offen/in_arbeit/fertig/bugfix/archiv) und die dabei verifizierten
|
||||
Schritte. Ergänzt die SKILL.md mit dem tatsächlich getesteten Deployment.
|
||||
|
||||
## Netzwerk-Einzahl
|
||||
|
||||
- **Ein** ZeroTier-Netzwerk: `6ab565387a75e498` (172.24.0.0/16).
|
||||
Das frühere Netz `b9a18a606fb05ae6` (10.36.223.x) ist **gelöscht**.
|
||||
- Agenten: A=172.24.196.119 (Master/Hub), B=172.24.34.142,
|
||||
SwitchHermes=172.24.20.26, LaptopHermes=172.24.178.202 (oft offline).
|
||||
Handy = kein Agent.
|
||||
|
||||
## NFS als Pflicht-Dateisystem (verifiziert)
|
||||
|
||||
Ein Task, der "Sub-Agenten selbst im Share schauen lässt" statt manuellem
|
||||
Auffordern, erfordert einen gemeinsam sichtbaren Ordner. **NFS ist Pflicht**;
|
||||
Samba/CIFS wird abgelehnt (Onboarding blockiert). Zwei Stolpersteine beim
|
||||
Einrichten:
|
||||
|
||||
1. **UFW blockt NFS stumm.** Symptom: `ping` zum Export-Host geht, aber
|
||||
`mount -t nfs …` hängt/fails und auch der Porttest
|
||||
`timeout 5 bash -c "echo >/dev/tcp/172.24.196.119/2049"` schlägt fehl.
|
||||
Fix (nur aufs ZT-Subnetz):
|
||||
```
|
||||
ufw allow from 172.24.0.0/16 to any port 2049 proto tcp # nfs
|
||||
ufw allow from 172.24.0.0/16 to any port 111 proto tcp # rpcbind
|
||||
ufw allow from 172.24.0.0/16 to any port 111 proto udp
|
||||
ufw allow from 172.24.0.0/16 to any port 20048 proto tcp # nfs lockd
|
||||
```
|
||||
Danach `exportfs -ra && systemctl restart nfs-kernel-server rpcbind`.
|
||||
|
||||
2. **requiretty/sudo auf Sub-Agenten (Zorin/PopOS).** `sudo` per SSH über
|
||||
stdin schlägt fehl ("ein Terminal ist erforderlich"). Lösung — Askpass:
|
||||
```bash
|
||||
echo '#!/bin/bash' > /tmp/ap.sh && echo "echo <PW>" >> /tmp/ap.sh && chmod +x /tmp/ap.sh
|
||||
export SUDO_ASKPASS=/tmp/ap.sh
|
||||
sudo -A <befehl>
|
||||
```
|
||||
|
||||
3. **NFS-Export nur aufs ZT-Subnetz** statt `*`:
|
||||
```
|
||||
/srv/work/tasks 172.24.0.0/16(rw,sync,no_subtree_check,no_root_squash)
|
||||
```
|
||||
|
||||
## Master-Skripte (auf dem Hub/VPS, /usr/local/bin/)
|
||||
|
||||
| Skript | Zweck |
|
||||
|--------|-------|
|
||||
| `duo-task-create.sh <ziel> <typ> <prio> <beschreibung>` | legt JSON-Task in `tasks/offen/` ab |
|
||||
| `duo-master-poller.sh` | Ausfall-Fallback: Task >30s in `offen/` → `ziel_agent="beliebig"` (Original bleibt in `original_ziel_agent`) |
|
||||
| `duo-master-report.sh` | generiert `dokumentationen.perfekt-ohne-en.de/index.html` live aus den Task-Ordnern |
|
||||
| `duo-agent-poller.sh <meine-ip> <meine-id>` | Sub-Agent-Poller: 10s-Takt, atomarer `mv`-Lock nach `in_arbeit/` mit Präfix C.D_ |
|
||||
|
||||
### Master-Crontab (versetzt)
|
||||
```
|
||||
* * * * * /usr/local/bin/duo-master-poller.sh >> /var/log/mesh-poller.log 2>&1
|
||||
* * * * * sleep 30; /usr/local/bin/duo-master-poller.sh >> /var/log/mesh-poller.log 2>&1
|
||||
* * * * * sleep 30; /usr/local/bin/duo-master-report.sh >> /var/log/mesh-doku.log 2>&1
|
||||
```
|
||||
|
||||
### Ausfall-Fallback (getestet)
|
||||
Der Poller wurde mit künstlich auf 45s gealtertem Task verifiziert:
|
||||
`ziel_agent` → `"beliebig"`, `original_ziel_agent` bleibt `"LaptopHermes"`.
|
||||
Atomarer `mv`-Rename in `duo-agent-poller.sh` entscheidet bei Konkurrenz:
|
||||
erster Erfolg gewinnt, zweiter scheitert und bricht still ab.
|
||||
|
||||
## Credential-Audit (Wichtig fürs nächste Mal)
|
||||
|
||||
- SSH-Zugangsdaten liegen **im Klartext im Skill** `multi-agent-mesh`
|
||||
(SSH-User/Passwort in Befehlen), NICHT in `.env` und es gibt keine SSH-Keys
|
||||
(`authorized_keys` leer). Rollen-Informationen stehen in `/srv/work/registry.json`.
|
||||
- Empfehlung (nicht eigenmächtig): Migration zu `age`/`gpg`-Vault + SSH-Public-Key.
|
||||
Wenn der Skill geteilt/gesynct wird, sind Klartext-PWs ein Leak-Risiko.
|
||||
|
||||
## Task-Dateiformat (Blueprint v1)
|
||||
|
||||
Felder: `task_id, ziel_agent, aufgabentyp, prioritaet, budget_limit_eur,
|
||||
status, ergebnis_pfad, original_ziel_agent` + z.B. `beschreibung, anlage_ts,
|
||||
in_arbeit_ts, fertig_ts`. Status = Ordnerzugehörigkeit, nicht nur das Feld.
|
||||
|
||||
## Doku-Website
|
||||
|
||||
- Domain: `dokumentationen.perfekt-ohne-en.de` (Nginx vhost + Let's Encrypt
|
||||
via `certbot --nginx -d …`). Stellt die Live-Übersicht aller Tasks aus dem
|
||||
Share dar; Agenten-Tabelle mit Status (online/offline).
|
||||
- Kein PHP nötig — statisches HTML wird durch `duo-master-report.sh` regeneriert.
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# Dokumentations- & Archiv-Subsystem (2026-08-03, validiert)
|
||||
|
||||
Nutzer-Präferenz abs. Priorität: **ALLES wird automatisch dokumentiert und
|
||||
jederzeit sichtbar** („Das will ich immer sehen können"). Doku ist kein
|
||||
nachgelagerter Text, sondern ein live generiertes Panel + chronologisches Archiv.
|
||||
|
||||
## Komponenten
|
||||
|
||||
| Komponente | Ort | Funktion |
|
||||
|------------|-----|----------|
|
||||
| Ereignis-Log | `/srv/work/log/events.jsonl` | Jede Aktion, JSONL append |
|
||||
| Live-Panel | `Dokumentationen.perfekt-ohne-en.de` | Echtzeit-Übersicht (Tasks, Agenten, Kosten, Log) |
|
||||
| Tages-Archiv | `Archiv.perfekt-ohne-en.de` | Pro-Tag-Zusammenfassung, chronologisch |
|
||||
|
||||
## Skripte
|
||||
|
||||
- `/usr/local/bin/mesh-log.sh <aktion> <detail> [agent] [kosten_eur]` —
|
||||
appendet Zeile an `events.jsonl`. Immer bei jeder Aktion aufrufen!
|
||||
```json
|
||||
{"ts":"<ISO>","aktion":"...","agent":"agent-a","kosten_eur":0.000,"detail":"..."}
|
||||
```
|
||||
- `/usr/local/bin/duo-master-report.sh` — regeneriert das Live-Panel aus
|
||||
`/srv/work/tasks` **und** `/srv/work/log/events.jsonl` (letzte 40 Events,
|
||||
heutige Kosten = summiert aus events.jsonl). Cron: `* * * * * sleep 30`.
|
||||
- `/usr/local/bin/duo-daily-archive.sh` — erzeugt `Archiv.perfekt-ohne-en.de/<datum>.html`
|
||||
mit Tages-Stats (Aktionen, Kosten, fertig/offen/bugfix) + Ereignisliste.
|
||||
Schreibt außerdem `index.html` als chronologische Linkliste.
|
||||
Cron: `50 23 * * *` (täglich 23:50).
|
||||
|
||||
## Pitfalls (real getestet)
|
||||
|
||||
- **`ls /path/*.json` unter `set -euo pipefail` bricht mit Exit 2 ab**, wenn der
|
||||
Glob leer ist (kein Match). Fix: `shopt -s nullglob` vor den Zähl-Loops
|
||||
einsetzen (gilt für `duo-master-report.sh` UND `duo-daily-archive.sh`).
|
||||
- **Website-Verifikation:** `curl -s -o /dev/null -w "%{http_code}" <url>` nach
|
||||
jeder Generierung. Standard-Docroot existiert, aber der generierte Inhalt
|
||||
fehlt erst → `403/404` = Skript brach ab, nicht die Website.
|
||||
- **Grid/Liste leeres Verzeichnis:** `grep -v index.html` auf leeren Glob
|
||||
ebenfalls mit `nullglob` schützen, sonst Exit 2.
|
||||
|
||||
## Einrichtung (Subdomain)
|
||||
|
||||
```bash
|
||||
mkdir -p /var/www/Archiv.perfekt-ohne-en.de
|
||||
# nginx vhost (listen 80 → root), ln -sf ins sites-enabled, nginx -t && reload
|
||||
certbot --nginx -d Archiv.perfekt-ohne-en.de --redirect --agree-tos -m admin@perfekt-ohne-en.de
|
||||
```
|
||||
Big-Real-Fix: Die Subdomain erreicht per DNS denselben Server (92.60.38.175),
|
||||
eigenes cert + eigener vhost pro Site.
|
||||
|
||||
## Budget
|
||||
|
||||
Kosten werden pro Ereignis in `events.jsonl` geloggt und pro Tag summiert
|
||||
(Live-Panel zeigt „Heutige Kosten"). Budget-Regel: 1 €/Agent/Tag, kein
|
||||
sprunghafter Modellwechsel ohne expliziten Nutzerwunsch.
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
# Failover-Poller Reaktivierung + Update-Cadence (2026-08-06)
|
||||
|
||||
## Ausgangslage
|
||||
|
||||
- Am **2026-08-03** wurde `mesh-master-poller.sh` deaktiviert (Flag `AUSFALL_FALLBACK_AKTIV=0`),
|
||||
weil das **30s-Fenster zu aggressiv** war: `VERPASST=30` < 1 Polling-Zyklus (30–60s), wodurch
|
||||
jeder frische Task (HelloWorld, Wissensbasis) fälschlich auf `ziel_agent='beliebig'` umgeleitet wurde.
|
||||
- Am **2026-08-06** (Nutzer zufrieden mit Systemzustand) wieder aktiviert — aber **robust** statt aggressiv.
|
||||
|
||||
## Nutzer-Grundregel (WICHTIG)
|
||||
|
||||
> „zeiten erfragen noch mit problemlöser vorher!!!" — **bevor Betriebs-Zeitparameter (Failover-Fenster,
|
||||
> Polling-Intervalle) gesetzt werden, IMMER den Problemlöser konsultieren**, nicht eigenmächtig raten.
|
||||
> Ablauf: `mesh-task-create.sh UnraidHermes "<auftrag>" brainstorming high 0.5 "<Briefing + JSON-Frage>"`
|
||||
> an einen online-rechenstarken Agenten (UnraidHermes), Ergebnis-JSON validieren, DANN umsetzen.
|
||||
> Der 03.08.-Fehler kam genau von Setzen ohne Abstimmung.
|
||||
|
||||
## Vom Problemlöser (UnraidHermes) empfohlene Parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"verpasst_sekunden": 180,
|
||||
"modus": "nur_alarm",
|
||||
"ping_check": true,
|
||||
"begruendung": "180s = 3 volle Polling-Zyklen bei 30-60s Cron-Intervall. Nur-Alarm statt auto_relabel verhindert Fehlumlackierung wie am 03.08. Ping-Check: Alarm feuert nur wenn Ziel-Agent wirklich down, nicht bei Latenz/Auslastung."
|
||||
}
|
||||
```
|
||||
|
||||
Begründung:
|
||||
- **180s statt 30s:** 30s war < 1 Zyklus → jeder frische Task sofort "verwaist". 60s = 1 Zyklus (zu knapp,
|
||||
kein Cron-Jitter-Puffer). 180s = 3 Zyklen → ein online Agent hat 3 Chancen den Task zu sehen; ein
|
||||
beschäftigter Agent wird nicht fälschlich als weg erkannt.
|
||||
- **Ping-Check:** `ping -c1 -W2 <zt-ip>` vor Alarm; nur wenn Ziel wirklich down → Alarm. Verifiziert
|
||||
2026-08-06: frischer Task = still, alter Task + online Agent = still (kein Fehlalarm). Laptop war
|
||||
wieder online → korrekt kein Alarm.
|
||||
- **Modus `nur_alarm`:** schreibt nur `AUSFALL_ALARM` ins Event-Log `/srv/work/log/events.jsonl`,
|
||||
Master entscheidet manuell. Kein Auto-Relabel (die Wurzel des 03.08.-Bugs). Späteres Upgrade auf
|
||||
`auto_relabel` NUR nach nachgewiesener Ping-Erkennungs-Stabilität.
|
||||
|
||||
## Umsetzung in mesh-master-poller.sh
|
||||
|
||||
- `AUSFALL_FALLBACK_AKTIV=1`
|
||||
- `VERPASST=180`
|
||||
- Schleife über `offen/*.json`: Alter = `now - anlage_ts`; wenn `>180s` → Agent-IP aus Registry auflösen,
|
||||
Ping-Check; wenn online → `continue` (kein Alarm); wenn down → Event-Log-Eintrag (`AUSFALL_ALARM`).
|
||||
- Cron: `* * * * * /usr/local/bin/mesh-master-poller.sh >> /var/log/mesh-poller.log 2>&1` (alle 60s).
|
||||
- Kein Auto-Relabel, kein `beliebig`.
|
||||
|
||||
## Update-Cadence (Nutzer-Entscheid 2026-08-06)
|
||||
|
||||
Brainstorming über Rhythmus (Technik/KI-Experte/Nerd/Kaufmann) ergab: **wöchentlich** statt täglich.
|
||||
|
||||
| | täglich | alle 3 Tage | **wöchentlich** |
|
||||
|---|---|---|---|
|
||||
| Risiko | klein, oft | mittel | kumuliert, 1× gebündelt |
|
||||
| Aufwand | 28×/Monat | 10×/Monat | **4×/Monat** |
|
||||
| Mesh-Sync | ständig driftend | ok | 1× sauber synchron |
|
||||
|
||||
Politik:
|
||||
- **Sicherheits-Hotfixes** (SSH, Auth, Keys, ZeroTier) → **sofort**, nicht warten.
|
||||
- **Feature-/Routine-Updates** → wöchentlich gebündelt; alle 4 Agenten in einem Zug + 1× Verifikation.
|
||||
- Changelog vor dem Durchziehen kurz auf Breaking-Changes prüfen.
|
||||
- Umsetzung: Cron-Job `mesh-hermes-wochenupdate` (Mo 06:00, deliver=telegram, no_agent) ruft
|
||||
`/root/.hermes/scripts/update_mesh.sh` — updatet Master + UnraidHermes .34.142 + SwitchHermes .20.26 +
|
||||
LaptopHermes .178.202 synchron (`hermes update --yes` via SSH + sshpass), verifiziert Versionen.
|
||||
Stille bei "kein Update nötig"; Meldung nur wenn es was zu tun gibt/Problem.
|
||||
|
||||
## Verifikations-Hinweis
|
||||
|
||||
- Der Alarm-Zweig (echte Down-IP) konnte an dem Tag live nicht mehr getestet werden (alle Agenten online;
|
||||
destruktiver Test wurde angehalten). Kern-Verhalten (frisch=still, alt+online=still) verifiziert; der
|
||||
Down-Pfad ist trivialer Code, aber als "validiert" erst nach einem echten Ausfall anzusehen.
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
# Mesh File-Based Task Queue — Detail (Stand 2026-08-02)
|
||||
|
||||
Entstanden aus Nutzer-Frust über leere Webhook-Antworten und Agenten, die
|
||||
Aufgaben versprechen aber nie Rückmeldung geben. Nutzer-Präferenz: Agents
|
||||
sollen sich über den Share DIREKT verständigen, OHNE manuellen Relay. Der
|
||||
Nutzer mischt sich nur bei kostenpflichtigen Aktionen ein (Essen bestellen,
|
||||
Token-Kauf).
|
||||
|
||||
## Warum File-basiert statt Webhook
|
||||
|
||||
- Webhooks A→B liefern 202, aber B antwortet ins Leere (void); Nutzer musste
|
||||
Relay spielen.
|
||||
- Hermes blockt curl/Shell aus Webhook-Sessions (Sandbox).
|
||||
- Rollback-Pfade unfair: Webhook = einseitig, keine Lebenszeichen.
|
||||
- File-Status über Ordner ist atomar, idempotent, konfliktfrei per `mv`.
|
||||
|
||||
## Ordnerstruktur
|
||||
|
||||
```
|
||||
/srv/work/
|
||||
├── todo/ # offene Aufgaben (Dateiname = <ZT-IP>_<name>.md)
|
||||
├── inProgress/ # wird bearbeitet
|
||||
├── finished/ # erledigt, verifiziert
|
||||
├── bugged/ # gescheitert / Formatfehler
|
||||
└── completed/ # fertige Artefakte (HTML, Dateien, ...)
|
||||
```
|
||||
|
||||
## Task-Dateiformat (YAML Frontmatter + Markdown)
|
||||
|
||||
```markdown
|
||||
---
|
||||
task_id: hello-world-001
|
||||
assigned_to: 172.24.34.142 # Ziel-Agent-ZT-IP (Dateiname = Präfix)
|
||||
created_by: agent-a
|
||||
project: Mesh-Test
|
||||
retries: 3
|
||||
status: todo
|
||||
---
|
||||
# Titel
|
||||
|
||||
Aufgabenbeschreibung. Ergebnis schreibt der Executor unter `# Ergebnis`.
|
||||
```
|
||||
|
||||
## Executor-Regeln (Sub-Agent)
|
||||
|
||||
1. Nur Dateien greifen, deren Dateiname die EIGENE ZT-IP trägt
|
||||
2. `todo/` → `inProgress/`
|
||||
3. Ergebnis unter `# Ergebnis` in dieselbe Datei
|
||||
4. `inProgress/` → `finished/` (Erfolg) oder → `bugged/` (eigener Fehler)
|
||||
|
||||
## Master-Cron (`scripts/master-task-handler.sh`)
|
||||
|
||||
- Cron: `*/5 * * * *`
|
||||
- Bereinigt verwaiste `.lock` (>15 min)
|
||||
- Stale inProgress-Dateien (>30 min, ohne Lock) → zurück nach todo
|
||||
- finished/ validiert: YAML-Frontmatter + `# Ergebnis` vorhanden
|
||||
- bugged/: `retries > 0` → dekrementieren + zurück nach todo; sonst bleibt
|
||||
- Artefakte nach completed/ spiegeln (optional auch auf Dokumentations-Website)
|
||||
|
||||
## Share-Sicherheit (nur ZeroTier, nie öffentlich)
|
||||
|
||||
Samba:
|
||||
```ini
|
||||
[work]
|
||||
path = /srv/work
|
||||
hosts allow = 172.24. # ZeroTier-Subnetz
|
||||
hosts deny = 0.0.0.0/0
|
||||
```
|
||||
NFS: `/srv/work <ZT_IP>(rw,sync,no_subtree_check)` in /etc/exports
|
||||
SSHFS: `sshfs root@<ZT_IP_A>:/srv/work /srv/work`
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Aufgabe in `todo/` legen reicht NICHT ohne Executor-Cron auf der Gegenseite
|
||||
→ bleibt ewig liegen (wie Message Bus ohne Poll-Cron).
|
||||
- Verwaistes Ergebnis-Slot: Webseite zeigt Aufgabenliste live aus Share;
|
||||
Render-Pfad `/completed/` immer anlegen.
|
||||
- Netzwerk konsolidieren: nur EIN aktives ZT-Netzwerk. Nach Wechsel Samba/NFS-
|
||||
Regeln, UFW, Skills, duo_state.json auf neue Subnetze prüfen.
|
||||
- Nutzer-Präferenz „kein Ghosting": versprochene Aktion AUSFÜHREN und Ergebnis
|
||||
melden — nie nur „ich mache es" schreiben und verstummen.
|
||||
|
||||
## Beispiel-Projektsync
|
||||
|
||||
Eine Dokumentations-Website (z.B. dokumentationen.perfekt-ohne-en.de) zeigt
|
||||
alle Tasks und ihre Status live aus dem Share; Hello-World-Test als erstes
|
||||
Mesh-Artefakt.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# PersoEnt: Google Health-Daten-Anbindung — Entscheidung (2026-08-07)
|
||||
|
||||
## Verhindert Wiederholung
|
||||
Nur damit starten zu wollen "Google Health Connect per REST abgiechen" ist ein **totes Pferd** — die nächste Session würde sonst denselben toten Weg vorschlagen.
|
||||
|
||||
## Autoreitative Fakten (verifiziert 2026-08-07, Rechen-Agent SwitchHermes + Master)
|
||||
1. **Google Fit REST API ist DEPRECATED** und wird abgeschaltet. Eine Neuentwicklung darauf ist nicht zukunftssicher.
|
||||
2. **Google Health Connect** (der Android-Nachfolger) hat **KEINE Server-/REST-Schnittstelle** — nur die Android-App-Erweiterung (SDK auf dem Gerät). Kein serverseitiger Datenpull wie bei alter REST-API.
|
||||
3. Apple-Pendant: **HealthKit** (iOS, nativ).
|
||||
|
||||
## Konsequenz für die Architektur
|
||||
Um deine (Nutzer-)echten Google-Gesundheitsdaten automatisch zu bekommen, muss PersoEnt **eine native App** sein, nicht nur eine Website:
|
||||
- **Android:** Health-Connect-SDK nativ in der App → Daten vom Handy lesen
|
||||
- **iOS:** HealthKit nativ
|
||||
- App synchronisiert zu unserem **PocketBase**-Backend (VPS) über REST.
|
||||
|
||||
## Entscheidung (Nutzerwunsch, 08-07)
|
||||
**Zuerst ein Android-Prototyp (APK), iOS später.** Nutzer testet APK auf seinem Handy.
|
||||
|
||||
## Baustraße / Toolchain-Status
|
||||
- VPS (Master): Flutter ❌, Java ❌, Android-SDK ❌, adb ❌ — aber reichlich Ressourcen (15 GB RAM, 278 GB frei).
|
||||
- LaptopHermes (172.24.178.202, RTX 2060): ebenfalls keine Flutter/Java-Toolchain.
|
||||
- VPS ist headless → APK-Build möglich (Gradle/Build-Tools), aber KEIN Android-Emulator zum In-Test; Nutzer testet die APK direkt am Handy.
|
||||
- Option A: Flutter auf dem VPS aufsetzen (Master-kontrolliert, DSGVO-sauber, alles unter uns). Option B: auf LaptopHermes bauen.
|
||||
- Backend + PocketBase laufen bereits (persoent.perfekt-ohne-en.de); das bleibt.
|
||||
|
||||
## Nächster Schritt (unentschieden zum Stand des Sessions-Ende)
|
||||
Baustraße wählen (A: VPS vs B: LaptopHermes), Flutter-SDK + Android-SDK + Gradle installieren, leeren APK-Prototyp bauen, Health-Connect-SDK + PocketBase-Sync danach.
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
# SSH Direct Review Protocol (A+B-Methode, 01.09.2026)
|
||||
|
||||
## Problem
|
||||
|
||||
Sub-Agenten sehen `/srv/work/tasks/` NICHT im NFS-Share. Sie haben keinen Zugriff auf
|
||||
Master-Dateien. URLs allein reichen nicht (`curl -s https://domain/` liefert leer oder
|
||||
scheitert am Tool-Limit). Datei-basierte Reviews mit 5 Prüfpunkten pro Task führen
|
||||
dazu, dass Sub-Agenten am Iterations-Limit scheitern.
|
||||
|
||||
## Lösung: A+B Methode
|
||||
|
||||
**A = Task kleiner schneiden** (1 Prüfpunkt statt 5 pro Task)
|
||||
**B = Inhalt per SSH direkt schicken** (scp + hermes chat --oneshot statt Datei-Queue)
|
||||
|
||||
## Workflow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
Master->>Master: Schreibe Einzel-Punkt-Prompt in /tmp/prompt.txt
|
||||
Master->>SubAgent: scp /tmp/prompt.txt user@zt-ip:/tmp/review.txt
|
||||
Master->>SubAgent: ssh user@zt-ip 'hermes chat --oneshot --query-file /tmp/review.txt'
|
||||
SubAgent->>Master: Ergebnis (stdout)
|
||||
Master->>Master: Prüft Ergebnis, baut bei Bedarf nächsten Prompt
|
||||
```
|
||||
|
||||
## Prompt-Format (erfolgreich getestet)
|
||||
|
||||
```
|
||||
PRUEFE FOLGENDE RESSOURCEN-SEITE AUF DESIGN-FEHLER:
|
||||
|
||||
CSS-Variablen (aktuell deployed):
|
||||
--bg:#0f172a
|
||||
--surface:#1e293b
|
||||
--ink:#4ade80
|
||||
|
||||
ANTWORTE NUR MIT: "OK" ODER "FEHLER: <beschreibung>"
|
||||
|
||||
1. Ist --muted(#94a3b8) auf --surface(#1e293b) lesbar?
|
||||
2. Ist die Card-Grid-Aufteilung sinnvoll?
|
||||
3. Fehlt ein visuelles Element?
|
||||
```
|
||||
|
||||
## Regeln
|
||||
|
||||
1. **NUR EIN Prüfpunkt pro Fachgebiet** — 3-5 eng verwandte Fragen in EINEM Prompt
|
||||
(z.B. alle Kontrast-Fragen zusammen), aber NICHT Inhalt+Design+Code in einem Task
|
||||
2. **Antwortformat erzwingen** — "ANTWORTE NUR MIT: OK ODER FEHLER: <beschreibung>"
|
||||
3. **CSS-Werte direkt einbetten** — keine URL, kein Pfad, kein Verweis auf Master-Dateien
|
||||
4. **hermes chat --oneshot** verwenden (nicht --query-file mit langen Sessions)
|
||||
5. **Bei Tool-Limit:** Task war zu groß, z.B. bei UnraidHermes 11 Prüfpunkte scheiterten,
|
||||
3-5 funktionierten. Noch kleiner schneiden.
|
||||
6. **scp vor ssh** — Prompt-Datei auf Sub-Agent kopieren, dann erst ausführen
|
||||
7. **PATH setzen** — Sub-Agenten haben `hermes` nicht in $PATH →
|
||||
`PATH=/home/mehlzoerwer/.local/bin:/usr/bin:/bin hermes chat ...`
|
||||
|
||||
## Warum das funktioniert
|
||||
|
||||
- **Kein Dateisystem-Zugriff nötig** — der Prompt-Text kommt via scp auf /tmp/
|
||||
- **Kein Tool-Limit durch file-reads** — der Agent muss keine URLs laden, keine Dateien suchen
|
||||
- **Kein Permission-Problem** — /tmp/ ist für alle beschreibbar
|
||||
- **Ein Checkpoint pro Durchlauf** — der Agent hat genau EINE Aufgabe: die 3-5 Fragen beantworten
|
||||
- **Schnell** (~30s pro Agent, ~2 Min für Team-Review aller 3)
|
||||
- **Kostengünstig** — ein Free-Modell ($0) für diese Mini-Tasks
|
||||
|
||||
## Fallstricke
|
||||
|
||||
- **Anführungszeichen im Prompt** — bei ssh-Befehl müssen einfache Anführungszeichen escaped werden.
|
||||
Lösung: Prompt in Datei schreiben, scp, dann `--query-file` nutzen. KEINE inline-Prompts per
|
||||
`ssh ... 'hermes chat -q "..."'` — das bricht bei Sonderzeichen.
|
||||
- **Timeout** — `hermes chat --oneshot` kann bei Modell-Last 60s+ dauern.
|
||||
`background=true, timeout=120` für die ssh-Ausführung.
|
||||
- **kein Ergebnis** — wenn der Sub-Agent am Tool-Limit scheitert, kommt "noch nicht fertig".
|
||||
Dann war der Prompt zu groß → noch kleiner schneiden.
|
||||
- **Falsches Antwortformat** — manche Agenten ignorieren "ANTWORTE NUR MIT" und schreiben
|
||||
Essays. Entweder Prompt verschärfen ("WENN DU MEHR SCHREIBST WIRD ES VERWORFEN") oder
|
||||
die Antwort manuell parsen.
|
||||
|
||||
## Getestete Konfigurationen (01.09.2026)
|
||||
|
||||
| Agent | Erfolg | Dauer | Prompt-Größe |
|
||||
|-------|--------|-------|-------------|
|
||||
| UnraidHermes | ✅ OK (nur nach URL-Korrektur) | 47s | 11 Punkte → zu groß, scheiterte. 3 Punkte → OK |
|
||||
| SwitchHermes | ✅ OK | 34s | 5 Design-Fragen |
|
||||
| LaptopHermes | ✅ OK | 6s | 5 Tech-Fragen (gab nur "OK" zurück) |
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- SSH-Key vom Master zum Sub-Agenten (ed25519, in `authorized_keys`)
|
||||
- `scp` + `ssh` auf beiden Seiten
|
||||
- Hermes auf dem Sub-Agenten installiert
|
||||
- Kein NFS nötig
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
# SSH-Key-Verteilung & Master Control Suite (Praktischer Workflow)
|
||||
> Stand: 31.08.2026 | Mesh mit 4 Agenten (Master + 3 Sub-Agenten)
|
||||
|
||||
## Problem: Master hat keinen SSH-Zugriff zu Sub-Agenten
|
||||
|
||||
SSH-Zugriff ist zwingend für Notfälle (Kill-Switch bei Budget-Spirale, Modell-Wechsel bei Kostenexplosion).
|
||||
Tasks via NFS-Share sind zu langsam — Sub-Agenten analysieren lange Beschreibungen statt zu handeln.
|
||||
|
||||
## Versuchter Lösungsweg: AcceptMasterKey-Task (gescheitert)
|
||||
|
||||
Der Master generierte einen ed25519-Key und legte Tasks an alle Sub-Agenten mit dem Befehl:
|
||||
```
|
||||
mkdir -p ~/.ssh && echo '<pubkey>' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
**Problem:** Sub-Agenten in der Hermes-Sandbox haben kein `terminal()` — sie haben nur `execute_code` (Python).
|
||||
Shell-Befehle (`mkdir`, `echo`, `chmod`) können sie nicht ausführen. Stattdessen *analysieren* sie den Task,
|
||||
beschreiben die Lösung, setzen sie aber NICHT um. Task endet mit "BLOCKED" oder "muss fortgesetzt werden".
|
||||
|
||||
**Erkenntnis für Task-Formatierung:**
|
||||
- `aufgabentyp: terminal` + nur `befehl:`-Feld = Sub-Agent führt NICHT aus (analyse)
|
||||
- Lange Beschreibungen (5+ Zeilen) = Sub-Agent analysiert / halluziniert
|
||||
- **Einzige Ausnahme:** Sub-Agent mit aktivem Gateway + Telegram-Zugang könnte per `hermes`-Terminal
|
||||
des Master auf dem Sub-Agenten selbst gesteuert werden (Henne↔Ei: dafür braucht man SSH)
|
||||
|
||||
## Funktionierender Workaround: Manuelle Erst-Verteilung
|
||||
|
||||
Der Nutzer hat auf **jedem Sub-Agenten** kurz die zwei Befehle ausgeführt:
|
||||
```bash
|
||||
# 1. SSH-Key akzeptieren
|
||||
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
||||
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILEn0Vb4KKwbNI5wiT+Z7VWIiY6iiO7F4BeWS2gMWPCP mesh-master@172.24.196.119" >> ~/.ssh/authorized_keys
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
|
||||
# 2. Modell setzen (je nach Agent)
|
||||
hermes config set model.default google/gemini-2.5-flash:free # Unraid/Laptop
|
||||
hermes config set model.default nvidia/nemotron-3-ultra-550b-a55b:free # Switch (Free Tier)
|
||||
```
|
||||
|
||||
**Aufwand pro Agent:** ~30 Sekunden. **Einmalig.** Danach hat der Master volle SSH-Kontrolle.
|
||||
|
||||
## Master Control Suite (eingesetzte Tools)
|
||||
|
||||
Nach erfolgreicher SSH-Einrichtung stehen folgende Skripte auf dem Master zur Verfügung:
|
||||
|
||||
| Skript | Funktion | Cron |
|
||||
|--------|----------|------|
|
||||
| `mesh-kill-all.sh` | Notfall: Alle Agents auf Ollama (kostenlos) | Manuell |
|
||||
| `mesh-budget-watch.sh` | Guthaben prüfen, Auto-Switch (Schwellen $1/$0.50/$0.10) | `*/30 * * * *` |
|
||||
| `mesh-model-broadcast.sh <modell>` | Setzt Modell auf ALLEN Agents gleichzeitig | Manuell |
|
||||
| `mesh-status-monitor.sh` | Trackt Task-Antworten + SSH, Telegram bei Änderung | `*/30 * * * *` |
|
||||
| `mesh-model-scout.py` | OpenRouter-Preisliste scannen, Modell zuweisen | `0 6 */3 * *` |
|
||||
| `mesh-budget-report.sh` | Guthaben + Modelle aller Agents als Telegram-Bullet-Liste | Manuell (Keyword/Telegram) |
|
||||
|
||||
**Budget-Report Format (Nutzerpräferenz):** Bullet-Liste statt Code-Block — auf Handy lesbar.
|
||||
Telegram-Code-Blöcke mit Leerzeichen-Ausrichtung sehen auf Desktop gut aus, auf dem Handy (20 Zeichen
|
||||
Breite) sind sie unleserlich. `• Agent — Modell`-Format rendert auf allen Geräten sauber.
|
||||
Kein Doppel-Output (Skript schreibt direkt via curl an Telegram + stderr wird unterdrückt).
|
||||
|
||||
## SSH-Aktivierung (nach erfolgreicher Erst-Verteilung)
|
||||
|
||||
Verifikation durch:
|
||||
```bash
|
||||
for ip in 172.24.34.142 172.24.20.26 172.24.178.202; do
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=3 "mehlzoerwer@$ip" "echo OK"
|
||||
done
|
||||
```
|
||||
|
||||
Dienste danach automatisch abstimmen:
|
||||
- Cron-Jobs auf Sub-Agenten pinnen: `hermes cron edit <id> --provider openrouter --model <modell>`
|
||||
- OS-Updates per `echo '<pw>' | sudo -S apt update && sudo -S apt upgrade -y`
|
||||
- Gateway auf Master als `systemctl enable hermes-gateway.service` (auto-start nach Reboot)
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# SSH/scp Task-Polling statt NFS-Freigabe (Sicherheits-Umbau 2026-08-03)
|
||||
|
||||
## Entscheidung
|
||||
NFS-Freigabe ist OBSOLET. Alle Sub-Agenten erhalten Tasks über **SSH/scp (Key-Auth, verschlüsselt)**.
|
||||
Grund: NFS (sec=sys) überträgt plaintext → Man-in-the-Middle-fähig. SSH = Ende-zu-Ende verschlüsselt,
|
||||
nur eigene Tasks sichtbar (gefiltert nach `*.C.D.*`), auditierbar.
|
||||
|
||||
`/srv/work/tasks/` bleibt als **zentrale Ablage auf dem Master** (offen/in_arbeit/fertig/bugfix/archiv),
|
||||
nur die Zugriffsart änderte sich: kein Mount, sondern pull/push per scp.
|
||||
|
||||
## Warum NFS hier nicht überall geht
|
||||
SwitchHermes = Ubuntu 24.04 auf Nintendo-Switch (Tegra-Kernel `4.9.140-l4t`):
|
||||
- `modprobe nfs` → `FATAL: Module nfsv4 not found`
|
||||
- NFS-Client + FUSE/sshfs fehlen (kein Kernelmodul, wenig Speicher → kein Kernel-Ausbau gewollt)
|
||||
→ SSH/scp ist die einzige portable Methode für ARM-Switch-Geräte.
|
||||
|
||||
## Architecture des SSH-Poller-Daemons
|
||||
- Jeder Agent hat **eigenen ed25519-Key** (auf Master: `/root/.ssh/<agent>_mesh`), Public-Key in `authorized_keys`.
|
||||
- Poller-Script `mesh-agent-poller.sh <meine-IP> <meine-ID> [master] [keyfile]`:
|
||||
1. `scp` pull eigene Tasks `offen/<JahrMonatTag>.<C>.<D>.*.json` nach `/tmp/mesh-tasks/`
|
||||
2. Atomarer Lock **per SSH auf dem Master**: `mv offen/... in_arbeit/...` (absolute Pfade!), `&& echo OK || echo LOST`
|
||||
3. Bearbeitung via `hermes chat -q "<PROMPT>" --quiet --max-turns 3`
|
||||
4. Ergebnis als Base64 ins JSON-Feld `result` schreiben (SSH-python), dann `mv in_arbeit/ → fertig/`, chmod 0666
|
||||
- **Daemon**: systemd-user-service (`~/.config/systemd/user/mesh-poller.service`, `Restart=always`, Loop alle 10s).
|
||||
NICHT nohup/Cron-@reboot als Loop — der stirbt beim SSH-Session-Ende.
|
||||
- Service-Start für User-Services: `export XDG_RUNTIME_DIR=/run/user/$(id -u); systemctl --user ...`
|
||||
|
||||
## Wichtigste Pitfalls (alle live getroffen)
|
||||
1. **`SSH_CMD` MUSS den Host enthalten**: `SSH_CMD="ssh -i $KEY ... root@172.24.196.119"`.
|
||||
Vergessen → `hostname contains invalid characters` + leerer Lock-Output = endlose SKIPs, obwohl der
|
||||
manuelle `mv`-Test auf dem Master klappt.
|
||||
2. **Relative Pfade im remote-mv vermeiden** — `../../../in_arbeit` geht 3 Ebenen hoch statt 1. Nutze absolute Pfade.
|
||||
3. **Datei-Rechte (Hauptgrund, warum Polling anfangs nie lief):** vom Master (root) erstellte/durch `mv`
|
||||
bewegte Task-Dateien behalten root-Owner → Sub-Agent (uid 1000) kann nicht reinschreiben, selbst bei
|
||||
0777-Dir. Fix: **`chmod 0666`** nach Task-Erstellung (`mesh-task-create.sh`) UND nach jedem `mv` im Poller
|
||||
(`in_arbeit` UND `fertig`). Sonst `PermissionError: [Errno 13]`.
|
||||
4. **fail2ban bannt Mesh-IPs** bei scp-Fehlschlägen → `Connection refused` auf Port 22, obwohl Ping klappt
|
||||
und sshd auf `0.0.0.0:22` lauscht. Fix: `ignoreip = <alle ZT-IPs>` in `[sshd]`-Sektion von
|
||||
`/etc/fail2ban/jail.local`. Verifizieren: `fail2ban-client status sshd` → IP aus `Banned IP list` nehmen.
|
||||
5. **Doppelte Daemon-Prozesse** (ein nohup-Rest + ein systemd-Rest) stören sich gegenseitig beim Lock.
|
||||
Nach Umbau: `systemctl --user stop`, `pkill -9 -f mesh-poller-daemon`, PID-Datei löschen, sauber `start`.
|
||||
Am Ende MUSS `pgrep -f mesh-poller-daemon` genau EINEN Prozess zeigen.
|
||||
6. **"No such file or directory" beim remote-scp-Glob** kann heißen, dass der Task bereits in `fertig/`
|
||||
liegt (ein früherer Lauf hat ihn gezogen). Erst `ls` auf Master prüfen, bevor man den Glob verdächtigt.
|
||||
|
||||
## NFS-Server deaktivieren (nach Migration)
|
||||
- `systemctl stop/disable nfs-kernel-server rpcbind`
|
||||
- `ufw delete allow from <ip> to any port 2049/111/20048` für jede Agenten-IP
|
||||
- Offene Mesh-Ports danach NUR: 22 (SSH), 8644 (Webhook), 11434 (Ollama) — alle gerichtet auf die 4 bekannten ZT-IPs.
|
||||
- `/etc/exports` bleibt als Datei liegen, aber inaktiv.
|
||||
|
||||
## NFS-Export-Härtung (falls man doch NFS nutzen muss)
|
||||
Export auf die 4 bekannten IPs statt `/16`, zur
|
||||
defense-in-depth: UFW-Regel (nur bekannte IPs) UND Export (bekannte IPs) konsistent:
|
||||
`/srv/work/tasks ip1(rw,sync,no_subtree_check,no_root_squash) ip2(...) ...`
|
||||
`exportfs -ra` reload separat ausführen, NICHT als gateway-gebundener Befehl (wird gekillt).
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/duo-agent-poller.sh
|
||||
# Sub-Agent Poller: Prüft alle 10s tasks/offen auf eigene (oder "beliebig") Tasks.
|
||||
# Nutzung: duo-agent-poller.sh <MEINE-ZT-IP> <MEINE-AGENT-ID>
|
||||
set -euo pipefail
|
||||
|
||||
TASKS="/srv/work/tasks"
|
||||
MEINE_IP="${1:?Meine ZT-IP fehlt}"
|
||||
MEINE_ID="${2:?Meine Agent-ID fehlt}"
|
||||
|
||||
# Suffix: letzte 2 Oktette der ZT-IP (z.B. 172.24.34.142 -> "34.142" als Präfix)
|
||||
OCT=$(echo "$MEINE_IP" | awk -F. '{print $3"."$4}')
|
||||
|
||||
for f in "$TASKS/offen"/*.json; do
|
||||
[ -f "$f" ] || continue
|
||||
ZIEL=$(python3 -c "import json;print(json.load(open('$f')).get('ziel_agent',''))" 2>/dev/null || echo "")
|
||||
# Nur Tasks für mich oder "beliebig" greifen
|
||||
if [ "$ZIEL" = "$MEINE_ID" ] || [ "$ZIEL" = "beliebig" ]; then
|
||||
# Atomarer Lock: mv/rename gewinnt, konfliktfrei auf NFS.
|
||||
NEW="$TASKS/in_arbeit/${OCT}_$(basename "$f")"
|
||||
if mv "$f" "$NEW" 2>/dev/null; then
|
||||
python3 - "$NEW" "$MEINE_ID" << 'PYEOF'
|
||||
import json, sys, time
|
||||
f, agent = sys.argv[1], sys.argv[2]
|
||||
t = json.load(open(f))
|
||||
t["status"] = "in_arbeit"
|
||||
t["in_arbeit_ts"] = str(int(time.time()))
|
||||
t["bearbeiter"] = agent
|
||||
json.dump(t, open(f, "w"), indent=2)
|
||||
print(f"Uebernommen: {t['task_id']} von {agent}")
|
||||
PYEOF
|
||||
# Bearbeitungslogik ist agentenspezifisch — hier einhängen.
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/duo-master-poller.sh
|
||||
# Master (Agent A): Erkennt unerledigte Tasks nach 3 verpassten Polling-Zyklen (30s)
|
||||
# und markiert ziel_agent als "beliebig" (original_ziel_agent bleibt erhalten).
|
||||
# Nur Master darf diesen Ausfall-Fallback triggern.
|
||||
set -euo pipefail
|
||||
|
||||
TASKS="/srv/work/tasks"
|
||||
VERPASST=30 # Sekunden, nach denen ein Task als übersehen gilt (3 Zyklen à 10s)
|
||||
NOW=$(date +%s)
|
||||
|
||||
for f in "$TASKS/offen"/*.json; do
|
||||
[ -f "$f" ] || continue
|
||||
ANLAGE=$(python3 -c "import json;print(json.load(open('$f')).get('anlage_ts',0))" 2>/dev/null || echo 0)
|
||||
AGE=$(( NOW - ANLAGE ))
|
||||
if [ "$AGE" -gt "$VERPASST" ]; then
|
||||
python3 - "$f" << 'PYEOF'
|
||||
import json, sys
|
||||
f = sys.argv[1]
|
||||
t = json.load(open(f))
|
||||
if t["status"] == "offen":
|
||||
t["original_ziel_agent"] = t.get("original_ziel_agent", t["ziel_agent"])
|
||||
t["ziel_agent"] = "beliebig"
|
||||
t["ausfall_umgeleitet"] = True
|
||||
json.dump(t, open(f, "w"), indent=2)
|
||||
print(f"Ausfall-Fallback: {t['task_id']} -> ziel_agent='beliebig' (orig: {t['original_ziel_agent']})")
|
||||
PYEOF
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/duo-task-create.sh
|
||||
# Master (Agent A): Legt Task in tasks/offen/ ab (JSON-Schema nach Blaupause v1).
|
||||
# Nutzung: duo-task-create.sh <ziel_agent-id> <aufgabentyp> <priorität(low|mid|high)> <beschreibung> [budget]
|
||||
set -euo pipefail
|
||||
|
||||
TASKS="/srv/work/tasks"
|
||||
ZIEL="${1:?ziel_agent fehlt (z.B. B, SwitchHermes, LaptopHermes)}"
|
||||
TYP="${2:?aufgabentyp fehlt (z.B. code, html, test, deploy)}"
|
||||
PRIO="${3:-mid}"
|
||||
BESCHREIBUNG="${4:?Beschreibung fehlt}"
|
||||
BUDGET="${5:-1.0}"
|
||||
|
||||
TS=$(date +%s)
|
||||
TASK_ID="T$(date +%Y%m%d)_${TS}"
|
||||
|
||||
cat > "$TASKS/offen/${TASK_ID}.json" << EOF
|
||||
{
|
||||
"task_id": "${TASK_ID}",
|
||||
"ziel_agent": "${ZIEL}",
|
||||
"original_ziel_agent": "${ZIEL}",
|
||||
"aufgabentyp": "${TYP}",
|
||||
"prioritaet": "${PRIO}",
|
||||
"budget_limit_eur": ${BUDGET},
|
||||
"status": "offen",
|
||||
"ergebnis_pfad": null,
|
||||
"beschreibung": "${BESCHREIBUNG}",
|
||||
"anlage_ts": "${TS}",
|
||||
"in_arbeit_ts": null,
|
||||
"fertig_ts": null
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "OK Task $TASK_ID angelegt fuer $ZIEL ($TYP, prio=$PRIO)"
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# Master-Task-Handler: verifiziert & validiert Tasks im Mesh-Share automatisch.
|
||||
# Läuft via Cron: */5 * * * * /path/scripts/master-task-handler.sh
|
||||
# Quelle /srv/work — Status=Ordnermodell (todo/inProgress/finished/bugged/completed)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
WORK="/srv/work"
|
||||
TODO="$WORK/todo"
|
||||
INPROG="$WORK/inProgress"
|
||||
FIN="$WORK/finished"
|
||||
BUG="$WORK/bugged"
|
||||
COMP="$WORK/completed"
|
||||
LOCK_TIMEOUT=15 # Minuten
|
||||
STALE_TIMEOUT=30 # Minuten
|
||||
|
||||
mkdir -p "$TODO" "$INPROG" "$FIN" "$BUG" "$COMP"
|
||||
|
||||
# 1) Verwaiste Locks bereinigen (>15 min)
|
||||
find "$INPROG" -name "*.lock" -mmin +$LOCK_TIMEOUT -delete 2>/dev/null || true
|
||||
|
||||
# 2) Stale Dateien in inProgress (ohne Lock, >30 min) -> zurück nach todo
|
||||
for f in "$INPROG"/*.md "$INPROG"/*.json; do
|
||||
[ -f "$f" ] || continue
|
||||
[ -f "$f.lock" ] && continue
|
||||
if [ "$(find "$f" -mmin +$STALE_TIMEOUT 2>/dev/null | wc -l)" -gt 0 ]; then
|
||||
mv "$f" "$TODO/" 2>/dev/null && echo "[$(date)] stale -> todo: $f"
|
||||
fi
|
||||
done
|
||||
|
||||
# 3) finished/ validieren (Frontmatter + # Ergebnis)
|
||||
for f in "$FIN"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
if ! head -20 "$f" | grep -q "^---$" || ! grep -q "^# Ergebnis" "$f"; then
|
||||
mv "$f" "$BUG/" 2>/dev/null && echo "[$(date)] formatfehler -> bugged: $f"
|
||||
continue
|
||||
fi
|
||||
# Optional: Artefakt in completed/ spiegeln
|
||||
# cp "$f" "$COMP/$(basename "$f")"
|
||||
done
|
||||
|
||||
# 4) bugged/ Retry-Buchhaltung (YAML: retries)
|
||||
for f in "$BUG"/*.md; do
|
||||
[ -f "$f" ] || continue
|
||||
retries=$(grep -m1 "^retries:" "$f" 2>/dev/null | awk '{print $2}' || echo 0)
|
||||
retries=${retries:-0}
|
||||
if [ "$retries" -gt 0 ]; then
|
||||
new=$((retries - 1))
|
||||
sed -i "s/^retries: .*/retries: $new/" "$f"
|
||||
mv "$f" "$TODO/" 2>/dev/null && echo "[$(date)] retry($new) -> todo: $f"
|
||||
else
|
||||
echo "[$(date)] bleibt in bugged (retries=0): $f"
|
||||
fi
|
||||
done
|
||||
Reference in New Issue
Block a user