Uh oh!
There was an error while loading. Please reload this page.
feat(desktop): add Work Board Phase 1 capture/list MVP - #3135
Conversation
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughSummaryThis PR adds Work Board Phase 1 to the desktop session workbar. Users can create and manage work items in Inbox or the current project. The panel supports:
Source of truthThe PR extends the existing operational-state database through The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred. Scope and complexityThis is the smallest coherent Phase 1 solution. The IPC boundary, preload bridge, renderer hook, panel, styles, tests, and documentation connect the existing store to the workbar. The added complexity is necessary for:
No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff. ValidationWork Board IPC tests cover:
The PR summary reports successful main/preload builds, desktop typechecking, Work Board IPC tests, and Biome checks. The full desktop test suite runs in CI. Required check status is otherwise unverified here. Review-relevant risks
WalkthroughThe desktop app now exposes Work Board storage through IPC, preload, and renderer layers. The session workbar includes a localized Work Board panel with filtering and item lifecycle actions. IPC tests cover registration, mutations, validation, events, and removal. ChangesWork Board desktop feature
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk:🔵 Low · up to The Work Board adds persistence and paginated loading, but restored Work Board tabs may be rejected and a failed continuation load can hide already loaded items while retrying the first page instead of the failed page. The PR is mergeable with explicit owner awareness and follow-up on these bounded correctness issues. Sequence Diagram(s)sequenceDiagram
participant User
participant WorkBoardPanel
participant useWorkBoard
participant maka.workBoard
participant WorkBoardIpc
participant WorkBoardStore
User->>WorkBoardPanel: create or mutate item
WorkBoardPanel->>useWorkBoard: invoke operation
useWorkBoard->>maka.workBoard: call bridge API
maka.workBoard->>WorkBoardIpc: invoke IPC channel
WorkBoardIpc->>WorkBoardStore: execute operation
WorkBoardStore-->>WorkBoardIpc: return result
WorkBoardIpc-->>maka.workBoard: return typed result
WorkBoardIpc-->>useWorkBoard: emit workBoard:changed
useWorkBoard->>maka.workBoard: reload current snapshot
maka.workBoard-->>WorkBoardPanel: render updated items
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)
92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRemove the duplicate reload after a successful mutation.
The main process emits
workBoard:changedfor every successful mutation, and the effect on Lines 78-88 reloads the projection. Line 95 starts a second list request for the same mutation. Also,loadreturnsvoid, soawaitdoes not wait for that request. Delete the explicit reload and use the change signal as the single reload path.As per path instructions, “Flag concrete cases where code can be deleted or simplified.”
Source: Path instructions
apps/desktop/src/renderer/work-board-panel.tsx (1)
15-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep Work Board copy in
DesktopConversationCopy.
getWorkBoardPanelCopycreates a second locale schema for the same desktop UI. Move these strings into aworkBoardPanelsection ofDesktopConversationCopy, then deleteWorkBoardPanelCopyandgetWorkBoardPanelCopy. This keeps locale completeness enforced byUiCatalogand prevents new locales from silently receiving English panel copy.As per path instructions, determine whether it is the smallest coherent solution at the existing source of truth.
Source: Path instructions
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 4889c448-0587-41c7-a07d-79276c8b5340
📒 Files selected for processing (17)
apps/desktop/src/main/__tests__/work-board-ipc-main.test.tsapps/desktop/src/main/runtime-host-boot.tsapps/desktop/src/main/work-board-ipc-main.tsapps/desktop/src/preload/bridge-contract.d.tsapps/desktop/src/preload/preload.tsapps/desktop/src/renderer/app-shell.tsxapps/desktop/src/renderer/chat-workbar.tsxapps/desktop/src/renderer/locales/conversation-copy.tsapps/desktop/src/renderer/session-workbar-tabs.tsapps/desktop/src/renderer/session-workbar.tsxapps/desktop/src/renderer/styles.cssapps/desktop/src/renderer/styles/work-board.cssapps/desktop/src/renderer/use-work-board.tsapps/desktop/src/renderer/work-board-panel.tsxapps/desktop/src/shared/work-board-ipc.tsdocs/README.mddocs/work-board-phase1.md
Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
somewan820
commented
Aug 17, 2026
Addressed the review round in
Verification: full desktop typecheck passes, main build + Work Board IPC tests pass, Biome clean. Copilot could not review this round because the requesting account hit its review quota; the change will be re-checked once quota resets. |
somewan820
commented
Aug 17, 2026
@liugddx — Phase 1 (Work Board capture/list MVP) from the #2560 delivery plan is ready for review. It builds on the merged Phase 0 contract/store (#3028) and adds the workbar tab with Inbox/current-project filtering, create/rename/move/complete/reopen/archive/restore/delete, and main-process IPC ownership. CI and bot feedback have been addressed: Astryx surface inventory regenerated (failing check fixed), persisted tab-kind restore fixed, create/rename drafts survive failed mutations, accessibility cleaned up, and panel copy moved into Could you take a look when you have a moment? Happy to adjust anything. 简体中文@liugddx —— #2560 delivery plan 里的 Phase 1(Work Board capture/list MVP)已就绪,等待 review。它基于已合并的 Phase 0 契约/store(#3028),新增 workbar tab,支持 Inbox/当前项目过滤、新增/改名/移动/完成/重开/归档/恢复/删除,以及 main 进程 IPC 所有权。 CI 和机器人反馈已处理:Astryx surface inventory 已重新生成(失败的检查已修复)、持久化 tab-kind 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 有空的话麻烦看一下,需要调整的地方请告诉我。 |
liugddx
left a comment
There was a problem hiding this comment.
Review — problem framing & scope
Solid, disciplined engineering. My comments are almost entirely about how the problem is defined (in #2560), not the code in this PR, which is clean.
What it solves / how (my read, please correct if off)
- Solves: the "capture deferred work without interrupting the active task" atom from #2560 — Phase 1 (capture/list MVP).
- How: a read-only Work Board tab in the workbar;
WorkBoardStoreowned by the main process, renderer is a projection that reloads onworkBoard:changed; 6 fail-closed IPC handlers with a Result type; scope/creator/provenance/revision model. Correctly avoids Runtime Host, model tools, and turn-tail injection.
Execution quality is high: Result types, optimistic revision locking, single reload path (no second execution authority), IPC-layer tests. 👍
First-principles / Occam concerns on the definition
The problem is named after the solution. The irreducible need is "don't let me lose this idea; let me start it later." But #2560 defines it as a Work Board with Inbox/project scope + lifecycle + provenance + linked-Session projection. Those are names of the answer. This locks all later phases to a board shape before we've asked whether a much smaller entity would do.
Occam — cheaper entities exist for the same atom. For an Agent product, "write the deferred item into a project
TODO.md/ issue" satisfies most acceptance criteria in #2560 (local-first, survives restart, auditable, later Agent-readable) with near-zero new machinery. The Non-goals say "not a Linear/Jira replacement," yet the structure being built (board, scope, lifecycle, status projection) is a smaller-shaped skeleton of exactly that. Worth an explicit note on why a store + state machine is required over a file.Riskiest assumption is validated last. The load-bearing bet — will users actually return to the board and start tasks from it? — isn't exercised until Phase 3. Front-loading the store/state-machine/provenance and back-loading that validation is the reverse of lean. Consider a cheap end-to-end spike of the capture→revisit→start-task loop before investing in Phases 2–4.
Credit where due
The boundary discipline is genuinely first-principles and correct: not polluting the Session Task Ledger (#2290), not injecting into every model turn, not creating a second execution-state authority. That separation of user intent vs model execution state is the strongest part of the design and this PR honors it.
Ask before merge/continuation
- One paragraph in #2560 (or the Phase-1 doc) on why a dedicated store beats a project file for the atom — if it's provenance + Session linking, say so explicitly; that's the actual justification for the machinery.
- Consider resequencing so the capture→start-task loop gets a thin validation before Phase 2–4 build-out.
Net: Approve on execution; request a scope/justification note on the problem definition before committing further phases.
简体中文
工程执行扎实,我的意见几乎都针对 #2560 的问题定义,不是本 PR 的代码。
解决了什么 / 怎么解的:交付 #2560 的 Phase 1(捕获/列表 MVP)。主进程独占 WorkBoardStore,渲染进程只读投影、收到 workBoard:changed 后 reload;6 个 fail-closed IPC handler + Result 类型;scope/creator/provenance/revision 模型;刻意不进 Runtime Host、不暴露模型工具、不注入每轮 turn。质量高(乐观锁、单一 reload 路径、IPC 测试)。
第一性原理 / 奥卡姆的疑问(针对定义):
- 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
- 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目
TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。 - 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。
值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。
合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。
结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。
somewan820
commented
Aug 17, 2026
@liugddx — thanks for the review. Both asks are addressed in
Happy to adjust the wording if you would like the rationale stated differently. 简体中文@liugddx —— 感谢 review。两点已在
如果你希望这段 rationale 换个措辞,告诉我即可。 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 19 changed files in this pull request and generated no new comments.
Suppressed comments (3)
apps/desktop/src/renderer/work-board-panel.tsx:196
- The create field uses a raw
<input>, which bypasses the established Astryx input components used elsewhere in desktop panels (e.g.@astryxdesign/core/TextInputinapps/desktop/src/renderer/session-inspector-panel.tsx:243). Using the design-system input will improve consistent styling/behavior (focus ring, disabled styling, keyboard handling) and avoid the “raw control” blocker noted in the Astryx surface inventory.
<input
className="maka-work-board-create-input"
value={newTitle}
onChange={(event) => setNewTitle(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') void create();
}}
placeholder={copy.createPlaceholder}
aria-label={copy.createPlaceholder}
/>
apps/desktop/src/renderer/work-board-panel.tsx:54
- The rename field uses a raw
<input>(and manual Enter/Escape handling), which bypasses the established Astryx control components and can mis-handle IME composition (Enter/Escape while composing). In this codebase, text entry in panels typically uses@astryxdesign/core/TextInput(e.g.apps/desktop/src/renderer/session-inspector-panel.tsx:243) and guards composition / blur edge-cases similarly topackages/ui/src/inline-rename-input.tsx:25-52. Also,maka-work-board-rename-inputis referenced here but has no corresponding CSS rule, so styling will fall back to browser defaults.
This issue also appears on line 187 of the same file.
<input
className="maka-work-board-rename-input"
value={props.renameValue}
onChange={(event) => props.onRenameChange(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') props.onRenameSave();
if (event.key === 'Escape') props.onRenameCancel();
}}
aria-label={copy.rename}
/>
apps/desktop/src/renderer/use-work-board.ts:70
- The non-Error fallback message here is hard-coded English ('Work Board load failed'), which can leak into non-English locales and is inconsistent with other renderer error normalization (which typically uses
String(error)and lets the UI supply localized titles). Consider usingString(error)for the detail field, sinceWorkBoardPanelalready provides a localized banner title.
error: error instanceof Error ? error.message : 'Work Board load failed',
liugddx
commented
Aug 17, 2026
Follow-up: concrete next steps (actionable)My earlier comment was framing/critique. Here is what I'm actually asking for, as a checklist. This PR is approvable as-is — items below are gates on continuing to Phase 2–4, plus two tiny things to land with this PR. Land with this PR (small)
Gate before Phase 2 (side-chat capture)
Then resume the planned order
What NOT to change (keep doing this)
TL;DR for the maintainer: merge this; add the two doc notes; then build the Phase 3 "Start task" spike before Phase 2 to validate the loop; then continue #2560's plan unchanged. 简体中文上一条是框架性评论,这条是给你的可执行清单。本 PR 可以直接合并;下面是"继续做 Phase 2-4"的前置门槛,外加两个随本 PR 落地的小项。 随本 PR 落地(小)
Phase 2 之前的门槛
恢复既定顺序
不要改(继续保持)
一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。 |
somewan820
commented
Aug 17, 2026
@liugddx — checklist items are landed in
The merge conflict with 简体中文@liugddx —— 清单项已在
与 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 19 changed files in this pull request and generated no new comments.
Suppressed comments (5)
apps/desktop/src/renderer/work-board-panel.tsx:191
- The create field is also a raw
<input>and triggers create on Enter even during IME composition. For consistency and correct IME/keyboard behavior, switch to the design-systemTextInputand ignore Enter while composing.
<input
className="maka-work-board-create-input"
value={newTitle}
onChange={(event) => setNewTitle(event.target.value)}
onKeyDown={(event) => {
apps/desktop/src/renderer/work-board-panel.tsx:54
- The rename field is a raw
<input>, which diverges from the renderer’s design-system controls, and it also commits on Enter even during IME composition (can prematurely save while composing CJK text). UseTextInputand guardevent.nativeEvent.isComposing(seepackages/ui/src/inline-rename-input.tsx).
This issue also appears on line 187 of the same file.
<input
className="maka-work-board-rename-input"
value={props.renameValue}
onChange={(event) => props.onRenameChange(event.target.value)}
onKeyDown={(event) => {
apps/desktop/src/renderer/work-board-panel.tsx:4
- This panel uses raw
<input>controls later in the file, but the renderer convention elsewhere is to use the design-systemTextInput(for consistent styling, sizing, and keyboard/IME behavior). Add theTextInputimport so the raw inputs can be replaced with the standard component.
import { useMemo, useState } from 'react';
import { Banner, EmptyState, Spinner } from '@astryxdesign/core';
import { Button } from '@astryxdesign/core/Button';
import { useUiLocale } from '@maka/ui';
apps/desktop/src/renderer/use-work-board.ts:71
- This fallback error string is hard-coded in English. Since the panel already provides a localized
copy.loadFailedtitle, consider omitting the non-Error fallback (or leaving it undefined) to avoid showing an English-only message in non-English locales.
items: current.items,
loading: false,
error: error instanceof Error ? error.message : 'Work Board load failed',
}));
apps/desktop/src/main/work-board-ipc-main.ts:151
- For non-
WorkBoardStoreErrorfailures, this forwardserror.messageback to the renderer. That can leak internal details (e.g. sqlite errors) to the UI. Prefer a generic message for unknown errors and rely on store errors for user-facing detail.
return {
code: 'unknown',
message: error instanceof Error ? error.message : 'Work Board operation failed',
};
liugddx
left a comment
There was a problem hiding this comment.
Thanks Quinn — the CAS + fork-surviving excerpt + Session linking is a fair reason a flat TODO.md can't cover, so the store shape reads as intentional now. Nice, disciplined boundary work too.
Approving. One thing to hold onto for later: before we build out Phase 2/4, let's land the thin capture → revisit → start-as-task loop first and confirm people actually come back to the board — as the doc now notes. No changes needed here.
简体中文
谢谢 Quinn —— CAS + fork 删除后仍存留的 excerpt + Session 关联,确实是 TODO.md 覆盖不了的,现在这套 store 的范围读起来是有意为之的。边界也做得很克制,赞。
Approve。后续记一个点:在铺开 Phase 2/4 之前,先把 thin 的 捕获 → 回看 → 启动任务 闭环落地,确认用户真的会回到看板——正如文档现在所记。本 PR 无需再改。
somewan820
commented
Aug 17, 2026
@Astro-Han — could you take a quick look at this one when you have a moment? Status:
No changes are expected from you unless something stands out; an approval would let this merge. Thanks! 简体中文@Astro-Han —— 方便的话请快速看一眼这个 PR:
除非有需要指出的问题,不需要额外改动;approve 后即可合并。谢谢! |
Astro-Han
left a comment
There was a problem hiding this comment.
The overall architecture is sound: WorkBoardStore remains the single mutation and persistence authority in Desktop main, the renderer is an IPC projection, and this does not create a second Runtime Host or Task Ledger authority. I also independently verified that the previous review threads are resolved on f47d56a, the existing approval covers this head, the PR is mergeable/clean, and the relevant CI is green.
I found no P0/P1 issues, but I think two P2 gaps should be closed before adding another approval:
[P2] Preserve the store's pagination contract in the renderer projection.
useWorkBoard()discardsWorkBoardPage.nextCursor, while the store intentionally has no total item cap and defaults to 50 results. Once an Inbox or project scope exceeds 50 active plus archived items, older items silently become unreachable; recently updated archived items can also crowd an older active item off the only page. Please retain the cursor and expose a bounded Load more path. Raising the limit to 100 would only move the cutoff.[P2] Keep the selected filter and effective mutation scope identical. If the current project disappears while the Project filter is selected,
scopeForFilter()silently falls back to Inbox, but the Project button and section label remain active.create()then writes the item to Inbox under a surface that still says Current project. Please derive one effective filter/scope and use it consistently for the label, query, and create operation, or atomically return the filter to Inbox whenprojectIdbecomes null.
One non-blocking follow-up:
- [P3] Guard composing Enter in create and rename. Both raw inputs treat every Enter as submission. Enter is also how CJK IMEs confirm a candidate, so this can create or rename an item with unfinished text. Reusing the established input seam, or applying the existing
isComposingguard fromInlineRenameInput, would close this cleanly.
The current Work Board tests exercise the main-process IPC/store boundary, but the Electron suite contains no Work Board renderer journey, so green CI does not cover these behaviors. A focused renderer/Electron regression for pagination/scope would provide the missing evidence without broadening the suite.
Go/stop: hold this head for the two small P2 renderer fixes; the P3 does not need to block. No PR split or architectural rewrite is needed. After those fixes, the Phase 1 shape looks ready to approve.
Codex assisted this review by tracing the current diff, existing feedback, owner boundaries, and CI evidence. The human reviewer is responsible for the final judgment and merge decision.
简体中文
整体架构是正确的:WorkBoardStore 仍是 Desktop main 中唯一的变更与持久化权威,renderer 只是 IPC 投影,也没有引入第二套 Runtime Host 或 Task Ledger 权威。我还独立确认了当前 f47d56a 上前序 review threads 均已解决、已有批准覆盖该 head、PR 可干净合并且相关 CI 全绿。
没有 P0/P1,但建议在新增 Approve 前关闭两个 P2:
- [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃
nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。 - [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在
projectId变为 null 时原子回到 Inbox。
一个非阻塞 follow-up:
- [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用
InlineRenameInput已有的isComposingguard 即可。
当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。
**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。
本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。
somewan820
commented
Aug 17, 2026
@Astro-Han — both P2 gaps and the P3 are fixed in
Verification: full desktop typecheck, main build + Work Board IPC tests, and Biome all pass. On the renderer/Electron regression suggestion: the desktop suite currently has no renderer test harness for this panel; I'd suggest adding a focused e2e journey in a follow-up rather than blocking this PR. Happy to add it after merge if you'd like. 简体中文@Astro-Han —— 两个 P2 和 P3 都已在
验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。 关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。 |
72766e1 to
f0d8770ComparePhase 1 slice 1: the Desktop main process owns WorkBoardStore and registers workBoard:list/create/update/archive/unarchive/remove handlers plus a workBoard:changed signal. Renderer code stays read-only through IPC; Runtime Host and model tools are not involved. Generated-by: Codex
Phase 1 slice 2: shared IPC result/change types, window.maka.workBoard namespace in the preload bridge, and a renderer useWorkBoard hook that reloads on the workBoard:changed signal. Generated-by: Codex
Phase 1 slice 3: compact capture/list MVP in the session workbar with Inbox / current-project filtering, manual create, rename, move, complete, reopen, archive, restore, and delete. The panel is a read-only renderer projection over the main-process WorkBoardStore IPC. Generated-by: Codex
Phase 1 slice 4: document the workbar surface, boundary, and main-process IPC ownership for the capture/list MVP. Generated-by: Codex
- accept the persisted work-board tab kind in isSessionWorkbarTabKind; - keep create/rename drafts when a mutation fails; - drop the incomplete tablist role and derive the panel aria-label from the filter; - rely on the workBoard:changed signal as the single reload path after mutations; - move Work Board panel copy into DesktopConversationCopy; - remove the branch-specific status from the Phase 1 doc; - regenerate the Astryx surface inventory for the new panel and stylesheet. Generated-by: Codex
Add the maintainer-requested rationale for a store over a project file (typed provenance, stable identity/CAS under concurrent writers, Session linking and result refs as the load-bearing reasons) and record the plan to validate a thin capture -> revisit -> start-as-task loop before Phases 2/4. Generated-by: Codex
Per maintainer checklist: state provenance + Session linking as the explicit justification for the store, write down the assumption Phase 3 must prove, and gate Phases 2/4 behind a thin flag-gated start-as-task spike. Generated-by: Codex
… Board panel Address Astro-Han P2/P3: - useWorkBoard retains nextCursor and exposes a bounded loadMore path; - the panel resets to Inbox when the current project disappears, keeping the filter, label, query, and create scope identical; - create and rename ignore Enter while an IME composition is active. Generated-by: Codex
…ation failures Address CodeRabbit: refresh or loadMore failures no longer replace the list with a fatal error when items already exist; a non-fatal banner keeps the items visible and retry re-runs the failed cursor (or the first page for refresh failures). Generated-by: Codex
- close the WorkBoardStore during desktop shutdown - pass revision CAS guards through all renderer mutations - preserve loaded pagination during mutation refreshes - use Astryx TextInput with IME-safe create and rename handling Generated-by: Codex
Query Work Board project scopes across canonical and absorbed project identities so relinking does not hide existing items. Normalize the identity set and bind pagination cursors to the complete scope. Generated-by: Codex
Keep the optimistic-concurrency revision captured when rename starts, so a change signal cannot turn a stale draft into an accepted overwrite. Generated-by: Codex
The pull request became ready after the last synchronize event; retrigger the required CI workflow for the verified head. Generated-by: Codex
Hash the normalized project identity filter before placing it in an opaque pagination cursor. This preserves the full alias set as the query authority without generating cursors that exceed their own validation limits. Generated-by: Codex
f0d8770 to
1c8d833Comparesomewan820
commented
Aug 24, 2026
Final verification on current head
The remaining merge-state blocker is |
jackwener
left a comment
There was a problem hiding this comment.
Review of exact head 5d4481ba648963a9488b78fbc134acbdd9bc0ed7: not ready to merge (2 P2, 1 P3).
The exact-head test and package checks are green. I also ran build:test, focused Core/Storage/Desktop tests (54/54), and the Composer mention-menu contract tests (10/10). A synthetic merge with current main built successfully and passed the same focused 54-test suite. The findings are inline below.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Astro-Han
left a comment
There was a problem hiding this comment.
Approving on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4, with test and package terminal green on that exact head.
I re-derived every finding I had left open rather than trusting the earlier round.
The paginated-refresh P2 is properly fixed.use-work-board.ts now re-fetches to the previously loaded extent through listWindow, which pages up to loadedItemCountRef with WORK_BOARD_PAGE_SIZE_MAX and drops duplicates by id, so a workBoard:changed signal after 50+10 items no longer collapses the view to the first page. The revision guard still discards responses from superseded loads, and a continuation failure keeps the existing items with a retry on the same cursor instead of replacing the list.
The row-handler P3 is fixed better than I asked. Splitting WorkBoardRow's props into an active | archived discriminated union means the archived branch cannot be handed active-only callbacks at all — the compiler enforces what was previously a convention. That is a stronger fix than dropping the unused handlers.
The double-submit guard on create is correct.createPendingRef is checked and set synchronously before the first await, so a second Enter cannot slip through; the createPending state is only for rendering, and the finally restores both on the failure path.
The Side Chat disposal fencing holds.performCompanionTurn re-checks isDisposed() after each await, and a fork created inside the call is cleaned up when disposal wins the race before the send. The new tests construct the race with deferred promises rather than asserting a single ordering, so they lock the behaviour rather than the implementation.
One observation, not a finding: when disposal wins after a successful send, the created fork is not scheduled for cleanup. That looks deliberate — a run is already in flight, and recoverOrphanedCompanionCopies exists for exactly this reclamation — but if that is the intent, it is worth a comment, since the two neighbouring disposal branches do clean up and this one silently does not.
Merging this on @astrohan's decision.
简体中文
已在 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4 上 approve,该 exact head 的 test 与 package 均为终态绿。
我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。
分页刷新那条 P2 确实修好了。use-work-board.ts 现在通过 listWindow 按之前已加载的规模重新取数:以 WORK_BOARD_PAGE_SIZE_MAX 翻页直到 loadedItemCountRef,并按 id 去重。因此加载了 50+10 条之后再来一次 workBoard:changed,视图不会再塌回第一页。代次守卫仍会丢弃被取代的加载结果;续页失败则保留已有条目并对同一 cursor 提供重试,而不是整体替换成错误态。
行处理器那条 P3 修得比我要求的更好。 把 WorkBoardRow 的 props 拆成 active | archived 判别联合后,archived 分支根本不可能拿到只属于 active 的回调——原先靠约定维持的东西现在由编译器保证。这比单纯删掉多余的 handler 更强。
创建的防重复提交守卫是对的。createPendingRef 在第一个 await 之前同步检查并置位,第二次回车无法穿过;createPending 状态只用于渲染;finally 在失败路径上也会把两者复位。
Side Chat 的 disposal 围栏站得住。performCompanionTurn 在每个 await 之后都重新检查 isDisposed(),且当 disposal 抢在 send 之前时,本次调用内创建的 fork 会被安排清理。新增的测试用 deferred promise 真正构造了竞态,而不是只断言某一种顺序——锁的是行为而不是实现。
一条观察,不是 finding:当 disposal 抢在成功 send 之后时,已创建的 fork 不会被安排清理。看起来是有意的——此时 run 已经发出,而 recoverOrphanedCompanionCopies 正是为这种回收准备的——但如果确实是有意的,建议补一句注释,因为相邻两个 disposal 分支都会清理,唯独这一处不清理。
本 PR 由 @astrohan 决定合并,我按其决定执行。
Astro-Han
commented
Aug 24, 2026
Uh oh!
There was an error while loading. Please reload this page.
Summary
Work Board Phase 1 (capture/list MVP) from #2560, built on the merged Phase 0 contract and store (#3028).
Adds a compact Work Board tab to the session workbar:
Boundary: the Desktop main process owns
WorkBoardStore; the renderer is a read-only IPC projection that reloads on theworkBoard:changedsignal. No Runtime Host involvement, no model-visible tools, no turn-tail injection.linkedSessionsand the linked-session projection remain deferred to Phase 3.Refs #2560
Verification
@maka/desktopmain and preload builds pass@maka/desktoptypecheck passes (preload / main / renderer / storybook)Checklist
Does this PR entail a change in behavior?
AI use
Select exactly one:
Tool(s) and scope: Codex (OpenAI) — implementation, tests, and documentation for Work Board Phase 1; the contributor reviewed the output and owns the final result. Affected commits carry
Generated-by: Codextrailers.