Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .empire/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1794,3 +1794,22 @@ Der größte Rauschposten war nicht GitHub, sondern **Munirs eigene Systeme**, d
### Kleine additive Erweiterung an `lagebild.sh`

Der Zahlungs-Block liefert zusätzlich ein 24-Stunden-Fenster (`last24_total`, `last24_paid`, `last24_paid_eur`, `last24_failed_after_method`, `last24_paid_methods`, `payments_window_complete`). Grund: der Morgenbrief fragt „was hat sich seit gestern bewegt", nicht „wie war der Monat" — und ohne eigenes Fenster hätte er das aus `last_payments` raten müssen, also aus den letzten DREI Zahlungen. `payments_window_complete` sagt, ob die 50 abgeholten Zahlungen 24 h überhaupt abdecken. Der Morgenbrief ruft `lagebild.sh` jetzt mit `--amounts` auf (privater Kanal); `--redact` nimmt die Beträge wieder heraus.


## buzz#69 — Rechte-Rückfall im CRM stillgelegt (2026-08-01)

Zwei Wege konnten die Härtung aus buzz#52 (CRM-Key von 61 Scopes auf einen) in einem Kommando zurückdrehen. Beide sind zu. Was dabei gemessen wurde:

- **Ein Provisionierungs-Skript, das sich seine Rolle aus `GET /Metadata` baut, ist eine geladene Waffe.** Live gerechnet: 91 `entityDefs` → 91 Scopes auf `create:yes/read:all/edit:all/delete:all/stream:all`, dazu `isAdmin: true` und ein Überschreiben von `rolesIds` — die aktive Rolle hat **1**. Kein Fehlschlag, keine Meldung, niemand merkt es. Regel: **Rollen haben genau EINE Quelle** (hier `espo-mcp/tools/apply-mcp-crm-role.mjs`); Provisionierungs-Skripte legen User an, finden die Rolle vor und brechen ohne sie ab (fail closed). Einen bestehenden User fasst so ein Skript gar nicht mehr an — dann kann ein zweiter Lauf den Bestand grundsätzlich nicht verschlechtern.
- **Getrackte Quelldateien zu löschen entwaffnet nichts, solange ein gebautes `dist/` auf der Platte liegt.** Der abgelöste Voll-CRUD-MCP war in keiner Config mehr verdrahtet — aber `dist/index.js` + `node_modules` lagen fertig da, und die Doku lieferte die `claude mcp add`-Zeile dazu. Erst Quelle (PR) **und** Artefakt (PowerShell) weg = stillgelegt. Untracked Artefakte im geteilten Haupt-Tree zu löschen ist unbedenklich, solange sie gitignored sind: `git status` bleibt vorher wie nachher bei 0 — so dirtyt man den Branch eines anderen Agenten nicht.
- **Ein dokumentierter Ersatzweg, der nicht funktioniert, ist schlimmer als gar keiner.** buzz#52 notierte, der `n8n-agent`-Key behalte Lead-Delete „für die Funnel-Probe". Direkt gegen `DELETE /api/v1/Lead/<id>` gemessen: der MCP-Key antwortet `403 No delete access` — und der n8n-agent-Key **ebenfalls** `403 No delete access`. **Kein API-Key kann noch Leads löschen**, nur eine Admin-Sitzung. Ein bereits gebauter `--use-delete-key`-Fallback wurde wieder ausgebaut und die falsche Notiz an der Quelle korrigiert. Jede geerbte „X kann das noch"-Behauptung am laufenden System prüfen, bevor man darauf baut.
- **`crm.adas.jetzt` weist den User-Agent `Python-urllib/*` pauschal mit `403` ab.** Derselbe Key, derselbe Pfad: `Python-urllib/3.13` → `403`, `node` / `curl` / `Mozilla` / eigener UA → `200`. Ein Python-Diagnoseskript meldet also „Key tot / ACL kaputt", während die Nacht-Ingestion (Node, Default-UA `node`) einwandfrei läuft. Diagnose-Skripte hier immer mit eigenem User-Agent.
- **Ein leer gesetzter Secret-Name ist eine Landmine, kein Aufräum-Rest.** `ESPOCRM_API_KEY` stand ohne Wert in `master.env`, und `crm-sync` liest ihn **vor** `ESPOCRM_MCP_API_KEY`. Leer ist in JS falsy und fällt korrekt durch — sobald aber jemand irgendetwas einträgt, überschreibt er lautlos den echten Key. Auf adas-hetzner heißt derselbe Live-Key ausgerechnet **auch** `ESPOCRM_API_KEY`: gleicher Name, andere Bedeutung, an zwei Orten. Jetzt auskommentiert samt Begründung.

