Skip to content

test(dashboard): run the personal-workspace contract suite in both workspace smokes - #4720

Merged
huangruiteng merged 1 commit into
mainfrom
codex/personal-workspace-contract-wiring-20260919
Sep 19, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/personal-workspace-contract-wiring-20260919

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

What this repairs

apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs is a 540-line contract suite (410 assertions) covering the Personal Workspace drawer, model, router, header, task board, run rows and settings surfaces: typed action kinds (todo.update, todo.create, todo.defer), the explicit resume_when defer contract, the "never emit owner_resume" sentinel, i18n keys, run identity across refreshes, and the Goal-scoped settings/status-source switchers.

No npm script and no CI job referenced it. rg -n 'personal-workspace-contract' matched nothing outside the file itself, so the suite could only rot, and nothing could catch the rot. (It is not stale today: it passes on current main.)

What changed

Wired the existing suite into the two Personal Workspace chains that already exist, instead of adding a third entry point:

script where it runs
smoke:personal-workspace local/debug chain (also the browser leg used by examples/dashboard-demo-readiness-smoke.py)
smoke:personal-workspace-packaged the chain CI runs: .github/workflows/frontstage-pages.yml and .github/workflows/python-tests.yml

Both chains already run smoke:goal-order and workspace-theme.test.mjs before their browser leg, so the contract suite now runs in the same step as the other unit-level checks the browser smoke depends on. The diff is two script strings.

