Repository navigation
feat(workspace): notify the owner when a new decision waits - #6533
huangruiteng merged 13 commits into
Conversation
bcf22ec to
1ddac9b
Compare
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
1ddac9b to
7120a2e
Compare
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
The workspace only re-reads status after the owner's own actions, so a decision raised while they are elsewhere stays unseen until they refresh. For a product whose promise is long-running work with human judgment kept in the loop, the item that needs the owner has to reach them. Add a per-browser, default-off "Desktop notifications" switch under Settings → Appearance. Turning it on asks the browser for permission from that click and keeps the preference only when granted; the page reads the browser's answer back (allowed, blocked with how to fix, unsupported). While on, the page re-reads the workspace in the background every 30 s (60 s hidden) through a new `onBackgroundRefresh` callback that keeps the current snapshots on screen, and sends one notification per new item that waits on the owner while the page is hidden or unfocused. Items already waiting are seeded as seen, so enabling or reloading replays nothing; progress, runs and counts never notify; bursts collapse into three items plus a summary. Clicking a notification returns to that Goal and opens the item in the drawer. Turning the switch off stops the background reads. The admission rule, identity, cadence and content live in a pure module with a node test; the browser scenario stubs the Notification API and uses fake timers to prove the opt-in, the background re-read, the first notification, no re-notification, click-through, off-state parity and reload re-seeding. Read-only sources never offer the switch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
Add attention-notifications-live: a disposable real Chat/Todo owner serves status.json, a user gate is added through the CLI while the tab is in the background, and the next background re-read raises exactly one notification carrying the gate text. The fixture scenario keeps the timing and permission-denied cases. Only the status path is proxied to the real owner; the document URL also ends in status.json (its statusUrl query), so a glob would swallow the page load. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
54fb491 to
dce3072
Compare
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
Signed-off-by: song <liusongstep@gmail.com>
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
…ications-20261015 Signed-off-by: song <liusongstep@gmail.com> # Conflicts: # apps/presentation/dashboard/package.json # apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs # examples/personal-workspace-browser-smoke.mjs
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
…ications-20261015 Signed-off-by: song <liusongstep@gmail.com> # Conflicts: # apps/presentation/dashboard/package.json # apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max
REQUEST_CHANGES:两项独立复现的 P2,见行内评论。 绑定当前完整15文件 +565/-3,head 1d9798b1d6272e569389b41e353425797e60994e,实际共同基线 a99cb07addfc163fda9435c6455cc9462b657855。
动机
把长时间工作交给 Agent 的所有者,在另一个窗口工作时仍需要及时知道哪项决定正在等自己。以前要回到工作区并手动刷新才能发现新决定;提案允许一次开启浏览器通知后自动读取并直达事项。但当前连不阻塞工作的可选提醒也会通知,外部解除通知权限后页面仍无法重新开启。应在不打扰既有关闭用户的前提下,只把新出现、确实等所有者决定的事项送到眼前,并能按页面提示从拒绝权限恢复。本次只评审页面保持打开时的浏览器通知;不认证真实 OS 送达、已安装桌面 WebView、服务端推送、Lark 或长期轮询成本。
改动思路
设置页保存浏览器本地偏好,点击开启时请求通知许可。一个共同激活条件控制后台全量 status 读取和新事项通知;页面未被注视时才构造通知,点击后先切到对应 Goal 再打开 drawer。原有 Todo/attention owner 和执行权限都没有写回。已有 typed attention 投影负责是否等待所有者,浏览器模块只应负责通知准入与展示;复用现有后台 status 读取比再建服务端通知系统更小,但不能把所有 User Todo 重新解释为决定。本 PR 应闭合一个默认关闭的浏览器通知旅程:授权、当前 blocking 事项差分、背景读回、直达事项及权限恢复;修复限于既有 projection 与本地 permission owner,不扩大到通用推送或执行授权。
这是一条有用且可回滚的功能切片。保留关闭用户原路径、许可与只读限制、失败后重试和可读文案,比另建一个独立服务更合适。最强反例是:所有正例都绿,但一个可选建议依然被当作必须决定来打扰,或浏览器已允许而 UI 永久停在拒绝状态。当前两者都能在真实构建中出现,故不能仅由正例批准。
具体改动
规范依据:固定基线 a99cb07addfc163fda9435c6455cc9462b657855 的 personal-workspace-user-guide 明确可选提醒会列出但不计为等你决定;同版本 design.md 要求诚实状态、错误恢复和就地操作。通知专用完整规范此前没有;默认关闭、仅新决定、许可回读与背景读取的目标来自本 PR 的具体请求框架,不能用新增测试替它自证。
- DECISION-ONLY:not_met。 独立真实 Todo CLI 创建
task_class=user_action,真实 status 给出waiting_on=codex,即并未等待所有者。当前打包页面却发出“Consider renaming the export folder”通知。已有投影的blocking=false被差分路径忽略。 - PERMISSION-RECOVERY:not_met。 打开时 API 为 denied,UI 禁用开关;将浏览器 permission 改为 granted,触发 focus/visibility、手动刷新、离开并重新打开设置后,仍显示“已拒绝”且 disabled=true。页面指导“允许后重新开启”,实际仍无法操作,只有重载整页重新初始化。
- OFF-ISOLATION:implemented。 实际基线和当前 head 各推进70/65秒时钟,关闭均为零许可请求、零通知、零后台读取;常规开启、停止轮询、原有身份去重与通知点击可达路径通过。
- PACKAGED-JOURNEY:implemented。 正常编译包配真实 Chat/Todo 后端,以独立一次性状态验证 CLI 新建 gate 后背景读取与 drawer 直达;仅 OS Notification 传输替代。真实 OS、安装 App 及跨 WebView 资格 deferred,未宣称完成。
关键代码讲解
attentionNotificationKey/newAttentionItems:用 Goal+Todo 身份差分,内容和排序不是业务身份;内容截断、三项加汇总留在浏览器投影层。updateAttentionNotifications:唯一主动请求 permission 的点击入口,成功后才持久化偏好,并 seed 当前事项。许可状态只在初始化和这个 handler 更新,外部变化无法触发 handler,因为 denied 已禁用了它。PersonalWorkspacePage的通知 effect:把整个model.userTodos交给差分,而不是现成的 blocking 子集;所以 typed 可选事项被重新解释为必须决定。这是新通知路径的缺口,不要求改变原列表或增加文本分类规则。WorkspaceSettingsPage:显示 checkbox 与中英 permission 状态;disabled 条件没有重新检查浏览器当前答案的机会。onBackgroundRefresh则复用 dashboard 的保留快照读取,不创建另一份业务状态。
对主干的风险
通知/return/suggestion 单元共28项通过;完整 workspace contract、显式项目 tsc 与 CSS 检查通过。正常打包后的通知、真实 Todo 后端通知、语言、capability-scope、return 和 suggestion 六场景通过。原生 canary 3 direct + 13 selected 通过。五个提交均有 DCO sign-off;新 head 与读取到的 main fbbe8a2c7a73d3355942783be606994b046ae755 静态 merge-tree 可组合,只是图证据,不冒称执行了该组合或远端 CI。未查询、等待或触发 CI。
P2-1 最小修复:通知 seed、signature、差分和内容统一消费已有 blocking 分类,保持 optional 仍可在工作区查看,不按文字猜测,不为通知另造业务状态。扩展真实 backend 场景:只有 user_action 时零通知,新 user_gate 或 run operator gate 才通知;同时保留更新文本、去重、停用及关闭路径。
P2-2 最小修复:在返回页面/打开设置或 permission change 时回读同一浏览器 permission owner,让外部解除拒绝能按提示再次开启,外部撤销也反映真实状态;不自动请求权限或悄悄启用。加入 denied→granted→点击开启,以及 granted→denied 的恢复/停用回归。当前判定使用动态 permission 替代 API,其余 React 状态和编译代码真实;未触发用户的真实权限提示或 OS 通知。
独立 base/head 外观截图显示,新设置沿原页面容器、单条分隔线和主题 token,桌面与390px没有新增视觉竞争;许可错误在同处可读。偏好默认关闭不增加原用户准备或确认轮次;首次开启多出的浏览器许可是必要授权。开启后每活跃 Goal 的轮询成本尚未做长期测量,不能把局部时钟测试当成运行净益。
语义与 CI 对齐
复用现有 WorkspaceAttention.blocking;新增四值 support 只是本地浏览器读模型,三种 Web Permission 加 unsupported,不是共享控制面词表或执行权。语义 advisory 没有检出支持范围内的新 carrier,但它未覆盖所有 UI union,不能据此断言无语义变化。前瞻性重构限于统一可通知事项选择器和 permission 读回,泛化全局 live-refresh 留给已有后续 owner,不是本次必需门。
初版私有探针另有一个“临时移出再返回”观察步骤超时,未据此增加第三个产品缺陷;修正后只重跑两个已复现反例和 granted+偏好开启的 reload,全部完成,reload 零重放。初次 tsc 命令从错误目录只打印帮助,已保留并改为显式项目执行通过,不算产品失败。
我的整体评价
REQUEST_CHANGES。 long_horizon 为 not_yet_proven:关闭路径保持,开启后确能让真实新 gate 到达页面,但无长期成本/净益资格。user_experience 为 not_yet_proven:正常开启到直达事项已验证,可选事项误打扰和权限恢复断路使完整承诺尚未满足。两项修复都可在既有 browser/attention owner 内完成,机制规模合适,不需要更大通知框架。当前评审不授权 source 修改、merge 或升级。
English verdict: REQUEST_CHANGES - HEAD 1d9798b. The packaged real-service positive path works, but optional nonblocking user actions also notify, and a browser permission grant cannot recover the disabled stale-denied UI without a reload. Reuse typed blocking classification and refresh permission readback; real OS/App delivery remains untested.
Review found two gaps in the needs-you notifications: - The diff read every User Todo of the Goal, so an optional user action (listed, not blocking) notified like a decision. Every listed item is now remembered when the switch turns on and on each read, and only an item first seen while the attention owner classifies it as waiting on the owner notifies (`notifiableAttention`). An optional action that later turns blocking because a gate appeared is not a new item either. - A browser that had blocked notifications disabled the switch for good: the only code that refreshed the permission readback was the toggle handler the disabled switch could never reach. The page now reads the browser's answer back on return (focus, visibility), when Settings open and when the permission owner reports a change; nothing asks for permission or turns the switch on by itself. A grant made in the site settings makes the switch usable again; a revocation while on shows as blocked and stops the background re-read. Unit test for the notifiable selector; contract pins for both rules; the fixture scenario adds the blocked → allowed → on → revoked path without a reload; the real-service scenario raises an optional user action through the CLI first (nothing notifies), then a gate (exactly one notification). The user guide states both behaviors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
|
Both P2s are fixed on head P2, optional actions notified. Every listed item is remembered when the switch turns on and on each read, and only an item first seen while the attention owner classifies it as waiting on the owner notifies ( P2, a denied permission could not recover. The page now reads the browser's answer back on return (focus and visibility), when Settings open, and when the permission owner reports a change ( Validation on this head. |
…ex/pr6533-mixed-notifications-20261016 Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max
REQUEST_CHANGES — head 28ab8ba671995ba83b123a650e939ad1c715e09e。 原来 optional 误通知与权限无法恢复两项已验证修复;当前仍有一项 P2:部分列表恢复会重放开启时已见的 gate。
动机
开启浏览器提醒、需要离开工作区继续其它工作的所有者。
以前 Agent 新增决定后,必须自己返回并刷新才看得到;现在显式开启后,后台读取应把新决定送达并直接打开事项。
正常编译页面配真实 Todo 后端已验证新 gate 到达和直达;但部分列表恢复会把开启时已经见过的事项误当新通知。
不交付服务端推送、真实 OS 通知、安装 App 资格或执行授权。
同次开启的已见身份仍需跨部分列表保留;真实 OS、安装 App 与长期轮询成本尚未验证。
这次重新核验完整提案,没有把 1d9798b1d6272e569389b41e353425797e60994e 的旧结论当作当前结论。背景读回与通知直达有真实用途;需要修的是同一观察周期的身份记忆,不需要另建推送系统或要求用户再次决定同一事项。
改动思路
已有 typed attention owner 负责逐条分类,通知只承担浏览器观察;修正共享投影比另写通知专用分类更合适,部分列表不得重置已经见过的身份。
本 PR 的边界是默认关闭的浏览器提醒、当前事项直达和权限恢复;保持同次开启的身份记忆,不扩展到服务端推送或执行权限。
下表是本 head 的源码路径说明,执行证据在后文,表格本身不是验证记录。
| 路径 | 当前行为 |
|---|---|
| 默认关闭 | 原页面与 Todo 操作继续;没有权限请求、外部通知或后台状态轮询 |
| 显式开启 | 浏览器允许 → seed 当前事项 → 原 status loader 后台读 → 仅新的 blocking 项通知 → 原 Goal drawer |
| 权限变化 | focus、visibility、设置打开与 permission change 重读;不自动请求许可 |
| 部分列表 | 当前代码把已见集合替换成当前所列集合;旧身份重新出现时被当成新事项,这是剩余缺口 |
共享 mixed queue 的改动有必要:同一批刚到达的 user_action 与 user_gate 不能都沿 Goal 级 waiting_on 被分类成决定。当前用原 attentionDetailsFromSnapshot 的 typed interaction,完整列表逐项计数,部分列表保留未知,所有原消费者共用这一修正;这项修正也在通知关闭时生效,PR 已明确披露,不能称所有投影都与基线逐字节相同。
具体改动
独立判断基准为 docs/guides/personal-workspace-user-guide.md,固定 revision 148290c003aa6e59722b2a12599cc83e0dd4a2f6,另对照同版本设计与 frontend-delivery 规则。新增的 §7.1 是本提案的说明,不作为自己的独立批准依据。
- 4.1.1:implemented。 原合同区分需要决定与可选提醒。真实 Todo 后端中,先到 optional、以及同一读中新到 optional+gate 均验证只有 gate 通知,原 optional 仍可见;修正没有加文字猜测规则。
- 4.1.2:部分来源的保守语义未贯穿新通知。 已有 return-marker 合同不是通知协议,不能把它的实现直接复制成通知 owner;但它以及既有
refreshAttention均明确不把部分/缺失视为事实消失。当前数量投影已保留 unknown,新增观察者却遗忘已见身份,故该不完整来源反例仍是 not_met。 - 权限恢复与停止:implemented。 正常编译场景已独立执行 denied→外部 granted→原页显式开启→revoked 停止读回;已允许时零新增许可请求。OS transport/permission 事实受控,其余页面与回调是实际编译代码。
关键代码讲解
notifiableAttention:从现成 blocking 字段选择可通知项,保留 optional 在原列表中的可见性;它不解析文本、不创造审批义务。buildPersonalHomeModel:把现有 typed interaction 用于逐条分类;只对没有明确分类的旧行保留 Goal 级 fallback,部分计数不冒充完整总数。updateAttentionNotifications:只有显式开关点击可请求浏览器权限,允许后持久化本地偏好并 seed 当前事项;权限外部变化沿同一 owner 读回。PersonalWorkspacePage notification effect:原 status loader 产生新的 model.userTodos 后,用 Goal+Todo 身份做差分;当前每次以 keys 覆盖 seen,导致部分来源后旧项重放。
此外,设置 props、两种语言、两条 CSS、背景刷新回调与脚本入口一同接通;两个浏览器场景分别覆盖夹具行为和真实 Todo/status。整批为15文件、+694/-14,8个提交均有 DCO,PNG/指南用于解释实际可选设置,没有新增控制面写入或消息授权。
对主干的风险
P2:不要在部分读回时忘记开启当下已知的身份。 在正常编译包中,把一个已存在且 open 的 gate 放进初始队列;开启时 seed 后为0通知。仅反转列表仍为0;随后同一来源返回 partial 视图:仍报告完整 open_count,但暂未列出这条 gate,再恢复相同 Todo ID、相同文本、相同 open 状态的完整视图,结果出现1条通知。整个过程中没有新增或重开 gate。页面无 JS 错误。这不是实际新决定,却突破了“开启时已等待的事项不重放”的承诺。
触发点是 seenAttentionKeysRef.current = keys,它保存的是上一张列表而不是本次开启的已见事实。最小修复是在同次开启内保留已经观察过的身份,不把缺失/截断当作遗忘依据;显式关闭和正确来源生命周期仍可重置。加入 full→partial→full 保持0、顺序变化保持0、真正新 gate 为1的包内回归。只加同一完整列表连续读两遍的测试不能覆盖这个缺口。
当前独立验证:29个通知/return/suggestion 单元通过;完整 workspace contract、显式项目 tsc、CSS、正常 Chat build/source verify 通过;八个正常打包场景(通知、真实后端通知、return、suggestion、语言、capability scope、user_action、Goal M2)通过;原生 canary 3 direct +13 selected 通过。实际 base/head 各推进70秒,关闭时均为0权限请求、0通知、0后台读取。上述通过保留,但不能覆盖独立部分来源反例。未查询、触发或等待 GitHub CI。
桌面与390px的设置视图已核验;普通默认关闭用户不增加准备或确认轮次,首次浏览器许可是必要授权。真实 OS 送达、安装 App/WebView、长期 N Goal 轮询成本没有在这次源码阶段被认证;不把这些更大验收当成本次局部修复的新增前置条件。
语义与 CI 对齐
复用现有 task_class/interaction/blocking。四值浏览器 support 是 Web permission 加 unsupported 的本地派生视图;没有新增 agent obligation。语义 advisory 后完整语义检查通过,但静态检查不覆盖观察记忆的全部行为。前瞻性重构已把 mixed queue 规则放回原 typed owner;本次身份保留修复也应留在这个浏览器观察边界,通用实时推送可保持既有后续。
我的整体评价
提案是有用且规模合适的 justified_increment,architecture assessment 为 retain;仍需修复当前 P2,结论为 REQUEST_CHANGES。long_horizon 与 user_experience 在这条路径上均为 regression:部分视图可触发无新事实的再次打扰,增加所有者处理成本。两项旧问题的修复与所有正向证据均保留;不要求扩大到新消息平台,也不把 review、绿色检查当成 merge 或安装授权。
English verdict: REQUEST_CHANGES - 28ab8ba; the optional-action and permission-recovery fixes pass current packaged real-service tests, but full→partial→full status replay forgets a seeded gate and emits a false new notification. Preserve seen identities for the activation lifetime; 29 unit cases, eight packaged scenarios and local canary pass. Real OS/App and sustained cost remain unqualified.
…tivation Review reproduced a false notification without any new fact: a gate already waiting when the switch turned on was seeded, a partial status read then omitted it while still counting it, and the next full read listed it again as "new". The diff replaced the seen set with each read's identities, so a partial source forgot what the owner had already been shown. Identities seen while notifications are on are now kept for the whole activation (the set only grows until the switch turns off or the page reloads), so an item that a partial read omits is still known when it returns. The contract test pins that the seen set is never replaced by the current read; the fixture scenario runs full → partial → full on a seeded gate and asserts nothing notifies. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
The per-row attention classification dropped the owner's count whenever the listing was partial: with 21 open and 3 listed, the Needs-you vital showed "—" instead of 21 and the home no longer knew the Goal waited on the owner. Listed rows keep their own class; the rows the owner counts but does not list carry none, so they take the queue's answer exactly as a listed legacy row does. Coverage still reports partial. The masthead scenario's "optional" mode now makes its listed rows optional by class as well as pointing the queue at the Agent, since an explicit class owns its row regardless of the queue's answer. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
|
Fixed on head P2, a partial read replayed a seeded gate. Identities seen while notifications are on are now kept for the whole activation: the seen set only grows until the switch turns off or the page reloads, so an item a partial read omits (while still counted) is still known when the next full read lists it again. The contract test pins that the seen set is never replaced by the current read. Fixture scenario: a gate already waiting at enable time, full → partial (gate unlisted, count kept) → full, zero notifications; the existing new-gate, dedupe, click, off and reload paths follow unchanged. Also on this head. The per-row classification from the previous commit dropped the owner's count whenever the listing was partial (the Needs-you vital showed "—" for 21 open, 3 listed, and the Validation on this head. |
… into codex/pr6533-partial-observation-20261016 Signed-off-by: song <liusongstep@gmail.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max
REQUEST_CHANGES — head 18e296e4461eee652abe08d0d8b2f78139b0c111。 上轮已见 gate 重放已验证修复;当前新加入的部分列表计数会把未列出的可选事项算成“等你决定”,仍有一项 P2。
动机
开启浏览器提醒、需要离开工作区继续其它工作的所有者。
以前 Agent 新增决定后,必须自己返回并刷新才看得到;现在显式开启后,后台读取应把新决定送达并直接打开事项。
已见 gate 在部分列表消失后恢复不再误通知;真实后端的新 gate 到达及直达仍通过,但混合事项的部分列表会夸大等你决定的数量。
不交付服务端推送、真实 OS 通知、安装 App 资格或执行授权。
混合部分列表的分类总数仍需保留未知或读取权威分类计数;真实 OS、安装 App 与长期轮询成本尚未验证。
普通任务是让所有者在其它窗口工作时收到真正新增的决定,并返回原事项;不是让其学习队列分类或自行核实每一个红色数字。复用现有状态 loader、typed attention 和本地浏览器偏好是更小的合法路线,不必增加服务端推送或新的消息权限。完整提案有实际用途,但同一组规范事项仅因预览被截断就改变决定总数,会把本应由 Agent 吸收的核实工作交还给所有者。
改动思路
已有 typed attention owner 负责逐条分类,浏览器观察者只负责身份记忆和通知;不能把未列出的事项按整个 Goal 的等待状态补算成决定。
本 PR 交付默认关闭的浏览器提醒、当前事项直达和权限恢复;共享 mixed queue 修正须保留部分分类未知,不能借通知开关隐藏默认路径的错误。
以下是当前 head 的源码说明,实际反例与打包验证另列。
| 路径 | 当前行为 |
|---|---|
| 默认关闭 | 无权限请求、通知或后台轮询;共享 attention 总数修正仍影响原页面 |
| 显式开启 | 读浏览器许可 → seed 当前身份 → 原 loader 定时读 → 新 blocking 项通知 → 原 Goal drawer |
| 权限外部变化 | focus、visibility、Settings、permission change 读回,不自行请求或开启 |
| 已见身份暂时缺失 | 同次开启的 seen 集合保留,full→partial→full 不再重放 |
| 混合部分列表 | 新代码以 Goal waiting_on 给所有未列出项猜类别,造成虚假的决定总数 |
逐条显式 task_class 应继续由原 attentionDetailsFromSnapshot 解释。旧行没有分类时的 fallback,与一个真实有分类但没出现在三条预览里的事项,是不同情况。前者兼容逻辑不能自动证明后者全是决定。通知只消费 blocking;其它 masthead、rail、建议和 return-marker 也消费相同摘要,因此修复必须守住共享 owner 的知识边界。
具体改动
独立依据在 diff 前固定为 docs/guides/personal-workspace-user-guide.md 的4.1.1/4.1.2,并复核同版本 design/frontend-delivery。4.1.1明确可选提醒可列出但不算决定;4.1.2的“不完整观察不是新事实”只作相邻接受原则,不把 return-marker 实现冒充通知协议。新增7.1指南属于被审提案。
- 通知身份与权限:implemented。 实际编译 observer 的已存在 open gate 在开启时 seed,反转、部分遗漏、原样恢复全为0通知;真正新 gate 仍有1通知并直达。旧 optional 误通知及 denied→granted→显式开→revoke 均在当前场景通过。
- 混合列表的决定总数:not_met。 完整列表能按行区分可选和决定;新补算把未列出的分类未知变成有数值的猜测,违背4.1.1,且在通知未开启时就影响原展示。
关键代码讲解
notifiableAttention:消费原 attention owner 的 blocking,仅让新出现的必要决定通知,optional 仍留在原列表。buildPersonalHomeModel:按现有 interaction 解释逐条分类;新未列出项补算错误地把1个 gate 加20个 optional 显示成19个决定。updateAttentionNotifications:只有显式开关点击请求许可;允许后持久化偏好并 seed 当前已见身份,外部许可变化沿原 owner 读回。PersonalWorkspacePage notification effect:同次开启保留已见身份并集,部分来源暂缺不再遗忘;仅新的 blocking 项交给通知和原 drawer。
本轮 buildPersonalHomeModel 的关键新增在 dashboard-page:969–975:unlisted=count-listedOpen,再以整个 Goal 的 blocking 布尔值把 unlisted 全加到 blockingCount。seen 的修复保持在原 page effect 内,只做并集保留,未另立身份 owner。其余完整提案包含本地设置及 props、30/60秒背景刷新回调、通知内容/批量上限、点击后延迟选择、双语与 CSS、指南/PNG、两条浏览器场景及节点测试;本头还调整 masthead optional fixture 的真实 task_class。整批16路径、+732/-15,10个提交 DCO 完整。与前一头逐路径比较后重读5个变化路径,未继承前次结论。
对主干的风险
P2:不要把未列出的事项按 Goal 等待状态补算为决定。 通过真实 collect_status 构造一次性 Goal:1个 user_gate、20个 user_action,均 open。原 owner 报21个 open,原队列上限12、project overview上限3;没有编造 UI 的 ready-made 模型。正常编译页面在未开启通知时,将原生 payload 显示为 19项等你决定、2项可选。仅反转同三项仍是19;换成同一规范队列的另三项预览变成18/3。真实需决定事项始终只有1个。masthead 的红色数字和快捷建议一起采用了这个猜测,页面无 JS 错误、0 Turn、0 action apply。
配对执行同一真实 payload、相同浏览器 harness 和正常编译入口:上一修复头 28ab8ba67 在三种视图均显示分类总数未知(—),本头变为19/18;独立不变量断言上一头通过、本头失败。未知在这里是合法信息,不能因旧 all-gate 场景期待21而伪造混合分类。所有 open 的总数可以继续展示,但不能换名为等你决定。最小修复是保留分类未知,或消费已有权威 owner 能证明的分类聚合;“没被预览列出”不能等价于“旧行没有 task_class”。补回真实1 gate+20 optional的有界视图回归,同时保留全 gate、完整 mixed、无总数和真正新 gate 的正反例,无需增加权限或新框架。
当前29个通知/return/suggestion 单元、完整 workspace contract、显式 TypeScript、CSS、正常 Chat build/source verify 通过;九个正常打包场景(通知、真实 Todo、return、suggestion、语言、capability scope、user_action、Goal M2、masthead)通过。原生 canary 3 direct+13 selected 通过,0 manual hold;advisory 在完整语义检查前执行。关闭时推进70秒为0许可请求、0通知、0后台读取;原实际 base 的相同 off 观察按源码/规则/消费者/平台/依赖失效矩阵保留。通过项不覆盖上面的真实 mixed partial 反例,当前失败没有归咎于 CI,也没有读取、触发或等待远程 CI。
语义与前瞻性检查
复用既有 task_class、interaction、blocking;四值 Web support 只是本地浏览器派生状态,没有新增原生执行义务。通知仍默认关闭,但共享摘要变更不能宣称全部隔离:已披露的逐条分类修复有依据,新未列出项猜测没有。前瞻性 pass 应继续留在原 typed projection,避免再添通知专用分类器;已修复的身份并集保留。普通默认路径增加0必要澄清轮次,浏览器许可只在用户主动开启时询问;真实 OS/App/WebView和长期N Goal轮询成本仍未验证,不以这些更大前沿扩大这次 P2 的修复要求。
我的整体评价
这是有用且规模适当的 justified_increment,architecture assessment 为 retain。当前通知修复成立,旧重放 blocker 可以关闭;仍应 REQUEST_CHANGES,因为共享部分列表的分类总数存在新回归。long_horizon 与 user_experience 在该路径为 regression:没有新的决定却呈现更多必须处理的工作。修复应集中于知识不足时的计数语义,保留已有通知、权限恢复和正向旅程,不要求重造推送系统,也不把本评审当作源码修改或合并授权。
English verdict: REQUEST_CHANGES - 18e296e; seeded full→partial→full replay is fixed, but a real bounded source with1 gate+20 optional actions renders19 decisions (18 under another equivalent preview), even with notifications off. The same packaged predecessor reports unknown. Preserve unknown category totals or consume authoritative typed aggregates;29 frontend cases,nine packaged scenarios and local canary pass but the independent mixed-source assertion fails.
Signed-off-by: song <liusongstep@gmail.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max
APPROVE — head 821a3da127a89db48c2b4ff6c164f19d7ec517ba。 上一头的 mixed partial 计数反例已修复;在官方实际 base f899e7c3859fa429e28b2fa37e6a072f665fe4de 上重新判断完整17路径提案,不继承旧结论。
动机
开启浏览器提醒、需要离开工作区继续其它工作的所有者。
以前 Agent 新增决定后,必须自己返回并刷新才看得到;现在显式开启后,后台读取应把新决定送达并直接打开事项。
正常编译页面配真实 Todo 后端已验证新 gate 到达和直达;已见身份跨部分列表不再重放,混合部分列表保留分类未知,完整分类列表显示正确总数。
不交付服务端推送、真实 OS 通知、安装 App 资格或执行授权。
本段浏览器提醒、权限恢复与共享分类修复已验证;真实 OS、安装 App/WebView及长期轮询成本仍沿既有验收推进。
普通任务是让所有者在其它窗口工作时收到真正新增的决定,再直接回到该事项。原页只有主动刷新才发现新工作;更小的合法路线是复用既有 loader 和 typed attention,加一个明确同意后才运行的浏览器观察者。用户不应为虚假红色数字额外核实整个队列,也不必先学习通知平台、另建团队或复制状态命令。这段提醒及共享摘要修复能独立使用和回滚,真实 OS 和安装态仍有各自验收。
改动思路
既有 typed attention owner 解释事项类别,只有完整分类行才证明分类总数;浏览器观察者仅保留同次开启的已见身份并消费 blocking,不另立权限、计数或通知专用分类器。
本 PR 交付默认关闭的浏览器提醒、原事项直达和权限恢复,并修正同源共享摘要;真实 OS、安装 App 与长期成本继续沿原验收,不把命令或通知 API 存在当作执行授权。
下表说明本 head 的源码路径,表格本身不是执行记录。
| 触发 | 结果与停止边界 |
|---|---|
| 默认关闭 | 不请求许可、不发通知、不后台读状态;可发现但关闭的设置 |
| 显式开启且浏览器允许 | seed 当前事项,沿原 loader 30/60秒读,仅新的 blocking 身份通知 |
| 点击通知 | 切到原 Goal 后延迟打开该事项的原 drawer |
| 外部许可变化或关闭 | 读回浏览器事实;撤销/关闭停止观察,不修改原生授权 |
| 有界列表暂缺旧事项 | 保留同次开启的 seen 身份,不把原样恢复误当新决定 |
| 分类来源不完整 | 保留分类总数未知;完整 typed 行集合才证明决定/可选各多少 |
设置和通知的 owner 是当前浏览器,类别和动作权威仍是原 Todo/attention owner。已有 Goal waiting_on 可以解释无分类的旧行,却不能证明未列出事项的类别。共享修复在通知关闭时也生效,PR 已明确披露;没有声称整个投影与旧错误输出逐字节相同。
具体改动
在新 diff 前先核对 personal-workspace-user-guide.md 的4.1.1/4.1.2及同版本 design、frontend-delivery、overall roadmap,正文 blob 与先前完整读取相同。4.1.1要求可选提醒不计为决定,4.1.2提供相邻的不完整观察保守原则;新7.1是本提案内容,不是独立批准来源。仓库经验建议已在新 diff 前读取,并用于普通任务与恢复判断,没有继承案例 verdict 或宣称长期净收益。
- 需要决定与可选事项:implemented。 原生有界混合来源显示分类未知;真实完整 cold Todo read 的21 gate、1 gate+20 optional、21 optional分别显示21、1、0。没有用新实现的输出反推期望值。
- 已见身份:implemented。 同一 open gate 在开启前 seed,反转、暂时漏列、原样恢复均0通知;真正新 gate 仍通知并直达。
- 许可恢复和停止:implemented。 原页 denied→外部 granted→显式开启→revoke 流程保留;已允许时不重新请求,撤销后不继续后台读取。
关键代码讲解
notifiableAttention:消费原 attention owner 的 blocking,仅让新出现的必要决定通知,optional 仍留在原列表。buildPersonalHomeModel:沿原 typed interaction 分类每条已知事项;只有完整行集合才产出分类总数,部分列表显示未知,不能以 Goal 等待状态补算。updateAttentionNotifications:只有显式开关点击请求许可;允许后持久化偏好并 seed 当前已见身份,外部许可变化沿原 owner 读回。PersonalWorkspacePage notification effect:同次开启保留已见身份并集,部分来源暂缺不再遗忘;仅新的 blocking 项交给通知和原 drawer。
其余完整提案接通本地偏好、设置 props、原 status loader 的背景刷新回调、批量内容上限、双语/CSS、指南与截图、节点单元及两条通知浏览器场景。当前17路径、+771/-24,12个提交均带 DCO。相对本轮上一头,14个实际 base/head blob 对完全相同,3个变化路径完整重读:共享摘要移除未列出项猜测;既有原生 status fixture 增加显式 mixed/optional/full 模式及真实 cold Todo read;masthead 场景按接受语义修复旧断言。没有把无关主干历史算入提案,也没有只检查最后几行修复。
对主干的风险
保留上一头 18e296e44 的失败和公开 REQUEST_CHANGES:真实一次性 Goal 有1个 user_gate和20个 user_action,原 owner 报21 open、队列列12、overview列3;正常页面曾误报19个决定,另一等价预览变成18。上一修复头 28ab8ba67 同一输入保留未知。这个反例确认了“open总数”不是“决定总数”,不需要用户再授予权限才能纠正。
当前 821a3da12 通过相同独立原生来源及正常编译 harness:原生三条、反转三条、另一等价三条预览均显示未知,0 JS错误、0 Turn、0 action apply;原先失败的不变量现在通过。新 packaged masthead 同时保留真实有界全 gate、混合、无列表、无总数的未知结果,以及真实完整来源的21/1/0正例;两种语言、390px、原工作计数、生命周期和不触发动作检查仍在。完整 fixture 只是把同一静态一次性 Goal 的真实 cold rows 接到真实已知总数,明确标为 full,不冒称原 bounded status 本来就包含这些行。
当前独立验证为29个通知/return/suggestion 单元、完整 workspace contract、显式项目 TypeScript、CSS、正常 Chat build/source verify,以及九个正常打包场景:通知、真实 Todo 通知、return、suggestion、语言、capability scope、user_action、Goal M2、扩展 masthead。原生 canary 4 direct+13 selected 通过,0 failure/hold;advisory 在完整语义检查前执行。当前官方 base f899e7c3 和本 head 都重新正常构建;关闭时各推进70秒均0权限请求、0通知、0后台读取。OS Notification 只是受控 transport,原 Todo/status及页面代码是真实调用方。没有读取、触发或等待 GitHub CI。
通知功能的关闭边界与共享修复分别判断:前者不产生隐蔽作用;后者依原4.1.1纠正分类并保留未知,属于已披露的无条件行为修正。旧脚本把全部 open 直接叫作决定的断言不能定义产品语义;现在完整分类正例和不完整负例一起约束它,不是把错误改成0或删掉测试。真正浏览器 OS 送达、安装 App/WebView和N Goal长期轮询成本仍未验,本次不把这些更大的既有前沿关闭,也不新增不相关验收。
语义与前瞻性检查
复用既有 task_class、interaction、blocking;四值通知 support 是浏览器本地派生状态,新 fixture 参数只属于测试。没有新原生枚举、权限或 agent obligation,也没有以文字子串猜类别。前瞻性 pass 保持在原 typed projection和observer,删除的是未列出项的平行猜测;无需新增聚合器、通知专用 classifier或一般推送框架。原意与授权已存在时增加0次强制澄清;首次浏览器许可是用户主动开启才需要的真实授权,读回和导航没有替代这个边界。
我的整体评价
完整当前提案是合适的 justified_increment,architecture assessment 为 retain。long_horizon 为 preserved:原停止、错误读回和权威 owner不变,已见身份没有因部分来源而丢失;user_experience 为 improved:真实新决定可达,旧事项不重放,混合未知总数不再伪造成待决定工作。因此批准此源码与受测打包旅程。两项旧 P2可按当前证据关闭,原失败记录和讨论保留;真实 OS/App、长期成本及更大工作区目标仍沿原验收,本评审不授予 merge 或安装操作权限。
English verdict: APPROVE - 821a3da; full current proposal re-reviewed on actual official basef899. The same real mixed-source counterexample now preservesunknown across bounded views, while real complete rows yield21/1/0. Seeded partial-return stays silent and new gates still notify/open.29 frontend cases,nine packaged scenarios,current base/head off parity and local canary pass; real OS/App and sustained cost remain unqualified.
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
Summary
The Personal Workspace re-reads status only after the owner's own actions (manual refresh, applied proposals, turn completion). A decision an Agent raises while the owner is in another window stays invisible until they come back and refresh. For a product whose promise is long-running work with human judgment kept in the loop, the item that needs the owner has to reach them.
This adds Settings → Appearance → Desktop notifications: a per-browser, default-off switch.
onBackgroundRefreshcallback that keeps current snapshots on screen (background: true, readScope: "all"). Off means no background reads at all.Owner / placement. The preference is a per-viewer convenience like the theme (local storage, no service write). Admission, identity, cadence and content are one pure module,
attention-notifications.ts, with a node test. Goal-channel (Lark) notifications keep their existing owner and settings; this does not touch them.Review round 2 (head after
1d9798b1d)notifiableAttention, the existingWorkspaceAttention.blocking). An optional user action never interrupts, including one that later turns blocking because a gate appeared.Review round 3 (head after
28ab8ba67)Validation
attention-notificationsadds full → partial → full on a gate already waiting at enable time (zero notifications).goal-masthead(its optional mode now uses optional rows by class),goal-suggestionsandsince-you-lookedpass against the per-row classification.attention-notificationsadds the blocked → allowed → on → revoked path without a reload (zero permission requests when already granted; background re-read stops on revocation). Real-service scenarioattention-notifications-liveraises an optionaluser_actionthrough the CLI first (nothing notifies), then auser_gate(exactly one notification). Unit test for the notifiable selector; contract pins for the seed, the diff, the readback sites and the no-request rule.attention-notifications.test.mjs: identity by Goal+Todo, new-item diff, admission rule (enabled, granted, not looking), support readback, cadence, bounded owner-language content.attention-notifications(dev build, stubbedNotification, Playwright fake timers): off by default with no permission request; enable → exactly one permission request, readback "allowed", nothing replayed; a gate added to the fixture arrives through the 30 s background re-read and notifies with the item text; the same item is not re-notified; clicking opens the Goal and the item in the drawer; turning off stops background reads and notifications; reload with the preference on notifies nothing.attention-notifications-live(real service): a disposable real Chat/Todo owner servesstatus.json; a user gate is added throughloopx todo addwhile the tab is in the background; the next 30 s background re-read raises exactly one notification carrying the gate text. Only the status path is proxied to the real owner; everything else keeps the fixture. This proves the service emits the identity fields the notification diff relies on, which the fixture scenario alone could not.personal-workspace-contract.test.mjspins feature-off parity: one activation predicate for polling and notifying (includes read-only), permission requested only from the toggle, enable seeds the current lane, background read keeps snapshots, both locales for the new keys.tsc --noEmit,check:css-custom-properties, workspace-theme and goal-order smokes,generate_semantic_inventory.py --changed-from HEAD(no new carriers), packaged bundle build. Adjacent scenarioscapability-scopeandworkspace-localepass.Not covered. The desktop App's WebView: the Tauri shell ships
tauri-plugin-notification, but whether its WebView exposeswindow.Notificationwas not exercised; the switch shows "unsupported" where the API is absent. Real OS notification delivery and the installed App are untested here. With N active Goals the background read is N scoped status requests per interval; it only runs while the switch is on.Default change. Notifications still default off: no permission prompt or background reads. The shared mixed-queue projection now honors each explicit Todo class even while notifications are off; optional items remain visible and do not count as decisions. Unlisted partial categories remain unknown.
Future-facing refactor. Considered: the dashboard has no generic status liveness; this slice adds background freshness only behind the opt-in. A general live refresh (or a server-pushed revision) would make the needs-you lane current for everyone and is the natural owner for this poll; recorded as a follow-up, not bundled.
🤖 Generated with Claude Code
Current repair qualification
Current published head:
821a3da127a89db48c2b4ff6c164f19d7ec517ba, based on official mainf899e7c3859fa429e28b2fa37e6a072f665fe4de. The author's activation-lifetime identity repair is retained: full → partial → full does not replay a seeded gate, while a genuinely new gate still notifies. Existing permission recovery and mixed listed-row classification remain in place.The earlier Release Artifacts failure expected21 blocking decisions from a bounded source's21 open User Todos. These are different facts. A real native producer with one gate and20 optional actions reports total21 and a controller-waiting Goal; its three-row overview lists gate/action/action. Classifying unlisted rows from that Goal-level answer produces19 blockers, independently reproduced on the preceding head. The shared projection now retains unknown category totals until the rows are complete. No permission or new provider contract is added.
The regression uses the existing real status producer plus an explicit real cold Todo read, not UI-derived expected values. Complete21 gates render21; complete one-gate/twenty-action rows render1; complete optional rows render0. Bounded mixed, bounded gates, omitted and unknown-total sources keep unknown blocking totals and partial disclosure. The original work-count, identity, lifecycle, bilingual,390px and no-action assertions remain. This reconciles the failing expectation with the existing rule that nonblocking wishes never count as decisions; it does not suppress the failure or reduce it to zero.
Current source passes the workspace contract, TypeScript check,29 notification/return/suggestion cases, semantic advisory, a normal Chat build and source-bundle integrity. Seven compiled packaged scenarios pass: goal-masthead (including full/bounded/mixed/native-source comparisons in both locales), attention-notifications (including full/partial/full and permission recovery), attention-notifications-live (same-read optional+gate), since-you-looked, goal-suggestions, attention-user-action and goal-workspace-m2. The real Todo/status tests are disposable; Notification is stubbed and no real model, first Turn, OS delivery or host installation is exercised.
The source repair applies to shared attention summaries even while notifications are off, as disclosed above. A qualified aggregate from the source would be needed to show a numeric category total without listing all rows; this browser repair does not invent that fact. The original failed hosted job remains failed evidence for its old head. Complete natural CI and independent review of this new head remain required, alongside installed App, real OS delivery and sustained-cost acceptance. No approval or merge is claimed.