docs(handoff): roll user-issues doc forward to v0.8.28
Replaces the v0.8.27 handoff with a fresh v0.8.28 doc focused on what's actually outstanding after the v0.8.27 ship: - P1: CNB.cool mirror automation (token had no push perm during v0.8.27 release), Ctrl+Enter as newline config flag (#1372), Windows task_manager test timeout bump, general test flakiness audit. - P2: comment-pinged issues awaiting reporter (#1112 snapshot growth, #1357 input/runtime overlap, #1281 Cmux notifications). - P3: deferred items (#1338 Windows panic, #1062 capacity recovery, #1067 musl build, #1364 hooks v2, #1343 desktop GUI). The v0.8.27 doc had ~25 items inline; the v0.8.28 doc only carries what's still outstanding (everything else landed in the v0.8.27 cycle — see PR #1375). Starts smaller so the next agent can ship a focused release rather than wade through completed work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,642 +0,0 @@
|
||||
# v0.8.27 — User-Issue Strategy Handoff
|
||||
|
||||
**Audience:** the AI agent picking up post-v0.8.26 user-bug work.
|
||||
**Scope:** the issues filed by users in the 24–48 hours after v0.8.26
|
||||
shipped, plus older issues with concrete fix shapes that didn't make
|
||||
v0.8.26.
|
||||
**This is layered on top of the in-flight v0.8.27 cycle** — there are
|
||||
already 16 community-PR commits on `work/v0.8.27`. Don't start over;
|
||||
add to it.
|
||||
|
||||
---
|
||||
|
||||
## Where you are
|
||||
|
||||
- **Working tree:** `/Volumes/VIXinSSD/whalebro/deepseek-tui`
|
||||
- **Active branch:** `work/v0.8.27` (off main at v0.8.26 tip)
|
||||
- **Already on the branch:** 16 commits — community PRs (#1316, #1317,
|
||||
#1181, #1203, #1140, #1247, #1223, #1185, #1220, #1233, #1235,
|
||||
#1197, plus a trackpad scroll fix and a card-rail UI tweak)
|
||||
- **Reference docs:** `.claude/HANDOFF_v0.8.26_security.md` for the
|
||||
release flow steps 7–11 (same shape applies for v0.8.27)
|
||||
- **Issue board:** GitHub `Hmbown/DeepSeek-TUI`
|
||||
|
||||
The previous agent only did community-PR cherry-picks. The strategic
|
||||
bug-fix work in this document is **not started**. Assume zero
|
||||
overlap.
|
||||
|
||||
---
|
||||
|
||||
## Hard rules
|
||||
|
||||
1. **STOP and ask Hunter** before merging the v0.8.27 PR, tagging,
|
||||
or publishing to crates.io / npm / Homebrew.
|
||||
2. No `--no-verify`, no `--no-gpg-sign`, no force push.
|
||||
3. Don't leak `.private/` content into PRs / CHANGELOG / release notes.
|
||||
4. v0.8.27 is **NOT** a security release. If a new GHSA arrives mid-
|
||||
cycle, branch v0.8.28 — don't bundle.
|
||||
5. Time-box thorny items at 30 min. Defer to v0.8.28 instead of
|
||||
sinking the cycle.
|
||||
|
||||
---
|
||||
|
||||
## P0 — ship these, they fix real user pain
|
||||
|
||||
### 1. Cross-terminal flicker (#1119, #1352, #1356, #1363, #1366, #1260, #1295)
|
||||
|
||||
**The most-reported bug since v0.8.26 shipped.** Five Ghostty / VSCode-
|
||||
terminal reports in 24 hours plus the existing Windows ones. **Same
|
||||
root cause, single fix.**
|
||||
|
||||
**Diagnosis.** v0.8.22 added a viewport-reset escape sequence to fix
|
||||
viewport drift after focus/resize. The sequence is:
|
||||
|
||||
```
|
||||
\x1b[r set scroll region to entire screen
|
||||
\x1b[?6l reset DECOM origin mode
|
||||
\x1b[H cursor home
|
||||
\x1b[2J erase entire screen
|
||||
\x1b[3J erase saved lines
|
||||
```
|
||||
|
||||
This fires on every redraw. `\x1b[2J\x1b[3J` is destructive — full
|
||||
clear. Terminals that don't optimize differential redraws (Ghostty,
|
||||
VSCode terminal in some configurations, Win10 conhost) blank-then-
|
||||
repaint every frame, producing visible flicker.
|
||||
|
||||
The smoking-gun datapoint is **#1356**: "doesn't flicker on M4 Air,
|
||||
doesn't flicker for Claude Code / Codex / Gemini CLIs in the same
|
||||
VSCode terminal." Other CLIs use the alt-screen buffer's natural
|
||||
double-buffering and don't emit a destructive reset every frame.
|
||||
|
||||
**Strategy — pick #1; #2 is the fallback.**
|
||||
|
||||
#### 1.A — Replace destructive reset with lighter sequence (~30 min)
|
||||
|
||||
In `crates/tui/src/tui/ui.rs` (search for the const that holds the
|
||||
reset sequence — likely named `VIEWPORT_RESET` or similar, check the
|
||||
v0.8.22 / v0.8.24 commits that mention `recover_terminal_modes` or
|
||||
`FocusGained`):
|
||||
|
||||
```rust
|
||||
// before
|
||||
const VIEWPORT_RESET: &str = "\x1b[r\x1b[?6l\x1b[H\x1b[2J\x1b[3J";
|
||||
|
||||
// after — drop the destructive 2J/3J; alt-screen buffer's existing
|
||||
// double-buffering handles the redraw without screen blanking.
|
||||
const VIEWPORT_RESET: &str = "\x1b[r\x1b[?6l\x1b[H";
|
||||
```
|
||||
|
||||
Add a regression test that asserts the constant doesn't contain
|
||||
`2J` or `3J` (the destructive parts). The viewport-drift fix that
|
||||
the original sequence was added to address came from #1041 / similar
|
||||
— verify it's still working with the lighter sequence by manual
|
||||
smoke on macOS Terminal.app.
|
||||
|
||||
#### 1.B — Audit redraw-rate and only emit on actual drift (~1 day)
|
||||
|
||||
If 1.A reintroduces drift, fall back to this: track previous viewport
|
||||
state and only emit the reset when drift is detected (post-resize,
|
||||
post-focus-gain, post-pager-close). Search for where the constant is
|
||||
emitted; if it's in the per-frame draw path, that's the bug.
|
||||
|
||||
#### 1.C — Per-terminal opt-out as belt-and-suspenders
|
||||
|
||||
Detect `TERM_PROGRAM=ghostty`, `TERM_PROGRAM=vscode` (also covers
|
||||
VSCode terminal), and known-flaky `TERM` values. Skip the reset
|
||||
entirely on those. Document this as a fallback in the commit message;
|
||||
prefer 1.A as the primary fix.
|
||||
|
||||
**Action:**
|
||||
1. File a tracking issue "Cross-terminal flicker survey" linking all
|
||||
seven reports (#1119, #1352, #1356, #1363, #1366, #1260, #1295).
|
||||
2. Apply fix 1.A.
|
||||
3. Manual smoke on macOS Terminal.app + iTerm2 + Ghostty (Hunter has
|
||||
Ghostty available; ask him).
|
||||
4. Comment on each linked issue: "Fixed in v0.8.27 — please update
|
||||
and reopen if you still see flicker."
|
||||
|
||||
---
|
||||
|
||||
### 2. Long-text wrap (#1344, #1351, possibly #1359)
|
||||
|
||||
**Diagnosis.** v0.8.25 fixed long markdown **table cells** (`wrap_cell_text`
|
||||
helper in `markdown_render.rs`). Long **paragraphs** and long **input
|
||||
lines** still clip at viewport width on some terminals, instead of
|
||||
wrapping. #1344 reports both directions; #1351 reports the same
|
||||
symptom plus a separate "table content shows `...`" issue. #1359 is
|
||||
"VSCode terminal won't wrap" — possibly the same root cause if VSCode
|
||||
reports terminal size differently.
|
||||
|
||||
**Strategy.**
|
||||
|
||||
1. Reproduce at narrow width: `COLUMNS=60 deepseek` → paste a 200-
|
||||
character input line, ask for a 200-character paragraph response.
|
||||
Confirm both clip rather than wrap.
|
||||
2. Trace the wrap paths:
|
||||
- `crates/tui/src/tui/markdown_render.rs::render_message` →
|
||||
`render_line_with_links` → `wrap_text` (paragraphs)
|
||||
- `crates/tui/src/tui/composer.rs` (or wherever the input box
|
||||
renders) — likely has its own wrap logic that diverged
|
||||
3. Unify on `wrap_text` from `markdown_render`. The composer should
|
||||
use the same width-aware wrapper as the transcript.
|
||||
4. Add snapshot tests for both surfaces at widths 40, 60, 80, 120.
|
||||
5. For VSCode-terminal-specific size detection issues (#1359), verify
|
||||
`crossterm::terminal::size()` returns the right value when run
|
||||
inside VSCode terminal. If wrong, look at `--columns` override.
|
||||
|
||||
**Cost:** 3-4 hours including tests.
|
||||
|
||||
---
|
||||
|
||||
### 3. Pager copy-out (#1354)
|
||||
|
||||
**Diagnosis.** When users hit `Alt+V` (tool details) or `Ctrl+O`
|
||||
(thinking content), they get a pager view. The pager intercepts mouse
|
||||
capture, so terminal-native selection is disabled inside it. There's
|
||||
no in-app copy keybinding. Result: users can see the content but
|
||||
can't copy it. High-frustration UX gap — pager users are usually
|
||||
specifically there to copy something out.
|
||||
|
||||
**Strategy.** Add a `c` (or `y`, vi-style) keybinding inside the
|
||||
pager view that copies the entire visible content to clipboard, with
|
||||
a status confirmation toast.
|
||||
|
||||
In `crates/tui/src/tui/views/pager.rs` (or wherever `PagerView` is
|
||||
defined — search for `impl ModalView for PagerView`):
|
||||
|
||||
```rust
|
||||
// inside handle_key, somewhere with the existing Esc/q/PgUp/PgDn handlers
|
||||
KeyCode::Char('c') | KeyCode::Char('y') => {
|
||||
let text = self.body_text(); // whatever method gives the full body
|
||||
if app.clipboard.write_text(&text).is_ok() {
|
||||
app.status_message = Some("Pager content copied".to_string());
|
||||
} else {
|
||||
app.status_message = Some("Copy failed".to_string());
|
||||
}
|
||||
return Vec::new();
|
||||
}
|
||||
```
|
||||
|
||||
Also surface the keybinding in the pager footer: append `[c copy]`
|
||||
to the existing affordance line.
|
||||
|
||||
Add a regression test that constructs a pager, sends `c`, and
|
||||
asserts the clipboard mock saw the body text.
|
||||
|
||||
**Cost:** ~45 minutes.
|
||||
|
||||
---
|
||||
|
||||
## P1 — should ship; clear shape, real impact
|
||||
|
||||
### 4. Ctrl+C context-sensitive (#1337, #1367)
|
||||
|
||||
**Diagnosis.** Two related issues:
|
||||
- **#1337:** Windows users expect `Ctrl+C` to copy (legacy Windows
|
||||
convention). Our binding is exit. They lose work copying.
|
||||
- **#1367:** Users don't know how to interrupt a long-running task.
|
||||
`Esc` works but isn't discoverable.
|
||||
|
||||
**Strategy — context-sensitive Ctrl+C (resolves both):**
|
||||
|
||||
Three branches based on app state:
|
||||
|
||||
| State | Ctrl+C behavior |
|
||||
|---|---|
|
||||
| **Selection active** | Copy + clear selection. No exit. |
|
||||
| **Turn in progress** | Interrupt the turn (same as Esc). No exit. |
|
||||
| **Idle, no selection** | First press: status hint "Press Ctrl+C again to exit". Second press within 2s: exit. |
|
||||
|
||||
This pattern is well-precedented (htop, less, tmux) and addresses
|
||||
both issues in one change. Mirror Vim's "are you sure" pattern for
|
||||
the idle case.
|
||||
|
||||
In `crates/tui/src/tui/ui.rs::handle_key_event`, find the
|
||||
`KeyCode::Char('c')` + `KeyModifiers::CONTROL` arm:
|
||||
|
||||
```rust
|
||||
KeyCode::Char('c') if m.contains(KeyModifiers::CONTROL) => {
|
||||
// Branch 1: selection active → copy
|
||||
if app.viewport.transcript_selection.is_active() {
|
||||
copy_active_selection(app);
|
||||
app.viewport.transcript_selection.clear();
|
||||
return Vec::new();
|
||||
}
|
||||
// Branch 2: turn in progress → interrupt
|
||||
if app.is_loading {
|
||||
// existing interrupt logic — same code path as Esc
|
||||
return interrupt_current_turn(app);
|
||||
}
|
||||
// Branch 3: idle → first press shows hint, second press within 2s exits
|
||||
let now = Instant::now();
|
||||
let recent_ctrl_c = app.last_ctrl_c.is_some_and(|t| now.duration_since(t) < Duration::from_secs(2));
|
||||
if recent_ctrl_c {
|
||||
return vec![ViewEvent::Exit];
|
||||
}
|
||||
app.last_ctrl_c = Some(now);
|
||||
app.status_message = Some("Press Ctrl+C again to exit".to_string());
|
||||
Vec::new()
|
||||
}
|
||||
```
|
||||
|
||||
Plus the discoverability hint for #1367: status bar during streaming
|
||||
shows `[Esc cancel · Ctrl+C twice exit]`.
|
||||
|
||||
**Cost:** ~2 hours including tests for each branch.
|
||||
|
||||
---
|
||||
|
||||
### 5. `notify` tool (#1322)
|
||||
|
||||
**Diagnosis.** Model-triggerable desktop notifications. Long agent
|
||||
runs would benefit from an "I'm done, look at me" pop-up. Other
|
||||
tools (Claude Code) have this.
|
||||
|
||||
**Strategy.** Add a built-in `notify` tool spec.
|
||||
|
||||
1. Add `notify-rust` to `crates/tui/Cargo.toml` (already cross-
|
||||
platform: macOS Notification Center, Linux libnotify, Windows toast).
|
||||
2. New tool in `crates/tui/src/tools/notify.rs`:
|
||||
```rust
|
||||
pub struct NotifyTool;
|
||||
|
||||
#[async_trait]
|
||||
impl ToolSpec for NotifyTool {
|
||||
fn name(&self) -> &'static str { "notify" }
|
||||
fn description(&self) -> &'static str {
|
||||
"Display a desktop notification to the user. Use sparingly — only when a long-running task completes or needs the user's attention."
|
||||
}
|
||||
fn input_schema(&self) -> Value { /* {title: required, body: optional} */ }
|
||||
fn capabilities(&self) -> Vec<ToolCapability> {
|
||||
vec![ToolCapability::RequiresApproval]
|
||||
}
|
||||
fn approval_requirement(&self) -> ApprovalRequirement {
|
||||
ApprovalRequirement::Auto // notifications are low-risk
|
||||
}
|
||||
async fn execute(&self, input: Value, _ctx: &ToolContext) -> Result<ToolResult, ToolError> {
|
||||
// truncate title to ~60 chars, body to ~200
|
||||
// skip if app is currently focused (don't notify about
|
||||
// the thing the user is watching) — read from
|
||||
// app.focus_state if available
|
||||
// call notify_rust::Notification::new()...
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Wire up in `tool_setup.rs` (probably register conditional on a
|
||||
`Feature::DesktopNotifications` feature flag, default-on).
|
||||
4. Add config opt-out: `[tools.notify] enabled = false`.
|
||||
|
||||
Auto-suppress when terminal is focused — the user is watching, no
|
||||
notification needed.
|
||||
|
||||
**Cost:** ~3-4 hours.
|
||||
|
||||
---
|
||||
|
||||
### 6. `/skills --remote` diagnostic (#1329)
|
||||
|
||||
**Diagnosis.** "Failed to fetch" with no details. Could be TLS,
|
||||
network policy, auth, rate limit. Bare error → undiagnosable.
|
||||
|
||||
**Strategy.** First fix is observability — surface the underlying
|
||||
error chain.
|
||||
|
||||
In `crates/tui/src/commands/skills.rs` (or wherever `--remote` is
|
||||
handled), find the `.unwrap_err()` or `.context(...)` that's
|
||||
collapsing the chain:
|
||||
|
||||
```rust
|
||||
// before
|
||||
return Err(anyhow!("Failed to fetch"));
|
||||
|
||||
// after
|
||||
return Err(err.context("Failed to fetch remote skills"));
|
||||
// or, when surfacing to the user:
|
||||
return CommandResult::error(format!("Failed to fetch remote skills:\n{err:#}"));
|
||||
```
|
||||
|
||||
Mirror the v0.8.23 #1244 fix shape (alternate `{err:#}` formatting
|
||||
for the full anyhow chain).
|
||||
|
||||
Once the underlying error is visible, the actual bug becomes
|
||||
diagnosable. Likely either a TLS issue (rustls vs system trust store)
|
||||
or the network policy blocking the registry endpoint.
|
||||
|
||||
**Cost:** ~30 minutes for the diagnostic improvement.
|
||||
|
||||
---
|
||||
|
||||
### 7. MCP lazy reload on config change (#1267 part 2)
|
||||
|
||||
**Diagnosis.** v0.8.26 fixed the diagnostic side (stderr capture).
|
||||
The "auto-reload after config edit" piece is still missing — users
|
||||
have to manually run `/mcp reload` after editing `~/.deepseek/config.toml`.
|
||||
|
||||
**Strategy — lazy hash check (no file watcher).** File watchers add
|
||||
long-lived tasks and have edge cases on remote / network filesystems.
|
||||
A lazy hash compare is bounded and cheap.
|
||||
|
||||
In `crates/tui/src/mcp.rs::McpPool`:
|
||||
|
||||
```rust
|
||||
pub struct McpPool {
|
||||
// ... existing fields
|
||||
config_hash: u64, // hash of mcp config at last (re)connection
|
||||
}
|
||||
|
||||
impl McpPool {
|
||||
fn current_config_hash(&self, config: &McpConfig) -> u64 {
|
||||
let mut hasher = std::hash::DefaultHasher::new();
|
||||
// hash the relevant fields: servers map, timeouts, sandbox_mode
|
||||
config.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub async fn get_or_connect(&mut self, server: &str, config: &McpConfig) -> Result<&mut McpConnection> {
|
||||
let new_hash = self.current_config_hash(config);
|
||||
if new_hash != self.config_hash {
|
||||
self.reload_all(config).await?;
|
||||
self.config_hash = new_hash;
|
||||
}
|
||||
// existing get_or_connect logic
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`McpConfig` and adjacent types may need `Hash` derived. If hashing
|
||||
the whole config tree is expensive, hash just the `[mcp_servers]`
|
||||
section + `sandbox_mode`.
|
||||
|
||||
**Cost:** ~2 hours including tests.
|
||||
|
||||
---
|
||||
|
||||
## P2 — nice-to-have if time permits
|
||||
|
||||
### 8. Layout overlap (#1357)
|
||||
|
||||
**Diagnosis.** Input box and inline runtime hint ("Cache: 99% hit |
|
||||
hit X | miss Y") render in adjacent rects but one isn't clearing its
|
||||
area properly when the other expands.
|
||||
|
||||
**Strategy.** Inspect `crates/tui/src/tui/ui.rs::render` — find the
|
||||
composer's reserved-rows calculation. It probably doesn't account for
|
||||
the hint line on resize / long-content. Fix the rect math.
|
||||
|
||||
**Cost:** ~2 hours (1 to repro, 1 to fix).
|
||||
|
||||
### 9. `/skills` filter argument (#1318)
|
||||
|
||||
**Diagnosis.** v0.8.26 added inter-row spacing (#1328 from @reidliu41).
|
||||
Reporter may want more.
|
||||
|
||||
**Strategy.**
|
||||
1. Comment on #1318 asking if v0.8.26's spacing is enough.
|
||||
2. If not, add `/skills <prefix>` arg → filter to skills whose names
|
||||
start with `<prefix>`. Mirror how `/help <topic>` works.
|
||||
|
||||
**Cost:** Triage ping; 30 min if filter wanted.
|
||||
|
||||
### 10. Status comments on partial fixes (#1112, #1267, #1318)
|
||||
|
||||
Three issues that are partly addressed and need the reporter to
|
||||
confirm:
|
||||
|
||||
- **#1112** — 1.2 TB snapshots. Cap added in v0.8.24. Comment:
|
||||
"500 MB cap added in v0.8.24. Are you still seeing growth above
|
||||
that? If so, please share `du -sh ~/.deepseek/snapshots`."
|
||||
- **#1267** — macOS Seatbelt blocks npx MCP. Already commented during
|
||||
v0.8.26 cycle. Don't re-comment.
|
||||
- **#1318** — `/skills` crowded. Comment: "v0.8.26 added inter-row
|
||||
spacing (#1328). Does this resolve it for you?"
|
||||
|
||||
**Cost:** ~5 minutes total.
|
||||
|
||||
---
|
||||
|
||||
## P3 — investigate or defer
|
||||
|
||||
### #1338 — Enter mid-run crashes Windows TUI
|
||||
|
||||
**Defer unless you have Windows.** Add stack capture so the next
|
||||
reporter gets actionable output:
|
||||
|
||||
```rust
|
||||
// in main.rs — add panic hook that logs to ~/.deepseek/last-panic.log
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
let _ = std::fs::write(
|
||||
dirs::home_dir().unwrap_or_default().join(".deepseek/last-panic.log"),
|
||||
format!("{info}\n{}", std::backtrace::Backtrace::capture()),
|
||||
);
|
||||
}));
|
||||
```
|
||||
|
||||
**Cost:** 30 min for the diagnostic; actual fix needs Windows VM.
|
||||
|
||||
### #1062 — Capacity-memory checkpoint cross-session recovery
|
||||
|
||||
Old, complex. Don't pull into v0.8.27. Needs scope conversation with
|
||||
Hunter.
|
||||
|
||||
### #1067 — glibc version required (older Linux distros)
|
||||
|
||||
Static-link the deepseek binary or add a musl build to release.yml.
|
||||
**v0.8.27 candidate if anyone has time** — purely a build-config
|
||||
change.
|
||||
|
||||
### #1364 — Hooks mutation rights + turn-end event
|
||||
|
||||
**Defer to v0.9.0.** Real ask — Claude Code hooks have this. Worth
|
||||
doing as part of a hooks-v2 task. Out of scope for a polish release.
|
||||
|
||||
### #1343 — Desktop GUI
|
||||
|
||||
**Defer.** Recurring request. v0.9.x territory at the earliest.
|
||||
Comment with roadmap status if not already.
|
||||
|
||||
---
|
||||
|
||||
## Issues to close as fixed in v0.8.26
|
||||
|
||||
These need a comment + close. Already verified by the previous agent:
|
||||
|
||||
| # | Title | Fixed by |
|
||||
|---|---|---|
|
||||
| #1163 | Mouse drag-select / copy doesn't auto-scroll | PR #1239 |
|
||||
| #1169 | Selection crosses sidebar | Mouse-capture default-on for WT |
|
||||
| #1255 | Win10 conversation can't scroll | Mouse-capture default-on |
|
||||
| #1292 | Mac trackpad text selection broken | Drag-select rewrite |
|
||||
| #1298 | Wheel scrolls input history not transcript | Mouse-capture default-on |
|
||||
| #1308 | base_url for ollama/vllm ignored | Config-load warning |
|
||||
| #1331 | Mouse wheel changed in v0.8.24 | Mouse-capture default-on |
|
||||
|
||||
**Action:** Run through with this comment template (translated for
|
||||
zh-CN issues #1255, #1292):
|
||||
|
||||
```
|
||||
Fixed in [v0.8.26](https://github.com/Hmbown/DeepSeek-TUI/releases/tag/v0.8.26).
|
||||
Please update with:
|
||||
|
||||
- npm: `npm install -g deepseek-tui@latest`
|
||||
- brew: `brew upgrade deepseek-tui`
|
||||
- cargo: `cargo install --force deepseek-tui-cli`
|
||||
|
||||
Reopen if you still hit it. Thanks for the report!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Branch state confirmation
|
||||
|
||||
```bash
|
||||
cd /Volumes/VIXinSSD/whalebro/deepseek-tui
|
||||
git checkout work/v0.8.27
|
||||
git pull origin work/v0.8.27 || true
|
||||
git log --oneline main..HEAD | head -20
|
||||
```
|
||||
|
||||
You should see ~16 commits already on the branch. Add to it; don't
|
||||
restart.
|
||||
|
||||
### Step 2 — Tackle in priority order (P0 → P3)
|
||||
|
||||
For each item:
|
||||
|
||||
1. Read the issue thread on GitHub. Note any reporter clarifications.
|
||||
2. Implement per the strategy above.
|
||||
3. Add tests (TDD where the strategy specifies; verification snapshot
|
||||
otherwise).
|
||||
4. After each commit:
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo clippy -p deepseek-tui --all-targets --all-features --locked -- -D warnings
|
||||
cargo test -p deepseek-tui --bin deepseek-tui --all-features --locked --no-fail-fast \
|
||||
2>&1 | grep "test result:" | tail -3
|
||||
```
|
||||
5. Add a CHANGELOG entry under `## [0.8.27]` `### Fixed` or `### Added`,
|
||||
crediting the issue number and original reporter.
|
||||
|
||||
The known-flaky test is
|
||||
`mcp_connection_supports_streamable_http_event_stream_responses` —
|
||||
passes in isolation, intermittent under load. Don't chase.
|
||||
|
||||
### Step 3 — Issue triage pass
|
||||
|
||||
After each P0/P1 fix lands, close the corresponding issue with a
|
||||
comment template. Don't wait until the end of the cycle — closing as
|
||||
you go keeps the issue list visibly responsive.
|
||||
|
||||
### Step 4 — Bump version when ready
|
||||
|
||||
```bash
|
||||
sed -i '' 's|^version = "0.8.26"|version = "0.8.27"|' Cargo.toml
|
||||
find crates -maxdepth 2 -name Cargo.toml -exec sed -i '' \
|
||||
's|version = "0.8.26"|version = "0.8.27"|g' {} +
|
||||
sed -i '' 's|"version": "0.8.26"|"version": "0.8.27"|' \
|
||||
npm/deepseek-tui/package.json
|
||||
sed -i '' 's|"deepseekBinaryVersion": "0.8.26"|"deepseekBinaryVersion": "0.8.27"|' \
|
||||
npm/deepseek-tui/package.json
|
||||
cargo update --workspace --offline
|
||||
./scripts/release/check-versions.sh
|
||||
```
|
||||
|
||||
Add `## [0.8.27] - YYYY-MM-DD` heading at the top of CHANGELOG.md.
|
||||
|
||||
### Step 5 — Full preflight + install
|
||||
|
||||
```bash
|
||||
cargo fmt --all -- --check
|
||||
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
||||
cargo test --workspace --all-features --locked --no-fail-fast \
|
||||
2>&1 | grep "test result:" | tail -10
|
||||
./scripts/release/check-versions.sh
|
||||
./scripts/release/publish-crates.sh dry-run
|
||||
cargo build --release --locked -p deepseek-tui-cli -p deepseek-tui
|
||||
node scripts/release/npm-wrapper-smoke.js
|
||||
|
||||
cargo install --path crates/cli --force --locked
|
||||
cargo install --path crates/tui --force --locked
|
||||
deepseek --version # confirm: deepseek 0.8.27 (<sha>)
|
||||
```
|
||||
|
||||
### Step 6 — STOP-FOR-MAINTAINER
|
||||
|
||||
Push the branch and open the release PR. Hand back to Hunter:
|
||||
|
||||
- PR number + link
|
||||
- Bullet list of all P0/P1/P2 items completed
|
||||
- Items deferred (P3 items) with reason
|
||||
- Preflight summary
|
||||
- "deepseek 0.8.27 installed at ~/.cargo/bin/, ready for testing"
|
||||
- Issues closed with v0.8.26 fixed-in comment
|
||||
|
||||
WAIT for Hunter's "go" before merging, tagging, or publishing.
|
||||
|
||||
### Step 7 — Release flow
|
||||
|
||||
Same as v0.8.26 — see `.claude/HANDOFF_v0.8.26_security.md` steps 8–11.
|
||||
Concretely: merge PR → auto-tag fires → release.yml builds matrix +
|
||||
GitHub Release → crates.io publish → npm publish → Homebrew formula
|
||||
update → verify GHCR → README post-merge bookkeeping.
|
||||
|
||||
**No GHSA flow this cycle.** If a new advisory comes in, branch
|
||||
v0.8.28 — don't bundle.
|
||||
|
||||
### Step 8 — CNB mirror (new for v0.8.27)
|
||||
|
||||
After GitHub Release is live:
|
||||
|
||||
```bash
|
||||
# If CNB_TOKEN is in repo secrets, the GitHub Action handles it
|
||||
# automatically on tag push. Verify:
|
||||
# https://cnb.cool/deepseek-tui.com/DeepSeek-TUI/-/tags
|
||||
|
||||
# Otherwise (one-time bring-up was done manually) push from local:
|
||||
git remote add cnb https://<token>@cnb.cool/deepseek-tui.com/DeepSeek-TUI 2>/dev/null || true
|
||||
git push cnb v0.8.27 main
|
||||
```
|
||||
|
||||
Add a banner to README.md and README.zh-CN.md if not already there:
|
||||
|
||||
```
|
||||
> 🇨🇳 国内镜像 / Mainland China mirror:
|
||||
> https://cnb.cool/deepseek-tui.com/DeepSeek-TUI
|
||||
> Issues and PRs: please use GitHub.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quality bar
|
||||
|
||||
Apply to every change:
|
||||
|
||||
- CI green (modulo documented flaky)
|
||||
- No new `unwrap()` / `expect()` outside test code
|
||||
- No new external network surfaces without `validate_network_policy`
|
||||
- New env vars or config keys → `config.example.toml` entry + CHANGELOG note
|
||||
- Behavior changes user-visible → CHANGELOG entry calling out the change
|
||||
|
||||
When in doubt, defer to v0.8.28. A clean release of 8 P0/P1 items beats
|
||||
a cluttered release of 15 with one regression.
|
||||
|
||||
---
|
||||
|
||||
## Output expectation
|
||||
|
||||
Realistic v0.8.27 landing zone on top of the existing 16 commits:
|
||||
|
||||
- **All 7 closable v0.8.26 issues** closed with comments
|
||||
- **P0 #1, #2, #3** fully shipped (flicker, wrap, pager copy)
|
||||
- **P1 #4, #5, #6, #7** at least 2 of 4 shipped
|
||||
- **P2 #8, #9, #10** at least the comment-pings
|
||||
- **CNB mirror** wired in
|
||||
|
||||
That's a substantial v0.8.27 that respects the "post-v0.8.26 inflow"
|
||||
framing. Users see real responsiveness to their reports.
|
||||
|
||||
If at any point something looks materially harder than this document
|
||||
suggests, STOP and surface to Hunter with the specifics. Don't
|
||||
freelance scope.
|
||||
@@ -0,0 +1,271 @@
|
||||
# v0.8.28 — User-Issue Strategy Handoff
|
||||
|
||||
**Audience:** the AI agent picking up post-v0.8.27 user-bug work.
|
||||
**Scope:** items that didn't make v0.8.27 (deferred P3 / comment-pinged
|
||||
P2) plus anything new that lands during the v0.8.27 → v0.8.28 inflow
|
||||
window.
|
||||
|
||||
---
|
||||
|
||||
## Where you are
|
||||
|
||||
- **Working tree:** `/Volumes/VIXinSSD/whalebro/deepseek-tui`
|
||||
- **Last shipped:** v0.8.27 (commit `aaccaee6`, tag `v0.8.27`) —
|
||||
17 community PRs + a focused user-issue sweep (flicker, wrap,
|
||||
pager copy-out, context-sensitive Ctrl+C, MCP auto-reload, notify
|
||||
tool, onboarding localization, paste UX rebuild, /skills filter).
|
||||
~25 issues closed in the cycle. See PR #1375 for the full list.
|
||||
- **Reference docs:** `.claude/HANDOFF_v0.8.26_security.md` for the
|
||||
release flow shape (same matrix → crates → npm → Homebrew → GHCR).
|
||||
|
||||
---
|
||||
|
||||
## Hard rules (same as v0.8.27 cycle)
|
||||
|
||||
1. **STOP and ask Hunter** before merging the release PR, tagging,
|
||||
or publishing to crates.io / npm / Homebrew.
|
||||
2. No `--no-verify`, no `--no-gpg-sign`, no force push.
|
||||
3. Don't leak `.private/` content into PRs / CHANGELOG / release notes.
|
||||
4. v0.8.28 is **NOT** a security release by default. If a new GHSA
|
||||
arrives mid-cycle, branch a hotfix — don't bundle.
|
||||
5. Time-box thorny items at 30-45 min. Defer rather than sink the
|
||||
cycle on one item.
|
||||
|
||||
---
|
||||
|
||||
## P1 — should ship; clear shape
|
||||
|
||||
### 1. CNB.cool mirror automated push
|
||||
|
||||
**Diagnosis.** v0.8.27 set up the CNB.cool destination repo at
|
||||
`https://cnb.cool/deepseek-tui.com/DeepSeek-TUI` but the initial
|
||||
bare-mirror push got 403 ("You do not have permission to push this
|
||||
repository") with the issued PAT under user `whalebro`. The repo
|
||||
shows as fresh / not-yet-initialized on the CNB side.
|
||||
|
||||
**Strategy.**
|
||||
|
||||
1. Confirm with CNB which scope the token needs (likely a "Push"
|
||||
permission separate from default repo read). Re-issue the
|
||||
PAT with that scope.
|
||||
2. Verify the manual bare-clone-then-mirror push works once with
|
||||
the new PAT:
|
||||
```bash
|
||||
git clone --bare https://github.com/Hmbown/DeepSeek-TUI.git /tmp/cnb-init
|
||||
cd /tmp/cnb-init
|
||||
git push --mirror https://whalebro:<NEW_TOKEN>@cnb.cool/deepseek-tui.com/DeepSeek-TUI
|
||||
```
|
||||
3. Wire up a `mirror-cnb` job in `.github/workflows/release.yml`
|
||||
that runs on tag push and pushes to CNB automatically. Store the
|
||||
token as a `CNB_PUSH_URL` GitHub Actions secret so future
|
||||
releases mirror without manual steps.
|
||||
4. After CNB has content, add the mirror banner to `README.md` and
|
||||
`README.zh-CN.md` near the top:
|
||||
|
||||
```
|
||||
> 🇨🇳 国内镜像 / Mainland China mirror:
|
||||
> https://cnb.cool/deepseek-tui.com/DeepSeek-TUI
|
||||
> Issues and PRs: please use GitHub.
|
||||
```
|
||||
|
||||
**Cost:** ~30 min once the token has the right scope; ~1-2 hr if
|
||||
the release.yml automation also needs the multi-platform Action
|
||||
sorted out.
|
||||
|
||||
### 2. Ctrl+Enter as newline (#1372, follow-up to #1331)
|
||||
|
||||
**Diagnosis.** On Windows + nushell (and possibly some PowerShell
|
||||
setups), users expect `Ctrl+Enter` to insert a newline. v0.8.27's
|
||||
keybinding contract is:
|
||||
|
||||
- `Alt+Enter` / `Shift+Enter` / `Ctrl+J` → newline
|
||||
- `Ctrl+Enter` → force-steer (submit into current turn)
|
||||
|
||||
The reporter for #1372 was on Windows + nushell. Comment-ping in
|
||||
the issue asks them to try `Alt+Enter` / `Ctrl+J` and confirm
|
||||
what `KeyEvent` the TUI actually sees via `RUST_LOG=debug`.
|
||||
|
||||
**Strategy.** Add `[tui] ctrl_enter_as_newline` config flag,
|
||||
default false. When `true`, swap the priority so `Ctrl+Enter`
|
||||
inserts a newline and `Ctrl+Enter+Ctrl` (or similar) force-steers.
|
||||
Document the trade-off in `docs/CONFIGURATION.md`.
|
||||
|
||||
**Cost:** ~1 hour.
|
||||
|
||||
### 3. Windows `task_manager` test flake
|
||||
|
||||
**Diagnosis.** `task_manager::tests::persists_and_recovers_task_records`
|
||||
uses `wait_for_terminal_state(&manager, &task.id, Duration::from_secs(3))`
|
||||
— that 3s timeout for durable-task recovery is tight under Windows
|
||||
CI file-I/O load. We saw one intermittent failure during the v0.8.27
|
||||
PR run (passed on retry).
|
||||
|
||||
**Strategy.** Bump the timeout to `Duration::from_secs(10)` (or
|
||||
`Duration::from_secs(if cfg!(windows) { 10 } else { 3 })`). No
|
||||
functional change; just buys headroom for Windows CI.
|
||||
|
||||
**Cost:** ~10 min.
|
||||
|
||||
### 4. Test flakiness under load (general)
|
||||
|
||||
**Diagnosis.** The full tui test suite (now ~2640 tests) shows
|
||||
intermittent failures on macOS under load:
|
||||
- `mcp_connection_supports_streamable_http_event_stream_responses`
|
||||
(documented flaky; in handoff)
|
||||
- `refresh_system_prompt_is_noop_when_unchanged` (new flake)
|
||||
- `save_api_key_for_openrouter_writes_provider_table` (new flake)
|
||||
- `tools::recall_archive::tests::list_archives_sorts_by_cycle_number`
|
||||
(new flake)
|
||||
|
||||
All four pass in isolation. Suggests env-var or filesystem state
|
||||
sharing between tests that contend under parallel pressure.
|
||||
|
||||
**Strategy.** Audit the four tests for shared global state
|
||||
(env vars, `~/.deepseek/`, tempdir patterns). The most likely
|
||||
culprit is `unsafe { std::env::set_var(...) }` blocks without
|
||||
holding a process-wide test mutex.
|
||||
|
||||
**Cost:** 1-2 hours.
|
||||
|
||||
---
|
||||
|
||||
## P2 — nice-to-have; awaiting reporter feedback
|
||||
|
||||
### 5. #1112 snapshot growth
|
||||
|
||||
v0.8.24 added a 500 MB snapshot cap + retention count + mid-session
|
||||
pruning (vs startup-only). v0.8.27 cycle comment-pinged the reporter
|
||||
asking for `du -sh ~/.deepseek/snapshots` on the latest version.
|
||||
If they confirm growth is bounded, close. If still unbounded, the
|
||||
likely culprit is iCloud Drive / network-FS interactions; investigate.
|
||||
|
||||
### 6. #1357 input/runtime-hint overlap
|
||||
|
||||
Windows reporter screenshots show input box overlapping the runtime
|
||||
hint line. Comment-pinged in v0.8.27 cycle asking for terminal
|
||||
width (`tput cols`), terminal type (Windows Terminal / VSCode / etc),
|
||||
and the input that triggered the overlap. Likely a reserved-rows
|
||||
calc bug in `crates/tui/src/tui/widgets/mod.rs` composer layout.
|
||||
|
||||
**Cost:** 1 hr repro + 1 hr fix once reporter responds.
|
||||
|
||||
### 7. #1281 Cmux notifications
|
||||
|
||||
Cmux is a tmux-derivative; `notify_done` doesn't fire inside Cmux.
|
||||
Comment-pinged asking for `echo "TERM_PROGRAM=$TERM_PROGRAM TMUX=$TMUX"`
|
||||
output and `[notifications]` config. Likely a one-line addition to
|
||||
`resolve_method()` in `crates/tui/src/tui/notifications.rs` once we
|
||||
know what env vars Cmux sets.
|
||||
|
||||
**Cost:** 30 min once reporter responds.
|
||||
|
||||
---
|
||||
|
||||
## P3 — investigate or defer
|
||||
|
||||
### #1338 Windows panic on Enter mid-run
|
||||
|
||||
The TUI's panic hook writes crash dumps to `~/.deepseek/crashes/`
|
||||
already. Comment-pinged the reporter asking for the most recent
|
||||
crash log. Without it the panic location is opaque. Defer until
|
||||
reporter shares the log; then targeted fix.
|
||||
|
||||
### #1062 Capacity-memory checkpoint cross-session recovery
|
||||
|
||||
Old, complex. Needs scope conversation with Hunter before any work.
|
||||
|
||||
### #1067 glibc version (older Linux distros)
|
||||
|
||||
Static-link via musl build target. Purely a release.yml addition
|
||||
(add a `x86_64-unknown-linux-musl` target alongside the gnu one).
|
||||
**Good candidate for v0.8.28** if release.yml work happens anyway
|
||||
(see Item #1 CNB mirror — both touch the release workflow).
|
||||
|
||||
**Cost:** ~1 hour to add the musl target + update the install script.
|
||||
|
||||
### #1364 Hooks v2 mutation rights + turn-end event
|
||||
|
||||
Real ask. Worth doing as part of a hooks-v2 rework. **Defer to
|
||||
v0.9.0** — out of scope for a polish release.
|
||||
|
||||
### #1343 Desktop GUI
|
||||
|
||||
Recurring request. v0.9.x territory at the earliest. Comment with
|
||||
roadmap status if not already.
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Branch state confirmation
|
||||
|
||||
```bash
|
||||
cd /Volumes/VIXinSSD/whalebro/deepseek-tui
|
||||
git checkout main && git pull
|
||||
git checkout -b work/v0.8.28
|
||||
```
|
||||
|
||||
### Step 2 — Tackle items in priority order (P1 → P2 → P3)
|
||||
|
||||
For each item:
|
||||
1. Read the issue thread on GitHub for any new reporter info.
|
||||
2. Implement per the strategy.
|
||||
3. Add tests (TDD where strategy specifies; verification snapshot otherwise).
|
||||
4. After each commit:
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
||||
cargo test -p deepseek-tui --bin deepseek-tui --all-features --locked --no-fail-fast \
|
||||
2>&1 | grep "test result:" | tail -3
|
||||
```
|
||||
5. Add a CHANGELOG entry under `## [0.8.28]` `### Fixed` / `### Added` /
|
||||
`### Changed`, crediting the issue + original reporter where applicable.
|
||||
|
||||
### Step 3 — Bump version when ready
|
||||
|
||||
```bash
|
||||
sed -i '' 's|^version = "0.8.27"|version = "0.8.28"|' Cargo.toml
|
||||
find crates -maxdepth 2 -name Cargo.toml -exec sed -i '' \
|
||||
's|version = "0.8.27"|version = "0.8.28"|g' {} +
|
||||
sed -i '' 's|"version": "0.8.27"|"version": "0.8.28"|' \
|
||||
npm/deepseek-tui/package.json
|
||||
sed -i '' 's|"deepseekBinaryVersion": "0.8.27"|"deepseekBinaryVersion": "0.8.28"|' \
|
||||
npm/deepseek-tui/package.json
|
||||
cargo update --workspace --offline
|
||||
./scripts/release/check-versions.sh
|
||||
```
|
||||
|
||||
Add `## [0.8.28] - YYYY-MM-DD` heading at the top of CHANGELOG.md.
|
||||
|
||||
### Step 4 — Preflight + release flow
|
||||
|
||||
Same as v0.8.27 — see PR #1375 and the v0.8.27 ship report for the
|
||||
exact channel-ship sequence (merge → tag → matrix → crates → npm →
|
||||
Homebrew → GHCR → CNB mirror → README on main → handoff transition).
|
||||
|
||||
If Item #1 (CNB mirror automation) lands, the CNB mirror push step
|
||||
becomes automatic on tag push.
|
||||
|
||||
---
|
||||
|
||||
## Open items that may still arrive
|
||||
|
||||
The v0.8.27 release shipped on 2026-05-10. The 24-48 hour inflow
|
||||
window after that release may surface fresh issues that should be
|
||||
prioritized for v0.8.28. Default: any issue with ≥3 unique reporters
|
||||
or any new flicker / panic / data-loss class bug gets P0 treatment.
|
||||
|
||||
---
|
||||
|
||||
## Quality bar (unchanged from v0.8.27)
|
||||
|
||||
Apply to every change:
|
||||
|
||||
- CI green (modulo documented flaky)
|
||||
- No new `unwrap()` / `expect()` outside test code
|
||||
- No new external network surfaces without `validate_network_policy`
|
||||
- New env vars or config keys → `config.example.toml` entry + CHANGELOG note
|
||||
- Behavior changes user-visible → CHANGELOG entry calling out the change
|
||||
|
||||
When in doubt, defer to v0.8.29. A clean release of 4 P1 items beats
|
||||
a cluttered release of 12 with one regression.
|
||||
Reference in New Issue
Block a user