pstack Feature Verification Map 实战指南:为 Agent 冷启动编写可执行的用户视角验证地图
【免费下载链接】pluginsCursor plugin specification and official plugins项目地址: https://gitcode.com/GitHub_Trending/plugins125/plugins
导读
本文以 pstack 插件中create-verification-skill技能自带的 Feature Verification Map(特性验证地图)示例为骨架,完整讲解"如何为你的应用编写一份让 AI Agent 能够冷启动驱动的验证地图"。你将掌握验证地图的目录组织方式、基线前置条件(Baseline Preconditions)、驱动约定(Driving Conventions)、证据与跳过报告标准(Proof and Skip Reporting),以及每个特性文件的四段式结构约定(Feature Entry Contract),并看到 Notes 应用完整的create-note.md与search.md实战示例。读完即可为自己的项目生成同规格的验证地图。
1. 什么是 Feature Verification Map,它解决什么问题
在 pstack 的工程理念里,"可验证"是交付的前提,对应其核心原则之一prove-it-works(对真实产物验证,而不是对代理结果或"能编译"自欺欺人)。但"验证"存在一个现实矛盾:验证脚本往往写得像写给人类的测试文档,充满了 CSS 选择器、坐标和隐式假设,一旦换一个从未见过该应用的 Agent 来执行,就会在中途迷路。
Feature Verification Map 正是为此而生的产物。它由create-verification-skill技能在生成项目本地验证技能时同步创建,其定位是:
This directory is the maintained source for verifying the user-facing behavior of Notes. Read the index before driving the app, then use the matching feature file as the recipe.
也就是说,验证地图目录是"验证用户可见行为的权威维护源"。先读索引(README),再按匹配的特性文件当作菜谱执行。它刻意把实现细节排除在外部("Keep implementation details out of the map"),只保留用户路径、稳定句柄、所需状态、命令和可观察证据——因为它的读者不是人类 QA,而是任务中途、对应用一无所知、需要"冷读取"的 Agent。
参考示例位于 pstack/skills/create-verification-skill/references/feature-map-example/,共三个文件:
README.md—— 地图索引与全局约定(本文主题);create-note.md—— 特性"创建笔记"的完整验证配方;search.md—— 特性"搜索笔记"的完整验证配方。
在create-verification-skill的 SKILL.md 中,生成流程第 3 步"Seed the feature map"明确要求:创建.cursor/skills/verify-<app>/features/README.md加每个用户可见特性一个文件(起步建议 3~5 个),并严格遵循本示例目录的形状。而maintain-verification-skill则把这份地图当作后续维护的唯一事实源——应用一变,地图就会"腐烂",需要定期以特性为单位做源审查 + 一次实机驱动的校正循环。
2. 地图索引 README:全局约定的四大部分
feature-map-example/README.md把全局规则收敛为四个部分,任何特性文件都必须在这份框架内书写。
2.1 Baseline preconditions(基线前置条件)
每次验证运行都必须从同一个可复现的起点出发,否则证据不可比、并发会互相污染。示例给出的基线是:
- 在
http://127.0.0.1:4173启动 Notes,并使用一次性数据目录; - 设置
NOTES_DATA_DIR=/tmp/notes-verify-$RUN_ID,使并发运行之间不共享状态; - 预置标题为
Quarterly plan和Grocery list的种子笔记; - 把
control-notes与notesCLI 加入PATH; - 运行
control-notes doctor,要求返回预期的 URL、数据目录和构建版本号; - 绝不去驱动一个不是本次验证运行启动的实例。
这里有几个值得注意的设计意图:
- 状态隔离是第一优先。
$RUN_ID式的临时目录保证多次并行验证互不踩踏,对应 pstack 原则里的 make-operations-idempotent(无论先前部分运行如何,都收敛到同一终态)。 - doctor 是入场安检。在
create-verification-skill生成的技能结构中,Doctor 是唯一一个只读检查,回答"这个实例值不值得驱动"——进程在不在、版本/构建对不对、端口是否归我们、鉴权是否有效。基线前置条件要求先跑 doctor,正是把这条规则下沉为每次运行的强制第一步。 - "只驱动自己启动的实例"是硬性安全线。从源码结构看,这与
control-ui的 guardrails(不要使用来自其他仓库的硬编码选择器/端口)以及maintain-verification-skill中"绝不在未健康检查的实例上驱动"的不变量一脉相承。
2.2 Driving conventions(驱动约定)
约定是让所有特性文件保持风格统一、让 Agent 少踩坑的通用规矩:
- 除非某个配方的前置条件另有说明,否则每个配方都从基线状态开始;
- 优先使用 ARIA role 与可访问名称,而不是 CSS 选择器或 DOM 位置;
- 把每条命令当作字面量对待,引号内的名字与 flag 保持不变;
- 浏览器操作一律通过
control-notes browser执行; - 终端操作一律通过
control-notes cli -- <command>执行; - 变更数据后要恢复种子数据;清理时不得移除证明产物(proof artifacts)。
"优先 ARIA role 与可访问名称"这一点,与control-ui的 Setup Pattern 第 6 条完全一致:"Prefer accessibility roles, labels, and stabledata-*selectors over coordinates."。坐标点击只在紧接截图之后才被允许(control-ui的 Guardrails 第 2 条),而本地图直接将其排除在默认约定之外,因为坐标与 DOM 位置在应用改版后最容易漂移,ARIA 名称则相对稳定。
2.3 Proof and skip reporting(证据与跳过报告)
证据标准决定了"验证过"到底意味着什么。README 给出的五条标准值得逐条展开:
- 捕获用户动作与结果状态,而不只是最终画面。例如"点击保存后出现
Note saved状态",动作与状态缺一不可; - UI 证据包括一份 ARIA 快照 + 一张能看到应用身份的截图;
- CLI 证据包括命令、stdout、stderr 与退出码;
- 变更(mutation)证据要求用第二个只读视图复核存储后的值——防止"看起来保存了"其实是假象;
- 每个证明产物都要记录对应的特性 ID 与入口点。
关于跳过(skip)报告,README 立了两条非常严格的红线:
- 无法到达某条路径时,要报告"尝试过的命令 + 未满足的前置条件",而不是含糊地说"这功能测不了";
- 不得把某个入口点通过另一条路径验证过就当作它被验证了。
这两条红线与maintain-verification-skill的 Live pass 不变量呼应:无法到达的特性只有在给出具体前置条件(鉴权、权限、OS、外部状态)与尝试过的路径时,才能标记为verified-unreachable。证据文化也直接对应 pstack 的 show-me-your-work 思路——可审查的决策痕迹。
2.4 Feature entry contract(特性文件契约)
这是整个地图格式的核心约束。每个特性文件必须:
- 以H1 标题 + 一段描述用户可见行为的段落开头;
- 之后恰好四个 H2 小节,顺序固定:
| H2 小节 | 内容 |
|---|---|
Sub-features | 列出简短 ID,每个行为一行 |
How to get to it (user POV) | 列出该特性的每一个用户入口点 |
Driving it with <harness> | 以Preconditions:开头,用带标签的 bullet 把每个用户动作与精确命令、可观察结果配对 |
Gotchas | 列出会浪费或使一次验证运行失效的陷阱 |
最后再次强调:"Keep implementation details out of the map. Name only user paths, stable handles, required state, commands, and observable proof."(把实现细节排除在地图之外,只点名用户路径、稳定句柄、所需状态、命令与可观察证据)。
这个四段式契约的价值在于:H1+H2 结构是机器可解析的。Agent 读取特性文件时能确定性地知道"行为是什么 → 用户怎么到达 → 我怎么驱动 → 什么会坑我",这正是"让一个从未见过应用的 Agent 冷启动执行"成为可能的结构基础。
3. 特性文件实战剖析一:Create a note
create-note.md演示了一个同时覆盖浏览器与 CLI、含取消与持久化确认的完整特性配方。
3.1 Sub-features 与用户入口点
四个子特性 ID 精确描述了行为切片:
create-open—— 从每个浏览器入口点打开空白编辑器;create-save—— 持久化标题与正文;create-cancel—— 丢弃未完成的浏览器草稿;create-cli—— 从终端创建同样形状的笔记。
用户视角的入口点(POV)则列出三种到达方式:
- 点击浏览器工具栏的
New note按钮; - 焦点不在可编辑字段内时按
n键; - 终端运行
notes create --title <title> --body <body>。
注意"焦点不在可编辑字段内"这个限定——它直接预告了 Gotchas 中的第一条陷阱。
3.2 Driving it with control-notes:动作 × 命令 × 可观察结果
配方以Preconditions:开头,声明三条:Notes 健康运行在http://127.0.0.1:4173;不存在标题为Release checklist的笔记;control-notes doctor报告预期的 URL 与一次性数据目录。
随后每个带标签的 bullet 都是"用户动作 → harness 命令 → 可观察结果"三元组:
- Open editor:
control-notes browser click --role button --name "New note"→ 出现名为Note editor的表单,焦点在Title文本框; - Enter content:
control-notes browser fill --role textbox --name "Title" --value "Release checklist"与--role textbox --name "Body" --value "Tag and publish"→Save note按钮变为可用; - Save note:
control-notes browser click --role button --name "Save note"→ 出现Note saved状态,标题读作Release checklist; - Confirm persistence:依次
control-notes browser click --role link --name "All notes"和--role link --name "Release checklist"重开笔记 → 编辑器显示两个已保存的值; - Cancel draft:开新笔记、填入
Discard me、点Cancel→ 笔记列表返回且没有Discard me链接; - CLI entry:
control-notes cli -- notes create --title "CLI note" --body "Created from terminal" --format json→ 退出码0,stdout 含新笔记 ID 与标题; - Proof:重开两篇已保存笔记后,
control-notes browser snapshot --aria --path artifacts/create-note/list.aria.txt与control-notes browser screenshot --path artifacts/create-note/list.png,产物中同时出现Release checklist与CLI note。
这段配方是 README 全部约定的一次完整示范:ARIA role+name 定位、动作与状态成对捕获、CLI 用--format json保证可稳定断言、证据落到artifacts/下的命名位置。
3.3 Gotchas:让验证运行免于浪费
- 文本框聚焦时按
n会输入字符而不是打开新编辑器; - 保存时标题会被 trim,断言应针对渲染后的标题而非草稿输入值;
- 仅凭保存状态不足以证明持久化,必须从列表重开笔记复核;
- 夹具清理时要移除
Release checklist与CLI note,但保留它们的证明产物。
第 3 条直接呼应 README 的 mutation proof 规则——"变更证据要求第二个只读视图"。这也与maintain-verification-skill中"证据必须在清理后仍存在于指定位置"的不变量一致。
4. 特性文件实战剖析二:Search notes
search.md展示了一个更复杂的交互型特性:标题/正文匹配、空态与清除态、键盘入口、CLI 双路径验证。
4.1 六个子特性与入口点
search-open—— 从每个支持的浏览器入口点打开搜索;search-match—— 返回标题与正文匹配且不改动笔记数据;search-open-result—— 在笔记编辑器中打开搜索结果;search-empty—— 对无匹配查询展示完整空态;search-clear—— 移除查询并恢复最近笔记视图;search-cli—— 终端返回同样的匹配结果。
入口点:工具栏Search按钮;焦点不在可编辑字段时按/;终端notes search <query>。
4.2 驱动配方的可观察结果设计
- 工具栏入口与键盘入口均断言"对话框
Search notes出现且焦点在搜索框",键盘入口额外断言页面没有插入斜杠字符; - 标题匹配:填入
quarterly→Search results含Quarterly plan且不含Grocery list(双向断言,匹配与排除同时验证); - 正文匹配:换成
budget→Quarterly plan仍可见且带正文匹配摘录; - 打开结果:点击
Quarterly plan→ 对话框关闭、编辑器标题读作Quarterly plan; - 空态:填入
volcano→ 搜索完成后出现No matching notes状态(注意"搜索完成后",即等待具体状态而非固定 sleep); - 清除:点
Clear search→ 搜索框为空,Recent notes区域替换结果列表; - CLI 命中与未命中:
notes search "quarterly" --format json返回含Quarterly plan的对象数组,notes search "volcano" --format json退出码0且 stdout 为[]——未命中也要证明,且用空数组表达; - Proof:快照与截图落盘到
artifacts/search/results.*,两个产物都要能识别出 Notes、查询词与Quarterly plan。
4.3 Gotchas:交互特性的专属陷阱
- 编辑器或搜索框聚焦时按
/会插入文本而不是打开搜索; - 结果有防抖延迟,应等待结果列表或空态出现,而不是固定 sleep(与
control-cli的"prefer deterministic waits over sleeps"完全同源); - 归档笔记默认被排除,除非用户启用
Include archived; - CLI 默认输出是给人看的格式,稳定断言必须用
--format json; - 打开结果会改变浏览器状态,证明下一个查询前要重新打开搜索——这是状态管理层面的关键提醒。
5. 地图如何融入验证技能的生成与维护闭环
Feature Verification Map 不是孤立文档,而是create-verification-skill产出的项目本地验证技能(.cursor/skills/verify-<app>/)的第三个组成部分。生成流程共五步:
- Interview the repo, not the user——从代码库回答 Surface / Run / Drive / Observe / Isolate 五个问题,优先复用仓库已有的 Playwright/Cypress/expect 脚本等 harness;
- Generate the skill——写出带 YAML frontmatter 的
SKILL.md,包含 Launch、Doctor、Drive、Evidence、Cleanup、Helpers 六个章节; - Seed the feature map——即本文主角,创建
features/README.md加每个特性一个文件,遵循示例目录的形状; - Prove the generated skill before handing it over——端到端跑一遍自己的指令:launch → doctor → 驱动一个已映射特性 → 捕获证据 → 清理,并确认清理后证据仍在命名位置;
- Offer the maintenance loop——把维护职责交给
/maintain-verification-skill。
而maintain-verification-skill则定义了这个地图的保质期管理:索引卫生(修正缺失/多余/重复/失效条目)、按特性并行的只读源审查、配方合并与漂移抽查、一次必须执行的实机 Live pass(每特性至少驱动一次,全程守住"不驱动未健康检查的实例 / 证据在清理后仍存在 / 驱动启动的东西不超出其用途寿命"三条不变量),最后以 clean / changed / blocked 三种结果之一收尾。核心取舍是:地图描述的某个行为应用不再有了,要么是文档漂移(修地图),要么是产品回归(上报,不在文档里掩盖)——这正是验证地图作为"维护的事实源"应有的地位。
6. 编写你自己的 Feature Verification Map:可复用的检查清单
综合上述内容,为任何项目(Web UI、CLI、Electron、纯服务)编写验证地图时,可按以下清单自检:
目录层(README)
- 是否声明了可复现的基线:端口/URL、一次性数据目录、种子数据、PATH 依赖、doctor 校验项?
- 是否用
$RUN_ID式隔离保证并发运行不共享状态? - 是否明确"只驱动本次运行启动的实例"?
- 是否写明驱动约定:ARIA role/name 优先、命令字面量、浏览器与 CLI 的入口封装?
- 是否写明证据标准:UI(ARIA 快照 + 带身份截图)、CLI(命令/stdout/stderr/退出码)、mutation(第二个只读视图)、每个产物带特性 ID 与入口点?
- 是否写明跳过报告规则:不可达路径要报命令与未满足前置条件,禁止用另一条路径冒充验证?
- 特性清单是否链接到同目录的各个特性文件?
特性文件层(每个 feature)
- H1 + 一段用户可见行为描述?
- 恰好四个 H2:
Sub-features/How to get to it (user POV)/Driving it with <harness>/Gotchas,顺序固定? Sub-features是否每行一个短 ID、一个行为?- 入口点是否覆盖用户能触达的所有方式(工具栏、快捷键、CLI)?
- 驱动段是否以
Preconditions:开头,每个动作都是"用户动作 → 精确命令 → 可观察结果"? - 是否同时验证"出现什么"和"不出现什么"(双向断言)?
- 是否包含持久化/副作用复核(重开、第二个视图)?
Gotchas是否覆盖焦点状态、格式差异(如 trim)、防抖等待、默认输出格式、状态污染?- 是否把实现细节排除在地图之外?
7. 结语
Feature Verification Map 把"验证"从一次性的临时脚本升级为可持续维护的工程资产。它的核心洞察是:验证配方的读者是冷的——一个任务中途、从未见过应用的 Agent。因此地图必须以机器可解析的结构(固定四段式契约)、用户视角的命名(ARIA role 与可访问名称)、字面量级精确的命令、以及"动作+状态成对"的证据标准来书写。结合 create-verification-skill 的生成流程与 maintain-verification-skill 的维护闭环,这套方法适用于任何语言、框架和平台——从 Web UI 到 CLI/TUI 再到纯服务。本文给出的 Notes 双特性示例(create-note.md、search.md)可作为直接对照的模板;对应的驱动 harness 约定可进一步参考 control-ui 与 control-cli。
【免费下载链接】pluginsCursor plugin specification and official plugins项目地址: https://gitcode.com/GitHub_Trending/plugins125/plugins
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考