Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(desktop): add Work Board Phase 1 capture/list MVP by somewan820 · Pull Request #3135 · apache/maka · GitHub
Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): add Work Board Phase 1 capture/list MVP by somewan820 · Pull Request #3135 · apache/maka · GitHub
Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): add Work Board Phase 1 capture/list MVP by somewan820 · Pull Request #3135 · apache/maka · GitHub
Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(desktop): add Work Board Phase 1 capture/list MVP by somewan820 · Pull Request #3135 · apache/maka · GitHub
Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): add Work Board Phase 1 capture/list MVP by somewan820 · Pull Request #3135 · apache/maka · GitHub
Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): add Work Board Phase 1 capture/list MVP by somewan820 · Pull Request #3135 · apache/maka · GitHub
Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(desktop): add Work Board Phase 1 capture/list MVP by somewan820 · Pull Request #3135 · apache/maka · GitHub
Skip to content

feat(desktop): add Work Board Phase 1 capture/list MVP - #3135

Merged
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1
Aug 24, 2026
Merged

feat(desktop): add Work Board Phase 1 capture/list MVP#3135
Astro-Han merged 23 commits into
apache:mainfrom
somewan820:feat/2560-work-board-phase1

Conversation

@somewan820

@somewan820somewan820 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

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:

  • global Inbox and current-project filtering;
  • manual create, rename, move (Inbox <-> project), complete / reopen, archive / restore, and delete;
  • empty, loading, and error states;
  • local-first persistence through the existing operational-state database.

Boundary: the Desktop main process owns WorkBoardStore; the renderer is a read-only IPC projection that reloads on the workBoard:changed signal. No Runtime Host involvement, no model-visible tools, no turn-tail injection. linkedSessions and the linked-session projection remain deferred to Phase 3.

Refs #2560

Verification

  • @maka/desktop main and preload builds pass
  • @maka/desktop typecheck passes (preload / main / renderer / storybook)
  • Work Board IPC tests pass (2/2)
  • Full desktop test suite runs in CI; several local suites require storage-root permissions unavailable in the sandbox

Checklist

  • Tests cover the change and fail without it (IPC and store layers)
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

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: Codex trailers.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

This 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:

  • Create and rename items.
  • Complete and reopen items.
  • Move items between Inbox and projects.
  • Archive and restore items.
  • Delete items.
  • Pagination with “Load more.”
  • Loading, error, retry, and empty states.
  • Chinese and English labels.
  • IME-safe create and rename input handling.

Source of truth

The PR extends the existing operational-state database through WorkBoardStore. It does not create a parallel persistence path.

The main process owns the store. The renderer receives a read-only IPC projection. Successful mutations emit workBoard:changed, which triggers renderer reloads.

Runtime Host integration, model-visible tools, turn-tail injection, and linked-session projections remain deferred.

Scope and complexity

This 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:

  • Structured IPC success and error results.
  • Input validation.
  • Change-event signaling.
  • Revision-guarded concurrent loads.
  • Cursor-based pagination and deduplication.
  • Archive-before-remove enforcement.
  • Consistent scope handling when projects disappear.
  • Preservation of create and rename drafts after failed mutations.

No code or tests can be removed or simplified without weakening behavior or regression coverage based on the current diff.

Validation

Work Board IPC tests cover:

  • Handler registration.
  • Item creation and listing.
  • Change-event emission.
  • Lifecycle mutations.
  • Archive-before-remove enforcement.
  • Invalid input rejection.
  • Final item removal.

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

  • The PR changes the user-visible desktop workbar and adds the public maka.workBoard preload API. Material changes in these areas require independent human review under repository policy.
  • The PR changes desktop IPC behavior and exposes item mutation operations across the main/preload boundary. Material security or public-contract changes require independent human review under repository policy.
  • The PR adds persisted work-board tab support and changes tab validation and restoration behavior. Material release or user-data behavior changes require independent human review under repository policy.
  • The PR adds localized user-visible copy and updates the Astryx surface inventory. Material governance or release-process changes require independent human review under repository policy.
  • The PR adds persisted Work Board item lifecycle operations, including archive and delete. Material user-data behavior changes require independent human review under repository policy.
  • Required checks are not directly verified here. The person performing the merge must review the final diff. A maintainer makes the final determination.

Walkthrough

The 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.

Changes

Work Board desktop feature

