diff --git a/plugins/waypoint/CHANGELOG.md b/plugins/waypoint/CHANGELOG.md index 93d2753..739c820 100644 --- a/plugins/waypoint/CHANGELOG.md +++ b/plugins/waypoint/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Fixed + +- `project-navigator`: a criterion you verified by hand is no longer reported as unverified. Once you confirm the result, the orchestrator records the verdict as "用户已确认" (confirmed by the user), which counts as a pass, so you are not asked to "accept as is" for something you just checked. Results you already saw before the review, as in interactive slices, are recorded the same way without asking you to repeat them. +- `project-navigator`: manual verification no longer assumes what you have. The account, character, items, and devices a manual check needs are asked when the plan is written, and the executor prepares steps and materials only from what you confirmed. +- `project-navigator`: slices nobody needs are kept out of the roadmap. Before adding a slice the orchestrator answers what breaks without it and whether there is a cheaper way, and recommends the cheapest option; reverse-engineering and measurement slices are scheduled only when their conclusions are needed and cannot be obtained otherwise. +- `project-navigator`: less repetition. The executor's own report is no longer relayed while the review is running, the same steps are written out in full only once, and repeated waiting notices get a one-line reply. +- `project-navigator`: when you interrupt or raise a question after choosing, the orchestrator answers first and does not change state or commit until you confirm again. + ## [2.0.1] - 2026-11-13 ### Fixed diff --git a/plugins/waypoint/agents/navigator-reviewer.md b/plugins/waypoint/agents/navigator-reviewer.md index 8ae1970..f4fb98b 100644 --- a/plugins/waypoint/agents/navigator-reviewer.md +++ b/plugins/waypoint/agents/navigator-reviewer.md @@ -19,7 +19,7 @@ model: sonnet - 只运行这几类命令: 只读 git 命令 (例如 `git diff`, `git log`, `git show`, `git status`), 委派提示词 "验证方式" 中的命令, 以及切片文件 "自测记录" 中的命令. 需要真实账号, 会产生费用或外部副作用的命令不运行. 不安装依赖, 不改动项目. - 委派提示词写明不构建, 不运行程序时, 不构建, 不运行; 只读代码与记录, 靠运行才能确认的标准写 "未验证". - 调用 MCP 取证工具时只做查询类的只读调用, 调用前先用 ToolSearch 加载; 不发请求, 不清空或修改工具中的记录. -- 结论只用委派提示词列出的三种, 不自造 "部分通过" 之类的说法. 找不到证据时写 "未验证", 并写明缺少什么; 不凭推测写 "通过". +- 结论只用委派提示词列出的三种, 不自造 "部分通过" 之类的说法. 找不到证据时写 "未验证", 并写明缺少什么; 不凭推测写 "通过". "用户已确认" 由编排会话在用户确认之后写入, 你不使用. - 接线要自己追: 从程序的真实入口读代码, 一步一步走到新代码, 不照抄实现方写的路径. ## 交回 diff --git a/plugins/waypoint/docs/project-navigator-maintaining.en.md b/plugins/waypoint/docs/project-navigator-maintaining.en.md index e26ecd2..825696e 100644 --- a/plugins/waypoint/docs/project-navigator-maintaining.en.md +++ b/plugins/waypoint/docs/project-navigator-maintaining.en.md @@ -71,14 +71,16 @@ Almost all of the 1.x iterations went into process control (the work order state - **The unit of work is the slice, one file per slice, in three parts.** The Plan is written by the orchestrator session, the Result by the executor, and the Review by the orchestrator session from what the reviewer subagent hands back. 1.x had three files, each with a fixed structure, and the workflow stopped when one could not be parsed. If changed: back to the three-file set. - **Ownership of the parts is ensured by the digest of the Plan part.** The guard can only judge by path and cannot govern parts inside a file. `slice start` records the digest of the Plan part and `slice review` compares it, refusing to submit for review on a mismatch; when the orchestrator session changed it according to the user's answer, run `slice replan`. Before hashing, leading and trailing whitespace on each line is removed, whitespace is collapsed, and table separator rows are normalized: the digest does not change when a formatting hook realigns tables. This can only detect, not prevent; if it turns out to happen often in practice, the fallback is to split the Plan and the Result into two files. -- **The scripts read only what they need.** The completion criteria (a numbered list), whether "需要决定" (needs decision) and "补做" (rework) are "无" (none), "执行状态" (execution status), and the "标准核对" (criteria check) table (the verdict can only be one of three, and every row needs evidence). For the rest they only check "not empty, no placeholder". Structure is not validated on write. If changed: the more structure rules, the more often work gets blocked. +- **The scripts read only what they need.** The completion criteria (a numbered list), whether "需要决定" (needs decision) and "补做" (rework) are "无" (none), "执行状态" (execution status), and the "标准核对" (criteria check) table (the verdict can only be one of the four in the spec, and every row needs evidence). For the rest they only check "not empty, no placeholder". Structure is not validated on write. If changed: the more structure rules, the more often work gets blocked. - **By default the user is asked to decide only once, after the review.** When "需要决定" in the plan is not "无", `slice start` refuses, and after the user answers it is run with `--decided`. 1.x had a fixed two per work order (review of the order and review of the work), and in most order reviews the user simply clicked agree. The pre-issue check was removed too: the orchestrator session can read code anyway, and reads it itself before writing the plan. If changed: every slice gets one more confirmation that carries no information. - **Hard constraints and working assumptions are two layers.** The hard constraints are the goal, the completion criteria, the explicit exclusions, and the module layout; the rest are working assumptions, and when they do not match reality the executor gathers evidence, works to what is actually there, and records it in the "修正" (corrections) table, which the review checks. 1.4.0 and earlier treated the plan as a contract, and a mismatched detail meant one round trip. If changed: exploratory tasks take one more round for every detail discovered. - **The skeleton is a hard rule.** The first slice of the first milestone is marked as the skeleton by the script, and its kind must be `build`; `slice plan` refuses other slices until the skeleton passes; the skeleton cannot be abandoned. If changed: back to "write every layer horizontally first, then find out at the end that they do not connect". - **Wiring is a fixed item of the review.** The executor writes the call path from the entry point to the new code in "接线证据" (wiring evidence), and the reviewer subagent traces it again itself. When it cannot be traced, the related criteria are judged fail, and a slice with a fail can only be reworked or abandoned, so a slice without wiring cannot be followed by the next one. This is ensured jointly by the review prompt and the refusal in `slice accept`; the script itself does not judge wiring. - **Only one slice is worked on at a time.** `current` has only one position. If changed: the base commit and the review scope are no longer well defined. - **Rework happens in the same file.** `slice redo` changes the execution status to "补做中" (rework in progress), adds the round number to the heading of the existing Review part, and appends an empty Review part at the end of the file; when parsing, the last of the parts with the same name is used. If changed: one file per round, or the previous round's review record is lost. -- **Unverified criteria can be explicitly accepted by the user** (`slice accept --accept-unverified`). In projects whose verification mode is `manual` or `measured`, every criterion that can only be confirmed by running will be unverified, and is accepted after the user confirms it by hand. Failed criteria cannot be accepted. +- **A criterion the user verified by hand is recorded as "用户已确认" (confirmed by the user) and treated like a pass.** The reviewer subagent cannot see the user's screen, so it can only judge a criterion that needs running as unverified. Up to 2.0.1 such criteria could only go through "accept as is" (`slice accept --accept-unverified`): the user had looked at the result in the game and said it was fine, yet the reply still said "3 unverified" and made them choose again. Now the orchestrator changes the verdict to "用户已确认" (`verdicts.confirmed` in the spec) once the user confirms, writes the user's words as evidence, and `slice accept` needs no flag. For results the user already saw before review (common in interactive slices), the reviewer's evidence starts with "用户实测: " (`userEvidencePrefix` in the spec) and the orchestrator changes them directly, without asking the user to do it again. "Accept as is" remains only for when the user decides not to verify; failed criteria cannot be accepted. The script does not check whether "用户已确认" is true; this rests on the rules alone. Consequence of changing it: the user finishes verifying and is still told "unverified". +- **Preconditions for manual verification are asked in the plan.** When a criterion needs the user to verify by hand, the account, character, items, and devices it needs go into the plan's "需要决定" (to decide) and are asked before the slice starts; the executor prepares steps and materials only from confirmed preconditions. In a real session the executor built a patch that could only be tested with an item the user did not own, followed by half an hour of looking for substitutes. Consequence of changing it: however clear the steps are, the user cannot perform them. +- **Before adding a slice, ask "what breaks without it" and "is there a cheaper way".** The rule lives in "新增切片之前" in `references/intake.md`; milestone review and requirement changes refer to it, and the cheapest way is the recommended option when asking. In a real session the user mentioned a phenomenon in passing, the orchestrator scheduled a reverse-engineering slice plus an integration slice and recommended the reverse engineering; only after the user had taken four screenshots did it turn out that letting the end user choose was enough. Consequence of changing it: slices nobody needs enter the roadmap, each needing the user's cooperation. - **Committing is not a state.** After `slice accept`, the orchestrator session invokes commit-message as the output instructs, and the state file is committed with that commit. ### Modularity constraints @@ -103,6 +105,8 @@ Almost all of the 1.x iterations went into process control (the work order state - **When the user must decide, the question and options come right after the first paragraph, and the options go in a code block.** The 2.0.0 rules were only "conclusion on the first line, progress on the second, options at the end". Real sessions then showed: the question buried under long background; adjacent "A. B. C." lines merged into one paragraph by Markdown; the progress line glued to the first line; every open doubt listed to the user at once during intake. Line breaks inside a code block are never merged, so options always use a code block. If changed: the user has to read the whole reply to learn what to answer. - **A question box tool must be used when present, but nothing depends on it.** The desktop app offers a question box at the end of a turn (`wrap_up`); the terminal does not. The rule is "when such a tool exists, use it every time, in Simplified Chinese, with buttons matching the options in the code block", and the code block always stays, so replies are complete in both environments. If changed: with the code block alone the desktop app has no buttons to click; with the question box alone the terminal shows no options. - **Progress goes at the end of the reply, and only when it changed.** On the second line it took the most visible spot in every reply, while its content rarely changed. +- **The same content is written out in full only once; the executor's own report is not relayed when submitting for review.** In a real session the executor's reported results were repeated at submission and again after the review, and the same set of manual steps was sent twice in full. +- **When the user interrupts or raises a question after choosing, answer first; no state change and no commit until they confirm again.** In a real session the user chose "accept and commit", interrupted at once with questions, and the orchestrator committed anyway while answering. ### Installation @@ -149,26 +153,26 @@ Modules in `runtime/lib/`: ## Troubleshooting -| Symptom | Root cause | Related files | How to verify | -| ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------ | -| Invoking the skill aborts at once, reporting that a permission check failed | `allowed-tools` was deleted or rewritten | `SKILL.md` | Invoke it once in the default permission mode | -| Running `project-navigator` reports that the command is not found | The plugin is not enabled, or the session started before the plugin was updated; `.cmd` is not found in PowerShell | `bin/` | `claude plugin list` to see whether the plugin is enabled; restart the session | -| Plugin commands or record writes pop up permission prompts | The local configuration lacks the allow rules, or the command does not start with `project-navigator` | `lib/settings.mjs` | The "放行规则" (allow rules) line of `status`; run `init` again | -| On entry, it says the records were written by an old version | The `schema` of the state file is less than 6 | `commands/enter.mjs`, `commands/init.mjs` | Run `init --archive` after the user agrees | -| The orchestrator session is refused when writing files outside `.navigator/` | This is rule 4; when a change is needed, put it into a slice | `lib/guard.mjs` | Drive `hook.mjs` through standard input, with an orchestrator session's transcript | -| A session that is not the orchestrator session is refused when writing business files | This project's orchestrator marker appears in the session transcript, for example because the session once invoked the skill | `lib/orchestrator-marker.mjs` | Search the transcript for "project-navigator 编排标记" (project-navigator orchestrator marker) | -| The executor subagent is refused when writing orchestration records | Under `.navigator/` only the slice file in progress and `findings/` can be written | `lib/guard.mjs` | The refusal reason states the writable scope | -| `slice start` says the Plan part is not filled in | A section is empty or still contains "(待填写" ("(to be filled in"); the completion criteria are not a numbered list | `lib/slice-file.mjs` | `check` lists the sections that are not filled in | -| `slice start` mentions a needed decision | "需要决定" (needs decision) is not a single line "无" (none) | `commands/slice.mjs` | After asking the user and writing in the answer, run with `--decided` | -| The executor subagent has ended, but `status` still says waiting | It did not change "执行状态" (execution status) to "完成" (done), or wrote it in another section | `lib/slice-file.mjs`, `lib/guidance.mjs` | Look at the "执行结果" (Result) part of the slice file; use SendMessage to ask it to fill it in | -| `slice review` says the Plan part was changed | The content of the Plan part changed after work started: the orchestrator session changed it according to the user's answer, or the executor subagent changed it by mistake | `digestPlan` in `lib/slice-file.mjs` | `git diff` on the slice file; in the former case run `slice replan` | -| The Review part is written, but `status` still says waiting for review | The criteria check table still has placeholders, or is not a valid table | `readVerdicts` in `lib/slice-file.mjs` | There must be a separator row under the header, and three cells per row | -| `slice accept` is refused | A section of the Review part is not filled in; a verdict is not one of the three; there are fewer rows than completion criteria; there is a fail; there is an unverified without the flag | `commands/slice.mjs` | The error message states each one | -| `slice plan` is refused, mentioning the walking skeleton | The skeleton slice has not passed review yet | `lib/slice-flow.mjs` | The line in `roadmap.md` that carries "最小骨架" (walking skeleton) | -| `roadmap` is refused, mentioning a missing slice that has already started | The draft does not carry the `id` of an existing slice | `lib/roadmap.mjs` | Compare with `roadmap.md` and give the existing entries their `id` | -| The filters check misses something or reports a false positive | The project file uses wildcards, macros, or a form the regular expressions do not cover | `lib/filters.mjs` | The count of "没有检查" (not checked) items in the output; reproduce with a minimal project file | -| init outputs "设置结果: 未执行" (setup result: not run), or on entry it says records would be ignored | The project's ignore rules ignore files under `.navigator/` or `.claude/` that must be committed | `commands/init.mjs`, `lib/paths.mjs` | `git check-ignore -v --no-index ` | -| The hook still produces output after uninstalling | `init.status` in the state file is not `uninstalled` | `commands/uninstall.mjs`, `runtime/hook.mjs` | Look at `init` in `state.json` | +| Symptom | Root cause | Related files | How to verify | +| ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| Invoking the skill aborts at once, reporting that a permission check failed | `allowed-tools` was deleted or rewritten | `SKILL.md` | Invoke it once in the default permission mode | +| Running `project-navigator` reports that the command is not found | The plugin is not enabled, or the session started before the plugin was updated; `.cmd` is not found in PowerShell | `bin/` | `claude plugin list` to see whether the plugin is enabled; restart the session | +| Plugin commands or record writes pop up permission prompts | The local configuration lacks the allow rules, or the command does not start with `project-navigator` | `lib/settings.mjs` | The "放行规则" (allow rules) line of `status`; run `init` again | +| On entry, it says the records were written by an old version | The `schema` of the state file is less than 6 | `commands/enter.mjs`, `commands/init.mjs` | Run `init --archive` after the user agrees | +| The orchestrator session is refused when writing files outside `.navigator/` | This is rule 4; when a change is needed, put it into a slice | `lib/guard.mjs` | Drive `hook.mjs` through standard input, with an orchestrator session's transcript | +| A session that is not the orchestrator session is refused when writing business files | This project's orchestrator marker appears in the session transcript, for example because the session once invoked the skill | `lib/orchestrator-marker.mjs` | Search the transcript for "project-navigator 编排标记" (project-navigator orchestrator marker) | +| The executor subagent is refused when writing orchestration records | Under `.navigator/` only the slice file in progress and `findings/` can be written | `lib/guard.mjs` | The refusal reason states the writable scope | +| `slice start` says the Plan part is not filled in | A section is empty or still contains "(待填写" ("(to be filled in"); the completion criteria are not a numbered list | `lib/slice-file.mjs` | `check` lists the sections that are not filled in | +| `slice start` mentions a needed decision | "需要决定" (needs decision) is not a single line "无" (none) | `commands/slice.mjs` | After asking the user and writing in the answer, run with `--decided` | +| The executor subagent has ended, but `status` still says waiting | It did not change "执行状态" (execution status) to "完成" (done), or wrote it in another section | `lib/slice-file.mjs`, `lib/guidance.mjs` | Look at the "执行结果" (Result) part of the slice file; use SendMessage to ask it to fill it in | +| `slice review` says the Plan part was changed | The content of the Plan part changed after work started: the orchestrator session changed it according to the user's answer, or the executor subagent changed it by mistake | `digestPlan` in `lib/slice-file.mjs` | `git diff` on the slice file; in the former case run `slice replan` | +| The Review part is written, but `status` still says waiting for review | The criteria check table still has placeholders, or is not a valid table | `readVerdicts` in `lib/slice-file.mjs` | There must be a separator row under the header, and three cells per row | +| `slice accept` is refused | A section of the Review part is not filled in; a verdict is not one of the four; there are fewer rows than completion criteria; there is a fail; there is an unverified without the flag | `commands/slice.mjs` | The error message states each one | +| `slice plan` is refused, mentioning the walking skeleton | The skeleton slice has not passed review yet | `lib/slice-flow.mjs` | The line in `roadmap.md` that carries "最小骨架" (walking skeleton) | +| `roadmap` is refused, mentioning a missing slice that has already started | The draft does not carry the `id` of an existing slice | `lib/roadmap.mjs` | Compare with `roadmap.md` and give the existing entries their `id` | +| The filters check misses something or reports a false positive | The project file uses wildcards, macros, or a form the regular expressions do not cover | `lib/filters.mjs` | The count of "没有检查" (not checked) items in the output; reproduce with a minimal project file | +| init outputs "设置结果: 未执行" (setup result: not run), or on entry it says records would be ignored | The project's ignore rules ignore files under `.navigator/` or `.claude/` that must be committed | `commands/init.mjs`, `lib/paths.mjs` | `git check-ignore -v --no-index ` | +| The hook still produces output after uninstalling | `init.status` in the state file is not `uninstalled` | `commands/uninstall.mjs`, `runtime/hook.mjs` | Look at `init` in `state.json` | ## Change process @@ -288,7 +292,7 @@ In a temporary repository, confirm each item with `claude --plugin-dir ./plugins 5. Deliberately write one wrong detail in "工作假设" (working assumptions) in the plan: the executor subagent does not hand back, finishes the work to what is actually there, and the "修正" (corrections) table has one row; "修正核对" (corrections check) in the review describes this correction. 6. The executor subagent tries to change the Plan part: `slice review` refuses to submit for review. 7. Deliberately leave the new code unconnected to the entry point: the wiring check in the review points it out, and the related criteria fail; they pass after rework. -8. A project whose verification mode is `manual`: the reviewer subagent does not build or run, criteria that can only be confirmed by running are judged unverified with the steps written out; the user accepts as is after confirming them. +8. A project whose verification mode is `manual`: the reviewer subagent does not build or run, criteria that can only be confirmed by running are judged unverified with the steps written out; after the user confirms them the orchestrator changes the verdicts to "用户已确认" and accepts without the flag. 9. In a Visual Studio project, add a source file that is not assigned to a filter: the filters check in the review prompt points it out. 10. Interactive slice: no executor subagent is dispatched, the orchestrator session does the work directly, and the findings are written to `.navigator/findings/`; a new session that invokes the skill can continue; the orchestrator session is still refused when writing business files. 11. The reviewer subagent is refused when it tries to write a file. diff --git a/plugins/waypoint/docs/project-navigator-maintaining.md b/plugins/waypoint/docs/project-navigator-maintaining.md index 6a83fdd..f1e4524 100644 --- a/plugins/waypoint/docs/project-navigator-maintaining.md +++ b/plugins/waypoint/docs/project-navigator-maintaining.md @@ -71,14 +71,16 @@ - **工作单位是切片, 一个切片一份文件, 分三段.** 计划由编排会话写, 执行结果由实现方写, 验收由编排会话按验收子代理交回的内容写. 1.x 是三份文件, 各有固定结构, 读不出来流程就停. 改掉的后果: 回到三件套. - **段的归属靠计划段的摘要保证.** 守卫只能按路径判断, 管不到文件里的段. `slice start` 记下计划段的摘要, `slice review` 比对, 不一致时拒绝送验; 是编排会话按用户的回答改的, 运行 `slice replan`. 摘要先去掉行首尾空白, 合并空白, 统一表格分隔行: 格式化 hook 重排表格时摘要不变. 这只能发现, 不能阻止; 实测中如果经常发生, 退路是把计划与执行结果分成两个文件. -- **脚本只读它需要的内容.** 完成标准 (编号列表), 需要决定与补做是否为 "无", 执行状态, 标准核对表 (结论只能取三种, 每行要有证据). 其余只检查 "不为空, 没有占位". 写入时不校验结构. 改掉的后果: 结构规则越多, 被拦下的次数越多. +- **脚本只读它需要的内容.** 完成标准 (编号列表), 需要决定与补做是否为 "无", 执行状态, 标准核对表 (结论只能取规格中的四种, 每行要有证据). 其余只检查 "不为空, 没有占位". 写入时不校验结构. 改掉的后果: 结构规则越多, 被拦下的次数越多. - **默认只在验收之后请用户决定一次.** 计划的 "需要决定" 不是 "无" 时, `slice start` 拒绝, 用户回答后带 `--decided`. 1.x 每张工单固定两次 (审阅与验收), 而多数审阅用户只是点同意. 发布前核对也去掉了: 编排会话本来就能读代码, 写计划前自己读. 改掉的后果: 每个切片多一次没有信息量的确认. - **硬约束与工作假设分两层.** 硬约束是目标, 完成标准, 明确不做, 模块安排; 其余是工作假设, 与实际不符时实现方取得证据后按实际做, 记进 "修正" 表, 由验收核对. 1.4.0 及之前把计划当合同, 细节不符就是一轮往返. 改掉的后果: 探索性的任务每发现一个细节就多一轮. - **骨架是硬规则.** 第一个里程的第一个切片由脚本标为骨架, 种类必须是 `build`; 骨架通过之前 `slice plan` 拒绝其它切片; 骨架不能放弃. 改掉的后果: 回到 "先把各层横着写完, 最后发现接不上". - **接线是验收的固定项.** 实现方在 "接线证据" 写出从入口到新代码的调用路径, 验收子代理自己再追一遍. 追不到时相关标准判不通过, 而不通过的切片只能补做或放弃, 所以没有接线的切片进不了下一个. 这条靠验收提示词与 `slice accept` 的拒绝共同保证, 脚本本身不判断接线. - **同一时间只做一个切片.** `current` 只有一个位置. 改掉的后果: 基准提交与验收范围不再确定. - **补做在同一份文件里进行.** `slice redo` 把执行状态改成 "补做中", 把现有的验收段标题加上轮次, 在文末追加一段空的验收段; 解析时同名的段取最后一个. 改掉的后果: 每轮一个文件, 或丢掉上一轮的验收记录. -- **未验证的标准可以由用户明确接受** (`slice accept --accept-unverified`). 验证方式为 `manual` 或 `measured` 的项目, 靠运行才能确认的标准都会是未验证, 由用户亲手确认后接受. 不通过的标准不能接受. +- **用户亲手验过的标准记为 "用户已确认", 与通过同样对待.** 验收子代理看不到用户的屏幕, 靠运行才能确认的标准它只能判未验证. 2.0.1 及之前, 这类标准只能走 "接受现状" (`slice accept --accept-unverified`): 用户在游戏里看过效果并说了没问题, 回复里仍然写 "3 条未验证", 还要他再选一次. 现在编排会话在用户确认后把结论改成 "用户已确认" (规格 `verdicts.confirmed`), 证据写用户的原话, `slice accept` 不需要参数. 用户在送验之前已经看过的结果 (交互式切片常见), 验收子代理的证据以 "用户实测: " 开头 (规格 `userEvidencePrefix`), 编排会话直接改, 不让用户再做一遍. "接受现状" 只留给用户决定不验证的情况; 不通过的标准不能接受. 脚本不核对 "用户已确认" 是否属实, 这一条只靠规则. 改掉的后果: 用户做完验证还被告知 "未验证". +- **人工验证的前提在计划里问清楚.** 有完成标准要用户亲手验证时, 用到的账号, 角色, 物品, 设备写进计划的 "需要决定", 开工前问用户; 实现子代理只用确认过的前提准备步骤与材料. 实测中出现过: 实现子代理做好了要某件装扮才能测的补丁, 用户没有这件, 之后来回半小时找替代品. 改掉的后果: 步骤写得再清楚, 用户也做不了. +- **新增切片之前先问 "不做会怎样" 与 "有没有更省事的办法".** 规则在 `references/intake.md` 的 "新增切片之前", 里程复查与需求变更引用它; 提问时推荐最省事的办法. 实测中出现过: 用户随口提到一个现象, 编排会话排了一个逆向切片加一个接入切片, 把逆向标为推荐, 用户配合截了四张图之后才发现让使用者自己选就够了. 改掉的后果: 路线里混进没人需要的切片, 而且每个都要用户配合. - **提交不是状态.** `slice accept` 之后编排会话按输出调用 commit-message, 状态文件随这次提交入库. ### 模块化约束 @@ -103,6 +105,8 @@ - **要用户决定时, 问题与选项紧跟在第一段之后, 选项放在代码块里.** 2.0.0 的规则只有 "第一行结论, 第二行进展, 末尾列选项", 实际会话中出现了: 问题埋在长篇背景之后; 相邻的 "A. B. C." 三行被 Markdown 合并成一段; 进展行与第一行粘在一起; 立项时把全部疑点一次列给用户. 代码块中的换行不会被合并, 所以选项固定用代码块. 改掉的后果: 用户读完整条回复才知道要回答什么. - **有提问框工具时必须用它, 但不依赖它.** 桌面应用在回合结束时提供提问框 (`wrap_up`), 终端里没有. 规则写成 "有这类工具时每次都用, 内容用简体中文, 按钮与代码块的选项对应", 代码块始终保留, 两种环境下回复都完整. 改掉的后果: 只写代码块时桌面应用里没有按钮可点; 只靠提问框时终端里看不到选项. - **进展写在回复末尾, 有变化时才写.** 放在第二行时, 它每条回复都占着最显眼的位置, 而内容多数时候没有变化. +- **同一段内容只完整写一次; 送验时不转述实现方的自述.** 实测中, 送验时先复述一遍实现方报告的结果, 验收回来再报一遍, 同一批操作步骤整段发了两次. +- **用户打断或在选择之后提出疑问时, 先回答, 重新确认之前不改状态, 不提交.** 实测中, 用户选了 "接受并提交" 后立刻打断并提出疑问, 编排会话一边回答一边照样提交了. ### 安装 @@ -163,7 +167,7 @@ | 实现子代理结束了, `status` 仍说等待 | 它没有把 "执行状态" 改成 "完成", 或写到了别的小节 | `lib/slice-file.mjs`, `lib/guidance.mjs` | 看切片文件 "执行结果" 段; 用 SendMessage 请它补写 | | `slice review` 说计划段被改动 | 开工后计划段的内容变了: 编排会话按用户的回答改了, 或实现子代理误改 | `lib/slice-file.mjs` 的 `digestPlan` | `git diff` 看切片文件; 前者运行 `slice replan` | | 验收段已写, `status` 仍说等待验收 | 标准核对表还有占位, 或不是合法的表格 | `lib/slice-file.mjs` 的 `readVerdicts` | 表头下要有分隔行, 每行三格 | -| `slice accept` 被拒 | 验收段有小节没填; 结论不是三种之一; 行数少于完成标准; 有不通过; 有未验证而没带参数 | `commands/slice.mjs` | 错误信息逐条写明 | +| `slice accept` 被拒 | 验收段有小节没填; 结论不是四种之一; 行数少于完成标准; 有不通过; 有未验证而没带参数 | `commands/slice.mjs` | 错误信息逐条写明 | | `slice plan` 被拒, 提到最小骨架 | 骨架切片还没有通过验收 | `lib/slice-flow.mjs` | `roadmap.md` 中带 "最小骨架" 的那一行 | | `roadmap` 被拒, 提到缺少已经开始的切片 | 草稿没有带上已有切片的 `id` | `lib/roadmap.mjs` | 对照 `roadmap.md` 把已有条目带上 `id` | | 筛选器检查漏报或误报 | 工程文件用了通配符, 宏, 或正则没有覆盖的写法 | `lib/filters.mjs` | 输出中的 "没有检查" 项数; 用最小的工程文件复现 | @@ -288,7 +292,7 @@ 5. 计划的 "工作假设" 故意写错一个细节: 实现子代理不交回, 按实际做完, "修正" 表有一行; 验收的 "修正核对" 写出这项修正. 6. 实现子代理尝试改计划段: `slice review` 拒绝送验. 7. 故意不把新代码接到入口上: 验收的接线核对指出来, 相关标准不通过; 补做后通过. -8. 验证方式为 `manual` 的项目: 验收子代理不构建, 不运行, 靠运行才能确认的标准判为未验证并写出步骤; 用户确认后接受现状. +8. 验证方式为 `manual` 的项目: 验收子代理不构建, 不运行, 靠运行才能确认的标准判为未验证并写出步骤; 用户确认后编排会话把结论改成 "用户已确认", 不带参数通过. 9. Visual Studio 项目中新增一个没有归入筛选器的源文件: 验收提示词的筛选器检查指出来. 10. 交互式切片: 不派实现子代理, 编排会话直接做, 结论写进 `.navigator/findings/`; 新开会话调用技能能接着做; 编排会话写业务文件仍被拒. 11. 验收子代理尝试写文件时被拒. diff --git a/plugins/waypoint/docs/project-navigator.en.md b/plugins/waypoint/docs/project-navigator.en.md index 574d1c4..23f38bf 100644 --- a/plugins/waypoint/docs/project-navigator.en.md +++ b/plugins/waypoint/docs/project-navigator.en.md @@ -151,11 +151,12 @@ project-navigator check --filters 5. **Corrections and pending criteria.** When a detail in the plan does not match reality, the executor subagent gathers evidence, works to what is actually there, and records each case in the "修正" (corrections) table, without stopping to ask you. When an individual completion criterion cannot be met as a matter of fact, it finishes the rest and lists the criterion under "待定标准" (pending criteria). You see both in one go at review time. 6. **Submit for review.** The script checks that the Plan part was not changed during implementation and that the Result part is filled in. 7. **Review.** The reviewer subagent checks item by item in a fresh context. There are only three verdicts: pass, fail, and unverified, and each comes with evidence. It traces the wiring from the entry point itself and does not copy the path the executor wrote. -8. **Your decision.** +8. **What you verify by hand.** A criterion that can only be confirmed by looking at the running program is judged unverified by the reviewer, with the steps written out. The account, character, items, and devices those steps need are asked when the plan is written. Once you have checked and said what you saw, the criterion is recorded as "用户已确认" (confirmed by the user) and treated like a pass, and you are not asked to choose again; the same goes for results you already saw before the review. +9. **Your decision.** | Situation | You can choose | | -------------------------- | -------------------------------------------------------------- | -| All pass | Pass and commit; pass, commit, and push | +| All pass or confirmed | Pass and commit; pass, commit, and push | | Some unverified, none fail | Accept as is and commit; rework | | Some fail | Rework; abandon this slice (the skeleton can only be reworked) | diff --git a/plugins/waypoint/docs/project-navigator.md b/plugins/waypoint/docs/project-navigator.md index 60ed6f3..c84ad2c 100644 --- a/plugins/waypoint/docs/project-navigator.md +++ b/plugins/waypoint/docs/project-navigator.md @@ -151,11 +151,12 @@ project-navigator check --filters 5. **修正与待定.** 计划中的细节与实际不符时, 实现子代理取得证据后按实际做, 逐条记进 "修正" 表, 不停下来问你. 个别完成标准按事实做不到时, 做完其余部分, 列为 "待定标准". 两者都在验收时由你一次看完. 6. **送验.** 脚本核对计划段在实现期间没有被改动, 执行结果已填好. 7. **验收.** 验收子代理在全新上下文中逐条核对, 结论只有通过, 不通过, 未验证三种, 每条都附证据. 它自己从入口追一遍接线, 不照抄实现方写的路径. -8. **你的决定.** +8. **你亲手验证的部分.** 要你进程序看效果才能确认的标准, 验收子代理只能判未验证, 并写出步骤. 步骤要用到的账号, 角色, 物品, 设备, 在写计划时就会先问你. 你验完并说了结果, 这条就记为 "用户已确认", 与通过同样对待, 不需要你再选一次; 你在送验之前已经看过的结果也一样. +9. **你的决定.** | 情况 | 你可以选 | | -------------------- | --------------------------------- | -| 全部通过 | 通过并提交; 通过, 提交并推送 | +| 全部通过或你已确认 | 通过并提交; 通过, 提交并推送 | | 有未验证, 没有不通过 | 接受现状并提交; 补做 | | 有不通过 | 补做; 放弃这个切片 (骨架只能补做) | diff --git a/plugins/waypoint/skills/project-navigator/SKILL.md b/plugins/waypoint/skills/project-navigator/SKILL.md index d0bdd41..68c7958 100644 --- a/plugins/waypoint/skills/project-navigator/SKILL.md +++ b/plugins/waypoint/skills/project-navigator/SKILL.md @@ -40,7 +40,7 @@ allowed-tools: Bash(node *) PowerShell(node *) Bash(project-navigator *) PowerSh | 种类 | 何时用 | 版式 | | ---------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------ | -| 等待 | 子代理在后台, 用户不用做事 | 一两句话: 在等什么, 结果回来后做什么. 不复述计划, 也不复述子代理的报告 | +| 等待 | 子代理在后台, 用户不用做事 | 一两句话: 在等什么, 结果回来后做什么. 不复述计划与子代理的报告; 情况没变时再次等待只回一句 | | 报告 | 做完了一件事, 不需要用户决定 | 第一段一句结论; 然后是不超过 5 条的要点. 细节留在文件里, 给出文件路径 | | 要用户决定或操作 | 需要用户选择, 回答或亲手做一件事 | 第一段一句话: 结果, 以及要用户做什么; 紧接着是选项代码块或编号的操作步骤; 之后才是理由与背景, 能少则少 | @@ -56,6 +56,7 @@ C. 一开始就支持多人 - 一次只问一件事. 其它待定的事不提前列给用户, 轮到时再问. - 每个选项一行, 以字母开头; 有推荐时把它排在 A 并标 "(推荐)". 选择之后会发生什么, 用半句话写在同一行. +- 推荐能达到目的的办法中最省事的一个. 要多排切片, 逆向或实测的办法, 只在没有更省事的办法时才推荐. - 用户看完第一段与代码块就应当能作答. 放在代码块之前的内容只有第一段. - 会话中有回合结束时的提问框工具时 (例如桌面应用的 `wrap_up`), 每次要用户决定或操作都调用它: 内容用简体中文, 按钮与代码块中的选项对应; 按钮数不够时放推荐项与最可能被选的另一项. 提问框已经显示问题句时, 正文不再重复问一遍, 代码块照常保留. 没有这类工具时只用代码块. @@ -63,6 +64,9 @@ C. 一开始就支持多人 - 进展有变化时, 在回复末尾单独一段写 "当前进展: ...", 照抄 `status` 的 "当前进展". 没有变化时不写. - 标题, 表格与加粗只在内容确实有这种结构时用; 一条回复只有一个需要用户回应的地方. +- 同一段内容 (操作步骤, 结果, 清单) 只完整写一次, 之后只写有变化的部分. +- 要用户亲手做的事, 先确认他做得到再给步骤: 不清楚他有没有某件东西时先问, 不先准备好再发现用不上. +- 用户打断, 或作出选择之后又提出疑问时, 先回答疑问; 重新得到确认之前, 不运行改变状态的命令, 不提交. - 工具调用之间的过渡句也用简体中文, 不超过一句. - 不贴代码. 用简体中文与英文 (ASCII) 标点, 每个标点后加一个空格. 写朴素的话, 不自造简称. diff --git a/plugins/waypoint/skills/project-navigator/guide/executor.md b/plugins/waypoint/skills/project-navigator/guide/executor.md index ef1dda8..42d878e 100644 --- a/plugins/waypoint/skills/project-navigator/guide/executor.md +++ b/plugins/waypoint/skills/project-navigator/guide/executor.md @@ -16,7 +16,7 @@ 2. 读项目规范 `.claude/rules/engineering.md` 与计划涉及的代码. 3. 核对计划: 逐条看 "工作假设" 与 "模块安排" 是否与实际相符, 读代码核实, 不凭记忆. 不符的按下方 "修正" 处理. 4. 实现: 只做计划写的事. 遵守 "模块安排" 与项目规范. -5. 自检: 逐条核对 "完成标准". 按委派提示词中的验证方式: 有命令时运行命令; 验证方式是用户手动运行或实测时, 不构建, 不运行程序, 把用户要亲手做的验证步骤写进 "自测记录". +5. 自检: 逐条核对 "完成标准". 按委派提示词中的验证方式: 有命令时运行命令; 验证方式是用户手动运行或实测时, 不构建, 不运行程序, 把用户要亲手做的验证步骤写进 "自测记录". 为用户准备人工验证的步骤或材料时, 只用计划 "需要决定" 中用户确认过的前提 (账号, 角色, 物品, 设备); 计划没有写明, 而步骤离不开某个前提时, 按 "中途交回" 问用户, 不假设用户有什么. 6. 填写 "执行结果" 段的每个小节, 把 "执行状态" 改成 "完成". 7. 按委派提示词的 "交回格式" 结束. diff --git a/plugins/waypoint/skills/project-navigator/references/change.md b/plugins/waypoint/skills/project-navigator/references/change.md index 07ae724..25ae861 100644 --- a/plugins/waypoint/skills/project-navigator/references/change.md +++ b/plugins/waypoint/skills/project-navigator/references/change.md @@ -4,7 +4,7 @@ 用户提出新的需求, 或要改已确认的需求时: -1. 先弄清楚变更的内容与原因, 说明它影响哪些已完成与未完成的切片. +1. 先弄清楚变更的内容与原因, 说明它影响哪些已完成与未完成的切片. 要新增切片时, 先过 `ref intake` 的 "新增切片之前": 不做会怎样, 有没有更省事的办法. 2. 有切片正在进行时, 先把它做完或放弃 (`slice drop`), 再改路线. 只影响当前切片的小调整, 按 `ref slice-loop` 的 "中途交回" 改计划并运行 `slice replan`. 3. 用户确认后: - 用 Edit 更新 `.navigator/requirements.md`. diff --git a/plugins/waypoint/skills/project-navigator/references/intake.md b/plugins/waypoint/skills/project-navigator/references/intake.md index 5f860bb..ead9380 100644 --- a/plugins/waypoint/skills/project-navigator/references/intake.md +++ b/plugins/waypoint/skills/project-navigator/references/intake.md @@ -54,6 +54,15 @@ 5. `findings` 切片默认是交互式 (`interactive` 为 true): 由编排会话在对话中直接做, 适合每一步都要用户配合的任务. 纯读代码或读文档, 不需要用户配合的, 写 `"interactive": false`, 交给后台的实现子代理. 6. 切片宜大不宜碎: 一次实现能做完, 验收能看清, 做完能运行, 就不再拆. 高风险的部分单独成一个切片. +### 新增切片之前 + +每排一个切片 (立项时, 里程复查时, 中途追加时都一样), 先回答两个问题: + +1. 不做它, 需求文件 "必须要做" 中的哪一条做不成? 答不出的不排进路线, 记进待办. +2. 有没有更省事的办法达到同样的目的? 例如让用户自己选或自己填, 而不是由程序推算; 用已有的资料, 而不是去逆向或实测. + +有更省事的办法时, 把它作为推荐项问用户, 费事的办法排在后面并写明多花什么. `findings` 切片尤其要过这一关: 只有结论确实会被后面的切片用到, 又没有别的办法得到时才排. 用户随口提到的一个现象不等于一项需求, 先问清楚他要不要软件处理它. + 写好草稿后运行 `project-navigator roadmap --from <草稿>`. 编号由脚本分配, 之后修改路线时已有的里程与切片带上 `id`. 路线视图在 `.navigator/roadmap.md`. 向用户展示路线并取得认可, 然后按命令输出的提交步骤提交, 再运行 `status` 进入切片循环. diff --git a/plugins/waypoint/skills/project-navigator/references/slice-loop.md b/plugins/waypoint/skills/project-navigator/references/slice-loop.md index 2eba56c..c6ece65 100644 --- a/plugins/waypoint/skills/project-navigator/references/slice-loop.md +++ b/plugins/waypoint/skills/project-navigator/references/slice-loop.md @@ -10,7 +10,7 @@ - 目标: 做完后使用者能做到什么. - 完成标准: 编号列表. 每条写成从入口或界面能看到的结果, 并写明怎么验证. "实现了某个函数" 不是合格的标准. - 明确不做: 容易被顺手做掉, 但不属于本切片的事. - - 需要决定: 必须由用户决定的事; 没有时写 "无". + - 需要决定: 必须由用户决定的事; 没有时写 "无". 有完成标准要用户亲手验证时 (进程序看效果, 连设备, 登录账号), 验证要用到的前提也写在这里问用户: 用哪个账号或角色, 手头有哪些物品, 在什么环境里. 取用户最容易满足的前提, 不替用户假设他有什么. - 模块安排 (只有 `build` 切片): 见下方. - 结论位置 (只有 `findings` 切片): 结论写进 `.navigator/findings/` 下的哪些文件. - 工作假设: 你对细节的判断. 与实际不符时实现方以实际为准. @@ -49,25 +49,34 @@ ## 送验与验收 1. "执行状态" 为 "完成" 后运行 `project-navigator slice review`. 命令会核对计划段没有被改动, 执行结果已填好. -2. 用 Agent 工具派出 `waypoint:navigator-reviewer`, 提示词是 `project-navigator brief review` 的输出原文. +2. 用 Agent 工具派出 `waypoint:navigator-reviewer`, 提示词是 `project-navigator brief review` 的输出原文. 这时按 "等待" 版式回复, 不转述实现方自己报告的结果: 它还没有被核实, 验收之后会再说一遍. 有可以和验收同时做的人工验证时, 只给出步骤. 3. 验收子代理交回后, 用 Edit 把内容原样写入切片文件的 "验收" 段: 每个小节对应它交回的一节, 不改结论, 不删证据. "用户结论" 先保留占位. -4. 运行 `status`, 按下一动作向用户报告并请用户决定. 报告依次写: - - 第一段一句结论: 通过, 不通过, 未验证各几条, 要用户做什么. +4. 处理用户已经确认过的标准, 见下方 "用户已确认". +5. 运行 `status`, 按下一动作向用户报告并请用户决定. 报告依次写: + - 第一段一句结论: 通过 (含用户已确认), 不通过, 未验证各几条, 要用户做什么. - 选项代码块, 选项见下表. - - 需要用户亲手验证的步骤, 写成编号列表; 没有时不写. + - 需要用户亲手验证的步骤, 写成编号列表; 没有时不写. 同一批步骤只完整给一次, 之前给过的只说 "步骤同前". - 需要用户知道的问题: 不通过的标准, 接线与结构核对中的问题, 不成立的修正, 会改变用户操作的风险. 没有时不写. 通过的标准与成立的修正不逐条复述, 给出切片文件的路径. -验收结论只有通过, 不通过, 未验证三种. 三种情况下用户的选择: +验收子代理给出的结论只有通过, 不通过, 未验证三种. 三种情况下用户的选择: | 情况 | 选项 | 用户选择之后 | | -------------------- | ------------------------------------- | ---------------------------------------------------------------------- | -| 全部通过 | A 通过并提交; B 通过, 提交并推送 | 把原话写入 "用户结论", 运行 `slice accept` | +| 全部通过或用户已确认 | A 通过并提交; B 通过, 提交并推送 | 把原话写入 "用户结论", 运行 `slice accept` | | 有未验证, 没有不通过 | A 接受现状并提交; B 补做 | A: 写入 "用户结论", 运行 `slice accept --accept-unverified`; B: 见补做 | | 有不通过 | A 补做; B 放弃这个切片 (骨架只能补做) | A: 见补做; B: 运行 `slice drop` | -- 需要用户亲手验证的标准 (验证方式为 `manual` 或 `measured` 的项目很常见) 会被判为未验证, 并写在 "人工验收" 中. 把步骤给用户, 用户确认结果后选 A, 在 "用户结论" 中写明用户看到了什么. +### 用户已确认 + +需要用户亲手验证的标准 (验证方式为 `manual` 或 `measured` 的项目很常见), 验收子代理看不到用户的屏幕, 只能判为未验证. 这不表示还欠着什么: 用户亲手验过并说了结果, 就是验证本身. + +- 用户确认之后, 用 Edit 把这条标准的结论改成 "用户已确认", 证据写用户的原话与日期. 它与通过同样对待, 不走 "接受现状". +- 用户在送验之前已经看过结果的 (交互式切片中当场确认的, 或验收期间同时做完人工验证的), 验收子代理的证据会以 "用户实测: " 开头. 这些直接改成 "用户已确认", 不让用户再做一遍, 也不让用户重新选择. +- 只有用户明确说了结果的才能改; 用户没有验证, 或结果与预期不符的, 保持未验证或按不通过处理. +- 还没有确认的, 把 "人工验收" 的步骤给用户. 步骤中的前提 (物品, 账号, 设备) 用户做不到时, 换成他做得到的重新准备, 不把寻找替代品的工作推给用户. +- "接受现状" 只用于用户决定不验证的情况. - `slice accept` 之后按输出调用 `waypoint:commit-message` 提交. 提交消息不附加任何署名. - 补做: 把要补的内容写入计划段的 "补做" (替换掉 "无"), 运行 `slice redo`. 命令把本轮验收归档在文件中, 并在文末给出新的 "验收" 段. 然后用 SendMessage 让原来的实现子代理继续, 找不到时重新派出. - 接线没有接上的切片不能通过: 相关标准会被判为不通过, 只能补做或放弃. 不要用 "下个切片再接" 绕过. @@ -77,11 +86,12 @@ 逆向, 实测这类任务每一步都要用户配合, 子代理不能与用户对话, 所以由你在对话中直接做. 它只产出结论, 不改业务代码. 1. `slice start` 之后, 在本会话按计划逐步做. 需要用户操作时直接说, 写到用户不用追问就能照做. -2. 每确认一条结论, 立即用 Write 或 Edit 写入 `.navigator/findings/` 下的文件, 再做下一步. 每条结论写明: 宿主或依赖的版本, 位置 (地址, 偏移, 文件与行号), 验证方法, 验证时间. 没有验证过的只能写成 "待验证". -3. 调试输出会占用上下文. 结论都在文件里, 进度在状态里, 所以随时可以让用户新开一个会话调用本技能接着做. -4. 不需要对话的重活 (例如在一份转储里批量搜索) 可以派给后台的通用子代理, 结果回到本会话. -5. 做完后填写 "执行结果" 段, 把 "执行状态" 改成 "完成", 运行 `slice review`, 照常派验收子代理. -6. 需要改业务代码时, 不在这个切片里改: 把结论写好, 在路线中排一个 `build` 切片. +2. 每确认一条结论, 立即用 Write 或 Edit 写入 `.navigator/findings/` 下的文件, 再做下一步. 每条结论写明: 宿主或依赖的版本, 位置 (地址, 偏移, 文件与行号), 验证方法, 验证时间. 没有验证过的只能写成 "待验证". 用户亲眼确认的结论, 验证方法写 "用户实测", 并写明用户看到了什么; 验收时它直接记为 "用户已确认". +3. 每查一步之前先想: 这条结论会用在哪个切片, 没有它哪项需求做不成. 答不出时停下来问用户还要不要查, 不为了把规则查全而让用户反复操作. +4. 调试输出会占用上下文. 结论都在文件里, 进度在状态里, 所以随时可以让用户新开一个会话调用本技能接着做. +5. 不需要对话的重活 (例如在一份转储里批量搜索) 可以派给后台的通用子代理, 结果回到本会话. +6. 做完后填写 "执行结果" 段, 把 "执行状态" 改成 "完成", 运行 `slice review`, 照常派验收子代理. +7. 需要改业务代码时, 不在这个切片里改: 把结论写好, 在路线中排一个 `build` 切片. ## 待办 @@ -93,7 +103,7 @@ 1. 对照里程目标, 向用户说明达成了什么, 没达成什么. 2. 与用户一起整理 `.navigator/backlog.md`: 每项归入下一个里程, 继续留着, 或删除. -3. 为下一个里程排出切片, 用 `roadmap --from <草稿>` 写入 (已有的里程与切片带上 `id`). 还没有下一个里程而用户有新的目标时, 一并加上. +3. 为下一个里程排出切片 (每个切片都要过 `ref intake` 的 "新增切片之前"), 用 `roadmap --from <草稿>` 写入 (已有的里程与切片带上 `id`). 还没有下一个里程而用户有新的目标时, 一并加上. 4. 用户确认后运行 `project-navigator milestone done <编号>`, 按输出提交. 全部里程完成后, 见 `ref change` 的 "收尾". diff --git a/plugins/waypoint/skills/project-navigator/runtime/commands/slice.mjs b/plugins/waypoint/skills/project-navigator/runtime/commands/slice.mjs index da12bac..524943a 100644 --- a/plugins/waypoint/skills/project-navigator/runtime/commands/slice.mjs +++ b/plugins/waypoint/skills/project-navigator/runtime/commands/slice.mjs @@ -291,7 +291,7 @@ function runSliceAccept({ projectRoot, state, spec, values, now }) { values["accept-unverified"] !== true ) { throw new WorkflowError( - `"${verdictTitle}" 中有 ${spec.verdicts.unverified} 的标准: 用户明确接受现状时, 运行 slice accept --accept-unverified.`, + `"${verdictTitle}" 中有 ${spec.verdicts.unverified} 的标准: 用户已经亲手确认的, 把结论改成 "${spec.verdicts.confirmed}" 并在证据中写下用户的原话; 用户没有验证而明确接受现状时, 运行 slice accept --accept-unverified.`, ); } saveState(projectRoot, next, now); diff --git a/plugins/waypoint/skills/project-navigator/runtime/lib/briefs.mjs b/plugins/waypoint/skills/project-navigator/runtime/lib/briefs.mjs index b804b71..5d88d34 100644 --- a/plugins/waypoint/skills/project-navigator/runtime/lib/briefs.mjs +++ b/plugins/waypoint/skills/project-navigator/runtime/lib/briefs.mjs @@ -202,7 +202,7 @@ function verificationRequirement({ project, spec }, titles) { const verification = /** @type {{mode: string, commands: string[]}} */ ( project.verification ); - const manual = `需要用户亲手运行或操作才能确认的标准, 结论写 ${spec.verdicts.unverified}, 并在 "${titles[SECTIONS.manual]}" 中写成逐步操作, 命令原样取自 "${titles[SECTIONS.tests]}"; 没有时写 "${spec.none}"`; + const manual = `需要用户亲手运行或操作才能确认的标准, 结论写 ${spec.verdicts.unverified}, 并在 "${titles[SECTIONS.manual]}" 中写成逐步操作, 命令原样取自 "${titles[SECTIONS.tests]}"; 没有时写 "${spec.none}". 其中 "${titles[SECTIONS.tests]}" 或结论文件已经写明用户亲眼确认过 (有时间, 有用户看到了什么) 的标准, 结论仍写 ${spec.verdicts.unverified}, 证据以 "${spec.userEvidencePrefix}" 开头并写出记录的位置, 不再写进 "${titles[SECTIONS.manual]}"`; return verification.mode === AUTO_VERIFICATION ? `验证运行: 运行 "验证方式" 中的命令与 "${titles[SECTIONS.tests]}" 中的命令, 原样记录结果; 检查是否删除, 放宽或跳过了已有测试. 需要真实账号, 会产生费用或外部副作用的命令不运行. ${manual}.` : `${titles[SECTIONS.manual]}: 本项目不靠自动测试验证, 不构建, 不运行程序. ${manual}.`; diff --git a/plugins/waypoint/skills/project-navigator/runtime/lib/guidance.mjs b/plugins/waypoint/skills/project-navigator/runtime/lib/guidance.mjs index 4e68994..b106751 100644 --- a/plugins/waypoint/skills/project-navigator/runtime/lib/guidance.mjs +++ b/plugins/waypoint/skills/project-navigator/runtime/lib/guidance.mjs @@ -233,7 +233,7 @@ function reviewingAction(slice, facts, spec) { return `验收全部通过, 请用户决定: A 通过并提交; B 通过, 提交并推送. 用户选择后${record}, 运行 ${command("slice accept")}.`; } if (facts.verdictClass === VERDICT_CLASSES.unverified) { - return `有标准未验证, 请用户决定: A 接受现状并提交 (${record}, 运行 ${command("slice accept --accept-unverified")}); B 补做 (${redo}).`; + return `有标准未验证. 用户已经亲手确认过的 (证据以 "${spec.userEvidencePrefix}" 开头, 或用户按 "${sectionTitle(spec, SECTIONS.manual)}" 做完并说了结果), 把结论改成 "${spec.verdicts.confirmed}", 证据写用户的原话与日期, 再运行 ${command("status")}, 不让用户重新选择. 还没有确认的, 先把步骤给用户; 用户不验证时请用户决定: A 接受现状并提交 (${record}, 运行 ${command("slice accept --accept-unverified")}); B 补做 (${redo}).`; } return slice.skeleton ? `有标准不通过, 这是最小骨架, 只能补做: 向用户说明后${redo}.` diff --git a/plugins/waypoint/skills/project-navigator/runtime/lib/slice-file.mjs b/plugins/waypoint/skills/project-navigator/runtime/lib/slice-file.mjs index 0b1ae23..a17c123 100644 --- a/plugins/waypoint/skills/project-navigator/runtime/lib/slice-file.mjs +++ b/plugins/waypoint/skills/project-navigator/runtime/lib/slice-file.mjs @@ -115,7 +115,8 @@ export const VERDICT_CLASSES = Object.freeze({ * @property {string} none 表示 "没有" 的写法. * @property {string} doneStatus 执行状态中表示做完的写法. * @property {string} reworkStatus 补做期间脚本写入执行状态的内容. - * @property {{pass: string, fail: string, unverified: string}} verdicts 标准核对的三种结论. + * @property {{pass: string, fail: string, unverified: string, confirmed: string}} verdicts 标准核对的结论: 前三种由验收子代理给出, confirmed 由编排会话在用户亲手确认之后写入. + * @property {string} userEvidencePrefix 验收子代理在证据开头标明 "已有用户亲眼确认的记录" 的写法. * @property {PartSpec[]} parts 各段. */ @@ -421,7 +422,8 @@ export function readVerdicts(parsed, spec) { } /** - * 给标准核对的结果归类: 有不通过的归为不通过, 否则有未验证的归为未验证. + * 给标准核对的结果归类: 有不通过的归为不通过, 否则有未验证的归为未验证; 用户 + * 已确认的标准与通过的标准同样对待. * * @param {readonly VerdictRow[]} rows 标准核对表各行. * @param {SliceSpec} spec 规格. diff --git a/plugins/waypoint/skills/project-navigator/spec/slice.json b/plugins/waypoint/skills/project-navigator/spec/slice.json index c124ed4..fcf30b4 100644 --- a/plugins/waypoint/skills/project-navigator/spec/slice.json +++ b/plugins/waypoint/skills/project-navigator/spec/slice.json @@ -7,8 +7,10 @@ "verdicts": { "pass": "通过", "fail": "不通过", - "unverified": "未验证" + "unverified": "未验证", + "confirmed": "用户已确认" }, + "userEvidencePrefix": "用户实测: ", "parts": [ { "id": "plan", @@ -33,7 +35,7 @@ { "id": "decisions", "title": "需要决定", - "hint": "必须由用户决定的事, 用户回答后把答案写在这里; 没有时写 无" + "hint": "必须由用户决定的事, 以及要用户亲手验证时需要的前提, 例如账号, 角色, 物品, 设备; 用户回答后把答案写在这里; 没有时写 无" }, { "id": "modules", @@ -94,7 +96,7 @@ { "id": "tests", "title": "自测记录", - "hint": "运行过的命令与结果; 需要用户亲手验证的步骤" + "hint": "运行过的命令与结果; 需要用户亲手验证的步骤; 用户已经亲眼确认的结果, 写明时间与用户看到了什么" }, { "id": "notes", diff --git a/tests/project-navigator/flow.test.mjs b/tests/project-navigator/flow.test.mjs index 8be0794..8dcbcbb 100644 --- a/tests/project-navigator/flow.test.mjs +++ b/tests/project-navigator/flow.test.mjs @@ -218,10 +218,21 @@ test("流程: 最小骨架从写计划到接受未验证的标准", () => { 标准核对: verdictTable("未验证"), ...reviewRest(true), }); - assert.match(line(ok(runner, ["status"]), "下一动作"), /接受现状/u); + assert.match(reviewBrief, /证据以 "用户实测: " 开头/u); + const unverifiedAction = line(ok(runner, ["status"]), "下一动作"); + assert.match(unverifiedAction, /接受现状/u); + assert.match(unverifiedAction, /改成 "用户已确认"/u); assert.match(refused(runner, ["slice", "accept"]), /用户结论/u); - fillSections(root, file, { 用户结论: "A, 接受现状." }); - assert.match(refused(runner, ["slice", "accept"]), /--accept-unverified/u); + fillSections(root, file, { + 标准核对: verdictTable("用户已确认"), + 用户结论: "A, 接受现状.", + }); + assert.match(line(ok(runner, ["status"]), "下一动作"), /验收全部通过/u); + fillSections(root, file, { 标准核对: verdictTable("未验证") }); + assert.match( + refused(runner, ["slice", "accept"]), + /用户已确认.*--accept-unverified/u, + ); const accepted = ok(runner, ["slice", "accept", "--accept-unverified"]); assert.match(accepted, /commit-message/u); assert.match( diff --git a/tests/project-navigator/slice-file.test.mjs b/tests/project-navigator/slice-file.test.mjs index b87dc1b..139ce3a 100644 --- a/tests/project-navigator/slice-file.test.mjs +++ b/tests/project-navigator/slice-file.test.mjs @@ -161,7 +161,7 @@ test("切片文件: 读出完成标准, 需要决定与执行状态", () => { assert.equal(readResultStatus(undefined, SPEC), ""); }); -test("切片文件: 标准核对表的结论只能取三种, 每行要有证据, 并按最差的归类", () => { +test("切片文件: 标准核对表的结论只能取规格中的几种, 每行要有证据, 并按最差的归类", () => { const withRows = (rows) => parseSliceFile( skeleton("build").replace( @@ -195,6 +195,15 @@ test("切片文件: 标准核对表的结论只能取三种, 每行要有证据, ), VERDICT_CLASSES.pass, ); + const confirmed = readVerdicts( + withRows([ + "| 1 | 通过 | a.js:1 |", + `| 2 | ${SPEC.verdicts.confirmed} | 用户 2026-11-13: 看到了 |`, + ]), + SPEC, + ); + assert.deepEqual(confirmed.problems, []); + assert.equal(classifyVerdicts(confirmed.rows, SPEC), VERDICT_CLASSES.pass); const invalid = readVerdicts( withRows(["| 1 | 部分通过 | a |", "| 2 | 通过 | |"]), SPEC,