# Outbox — persian

Write progress, results, scoping docs here for other agents and Cameron to pick up.

---

## 2026-06-22 — current-state assessment + priority order (bootstrap pass)

Read the full scaffold (README, app.py, tutor_system.md, chat.html/js/css), checked
the running process, and exercised `/persian/api/ask` end-to-end against both
localhost:5001 and the public omidlab.net proxy.

### What works
- **App is live and healthy** — running (PID 1396845, nohup'd `app.py`), `/persian/health`
  returns `{ok:true, voice_set:true}` both locally and at `https://omidlab.net/persian/health`.
  Reverse proxy through `serve.py` is solid.
- **Tutor quality is genuinely good.** "How do I say good morning" → correct structured
  response (صبح بخیر / sobh bekheyr / Good morning) with a *useful* Notes line about the
  poetic reply *sobh sefid*. The system prompt's structured-vs-freeform split parses cleanly.
- **Finglish transliteration** matches the informal Iranian convention as intended.
- **Audio caching** by content-hash works; fresh generations play fine.
- **UI is clean** — RTL Persian rendering, tap-to-breakdown, markdown for freeform answers,
  dark theme. No obvious layout breakage.

### What I fixed today (in-scope, low-risk, verified)
- **Bug: cached audio 404'd on the public site.** `elevenlabs_tts()` returned
  `/static/audio/<hash>.mp3` (no `/persian` prefix) on a **cache hit**, but
  `/persian/static/audio/...` on a cache **miss**. Behind the proxy the prefix-less URL
  resolves to `omidlab.net/static/...` → **404**. Net effect: the *second* time you ask any
  phrase, the Play button silently did nothing in production. Worked locally only because
  Flask serves `/static` directly on :5001, masking it.
  Fixed `app.py:77` to return the `/persian`-prefixed URL. Verified: cache-hit URL now 200s
  publicly. (This means many of your earlier "Play does nothing" moments, if any, were this.)

### What's brittle / risks
1. **Process persistence** — nohup'd, dies on reboot (ROLE TODO #2). Also running with Flask
   `debug=True` + reloader in production — fine for now, but it's the dev server and auto-restarts
   on file edits (handy for me, not ideal for prod stability).
2. **No auth** — anyone with the URL spends your Claude + ElevenLabs quota (TODO #4).
3. **Swallowed audio errors** — `api_ask` catches all exceptions into `audio_error` and returns
   200. Against fleet "fail loud" guidance, but here it's a deliberate UX choice (text answer
   should survive a TTS hiccup). Leaving as-is; flagging the trade-off.
4. **Voice** — currently George (English-accented, free tier). Persian ~70% native. Waiting on
   your ElevenLabs upgrade to swap to Nazy Entezari (TODO #1).
5. **No persistence of conversation/vocab** — history is browser-session only; nothing saved.

### My recommended priority order
1. **Process persistence (TODO #2)** — highest reliability win, and a prerequisite for
   everything proactive. Recommend a **tmux pane in the `agents` session** (mirrors how
   `serve.py` runs, low ceremony, you can see it) over systemd, unless you want true
   boot-survival → then systemd. Quick call from you. Also switch off Flask debug for prod.
2. **Voice upgrade (TODO #1)** — biggest *felt* quality jump, gated only on your $5/mo upgrade.
   Ready to swap + clear cache the moment you say go.
3. **Auth (TODO #4)** — cheap, stops quota free-riding. Mirror the notes-page header pattern.
4. **Vocab tracking + SRS (TODO #3)** — first real feature; the foundation for daily review
   and the proactive killer-feature. Start with the SQLite store + "save to vocab" button.
5. **Glasses integration (TODO #5)** + **proactive mode (TODO #6)** — the north-star, but
   depends on #4 and coordination with the `glasses` agent. Later.
6. Lessons mode (#7), Khanega ingestion (#8) — further out.

### Open questions for Cameron
- **Persistence:** tmux pane (like serve.py) or systemd unit? (I lean tmux for now.)
- **ElevenLabs:** upgraded yet? If so I'll swap to Nazy + clear the cache immediately.
- **Auth:** want me to add the simple shared-password header now, or hold off while it's just you?
- Couldn't find the two life_manager memories referenced in ROLE.md
  (`feedback_brandon_practice_familiarity.md`, `user_company_vision.md`) — not in
  `-data-cameron-life/memory/`. Non-blocking, but if there's context there I should have,
  point me at the right path.

---

## 2026-06-22 — actions taken (post manager context drop)

Manager confirmed host = VPS omid-fleet (Hetzner CCX33, 7.6GB RAM, 0 swap), with a recent
OOM event; debug=False + tmux pane endorsed. Acted on that:

- **Migrated to persistent service window.** App now runs in a dedicated **`srv_persian`**
  tmux window in the `agents` session (mirrors `serve.py`/`srv_omidlab`), launched via
  `bash run.sh 2>&1 | tee app.log`. Killed the old nohup'd processes first to relieve memory
  pressure (also collapsed the debug reloader's extra processes).
- **debug=True → debug=False** in `app.py` (prod-appropriate, single process, lower footprint).
- **Fixed `run.sh`** — it sourced `.env` but launched system `python3` (no anthropic/flask).
  Now `exec`s `./.venv/bin/python app.py`. This is now the canonical, reproducible launcher.
- **Verified after migration:** local + public `/persian/health` green; full ask→cached-audio
  round-trip 200 publicly; all other omidlab routes (`/`, `/life`, `/notes`, `/567_project`,
  `/meeting_notes`) still 200 (serve.py untouched).
- **ROLE.md updated:** dropped the two non-existent life_manager memory refs (per manager);
  rewrote the Process section to describe the `srv_persian` tmux setup + debug=False.

**Still open:**
- **Reboot survival** — tmux window survives shell-exit and agent-proc OOM, but NOT a full
  reboot. systemd unit deferred (manager confirmed tmux is right for now). Revisit if reboots
  become common.
- **No git backup on this VPS** — `agents_stuff`/`life` are not git repos here, so I could not
  commit the ROLE/app changes. Tracked under the fleet migration (`project_migrate_offlab_pending`).
  Changes are on disk; flagging so they get captured when the repo lands.
- Voice upgrade (#1) + auth (#4) still awaiting Cameron.

---

## 2026-06-23 — chat persistence + Lessons feature shipped

**Chat persistence across refreshes** (Cameron's request): transcript + Claude context
now stored in `localStorage` (`chat.js`), replayed on load. Added a "Clear" button in the
header. Capped context sent to Claude to last ~30 turns so cost stays bounded as history grows.
Enabled `TEMPLATES_AUTO_RELOAD` so template edits don't need a restart with debug off.

**Voice question** — diagnosed precisely: current George voice is English-trained (premade),
hence the non-Persian accent. Confirmed via the ElevenLabs API that **free tier hard-blocks
shared-library (Persian-native) voices** — synthesis returns 402 `paid_plan_required`. So the
Nazy swap genuinely needs the $5/mo Starter upgrade; no free workaround inside ElevenLabs.
Sent Cameron the upgrade link (https://elevenlabs.io/app/subscription). Ready to swap on his go.

**Lessons mode (v1) shipped** — 11 curated conversational lessons, 132 phrases, transliteration-
first + casual register (per Cameron: speaking fluency, no alphabet/formal/writing):
greetings, how-are-you, taarof, family, endearments, food-guest, making-plans, feelings-wants,
everyday-verbs, reactions-fillers, out-about. Each lesson = clickable card → detail view with
phrase rows (translit big, English, small Persian, ▶ audio), a sample dialogue, and an
"ask about this lesson" box that reuses the tutor with lesson context.
- New files: `lessons/specs.json` (curated), `gen_lessons.py` (offline Claude authoring →
  `lessons/data/*.json`), `lessons.py` (read-only store), `templates/lessons.html`, `static/lessons.js`.
- New routes: `/persian/lessons`, `/api/lessons`, `/api/lessons/<id>`, `/api/tts`.
- Content is regenerable: `./.venv/bin/python gen_lessons.py [--force] [ids...]`.
- Lesson audio is on-demand → it'll automatically use the new voice once the upgrade+swap+cache-clear happens.
- Verified: 11/11 lessons load, page + all routes 200 locally and via public proxy.

Vault slice (`vault/fleet/agents/persian/`) updated: overview, tasks, memory.

**Open for Cameron:** (1) upgrade ElevenLabs → I swap to Nazy; (2) review the lessons, tell me
which topics to drop/add or where the level's off; (3) auth still TBD.
