fix(dashboard): restore completed-task readback and workspace reliability - #3961
huangruiteng merged 17 commits into
Conversation
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
…dback Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
…ures Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
`chat_server.py` sat at its maintainability ceiling (1503 lines, 25 `Any` names) before this PR, and the inline completed-todos handler pushed it to 1560 lines, so the control-plane maintainability ratchet failed in CI. Follow the existing `ChatStatusRequestMixin` shape: move the handler into `loopx/chat_completed_todos_api.py` as `ChatCompletedTodosRequestMixin`, which owns the route constant, the query allowlist, paging bounds, the scope check, and the read-only todo projection. The mixin depends on the handler only through the `_send_error`/`_send_json` stubs and a new `_compact_todo_record` hook, so it does not import `chat_server` back. `chat_server.py` returns to 1509 lines and 25 `Any` names; no ratchet baseline entry is edited. Two behavior corrections to the readback surfaced while moving it: - a done record without `todo_id` is dropped from the page instead of being serialized with a null id, because the dashboard response schema requires a string id and one such record would fail the whole page; - an unknown goal (`ValueError` from `list_goal_todos`) now answers 404 rather than 400; malformed queries keep 400 and unreadable sources 503. Tests patch the new owner module and cover both corrections. Validation: - python3 -m pytest tests/test_chat_server_cors.py tests/canary/test_maintainability_ratchet.py tests/control_plane/test_m6_quality_gates.py -q -> 21 passed - python3 examples/control_plane/control-plane-maintainability-ratchet-smoke.py -> ok - ruff: new module and test clean; chat_server.py findings identical to main Signed-off-by: song <liusongstep@gmail.com>
…lane On `main` the task surfaces render inconsistently: the `.personal-task-card` lane picks up the LoopX theme rule (13px / 600 / 18px titles, 11px metadata) while the attention, schedule, and completed lanes are plain buttons that fall through to the global defaults (16px / 700 / 24px titles, 12.8px metadata). The theme contract test asserted the card rule text, not the rendered result, so the mismatch was never caught. Make the scale explicit and identical for the board lanes, the board cards, the loopx-theme override, and the new grouped list rows, using the design system tokens: titles Body M `14px / 500 / 20px`, metadata Body S `12px / 400 / 16px`, and group headers `12px / 600 / 18px` to match the existing lane headers. Cards still do not shrink below their content and long titles still clamp. The theme contract assertion now names the Body M scale for task titles. Disclosure: this is a deliberate first-screen typography change on the Tasks view; measured before/after values and screenshots are in the PR. Validation: - node src/features/personal-workspace/workspace-theme.test.mjs -> ok - node src/features/personal-workspace/personal-workspace-contract.test.mjs -> ok - rendered metrics via Playwright: all four lanes in both views report header 12px/600/18px, title 14px/500/20px, meta 12px/400/16px Signed-off-by: song <liusongstep@gmail.com>
ed7089b to
6e7a639
Compare
|
Review follow-ups pushed; exact head
Local: chat-server and ratchet pytest 21 passed; dashboard contract tests, |
The grouped list view stays available behind the List/Board switch, but Tasks opens in the four-lane board as it does on `main`. The packaged Personal Workspace browser smoke (a required Frontstage step) locates the progress and completed columns through the board structure, and changing the default made those assertions fail in CI. Keeping the board default also leaves the Tasks first screen unchanged, so the only first-screen change in this branch is the typography scale disclosed separately. The user guide describes the board as the default and the list as the switchable view; the task-board scroll smoke no longer needs to switch views before measuring the lanes. Validation: - node src/features/personal-workspace/personal-workspace-contract.test.mjs -> ok - node src/features/personal-workspace/workspace-theme.test.mjs -> ok - npm run smoke:personal-workspace-packaged -> personal-workspace-browser-smoke (packaged): ok Signed-off-by: song <liusongstep@gmail.com>
The Frontstage workflow rebuilds the packaged Chat dashboard with `npm run build:chat` and fails when `loopx/web/chat` differs from a clean source build. Regenerate `index.html` and the hashed bundles for the source changes in this branch. As with previous regenerations, earlier bundles stay tracked because the chat build keeps `emptyOutDir: false`. Validation: - npm run build:chat -> tsc --noEmit and vite build passed - npm run smoke:personal-workspace-packaged -> ok against these assets Signed-off-by: song <liusongstep@gmail.com>
2a5c1cf to
b870c21
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
动机
这个 PR 处理的是一组真实且相邻的 Personal Workspace 可靠性问题:状态投影只携带近期完成任务,导致摘要总数和可读明细不一致;长任务卡会挤压重叠;首次实时状态尚未读到时会展示示例数据;执行 Session 探测失败缺少反馈;访问摘要又会在首次计算后冻结。目标用户是本地 dashboard 操作者,理想结果是:默认仍进入 Board,按需从现有 Todo authority 读取当前已完成 advancement tasks,同时在实时源、执行发现和页面渲染失败时给出诚实的降级状态。单纯扩大现有 status projection 的近期列表不能同时解决受限投影与按 Agent 分页,因此复用 list_goal_todos 的只读端点是合理的小机制;不过当前 exact head 还不能直接进入主干。
改动思路
入口分成三条:DashboardPage 先从 loopback status service 获取实时投影;GoalTasksView 仅在用户点击“查看当前全部已完成”时调用 /api/chat/todos/completed;PersonalGoalHome 独立轮询 task Session 并对离线/部分失败退避。服务端由 ChatCompletedTodosRequestMixin._completed_todos 校验 query、Goal scope 和分页边界,把既有 list_goal_todos 作为权威读取,再过滤 active、done、advancement task,经过 chat redaction 后返回。前端以 Goal/Agent 组合为缓存键,合并分页结果并拒绝过期响应。页面级异常由 router error component 接管,访问摘要固定第一次访问基线、只刷新计数。正向路径和失败路径的 owner 都较清楚:Todo authority 决定记录,chat handler 决定公开投影,组件决定展示/重试。
具体改动
精确 diff 为 22 个文件、+608/-57:约 380 行生产源码,144 行测试/Smoke,11 行文档,130 行生成 bundle。生产改动包括 102 行完成任务 API、新的前端 fetch schema、Task Board/List 视图及样式、稳定的 i18n context、访问摘要基线、执行发现退避、初始 live loading/error 页面、periodic-report Vite proxy 和准确的文件操作标签;测试覆盖 API 的 scope/paging/redaction/错误状态以及若干 dashboard contract;生成资源与源码同步。
关键代码讲解
loopx/chat_completed_todos_api.py:39的ChatCompletedTodosRequestMixin._completed_todos是读取边界:只接受 allowlist query,限制 offset/limit,验证 server Goal scope,调用现有 Todo owner,最后再裁剪和脱敏;未知 Goal 返回 404、底层 I/O 返回 503。apps/presentation/dashboard/src/features/personal-workspace/goal-tasks-view.tsx:176的loadCompleted用[goalId, selectedLaneId]隔离结果,分页合并按 todo id 去重,并在切换 Goal/泳道后丢弃迟到响应。apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx:950的访问摘要 effect 将 localStorage 时间只读入一次,因此后续投影刷新不会移动 baseline。apps/presentation/dashboard/src/views/dashboard-page.tsx:1777的执行发现 effect 在页面隐藏时暂停请求,对失败指数退避,并区分部分详情失败与整体离线。apps/presentation/dashboard/src/views/dashboard-page.tsx:2793的statusRequestActive保证默认实时源成功前不再把 bundled example 当成 live 数据展示。
对主干的风险
阻塞项:当前分支相对最新 main 已有实际内容冲突,不能安全合并。 我用 git merge-tree --write-tree origin/main HEAD 复核,冲突落在 goal-tasks-view.tsx、personal-workspace-page.tsx、chat_server.py 和生成的 loopx/web/chat/index.html。触发条件就是现在将 b870c21b 合入最新主干;Git 无法自动决定主干新增改动与本 PR 的 dashboard/chat-server 改动如何组合,错误结果是 PR 无法落地,手工误选还可能丢失任一侧行为。最小修复是将分支 rebase/merge 到最新 main,逐处保留双方语义,重新执行 npm run build:chat 生成唯一的当前 bundle,并跑完整必需检查;请不要只解决 index.html 的 hash 冲突。
代码本身未发现第二个阻塞缺陷。完成任务 API 不是 feature-gated/default-off 功能,canLoadCompleted 只区分只读外部源与本地 chat capability,旧的只读路径不产生新请求;没有新增 actor/authority 名称,也没有字符串启发式状态分类。剩余产品风险主要是 offset 分页不是快照隔离,以及完整页面/主题/设备矩阵尚未覆盖。CI 当前 14 项成功;本地完成任务 smoke、Personal Workspace contract 和 theme contract 通过。Python pytest 与完整本地 build 未复跑成功,原因分别是复核 worktree 的 Python 环境没有 pytest、借用的 node_modules 缺少 @fontsource-variable/geist;这些由远端 pytest/build 绿灯部分补足,但 rebase 后必须重新验证。
我的整体评价
机制与问题基本成比例:只读 API 复用既有 authority,分页与 redaction 边界明确;前端状态量虽增加,但对应真实的分页、过期响应和失败恢复;生成 bundle 占较大 diff,却是仓库 Frontstage 交付约束。行为变化也在 PR 描述和用户指南中披露,公共命名没有夸大权限。尽管实现质量总体良好,merge_state=DIRTY 且四处内容冲突意味着当前 exact head 不能批准。请先与最新主干整合、重建资源并让全套 CI 在新 head 上通过;我会按新 SHA 重新检查冲突解决与行为保持。
Verdict: REQUEST_CHANGES
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
|
Rebased onto latest upstream main |
|
Rebased onto latest upstream main |
Signed-off-by: song <liusongstep@gmail.com>
…ex/dashboard-readback-reliability Signed-off-by: song <liusongstep@gmail.com>
|
Update verified on remote head Correction to earlier updates: this branch was merged with main, not rebased. Earlier push attempts had not reached GitHub. Also, build:chat does exist in the dashboard package; its reported missing-script error came from running npm in the repository root. Resolved the dashboard/chat conflicts while preserving cursor-based Board history and bounded active-completed List readback with distinct handler methods. Fixed the remaining packaged index.html references to the rebuilt JS/CSS assets; those references had incorrectly retained the main bundle after the previous conflict resolution. Validation rerun after integration: 23 Python tests passed (chat CORS/active readback, real synthetic Markdown HTTP history, maintainability ratchet); build:chat passed including TypeScript checking; theme, workspace drawer and completed-todos contracts passed. Build retains the bundle-size warning. Full theme/device browser matrix and real Agent/write/remote-source acceptance remain unverified. New-head CI and maintainer re-review are still required; no merge performed. |
huangruiteng
left a comment
There was a problem hiding this comment.
动机
这个 PR 修复 Personal Workspace 中一组彼此关联的可靠性问题:状态投影只包含近期完成任务,导致完成总数与可读明细不一致;任务卡在内容较长时发生重叠;首次实时状态尚未返回时会误显示示例数据;执行 Session 探测失败缺少明确降级;访问摘要的基线还会随刷新漂移。期望结果是继续以 Board 为默认入口,仅在用户明确切换 List 并请求时,从既有 Todo authority 分页读取当前已完成 advancement tasks,同时让实时状态、执行发现和页面错误都有诚实且可恢复的表现。
改动思路
实现把职责分在清晰的边界上:DashboardPage 负责实时状态的 loading/error 与显式 example mode;PersonalGoalHome 负责执行 Session 发现、页面隐藏时暂停、失败退避及 partial/offline 提示;GoalTasksView 默认展示 Board,只有 List 的完成任务读取动作才调用 /api/chat/todos/completed。服务端的 ChatCompletedTodosRequestMixin._active_completed_todos 校验 query、分页和 Goal scope,复用现有 list_goal_todos authority,过滤 active、done、advancement 记录,再经 chat redaction 后返回只读结果。访问摘要固定首次访问基线、只更新后续计数,router recovery 接住页面级异常。
具体改动
精确 diff 为 24 个文件、+750/-56,覆盖完成任务只读 API、chat server 路由与 CORS contract、dashboard API schema、Tasks Board/List UI、i18n context、访问摘要、执行发现退避、router recovery、Vite proxy、用户指南,以及与源码同步的生成 bundle。新增测试验证完成任务读取的 Goal scope、分页、过滤、脱敏和错误路径,也补充了 chat server CORS 测试及 dashboard contract/theme smoke。
关键代码讲解
loopx/chat_completed_todos_api.py中的ChatCompletedTodosRequestMixin._active_completed_todos是新的读取边界:它拒绝未知参数和越界分页,验证当前 server Goal,调用既有 Todo owner,再过滤、脱敏并分页,因此没有创建第二套状态 authority。apps/presentation/dashboard/src/features/personal-workspace/goal-tasks-view.tsx的loadCompleted以 Goal/Agent 组合隔离请求和缓存,合并分页时按 Todo id 去重,并避免切换上下文后的迟到响应污染当前视图。PersonalGoalHome的 execution-discovery effect 在页面隐藏时暂停轮询,对失败指数退避,并区分详情部分失败和整体离线,避免短暂不可用被伪装成空结果。DashboardPage的statusRequestActive/loadFromUrl路径在实时请求完成前保持 loading/error,只有显式 example mode 才使用 bundled example。PersonalWorkspacePage的 visit-digest effect 固定首次访问基线,使后续投影刷新只改变计数而不会移动比较窗口。
对主干的风险
上一版 b870c21b876a755f8fc0aa12262fa5174cbe242a 与最新主干存在内容冲突,因此我请求了变更。当前 exact head e4140964657da921f507e697921037e5d15e9a76 已合入主干并重新生成 packaged dashboard;GitHub 现在报告 MERGEABLE,提交的 index.html 也引用本次构建生成的资源。本地复核通过 git diff --check、tests/test_chat_server_cors.py 9/9、npm run smoke:completed-todos、npm run build:chat、Personal Workspace drawer contract 和 workspace theme contract;远端 sign-off、dependency review、macOS、Windows、PowerShell、两个 build 和 Sonar 检查均通过。
剩余风险主要是 offset 分页反映的是当前状态而非快照;并发变更时页边界可能移动,不过前端按 id 合并可降低重复展示,且该端点的产品语义本就是 current-state readback。完整真实浏览器、设备及远端数据源矩阵未在本次本地复核中重跑。远端 pytest 在提交本 review 时仍在运行;它仍应作为合入前的必需保护,不应绕过。
我的整体评价
新 head 已解决上一轮唯一阻塞项,整体机制与问题规模相称:只读 API 复用现有 authority,校验、过滤和 redaction 边界明确;前端新增状态分别对应分页、过期响应、离线退避和真实 loading/error,而非泛化框架;生成 bundle 虽占据较大 diff,但已由本地与远端 build 交叉验证。基于 exact head 与上述证据,我批准该改动;合入仍以剩余必需 CI 全绿为前提。
English verdict: APPROVE — the previous mainline-conflict blocker is resolved on e4140964657da921f507e697921037e5d15e9a76; local API/dashboard validations and all completed remote checks pass, with full browser/device/remote-source coverage still unverified and the in-progress pytest check required before merge.
|
Reviewed at head First, the direction is right on the parts that matter most: the readback goes through the canonical todo reader ( Three findings before this should merge: 1. Two completed-task contracts now coexist (structure)
The user-visible consequence: on one Tasks page, switching Board ↔ List silently swaps both the data source and the definition of "completed". Board's history includes archived work; List's readback excludes it, so the same lane's count changes with the view. The rebase note says both contracts were preserved to resolve the conflict — understandable as a stepping stone, but the end state should be decided now, before more callers depend on the second URI. Suggested convergence: either extend the existing cursor endpoint with a 2. Offset paging reintroduces a problem
|
Signed-off-by: huangruiteng <huangrt01@163.com>
|
Reviewed and refined on the author's original fork branch at No remaining blocking findings in the reviewed scope. The three findings from the earlier review are resolved by convergence, not by maintaining two APIs: List now uses #3970's snapshot history, so the data scope/count and cursor semantics match Board, and both inherit the existing loopback Origin gate. The duplicate offset route, module, client and dedicated smoke are removed. Product/architecture judgment: positive. The original reliability improvements remain, while one history owner removes a user-visible semantic mismatch and duplicated state. This uses the existing built-in Workspace/history surface, with no new capability, task-state rules, Agent execution or provider authority. Board stays default; List is optional. The bounded future-facing cleanup is applied in this PR, including removing the reverse import of the chat server's private compact helper. Owner-requested behavior disclosure: local history preserves the Todo reader's task text, paths and evidence; it no longer applies extra projection truncation/path replacement. The upstream Markdown text normalization limit remains 500 characters. Remote read-only sources still do not query local history, foreign Origin requests are rejected, and public/export projections are unchanged. Browser screenshots use synthetic fixtures only. Validation on this exact head:
Resolved validation attempts: the added evidence fixture initially used a legacy inline marker rather than canonical metadata; corrected to Merge decision: owner-authorized self-merge after the new-head CI finishes successfully. No force push, private state, credentials, local runtime logs or public screenshots were included. Credit for the original reliability and List-view contribution remains with @songoow. |
|
Self-merged with owner authorization after all new-head validation checks passed. Final reviewed head: Verified the merged tree is identical to the validated head. Python Tests (including the full pytest job and Windows lifecycle tests), Frontstage build/browser acceptance, desktop builds, release build, DCO, dependency review and Sonar checks succeeded. Release upload/deploy/publish jobs were skipped by their normal PR conditions, not waived validation. The original fork branch was updated by a normal fast-forward push and retained; no contributor history was force-pushed. Thanks @songoow for the reliability and optional List contribution, and @now-ing for identifying the history-contract duplication. |
Summary
Preserve @songoow's Workspace reliability fixes and optional grouped List layout, while reusing the snapshot history shipped by #3970. Board remains the default.
CompletedTaskLaneand/api/chat/completed-todoscursor endpoint. Both include archived completed work, exclude continuous monitors, and support Agent filtering. Switching views retains loaded rows, total, errors, and the snapshot; List starts collapsed and loads on scroll.Behavior and ownership
This stays within the existing dashboard and chat history read surface; no new capability/provider, Agent execution, Todo mutation, or authority boundary is introduced. Local history content is intentionally less compact, as requested by the owner. Public sharing and file-export sanitization are not relaxed.
The bounded future-facing refactor is applied here: a single snapshot owner replaces duplicate UI state and backend/client logic. Snapshot limits remain 40 rows/page, five-minute lifetime, eight cached snapshots, and 16 MiB cache budget. Oversized snapshots fail visibly rather than silently truncating history.
Default changes inherited from the original contribution: initial live loading does not show example tasks; task titles use 14px/500/20px, metadata 12px/400/16px, and group headers 12px/600/18px. Board remains the default; List is optional. Owner preview and the implementation direction were approved before this update.
Validation on
a8e6eca4564f801a383f28d8761f57901bbef754npm run build:chat: passed (TypeScript + production bundle; existing large-chunk warning).npm run smoke:personal-workspace-packaged: passed, including desktop/mobile Workspace scenarios, 4,087 completed rows, bounded List DOM, and zero new history requests when switching the fully loaded Board/List views. Browser fixtures are synthetic, not live Agent acceptance.pytest tests/test_chat_completed_todos.py tests/test_chat_server_cors.py: 11 passed. Real temporary Markdown through the HTTP server proves text/path/evidence preservation without file writes and foreign-Origin rejection; focused snapshot tests cover concurrent paging, scope, expiry, capacity, and byte bounds.cqr_43a992b6cc35d7e2e9b4, verified valid for fingerprint43a992b6cc35d7e2e9b4c59c3434d2ccf7cd221c2fcf09cfeef4c0b7ff369003(24 changed paths).Packaged assets are regenerated; older tracked bundles follow the repository's
emptyOutDir: falseconvention. Private state, screenshots, credentials, and runtime logs are excluded.Final premerge passed (4 direct checks and 15/15 selected checks; no failures, skips or manual holds), and all new-head validation CI checks passed. Owner-authorized self-merge completed as
bb71b386254540926489cd6c61bcc6a6308accef; its tree matches the validated head. Release upload/deploy/publish jobs were normally skipped for a PR. No live Agent execution, production writes, or exhaustive theme/device matrix is claimed.