[REQ-128][CODE] 宿主合同注册 opencode-plugin profile,并把 engine:config/engine:plugin 登记进 host capability registry - #814
Merged
Conversation
added 3 commits
August 3, 2026 02:09
…in 转正为 host capability
第五种载荷:会在引擎进程内以同权限执行 JS 的 managed OpenCode Plugin。前三相不是忘了做,
是主动立了三道 exact-set + 一条反向断言挡住它悄悄进来;本票把那条反向断言**翻成正向**
(只删掉它 = 少一道闸),并把 registry 恰含哪五个 profile 连同 mediaType/schemaPath 一起钉住。
能力**不新造词**:`engine:config` / `engine:plugin` 是 renderer 授权屏对 legacy 插件安装
一直在用的两个词(shared/ext-capability-authorization.ts:30 今天就同时申请这一对),直接登记
进 host registry。只派生一个新词会让 managed 路径比 legacy 路径**披露得更少** —— 用户看不到
「这东西会写引擎配置」。host token 只靠 registry membership 判定,无 `alpha.*` 命名约束。
载荷是单个内容寻址的 JS 资产:`mediaType: "text/javascript"`(判别式,不放宽成 string)+
独立的界 `maxScriptAssetBytes = 2 MiB`,刻意与 markdown(5 MiB)和 payload(1 MiB)都不同值。
不引入 bundler、不改写第三方字节。
基线连锁表未点名、实跑才暴露的两处:
- `CAPABILITY_RE` 与信封 schema 的 capability pattern 都是 `^[a-z][a-z0-9.-]{0,95}$`,**不含冒号**
⇒ `engine:config` 结构上进不了信封。两处同步加 `:`。它只是文法/DoS 界,真正的准入是
`isPackageCapabilityV1` 的 registry membership,故放宽一个字节不放宽准入面。
- `req128-capability-matrix` 按测试标题钉死证据,重命名那条断言后必须同步 matrix.tsv。
Fixes #807
Refs #699
F1(MAJOR,真实回归):上一版把 capability 文法放宽成通用字符类 `[a-z0-9.:-]`,顺带把 `a::b` / `a:` / `engine:` / `a:b:c:d` 一起判成合法。危险的不是"多认几个字符串",而是它们挂在 **optional** 叶子上时:membership 让叶子 skipped,而整包仍判 accepted —— 畸形值进来了又哪里都 不出现。实跑复现:optional leaf 的 capability 改成 `a::b` ⇒ `package=accepted / leaf=skipped`。 改成"原文法 或 精确的 engine:config|engine:plugin"两选一,`decoder.ts` 与信封 schema 两处 **逐字一致**(已用程序比对,不靠肉眼)。补一条 graphCase:判据是**整包被拒**,不是叶子被跳过。 F2(MAJOR):新 profile 的负向覆盖只有"量"没有"轴" —— 超界与错 mediaType 都在讲资产取值, 而"未知键 / 缺字段 / 类型错"三条轴此前借的是 agent 与 mcp-remote 的用例,**是别的解码臂**。 给 OPENCODE_PLUGIN_BEHAVIOR_KEYS 加一个键、或把 bytes 强转成数字,原断言全绿。补三条 plugin 专属具名负例。 F3(MAJOR,本轮最关键):边界夹具虽然从 registry 读期望值,但 registry 里钉的**恰好也是** 2 MiB ⇒ 把 decoder 写死成字面量 2097152 仍满足全部断言。比较基准与被测对象碰巧相等 = 闸是瞎的。 改成**把界挪到 4096、验证边界跟着走**,并断言"原来那个恰好合法的 2 MiB 现在必须越界"; 另断言 opencode-plugin schema 的 `maximum` 等于 registry 的界(发布 schema 与宿主 decoder 是分开维护的两份,此前只查了 additionalProperties)。 F4(MINOR):`docs/contracts/host-extension-package-v1.md` 的 current SHA 是 `1ed320…` —— 它在 `284916c7`(#729)时为真,`74af30d1`(#749 合同 v2)之后就陈旧了,**在本票之前**。 仓内没有任何东西读这份文档,所以那段期间每道闸都是绿的。同步 SHA 与 last_reviewed, 并在文档里写明"这是复述的散文,不是被校验的 pin",让下一个人别再信它。 Refs #807
…ile / 五 capability,并把 aw#112 的资产字节前移 T3(alpha-web#129)重生的 producer 产物落到消费侧。语料 36 → 39:`input.plugin.valid.json`、 `expected.plugin.compiled.json`、以及 plugin 组件引用的脚本资产 `asset.generic-plugin.js` (本仓 vendor 清单里第一份 `.js` 字节,与两份 `.md` 同走非 JSON 分支)。一个文件都没被撤掉, 所以 vendor 「只写不删」这次没留下无主残留;目录清单断言仍是那条判据。 **这一跳与 `#807` 必须原子落地**:`extension-package-artifact.test.ts:107` 要求 vendored 宿主 副本与本仓活的 host artifact 逐字节相同。`#807` 单独在时那条恒红(实测 base:consumer 53 pass / 1 fail,step [4] 的 `&&` 链就此短路,ext 与 ui-mac 那两步根本没跑)。re-vendor 之后 `bash scripts/alpha-check.sh` 全绿。 closure exact-set(`:145-196`): - profile 五个(加 `opencode-plugin`),registry 与 `generic-profiles.v1.json` 两份各断一次; - capability 五个(加 `engine:config` / `engine:plugin`),`generic-rules.v1.json` 与 registry 互相当判据; - `rules.excluded` 从 `arrayContaining` 收成 exact-set。`managed-plugin` 离开排除表是 Phase 4 的 合同事实,而 arrayContaining 只挡「悄悄少一个」—— 上游把某一档重新塞回排除表正是让已转正的 profile 静默失效的走法,那个方向此前不红; - `vectors.valid` 五份(加 `input.plugin.valid.json`),五份都要过发布的声明 schema。 lock 39 条、manifest 38 条:manifest 装不下自己的哈希(`producerCommit.embedded: false` 是同一 理由的另一半),这个「差一」不写成第二个常数,由 `lock.files == manifest.files ∪ {manifest}` 那条 `toEqual` 对死。 `aw#112`(frontmatter)带来的宿主侧影响,单独记在这里:四份被点名的宿主语料 (`package-admission.test.ts` / `package-update.test.ts` / `ext-package-detail-wiring.cases.ts` / `package-mixed-bundle.fixture.ts`)**一行代码都没改** —— 它们全部从 vendored 字节现算摘要, 没有任何硬编码的 sha,已用五个旧哈希 + 旧 commit 全仓 `grep -a` 交叉验证为零命中。 真正变化的是 `package-mixed-bundle.fixture.ts` 抬头那段**理由**:它当初手工重造资产字节,是因为 上游两份 markdown 没有 frontmatter、宿主装不进去;`aw#112` 已经把这个缺口关了,而 fixture 里那 两串常量如今与 vendored 资产**逐字节相同**(118 / 103 字节)。注释照原样留着就成了谎话,故改写 为实况,并明写「这份等同没有任何断言看着」—— 加那道闸不在本票边界内。 `docs/contracts/host-extension-package-v1.md` 四项对齐(手工核对,没有任何代码读这份 Markdown, 所以它陈旧时不会有东西变红):producer pin commit / aggregate 从 `b7174810` / `ae9f43cc` 前移到 `9fcd83d6` / `2a36d9cb`(它跨过 `#759` 一直陈旧而每道闸全绿),profile 四 → 五、capability 一 → 五、 「Alpha Connection absent」翻正(`#749` 就已转正),并补一句它和上面那个 aggregate 一样是转述散文。 `v2 profile/capability closure …` 这条测试的**标题保持不动**:它是 `docs/verification/2026-07-31-req128-capability-matrix/matrix.tsv:7` 的 evidence key, 改名会去动一份已归档的 Phase 1 验证证据。 Fixes #811 Refs #699
jinjunnn
force-pushed
the
feat/807-opencode-plugin-profile
branch
from
August 3, 2026 07:41
e33147e to
533a255
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
大白话
宿主合同今天认四种载荷。这张票加第五种:managed OpenCode Plugin —— 一段会在引擎进程内、以引擎自己的权限执行的 JS。
前三相
opencode-plugin不是被忘了,是被主动挡着:三处 exact-set 加一条反向断言(host-extension-package-artifact.test.ts:165明写 registry 不许出现这个词)。本票把那条闸翻成正向,而不是删掉它——只删等于少一道闸。能力不新造词。 用既有的
engine:config+engine:plugin(renderer 授权屏对 legacy 插件安装一直用的那一对,shared/ext-capability-authorization.ts:30今天就同时申请这两个)。新造一个只披露"会执行 JS"的词,会让 managed 路径比 legacy 路径披露得更少 —— 用户看不到"这东西会写你的引擎配置"。退出条件逐条兑现
bun generate-artifact.ts --checkexit 0checked 13 HostExtensionPackageV1 files/exit=0bash scripts/alpha-check.sh全绿artifactSha256f1fd0476477cf02b666615adce0d6abcd985f6b77b3d0df73ca2800bd5b62bcfexpect(reachable).toEqual(vocabulary),vocabulary 从 registry 读 ⇒ plugin 少派生任何一个都红(实测见绕过 ④)maxScriptAssetBytes恰好值接受 / +1 拒绝,期望值从 registry 读HOST_EXTENSION_PACKAGE_LIMITS_V1.maxScriptAssetBytes,零字面量maxCapabilities实读 + 复核结论⑥
maxCapabilities复核(自己实读,不是转述)maxCapabilities = 16。它是decoder.ts:685里decodeCapabilities()的数组长度上限,而decodeCapabilities只有两个调用点::372(envelope.capabilities)与:639(单个 component 的capabilities)。它不是 registry 词表大小的上限,词表从 3 涨到 5 与它无关。实测:语料里单组件 capabilities 最多 2 条(
opencode-plugin恰好声明["engine:config","engine:plugin"]),单信封并集最多 2 条;即使一个包把五个 token 全用上,5 <= 16仍成立。⇒ 本票不动这条界。边界
只碰
packages/ui-mac/src/shared/host-extension-package-contract/(加一份docs/verification证据台账的同步,见下)。宿主实现(admission / 事务 / 卸载)、renderer、alpha-web 一行未动 —— 那些是 T2a / T2b / T5 / T3 的边界。1. capability 文法根本不允许冒号 —— 不改的话这个 profile 结构上发不出来。
decoder.ts的CAPABILITY_RE与alpha-package-envelope-v1.schema.json的$defs/capabilities.items.pattern都是^[a-z][a-z0-9.-]{0,95}$。实跑:⇒
engine:config连进不了信封,decodeCapabilities会先报invalid format,根本走不到 registry membership 那一步。两处同步加:。这个放宽不放宽准入面:该正则只是文法/DoS 界,真正决定一个 token 认不认的是
isPackageCapabilityV1的 registry membership(registry.ts,membership 而非前缀)。两处必须逐字一致 —— 一个过了发布 schema 却被宿主 decoder 拒掉的 producer,意味着合同在说谎。2.
req128-capability-matrix按测试标题钉死证据。docs/verification/2026-07-31-req128-capability-matrix/matrix.tsv:14把schemas are strict, payload-ref-only, and exclude Phase 2/4 profiles这个标题字符串登记为证据。那个标题在本票之后是假话(它不再排除 Phase 4),改名后 matrix 自闸当场红。同步改了那一格。这是那道闸在正常工作,不是它挡路。绕过实施记录(四条,正反两向,原始输出)
基线
bun test src/shared/host-extension-package-contract= 90 pass / 0 fail / Ran 90 tests across 3 files。① 往 registry 加第六个 profile 而不改 exact-set ⇒ 必须红
② 删掉新 profile 的 schema 文件 ⇒ 必须红
③ 翻正向的那条断言自己是不是闸? —— 把新 profile 的
schemaPath指向 skill 的 schema。profileId 列表仍然完全正确,:64那条 exact-set 照绿:⇒ 翻正之后的断言比它替换掉的反向断言更强:反向断言只管"这个词不出现",正向这条管完整绑定(
id@version+ mediaType + schemaPath)。④ 拿掉脚本资产上界 ⇒ 边界对夹具必须红
四次实验都在本 worktree 内做、做完立刻还原;还原后
git status干净、HEAD 未动、90 pass / 0 fail复现。alpha-check.sh各步结果整轮只有两行
(fail),而且是同一条测试(一次在 [4] 的整包地板里,一次在 [5] 的逐文件点名里):这就是基线 §6 硬序写死的那条反向 pin(
packages/alpha-contracts-consumer/src/extension-package-artifact.test.ts:107-110):vendored 的宿主副本必须与本仓活的宿主 artifact 逐字节相同。T1 单独合并时它必红,这是设计如此。没有为了让它绿去动那条 pin,也没有动 vendored 副本。⇒ 本 PR 不单独合并。按基线 §6,T1 必须与 T4(re-vendor)在同一个 PR / 同一个 merge unit 里原子落地。
[4]在 contracts-consumer 处&&短路,ext 与 ui-mac 因此没在那一步里跑到。单独跑过,都绿:与 base fail-set 的差
base(本分支 base commit
a828ec81,我自己在同一 worktree 实测)=✅ all local gates green,fail-set 为空集。⇒ 现在唯一的红全部是本 PR 引入的,而它的根因只有一个,且是 T4 的已知预期红。
一处订正:派发时给的 base 是
Ran 3756 tests across 255 files。实测不是这个数。 我用git checkout HEAD~1 -- <paths>把工作树还原成 base 内容量了一次:差 +3,与我新增的三条断言(2 条 negativeCase + 1 条边界 test)逐条对得上,文件数不变。派发里的 3756/255 是个过期数字,不是本 PR 造成的差异。
主动没做的事
packages/alpha-contracts-consumer/vendor/**或那条反向 pin —— 那是 T4 的边界,且票面明确要求这条红照实报。synthetic-decoder.ts。基线把它列在 T1 边界里,但实读之后它不需要改:它只读maxPayloadBytes与alpha.secret-prerequisite.v1,profileId形参本来就是PackageProfileIdV1,联合一扩就自动覆盖新成员。改它只会白白扰动 artifact 字节。decoder.ts的 payload 分派重构成穷举 + 未知即拒。那个兜底式三元(else → decodeMcpRemotePayload)是基线 §5 第 7 类点名的对象,但那一类的票主是 T2b(咽喉在package-admission.ts:506的 kind 分流)。本票只按边界"加一臂"。基线 §2.4 也已实读定性:这里的兜底靠类型与decoder.ts:406(supportComponent具名拒绝component-profile-unsupported)保证,它自己不是闸。targetDir/ argv / environment 之类字段。载荷落在哪、引擎怎么被指过去,是宿主决定,不是 producer 声明 —— 那是 T2b 的事。PackageAssetRefV1之类的联合别名。两个具名类型够用,TS 在使用点自己推;为将来可能的消费方先造抽象是禁止项。Fixes #807
Refs #699
R1 修复(commit
6052bb60)—— Codex 审计四条,全部采纳新
artifactSha256(再次变化,以此为准):fb196fc1d187acb334b144374aeec2fe1c7da76f5b1bd4fac2df1c118ee0bba2F1(MAJOR)加
:顺带放行了畸形 capability —— 已确认是真实回归这条是我漏掉的。 派发时明确要我查「加
:有没有顺带放宽别的东西」,我只验证了两个目标 token 能过、没验证别的什么也跟着能过。自己复跑确认:危险不在「多认了几个字符串」,而在:membership 只对 required 组件拒绝,optional 组件被 skipped 之后整包仍然 accepted —— 畸形值进来了,又哪里都不出现。
修法:两处都改成「原文法 或 精确的
engine:config|engine:plugin」,不留通用字符类。用程序比对而不是肉眼确认两处逐字一致:新负例的判据是「整包被拒」,不是「叶子被跳过」 —— 后者对畸形值恒真,杀不掉这个缺陷。放在
graphCases,其 runner 直接断result.ok === false。绕过(把通用字符类放回去):
F2(MAJOR)plugin 的负向覆盖只有「量」没有「轴」
确认属实:超界与错 mediaType 都在讲资产取值;「缺字段」来自 agent(
:205)、「未知 behavior key」来自 mcp-remote(:938),都是别的解码臂。补三条 plugin 专属具名负例(未知键 / 缺asset/bytes是字符串),保留原超界用例。三次绕过,各自只打红自己那一条:
F3(MAJOR)边界夹具分不清「接线到 registry」与「硬编码 2 MiB」—— 本轮最关键
审计说得对,而且我上一轮正文里把它当成已经解决的:我确实"从 registry 读期望值"了,但 registry 里钉的恰好也是 2 MiB,锚点和被测对象碰巧相等。
修法:行为测试里临时把内存 registry 的界挪到
4096(既不是maxScriptAssetBytes,也不是 markdown 5 MiB / payload 1 MiB 任何一条),验证边界跟着移动,finally恢复并回断。决定性的一条是:界挪到 4096 之后,原来那个恰好合法的 2 MiB 必须变成越界。另加一条:opencode-plugin.v1.schema.json的maximum必须等于 registry 的界(发布 schema 与宿主 decoder 是分开维护的两份,此前只查了additionalProperties)。绕过(把 decoder 写死成字面量
2097152):请特别注意这次绕过证明了什么:硬编码之下,原来那两条「恰好接受 / +1 拒绝」断言仍然全部通过 —— 是新加的「界挪走」那一段单独把它抓出来的。这正是审计指出的常量巧合盲区,上一版的闸确实是瞎的。
F4(MINOR)活跃合同索引发布着一个用不了的 pin
docs/contracts/host-extension-package-v1.md:19的1ed320…—— 查过来源:它在284916c7(#729)时是真的,在74af30d1(#749合同 v2)之后就陈旧了。这份文档在本票之前就已经陈旧,不是我弄坏的,但也不该由我默默改掉。 已同步 SHA 与last_reviewed,并在文中写明它是复述的散文、不是被校验的 pin,附上陈旧区间。F4 的绕过问题:文档与 manifest 不一致时有没有东西会红?——没有。如实说没有。
两条检索轴:
grep -aacross*.ts/*.sh/*.py/*.yml/*.mjs/*.json⇒ 零命中。artifactSha256与仓内别处比对」:命中全部落在 manifest ↔ vendored ↔ lock 之间,没有一处对 Markdown。还有一条执行出来的证据:该值从
#749错到#806,期间每道闸都是绿的 —— 包括我自己在a828ec81上跑的那次✅ all local gates green,当时文档正写着1ed320…。按指示没有为此新造闸门。顺带一个自证:我这轮自己踩了一次同一个坑——恢复 F1 注释后 artifact SHA 又变了一次,而文档里还写着上一版的值;没有任何东西会红,只能手工核对:
R1 之后的门
整轮仍然只有两行
(fail),且是同一条测试(T4 的反向 pin,一次在 [4]、一次在 [5]):没有动那条反向 pin,也没有动 vendored 副本。
[4] 在 contracts-consumer 处
&&短路,ext / ui-mac 单独跑:与 base(实测
3762 / 257)的差 = +7,与我的枚举逐条对得上:R0 加 3 条(2 negativeCase + 1 边界 test)、R1 加 4 条(1 graphCase + 3 negativeCase)。文件数不变。R1 里我踩到、并且抓住了的一个本机陷阱
绕过实验用
git checkout HEAD -- decoder.ts还原时,把同一文件里我刚写、尚未提交的注释一起抹掉了 —— 代码是修好的 alternation,注释却退回旧版、还在说「widening the character class by one byte」,即注释在说谎而没有任何测试会红。靠git diff逐行看出来的,不是靠印象。这正是 CLAUDE.md 记着的那条(实验后git checkout --会连未提交的真实改动一起抹掉),这次的变体是「同一个文件里,一半是实验一半是真改动」。审计没点到、我这轮另外发现的一条(没有改)
同一份
docs/contracts/host-extension-package-v1.md:40-41写着消费侧闭包是「four profiles, one capability,cloudand Alpha Connection absent」。这句今天也是假的:消费侧测试实际断言的是三个 capability,且 Alpha Connection 早在合同 v2(#749)就转正了。我没有改它,两个理由:①它描述的是 producer/consumer 闭包,票主是 T3 / T4,不是 T1;②那句话在 T4 re-vendor 之后还要再变一次(profile 变五个),现在改等于猜一个尚未发生的状态。登记在此,请编排者决定挂给谁。
T4(
#811,commit533a2551)—— re-vendor producer artifact + lock + closure本 PR 现在同时闭合
#807(T1)与#811(T4)。 基线 §6 把两者钉成同一个 merge unit:extension-package-artifact.test.ts:110要求 vendored 宿主副本与本仓活的 host artifact 逐字节相同,所以 T1 单独在时本仓自己的正式门就是红的。上面那条「预期红」现在绿了。
大白话
把 alpha-web 重生后的 producer artifact 逐字拷回本仓的 vendored 副本,更新 lock 与 closure 断言,
并把落后一个 commit 的那批资产字节一起前移。
①
bash scripts/alpha-check.sh—— 全绿[4]不再短路,三个包都真的跑到了:推送时 pre-push 钩子在同一棵树上又整跑了一遍,同样
✅ all local gates green。与 base fail-set 的差
base = 本分支 T1 的 HEAD
6052bb60,工作树干净,我自己实跑:6052bb60)533a2551)[4]contracts-consumer[4]ext&&链在 consumer 处短路)[4]ui-mac[5]gate files(fail)行数那条唯一的红是:
re-vendor 之后 Received 侧变成
c9148c6f…,两边相等 ⇒ 这条转绿。这就是本票的成功判据。另有一条新红是我自己引起并当场修掉的:
scripts/vendor-lock-degradation.test.ts:94-95把旧 commit写成字符串断言,pin 一动它就红(这正是它该有的行为),同步改成新 commit / 新条数。
逐文件 sha256 比对
不用 vendor 脚本自己的输出当判据(那是自指等价链)。独立拿
git -C ../alpha-web show <commit>:<path>的字节 +
shasum -a 256跑一遍:这套比对自己先证明能测出已知的坏:同一个循环拿旧 pin
6e0db57d的字节再跑一遍,19 + 3 = 22,与
git diff --stat 6e0db57d 9fcd83d的 22 个文件逐一对上。38 vs 39:两个数不一样,原因是结构性的
✓ … (39 files)/ls/ lockfiles[]extension-package-producer-artifact.v1.json的files[]程序核对:
set(disk) - set(manifest.files) == {"extension-package-producer-artifact.v1.json"},反向为空集。manifest 装不下自己的哈希,这与它
producerCommit: {embedded: false}是同一个理由的两半。这个「差一」没有写成第二个魔数:
extension-package-artifact.test.ts:58-60断言lock.files 路径集 == manifest.files 路径集 ∪ {manifest 自己},lock.files.length单独钉 39。(上一版 lock 是 36、manifest 35,同一个关系。)
closure exact-set:两个方向各绕过一次,外加一次反事实
控制组(未改动,只跑这一条):
1 pass / 6 filtered out / 0 fail。方向 A — 悄悄多一个。 往 vendored registry 与
generic-rules.v1.json同时加一个未经宿主登记的engine:shell(两份保持互相自洽,单靠「两份互相当判据」抓不到):方向 B — 悄悄少一个。 从 registry / rules / profiles 三份一起删掉
engine:plugin与opencode-plugin(三边一起变小并保持自洽):方向 C —
excluded表悄悄多一个(本 PR 把它从arrayContaining收成 exact-set 的理由)。把
managed-plugin塞回排除表:反事实(证明这次收紧是承重的,不是装饰):同一个变异不动,只把断言换回旧的
expect.arrayContaining([...])形状 ——⇒ 旧写法下,「上游把
managed-plugin重新塞回排除表、让刚转正的 profile 静默失效」不会红。三次实验都在本 worktree 内做、
git status干净时开始、做完立刻git checkout HEAD --还原,还原后
git status空、git diff HEAD无输出、HEAD 未动、7 pass / 0 fail复现。②
aw#112frontmatter 变化牵动的宿主测试语料 —— 逐条,不混在 profile 变更里上游那一跳到底改了什么(
aw@e614b5e,[#112][CODE] canonical 语料资产补 frontmatter):(skill 同形,少一行
mode。52 → 118 字节 / 52 → 103 字节。)src/main/package-admission.test.tsexpected.mcp-remote.compiled.jsonpayloads映射重序列化、摘要现算。那份产物本轮只动了compatibility.reportSha256一行,而它不含 markdown 资产,frontmatter 与它无关。src/main/package-update.test.tsexpected.bundle.compiled.jsonAGENT_V1/V2、SKILL_MD(本来就带 frontmatter),每代的payloadRef/manifestDigest/ owner token 全部现算。产物里那些bytes: 375→376、sha256、assetManifestSha256的变动它一个都没硬编码。test-component/ext-package-detail-wiring.cases.tstest-component/package-mixed-bundle.fixture.tsexpected.bundle.compiled.json交叉验证「真的没有硬编码」,而不是靠读代码的印象:把旧语料里五个会变的哈希 + 旧 commit
拿去全仓
grep -rna -a(带-a,防 NUL 假阴):⇒ 四份语料里三份一行未动,是因为它们从 vendored 字节现算,不是因为断言太松没看见。
package-mixed-bundle.fixture.ts:改的是理由,不是字节该文件抬头原文写着:vendored 的两份 markdown 没有 frontmatter,宿主
(
agentMdToEntry要---+description+ 非空 body;skillGenerationProbe要 frontmattername等于 item key)因此装不进去,所以夹具手工重造了资产字节;并把修复挂在
aw#108。aw#112已经把这个缺口关了,而且关得很彻底 —— 夹具里那两串常量逐字节等于新的 vendored 资产:(这两个值也正是新
expected.bundle.compiled.json里那两个behavior.asset.sha256。)注释照原样留着就成了谎话 —— 而没有任何测试会因为它说谎而红,正是 CLAUDE.md 里那条。
所以改写为实况,并明写两件事:①夹具仍然自建信封,理由换成了两条与 frontmatter 无关的
(第四格「已策展但宿主不支持」的 optional leaf、
breakSkillFrontmatterName开关,上游产物里都没有);②这份字节等同没有任何断言看着 ——
assertMatchesVendoredBundleShape只比形状,别把它读成「字节也验过了」。同一理由也补进那个函数的 doc comment。
③ 合同索引
docs/contracts/host-extension-package-v1.md四项手工核对没有任何代码 / 脚本 / CI 读这份 Markdown —— 两条独立检索轴(轴一:谁引用这个文件路径;轴二:
谁把
artifactSha256与仓内别处比对)在 T1 那轮已经跑过,命中全部落在 manifest ↔ vendored ↔ lock之间。实证是那个值从
#749一路错到#806期间每道闸都绿。既然没有自动判据,手工核对的过程就是证据;按指示没有为此新造闸门。
agent/mcp-local/mcp-remote/opencode-plugin/skill;generic-profiles.v1.json的 mappings 同 5 个alpha.connection.v1/alpha.mcp-oauth.v1/alpha.secret-prerequisite.v1/engine:config/engine:plugin(generic-rules.v1.json的capabilities与 registry 逐字相同)#749就转正了#749」;cloud仍在排除表,这半句保留alpha-web@b71748103ce65f97e3e5c8ac03f08152a0a1456f+ aggregateae9f43cc…9fcd83d66ea8f5b13081f434ace150e739a0536e+2a36d9cb…#759一直陈旧而每道闸全绿顺带把
excluded也写进去(cloud/legacy-projection/nested-bundle/provider-adapter/publish-wrapper),并注明managed-plugin是在#807注册opencode-plugin时离开的 ——消费侧那条断言现在是 exact-set,文档与它一一对应。
主动没做的事
上游下次动资产内容它会安静地退回一份手抄替身。但那是新加一道闸,不在 re-vendor 的边界内
(票面「禁止过度工程:不重构、不加闸」)。已把这件事写进夹具抬头,不让它变成隐性知识。
建议单开一张窄票。
opencode-plugin一档,标题却只提 OAuth / AlphaConnection。原因:这个字符串是
docs/verification/2026-07-31-req128-capability-matrix/matrix.tsv:7的 evidence key,由packages/ui-mac/src/main/req128-capability-matrix.test.ts反查 —— 我先改了名,那条自闸当场红(
+ "…extension-package-artifact.test.ts :: v2 profile/capability closure covers OAuth and Alpha Connection and still excludes cloud"),说明改名要动一份已归档的 Phase 1 验证证据。已还原并在测试上方注明理由。
#809/#810/ T3 的边界)。packages/ui-mac/src/shared/host-extension-package-contract/**—— T1 已经在这个 PR 的上半场处理完,T4 只消费它的字节。
顺手发现、没有改、请编排者决定挂给谁
同一份
docs/contracts/host-extension-package-v1.md结尾第 88-89 行写着:「不保留 optional-child 状态」这半句今天是假的。
packages/ui-mac/src/shared/catalog-package-view.ts:30-36的
CatalogPackageComponentV1逐组件带included: boolean与skipReasonCode: PackageComponentSkipReasonV1 | null(注释原文:「includedis the only authority onwhether a component takes part in the install」),而
CatalogPackageViewV1.components是一行一个组件。Alpha Connection 那半句我没有查证,不猜。
没有顺手改的理由:①它讲的是 renderer wire,票主更像
#810,不是本票的 re-vendor;②只修一半会写出一句新的半真话。登记在此。
git push走 pre-push 钩子(=alpha-check.sh)之后,远端分支尖端不是我提交的那个 commit:多出来的三个 commit 长这样(reflog 显示它们是在钩子跑测试的那一刻产生的):
根因是
#754那一类的另一个实例。git push会把GIT_DIR导出给钩子进程(linked worktree下实测必有),而
GIT_DIR打得过-C/cwd。上游测试夹具packages/opencode/test/fixture/fixture.ts:93正是$`git commit --allow-empty -m "root commit ${dirpath}"`.cwd(dirpath)—— 它以为自己在临时目录里建 fixture 仓,实际把空提交打进了本仓当前分支。
当场复刻(不是推断):A、B 两个空仓,
cwd=A但环境里带 B 的GIT_DIR——处置:
git reset --hard 533a2551+--force-with-lease=…:e33147ed强推回真实尖端,本次强推用
--no-verify—— 理由不是「树被别人占着」,而是钩子自己就是肇事者,再跑一次会再造三个。现在
origin/feat/807-opencode-plugin-profile == 533a2551,工作树干净,PR head 已回正。没有顺手修根因:肇事文件在
UPSTREAM_PATHS下(packages/opencode/**),alpha 不碰;真正的修法应该落在钩子侧(照
packages/alpha-contracts-consumer/scripts/git-cross-repo.ts已经做过的那样,用
git rev-parse --local-env-vars清掉仓库局部变量再跑测试)。建议单开一张票。从
#809(T2b)从本分支派生的角度看还有一条:如果它已经 fetch 到e33147ed,rebase 前需要重新 fetch —— 那三个 commit 已经不在远端了。
Fixes #811