Layer / File(s)Summary
IPC boundary and lifecycle handlers
apps/desktop/src/shared/work-board-ipc.ts, apps/desktop/src/main/work-board-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
Defines typed IPC results and change events. Registers list and mutation handlers with validation, error conversion, and change notifications. Adds lifecycle and registration tests.
Typed preload bridge
apps/desktop/src/preload/bridge-contract.d.ts, apps/desktop/src/preload/preload.ts
Exposes typed Work Board operations and change-event subscriptions to the renderer.
Renderer data and mutation state
apps/desktop/src/renderer/use-work-board.ts
Loads Work Board snapshots, suppresses stale requests, handles errors and retries, subscribes to changes, and wraps mutations.
Workbar panel and user interface
apps/desktop/src/renderer/session-workbar-tabs.ts, apps/desktop/src/renderer/session-workbar.tsx, apps/desktop/src/renderer/app-shell.tsx, apps/desktop/src/renderer/chat-workbar.tsx, apps/desktop/src/renderer/work-board-panel.tsx, apps/desktop/src/renderer/locales/conversation-copy.ts, apps/desktop/src/renderer/styles.css, apps/desktop/src/renderer/styles/work-board.css
Adds the persisted Work Board tab and launcher entry. Renders filtering, creation, renaming, completion, scope changes, archiving, restoring, and deletion with localized copy and styling. Passes the current project ID to the panel.
Phase 1 documentation
docs/work-board-phase1.md, docs/README.md, docs/astryx-surface-file-inventory.md, docs/astryx-surface-file-inventory.paths
Documents the Phase 1 Work Board surface and records the added renderer files in the surface inventory.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk:🔵 Low · up to 8d761

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
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Ai Use Disclosure⚠️ WarningThe description discloses Codex use but selects neither required AI-use declaration; all nine PR commits have valid standalone Generated-by: Codex trailers.Select “Generative tooling made a substantive contribution” and state Codex and its scope. See “Human ownership and AI attribution” in CONTRIBUTING.md.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
Title check✅ PassedThe title clearly and concisely identifies the desktop Work Board Phase 1 capture/list MVP, which is the main change.
Description check✅ PassedThe description includes the required summary, verification, AI use, checklist, behavior change, issue reference, scope, and known test limitation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/desktop/src/renderer/use-work-board.ts (1)

92-97: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the duplicate reload after a successful mutation.

The main process emits workBoard:changed for 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, load returns void, so await does 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 win

Keep Work Board copy in DesktopConversationCopy.

getWorkBoardPanelCopy creates a second locale schema for the same desktop UI. Move these strings into a workBoardPanel section of DesktopConversationCopy, then delete WorkBoardPanelCopy and getWorkBoardPanelCopy. This keeps locale completeness enforced by UiCatalog and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 18c526c and 32b4184.

📒 Files selected for processing (17)
  • apps/desktop/src/main/__tests__/work-board-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/work-board-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/app-shell.tsx
  • apps/desktop/src/renderer/chat-workbar.tsx
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/session-workbar-tabs.ts
  • apps/desktop/src/renderer/session-workbar.tsx
  • apps/desktop/src/renderer/styles.css
  • apps/desktop/src/renderer/styles/work-board.css
  • apps/desktop/src/renderer/use-work-board.ts
  • apps/desktop/src/renderer/work-board-panel.tsx
  • apps/desktop/src/shared/work-board-ipc.ts
  • docs/README.md
  • docs/work-board-phase1.md

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment threadapps/desktop/src/renderer/session-workbar-tabs.ts Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threadapps/desktop/src/renderer/work-board-panel.tsx Outdated
Comment threaddocs/work-board-phase1.md Outdated
@github-actions
github-actionsBot requested a lite review from CopilotAugust 17, 2026 03:34

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820

Copy link
Copy Markdown
ContributorAuthor

Addressed the review round in 57dde789c:

  • CI: regenerated the Astryx surface inventory so work-board-panel.tsx and work-board.css are tracked (fixes the failing astryx_surface check).
  • Inline findings: isSessionWorkbarTabKind accepts work-board; create/rename drafts survive failed mutations; incomplete tablist role removed; branch-specific doc status removed.
  • Nitpicks: mutations now rely on the workBoard:changed signal as the single reload path (no duplicate list), and panel copy moved into DesktopConversationCopy so locale completeness stays enforced.

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

Copy link
Copy Markdown
ContributorAuthor

@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 DesktopConversationCopy. Desktop typecheck, main build, Work Board IPC tests, and Biome all pass.

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 恢复已修复、失败时不再清空新增/改名草稿、可访问性已清理、面板文案已并入 DesktopConversationCopy。desktop typecheck、main build、Work Board IPC 测试和 Biome 均通过。