Validation

  • node src/features/personal-workspace/personal-workspace-contract.test.mjs → personal workspace drawer contract smoke passed on this head (same run fails nothing on origin/main; the file was simply never invoked).
  • Each leg of the modified chain runs green with the repo's node deps: npm run smoke:goal-order → Goal presentation order invariants passed; node workspace-theme.test.mjs → workspace-theme contract: ok; the newly wired suite → passed.
  • pytest tests/test_python_ci_workflow.py -q → 43 passed (that test pins the presentation job's npm run smoke:personal-workspace-packaged step, which this change keeps).
  • loopx canary premerge --from-git-diff → 0 failures / 0 advisory failures, self_merge_allowed: true.
  • The browser leg (examples/personal-workspace-browser-smoke.mjs) is unchanged; it needs the packaged chat build plus Playwright chromium, so this environment verifies it only through CI (frontstage-pages.yml builds chat, checks packaged assets, installs chromium, then runs the same script).

Boundary

apps/presentation/dashboard/package.json is a shipped application surface, so this is proposed as a PR with an exact-head review and left for the maintainer — never self-merged, even though the change is test-only wiring. No runtime behavior, packaged asset, permission, state or protocol change.

…rkspace smokes

apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs
is a 540-line contract suite for the Personal Workspace drawer, model,
router and settings surfaces, but no npm script and no CI job referenced
it: it could only rot, and until this change nothing could catch it.

The suite passes on current main, so this wires it into the two chains
that already exist instead of adding a third entry point:

- smoke:personal-workspace (the debug/browser chain)
- smoke:personal-workspace-packaged (the chain CI runs in
  frontstage-pages.yml and python-tests.yml)

Both chains already run smoke:goal-order and the workspace-theme contract
before the browser leg, so the source-contract suite now runs in the same
step as the other unit-level checks that the browser smoke depends on.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Reviewed exact head: 4720@21c46f7d6be37cae896e2fdd16daddeb6096e6bc
Review basis: loopx pr-review --state all --review-priority owner-first packet (execution policy_revision: 7), the full local diff, and an exact-origin/main control worktree.

动机

apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-contract.test.mjs 是一条 540 行、410 条断言的 Personal Workspace 契约套件,覆盖 drawer/model/router/header/任务板/run row/设置面:typed action kind(todo.update/todo.create/todo.defer)、显式 resume_when defer 契约、"绝不发出 owner_resume 哨兵"、i18n key、刷新后保持的 run 身份、以及 Goal 级设置与状态源切换器。

它没有被任何 npm script、任何 CI 作业、任何 example 引用(rg -n 'personal-workspace-contract' 只命中文件自身),因此它只能腐烂,且腐烂时没有任何东西能发现。它今天并不陈旧——在 origin/main 上直接运行是通过的;缺的是执行,不是断言。

这是本 lane 的 P2 待办(todo_03f0c05d52ab)明确要求的二选一:要么修好陈旧断言并把它接进 CI 或 canary 真正运行的 smoke,要么退役该文件并把仍成立的契约价值迁入已接入的 smoke。本 PR 选前者,且用最小改动完成。

改动思路

不新增第三处入口,而是把既有套件接入已经存在的两条 Personal Workspace 链:

script 运行位置
smoke:personal-workspace 本地/调试链,也是 examples/dashboard-demo-readiness-smoke.py 调用的 browser 组
smoke:personal-workspace-packaged CI 实际运行的链:.github/workflows/frontstage-pages.yml 与 .github/workflows/python-tests.yml

两条链本来就已经在该位置运行 smoke:goal-order 与 workspace-theme.test.mjs,所以新 leg 插在它们之后、浏览器 leg 之前——与同为"读取 dashboard 源码并断言契约、不需要浏览器"的检查放在同一步。比较过的替代方案(退役该 540 行套件;新建 smoke:personal-workspace-contract 脚本并新增一个 CI step)都会丢失覆盖或增加需要同步的入口。

具体改动

  • apps/presentation/dashboard/package.json(+2/−2):smoke:personal-workspace 与 smoke:personal-workspace-packaged 各插入 node src/features/personal-workspace/personal-workspace-contract.test.mjs &&。脚本名、既有 leg、浏览器 leg 的环境变量与顺序语义均未改变。

关键内容讲解

&& 链是短路布尔序列:任一 leg 非零退出即整链失败,因此新增 leg 的效果是让契约回退成为硬失败,并且失败发生在浏览器 leg 之前——与同链内 goal-order、workspace-theme 的定位一致。smoke:personal-workspace-packaged 是 CI 的必过文本(tests/test_python_ci_workflow.py:193 断言 presentation 作业包含该 npm run),所以"接进这条链"等价于"进入 CI 门禁",不需要改 workflow、不需要新增作业,也不需要把本地路径或环境细节写进仓库。选择接入而非退役的理由是覆盖层次不同:已被 CI 执行的 packaged browser smoke 走渲染路径,而这条套件走源码级 typed 契约(action kind 字面量、resume_when、owner_resume 哨兵、run 身份、i18n key),两者并存而非互相替代。

对主干的风险

  • 影响面只有两条脚本字符串;不改源码、资产、lockfile、workflow、Python 或任何运行时/权限/状态/协议行为。
  • 唯一新增失败面就是被接入的套件本身,这正是意图。该文件只读同一 checkout 内的源码文本并断言,不联网、不用凭据、不占端口、不依赖 fixture。
  • 需要提防的真实风险:新增 leg 在 CI 上失败而本地为绿(路径或 node 版本差异)。缓解证据是 CI 使用 node 24、与本机 v24.21.0 一致,且套件只用 node:fs 读自身相对的仓库文件;另一个风险是源码级断言对同语义重构敏感——但这是该文件原本的形态,本 PR 未改动任何断言,因此摩擦不是接入动作引入的。
  • 未在本机验证的维度已如实标注:smoke:personal-workspace-packaged 的完整执行需要 npm run build:chat 与 Playwright chromium(由 CI 的前置步骤提供),本机只运行了同一条链的三个 node leg。

我的整体评价

一个 540 行的契约套件没有任何执行入口,是"看起来有覆盖、实际没有覆盖"的典型形态;本 PR 用两条脚本字符串把它接进本地与 CI 链,让既有断言真正生效,且没有改写任何断言、没有新增入口。diff 1 文件 +2/−2,可完整回滚,属于小而完整的一刀,无阻断性发现(findings: [])。建议合并。

非阻断 P2 建议(不阻挡合并):该套件是源码级文本断言,长期看对同语义重构较敏感;若维护者希望降低重构摩擦,值得另开一刀评估把其中一部分断言替换为渲染/行为级契约,并同时决定是否删除对应的源码级断言——本 PR 刻意不碰这一层。

English verdict: APPROVE - head 21c46f7 wires the existing 540-line personal-workspace contract suite into the two workspace smoke chains (smoke:personal-workspace and the CI-run smoke:personal-workspace-packaged) instead of adding a new entry point; the diff is 1 file +2/-2 with no assertion change and no runtime, asset, workflow or Python change, the newly wired leg plus the other two legs run green locally, pytest tests/test_python_ci_workflow.py is 43 passed, loopx canary premerge reports 0 failures, and the only residual items are the packaged-asset rebuild plus Playwright browser leg (CI-only here) and the source-text nature of the suite, which this PR deliberately leaves untouched.

@huangruiteng
huangruiteng merged commit da6d797 into main Sep 19, 2026
28 checks passed
@huangruiteng
huangruiteng deleted the codex/personal-workspace-contract-wiring-20260919 branch September 19, 2026 02:08
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.

1 participant