diff --git a/docs/superpowers/specs/2026-03-22-codex-hud-design.md b/docs/superpowers/specs/2026-03-22-codex-hud-design.md index 33007d9..c7c7e0d 100644 --- a/docs/superpowers/specs/2026-03-22-codex-hud-design.md +++ b/docs/superpowers/specs/2026-03-22-codex-hud-design.md @@ -356,7 +356,128 @@ inline 初始化失败 --> 自动降级到 passthrough mode - install 必须可逆(one command uninstall) - shell rc 修改使用标记块,安装幂等 -## 10. Confidence & Risk Assessment +## 10. Pre-Implementation Spikes + +> 这三个 spike 必须在写 implementation plan 之前完成。每个 spike 是独立的小脚本或手工验证,目标是锁定架构假设,不是实现功能。 + +--- + +### Spike 1: Pane Mode Capability + +**目标:** 确认 tmux/zellij 下可靠分屏、在主 pane 跑真实 codex、退出后自动清理 HUD pane。 + +**验证方法:** +```bash +# 在 tmux session 内运行: +# 1. 用 display-message 探测 session 可操作性 +tmux display-message -p '#{session_id}' + +# 2. split 出 4 行底部 pane,在里面跑 watch 模拟 HUD +tmux split-window -v -l 4 'watch -n 0.5 date' + +# 3. 在主 pane 跑真实 codex(或任意 full-screen TUI,如 vim) +codex + +# 4. 退出 codex,验证 HUD pane 是否自动关闭 +# 用 tmux kill-pane -t 从代码层面关闭 +``` + +**通过标准:** +- `display-message` 在正常 session 下返回有效 session_id,在嵌套/只读 session 下可检测失败 +- split pane 成功,HUD pane 内容独立于主 pane,主 pane codex TUI 不受干扰 +- codex 退出后,`tmux kill-pane` 能可靠关闭 HUD pane,不留残留 + +**失败后的降级决策:** +- 探测不可靠 → pane mode 进入条件收紧,宁可误降级到 inline 也不误进 pane +- kill-pane 不可靠 → pane 生命周期改为"codex 退出时发送信号给 HUD 子进程,HUD 进程自行退出" + +--- + +### Spike 2: Data Source Stability + +**目标:** 确认 `state_5.sqlite` 和 `sessions/*.jsonl` 哪些字段在活动会话里稳定更新,哪些不可依赖。 + +**验证方法:** +```bash +# 1. 跑一个真实 codex session,同时在另一窗口执行: + +# SQLite:每 2 秒看 threads 表最新行 +watch -n 2 'sqlite3 ~/.codex/state_5.sqlite \ + "SELECT model, cwd, git_branch, tokens_used, updated_at \ + FROM threads ORDER BY updated_at DESC LIMIT 3"' + +# SQLite:看 jobs/agent_jobs 表是否有更新 +sqlite3 ~/.codex/state_5.sqlite ".tables" +sqlite3 ~/.codex/state_5.sqlite "SELECT * FROM jobs LIMIT 5" + +# JSONL:tail 最新 session 文件,观察 type 字段分布 +tail -f ~/.codex/sessions/$(date +%Y/%m/%d)/rollout-*.jsonl | \ + python3 -c 'import sys,json; [print(json.loads(l).get("type","?")) for l in sys.stdin]' +``` + +**通过标准(按字段逐一确认):** + +| 字段 | 预期来源 | 通过标准 | +|------|----------|----------| +| `threads.model` | SQLite | session 开始时写入,不频繁变化 | +| `threads.cwd` | SQLite | 准确反映当前工作目录 | +| `threads.git_branch` | SQLite | 与 `git branch --show-current` 一致 | +| `threads.tokens_used` | SQLite | 随对话进行递增 | +| `threads.updated_at` | SQLite | 每轮对话后更新 | +| JSONL `type` 字段 | JSONL | 每个事件都有 type,且值稳定可枚举 | +| JSONL tool/task 事件 | JSONL | 至少能识别 tool 调用开始/结束、task 进度 | + +**失败后的降级决策:** +- `tokens_used` 不更新 → HUD 不显示 tokens,标 n/a +- JSONL 事件名不稳定 → normalize 层对所有未知 type 返回 null,HUD 对应字段降为 n/a +- `git_branch` 不准确 → 改为本地 `git branch --show-current` 补充 + +--- + +### Spike 3: Inline Fallback Safety + +**目标:** 确认 `codex --no-alt-screen` 下能预留底部区域,且 wrapper crash/exit 后不会弄脏终端。 + +**验证方法:** +```bash +# 1. 确认 --no-alt-screen 模式下 codex 以滚动方式输出 +codex --no-alt-screen + +# 2. 用小脚本模拟 scroll region + 底部 HUD,然后正常退出和异常退出各跑一次 +node -e " +process.stdout.write('\x1b[s'); // save cursor +process.stdout.write('\x1b[1;' + (process.stdout.rows - 4) + 'r'); // scroll region +process.stdout.write('\x1b[' + (process.stdout.rows - 3) + ';1H'); // move to HUD area +process.stdout.write('=== HUD TEST LINE ==='); +process.stdout.write('\x1b[u'); // restore cursor +setTimeout(() => { + // cleanup + process.stdout.write('\x1b[r'); // reset scroll region + process.stdout.write('\x1b[' + process.stdout.rows + ';1H\x1b[2K'); // clear HUD + console.log('cleaned up'); +}, 3000); +" + +# 3. 验证 Ctrl-C 后终端是否正常(光标可见,滚动恢复) +# 4. 在不同终端宽度下测试(80, 120, 200 列) +``` + +**通过标准:** +- scroll region 设置后,主内容区域可滚动,底部 4 行固定不动 +- 正常退出后终端恢复原始状态(光标可见,scroll region 清除) +- Ctrl-C / kill 后终端不残留 HUD 内容,不留隐藏光标 +- macOS Terminal.app + iTerm2 + Ghostty 下行为一致(至少两种实测) + +**失败后的降级决策:** +- scroll region 在任一目标终端不稳定 → inline mode 标记为"实验性",默认不启用,需 `--inline` flag 显式开启 +- cleanup 不可靠 → cleanup 逻辑改为双重保险:正常路径 + `process.on('exit')` 兜底 + +--- + +**三个 spike 全部通过后,才进入 implementation plan。** +每个 spike 的结论(通过 / 部分通过 + 条件 / 失败 + 降级)应记录在 implementation plan 的前置说明中。 + +## 11. Confidence & Risk Assessment ### 10.1 已确认(可直接依赖)