Skip to content

feat(workhub): unify work identity and Host-owned target selection - #5198

Merged
ARE404 merged 1 commit into
apache:mainfrom
ARE404:feat/workhub-conversation-colors
Sep 13, 2026
Merged

feat(workhub): unify work identity and Host-owned target selection#5198
ARE404 merged 1 commit into
apache:mainfrom
ARE404:feat/workhub-conversation-colors

Conversation

@ARE404

@ARE404 ARE404 commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

WorkHub shows separate prompt/answer accents, compact colored Workspace / Session labels, and status before the prompt timestamp. Status replaces the standalone delegation result card. Message stripes toggle Work filtering; rail activation locates the conversation, then filters, then restores all conversations. Identity links retain Session navigation.

Ambiguous target selection uses the default production Coordination path. The model discovers candidates and calls tasks.select_and_delegate; the existing Host interaction coordinator publishes a durable Form and passes the confirmed target directly to Action Gate. Digits and arrows select, Enter confirms, and Esc cancels.

Ownership

  • Host owns offered identities, persistence, validation, replay and closure. Target admission rechecks the accepted Session/workspace. Existing immutable routing decisions remain intact.
  • Main owns the complete native progress-to-conversation transition. Renderer reports content needs; docked placement, passive reveal, dismissal and focus remain under the existing presentation owner.
  • The existing submission owner separates Send/rejected Retry from unknown-admission reconciliation and read-only refresh.
  • Removed the old pre-admission selector return value, renderer waiting Promise and dedicated selector component. Reuses existing Forms, ChoicePanel, interaction persistence and Action Gate. No new storage table or lifecycle controller.
  • The experimental routing model remains optional. Rebased onto current main; protocol epoch is 151.

Verification

  • Full repository build and Desktop typecheck passed.
  • Desktop: 2,584 tests passed; shared UI: 438; affected Host suites: 199.
  • WorkHub Storybook: 16 interactions passed; native Electron: 6 tests passed.
  • Lint, format, ASF headers, Windows/Astryx inventories, Electron budget and renderer architecture checks passed.
  • Real-provider native acceptance covered target selection, keyboard confirmation, selected-target execution, pending-form reload, passive floating-window display and cancellation. Selection/execution was repeated after rebasing. These results establish the tested paths, not complete recovery of every in-flight transcript item.
  • A pre-existing Resume/Stop test now keeps its fake target running until explicit Stop, eliminating a completion race without weakening assertions or changing production stop logic.

AI use

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

Codex implemented changes, tests and this description. Retain Generated-by: Codex when squashing.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally
  • Yes — behavior changes are described under Summary above
  • No

@github-actions github-actions Bot added the effort/XL Under 2500 readable lines label Sep 11, 2026
@ARE404
ARE404 marked this pull request as ready for review September 11, 2026 14:26

@Astro-Han Astro-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.

Design verdict. Target selection is decided at the right authority: the Host keeps the pending offer (per-Turn, 64 entries, 10 min TTL), re-validates the opaque candidateRef against a fresh candidate set, binds the durable routingDecision before admission, and the renderer only echoes requestId + candidateRef. Loss of the offer (Host restart, expiry, removed target, second prompt) degrades to "ask again", which is fine for unadmitted draft state. But the pause is signalled by throwing TargetSelectionRequired out of prepareWorkHubRoutingDecision / prepareFreshContent, through RootTurnCoordinator.startRootMessage, and catching it back in the coordinator. startRootMessage runs under runCommand, which treats any non-whitelisted error as an authority failure and drains the Host. The selector therefore takes the Runtime Host down on its first use. Not merge-ready; the fix is local in shape (return a value instead of throwing) but must go through the root authority's outcome type.

P0 — Every ambiguous prompt drains the Runtime Host

Reachability ①: user sends a prompt the routing model classifies clarify.

  • packages/runtime-host/src/server/workhub-coordination-coordinator.ts:808,898 throw TargetSelectionRequired; :919,923,927 throw plain Error from #selectedRoutingDecision, which runs inside prepareFreshContent.
  • Both run inside startRootMessage's admission task (packages/runtime-host/src/server/root-turn-coordinator.ts:2032-2155), which is wrapped by runCommand (:3347-3360): anything that is not RuntimeHostedRootConflictError / RuntimeHostedRootUnavailableError / HostedRootAdmissionGateError / shutdown-cancel calls this.requestHostDrain() before rethrowing.
  • packages/runtime-host/src/server/host-kernel.ts:356-364 then sets #shutdownRequested, arms the shutdown deadline and begins the composition drain; every later operation, including the selection answer, is refused with host_draining (:622).

Repro (ran locally through the real RootTurnCoordinator, then removed): append to root-turn-coordinator.test.ts a createFailureFixture({ prepareWorkHubRoutingDecision: async () => { throw new Error('WorkHub target selection required'); } }), create the coordination stable session, call startWorkHubCoordinationMessage(...). It rejects and fixture.drainRequested() === true; no admission is written. The PR's coordinator test cannot see this because its startWorkHubCoordinationMessage fixture bypasses RootTurnCoordinator.

Fix: make the pause a value, not an exception. prepareWorkHubRoutingDecision returns { kind: 'decision', decision } | { kind: 'target_selection', request }; prepareFreshWorkHubExecution propagates it and startRootMessage returns completedStart(...) with a dedicated outcome (e.g. ok: false, code: 'target_selection_required', request) that #answer maps to targetSelection. #selectedRoutingDecision returns { kind: 'rejected', outcome: turnFailure('operation_conflict', …) } through prepareFreshContent instead of throwing (today a mismatched candidateRef also surfaces as internal_failure + console.error, operation-dispatcher.ts:369-377). Add the real-authority test above with drainRequested() === false.

P1 — Intent-unclear prompts can no longer reach the assistant

Reachability ①: any prompt whose intent the model cannot classify ("hi", "帮我看看这个").

applyWorkHubRoutingPolicy maps intent.kind === 'unclear' to clarify (packages/core/src/workhub-routing.ts:127). The coordinator turns every model clarify into a selector when allowTargetSelection (workhub-coordination-coordinator.ts:866-878), and startRootMessage always passes true (root-turn-coordinator.ts:2096). The selector offers only existing candidates, create_new, or "continue explaining", which returns the draft unsent (apps/desktop/src/renderer/features/workhub/ui/workhub-target-selector.tsx:44-54, use-workhub-controller.ts:186-198). On main a clarify Turn is admitted and the assistant asks in conversation; now "Which work do you want to continue?" is shown for prompts that are not about continuing work, and there is no way to deliver them. The test asserts this ('intent clarification also waits before admission').

