Skip to content

docs(verification): #643 E7 打包版取证准备 —— 探针 + 证据骨架 + 一个阻断项 - #646

Merged
jinjunnn merged 4 commits into
alphafrom
verify/643-e7-packaged-live
Jul 28, 2026
Merged

docs(verification): #643 E7 打包版取证准备 —— 探针 + 证据骨架 + 一个阻断项#646
jinjunnn merged 4 commits into
alphafrom
verify/643-e7-packaged-live

Conversation

@jinjunnn

Copy link
Copy Markdown
Owner

本 PR 只交付取证准备;实际取证待 owner 登录后执行。不要合并到取证完成为止(或按 owner 判断先合准备、再补结果)。

Refs #643 — 故意不写 Fixes:#643 的证据还没采到。

为什么

#223 的 AC5「packaged 真调用与 keyless fallback 均有证据」需要已登录的真机,而登录只能由 owner 本人完成。
所以这一票能提前做完的只有「取证之前的一切」,做到 owner 一坐下就能一次跑完。

交付

  1. 打包应用:ship:mac 重打并真机启动验(alpha@94a76b669)。装机版 app.asar 的 sha256
    钉进探针 —— 在错的构建上跑出来的绿是假绿,第一条判据就把它挡住。
    (装机版此前是 2026-07-25 的 514ae7438,早于 fix(e7): 云搜索主权闸下沉传输层与插件钩子 —— 修冷启动时序、失败诚实化、kill-switch 真能关 #639,不能用。)
  2. 探针 docs/verification/2026-07-27-e7-packaged-live/probe.ts —— 两相位,各自对错误的登录态 fail-closed:
    • 默认 = 登录态取证;未登录 → blocked + exit 2 + 零证据产出。
    • --keyless = 登出态取证;仍在登录态 → blocked + exit 2。
    • 故意不做自动识别:会自己切相位的探针永远报不出「未登录,无法取证」。
    • 每项自带运行前定好的判据(JSON 里的 criterion);造不出来的失败态记 not-producible + 理由 + L1 覆盖位置,不记成绿
    • 零密钥:用的每个凭证都是打包应用自己写进 <userData>/alpha-secrets/ 的,运行时读、写盘前 redact()
    • 幂等:只新建 scratch 会话与读取状态。
  3. 证据骨架 docs/verification/2026-07-27-e7-packaged-live/README.md —— 被测件指纹、复现步骤(可复制粘贴)、
    逐项判据表(登录态那半留空)、边界(不证明什么)。

准备阶段已实跑出来的两件事

待 owner 裁决

  • 402 采不采:[E7][VERIFY] 打包真调 + keyless 兜底 + 计费/失败证据(L2/RC) #643 正文写 out-of-scope 且「失败集证据无 402 项」;基线票 6 的 2026-07-25 更正写相反
    (402 已是可采集的真实失败态,须含)。探针按 Issue 正文执行,冲突同时记进 JSON。
  • 云工具真实 id:MCP catalog 会拼 server 名前缀(cloud_ + 远端名),而实测远端名已经是
    cloud_web_search ⇒ 引擎侧可能是 cloud_cloud_web_search(全仓 grep 零命中)。
    探针记录实测值不假定;若不一致,所有按字面量下的闸需另开 CODE 票重核。

文档影响

docs/verification/ 新增一个带日期的目录(证据类,maintain-repository-docs 契约)。
未改动 docs/verification/ 下任何既有目录。无状态/优先级写进 Markdown。

本地闸门

  • north-star:diff 仅 docs/verification/2026-07-27-e7-packaged-live/**,零 UPSTREAM_PATHS 命中 ✅
  • docs gate:python3 scripts/check-doc-links.py → 7 个相对链接全解析 ✅
  • typecheck / unit:docs-only 改动,无 tsconfig 覆盖 docs/,不受影响

🤖 Generated with Claude Code

jinjunnn and others added 2 commits July 27, 2026 21:34
#223 AC5「packaged 真调用与 keyless fallback 均有证据」的取证需要 owner 本人的登录态,
只有取证之前的一切可以先做完。本 PR 交付那一切:

- 打包应用重打并真机启动验(alpha@94a76b669;装机版 app.asar 的 sha256 钉进探针,
  在错的构建上跑出来的绿会被第一条判据挡住)。
- 一个两相位探针:默认 = 登录态,`--keyless` = 登出态,**各自对错误的登录态 fail-closed**。
  故意不做自动识别 —— 会自己切相位的探针永远报不出「未登录,无法取证」。
  每项自带运行前定好的判据,输出机器可读 JSON;造不出来的失败态记 `not-producible` + 理由,
  不记成绿。探针零密钥:用的每个凭证都是打包应用自己写进 alpha-secrets 的,写盘前经 redact。
- 证据骨架:逐项判据表(登录态那半留空待 owner)、复现步骤、边界。

准备阶段已实跑出两件事:

1. **未登录自检**:登录态相位 blocked + exit 2,零证据产出(results/logged-in-*.json)。
2. **一个阻断项**:登出态 keyless 真调在本构建上是坏的。`readBoundedBody` 用的
   `Stream.runForEachWhile` 在谓词恒真时仍只读第一个 chunk(实测 4,090 / 18,063 字节),
   于是任何多 chunk 的搜索响应被截断 → `invalid_response`。#639 之前该处是 `response.text`,
   属那一刀带进来的回归;本仓 read.ts:143-145 早已记过同一个 API 的雷。
   修复归一张 CODE 票,不在本 VERIFY 票里动源码。

另记两项待裁决:402 的采集范围(Issue 正文与基线票 6 的 2026-07-25 更正相反),
以及云工具在引擎里的真实 id 可能是 `cloud_cloud_web_search`(MCP catalog 会拼 server 名前缀)。

Refs #643
上一份装机产物(`94a76b669` / `8706d0c4…`)早于 #648 的 `readBoundedBody` 修复,
K1.5(登出态 keyless 真调)在其上必红 —— #643 的 AC2 不可能转绿。在当前 alpha
HEAD `e578e00ae` 上重跑 `ship:mac` 并重新装机,证据目录的指纹与 `probe.ts` 的
`PINNED_ASAR_SHA256` / `PINNED_COMMIT` 同步换成新产物
(`60589c59c58e44ac0daede93fc7397a8a04365f5345eac4312e205a0d8f48e44`)。

真机启动已验:main / renderer / GPU / NetworkService / NodeService 五进程齐,
CDP 列出 `oc://renderer/index.html` page target(窗口真开),
`server ready { url: 'http://127.0.0.1:50360' }`,crash.log 无崩溃。
本仓两个打包坑(source-only contracts-consumer 留原始 .ts、eager ajv `new Function`
撞 renderer CSP)均未复现。

阻断项解除,用两条独立判据确认「打包版里真的生效」而非「源码合了就假定」:
1. 解包 app.asar,`out/main/chunks/node-*.js` 的 `McpWebSearch.readBoundedBody`
   确为 `Stream.runForEach` + `catchTag("BodyCapReached")`;
2. 真机跑 `--keyless`:K1.5 `completed`,拿到完整多 chunk 的真实搜索结果,exit 0、
   10/10 必需项通过。

两条反向 fail-closed 自检也在新产物上复验:登出态跑登录态相位 → blocked + exit 2;
登录态跑 `--keyless` → blocked + exit 2。两者都只记前置、零证据产出。

未改动源码,未改动 `docs/verification/` 下任何其它目录;旧产物的原始记录
(`*20260727T0952*.json`)保留不动。登录态那一半仍待 owner 本人执行。

Refs #643

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jinjunnn
jinjunnn force-pushed the verify/643-e7-packaged-live branch from 9c29a4f to 337d9b3 Compare July 28, 2026 01:42
@jinjunnn

Copy link
Copy Markdown
Owner Author

重打包到 e578e00ae —— 阻断项已解除,登出态那一半跑完了

原 PR 描述里那个阻断项(readBoundedBodyStream.runForEachWhile 只读首个 chunk → keyless 真调拿到截断 JSON)
的修复已合入 alpha:e578e00ae(PR #648 / ac#647)。上一份装机产物早于它,K1.5 在其上必红、AC2 不可能转绿 ——
所以不是「重跑一次试试」,是被测件本身不合格。已在当前 alpha HEAD 上重打包并重新装机。

新产物

基线 commit e578e00ae(alpha,工作树干净)
sha256(app.asar) 60589c59c58e44ac0daede93fc7397a8a04365f5345eac4312e205a0d8f48e44
构建时间 2026-07-27T21:31:42 -0400
应用版本 / 引擎 0.1.2 / 1.17.13

(旧:94a76b669 / 8706d0c44e17d475ab136717ba9cbbbfad4c968ae34689ed377fdecc19fb43ff。)
README.mdprobe.tsPINNED_ASAR_SHA256 / PINNED_COMMIT 已同步 —— 不换的话第一条判据会直接把 owner 挡住。

真机启动验

五进程齐(main / renderer / GPU / NetworkService / NodeService sidecar);CDP 列出 oc://renderer/index.html
page target(窗口真的打开);logs/20260728T013220/main.logserver ready { url: 'http://127.0.0.1:50360' };
同目录 crash.log 只有 crash reporter started。本仓两个打包坑均未复现 —— asar 里 contracts-consumer
没有以原始 .ts 被运行时 require(启动无崩溃),renderer.log 只有既有的 connect-src 源列表告警、无 eval 被拒。

阻断项:已解除 ✅

刻意用两条独立判据确认修复在打包版里生效,而不是「源码合了就假定」:

  1. 解包 app.asar —— out/main/chunks/node-*.js 里的 McpWebSearch.readBoundedBody 确为
    Stream.runForEach + catchTag("BodyCapReached"),打进去的是修好的那份。
  2. 真机跑 --keyless —— K1.5 status === "completed",输出是完整的多 chunk 真实搜索结果:
[PASS] K1.1  no alpha cloud MCP server is registered while logged out
[PASS] K1.2  the local keyless websearch is no longer denied once the platform stops paying
[PASS] K1.3  a BYOK tool-calling model is available to drive the keyless call
[PASS] K1.4  the local keyless websearch tool is offered to the model again
[PASS] K1.5  a real keyless web search actually returns results
[PASS] K1.6  if the keyless call fails, the failure reaches the model as a discernible error

checks: 10  required-failures: 0        EXIT=0

K1.5 实测 outputHead:Title: 7dad00e feat(opencode): Phase 4 … URL: https://github.com/… —— 真结果,不是截断 JSON。
驱动模型是 BYOK 的 deepseek-byok / deepseek-v4-flash(登出态本来就该走自己的钥匙)。

两条反向 fail-closed 自检也在新产物上复验

自检 结果
登出态跑登录态相位(默认) [BLOCKED] P0.4 + EXIT=2,只记 P0.1–P0.3 前置,零证据产出
登录态跑 --keyless [BLOCKED] K0.4 + EXIT=2,同样零证据

这条闸是防止取证脚本自欺的关键,重打包后两个方向都复验过。

本地闸门

  • north-star:diff 仅 docs/verification/2026-07-27-e7-packaged-live/**,UPSTREAM_PATHS 命中 0
  • docs gate:python3 scripts/check-doc-links.py → 10 个相对链接全解析 ✅
  • typecheck / unit:docs-only 改动,无 tsconfig 覆盖 docs/(pre-push 钩子亦全绿)

机器当前状态 / owner 还需要做什么

应用已装新产物、ALPHA_CDP=1 已开、当前处于登出态(为跑 keyless 相位而登出,走的是应用自己的
「退出登录」路径;BYOK 钥匙没动)。owner 只剩登录 + 跑一条命令:

# ① 在应用里登录(平台代付模式),等模型目录出来
# ② 登录态取证
cd ~/app/alpha-code && git checkout verify/643-e7-packaged-live && git pull
bun docs/verification/2026-07-27-e7-packaged-live/probe.ts

若应用被关掉了,先 pkill -f "/Applications/alpha-code.app" ; sleep 2 ; ALPHA_CDP=1 open -a /Applications/alpha-code.app

仍待 owner 裁决的两项没变:402 采不采(#643 正文 out-of-scope vs 基线票 6 的 2026-07-25 更正)、
以及云工具在引擎侧的真实 id(P1.3/P1.4 记录实测值、不假定)。

未合并、未关闭 #643 —— 登录态那一半的证据还没采到。

owner 2026-07-27 在 #643 上裁定 402「采」(多采一项没有害处,漏采要再叫 owner 一次),
但上一轮探针仍按 Issue 正文走 out-of-scope —— 裁决没落进代码。本次补上。

**P3.7 从「跳过」改成真打的判据**,且不预设结论:发一次带真 bearer 的
`POST /v1/tools/web_search`,按运行前定好的规则判读 ——
402 ⇒ pass;200 ⇒ `not-producible`(账户被预授权通过,这本身就是「今天产生不了」的
可观测证据,带实测 balanceFen/walletUsedFen/plan 作理由);其它状态 ⇒ fail(LOUD)。
不记成绿,也不静默跳过 —— 与 P3.4/P3.5/P3.6 同一套处置。

可产生性已按平台源码判定(alpha-platform `packages/gateway/src/worker.ts`):
- 臂 A(per-job 预算耗尽)**桌面端不可达** —— 需 `auth.via === "job"` 的 job token
  (`lib/tenant-auth.ts:114-123`);桌面端拿的是 route-purpose 绑定 JWT,jobId 恒空 ⇒
  `perJobPrecall` 直接 pass/enforced:false。与 P3.4 同源。
- 臂 B(`accountPreauth` 拒绝)只在账户余额+会员额度双空时触发;估价是路由常量,
  客户端无调价杠杆。把账户打空是破坏性计费变更,且会连带打红同轮的 P2.1/P2.2/P2.3 ——
  402 证据与 AC1/计费证据不可能在同一账户形态下同时取到。**owner 无需为 402 做额外操作。**
映射本身由 L1 覆盖:`packages/opencode/test/tool/alpha-websearch-failure.test.ts`。

`#643` 正文冲突已消:out-of-scope 与退出条件两处按裁决改成与基线票 6 的 2026-07-25
更正一致(须含 402,产生不了则记 not-producible)。

README §7 补上 **`MISMATCH` 的判读方法** —— 此前只记录不解读,等于把判断留给一个没有
上下文的人。现在写清:出现 MISMATCH 意味着所有按 `cloud_web_search` 字面量下的闸
**当前是空闸门**(盯着一个引擎从不注册的工具名),并逐个列出要重核的闸(permission deny /
ext `tool.execute.before` / catalog 拼名)、下错了各会怎样、以及判据必须是「用真实 id
跑一遍确认真的命中」而不是 grep 到字面量。

证据同步重跑,使committed 证据由 committed 探针产出:
登出态相位在新产物上独立两遍均 10/10(K1.5 不是侥幸);两条反向 fail-closed 自检复验通过。

本地闸门:north-star UPSTREAM 命中 0;docs gate 11 个相对链接全解析。
未改源码,未改 `docs/verification/` 其它目录,未动应用登录态(保持登出)。

Refs #643

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jinjunnn

Copy link
Copy Markdown
Owner Author

402 裁决已落进代码 + MISMATCH 判读方法 —— 现在可以喊 owner 了

上一轮的报告把 402 写成「carry over unchanged」,那是漏做:owner 2026-07-27 已在 #643裁定「采」,
但探针还在按 Issue 正文走 out-of-scope。已补上。

1. P3.7:从「跳过」改成真打的判据,且不预设结论

不再用余额去推断结果 —— 发一次带真 bearer 的 POST /v1/tools/web_search,看它真的回什么,
按运行前定好的规则判读:

观测 记法 含义
HTTP 402 pass(required) 真拿到 402 证据
HTTP 200 not-producible(非 required) 账户被预授权通过 —— 这本身就是「今天产生不了 402」的可观测证据;JSON 带实测 balanceFen/walletUsedFen/plan 作理由
其它状态 fail(required) 意外状态 LOUD,先查清再接受这一轮

不记成绿,也不静默跳过 —— 与 P3.4/P3.5/P3.6 同一套处置。

2. 可产生性:按平台源码判定,结论是 owner 无需额外操作

依据 alpha-platform packages/gateway/src/worker.tswebSearchHandler,只有两条臂能出 402:

桌面端可达?
A. per-job 预算耗尽(perJobPrecallkind:"over") 不可达。需 auth.via === "job" 的 job token(lib/tenant-auth.ts:114-123,JOB_TOKEN_SECRET 签、claims 带 job_id);桌面端拿的是 route-purpose 绑定 JWT(via:"jwt"),auth.jobId 恒空 ⇒ perJobPrecall 直接 pass/enforced:false。与 P3.4 同源:桌面端铸不出那个形状的凭证
B. accountPreauth 拒绝 仅当账户余额与会员额度双空。估价是路由常量 BILLABLE_ROUTES[…].estimatedCostUsd,请求体只有 {query,max_results},客户端没有任何调价/调额杠杆

所以:没有可以临时切的开关,owner 不需要为 402 做任何额外操作。把账户打空去凑 402 是对 owner
计费状态的破坏性变更,而且会连带让同一轮的 P2.1/P2.2(真调)与 P2.3(计费)一起失败 ——
402 证据与 AC1/计费证据不可能在同一个账户形态下同时取到。探针会照实记录当天的账户形态。
映射本身(两条臂 → payment_required)由 L1 覆盖:packages/opencode/test/tool/alpha-websearch-failure.test.ts

⚠️ 一个副作用先说清楚:账户有额度时这一条会真的成功搜一次,即多一笔 web-search 流水。
它发生在 P2.3 量完钱之后,不影响 P2.3 的差分;但读账本会看到 3 笔而非 2 笔,第 3 笔就是它。

3. #643 正文冲突已消

out-of-scope退出条件 两处已按裁决改成与基线票 6 的 2026-07-25 更正一致
(「须含 402 项」;产生不了则记 not-producible + 实测账户形态 + L1 覆盖位置)。
下一个读票的人不会再撞见这处矛盾。

4. MISMATCH 判读方法 —— 写进 README §7

此前只记录不解读,等于把判断留给一个没有上下文的人。现在写清:出现 MISMATCH 意味着所有按
cloud_web_search 字面量下的闸当前是空闸门(盯着一个引擎从不注册的工具名),并逐个列出:

要重核的闸 位置 下错了会怎样
permission deny(云优先时抑制本地 keyless) packages/ui-mac/src/main/cloud-web-search.ts 抑制不生效或误伤,P1.5/P1.7 的绿变成假绿
ext 的 tool.execute.before 钩子 packages/ext/src/ kill-switch(#223 AC4)拦不住真实工具名 = 关不掉
引擎侧拼名 packages/opencode/src/mcp/catalog.ts:117-119 模型看到的工具与闸盯的工具不是同一个

重核判据不是「grep 到字面量」,而是用真实 id 跑一遍这三个闸并确认真的命中。探针的 MISMATCH
note 也改写成同样的口径,并指向 README §7。

5. 证据重跑 + 本地闸门

committed 证据由 committed 探针产出:登出态相位在新产物上独立跑了两遍,均 10/10
(K1.5 不是一次侥幸);两条反向 fail-closed 自检(登出态跑登录态相位 / 登录态跑 --keyless)
blocked + EXIT=2、零证据产出。

  • north-star:diff 仅 docs/verification/2026-07-27-e7-packaged-live/**,UPSTREAM_PATHS 命中 0
  • docs gate:11 个相对链接全解析 ✅

owner 的最终步骤

应用已装新产物(e578e00ae / 60589c59c…)、ALPHA_CDP=1 已开、保持登出态
只剩登录 + 一条命令,402 不需要任何额外动作:

cd ~/app/alpha-code && git checkout verify/643-e7-packaged-live && git pull

# ① 在应用里登录(平台代付模式),等模型目录出来

# ② 登录态取证 —— 一条命令跑完(含 402 判据,无需额外操作)
bun docs/verification/2026-07-27-e7-packaged-live/probe.ts

若应用已被关掉,先:

pkill -f "/Applications/alpha-code.app" ; sleep 2
ALPHA_CDP=1 open -a /Applications/alpha-code.app

未合并、未关闭 #643 —— 登录态那一半的证据还没采到。

owner 亲自登录后跑完登录态相位。23 项判据:AC2(登出态 keyless)已在前一轮通过;
本轮登录态暴露六条必需红,其中两条是真缺陷、两条是探针判据缺陷:

真缺陷
- 云 MCP tools/call 恒 forbidden:cloud_web_search 要 purpose=model.invoke,
  而桌面云通道令牌 ALPHA_CLOUD_TOKEN 恒为 cloud.dispatch(alpha-platform#131)
- /mcp 传输层零鉴权,tools/list 与账户无关 ⇒ P1.3 那条 PASS 是空绿(alpha-platform#132)

探针判据缺陷(alpha-code#651)
- P1.5 断的是「每个 agent 配置键有 deny」这个机制,而有效判决本来就全是 deny
- P1.2 断言观测点错:引擎加载时已解析 {file:},/config 天然看不到 file ref
- P3.8 判据构造上不可满足(工具已被 Permission.disabled 过滤,模型拿不到)

另:引擎侧真实工具 id 实测为 cloud_cloud_web_search(双前缀),按字面量
cloud_web_search 下的 permission 闸因此是空闸门(alpha-code#650)。

#643 不能关:AC1 待 #131 修复并手动部署后重取,AC3 待 #651 收敛判据。
@jinjunnn
jinjunnn merged commit 99cc303 into alpha Jul 28, 2026
6 checks passed
@jinjunnn
jinjunnn deleted the verify/643-e7-packaged-live branch July 28, 2026 03:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant