Skip to content

fix(dashboard): restore completed-task readback and workspace reliability - #3961

Merged
huangruiteng merged 17 commits into
loopx-project:mainfrom
songoow:codex/dashboard-readback-reliability
Sep 6, 2026
Merged

huangruiteng merged 17 commits into
loopx-project:mainfrom
songoow:codex/dashboard-readback-reliability

Conversation

@songoow

@songoow songoow commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Preserve @songoow's Workspace reliability fixes and optional grouped List layout, while reusing the snapshot history shipped by #3970. Board remains the default.

  • One completed-task history: Board and List use the same CompletedTaskLane and /api/chat/completed-todos cursor 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.
  • Remove duplicate behavior: retire the unshipped offset endpoint, its handler/client, and endpoint-specific smoke. No second definition of “completed” or live-offset pagination remains.
  • Local owner readback: select task display fields directly instead of importing the chat server's private compact helper. Preserve the Todo reader's text, paths, and evidence without additional truncation or path replacement. The Markdown reader's existing 500-character text normalization still applies; public/export projections are unchanged. The existing loopback Origin guard and read-only remote-source restriction remain.
  • Reliability retained: stable locale context, reloadable route-error screen, real loading state instead of transient example tasks, execution-discovery error reporting/backoff/hidden-page suspension, stable previous-visit digest baseline, and development report proxy.
  • Presentation retained: optional grouped List, non-shrinking cards, consistent task typography, and accurate “Go to conversation” / “Export summary” labels. The completed List uses matching row alignment and an accessible collapse control.

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 a8e6eca4564f801a383f28d8761f57901bbef754

  • npm 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.
  • Workspace drawer, theme, goal-order, and task-board-scroll contracts: passed.
  • Changed-test Ruff, Python compilation, and diff hygiene: passed.
  • Exact-scope quality receipt: cqr_43a992b6cc35d7e2e9b4, verified valid for fingerprint 43a992b6cc35d7e2e9b4c59c3434d2ccf7cd221c2fcf09cfeef4c0b7ff369003 (24 changed paths).

Packaged assets are regenerated; older tracked bundles follow the repository's emptyOutDir: false convention. 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.

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>
@songoow
songoow force-pushed the codex/dashboard-readback-reliability branch from ed7089b to 6e7a639 Compare September 5, 2026 16:06
@songoow

songoow commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator Author

Review follow-ups pushed; exact head b870c21b8, four commits on top of the reviewed set:

  • 73899f81e moves the completed-todos handler into loopx/chat_completed_todos_api.py (ChatCompletedTodosRequestMixin, no reverse import), which resolves the maintainability-ratchet failure; identity-less records are dropped and an unknown goal answers 404, both tested.
  • 6e7a63993 unifies task typography across all lanes and both views onto the Body M / Body S tokens; on main the card lane rendered 13px while the other lanes rendered 16px. Disclosed in the body; screenshots below.
  • 433fbbfe1 keeps Board as the default Tasks view. The packaged browser smoke locates the Tasks columns through the board structure, which is why the previous head failed the Frontstage step after the asset check passed. List stays behind the switch.
  • b870c21b8 regenerates the packaged assets required by the Frontstage check.

Local: chat-server and ratchet pytest 21 passed; dashboard contract tests, smoke:completed-todos, and smoke:personal-workspace-packaged pass; build:chat passes; premerge canary 15/15.

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>
@songoow
songoow force-pushed the codex/dashboard-readback-reliability branch from 2a5c1cf to b870c21 Compare September 5, 2026 16:43

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

动机

这个 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>
@songoow

songoow commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto latest upstream main 788acc143. Resolved conflicts by preserving both completed-task contracts: cursor-based /api/chat/completed-todos remains available for Board, while bounded active advancement readback remains /api/chat/todos/completed for List. Rebuilt packaged chat assets. Focused validation: 23 Python tests completed; dashboard build was run before the rebase script-name mismatch was found, and should be rerun by CI.

@songoow

songoow commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased onto latest upstream main 788acc143. Conflicts resolved by preserving both completed-task contracts: cursor-based /api/chat/completed-todos remains available for Board, while bounded active advancement readback remains /api/chat/todos/completed for List. Packaged chat assets rebuilt. Focused validation: 23 Python tests passed; CI will rerun the dashboard build on the rebased head.

Signed-off-by: song <liusongstep@gmail.com>
…ex/dashboard-readback-reliability

Signed-off-by: song <liusongstep@gmail.com>
@songoow

songoow commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator Author

Update verified on remote head e41409646 (normal push; no force push). Latest upstream main at integration time: bf217e1e01.

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
huangruiteng previously approved these changes Sep 6, 2026

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

动机

这个 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.

@now-ing

now-ing commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Reviewed at head e4140964 (up to date with main at bf217e1e). Scope: the completed-task readback path and the workspace reliability fixes.

First, the direction is right on the parts that matter most: the readback goes through the canonical todo reader (list_goal_todos / file authority) instead of re-deriving completion in the UI, redaction is reused from _compact_todo with evidence stripped and identity-less records dropped, query validation runs before any read, and the handler mixin reaches the host through abstract hooks instead of importing back into chat_server — cleaner than the existing CompletedTodoRequestMixin, which still does from .chat_server import _compact_todo. The reliability side (stable i18n context identity, root error component with a reload fallback, discovery backoff plus suspend-when-hidden, fixed digest baseline, vite report proxy) is tidy and self-contained. On this head I ran tests/test_chat_server_cors.py + tests/test_chat_completed_todos.py (14 passed), smoke:completed-todos, and the personal-workspace contract test — all green locally.

Three findings before this should merge:

1. Two completed-task contracts now coexist (structure)

main already ships loopx/chat_completed_todos.py serving /api/chat/completed-todos — cursor/snapshot paging, includes archived work, consumed by CompletedTaskLane in the Board view. This PR adds loopx/chat_completed_todos_api.py (module name differs by _api) serving /api/chat/todos/completed — offset paging, active advancement records only, consumed by the new List view. Same resource, two URIs, two near-identical module names, two filter scopes, two paging models.

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 scope=active_advancement filter and let List consume it, or fold the new module into chat_completed_todos.py. If both routes must stay for now, at minimum merge the two modules and document the intended end state.

2. Offset paging reintroduces a problem main already solved (correctness)

CompletedTodoPages exists precisely because "snapshots keep concurrent completions from shifting page boundaries" — and a live dashboard is exactly that environment. The new endpoint pages by offset against a freshly computed sort on every request: between two load-more clicks, a newly completed task shifts every later row, so the next page either repeats items (the frontend Map dedupe absorbs those) or skips items entirely (unrecoverable — the UI ends up showing "shown N / total M" with M unreachable). total is likewise recomputed per request with no consistency tie to pages already served. If converging on the snapshot endpoint is too big a step for this PR, an intermediate fix is to reuse CompletedTodoPages under the new scope.

Related count-semantics jump in goal-tasks-view.tsx: before loading, the done lane shows Math.max(goal.doneTodoCount, localItems), where doneTodoCount derives from advancement_done_count — which folds archived completions in (loopx/control_plane/coordination/local_authority.py:234-236). After "Load all completed" it snaps to the endpoint's total, which counts active records only. With any archived work present, the count visibly shrinks after loading. Either label the List scope explicitly ("active completed") or reconcile the count source.

3. The new GET route lacks the loopback origin gate (small)

do_GET has no global origin check; the sibling endpoint on main guards itself with _require_loopback_origin() (as does _ssh_hosts). _active_completed_todos does not. Output is redacted and the server binds loopback, so this is defense-in-depth parity rather than an open hole — but the same-resource endpoint one dispatch entry above it has the gate, and the fix is one line.

On the tests: the pytest additions are good — they pin scope/pagination/redaction ("/private/project" not in json.dumps(payload), evidence stripped), 404 on unknown goal, and 400-before-read validation, and the smoke pins the response contract plus rejection paths. What they cannot pin (by construction, since list_goal_todos is monkeypatched) is cross-endpoint scope and ordering consistency, which is exactly why finding 1 is worth deciding before merge rather than after.

Suggested resolution: findings 2 (paging consistency, or at least explicit scope labeling for the count) and 3 (origin gate) are cheap and worth fixing inside this PR; finding 1 is an architecture call for maintainers — merging as a stepping stone is fine if the convergence end state is agreed, but the duplicate module name should not survive it.

Signed-off-by: huangruiteng <huangrt01@163.com>
@huangruiteng

Copy link
Copy Markdown
Collaborator

Reviewed and refined on the author's original fork branch at a8e6eca4564f801a383f28d8761f57901bbef754, against main@bf217e1e01bec79f357c9ecbd580cf2dfa73db8b.

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:

  • npm run build:chat and npm run smoke:personal-workspace-packaged passed. Browser coverage includes desktop/mobile Workspace paths, 4,087 history rows, bounded List DOM, and retained snapshot/count across view switching without another history query.
  • Real temporary Markdown through the production HTTP handler plus CORS/snapshot tests: 11 passed; text, paths and evidence remain intact and no state file is written.
  • Ruff on affected tests, Python compilation, drawer/theme/goal-order/task-scroll contracts and diff hygiene passed.
  • loopx canary premerge --from-git-diff --goal-id loopx-meta: passed, 4 direct checks plus 15/15 selected checks, 0 failures, 0 skips, 0 manual holds. The set covers maintainability, Todo/read-model projections, frontstage fixtures, documentation/content workflows and exact-path public-boundary scanning (clean). These supplement the focused backend and packaged-browser tests rather than standing in for UI coverage.
  • Strict exact-scope quality receipt cqr_43a992b6cc35d7e2e9b4: valid; fingerprint 43a992b6cc35d7e2e9b4c59c3434d2ccf7cd221c2fcf09cfeef4c0b7ff369003, 24 changed paths. Safe fix allowed/applied in one bounded pass; 0 blockers/warnings/advisories.

Resolved validation attempts: the added evidence fixture initially used a legacy inline marker rather than canonical metadata; corrected to loopx:todo metadata and reran successfully. The initial premerge policy hold was an absent exact-scope receipt, now recorded and verified. The production build retains the existing non-blocking large-chunk warning. No required validation is skipped; live Agent execution, production writes and exhaustive device/theme acceptance are not claimed.

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.

@huangruiteng
huangruiteng merged commit bb71b38 into loopx-project:main Sep 6, 2026
14 checks passed
@huangruiteng

Copy link
Copy Markdown
Collaborator

Self-merged with owner authorization after all new-head validation checks passed. Final reviewed head: a8e6eca4564f801a383f28d8761f57901bbef754; squash merge: bb71b386254540926489cd6c61bcc6a6308accef.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants