Skip to content

[REQ-105][CODE] #319 receipt 记下并显示 Excel 连接器执行物的内容地址 - #1110

Merged
jinjunnn merged 1 commit into
alphafrom
feat/319-receipt-artifact-digest
Aug 25, 2026
Merged

[REQ-105][CODE] #319 receipt 记下并显示 Excel 连接器执行物的内容地址#1110
jinjunnn merged 1 commit into
alphafrom
feat/319-receipt-artifact-digest

Conversation

@jinjunnn

Copy link
Copy Markdown
Owner

Fixes#319
Refs jinjunnn/alpha-work#7

先勘破:「当前显 1.0.0 与静态 0.1.8 不一致」今天长什么样

票面括号里的 0.1.8 是 2026-07-14 审计时的社区 excel-mcp-server,REQ-133/REQ-135 之后
它已退役(shared/office-advisories.ts:61-68),Hub Excel 只剩随包的 mcp:alpha-excel
数字换了,形态一个字没变,而且在代码里可逐行指认:

事实坐标
Excel 卡片版本 = 1.0.0packages/ui-mac/src/renderer/extensions/alpha-catalog.json:194
真正执行的是随包 office-mcp/server.py + 钉版 openpyxl==3.1.5alpha-catalog.json:196-208
receipt 的 version 取的就是那个卡片版本packages/ui-mac/src/main/ext-install-planner.ts:426(entry.version ?? catalogVersion)→ :1362
payloadDigest 只在 entry.remoteAsset.files 在场时才有 —— MCP 条目没有 remoteAsset ⇒ 修复前恒缺省ext-install-planner.ts:432-440
manifestDigest 覆盖的是合成 manifest 的 canonical JSON,不含 installSpec/命令/钉版 ⇒ 换掉 server.py 它一字不变ext-install-planner.ts:418-442 + ext-manifest-v2.ts:301-303
renderer 那一侧更早断:v1 投影丢掉全部 digestext-receipt-v2.ts:793-805(修复前)
renderer 读的正是这份 v1 视图alpha-installs.ts:214ext-ipc.ts:440preload/index.ts:235use-extensions.ts:523extension-detail.tsx:646
带 records[](含 digest)的 ext-list-installs-v2ext-ipc.ts:1296 注册,但没有 preload 绑定,renderer 消费不到preload/index.ts

所以这不是「版本号写错了」,是数据模型缺一项:receipt 上没有任何一个字段能指认
「这次安装会执行哪一份字节」,于是 1.0.0 既证伪不了也印证不了。

改了什么

  • main/ext-mcp-policy.ts —— 随包 Office server 的 realpath 本来就只有这道写盘咽喉在解
    (:69-77),digest 与那次解路径同点取得,不留「解的是 A、哈的是 B」的缝。放行时返回
    artifactDigest(aggregateFilesDigest,与 skill payloadDigest 同域);读不出随包文件与
    「随包 server 不可用」落同一条既有 fail-closed 分支 —— 不写一张说不出执行物的 receipt。
  • main/ext-install-planner.ts —— 单装 MCP 与 bundle 成员两条路都把裁决里的 digest 原样
    落进 receipt(planner 不自算、不推断)。seed 路径按既有语义仍拒 workspace-policy MCP,未动。
  • main/ext-receipt-v2.ts / preload/types.ts / main/alpha-installs.ts —— digest 随 v1 投影
    出到 renderer 真正读得到的那份视图;落盘校验只认合法形状或不在场。
  • shared/receipt-artifact-facts.ts(新)+ extension-detail.tsx + i18n(en/zh) —— 头部在版本
    旁显示短 digest,完整值在 titledata-artifact-digest 上;缺省或形状非法一律显式说
    「未记录」,不留空、不拿 catalog 版本顶上(AC5:不得把未知或未验证 digest 表示为已审计)。
  • scripts/gate-files.tsv —— 新的子进程宿主按 #647 登记簿要求登记(不登记即红,实测)。
  • CHANGELOG.md —— 用户可见变化,含四条诚实边界。

判据,以及它们真的会红

八次绕过实验,每次都在干净树上做、跑完 git checkout -- 还原并复验干净:

#故意写坏的实现结果
E1策略闸口不返回 digest(压根不算)policy 2 fail,planner 3 fail
E2算了但 planner 不落账(只活在这次调用里)planner 2 fail —— 落盘与重启两条
E3records[] 落了、v1 投影丢掉(用户永远看不到)planner 1 fail —— 正是「重启后两份视图」那条
E4manifestDigest 冒充 payloadDigestplanner 2 fail
E5详情页删掉整段显示(落盘了但不显示)组件宿主 fail
E6去掉格式闸,账本里是什么就显示什么纯函数 10 fail;组件里 DOM 实测渲染出 Artifact digest sha256:deadbeef…,即 AC5 要禁的那一幕
E7非 Office 的 MCP 也凭空多出 digestpolicy 2 fail + planner 1 fail
E8哈路径而不是哈字节(digest 形状合法却什么都不指认)policy 2 fail + planner 3 fail

判据设计上刻意避开两个本仓踩过的坑:

  • 锚点不与被测对象同源 —— 期望值由测试用 node:crypto 现算随包 server.py 的字节,
    不 import 生产返回值、不抄字面量常量;另加一条「换成不可能的文件哈希聚合值必须不同」,
    堵掉「压根没读文件、只返回常量」。
  • 重启可恢复不靠内存读取器 —— 装完直接 JSON.parse 账本文件,records[]receipts[]
    两份视图分别判(E3 证明这不是一句空话:只丢 v1 那半边,判据照红)。

本地门(真实输出,非「应该会绿」)

base = origin/alpha @ 8ba7d11ec,base fail-set (先量的:typecheck exit 0;4455 pass / 0 fail / Ran 4455 tests across 308 files)。

$ bun run typecheck # packages/ui-mac
$ tsgo -b
TYPECHECK EXIT=0 (0 行 error TS)
$ bun test src # packages/ui-mac
4479 pass
0 fail
20706 expect() calls
Ran 4479 tests across 310 files. [204.44s]
UNIT EXIT=0
$ bash scripts/north-star-guard.sh
✓ zero upstream package edits (baseline origin/alpha; ADR-033 收编白名单 + ADR-043 alpha 自有谓词除外)
NORTHSTAR EXIT=0
$ python3 scripts/check-doc-links.py CHANGELOG.md
✓ 1 relative link(s) resolve across 1 file(s)
DOCS EXIT=0

与 base 的差:+24 pass / ±0 fail / +2 files。24 条正是本 PR 新增的判据数
(policy 4、planner 4、纯函数 15、子进程宿主 1;组件 4 条在子进程内跑,不计入 ui-mac 计数)。
seed 资产门不适用 —— diff 未触及 resources/extension-seed、同步脚本或 catalog 快照。

pre-push 用了 --no-verify:钩子 exec 的是 scripts/alpha-check.sh,它第 [8/9] 步会在
共享主 checkout 里真建探针 worktree 并写共享 core.hooksPath;此刻 ac-1108 / ac-402
两条 lane 在跑,CLAUDE.md 明确要求并行期间不得逐 lane 跑它(会互相制造假红,并泄漏探针树)。
上面四道门已在本 worktree 内逐一跑过。push 前后确认:core.hooksPath 仍是 .githooks
git worktree list 无新增探针树。