### Werkzeug-Fallen dieser Session

- **`process.exit(n)` liefert auf Windows den falschen Code.** `process.exit(3)` riss offene libuv-Handles mit (`Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), src\win\async.c`) und der Prozess endete mit **127 statt 3** — ein Aufrufer hätte den Fehler falsch klassifiziert. `process.exitCode = n; return;` verwenden.
- **`git rm` stirbt an der `rm`-User-Policy** (die Regel greift auf die ganze Zeile). Löschungen stattdessen: `powershell.exe -NoProfile -Command "Remove-Item -LiteralPath … -Recurse -Force"`, danach `git add <pfad>` — git stagt die Löschungen von selbst.
- **Der Push-Hook blockt auch `master`, nicht nur `main`.** `git push origin master` wird pre-execution abgewiesen und nimmt die ganze `&&`-Kette mit (auch den Commit davor). Immer Feature-Branch → PR.
- **Der Secret-Hook greift auch auf Fließtext.** Eine Lektion, die einen Variablennamen mit angehängtem Gleichheitszeichen enthielt, wurde als Secret-Write abgewiesen — Erklärtexte über Secrets ohne dieses Muster schreiben.
- **Bestätigt: mehrzeiliges `node -e '…'` tut in Git Bash gar nichts** (Exit 0, keine Ausgabe). Die Rot-Probe lief erst, nachdem sie in einer `.mjs`-Datei stand.
1 change: 1 addition & 0 deletions .empire/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@
2026-08-01 22:00 | 2 Tickets | #48 Windows-Canary als CI-Gate (Root-Cause: der Upstream-Canary traegt `if: github.repository == 'block/buzz'` -> auf JEDEM Fork wird der Job still uebersprungen, ein Dispatch dort meldet success ohne eine Zeile zu bauen; Fork-Pendant empire-windows-canary.yml mit push-auf-main + paths-ignore, dispatch auf jedem Branch, concurrency, timeout 120; Auto-Trigger beim Merge von PR #102 bewiesen: Run 30712467814 success, 18/18 Steps, 32m32s, Artefakt 52.342.569 Bytes; Rot-Probe Typfehler in buzz-cli -> failure in "Build sidecars" mit E0308/exit 101; Doku-PR #107 loeste dank paths-ignore KEINEN Lauf aus) + #79 CRM-Loeschzeile im Gate-Batch (Quelle action_history_record read-only ueber den bestehenden ssh-Pfad -> kein neuer Espo-User; Access-Log und `deleted:true`-Grabstein begruendet verworfen; Schwelle aus 8 Tagen gemessen: taeglich exakt n8n-agent 1x Lead + 1x CTouchpoint; 5 Proben: Ruhetag gruen, Ausschlag 61/59 aufgeschluesselt, Detektor rot->gruen per echter Testloeschung ueber delete:own, tote Quelle = benannte LUECKE statt "0", ACL-Waechter danach 3/3; Kanal-Beweis Buzz-Event 10f831820480c508) | PRs #102 #107 #111 | Gardener: #113 (Fork-Actions entruempeln: ein main-Push zuendet 6 Upstream-Workflows, Sprig scheitert reproduzierbar an "release not found" -> ein dauerhaft roter Actions-Tab entwertet das neue Gate); #114 als Duplikat zu #69 geschlossen | Befunde: der Gutfall darf nicht formuliert werden — die erste Fassung schrieb die feste Formel "1x Lead + 1x CTouchpoint" und behauptete bei EINER Loeschung einen CTouchpoint, den es nicht gab (erfundene Zahl im Fuehrungsbrief, gefixt); einer der drei Espo-Zugangsnamen im zentralen Secrets-File antwortet 401 (gehoert zu #69); `UID` ist in bash readonly (MSYS) -> die Zuweisung scheitert still und man arbeitet mit der Shell-UID weiter | Blocker: Approval-Gate weiterhin OHNE echte Freigabe (gate-audit nur requested/timeout, auch ein Fremd-Request um 19:57 lief in Timeout) -> #32/#42 bleiben zu; claude-Kontingent (#3), Codex bis 08.08. (#13/#18)
2026-08-01 22:45 | 3 Tickets Betrieb gehaertet | adas-empire#79 Eskalationsweg beweisbar (Kanarie durch denselben empire_notify-Code, Ankunft in der ECHTEN INBOX belegt; 3 Rot-Proben; tote Adresse munirdue@ raus) + buzz#77 Sentry-Liveness (Mention+Token, Kuma sentry-liveness, echter Reboot: Agent ohne Zutun in 29 s zurueck, 87/87 Container) + buzz#86 Token-Waechter mit zwei Alarmwegen (Token-Tod pusht status=down MIT Fix-Befehl, Rechner-aus heilt sich per StartWhenAvailable+Logon-Trigger) | PRs adas-empire#83/#84, buzz#112/#116, google-mcp#5 | BEFUND (systemisch): 9/11 Kuma-Push-Monitore mit maxretries=1 bei retryInterval==interval alarmieren erst nach der DOPPELTEN dokumentierten Zeit, ein status=down erzeugt dort nur PENDING/important=0 -> agency-infra#135 | Widerlegte Praemissen: kein Bounce-Regen im Postfach, Eskalationsmails KAMEN an (nur unbewiesen), buzz#77-Logrotation existierte laengst, Monitor 54 hatte nie Alarm geschlagen | Blocker: keine
2026-08-01 23:10 | 1 Ticket | #106 Morgenbrief + Gate-Batch lesbar (Brief statt Systemreport: Kopfzeile in ganzen Worten, Geld/Menschen/Tag/Laeuft/Zwei-Minuten; Gate-Batch nummeriert mit Ja-/Nein-Folgen AUS dem Ticket-Text; reiner Text, Umbruch bei 64) | Fund: 50 von 50 Inbox-Nachrichten der letzten 5 Tage waren Maschinen -> Ausschluss gehoert IN die Gmail-Abfrage, sonst bleibt eine Kundenmail hinter dem 50er-Deckel unsichtbar | Rot-Proben 3/3 (MCP tot, Mollie tot, gh tot) + Breiten-Probe 64 | Fallen: read/IFS-TAB schluckt Leerfelder; jq -n mehrzeilig stirbt in MSYS-argv; state=error != fehlt (stille 0); laufendes Bash-Script nicht editieren | Blocker: keine
2026-08-01 23:45 | 1 Ticket | buzz#69 Rechte-Rueckfall im CRM stillgelegt (Provisionierungs-Skript baut keine Rolle mehr: 91 Metadata-Scopes vs. 1 aktiver, fail closed + Bestandsschutz; Voll-CRUD-MCP entfernt inkl. gebautem dist/; purge-test meldet Klartext + Exit 3 statt 403-Stacktrace; leerer ESPOCRM-Schluessel in der zentralen Secrets-Datei auskommentiert) | PRs agency-infra#152, agency-tools#58, espo-mcp#6 | Befunde: n8n-agent-Schluessel kann EBENFALLS nicht loeschen (Ticket-Annahme falsifiziert, Fallback wieder ausgebaut); crm.adas.jetzt blockt UA Python-urllib mit 403; process.exit(3) liefert 127 auf Windows | Zurueckgetreten: #113 (Doppel-Claim, paralleler Agent war weiter; Vorflug-Messung als Kommentar hinterlassen) | Blocker: keine
Loading