Fix (smallest): add a third selection { kind: 'answer_here' } that admits with { disposition: 'answer_here' }, and make the "none of these" button send it instead of dismissing. Alternative: let the policy distinguish target ambiguity from intent ambiguity and only pause on the former.

P2 — New text and keycaps are hand-rolled instead of Astryx roles

Reachability ① (every rendered label). RadioList, Button and TextInput are used correctly; the surrounding text is not. packages/ui/src/choice-panel.tsx:83 renders a raw <kbd className="maka-choice-shortcut"> where Astryx exports Kbd; the hint at :85, workhub-selection-hint and workhub-delegation-status are <p>/<span> with font-size: 11px/12px literals (workhub.css:281,307, packages/ui/src/styles.css:1147,1149), giving 15.7px/17.1px line boxes off the 4px grid. The Workspace / Session label is a Button variant="ghost" restyled by CSS to height: auto; padding: 0; font-size: 11px (workhub.css:275-281) — fighting the primitive to get a text-sized navigation control. DESIGN.md §7 "Role, Not Axes" and §9 "Use Astryx primitives as the default seam"; the settings pages already use <Text type="supporting" color="secondary"> 68 times. Fix: Kbd keys="1" for shortcuts; Text type="supporting" color="secondary" for hint and status; Link (no href renders a <button>) with type="supporting" for the identity label, keeping only the --workhub-work-hue override since Astryx has no per-hue variant. That removes the three CSS literals and the ghost-button override.

Notes (not blocking)

  • AskUserQuestion vs. selector. Two composer-area choice surfaces now exist with different plumbing (Host-transient offer vs. interaction authority). Keeping target selection before admission is justified because the routing decision must be durable before the model runs; say so in the PR body so the split does not look accidental.
  • Epoch. 143 is correct against current main (142). #5164 also bumps 142→144; whichever merges second must re-bump.
  • Prompt status noise (P3). workhub-root.tsx:85 gives every historical turn_state a status, so every completed prompt reads "Completed · time" (verified in DOM for product-workhub--question-lifecycle and --colored-work-history). Only non-terminal, failed or aborted states change what the user does next; consider dropping completed for plain coordination turns.
  • PNGs under docs/images/pr/ (P3). No tracked rule forbids them, but CONTRIBUTING.md:87 asks for screenshots in the PR, and the raw.githubusercontent.com/ARE404/... links die with the branch. Attach to the PR and drop the files.
  • Scope. The unchecked real-model flow matters for merge only for AskUserQuestion under the bypass coordination session; the selector itself is fully exercised without a model once the P0 path is fixed.

Verified: @maka/runtime-host coordinator/protocol/tool-profile tests (41 pass); @maka/desktop workhub-send-visibility + workhub-anchor-rail (26 pass); both new tests go red with the fix reverted; storybook-static computed styles for product-workhub--colored-work-history, --target-selection, --question-lifecycle, product-ask-user-question--keyboard-choices in light and dark (accent bars 3px on the sender edge, labels oklch(0.42 0.075 h) / oklch(0.8 0.09 h), status before timestamp); real key presses select radios in both stories.

中文

设计结论。 目标选择的决定权放在了正确的权威上:Host 持有待选 offer(按 Turn,64 条,10 分钟 TTL),用最新候选集重新校验不透明的 candidateRef,在 admission 前绑定持久的 routingDecision,renderer 只回传 requestId + candidateRef。offer 丢失(Host 重启、过期、目标被删、第二条 prompt)都退化为"再问一次",对未 admit 的草稿态是合理的。但暂停是通过从 prepareWorkHubRoutingDecision / prepareFreshContent 抛出 TargetSelectionRequired、穿过 RootTurnCoordinator.startRootMessage、再在 coordinator 里 catch 回来实现的。startRootMessage 跑在 runCommand 里,任何不在白名单的异常都会被当作权威故障并触发 Host drain。所以选择器第一次使用就会把 Runtime Host 拉下线。不可合并;修法形态上是局部的(返回值而不是抛异常),但必须经由根权威的 outcome 类型。

P0 — 每条歧义 prompt 都会 drain Runtime Host

可达性 ①:用户发送一条被路由模型判为 clarify 的 prompt。

  • workhub-coordination-coordinator.ts:808,898 抛出 TargetSelectionRequired:919,923,927#selectedRoutingDecision 里抛普通 Error,而它在 prepareFreshContent 内执行。
  • 两者都在 startRootMessage 的 admission task 内(root-turn-coordinator.ts:2032-2155),外层是 runCommand:3347-3360):不是 RuntimeHostedRootConflictError / RuntimeHostedRootUnavailableError / HostedRootAdmissionGateError / shutdown-cancel 的异常都会先 this.requestHostDrain() 再重抛。
  • host-kernel.ts:356-364 随即置位 #shutdownRequested、启动关机 deadline、开始 composition drain;之后所有操作(包括选择后的 answer)都被拒为 host_draining:622)。

复现(已在本地经真实 RootTurnCoordinator 跑过,随后删除):在 root-turn-coordinator.test.ts 末尾追加 createFailureFixture({ prepareWorkHubRoutingDecision: async () => { throw new Error('WorkHub target selection required'); } }),创建 coordination stable session,调用 startWorkHubCoordinationMessage(...)。结果 reject,且 fixture.drainRequested() === true,没有写入 admission。PR 里的 coordinator 测试看不到这一点,因为其 startWorkHubCoordinationMessage 夹具绕过了 RootTurnCoordinator

修法:把暂停做成返回值而非异常。prepareWorkHubRoutingDecision 返回 { kind: 'decision', decision } | { kind: 'target_selection', request }prepareFreshWorkHubExecution 透传,startRootMessage 用专门的 outcome(如 ok: false, code: 'target_selection_required', requestcompletedStart(...),由 #answer 映射为 targetSelection#selectedRoutingDecision 通过 prepareFreshContent 返回 { kind: 'rejected', outcome: turnFailure('operation_conflict', …) } 而不是抛(现在 candidateRef 不匹配也会变成 internal_failure + console.erroroperation-dispatcher.ts:369-377)。补上面这个真实权威的测试并断言 drainRequested() === false

P1 — 意图不明的 prompt 再也到不了助手

可达性 ①:任何模型无法归类意图的 prompt("hi"、"帮我看看这个")。

applyWorkHubRoutingPolicyintent.kind === 'unclear' 映射为 clarifyworkhub-routing.ts:127)。coordinator 在 allowTargetSelection 时把所有模型 clarify 都变成选择器(workhub-coordination-coordinator.ts:866-878),而 startRootMessage 总是传 trueroot-turn-coordinator.ts:2096)。选择器只提供现有候选、create_new 或"继续说明"——后者只是把草稿退回不发送(workhub-target-selector.tsx:46-58use-workhub-controller.ts:192-201)。mainclarify 会 admit Turn,由助手在对话里追问;现在对与"继续哪个工作"无关的 prompt 也弹出该问题,且没有任何投递路径。测试还专门断言了这一行为('intent clarification also waits before admission')。

