Skip to content

fix(chat): restore workspace choices for gated goals - #4363

Open
luw2007 wants to merge 2 commits into
loopx-project:mainfrom
luw2007:codex/fix-goal-create-workspace-gate
Open

luw2007 wants to merge 2 commits into
loopx-project:mainfrom
luw2007:codex/fix-goal-create-workspace-gate

Conversation

@luw2007

@luw2007 luw2007 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Problem

A persisted gated goal.create proposal retained workspace candidates in the service payload, but the dashboard projection discarded them after reload. The drawer then mislabeled workspace selection as host-only confirmation and exposed no workspace button.

Fix

  • restore workspace candidates and the regenerate request from persisted proposals
  • label workspace-selection gates with their actual selection impact
  • cover reload, workspace click, and regenerated workspace_ref in the packaged browser smoke
  • rebuild the packaged Chat asset

Validation

  • npm run build:chat
  • python3 examples/loopx-chat-actions-smoke.py
  • LOOPX_PERSONAL_WORKSPACE_SCENARIO=typed-actions LOOPX_PERSONAL_WORKSPACE_PACKAGED=1 node examples/personal-workspace-browser-smoke.mjs
  • loopx canary premerge --from-git-diff (passed; 4 selected, 0 failures, 0 manual holds)

Future-facing pass: unnecessary. The existing gate payload and workspace-selection callback already own the contract; this change only preserves that data through reload.

Signed-off-by: luw2007 <luw2007@gmail.com>
Signed-off-by: luw2007 <luw2007@gmail.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

审查对象:04cb337690c591bc50eb64b417230d84585b457b

动机

这个 PR 修复 packaged Chat 的一个明确断链:首次收到 workspace_selection_required error 时,页面能从 gate payload 提取 candidates 并显示 workspace 按钮;但 proposal 持久化并在重载后经 workspaceProposal 重建时,projection 丢失了 candidates 和原始 regenerate request。结果是同一 typed gate 在 reload 前后表现不同——重载后被误标为“Host confirmation required”,用户也无法选择 workspace。PR 把现有 fresh-error 语义复用到 stored proposal path,目标和范围都清晰。

改动思路

改动没有发明新的 backend authority:proposal.gate.candidates 仍是服务端候选来源,goal.createnormalized_parameters/context 仍是 regenerate request 的基础。新增 helper 统一过滤 candidate shape;workspaceProposal 对 gated goal.create 保存 sourceRequestworkspaceCandidates;drawer 有候选时显示真实 proposal title/impact 和按钮,点击仍调用既有 onSelectWorkspaceCandidate,由 action preview 服务重新校验并生成带 workspace_ref 的确认预览。

测试新增一条 persisted proposal 的 packaged browser 路径:加载已有 gated proposal、打开 drawer、点击 Workspace 1、断言新 preview 的 workspace_ref,并拒绝 host-only 文案。这是对真实发布 bundle 的有效回归。不过当前 exact head 的 generated bundle 是基于旧 main 构建的,已经不能和现在的发布资产集合干净合并。

具体改动

  • personal-workspace-page.tsx 抽取 workspaceCandidatesFromGate,并让 stored workspaceProposal 复用 candidate parsing;gated goal.create 还原 sourceRequest
  • context-drawer.tsx 在 candidate-bearing gate 上展示 proposal title/impact,已有 workspace button/callback 保持不变。
  • typed-actions.mjs 增加 51 行 packaged reload/click/regenerate regression。
  • loopx/web/chat/asset-retention.json、hashed JS 和 index.html 是本次 source rebuild 的发布产物。

关键代码讲解

  1. workspaceCandidatesFromGatepersonal-workspace-page.tsx:544)只接受同时含 string workspace_ref/label 的 candidate,非法 payload 不进入 UI。
  2. workspaceProposalpersonal-workspace-page.tsx:560)现在从 persisted gate 恢复 candidates;在 gated goal.create 上保留 action kind、context、idempotency key、normalized parameters 和 summary,让选择按钮能走既有 regenerate API。
  3. drawer 的 gated renderer(context-drawer.tsx:980)用 candidate presence 区分 workspace-selection 与 host-only gate;onSelectWorkspaceCandidate 仍以 immutable spread 方式覆盖所选 workspace_ref,没有绕过 preview/apply gate。
  4. packaged browser case 以 stored loopx_chat_action_proposal_v1 开局,并同时验证 UI 文案与发给 service 的参数,覆盖了此次缺失的 reload seam。

对主干的风险

[P1] 当前 exact head 的 packaged Chat 资产无法与 current main 集成。origin/main@6c1a4d2cc37280a1d652b4bf67afd9c7d69ce19e 和本 head 执行 merge-tree,出现:

  • loopx/web/chat/asset-retention.json content conflict;
  • index-LRc2f6MH.js -> index-DZ4wXv1S.js 与 main 删除发生 rename/delete;
  • index-DZ4wXv1S.js modify/delete;
  • loopx/web/chat/index.html content conflict。

GitHub 也把 PR 标为 DIRTY。手工选择一侧的 hash/retention 不能证明 bundle 与合并后源码一致。最小修复是 rebase latest main,在合并后的 source 上重新运行 npm run build:chat,提交新的 hashed asset、retention 和 index,再跑 source + packaged browser smokes。更新 branch 会改变 exact head,因此需要重新审查。

本 head 上我验证了 loopx-chat-actions-smoke 通过,packaged typed-actions browser scenario 也通过;说明源码修复和当前 bundle 自洽。当前没有任何远端 check run,所以除了冲突外仍缺 GitHub required-check 证据。默认/权限边界是隔离的:没有 candidates 时仍走原 host-only 文案;选择 workspace 只 regenerate preview,不授予 apply/write authority。91 additions/27 deletions 的范围与问题相称,future-facing 新抽象不需要扩大。

我的整体评价

源码设计是最小、复用既有 owner、且 packaged test 覆盖到真实回载路径的好修复;我没有发现其 gate/authority 语义本身需要重做。但 PR 当前不能干净进入主干,且生成资产正是产品交付的一部分,不能把冲突当作普通“之后再更新”。因此本轮结论是 REQUEST_CHANGES:rebase current main、从 merged source 重建 bundle、跑完两条 smoke 和 required CI 后,再对新 exact head 复审。

English verdict: REQUEST_CHANGES for exact head 04cb337690c591bc50eb64b417230d84585b457b. The persisted workspace-gate fix and packaged browser path pass locally, but the PR is DIRTY: current main conflicts with its retention file, index, and hashed bundle. Rebase, rebuild build:chat, rerun source/packaged validation, and request review on the new head.

Sign up for free to 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.

2 participants