有空的话麻烦看一下,需要调整的地方请告诉我。

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:50

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@somewan820
somewan820 requested a lite review from CopilotAugust 17, 2026 06:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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; WorkBoardStore owned by the main process, renderer is a projection that reloads on workBoard: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

  1. 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.

  2. 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.

  3. 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 测试)。

第一性原理 / 奥卡姆的疑问(针对定义):

  1. 用解法命名了问题。原子需求只是"别让我忘了,以后能启动";却被定义成带 scope/lifecycle/provenance/Session 关联的看板。这些是答案的名字,会把后续所有 phase 锁死在"看板"形态。
  2. 奥卡姆——同一原子需求有更省的实体。对 Agent 产品,"写进项目 TODO.md/issue"几乎零新实体,却能满足本地优先、重启存活、可审计、Agent 可读等大部分验收标准。Non-goals 说不做 Linear/Jira,但所建结构正是其更小骨架。建议明确说明为何需要 store + 状态机而非一个文件。
  3. 最该验证的假设放到最后。"用户真会回来看看板并启动任务吗"直到 Phase 3 才触及。建议在 Phase 2-4 前,先廉价打通"捕获→回看→启动任务"闭环做验证。

值得肯定:边界划得非常清醒且符合第一性——不污染 Session Task Ledger(#2290)、不注入每轮上下文、不做第二套执行权威。这是设计最强的部分,本 PR 也严格遵守。

合并/继续前建议:在 #2560 或 Phase-1 文档补一段"为何用专用 store 而非项目文件"的理由(若是 provenance + Session 关联,请明说);并考虑重排顺序,先验证核心闭环再铺 Phase 2-4。

结论:执行层面 Approve;在继续后续 phase 前,请补充问题定义的范围/理由说明。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — thanks for the review. Both asks are addressed in 6d261ee20:

  • Why a dedicated store instead of a project file: added to docs/work-board-phase1.md. A TODO.md / issue would cover the literal capture-and-list atom, but the product(desktop): capture deferred work in a project-aware Work Board #2560 acceptance criteria also require typed provenance + a bounded excerpt that survives side-chat fork deletion, stable per-item identity + revision CAS for concurrent Desktop writes, and later Session linking / result refs. Those are the load-bearing reasons for the store shape; if they were not in scope, a project file would indeed suffice.
  • Sequencing: agreed. The doc now records the plan to validate a thin capture -> revisit -> start-as-task loop before expanding Phases 2 and 4.

Happy to adjust the wording if you would like the rationale stated differently.

简体中文

@liugddx —— 感谢 review。两点已在 6d261ee20 处理:

  • 为什么用专用 store 而不是项目文件:已加入 docs/work-board-phase1.mdTODO.md / issue 能满足字面上的捕获与列表原子需求,但 product(desktop): capture deferred work in a project-aware Work Board #2560 的验收标准还要求强类型来源引用 + 在侧栏 fork 删除后仍存留的有界 excerpt、并发 Desktop 写入下稳定的逐项身份 + revision CAS,以及后续的 Session 关联 / result refs。这些才是 store 形态的承重理由;如果这些不在范围内,项目文件确实够用。
  • 顺序安排:同意。文档已记录计划:在铺开 Phase 2/4 之前,先用一条 thin 的 capture → 回看 → start-as-task 闭环做验证。

如果你希望这段 rationale 换个措辞,告诉我即可。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/TextInput in apps/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 to packages/ui/src/inline-rename-input.tsx:25-52. Also, maka-work-board-rename-input is 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 using String(error) for the detail field, since WorkBoardPanel already provides a localized banner title.
 error: error instanceof Error ? error.message : 'Work Board load failed',

@liugddx

Copy link
Copy Markdown
Member

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)

  • Add a "Why a store, not a file" note (3–5 sentences) to docs/work-board-phase1.md. State the one thing that justifies the machinery over a project TODO.md: it's provenance + Session linking (Phase 3). If that's the reason, say it explicitly so the scope reads as intentional, not accidental.
  • Write down the assumption we're betting on, in the same doc: "Users will return to the board and start tasks from it." One sentence. This becomes the thing Phase 3 must prove.

