Repository navigation
[#1287] 签名扩展包的上架呈现段:字段集以 schema 为唯一权威,旧信封不回归(跨仓 pin 经 alpha-web#194 打通;先合 #194,再按正文第 3 步换 sha) - #1312
Merged
Merged
Conversation
Hub 里一个签名扩展包此前只有名字与一句话说明:没有开发者、没有长说明、没有截图、 没有隐私政策/服务条款链接,而用户点「安装」时装的是一个能读文件、能上网的东西。 发布者那边也没有地方填这些。 ## 契约 `alpha-package-envelope-v1.schema.json` 新增**可选**顶层 `listing` 段,11 个字段 (`brandColor` `category` `defaultPrompt[]` `developerName` `logo` `logoDark` `longDescription` `privacyPolicyUrl` `screenshots[]` `termsOfServiceUrl` `websiteUrl`)。 **字段集只有一个权威。** `decoder.ts` import 那份 schema,在装载期把 `$defs.listing` 派生成规则表(`PACKAGE_LISTING_RULES_V1`);decoder 里没有任何字段名字面量,加一个展示 字段 = 改那一个 JSON。派生是 fail-closed 的:读不懂的关键字 / `type` / `format` / `$ref`, 或 `maxLength` 大到一个合规值仍会被 `maxStringBytes` 拒掉,一律**抛**。 URL 字段的 schema `pattern: "^https://"` 是**下界**(与 `payloadRef.url` 同一惯例); 宿主侧走 `decodeHttpsUrl`,带凭据 / 非规范化 / 非 https 一律拒。 ## 呈现 安全视图 `CatalogPackageViewV1` 逐字转发这一段(不补默认、不拼接、不影响 verdict/action)。 Hub 卡片:logo + 品牌色 + 开发者行,并进搜索索引;详情页:长说明、开发者、分类、截图条、 用法示例、三条政策链接(走 `external-link` → `shell.openExternal`,不是应用内导航)。 `logoDark` 的深浅切换在 CSS 里,选择器逐字取自 `tokens.css`,不在 JS 里重算一遍。 ## 不回归 `decoder-corpus.v1.json` 的 diff 是 **+72 / −0**:17 条既有语料的信封一字未动、全都没有 `listing`(那正是今天每一个已发布条目的形状),只新增一条 `listing-full-v1`。 Refs #1287
…listing checkpoint (alpha-web#194) alpha-web#194 升宿主 pin 到本分支 checkpoint 96eee4c(带过渡对)并重生 artifact, republish 的宿主副本 host-extension-package-artifact.v1.json 追平本仓 live(sha 253aa607…), 反向 pin 闸 extension-package-artifact.test.ts:112 由红转绿。 机械 re-vendor:`bun run --cwd packages/alpha-contracts-consumer vendor`,40 → 40 文件, lock commit 3ebca3c9 → eb2205b3、aggregate ec555a1d → 432ca3ef;四处字面量同步 (vendor-alpha-contracts.ts / extension-package-artifact.test.ts / vendor-lock-degradation.test.ts:94 / lock)。 ⚠ eb2205b3 是 alpha-web#194 的**分支头**,是过渡值:alpha-web#194 squash-merge 之后必须把 四处字面量换成合并 sha 并重跑 vendor(tree 相同 ⇒ 40 份字节与 aggregate 不变,只有 commit 变), 再合本 PR。lock 若停在一个会被删的分支 sha 上,commitBound 的 check:vendor 在该对象不可达后 会 fail-closed。
…extended (40→41, 8→11, 91→103) scripts/assert-gate-files.sh 对这三份闸门文件报「实际 > 登记:新增用例还没登记,现在它们可以被静默删掉」, 按脚本自己的处方 --update 写回,只动这三行。它们是 #1312 自己扩的三个测试文件,与跨仓 pin 无关。
alpha-web#194 合并为 cdaed4949。按跨仓协议第 3 步,把四处字面量从分支 checkpoint eb2205b3 换成该 squash sha 并重跑 vendor —— 同一棵 tree,40 份字节与 aggregate 432ca3ef 均不变,测试里的 aggregate 字面量就是自证。 check:vendor rc=0;alpha-contracts-consumer 60 tests / 0 fail(含反向 pin extension-package-artifact.test.ts:112,vendored sha 与 live 逐字相同)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jinjunnn
marked this pull request as ready for review
September 9, 2026 08:48
jinjunnn
added a commit
that referenced
this pull request
Oct 15, 2026
* test(ext-hub): freeze legacy cloud dispatch branches before lifecycle extraction (#1527) Exact envelopes, input gating, run saving and result/artifact display for research/docs/word/excel/powerpoint, office gating without an Office identity, code-review diff and upload contracts, foreign job events, failed terminals and not-ready gating. Written against unmodified cloud-dispatch-box.tsx so the extraction must pass them unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(ext-hub): extract cloud job lifecycle into cloud-job-lifecycle.ts (#1527) Mechanical move of beginJob/finish/cancelJob and dispatchError out of cloud-dispatch-box.tsx so the catalog-described form can share them (baseline section 2.4, R1-7). The legacy box keeps its own consent reset that beginJob used to perform. The freeze tests from the previous commit pass unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ext-hub): cloud@1 host package profile with strict payload decoder (#1527) Registers cloud@1 (F3) in the host registry with a closed form schema: pipelineKind, 0..16 text/textarea/select fields, 0..16 fixed inputs, display-only holdUsd and 1..16 upstreamData lines. The decoder mirrors every schema bound and enforces the three invariants the schema cannot express (unique names across fields and fixed, unique options with default among them, minChars <= maxChars); any violation refuses the whole payload. A cloud payload derives no host support token. Adds the cloud-v1 corpus case, regenerates the artifact and documents the profile in CONTRACT.md. Schema/decoder bound parity is pinned by cloud-payload-v1.test.ts reading bounds from the published schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ext-hub): install cloud packages as receipts with cloud:dispatch authorization (#1527) - package-installability: compatible cloud roots project the decoded behavior as CatalogPackageViewV1.cloud (F4); cloud component names pass the same fs-safe gate as skill/agent/command. - ext-package-lifecycle: cloud profile -> cloud child kind, tx key cloud--<name> (same key as legacy cloud receipts). - package-admission: packageAuthorizationCapabilitiesV1 derives [cloud:dispatch] from the verified profile once; confirmation diff, plan preview, capabilityDigest binding and transaction items all consume it (I10). Cloud routes to buildCloudTxItems. - ext-package-tx-builders: receipts-only buildCloudTxItems. - ext-package-uninstall: cloud arm removes grants.json idempotently. - shared/package-admission: plan item kind gains cloud. package-cloud.test.ts drives the corpus cloud-v1 package through the production evaluator, admission, real transaction, ledger and uninstall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(ext-hub): render the catalog-described cloud form and list cloud packages (#1527) - cloud-form-box.tsx (new): renders cloud@1 fields in order as text, textarea or select (default preselected), labels/help/upstreamData as text nodes only (I4), fixed inputs merged from the signed payload and never rendered (I5), gating on required and trimmed minChars, envelope {autonomy, kind: pipelineKind, input, constraints} without budget through window.api.cloud.dispatch, shared job lifecycle for status, saving, artifacts and cancel, display-only hold line. - cloud-job-lifecycle.ts: dispatchError maps pipeline_input_invalid and unknown_pipeline_kind to a refresh-or-update message. - extension-hub.tsx: cloud section lists cloud packages beside legacy cloud entries; extension-detail.tsx: package detail mounts the form when the safe view carries cloud. - i18n en/zh/zht: cloudErrRequirementsChanged, cloudHold. - Tests: cloud-form-box.test.ts; ext-package-detail-wiring gains the production hub/detail path for a cloud package (14 -> 15). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(gates): register cloud payload gate and re-anchor shifted media-type residue rows (#1527) - gate-files.tsv: cloud-payload-v1.test.ts (exact 9); wiring prose 15. - alpha-path-residue.tsv: the four contract/main files whose lines moved now list their application/vnd.alpha.* media-type identifiers at the current lines, including the new cloud.v1 identifier rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(ext-hub): isolate each gating variable in the cloud form test (#1527) The required-select gate masked the trimmed-minChars gate; choosing the select first makes a dropped minChars check turn the test red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(ext-hub): cloud@1 contract index, changelog, and 0.1.20 version (#1527) - docs/contracts/host-extension-package-v1.md: six profiles, the cloud view projection and authorization derivation, and the expected cross-repository reverse-pin red until alpha-web republishes and this repository re-vendors (same hop as #840/#1312). - CHANGELOG [Unreleased] Added entry. - ui-mac 0.1.19 -> 0.1.20 with the two consumer-pin fixtures and the bun.lock workspace version, as shipped-version-propagation requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(contracts): re-vendor alpha-web artifact for cloud@1 (#1527) Return re-vendor of the alpha-web producer artifact that pins this branch's host contract checkpoint 1167048 (alpha-web#270, transition pair d41c5fdc… -> 485f1de9…). The vendored host artifact copy is byte-identical to the live host artifact again. - vendor pin: alpha-web@b02fc0fd8b71f9b17d621bceb3de9fb9698aa425, artifact 95da7a8b…, 40 files (no file added or removed); also absorbs the alpha-web#265 declaration-schema change (two officialRecipe consts) that was never re-vendored. - closure test: the host registry now lists six profiles including cloud; producer mappings stay five and cloud stays excluded until the compiler half of alpha-web#270. After alpha-web squash-merges #270, replace b02fc0fd… with the squash commit in vendor-alpha-contracts.ts, the lock, extension-package-artifact.test.ts and vendor-lock-degradation.test.ts (bytes and artifactSha256 unchanged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(ext-hub): audit R1 minors — register cloud test gates, note fixed.value byte bound, align changelog wording (#1527) - gate-files.tsv: exact counts for cloud-dispatch-box (24), package-cloud (4) and cloud-form-box (9), confirmed by assert-gate-files.sh --update writing nothing else. - host-extension-package-v1.md: fixed[].value is bound by the host's 4096 UTF-8 byte maxStringBytes (decoder.ts:1755), not only the schema's 4096-character maxLength; producers measure bytes. - CHANGELOG: use the UI label for cloud:dispatch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(contracts): pin alpha-web extension-package artifact to merged b87abdf (#1527) Checkpoint step 3: alpha-web#273 squash-merged as b87abdf with the same tree as b02fc0fd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: jinjunnn <slmbaovanetti99@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes #1287
跨仓 pin:已按 #840 先例打通(alpha-web#194 → 本 PR),合并顺序在下面
extension-package-artifact.test.ts:112(vendored 宿主副本 ↔ 本仓 live 产物逐字节相同)此前是本 PR 引入的真红:任何宿主契约变更都会让它红,直到 alpha-web 重新发布。现在两侧 sha 已对上:
先例
b74685d71(#854 =feat #840+chore: re-vendor #853)的动作序列,照做:96eee4c17303f956c1554e74048bb76739d7db9b(与 alpha-web#98 同形的 commit 级 checkpoint 协议),恢复HOST_CONTRACT_PIN_TRANSITION(只收 livedeaa72de…/ pin369b73f0…这一对),重生 artifact(aggregate432ca3ef…,40 文件)。四道门 0/0/0/0。5258d598e:bun run vendor自 alpha-web@eb2205b3(#194 分支头),40 → 40 文件;四处字面量同步。:112 绿。第 3 步:#194 合并后、合本 PR 前,必做
lock 里的
eb2205b3是 #194 的分支头;squash 之后那个对象会随分支删除而不可达,commitBound的check:vendor到时会 fail-closed。换成合并 sha 是机械的(同一棵 tree ⇒ 40 份字节与 aggregate432ca3ef不变,只有 commit 变),四处字面量 + lock:不要 rebase 本分支:alpha-web#194 的 pin 钉的是
96eee4c1这个对象,rebase 会把它变成孤儿。与
origin/alpha(7a868d22)的git merge-tree试合干净(rc=0)。顺带发现:gate-files 登记簿有三行是本 PR 自己欠的
bash scripts/assert-gate-files.sh对package-installability.test.ts(40→41)、host-extension-package-artifact.test.ts(8→11)、package-envelope-v1.test.ts(91→103)报「实际 > 登记:新增用例还没登记」—— 它们是本 PR 自己扩的三个测试文件,与跨仓 pin 无关;
本 PR 上一版正文写「gate files 唯一 fail = 跨仓 pin」不准确(当时就有这三条红)。
已按脚本自己的处方
--update写回,只动这三行(51f552a0f),之后 190/190 rc=0。这个 PR 干了什么
Hub 里一个签名扩展包此前只有名字和一句话说明。用户点「安装」时装的是一个能读文件、
能上网的东西,而他拿不到「谁做的 / 长什么样 / 隐私政策在哪」;发布者也没有地方填。
契约(AC1)
alpha-package-envelope-v1.schema.json新增可选顶层listing段,11 个字段:brandColor·category·defaultPrompt[]·developerName·logo·logoDark·longDescription·privacyPolicyUrl·screenshots[]·termsOfServiceUrl·websiteUrl。字段集只有一个权威。
decoder.ts直接import那份 schema(与registry.tsimportregistry JSON 同一形态),在装载期把
$defs.listing派生成PACKAGE_LISTING_RULES_V1。decoder 里没有任何字段名字面量,也没有任何 maxLength/pattern 字面量 —— 加一个展示字段
= 改那一个 JSON,decoder 不动。
派生是 fail-closed 的,而且这一点有反向用例:
buildPackageListingRulesV1接受一份$defs参数,测试先用一份最小 schema 证明它读得懂(正样本),再逐条喂七种坏 schema(不认识的关键字 / 不认识的
format/ 缺 bound / 未知$ref/ array 缺maxItems/decoder 实现不了的
type/maxLength大到合规值仍会被maxStringBytes拒),断言每一条都抛。URL 字段的 schema
pattern: "^https://"是下界(与payloadRef.url同一惯例,CONTRACT.md 已登记);宿主侧走
decodeHttpsUrl,带凭据 / 非规范化 / 非 https 一律拒。呈现(AC1 后半)
CatalogPackageViewV1.listing?—— main 逐字转发,不补默认、不拼接、不参与 verdict/action。developerName并进搜索索引。(
class="external-link"→ 全局 click 拦截 → main 的shell.openExternal,scheme 白名单,不是应用内导航)。
logoDark的深浅切换在 CSS 里,选择器逐字取自alpha-ui/tokens.css的那一对;不在 JS 里重算「现在算不算深色」(那会是同一条规则的第二个真源,且不跟着换主题更新)。
img-src 'self' data: blob: https:(renderer-security.ts:73),远程展示图不需要放宽 CSP —— 这是先读了那一行才决定渲染远程图的。
不回归(AC2)
testvectors/decoder-corpus.v1.json的 diff 是 +72 / −0:17 条既有语料的信封一个字节都没动、全部没有
listing(那正是今天每一个已发布条目的形状),只新增一条listing-full-v1。判据钉住这件事本身:package-envelope-v1.test.ts「every published envelope shape carries no listing and decodesunchanged」—— 数出 17 条(不是
> 0),逐条断言 status 不变、且解出来的信封上listing不存在(不是空对象);package-installability.test.ts「the signed listing crosses to the safe view verbatim…」——同一个信封,有/无 listing 两次评估,
verdict/action逐字相等,"listing" in view === false。变异自证
改弱 decoder(去掉
decodeHttpsUrl那一支)⇒ 新增判据当场红 2 条(embedded credentials / non-canonical host casing),还原后回绿。
「先证明这个手段能测出已知的坏,再用它判未知的好」。
票面 11 项字段的复核结果(实读 codex 源码 + 本仓 schema)
拉了
openai/codex@main的codex-rs/plugin/src/manifest.rs(HTTP 200 / 6445 B)对照。PluginManifestInterface实为 15 个字段,票面列的 11 项里:shortDescription其实已经在了 —— 就是presentation.description(必填,≤500),今天已经渲染在卡片
alpha-ext-card-desc与详情页alpha-ext-dabout,还在搜索索引里。票面说「展示类字段只有
displayName一个」是错的,是两个。本 PR 没有再加一个shortDescription:两个字段回答同一个问题,发布者就得猜宿主读哪一个。composer_icon—— codex 有,本 PR 刻意不加(见下方「主动没做的」)。longDescriptiondeveloperNamecategorywebsiteUrlprivacyPolicyUrltermsOfServiceUrlbrandColorlogo+logo_darkscreenshots[]defaultPrompt[])确实都不在信封里,已全部加上。
display_name/capabilities同样不抄:前者 =presentation.displayName,后者本仓是编译器派生的,比作者声明严。
logoDark,不是logo_dark):这个信封通篇 camelCase。主动没做的
composerIcon。 codex 有它,但本仓没有任何 composer 面消费扩展包图标 ——一个签名进契约却没人读的字段,是一句谁也核不了的声明。要就单独立票,连消费面一起做。
logo/logoDark/screenshots是渲染层直接<img src>的 URL,没有 sha256、也没人校验,发布者签名后仍可换字节。抄 payload 的
{sha256,bytes,url}形状会声明一个这条路上没人查的摘要 —— 那比不声明更坏。已在 CONTRACT.md 里如实登记为降级;要真校验就得建图片 fetch/CAS 通道,不在本票。
bun run vendor(alpha-web@eb2205b3 的 git 对象),没有手编;那个跨仓 pin 测试只换了 lock 的 commit / aggregate 字面量,判据一字未松。alpha-check.sh全量,改为逐条单跑(见下)。理由:它的第 [9/14] 步会真建探针worktree 并写全 worktree 共享的
core.hooksPath,而同机还有两条 lane 在跑门 ——governance/local-verification-traps.md明写并发时它会互相制造假红。(pre-push 钩子还是把它跑了一遍;跑完我核对过共享
core.hooksPath仍是.githooks,git worktree list里也没有泄漏的探针树。)extension-detail.tsx1640 /extension-hub.tsx3678 /en.ts1735 /zh.ts1698 /decoder.ts1583)。它们在本 PR 之前就远超阈值,#1289的阈值告警不硬红;拆分各自另立票。scripts/module-size-ratchet.tsv的packages/ui-mac/src/main基线抬了 14 行(53539 → 53553),按闸门自己的规定人手改 + 写明理由:
package-installability.ts要把呈现段逐字转发到安全视图(两个可选形参 + 三处调用点 + 说明)。少了这 14 行,呈现段就停在 main 里、渲染层拿不到,AC1 结构上做不到;搬进新文件也一样涨 tree。
顺带修的(都是本 PR 改动的直接后果,不是顺手改)
scripts/alpha-path-residue.tsv:#1206的.alpha残留登记表按file:line钉死,我的改动让
package-installability.ts/generate-artifact.ts/decoder-corpus.v1.json行号漂移。逐条复核过那些残留今天仍然该留着(全部是
application/vnd.alpha.*媒体类型),只改行号;新增一行是新语料
listing-full-v1自己的媒体类型。package-envelope-v1.test.ts里 accepted 语料名的 exact-set 加入listing-full-v1。本地门(真实输出;B2 之后在
51f552a0f上逐条重跑,除 [9/14] worktree bootstrap 探针外全部单跑)在
bash scripts/worktree-bootstrap.sh ac-1287建的 worktree 里跑,base842c374a7(收尾时 base 已前进到
080810f3c,该提交只动 docs,不影响任何代码门)。bash scripts/north-star-guard.sh✓ zero upstream package editsbash scripts/assert-frontend-patch-roundtrip.sh✓ round-trip:packages/{app,ui} 恒等于 pin e11dbd02…python3 scripts/assert-no-nul-bytes.py✓ 8103 个版本控制文件零字面 NUL 字节bun run --cwd packages/ui-mac typecheck+--cwd packages/alpha-contracts-consumer typecheckerror TS计数 0 / 0ALPHA_KNOWN_FAILS_FILE=scripts/known-fails.tsv bash scripts/bun-test-floor.sh 3000 packages/ui-mac srcknown-fails.tsv未动)bun test --cwd packages/extbun test --cwd packages/alpha-contracts-consumerbun generate-artifact.ts --checkchecked 13 HostExtensionPackageV1 filescheck:vendorverified 40 contract artifacts from jinjunnn/alpha-web@eb2205b3… (lock + staged upstream bytes)bash scripts/assert-gate-files.sh✓ 190 个闸门文件全部在位且真的跑过(条数与登记精确一致)(登记簿三行写回后)bash scripts/assert-seed-assets.sh✓ seed/vendored resources presentpython3 scripts/check-doc-links.py CONTRACT.md✓ 0 relative link(s) resolve across 1 file(s)bash scripts/assert-required-contexts.sh✓ required contexts 与 alpha 分支保护逐条相同(记录 4 条)bun packages/ext/scripts/context-injection-inventory.ts --check✓ inventory matches …snapshot.txtbash scripts/north-star-drift-metrics.shbash scripts/assert-module-size.sh= 基线;② 五个超 800 行告警(不硬红,理由见上)bun packages/ui-mac/scripts/verify-byok-catalog.tsDASHSCOPE_API_KEY/MOONSHOT_API_KEY(与本 PR 无关,base 同)docs gate 的「0 条链接」不是瞎子给的:拿一个已知坏链跑同一个脚本 ⇒ RC=1、
✗ 1 broken relative link(s) (of 1 checked)。先证明它测得出已知的坏,再信它这次的绿。与 base fail-set 的差
先说我没测的:我没有在干净的 base 树上跑过 ui-mac 全量(那需要占一棵别人的 worktree
或再建一棵,而同机三条 lane 在跑)。所以下面的差是分两种证据拼出来的,不是一句「base 是绿的」:
bun test src/shared/host-extension-package-contract src/main/package-installability.test.ts src/renderer/extensions/ext-package-presentation.test.ts src/renderer/i18n/locale-regression.test.ts⇒ 155 pass / 0 fail,typecheckRC=0 / 0 条。.alpha残留登记表行号漂移,已修)。全量既然 0 fail,就不存在「base 有而我没修」的红。vendored == live@base ≠ live@PR,base 上那条断言两侧相等 ⇒ 当时为绿。
scripts/known-fails.tsv一字未改。artifact-quota.test.ts:747的#1300墙钟假红本轮没有触发(4852/4852 全过,两次全量跑皆然)。