Personal workspace: manager home, Lark connections, and session records - #3167
Conversation
Add goal_channel_notification_projection_v0: a public-safe read model of each goal's .loopx/goal-channel.json binding (configured, enabled, human-gate auto-notify, receipt count, last notified time). Private provider material (chat ids, message ids, bot app ids, sender profiles) never enters the projection; receipts are counted, not copied, and the packet passes assert_public_packet. Missing or corrupt binding files degrade to an unconfigured row so the status hot path never fails. Mount the projection as a top-level status payload field and cover it with focused pytest cases plus the collection read-model smoke.
…ard, draft lifecycle fixes First-run tier-1 improvements: - Morning digest card on the manager home (done/failed/awaiting since last visit) plus an agent panorama strip from the management projection. - Composer context hint naming the receiving agent per-goal session. - Kanban in-progress column sorts by P0/P1/P2 with priority badges. - Goal drawer gains a Lark notification card reading the new goal_channel_notification_projection, reachable via a header info button (the goal drawer previously had no entry point). UX fixes from the 2026-08-13 Playwright report: - Drafts are keyed by goal+agent and persisted to sessionStorage, so switching goal/agent no longer leaks drafts and refresh keeps them. - Quick prompts append instead of overwriting a non-empty draft. - Composer auto-grows up to the 120px cap. - Terminal proposals (applied/rejected) leave the timeline on drawer close and are not restored; duplicate proposal cards dedupe by action+todo+title+status; apply errors surface the server message. - favicon 404 silenced.
huangruiteng
left a comment
There was a problem hiding this comment.
详细中文评审(exact head: d851afac059565f2408c8d7a1bb3f60964b940ca)
动机
本 PR 实现个人 Agent 工作区第一梯队:晨间摘要卡、Agent 全景条、飞书 Goal Channel 通知绑定状态的 public-safe 投影,以及 2026-08-13 Playwright UX 报告中的草稿隔离/持久化、快捷提示追加、输入框自动增高、重复提案去重、已应用/已拒绝提案关闭即消失、apply 真实错误展示、favicon 修复。动机明确,把工作区从"能看"推进到"早上回来一眼知道发生了什么",并补齐 UX 报告验收项。
改动思路
后端新增只读投影 goal_channel_notification_projection_v0:从 .loopx/goal-channel.json 读取每 Goal 的绑定状态(configured/enabled/human_gate_auto_notify_enabled/receipt_count/last_notified_at),chat_id、message_id、bot_app_id、sender profile 等私有字段一律不进入投影,出口经 assert_public_packet 强制校验;绑定缺失/损坏优雅降级为"未配置",status 热路径不会因绑定状态失败。前端基于该投影渲染通知卡,并完成摘要/全景条/看板排序/抽屉入口等 UI。设计符合"投影从 public-safe 状态构建、私有材料不落前端"的边界原则。
具体改动
关键代码讲解
build_goal_channel_notification_projection(extensions/lark/goal_channel_notification.py):逐 goal 解析绑定文件,_public_text对target_ref做 chat/message ID 正则剥离;receipt 只投影数量与最近时间;assert_public_packet兜底。全路径 best-effort try/except,无绑定文件/损坏 JSON 返回 unconfigured 行。collect_status(control_plane/status/collection.py):把通知投影挂进 status 收集(隔离 try/except,异常时整块降级为空),status.py注入 builder。- 前端:
personal-workspace-page.tsx增加晨间摘要卡与 Agent 全景条(4 卡片);goal-tasks-view.tsx增加 P0/P1/P2 排序与徽章;context-drawer.tsx增加 ℹ 抽屉入口与飞书通知卡;personal-workspace-model.ts处理草稿按 Goal+Agent 隔离/持久化、提案去重与关闭即消失;CSS 与 status.ts 类型同步。 - 验证:
test_status_collection_material_capability_wiring.py+test_lark_goal_channel_notification.py10/10 passed;status-collection-readmodel-smoke.pyok;浏览器 smoke 增加摘要卡/全景条断言(第 17 项)。
对主干的风险
- CI 说明(合入前置项,非本 PR 引入):CI pytest 在
cli-output-budget-regression-smoke.py::test_manifest_covers_the_declared_agent_facing_surface_set失败(manifest 缺少dashboard分类)。我在 exact head 与 base(frontend-control-plane-im-prototype-rfc@ 21530d5)都复现同一失败,该失败在 base 已存在,不是本 PR 引入。合入前需要先修 base 的 budget manifest(把dashboard加入CLI_OUTPUT_COMMAND_CLASSIFICATION_BY_ID或更新 manifest),或由本 PR 顺带补上该分类——否则 CI 无法绿。 - 本地验证:10 个新增 pytest + status readmodel smoke 通过;前端 diff 无
chat_id/message_id/bot_app_id/sender/token等私有字段泄漏;plan 文档公开安全(仅以说明性文字列出被排除的私有字段名)。 - 前端编译(tsc)与 Playwright 由 PR 声明通过,本环境未复跑浏览器 smoke;建议合入前以 CI/本地复核为准。
我的整体评价
APPROVE。后端投影边界正确(public-safe + 降级 + 兜底断言),前端改动贴合 UX 报告验收项,测试覆盖新增行为。唯一合入前置项是 base 已有的 CLI-output-budget manifest 缺口(dashboard),与 #3167 内容无关但需先修复;本评审不构成合并授权。
English Verdict
- PR: #3167 — Personal workspace tier-1: digest, worker strip, goal notification projection, draft lifecycle fixes
- Exact head:
d851afac059565f2408c8d7a1bb3f60964b940ca - Verdict: APPROVE
- Key finding: Public-safe goal-channel notification projection (private chat/message IDs excluded,
assert_public_packetenforced, graceful degradation) plus the tier-1 dashboard UI (digest, worker strip, board P0/P1/P2, drawer entry, draft-lifecycle fixes). 10 new focused tests and the status read-model smoke pass at the exact head; no private fields leak into the frontend diff. - Merge prerequisite (not caused by this PR): CI pytest fails on the pre-existing budget-manifest check (
dashboardmissing fromCLI_OUTPUT_COMMAND_CLASSIFICATION_BY_ID); reproduced identically at the base branchfrontend-control-plane-im-prototype-rfc(21530d5). Fix the base manifest (or include the one-line classification here) before merge.
Clicking confirm on a gate.resolve proposal can never apply from the chat surface (protected canonical authority), yet the drawer previously showed only an opaque English gate summary and a disabled apply button. Now the gated state explains in plain language that nothing was written, renders the exact loopx todo complete command with the proposal's goal and todo ids (approve/reject), and hides the dead apply button; timeline cards for gated proposals read 查看处理方式 instead of 确认并应用.
huangruiteng
left a comment
There was a problem hiding this comment.
详细中文评审(exact head: ee35f9fc2261a4dcb2ededc4567ae37992de6bee)
动机
新提交 Turn gated gate.resolve proposals into actionable CLI guidance 解决上一轮未覆盖项(UX-06 的相邻问题):页面无权直接批准 gate.resolve 这类权限变更,旧 UI 只显示"需要宿主确认"却没有给出可执行路径。该提交把 gated 提案变成"明确说明 + 可直接执行的 CLI 审批命令",用户不需要猜测怎么处理。
改动思路
对 gated 状态的 gate.resolve 提案:时间线按钮文案改为"查看处理方式";抽屉内明确提示"页面无权直接批准这类权限变更,你的点击没有写入任何内容",并从提案字段中读取 goal id / todo id 渲染一条 loopx todo complete --goal-id ... --todo-id ... --decision-outcome approve|reject 命令(可全选复制);gated 状态隐藏"确认并应用"主按钮(本来就会失败),保留"稍后/拒绝/重新生成"。纯前端改动,不触碰授权边界。
具体改动
channel-timeline.tsx:gated 提案按钮文案 → "查看处理方式"。context-drawer.tsx:gated 状态说明补强(明确"点击没有写入任何内容");gate.resolve 渲染 CLI 审批提示卡(字段缺省时静默不渲染);主 apply 按钮对 gated 隐藏。personal-workspace.css:CLI 提示卡样式(可全选 code 块)。
对主干的风险
- 本地验证:新增/既有 10 个后端测试通过(前端改动不影响控制面);CLI 命令仅由公开字段(goal id/todo id)拼接,无凭据/私有信息。
- CI:该 head pytest 仍因 **base 已存在的 budget-manifest 缺口(
dashboard未登记)**失败,与本提交无关(前端-only);合入前置项仍为修复 base manifest。 - 风险低:不新增绕过授权;gated 提案本就不能在页面应用,隐藏主按钮反而更诚实。
我的整体评价
APPROVE。该提交把"需要宿主确认"从死胡同变成可执行路径,文案诚实、命令可复制、无授权绕过。合入前置项与上一轮相同(base budget-manifest);本评审不构成合并授权。
English Verdict
- PR: #3167 — Personal workspace tier-1
- Exact head:
ee35f9fc2261a4dcb2ededc4567ae37992de6bee - Verdict: APPROVE
- Key finding: Gated
gate.resolveproposals now render an actionable, copyable CLI approval command (loopx todo complete ... --decision-outcome approve|reject) with honest copy that the page wrote nothing; the apply button is hidden for gated status. Frontend-only, no authority bypass. - Validation: 10 focused backend tests pass; CI pytest failure remains the pre-existing base budget-manifest gap (dashboard classification), unrelated to this delta.
Add goal-channel endpoints to the chat server (targets list, setup, configure) that reuse the existing lark goal-channel backend, and a notification settings drawer in the personal workspace: per-goal binding status, bind-to-target flow with preview/confirm, and a human-gate auto-notify toggle. Goal drawer notification card now links to the panel instead of printing CLI hints.
huangruiteng
left a comment
There was a problem hiding this comment.
详细中文评审(exact head: fe38dfde1fb77bf72b0fe60702e4f3b08d12c24c)
动机
新提交 Personal workspace: in-page Lark notification settings panel 在个人工作区内增加页面内飞书通知设置面板:列出共享 provider target、为 Goal 配置 Goal Channel 绑定、切换 human-gate 自动通知。动机是把"绑定/配置"从 CLI 推进到工作区 UI,同时保持 preview→execute 与 extension-ready 门禁。
改动思路
- chat server(loopback-only):新增三个端点——
GET /api/chat/goal-channel/targets(列出 provider targets)、POST /api/chat/goal-channel/setup(绑定 Goal Channel,execute仅当 true 才落库,否则返回预览)、POST /api/chat/goal-channel/configure(切换 human-gate auto-notify)。所有操作先校验 Lark extension 已激活且带LARK_GOAL_CHANNEL_PERMISSION;provider API 失败只回 public_summary/blocker,不泄漏私有细节。 - 前端:新增 notifications drawer selection,回调接线
fetchGoalChannelTargets / setupGoalChannel / configureGoalChannelAutoNotify,页面入口 onOpenNotifications。 - 验证:新增
examples/loopx-chat-server-smoke.py覆盖三个端点。
具体改动
关键代码讲解
_goal_channel_context:解析 goal 的 source registry 与绑定文件路径,跨 registry 时正确加载 source registry。_goal_channel_setup:请求体白名单(goal_id/target/execute);_goal_channel_extension_ready校验扩展激活与权限;provider target 必须已配置;setup_lark_goal_channel(execute=...)遵循 preview/confirm 语义。_goal_channel_configure:仅允许 goal_id + auto_notify_human_gates;开启自动通知时同样要求 extension-ready;错误 typed(invalid/extension_unavailable/provider_target_missing/provider_api_failed)。DEFAULT_CHAT_HOST=127.0.0.1且非 loopback host 直接拒绝——新端点保持在本地信任边界内。
变更规模
9 个文件 +540/-8:chat_server +171、smoke +40、前端 model/page/css(+42)、其余接线。
对主干的风险
- 本地验证:
examples/loopx-chat-server-smoke.pyok;test_lark_goal_channel_notification.py+test_status_collection_material_capability_wiring.py10/10 passed。 - CI:build/dependency-review SUCCESS;pytest 失败为 base 预存的 budget-manifest 缺口(此前已独立核实,与 #3167 前端/chat-server 改动无关)。
- 安全边界:端点仅 loopback 可达;扩展未激活/未授权时拒绝;
execute显式门控;错误信息 public-safe;无凭据/私有路径进入前端 diff。 - 风险低:复用既有 Lark 扩展与 preview/confirm 语义,不新增权限绕过。
我的整体评价
APPROVE。页面内通知设置面板实现克制:loopback-only、extension-ready 门禁、preview→execute、typed 错误与 public-safe 输出;smoke 与聚焦测试通过。CI 前置项仍为 base budget-manifest 修复;本评审不构成合并授权。
English Verdict
- PR: #3167 — Personal workspace tier-1
- Exact head:
fe38dfde1fb77bf72b0fe60702e4f3b08d12c24c - Verdict: APPROVE
- Key finding: In-page Lark notification settings panel backed by three loopback-only chat-server endpoints (targets/setup/configure) with extension-ready + permission gating, preview-to-execute semantics, typed public-safe errors, and frontend drawer wiring. Smoke and 10 focused tests pass; build/dependency-review green.
- Note: CI pytest failure is the pre-existing base budget-manifest issue, unrelated to this delta; merge after base fix.
最新控制面预览已按首屏审核意见完成并推送最新实现:
验证:dashboard build、chat action smoke、run-history smoke、Playwright browser smoke、git diff check 均通过。 盲测最终定点复测:6 次操作、47 秒、0 次回退,可从 Task 进入完整执行过程与结果。 |
huangruiteng
left a comment
There was a problem hiding this comment.
PR #3167 Merge-Readiness Qualification
精确评审头(Exact Head): 3167@abb3e600424aca89007f895309197621a0810308
Base: frontend-control-plane-im-prototype-rfc(方向地图中 Operator Surface / IM 孵化的既定目标分支,base 本身无问题)
详细中文评审(合并就绪资格)
结论
当前 head 不满足合并就绪条件。PR 保持 APPROVED 的 reviewDecision,但那是 08-13 15:36 对 head fe38dfde1fb7 的批准;此后作者又推入 25 个 commit(08-13 → 08-16),exact head 变为 abb3e600,与上次批准之间的 delta 为 58 个文件、+6537/-716(工作区/Lark/session 功能、UI 简化、控制面修复、测试与生成 bundle)。旧批准不能自动覆盖这么大的新 delta;mergeStateStatus=UNSTABLE(pytest 仍 IN_PROGRESS,其余 6 项 check SUCCESS)也说明合并门未全绿。
依据(exact head 实测)
| 项 | 值 |
|---|---|
| 上次批准 head | fe38dfde1fb77bf72b0fe60702e4f3b08d12c24c(08-13 15:36Z,三次批准中的最后一次) |
| 当前 head | abb3e600424aca89007f895309197621a0810308(08-16 05:29Z) |
| 上次批准后新增 commit | 25 个 |
| 上次批准后 delta | 58 files, +6537/-716 |
| mergeStateStatus / mergeable | UNSTABLE / MERGEABLE |
| checks | dependency-review + build×2 SUCCESS;pytest IN_PROGRESS;其余 SKIPPED |
| 目标 base | frontend-control-plane-im-prototype-rfc(孵化分支,符合方向地图) |
对主干(集成分支)的风险
- 批准过期(P1,阻断合并):75% 的 diff 是在最后一次批准之后加入的;未经 exact-head 复审就合入会把未审的大块功能/UI/控制面改动带进孵化分支。要求:对
fe38dfde..abb3e600的 58 文件 delta 做一次 exact-head 双语复审(或按 UI / Lark / control-plane / tests 分区审完再合并),之后重新 approve 当前 head。 - pytest 未绿(P1,阻断合并):UNSTABLE 状态下不得合并;pytest 变绿并读回后再合。
- 生成 bundle 提交(P2):
chore(ui): normalize generated chat bundle是生成产物;确认其与源码一致且不被后续手改,避免 bundle 漂移。 - 大 PR 分批(P2):7750 行新增、64 文件;建议后续把 Lark 连接管理、session records、UI 面板拆成可独立合入的批次,降低单次评审面。
我的整体评价
方向与已有批准基础没问题(此前三次批准均认可实现质量),但「已批准」状态对 abb3e600 不成立:25 个新 commit 的大 delta 未经 exact-head 复审,pytest 也未绿。按资格判定,当前不可合并;完成 delta 复审 + pytest 全绿后再复审放行。
English Verdict
Verdict: REQUEST_CHANGES (merge-readiness not established) — exact head 3167@abb3e600424aca89007f895309197621a0810308.
The existing APPROVED reviews were submitted for head fe38dfde on 08-13; the current head adds 25 commits and a +6537/-716 delta across 58 files, and mergeStateStatus is UNSTABLE with pytest still IN_PROGRESS. Merge is blocked until (1) the fe38dfde..abb3e600 delta receives an exact-head bilingual re-review with a fresh approval, and (2) pytest passes. The integration-branch base is correct per the technical-directions map.
Exact-head readiness update
The requested exact-head re-review remains the only merge-readiness action. No merge has been performed. |
huangruiteng
left a comment
There was a problem hiding this comment.
PR #3167 全量双语评审(re-review)— Personal Workspace 交互流与热路径预算
精确评审头(Exact Head): 3167@6083f77412b5d7962c5f11c03f562e28687b306c
Base: frontend-control-plane-im-prototype-rfc | 类型: UI/control-plane(本轮 delta 15 files, +598/-276) | 作者: maxliux5
详细中文评审
动机
上一轮合并就绪资格评审(3167@abb3e600)的阻断点有二:旧批准(fe38dfde)不覆盖后续大 delta,以及 pytest 未绿。本轮新 head 6083f7741 新增 2 个 commit,把 Session 抽屉、Task 对话回执、Manager 对话托盘与主题切换等交互补齐,并更新 browser smoke 与 contract test;GitHub pytest 已转绿。但 exact head 上两个 durable 预算 smoke 失败:dashboard_status_json 与 status JSON 各 surface 超出 hot-path interface budget——这是本轮必须阻断的新问题。
改动思路
UI 侧沿用“单一数据模型 + 显式交互入口”的思路:Run 抽屉拆成“执行过程与结果 / 详情与操作”双 tab,用 sessionMessages 渲染真实执行记录;Task 视图新增“最近对话与 Task 状态”回执,回复可“转为 Task”草稿(预览优先);Manager 主页去掉冗余 worker strip,新增发送触发的对话托盘与完整 Chat 视图;主题切换(paper/brutal)持久化到 localStorage。前端同时把 execution session 结果从布尔值升级为完整 snapshot,供抽屉展示。
具体改动(相对 abb3e60)
run-row.tsx(+2/-1):run 行增加“查看执行过程与结果”可访问标签与 open label。channel-header.tsx(+21/-1):Manager Chat 导航(总览/Chat)与主题切换按钮。context-drawer.tsx(+100/-54):run 抽屉双 tab(record/details),sessionMessages记录、空状态、running 脉冲;决策按钮改为“查看影响并决定”。goal-tasks-view.tsx(+32/-2):最近对话回执 + “转为 Task”草稿桥。personal-workspace-model.ts(+8):新增WorkspaceSessionMessage与sessionMessages。personal-workspace-page.tsx(+106/-25):ManagerConversationTray、managerChatOpen、主题状态、回执显隐、Task 草稿生成、worker strip 移除。personal-workspace.css(+57/-15):回执、托盘、双 tab、session record 样式与 brutal 主题变体。dashboard-page.tsx(+52/-31):isManagerProjectionQuestion稳定意图短语、next-action 优先于只读边界、execution session snapshots(完整ChatSessionSnapshot)、manager 问答用全量 projection。personal-workspace-contract.test.mjs(+42/-3)与personal-workspace-browser-smoke.mjs(+56/-14):覆盖新交互与主题。loopx/web/chat/(bundle + index.html):生成产物随源码更新。
关键行为讲解
- Run 抽屉双 tab:默认展示执行记录(
sessionMessages的 user/assistant 消息 + 空状态),操作(恢复/纠偏/中断/关闭)移到“详情与操作”,避免把元数据与执行证据混排。 - Task 对话回执:只读分析不会静默改 Todo;最新回复提供“转为 Task”草稿桥(
创建一个 Task:<reply 摘要>),发送前仍走 preview——符合“预览优先、不隐式写”的既有边界。 - Manager 对话托盘:发送后才出现、可展开为完整 Chat 视图、总览保持四车道;
managerChatOpen与managerConversationReceiptVisible由测试锁定生命周期。 - Manager 问答优先级:
asksForNextAction现在先于“状态/健康”匹配,且只读边界提问(“只读回答”)不再把 next-action 降级——测试断言 next-step 回答仍留在 manager 路由。
正向路径
Task 只读分析 → Chat 返回两点理由 → Tasks 页出现对话回执(“Agent 已回复/本次对话没有直接修改 Tasks”)→ “转为 Task”生成草稿 → 发送后走 preview 确认 → 不隐式写 Todo。
负向路径
run 无记录 → 空状态解释等待/恢复路径;resume_failed → 恢复面板;manager 发送后回执只在本次会话出现,返回总览即清除;主题切换失败时保留内存偏好。
对主干的风险
阻断项(P1,热路径预算回归):exact head 上两个 durable smoke 失败(已复现):
examples/control_plane/hot-path-interface-budget-smoke.py:dashboard_status_json= 18747 chars > 18200 预算(operator dashboard 首屏)。examples/control_plane/cli-output-budget-regression-smoke.py:status的 crowded/multi_agent/small JSON surface 各自 +~712 chars / +25 lines(allowance 4),compact payload +497566(allowance 96256)——超出同 fixture base/head 增长容许量。
说明:本 PR 的 browser smoke 与 contract test 通过,但 status 投影的实际增长突破了仓库预算 gate;合并前必须把 dashboard_status_json / status JSON 压回预算(例如对新增字段做 compact/omit 或在投影层裁剪),而不是仅改 smoke。
已解除:pytest 现已 PASS(5m45s);exact-head 复审本身补上了上一轮缺失的 delta 覆盖。
非阻断(P2):
- 生成 bundle(
index-BvZUAClC.js/css)是构建产物;请确认与源码构建一致、不被手改(本轮无法在干净 worktree 独立重跑 vite build)。 - 大 PR 分批建议保留:后续把 Lark 连接、session records、UI 面板拆批,降低单次评审面与回归定位成本。
personal-workspace-browser-smoke在干净 worktree 需 dashboard node_modules(vite/playwright),本轮未独立运行;source-level contract test 已通过。
验证矩阵(exact head 实测):
| 场景 | 结果 |
|---|---|
personal-workspace-contract.test.mjs |
PASS(source contract) |
hot-path-interface-budget-smoke.py |
FAIL:dashboard_status_json 18747 > 18200 |
cli-output-budget-regression-smoke.py |
FAIL:status JSON +712 chars/+25 lines 超 allowance |
| GitHub checks | pytest/build/dependency-review pass |
| 本轮 delta | 15 files, +598/-276(UI + smoke + bundle) |
我的整体评价
交互设计与可访问性明显进步:执行记录、Task 对话回执、Manager 托盘与主题切换都有明确状态机与测试锚点,上一轮“旧批准不覆盖新 delta”与 pytest 未绿两个问题已解决。但 status/dashboard 热路径 payload 突破预算 gate 是硬阻断——仓库用 durable smoke 守住 operator 首屏与 CLI 输出边界,当前 head 把这两道门都顶开了。结论:REQUEST_CHANGES(窄化到热路径预算回归修复)。
English Verdict
Verdict: REQUEST_CHANGES — exact head 3167@6083f77412b5d7962c5f11c03f562e28687b306c.
Summary: The UI delta is well-designed and covered (session record drawer, task conversation receipt with preview-first task draft, manager conversation tray, theme toggle; source contract test passes; pytest is green). However, the exact head breaks two durable budget gates: dashboard_status_json is 18,747 chars vs a 18,200 budget, and the status JSON surfaces grew ~712 chars / 25 lines beyond the CLI output-budget allowance. Both failures are reproduced locally. Merge is blocked until the status/dashboard projection is brought back under budget (or the growth is justified through the proper budget process — not by editing smokes).
P2: verify the generated chat bundle matches the source build; consider splitting Lark/session/UI work into smaller batches.
118325a
into
frontend-control-plane-im-prototype-rfc

Summary
Validation
Notes