修法(最小):增加第三种 selection { kind: 'answer_here' },以 { disposition: 'answer_here' } admit,并让"都不是"按钮发送它而不是 dismiss。替代方案:让 policy 区分目标歧义和意图歧义,只在前者暂停。

P2 — 新增文字和快捷键帽是手写的,不是 Astryx 角色

可达 ①(每个渲染的标签)。RadioListButtonTextInput 用得对,周围的文字没有。packages/ui/src/choice-panel.tsx:83 手写 <kbd className="maka-choice-shortcut">,而 Astryx 有 Kbd:85 的提示、workhub-selection-hintworkhub-delegation-status 都是带 font-size: 11px/12px 字面量的 <p>/<span>workhub.css:281,307packages/ui/src/styles.css:1147,1149),行框 15.7px/17.1px,不在 4px 网格上。Workspace / Session 标签是被 CSS 改成 height: auto; padding: 0; font-size: 11pxButton variant="ghost"workhub.css:275-281),为得到文字尺寸的导航控件而对抗原语。DESIGN.md §7 "Role, Not Axes"、§9 "Use Astryx primitives as the default seam";settings 页已用 <Text type="supporting" color="secondary"> 68 次。修法:快捷键用 Kbd keys="1";提示和状态用 Text type="supporting" color="secondary";身份标签用 Link(无 href 时渲染 <button>)加 type="supporting",只保留 --workhub-work-hue 覆盖(Astryx 没有按色相的变体)。这样三处 CSS 字面量和 ghost 按钮覆盖都可删。

备注(不阻塞)

  • AskUserQuestion 与选择器。 composer 区域现在有两套选择界面、两套管线(Host 临时 offer vs. interaction authority)。把目标选择放在 admission 前是有理由的(路由决定必须在模型运行前持久化),请在 PR 正文写明,避免看起来像是偶然分裂。
  • Epoch。 相对当前 main(142),143 正确。#5164 也从 142 升到 144;后合并者需要再升。
  • Prompt 状态噪音(P3)。 workhub-root.tsx:85 给每条历史 turn_state 都赋状态,于是每条已完成的 prompt 都显示"已完成 · 时间"(在 product-workhub--question-lifecycle--colored-work-history 的 DOM 中确认)。只有非终态、失败或中止会改变用户的下一步;建议普通 coordination turn 不显示 completed
  • docs/images/pr/ 下的 PNG(P3)。 没有仓库规则禁止,但 CONTRIBUTING.md:87 要求截图放在 PR 里,且 raw.githubusercontent.com/ARE404/... 链接会随分支删除失效。改为 PR 附件并删掉文件。
  • 范围。 未勾选的真实模型流程只对 bypass 模式 coordination session 下的 AskUserQuestion 有合并意义;选择器本身在 P0 修复后无需模型即可完整验证。

已验证:@maka/runtime-host coordinator/protocol/tool-profile 测试(41 通过);@maka/desktop workhub-send-visibility + workhub-anchor-rail(26 通过);两个新测试在还原修复后均变红;storybook-static 在 light/dark 下对 product-workhub--colored-work-history--target-selection--question-lifecycleproduct-ask-user-question--keyboard-choices 的计算样式(发送方边缘 3px 色条、标签 oklch(0.42 0.075 h) / oklch(0.8 0.09 h)、状态在时间戳之前);真实按键在两个 story 中都能选中单选项。

Comment thread apps/desktop/src/renderer/styles/workhub.css Outdated
Comment thread packages/ui/src/choice-panel.tsx
Comment thread packages/runtime-host/src/server/workhub-coordination-coordinator.ts Outdated
Comment thread packages/runtime-host/src/server/workhub-coordination-coordinator.ts Outdated
Comment thread packages/runtime-host/src/server/root-turn-coordinator.ts Outdated
Comment thread apps/desktop/src/renderer/features/workhub/ui/workhub-root.tsx
Comment thread apps/desktop/stories/ask-user-question.stories.tsx Outdated
Comment thread apps/desktop/src/renderer/features/workhub/ui/workhub-target-selector.tsx Outdated

@Astro-Han Astro-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.

Status check on 9f31d58fa. Three commits landed since the last round; none touches packages/runtime-host or packages/core, so the P0 and P1 stand unchanged. 76639a86a resolves one inline item. bb8bf7ad0 is a new alignment tweak. 9f31d58fa adds a conversation filter that is not in the PR title, body, or any issue; as fresh code it has a P1 of its own. Still not merge-ready.

Item Status on 9f31d58fa
P0 — ambiguous prompt drains the Host Open. Re-ran the probe through the real RootTurnCoordinator (createFailureFixture({ clientCapabilities, prepareWorkHubRoutingDecision: async () => { throw new Error('WorkHub target selection required') } }), v2 coordination Session, startWorkHubCoordinationMessage): routing called once, rejects with that error, drainRequested() === true, no admission written. workhub-coordination-coordinator.ts:82,808,896 and root-turn-coordinator.ts:3347-3360 are byte-identical to the reviewed head.
P1 — intent-unclear prompts cannot reach the assistant Open. workhub-coordination-coordinator.ts:866-878 still maps every model clarify to a selector when allowTargetSelection; root-turn-coordinator.ts:2096 still passes true; the selector still has no "answer here" delivery.
P2 — hand-rolled text/keycaps Open, and grown. choice-panel.tsx:72 still renders <kbd className="maka-choice-shortcut">; workhub.css:306 (.workhub-selection-hint, 12px) and :279-283 (ghost Button forced to text size) unchanged. 9f31d58fa adds .workhub-conversation-filter { font-size: 12px } (workhub.css:325, computed line box 17.1px) in the same style.
Inline 04 — --workhub-selection-label duplicates --workhub-work-label Open (workhub.css:317-318).
Inline 05 — hint strings inline in ChoicePanel Addressed by 76639a86a: questions.keyboardHint in conversation-copy.ts, read via getConversationCopy(locale).
Inline 06 — inline import() types in the coordinator Open (:81,810,884).
Inline 07 — throw new Error for client mismatch in #selectedRoutingDecision Open (:919,923,927); folds into the P0 fix.
Inline 08 — allowTargetSelection always true Open (root-turn-coordinator.ts:1569,2096).
Inline 09 — every completed prompt shows "Completed · time" Open (workhub-root.tsx:85).
Inline 10 — toBeChecked() without waitFor Open at the same lines; note the four stories passed in this static build (light), so the flake is timing-dependent, which is the reason to wrap them.
Inline 11 — duplicated hostCwd basename expression Open (workhub-target-selector.tsx:49, linked-work.ts:64).
Notes (PNGs in docs/images/pr/, epoch, status noise) PNGs still in the tree. Epoch 143 vs origin/main 142 is still correct.

