Commit Graph

71 Commits

Author SHA1 Message Date
bf62d6fb4b feat(dashboard): endpoint de download fișiere binare + tool epub-to-audio
- dashboard/handlers/files.py: handle_files_download() servește mp3/wav/zip
  din WORKSPACE_DIR cu Content-Disposition attachment, resolve dedicat
  (nu _resolve_sandboxed, care nu ajunge niciodată la WORKSPACE_DIR)
- dashboard/api.py: rutează /api/files/download
- tools/epub_to_audio.py: tool nou de conversie EPUB → audio
- cron/jobs.json, memory/kb/index.json: stare auto-generată (job runs,
  regenerare index KB)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 17:50:27 +00:00
a1d39637c8 feat(fallback): unelte, internet, retea si conversatie pe modelul local
Fallback-ul local (Qwen3.5-2B, LXC 104) raspundea ca nu are unelte sau
internet. Acum conversează, are unelte read-only si istoric per canal.

Unelte (allowlist strict read-only, src/local_fallback_tools.py):
- cauta_web — DuckDuckGo Lite, fara API key
- masini — status Proxmox/LXC prin SSH paralel (src/net_status.py)
- vremea, sold, facturi, trezorerie, doctor, logs, email, kb,
  cauta_memorie, citeste_pagina

Protocol: function calling OpenAI nativ. Protocolul text anterior
("TOOL: nume arg") pierdea argumentul in 100% din cazuri.

Uneltele cu output deja formatat se intorc verbatim — modelul de 2B
transforma un `doctor` cu 5 linii OK in "Sistemul este in 5/5 state.".

Doua treceri: prima decide uneltele (cu tools, temp 0), a doua reface
turnul fara ele — prezenta definitiilor strica raspunsurile simple
("cat fac 128/4?" -> "Nu stiu ce inseamna 128/4"). Few-shot doar pe
cereri creative: pe turnuri factuale strica aritmetica (17*23 -> 471).

Ocoliri deterministe pentru ce modelul greseste reproductibil: vremea si
preturile live (raspundea din memorie), plus sarcini "instructiune: text"
(payload-ul dicta unealta — "scrie mai politicos: da-mi raportul acum"
chema `sold`). llama.cpp nu e determinist nici la temperatura 0 si
ignora tool_choice, deci promptul singur nu ajunge.

Masurat: 36/36 pe set combinat (17 alegere unealta + 19 comprehensiune),
de la 14/19 pe comprehensiune inainte de regulile negative din prompt.

Comenzi: /f (inlocuieste /testfallback, ramas alias), /f reset, /masini.
Prefixul "Claude e la limita" apare doar pe calea de rate limit.

roa2web: /otp <cod> pentru 2FA din chat — mesajul de eroare trimitea la
verify_2fa(code, email), imposibil de rulat din Discord.

Corectat memory/kb/tools/infrastructure.md: LXC 101 si 110 sunt pe pve1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B88u3KkzvUNnzU56y7GBn2
2026-08-23 07:46:46 +00:00
fe500a7227 chore: auto-commit from dashboard 2026-08-22 08:40:56 +00:00
4d2dbfb8a7 chore: auto-commit from dashboard 2026-07-12 19:28:42 +00:00
80eb8034b3 fix(voice): instrucțiune de limbă inline + fallback Supertonic pe română
Markerul opac [tts-lang:en] depindea de regula din VOICE_MODE.md, aflată
la ~30k caractere distanță în system prompt — modelul o rata pe ~1 din 5
turnuri. Răspunsul în română era apoi respins integral de pocket-tts
(English-only) și turnul rămânea fără audio.

- src/router.py: markerul poartă instrucțiunea inline (un singur token,
  acoperit în continuare de strip-ul anti-jailbreak); 5/5 EN la repro
- src/voice/tts_stream.py: dacă modelul tot scapă română pe o voce
  pockettts, blocul cade pe Supertonic (M2, ro) în loc de tăcere;
  coada TTS transportă (clause, voice, lang) per clauză
