test(steward): assert the shipped steward prompt row and close journey gap 1 - #4616
Conversation
…ng it The steward journey recorded its prompt row as gap 1 because the bounded prompt set lived in the client model while the conversation could not reach it. PR #4591 shipped the row, so the scenario now asserts the shipped contract: the row must expose the five released labels, clicking 看阻塞 must post its message as an accepted Turn, and the composer must stay empty afterwards. A regression fails with the missing labels named, so it cannot pass silently. Both locale mirrors renumber the remaining gaps and record why the prompt row is no longer one of them. Validation: steward-journey in development and packaged modes; the negative control (row buttons removed) fails naming 看阻塞 / 查证据 as missing; the full seven-scenario personal-workspace development suite; workspace-theme contract; docs-governance-smoke; docs-asset-integrity-smoke; loopx check --scan-path over the three changed files (errors=0, boundary scan clean). Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…ip-20260917 Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
动机
owner 已在 Goal 对话里回复 ,接受 PR #4591 在 head 18153f5 上交付的管家快捷提示行(预览树与当时的 head 是同一份 revision),#4591 以 aae37d90a 合入。于是本车道记录的 journey gap 1——“管家有界提示集只活在客户端模型里、对话里点不到”——在事实层面已经关闭,但用例本身还停在旧结论上:场景仍然对提示行做探针,缺口表里仍然列着它。
这个 PR 就是把用例追平产品:拍子 2 从“探针”升格为“断言”,缺口 1 从缺口表移除,中英两份镜像同步重编号。不这样做的话,用例会在产品已经做到之后继续宣称这是缺口,这正是「状态真相落在事实之后」的那类漂移。
改动思路
断言的写法选择了“正向列出已发布契约 + 反向可证伪”两条腿,而不是只断言存在某个元素:
- 提示行必须暴露五个已发布标签(询问下一步 / 向 Agent 获取进度报告 / 配置定时检查 / 看阻塞 / 查证据),缺哪个就在失败信息里点名哪个;
- 点击“看阻塞”必须把它的消息作为一次被接受的 Turn 发出(复用的是场景已有的 fixture turn 记录,不是新增替身);
- 点击后输入区必须保持为空——这正是 owner 要的“点击即发送、不留草稿”。
第 3 条是 owner 那条“全都改成立即发送”的要求在用例里的落点,所以它必须被断言而不是被描述。断言在 owner 输入之前读取,避免把 owner 自己敲的字误认成渲染出来的控件。
具体改动
examples/personal-workspace-browser/steward-journey.mjs:拍子 2 的probe换成check,新增五标签断言、看阻塞点击断言(等待 fixture 里出现对应 turn)、以及composer.inputValue() === ""断言;record("2-steward-prompts", …)记录实际读到的标签、是否发出 turn、点击后的输入区内容,失败时这些值直接出现在报告里。docs/product/use-cases/steward/README.md与README.zh-CN.md:拍子 2 的描述补上快捷提示行与“点击即发送、不留草稿”;缺口表删除原缺口 1 并把其余缺口重编号为 1–5;正文里指向缺口编号的交叉引用同步改正;新增一段说明提示行“从缺口变为断言”的原因与失败行为。- 没有改任何产品代码:
apps/presentation/dashboard与loopx/web/chat在本 PR 里逐字节未动。
对主干的风险
需要说清楚的是这次断言到底证明了什么、没证明什么。它证明的是这一版包装好的前端在确定性 fixture 上确实渲染了这五个 chip、点击确实发出一次被接受的 Turn、且不留草稿;它不证明真实 Goal 上的语义正确性,也不覆盖 Lark 受众与 remote/cloud host —— 这两条仍然如实留在用例的“未测”清单里。
断言会不会只是变绿?我把两个 chip(看阻塞、查证据)从 personal-workspace-page.tsx 摘掉后重跑:场景失败并点名 missing: 看阻塞 / 查证据,同时报出 chip 与 Turn 两条断言;恢复文件后回到绿色。这条负向对照说明新断言无法在提示行缺失时静默通过。开发态与打包态(LOOPX_PERSONAL_WORKSPACE_PACKAGED=1,从已发布的 /chat/ bundle 提供)都跑到 beats=4,gap 列表里已经没有 2-steward-prompts。
顺带修掉一个仓库级红:本 PR 第一次 check 跑的 test-shard (2) 失败在 tests/canary/test_maintainability_ratchet.py(chat_actions.py 1604 行 > ledger 1590)。该失败在干净的 origin/main worktree 上可复现、也是 main 自己的失败(run 35176730888),与本 PR 的 diff 无关,属于所有在飞 PR 的公共阻塞;已由 #4618 结清(3937f6b99),本 head 是 rebase 到含该修复的 main 之后重跑的。
我的整体评价
把“产品已经做到、用例还说是缺口”的漂移收口,并且用负向对照证明断言有牙齿,这是这次改动的主要价值;产品面零改动,回退成本是删一个 commit。建议在该 head 的必过检查全绿的前提下合并。
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Head: 2773256
English verdict: APPROVE
huangruiteng
left a comment
There was a problem hiding this comment.
动机
owner 已在 Goal 对话里回复「确认首屏」,接受 PR #4591 在 head 18153d15c 上交付的管家快捷提示行(预览树与当时的 head 是同一份 revision),#4591 以 aae37d90a 合入。于是本车道记录的 journey gap 1——“管家有界提示集只活在客户端模型里、对话里点不到”——在事实层面已经关闭,但用例本身还停在旧结论上:场景仍然对提示行做探针,缺口表里仍然列着它。
这个 PR 就是把用例追平产品:拍子 2 从“探针”升格为“断言”,缺口 1 从缺口表移除,中英两份镜像同步重编号。不这样做的话,用例会在产品已经做到之后继续宣称这是缺口,这正是「状态真相落在事实之后」的那类漂移。
(更正:本条评审的前一版把 owner 那句话误写成命令替换而丢失,并把 head 误记为 18153f5;同一 head 上以此版为准。)
改动思路
断言的写法选择了“正向列出已发布契约 + 反向可证伪”两条腿,而不是只断言存在某个元素:
- 提示行必须暴露五个已发布标签(询问下一步 / 向 Agent 获取进度报告 / 配置定时检查 / 看阻塞 / 查证据),缺哪个就在失败信息里点名哪个;
- 点击“看阻塞”必须把它的消息作为一次被接受的 Turn 发出(复用的是场景已有的 fixture turn 记录,不是新增替身);
- 点击后输入区必须保持为空——这正是 owner 要的“点击即发送、不留草稿”。
第 3 条是 owner 那条“全都改成立即发送”的要求在用例里的落点,所以它必须被断言而不是被描述。断言在 owner 输入之前读取,避免把 owner 自己敲的字误认成渲染出来的控件。
具体改动
examples/personal-workspace-browser/steward-journey.mjs:拍子 2 的probe换成check,新增五标签断言、看阻塞点击断言(等待 fixture 里出现对应 turn)、以及composer.inputValue() === ""断言;record("2-steward-prompts", …)记录实际读到的标签、是否发出 turn、点击后的输入区内容,失败时这些值直接出现在报告里。docs/product/use-cases/steward/README.md与README.zh-CN.md:拍子 2 的描述补上快捷提示行与“点击即发送、不留草稿”;缺口表删除原缺口 1 并把其余缺口重编号为 1–5;正文里指向缺口编号的交叉引用同步改正;新增一段说明提示行“从缺口变为断言”的原因与失败行为。- 没有改任何产品代码:
apps/presentation/dashboard与loopx/web/chat在本 PR 里逐字节未动。
对主干的风险
需要说清楚的是这次断言到底证明了什么、没证明什么。它证明的是这一版包装好的前端在确定性 fixture 上确实渲染了这五个 chip、点击确实发出一次被接受的 Turn、且不留草稿;它不证明真实 Goal 上的语义正确性,也不覆盖 Lark 受众与 remote/cloud host —— 这两条仍然如实留在用例的“未测”清单里。
断言会不会只是变绿?我把两个 chip(看阻塞、查证据)从 personal-workspace-page.tsx 摘掉后重跑:场景失败并点名 missing: 看阻塞 / 查证据,同时报出 chip 与 Turn 两条断言;恢复文件后回到绿色。这条负向对照说明新断言无法在提示行缺失时静默通过。开发态与打包态(LOOPX_PERSONAL_WORKSPACE_PACKAGED=1,从已发布的 /chat/ bundle 提供)都跑到 beats=4,gap 列表里已经没有 2-steward-prompts。
顺带修掉一个仓库级红:本 PR 第一次 check 跑的 test-shard (2) 失败在 tests/canary/test_maintainability_ratchet.py(chat_actions.py 1604 行 > ledger 1590)。该失败在干净的 origin/main worktree 上可复现、也是 main 自己的失败(run 35176730888),与本 PR 的 diff 无关,属于所有在飞 PR 的公共阻塞;已由 #4618 结清(3937f6b99),本 head 是 rebase 到含该修复的 main 之后重跑的。
我的整体评价
把“产品已经做到、用例还说是缺口”的漂移收口,并且用负向对照证明断言有牙齿,这是这次改动的主要价值;产品面零改动,回退成本是删一个 commit。建议在该 head 的必过检查全绿的前提下合并。
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Head: 2773256
English verdict: APPROVE
Goal And Delivered Outcome
LOOPX_PERSONAL_WORKSPACE_SCENARIO=steward-journeyprintedgaps=2-steward-prompts,3-confirm,…; it now printsgaps=3-confirm,4-readiness,5-correction,6-recovery,7-return, and beat 2 asserts the shipped contract instead of probing for it.main(809f0cf). Related to the steward use-case qualification; no tracking issue.Scope And Continuation
询问下一步/向 Agent 获取进度报告/配置定时检查/看阻塞/查证据), that clicking看阻塞posts its message as an accepted Turn, and that the composer is left empty. A regression throws with the missing labels named rather than passing silently. Both locale mirrors renumber the remaining gaps and record why the prompt row left the list. Gaps 2–5 remain, each still recorded with the probe that looked for the surface.Validation
real_entrypointpassedLOOPX_PERSONAL_WORKSPACE_SCENARIO=steward-journey node examples/personal-workspace-browser-smoke.mjs— development mode ok, beats=4; packaged mode (LOOPX_PERSONAL_WORKSPACE_PACKAGED=1) ok against the shippedloopx/web/chatbundleregression_paritypassed看阻塞/查证据buttons frompersonal-workspace-page.tsxmakes the scenario fail naming both labels plus the chip and Turn assertions; restoring the file returns it to green. The assertion cannot pass on a missing rowintegrationpassednode examples/personal-workspace-browser-smoke.mjs— all seven development scenarios okunitpassedworkspace-theme.test.mjs(ok),npm run smoke:goal-order(order invariants passed)staticpasseddocs-governance-smoke ok;docs-asset-integrity-smoke: ok (6 assets verified);loopx check --scan-pathover the three changed files reportserrors=0andpublic boundary scan clean: 3 filesFrontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
docs/product/use-cases/steward), the frontend-first end-to-end journey for the local steward lane.Shared-authority RFC fixture impact
Boundary Checklist
noneSigned-off-bytrailer中文摘要
loopx/web/chatbundle)的 steward-journey 场景均通过;负向对照(移除两个按钮)会以点名缺失标签的方式失败;七个场景的开发套件、workspace-theme、goal-order、docs-governance-smoke、docs-asset-integrity-smoke 通过;三个改动文件的loopx check --scan-path为 errors=0 且边界扫描干净。