feat(grok): add strict repository-read co-presence profile - #826
Conversation
独立窄审 · PR #826 (Draft) — repo-read stacked on #825Verdict: CLEAN — no BLOCKER, no MAJOR, no MINOR. Two informational notes at ⑦. Reviewer: 通信IM马 (independent, read-only). Extracted PR tree via Scope framing: this PR is stacked on #825 — it adds a third Dependency: MUST NOT merge before #825. #825 source ( Provenance
Per-file drift Report source_commit anchor honesty: report L6 declares Item-by-item① exact
|
我把这个 PR 与 #836 的合成体放进真门跑了一遍:绿背景:这两个 PR 都改 真合 + 跑 合成体在这道门上是绿的。 这不是对两个 PR 各自的评审意见,只回答一个问题: 顺带一个值得知道的对照(已单独立为 #865)同一棵合成体,在宿主机上跑 所以在本机跑这两个文件的单测,结果不可用;要判断这块有没有回归,只能走 Docker 门。 (只读:worktree 已 |
|
Draft,不合,只留 review。这个 PR 的能力边界建得很扎实,三条我都追到代码确认了 —— 然后发现一条没人守着的隐含前提。 一、三条我核过的(都成立)① if (tools === undefined || tools.length === 0) return "commhub-only";
if (tools.length === 1 && tools[0] === "WebSearch") return "x-search";
if (tools.length === 3 && tools[0]==="Read" && tools[1]==="Grep" && tools[2]==="Glob") return "repo-read";
throw new Error('… supports only the exact tools profiles …');任何没被逐字列举的组合都抛,不是「取交集」也不是「按前缀放行」。注释那句「Co-presence deliberately does not accept a general tool allowlist」把设计意图写清楚了。 (顺带:它对顺序敏感 —— ② env 是无条件覆盖的,ambient 值改不了它: const GROK_COPRESENCE_CAPABILITY_PROFILE = GROK_COPRESENCE
? selectGrokCopresenceCapabilityProfile(…)
: "commhub-only";
process.env[GROK_COPRESENCE_PROFILE_ENV] = GROK_COPRESENCE_CAPABILITY_PROFILE; // ← 无条件不是 ③ 边界是进程级的,不是每轮重算的 —— 注释写着「Policy/runtime modules are dynamically imported only after this boot-time pin and never re-read the node config per turn」。这一条我实测了(见下)。 🔴 二、③ 的正确性依赖一个没有任何东西在守的前提我把 cli.ts 里所有对 copresence 模块的引用列了出来: 结论成立。 但请注意它为什么成立:因为没有人在 cli.ts 顶部静态 import export const GROK_COPRESENCE_CAPABILITY_PROFILE = readPinnedGrokCopresenceCapabilityProfile();⇒ 一旦有人在 cli.ts 顶部写下 而它会是静默的:代码照样编译、类型照样对、单测里如果先设 env 再 import 也照样绿 —— 只有在「ambient 里恰好有值」的那台机器上才会表现出来,而表现形式是能力面被悄悄放宽或收紧。 建议加一道很便宜的门:断言
三、一处值得肯定的克制
「打开读能力」和「限制读的范围」被当成两件事分别处理了 —— 只做前者就是把整台机器的文件交出去。这一步很容易漏。 四、和 #830 的关系这个 PR 定义了 allow 侧( |
…1004) * fix: fail closed on broken Grok CommHub MCP * test: exercise Grok CommHub MCP readiness handshake * test: add isolated Grok MCP readiness gate * test: exercise the real Grok MCP doctor * docs: report Grok vendor MCP readiness * test: gate Grok readiness on product startup path * test: keep Grok gate dependency layers source-independent * test: anchor Grok readiness to runtime events * docs: record exact Grok MCP readiness evidence * fix(grok): 把 #825 的 MCP 就绪 fail-closed 落到 main(rebase + 补两处今天新门抓到的) #825(`fix(grok): fail closed when CommHub MCP is not ready`)从 2026-08-15 起是 draft,落后 main 120 个提交,上次 CI 只跑了 10 个 check(今天一个 PR 跑 13–23 个)。 它是 grok 栈 `#825 → #826 → #830 → {#836,#867}` 的**唯一出口**,它不落地, 上面四条谁都到不了 main。 **没有 force-push 那条共享 draft 分支** —— 本 PR 从我自己的分支发,内容是它那 9 个 补丁 rebase 到 `origin/main`(`rc=0`,0 冲突)再加下面两处修补。 ## 今天新合的两道门抓到了它两处 上一轮我测过「#825 引入新失败 0」,那句话当时是真的 —— 但那两道门是**今天晚些时候** 才合进 main 的,门槛抬高之后它就红了: 1. `test-suite-registration`(#1003) `tests/test813-grok-mcp-readiness/` 是新增套件,没有任何 CI 会跑它。 🔴 这条特别值得修而不是豁免:**#825 的目的就是「MCP 没就绪时 fail closed」, 而验证这件事的套件如果不进 CI,fail-closed 这个保证就没有任何东西持续守着。** 照 test831 的同构做法接进 qa.yml:两处 `paths` + build/run 两个 step。⚠️ 它的 Dockerfile 收的 build-arg 叫 `SOURCE_COMMIT`(不是 831 的 `TEST831_SOURCE_COMMIT`),容器里由 `ENV TEST813_SOURCE_COMMIT` 承接 —— 照抄 831 的参数名会静默拿不到值。 2. `doc-symbol-pins`(#1002) `docs/message-lifecycle.md` 把 `shouldSkipMessage` 钉在 `cli.ts#L4639`, 而 #825 给 cli.ts 加了 54 行,真值现在是 **4662**。 文本 `cli.ts:4639` 和 URL `#L4639` 是同一句话的两副面孔,两处都改 —— 只改 URL 会留下渲染出来给人读的那个错数字。 ## 验证 rebase 到 origin/main rc=0,0 冲突,9 个补丁 test-suite-registration rc=0 suites=198 registered=34 orphans=164 new=0 doc-symbol-pins rc=0 l1-paths-sync / qa-trigger-coverage / workflow-structure / docs-integrity / no-escaped-comments 全部 rc=0 `registered` 从 33 变 34、`orphans` 仍是 164 —— 新套件是接进 CI 了,不是塞进基线蒙混。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(test813): 一条从写下起就是空的变异 —— 接进 CI 的第一次运行就把它照出来了 把 test813 接进 CI(本 PR 上一个提交)之后,它在 GitHub runner 上第一次运行就红: MCP_READINESS_PASS tools=commhub_get_all_status,commhub_send_message,commhub_send_task,commhub_upload_file FAIL: mutation survived: upload-tool-removed MCP_READINESS_PASS tools=…(同一份列表,commhub_upload_file 仍在) 不是产品没拦住,是**变异根本没发生**: sed -i '/^[[:space:]]*"commhub_upload_file",[[:space:]]*$/d' agent-network/src/node-server.ts 那个模式要求整行只有 `"commhub_upload_file",`,而真实那行是 ` name: "commhub_upload_file",`(对象字面量的字段)。实测命中 **0** —— 在 origin/main 上也是 0,**它从写下那天起就没匹配过任何一行**。 套件此前不在任何 CI 里(#861 说的 164 个孤儿之一),所以没人见过它红。 删整行会破坏对象字面量语法,改成改名: sed -i 's/name: "commhub_upload_file"/name: "commhub_upload_file_MUT"/' … 干跑验证:文件确实变了(`243: name: "commhub_upload_file_MUT",`),已还原。 ## 顺带堵住这一类,不只这一条 给 `expect_red` 加空变异防护:跑完变异命令后,若两个源文件**都**没被改动就直接判红, 并说清楚是「sed 模式和源码对不上」而不是「产品没拦住」。 这两种结论指向完全不同的下一步 —— **去改产品 vs 去改测试** —— 而它们在没有这道防护 时打印出来是同一句 `mutation survived`。 同一个套件里另外三条 sed 都逐条查过,各自命中 1(其中打 fake-grok.mjs 那条还自带 `grep -Fxc … -eq 1` 基数守卫),只有这一条是空的。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: t <internlmorg@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(grok): add strict repo-read copresence profile * docs(tests): record restacked Grok repo-read evidence * feat(grok): 把 #826 的 strict repo-read 共存档位落到 main grok 栈 `#825 → #826 → #830 → {#836,#867}` 的第二层。#825 已由 #1004 落地 (main `89a6164e`),所以这一层现在可以直接对 main 落。 **没有 force-push #826 那条共享 draft 分支**,本 PR 从我自己的分支发。 ## rebase 方式:只取它自己那两个提交 直接 `git rebase origin/main` **会冲突** —— 冲在 `436b4bdb fix: fail closed on broken Grok CommHub MCP`,那是 **#825 自己的提交**,已经以 squash 形式在 main 上了,而 #826 的历史里还带着它。 正确做法是把它自己那部分接到 main 上: git rebase --onto origin/main <#825-head> <#826-head> rc=0 它自己的两个提交: 4496835 feat(grok): add strict repo-read copresence profile 1149676 docs(tests): record restacked Grok repo-read evidence 结果 12 files changed, 284 insertions(+), 36 deletions(-)。 ## 今天这套门抓到一处 `doc-symbol-pins`:`docs/message-lifecycle.md` 把 `shouldSkipMessage` 钉在 `cli.ts#L4662`,而本层又往 `cli.ts` 加了行,真值变成 **4669**。文本和 URL 两处都改。 🔴 **这是同一条 pin 今晚第二次漂**(#1004 刚把它从 4639 改到 4662)。 往 `cli.ts` 这种热点文件钉**行号**,几乎每个碰它的 PR 都会把它顶漂 —— 改成只钉符号、不钉行号才是根治,但那是另一条改动,不在这里顺手做。 其余全绿:test-suite-registration(suites=198 registered=34 orphans=164 new=0)/ doc-source-pins / no-escaped-comments / no-memory-slugs / home-path-baseline / public-script-safety / l1-paths-sync / copresence-profile-pin。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: t <internlmorg@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
这条的内容已落到 main(#1006,
|
| 文件 | 新增行 | 在 main 上找不到 |
|---|---|---|
agent-network/src/grok-copresence-disclosure.ts |
11 | 0 |
agent-network/src/grok-copresence-disclosure.test.ts |
15 | 0 |
agent-node/src/cli.ts |
16 | 0 |
agent-node/src/runtime/grok-build-cli-home.ts |
13 | 0 |
agent-node/src/runtime/grok-build-cli-home.test.ts |
8 | 0 |
agent-node/src/runtime/grok-copresence/policy.ts |
8 | 0 |
agent-node/src/runtime/grok-copresence/profile-selection.ts |
25 | 0 |
agent-node/src/runtime/grok-copresence/profile-selection.test.ts |
13 | 0 |
agent-node/src/runtime/grok-copresence/profile-process-probe.ts |
13 | 0 |
agent-node/src/runtime/grok-copresence/profile-process.test.ts |
30 | 0 |
agent-node/src/runtime/grok-copresence/runtime.ts |
1 | 0 |
docs/tests/report-grok-copresence-repo-read-stage2.txt |
95 | 0 |
13 个文件、248 行新增,一行不缺。
落地时改的一处
doc-symbol-pins 抓到 docs/message-lifecycle.md 把 shouldSkipMessage 钉在
cli.ts#L4662,而本层又往 cli.ts 加了行,真值变成 4669。文本和 URL 两处都改。
(我在 #1006 正文里曾写「改成只钉符号不钉行号才是根治」,那句已撤回 ——
实测去掉 #L 会让这条 pin 整个掉出那道门的正则,扫到 15→14 / 判定 5→4 / rc 1→0,
是把覆盖删掉而不是修好。详见 https://github.com/sleep2agi/agent-network/pull/1006#issuecomment-5329877912)
栈的后续
#830 → {#836, #867} 三层的落地手法已在本地干跑验证:
#830 git rebase --onto <main> <#826-head> <#830-head> rc=0,7 个提交,七道门全绿
#867 同样形状 rc=0,7 个提交
#836 🔴 同样形状会失败 —— 它的区间有 49 个提交,其中 48 个已经在 main 上
(分支上有过一次 merge main)。改用 cherry-pick 它自己那 1 个 fc221fc5,rc=0
Stacked on #825. This supersedes #820; do not merge before #825.
Goal: move the live
通信狗from communication-only/x-search validation toward bounded AgentNetwork code review, without ACP SDK, shell, write, web, media, or subagent capability.Frozen coordinates:
8186b79de8e2f904c28bec268d93a523503a6845449683586a5a2ba44e99eb8c595be25d7467c967114967626f20c7ef036c3d0e0dab295e1f983a8921cd907323b374f2df6c722d314c05597ea0c093sha256:d46c3dc573bba4e282750df6877fc893e490cc5d2fdfeab5c2de2e0c246dae53sha256:459b3239eae27afb6aa6a4cac7375f45c10c2df223083352187c92ca820cd60fExact-source Docker evidence:
1284 pass / 0 fail / 4406 expect / 91 files47/0/364; build PASS5/0/30; tsc PASSanet-strict→anet-workspacefailure17/17 MATCHThis restack preserves #825's canonical Bun resolution and four-tool doctor. It intentionally removes the superseded internal bare-Bun resolver from #820.
No package was published and no live node was restarted or reconfigured. A single-node pilot remains gated on #825 review/merge, this PR's independent review, rollback coordinates, a fresh Grok session, kernel-enforcement observation, and a real read+CommHub UAT.
Full report:
docs/tests/report-grok-copresence-repo-read-stage2.txt.