Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions docs/product/use-cases/steward/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ fact the workspace surfaces render, not a claim about a live Goal.
| 1 | Looks at the first screen | Goal board lanes (needs you / running / observing / scheduled), each Goal card naming its Agent and its next sentence | Proven |
| 2 | Asks the steward in the Goal conversation | The steward's bounded prompt row (`询问下一步` / `向 Agent 获取进度报告` / `配置定时检查` / `看阻塞` / `查证据`) sends its message on click and leaves no draft, the ask becomes an accepted Turn, and the admitted team plan card lands in the same conversation | Proven |
| 3 | Reads the card | Per lane: the Agent, the first bounded Todo with priority and action kind, the acceptance signal, and an explicitly unstaffed lane that keeps the work it did not staff; the quota envelope and stop condition; a statement that confirming is what creates the lanes | Proven |
| 4 | Confirms | Exactly one apply and one durable write; the card reports that LoopX state will refresh | Proven, but see gap 1 |
| 5 | Checks who can actually work | — | Gap 2 |
| 6 | Corrects or pauses one lane | — | Gap 3 |
| 7 | Waits for a lane to fail and asks who fixes it / judges completion | — | Gaps 4, 5 |
| 4 | Confirms | Exactly one apply and one durable write; the result names each assigned lane and each item left unstaffed with its reason, and says the execution progress is not what this result claims | Proven |
| 5 | Checks who can actually work | — | Gap 1 |
| 6 | Corrects or pauses one lane | — | Gap 2 |
| 7 | Waits for a lane to fail and asks who fixes it / judges completion | — | Gaps 3, 4 |

Beats 5–7 are recorded by the scenario as typed gaps with the probe that looked
for them. They are not "not implemented here" hand-waving: the scenario names
Expand Down Expand Up @@ -74,11 +74,18 @@ gitignored. No live Goal, Agent, credential or local path is read or captured.

| # | Gap | Evidence the scenario recorded | Owner surface |
| --- | --- | --- | --- |
| 1 | A confirmed plan does not distinguish committed / partial / all-gap / stale / rejected per lane | probe: the only outcome sentence is the generic applied notice | steward plan commit (roadmap R1 remainder) |
| 2 | No per-lane readiness ladder (registered → bound → launchable → executing) | probe: no lane-readiness element or phrase | steward readiness (roadmap R2 / audit F6) |
| 3 | No lane-level correction (pause or supersede a confirmed commitment) | probe: no lane-correction element; only run steering exists | shared alignment (roadmap R4) |
| 4 | A failed lane does not name its blocker owner and next step | probe: no lane-blocker element or phrase | recovery/continuation (roadmap R3) |
| 5 | Completion is not judged by the lane's returned result | probe: no lane-return element or phrase | return delivery (roadmap R3) |
| 1 | No per-lane readiness ladder (registered → bound → launchable → executing) | probe: no lane-readiness element or phrase | steward readiness (roadmap R2 / audit F6) |
| 2 | No lane-level correction (pause or supersede a confirmed commitment) | probe: no lane-correction element; only run steering exists | shared alignment (roadmap R4) |
| 3 | A failed lane does not name its blocker owner and next step | probe: no lane-blocker element or phrase | recovery/continuation (roadmap R3) |
| 4 | Completion is not judged by the lane's returned result | probe: no lane-return element or phrase | return delivery (roadmap R3) |

The confirm result used to be gap 1, recorded as "the only outcome sentence is
the generic applied notice". A confirmed plan now reports which work it
assigned, to whom, and what it left unstaffed and why, so the scenario asserts
that contract instead of treating it as a gap: the result must name every
assigned lane and every unstaffed item with its reason. What it still does not
claim is execution — the surface says the assignment is recorded and points at
the Goal for progress, which is why beat 5 remains gap 1 rather than proven.

The steward prompt row used to be gap 1. The scenario now asserts it instead of
probing it: the row must expose the five shipped labels, clicking `看阻塞` must
Expand Down
22 changes: 13 additions & 9 deletions docs/product/use-cases/steward/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
| 1 | 看首屏 | Goal 看板四条 lane(需要你/执行中/观察中/已安排),每张 Goal 卡给出 Agent 与下一步那句话 | 已证明 |
| 2 | 在 Goal 对话里向管家提要求 | 管家的有界快捷提示行(询问下一步 / 向 Agent 获取进度报告 / 配置定时检查 / 看阻塞 / 查证据)点击即发送、不留草稿,这句话成为一个被接受的 Turn,被准入的团队计划卡落在同一个对话里 | 已证明 |
| 3 | 阅读计划卡 | 每条 lane 的 Agent、第一刀 Todo(含优先级与 action kind)、验收信号,以及一条明确“未配齐”并保留未派工工作的 lane;配额包络与停止条件;以及“确认才会建 lane”的说明 | 已证明 |
| 4 | 确认 | 恰好一次 apply、一次 durable write;卡片提示 LoopX 状态将刷新 | 已证明,但见缺口 1 |
| 5 | 检查到底谁能干活 | — | 缺口 2 |
| 6 | 暂停或撤销某条 lane | — | 缺口 3 |
| 7 | 等某条 lane 失败,问谁负责修 / 用什么判定完成 | — | 缺口 4、5 |
| 4 | 确认 | 恰好一次 apply、一次 durable write;结果逐条点名已分配的工作与落在哪条 lane,以及哪些没派出去、原因是什么,并说明这份结果不主张执行进度 | 已证明 |
| 5 | 检查到底谁能干活 | — | 缺口 1 |
| 6 | 暂停或撤销某条 lane | — | 缺口 2 |
| 7 | 等某条 lane 失败,问谁负责修 / 用什么判定完成 | — | 缺口 3、4 |