明确没做的事(留给主 session 裁决)

  1. 没往 catalog / seed / bundle 的生产侧加 digest 字段。 AC2 说「catalog、seed/bundle lock
    与 receipt 显示完全一致的 digest」,但 catalog 侧是 alpha-web 产出、alpha-catalog.json
    生成快照(REQ-133 基线原文:"not edited by either decision"),且构建侧被本票列为 out-of-scope。
    本 PR 只做桌面端能独立取得且可复现的那一半:随包字节的内容地址。
    建议(不是本票 AC):开一张跨仓票,让 catalog 的 mcp:alpha-* 条目携带同一域的
    artifact digest,再加一条「catalog 值 ≠ receipt 值即拒装」的闸 —— 那时 AC2 的「完全一致」
    才是可机器判定的。
  2. 没有校验钉版依赖(openpyxl==3.1.5)的字节。 它由 uv run --with 在首次运行时联网取,
    桌面端装的时候拿不到,伪造一个就是把未验证的东西说成已审计。钉版本身仍由既有的
    exact-command 闸口(checkAlphaOfficeMcpSafety)逐字守着。
  3. 没有把 digest 做成「安装后持续校验」。 receipt 记的是安装当刻的事实;若要「每次
    启动比对随包字节是否仍与 receipt 一致」,那是一条新行为(且要先定漂移了怎么处置),
    超出本票 AC。建议另开票。
  4. 没有给 ext-list-installs-v2 补 preload 绑定。 它注册了却无 renderer 消费方 —— 是既有
    状态,不是本票引入的;本 PR 走 v1 投影这条 renderer 真正在读的路,没有顺手扩通道。
    建议记一张清理票(要么接上,要么删掉)。
  5. 没有改 .claude/rules/adrs/ 里的 ADR-028。 receipt schema 描述在受保护的 rule 资产里,
    改它是一次决策而不是实现。建议由主 session 判断是否需要同步一句。
  6. 没有出设计稿。 新增的是既有 alpha-ext-dhead-meta 行里的一个 <span> + 一个复用
    alpha-ext-dhead-id 样式的 <code> 芯片(与同一行里已有的 id 芯片同款),没有新组件、
    没有新视觉语言。若认为仍需过 design-loop,请裁决。

🤖 Generated with Claude Code

https://claude.ai/code/session_01QHDS2BFKuzm9uNH1svU2oj

REQ-105 AC2/AC5 的缺口不是「版本号错了」,是**数据模型缺一项**:receipt 上的
`version` 取自 catalog 卡片(`mcp:alpha-excel` = 1.0.0),它命名连接器、不命名
连接器执行的字节;`payloadDigest` 只在 `entry.remoteAsset.files` 在场时才有值,
而 MCP 条目没有 remoteAsset ⇒ 修复前 receipt 上零 digest 能指认执行物;
`manifestDigest` 覆盖的是合成 manifest 的 canonical JSON,里面没有
installSpec/命令/钉版,换掉 server.py 它一个字不变。renderer 那一侧更早断:
`toV1Receipt` 投影时丢掉全部 digest,而 renderer 读的正是这份 v1 视图
(`listInstalls` → `ext-list-installs`)。
改动:
- `ext-mcp-policy.ts` —— 随包 Office server 的 realpath 本就只有这道写盘闸口在解,
digest 与那次解路径同点取得(不留「解的是 A、哈的是 B」的缝)。放行时返回
`artifactDigest`(与 skill payloadDigest 同域 = aggregateFilesDigest);读不出
随包文件与「随包 server 不可用」落同一条 fail-closed 分支。
- `ext-install-planner.ts` —— 单装与 bundle 成员两条路都把裁决里的 digest 原样落进
receipt(planner 不自算、不推断);seed 路径按既有语义仍拒 workspace-policy MCP。
- `ext-receipt-v2.ts` / `preload/types.ts` / `alpha-installs.ts` —— digest 随 v1 投影
出到 renderer 读得到的那份视图;落盘校验只认合法形状或不在场。
- `shared/receipt-artifact-facts.ts` + `extension-detail.tsx` + i18n —— 头部在版本旁
显示短 digest(完整值在 title/data 属性);缺省或形状非法一律显式说「未记录」,
不留空、不拿 catalog 版本顶上(AC5)。
判据:
- 落盘 + 重启可恢复:装完直接 `JSON.parse` 账本文件,records[] 与 receipts[] 两份
视图都必须带上独立算出的内容地址 —— 只记在内存里的实现在这里必红。
- digest 追内容不追路径(同路径不同字节必须不同值),且不是 manifestDigest 的别名。
- 非 Office 的 MCP 不得凭空多出 digest;畸形串不得被当身份呈现。
- 详情页判据挂在生产 ExtensionDetail 上(真 Solid DOM),已登记进
scripts/gate-files.tsv。
Refs jinjunnn/alpha-work#7
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHDS2BFKuzm9uNH1svU2oj
@jinjunnn
jinjunnn merged commit 39ffbf1 into alphaAug 25, 2026
5 of 6 checks passed
Sign up for freeto 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.

[REQ-105][CODE] Persist and display Excel package version and artifact digests in receipts

1 participant

@jinjunnn