chat: replace triage backend with chat singleton master
Third of three sub-lodes for the chat backend rewrite (parent plan: chat-refactor). The big flip: new `/api/chat` singleton backend replaces `/api/triage`, and every legacy chat path is removed. Backend (new) - `convey/chat.py` is the singleton chat runtime and endpoint surface. Single-process Flask worker assumption is stated in a top-of-file comment. Module-level `threading.Lock` guards the single-slot chat generate. `start_chat_runtime(app)` wires callosum cortex/finish + cortex/error subscriptions and performs the idempotent crash- recovery scan on boot. - Endpoints: POST /api/chat (append owner_message, schedule generate), GET /api/chat/session, GET /api/chat/stream/<day>, GET /api/chat/result/<use_id>. Chat stream is the queue; no separate in-memory queue exists. Source of truth for all responses is the stream itself, reduced on the fly — never the exec talent log. - Active-exec cap = 2 (3rd request fires a remediation chat generate with the spec literal "max active — waiting for one to finish"). Loop cap = 3 consecutive exec cycles without an owner_message (then append chat_error with "chat had trouble — try again"). Any owner_message resets the loop counter. - `convey/utils.spawn_agent()` and `think/cortex_client.cortex_request()` now accept an optional caller-supplied `use_id`. Default keeps the auto-allocation path. - Exec dispatch prompt is assembled inline in chat.py per spec E2 (task + context hints + location + last 6 chat turns; no digest). `spawn_agent(name="exec", ...)` — never "unified". Cleanup - Deleted `convey/triage.py` (renamed to `convey/chat.py`), `apps/home/events.py`, `think/conversation.py`, `talent/conversation_memory.py`, `talent/triage.md`, `tests/test_conversation.py`, `tests/test_home_events.py`. - Deleted the `_resolve_talent_path` `unified` alias branch and the transient `_UNDISCOVERED_SYSTEM_TALENTS` narrowing that 2a had added. - Removed `think/cortex.py`'s `TRIAGE_AGENT_NAMES`-based display decoration. `convey/chat.py` now produces the `display` field for chat-flow finish events directly. - `think/awareness.py::compute_thickness()` previously pulled recent exchanges from the deleted `think.conversation`; it now reads the chat stream via a local helper. - `convey/templates/app.html` — minimal URL swap from `/api/triage` to `/api/chat` with recovery-shape adjustment. Full UI rewrite is lode 3. Tests - New: `test_chat_runtime.py` (cap logic, crash recovery, cortex correlation), `test_convey_chat.py` (endpoint contracts), `test_no_legacy_chat_imports.py` (regression gate for the deleted symbols and "unified" string), plus the chat-turn FTS5 coverage case in `test_journal_index.py`. - `conftest.py` has an autouse cleanup that resets the module-level chat runtime between tests. - `make ci`: 3740 passed, 4 skipped (pre-existing sandbox-only plus the 3 search/graph harness skips from 2a). `make verify-api`: 51 endpoints green. `make review` browser phase blocked on pinchtab startup — infrastructure issue unrelated to 2c. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>