bb8bf7ad0 gives every WorkHub prompt a transparent 3px end border so unlinked and delegated prompts share a metadata edge, with a story assertion; no concerns.

9f31d58 — conversation filter: does not belong in this PR

Design. The commit adds a second interaction model on top of the identity rails: double-click / shift-click on a navigation item filters the conversation, a single click now opens the Session only after a 500 ms timer, and every linked prompt and answer grows an invisible 12 px <button> on its accent edge. None of this is in the PR summary, the verification list, or an issue. It lands mid-review on a PR that already carries an open P0, and it changes the primary rail gesture for everyone. Split it out and open it against a short design note (which gesture, why a hidden edge button when the visible label already opens the Work), after P0/P1 are fixed here.

P1 — Sending while filtered makes the user's own prompt disappear

Reachability ①: filter by any Work (edge button or rail double-click), type a follow-up, press Enter.

workhub-conversation.tsx:120-121,133 keep only messages, liveTurn, and transient messages whose turnId is in matchingTurns, and matchingTurns comes from workLinks, which linked-work.ts:64-96 derives only from durable delegation_assigned / tasks tool results. A new Turn has no link until delegation lands, so the optimistic bubble, the admitted prompt, the streaming answer, and the running status are all hidden; runningStatus is forced false (:134). Reproduced on product-workhub--colored-work-history in the static build: filter to 支付回调幂等性, send FILTERED_SEND_PROBE: transcript stays at 2 turns, no transient row, no running status, filter bar still shown; clearing the filter reveals 5 turns including the new prompt and answer. For a clarify/answer_here Turn the message never appears until the user clears the filter by hand.

Fix (smallest): clear selectedWork on send (highlight.selectWork(undefined) in the send path, or in the controller's send), so the transcript returns to the full view exactly when new content is about to arrive. If the filter is meant to survive a send, the pending Turn and the live Turn must be exempt from the filter until their link exists.

P2 — Single click on the navigation rail now waits 500 ms

Reachability ①: every click on a rail item. workhub-navigation-rail.tsx:141 defers onOpenSession behind setTimeout(..., 500) so a double-click can be distinguished; on main the same click opens immediately. Measured in the static build: a single click resolves nothing for 500 ms. The primary action of the rail should not pay for a secondary gesture; keyboard-only users already get the immediate path (event.detail === 0). If the filter stays, put it on a modifier or a distinct control and keep single click immediate.

P2 — Hidden 12 px edge buttons in the tab order

Reachability ①. Each linked prompt and answer renders .workhub-message-rail (chat-turn.tsx:575,645,675; workhub.css:321-323): 12 px wide, transparent, no text, tabIndex 0, positioned over the accent border. In product-workhub--colored-work-history that is six extra focus stops between the rail and the composer, each right before a visible identity button that already opens the same Work. Nothing on screen tells a pointer user the edge is clickable except the title. DESIGN.md's rule that an element must change the user's next action is not met; if a per-message filter entry is wanted, add it as a visible action on the existing label (MoreMenu or a second Button), not as an invisible hit area.

中文

针对 9f31d58fa 的状态核对。上一轮之后新增三个 commit,均未触及 packages/runtime-hostpackages/core,因此 P0 与 P1 原样保留。76639a86a 解决了一条 inline 意见;bb8bf7ad0 是新的对齐微调;9f31d58fa 新增了对话筛选,PR 标题、正文和任何 issue 都没有提到,作为新代码它自身带来一个 P1。仍不可合并。

项目 9f31d58fa 上的状态
P0 — 歧义 prompt 会 drain Host 未修。 经真实 RootTurnCoordinator 重跑探针(createFailureFixture({ clientCapabilities, prepareWorkHubRoutingDecision: async () => { throw new Error('WorkHub target selection required') } }),v2 coordination Session,startWorkHubCoordinationMessage):routing 被调用一次,以该错误 reject,drainRequested() === true,未写入 admission。workhub-coordination-coordinator.ts:82,808,896root-turn-coordinator.ts:3347-3360 与上次评审的 head 逐字节相同。
P1 — 意图不明的 prompt 到不了助手 未修。 workhub-coordination-coordinator.ts:866-878 仍在 allowTargetSelection 时把所有模型 clarify 变成选择器;root-turn-coordinator.ts:2096 仍传 true;选择器仍没有"就在这里回答"的投递路径。
P2 — 手写文字/快捷键帽 未修,且扩大。 choice-panel.tsx:72 仍是 <kbd className="maka-choice-shortcut">workhub.css:306.workhub-selection-hint,12px)与 :279-283(ghost Button 被压成文字尺寸)未变。9f31d58fa 又以同样方式加了 .workhub-conversation-filter { font-size: 12px }workhub.css:325,计算行框 17.1px)。
Inline 04 — --workhub-selection-label--workhub-work-label 重复 未修(workhub.css:317-318)。
Inline 05 — ChoicePanel 内联文案 已修76639a86a):conversation-copy.ts 增加 questions.keyboardHint,经 getConversationCopy(locale) 读取。
Inline 06 — coordinator 内联 import() 类型 未修(:81,810,884)。
Inline 07 — #selectedRoutingDecision 对客户端不匹配 throw new Error 未修(:919,923,927);随 P0 修法一起消失。
Inline 08 — allowTargetSelection 恒为 true 未修(root-turn-coordinator.ts:1569,2096)。
Inline 09 — 每条已完成 prompt 显示"已完成 · 时间" 未修(workhub-root.tsx:85)。
Inline 10 — toBeChecked() 没包 waitFor 未修,行号不变;本次静态构建(light)四个 story 都通过,说明是时序相关的 flake,这正是要包 waitFor 的原因。
Inline 11 — hostCwd 取名表达式重复 未修(workhub-target-selector.tsx:49linked-work.ts:64)。
备注(docs/images/pr/ 的 PNG、epoch、状态噪音) PNG 仍在仓库里。epoch 143 相对 origin/main 的 142 仍正确。

bb8bf7ad0 给每条 WorkHub prompt 加 3px 透明尾边框,让未链接和已委派的 prompt 共用一条元数据边缘,并加了 story 断言;没有问题。

