feat(portal): live daemon status + recent-turns panel#59
Merged
Conversation
The portal was a pure config editor — you couldn't tell from it whether 'channels start' was actually running or what it had done. Now the daemon writes a heartbeat + a bounded ring of content-free turn metrics under <config_dir>/status/, and the portal shows a live 'Daemon running/stopped + recent turns' card (polls every 5s). - channels/status.py: StatusStore (atomic heartbeat write/read + is_running via 60s-stale window; record_turn ring capped at 50; read_turns newest-first). - observability.py: optional, failure-isolated set_turn_sink() called after log_turn (no sink = turn path unchanged). - channels start: writes run.json, mirrors each TurnEvent into the ring, 20s heartbeat task, clears run.json on clean exit. - portal: status_overview() + GET /api/status; #status card (running dot, uptime, recent turns as adapter/instance/outcome/latency/chars — NEVER message content). 8 new tests; full suite 498 pass/1 skip. Playwright-verified card renders running + 4 turns from a seeded heartbeat. Adversarial (Explore subagent): VERDICT CLEAN (heartbeat lifecycle, atomic writes, single-writer race, privacy content-free, sink failure-isolated).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Portal 之前是纯配置编辑器 —— 你从界面上看不出
channels start到底跑没跑、跑了之后做了啥。现在守护进程会把心跳和一圈内容无关的 turn 指标写到<config_dir>/status/,portal 顶部显示一张实时 「Daemon running / stopped + 最近对话」 卡(每 5s 轮询)。channels/status.py:StatusStore—— 原子心跳 write/read +is_running()(60s 陈旧窗口判活)+record_turn()环(封顶 50 条)+read_turns()(最新在前)。observability.py:可选、故障隔离的set_turn_sink(),在log_turn之后调用(不设 sink 时 turn 路径字节不变)。channels start:写run.json,把每个TurnEvent镜像进环,20s 心跳任务,干净退出时清run.json。status_overview()+GET /api/status;#status卡(运行绿点、运行时长、最近 turn 显示为adapter/instance · outcome · latency · chars—— 永远不含消息内容)。Testing
ruffclean;全套件 498 pass / 1 skip(3 个 pre-existing Windows-only fail 无关)。status_overviewrunning/stopped、/api/status路由、observability sink 注册+故障隔离、静态守卫。Daemon running — 2 channels · up 2m 56s+ 4 条 turn(telegram/demo-tg · claude · ok · 2100ms · 120c等)。🤖 对抗评审
Reviewer: Claude
general-purposesubagent(此机无 Codex),read-only,一轮。VERDICT: CLEAN:hb_task在try前定义,finally里必被 cancel+await(无孤儿任务);write_run异常被 suppress,循环存活。set_turn_sink全局态 + 执行上下文 —log_turn在 asyncio loop 内被调,record_turn的 ≤50 行文件 IO <1ms,相对 provider 延迟可忽略;sink 异常在log_turn内捕获,绝不打断 turn;测试都finally清 sink。tempfile+os.replace原子,读端不会读到撕裂文件;单 event loop 写者无交错;lines[-max_turns:]封顶,无无限增长。run.json;硬杀留下的心跳 >60s 判 stopped;心跳 20s vs 60s 窗口 3× 余量。run.json/缺字段全类型守卫 → 默认 stopped;read_turns跳坏行;status/不存在 → stopped;turn 字段经el(...)文本节点渲染,无 XSS。无
RISK,无需 fix/rebut。