Repository navigation
feat(workspace): show what changed since the owner last looked - #6545
Conversation
5fe7118 to
45f1bdb
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | model=gpt-6.1-sol | provider=OpenAI | declaration_source=runtime_reported | reasoning_effort=max
动机
REQUEST_CHANGES,精确 head 45f1bdbcf8740589a4afe3fccdf0ecd2f993dbaa。有两个可执行的阻塞项:
- [P2] storage 不可用会使整个 Goal 页面崩溃。
personal-workspace-page.tsx:1287在调用readSeenMarker前就求值window.localStorage。浏览器策略可以让这个属性 getter 抛SecurityError,因此 helper 内捕获getItem/setItem错误来不及处理。我在已正常初始化的页面注入该 getter 故障后,普通点击 Goal:固定基线仍完整显示 Goal,候选只剩“页面暂时无法显示 / Page unavailable”。请把取得 Storage 对象也放入容错边界;本地查看记忆不可用时仍应允许正常阅读。回归应覆盖属性 getter,不能只覆盖方法抛错。 - [P2] 可选提醒被计作必须决定的事项。
personal-workspace-page.tsx:1263把本 Goal 的全部model.userTodos都变成decisionIds。通过真实隔离 Todo owner 新建一项role=user、task_class=user_action、无 blocking/global gate 的可选提醒后,现有概况和 Needs you 正确显示 0 等你决定、1 项可选,新行却显示 “新增 1 项等你决定”。请复用已有 typed attention 的 blocking 分类;验证 optional-only 返回不产生 new-decision,再验证真正阻塞事项仍产生一项。
已经在现有工作区中选择 Goal、想知道离开期间发生了什么的用户。返回 Goal 的用户原来必须重新阅读概览,才能发现离开期间的工作、决定和成果变化。本 PR 打算用浏览器本地的上次查看标记,在返回概览时直接显示一行变化;普通阅读不用额外填写信息或确认。实际候选把可选提醒计入待决定,并在浏览器拒绝 storage getter 时使整个 Goal 页面不可用。这会让原本为了节省扫读的一行摘要,变成额外的误导或恢复工作。预期收益是直接知道需要继续什么,而不是增加一个必须维护的设置。已在打包页面确认本地已读和无变化返回可用,同时复现整页 storage 故障与可选提醒误计。本 PR 不负责 target declaration、服务端通知、花费区间或运行调度。当前已验证的局部能力与上述缺陷必须分别判断。
改动思路
原始任务是回来后理解变化并继续工作。保持现状需要重新扫读各区;更小的合法方案是利用已有 Goal/source、摘要与 typed attention 的 blocking 分类,自动准备一行准确变化,不让用户重输目标、选技术参数或重新授权。现有意图和读取权限已足够,必需新增交互为零轮;选 Goal/概览是导航,Mark seen 是自愿确认已读而非操作授权。storage 不可用应只失去本地记忆,普通阅读仍可继续,不能迫使用户反复刷新。当前两项回归使这条最短路径失真或中断。
实现复用现有状态摘要和概览,在浏览器保存按 source/Goal 分隔的计数与编号,比较新观测后显示 material delta。无新服务、轮询或 privileged operation,是合适的有界路线。它不需要一个新的能力配置开关,已有上下文足够自动准备答案;Mark seen 的作用仅为本地阅读状态。下面是源码边界的说明表,不是执行证据:
| 输入/返回场景 | 应有行为 | 本轮观察 |
|---|---|---|
| 已看过且没有新变化 | 不显示一行 | Mark seen 后返回通过 |
| 新增可选提醒 | 保持 optional,不能成为 required decision | 新行误计,阻塞项 2 |
| 取得浏览器 storage 被拒绝 | 只失去本地记忆,Goal 仍可读 | 整页 error boundary,阻塞项 1 |
| 查看、切换概览、确认已读 | 本地记忆变化,无 action/quota/validator 写入 | 当前隔离网络记录通过 |
现有 Goal workspace presentation 的本地查看记忆与摘要;无新的服务端通知、quota、调度、权限或 Todo 写 owner。本地 view-memory 有实际必要;两项有界修复可恢复既有页面与分类不变量,不需另建框架。保留独立 since-you-looked.ts reducer/storage adapter 有实际 caller,局部 TypeScript 归属合理。相关未来改造应先复用 useGoalWorkspaceDigest 已使用的 WorkspaceAttention.blocking 语义,收拢 storage 取得与读写的失败边界;不必增加新的 Python owner、审批或 marker 服务。当前 PR 的说明还把“离开且行已显示才确认”改为所有完整访问离开均前进;这是决策语义变化,须单独证明不会提前确认未呈现变化,不能只用“加载时只会少计”的文字取代验证。本轮未复现该额外场景,不将它列成第三个已确认缺陷。
具体改动
评审范围是 immutable base 34f615d9836060701cf48111db230764566a9832 到 45f1bdbcf8740589a4afe3fccdf0ecd2f993dbaa,13 个路径,文本 +440/-5,另有一张 PNG。spec_ref 为 docs/architecture/rfcs/goal-workspace-surface-v0.md,spec_revision 为 34f615d9836060701cf48111db230764566a9832,使用变更前已接受的规范。
- 5.7:not_met。 marker、material line、无费用和本地存储方向已落地;决定分类和 storage 失败隔离未满足,最小修复见两项 finding。新写的 checkpoint 不能反过来证明原接受条款已满足。
- V5:not_met。 实际隔离 File/HTTP/CLI 与打包浏览器已覆盖一次返回、Mark seen/no-change;optional-only 反例失败。作者的浏览器 fixture 能证明组合,但不是完整生产 classification 的 oracle。
- V6:not_met。 已检查 populated 1440/390px 概览与 reduced motion,新增一行和按钮可读;storage-unavailable 的恢复失败。未声称完成所有语言、键盘、主题与 loading 组合。
- V9:implemented。 当前普通读取、概览导航和已读确认的请求记录无 action、quota 或 validator 写入。本地确认不铸造 Todo 完成或授权。
- M3:deferred。 此 PR 明确只提议 return 一半;V8 target declaration 与 installed/live qualification 由既有 RFC/acceptance owner 保留,不要求本 PR 顺带实现。
关键代码讲解
buildSeenMarker/compareSeenMarker:新 schemagoal_seen_marker_v0是浏览器本地持久格式,存 counts、bounded ids 和 observation time;done id 与 done-count growth 结合,decision/result 用未见 id,blocked 用数量增长。不是新共享 actor/执行协议,成本依 D7 继续省略。seenMarkerInput:接已有workspaceDigest与 timeline。这里必须继承 typed source 的分类含义;只按 Goal 筛选 userTodos 丢掉 optional/blocking 差别,是 finding 2 的入口。现有useGoalWorkspaceDigest已将blocking送给共同 reducer,无需另造文字分类规则。readSeenMarker/writeSeenMarker:非法 JSON 和方法调用失败已降级;但调用方获取 Storage 对象在 try 外。页面 effect 在 Goal loaded 时无条件执行,所以故障影响整个 Goal mount,而不只新摘要。GoalOverview:material line 使用role=status和本地 Mark seen,保留 hero、Needs you、work/results;中英文 copy、轻量 CSS 与 marker/helper tests 同批更新。新的 browser scenario 和 package smoke entry 只验证自身输入覆盖,不能推断生产可选提醒都是真正决定。- 文档新增 user guide、示例截图和 RFC checkpoint,说明费用不纳入、无服务写入、target declaration 未完成。截图已查看,属于合成工作区;它能帮助理解呈现,不能证明真实状态分类、持久采用或安装行为。
对主干的风险
独立本地验证:11 项 node/helper/contract 检查通过;候选和固定基线的正常前端构建通过;TypeScript/control-plane、configured mypy、semantic advisory 后完整相关语义检查、whitespace 和 DCO 通过。risk-based premerge 的 3 项 direct checks、13 项所选执行检查通过,但独立 quality gate 为 quality_receipt_missing,不能报告总门禁通过。没有查询、轮询或等待 GitHub CI,也没有合并操作。
最重要的是上述绿色检查之外的两个真实入口反例。浏览器连接的是隔离的真实 File/HTTP/CLI Todo owner,其他非目标 API 由仓库合成 fixture 提供,没有访问活跃 Goal 或调用模型。storage 对照在两棵正常构建树上使用同一“初始化 Home 后拒绝 getter,再普通选择 Goal”的故障;基线可读、候选整页错误。optional 对照由实际 CLI 创建合成提醒,再读取同一页面的现有 0 blocking/1 optional 与新增行的 1 decision,排除了只让 reducer fixture 自证的情况。Mark seen 后无变化返回为正例,并记录无后端动作写入。
这次只实现 M3 的 return 子集;target declaration/V8、安装后的 App、真实团队/模型采用,以及跨来源/多 Goal、超 200 项重排和未显示行的离开确认未完成独立资格验证。失败的直接路由/等待脚本没有形成第三项产品结论。超限 marker、分页顺序、partial/loading 的全部 counterfactual 均保留未验证,不能据绿色 helper tests 宣称全 V5 完成;也不能据 screenshot 宣称持续运行被验证。回滚可移除这次本地 marker/摘要,不需要修改 canonical Todo 或服务端执行记录。最小修复只涉及现有 storage adapter 与 typed attention 选择,随后重跑两个反例和原正例即可;不应通过改变预算、测试期待或把 optional 文案强改成阻塞来消除失败。
语义与 CI 对齐
goal_seen_marker_v0 留在 presentation 的本地持久格式,没有新增共享注册 vocabulary。分类则有现成 typed owner:optional user action 不能仅因出现在 userTodos 列表就被称为“等你决定”。默认所有 Goal 页面会启用比较,虽本地、无 privileged effect,也不能说是默认关闭的可选模块。feature 无法使用的 storage 分支需保留旧页面可读性;当前固定基线/候选对照证明该分支存在回归。没有新的 quota、lease、权限或机器义务,Mark seen 不改变 attention queue。
我的整体评价
REQUEST_CHANGES — 45f1bdbcf8740589a4afe3fccdf0ecd2f993dbaa。 这是有价值且大小合理的 return 子集,当前 head 尚未达到可交付的一行真实变化:两项已确认回归分别使 Goal 无法打开和把可选提醒误称必须介入。请修复这两处后,在新精确 head 重新验证整个相关返回入口,而非只更新 helper 期待。
long_horizon:regression,判断限于用户稍后返回、持续理解并继续工作的路径;没有声称后台调度或模型执行退化。user_experience:regression,已知可选信息造成不必要介入,storage 故障新增整页恢复。无改动仍能读 Goal,使用既有 typed classification 加容错本地记忆的更小合法方案能保留收益,并把额外交互保持在零轮。相关 future-facing pass 已识别为既有分类和 storage 边界的有界复用,尚未由本次只读评审修改源码;更广的框架重构没有必要。
English verdict: REQUEST_CHANGES — exact head 45f1bdb. The returning-owner slice is proportionate, but denied localStorage property access now crashes the whole Goal, and an optional user action is reported as a new required decision. Both were reproduced through the packaged UI with a real disposable Todo backend; the storage fault leaves the immutable baseline usable. Guard storage acquisition and reuse typed blocking classification, then rerun the negative and no-change journeys. Local selected checks pass with a separate missing quality receipt; installed/live adoption remains unqualified.
| if (seenComparedRef.current === scope) return; | ||
| seenComparedRef.current = scope; | ||
| const key = seenMarkerStorageKey(seenStatusSource, selectedGoal.goalId); | ||
| const previous = readSeenMarker(window.localStorage, key); |
There was a problem hiding this comment.
[P2] Guard acquisition of localStorage, not only its methods
Browser storage policy throws SecurityError from window.localStorage when a loaded Goal is selected. Exception escapes before helper try/catch and triggers the global React error boundary, making the entire Goal unavailable. Same initialized-page fault on immutable base retains the Goal. Resolve the Storage object inside a guarded adapter and use null/no-op when unavailable; preserve Goal rendering and navigation. Regression: Initialize Home with healthy storage, then make the localStorage property getter throw, select an ordinary Goal, and assert its content/actions remain accessible. Existing getItem/setItem-only fault tests miss argument evaluation.
| return { | ||
| observedAt: workspaceDigest.observedAt, | ||
| counts: { open: workspaceDigest.work.open, blocked: workspaceDigest.work.classes.blocked, done: workspaceDigest.work.done }, | ||
| decisionIds: model.userTodos.filter((item) => item.goalId === selectedGoal.goalId).map((item) => item.todoId), |
There was a problem hiding this comment.
[P2] Count blocking decisions separately from optional user actions
After a prior look, the real Todo owner adds one role=user/task_class=user_action reminder with no blocking/global gate. New line says 1 new decision for you while the unchanged typed digest correctly says 0 blocking and 1 optional. An optional reminder becomes a false required intervention. Use the existing typed attention blocking/decision classification when selecting decision ids, preserving optional user actions as optional. Regression: Use a real disposable Todo backend: return with only a new optional user_action and assert no new-decision delta; then add an actual blocking decision and assert one. Compare the line with the masthead/Needs you digest.
|
Thanks for the two reproductions. Both are fixed on head Finding 1, denied storage took the Goal down. Finding 2, optional reminders counted as decisions. Leave-acknowledge semantics. You were right that "every complete leave advances" could acknowledge a change that arrived mid-visit without being shown. Leaving now records only the look that was compared on that visit; Mark seen still records what is on screen. RFC checkpoint and user guide say so. One more defect the real service exposed. Against a real Validation on this head. Unrelated CI. The |
|
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 |
A returning owner had to re-read a Goal's overview to find out what changed while they were away. RFC goal-workspace-surface-v0 §5.7 specifies the answer: a browser-local marker and one quiet line. Per (status source, Goal) the browser keeps a `goal_seen_marker_v0` in local storage: typed counts, decision ids, bounded done Todo and result ids and the observation time; no titles, content or cost, no service write. On open the page compares the current digest with the marker and shows one line above the hero only for a material delta (newly done, new decisions, newly blocked, results returned) with "Mark seen". Mark seen writes the current observation; leaving the Goal records the owner's latest complete look and acknowledges a shown line. A visit with nothing new still advances the observation time, and because every count is "present now and not in the marker", a Goal still loading on open can only under-count, never invent a change. Cost stays omitted per D7. The RFC gains a dated checkpoint for this half of M3; target declaration remains undelivered. Validation: node test for the marker and comparison (no-change pair, trimmed marker, unavailable done count, storage round-trip and garbage); browser scenario for the V5 journey (first visit silent, no-change return silent, work and decision changes shown and dismissed, leave-acknowledge, reload, no service write); contract pins; overview scenarios goal-showcase, goal-masthead, goal-workspace-m2, first-goal-home; tsc; CSS token check; semantic inventory advisory. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
…d storage Review of the since-you-looked line found two regressions and one semantic gap: - Every user Todo of the Goal was a "decision", so an optional user action raised while the owner was away was reported as a new item waiting on them. Decisions now reuse the typed attention classification (WorkspaceAttention.blocking); optional items are listed, never counted. - window.localStorage was read as a bare property before the guarded helpers, so a browser that denies storage (the getter throws) took the whole Goal down. Acquisition now sits inside the same boundary; without storage the owner loses only the local look memory. - Leaving a Goal advanced the marker to the latest observation, which could acknowledge a change that arrived during the visit without being shown. Leaving now records the look that was compared on that visit; Mark seen still acknowledges what is on screen. Against a real service the owner's refresh lands after the Goal opens, so the comparison latched on the snapshot about to be replaced and the line never appeared. The comparison now waits for an owner refresh in flight. Helper test covers the throwing getter; the fixture scenario adds the denied-storage return; the contract test pins the four rules; RFC checkpoint and user guide state the revised semantics. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
Add since-you-looked-live: a disposable real Chat/Todo owner serves status.json; the CLI raises an optional user action (silent on return), then completes an agent task and raises a gate (one newly done, decisions equal to the typed lane's own count); Mark seen clears the next return. Only the status path is proxied; the document URL also ends in status.json. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
9d5e0c5 to
95caadb
Compare
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
精确头 95caadb5638ca52b2945421b692383ab1a7572d0;实际不可变 merge-base 3bc74f9317479e36286414d899a77172a115e330。本次重审覆盖完整14路径、旧审头 45f1bdbcf8740589a4afe3fccdf0ecd2f993dbaa 所指修复,以及 rebase 后三个提案提交;并非把主干新增提交当成本PR改动。
动机
隔一段时间回到 Goal、想先知道工作和决定有何变化的所有者。原来必须自己对照任务和决定;新行用本机上次查看标记概括已完成、新决定、受阻及成果,减少重新查找。新行已接入打包页面,但访问中标记已读后离开会退回旧快照,201个未变决定也会重复显示新增,尚未满足无变化保持安静。本 PR 不增加通知、服务端历史、区间花费、执行或验收权限,也不完成 M3 的目标声明旅程。
改动思路
上次查看历史属于浏览器局部展示偏好,复用现有 typed digest 和阻断分类、在前端小模块保存有界标记,归属合理;应在同一 owner 保证确认不回退,并区分历史截断与真正新增。当前 PR 的边界是可信的返回变化提示及明确已读,不是新的事件账本或完整 M3;本头两条错误事实应在现有模块与页面生命周期中修复后重验。无需把所有历史上传到服务端或引入事件存储;过去看过什么不能从当前快照还原,保存最少本机观察信息有价值。查看、返回与刷新本来就应由现有入口完成,用户不应重填已知上下文或新增确认回合。已有“标记已读”可以作为自愿操作,但一次明确操作不能在离开时被撤回。
源码流程为现有状态/Todo → typed digest 与阻断分类 → 当前观察 → 本机 per(source,Goal) 标记 → material变化行 → 已读/离开更新。该文字说明是源码解释,不代替实际执行证据。展示保持概况、目标与下一步的层级,变化行位于 Direction 上方;独立看过1440px整屏与误报截图。没有渲染额外流程图,避免把示意误作证明。
具体改动
独立依据 docs/architecture/rfcs/goal-workspace-surface-v0.md,spec_revision 3bc74f9317479e36286414d899a77172a115e330,先读已接受的§5.7及V5/V6/V9/M3,再判断本次对RFC的更新。V5要求有变化可关闭、无变化不出现,当前两处 not_met;V6要求部分/不可用仍真实,截断历史不构成新增证明;V9保留,无Turn/action/quota/validator动作。M3中的目标声明通过验收owner另行交付,本切片没有完成它。
完整 +622/-6:133行局部模块、页面74行接线,Overview/双语文案/5行CSS,79行单测与contract,两个114/102行浏览器场景及catalog/npm接入,公共合成截图、指南和RFC检查点。仅保存schema、计数、少量ID与时间,200项上限避免无限增长;没有正文、模型调用或服务端写入。
关键代码讲解
buildSeenMarker/compareSeenMarker(since-you-looked.ts:48/66)保存每类最后200个去重ID。done分支结合总数增长限制截断膨胀,但74行decision差集及76行result差集直接把未保留ID当新;本次实际复现的是decision,未声称另跑过result的UI反例。advanceSeenMarker/markSeen及离开effect(page:1282、1310、1317)区分latest与compared,目的是不吞掉访问中未展示的新增。这个意图合理,但明确已读后仍保留旧compared,离开又写回旧值;应统一同一局部owner的确认状态,不新增另一套后端。- Overview变化行和受保护storage accessor把功能接到实际调用者,role=status与本地化沿用既有组件。读取window.localStorage getter与方法均在catch内;普通optional user_action沿用blocksAgent/globalGate过滤。两处上一轮P2已由当前独立正例覆盖,不再原样重复旧阻断。
对主干的风险
[P2] 明确已读不能被离开时的旧快照覆盖(page:1319)。 实际打包UI:先看1项,离开后增到2项,返回显示新增1项;访问中刷新为3项,再点“标记已读”。独立读取localStorage确认已存3个ID。离开Goal立刻被1310行写回访问初始的2个ID;同样3项返回又提示新增1项。最小修复是已读同时推进compared确认,或让leave不能覆盖更新的明确确认;仍应保留“未点已读且变化未展示时下次再提示”。补完整刷新→已读→离开→返回回归,比只断言点击后文字消失更有辨别力。
[P2] 200项截断不能制造新增决定(since-you-looked.ts:74)。 同一完整快照有201个阻断决定,首次标记只保留ID1…200;没有任何来源变化,离开再返回时ID0不在历史中,于是显示“新增1项等你决定”,概况仍为201。重复返回持续出现。实际不可变基线同样201项只有正常概况、没有误报新事实。不要取消上限或简单把未保留项认定新;应在有界标记中表达覆盖边界,只有足够证据才给新增数。补201不变、顺序/窗口变化与真正新项,并检查其他有界ID类。
独立验证:实际base/head正常构建/typecheck;8个Node用例、workspace contract、CSS,以及 since-you-looked、since-you-looked-live、goal-showcase、goal-masthead、goal-workspace-m2、first-goal-home 六个打包场景通过。live场景使用一次性真实Chat HTTP/Todo文件/CLI,覆盖普通可选项、完成、阻断、已读返回;拒绝storage getter也已在实际打包caller验证可用。它们未覆盖上述两条新反例,不能用绿色测试代替反例。
premerge的3direct+13selected产品检查全部执行通过、0manual hold,但整体exit1/ok=false,quality_stale_receipt:旧私有收据对应另一基线/差异,当前资格未重新记录。没有伪造或改写收据,没有宣称整体canary绿色。diff检查与分支DCO通过;advisory在全树semantic之前执行,两层通过,但有限词汇扫描不证明此新局部历史语义正确。没有查询、派发或等待远程CI。
复用证据与当前头
发布前发现 rebase:旧头 9d5e0c5eb7ef199f5d1464c6fdb336f3a6117f8b → 当前 95caadb5638ca52b2945421b692383ab1a7572d0,新实际基线 3bc74f9317479e36286414d899a77172a115e330。逐一核对完整14路径:生产模块、页面、样式、文案、RFC、两个场景均为相同Git blob;仅随主干分片接入,新增catalog命令也运行新单测。新旧两个arm重新正常构建,全部14个assets及index.html逐字节一致。因此前述Ego反例复用到当前编译产物,非声称换头后另开浏览器重跑。当前头另独立重跑8单测、contract、CSS、六个打包场景(含真实一次性Todo后端)及premerge,产品检查仍过,两条错误代码和产物均未变。 构建刷新依赖时我同时启动了CSS检查,先因postcss/postcss-value-parser暂时不可解析失败;构建全部完成后相同CSS命令分别通过,完整错误记录保留。未删除断言、改超时或改产品代码,整体premerge的质量收据失败仍未通过。
语义与 CI 对齐
反例使用正常编译产物、生产typed digest与公共示例改出的合成状态;直接观察DOM和本机存储,没有把模型或已安装App模拟成通过。已有无害会话创建/model-options请求照旧,0Turn,不向活动Goal转发。完整M3、真实模型与安装采用仍未验收。schema和latest/compared词汇保持前端局部,核心count/阻断owner未复制。相关重构应在同一ack/compare边界收紧,而非添加通用框架;修复须保留先前storage/optional及未展示更新不吞掉的反向案例。
我的整体评价
REQUEST_CHANGES。上次查看历史属于浏览器局部展示偏好,复用现有 typed digest 和阻断分类、在前端小模块保存有界标记,归属合理;应在同一 owner 保证确认不回退,并区分历史截断与真正新增。当前 PR 的边界是可信的返回变化提示及明确已读,不是新的事件账本或完整 M3;本头两条错误事实应在现有模块与页面生命周期中修复后重验。long_horizon与user_experience在当前合法状态下 regression:已读回退、旧项误称新增,使同一上下文反复占用注意力。两个最小修复都在当前局部实现内,机制整体归属与成本合理,无需扩展成另一项交付。当前绿色检查、作者修复声明或旧commit不能证明这两条反例已解决;请在修复精确头重新运行同一真实入口再结论。
English verdict: REQUEST_CHANGES - 95caadb. Two compiled-UI P2 regressions remain: leaving overwrites a newer explicit read acknowledgment (3 IDs back to2), and unchanged201 decisions repeatedly produce one false new decision after the200-ID cap. The prior storage-access and optional-attention issues pass current independent coverage. Local product checks pass, but overall premerge still fails stale quality qualification; installed/model/M3 acceptance remains separate.
| }, [selectedGoal, seenGoalLoaded, seenStatusSource]); | ||
| function markSeen() { | ||
| if (!sinceYouLooked) return; | ||
| advanceSeenMarker(sinceYouLooked.goalId, sinceYouLooked.source, "latest"); |
There was a problem hiding this comment.
[P2] 标记已读时也要推进离开使用的确认快照
实际打包页面中,先以2项决定进入,访问中刷新为3项,再点“标记已读”:localStorage已存3个ID,但离开时1310行仍用compared覆盖回2个,原样返回又提示新增1项。此处写latest后没有更新compared。请保证离开不能回退较新的明确确认,同时保留未展示且未确认的访问中变化供下次提示。补刷新→已读→离开→无变化返回的完整回归,断言标记仍为3且不再提示。
| const doneByCount = previous.counts.done !== null && current.counts.done !== null | ||
| ? Math.max(0, current.counts.done - previous.counts.done) : doneById; | ||
| const newlyDone = Math.min(doneById, doneByCount) || Math.max(0, doneByCount > 0 && doneById === 0 ? doneByCount : 0); | ||
| const newDecisions = current.decisionIds.filter((id) => !seenDecisions.has(id)).length; |
There was a problem hiding this comment.
[P2] 有界历史缺失不能直接证明决定是新增
buildSeenMarker仅保留最后200个ID。实际完整快照含201个阻断决定,首次查看标记为ID1…200;来源完全未变,离开再返回时旧ID0被此差集算成新增1项,每次返回重复误报。请保持存储有界,但表达记忆的覆盖范围并要求足够新增证据,不能把被截断的旧项视为新项。补同样201项、重排/窗口变化与真正新项的实际caller回归;也检查其他有界ID类别。
…d coverage Two legal states still produced a wrong line: - Mark seen after a refresh during the visit recorded three decisions, but leaving wrote the two the visit had started on back over it, so the next return reported one new decision again. Mark seen now becomes the look the visit stands on, and no write moves the marker back behind a newer acknowledgment. - With 201 unchanged decisions the marker kept 200 ids, so the one it did not retain counted as new on every return. Stored ids are evidence only while they cover the owner's whole set; past the cap, or for a partially listed set, only growth of the owner's count reports a new decision or result. The marker therefore records the attention owner's blocking count and the result count (schema goal_seen_marker_v1, browser-local). Node tests cover 201 unchanged, a reordered window, real growth, a partial listing, resolved-and-raised within the cap and capped results. The fixture scenario adds refresh, Mark seen, leave, return with the acknowledgment retained, and a 201-decision workspace that stays silent. RFC checkpoint and user guide state both rules. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
|
Both P2s are fixed on head P2, leaving overwrote a newer Mark seen. P2, the 200-id cap manufactured a new decision. Stored ids are now evidence only while they cover the owner's whole set ( Validation on this head. |
|
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>
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:还有一个未知完成总数下的误报。 精确头 3d5d5aaaf9023df1cec1ed40a1e880cf06fee030;实际不可变 merge-base 9fc858cbd8ee6d0268a4af11ed0f86e84ae6f755。
发布前发现 rebase:旧头 47a7ba58e85158974ba17876e840034b517cdef9 → 当前 3d5d5aaaf9023df1cec1ed40a1e880cf06fee030,实际基线 9fc858cbd8ee6d0268a4af11ed0f86e84ae6f755。逐一核对当前14路径,其中12个Git blob相同。生产selector/marker、typed digest、模型事实来源、Overview和相关样式/文案/场景未变;页面只整合已在主干的Settings lazy/Suspense,contract附其断言,6535的CSS另附loading类。其余主干变化不算作本PR改动。独立acceptedRFC、AGENTS与design在新基线逐字相同,frontend-delivery新增的按需加载说明已读。两个候选与两个基线在新头重新正常构建,10/11单测、contract、CSS、5/6打包场景(含真实一次性Todo后端)及premerge产品检查全部重跑通过。前述额外Ego画面和DOM/存储反例是在rebase前同批旧头执行,依此调用路径失效检查复用,并非声称新头又开Ego。6545当前helper另独立重跑未知201完成反例仍得1。两次整体premerge的质量资格失败仍保留;没有把旧收据改成当前资格。
动机
隔一段时间回到 Goal、想先知道工作和决定有何变化的所有者。原来需要自己对照记录;新的一行摘要用这个浏览器上次看过的状态说明变化,省去重新查找。明确已读回退和201项不变决定的误报已修复,但完成总数未知时,201条完全未变的完成记录仍被报告为新完成1项。本 PR 不增加通知、服务端历史、区间费用或执行权限,也不完成 M3 的目标声明。
改动思路
历史观察留在前端小模块,当前任务和阻断分类继续复用现有类型化 owner,归属与成本合理;确认不回退的修复也留在原页面生命周期中。当前 PR 应交付可信的返回提示;剩余修复只需在现有比较器对未知完成总数保留覆盖边界,并补实际页面回归,无需新增事件服务。当前事实来自原类型化投影,是否新增只由足够观察证据决定;不需要让用户重填已有上下文。原任务只需打开Goal并自愿选择已有入口,新增准备、澄清、确认回合均为0。原有作用域和执行许可仍由原操作owner承担,展示出现不代表可执行。
源码路径用文字说明为:Status/Todo owner -> buildPersonalHomeModel -> shared GoalWorkspaceDigest -> seenMarkerInput -> bounded browser-local marker -> compareSeenMarker -> Overview line -> explicit acknowledgment or leave。这是解释而非执行证据;没有渲染流程图,使用下文前后状态与实际caller读回避免把示意图当验证。代表性有数据的整屏已检查目标、owner、状态、下一步是否同时清楚;只有变化和建议占用新的注意力。
具体改动
独立规范 docs/architecture/rfcs/goal-workspace-surface-v0.md,spec_revision 9fc858cbd8ee6d0268a4af11ed0f86e84ae6f755。先读已接受的§2、§5.3、§5.6、§5.7及对应验证,再评估本提案;更新的RFC检查点不能自证实现。判定:V5 not_met;V6 not_met;V9 implemented;M3 out_of_scope。V2关注同快照事实同义,V5关注无变化安静和已读,V6关注未知/部分的真实性,V9保留原副作用边界;只列本次实际适用的标准。
本次完整14路径 +740/-6:155行局部标记比较器、页面85行接线、Overview状态行、双语文案和样式,103行单测、contract,两个真实/合成浏览器场景、catalog/npm入口、指南与RFC检查点及公开合成截图。v1标记只保存计数、编号、时间;200项上限保留,正文、费用、服务端写入均未加入。v0在本未合并提案内被替换,旧格式安全回到首次查看;未发现已发布的旧格式兼容承诺,不引入双版本分支。
关键代码:buildSeenMarker(since-you-looked.ts:48)截取最后200个去重编号;compareSeenMarker(:78–82)的完成分支在总数未知时仍直接用编号差集。coversWholeSet已用于决定和成果,尚未保护这个分支。personalAgentTodoFacts(dashboard-page.tsx:760起)明确让缺失完成总数保持undefined,useGoalWorkspaceDigest再保留null;201条done记录照样进入rail。因此反例通过真实生产调用路径可达,不是给helper塞了一个页面永不产生的对象。
advanceSeenMarker、markSeen与离开effect(page:1289起)现在拒绝较旧观察覆盖,并把明确已读推进compared。当前实际页面验证201项不变决定保持安静;202项进入提示新增1项,访问中刷新203后点已读,离开并原样返回仍记203、无摘要。原两项P2已实证修复,不再作为本轮阻断重复提出。
对主干的风险
[P2] 完成总数未知时,截断的旧完成记录仍被当成新增(since-you-looked.ts:82–81)。 原始状态不给advancement_done_count、done_count或done总数,只列201条稳定的已完成工作;概况正确写“未报告完成数/总数未知”。首次标记为done=null、200个doneTodoIds。来源一条也没变,离开再回来便显示“新完成1项”;点“标记已读”后文字消失,再离开并原样返回又出现。独立读回本机标记确认依然是同样200个编号,完整合成来源仍是同样201条。实际不可变基线没有这条错误新事实。
原因是缺一个总数时doneByCount回退doneById,被200上限丢掉的旧编号便成为新增证据。不要取消上限,也不要把未知改成零;在同一模块表达或证明完成历史的覆盖边界,只在有足够证据时给新增数。补未知总数+201项不变、已读后再返回、部分来源后补列表与真正新增的辨别用例;将当前正确的决定/成果规则扩展或复用到完成分类即可,不需要事件服务。现有11单测和六个打包场景均绿,缺的恰是这个组合,不能用它们否定独立反例。
局部重构检查:明确确认和离开已统一在原页面owner;同一有界历史规则应覆盖各类别,避免重复知识分叉。更大存储框架没有实际需要。v1重置历史仅改变本机提示,不授予任务或验收权限。
独立验证:当前候选正常构建/typecheck;新基线与两个候选四个arm均在rebase后重新正常构建;额外Ego观察按上述失效检查复用,当前打包场景另已重跑。11个Node用例和六个打包场景、workspace contract和CSS均通过;native live场景使用一次性真实Todo/HTTP/CLI,不是活跃Goal。私有反例通过原始合成API进入真实生产模型与编译页面,明确保留合成边界。未调用付费模型、安装产品或派发/查询/等待远程CI。
premerge三项direct与十三项selected产品检查全执行通过、0manual hold;整体exit1/ok=false,quality_stale_receipt。这是当前差异的独立质量资格状态,尚未满足,未改写或补造收据,不能说整体canary绿色。diff/DCO检查通过。开发期语义advisory先于全树semantic执行;它报告0个支持的carrier,不能证明新TS union、局部JSON或提示语没有语义风险,全树检查通过也不能覆盖本次独立业务反例。
语义与 CI 对齐
该能力仍属于当前前端展示owner。无feature-off或opt-in主张,新默认已在公共指南披露;不虚构关闭模式的等价证明,也不从提示出现推导调度、验收或合并权限。新命名只覆盖局部建议/观察,不暗示更广的Agent生命周期。安装、真实模型、完整父级验收与质量资格分别保留边界;已有基线任务历史总数等未触及问题不归咎于本PR。所有当前失败原样保留,不删断言、扩大超时或放宽预算。
我的整体评价
REQUEST_CHANGES。原两处修复有效,但 V5、V6 仍有一项可达 P2:未知总数下,未记住不能证明新发生。当前切片的长期使用和用户旅程仍受重复假进度影响。按现有owner补覆盖证据并复跑当前真实页面反例即可;保留已验证的确认单调性、已知201项决定安静与真正增长提示。 历史观察留在前端小模块,当前任务和阻断分类继续复用现有类型化 owner,归属与成本合理;确认不回退的修复也留在原页面生命周期中。当前 PR 应交付可信的返回提示;剩余修复只需在现有比较器对未知完成总数保留覆盖边界,并补实际页面回归,无需新增事件服务。
English verdict: REQUEST_CHANGES — the prior acknowledgment and201-decision defects are fixed. With an unavailable done total, however,201 unchanged completed rows still produce a false newly-done count on every return, including after Mark seen. Keep history bounded and require sufficient completion evidence; the separate stale quality receipt remains a merge hold.
| const doneById = current.doneTodoIds.filter((id) => !seenDone.has(id)).length; | ||
| const doneByCount = previous.counts.done !== null && current.counts.done !== null | ||
| ? Math.max(0, current.counts.done - previous.counts.done) : doneById; |
There was a problem hiding this comment.
[P2] 完成总数未知时也要尊重有界历史的覆盖范围
实际打包页面中,原始状态没有advancement_done_count/done_count/done总数,但列出201条未变的已完成Todo;生产模型保留done=null,标记只存200个ID。这里把doneByCount回退为doneById,于是被截掉的旧编号在每次原样返回时都变成“新完成1项”;点“标记已读”再离开、返回仍重复。概况同时正确显示“未报告完成数/总数未知”。请保持200项上限,只有足够覆盖/计数证据才判断新增;把决定/成果已经采用的边界也落实到完成分支。补未知总数+201项不变、明确已读后返回、部分列表补齐及真正新增的实际caller回归,不能把未记住等同于新发生。
…ut a total With no owner done total, the comparison fell back to the done ids alone, so 201 unchanged completed rows (the marker keeps 200) reported one newly done item on every return, including after Mark seen. The done branch now follows the same coverage rule as decisions and results: a known total is the evidence and ids only refine it; without a total the ids count only while the marker kept every one it saw, and a capped history proves no completion. Nothing is reported rather than a guess. Node tests cover 201 unchanged with no total, a new id past the cap with no total (no evidence either way), a complete history still counting a real completion, and a total that appears later. The fixture's capped workspace now also carries 201 completed rows with no done total and stays silent on return. RFC checkpoint and user guide state the rule. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <liusongstep@gmail.com>
|
Fixed on head P2, a capped done history with no total reported a completion. The done branch now follows the same coverage rule as decisions and results. With a known done total, the total's growth is the evidence and ids can only refine it downward (unchanged). Without a total, the ids are the only evidence and count only while the marker kept every one it saw ( Validation on this head. The acknowledgment monotonicity, the 201-decision silence and the real-growth prompt from the previous round are unchanged. |
|
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 |
…d-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 # apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx # docs/guides/personal-workspace-user-guide.md
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
完整复审,绑定 6545@6b3cefd329be473ae750a9e6f170266d535f7dbf;实际比较基线为 7961c7689f364d9e421f4c5a48c924f24f140de1。
动机
隔一段时间回到 Goal、想先知道工作和决定有何变化的所有者。原来需要自己对照记录;新的一行摘要用这个浏览器上次看过的状态说明变化,省去重新查找。返回时只显示有证据的变化;完成总数未知且历史超出200项时保持安静,明确已读不会被离开撤回。本 PR 不增加通知、服务端历史、区间费用或执行权限,也不完成 M3 的目标声明。
M3的目标声明、已安装App采用、真实模型收益及整个workspace验收仍不在本批结论内。本地标记是上次观察,不是任务权威;为这个有用的小批增加服务端事件系统并不合算。
改动思路
状态仍由现有 typed digest、Todo 和 attention lane 判定;浏览器只存每个来源/Goal 的计数、时间和每类最多200个编号。过去的观察无法从当前快照还原,最小本地历史有明确价值。历史观察留在前端小模块,当前任务和阻断分类继续复用现有类型化 owner,归属与成本合理;确认不回退的修复也留在原页面生命周期中。
本轮把完成分支也接到已有覆盖判断:有可比总数时以增长为证据;没有总数且保存过的历史已截断,就不从未保存的编号猜测新增。这接受有限少报,以免反复制造虚假进展。Mark seen更新当前观察和本次比较基线;离开只确认已经呈现的观察,期间未呈现的新变化仍留给下一次。
具体改动
完整差异为14路径、+772/-6:marker、页面生命周期、Overview行和样式、双语文案、package测试接线、单测与两个浏览器场景,以及RFC/指南和合成截图。发布前fresh read发现新整合头6b3,旧7eec未发布评审票。相较7eec,7条重叠路径完整保留main796已合入的快捷建议:两组import/function、contract断言、浏览器场景和npm测试命令均在。marker helper和Overview行语义保持;当前组合已经重新构建与验证,不把旧执行改记为新头。
关键代码讲解
buildSeenMarker/compareSeenMarker保留200项上限及null语义;未知且截断完成历史返回0,已知增长及范围内真实新增保持。advanceSeenMarker/markSeen保留单调确认,等在途owner刷新结束后才锁定比较,不在离开时撤销更新的确认。GoalOverview只渲染material delta并提供自愿确认;存储getter/read/write都受guard保护,拒绝storage不阻断Goal。
独立规范是基线中的Goal workspace §5.7及V5/V6/V9:V5无变化安静/可确认、V6未知/不可用保持真实、V9无自动工作副作用在本批已实现;M3另一半目标声明明确out of scope。
对主干的风险
当前6b3:12项marker/storage和10项suggestions测试、两组workspace contract、CSS及正常typecheck/build通过;5个当前打包场景since-you-looked/live、goal-suggestions/live和capability-scope通过。live场景用一次性真实Chat/Todo或runtime-probe;旧3d5/7eec的1→0同输入反例保留原版本,当前marker blob与7eec相同,6b3重新运行unknown201场景和额外Ego返回,done=null/200项且无新增行。当前premerge3 direct+13 selected产品检查通过,整体exit1/quality_stale_receipt保留。未查或等待CI。
额外Ego检查使用正常编译产物和实际生产模型,201条未变完成记录、总数缺失、marker仅200项:返回后无新增行,概况仍显示“未报告完成数/总数未知”。合成API限定输入,不代表已安装App;live场景另经真实一次性后端。此前明确已读回退、201决定误报与本轮201完成误报均有当前回归。
当前头已把读取的main796作为真实祖先,原四处冲突已由作者整合;本轮检查确认快捷建议和since两套逻辑/断言/场景均保留,12+10单测、5个当前打包组合场景与当前类型/构建通过。旧7eec反例和6场景仍保留其原执行版本;当前Ego重载同一浏览器v1标记仍为done=null、200项、无新增行。当前scope质量receipt过期和有效旧阻塞评审继续是独立限制,最终readiness需另读;本票不授merge或dismissal。
语义与 CI 对齐
新标记保持为浏览器本地 goal_seen_marker_v1,不创建共享权限或Actor生命周期。格式不识别就安静地按首次查看处理;无已发布v0兼容义务证据。它是默认增加的返回展示,不是假定default-off。没有安装Agent提示词、自动Turn、验收、配额或服务写入。中英文指南已准确披露有界未知时少报。
我的整体评价
APPROVE当前源码提案。 重复返回的长期可接续性与实际查看体验均改善:不再反复催促旧完成项,真实变化仍可见,既有意图和权限下新增0轮必要交互。当前批只交付可信的本地返回摘要及确认旅程;目标声明、通知、服务端历史、安装采用另属原owner。相关refine已复用覆盖helper,没有引入新框架;一份受guard的本地schema足够。当前主干整合已在此head核验;质量资格、旧review reconciliation和最终readiness保持开放,本次没有merge或修改作者源码。
English verdict: APPROVE — 6b3cefd329be473ae750a9e6f170266d535f7dbf preserves the capped unknown-history fix while integrating main796 and the state-aware suggestions. Current12+10 tests,5 packaged journeys, normal build/types, product canaries and the compiled UI pass. Original execution revisions remain explicit; stale quality qualification and review/merge closeout remain separate holds. No CI polling or merge.
Summary
A returning owner had to re-read a Goal's overview to find out what changed while they were away. RFC goal-workspace-surface-v0 §5.7 specifies the answer: a browser-local "since you looked" marker and one quiet line on return. This delivers that half of M3.
goal_seen_marker_v1with typed counts (open, blocked, done), decision ids, a bounded set of done Todo ids and result ids (200 each), and the observation time. No titles, content or cost; no service write.since-you-looked.ts; the page owns the lifecycle;GoalOverviewrenders the line. No new persisted protocol or Python decision source.Review round 2 (head after
45f1bdbcf)The reviewer's two findings are fixed and one semantic concern is closed:
decisionIdsnow takes only items the attention lane marks as waiting on the owner (WorkspaceAttention.blocking); an optionaluser_actionis listed, never counted.seenMarkerStorage), so alocalStoragegetter that throws leaves the Goal readable. Helper test covers the throwing getter; the fixture scenario injects it after a normal start and opens the Goal.loopx chatthe owner's refresh lands after the Goal opens; the comparison now waits for an in-flight owner refresh instead of latching on the snapshot it replaces. Found only through the real-service scenario below.The
"latest" | "compared"argument ofadvanceSeenMarkeris a local function parameter, not a shared vocabulary.Review round 3 (head after
95caadb56)goal_seen_marker_v1, browser-local); past the 200-id cap, or for a partially listed set, only growth of that count reports a new decision or result. 201 unchanged decisions stay silent.Review round 4 (head after
3d5d5aaaf)Validation
counts.done === null) and stays silent on return. Node tests add the no-total cases.since-you-lookedadds the refresh → Mark seen → leave → return path (acknowledgment retained) and a 201-decision workspace that seeds 200 ids plus the count and stays silent on return. Node tests cover 201 unchanged, a reordered window, real growth, a partial listing, resolved-and-raised within the cap and capped results.since-you-looked-live(real service): a disposable real Chat/Todo owner servesstatus.json; the CLI raises an optionaluser_action(return stays silent), then completes an agent task and raises auser_gate(line shows one newly done and exactly the lane's own count of decisions); Mark seen clears the next return. Only the status path is proxied; other reads keep the fixture.since-you-looked.test.mjs(12 cases): bounded typed marker; no-change pair silent; known work, decision, blocked and result changes counted since the marker; shrinking queues and resolved decisions never count; a trimmed marker cannot inflate newly done beyond the done-count growth; ids decide when no done count is projected; storage round-trips per source and Goal and tolerates garbage and blocked storage.since-you-looked(V5 journey): first visit seeds silently; no-change return silent; a Todo completed and a gate raised while away show "新完成 1 项、新增 1 项等你决定" with no cost; Mark seen advances the marker; a later change shown and then left without marking is acknowledged on the next return; the marker survives reload; no non-GET request to the service beyond the existing session and catalog reads.personal-workspace-contract.test.mjspins the marker's typed fields, per-source-and-Goal scoping, the material-only rule, the leave-acknowledge rule and both locales. Overview-adjacent scenariosgoal-showcase,goal-masthead,goal-workspace-m2,first-goal-homepass. dashboardtsc --noEmit,check:css-custom-properties,generate_semantic_inventory.py --changed-from HEAD.Not covered. Installed App; real-model acceptance; target declaration (the other half of M3) is not part of this PR.
Default change. None visible until a return with a material delta.
🤖 Generated with Claude Code
Current source integration qualification
Current published head:
6b3cefd329be473ae750a9e6f170266d535f7dbf, including official main7961c7689f364d9e421f4c5a48c924f24f140de1and the merged state-aware suggestions. Both sets of feature imports/functions, contract assertions, browser scenarios and npm test commands remain present.An independently composed local integration passed 12 marker/storage cases, combined workspace contracts, frontend typecheck, normal bundle build and the packaged scenarios: since-you-looked, since-you-looked-live, capability-scope and goal-suggestions. Real-service scenarios use disposable synthetic Chat/Todo or runtime-probe state; notifications stay stubbed, no model is run.
Before publication the remote branch had already received this integration from another executor, so no duplicate commit or push was made. The complete Git comparison finds identical product and scenario blobs; the only differences are the ordering of the same npm command and contract-line multisets, plus a documentation blank line where applicable. The exact published assertion source was independently executed against those identical product files and passed. Browser/build evidence is reused under that explicit boundary; this is not a claim of whole-tree equality or a newly built package at the peer commit.
Current-head independent review and complete natural hosted CI remain required; earlier approvals, local smokes and pushes are not full acceptance. Installed App, real-model, OS notification delivery and the owning broader capability gates remain separate as applicable. No merge, deployment, review publication or manual hosted rerun is performed here.