fix(dashboard): load Goals with native Todos without source indexes - #5028
Conversation
fe23193 to
f34958b
Compare
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
f34958b to
1491bcb
Compare
There was a problem hiding this comment.
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
Reviewed exact head: 1491bcb
动机
旧 Dashboard 状态解析把 Markdown Todo 的 source index 当成每条 Todo 的必填身份。原生 Todo 只有稳定的 todo_id、没有 index 时,整份 Goal 状态会在解析阶段失败,连同可继续处理的 Goal 一起从工作区消失。这个 PR 要修的是可达性与真实身份,不是更改 Todo 的调度或权限;修后状态可载入,持续工作不会被一个不存在的展示坐标阻断。
改动思路
入口仍是 Dashboard 的状态拉取与 parseStatusPayload;权威输入仍是 Core 返回的 Todo 数据。todoItemSchema 允许 index 缺省或 null,但要求 index 与非空 todo_id 至少存在一个;todoIndexItemSchema 继承同一身份约束。UI 继续优先用 todo_id 做稳定 key,旧 Markdown Todo 才回退到 source index。比起给所有原生 Todo 伪造 index,这使解析边界承认已有身份,且不产生第二套持久状态。失败仍由状态请求/重试路径负责,未引入新的控制面动作。
具体改动
关键代码讲解
- apps/presentation/dashboard/src/data/status.ts 的 todoItemSchema(约第 65–105 行)从强制 number 改为可缺省坐标,并用 refine 拒绝同时没有 index 和 todo_id 的匿名行;字符串 index 仍拒绝。
- 同文件 todoIndexItemSchema(约第 116 行)改用 safeExtend,确保索引行继承上述 refinement,而不是绕过身份约束。
- apps/presentation/dashboard/src/views/dashboard-page.tsx 的 personalAgentTodoFromItem 不再向展示模型传递未使用的 index;其行 key 仍先取 todo_id,旧数据保留 index 回退。
- 状态契约 smoke 注入无 index、null index、旧式 index、匿名和类型错误五类输入;打包浏览器 smoke 把原生 Todo 注入 ready Goal,验证单个 Goal 曾失败再恢复时工作区仍可载入。我的独立复跑中,status-projection-contract、build:chat 和 workspace-progressive 均通过;一次当前 scoped status 的真实解析也通过,但那个 live Goal 当时没有原生 Todo,不能拿它冒充原生 Todo 端到端验收。
对主干的风险
本次默认解析规则确实放宽,但范围被“index 或 todo_id”约束住;未改变 claim、租约、权限、quota 或持久化语义。浏览器路径证明整页不再被混合数据击穿,却没有逐项断言原生 Todo 在视觉列表中的身份,这属于后续可增强的覆盖,不是目前发现的回归。真正的合并阻断是仓库现行必跑的 semantic-vocabulary-drift-smoke:同一 PR 基线与此 head 的 control_plane/生成器/预算实现均未变化,而独立运行仍报 44 个独立维护的 py/ts twins,预算 43。按仓库的预算决策规则,不能为了这两个 PR 偷改阈值,也不能把红色必跑验证当作通过;需要维护者在基线修复或作有证据的预算决定后重跑原检查。未查询或等待远端 CI。
语义与 CI 对齐
Todo 身份沿用已有 todo_id,没有新增共享控制面词汇;44/43 是现有绝对预算未过,并非本 PR 新增 twin。它仍是当前候选必须通过的本地检查,故我的结论是 hold,而不是以“基线也红”为理由自合并。
我的整体评价
功能方向和实现边界成立:长期工作可达性改善,用户不再因原生 Todo 缺少展示索引而丢失 Goal 视图;负例仍拒绝匿名数据。没有发现需要改动本 PR 代码的确定性缺陷,相关重构也已保持在相邻 schema/展示边界内。由于必跑预算检查在 exact head 仍失败,本次只能给出 REQUEST_CHANGES/合并暂缓;基线门槛修复并复跑通过后,可对不变 head 重新审结。此 PR 涉及 apps 行为,按仓库规则不得自合并。
English verdict: REQUEST_CHANGES
Goal And Delivered Outcome
todo_idvalues and intentionally have no synthetic source index.main.Scope And Continuation
Validation
1491bcb290b388f88db2ce94f3dd0a0fc38189e2againste5e1a6a9e2c85ef62b9f35b91dbeede09c6b7486.npm run smoke:status-projection-contract: mixed native/legacy Todo rows parse; anonymous and malformed identity rows fail.npm run smoke:workspace-progressive: packaged browser journey loads a synthetic Goal with a native Todo and preserves retry/mobile behavior.npm run build:chat,npm run build:desktop,git diff --check origin/main...HEAD, and changed-path public/private boundary scan.todo_idand invalid anonymous rows remain rejected.loopx canary premerge --from-git-diff --goal-id loopx-meta: 9 of 10 selected catalog checks passed;semantic-vocabulary-drift-smoke.pyreports 44 maintained Python/TypeScript twins against a budget of 43. The base branch also has 44; this diff adds no control-plane twin. Quality receipt and public boundary check passed.Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
Boundary Checklist
Signed-off-bytrailer (git commit -s).