feat(daemon): add batch extension activation APIs - #8788
Conversation
Automated API / E2E test report
No interactive browser/TUI test was applicable. A separate full run of the large CLI server test file completed 895/898; the three failures were unrelated socket/Live timeout and temporary-directory cleanup races, and the affected Extension batch tests passed on focused rerun. |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
🩺 serve daemon A/BBuilt the PR base vs this PR head
|
| field | PR base (before) | this PR (after) |
|---|---|---|
features[] |
— | "extension_batch_activation_v2" |
— Qwen Code · serve A/B
|
Re-run at the author's request against the new head — which replaces the identity-based draft with the final name-only Template looks good ✓ Problem: observed need, not theory. V2 clients (the description names opencode) must set a global default and per-workspace overrides for several Extensions in one flow — including before an Extension is installed — and the singular installed-only, id-addressed routes cannot express that lifecycle. The PR builds on the merged batch skill toggle (#8664), which already validated this exact optimization pattern on the Skills surface. Direction: aligned. Additive, capability-gated V2 routes inside the established Extension Management V2 ownership model (global defaults vs. exact workspace overrides, trusted-runtime gating, no primary fallback). The name-only contract is the version this thread's maintainer discussion converged on, the legacy compatibility route was cut two rounds ago, and singular routes stay installed-only. No direction escalation. Size: core paths touched ( Approach: the scope is right for the stated goal — two batch routes plus the name-declaration machinery they need (declarations, legacy-projection remainder), with every edit serving the contract and no drive-by changes. One structural note carried into the review: the new SDK surface sits on the browser-bundle budget path, and that gate is enforced only at full-build time (see Stage 2). Risk: no elevated-risk paths matched (no revert-correlated files touched). Moving on to code review. 🔍 中文说明应作者请求对新 head 重新运行——本次 head 将身份(identity)草案替换为最终的仅名称 模板完整 ✓ 问题:已观测到的需求,不是理论问题。V2 客户端(描述中点名 opencode)需要在一个流程中为多个 Extension 设置全局默认值与 workspace 级 override——包括在 Extension 安装之前——按 id 的单项已安装路由无法表达该生命周期。本 PR 建立在已合并的技能批量开关(#8664)之上,该 PR 已在 Skills 面验证了完全相同的优化模式。 方向:对齐。在既定 Extension Management V2 所有权模型内新增按 capability 管控的 V2 路由(全局默认值 vs. 精确 workspace override、受信任 runtime 管控、不回退 primary)。仅名称契约是本 thread 维护者讨论收敛的版本,legacy 兼容路由已在两轮前砍掉,单项路由保持仅已安装。方向无需升级。 规模:触及核心路径( 方案:范围与既定目标匹配——两条批量路由加其所需的名称声明机制(声明、legacy 投影 remainder),每项改动都服务于契约,无夹带改动。一条带入审查的结构性提示:新增 SDK 公开面位于浏览器 bundle 预算路径上,而该闸门只在完整构建时执行(见 Stage 2)。 风险:未命中高风险路径(未触及与 revert 相关的文件)。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not explored to full depth (tool budget reached): You are review agent reverse-audit — Reverse audit agen...: (none); chunk 2: did not run build/typecheck/tests — verification was static cross-file reading (the type refactor is TS-enforced and the capability lists were checked by hand a…; You are review agent reverse-audit — Reverse audit agen...: I did not execute the new unit tests or typecheck — my role was gap-hunting by inspection; runtime verification belongs to the verification stage.. Not reviewed: reverse audit — stopped before round 5 by the review time budget.
中文说明
已审查。 建议见行内评论。 未探索到全部深度(达到工具调用预算):You are review agent reverse-audit — Reverse audit agen...:(none);chunk 2:did not run build/typecheck/tests — verification was static cross-file reading (the type refactor is TS-enforced and the capability lists were checked by hand a…;You are review agent reverse-audit — Reverse audit agen...:I did not execute the new unit tests or typecheck — my role was gap-hunting by inspection; runtime verification belongs to the verification stage.。 未审查:反向审计——评审时间预算不足,未能开始第 5 轮。
— qwen3.8-max via Qwen Code /review (v0.21.8)
|
@qwen-code /triage |
|
Sandboxed verification: The verification run did not complete, so the phases below may be partial or missing entirely. Advisory evidence for human reviewers — not a review, an approval, or a CI check. 中文 — 判定:
|
Local real-stack verification (maintainer)I built this PR locally and ran the Reviewer Test Plan against a real Environment
Both arms ran the identical probe script, so every row below is a like-for-like comparison.
Before / afterOn base all three routes are Reviewer Test Plan resultsTP-1 — global batch.
The efficiency claim is measurable: three separate singular TP-2 — workspace batch on a non-primary trusted workspace. One generation (7 → 8); on-disk override is TP-3 — rejections. All refused synchronously, with the store generation and every runtime's
A batch of only-missing ids is accepted ( TP-4 — compatibility batch.
Two things I checked beyond the test plan
so the legacy rule cannot leak back in. The final projection confirms the resulting three-way split — Typed SDK helpers against the live daemon. All three reach the real routes and terminate Test suites (head, local)
I did not hit the three unrelated socket/Live cleanup timing failures the PR description mentions from the full VerdictLGTM from a verification standpoint. Ownership, ordering, one-generation persistence, per-runtime reconciliation scoping, and the untouched legacy semantics all reproduce on the real stack, and the surface is cleanly additive — base simply 404s. 中文版本地真实环境验证(维护者)我在本地构建了这个 PR,并在真实的 环境
两条腿跑的是同一个探针脚本,因此下面每一行都是严格的同条件对比。
前后对比base 上三条路由全部 Reviewer Test Plan 结果TP-1 — 全局批量。
性能主张是可测量的:三次独立的单项 TP-2 — 非 primary 受信任 workspace 的批量。 一个 generation(7 → 8);磁盘上只写了 TP-3 — 拒绝路径。 全部同步拒绝,store generation 和三个 runtime 的
全是缺失 id 的批量会被接受( TP-4 — 兼容批量。
测试计划之外我额外查的两点legacy path rule 生效时的 因此 legacy 规则无法回渗。最终 projection 印证了这个三向分化—— 类型化 SDK helper 打真实 daemon。 三个 helper 都能打到真实路由并终态 测试套件(head,本地)
PR 描述中提到的 结论从验证角度 LGTM。ownership、顺序保持、单 generation 持久化、按 runtime 的 reconciliation 范围,以及未被改动的 legacy 语义,全部在真实环境中复现;接口是干净的增量能力——base 上直接 404。 |
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 686 passed · 0 failed · 686 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:686 通过 · 0 失败 · 686 总计 Verification reportPR 8788 Deep Verification — feat(daemon): add batch extension activation APIsVerdict: 中文摘要结论: A/B 结论(对照组 = merge-ref base
Findings:无阻塞项、无需修改。两条环境性说明见「未覆盖/方法」(main 在 merge ref 之后继续前进、容器预置 bundle 过旧已重建),均非 PR 代码问题。 未覆盖: Central claim and A/B proofCentral claim: V2 batch activation persists all valid targets in ONE Extension Store The harnesses are mock-free with respect to the unit under test: they boot the real Wire-level A/B (identical scenario, head vs base build)
Head: 36/36, base: 10/10, untrusted cell: 5/5. Core-level cells (store + manager, real filesystem)
Witness capture: Refactor regression A/B (the pre-existing singular paths)The PR extracted CorrectionsNone — first verification round; no prior review claims to correct. FindingsNone blocking. No behavior contradicts the PR description, the design doc, or the
Reviewer Test Plan walkthrough (per step)
Mutation matrix (vacuity of the PR's new tests)Unmutated controls green:
Both kills landed on the intended behavioral assertion (not an import/compile break), and Targeted gates (all executed, all green)
Not covered
MethodologyEnvironment: CI verify container ( Evidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
Code reviewFull re-review at the new head — the wire contract changed from full identities to name-only 1. The SDK additions break the repo's own browser-bundle budget, so a clean 2. The global batch route reconciles a narrower runtime set than its singular twin. It passes 3. The protocol doc still describes the retired identity contract. In Everything else verified clean, and the machinery is genuinely good:
The request → result flow at this head: sequenceDiagram
participant P1 as Client (SDK)
participant P2 as V2 batch route
participant P3 as Operation queue
participant P4 as ExtensionManager
participant P5 as ExtensionStore
participant P6 as Target runtimes
P1->>P2: PUT batch activation (1-100 names, state)
P2->>P2: validate and dedupe case-insensitively, reject malformed with 400
P2->>P3: queue one operation, reply 202
P3->>P4: resolve names to loaded ids or provisional ids
P4->>P5: one locked write, one generation bump
P5-->>P4: committed snapshot
P4->>P6: refresh all runtimes (global) or one runtime (workspace)
P3-->>P1: terminal result, ordered per-name results
Files changed (24)
Testing — the PR's own CIThis run did not build or execute any PR code; the evidence below is the PR's own CI at the reviewed commit, fetched once via the API. No check has failed at the time of writing; the web-shell E2E smoke leg is still in flight (the table region updates in place as CI settles). Skipped entries are conditional jobs (non-ubuntu unit legs, CLI integration suite), not failures. Green CI does not substantiate the merge here: none of these jobs runs the SDK bundle-size assertion, which is exactly the gate the sandboxed lane below shows failing.
Sandboxed verification — ❌ failed at this head. The Real-scenario testing: N/A for this CI run — daemon API surface, no TUI change. 中文说明代码审查(针对新 head 的完整复审,
其余均验证无误,机制质量很高: 测试:本次运行未构建或执行任何 PR 代码,以上为 PR 自身 CI(API 一次性抓取)。撰写时无检查失败,web-shell E2E smoke 仍在运行(表格区域随 CI 结束原地更新);跳过的是条件性任务。绿色 CI 不能为本次合并背书:这些任务都不运行 SDK bundle 尺寸断言,而那正是沙箱验证显示失败的闸门。沙箱验证在本 head ❌ 失败(构建即失败,非基础设施噪音;早前 ✅ 认证的是 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 2/5 — the machinery is well built and the direction is right, but the SDK additions break the repo's own full-build gate at merge and two artifacts of the final contract rewrite weren't carried through. Stepping back: my independent proposal for this problem was simpler on persistence — a separate name-keyed desired-state map instead of provisional store entries — and the PR's choice to keep one source of truth in the store is the better one for effective-state queries, even at the cost of the remainder machinery. The name-only contract is a clear improvement over the identity draft: callers can't be wrong about ids they cannot know pre-install, and dedup-by-name makes case handling deterministic. Six rounds in, the diff stayed honest — scope was cut when questioned, every wire change is documented in the description, and the store/manager core is atomic and well-tested. What stops this run is concrete, not procedural:
None of these question the design; all three are small, well-scoped fixes. The maintainer's earlier real-stack verifications in this thread covered prior heads ( Requesting changes on the three findings. Once the build gate passes on the merged state, the reconciliation set matches the singular route, and the protocol doc describes the contract the code actually ships, this is in good shape to land. 中文说明总体回顾:我的独立方案在持久化上更简单(独立的按名称 desired-state 映射),PR 选择 Store 内单一事实来源对生效状态查询更优,代价是 remainder 机制;仅名称契约明显优于身份草案。六轮迭代 diff 保持诚实,核心机制原子且测试充分。 本轮止步于具体问题而非程序问题:(1)合并后无法构建——沙箱 就三项发现请求修改:构建闸门在合并态通过、协调集合与单项路由一致、协议文档与代码实际交付的契约一致之后,本 PR 即处于可合并的良好状态。 — Qwen Code · qwen3.8-max Reviewed at |
|
Triage re-run completed without a new review.
The stage comments above were updated with the latest result. View workflow run. 上方各阶段评论已更新为最新结果。查看工作流运行。 |
|
Thanks for the detailed review. I agree with the main scope concern: For the remaining V2 surface, I will add regression coverage for all-missing no-op batches, global rejection of The subsequent real-daemon and sandboxed verification confirmed the current ownership, generation, reconciliation, and trust behavior, but the additional tests are still worthwhile as permanent regression guards. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): PR #8788 adds capability-gated V2 batch extension activat...: none — all checks above completed within budget.; PR #8788 adds capability-gated V2 batch extension activat...: none — all checks above completed within budget.; PR #8788 adds capability-gated V2 batch extension activat...: none — all checks above completed within budget.; PR #8788 adds capability-gated V2 batch extension activat...: none — the full chunk (diff lines 976-1326, untruncated) and all source files it depends on were read.; PR #8788 adds capability-gated V2 batch extension activat...: none — all checks above completed within budget., and 1 more.
中文说明
未探索到全部深度(达到工具调用预算):PR #8788 adds capability-gated V2 batch extension activat...:none — all checks above completed within budget.;PR #8788 adds capability-gated V2 batch extension activat...:none — all checks above completed within budget.;PR #8788 adds capability-gated V2 batch extension activat...:none — all checks above completed within budget.;PR #8788 adds capability-gated V2 batch extension activat...:none — the full chunk (diff lines 976-1326, untruncated) and all source files it depends on were read.;PR #8788 adds capability-gated V2 batch extension activat...:none — all checks above completed within budget.,另有 1 条。
— qwen3.8-max via Qwen Code /review (v0.21.10)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
Not explored to full depth (tool budget reached): chunk 3: did not execute the test file — node_modules is absent in the review worktree and I chose not to spend the remaining tool budget on a monorepo install + build…; PR #8788 adds capability-gated V2 batch extension activat...: integration-tests/cli/qwen-serve-routes.test.ts was not executed (requires a built bundle); its capability insertion was verified statically against registry or…; You are review agent reverse-audit — Reverse audit agen...: none — all planned checks completed within budget.; You are review agent reverse-audit — Reverse audit agen...: none — all planned checks completed within budget.; You are review agent reverse-audit — Reverse audit agen...: none — all planned checks completed within budget..
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
中文说明
已审查。 建议见行内评论。
未探索到全部深度(达到工具调用预算):chunk 3:did not execute the test file — node_modules is absent in the review worktree and I chose not to spend the remaining tool budget on a monorepo install + build…;PR #8788 adds capability-gated V2 batch extension activat...:integration-tests/cli/qwen-serve-routes.test.ts was not executed (requires a built bundle); its capability insertion was verified statically against registry or…;You are review agent reverse-audit — Reverse audit agen...:none — all planned checks completed within budget.;You are review agent reverse-audit — Reverse audit agen...:none — all planned checks completed within budget.;You are review agent reverse-audit — Reverse audit agen...:none — all planned checks completed within budget.。
未审查:反向审计——在 5 轮的反审轮数上限内未收敛。
— qwen3.8-max via Qwen Code /review (v0.21.10)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not explored to full depth (tool budget reached): You are review agent reverse-audit — Reverse audit agen...: none — all checks above completed within budget.; You are review agent reverse-audit — Reverse audit agen...: none — all planned checks completed within budget.; You are review agent reverse-audit — Reverse audit agen...: none — all checks above completed within budget..
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
中文说明
已审查。 建议见行内评论。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未探索到全部深度(达到工具调用预算):You are review agent reverse-audit — Reverse audit agen...:none — all checks above completed within budget.;You are review agent reverse-audit — Reverse audit agen...:none — all planned checks completed within budget.;You are review agent reverse-audit — Reverse audit agen...:none — all checks above completed within budget.。
未审查:反向审计——在 5 轮的反审轮数上限内未收敛。
— qwen3.8-max via Qwen Code /review (v0.21.10)
|
@qwen-code /triage |
|
Sandboxed verification: ❌ not passed — the PR could not be built - workflow run The PR could not be built because 中文 — 判定:❌ 不通过 · PR 构建失败由于 Install/build log— Qwen Code · sandboxed verification |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Three blocking findings at 31ee0e37 — full detail in the Stage 2 comment above:
- Merge breaks the build: the new SDK surface puts the browser daemon bundle at 200998 bytes, over the 200704-byte budget enforced by
assertBrowserSafeBundle; the sandboxed/verifylane failed twice on this head atnpm ci(run 32013547139). The branch is also stale againstmain's budget constant (185 vs 196 KiB). - Global batch route refreshes
workspaceRegistry.list()instead oflistAll(), silently skipping internal live-conversation runtimes — diverging from the singular route and the PR's own "reconciles every registered runtime" claim. docs/developers/qwen-serve-protocol.mdstill documents the retired identity-based contract (extensions: [{extensionId, name}]) while the code ships the name-onlyextensionNamescontract.
The store/manager machinery itself is solid — these are three well-scoped fixes. 🙏
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Deferred under the convergence posture (round 7, not a blocker) — recorded, not requested in this round:
docs/developers/qwen-serve-protocol.md:362 — [review] protocol doc still documents the superseded identity-based batch wire contract (extensionNames is the shipped contract)packages/core/src/extension/extension-store.ts:398 — [probe] discovery promotion drops declarationOnly without recording artifactGeneration, diverging from the design doc
中文说明
收敛姿态下延后(第 7 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.13)
…-toggle # Conflicts: # docs/developers/daemon/11-capabilities-versioning.md # packages/sdk-typescript/package.json
|
All active
The two round-7 Critical threads were replied to with commit-level evidence and resolved. The review's explicitly non-blocking Current-head focused evidence: Core Extension Store/manager 223/223; CLI Extension routes 39/39; SDK build, source/test-fence typecheck, and public-surface typecheck all pass. The new CI run is in progress after the normal @wenshao please re-review when CI finishes. |
Resolved on 6114a01: merged current main for the 197 KiB SDK budget and WorkspaceRegistry.listAll(), changed the global batch route to listAll(), and corrected the name-only protocol documentation. Current-head SDK build/type fences and focused Core/CLI suites pass.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not explored to full depth (tool budget reached): "agent reverse-audit (round 5)": did not exhaustively trace whether applyStoreActivation in the manager reads by id vs name when mutateMany 's by-name merge ( existingByName ) applies a poli….
Deferred under the convergence posture (round 8, not a blocker) — recorded, not requested in this round:
docs/design/daemon-extension-batch-toggle.md:55 — [review] design doc attributes artifactGeneration recording to discovery promotion, which never records itpackages/cli/src/serve/routes/workspace-extensions.ts:2124 — [review] all-unknown inherit no-op (updated:false reconciliation skip) has no route-level testpackages/core/src/extension/extension-store.ts:407 — [review] ensureInitialized name-mismatch conflict throw untestedpackages/core/src/extension/extensionManager.ts:789 — [review] known-name lookup of getExtensionActivationForNameFromSnapshot never exercised by a real testpackages/core/src/extension/extension-store.ts:688 — [probe] uninstall never returns consumed V1 rules to the remainder; install→uninstall erases the V1 entry from both carriersintegration-tests/cli/qwen-serve-routes.test.ts:373 — [test] integration test asserting the capability envelope sits outside every npm workspace; confirm the CI integration job ranpackages/core/src/extension/extension-store.ts:26 — [test] hunk-survived: declarationOnly type declaration (typecheck-only gate)packages/core/src/extension/extension-store.ts:42 — [test] hunk-survived: batch outcome / remainder type declarations (typecheck-only gate)packages/core/src/extension/extension-store.ts:285 — [test] parseState non-true declarationOnly guard untested in rejection directionpackages/core/src/extension/extension-store.ts:311 — [test] malformed persisted legacyProjectionRemainder guard untested in rejection direction
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 5)":did not exhaustively trace whether applyStoreActivation in the manager reads by id vs name when mutateMany 's by-name merge ( existingByName ) applies a poli…。
收敛姿态下延后(第 8 轮,非阻断)——已记录,本轮不要求修改:共 10 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.13)
All five round-8 Critical findings were fixed in 7199382, replied to individually, and resolved with focused Store, manager, and route verification. The ten items explicitly marked deferred/non-blocking remain recorded for follow-up under the review convergence policy.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No blocking issues. LGTM! ✅
Deferred under the convergence posture (round 9, not a blocker) — recorded, not requested in this round:
packages/core/src/extension/extension-store.ts:564 — [probe] fresh-store/import minted policies lack artifactGeneration — exempt from the new demotion guard and reinstall adoption (ghost-policy class)packages/cli/src/serve/routes/workspace-extensions.ts:2124 — [probe] workspace batch updated-passthrough / all-unknown inherit no-op has no route-level testpackages/core/src/extension/extensionManager.ts:791 — [probe] getExtensionActivationForNameFromSnapshot CLI-override fallback branch never exercised by a real testintegration-tests/cli/qwen-serve-routes.test.ts:373 — [test] capability-envelope integration test sits outside every npm workspace; confirm the CI integration job ran (this round ran it locally: 36/36)packages/core/src/extension/extension-store.ts:26 — [test] hunk-survived: declarationOnly/preserveActivationOnNextInstall marker fields (typecheck-only gate)packages/core/src/extension/extension-store.ts:37 — [test] hunk-survived: legacyProjectionRemainder snapshot field / batch-outcome type declarations (typecheck-only gate)packages/core/src/extension/extension-store.ts:285 — [test] hunk-survived: parseState marker validation clauses untested in the rejection directionpackages/core/src/extension/extension-store.ts:313 — [test] hunk-survived: persisted legacyProjectionRemainder guard untested in the rejection direction
中文说明
无阻断问题。LGTM!✅
收敛姿态下延后(第 9 轮,非阻断)——已记录,本轮不要求修改:共 8 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.13)
ytahdn
left a comment
There was a problem hiding this comment.
Round review of head 71993824 (5-agent fan-out across core / serve / SDK / docs+integration / cross-layer consistency).
Verdict: Comment — no Criticals; 1 Major + 4 Minor inline, plus Suggestions. The three-layer contract is consistent end-to-end (verified below).
Verified clean
- Field names, error codes (
invalid_extension_names/invalid_extension_name/invalid_extension_activation/untrusted_workspace), and response shapes match exactly across SDK → routes → controller → core. - Capability gating:
extension_batch_activation_v2: { since: 'v1' }follows the versioning doc's new-tag rule; both routes behindmutate({ strict: true }). - Trust/scope: workspace selectors fail closed (unknown → 400, untrusted → 403, inactive → 503), override key is the registry's canonical cwd, no fallback to the primary runtime; global op isolates per-runtime refresh failures into
warnings. - The unknown-name desired-state contract holds on both ops end-to-end (declare → adopt on install → preserve activation);
inheritnever declares. - Events: one
extensions_changedper runtime refresh, no double-emit;updated: falseskips refresh/broadcast by design.
Also noted (not inline)
- Test gaps without a single anchor: the
ensureInitializedid/name-mismatch throw (Major above) is unpinned; duplicate/case-variant identities within one store-level batch are untested; singular mutation against a batch-demoted policy is untested. - The 100-name cap is enforced only at the route layer (
MAX_EXTENSION_BATCH_SIZE); store/manager accept unbounded arrays. This matches theMAX_WORKSPACE_SKILL_BATCH_SIZEprecedent — noting for awareness only. - Integration suite adds the capability to the envelope baseline but exercises neither new op end-to-end — consistent with house convention for V2 routes, not a blocker.
- AGENTS.md gate: ~555 added core production lines in a
featPR → maintainer-awareness escalation, not a hard block.
ytahdn
left a comment
There was a problem hiding this comment.
Incremental review of 71993824..71bea4dc: 1 Critical + 1 Major. Both were reproduced against the exact head with focused Store probes. The existing targeted tests pass, but do not exercise these intersections.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Deferred under the convergence posture (round 10, not a blocker) — recorded, not requested in this round:
packages/core/src/extension/extension-store.ts:1096 — [probe] demotion heuristic misfires when the artifact directory differs from the policy name by more than casepackages/cli/src/serve/routes/workspace-extensions.ts:2124 — [probe] workspace batch updated:false passthrough (reconciliation skip) has no route-level testpackages/core/src/extension/extensionManager.ts:796 — [probe] getExtensionActivationForNameFromSnapshot CLI-override fallback branch never exercised by a real testintegration-tests/cli/qwen-serve-routes.test.ts:373 — [test] capability-envelope integration test sits outside every npm workspace; the CI integration job was skipped at this headpackages/core/src/extension/extension-store.ts:425 — [test] mutant survived: rename-guard condition forced true leaves all tests green (equal-name path unpinned)packages/core/src/extension/extension-store.ts:559 — [test] mutant survived: remainder-filter guard forced true leaves all tests green (no-rename pass-through unpinned)packages/core/src/extension/extension-store.ts:26 — [test] hunk survived: declarationOnly/preserveActivationOnNextInstall marker fields (typecheck-only gate)packages/core/src/extension/extension-store.ts:37 — [test] hunk survived: legacyProjectionRemainder snapshot field / batch-outcome type declarations (typecheck-only gate)packages/core/src/extension/extension-store.ts:285 — [test] hunk survived: parseState marker validation untested in the rejection directionpackages/core/src/extension/extension-store.ts:313 — [test] hunk survived: parseState legacyProjectionRemainder validation untested in the rejection directionpackages/core/src/extension/extensionManager.test.ts:2580 — [probe] new manager test assertion never reaches the new declarationOnly guards it appears to pin
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
收敛姿态下延后(第 10 轮,非阻断)——已记录,本轮不要求修改:共 11 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.21.13)
ytahdn
left a comment
There was a problem hiding this comment.
Incremental review of 71bea4dc..f6b0d942: the two previously reported findings are fixed, but the persisted artifact-directory repair misses one sibling re-key path. One Critical inline.
ytahdn
left a comment
There was a problem hiding this comment.
Incremental verification complete on head f6b6f69. The previously reported artifact lifecycle and V1 remainder issues are fixed, including the stale-id case-sensitive re-key and uninstall path. Focused ExtensionStore suite passed 94/94; no remaining blocking findings.
doudouOUC
left a comment
There was a problem hiding this comment.
Reviewed the full diff (2837+, 131-, 24 files) at head f6b6f690. All 67 prior review threads are resolved; CI passes.
Key verifications:
- ID safety: Provisional declarations use
hashValue(name.toLowerCase())producing 64-hex sha256, passingassertIdentityvalidation. - State parsing: Global route correctly rejects
inheritviaparseActivationState; workspace route accepts it viaparseWorkspaceBatchActivationState. - Trust boundary: Workspace route enforces
requireTrustedWorkspaceRuntimeand never falls back to the primary runtime. - Reconciliation scope: Global batch reconciles all registered runtimes; workspace batch reconciles only the selected trusted runtime.
- No-op optimization:
inheritfor unknown names skips declaration creation (mutateManywithdeclareUnknown=false) and returnsupdated: false, which the controller uses to skip reconciliation. - Backward compatibility: Singular V2 activation routes still reject
declarationOnlypolicies (verified in bothmutate()and the route-levelpolicy.declarationOnlyguard). - Legacy projection:
legacyProjectionRemaindercorrectly preserves unmapped rules across declaration/installation lifecycles. - FS safety:
extensionArtifactExistshandles case-insensitive filesystems via directory listing fallback. - Test coverage: 956 (store) + 643 (routes) + 187 (manager) lines of focused tests.
No Critical or High-severity issues found. LGTM.
|
Released in v0.21.15. |








What this PR does
This PR adds capability-gated V2 batch activation operations for Extensions. One global operation updates
defaultActivationfor up to 100 Extension names and reconciles every registered runtime. One workspace-qualified operation updates exact overrides for a selected trusted workspace, acceptsenabled,disabled, orinherit, and reconciles only that runtime.The public batch contract is name-based: callers send
extensionNames. Settingenabledordisableddoes not require an installed artifact. An unknown name creates a desired-state declaration; installing or discovering an Extension with that name later binds the declaration to its internal artifact ID and preserves the declared global and workspace activation.inheritfor an unknown workspace target is a no-op and does not create a declaration.Names are matched case-insensitively. Targets are validated and persisted atomically in one Extension Store generation, request order is preserved after deduplication, and one manager refresh is performed. Typed SDK helpers and protocol documentation cover both V2 batch surfaces. Singular V2 activation routes remain ID-addressed and installed-only.
Why it's needed
V2 clients such as opencode need to declare a global default and workspace-specific overrides before an Extension is installed. Extension names are the user-facing loading identity, while artifact IDs are internal and may not be known before installation. Repeating singular ID-based calls cannot support that lifecycle because singular routes resolve installed Extensions first.
These operations preserve the ownership model of the singular routes while adding pre-install desired state and reducing repeated Store writes and runtime reconciliations.
Reviewer Test Plan
How to verify
enabled,disabled, andinheritbatches against a non-primary trusted workspace. Confirmenabledanddisabledmay declare an uninstalled name, whileinheriton an unknown name is a no-op. Confirm only that workspace's exact overrides change and only the selected runtime reconciles.Evidence (Before & After)
N/A — daemon API, persistence, SDK, and documentation changes only.
Tested on
Environment (optional)
Node.js 22. On the current merged head, focused verification passed 223 Core Store/manager tests and 39 CLI route tests. The SDK build, source/test-fence typecheck, and public-surface typecheck passed, including the browser bundle size budget.
Risk & Scope
extensionNamescontract. Singular V2 routes are unchanged.Linked Issues
Builds on #8664.
中文说明
本 PR 为 Extension 新增 capability 管控的 V2 批量激活操作。全局接口一次按最多 100 个 Extension 名称修改
defaultActivation,并协调进程内全部 runtime(包括内部 conversation runtime);workspace 接口针对指定且受信任的 workspace 修改精确 override,支持enabled、disabled和inherit,只协调目标 runtime。公开契约只传
extensionNames。设置enabled或disabled不要求 Extension 已安装:未知名称会创建 desired-state 声明;之后安装或发现同名 Extension 时,声明会绑定到内部 artifact ID,并保留已声明的全局和 workspace 状态。对未知名称执行inherit是 no-op,不会创建声明。名称按大小写不敏感匹配;目标会原子校验并在一个 Store generation 中持久化;去重后保持请求顺序;manager 只 refresh 一次。单项 V2 激活接口保持不变,仍按 ID 寻址且只允许操作已安装 Extension。
当前合并后 HEAD 的聚焦验证通过 223 个 Core Store/manager 测试和 39 个 CLI 路由测试;SDK build、source/test-fence typecheck 与 public-surface typecheck 均通过,并覆盖 browser bundle 体积预算。