Uh oh!
There was an error while loading. Please reload this page.
feat(desktop): task ledger panel — snapshot pull, honest cancel, race-hardened refresh (#15 P0-task UI) - #553
Conversation
…sh (apache#15 P0-task UI) Read-mostly session task panel above the chat shell: status badge, subject, relative time, and a single user affordance — cancel. The ledger belongs to the model (TaskCreate/TaskUpdate); the renderer pulls snapshots over tasks:list/tasks:cancel invoke IPC and refreshes on sessions:changed reason 'task-updated', emitted by a notification decorator around the shared store so model tools, turn-tail reads and the cancel IPC all flow through one instance. The cancel status literal is pinned in the main-process handler; renderer args carry only ids. Empty ledger renders nothing.
…r-only refresh, fail-to-empty Four fixes from adversarial review of the panel slice: - cancelled records a user veto and is now frozen at the store layer (no status flip or subject edit; model and cancel IPC alike), and a completed task cannot be cancelled — a stale panel snapshot racing the model's finish must not destroy the completion record. Reopening completed work stays legal. TaskUpdate's description teaches the rule. - task-updated no longer triggers the full session-list re-pull (the ledger never changes list metadata), so a task-heavy run stops churning the sidebar. - task-updated no longer resets per-session event-stream health: it is not paired with a transcript re-pull, so it would mask a dead event stream while the model keeps updating tasks. - a failed tasks:list now clears the panel (guarded by the active- session check) instead of leaving the previous session's tasks rendered under the new session.
Third adversarial-review round (18-agent, high effort):
- session switch clears the panel synchronously before fetching, so the
previous session's rows (and their cancel ids) can never render under
the new session.
- with that guarantee, a failed tasks:list keeps the last known
snapshot instead of blanking the panel (the earlier fail-to-empty fix
silently unmounted the panel on one transient IPC failure).
- ledger snapshots are ordered by a monotonic sequence; an older
response can no longer overwrite a newer one.
- tasks:cancel now returns { outcome: 'cancelled' | 'already_terminal',
tasks }: a user click racing the model's own terminal transition
converges silently to the truth instead of a misleading retry toast,
and the returned snapshot removes the follow-up list round-trip.
cancelled -> cancelled with a status-only patch is an idempotent no-op
at the store (no write, no error).
- a11y: focus returns to the panel root when the cancel button unmounts
under focus, and an aria-live status line announces the outcome.
- task-updated effect semantics moved from source-regex contracts to
behavior probes in the effect-stability harness; dead empty-state
branch and CSS removed; border width tokenized per the new
border-width converge contract.UncertaintyDeterminesYou4ndMe
commented
Jul 5, 2026
验证数字补充(最终 HEAD c09c7b6,本地 darwin/node 22):
|
Astro-Han
commented
Jul 5, 2026
能增加一些截图,展示一下显示效果吗~ |
Adds a 'task-ledger' visual-smoke fixture: seeds the turn session with a tasks.json covering all four statuses (in_progress / pending / completed / cancelled) so `npm run screenshots:single -- task-ledger` renders the session task panel above the chat — status badges, the relative-time column, and the cancel affordance shown only on non-terminal rows. Gives reviewers a reproducible screenshot and closes the panel's missing visual baseline.
UncertaintyDeterminesYou4ndMe
commented
Jul 5, 2026
The ledger is persistent session state, unlike the transient tool / reasoning / streaming surfaces — so it moves out of the main column (where it competed with streaming output for vertical space) into its own right-side rail, following the ArtifactPane collapsible-aside precedent: 300px expanded, 32px collapsed strip with a 任务-count badge and an in-progress dot, localStorage-remembered collapse, null render for an empty ledger, and the artifact pane's bottom-drawer behavior at the narrow breakpoint. The rail is only the collapse shell — list rendering, cancel, focus recovery and aria-live stay in the controlled TaskLedgerPanel, now imported via a @maka/ui subpath export to keep the lazy-split discipline. ChatView drops its tasks/onCancelTask props.
UncertaintyDeterminesYou4ndMe
commented
Jul 5, 2026
布局更新(4005727):任务面板从聊天上方迁移到独立右侧可折叠任务栏。 动机:任务台账是持续状态,不该和工具调用/推理/流式输出争抢主栏垂直空间;右栏让它常驻可见、聊天主栏完整归还给对话流。 实现完全复用 ArtifactPane 的可折叠 aside 先例:
复现截图(浅/深/窄三个变体): npm --workspace @maka/desktop run screenshots:single -- task-ledger新截图随后由作者贴上。验证:typecheck 干净,@maka/ui 44/0,@maka/desktop 2047/0(含新增 rail 契约:折叠持久化、空台账不渲染、细条计数)。 |
UncertaintyDeterminesYou4ndMe
commented
Jul 5, 2026
jackwener
left a comment
There was a problem hiding this comment.
Reviewed against today's main. Two parts of this PR are genuinely strong and worth preserving through a rework: the race hardening is verified sound (shared monotonic snapshotSeq across refresh+cancel, applySnapshot gated on seq AND active-session identity — ordered, not debounced), and the CSS is fully governance-compliant (all tokens, sanctioned radius families, real @maka/ui primitives — zero violations found).
Why this needs rework rather than a rebase: main's ledger model has moved underneath the PR. packages/core/src/task-ledger.ts now defines SIX statuses (pending/in_progress/blocked/failed/completed/cancelled) with a transition state machine (canTransitionTaskStatus), evidence requirements, and reopen-via-explicitReopen — which explicitly allows cancelled→pending and completed→in_progress. The PR hard-codes the old 4-status world: task-ledger-panel.tsx status maps are missing blocked/failed (TS compile error after rebase), and the new explainTaskUpdateRejection pins 'cancelled is frozen; no resurrection' — mutually exclusive with main's reopen design. Please rebuild on main's semantics: drop explainTaskUpdateRejection (main owns transitions now), extend label/badge maps to all 6 statuses, decide badge treatment for blocked/failed. 7 files also conflict textually, concentrated in the ledger primitives; the SessionChangedReason/'task-updated' IPC seam itself is still clean.
Also address in the rework:
- Cancel is cooperative, not a turn-abort — the model only sees it at the next turn boundary (ledger tail fragment rebuilt per turn). That's a defensible design but the UI should say so: show 取消中 (advisory) distinct from 已取消, or note it in the panel.
already_terminalis silently swallowed: user clicks 取消 on a just-completed task and the row flips to 已完成 with no explanation. Add a toast/aria-live note for that outcome.- Tests: most of task-ledger-ui-contract.test.ts is regex-against-source (pins strings, not behavior). Keep the two real behavioral tests (onMutation containment, task-updated effect routing) and add behavioral coverage for the already_terminal race and snapshotSeq ordering — those are the claims the PR title makes.
One nice surprise: the implementation is a right-side collapsible rail that renders nothing when empty — better than the 'panel above the chat' the description promises. Update the description to match.
Happy to re-review after the rework; the hard parts (races, governance) are already right.





#15 task-tracking 的 UI 切片,接 #537(model-facing 原语,已合并)。
做了什么
聊天界面上方新增会话任务面板:实时展示模型台账(状态徽标 + 标题 + 相对时间),用户唯一操作是取消任务(叫停某项工作)。台账归模型所有,面板刻意只读为主——#15 原文:「not a UI-only checklist」。
架构(全部锚在现有先例)
SessionChangedReason+'task-updated';wiring 的onMutation观察者把每次台账变更(模型工具与取消 IPC 共享同一 wired store)桥到sessions:changedtasks:list/tasks:cancel(invoke);cancel 的 status 字面量钉死在 main 进程 handler,渲染层只传 idpackages/ui/src/task-ledger-panel.tsx受控组件;空清单不挂载,零视觉负担关键语义(三轮对抗审查驱动,共修复 9 项确认发现)
取消是不可逆的用户否决:
cancelled在 store 层冻结(模型不能翻回、不能改标题;想继续只能建新任务);completed不可被取消(用户的陈旧快照与模型完成竞态时,不能摧毁完成记录);重开 completed 合法;cancelled→cancelled 幂等(无写、无错)。取消对竞态诚实:
tasks:cancel返回{ outcome: 'cancelled' | 'already_terminal', tasks }——用户点取消恰逢模型改终态时,渲染层静默收敛到真实状态,不弹「请稍后重试」这种永远重试不通的误导 toast。刷新无竞态:会话切换同步清空(旧会话任务不会在新会话下渲染、stale 取消按钮不会错配 id);同会话请求单调 seq 排序(旧响应不覆盖新快照);瞬时失败保留最后已知快照(不再静默清空面板)。
信号最小化:task-updated 不触发全量会话列表刷新(台账不改列表元数据),不重置会话事件流健康(它不伴随 transcript 重拉,重置会掩盖死掉的事件流)。
a11y:取消按钮 unmount 后焦点回收到面板根;aria-live 播报结果;过 check-a11y。
验证
@maka/core/@maka/storage/@maka/runtime/@maka/ui/@maka/desktop五套件(数字见 PR 评论区补充)npm run typecheck/git diff --check干净