Repository navigation
The persistent "update available" header chip is never rendered — app.update_available is written and never read #7136
Description
Before you start
- I searched existing issues and this is not a duplicate. 我已搜索过现有 issue。
What happened? 发生了什么?
saw no notification
Steps to reproduce 复现步骤
Using codewhale - the update available notification for 0.10.2 was not given
Codewhale version 版本
codewhale 0.10.1
Where did it happen? 在哪里出现?
TUI (terminal interface)
Operating system 操作系统
Linux
How did you get Codewhale? 获取方式
Official script (codewhale.net/install.sh)
Provider and model 提供商和模型
No response
Terminal and shell 终端
No response
codewhale doctor (optional)
Logs, screenshots, anything else (optional)
The persistent "update available" header chip is never rendered — app.update_available is written and never read
Labels: bug
Surface: TUI (terminal interface)
Version: codewhale 0.10.1 (fead51eeea0f)
OS: Linux (Linux Mint 22.2, x86-64)
Confirmed present in: v0.10.1 tag and HEAD (687a38c6e, 2026-10-28) — 281 commits after the tag, still unreproduced.
What happened
A newer release was available and Codewhale never told me — or told me only for twelve seconds, at startup, four days before I noticed.
Running 0.10.1 with 0.10.2 published:
| Fact | Value |
|---|---|
| Installed | codewhale 0.10.1 (fead51eeea0f), /usr/local/bin/codewhale, 8 Oct |
v0.10.2 published |
2026-10-24T17:31:14Z |
| Session started | 2026-10-25T04:27:16Z |
| Update-check cache written | 2026-10-25T04:27:11Z — 5 seconds before the session row |
| Cache contents | {"checked_at_unix": 1792902431, "latest_tag": "v0.10.2"} |
So the startup check ran, reached GitHub about ten hours after the release, and correctly found v0.10.2. The session that was told is the session I was using — and I saw nothing across four days of continuous use.
Nothing suppressed it: no CODEWHALE_NO_UPDATE_CHECK, no NO_UPDATE_NOTIFIER, no CI variables set, no [update] section in config.toml. The default check interval is one hour, so a fresh launch re-checks.
The defect
app.update_available is assigned when the startup check completes, and nothing in the tree ever reads it. A repo-wide grep returns exactly three hits — the field declaration, its initialisation to None, and the assignment:
crates/tui/src/tui/app.rs:1676: pub update_available: Option<String>,
crates/tui/src/tui/app/init.rs:850: update_available: None,
crates/tui/src/tui/ui/event_loop.rs:2247: app.update_available = Some(notice.chip_label());
There is no render site, no serialisation, no non-Rust consumer. The write is dead.
The field's own doc comment describes the feature that is missing (app.rs:1672–1675):
/// Header chip label (e.g. `↑ v0.9.5`) set once by the fire-and-forget
/// startup version check when a newer stable release exists. Drives the
/// small persistent update chip in the header so the affordance survives
/// the transient toast without nagging (FINISH-0.9.4 #14).
pub update_available: Option<String>,And the assignment site repeats the claim (event_loop.rs:2240–2242):
// Transient toast for immediate visibility, plus a durable
// in-transcript notice so the prompt survives the toast TTL and
// stays actionable during a busy session (#3961). The persistent
// header chip keeps a quiet affordance after both (#14).#14 — the persistent chip — was implemented as a field, a label generator and a doc comment, but never wired to the header.
Why the test suite did not catch it
There is a test for the chip, and it passes:
// crates/tui/src/tui/ui/tests.rs:18665-18667
// The persistent header chip is quiet: version only, no action verb —
// the toast and this block carry the instructions (#14).
assert_eq!(notice.chip_label(), "↑ v0.8.47");It asserts the label generator returns the right string. It never asserts that any render path consumes it — so a fully working chip_label() with no consumer passes cleanly. That is the shape of the defect: the unit under test is correct, and the integration is absent.
What this costs a user
The three intended surfaces degrade to two, and the surviving durable one is weak:
| Surface | State | Reality |
|---|---|---|
| Toast | works | 12 seconds (VERSION_HINT_TOAST_TTL_MS = 12_000) |
| Transcript system message | works | scrolls away during a working session |
| Persistent header chip | absent | written and never read |
The failure mode is worst for the exact user the feature exists for: someone in a long session. The check runs once per process at startup; a session lasting days — mine is four days old — sees the toast once, at the moment of launch, when the terminal is still drawing. After that there is no indication at all, and the version drift is silent.
What I expected
A quiet, persistent affordance in the header reading ↑ v0.10.2 for as long as a newer release is known — as the doc comment and the #14 reference both describe. Clicking or a keystroke would show the update command.
Steps to reproduce
- Install any release older than the latest, e.g.
0.10.1, with0.10.2available. - Ensure no
[update]config and no suppression env vars. - Delete
~/.codewhale/update-check.jsonif present, then start a new session. - A toast appears for 12 seconds and one transcript line is written.
- Look at the header. There is no chip. It never appears, in this session or any later one, even though
app.update_availableisSome("↑ v0.10.2").
Suggested fix
Two lines and a render, in rough order:
- Render it. Emit the chip wherever the header's status chips are composed, gated on
app.update_available.is_some(). The label is already correct and quiet (version only), so no wording decision is needed. - Add an integration test that fails today. Assert that after the version check resolves with a newer tag, the composed header contains the chip text — not that
chip_label()returns a string. A test at the render boundary is what was missing. - Optionally, consider whether the 12-second toast alone is adequate for sessions that outlive the check interval; the chip was the answer to that question, which is why its absence matters.
Notes
- The install resolved to
InstallMethod::Binary(path has nocellar,node_modulesor.cargo/bin), so the notice would advertisecodewhale update. On this machine the binary isroot:rootin/usr/local/bin, so a self-update run as a normal user cannot replace it — a separate observation, not part of this defect, but it makes the advertised command fail with a permissions error rather than prompting for elevation. app.update_availableisNoneinitialised and never cleared. If a future render is added, check whether it should also be cleared when a check returns "no newer release" after previously finding one — the cache stores the latest tag, so a downgrade inlatest_tagis possible in principle.chip_label()already distinguishes a staged release (↑ v{latest} ready) from an unstaged one (↑ v{latest}), which suggests a download-and-stage flow was intended alongside the chip. Worth confirming whether that path exists before wiring the chip.- Reported from a read-only inspection of the source at
687a38c6eplus the on-disk state of a0.10.1install; no code was modified and no issue was filed.
Investigation claim — 2026-10-28
Coordinator verified on current Engine source that the update label is assigned but no rendering path reads it. Initial read-only scope is the TUI's existing footer and pet presentation (crates/tui/src/tui/phase_strip.rs, crates/tui/src/tui/pet_watch/full.rs) and their existing rendering fixtures. A safe persistent affordance must preserve higher-priority status and remote-control signals and handle narrow/ASCII layouts. Source is frozen for a contributor build; no fix or verification is claimed yet.
Activity
[-]he persistent "update available" header chip is never rendered — `app.update_available` is written and never read[/-][+]The persistent "update available" header chip is never rendered — `app.update_available` is written and never read[/+]fix(tui): retain update notice in the metrics line
Hmbown commentedon Oct 29, 2026
Fixed on main in 71bc853 and bd9d442: the update notice now stays in the metrics line and opens the update check when clicked, so it is no longer lost; thank you @ronohara for the report.