第 5–7 拍由场景以 typed gap 记录,并带上“探针找过什么”的证据:不是“这里先不做”的一句话,而是列出了查找的选择器和文本、以及实际找到什么。

Expand Down Expand Up @@ -52,11 +52,15 @@ LOOPX_PERSONAL_WORKSPACE_SCENARIO=steward-journey \

| # | 缺口 | 场景记录的证据 | 归属面 |
| --- | --- | --- | --- |
| 1 | 确认后不区分 committed / partial / all-gap / stale / rejected | 探针:只有一条通用的“已应用”提示 | 管家计划落地(roadmap R1 剩余项) |
| 2 | 没有 per-lane readiness 阶梯(registered → bound → launchable → executing) | 探针:无 lane-readiness 元素或文本 | 管家 readiness(roadmap R2 / 审计 F6) |
| 3 | 没有 lane 级纠偏(暂停或撤销已确认承诺) | 探针:无 lane-correction 元素;只有运行中 Turn 的纠偏 | shared alignment(roadmap R4) |
| 4 | lane 失败后不说明阻塞归属与下一步 | 探针:无 lane-blocker 元素或文本 | 恢复与继续(roadmap R3) |
| 5 | 不用 lane 的回传结果判定完成 | 探针:无 lane-return 元素或文本 | 交付回收(roadmap R3) |
| 1 | 没有 per-lane readiness 阶梯(registered → bound → launchable → executing) | 探针:无 lane-readiness 元素或文本 | 管家 readiness(roadmap R2 / 审计 F6) |
| 2 | 没有 lane 级纠偏(暂停或撤销已确认承诺) | 探针:无 lane-correction 元素;只有运行中 Turn 的纠偏 | shared alignment(roadmap R4) |
| 3 | lane 失败后不说明阻塞归属与下一步 | 探针:无 lane-blocker 元素或文本 | 恢复与继续(roadmap R3) |
| 4 | 不用 lane 的回传结果判定完成 | 探针:无 lane-return 元素或文本 | 交付回收(roadmap R3) |

确认结果原为缺口 1,当时的证据是“只有一条通用的『已应用』提示”。现在确认后的结果会
说明它分配了哪些工作、分给谁、哪些没有派出去以及原因,因此场景改为对它做断言而不是记为
缺口:结果必须点名每一条已分配的 lane 与每一项未派工的工作及其原因。它仍然不主张执行——
界面说的是“分配已记录,执行进度请查看目标”,所以第 5 拍仍是缺口 1,而不是已证明。

管家快捷提示行原为缺口 1。现在场景对它做断言而不是探针:提示行必须出现五个已发布标签,
点击“看阻塞”必须把消息作为一次被接受的 Turn 发出,输入区必须保持为空。回归时失败信息
Expand Down
28 changes: 26 additions & 2 deletions examples/personal-workspace-browser/steward-journey.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
// Everything here is synthetic: the fixture substitutes the agent turn, and no
// live workspace, Goal, agent or local path is read or captured.

import { writeFile } from "node:fs/promises";
import { readFile, writeFile } from "node:fs/promises";
import { resolve } from "node:path";

import { outputDir, packaged } from "./fixture.mjs";
import { outputDir, packaged, repoRoot } from "./fixture.mjs";
import { openWorkspacePage } from "./scenario-context.mjs";

const GOAL_ID = "product-release";
Expand Down Expand Up @@ -345,6 +345,30 @@ export const stewardJourneyScenario = {
console.log(
`steward-journey mode=${servedMode} beats=${beats.length} gaps=${gapBeats.join(",") || "none"}`,
);

// The product case is the owner-facing reading of this run, and it drifted
// silently once already: a lane delivered the confirm result while the
// table still called it a gap. The tables are checked here rather than in
// review because the run is the only thing that knows which gaps are open.
for (const [caseDoc, gapHeading] of [
["docs/product/use-cases/steward/README.md", "Recorded Gaps And Owners"],
["docs/product/use-cases/steward/README.zh-CN.md", "已记录缺口与归属"],
]) {
const text = await readFile(resolve(repoRoot, caseDoc), "utf8");
const headingAt = text.indexOf(`## ${gapHeading}`);
check(headingAt >= 0, `${caseDoc} keeps its gap section heading`);
const section = headingAt < 0 ? "" : text.slice(headingAt).split(/\n## /)[0];
const gapRows = section.split("\n").filter((line) => /^\| \d+ \| /.test(line));
check(
gapRows.length === gapBeats.length,
`${caseDoc} lists ${gapRows.length} gaps but this run recorded ${gapBeats.length} (${gapBeats.join(", ") || "none"})`,
);
check(
!text.includes("the only outcome sentence is the generic applied notice")
&& !text.includes("只有一条通用的“已应用”提示"),
`${caseDoc} still keeps the confirm-result gap this run proves present`,
);
}
} finally {
await context.close();
}
Expand Down
Loading