Gate before Phase 2 (side-chat capture)

  • Do a thin Phase 3 spike FIRST, before Phase 2. Wire one hard-coded item → "Start task" → new Session → link back. No polish. Goal: prove the capture→revisit→start loop has real pull. If nobody uses it, we stop here and the store stays a simple list.
  • Put the spike behind a flag; it doesn't need to ship. It needs to answer "does the loop get used."

Then resume the planned order

What NOT to change (keep doing this)

  • Keep the store in the main process as the single mutation authority.
  • Keep the renderer read-only / reload-on-signal.
  • Keep Work Board out of the Session Task Ledger, out of model turns, out of Runtime authority. This boundary is correct — don't soften it under any Phase.

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 落地(小)

  • docs/work-board-phase1.md 补 3-5 句"为何用 store 而非文件":唯一能撑起这套机制的理由是 provenance + Session 关联(Phase 3),请明说,让范围显得是有意为之。
  • 同一文档写下我们在赌的假设:"用户会回到看板并从中启动任务。" 一句话,作为 Phase 3 必须验证的目标。

Phase 2 之前的门槛

  • 先做一个极薄的 Phase 3 spike,插在 Phase 2 之前:硬编码一个事项 → "开始任务" → 新 Session → 关联回来。不做打磨。目的:验证"捕获→回看→启动"闭环真有人用。若没人用,就停在这里,store 保持简单列表即可。
  • spike 放在 flag 后,不必上线,只需回答"闭环有没有被用起来"。

恢复既定顺序

不要改(继续保持)

  • store 留在主进程,作为唯一写入权威;渲染进程只读、收信号 reload;Work Board 不进 Session Task Ledger、不进模型每轮上下文、不做 Runtime 权威。这条边界是对的,任何 phase 都别放松。

一句话给维护者: 合这个 PR;补两条文档;在 Phase 2 之前先做 Phase 3 "开始任务" spike 验证闭环;然后按 #2560 原计划继续。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@liugddx — checklist items are landed in f47d56a68:

  • Why a store, not a file: docs/work-board-phase1.md now states in a few sentences that the one justification is provenance + Session linking (typed source refs / bounded excerpt surviving fork deletion, Phase 3 linking an item to the Session it starts), with stable identity + CAS for concurrent writers; if those were not in scope, a project file would suffice.
  • Assumption: the doc records the bet — “users will return to the board and start tasks from it” — as the thing Phase 3 must prove.
  • Sequencing: Phases 2 and 4 are gated behind a thin, flag-gated Phase 3 spike (hard-coded item -> “Start task” -> new Session -> link back, no polish).

The merge conflict with main is resolved by merging origin/main into this branch (3eacc39a7); the only conflict was the regenerated Astryx surface inventory. Desktop typecheck, main build, and Work Board IPC tests pass. The PR should now be mergeable.

简体中文

@liugddx —— 清单项已在 f47d56a68 落地:

  • 为什么用 store 而不是文件docs/work-board-phase1.md 现在用几句话明确:唯一撑起这套机制的理由是 provenance + Session 关联(side-chat 捕获保留强类型来源引用 / fork 删除后仍存的有界 excerpt,Phase 3 把看板事项关联到它启动的 Session),加上并发写入下的稳定身份 + CAS;如果这些不在范围内,项目文件确实够用。
  • 假设:文档记录了赌注——“用户会回到看板并从中启动任务”——作为 Phase 3 必须验证的目标。
  • 顺序:Phase 2 和 Phase 4 现在被一个薄的、flag 控制的 Phase 3 spike 门槛卡住(硬编码事项 -> “开始任务” -> 新 Session -> 关联回来,不做打磨)。

main 的合并冲突已通过把 origin/main 合入本分支解决(3eacc39a7);唯一冲突是重新生成的 Astryx surface inventory。desktop typecheck、main build 和 Work Board IPC 测试均通过,PR 现在应该可以合并了。

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-system TextInput and 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). Use TextInput and guard event.nativeEvent.isComposing (see packages/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-system TextInput (for consistent styling, sizing, and keyboard/IME behavior). Add the TextInput import 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.loadFailed title, 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-WorkBoardStoreError failures, this forwards error.message back 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',
};

@liugddxliugddx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
ContributorAuthor

