docs: 对外文档诚实批(lane E / #2154 切片 C)——6 条报告逐条复核后修 5 条,1 条改判,3 处归属拒绝猜号 - #2237
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3149e8a to
ac0b714
Compare
CONTRIBUTING.md:36 原称 `make test` = Go 单元 + 前端 vitest,与紧邻行 `make fe-test` 自相矛盾。实测 Makefile:59 `test: test-edge test-hub`, 两个子目标均为 `go test ./... -short`(Makefile:62/70),前端 vitest 由 独立 `fe-test` 目标承载。 改为只列目标名 + 声明「目标内容以根 Makefile 为准」,避免文档复述目标 内容再次漂移;AGENTS.md L0 行的入口列同步标注 (Go)/(前端) 消歧。 docs/developer-quickstart.md:122-123 本来就是正确口径,未改。 门禁: python3 scripts/verify/verify-doc-ssot.py PASS Co-authored-by: Cursor <cursor@vectorcontrol.tech>
复核成立:仓内有 cliff.toml(git-cliff 模板,含 conventional commit 分组) 与 .github/workflows/release.yml:496-538(Generate release notes with git-cliff → softprops body_path: release-notes.md),即逐版本用户可见变更 的真实 SSOT 是自动生成的 GitHub Release 正文。 实测 v0.6.1 之后 master 有 462 个 commit(185 fix / 52 feat / 49 refactor / 10 perf 等),原文「暂无未发布变更」为假。按任务要求不手写会立刻过期 的 Unreleased 列表,改为明确声明 SSOT + 生成方式 + 指向 releases 页面。 附带发现(本 commit 不改,另在 PR/#2154 登记):tag v0.6.1(6c7c64c) 不是 当前 master 的祖先(git merge-base --is-ancestor 返回否,历史在 372a211 2026-08-14 重新生根),release.yml:42 的 tag-guard 对该旧 tag 会判失败。 门禁: verify-doc-ssot.py PASS;CHANGELOG.md 80 行(预算 90) Co-authored-by: Cursor <cursor@vectorcontrol.tech>
复核成立(.github/workflows/checks.yml): - vuln-scan-js:2350/2362 同时跑 pnpm audit --prod 与全量 pnpm audit(#2154 F-d), 原文只写 --prod 漏了 dev-chain 全量审计。 - vuln-scan-rust:2445 的门禁是 scripts/verify/verify-rust-advisories.sh(包装 cargo-audit 并把 unsound 类纳入判定 + allowlist 到期日),原文写「cargo audit」 指错了工具:裸 cargo audit 正是该脚本要堵的跨生态假绿。 - 三个 job 均 needs: changes 路径过滤(go/frontend/desktop)+ workflow_dispatch, 原文「CI 持续扫描」不准确。 - 三者判定统一走 scripts/verify/verify-vulnerability-gates.sh(#1534 fail-closed), 原文未提。 额外复核发现(原文亦不诚实,一并修):line 22 称已修复漏洞在 release notes 的 「security 分组」披露,但 cliff.toml 的 ^security parser 永远不可能命中—— scripts/verify/verify-commit-messages.sh:8 的类型白名单不含 security,且当前 462 个 commit 中 security 类型计数为 0。改为如实说明落点。 门禁: verify-doc-ssot.py PASS Co-authored-by: Cursor <cursor@vectorcontrol.tech>
…始补 corepack 三处复核: 1. 结构表漏目录:pkg/(pkg/go.mod 独立 module,根 go.work 第三个 use 项,含 errcode/jwtutil/logmask/testkit 等 9 包)与 app/workbench(@agenthub/workbench, workbench → shared 单向依赖,由 verify-frontend-package-boundary.py 强制) 均真实存在但未登记。中英各补两行。 2. docs/ 描述称含「路线图」为假:ls docs/ 只有 architecture/governance/reference/ component-acceptance 等,无任何 roadmap 文件;verify-doc-ssot.py:100 的 ROADMAP.md 禁令理由即「roadmap state lives on GitHub issues, not a repo file」。 改为「架构、治理与设计文档(进度与路线图在 GitHub issues,不在 docs/)」。 注:报告称「路线图/roadmap 被门禁主动禁止」不准确——门禁禁的是 ROADMAP.md 文件与 docs/roadmap 路径,不是「路线图」这个词;假声明本身成立,故仍修。 3. README_EN.md:65 pnpm dev → corepack pnpm dev:同一代码块 step 4 已用 corepack pnpm install,step 5 却裸 pnpm;docs/developer-quickstart.md:77/83/91/99 一律 corepack pnpm。中英现已一致。 门禁: verify-doc-ssot.py PASS(含 DOC-README-PARITY 两个配对标记) Co-authored-by: Cursor <cursor@vectorcontrol.tech>
…e 子索引补 CI/CD policy 双向差集复核(ls scripts/verify/ + scripts/release/ × grep .github/workflows/): 已接线但 verifier-map 未登记(全部逐条核对 checks.yml 行号): - verify-vulnerability-gates.sh → vuln-scan-js:2360/2373 + vuln-scan-go:2406(自测 :1981) - verify-rust-advisories.sh → vuln-scan-rust:2445(自测 :2449) - verify-quality-debt-ratchet.py → validate:2084(自测 :2087) - verify-auth-dep-ownership.py → validate:2081 - verify-migration-idempotency.py → validate:2027 - verify-i18n-deadkeys.py → validate:2030 - verify-oidc-code-ssot.py → validate:1971(自测 :1974) - verify-orchestrator-deps.py → go-edge:189(自测 :194) - verify-fixture-connection-pinning.py → validate:2059(自测 :2062) - verify-devserver-contract.py → validate:1956 - verify-real-e2e-artifacts.py → real-e2e-stack:1856 - merge-coverprofiles.py → go-edge:113 / go-hub:333 顺带修正两处不诚实: 1. 原「真实登录/OIDC e2e 链路」行写 无 | 无,实测 lane 入口 scripts/e2e/run-real-e2e-lane.sh 已接线 checks.yml:1846 real-e2e-stack (dispatch-only)。改为如实区分「lane 已接线」与「verify-oidc-flow.py / verify-login-fixture-topology.py / verify-approved-real-preflight.py 仍未被 任何 workflow 调用」。 2. 宏观四行(2026-08-29 新增)与主表之间有空行,markdown 缺表头+分隔行会被 渲染成普通文本而非表格行。删掉该空行使其并入主表。 docs/architecture/README.md 子索引漏 github-actions-ci-cd-policy.md,而主索引 docs/architecture.md:101 已登记——补上该行消除单侧缺口。 门禁: verify-doc-ssot.py PASS(verifier-map 校验脚本路径 53→66、CI 文件 45→58); verify-ci-gates.py PASS;verifier-map.md 87 行(预算 120)、 docs/architecture/README.md 28 行(预算 40) Co-authored-by: Cursor <cursor@vectorcontrol.tech>
…web→#1581) 复核方式:gh issue view 逐个确认存在性与归属,不照抄报告。 改的两条(归属已确认): - frontend-desktop: Lint 1573 → 1575 #1575 = [P2] 偿还 Desktop ESLint 债并移除 frontend-desktop lint 软门禁; 正文明确「quality-debt baseline 暂时把该条目指向 #1536 …不能继续作为实际 lint 债的 owner」+ 完成条件含「删除该 soft gate」,即本条债的 owner。 - frontend-web: Lint Web 1573 → 1581(reason 内的 #1575 同步改 #1581) #1581 = [P2] 清零 Web ESLint warnings并让 Lint Web 对新增 warning fail-closed; 正文原话「#1575 只负责 Desktop ESLint,不覆盖 Web」。报告称本条应为 1575, 实测 1575 明确排除 Web,真实 owner 是 #1581 —— 报告此点不成立,已按实测改。 不改的三条(归属无法确认,按任务要求不猜号,另在 #2154 登记「归属待确认」): - frontend-mobile: Lint (mobile rules):#1575 自述只管 Desktop、#1581 自述只管 Web,gh search 未找到 mobile lint 软门禁的专属 issue;reason 里的 (#1575) 无据。 - validate: Verify i18n callsites ratchet:repo 内 checks.yml:2126 / verifier-map / CHANGELOG 一律写 #1612,但 gh 实测 #1612 是 PR「docs(progress): MASTER 同步」, files 只有 docs/progress/MASTER.md,与 i18n 无关 —— 即全仓的 #1612 引用本身可疑。 - vuln-scan-rust: cargo clippy (advisory):reason 写 #1578;#1578 存在且创建了 vuln-scan-rust job,但其正文/验收条件全程无 clippy(只讲 glib UB + advisory gate),无法确认它是「24 条 clippy warning 待归零」这笔债的 owner。 以上三条 issue 仍是 1573(= Hub lint fingerprint ratchet,可证伪的错误值), 本 commit 不用未确认号覆盖,留待人工裁决。 注:verify-quality-debt-ratchet.py 只校验 issue 为正整数与字段齐全(:232-236), historical ratchet 只比较 review_by(:440-442),故改 issue/reason 不触发棘轮。 门禁: python3 scripts/verify/verify-quality-debt-ratchet.py → 9 pass / 0 fail; git diff --check 干净 Co-authored-by: Cursor <cursor@vectorcontrol.tech>
净变化 286 → 284(-2),门禁上限 300,wc -l AGENTS.md = 284。 加(+2 行):§12 发布流程新增一行「变更记录 owner」,声明 commit 级用户可见 变更由 git-cliff 从 tag 区间生成到 GitHub Release 正文(cliff.toml + .github/workflows/release.yml),任何文档不手写;CHANGELOG.md 只保留已发布 版本 longform,不维护 Unreleased 清单。与本分支 CHANGELOG.md 的 SSOT 声明 互为指针,避免两处各自漂移。另加 1 行 markdown 空行使其独立成段。 删(-4 行,均为复制 owner 文档内容因而最易漂移的行): 1. §9.5「- 里面有什么:全表三列(规则/验证脚本/CI job)+ 维护规则;`无` 表示 暂无机器验证,靠人工自觉,规则本身不因此失效。」——与 docs/governance/ verifier-map.md:10 逐句重复(该文件才是映射 SSOT,本文件按 §0 只留指针)。 2. §9.5「- 权威范围:映射只描述“有没有机器管”,规则本身的权威仍是本文件;两者 冲突时以本文件为准,并更新映射表。」——同与 verifier-map.md:10 重复,且 优先级已由 §0 事实源优先级(AGENTS.md 第 2 位 / 其他专题文档第 5 位)确立。 删后该权威声明仍在 verifier-map.md 单点存在,未丢信息。 3-4. 上述两条删除后 §9.5 只剩单元素 bullet 列表(格式噪声),把该 bullet 并入 上一段,省掉 bullet 行与其前空行。 保留 §9.5 的 verifier-map 指针行本体,check_verifier_map_owner 仍通过。 门禁: verify-doc-ssot.py PASS(AGENTS.md 反引号路径 95→96 全部存在); wc -l AGENTS.md = 284 ≤ 300 Co-authored-by: Cursor <cursor@vectorcontrol.tech>
ac0b714 to
b017f0f
Compare
主机侧合并前补记:分支已 rebase,正文两处 sha 引用订正正文写的是「HEAD = 按正文自己给的条件重跑并复现(rebase 后必须重证的部分):
正文里所有本地实测结论因写集零变化而原样成立。合并为 squash,标题按仓库约定补 |
…rward/regenerate 按 handler fail-closed、派发器 7 处静默 break 改为一次可感知反馈 (#2154) (#2238) ## 一句话 Desktop 右键菜单里 pin/unpin/recall 是**点了没反应**(平台层有 mutation 但没转发进 workbench deps),forward/regenerate 是**渲染了但根本没有 port**;派发器 7 处 `if (!handler) break;` 让这些点击零反馈消失。本 PR:能接的接上(3 个),接不上的按 handler 存在性 fail-closed 不渲染(2 个),并把静默 `break` 全部换成一次可感知反馈。 ## 1. 锚点核实结论(主机侧 4 条,逐条复核) | # | 主机侧结论 | 复核结果 | |---|---|---| | 1 | 门禁是 `hubMessageActions: Boolean(deps.sessionId)`,判据是"有没有 sessionId"而非"handler 存不存在" | ✅ 成立,且比描述更严重:`AgentHubWorkbenchHelpers.ts:157` 把 `props.activeConversationId` **直接当 sessionId** 传下去(注释:`#1383 REST message actions: activeConversationId doubles as the session id`)。Desktop 在 Hub IM 会话下 `activeConversationId` = hub session id ⇒ 门禁恒真 ⇒ pin/unpin/recall 照渲染。改前 516-517 行注释宣称"Desktop/demo shells get an honest, shorter menu (#1818)",与事实相反(Desktop 有 session id) | | 2 | mappers 有"5 处以上" `if (!handler) break;` | ✅ 成立,精确是 **7 处**同形态静默分支(改前行号):624 regenerate(变量名 `regenerateHandler`)/ 654 approval / 667 pin / 680 unpin / 693 forward / 706 recall / 719 react。全部零反馈、零日志 | | 3 | desktop 平台层 mutation 确实存在,路径应含 `/platform/` | ✅ 路径修正成立:`app/desktop/src/platform/useDesktopWorkbenchModel.ts` 的 `DesktopChatActions` 有 `sendMessage/recallMessage/editMessage/pinMessage/unpinMessage/markRead`(479-487 行接 `useHubRecallMessage/useHubPinMessage/useHubUnpinMessage`,hook 在 `app/desktop/src/api/sessionQueries.ts`)。**但只有这 3 个能用**:desktop api 层没有 forward hook(shared `hubClient.forwardMessage` 存在,desktop 未包)、desktop 全仓 grep 不到 regenerate、shared hubClient 也没有 addReaction | | 4 | App.tsx grep 不到 `onPinMessage\|onUnpinMessage\|onRecallMessage` | ✅ 成立,**具体缺 3 个转发**:`onPinMessage` / `onUnpinMessage` / `onRecallMessage`。改前 desktop App.tsx 只转发 `onEditMessage`(674-682)与 `onApprovalDecision`。`onForwardMessage` / `onRegenerate` / `onAddMessageReaction` 同样没有,但属"平台层没有对应 mutation",不是漏转发 | 前提全部成立 ⇒ 按"优先接真 mutation + 其余 fail-closed + 派发器不再静默"执行,没有另造修法。 ## 2. 改了什么 **`app/workbench/src/workbenchTranscriptChromeActionMappers.ts`** - 菜单选项 `hubMessageActions?: boolean` → `capabilities?: TranscriptMenuActionCapabilities`(`pin/unpin/recall/forward/regenerate` 五个独立布尔,缺省全 false = fail-closed)。pin 与 unpin **分开**:条目按 `block.pinned` 二选一,只接了一个方向的 shell 不再渲染死的那一半。forward 仍需 `conversations`(选择器是唯一真实转发路径,#1385),recall 仍限 `author.role === 'human'`,regenerate 仍限 agent 文本块。 - 新增 `UNAVAILABLE_ACTION_TOAST_KEY = 'toast.actionUnavailable'` + `announceUnavailableAction()`,替掉全部 7 处静默 `break`:恰好一次 toast、绝不播报成功文案、绝不产生 softHide/pulse/composer 等假副作用。键未落地时回落到 effect 自带的 `failureMessage`(已本地化),因此既不会静默也不会露出裸键。 **`app/workbench/src/workbenchTranscriptChromeHelpers.ts`** - `contextMenuGroups` 由 handler 存在性算 capabilities:`pin/unpin/recall = Boolean(sessionId) && deps.onXxx !== undefined`(planner 没有 sessionId 造不出 effect,#1818),`forward = deps.onForwardMessage !== undefined`,`regenerate = deps.onRegenerate !== undefined`。 - 修正 `sessionId` 的 doc 注释(原文断言"Absent on Desktop/demo shells",是假的)。 **`app/desktop/src/App.tsx`** - 转发 `onPinMessage/onUnpinMessage/onRecallMessage` 到 `workbench.chatActions.{pinMessage,unpinMessage,recallMessage}`,沿用既有 `onEditMessage` 的 `hub-message-` 前缀剥离约定(契约见 `AgentHubWorkbenchTypes.ts:167`:handler 收到的是 raw block id,由 parent 剥前缀);`chatActions` 缺失(demo/Hub 未就绪)时传 `undefined`。 - forward/regenerate/reaction **不接**(desktop 无对应 mutation,且 forward hook 要改 `app/desktop/src/api/sessionQueries.ts`,不在写集)⇒ 靠 fail-closed 让条目消失。 **测试**:`workbenchTranscriptChromeActionMappers.test.ts`、`workbenchTranscriptChromeHelpers.test.ts`、新增 `app/desktop/src/__tests__/App.messageActions.test.tsx`;另有 2 个写集外夹具修正(见 §6.2)。 ## 3. 不变量 → 测试映射(全部绿) | 不变量 | 测试 | 断言方式 | |---|---|---| | handler 缺失 ⇒ 菜单不出现 pin/unpin/recall/forward/regenerate | mappers `renders handler-backed menu entries only when the capability is declared (#2154)`;helpers `omits handler-backed menu entries when no handler is wired, even with a session id (#2154)` | 有 sessionId、无 handler ⇒ 逐条 `not.toContain`;另覆盖 pin/unpin 半开、recall 作者门、forward 无会话列表 | | handler 存在 ⇒ 点击真的派发到该 handler | helpers `renders each wired action and dispatches the click to its handler (#2154)`;mappers `dispatches a declared menu entry to its action string (#2154)`;desktop `forwards the Hub pin/unpin/recall ports with the block-id prefix stripped` | 菜单项 `onClick()` → spy handler 被调用(helpers 层断言 `onPinMessage('u1','sess-1')` 等 5 个 port;desktop 层断言 `chatActions.pinMessage('m1','sess-1')`,即前缀已剥) | | 任何"无 handler"分支必须产生一次可感知反馈,不允许静默 break | mappers `announces every unwired action exactly once instead of dropping it silently (#2154)`(7 个 effect 逐个)+ `announces Hub REST side effects when handlers are not wired` + `announces approval effects when no decision handler is wired` + `prefers the dedicated unwired-action copy…` + `falls back to the effect failure copy when the dispatcher gets no translate function` | `toHaveBeenCalledTimes(1)`、不是成功文案、且 softHide/pulse/dispatchComposer 均未被调用 | | desktop 接不上的 port 保持 undefined(菜单因此不渲染) | desktop `leaves the ports Desktop cannot back undefined so the menu hides them`、`withholds every message port when Hub chat actions are unavailable` | props 断言 `onForwardMessage/onRegenerate/onAddMessageReaction === undefined`;chatActions 缺失时 4 个全 undefined | ## 4. 红 → 绿证据 **红(实现改之前,tree = `bdbf810` + 红测试,提交为 `b7b2c785`)** ``` # pnpm --filter @agenthub/workbench exec vitest run \ # src/workbenchTranscriptChromeActionMappers.test.ts src/workbenchTranscriptChromeHelpers.test.ts ❯ src/workbenchTranscriptChromeHelpers.test.ts (27 tests | 1 failed) × omits handler-backed menu entries when no handler is wired, even with a session id (#2154) ❯ src/workbenchTranscriptChromeActionMappers.test.ts (32 tests | 6 failed) × announces Hub REST side effects when handlers are not wired (#2154) × announces approval effects when no decision handler is wired (#1821, #2154) × renders handler-backed menu entries only when the capability is declared (#2154) × dispatches a declared menu entry to its action string (#2154) × announces every unwired action exactly once instead of dropping it silently (#2154) × prefers the dedicated unwired-action copy once the locale bundle resolves it (#2154) Test Files 2 failed (2) Tests 7 failed | 52 passed (59) ``` 典型红断言:`expected [ 'context.copy', …(8) ] to not include 'context.regenerate'`(有 sessionId 无 handler 时条目照样渲染);`pin: expected "vi.fn()" to be called 1 times, but got 0 times`(派发器静默)。 ``` # pnpm --filter agenthub-desktop exec vitest run src/__tests__/App.messageActions.test.tsx × forwards the Hub pin/unpin/recall ports with the block-id prefix stripped AssertionError: onPinMessage must reach the workbench deps: expected undefined to be type of 'function' Test Files 1 failed (1) Tests 1 failed | 2 passed (3) ``` **实现落地后又抓出 2 处"旧断言就是那条假事实"的连带红**(均在写集外,见 §6.2): ``` useWorkbenchTranscriptChrome.test.ts × builds context menu groups shaped for agent and user blocks AssertionError: expected false to be true (只给 sessionId、不给 handler 就断言 regenerate 条目存在) __tests__/transcript.test.tsx × opens the design card context menu and multi-select toolbar… expected […] to have a length of 6 but got 5 (无 forward port 的 shell 仍断言"转发"条目存在) ``` **绿(合并时 HEAD `321992ca`,base `c87178b3`;下表原跑于 `094ba8ae` / `d1dc97fd`,第二次 rebase 后已在 `321992ca` 上全部重跑复现,见 §7)** ``` workbench: 4 files / 92 tests passed (mappers + helpers + useWorkbenchTranscriptChrome + __tests__/transcript) desktop : 1 file / 3 tests passed (App.messageActions) web : 1 file / 10 tests passed (src/App.test.tsx,回归面:web 也吃这套门禁) ``` ## 5. 门禁表 两轮:rebase 前(HEAD `1a78581`,base `bdbf810`)与 rebase 后(HEAD `094ba8ae`,base `d1dc97fd`,已 push)。rebase 只带入 hub-server Go 改动(`git diff --stat bdbf810..d1dc97f` 全是 `hub-server/**`),FE 树 byte-identical;下表全部为 **rebase 后 HEAD `094ba8ae`** 实跑结果。**注:合并前本分支又 rebase 了一次(→ `321992ca`,base `c87178b3`),下表所有本机可跑项均已在新 HEAD 重跑并逐项复现,见 §7。** | 门禁 | 命令 | 结果 | HEAD | |---|---|---|---| | workbench 单包单测 | `pnpm --filter @agenthub/workbench exec vitest run src/workbenchTranscriptChromeActionMappers.test.ts src/workbenchTranscriptChromeHelpers.test.ts src/useWorkbenchTranscriptChrome.test.ts src/__tests__/transcript.test.tsx` | 4 files / 92 passed | `094ba8ae` | | desktop 单包单测 | `pnpm --filter agenthub-desktop exec vitest run src/__tests__/App.messageActions.test.tsx` | 1 file / 3 passed | `094ba8ae` | | web 回归面单测 | `pnpm --filter agenthub-web exec vitest run src/App.test.tsx` | 1 file / 10 passed | `094ba8ae` | | 文档 SSOT | `python3 scripts/verify/verify-doc-ssot.py` | `doc SSOT ok` | `094ba8ae` | | 空白/冲突标记 | `git diff --check origin/master...HEAD` | clean | `094ba8ae` | | i18n 硬编码棘轮 | `python3 scripts/verify/verify-i18n-callsites.py` | PASS(74 files / 597 行 ≤ baseline 78/608) | `094ba8ae` | | 前端包边界 | `python3 scripts/verify/verify-frontend-package-boundary.py` | PASS | `094ba8ae` | | workbench 类型 | `pnpm --filter @agenthub/workbench exec tsc --noEmit` | 0 error | `094ba8ae` | | desktop 类型(app) | `pnpm --filter agenthub-desktop exec tsc --noEmit -p tsconfig.app.json` | 0 error | `094ba8ae` | | desktop 类型(含测试) | `pnpm --filter agenthub-desktop exec tsc --noEmit -p tsconfig.json` | 0 error | `094ba8ae` | | eslint(仅改动文件) | `pnpm exec eslint <8 个改动文件>` | 2 problems,**均 pre-existing**(见 §6.9) | `094ba8ae` | 按指令**未跑**:全量 vitest、coverage、`pnpm -r build`、全量 `tsc`(CI 是权威)。命令坑记录:desktop 包名是 `agenthub-desktop` 不是 `@agenthub/desktop`;`pnpm --filter X vitest run` 会 `ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT`,必须 `exec vitest run`。 ## 6. 证据等级 / 未验证项 / 可能错的地方 **证据等级** - **L1(jsdom 单测,真实断言)**:菜单条目按 handler 存在性渲染、点击派发到 spy handler、7 个无 handler 分支各产生恰好一次 toast 且无假副作用、desktop App 把 3 个 port 转发到 `chatActions` 且剥掉 `hub-message-` 前缀。 - **L2(静态)**:workbench/desktop(含测试)tsc 0 error、eslint 无新增问题、4 个 verify 脚本 PASS。 - **L3(真实端到端)=无**:没起 Tauri/真实 Hub,没有真人点过菜单,没有真实 REST 往返证据。 **未验证 / 可能错** 1. **缺 i18n 键(只登记未改)**:`toast.actionUnavailable`(zh 建议"该操作在当前端未接入",en "This action is not wired in this client")。资源面 `app/shared/src/chatview/i18n/resources.ts` 不在写集 ⇒ 未加。当前行为:键缺失时回落到该 effect 的 `failureMessage`(如"置顶失败,请重试")——**不静默、不假成功,但"请重试"语义不准**(该端永远不会成功)。键一落地自动切换到专用文案,无需再改代码。 2. **写集外改了 2 个测试文件(各 1 处,已独立成 commit,可直接 drop)**:`94972dec` `useWorkbenchTranscriptChrome.test.ts`(夹具补 `onRegenerate/onRecallMessage` 两行,断言一字未改)、`79ecae30` `__tests__/transcript.test.tsx`(菜单条目数 6→5 + "转发"改断言不存在)。理由:这两处旧断言正是本 PR 要消灭的假事实("有 sessionId 就有 handler 条目"/"有 conversations 就有转发条目"),不改则 CI 必红。若主机侧判定越界,请 drop 这两个 commit 并由写集内 lane 重做。 3. **`AgentHubWorkbenchTypes.ts:165-170` 的 doc 注释现在是假的**(仍写"Desktop/demo shells omit them and pin/unpin/recall/react stay hidden (#1818)")。该文件不在写集 ⇒ 未改,登记为后续 1 行注释修正。 4. **desktop forward 未接**:真接需要 `app/desktop/src/api/sessionQueries.ts` 新增 `useHubForwardMessage`(shared `hubClient.forwardMessage` 已有)+ `DesktopChatActions` 扩字段 + App.tsx 转发。api 层不在写集 ⇒ 未做,改为不渲染条目。 5. **desktop regenerate 未接(且我故意没接)**:web 的做法是 App.tsx 直接 `createHubClient(...).regenerateAgentTask(messageId)`,desktop 技术上可照抄,但**我没有验证 desktop 的 Hub 任务语义下 regenerateAgentTask 是否正确**(desktop 另有 DesktopHubTaskBridge/agent task 路径),所以选择 fail-closed 不渲染而不是接一个语义未证的 port。 6. **coverage 未跑**:新增生产分支(`announceUnavailableAction` 的 t 有/无两路、`capabilities ?? {}` 默认值、5 个 capability 计算)都有对应用例,但包级阈值是否被拉低只有 CI 能判。 7. **只跑了 6 个测试文件**,不是全量。其余 FE 测试里是否还有别处断言"有 conversations 就渲染转发",我用 label grep(`context.forward|context.pinMessage|context.unpin|context.recall|context.regenerate|转发|置顶|撤回|重新生成`)扫过 app/{workbench,web,desktop,shared} 与 e2e,认为没有第二处,但 grep 不是权威。 8. **可见 UX 变化(需产品确认,不是 bug)**:desktop 的"重新生成""转发"条目消失;web 在 `chatActions` 缺失(Hub 未就绪/demo)时"转发/置顶/撤回"也消失。这是 fail-closed 的直接后果——消失的正是原先点了没反应的条目。 9. **eslint 2 个 pre-existing 问题(非本 PR 引入,已用 origin/master blob 探针证明)**:`workbenchTranscriptChromeActionMappers.ts:5` `'AppError' is defined but never used`(error)、`desktop/src/App.tsx:260` `useMemo missing dependency: 'tIm'`(warning)。探针做法:`git show origin/master:<file> >` 临时同目录文件再 eslint,结果与改动后一致;临时文件已删,`git status` 干净。未顺手修(与本 lane 无关,且可能有棘轮基线归属)。 10. **多 lane 环境说明**:本机在 `.worktrees/fe-ctx-menu` 单写者作业;worktree 的 `app/node_modules` 是软链到主 checkout、各包 `node_modules` 是 `cp -a` 复制(内部 `@agenthub/*` 为相对符号链接,已核实指向 worktree 自己的 workbench/shared,跨包测试确实跑的是本分支代码)。未动其他 worktree,未合并任何分支。 ## 7. 剩余 blocker(需主机侧决策,非技术阻塞) 1. §6.2 两个写集外 test commit:接受 or drop 重做。 2. `toast.actionUnavailable` 键由谁落(i18n 资源面 lane)。 3. desktop forward / regenerate 是否另开 lane(§6.4、§6.5)。 4. §6.3 的 1 行注释修正归谁。 ## 7. 主机侧合并前订正与复跑(第二次 rebase) 正文写的是 HEAD `094ba8ae` / base `d1dc97fd`。实况:master 又前进了两个 commit(`530b4d99`→`94aef98a`→`c87178b3`,即 #2239 / #2240 / #2237),本仓 `required_status_checks.strict: true` 使 PR 转 `BEHIND` 而**阻塞合并**,故已 rebase 到 `c87178b3`,HEAD 现为 **`321992ca`**。 **写集零变化**:`git diff --stat 094ba8a 321992c -- app/desktop app/workbench` 输出为**空**;两者全量 diff 只含 master 自身的 `app/pnpm-lock.yaml`+`app/pnpm-workspace.yaml`(#2240)、`edge-server/internal/events/**`(#2234/#2239)、`docs/**`+`AGENTS.md`+`scripts/verify/quality-debt-baseline.json`(#2237),与本 lane 8 个文件零重叠;6 个 commit subject 逐条一一对应。 **在新 HEAD `321992ca` 上重跑的门禁(逐项复现正文数值)**: | 门禁 | 结果(`321992ca` 实跑) | 与正文声明 | |---|---|---| | workbench 4 个测试文件 | 4 files / **92 passed** | 一致 | | desktop `App.messageActions` | 1 file / **3 passed** | 一致 | | web 回归面 `src/App.test.tsx` | 1 file / **10 passed** | 一致 | | `verify-doc-ssot.py` | `doc SSOT ok`(66 script paths / 58 CI files、96 AGENTS paths) | 一致 | | `git diff --check origin/master...HEAD` | clean | 一致 | | `verify-i18n-callsites.py` | PASS:current **74 files / 597** ≤ baseline 78/608 | 一致 | | `verify-frontend-package-boundary.py` | PASS(376 shared + 421 workbench,0 违规) | 一致 | | workbench `tsc --noEmit` | **0 error** | 一致 | | desktop `tsc -p tsconfig.app.json` | **0 error** | 一致 | | desktop `tsc -p tsconfig.json`(含测试) | **0 error** | 一致 | | eslint(8 个改动文件) | **2 problems(1 error + 1 warning)** | 一致 | **并把正文§6.9 那句「2 problems 均 pre-existing」独立验证过**(不只采信):master 版 `workbenchTranscriptChromeActionMappers.ts` 第 5 行同样有 `import { AppError } from '@shared/errors';`,且 `AppError` 在 master 版与本 PR 版的出现次数**都是 1**(即只有 import 行、无使用点)→ 该 error 非本 PR 引入;`desktop/src/App.tsx` 的 `tIm` useMemo 缺依赖在 master 的 **259 行**即已存在 → 同样 pre-existing。 **源码级复核修复本体**:`hubMessageActions` 在生产代码中**已彻底消失**(全仓只剩 `workbenchTranscriptChromeHelpers.test.ts:1095` 一条注释在记录旧行为);`workbenchTranscriptChromeHelpers.ts:515-531` 改为逐 action 的 `capabilities{pin,unpin,recall,forward,regenerate}`,其中 pin/unpin/recall = `Boolean(deps.sessionId) && deps.onXxx !== undefined`,forward/regenerate 只看 handler 存在性;`if (!handler) break` 形态在 mappers 中**归零**,`announceUnavailableAction` 定义于 `:556` 并恰好有 **7 个调用点**(`:625/:660/:693/:709/:725/:741/:757`),与报告所述 7 处静默分支一一对应;`desktop/src/App.tsx:693/703/713` 确实把 `onPinMessage/onUnpinMessage/onRecallMessage` 接进 deps。 **CI run(HEAD `321992ca`)**:**全绿** —— 25 successful / 16 skipped / **0 failing / 0 pending**,其中此前唯一的红 `Vuln scan (pnpm audit prod+full)` 现为 **pass(33s)**:它当初失败的原因是 base 早于 #2240(`app/pnpm-lock.yaml` 仍锁 xmldom 0.8.13/0.9.10 + fast-uri 3.1.5),rebase 带入 #2240 的 override 后自动解除,**本 PR 未为过门禁改任何依赖或例外登记**。`mergeStateStatus: CLEAN`。
这条 lane 是什么
实施 lane E(纯文档)= #2154 Feynman 文档探索批的切片 C「对外文档诚实批」。
唯一非
.md改动是第 6 条的scripts/verify/quality-debt-baseline.json(任务书明示的唯一例外)。未改任何
.go/.ts/.tsx/.yaml契约文件。分支已
git fetch origin && git rebase origin/master;上游新增的dc7df53d(纯 Go:edge lifecycle safego 去重 + panic observer)与本分支 9 个文件零重叠,rebase 无冲突,因此没有触发「两边都保留 + 行数预算重新核算」的解冲突流程。HEAD =
8f2ade25,base =dc7df53d(origin/master)。7 个 commit,每个 commit 一次门禁。push 后 master 又前进了一次,本分支故意没有再 rebase(供主机侧合并时裁决):push 完成后
origin/master由dc7df53d前进到77c75e44(Lane B:edge EventLog/Bus seq 顺序不变量,只动edge-server/internal/events/下 5 个.go)。实测与本分支 9 个文件零重叠(comm -12输出为空),git merge-tree0 个冲突标记,gh api pulls/2237返回mergeable=true/mergeable_state=behind。不再 rebase 的理由:(1) squash merge 会落到 master 当前 tip,线性历史要求仍满足,behind不阻塞;(2) rebase 会改掉 HEAD sha8f2ade25,而该 sha 已作为证据写进本 PR 正文与 #2154 登记评论,且会让上面那次已全绿的 CI run 失效、需要重跑重证 —— 零收益、有诚实成本。若主机侧偏好 up-to-date 分支,rebase 后请重跑verify-doc-ssot.py+wc -l AGENTS.md(预期仍 284)并同步更新两处 sha 引用。逐条三段式证据(文档原话 → 代码/CI 事实 → 改后原话)
1.
CONTRIBUTING.md:36称make test跑前端 vitest —— ✅ 报告成立文档原话
(自相矛盾:紧邻行已把前端 vitest 单独归给
fe-test)代码事实
Makefile:51→test: test-edge test-hubMakefile:53-54→test-edge: cd edge-server && go test ./... -short -count=1 -timeout 60sMakefile:61-62→test-hub: cd hub-server && go test ./... -short -count=1 -timeout 60sMakefile:154-155→fe-test: cd app && pnpm -r testtest的依赖闭包里没有任何前端目标(.PHONY行Makefile:11-12亦把test与fe-test分列)docs/developer-quickstart.md:122是正确口径(「make test|test-edge+test-hub,均为go test -short」),:123把前端归给fe-test;AGENTS.md:137的入口列把两个目标并列但未区分谁跑什么,读起来像make test也含 vitest。改后原话
AGENTS.md:137入口列同步改为`make test`(Go)/ `make fe-test`(前端)。docs/developer-quickstart.md:122-123本来就是事实,未改(它已声明「本地入口以根Makefile为准」)。四处口径统一方式 = 指明目标名 + 指回 Makefile/quickstart 作 SSOT,不复述目标内容,因此 Makefile 日后改闭包不会再让文档说谎。
2.
CHANGELOG.md:5-7「暂无未发布变更」—— ✅ 报告成立,且 SSOT 判断成立文档原话
代码/CI 事实
cliff.toml存在且是 git-cliff 模板(conventional_commits=true,commit_parsers 分 10 组,slice(end=20)控长).github/workflows/release.yml:496-518→Generate release notes with git-cliff,"$GIT_CLIFF" --latest -o release-notes.md.github/workflows/release.yml:534-538→softprops/action-gh-release@v3+body_path: release-notes.mdgit rev-list --count v0.6.1..HEAD= 462(185 fix / 61 test / 52 feat / 49 refactor / 40 docs / 39 chore / 23 ci / 10 perf),其中含 wire 行为变更(如 HEADdc7df53d前一条d1dc97f的 pageSize clamp 语义)。scripts/verify/verify-doc-ssot.py:241注释原话 "CHANGELOG.md is immutable historical prose"(该文件因此被排除出活跃文档扫描),且:264给CHANGELOG.md定了 90 行预算 —— 手写清单既必然过期又会挤爆预算。改后原话(按要求不手写会立刻过期的列表,改为声明 SSOT + 生成方式 + 指向 release 页面)
文件头一句同步从「所有值得发布的用户可见变化……记录在这里」改为「已发布版本的 longform 记录……commit 级明细由 git-cliff 生成到 Release 正文」。
CHANGELOG.md80 行(预算 90)。AGENTS.md§12 加了指回同一 SSOT 的 owner 行(见第 7 节)。3.
SECURITY.md:23安全门禁工具名指错 —— ✅ 报告成立(且原文另有一处更严重的不诚实)文档原话
CI 事实(
.github/workflows/checks.yml)pnpm audit --prodvuln-scan-js两条都跑::2359-2360pnpm audit --prod+:2372-2373全量pnpm audit(#2154 F-d新增,workflow 注释原话:--prod对 dev/lint/build 链通告「完全不可见」,brace-expansion 两枚 high 曾在--prod全绿下钉在 lockfile 里)cargo auditvuln-scan-rust:2445的门禁是scripts/verify/verify-rust-advisories.sh(:2437-2441先cargo binstall cargo-audit)。该脚本头注释原话:裸cargo audit把 unsound 类当 informational 警告保持绿灯,「the exact cross-ecosystem false-green this gate closes」——原文指错的正是被这个门禁堵掉的假绿工具scripts/verify/verify-vulnerability-gates.sh(#1534)做:工具故障/输出不可解析/存在漏洞都判红,唯一豁免通道是scripts/verify/vulnerability-exceptions.json。原文没提这个单点判定器needs: changes路径过滤:vuln-scan-js:2327-2329(frontend) /vuln-scan-go:2382-2384(go) /vuln-scan-rust:2414-2416(desktop),workflow_dispatch时无条件跑 ⇒ 不是每次 push 全量扫描gosec::161-169(go-edge) /:363-370(go-hub),输出经scripts/verify/verify-gosec-gates.shfail-closed 判定追加复核出的第二处不诚实(报告未提,我一并修了):原文承诺漏洞在 release notes 的「security 分组」披露 —— 该分组结构上永远为空:
cliff.toml的 parser{ message = "^security", group = "🔒 Security / 安全加固" }要求security提交类型,但scripts/verify/verify-commit-messages.sh:8的白名单是TYPES="init|feat|fix|docs|refactor|chore|test|perf|ci|revert",不含security;实测当前 462 个 commit 中^security(\(|:)计数 = 0。改后原话(节选)
未改的相邻声明也已复核为真:
SECURITY.md:32称scripts/release/verify-release-gate.py读本表 —— 实测verify-release-gate.py:281risk_path = os.path.join(repo_root, "SECURITY.md"),:183-184捕获rotate required/verification required。✅4.⚠️ 部分成立(禁用词那条报告说法不准确,但假声明本身成立)
README.md/README_EN.md——(a) 结构表漏
pkg/与app/workbench—— ✅ 成立事实:
go.work的第三个use项是./pkg;pkg/go.mod是独立 module,含errcode/jwtutil/logmask/safego/testkit/debug/otelids/outboundmetrics/reqlog9 个包(pkg/README.md自述「hub-server与edge-server共享的 Go 包」)。app/workbench/package.json=@agenthub/workbench,其 README 与AGENTS.md:67均声明「依赖方向 workbench → shared 单向」,并由scripts/verify/verify-frontend-package-boundary.py(checks.yml:2044 接线,负向自测 :2047)机器强制。两者都真实存在却未在结构表登记。改后(中英各补两行,同步):
(b)「路线图/roadmap 被门禁主动禁止却仍存在」—— ❌ 报告说法不成立,但底下的假声明成立
实测
verify-doc-ssot.py的禁用面只有::100根级文件ROADMAP.md不得存在(理由原文 "roadmap state lives on GitHub issues, not a repo file")、:109陈旧路径docs/roadmap、:200正则ROADMAP\.md。没有任何规则禁「路线图」或「roadmap」这个词,所以「门禁主动禁止却仍存在」是错的(门禁跑得过就是证据)。但 README 的声明本身是假的:
ls docs/=README.md api-reference.md architecture architecture.md archives component-acceptance component-acceptance.md decisions.md developer-quickstart.md governance history.md images reference—— 无任何 roadmap 文件;全docs/只有docs/history.md与docs/architecture/10-macro-engineering-design.md两处提到该词,都不是路线图载体。改后(中英同步,把假声明换成门禁理由本身陈述的真事实):
(c)
README_EN.md:65应为corepack pnpm dev—— ✅ 成立事实:
docs/developer-quickstart.md:77/83/91/99一律corepack pnpm ...;README.md:64-65(中文)已是corepack pnpm install/corepack pnpm dev;README_EN.md:64也是corepack pnpm install,唯独:65裸pnpm dev—— 同一代码块内 step4/step5 自相矛盾,且未corepack enable时 step5 会 command-not-found。(CI 侧不用 corepack:workflow 走
pnpm/action-setup@v6,所以判据取本地启动 SSOT = quickstart。)改后:
parity 门禁复核:
verify-doc-ssot.py:check_readme_parity()只校验两对成熟度标记 ——("Mobile 装配中","Mobile in assembly")、("Desktop/Web 主线","Desktop/Web are the mainline"),要求同现或同缺。这 4 个标记在README.md:31/README_EN.md:31均在,本 PR 未触碰;改动前后DOC-README-PARITY均 PASS。中英两份的结构表/快速开始改动是逐行对称做的。5.
verifier-map.md+docs/architecture/README.md双向差集 —— ✅ 成立(补 12 行 + 修 2 处不诚实 + 补 1 行索引)方法:
ls scripts/verify/ scripts/release/(*.py|*.sh)×grep .github/workflows/双向差集,再用 python 解析 checks.yml 的 job 缩进定位每条命中的 job ID + 行号,只登记「真有 workflow 调用」的。补登记的 12 条(每行都指向真实存在的脚本 + 真实 job,
verify-doc-ssot.py逐条校验,校验数 53→66 脚本路径 / 45→58 CI 文件)scripts/verify/verify-vulnerability-gates.shvuln-scan-js:2360,2373+vuln-scan-go:2406;自测validate:1981scripts/verify/verify-rust-advisories.shvuln-scan-rust:2445;自测:2449scripts/verify/verify-quality-debt-ratchet.pyvalidate:2084;自测:2087scripts/verify/verify-auth-dep-ownership.pyvalidate:2081scripts/verify/verify-migration-idempotency.pyvalidate:2027scripts/verify/verify-i18n-deadkeys.pyvalidate:2030scripts/verify/verify-oidc-code-ssot.pyvalidate:1971;自测:1974scripts/verify/verify-orchestrator-deps.pygo-edge:189;自测:194scripts/verify/verify-fixture-connection-pinning.pyvalidate:2059;自测:2062scripts/verify/verify-devserver-contract.pyvalidate:1956scripts/verify/verify-real-e2e-artifacts.pyreal-e2e-stack:1856scripts/verify/merge-coverprofiles.pygo-edge:113/go-hub:333每行的规则描述取自对应脚本的 docstring 原文(例如
verify-i18n-deadkeys.py的保守死键判据、verify-auth-dep-ownership.py的「同进程多 App 互相覆盖安全配置」、verify-migration-idempotency.py的 #2125 follow-up),不是我自己编的。旁证:
docs/architecture.md:112早已把verify-orchestrator-deps.py称为「机器门禁」,而 verifier-map 里查无此行 —— 正是「已接线但未登记」。顺带修的两处不诚实(差集反向侧发现)
scripts/e2e/run-real-e2e-lane.sh已接线checks.yml:1846(real-e2e-stack,dispatch-only),且该脚本:53/:371内部调用verify-real-e2e-lane-manifest.py做六字段合同自检。改为如实区分「lane 入口已接线(仅workflow_dispatch,不阻塞 PR)」与「verify-oidc-flow.py/verify-login-fixture-topology.py/verify-approved-real-preflight.py仍未被任何 workflow 调用」(这三个的未接线状态由grep .github/workflows/*.yml零命中实证)。|---|分隔行 ⇒ GitHub 会把这 4 行渲染成普通文本而非表格行(表格被截断)。删掉该空行使其并入主表。这是渲染缺陷,不是内容问题,四行原文一字未动。docs/architecture/README.md漏项:ls docs/architecture/有 13 个.md,子索引只列01-11,漏github-actions-ci-cd-policy.md;而主索引docs/architecture.md:101已登记它 —— 单侧缺口。补一行(并注明「无编号,主索引同样登记」)。28 行(预算 40)。verifier-map.md87 行(预算 120)。6.⚠️ 报告部分成立:6 条里只敢改 2 条
scripts/verify/quality-debt-baseline.json的issue归属 ——复核方式:
gh issue view <n>逐个读标题 + 正文 + 文件面,不照抄报告。原状:6 条 soft_gate 里 5 条issue都是1573。先确认
1573对这 5 条可证伪:gh issue view 1573= 「[P1] 建立 Hub lint finding fingerprint ratchet并移除整 job 软放行」,正文原话「当前 Hub golangci-lint 仍有约 69 个存量 finding」+「#1568 继续只负责 complexity exclusion,不混用 owner」⇒ 它是 Hub 专属,且明令禁止混用 owner。改了的 2 条(归属已确认)
frontend-desktop: Lintgh issue view 1575= 「[P2] 偿还 Desktop ESLint 债并移除 frontend-desktop lint 软门禁」;正文原话「quality-debt baseline 暂时把该条目指向 #1536,但 #1536 是治理机制任务……不能继续作为实际 lint 债的 owner」,完成条件含「quality-debt baseline 删除该 soft gate」⇒ 就是本条债的 owner。(reason 字段原本已写(#1575),只有issue字段是错的)frontend-web: Lint Webgh issue view 1581= 「[P2] 清零 Web ESLint warnings并让 Lint Web 对新增 warning fail-closed」;正文原话「#1575 只负责 Desktop ESLint,不覆盖 Web」。reason里的(#1575)一并改为(#1581)。第 1 条
go-edge: Lint= 1840 本来就对(gh issue view 1840= 「[P2] edge-server lint 棘轮补齐:基线登记 + fail-closed 门禁(对偶 #1812)」),未动。没改的 3 条(按任务书「若无法确认归属就不要改」,已登记 #2154「归属待确认」)
frontend-mobile: Lint (mobile rules)gh search issues用mobile eslint/frontend-mobile/Lint (mobile rules)/mobile lint 软均未找到 mobile lint 软门禁的专属 issue。reason 里的(#1575)同样无据,但没有可替换的已确认号validate: Verify i18n callsites ratchet (#1612)gh issue view 1612返回的是 PR「docs(progress): MASTER 同步 — #1535 合并状态与复杂度豁免偿清」,gh pr view 1612 --json files显示只改了docs/progress/MASTER.md一个文件,与 i18n 毫无关系。而仓内checks.yml:2126step 名、docs/governance/verifier-map.md:27、CHANGELOG.md:44三处一致写#1612⇒ 是全仓的 #1612 引用本身可疑,不是 baseline 单点笔误。无法从仓内或 GitHub 定位真号,不猜vuln-scan-rust: cargo clippy (advisory)(#1578);gh issue view 1578确实存在且是创建vuln-scan-rustjob 的那个 issue(「补 Rust advisory 门禁」),但其正文与验收条件全程不含 clippy(gh issue view 1578 --json body | grep -i clippy零命中),验收项讲的是 glib UB + advisory gate。「24 条 clippy warning 待归零」这笔债没有可确认的 owner issue(gh search issues clippy只命中 #2154 自身)这 3 条我故意留着已知错误的 1573 不用未确认号覆盖:把错号换成另一个猜出来的号,只会让下一个人更难发现它是错的。改为在 #2154 显式登记待裁决。
改动安全性复核:
verify-quality-debt-ratchet.py:232-236只校验issue存在且为正整数、不校验取值;:426-453的 historical ratchet 只比较review_by(延期需extension_reason),不比较issue/reason⇒ 本次改动不触发任何棘轮。改完按要求实跑:9 pass / 0 fail,并加跑负向自测scripts/verify/tests/verify-quality-debt-ratchet.Tests.py:15 tests OK。7.
AGENTS.md§12 加「CHANGELOG owner」——净减 2 行加(+2 行:1 行内容 + 1 行 markdown 空行),§12 发布流程:
与第 2 条的 CHANGELOG 声明互为指针,两处不会各自漂移。
删(-4 行,全部是「复制 owner 文档内容」因而最易漂移的行,逐条说明为什么已过期/冗余)
- 里面有什么:全表三列(规则/验证脚本/CI job)+ 维护规则;\无` 表示暂无机器验证,靠人工自觉,规则本身不因此失效。—— 与docs/governance/verifier-map.md:10**逐句重复**(原文:「下表列出有机器管的规则与其验证脚本、CI job。\无` 表示暂无机器验证,靠人工自觉,规则本身不因此失效。」)。verifier-map 才是映射 SSOT,AGENTS.md §9.5 自己第一句就写着「本节不复制长表」,却在下面复制了表的列结构与图例。- 权威范围:映射只描述“有没有机器管”,规则本身的权威仍是本文件;两者冲突时以本文件为准,并更新映射表。—— 同与verifier-map.md:10重复(原文:「映射只描述「有没有机器管」;规则本身的权威是 `AGENTS.md`,两者冲突时以 `AGENTS.md` 为准并更新本表。」);且优先级已由 §0 事实源优先级(AGENTS.md 第 2 位 / 其他专题文档第 5 位)确立。删后该权威声明仍在 verifier-map.md 单点存在,信息零丢失。3-4. 上述两条删完后 §9.5 只剩单元素 bullet 列表(格式噪声),把该 bullet 并入上一段,省掉 bullet 行与其前面的空行。
保留 §9.5 的 verifier-map 指针行本体 ⇒
check_verifier_map_owner()(要求 AGENTS.md 含docs/governance/verifier-map.md)仍 PASS。wc -l AGENTS.md= 284(改前 286,门禁上限 300)。净变化 −2,符合「净中性或净减少」。复核后判定「不成立 / 已过期」而跳过或改判的条目
verify-doc-ssot.py只禁根级文件ROADMAP.md(:100)、路径docs/roadmap(:109)、正则ROADMAP\.md(:200);没有任何规则禁「路线图」这个词,门禁本来就跑得绿docs/里确实没有路线图),故仍按事实改,改法换成门禁理由本身陈述的真事实(roadmap 在 GitHub issues)gh issue view 1581正文原话「#1575 只负责 Desktop ESLint,不覆盖 Web」#1612是 PR「docs(progress): MASTER 同步」,files只有docs/progress/MASTER.md;而仓内三处(checks.yml:2126 / verifier-map:27 / CHANGELOG:44)一致引用 #1612docs/developer-quickstart.md:122-123其实是正确的那一处门禁表(HEAD =
8f2ade25,base =dc7df53d= origin/master,rebase 后无冲突)python3 scripts/verify/verify-doc-ssot.pydoc SSOT ok;verifier-map 66 脚本路径 / 58 CI 文件全部存在;AGENTS.md 96 个反引号路径全部存在;DOC-README-PARITYPASSpython3 scripts/verify/verify-ci-gates.pyci gate policy okpython3 scripts/verify/verify-quality-debt-ratchet.pypython3 scripts/verify/tests/verify-quality-debt-ratchet.Tests.pypython3 scripts/verify/verify-project-skills.pyskills root absent (.agents removed) — whitelist gate trivially passes)python3 scripts/verify/verify-conventions.pyPassed: 1 | Failed: 0python3 scripts/verify/tests/verify-doc-entrypoints.Tests.pyRan 1 test … OK(证明主门禁没被我的改动弄钝)git diff --check origin/master..HEADwc -l AGENTS.mdvalidatego-edgego-hubwindows-gowindows-frontendbackend-requiredfrontend-requiredwc -l按纪律未跑:
go test、go build、vitest、coverage、全量 golangci-lint、docker、make *(4 核机 + 并行 lane)。golangci-lint 幽灵:本 lane 未跑 golangci-lint,未遇到指向已删除 worktree 路径的缓存幽灵 issue。
未验证项(诚实声明)
没有跑任何 CI→ 已验证(本条从「未验证」升级为「已验证」,PR 开出后回写):GitHub Actions run 33658111957 结果 22 SUCCESS / 17 SKIPPED / 0 非绿,7 个 required 聚合(validate/go-edge/go-hub/windows-go/windows-frontend/backend-required/frontend-required)全部 SUCCESS。validate是承载verify-doc-ssot.py+verify-ci-gates.py+verify-quality-debt-ratchet.py+verify-conventions.py+verify-project-skills.py的 job,它 SUCCESS ⇒ 本 PR 全部 9 个文件的改动在 CI 上被同一套门禁判绿,不只是我本地判绿。go-*侧也跑了(因为scripts/verify/**在changesjob 的go路径过滤里,baseline JSON 改动触发了 Go lane),go-hub的 golangci-lint + 覆盖率门禁 SUCCESS ⇒ 未出现缓存幽灵。顺带活体印证第 3 条的改法:
Vuln scan (pnpm audit prod+full)、Vuln scan (govulncheck)、Vuln scan (cargo audit)、全部frontend-*、Visual QA *、Design CSS syntax在本 PR 均为 SKIPPED —— 正是我写进SECURITY.md的「三个 vuln-scan job 都经changesjob 路径过滤触发,不是每次 push 全量扫描」的实时证据(本 PR 不含app/**改动)。docs/architecture/README.md新增行、verifier-map.md宏观四行并入主表后是否真的渲染成表格。gh issue view读到的是 issue/PR 的当前标题与正文,不能证明「该 issue 在软门禁被引入的那一刻就是 owner」。desktop→[P2] 偿还 Desktop ESLint 债并移除 frontend-desktop lint 软门禁 #1575 / web→[P2] 清零 Web ESLint warnings并让 Lint Web 对新增 warning fail-closed #1581 的判定依据是两个 issue 正文逐字点名了对应的 baseline 条目与 step 名,这是我能拿到的最强证据,但仍属文档考古而非当事人确认。v0.6.1与 master 历史脱钩这件事我只做了git merge-base --is-ancestor/git merge-base两个命令的验证,没有去查 release.yml 的历史 run 是否真的因此失败过,也没有验证 git-cliff 在无前序 tag 时的实际输出长度。它超出纯文档 lane 范围,只登记不动手。verify-doc-ssot.py/verify-ci-gates.py/verify-quality-debt-ratchet.py/verify-conventions.py/verify-project-skills.py及两个负向自测。需要人工裁决 / 后续 issue(已同步登记 #2154)
issue归属待确认:frontend-mobile: Lint (mobile rules)、validate: Verify i18n callsites ratchet、vuln-scan-rust: cargo clippy (advisory)(现值均为可证伪的 1573)。#1612引用可疑(checks.yml:2126 / verifier-map:27 / CHANGELOG:44 三处),需定位 i18n callsite ratchet 的真实接线 issue/PR。v0.6.1不在 master 祖先链上 ⇒release.yml:42tag-guard 与 git-cliff--latest的前序 tag 解析都受影响,下一次打 tag 前需裁决(重打 tag / 调整 cliff 调用 / 接受全量分组)。cliff.toml的^securitycommit parser 是死分支(提交类型白名单不含security):要么给白名单加security,要么删掉这个 parser 并改用 label/其它机制披露安全修复。本 PR 只把 SECURITY.md 的承诺改成与现状一致,没动 cliff.toml(属产品配置,非纯文档 lane 范围)。scripts/verify/tests/merge-coverprofiles.Tests.py与scripts/verify/tests/verify-real-e2e-artifacts.Tests.py存在于磁盘但没有任何 workflow 调用(grep .github/workflows/零命中)⇒ 两个负向自测是死的。我在 verifier-map 里因此没有把它们写成「负向自测」(只登记了脚本本体),避免制造新的假绿声明;是否接线请裁决。continue-on-error软门禁仍在 checks.yml 里活着(verify-quality-debt-ratchet.py的 zombie 检查 PASS 即证明这点),且两条的review_by都是2026-10-01。即「偿还 ESLint 债并移除软门禁」的 issue 关了、软门禁没移除。属治理不一致,非本 lane 范围。流程事故记录(不影响代码,但影响交付物可信度,故如实记)
开出本 PR 后、往 #2154 贴登记评论时,另一条并行 lane(Lane A,#2154 评论
5513247382)在同一分钟覆写了/tmp/pr-body.md—— 两条 lane 用了同一个临时文件名。后果与处置:gh pr create在覆写发生前已执行完毕。事后用gh pr view 2237 --json body回读实测 21035 字符,首句「## 这条 lane 是什么」、末句「Closes 无(本 PR 是 [整理] 静态审计 backlog 跟踪(探索 lane 剩余项) #2154 的切片 C…)」,且grep -c "toast.actionUnavailable"(对方正文特征串)= 0 ⇒ 内容是我的、完整的。gh api -X PATCH .../issues/comments/5513238457原地编辑修正(不新贴第二条制造噪声),并复核修正后正文里对方 lane 的 5 个特征串(§6.4/§6.5/desktop forward/regenerate 是否另开 lane/i18n 资源面 lane)全部 0 命中、我的 4 个结构节各 1 次。/tmp/laneE-doc-honesty-2237/。这与AGENTS.md「一个 worktree 同时只放一个写 agent」是同一类风险,但发生在 worktree 之外的共享/tmp,现有规则没覆盖到。Closes 无(本 PR 是 #2154 的切片 C,#2154 由主机侧统一收口,不在此自动关单)。