9f31d58 — 对话筛选:不属于本 PR

设计。该 commit 在身份色条之上叠加了第二套交互模型:导航项双击/shift 点击筛选对话,单击改为 500 ms 定时后才打开 Session,每条已链接的 prompt 和回答在色条边缘多出一个不可见的 12px <button>。这些都不在 PR 摘要、验证清单或任何 issue 里。它在评审进行中落到一个仍有 P0 未修的 PR 上,还改变了所有人的主手势。建议拆出去,先修好这里的 P0/P1,再附一段简短设计说明(选哪种手势、可见标签已能打开 Work 为何还要隐藏边缘按钮)单独开 PR。

P1 — 筛选状态下发送,用户自己的 prompt 会消失

可达性 ①:按任一 Work 筛选(边缘按钮或导航栏双击),输入后续内容,回车。

workhub-conversation.tsx:120-121,133 只保留 turnIdmatchingTurns 中的消息、liveTurn 和 transient 消息,而 matchingTurns 来自 workLinks,后者由 linked-work.ts:64-96 仅从持久化的 delegation_assigned / tasks 工具结果推导。新 Turn 在委派落地前没有链接,于是乐观气泡、已 admit 的 prompt、流式回答、运行状态全部被隐藏;runningStatus 被强制为 false:134)。已在静态构建的 product-workhub--colored-work-history 上复现:筛选到 支付回调幂等性,发送 FILTERED_SEND_PROBE:transcript 仍是 2 个 turn,没有 transient 行,没有运行状态,筛选条仍在;清除筛选后出现 5 个 turn,包含新 prompt 和回答。对 clarify/answer_here 的 Turn,消息在用户手动清除筛选前永远不出现。

修法(最小):发送时清除 selectedWork(在发送路径或 controller 的 send 里调 highlight.selectWork(undefined)),让 transcript 恰在新内容到来时回到完整视图。如果筛选必须跨发送保留,则 pending Turn 和 live Turn 在链接存在前必须豁免筛选。

P2 — 导航栏单击要等 500 ms

可达性 ①:每次点击导航项。workhub-navigation-rail.tsx:141onOpenSession 推迟到 setTimeout(..., 500) 之后以区分双击;main 上同一次点击立即打开。静态构建实测:单击 500 ms 内没有任何反应。主动作不该为次要手势买单;键盘用户已经走即时路径(event.detail === 0)。若保留筛选,请放到修饰键或独立控件上,单击保持即时。

P2 — tab 顺序里的隐藏 12px 边缘按钮

可达性 ①。每条已链接的 prompt 和回答渲染 .workhub-message-railchat-turn.tsx:575,645,675workhub.css:321-323):12px 宽、透明、无文字、tabIndex 0,覆盖在色条上。在 product-workhub--colored-work-history 里,这在导航栏与 composer 之间多出六个焦点停靠点,每个都紧挨着一个已经能打开同一 Work 的可见身份按钮。除了 title,屏幕上没有任何提示告诉指针用户边缘可点。不满足 DESIGN.md"元素须改变用户下一步动作"的规则;若需要按消息的筛选入口,应作为现有标签上的可见动作(MoreMenu 或第二个 Button),而不是不可见热区。

Comment thread apps/desktop/stories/workhub.stories.tsx Outdated
Comment thread apps/desktop/stories/workhub.stories.tsx Outdated
Comment thread apps/desktop/src/renderer/features/workhub/ui/workhub-conversation.tsx Outdated

@Astro-Han Astro-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.

Thanks for addressing the previous review and documenting the remaining acceptance work. I re-reviewed d75735f6d3e63a2493344e44d6ba2b27e46a002d across architecture, implementation, and simplification. Two local P2 issues remain, both noted inline; no new P0/P1 was identified.

The authority boundary now looks sound. Target selection returns a typed outcome before admission, the Host validates the offered identity and refreshes the same target against current candidates, and execution still passes through the existing Action Gate. The transient offer, fresh candidate binding, durable routing decision, and final authorization check protect distinct obligations; I do not see a justified architecture rewrite or a redundant authority to remove.

The earlier Host-drain, intent-only clarification, and ordinary filtered-send findings are addressed. The ChatView remount, Astryx primitives, shared copy/color/path handling, type imports, and Storybook assertion/viewport concerns are also addressed. I am withdrawing the earlier claim that allowTargetSelection is always true: recovery and follow-up use the false default, while direct starts opt in. The 500 ms rail delay is gone. I am not carrying forward the completed-status or rail-interaction preferences as correctness findings under the currently documented behavior.

The useful remaining simplification is to make clearing the presentation filter part of a shared send/retry entry point. The ordinary send fix currently leaves Retry uncovered. The shared identity color cleanup also introduced a missing dark-theme selector, detailed inline.

Validation in this review: an Electron computed-style probe reproduced the light-theme issue; an exact-head WorkHubRoot/controller DOM probe reproduced failed send → filter → successful Retry with the new messages hidden until clearing the filter. Host code and its regression tests were inspected, but the full suite was not rerun. Current CI is passing. The real-model routing → selection → execution flow and actual floating-window expansion/focus/dock transitions remain unverified, as listed in the PR; fixture checks do not replace those acceptance items.

AI assistance: Codex coordinated three deep reviewers; findings were cross-checked against the current production paths and the focused probes.

Comment thread apps/desktop/src/renderer/styles/workhub.css Outdated
Comment thread apps/desktop/src/renderer/features/workhub/ui/workhub-root.tsx Outdated
@ARE404
ARE404 force-pushed the feat/workhub-conversation-colors branch 2 times, most recently from 7a92599 to 1de55db Compare September 12, 2026 10:56
Astro-Han

This comment was marked as duplicate.

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks for working through the review feedback. I think this now needs a bounded architecture consolidation before another rereview, rather than only a guard around the latest P2. This assessment is based on 1de55db5d7601d8875c13a563d8bc346e2add8d4.

The Host admission and execution boundary looks sound. The remaining pattern is in UI orchestration: submission visibility is enforced separately by Send and Retry, while showConversation mixes expanding content with changing window placement. Fixing individual callers leaves the next caller exposed to the same assumptions. The Storybook presentation stub also hides the actual window-command behavior. I should have distinguished these UI contracts from the already-correct Host architecture more clearly in the earlier review.

Please give your implementation agent the following prompt. The goal is to remove those recurring coordination obligations within this PR, not to add another controller or redesign all of WorkHub.


Implementation agent prompt

Review and consolidate the UI orchestration in this PR before fixing the latest inline P2. Start by reading the current production paths and the review history; do not assume every previous suggestion is correct. Pin your work to the latest PR head and account for changes from main.