@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-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. [P2] Preserve the store's pagination contract in the renderer projection.useWorkBoard() discards WorkBoardPage.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.

  2. [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 when projectId becomes 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 isComposing guard from InlineRenameInput, 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:

  1. [P2] renderer 应保留 store 的分页契约。 当前 hook 丢弃 nextCursor,而 store 没有总量上限且默认只返回 50 条。某个 Inbox 或项目超过 50 条 active + archived item 后,旧事项会静默不可达;最近更新的归档项也可能把较旧的 active item 挤出唯一一页。请保留 cursor 并提供有界的“加载更多”,单纯把上限改成 100 只会移动截断点。
  2. [P2] UI 筛选与实际写入 scope 必须一致。 当前项目消失时,Project filter 和区块标签仍保持选中,但查询已静默回退 Inbox,新增事项也会写入 Inbox。请让标签、查询和新增共用同一个 effective filter/scope,或在 projectId 变为 null 时原子回到 Inbox。

一个非阻塞 follow-up:

  • [P3] 新增和改名应忽略 IME composition 中的 Enter。 中日韩输入法用 Enter 确认候选词,当前实现可能提前创建或保存未完成标题。复用现有输入 seam,或采用 InlineRenameInput 已有的 isComposing guard 即可。

当前测试只覆盖 main IPC/store,Electron suite 没有 Work Board renderer journey,因此全绿 CI 不能覆盖上述行为。补一条聚焦的 pagination/scope renderer/Electron 回归即可,无需扩大测试范围。

**结论:**先完成两个小的 P2 renderer 修复;P3 不阻塞。无需拆 PR 或改架构,修复后即可 Approve。

本次审查由 Codex 协助追踪当前 diff、前序反馈、职责边界和 CI 证据;最终判断与合并责任仍由人工 reviewer 承担。

@somewan820

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han — both P2 gaps and the P3 are fixed in 8d761edea:

  1. Pagination contract: useWorkBoard now retains WorkBoardPage.nextCursor and the panel exposes a bounded “Load more” path, so items beyond the store's 50-item default page are reachable instead of silently disappearing.
  2. Scope consistency: when the current project disappears, the filter atomically returns to Inbox, so the section label, list query, and create operation all use the same effective scope.
  3. IME (P3): create and rename ignore Enter while an IME composition is active.

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 都已在 8d761edea 修复:

  1. 分页契约useWorkBoard 现在保留 WorkBoardPage.nextCursor,面板提供有界的“加载更多”,store 默认 50 条之外的事项不再静默不可达。
  2. scope 一致性:当前项目消失时 filter 原子回到 Inbox,区块标签、列表查询和新增操作都使用同一个 effective scope。
  3. IME(P3):输入法 composition 期间,新增和改名会忽略 Enter。

验证:desktop 全量 typecheck、main build + Work Board IPC 测试、Biome 均通过。

关于 renderer/Electron 回归测试:目前 desktop 测试体系没有这个面板的 renderer 测试 harness,建议作为 follow-up 加一条聚焦的 e2e journey,而不是阻塞本 PR。如果你需要,合并后我可以补。

@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from 72766e1 to f0d8770CompareAugust 24, 2026 08:16
Phase 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
@somewan820
somewan820force-pushed the feat/2560-work-board-phase1 branch from f0d8770 to 1c8d833CompareAugust 24, 2026 09:52
@somewan820

Copy link
Copy Markdown
ContributorAuthor

Final verification on current head 5d4481ba6:

  • Added the focused renderer regression requested for paginated mutation refresh: load 50 + 10 items, emit workBoard:changed, then verify all 60 items remain loaded and the refresh requests the prior window depth.
  • Rechecked the alias-cursor P2: the fingerprint is a fixed SHA-256/base64url digest of the complete normalized identity set, with the existing 15-alias / 101-row cross-page regression.
  • All review threads are now answered and resolved; GitHub reports the PR as MERGEABLE against base 1e1c886a.
  • Linux CI passed: https://github.com/apache/maka/actions/runs/32715018884
  • Windows release check passed: https://github.com/apache/maka/actions/runs/32715018879

The remaining merge-state blocker is REVIEW_REQUIRED; please re-review the current head.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadapps/desktop/src/renderer/work-board-panel.tsx

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的 testpackage 均为终态绿。

我没有沿用上一轮的结论,而是把此前未闭合的每一条都重新从代码推导了一遍。

分页刷新那条 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

Copy link
Copy Markdown
Contributor

LGTM. Merging at @astrohan's request — test and package are green on 8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4.

简体中文

LGTM,按 @astrohan 的要求合并——8ebf4cf88ca3190bf8ae1a90c45c698f1dea9db4testpackage 均为绿。

@Astro-Han
Astro-Han merged commit 863d7ae into apache:mainAug 24, 2026
2 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@somewan820@liugddx@Astro-Han@jackwener@hqhq1025