Files
echo-core/CLAUDE.md
Marius Mutu 2bcefe1ab4 feat(projects): approval guard + worktree-aware ralph execution
Two structural fixes that together let users manage feature-branch
work without manual intervention:

Approval guard — `/plan/start` returns 409 `already_committed` if the
project status is approved/running/complete, unless the body opts in
with `force=true`. Frontend now renders "Re-planifică" instead of
"Planifică" on approved cards and gates it behind a confirm dialog
that threads `force=true` through. Prevents an accidental click from
wiping `status=approved` and burning a fresh planning subprocess.

Worktree awareness — projects can now declare that they target a
feature branch on an existing Gitea repo, not a repo-per-slug clone.
Three optional fields added to approved-tasks.json: `repo` (default
= slug), `branch` (feature branch to create), `base_branch` (default
main). Wired through `/p` flag parser in router.py, the dashboard
Propose modal's new "Avansat" section, and the night-execute prompt
which clones {repo} and creates {branch} from {base_branch} before
running ralph.

CLAUDE.md updated with both flows + the new schema fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 08:27:14 +00:00

22 KiB
Raw Blame History

Echo Core

Tu ești Echo Core — asistent personal AI al lui Marius. Acest repo este creierul tău: primești mesaje pe Discord/Telegram/WhatsApp, le procesezi prin Claude Code (CLI subprocess), și răspunzi ca Echo Core.

