MUL-4884: fix(issues): anchor the working chip on issues the filter actually shows#5540
Open
NevilleQingNY wants to merge 2 commits into
Open
MUL-4884: fix(issues): anchor the working chip on issues the filter actually shows#5540NevilleQingNY wants to merge 2 commits into
NevilleQingNY wants to merge 2 commits into
Conversation
…ows (MUL-4884) The header chip showed three units at once: the number counted distinct issues, the avatar stack counted agents (with a rival "+N"), and the hover card counted tasks — under a label with no noun at all. Each figure was self-consistent; together they read as a miscount. c4209ec flipped the number from agents to issues but left the other three surfaces on their old units. The chip now says one thing — "N issues in progress" — where N is the number of rows clicking it leaves. Data layer: - The count is no longer re-derived from the task snapshot. The surface exposes `workingScopeIssues`: the render pipeline's own output with `workingOnly` forced on, so "chip count === row count" holds by construction instead of by convention. It previously counted running issue_ids against the PRE-filter set, so any active status/assignee/label filter — or a sub-issue hidden by the display toggle — made the chip disagree with the list it was filtering. - Chat/autopilot tasks carry issue_id "" (not null). They used to collapse into one bucket and read as a phantom issue, inflating the count by exactly one whenever any were running. They are now bucketed out and disclosed. - The list loads one page per status (50), so running work can exist past the window. The count stays list-anchored — counting rows a click cannot show would break the chip's whole promise — and the gap is stated in the hover card rather than dropped silently. UI: - Avatar stack is ambience, not a statistic: no "+N"; the tail fades and the exact roster lives in the hover card. - Hover card groups rows by issue, names both counted units in its header ("3 issues in progress · 4 tasks"), and footnotes anything excluded — only when non-zero. - Colour is two-step: idle activity is a brand tint, the filled state is reserved for "filter is ON". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…tually draw (MUL-4884) Review found two modes where `workingScopeIssues` still came from a different set than the one on screen — the exact drift this change set is meant to remove. Swimlane: scoped to the statusless `swimlaneIssues`, but SwimLaneView draws its cards from `issues` (status filter applied) and only uses the statusless set for LANE DISCOVERY. A status-filtered swimlane counted rows the canvas never drew. Swimlane needs no branch at all — it renders the flat filtered list like board and list do — so the special case is gone rather than corrected. Gantt: the canvas applies two rules of its own before drawing — a row needs a date to be placed, and done/cancelled hide unless `ganttShowCompleted` is on — so a done issue with a running agent counted toward the chip while the canvas refused to draw it. Those rules now live in the surface (`ganttCanvasRows`) instead of privately inside GanttView, and both `filteredGanttIssues` and the working scope go through them. Mirroring the rules in a second place would have reproduced the original bug with extra steps; hoisting them means the chip narrows the same set the canvas draws, by construction. GanttView goes back to being a renderer: it orders rows, it does not decide which ones exist. Regression tests cover both, and each was confirmed to fail against the code it guards: swimlane asserts the statusless lane source still holds the wider set, so a revert fails loudly rather than passing by luck; gantt covers the hidden done row, the undated row, and the show-completed toggle widening both the canvas and the scope together. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes MUL-4884.
方案 A(issue 锚定),按 naiyuan 的风格约束实现。主 chip 只说一件事:「N 个 issue 进行中」,N = 点击后列表剩下的行数。
根因回顾
芯片把三个口径并排摆在一起:数字数 issue、头像堆数 agent(还带一个抢戏的
+N)、悬浮卡数 task,而标签「工作中」没有主语。三个数各自都没算错,凑在一起就读成"算错了"。成因是c4209ec7c只把数字从 agent 口径改成 issue 口径,另外三个面留在旧口径。数据层三项修复
1. 幽灵 issue(
issue_id === "")packages/core/types/agent.ts:248写明 chat/autopilot 的 taskissue_id是空字符串(不是 null)。原来new Set(running.map(tk => tk.issue_id))把它们全塌成一个""桶 → 只要有 ≥1 个这类 running task,计数恒定多 1,且这个桶点开筛选匹配不到任何行。现在在
deriveWorkingChipView里显式分桶(参考surface/activity.ts:63的写法),不计入数字,并在悬浮卡里单独标注"不计入"——不静默丢弃,也不混进 issue 分组。2. 计数改用过滤后的可见集合
取舍:没有把"过滤后的集合"当参数传给芯片,而是让芯片彻底不再自己派生这个数。
新增
workingScopeIssues(use-issue-surface-data.ts):把渲染列表的那条 pipeline 原样跑一遍、只把workingOnly强制打开。它和列表用的是同一个applyIssueFilters、同一份 filter state、同一个数据源。这样 "芯片数字 = 列表行数"是构造性成立的,不是靠约定维持的——打开筛选只是往同一条 pipeline 上加
workingOnly,所以无论筛选当前开还是关,这个集合就是"点击后的列表"。按视图模式分支,镜像渲染分支:gantt 用自己的 scheduled-only 投影,assignee board 从
groups走,swimlane 不吃 status 筛选。3. 分页天花板(
ISSUE_PAGE_SIZE = 50)——选了「标注」而不是「去掉分页影响」取舍理由:这两个目标是冲突的,只能二选一。
列表每个 status 只取首页 50 条。如果把计数改成"不受分页影响"(直接从 snapshot 全量数),数字就会包含点击后根本显示不出来的行——那恰好打破方案 A 的核心承诺(数字 = 点击结果)。
所以计数继续锚定列表(相对于承诺,它是准确的:点击后确实就这些行),漏掉的部分在悬浮卡里显式说出来:
这条同时覆盖"被用户筛选条件排除"和"在已加载页之外"两种情况——对用户来说都是"不在你现在看的范围里",没必要区分成两条。零的时候不显示。
如果之后要真正消灭这个缺口,正确的解法是服务端出一个 workspace 级的 running-issue 计数接口,不在这个 PR 的范围里。已知技术债:计数范围受每状态 50 条首页限制,目前靠悬浮卡标注而非消除。
UI(方案 A + 风格约束)
+N:新增overflow="fade",超出 3 个时最后一个头像淡出,精确名单在悬浮卡。其他两个调用方(issue-agent-header-chip/issue-agent-activity-indicator)默认仍是count,行为不变。N 个 issue 进行中。窄屏只显示数字,但aria-label始终是完整句子。3 个 issue 进行中 · 4 个 task)+ 按 issue 分组的行;只在非零时追加最多两条 footnote。没有统计卡片、没有图标堆砌。border-brand/30 bg-brand/5(弱蓝提示),筛选开启才bg-brandfilled。有测试钉住这个契约。conventions.zh.mdx:UI 短句里task/issue保持小写英文(与相邻的hover_header_tasks一致),全角标点。注意:Lalo mock 里写的是「4 个任务」,我按 conventions 用了「4 个 task」。如果 naiyuan 更想要「任务」,改 4 个 locale 的一行即可。filter_active_label原文案「正在查看工作中的智能体」说的是 agent,但这个筛选过滤的是 issue —— 一并改成「正在查看进行中的 issue」。chip_label(无主语的「工作中」),4 个 locale 同步。验证
pnpm exec tsc --noEmit(views):0 error。pnpm exec vitest run(views 全量):215 files / 2182 tests 全绿,含 i18n parity(en/zh-Hans/ja/ko 四语同步)。issue-surface.tsx那条 unused eslint-disable 在 origin/main 上就有)。端到端钉住核心承诺的新测试(
use-issue-surface-controller.test.tsx,跑真实 controller + 真实 query pipeline,只 mock API):workingScopeIssues与issues(渲染行)逐项相等;showSubIssues关闭时,只跑在子 issue 上的活不计入(否则芯片说 1、点开 0 行);issue_id === ""的 chat task 不产生幽灵行。外加芯片/悬浮卡层的单元测试:截图同款场景(4 task / 4 agent / 3 issue)渲染成「3 issues in progress」+ 无
+N;空 issue_id 分桶;颜色两层契约;中英文案。未做的验证:没有起真实 backend + daemon 跑一遍界面,所以视觉呈现(间距、淡出观感、实际 dark mode 蓝)没有截图确认——数字契约是靠上面的 controller 级测试证明的,样式部分建议 naiyuan 本地扫一眼。
🤖 Generated with Claude Code