1. Define the contracts and their owners before editing.

Trace ordinary Send, rejected-send Retry, unknown-admission recovery, transcript-read recovery, target selection, AskUserQuestion hydration, and progress/floating/docked presentation through the renderer, preload, and main process. State which existing owner decides each fact and which side effects each entry is allowed to cause. In particular:

  • Runtime Host remains the sole authority for admission, execution, interactions, and terminal state. Renderer state is presentation or pending-request coordination.
  • A newly submitted or resubmitted prompt and its answer must be visible without manually clearing a Work filter. Read recovery and unknown-admission reconciliation are not new submissions and must not accidentally resend work.
  • Presenting a question or target selector must preserve the current docked/floating placement. Expanding an existing progress card is a distinct operation from explicitly detaching or docking the window.
  • Pending interactions must retain their answers/selection and usable keyboard focus through supported presentation transitions; focusing an ordinary composer must not override an active interaction.

2. Consolidate at the existing boundaries.

  • Remove the need for individual Send/Retry buttons to remember the same submission-visibility rule. Put that rule at an existing shared submission boundary, while preserving the distinct retry/recovery semantics above. Do not move presentation filtering into Runtime Host.
  • Inspect every production caller of showConversation. Today an absent progressRequest can reach detach(true), although the caller only intends to reveal an interaction. Narrow this contract so expanding a progress card cannot silently mean changing placement. Check whether requiring the current progress request lets the optional/no-request branch disappear; keep explicit placement changes on the existing detach/dock path. Validate stale requests at the existing presentation owner.
  • Keep local content expansion, native placement, and focus responsibilities explicit. Do not introduce a second presentation state machine, mirrored lifecycle flags, or another event loop to synchronize them. If additional state is truly necessary, identify the concrete obligation that cannot be met by an existing owner.
  • Preserve the multi-Turn buffer and stable rejected-prompt identity from the latest main integration.

3. Perform an ablation pass.

After the consolidation, try removing the superseded branches, duplicated caller-side side effects, and tests that only assert those old mechanisms. Retain the smallest tests protecting distinct observable contracts. Do not delete the Host offer cache, fresh candidate rebinding, durable routing decision, or Action Gate merely to reduce code: those currently protect separate expiry/recovery, freshness, replay, and authorization obligations. Do not change the documented navigation/filter product behavior as an incidental cleanup.

4. Verify through the real boundaries.

Use the existing test seams and native E2E infrastructure. Cover ordinary submission and rejected Retry while filtered; ensure read/unknown-admission recovery does not create a duplicate submission. Exercise a docked selector and a hydrated question through the real presentation command handling and assert that placement stays docked. Check progress-card expansion, pending-interaction focus, and floating/docked transitions with the actual window implementation. A no-op Storybook showConversation stub cannot establish these contracts.

Complete the PR's already-listed real-model ambiguous-routing → selection → target-execution acceptance, or state precisely why it remains unverified. Investigate the failing exact-head CI transcript-scroll-cost E2E without assuming either a PR regression or a flake; preserve any failed aggregate result in the validation report. Run the affected checks rather than adding a broad new test matrix.

5. Report the final architecture, not just the patch.

Update the PR description with the problem, the final responsibility boundaries, which old paths or obligations were removed, and the evidence for each acceptance item. Explain any complexity that survived ablation. If a real product decision is needed, surface that specific decision before inventing behavior.

Do not stop after adding if (progress) to the current effect. Completion means the contract prevents equivalent mistakes at other callers, the superseded behavior is removed, and the observable interaction/presentation paths have been verified.


I will rereview the consolidated architecture first, then the implementation and simplification. AI assistance: Codex helped trace the current call paths and draft this review guidance.

@Astro-Han Astro-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.

Thanks for making the consolidation explicit. At 21b30caf9, Send and rejected Retry now share the submission callback, while unknown-admission and read recovery keep their separate meanings. Progress expansion requires a current request and no longer falls back to detach. These are the right ownership boundaries, and the old docked-placement P2 is fixed.

One P2 remains in the progress-to-conversation transition, detailed inline: expanding before the first progress paint leaves the native window hidden. Please complete that transition in the existing presentation owner, preserving stale-request rejection, explicit placement changes and no-focus-stealing behavior. This needs a local correction and a regression through the existing main handler, not another controller.

Verification: rebuilt this exact head; 46 presentation/controller tests passed. The real Electron pending-question test also passed: docked delivery, reload hydration, explicit detach/dock, retained selection and focus in the question panel. An additional probe through the existing main-handler harness reproduced the early-expansion failure. I have not run a real provider routing session. The two remaining acceptance items already disclosed in the PR body—real-model target routing and complete native progress/selector delivery—remain open, so I am not approving yet.

AI-assisted rereview with Reviewer Sol and the coordinating Codex agent; the latter independently checked the changed boundaries and ran the validations above.

Comment thread apps/desktop/src/main/workhub-presentation.ts Outdated
@ARE404
ARE404 force-pushed the feat/workhub-conversation-colors branch from 21b30ca to 154f4a3 Compare September 12, 2026 14:57

@Astro-Han Astro-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.

Thanks for the focused fix and the clear acceptance notes. I rereviewed 59565b1aa. The previous early-expansion P2 is closed: the existing Main presentation owner now completes passive reveal when expansion consumes the progress request before paint, while preserving hidden mode, request freshness and no-focus-stealing behavior. No new controller or lifecycle authority is needed.

I rebuilt this head and ran the 48 presentation/submission tests successfully. Reverting only the presentation fix made the new before-first-paint regression fail with the window still hidden; restoring it passed. The real Electron pending-question test also passed, covering reload hydration, explicit detach/dock, retained selection and focus. No new P0–P3 code finding emerged from this rereview.

I am keeping this as a comment until the two remaining acceptance items are demonstrated:

  • Actual progress-window delivery and automatic expansion into a pending question/target selector through the real native presentation handler, including selection/answer completion and no focus stealing.
  • Real-model ambiguous routing, user selection, and execution against the selected target.

The docked deterministic-backend E2E and the native-handler harness establish useful boundaries, but not those complete paths. Please attach reproducible steps and observed results to this PR; there is no evidence here calling for another architectural rewrite. Current CI is still running and the PR is mergeable.

AI-assisted rereview and local verification with Codex.

中文

上次进度窗过早展开后仍隐藏的 P2 已闭环,修复沿现有 Main 展示权威完成,没有增加控制器。48 项测试、回退失败/恢复通过的负对照,以及真实 Electron 的问题面板重载和显式窗口切换验收均通过。本次没有新的 P0–P3 代码问题。