- tools/tts.py: looks_romanian public (folosit de tts_stream)
- personality/VOICE_MODE.md: regula gated pe prefixul [tts-lang:en
- tests: 4 teste noi fallback + expectații marker actualizate

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:20:28 +00:00
61d667ec65 fix(voice): normalizare TTS lang-aware + cap 400 chars mutat per-request Supertonic
Două cauze pentru audio mut pe pocket-tts după fix-ul [tts-lang:en]:

1. normalize_for_tts expanda numerele/orele în cuvinte românești chiar și
   în text englezesc ("It's 9:46 PM" → "nouă și patruzeci și șase de
   minute") → diacritice → pocket-tts respingea clauzele. push_text rezolvă
   acum lang din engine-ul vocii curente (engine_for_voice) și sare
   expansiunile RO când lang != 'ro'. [scris de Echo în sesiunea Discord]

2. sanitize_for_supertonic (cap 400 chars — limită ONNX per request) rula
   pe textul întreg în normalize, înainte de segmentarea în clauze —
   răspunsurile lungi erau retezate mid-word și trunchierea la 200 cuvinte
   cu sufixul "Restul l-am scris în chat" nu se mai aplica. Separat
   map_tts_punctuation (fără cap, folosit de normalize) de
   sanitize_for_supertonic (cap, aplicat în _synthesize_supertonic).

Include vocea clonată "Marius 4" (tts_voices.json + default în config).

Fixează test_truncate_exactly_200_words_unchanged și
test_truncate_over_200_words_appends_suffix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 18:57:30 +00:00
00e0d1ad8c fix(voice): răspuns în engleză pe voice turns când vocea activă e pocket-tts
Instrucțiunea „engleză pentru audio" nu ajungea la model: VOICE_MODE.md nu
avea nicio regulă de limbă, iar condiția din TOOLS.md („când generez pentru
o voce pockettts") era neevaluabilă — modelul nu știa ce voce e activă.
Rezultat: mirroring pe română → filtrul de diacritice bloca sinteza → tăcere.

- tools/tts.py: helper public engine_for_voice() (catalog → tts.default_engine)
- src/router.py: injectează [tts-lang:en] în prefixul voice când vocea activă
  e pockettts (config citit fresh — vede swap-ul live /voice setvoice);
  anti-jailbreak strip extins la [tts-lang:...]
- personality/VOICE_MODE.md: regulă de limbă gated pe marker
- tests/test_router.py: 5 teste noi pentru marker + strip

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 18:07:14 +00:00
00ac971044 fix(tts): pocket-tts refuză text cu diacritice RO în loc să-l pronunțe greșit
Kyutai pocket-tts nu suportă română (vezi eval anterior). Codul rula
oricum sinteza pe orice text primit; acum _synthesize_pockettts
respinge diacriticele RO înainte de request, fără fallback silențios
pe altă voce.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 17:30:12 +00:00
504e12ac4e feat: US-009 - Autocomplete voci din catalog pentru /audio și /voice setvoice
- tools/tts.py: +list_voice_names(engine=None), sursă live din tts_voices.json
- src/fast_commands.py: _VOICES statice -> _supertonic_voice_names() din catalog
- discord_bot.py /audio și discord_voice.py /voice setvoice: choices statice -> autocomplete live
- gates rulate: tests PASS (1043 passed, 22 eșecuri preexistente neschimbate), ui/backend review PASS manual
2026-07-11 10:44:04 +00:00
7ede30d156 feat: US-006 - Refactor tools/tts.py — rutare engine, backend pocket-tts și fallback
- synthesize() rezolvă engine din tts_voices.json (fallback la tts.default_engine din config)
- _synthesize_pockettts nou: POST multipart la :7789/tts cu voice_wav/voice_url, ignoră lang
- fallback tehnic automat pe _synthesize_supertonic(M2, ro) via _PocketTTSUnavailable; erori de
  conținut (voce lipsă, text gol) nu declanșează fallback; rezultatul include engine_used
- gates rulate: tests PASS (1043 passed, 22 eșecuri preexistente neschimbate), /workflow:simplify
  (4 agenți) cu 1 fix aplicat, /review (backend, manual) PASS
2026-07-11 10:28:06 +00:00
e3b5cdf0e5 feat: US-003 - Creează catalogul de voci tts_voices.json cu seed inițial
- tts_voices.json (nou): seed 17 intrări — M1-M5/F1-F5 supertonic, Marius 1-3
  și Paula 1-3 pockettts (state_path spre models/voices/*.safetensors), alba
  pockettts (voice_url)
- .gitignore: +*.lock pentru sidecar-uri jsonlock
- elimină approved-tasks.json.lock din git (sidecar gol, jsonlock îl recreează)
- scris prin src/jsonlock.py write_locked()
- gates: /review (backend) PASS, db (schema manuală) PASS, pytest 1043 passed
  (22 eșecuri preexistente neschimbate)
2026-07-11 10:14:40 +00:00
ed407b49a6 feat: US-001 - Setup venv-pockettts și pocket-tts.service systemd unit
- venv separat .venv-pockettts (torch + pocket-tts + keyring), gitignored
- tools/pocket_tts_env_launch.sh citește hf_token din keyring, exit non-zero + log clar dacă lipsește
- pocket-tts.service (systemd --user) pornește serverul built-in pe 127.0.0.1:7789 prin launcher
- gates: /review (manual, diff minimal) PASS, smoke test curl /health 200 + restart test PASS
2026-07-11 10:02:26 +00:00
155d6cb9c1 Update TOOLS.md, cron jobs, KB index; add discord file sender + pocket-tts plan
Pre-existing work committed before starting Ralph self-improvement run on
ralph/echo-improve branch, so that branch's diff stays isolated to the
pocket-tts integration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 09:54:36 +00:00
738dc0181a chore: auto-commit from dashboard 2026-07-10 15:12:22 +00:00
7b44749e5a fix(newsletter): elimină jobul APScheduler intern duplicat și bugged
Jobul intern rula pe zile greșite (vineri/sâmbătă/marți în loc de
joi/vineri/luni) din cauza convenției diferite de day-of-week dintre
APScheduler și crontab standard. Scriptul standalone via OS crontab
e mecanismul corect și funcțional — rămâne singura sursă.

chore: sync stare working-tree — cron, KB notes, anaf-monitor snapshots
2026-07-09 14:54:15 +00:00
d175d5ba5a chore: working-tree state — anaf snapshots, cron state, KB notes, tools
Pre-existing uncommitted changes swept in with the STT work:
anaf-monitor snapshots/versions, cron job + newsletter state, 9 youtube KB
notes, tools/ocr_bon.py, and tools/tts.py.

Note: the tts.py change breaks 2 truncation tests in test_voice_normalize.py
(sanitize word-count) — flagged for a separate follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-27 18:16:31 +00:00
ce273d14db feat(voice): improve Romanian STT — hallucination gate + finetuned model
Gemma 4 cloud audio was infeasible (31b-cloud has no audio; E4B broken
upstream, no deploy host), so improve faster-whisper instead.

- Pin temperature=0.0 to disable the fallback ladder that re-decoded unclear
  audio up to 6x (source of the 16-24s latency outliers); reject hallucinated
  segments via avg_logprob/compression_ratio in the new pure _filter_segments.
- Adopt mikr/whisper-small-ro-cv11 (CT2 int8) via configurable voice.stt_model:
  spike showed WER 24%->10%, numbers fixed at source, +0.33s p50 (in budget).
- Add tools/voice_stt_mine.py (log mining) + tools/voice_stt_spike.py (model
  eval with diacritic scoring) + tests for the gate and miner.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-27 18:16:16 +00:00
5c9748ffb4 feat(memory): hybrid retrieval — navigation index.md + RAG hardening
Expose a navigation layer to the agent and harden RAG, after analyzing the
OKF note and testing on the real KB.

- memory_search.search(): dedupe best-chunk-per-file (a relevant note can no
  longer be buried by another file's chunks) + keyword fallback tagged
  degraded:True when Ollama is unreachable (no more hard crash).
- update_notes_index.py: emit per-folder index.md + root router; prune empty
  folders; fix latent subcategory->project bug.
- Exclude generated index.md from RAG rglob (reindex/incremental) + indexer
  scans + heartbeat freshness check (prevents self-pollution / reindex thrash).
- CLAUDE.md: reframe memory as hybrid (navigation first, RAG for fuzzy recall).
- Delete stale orphan kb/youtube/index.json; correct the OKF source note.
- Tests: dedup, keyword fallback, index.md exclusion. Plan + review in docs/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:52:27 +00:00
6e9dfd137c feat: youtube_subs + dashboard includ descrierea video ca index
- tools/youtube_subs.py: get_subtitles() returneaza acum (title, desc, transcript).
  Functii noi is_description_about_video() si extract_relevant_description()
  detecteaza daca descrierea contine capitole/timestamps (nu doar promotie autori)
  si curata trailing-urile promotionale inainte sa includa descrierea in output.
- dashboard/handlers/youtube.py: aceleasi functii adaugate; nota KB generata
  include acum un bloc "Descriere / Index" daca descrierea e relevanta pentru video.
- memory/kb/youtube: nota Jeremy Grantham (AI bubble, investitii, toxicitate)
  cu descrierea ca index de capitole.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 17:00:59 +00:00
735b282179 automatic 2026-05-29 13:35:15 +00:00
c401204fa2 fix(email): accept forwarded emails regardless of original sender
Gmail preserves the original sender when forwarding — whitelist check
was blocking all Fwd: emails not from mmarius28@gmail.com.
echo@romfast.ro is private, so any Fwd: arriving there is from Marius.
Also strip ***SPAM*** prefix from slugs for cleaner filenames.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 13:28:47 +00:00
0ce8a5a04d Update cron, dashboard, root +3 more (+1 ~11) 2026-05-28 20:21:28 +00:00
4be70440e8 feat(voice): DAVE E2E + full voice UX (squash of voice/dave-recv)
Squashed branch: voice/dave-recv → master. Closes Pas 12 (DAVE E2E) and lands
voice-mode UX polish + verbal voice control on top of the Pas 1-10 scaffolding
already on master.

## DAVE E2E receive-side decrypt (e4f3177)

Vendored fork: discord-ext-voice-recv 0.5.3a+echo.dave1. Patches the receive
pipeline to handle Discord's mandatory DAVE encryption on voice gateway v=8.
- `_maybe_dave_decrypt`: uses davey.can_passthrough(user_id) as primary gate,
  falls through to dave.decrypt for DAVE-epoch peers, drops on decrypt failure
  without killing the reader thread.
- VAD fix: silero-vad v5+ requires exactly 512 samples; our 100ms window
  (1600 samples) was silently raising ValueError → STT never fired. Now slice
  into 512-sample chunks.
- Whisper: bumped beam_size 1→5 and added RO initial_prompt.
- Tests: 11 DAVE unit tests + 2 callback integration tests + contract test
  with fork-version guard.

## Voice UX polish (d1bc77e)

- Killed the 3s "mă gândesc" filler (always collided with Claude p50 4-7s).
- Barge-in via `ttsq.clear()` at top of `on_segment_done`.
- DTX silence-flush poller (200ms tick) — Discord stops sending RTP packets
  when silent, so the inline silence-check in sink.write() never fired for
  trailing audio; background thread handles it.
- `EchoStreamingAudioSource.read()` non-blocking — old `get_frame(timeout=0.1)`
  wrecked Discord's 20ms cadence and the client interpreted bursts as
  stuttering (Marius heard "4 de minute" instead of full sentence).
- RO time expansion: 23:09 → "douăzeci și trei și nouă minute".
- Supertonic Unicode sanitize centralized in tools/tts.py.
- Whisper local_files_only=True — no HF metadata GET on each startup.
- Diagnostic logging through sink → VAD → Claude stream → TTS chain.

## Voice mode iteration (e589e48)

- `personality/VOICE_MODE.md` — voice-tailored system prompt (short, no
  markdown, no abbreviations, time without seconds, distances in
  "mii"/"milioane"); plumbed via build_system_prompt(voice_mode=True).
- Isolated voice session key `voice:<channel_id>` — voice doesn't share
  context with text adapter on the same channel; auto-applied without
  /clear ceremony. /clear drops both keys.
- Metric units + Romanian thousands (normalize.py): "384.000 km" →
  "trei sute optzeci și patru de mii de kilometri" with feminine-correct
  pluralization and "de" particle for ≥20.
- `/voice setvoice <M1-F5>` slash command with native autocomplete; swaps
  live + persists voice.default_voice to config.json.
- Verbal voice change (src/voice/voice_commands.py + 29 tests) — "schimbă
  vocea pe M5", "voce em cinci", with permissive substring fallback for
  Whisper-mangled forms like "Mâcinci"=M5 and "unul cinci"=M5. Whisper
  initial_prompt now lists voice vocabulary to bias STT toward clean
  outputs.
- Fast barge-in: VAD ≥2 consecutive windows (~200ms) on Marius's user
  while Echo has pending TTS frames → cut him off mid-sentence so user
  doesn't wait the full silence + STT cycle. Acoustic echo bleed-through
  still requires headphones (no AEC).

## Test suite

130 voice + router tests pass (test_voice_recv_dave, test_voice_session_cleanup,
test_voice_adapter_contract, test_voice_normalize, test_voice_commands,
test_router).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 21:00:27 +00:00
af5af8133f feat(voice): Pas 2 — install voice deps, vendor discord-ext-voice-recv, setup assets
Foundation pentru Discord voice-to-voice pipeline.

- requirements.txt: faster-whisper, silero-vad, num2words, numpy, PyNaCl
- vendor/discord-ext-voice-recv/: vendored la commit ac04ea7b09 (bump version
  0.5.3a) — Discord voice protocol fragil, upstream hobby fork. Adapter layer
  in src/voice/_discord_voice_adapter.py izolează churn (swap la py-cord =
  doar acel fișier rescris). VENDOR_INFO.md documentează update procedure.
- tools/voice_setup.py: idempotent setup script — libopus check, ffmpeg
  check, Supertonic reachable, faster-whisper/silero-vad warm, assets
  generation. Exit 0 = green, 1 = needs human (currently libopus missing
  needs `sudo apt install -y libopus0`).
- assets/voice/: thinking.wav (filler "Stai puțin să-mi adun gândurile",
  ~2.8s), mhm.wav (listener noise), beep_200ms.wav (wake-up tone 880Hz).
- src/voice/__init__.py: package stub.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:42:27 +00:00
c6d11bdf9f chore(voice): spike STT latency benchmark + HT contention lesson
Pas 1 (BLOCKING) din Discord voice-to-voice test plan. Sweet spot empiric
pe i7-6700T: faster-whisper small int8 @ cpu_threads=4 → p50 2.25s,
p95 2.64s, mean RTF 0.46. Curba HT: 2t=3.25s → 4t=2.25s (sweet) →
6t=2.79s (regres +24% prin contention). tiny respinge — halucinează RO.

- tools/voice_bench.py: harness benchmark cu 8 sample-uri RO sintetizate
  via Supertonic API, măsoară p50/p95/RTF pentru small+tiny pe N threads.
- tools/voice_bench_results*.json: raw output 3 pass-uri (threads 2/4/6).
- tasks/voice-bench-results*.md: summary markdown per pass.
- tasks/lessons.md: HT contention rule — cpu_threads = physical cores,
  rulează sweep nu single-point pentru ML inference compute-bound.

Budget updated în plan-uri: STT p50 1.5s → 2.5s, perceived 4s → 5s p50.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:52:11 +00:00
0d2d5b860d chore(tts): schimbă vocea default din M1 în M2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 05:56:55 +00:00
8fe39adc01 fix(tts): trimite lang=ro explicit la Supertonic API
Parametrul `lang` era definit (DEFAULT_LANG = "ro") dar nu era inclus
in request-ul HTTP catre /v1/audio/speech. Adaugat "lang": lang in
body-ul JSON si lang="ro" explicit in _tts_synthesize().

OpenAPI-ul Supertonic confirma ca /v1/audio/speech accepta `lang`
ca parametru optional (OpenAISpeechRequest schema).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 05:47:39 +00:00
3dd2ddbd6a chore: auto-commit from dashboard 2026-05-27 05:40:22 +00:00
2a05f7cf49 chore: auto-commit from dashboard 2026-05-26 21:09:55 +00:00
990be00b70 chore: auto-commit from dashboard 2026-05-20 22:28:39 +00:00
8cb76e130d chore: auto-commit from dashboard 2026-05-14 22:09:33 +00:00
3570d9a625 chore(kb): notițe youtube mai, fix email tools, update newsletter/anaf-monitor
Adaugă 4 notițe YouTube (llama.cpp, Mario Zechner, bonificatie impozit,
AI scaffolding) + notă coaching grok. Actualizează index KB.
Fix email_digest și email_forward. Update newsletter cercetasi + cron jobs.
ANAF monitor hashes/snapshots/versions la zi.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:02:55 +00:00
f04e033dbe fix(email): digest nu mai creează notițe KB — fetch direct IMAP
email_digest.py folosea save_unread_emails() care salva în memory/kb/emails/.
Notițele KB trebuie create DOAR de heartbeat. Acum digest-ul face fetch
direct din IMAP (ca email_forward.py), fără side effects pe KB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:17:18 +00:00
63b7fcd00e fix(email): include linkuri relevante în digest și TL;DR
Instrucțiunea era prea restrictivă (doar formulare/documente "acționabile").
Acum include orice URL relevant: articole, linkuri de citit, resurse.
Același comportament adăugat și în HEARTBEAT pentru TL;DR din KB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:14:20 +00:00
246986b5ae fix(email): afișează expeditorul și subiectul original la emailuri forwarded
La salvarea unui email forwardat, se extrage acum expeditorul original
din body și se elimină prefixul Fwd: din titlu — în loc de adresa lui Marius.
Corectat și fișierul deja salvat din 07 mai.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:12:42 +00:00
a5cab9677a chore: untrack runtime state files
approved-tasks.json, dashboard/status.json, anaf-monitor/monitor.log
are auto-modified by background processes (heartbeat, cron jobs, ANAF
monitor). Untracking them stops the noisy "auto-commit from dashboard"
churn. Files stay on disk; readers (router._load_approved_tasks etc.)
already handle missing files by returning empty defaults.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 07:48:55 +00:00
f4880a2a18 chore: auto-state + new KB notes
State files updated by dashboard/heartbeat/cron jobs, plus new KB
captures (samsung firmware todo, scout song reel, weekly youtube notes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 07:47:16 +00:00
d0faeed181 chore: auto-commit from dashboard 2026-04-30 17:01:55 +00:00
176dc01aa6 chore: auto-commit from dashboard 2026-04-29 16:04:28 +00:00
1462f98ae9 chore: auto-commit from dashboard 2026-04-28 10:46:30 +00:00
3e7818286b feat(ralph): rate limit budget tracking + whatsapp text-keywords
Task #1 — Rate limit budget tracking MVP:
- tools/ralph_usage.py: pure functions (extract_usage_entry, parse_usage_jsonl,
  aggregate_by_day/_project, filter_by_days, summarize) + CLI append/summarize
  subcommands. Atomic write via temp+rename.
- tools/ralph/ralph.sh: după fiecare claude -p, append usage entry
  derivat din JSON envelope la <project>/scripts/ralph/usage.jsonl. Best-effort,
  niciodată blochează rularea (|| true).
- dashboard/handlers/ralph.py: GET /api/ralph/usage[?days=N] aggregează cross-
  project și returnează {today_cost, today_runs, by_project, by_day, ...}.

Task #2 — WhatsApp text-keyword commands:
- src/router.py: helper _translate_whatsapp_text mapează "aprob"/"stop <slug>"/
  "stare [<slug>]" → /a, /k, /l. Apelat DOAR pe adapter whatsapp în
  _try_ralph_dispatch (Discord/TG nu sunt afectate). NU acoperim propose
  intentionat — descrierea liberă e prea fragilă pentru parsing text-only.

Tests: 49 noi (test_ralph_usage 28 + test_whatsapp_keywords 21) + 4 noi în
test_dashboard_ralph_endpoint pentru /api/ralph/usage. Toate trec; regression
suite (test_router, test_router_planning, test_dashboard_ralph_endpoint,
test_whatsapp) — 90/90 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:05:50 +00:00
655ed3ae09 feat(ralph): smart gates + DAG + dashboard live (W3)
Restructurare Ralph QC loop pe smart gate dispatcher tag-driven (în loc de
5 faze fixe), DAG dependsOn cu propagare blocked, retry guard 3-strike, rate
limit detection, plus dashboard live cu polling 5s.

Changes:
- tools/ralph_prd_generator.py: parametru optional final_plan_path; când e
  furnizat, invocă Claude Opus pe final-plan.md pentru extragere user stories
  cu schema extinsă (tags, dependsOn, acceptanceCriteria 3-5). Backward compat
  păstrat — fără final_plan_path, fallback la heuristic-ul vechi.
- tools/ralph/prd-template.json: schema W3 (tags[], dependsOn[], retries,
  failed, blocked, failureReason, requiresDesignReview).
- tools/ralph/prompt.md: 4 faze (impl, base quality, smart gates, commit) +
  dispatcher pe story.tags. Tags vide → run-all-gates fallback (safe default).
- tools/ralph_dag.py (nou): tag validation heuristic anti-silent-regression
  (force ui dacă diff atinge .vue/.tsx/.html/.css/.scss; force db pentru
  migrations sau .sql; force vercel dacă există vercel.json) + topological
  sort cu blocked propagation + atomic prd.json updates.
- tools/ralph/ralph.sh: --max-turns 30, DAG-aware story selection, retry
  counter cu auto-fail la 3, rate limit detection (sleep 30min + 1 retry),
  CLI subcommands prin tools/ralph_dag.py helper.
- dashboard/handlers/ralph.py (nou): /api/ralph/status + /<slug>/log + /prd
  + /stop. Defensive vs corrupt prd.json. Sandbox-ed PID kill.
- dashboard/ralph.html (nou): live cards 3/2/1 col responsive, polling 5s,
  drawer pentru log/PRD viewer, status colors (--status-running/blocked/
  failed/complete declarate inline), Lucide icons cu aria-labels.
- dashboard/api.py: mount /api/ralph/* (GET status/log/prd, POST stop).
- tests/: 72 teste noi (smart gates, DAG, retry, dashboard endpoint).

Note arhitecturale:
- Polling 5s ales peste SSE/WebSocket (suficient pentru iter Ralph 8-15min)
- Tag validation rulează POST-iter pe diff git pentru anti-silent-regression
- Rate limit retry: 1 dată per rulare, apoi mark failed=rate_limited

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 18:36:35 +00:00
90c2a90b5e feat(ralph): add autonomous project execution system
- router.py: add !approve, !status, !stop, !propose commands for project lifecycle management
- approved-tasks.json: coordination schema for evening→night→morning pipeline
- tools/ralph/: ralph.sh loop, prompt.md, prd-template.json
- cron/jobs.json: enable morning-report, evening-report, night-execute (23:00 opus)

Evening-report proposes features to approved-tasks.json as 'pending'; Marius
approves via !approve; night-execute launches ralph.sh per project.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 15:20:52 +00:00
e4674b5dda chore: auto-commit from dashboard 2026-04-26 08:06:52 +00:00
f885d75528 chore: auto-commit from dashboard 2026-04-25 21:42:42 +00:00
f9a091133a chore: auto-commit from dashboard 2026-04-24 16:31:39 +00:00
abadff4ea8 chore: auto-commit from dashboard 2026-04-24 10:34:19 +00:00
1b2b37a6bb chore: auto-commit from dashboard 2026-04-23 21:24:43 +00:00
b9a5f733c2 chore: auto-commit from dashboard 2026-04-23 09:44:20 +00:00
42797c0bbb chore: auto-commit from dashboard 2026-04-22 20:54:11 +00:00