Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions PR_BODY_BILINGUAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Multi-Tab & Cross-Tab Collaboration System
# 多标签与跨标签协作系统

## 🎯 Summary 摘要

完整的 9 标签多主 Agent 协作系统,将单个 TUI 窗口从单对话扩展为多 Agent 协作环境。这是 CodeWhale TUI 历史上最大的功能增强之一。

Complete 9-tab multi-agent collaboration system that transforms a single TUI window from single-conversation to multi-agent collaboration environment. This is one of the largest feature enhancements in CodeWhale TUI history.

---

## ✨ New Features 新功能

### Phase 1: Multi-Tab System 多标签系统
- 最多 9 个并发标签页(Chat / Delegation / Review / Meeting 4 种类型) / Up to 9 concurrent tabs (4 types: Chat/Delegation/Review/Meeting)
- 标签栏顶部可视化(2+ 标签时显示,组颜色背景) / Top tab bar visualization (shown when 2+ tabs, group color background)
- 完整快捷键(Ctrl+1-9, Ctrl+Tab, Ctrl+Shift+N/W, Ctrl+\`) / Complete keyboard shortcuts
- 智能 @ 提及解析(`@Tab2` 自动切到该 tab) / Smart @-mention parsing (auto-switch to referenced tab)
- 持久化(`~/.codewhale/tabs.json`,原子写入) / Persistence (atomic write to ~/.codewhale/tabs.json)
- VecDeque 性能优化(O(1) 移除,256 边界保护) / VecDeque perf optimization (O(1) removal, 256 boundary)

### Phase 2: Cross-Tab Collaboration 跨标签协作
- 任务委托(4 优先级:Low/Normal/High/Urgent,真实流转) / Task delegation (4 priorities, real data flow)
- 跨标签审查(ReviewRequest 事件) / Cross-tab review (ReviewRequest events)
- 会议模式(3-pane MeetingView,6 种消息类型) / Meeting mode (3-pane MeetingView, 6 message types)
- 上下文共享(SharedContext 同步) / Context sharing (SharedContext sync)
- 右键菜单集成(4 种协作入口) / Right-click menu integration (4 collaboration entries)
- TabPickerView 选择目标 tab / TabPickerView for target selection

### Phase 3: Tab Groups 标签分组
- 8 种颜色分组(Red/Orange/Yellow/Green/Cyan/Blue/Magenta/Gray) / 8 color groups
- 标签栏显示组颜色标签 `⟨Bl⟩` / Group color tag shown in tab bar
- 活动标签使用组颜色作为背景 / Active tab uses group color as background
- Cycle 切换组 / Cycle through groups
- 分组也持久化 / Group assignments persisted

---

## 📊 Metrics 指标

| 指标 / Metric | 数值 / Value |
|------|------|
| **新模块 / New modules** | 11 个 / 11 |
| **总测试 / Total tests** | 70+ (44 单元 / unit + 9 e2e 渲染 / render + 7 性能基准 / benchmarks + 14 键盘 e2e / keyboard e2e) |
| **代码增量 / Code delta** | +5,000 行 / lines |
| **文档 / Documentation** | 3 份 (KEYBINDINGS/ARCHITECTURE/TROUBLESHOOTING) |
| **编译警告 / Compile warnings** | 仅遗留 (multi_agent 旧模块) / Legacy only (multi_agent old module) |

---

## 🚀 Performance Benchmarks 性能基准

```
[bench] create 9 tabs 148µs (16µs/op) 创建 9 个标签
[bench] 1000 tab switches (next) 188µs (188ns/op) 1000 次标签切换
[bench] 1000 delegations 357µs (356ns/op) 1000 次任务委托
[bench] drain 100 priority-sorted tasks 152µs (1.5µs/op) 排出 100 个优先级任务
[bench] 9 tabs + 20 delegations persistence snap=61µs ser=452µs de=483µs 持久化
[bench] 9 group lookups 24µs (2.7µs/op) 9 次组查找
```

---

## 🔑 New Keyboard Shortcuts 新快捷键

| Chord 快捷键 | Action 动作 |
|-------|--------|
| `Ctrl+\`` | Open tab switcher overlay / 打开标签切换器 |
| `Ctrl+1..9` | Switch to tab N / 切换到第 N 个标签 |
| `Ctrl+Tab` | Next tab / 下一个标签 |
| `Ctrl+Shift+Tab` | Previous tab / 上一个标签 |
| `Ctrl+Shift+N` | New tab / 新建标签 |
| `Ctrl+Shift+W` | Close current tab / 关闭当前标签 |
| `Ctrl+Shift+D` | Process pending delegation / 处理待办委托 |

---

## 📁 Files Changed 文件变更 (Highlights 摘要)

### New modules 新模块
- `crates/tui/src/tui/tab/` - Multi-tab system 多标签系统 (8 files)
- `crates/tui/src/tui/views/tab_switcher.rs` - Switcher overlay 切换器
- `crates/tui/src/tui/views/tab_picker.rs` - Target picker 目标选择器
- `crates/tui/src/tui/views/meeting_view.rs` - Meeting modal 会议视图

### New docs 新文档
- `docs/TROUBLESHOOTING.md` - 8 common issues + solutions / 8 个常见问题+解决方案

### Modified 修改
- `crates/tui/src/tui/app.rs` - TabManager integration
- `crates/tui/src/tui/ui.rs` - Layout, shortcuts, dispatch hook / 布局、快捷键、分发钩子
- `crates/tui/src/tui/mouse_ui.rs` - Context menu collaboration / 右键菜单协作
- `crates/tui/src/tui/views/mod.rs` - New ViewEvent variants / 新 ViewEvent 变体
- `docs/KEYBINDINGS.md` - All new shortcuts / 完整快捷键
- `docs/ARCHITECTURE.md` - Multi-tab system section / 多标签系统章节

---

## 🧪 Test Plan 测试计划

- [x] `cargo check` - 0 errors / 0 错误
- [x] `cargo test tui::tab` - 70+ tests pass / 70+ 测试通过
- [x] `cargo test tui::tab::render_tests` - 9 e2e render tests pass / 9 个 e2e 渲染测试
- [x] `cargo test tui::tab::benches` - 7 performance benchmarks / 7 个性能基准
- [x] `cargo test tui::tab::key_e2e` - 14 keyboard event e2e tests / 14 个键盘 e2e 测试
- [x] `cargo test tui::tab::persistence` - 8 persistence tests / 8 个持久化测试
- [x] Manual: All shortcuts verified / 手动验证:所有快捷键
- [x] Manual: Tab bar renders correctly at various widths / 手动验证:标签栏各宽度
- [x] Manual: Group colors display correctly / 手动验证:组颜色显示

---

## ⚠️ Breaking Changes 破坏性变更

**None. / 无。** All new functionality is additive / 所有新功能都是增量添加:
- TabManager defaults to empty (no existing tabs affected) / TabManager 默认空(不影响现有标签)
- All new keyboard shortcuts use Ctrl+ combinations (no conflict) / 快捷键全部用 Ctrl+(无冲突)
- All new ViewEvent variants are additions / ViewEvent 变体全部为新增
- Persistence file is created on first save, ignored if missing / 持久化文件首次保存时创建

---

## 🔒 Security Considerations 安全考虑

- **Persistence file**: atomic write (temp + rename) / 原子写入
- **File size limit**: 1MB (prevents OOM) / 1MB 大小限制(防 OOM)
- **Schema version detection**: forward/backward compatibility / 模式版本检测
- **@ 提及解析**: boundary detection (no false positives on `email@2`) / 边界检测
- **No new external dependencies** (uses existing chrono, serde, ratatui) / 无新增依赖

---

## 📚 Documentation 文档

All new features are documented in / 所有新功能文档:
- `docs/KEYBINDINGS.md` - Tab shortcuts section / 标签快捷键章节
- `docs/ARCHITECTURE.md` - Multi-Tab/Multi-Agent System section / 多标签系统章节
- `docs/TROUBLESHOOTING.md` - 8 common issues + file location / 8 个常见问题

---

## 🎬 Migration Path 迁移路径

No migration needed / 无需迁移:
1. After merge, users start with empty tab list / 合并后用户从空标签列表开始
2. First `Ctrl+Shift+N` creates a tab / 首次 `Ctrl+Shift+N` 创建标签
3. Tabs auto-persist on shutdown, auto-restore on next launch / 关闭时自动保存,下次启动自动恢复

---

## 🤖 Generated with Claude

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
176 changes: 176 additions & 0 deletions STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Status: Multi-tab system harvest to upstream `Hmbown/CodeWhale`

_Last updated: 2026-06-06 (post-Phase 2 rebase)_

This file is a working-state snapshot, not a strategy doc. For the strategic
plan and Phase 0/1/2 ordering, see `.claude/plans/github-deepseek-tui-skill-proxy-woolly-crescent.md`.
For the per-thread triage flow + GraphQL tooling, see `phase2-playbook.md`.

---

## 1. Pull requests

### PR #2753 — `feat(tui): multi-tab system with cross-tab collaboration`

| field | value |
| --- | --- |
| head | `8e260880` (on `feat/multi-agent-v0850`) |
| base | `codex/v0.9.0-stewardship` (changed from `main` to drop the unrelated 187-commit stewardship delta) |
| state | OPEN |
| size | 25 files changed, 5,961 insertions(+), 2 deletions(-) |
| checks | GitGuardian pending · Greptile ✓ · CodeWhale CI matrix = `action_required` (fork-PR gate, can't be approved from contributor side) |
| Hmbown verdict | "too large for the current v0.9 stabilization harvest… narrow tab-core/persistence slice after UTF-8 truncation and stub collab paths are resolved" |

Last comment chain is closed: Hmbown confirmed the narrow-harvest path; my
reply on `4638292100` committed to flipping to the narrow slice first.

After the narrow slice (#2864) opened, I rebased #2753's v0850 onto the new
stewardship head (`5bd2f6a9`, 0 conflicts) and cherry-picked the 6 Phase 2
bot-review fixes (commit `7038ab36`) on top. Reply on `4638863128` flagged
the one remaining Greptile P1 thread as stale (already addressed in the
prior `2269d656` round).

### PR #2864 — `feat(tui): add multi-tab system core (manager + persistence)`

| field | value |
| --- | --- |
| head | `7fcd7d74` (on `feat/tab-core-narrow`) |
| base | `codex/v0.9.0-stewardship` (rebased to `5bd2f6a9`) |
| state | OPEN |
| size | 12 files changed, 3,644 insertions(+), 1 deletion(-) |
| checks | gate ✓ · GitGuardian ✓ · Greptile ✓ |
| scope | `tab/{mod,manager,persistence}.rs` + their `#[cfg(test)]` modules + `tui/mod.rs` module decl + `tools/shell.rs` redundant-cast cleanup |

This is the harvest Hmbown asked for. 9 new bot review comments landed on
the original head `649d3990`; the 6 fixable ones are addressed in `7fcd7d74`,
the 3 deferred ones (close_tab cleanup, cross_tab_links snapshot, group
ID collision) are explicitly out of scope for the narrow slice. See § 4.

---

## 2. Branches

```
feat/tab-core-narrow 7fcd7d74 ← current, PR #2864 head
feat/multi-agent-v0850 7038ab36 ← current, PR #2753 head (rebased
onto new stewardship head + the
6 Phase 2 fixes cherry-picked)
rebase/stewardship-measured 88dc3843 ← stale; pre-#2862 backup. Superseded
by the current v0850 head which is
itself the latest 0-conflict
measurement.
upstream/codex/v0.9.0-stewardship 5bd2f6a9 ← target base for both PRs
```

Stewardship moved +3 commits since the first measurement
(`cc3cbc82`, `137d65c3`, `5bd2f6a9`); only `5bd2f6a9` (git status metadata
in `runtime_api.rs` + docs) is non-doc and it doesn't overlap the
multi-tab diff, so the rebase stayed 0-conflict.

---

## 3. Local CI matrix

Run on `rebase/stewardship-measured` (Windows runner), flags matching
`.github/workflows/ci.yml`:

| step | result |
| --- | --- |
| `cargo fmt --all -- --check` | exit 0 |
| `cargo clippy --workspace --all-features --locked -- -D warnings` | exit 0, 0 errors, 0 warnings |
| `cargo test --workspace --all-features --locked` | 4023 passed, 6 failed |
| `git diff --exit-code -- Cargo.lock` | exit 0 |

The 6 failures are **pre-existing** on the baseline `2269d656` (the v0850
state before this round of cleanups) and reproduce after `git stash` of all
the cleanups, so they are not caused by this PR. They cluster in
`commands::skills::*` (filesystem), `settings::tests::settings_path_defaults_…`
(path), and three `tools::shell::*` Windows-runtime tests. None of them touch
the tab system or any file path the PR changes.

Tab-scoped test subsets on the narrow branch:

| subset | result |
| --- | --- |
| `cargo test -p codewhale-tui tab::` | 72/72 pass |
| `cargo test -p codewhale-tui tui::views::` | 59/59 pass |
| `cargo test -p codewhale-tui delegator::` | 7/7 pass |

---

## 4. Pending bot review threads on PR #2864

9 unresolved review threads on `649d3990`. Triage pending — see
`phase2-playbook.md` for the decision tree.

| # | author | path:line | severity | summary |
| --- | --- | --- | --- | --- |
| 1 | gemini | `tab/manager.rs:316` | high | `close_tab` leaves orphaned delegations + active meetings |
| 2 | gemini | `tab/persistence.rs:132` | medium | oversized file silently returns `default()` → data-loss risk on next save |
| 3 | gemini | `tab/mention.rs:164` | medium | `resolve_tab_mention` sorts input → semantic bug (mention ≠ visual order) |
| 4 | gemini | `tab/persistence.rs:64` | medium | `PersistedDelegation` has no `status` field → in-flight `InProgress` reverts to `Pending` |
| 5 | gemini | `tab/manager.rs:184` | medium | `cross_tab_links` not snapshotted → collab topology lost across restart |
| 6 | gemini | `tab/manager.rs:477` | medium | `delegate_task` accepts non-existent tab IDs |
| 7 | gemini | `tab/manager.rs:512` | medium | `start_meeting` accepts non-existent participant IDs |
| 8 | greptile | `tab/group.rs:79` | P2 | `TabGroup::new()` ID from `timestamp_millis()` — same-ms collision |
| 9 | greptile | `tab/manager.rs:435` | P2 | `pending_tasks` misnamed — returns completed `DelegationResult`s |

The narrow-harvest promise to Hmbown was that #2864 is "tab-core +
persistence" only, with collab/UI deferred to a follow-up PR. That means:

- **In-scope for #2864** (defensible as bugfixes of the shipped surface):
#2, #3, #4, #6, #7, #9.
- **Belongs to the follow-up collab/UI PR** (file paths or behaviours that
Hmbown was told would not be in this slice): #1 (`close_tab` cleanup is
*correct* to add, but it materialises a behaviour the collab surface was
supposed to provide; could go either way — see playbook).
- **Out of scope / cosmetic** (does not block a merge of a WIP-stub module):
#5, #8 — `cross_tab_links` and `group.rs` are part of the stub collab
surface; fixing the ID scheme or the snapshot shape there is correctly
the follow-up PR's problem.

---

## 5. Stewardship-related fixes already applied to #2864

Carried over from PR #2753 review and re-verified on the narrow branch:

- UTF-8-safe `chars().count() + chars().take(N).collect()` in
`views::tab_picker` and `views::tab_switcher` (byte slicing would panic
on a multi-byte char at the cut point).
- `sort_by` → `sort_by_key(Reverse)` in delegator/meeting sort sites.
- `#![allow(dead_code, unused_imports)]` scoped to the WIP collaboration
surface (`tab/delegator`, `tab/meeting`, `tab/cross_tab`, `tab/group`,
`tab/mention`, `tab/persistence`, `tab/manager`, `tab/mod`,
`views/meeting_view`) with rationale captured next to each allow.
- Dropped a pre-existing redundant cast in
`crates/tui/src/tools/shell.rs` (`child.as_raw_handle()` already returns
`*mut c_void`).
- `pub use manager::TabManager;` re-export retained as the public entry
point for the follow-up wiring (allow on `unused_imports` is in scope).

---

## 6. Open questions for the user

These are decisions that need a human call, not a mechanical action. The
playbook flags them at the relevant step.

1. **#1 (close_tab cleanup) — include in #2864 or defer?** Fixing the
orphan leak requires the new code to know which delegations and
meetings to keep, which implicitly defines a public behaviour for the
collab surface. If Hmbown wanted that surface unchanged, this should
go to the follow-up PR; if it can be considered a defensive
correctness fix on `close_tab`, it belongs here.
2. **#5 (cross_tab_links snapshot) — include in #2864 or defer?** The
`cross_tab_links` field is on `TabManager` and the snapshot already
takes a `&TabManager` reference, so adding it to the snapshot is a
1-liner — but the *behaviour* of which links get restored, and the
shape of the persisted cross-link, is part of the collab design.
3. **#6, #7 (delegate_task / start_meeting validation) — return `None`
vs add `Result`?** Both methods currently return a `String` task ID /
meeting ID. Changing to `Option<String>` is a public-API change that
callers in the (deferred) UI pass will see. It is also a one-line
change each. The narrow-harvest doesn't *have* any in-tree callers
of those methods, so the rename is safe locally, but worth a moment
of thought before committing it.
2 changes: 1 addition & 1 deletion crates/tui/src/tools/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ impl WindowsJob {
)
.map_err(windows_io_error)?;

let process_handle = HANDLE(child.as_raw_handle() as *mut core::ffi::c_void);
let process_handle = HANDLE(child.as_raw_handle());
AssignProcessToJobObject(job.handle, process_handle).map_err(windows_io_error)?;
}

Expand Down
32 changes: 32 additions & 0 deletions crates/tui/src/tui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,8 @@ pub struct App {
pub todos: SharedTodoList,
/// Durable runtime services exposed to model-visible task/automation tools.
pub runtime_services: RuntimeToolServices,
/// Tab manager for multi-tab and cross-tab collaboration system
pub tab_manager: crate::tui::tab::TabManager,
/// Last MCP manager/discovery snapshot shown in the UI.
pub mcp_snapshot: Option<crate::mcp::McpManagerSnapshot>,
/// Number of MCP servers declared in the user's config at app boot.
Expand Down Expand Up @@ -2201,6 +2203,7 @@ impl App {
shell_manager: Some(shell_manager),
..RuntimeToolServices::default()
},
tab_manager: crate::tui::tab::TabManager::new(),
mcp_snapshot: None,
// Read the MCP config once at boot to know how many servers
// the user has declared. The footer chip uses this even when
Expand Down Expand Up @@ -2288,6 +2291,35 @@ impl App {
}
}

/// Create a minimal App for testing - no config loading, no engine setup.
/// Only the fields that render tests need (tab_manager, etc.) are populated.
#[allow(dead_code)] // reserved for the render_tests follow-up; not used in this PR
pub fn new_for_test() -> App {
use std::path::PathBuf;
let options = TuiOptions {
model: "test-model".to_string(),
workspace: PathBuf::from("."),
config_path: None,
config_profile: None,
allow_shell: false,
use_alt_screen: true,
use_mouse_capture: false,
use_bracketed_paste: true,
max_subagents: 1,
skills_dir: PathBuf::from("."),
memory_path: PathBuf::from("memory.md"),
notes_path: PathBuf::from("notes.txt"),
mcp_config_path: PathBuf::from("mcp.json"),
use_memory: false,
start_in_agent_mode: false,
skip_onboarding: true,
yolo: false,
resume_session_id: None,
initial_input: None,
};
App::new(options, &Config::default())
}

fn discover_cached_skills(
workspace: &std::path::Path,
skills_dir: &std::path::Path,
Expand Down
Loading