Files
ROMFASTSQL/CONTEXT_HANDOVER_20260830.md
Claude Agent 57cd50c1e7 docs: context handover pentru puntea Discord
Starea de rulare, ce s-a verificat empiric in productie, cele 10 capcane gasite
pe parcurs (cu #1 — /cleanup care omora sesiunile de lucru — marcata explicit),
deciziile care nu se redeschid, si singurul lucru ramas: tokenul Proxmox cu ACL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
2026-08-30 12:59:39 +00:00

199 lines
9.7 KiB
Markdown

# CONTEXT HANDOVER — Punte Discord → Claude Code (LXC 171)
**Data:** 2026-08-30 13:00 UTC · **Stare:** LIVRATA, in productie, pe `master` (impins)
**Commit curent:** `c8a5d41` · **Repo:** romfast/ROMFASTSQL · **Branch:** `master`
Nu e un task intrerupt. Puntea functioneaza si e folosita. Documentul asta exista ca o
sesiune noua sa poata prelua operarea si continuarea fara sa redescopere totul.
---
## 1. Ce e
Bot Discord care comanda LXC 171 (si, prin el, infrastructura) dintr-un canal privat, de pe
telefon. Bot subtire `discord.py` peste CLI-ul `claude`, cu **proces persistent per fir**
alimentat pe stdin cu `--input-format stream-json` — de aceea un mesaj trimis in timpul unui
tur functioneaza ca **steering**, nu deschide tur nou.
Plan sursa: `~/.gstack/projects/romfast-ROMFASTSQL/claude-master-plan-discord-bridge-20260830.md`
(15 taskuri, toate facute). Executat cu 5 agenti in 3 lane-uri paralele.
**Cod:** `proxmox/lxc171-claude-agent/discord-bridge/`
**Documentatie:** `discord-bridge/README.md` (operare completa), `discord-bridge/INTERFACES.md`
(contractul intre module — de citit inainte de orice modificare), `discord-bridge/security/README.md`.
---
## 2. Stare de rulare (verificata la 12:58 UTC)
| | |
|---|---|
| Serviciu | `systemctl --user status claude-discord` → **active**, `NRestarts=0` |
| Bot | `ClaudeAgent#7891`, app id `1543576449624186880` |
| Guild | Romfast `1430476698264145922` |
| Canal | **privat** `#claude-agent` `1543581063496859679` |
| User permis | `949388626146517022` (marius.mutu) |
| Director de lucru | `/workspace/claude-agent/` (repo git propriu) |
| Model | `sonnet` (default), `/model opus` per fir |
| Cost ziua curenta | $1.5456 (plafon `COST_CAP_USD_DAY=5.00`) |
| Comenzi slash | 7, inregistrate pe guild |
**Stare pe disc:** `~/.claude-discord/` — `env` (0600, token+allowlist), `state.json`,
`bot-settings.json` (hook), `approvals/`, `logs/`, `venv/`.
Cod versionat in repo; secretele NU.
### Comenzi (slash, nu prefix `!` — migrat)
`/new [fork]` · `/cd <cale>` · `/model <sonnet|opus>` · `/status` · `/stop` · `/cleanup [force]` · `/help`
Mesajele obisnuite (fara comanda) merg la Claude ca prompt.
---
## 3. Ce s-a verificat EMPIRIC in productie (nu doar teste)
Astea sunt afirmatii cu dovada, nu presupuneri:
| Ce | Dovada |
|---|---|
| Steering mid-tur | mesaj la 6s intr-un tool call de 25s a schimbat raspunsul final (ALFA→BETA), CLI 2.1.251 |
| Comenzi slash | 7 citite inapoi din API-ul Discord, nu doar din log |
| Confirmare operatiuni ireversibile | `12:24:25 PENDING` → butoane in canal → `12:24:39 ALLOW` → `/tmp/test-punte` sters |
| Fail-closed | acelasi `rm -rf` fara aprobare → `DENY` la timeout, directorul intact |
| Proces persistent | pid viu la 2 min dupa incheierea turului (reaper la 20 min) |
| `--resume` peste restart | aceeasi sesiune reluata dupa ce restartul a omorat procesul |
| `KillMode=control-group` | restart mid-sesiune, zero orfani |
| Parser tolerant | `rate_limit_event` aparut real in stream, ignorat cu un singur log |
| `/cleanup` dupa fix | rulare seaca pe container: „Niciun proces orfan" |
| Suita | **322 passed** cu discord.py, **319 passed + 3 skipped** fara |
Rulare teste: `cd proxmox/lxc171-claude-agent/discord-bridge && python3 -m pytest`
E2E (CLI real, ~40s, costa): `pytest -m e2e`
---
## 4. Capcane gasite pe parcurs — NU le reintroduce
Astea au costat timp si unele erau periculoase. Sunt fixate; sunt scrise aici ca sa nu
fie „simplificate" inapoi.
1. **`/cleanup` omora sesiunile de lucru.** Definitia initiala („orice proces `claude`
absent din `state.json`") prindea sesiunile Claude interactive de pe container:
rularea seaca propunea 25 de procese / 3864 MB, inclusiv sesiunea din care ar fi fost
data comanda. **Apartenenta la cgroup-ul `claude-discord.service` e conditie NECESARA
pentru toate familiile**, fail-closed la cgroup necitibil. Sesiunile de lucru stau in
`tmux-spawn-*.scope`. Exista test de regresie pe instantaneul real; verificat prin
mutant ca musca (fara filtru pica 3 teste).
2. **`pid_start_time` e in SECUNDE**, nu ticks (`session_store.py` converteste; `state.json`
viu contine secunde). Un consumator care compara ticks nu se potriveste niciodata — si
cum acea comparatie protejeaza turul in desfasurare, esecul e tacut si face eligibil
pentru omorare exact ce trebuia protejat. Documentat in `INTERFACES.md`.
3. **`/proc/uptime` e virtualizat de lxcfs, `starttime` nu.** Calculul naiv al varstei dadea
`4123168064` pentru un proces de 10 minute. Se foloseste `btime` din `/proc/stat`.
4. **Utilizatorul containerului se numeste `claude`**, deci potrivirea pe subsirul `/claude`
marca orice proces din home-ul lui. Potrivire pe basename per argument.
5. **Log dublat:** unitul redirecteaza stdout in `bot.log` (`StandardOutput=append:`) SI
codul avea `FileHandler` pe acelasi fisier. `FileHandler` ramane doar la rulare manuala
(`INVOCATION_ID` absent).
6. **`View Channel` lipsea** din permisiunile de invitatie — fara ea botul nu vede canalul,
oricat de permis ar fi in allowlist. Link corect (`permissions=309237763136`):
`https://discord.com/oauth2/authorize?client_id=1543576449624186880&scope=bot%20applications.commands&permissions=309237763136`
7. **Comenzile slash au nevoie de scope `applications.commands`** (nu doar `bot`) si de
**sync pe guild** (instantaneu; global ~1h). Fiecare comanda face `defer()` — altfel
Discord marcheaza interactiunea esuata dupa 3s desi comanda a rulat.
8. **`~/bin` trebuie sa fie in PATH-ul unitului**, altfel wrapperul `infra` e prezent pe
disc dar negasibil de bot (stratul 4 de securitate inert).
9. **Raportul `/cleanup`** depasea 2000 de caractere cu rezultatele atasate → mesaj respins
de Discord exact la `force:True`. Buget de caractere `MAX_REPORT_CHARS=1800`.
10. **Discord API respinge User-Agent-ul implicit al `urllib`** cu `error code: 1010`
(Cloudflare). Arata identic cu un refuz de permisiuni. Pune un User-Agent real.
---
## 5. Decizii de arhitectura — luate deliberat, NU le redeschide
Sunt in plan si au fost reconfirmate de utilizator. Un agent nou care le „descopera" ca
probleme pierde timp si risca sa strice lucruri.
- **`--permission-mode bypassPermissions` e intentionat.** Regulile `deny` sunt strat
cosmetic, NU bariera — verificat: `/usr/bin/ssh -V` si `bash -c "ssh -V"` trec pe langa.
Bariera reala e hook-ul PreToolUse.
- **Accesul larg la infrastructura si la `/workspace` e FUNCTIONALITATE ceruta**, nu bug.
De aceea: fara user separat `cdbot`, fara allowlist de proiecte pentru `/cd`.
- **Fara audit append-only** (stratul 3), respins constient. **Consecinta asumata:** la o
problema nu exista jurnal independent; logurile pot fi sterse de chiar procesul care le
scrie. Hook-ul si botul ruleaza sub acelasi user, deci un agent isi poate scrie singur
`"status":"allow"`.
- **Fara reluare automata a turului pierdut** — risc de dubla executie sub bypassPermissions.
- Fara Agent SDK, fara dashboard web, fara user separat, fara test golden pe JSONL inregistrat.
---
## 6. CE A RAMAS DE FACUT
### 6.1 Token Proxmox cu ACL (singurul lucru neterminat)
**Nu e conditie de functionare.** Puntea merge acum folosind cheile SSH root deja prezente
pe container — adica orice tur are drepturi depline pe cluster. Tokenul restrange asta.
Comenzile exacte sunt in `discord-bridge/security/README.md`. Pe scurt, ca root pe `pvemini`:
```
pveum user add claude-bridge@pve
pveum role add ClaudeBridge -privs "Datastore.Audit,Sys.Audit,Sys.Console,Sys.Syslog,VM.Audit,VM.Monitor,VM.Console,VM.PowerMgmt"
pveum acl modify /
pveum user token add claude-bridge@pve discord --privsep 1
pveum acl modify / --tokens 'claude-bridge@pve!discord'
```
`VM.Allocate` (creare/distrugere guest) e lasat afara INTENTIONAT.
Apoi: secretul (afisat o singura data) in `~/.claude-discord/env` ca `PVE_TOKEN_ID` /
`PVE_TOKEN_SECRET`, `chmod 600`. De decis daca ACL ramane pe `/` sau se restrange la un
subset de guest-uri.
### 6.2 Idei ramase, neprioritizate
- Atasamente Discord → Claude (download in /tmp + cale in prompt) — era v1.1 in plan.
- Rate limit-ul Discord la render e singura degradare tacuta ramasa: are tratare (interval
adaptiv 1s→5s), nu are test. Acceptat, fiindca esecul e intarziere, nu pierdere.
- `/cleanup` nu prinde procese iesite complet din cgroup — pret deliberat, ca sa nu atinga
niciodata sesiunile interactive.
---
## 7. Fisiere cheie
| Fisier | De ce conteaza |
|---|---|
| `discord-bridge/INTERFACES.md` | contractul intre module + proprietatea pe fisiere. **De citit primul.** |
| `discord-bridge/README.md` | operare, instalare, depanare, limitari oneste |
| `discord-bridge/runner.py` | proces persistent, reaper, respawn `--resume` |
| `discord-bridge/session_store.py` | state.json atomic, lock per fir, PID reuse |
| `discord-bridge/cleanup.py` | vezi capcana #1 — cea mai periculoasa zona |
| `discord-bridge/security/confirm_hook.py` | hook PreToolUse, fail-closed |
| `discord-bridge/security/infra` | wrapper cu lista explicita de hosturi (exit 3 la host necunoscut) |
| `discord-bridge/ops/claude-discord.service` | `KillMode=control-group`, MemoryHigh 3G / Max 6G, PATH |
| `~/.gstack/.../claude-master-plan-discord-bridge-20260830.md` | planul original, cu justificarile |
## 8. Comenzi de operare
```bash
systemctl --user status claude-discord
journalctl --user -u claude-discord -n 200
tail -f ~/.claude-discord/logs/bot.log
tail -f ~/.claude-discord/logs/confirm_hook.log # cine a cerut/primit aprobare
cd proxmox/lxc171-claude-agent/discord-bridge && python3 -m pytest
python3 cleanup.py # rulare seaca, sigura
```
**Memorie:** un fir costa ~440 MB (claude 300 + servere MCP 137). Plafon 4 procese → ~1,8 GB,
sub `MemoryHigh=3G`. Containerul are 16 GB.