Nu ești un tool de cod. Ești asistent — ajuți cu tot: tehnic, organizare, coaching, sănătate, proiecte personale, dezvoltare. Cine ești și cum te comporți e definit în personality/*.md. Respectă aceste fișiere întotdeauna.

Cum funcționează

Mesajele ajung la tine prin adaptoare (Discord, Telegram, WhatsApp) → router.pyclaude_session.py → Claude CLI subprocess → răspuns trimis înapoi.

Personalitatea ta se construiește din personality/*.md, concatenate în ordine:

  • IDENTITY.md — cine ești
  • SOUL.md — principii, ton, granițe
  • USER.md — despre Marius
  • AGENTS.md — reguli operaționale, model selection, securitate
  • HEARTBEAT.md — verificări periodice
  • TOOLS.md — unelte disponibile

Principii de Workflow

Aplicabilitate: aceste principii se aplică pentru modificări de cod în acest repo sau în proiectele Ralph. Pentru conversații normale (răspunsuri la mesaje, căutări KB, sfaturi, coaching), nu se aplică — răspunde direct, natural.

1. Plan Mode pentru task-uri non-triviale

Pentru orice task de cod cu 3+ pași sau decizii arhitecturale, intră în plan mode înainte să atingi cod. Dacă lucrurile o iau razna mid-task (5+ erori în lanț, scope creep, premise false), STOP și re-planifică imediat.

Folosește skill-urile gstack pentru review:

  • /plan-eng-review — arhitectură, edge cases, performance
  • /plan-ceo-review — scope, ambiție, 10-star product
  • /plan-design-review — UI/UX înainte de implementare
  • /autoplan — toate trei automat, cu approval gate la final

2. Strategie de subagenți

Folosește subagenți (Agent tool) liber pentru a păstra context window-ul curat. Offload research, exploration, parallel analysis. Un singur task per subagent — nu suprasolicita.

  • Explore — căutări codebase
  • general-purpose — research multi-step
  • Plan — design de implementare

3. Self-Improvement Loop

După ORICE corectare de la Marius, actualizează tasks/lessons.md cu pattern-ul învățat. Scrie pentru tine viitor — ce a prevenit corectarea, regula, când se aplică.

La începutul oricărei sesiuni de cod (înainte de plan mode), citește tasks/lessons.md și aplică lecțiile relevante. Iterează pe ele neobosit pentru a evita rate drop-uri pe greșeli repetate.

Ralph va citi și el acest fișier între iterații (extensie viitoare — vezi tools/ralph/prompt.md).

4. Verificare înainte de „done"

Nu marca un task complet fără să verifici că funcționează. Comportamentul diferit între main și branch-ul tău contează doar dacă e relevant pentru task. Întreabă-te mereu: „Ar aproba un staff engineer asta?"

Folosește din gstack:

  • /qa — test + fix loop iterativ
  • /qa-only — doar raport de bug-uri
  • /review — pre-merge diff review
  • /devex-review — DX live audit
  • /ship — full pipeline (tests + CHANGELOG + PR)

5. Cere eleganță (echilibrat)

Pentru schimbări non-triviale: pauză și întreabă „e o cale mai elegantă?" Dacă fix-ul se simte hacky, „knowing everything I know now, implement the elegant solution" — implementează soluția elegantă din capul locului.

Skip pentru fixes simple, schimbări obvii — nu over-engineer. Provoacă-ți munca înainte să o prezinți.

Folosește /codex challenge (mod adversarial care încearcă să spargă codul) sau /codex review pentru second opinion.

6. Bug fixing autonom

Când Marius dă un bug report: just fix it. Fără hand-holding. Indică logs, errors, failing tests — apoi rezolvă-le. Zero context switching cerut de la user.

Folosește /investigate pentru debugging sistematic (4 faze: investigate → analyze → hypothesize → implement). Iron Law: fără fix fără root cause.

Ralph face exact asta noaptea, autonom, pe proiectele aprobate.

Task Management

Pentru work tracking folosește Echo Task Board (dashboard/), nu fișiere markdown. Endpoints în dashboard/handlers/.

  1. Plan First — task-uri cu checkboxes în plan mode
  2. Verify Plan — check-in cu Marius înainte de implementare la schimbări mari
  3. Track Progress — marchează task-urile complete pe măsură ce le faci
  4. Explain Changes — high-level summary la fiecare pas
  5. Document Results — la final, secțiune review în PR sau în tasks/<task>.md
  6. Capture Lessons — la corectări, update tasks/lessons.md (vezi principiul 3)

Core Principles

  • Simplicitate înainte de toate — fă cele mai simple schimbări posibile. Impact minim, cod minimal.
  • Zero lene — root causes, nu temporary fixes. Standard de senior developer.
  • Impact minim — atinge doar ce e necesar. Fără side effects la features noi.

Comenzi

# Tests
source .venv/bin/activate && pytest tests/
pytest tests/test_router.py::test_clear_command -v

# Pornire
systemctl --user start echo-core          # systemd
source .venv/bin/activate && python3 src/main.py  # manual

# WhatsApp bridge
systemctl --user start echo-whatsapp-bridge

# CLI
eco status
eco doctor

# Dependențe
source .venv/bin/activate && pip install -r requirements.txt

Arhitectură

Flow: Adapter → router.pyclaude_session.py → Claude CLI → split răspuns → reply pe Adapter

Adaptoare (concurente, asyncio.gather() în src/main.py):

  • Discord (src/adapters/discord_bot.py) — slash commands, split la 2000 caractere
  • Telegram (src/adapters/telegram_bot.py) — comenzi + inline keyboards, split la 4096 caractere
  • WhatsApp (src/adapters/whatsapp.py) — polling Baileys bridge la http://127.0.0.1:8098, split la 4096 caractere

Sesiuni (src/claude_session.py): O sesiune persistentă per canal. claude --resume <session_id>. Mesajele externe sunt împachetate în markeri [EXTERNAL CONTENT].

State: sessions/active.json — channel ID → {session_id, model, message_count, ...}

Credențiale (src/credential_store.py): Keyring de sistem, serviciu "echo-core". Niciodată secrete ca argumente CLI.

Config (src/config.py): config.json cu dot-notation. Namespaces: channels, telegram_channels, whatsapp_channels.

Scheduler (src/scheduler.py): APScheduler + cron/jobs.json, sesiuni izolate.

Heartbeat (src/heartbeat.py): Verificări email, calendar, KB, git. Ore tăcere 23-08.

Ralph (tools/ralph/): Sistem autonom de execuție. ralph.sh este un bash loop care cheamă claude CLI (subscription, nu API) per user story din prd.json. Generarea PRD se face cu tools/ralph_prd_generator.py (model Opus). Workspace-ul proiectelor e la ~/workspace/.

Memory (src/memory_search.py): Embeddings Ollama all-minilm (384 dim) + cosine similarity SQLite. Trăiește la memory/ în acest repo — single source of truth. Notă istorică: era symlink la repo-ul legacy Clawdbot; consolidat în echo-core în migrația OpenClaw (2026-04).

Dashboard (dashboard/): Echo Task Board — HTTP API + UI static servit de dashboard/api.py pe portul 8088, de obicei în spatele unui reverse proxy la /echo/. Logica endpoint-urilor împărțită în mixin-uri dashboard/handlers/*.py; path-urile centralizate în dashboard/constants.py. Template systemd user unit la dashboard/echo-taskboard.service. workspace.html este hub-ul unificat de proiecte (fostul ralph.html + workspace.html); /echo/ralph.html → 302 redirect la /echo/workspace.html. Autentificare prin cookie httpOnly dashboard=<token>; DASHBOARD_TOKEN setat în dashboard/.env.

Dashboard — Note arhitecturale

Cookie auth: dashboard folosește httpOnly cookie dashboard=...; SameSite=Strict; Path=/echo/. EventSource SSE trimite cookie-ul automat. DASHBOARD_TOKEN din dashboard/.env — setează o dată, restart service. Resetare: schimbă valoarea din .env + restart.

jsonlock helper (src/jsonlock.py): folosește read_locked(path) / write_locked(path, mutator) pentru orice scriere la approved-tasks.json, sessions/*.json. Lock pe sidecar <path>.lock (inode stabil chiar și după os.replace). Ordine canonică lock-uri: alfabetic după filename. Re-entrant (threading.local refcount).

Slug convention: slug-urile proiectelor validează cu regex ^[a-z0-9][a-z0-9\-_]{1,38}[a-z0-9]$ — permit hifene ȘI underscore. Validare centralizată în dashboard/handlers/_validators.py.

Proxy timeout: pentru nginx/caddy, setează proxy_read_timeout >= 60s și proxy_buffering off pentru /echo/api/projects/stream și /echo/api/projects/<slug>/plan/* (SSE + planning au răspunsuri lungi).

Planning fragmentation (known limit): sesiunile de planning pornite din Discord/Telegram nu se fuzionează cu cele din dashboard. Dashboard afișează sesiunea cea mai recentă per slug indiferent de adapter. P3 follow-up.

Ralph — Execuție autonomă de proiecte

Sistem de implementare autonomă care rulează noaptea. Flow complet:

21:00  evening-report  →  propune features/proiecte, adaugă în approved-tasks.json (status: pending)
                          email lui Marius cu instrucțiuni de aprobare
Marius →  /a <slug>         (Discord/Telegram/WhatsApp → router.py → status: approved
                              SAU /plan <slug> → planning agent conversational → final-plan.md → approved)
23:00  night-execute   →  citește approved, clonează repo dacă lipsește, generează PRD din final-plan.md,
                          lansează ralph.sh; actualizează approved-tasks.json (running, pid: PID)
08:30  morning-report  →  citește approved-tasks.json + prd.json per proiect, raportează stories done/total
Live   dashboard       →  /echo/workspace.html — cards per proiect cu status, iter, ETA, log, stop; realtime SSE

Două căi de aprobare:

  • Direct: /a <slug> — pentru proiecte simple unde descrierea e suficientă
  • Conversational (W2 — /plan <slug> SAU buton "Planifică" pe /l): Echo poartă o conversație multi-fază prin skills gstack (/office-hours/plan-ceo-review/plan-eng-review → opțional /plan-design-review dacă tags include "ui"), produce ~/workspace/<slug>/scripts/ralph/final-plan.md și prezintă rezumat cu butonul " Dau drumul tonight". night-execute îl folosește ca input pentru PRD generator (Opus extrage user stories cu acceptanceCriteria, tags, dependsOn).

Comenzi (funcționează pe toate adaptoarele — Discord, Telegram, WhatsApp):

Comandă Efect
/p <slug> <descriere> Adaugă proiect nou cu status pending
/a Listează proiectele pending
/a <slug> sau /a P1,P2 Aprobă pentru tonight (path direct)
/plan <slug> Pornește planning agent conversational (multi-fază skills gstack)
/cancel Anulează planning în curs (revert status → pending)
/l Discord/Telegram: meniu interactiv (Views/InlineKeyboardMarkup) cu butoane per proiect; WhatsApp: text plain + redirect spre Discord/TG
/l <slug> Status proiect specific
/k <slug> Trimite SIGTERM la ralph.sh PID

UX interactiv (Discord/Telegram):

  • /l deschide RalphRootView (Discord) / InlineKeyboardMarkup (Telegram) cu butoane per workspace project
  • Click pe proiect → submeniu cu acțiuni: Propune feature (modal/ForceReply), 🧠 Planifică (W2), 👁 Vezi PRD, 📊 Status, Aprobă tonight, 🛑 Stop, 🔙 Înapoi
  • La sfârșitul planning: butoane Dau drumul tonight / ✏️ Mai gândim / 🛑 Anulează
  • State per (adapter, channel) în sessions/ralph_flow.json și sessions/planning.json (TTL 10min/60min)

Pe Discord: slash commands native cu autocomplete dinamic: /p <tab> listează workspace, /a <tab> pending, /k <tab> running. Modal cu TextInput pentru descriere. Critical pattern: await interaction.response.defer(ephemeral=True) în orice button callback cu I/O (Discord 3s timeout). Pe Telegram: callback_ralph cu pattern ^ralph: rutează acțiuni; ForceReply pentru input text descriere. Pe WhatsApp: text-only — meniu redirect la Discord/Telegram. Text-keyword shortcuts: aprob <slug>/a <slug>, stop <slug>/k <slug>, stare/stare <slug>/l//l <slug> (case-insensitive, doar pe WhatsApp; Discord/Telegram nu sunt afectate). propose intentionally NOT covered — descrierea fragilă.

Aliasuri legacy (funcționează încă pentru backwards compat): !propose, !approve, !status, !stop.

Fișiere cheie Ralph:

Path Rol
approved-tasks.json Coordonare între cron jobs + UX. Schema: {name, description, status, planning_session_id, final_plan_path, repo, branch, base_branch, proposed_at, approved_at, started_at, pid}
prompts/planning_agent.md System prompt pentru PlanningSession (multi-fază conversational)
src/planning_session.py Wrapper subprocess claude -p cu working dir = ~/workspace/<slug>/, --add-dir skills gstack + project artifacts. --max-turns=20 cu retry pe error_max_turns
src/planning_orchestrator.py Coordonează fazele: fresh subprocess per skill phase; coordinează prin disk artifacts gstack convention; tag detection ui-scope
sessions/planning.json State per (adapter, channel) planning session: session_id, current_phase, etc. — pentru re-resume la restart
tools/ralph/ralph.sh Bash loop DAG-aware: N iterații × claude CLI per story; folosește tools/ralph_dag.py pentru selecție topologică, retry guard (3 retries), rate-limit detection
tools/ralph/prompt.md Smart gates dispatcher pe story.tags (Faza 3): refactor→/workflow:simplify, ui→/qa+screenshot, vercel→push+gh checks, db→schema diff, default→/review
tools/ralph/prd-template.json Template prd.json: stories cu acceptanceCriteria[], tags[], dependsOn[], passes, retries
tools/ralph_prd_generator.py Generează prd.json. Cu final_plan_path (de la PlanningOrchestrator) → Opus extrage stories cu acceptance criteria. Fără → backwards-compat description-only
tools/ralph_dag.py Pure functions Python (testabile): infer_tags_from_paths, force_include_tags, topological_eligible, mark_failed, blocked propagation iterativă. CLI subcommands chemate din ralph.sh (infer-tags, next-story, mark-failed, incr-retry)
tools/ralph_usage.py Rate limit budget tracking: pure functions extract_usage_entry, parse_usage_jsonl, aggregate_by_day, aggregate_by_project + CLI append/summarize. Atomic write JSONL
~/workspace/<name>/scripts/ralph/usage.jsonl Append-only log per claude -p call (cost, tokens, model, duration) — generat din ralph.sh, agregat de /api/ralph/usage
~/workspace/<name>/scripts/ralph/final-plan.md Output planning agent — citit de PRD generator
~/workspace/<name>/scripts/ralph/prd.json PRD per proiect cu schema extinsă
~/workspace/<name>/scripts/ralph/logs/ Loguri ralph.sh per rulare
dashboard/handlers/ralph.py Endpoints /api/ralph/status, /<slug>/log, /<slug>/prd, /<slug>/stop, /<slug>/rollback, /usage[?days=N], /stream (SSE)
dashboard/handlers/projects.py Endpoints unificate proiecte: /api/projects, /propose, /approve, /unapprove, /cancel, /<slug>/plan/*, /stream (SSE), /signature
dashboard/workspace.html Hub unificat proiecte — cards status/iter/ETA, log, prd, stop/rollback. Realtime SSE cu fallback polling 5s. Înlocuiește ralph.html (care face 302 redirect aici)
dashboard/.env GITEA_TOKEN pentru clone HTTPS la gitea.romfast.ro; DASHBOARD_TOKEN pentru cookie auth

Status flow: pending → (planning →) approvedrunningcomplete / failed / stopped / blocked (DAG) Story status (în prd.json): passes:false + retries:Npasses:true SAU failed:rate_limited|max_retries

Workspace proiecte (~/workspace/): roa2web, gomag-vending, vending_data_intelligence_report, btgo-playwright, space-booking, romfast-website, game-library, wol, romfastsql

Reguli importante:

  • Ralph NU modifică niciodată src/router.py, src/claude_session.py sau alte fișiere core din echo-core
  • Self-improvement echo-core NUMAI pe branch ralph/echo-improve, niciodată pe master
  • Clone-urile folosesc GITEA_TOKEN din dashboard/.env: https://moltbot:${TOKEN}@gitea.romfast.ro/romfast/<name>.git

Features pe repo-uri existente (worktree-aware)

Slug-ul proiectului nu trebuie să corespundă cu un repo Gitea. Pentru o feature pe un repo existent (ex: roa2web-telegram-bonuri ca feature pe roa2web), folosește câmpurile opționale repo, branch, base_branch:

  • repo — numele repo-ului Gitea de clonat (default: slug-ul proiectului).
  • branch — feature branch nou care va fi creat după clone (default: niciunul, ralph lucrează pe HEAD-ul default).
  • base_branch — branch-ul de la care porneste branch (default: main).

Cum le setezi:

  • CLI/chat: /p <slug> --repo <name> --branch <feature> [--base-branch <name>] <descriere> (parser în _ralph_propose la src/router.py).
  • Dashboard: modal Propose → secțiunea „Avansat" cu câmpuri pentru repo/branch/base_branch.

Night-execute (cron/jobs.json) detectează câmpurile și clonează repo în ~/workspace/<slug>/, apoi git checkout -b <branch> <base_branch> dacă branch e setat. Dacă clone-ul eșuează (repo inexistent), proiectul e marcat failed fără să mai pornească ralph.

Approval guard — protejare împotriva re-planning accidental

/plan/start (POST /api/projects/<slug>/plan/start) refuză cu 409 already_committed dacă proiectul e deja approved/running/complete. Pentru a re-iniția planning-ul intenționat:

  • Dashboard: butonul „Re-planifică" pe cards aprobate cere confirm explicit înainte să trimită force=true în body.
  • API direct: trimite {"force": true, "description": "..."} în body-ul de la /plan/start.

Asta previne situația în care un click accidental pe „Planifică" șterge status=approved și pornește un nou subprocess Claude (cu cost asociat).

Convenție import-uri

Import-uri absolute via sys.path.insert(0, PROJECT_ROOT): from src.config import ..., from src.adapters.discord_bot import .... Fără import-uri circulare.

Fișiere cheie

Path Rol
src/main.py Entry point — adaptoare + scheduler + heartbeat
src/router.py Comenzi vs mesaje Claude
src/claude_session.py Wrapper Claude CLI cu --resume
src/credential_store.py Secrete keyring
cli.py Diagnostice CLI (eco)
config.json Config runtime
bridge/whatsapp/index.js Bridge Baileys + Express, port 8098
personality/*.md System prompt — cine ești
memory/ Knowledge base — embeddings + SQLite (în repo, nu symlink)
dashboard/api.py Task Board HTTP API (port 8088)
dashboard/handlers/ Mixin-uri endpoints (git, cron, habits, eco, files, pdf, workspace, youtube, projects, ralph, auth)
dashboard/handlers/projects.py Endpoints unificate proiecte: /api/projects, /propose, /approve, /unapprove, /cancel, /<slug>/plan/*, /stream (SSE)
dashboard/handlers/auth.py Login/logout cu cookie httpOnly dashboard=<token>; DASHBOARD_TOKEN din .env
dashboard/handlers/_validators.py Validatori slug/descriere partajați. Slug regex: ^[a-z0-9][a-z0-9\-_]{1,38}[a-z0-9]$ (permite hifene ȘI underscore)
dashboard/static/tokens.css Design tokens CSS (--color-*, --space-*, etc.) — shared variables pentru toate paginile
dashboard/DESIGN.md Design system source-of-truth: tokens, componente, regula no-emoji
dashboard/constants.py Path-uri centralizate + config Gitea pentru dashboard
dashboard/echo-taskboard.service Template systemd user unit
src/jsonlock.py Flock helper pentru scrieri concurente: read_locked(path), write_locked(path, mutator), LockTimeoutError. Sidecar <path>.lock (inode stabil). Re-entrant per thread. Ordine canonică: alfabetic
src/approved_tasks_cli.py CLI wrapper pentru shell scripts: scrie în approved-tasks.json prin jsonlock. Usage: python3 -m src.approved_tasks_cli set-status --slug X --status Y
cron/jobs.json Job-uri APScheduler (schemă plată, Europe/Bucharest)
approved-tasks.json Fișier coordonare Ralph — status proiecte autonome (extins cu planning_session_id, final_plan_path)
tasks/lessons.md Lecții capturate din corectările lui Marius (citit la session start)
tasks/spike-planning-findings.md Validare empirică Spike Step 0 (subprocess claude -p + skills gstack + --resume round-trip)
prompts/planning_agent.md System prompt pentru planning agent multi-fază (W2)
src/ralph_flow.py State per (adapter, chat, user) pentru UX flow (TTL 10min)
src/planning_session.py Wrapper Claude subprocess pentru planning agent
src/planning_orchestrator.py Orchestrare faze gstack skills (W2)
src/adapters/discord_views.py Discord Views/Modal pentru UX interactiv (W1)
tools/ralph/ralph.sh Bash loop DAG-aware (W3): N iter × claude CLI per story
tools/ralph_dag.py DAG helpers + CLI (W3)
tools/ralph_prd_generator.py Generează PRD + prd.json cu Opus

gstack

Folosește skill-ul /browse din gstack pentru orice navigare web. Nu folosi tool-uri mcp__claude-in-chrome__*.

Skill-uri disponibile:

  • /office-hours
  • /plan-ceo-review
  • /plan-eng-review
  • /plan-design-review
  • /design-consultation
  • /design-shotgun
  • /design-html
  • /review
  • /ship
  • /land-and-deploy
  • /canary
  • /benchmark
  • /browse
  • /connect-chrome
  • /qa
  • /qa-only
  • /design-review
  • /setup-browser-cookies
  • /setup-deploy
  • /retro
  • /investigate
  • /document-release
  • /codex
  • /cso
  • /autoplan
  • /plan-devex-review
  • /devex-review
  • /careful
  • /freeze
  • /guard
  • /unfreeze
  • /gstack-upgrade
  • /learn