暂保留 COMMENT,待完成正文两项验收:真实进度窗自动展开并交付问题/目标选择,以及真实模型歧义路由→选择→目标执行。现有分层测试不替代这两条完整路径;也没有证据要求再做一次架构重写。CI 尚在运行。

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks for working through all the follow-ups! It would be helpful to add a couple of short demo videos to the PR body, both to show how the feature feels and to make the remaining acceptance easier to verify:

  • The actual progress window automatically expanding into a question or target selector, then completing the interaction without stealing focus.
  • A real-model ambiguous request going through target selection and executing against the selected target.

A brief note with the tested commit, OS, steps, and whether the backend is real or simulated would be enough. No polished walkthrough needed—a simple screen recording of each flow would help me review the behavior and give future readers a useful feature demo. Thanks!

@ARE404

ARE404 commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Ran the production Electron build at 59565b1 with real deepseek-v4-flash, isolated data, and no injected model/presentation responses.

The native progress → automatic question expansion → keyboard answer → completion path passed. The progress window became visible without taking focus from the main window; after explicit activation, 2 + Enter selected blue and the real model completed with “你选择了蓝色。”

The real model also resolved two same-named works through a generic AskUserQuestion, then delegated to the chosen beta Session. beta/acceptance.txt exists with exactly the requested 32 bytes; alpha has no file. I verified the target Session's execution and completion, not merely admission.

However, the dedicated target selector did not appear. Source tracing explains why: execution-composition only installs prepareRoutingDecision when dependencies.workHubRoutingModel is provided (lines 1525–1527), and passes that same optional dependency to the coordinator (line 1942). The current production entry points do not supply it; the factory and injection are used only by tests. These lines came from #5152, before this PR. The generic question flow therefore cannot close the dedicated-selector acceptance. This is now recorded as a production integration gap rather than unexecuted testing.

Reproducible prompts, actual transition timestamps, focus observations, Session identities, file evidence and screenshots: https://github.com/ARE404/maka-agent/blob/6aa526a8d6c7e45b2ec656152ee02120339322ca/docs/workhub-native-acceptance-2026-09-12.zh-CN.md

The tested head's CI also failed separately in colored-work-history (expected 已完成, observed 已接收); that failure remains recorded. This evidence-only commit does not change production routing or fix that story.

@Astro-Han Astro-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.

Thanks for doing the real-provider acceptance and distinguishing the generic question flow from the dedicated target selector. The report provides useful evidence for native passive expansion, answering a question, and execution in the selected Session. The evidence-only update from 59565b1aa to 6aa526a8d does not change the implementation I previously tested.

I checked the routing composition and the upstream decision. There is an important qualification: the missing production default is intentional. #5152 explicitly keeps the current production routing default until the comparative production-path evidence required by #3492 exists. #3492 still leaves real-model comparison, strategy selection and rollout open. Please do not close this by simply injecting createHostWorkHubRoutingModel in production.

The design/acceptance question for this PR is now concrete: its dedicated pre-admission selector depends on a routing path that is not enabled for normal users. A generic AskUserQuestion followed by delegation is a useful supported workflow, but does not demonstrate that selector or its admission contract.

Please reconcile the deliverable with that existing gate before merging:

  • If the dedicated selector is part of this release, complete the required comparison/strategy decision through the existing maka eval and Host admission path, then integrate and verify the selected production route. Reuse the existing Host composition and Action Gate; do not add a UI routing authority or another evaluation framework.
  • If that routing rollout remains deferred, propose a coherent scope for the currently usable identity/status/question improvements and identify which inactive selector-specific machinery should be deferred or removed. Merely relabeling unreachable UI as delivered would not resolve the issue.

This is a scope and integration decision, not evidence that the default was accidentally omitted or that the already-reviewed Host safeguards need another rewrite. I am keeping COMMENT pending that decision.

The current head also fails CI in product-workhub--colored-work-history: expected 已完成, received 已接收. Please establish whether this is a fixture scheduling issue or incorrect status projection before changing the test; do not weaken the assertion or assume a flake. GitHub currently also reports a conflict with main.

A short video of the successful native flow would still be useful for the PR presentation; it is separate from the routing decision above.

AI-assisted follow-up: Codex checked the current source, acceptance report, upstream rollout constraints and CI logs.

中文

真实模型验收确实推进了:原生进度窗被动展开、问题回答以及选定 Session 执行已有证据。但专用选择器依赖的路由没有默认启用,这是 #5152#3492 评估门槛作出的有意决定,不能作为漏接依赖直接注入。

请先收敛本 PR 的交付范围:若本次要交付专用选择器,需完成已有比较评估/策略决策,再接入并验收真实生产路径;若路由上线继续延后,请提出当前可用功能的完整范围,并说明哪些未启用的选择器机制应延后或删除。不要新建路由权威或评估框架,也不要把通用提问当成专用选择器验收。

当前 CI 仍在 colored-work-history 上出现“已完成/已接收”不一致,另有 main 冲突,需查明并解决。保持 COMMENT;不要求再推翻已经验证过的 Host 边界。

@ARE404
ARE404 force-pushed the feat/workhub-conversation-colors branch from 6aa526a to d2ad326 Compare September 13, 2026 04:50
@ARE404 ARE404 changed the title feat(workhub): add work identity, prompt status, and keyboard selection feat(workhub): unify work identity and Host-owned target selection Sep 13, 2026

@Astro-Han Astro-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.

Thanks for the substantial rework. I am approving this head with the three non-blocking P2 follow-ups noted inline.

The architecture concern from my earlier review is addressed: the default Coordination model invokes a management tool, target confirmation reuses the existing durable Form lifecycle, and Action Gate retains execution admission and identity checks. The old pre-admission selector and renderer waiting Promise are gone, and this change does not enable additional pre-routing LLM calls. I do not think another architecture rewrite is needed.

Please keep the remaining fixes local: preserve distinguishable option labels after truncation, retain the shared Form's Decline action, and keep separate delegation statuses when deduplicating Session labels.

AI-assisted review with Codex and two Reviewer Sol passes, followed by independent reproduction of the three findings. The affected Host baseline passed 90 tests; targeted rendering probes confirmed the UI findings. CI is green at d2ad326. I reviewed the native acceptance report but did not independently rerun native UI acceptance in this pass.

// An opaque, durable binding: only an exact offered value is accepted by
// the form authority. Names and model-written answers never resolve identity.
value: JSON.stringify([ref, candidate.sessionId, digest(candidate.workspace)]),
label: page.candidates.some(

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.

[P2] Keep option labels distinguishable after truncation

Could the uniqueness check run on the final bounded labels? Two Sessions with the same long name in different workspaces can lose the distinguishing path suffix during the 190-byte truncation. Since the full cwd values differ, neither receives the short Session id here. The shared Form then rejects the request with Duplicate form option label, surfaced as persistence_failed, before any selector appears. I reproduced this through the production composition handler with two valid 80-character Chinese names and different directories. Reserving space for a stable distinguishing suffix when the bounded labels collide should fix this locally; a focused regression through Form publication would protect the behavior.

</div>
<div className="maka-form-interaction-primary-actions">
<Button
{!singleChoice && <Button

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.

[P2] Preserve Decline for shared single-select Forms

Could this shared Form keep its Decline action in singleChoice mode? The condition applies to any required, single-field single-select Form, including ordinary MCP forms, rather than only WorkHub target confirmation. The existing protocol distinguishes decline from cancel and passes that distinction back to the requesting tool. Rendering such a Form at this head confirms that only Cancel and Submit remain, so the user can no longer explicitly decline. Keeping the existing button is enough; WorkHub already treats either non-accept result as no delegation.

const grouped = new Map<string, WorkHubLinkedWork[]>();
for (const work of assignments) {
const works = grouped.get(work.coordinationTurnId) ?? [];
if (!works.some((item) => item.targetSessionId === work.targetSessionId)) works.push(work);

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.

[P2] Keep delegation statuses separate when deduplicating Session identity

Could the Session-label deduplication leave the individual delegation records available for status rendering? Different tool calls in one Coordination Turn can delegate to the same Session under different action ids, and feedback is keyed by assignment id. Keeping only the first link here hides later work: a rendered example with the first assignment completed and the second running produces one completed status and no running status. The Session label/accent can still be shared, while the existing per-assignment feedback remains represented. This is a projection fix; no Host or storage redesign is needed.

@ARE404
ARE404 merged commit 6dfec43 into apache:main Sep 13, 2026
1 check passed
Shouly pushed a commit to Shouly/maka that referenced this pull request Sep 13, 2026
…f0a5d)

Eight upstream commits. One reaches the new renderer's contracts: apache#4878
returns expected failures as codes across IPC — the five Session setters
(`setPermissionMode`, `setCollaborationMode`, `setOrchestrationMode`,
`setModelConfiguration`, `setThinkingLevel`) answer
`DesktopSessionUpdateResult` instead of throwing, `abandonPlanProposal`
answers `PlanControlIpcResult` like its siblings, `send` / `submitMessage`
gain an `attachment_blocked` refusal, and the attachment guard throws a typed
`AttachmentIngestBlockedError` in place of the `attachment_ingest:<code>`
message token. Also in: apache#5216 makes HTML artifacts directly openable
(`app.showArtifactInFolder` beside `openArtifactPath`, which now hands an
HTML artifact to the default app; `isArtifactUserVisible` admits HTML tool
results), apache#5198 unifies WorkHub conversation identity and Host-owned choices
(an Astryx `ChoicePanel` in packages/ui, `keyboardHint` copy, a
`preserveFocus` scroll target), apache#5249's skill picker fix
(`selectedSkillIds` in `chat-input-behavior`), apache#4815 admits structured-only
Messages (`hasMeaningfulMessageContent` in core), apache#4862's ACP live session
lifecycle in the CLI, apache#5204's workbar tab scrollbar css and apache#5180's wider
locale hygiene gate.

Resolution per the sync policy: conflicts under the old renderer's trees,
packages/ui's deleted components, stories, e2e specs and the main tests that
import them stay deleted, and upstream's new files there are dropped
(`features/workhub/model/workspace-name.ts`, packages/ui's `choice-panel.tsx`
and its `index.ts` export, the `styles/base.css` / `workhub.css` /
`maka-tokens.css` edits, `expected-error-presentation.test.ts` and the WorkHub
main tests). git's rename pairing had put upstream's
`features/session-settings/ports.ts`, `features/workhub/testing.ts` and
`platform/desktop/create-session-settings-services.ts` into
`bridge/e2e-fixture.ts`, `components/ui/skeleton.tsx` and
`lib/ported/display-frame-scheduler.ts`; all three keep ours. The renderer
architecture ledger keeps ours, rewritten with `--write`. The e2e budget and
`transcript-scroll-cost.spec.ts` keep ours; upstream's new
`expected-failure-feedback.spec.ts` is trimmed to its second case (the IPC
round trip of the setting and Plan codes), the first needing the WorkHub
surface this build does not ship.

Re-implemented for the new contracts:
- `bridge/sessions.ts` unwraps every update result and rethrows a refusal as
  `ExpectedOperationError` (new `bridge/expected-operation-error.ts`, ported
  from upstream's `operation-diagnostics.ts`), so the turn actions store and
  every caller keep awaiting a summary. `localizedShellErrorMessage` renders
  the code through the new `updateFailures` copy on every surface and
  `AttachmentIngestBlockedError` by its `code`; `sessionSettingFailureCopy`
  is upstream's.
- `ChatInput` routes an `attachment_blocked` refusal through upstream's
  `showSubmissionFeedback` and keeps the draft with the ingest reason;
  `showSkillInvocationFeedback` stays exported for the partial-success toast.
- `FilesTab` reveals through `showArtifactInFolder`; an HTML row shows "View
  in Maka", its menu offers "Open in Default App" first, and its preview's
  external action opens rather than reveals. `artifact-copy` gains
  `viewInMaka` / `openInDefaultApp` in three locales.
- `TipTapEditor` hides Skills already in the draft from the picker (apache#5249);
  the chips are atoms here, so the set is read from the document rather than
  from the `/skill:x` text upstream scans.
- `composer-state.test.ts` asserts the typed preflight error.

packages/ui: `use-chat-scroll.ts` merged cleanly (`preserveFocus`) on top of
our `holdTurn` extensions; `conversation-copy.ts` and `chat-input-behavior.ts`
take upstream's additions. apache#5217's live-turn buffer stays out as before.

The compatible-change declaration is unchanged this round. The release
checklist's baseline note records that the eight commits add no unlisted
renderer surface.

Gates: build:test + build:renderer, typecheck, biome lint and format, locale
hygiene (the widened apache#5180 gate), ASF headers, renderer architecture ledger
(rewritten with `--write`), e2e budget, third-party notices, knip (39 unused
files, unchanged from the twelfth sync), workspace dist tests (desktop 3484
of 3498 with 14 skipped, every other workspace green), Electron smoke (44
checks, no renderer errors), core-dialogue smoke, streaming-switch smoke, and
the trimmed `expected-failure-feedback` e2e case against the real preload.
`packages/runtime` `model-adapter-onerror` fails on this machine before and
after, as in the eleventh and twelfth syncs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Under 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants