Skip to content

feat(external-context): Publish the Mem0 Extension package - #10653

Merged
wenshao merged 3 commits into
QwenLM:mainfrom
doudouOUC:feat/external-context-mem0-distribution
Sep 1, 2026
Merged

feat(external-context): Publish the Mem0 Extension package#10653
wenshao merged 3 commits into
QwenLM:mainfrom
doudouOUC:feat/external-context-mem0-distribution

Conversation

@doudouOUC

Copy link
Copy Markdown
Collaborator

What this PR does

This PR makes the existing retrieval-only external-context-mem0 Extension installable as the public npm package @qwen-code/external-context-mem0. It keeps the package and Extension manifest aligned with the Qwen Code release version, adds the package to the shared published-version guard, and adds a provenance-enabled release step behind an explicit bootstrap gate. The published package contains only the bundled runtime, canonical schemas, manifest, and documentation; it does not contain provider presets or administrator configuration.

Why it's needed

PR #10634 made administrator-owned Mem0 dialects usable but left the Extension private, so administrators still lacked a supported scoped npm installation and update path. Publishing the self-contained package closes that distribution gap without adding Qwen Core integration or Qwen-maintained Hologres, PolarDB, RDS, or other provider data.

Reviewer Test Plan

How to verify

  • Build and test the Extension package and confirm all 49 package tests pass.
  • Inspect the dry-run npm tarball and confirm it contains exactly six files: the README, bundled runtime, package metadata, Extension manifest, and two schemas, with no provider-specific dialect or administrator configuration.
  • Install the package through a temporary npm registry into an isolated Qwen home with the released Qwen Code CLI and confirm the Extension list reports npm as the source and the expected release version.
  • Start the installed bundle with temporary administrator-owned instance and dialect files plus a synthetic loopback HTTP provider, then confirm initialize, tools/list, and context_search produce the bounded request and normalized response. Confirm a running process retains its startup configuration, a restart loads the updated dialect, and invalid dialect errors do not expose paths or configuration values.
  • Run the focused release/version script tests and confirm 102 tests pass with one existing host-specific skip, then run the repository build, typecheck, and lint checks.

Evidence (Before & After)

N/A — this is package distribution and release automation work with no TUI change.

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

macOS; Node.js 24.12.0; global Qwen Code 0.22.3; temporary loopback npm registry and synthetic HTTP provider.

Risk & Scope

  • Main risk or tradeoff: Release publishing depends on a one-time npm bootstrap and trusted-publisher setup. The workflow keeps publishing disabled until NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED is explicitly enabled, publishes this package before the other npm packages, and includes it in the shared published-version guard so a partial release cannot be overwritten.
  • Not validated / out of scope: A real npm.org publish and trusted-publisher configuration are intentionally left to the release administrator. Live provider services, provider presets, administrator configuration, Qwen Core wiring, write operations, and Auto Recall remain out of scope.
  • Breaking changes / migration notes: None for the existing Extension runtime or administrator-owned configuration. The public package becomes available only after the one-time bootstrap publish and repository-variable enablement.

Linked Issues

Related: #10634

中文说明

本 PR 做了什么

本 PR 将现有仅支持检索的 external-context-mem0 Extension 调整为可通过公开 npm 包 @qwen-code/external-context-mem0 安装。它让 package 与 Extension manifest 始终和 Qwen Code 发布版本保持一致,将该包纳入统一的已发布版本保护,并在显式 bootstrap 开关后增加启用 provenance 的发布步骤。发布包只包含 bundle 后的运行时、规范 schema、manifest 和文档,不包含厂商 preset 或管理员配置。

为什么需要

PR #10634 已经支持管理员自有的 Mem0 dialect,但 Extension 仍为私有包,因此管理员仍缺少受支持的 scoped npm 安装和升级路径。发布这个自包含 package 可以补齐分发能力,同时不引入 Qwen Core 集成,也不由 Qwen 维护 Hologres、PolarDB、RDS 或其他厂商数据。

Reviewer 测试计划

如何验证

  • 构建并测试 Extension package,确认全部 49 个 package 测试通过。
  • 检查 npm dry-run tarball,确认其中恰好只有六个文件:README、bundle 后的运行时、package 元数据、Extension manifest 和两个 schema,并且不包含任何厂商特定 dialect 或管理员配置。
  • 使用已发布的 Qwen Code CLI,通过临时 npm registry 将该包安装到隔离的 Qwen home,确认 Extension 列表显示来源为 npm 且版本与预期发布版本一致。
  • 使用临时的管理员自有 instance/dialect 文件和合成的本机 HTTP provider 启动已安装 bundle,确认 initializetools/listcontext_search 生成受限请求并归一化响应;同时确认运行中的进程保持启动时配置,重启后加载更新的 dialect,非法 dialect 错误不会暴露路径或配置值。
  • 运行聚焦的发布和版本脚本测试,确认 102 个测试通过、仅有 1 个既有的宿主环境相关 skip,然后运行仓库 build、typecheck 和 lint 检查。

证据(Before & After)

N/A——这是 package 分发和发布自动化工作,没有 TUI 变化。

测试平台

OS 状态
🍏 macOS ✅ 已测试
🪟 Windows ⚠️ 未测试
🐧 Linux ⚠️ 未测试

环境(可选)

macOS;Node.js 24.12.0;全局 Qwen Code 0.22.3;临时本机 npm registry 和合成 HTTP provider。

风险与范围

  • 主要风险或取舍:发布流程依赖一次性的 npm bootstrap 和 trusted publisher 配置。工作流会在显式启用 NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED 前保持发布关闭,先于其他 npm package 发布此包,并将其纳入统一的已发布版本保护,避免覆盖部分完成的发布。
  • 未验证 / 不在范围内:真实 npm.org 发布和 trusted publisher 配置有意留给发布管理员执行。真实 provider 服务、厂商 preset、管理员配置、Qwen Core 接线、写操作和 Auto Recall 均不在本 PR 范围内。
  • 破坏性变更 / 迁移说明:现有 Extension 运行时和管理员自有配置没有破坏性变更。公开 package 只有在完成一次性 bootstrap 发布并启用仓库变量后才会可用。

关联事项

相关:#10634

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

E2E test report

Validated the packaged Extension through the same npm installation boundary administrators will use:

  • Published the generated tarball to a temporary loopback npm registry and installed @qwen-code/external-context-mem0 into an isolated Qwen home with global Qwen Code 0.22.3.
  • Confirmed the installed Extension metadata reports npm as its source and version 0.22.3.
  • Started the installed bundle against temporary administrator-owned instance and dialect files plus a synthetic loopback HTTP provider.
  • Completed MCP initialize, tools/list, and context_search; verified the exact bounded POST request and normalized search response.
  • Confirmed the running process retained its startup dialect, restarting loaded a modified dialect, and invalid dialect failures remained redacted.
  • Confirmed the npm dry-run tarball contains exactly six intended files and no provider-specific dialect or administrator configuration.

Additional verification: 49 Extension tests passed; 102 focused release/version tests passed with one existing host-specific skip; repository build, typecheck, and lint passed before the final rebase; the post-rebase focused tests and npm publish dry-run passed again.

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@doudouOUC
doudouOUC force-pushed the feat/external-context-mem0-distribution branch from 23c2f04 to cc6a742 Compare August 31, 2026 16:06
@github-actions

Copy link
Copy Markdown
Contributor

Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration.

中文

请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR! Re-run on a moved head — the branch took a fix round (bf51b8d7), a rebase onto current main (77825938), and a final hardening round landing at 49ea2a0b11. All stages re-executed at the new head; the gate findings below replace the earlier pass.

Template looks good ✓

  • Problem: unchanged from the first pass and still real — the Mem0 Extension landed as a private: true workspace via feat(external-context): Load administrator-owned Mem0 dialects #10634, so administrators have no supported npm install/update path even though qwen extensions install @scope/name already exists. Publishing closes a genuine distribution gap.
  • Direction: this stayed escalated in the prior run because the diff touches release infrastructure (release.yml) and mints a new public package — per triage policy that is a maintainer decision. That decision has now been made: @wenshao, a CODEOWNER for release.yml, ran the reviewer test plan end to end on Linux at this exact head (plus release-side A/B scenarios), reported "recommend merge" with no blocker, and approved the PR. The escalation is resolved; the bootstrap plan's one open doc item (ordering of the one-time bootstrap) is recorded in the thread as a pre-bootstrap follow-up.
  • Size: small and focused — 168 changed lines across 12 files, no core paths (Stage 0 N/A). Breakdown: ~57 production lines (release.yml 24, scripts/version.js 22, package/extension manifests 8, get-release-version.js 1, size baseline 2), ~45 docs, ~66 tests.
  • Approach: minimal, mirrors the established inline-publish pattern. The three non-blocking findings from the maintainer verification were deferred to follow-ups rather than widening this PR, each recorded in the thread — consistent with the five-round rule in AGENTS.md.
  • Risk: no high-risk path matches (Stage 1e). The elevated-review signal remains the area, not the diff: release.yml + a new public package — now covered by the maintainer sign-off above.

Moving on to code review. 🔍

中文说明

感谢贡献!本次是在 head 变化后的 re-run——分支经历了一轮修复(bf51b8d7)、一次 rebase 到最新 main(77825938),最终落在 49ea2a0b11。所有阶段已在新 head 上重新执行,以下门禁结论取代上一轮。

模板完整 ✓

  • 问题:与首轮一致且真实存在——Mem0 Extension 经由 feat(external-context): Load administrator-owned Mem0 dialects #10634private: true 工作区落地,管理员缺少受支持的 npm 安装/升级路径,而 qwen extensions install @scope/name 早已可用。发布该包补上真实的分发缺口。
  • 方向:上一轮因 diff 触及发布基础设施(release.yml)并新增公开包而保持升级——按 triage 策略这属于维护者决定。该决定现已作出release.yml 的 CODEOWNER @wenshao 在此 head 上于 Linux 完整跑通了测试计划(并补充发布侧 A/B 场景),结论为"建议合并且无阻塞问题",并已批准本 PR。升级已解除;bootstrap 方案唯一遗留的文档项(一次性 bootstrap 的顺序)已作为前置跟进事项记录在线程中。
  • 规模:小而聚焦——12 个文件共 168 行改动,未触及核心路径(Stage 0 不适用)。构成:生产代码约 57 行(release.yml 24、scripts/version.js 22、包与扩展 manifest 8、get-release-version.js 1、size baseline 2),文档约 45 行,测试约 66 行。
  • 方案:最小化,沿用既有的内联发布模式。维护者验证中的三条非阻塞发现已按 AGENTS.md 的五轮规则延期为跟进项并逐条记录在线程中,未扩大本 PR 范围。
  • 风险:未命中高风险路径(Stage 1e)。需要升级审查的仍是领域而非 diff 本身:release.yml + 新的公开包——现已由上述维护者签核覆盖。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 49ea2a0b11549a9061f14223475f81c739e1d905 · re-run with @qwen-code /triage

@doudouOUC doudouOUC self-assigned this Aug 31, 2026

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline.

Not explored to full depth (tool budget reached): "agent 6b": live execution of scripts/tests/{get-release-version,package-scripts,release-workflow}.test.js and integrations/external-context-mem0/src/manifest.test.ts (veri….

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

Test Plan (not a blocker): 102 tests pass — this review observed 49 passed.

中文说明

已审查。 建议见行内评论。

未探索到全部深度(达到工具调用预算):"agent 6b"live execution of scripts/tests/{get-release-version,package-scripts,release-workflow}.test.js and integrations/external-context-mem0/src/manifest.test.ts (veri…

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

Test Plan(非阻断):102 tests pass — this review observed 49 passed

— qwen3.8-max via Qwen Code /review (v0.22.3)

Comment thread .github/workflows/release.yml
Comment thread scripts/tests/get-release-version.test.js
Comment thread scripts/tests/package-scripts.test.js
Comment thread scripts/tests/release-workflow.test.js
@qqqys

qqqys commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Verification report — head cc6a742b (no Critical found)

Code review at head found no merge-blocking issues. The publish wiring is structurally identical to the existing @qwen-code/audio-capture step (dry-run handling, already-published skip guard, --provenance), and stays dark behind vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED until the one-time npm bootstrap — merging alone publishes nothing. Verified at head:

  • Published-package guard: @qwen-code/external-context-mem0 joins PUBLISHED_PACKAGES in scripts/get-release-version.js; doesVersionExist tolerates the not-yet-existing package (E404 handled in both strict and best-effort modes), so the first release run is not broken by the new list entry.
  • Version wiring: the workspace is in the root workspaces list, so npm version bumps it during release:version; the new scripts/version.js step keeps qwen-extension.json aligned; the release commit stages integrations/*/qwen-extension.json. Root version 0.22.3 matches the PR's package and manifest versions.
  • Build ordering: scripts/build.js includes integrations/external-context-mem0 in the build order, so dist/main.js exists before the publish step runs.
  • Publish job context: the step runs inside the publish job (environment: production-release, id-token: write), the same trusted-publishing context as its siblings.

This run then verified the actual distribution boundary end to end:

Setup: branch built from scratch (npm ci, full prepare build), tmux 220x50, scratch $HOME, API-key auth, model qwen3.8-max.

  1. Wiring tests at head (102 pass): scripts/tests/package-scripts.test.js 19/19, release-workflow.test.js 31/31 (1 pre-existing skip), get-release-version.test.js 53/53 — covering the gate condition, step ordering, trusted-publishing requirements, and manifest alignment.
  2. Extension suite at head (49/49): manifest/profile/request-engine/schemas/main/mcp tests, including the new packaging assertions (private gone, exact name/version/repository, manifest version == package version).
  3. Tarball audit: npm pack ships exactly 6 files — dist/main.js, package.json, qwen-extension.json, README.md, schemas/dialect.schema.json, schemas/instance-config.schema.json. No sources, fixtures, or dev tooling; dependencies absent (self-contained bundle), matching the design contract.
  4. Registry install path (the documented command): published the tarball to a temporary loopback registry and ran the administrator-facing command qwen extensions install @qwen-code/external-context-mem0 --registry http://127.0.0.1:4873 --consent against a hermetic $HOMEExtension "external-context-mem0" installed successfully and enabled.; extensions list shows it with Source: @qwen-code/external-context-mem0 (Type: npm), Release tag: 0.22.3, and the external-context-mem0 MCP server.
  5. tmux TUI: /mcp shows external-context-mem0 ✓ connected (Source: Extension) with exactly 1 tool — mcp__external-context-mem0__context_search; a live model turn settled clean, /quit clean, no orphan processes.

Direction (publishing a new public package + release.yml changes) was escalated to maintainers by triage per policy; this report takes no position on that call. Non-blocking observation: the README's "This command becomes available after the package's first registry release" wording is accurate — until the bootstrap publish lands, the command resolves but the package 404s on the public registry.


中文摘要:在 head 上完成代码审查,未发现阻塞合并的 Critical。发布步骤与现有 @qwen-code/audio-capture 步骤同构(dry-run、已发布跳过、--provenance),且在维护者完成一次性 npm bootstrap 前由仓库变量保持关闭——仅合并不会发布任何内容。已验证:新包加入 PUBLISHED_PACKAGES 后 E404 兼容(首个发布不会因此失败);版本链路(workspace bump + version.js 同步 manifest + 发布提交暂存)完整;构建顺序保证发布前 dist/main.js 存在。端到端验证:102 个发布接线测试 + 49 个扩展测试全部通过;npm pack 仅含 6 个约定文件(运行时/schema/manifest/README);打包发布到临时 loopback registry 后,文档中的 qwen extensions install @qwen-code/external-context-mem0 --registry ... 命令安装成功并启用;tmux TUI 中 /mcp 显示 external-context-mem0 ✓ connected,工具 context_search 正常注册,真实模型回合正常,/quit 干净、无残留进程。方向性问题(发布新公开包)已由 triage 转交维护者决定,本报告不作倾向性意见。

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

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: 98 passed · 0 failed · 98 total

Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:98 通过 · 0 失败 · 98 总计

抖动门:✅ 4 changed test file(s) x 5 identical rounds, no divergence

Verification report

PR #10653 Deep Verification — feat(external-context): Publish the Mem0 Extension package

Verdict: merge-ready — 98 scripted assertions, 98 pass / 0 fail. Verified head: cc6a742bcb880c3630a76bf99f78d7a3d1cfea56 (git rev-parse HEAD^2, matches the snapshot headRefOid; base tip be585888). Single-commit PR; the one commit is locally reachable and matches the metadata snapshot.

中文摘要
  • 结论:merge-ready。98 条脚本化断言全部通过,未发现阻塞性问题。
  • A/B 结论(均对比 base be58588 与 head cc6a742 的 worktree/产物,见下表与证据图):
    • 可发布性翻转:head 的 npm publish --dry-run 正常进入发布流程(6 文件:README、打包运行时、package.json、扩展清单、两个 schema);base 因 private: true 被 npm 静默跳过(注意:是 exit 0 的 skip,不是 EPRIVATE)。
    • 推送时守卫翻转:同一注册表状态(仅 mem0 已发布该版本)下,head 以 exit 3 拒绝并点名 @qwen-code/external-context-mem0,base 完全探测不到该包(11 包)而放行。
    • version.js 翻转:head 将 qwen-extension.json 版本对齐到发布版本;base 停留在 0.1.0。
    • 发布步骤回放:门槛变量真值表(仅 == 'true' 且仓库为 QwenLM/qwen-code 时执行)、--dry-run/dist-tag/已发布跳过分支、步骤顺序(先于其他 npm 包)全部符合预期;步骤体逐字提取自 release.yml 执行。
    • E2E:打包产物经回环 stub registry 由真实 CLI 安装成功,extensions list 报告 npm 来源与 0.22.3;安装后的 bundle 以真实 MCP stdio 启动,context_search 发出受方言约束的有界请求并返回归一化结果;运行中进程保持启动配置、重启加载新方言;非法方言报错不泄露路径或凭据。
  • Findings:1 条建议级(既存、非本 PR 引入)——MCP serverInfo.version 在源码中硬编码为 0.1.0,与包/清单对齐后的版本不一致;1 条信息级——npm 10 对私有 workspace 包的 publish 是 exit 0 的静默跳过,复制此发布步骤模式时需注意。详见 Findings。
  • 未覆盖:真实 npm.org 发布与 trusted publishing 配置(PR 明确留给发布管理员);与当前 main 的试合并(离线沙箱无法访问远端);yamllint(容器 pip3 权限受限);仓库级全量测试/全量 lint(仅跑受影响范围)。

Scope

  • Central claim: the extension becomes a publishable public npm package whose tarball contains exactly the bundled runtime, canonical schemas, manifest, and documentation — and stays gated until the one-time bootstrap.
  • Secondary claims: (1) release wiring — manifest/version alignment in version.js, staging of qwen-extension.json in the release commit, inclusion in the shared already-published guard, publish step behind vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED and ordered before the other npm packages; (2) the published artifact actually works through the real extension installer and runtime.

Central claim + A/B

Head cells run at HEAD (merge commit), control cells at HEAD^1. Per-arm worktree builds were byte-identical (1,248,322 B each; expected — the PR touches no src/); the packed artifact in the cells below is the main-tree build produced under the lockfile's node_modules layout (see the worktree incident under Not covered). Witness: 01-pack-ab-head-vs-base.png.

# Cell Arm Oracle Result
1 npm pack --json head exactly 6 entries: README.md, dist/main.js, package.json, qwen-extension.json, schemas/dialect.schema.json, schemas/instance-config.schema.json ✅ 6/6, tar -tzf corroborates
2 npm publish --dry-run --access public head proceeds: Publishing to … (dry-run) + + @qwen-code/external-context-mem0@0.22.3
3 same base skipped: Skipping workspace @qwen-code/external-context-mem0, marked as private, no Publishing to ✅ flip
4 files[] / tarball content both identical between arms (the diff is publishability only)

One environment nuance found while building this oracle: npm 10.9.8 does not return EPRIVATE for a private workspace package — it silently no-ops with exit 0 (see Findings F2).

Version-guard A/B (shared published-version guard)

Real node scripts/get-release-version.js --assert-unreleased=9.9.9 under PATH-stubbed npm/git/gh (the stubs emulate "only mem0 has shipped 9.9.9"). Witness: 03-guard-ab-head-vs-base.png.

Cell Arm Registry state Exit Behavior
1 head mem0@​9.9.9 shipped 3 ::error:: refusal naming @qwen-code/external-context-mem0 + recovery guidance; stub log shows the probe view @qwen-code/external-context-mem0@9.9.9 version
2 base identical 0 passes; mem0 never probed (11 packages) — the flip
3 head nothing shipped 0 all 12 packages probed
4 head mem0 + channel-feishu shipped 3 refusal names both (partial-publish accounting)

Precondition checked: the base script already carries --assert-unreleased; the only arm difference is the PUBLISHED_PACKAGES entry.

Version-alignment A/B (scripts/version.js)

Full replay of node scripts/version.js 9.9.9-verify.0 per arm with an npm emulator (applies npm version bumps, skips npm install). Witness: 04-version-js-ab.png.

Arm package.json qwen-extension.json
head 9.9.9-verify.0 9.9.9-verify.0 (aligned)
base 9.9.9-verify.0 0.1.0 (left behind) — the flip

Followed by the staging replay (witness: logs 05-gitadd.log): the head git add line (extracted verbatim from release.yml) stages the bumped manifest; the base line, run on the same tree, leaves it unstaged. The glob integrations/*/qwen-extension.json matches both integration manifests; the sibling external-context manifest is unmodified, so staging it is a no-op.

Publish-step replay (gate + flags)

The step's run: block was extracted from release.yml by a YAML parser and executed under bash --noprofile --norc with a recording npm stub. Witness: 02-publish-step-replay.png.

  • if: expression pinned verbatim, then evaluated as a truth table: runs only for github.repository == 'QwenLM/qwen-code' and vars.…ENABLED == 'true' (case-sensitive; unset/''/false/TRUE all skip; fork repos skip even with the var set).
  • Dry run → publish --provenance --access public --tag=preview --dry-run (no registry probe).
  • Real run, version absent → probe view @qwen-code/external-context-mem0@<ver> version, then publish --provenance --access public --tag=latest.
  • Real run, version present → ::notice::… already published; skipping, exit 0, no publish call.
  • Ordering: mem0 step index < audio-capture < qwen-code, and after Build Bundle and Prepare Package (so dist/main.js exists at publish time — scripts/build.js builds integrations/external-context-mem0 on the non---cli-only path the release job uses).
  • Base arm: no mem0 step exists; base git add line lacks the manifest glob.

E2E: registry install + runtime boot

Witness: 06-install-and-runtime-e2e.png; logs under logs/e2e/ and logs/runtime/.

  • The head tarball was served by a loopback stub npm registry; the real CLI (packages/cli/dist/index.js extensions install @qwen-code/external-context-mem0, isolated HOME+QWEN_HOME) fetched the packument and tarball (request log asserted), installed, and extensions list printed Mem0 External Context (0.22.3) with Source: @qwen-code/external-context-mem0 (Type: npm). The installed manifest version is 0.22.3 — this PR's manifest bump makes that true now, and the version.js alignment step keeps it true for every future release (without them, the list would show the manifest's stale 0.1.0).
  • The installed bundle was booted as a real MCP stdio server with admin-owned instance/dialect files and a loopback provider: initialize and tools/list (exactly context_search) succeed; context_search issued exactly GET /memories/search?query=team+conventions&app_id=verify-app&top_k=5 with the credential on the dialect-bound x-api-key header, and returned the normalized item; a running process kept its startup dialect after the file changed, a restart picked up the new path; an invalid dialect exited 1 with a bounded message leaking neither paths nor credential values.

Mutation matrix (vacuity)

Each PR hunk reverted in the head worktree; the PR's own tests were required to go red on the intended assertion. Witness: 05-mutation-matrix.png.

Mutant Suite(s) Result
M0 unmutated control all green (2 + 301 tests)
M1 private: true restored, repository removed manifest.test.ts red on is self-contained and exposes only context_search
M2 mem0 dropped from PUBLISHED_PACKAGES get-release-version.test.js red on pins the full published-package set
M3 mem0 publish step removed from release.yml release-workflow + package-scripts red on all three step-related tests
M4 version.js alignment step removed package-scripts.test.js red on keeps the Mem0 Extension manifest aligned with release versions
M5 positive control (pre-existing audio-capture entry dropped) get-release-version.test.js red — the harness can fail

Postcondition: worktree pristine after all reverts. All four hunks are load-bearing; no survivors.

Corrections

None — no prior round and no inaccurate bot claims to correct.

Findings

F1 — Suggestion (pre-existing, surfaced by this PR's purpose): the MCP server identifies itself as 0.1.0 forever.
integrations/external-context-mem0/src/mcp.ts:22 hardcodes version: '0.1.0' in new McpServer(...). After this PR, package.json and qwen-extension.json track the Qwen Code release version, but the initialize response's serverInfo.version (observed live: "version":"0.1.0" from the built bundle) stays stale. Cosmetic/observability only — nothing gates on it — but it is the one version surface the alignment work does not reach. Not introduced by this PR (the line predates it); a follow-up could inject the version at build or startup. No code change requested here.

F2 — Informational (npm semantics, no change requested): publishing a private workspace package is a silent exit-0 skip.
Measured on npm 10.9.8: npm publish [--dry-run] on a package still marked private inside a workspace prints Skipping workspace …, marked as private and exits 0 — no EPRIVATE, no failure. Consequence: a copy of this PR's publish-step pattern pointed at a not-yet-unprivated package would pass CI while publishing nothing. This PR is not exposed (the step is additionally gated by the repo var, and the package is unprivated in the same diff), but the skip is invisible unless someone reads the log line — worth knowing for future package additions.

Not covered

  • Real npm.org publish and trusted-publisher setup — intentionally left to the release administrator by the PR; the gate keeps the step disabled until NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED is set. The disabled-gate path and the enabled-step semantics are replayed, not a live publish.
  • Trial merge into current main — this sandbox has no network/token to fetch origin; verified against the PR's base only. The base is the merge-base tip, so the A/B is exact for what lands unless main has moved (not observable here).
  • yamllintpip3: Permission denied in this container; actionlint (live-controlled) and shellcheck ran clean instead.
  • Repo-wide test suite / repo-wide lint — targeted gates only: extension suite 49/49; PR-touched script suites + size ratchet 301 passed, 0 skipped (the container has GNU realpath, so the host-specific skip the test plan mentions did not apply); tsc --noEmit, eslint (live-controlled by a planted unused-var), prettier, actionlint all clean; size baseline 57772 release.yml matches the file's byte size exactly.
  • Harness-infrastructure incident (self-inflicted, resolved): the first tarball round was built in a worktree, which has no node_modules; esbuild silently resolved the hoisted ajv@6 instead of the package's nested ajv@8, producing a bundle that crashed at startup (import_ajv.Ajv is not a constructor). Diagnosed, rebuilt/packed from the main tree (which carries the lockfile layout from npm ci, exactly as the publish job's npm ci --ignore-scripts reproduces it), and all downstream cells re-ran green against the correct artifact. Not a PR defect — recorded so the node_modules-less-worktree trap is visible.
  • Windows behavior — the author tested macOS only; this round ran Linux E2E end to end, which covers the untested-by-author lane for install/run.
  • Live provider services / Auto Recall / write operations — out of the PR's declared scope; not exercised.

Methodology

Ran inside the CI verify container (node v22.23.2, npm 10.9.8, same node:22-bookworm runtime as the triage lanes). Working tree = refs/pull/10653/merge at depth 2; scratch worktrees at tmp/base-tree (HEAD^1) and tmp/head-tree (HEAD), removed after use. Harnesses live in the artifact dir as harness-01…09*.mjs (+ stub dirs) with raw logs under logs/; each prints [PASS]/[FAIL] lines and an ASSERTIONS pass=N fail=M footer, and the 98 totals in assertions.json are the sum of those footers from the final runs. Techniques: npm pack/publish --dry-run on both arms; step bodies extracted from release.yml by a YAML parser and executed against a recording npm stub; the push-time guard run as a real process under PATH-stubbed npm/git/gh; scripts/version.js replayed with an npm emulator that applies npm version bumps and skips npm install; staging replayed via the extracted git add line; a loopback HTTP stub registry + the built CLI for the install E2E (with HOME/QWEN_HOME pinned after discovering QWEN_HOME wins over $HOME in Storage.getGlobalQwenDir()); and a loopback memory provider speaking the GET dialect for the runtime-boot wire assertions. Evidence images in evidence/ were rendered by scripts/verify-capture.mjs.

Flakiness gate log

rounds=5 files=4 skipped=0
file integrations/external-context-mem0/src/manifest.test.ts: (cd integrations/external-context-mem0) npx --no-install vitest run ./src/manifest.test.ts
file scripts/tests/get-release-version.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/get-release-version.test.js
file scripts/tests/package-scripts.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/package-scripts.test.js
file scripts/tests/release-workflow.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/release-workflow.test.js


per-file results (P=pass F=fail I=infra-exit, one letter per run):
  integrations/external-context-mem0/src/manifest.test.ts: PPPPP
  scripts/tests/get-release-version.test.js: PPPPP
  scripts/tests/package-scripts.test.js: PPPPP
  scripts/tests/release-workflow.test.js: PPPPP

verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence

--- per-invocation detail (full copy in the artifact) ---
round 1 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 1 · scripts/tests/get-release-version.test.js: P (exit 0)
round 1 · scripts/tests/package-scripts.test.js: P (exit 0)
round 1 · scripts/tests/release-workflow.test.js: P (exit 0)
round 2 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 2 · scripts/tests/get-release-version.test.js: P (exit 0)
round 2 · scripts/tests/package-scripts.test.js: P (exit 0)
round 2 · scripts/tests/release-workflow.test.js: P (exit 0)
round 3 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 3 · scripts/tests/get-release-version.test.js: P (exit 0)
round 3 · scripts/tests/package-scripts.test.js: P (exit 0)
round 3 · scripts/tests/release-workflow.test.js: P (exit 0)
round 4 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 4 · scripts/tests/get-release-version.test.js: P (exit 0)
round 4 · scripts/tests/package-scripts.test.js: P (exit 0)
round 4 · scripts/tests/release-workflow.test.js: P (exit 0)
round 5 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 5 · scripts/tests/get-release-version.test.js: P (exit 0)
round 5 · scripts/tests/package-scripts.test.js: P (exit 0)
round 5 · scripts/tests/release-workflow.test.js: P (exit 0)

Evidence images

01-pack-ab-head-vs-base

02-publish-step-replay

03-guard-ab-head-vs-base

04-version-js-ab

05-mutation-matrix

06-install-and-runtime-e2e

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Code review

Re-reviewed the full diff at the new head 49ea2a0b11 (not just the delta since the prior pass). My independent proposal from the title and motivation is unchanged, and the PR still matches it. The rounds since the last triage added test hardening (a guard that the manifest version is read from the exact readJson(mem0ManifestPath) source, a guard that the write goes through the repository's canonical prettier invocation, comment renumbering in version.js) and a mechanical rebase onto current main, whose release.yml growth the .size-baseline bump absorbs. I re-verified every load-bearing integration point against the base code myself:

  • The publish step is a structural clone of the Publish @qwen-code/audio-capture sibling with one legitimate addition: the vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED == 'true' gate. The gate fails closed — an unset variable is not 'true', so merging ships nothing until a release administrator flips it. The step runs inside the existing publish job, which already carries the production-release environment and id-token: write that provenance requires; no secrets are interpolated into run scripts (outputs flow through env:).
  • Safe before the bootstrap exists. The new PUBLISHED_PACKAGES entry in scripts/get-release-version.js cannot break a release while the package is absent from npm: doesVersionExist treats the E404 from a missing package as "absent" in both best-effort and strict mode (a non-E404 probe failure still throws in strict mode, as it should). This matches the code I read and the maintainer's live probe against registry.npmjs.org cited below.
  • Version wiring is complete end to end. The workspace is in the root workspaces list and not in version.js's exclusion list, so npm version bumps it; the new step runs right after newVersion is read from the root package.json, using the script's own readJson/writeJson/run helpers; release.yml stages integrations/*/qwen-extension.json into the release commit; scripts/build.js builds the integration before the publish step; the files list ships exactly the six expected files.
  • Everything new is pinned by a test: publish-step structure and ordering, the npm-11/trusted-publishing requirements loop, the gate condition, the widened staging line, the PUBLISHED_PACKAGES array, and manifest name/version/repository alignment. The .size-baseline ratchet is enforced by scripts/tests/workflow-size.test.js and is green at this head (the maintainer also confirmed 57772 matches the file byte-for-byte).

No merge-blocking findings. The three items from the maintainer verification (bootstrap-ordering doc sentence, the MCP identity still advertising 0.1.0, the missing dist/main.js guard) are non-blocking and recorded in the thread as follow-ups.

Testing

Check Conclusion
Qwen Code CI (pull_request run) ✅ success
Security Checks (pull_request run) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Dependency CVE audit ✅ success
Secret scan (TruffleHog) ✅ success
Desktop Shell (ubuntu-22.04 / windows-2022) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
Test (macos-latest / windows-latest, Node 22.x) ⏭️ skipped (repo convention for this event)

CI is fully settled on the reviewed head — zero pending pull_request runs, zero failures: both workflows (Qwen Code CI, Security Checks) succeeded, 18 checks green and 103 skipped (bot orchestration plus the platform unit legs that don't run on this event; the ubuntu leg covers the full suite including the release-script tests).

What the evidence does and does not cover:

  • The author tested on macOS (their claim, not re-run here); CI covers Linux via the green ubuntu leg, and no Windows unit leg runs on this event. The change is release automation with no runtime platform specificity, so this is acceptable rather than a gap hidden behind the author's word.
  • Maintainer @wenshao ran the full reviewer test plan end to end on Linux (Debian 13, Node 22, npm 11) at exactly this head — loopback-registry install, MCP runtime E2E against a synthetic provider, a real scripts/version.js run, and a base-vs-PR A/B of the bootstrap-ordering scenario. His report concludes "recommend merge" with no blocker; it is maintainer evidence and credited as such — it was not produced by this triage run.
  • Sandboxed verification: the @qwen-code /verify job triggered by this re-run was still in flight at posting time; its report will land in this thread separately. It settles the same install-and-serve claims through the npm boundary that the maintainer's local run already exercises, so it is corroborating here rather than the only evidence.
  • This run executed no PR code (triage policy).
  • Not verified, by design: the real npm.org publish and trusted-publisher setup — the one-time bootstrap is explicitly left to the release administrator, and the gate keeps the step dark until then.

Real-scenario testing

N/A for this run — unattended CI path, and the PR has no TUI surface. Live install-path behavior is covered by the maintainer's local verification cited above.

中文说明

代码审查:已在新 head 49ea2a0b11 上重新审查完整 diff(而非仅看与上一轮的增量)。我基于标题和动机独立拟定的方案不变,PR 依然与之吻合。上一轮审查之后的改动是测试加固(守护 manifest 版本确实从 readJson(mem0ManifestPath) 这一来源读取、守护写回使用仓库规范的 prettier 命令、version.js 注释重新编号)以及一次机械性 rebase——rebase 带来的 release.yml 增长已由 .size-baseline 的上调吸收。我对每个关键集成点都亲自对照基线代码重新核实:

  • 发布步骤是 Publish @qwen-code/audio-capture 兄弟步骤的结构克隆,唯一增量是 vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED == 'true' 门控。门控失败即关闭——变量未设置时不等于 'true',因此仅合并不会发布任何东西,须由发布管理员显式打开。该步骤运行在既有 publish job 内,继承 production-release 环境与 id-token: write(provenance 所需);run 脚本中无密钥内插(输出经 env: 传入)。
  • 预发布窗口期安全scripts/get-release-version.js 新增的 PUBLISHED_PACKAGES 条目在包尚不存在于 npm 时不会破坏发布——doesVersionExist 在尽力与严格两种模式下都把缺失包的 E404 视为"不存在"(严格模式下非 E404 的探测失败仍会按预期抛错)。这与我读到的代码一致,也与下文引用的维护者对 registry.npmjs.org 的实测相符。
  • 版本联动端到端完整:该工作区在根 workspaces 列表中且不在 version.js 排除名单内(npm version 会 bump 它);新步骤紧跟从根 package.json 读取 newVersion 之后,使用脚本自带的 readJson/writeJson/runrelease.ymlintegrations/*/qwen-extension.json 暂存进发布提交;scripts/build.js 在发布步骤前构建该集成;files 清单恰好包含预期的六个文件。
  • 每一项新增都有测试固定:发布步骤结构与顺序、npm-11/trusted-publishing 要求循环、门控条件、扩宽的暂存行、PUBLISHED_PACKAGES 数组、manifest 包名/版本/repository 对齐。.size-baseline 棘轮由 scripts/tests/workflow-size.test.js 强制,在该 head 上为绿(维护者亦确认 57772 与文件字节级一致)。

无阻断合并的问题。维护者验证中的三项(bootstrap 顺序的文档句子、MCP 身份仍自报 0.1.0、缺失的 dist/main.js 守卫)均为非阻塞,已作为跟进项记录在线程中。

测试证据:被审 head 上 CI 已完全收敛——pull_request 运行零待处理、零失败:两个工作流(Qwen Code CISecurity Checks)均成功,18 项检查通过、103 项跳过(机器人编排及该事件不执行的平台单测段;ubuntu 段覆盖含发布脚本测试在内的完整套件)。

证据覆盖范围说明:

  • 作者在 macOS 上测试(作者自述,本次未复跑);CI 经由绿色 ubuntu 段覆盖 Linux;该事件不执行 Windows 单测段。改动属发布自动化、无运行时平台特异性,判定为可接受,而非以作者自述掩盖缺口。
  • 维护者 @wenshao 在完全相同的 head 上于 Linux(Debian 13、Node 22、npm 11)完整跑通测试计划——临时 registry 安装、对合成 provider 的 MCP 运行时 E2E、一次真实的 scripts/version.js 执行、以及 bootstrap 顺序场景的 base 与 PR A/B。其报告结论为"建议合并且无阻塞问题";这是维护者证据并按此归属——并非本次 triage 运行产生。
  • 沙箱验证:本次 re-run 触发的 @qwen-code /verify 在发布时尚在运行中,其报告将单独发布在线程中。它验证的是维护者本地运行已演练过的同一套安装与服务声明,因此此处是佐证而非唯一证据。
  • 本次运行未执行任何 PR 代码(triage 策略)。
  • 按设计不验证:真实 npm.org 发布与 trusted-publisher 配置——一次性 bootstrap 明确留给发布管理员,门控在那之前保持发布关闭。

真实场景测试:本次运行不适用——无人值守 CI 路径,且 PR 无 TUI 面。安装路径的实际行为由上文引用的维护者本地验证覆盖。

Qwen Code · qwen3.8-max

Reviewed at 49ea2a0b11549a9061f14223475f81c739e1d905 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean at every stage, and the policy hold from the last run is resolved by a CODEOWNER's sign-off; the point off a clean 5 is the three non-blocking follow-ups recorded below, one of which must land before the bootstrap is performed.

Stepping back: my independent proposal for this problem and the PR's approach are still the same plan, and the implementation remains the conservative version of it — publishing stays dark behind a repository variable until the one-time bootstrap, the already-published guard and the step's own npm view skip make retries and a bootstrapped first release safe, provenance is on, and every invariant the change introduces is pinned by a test. The two hardening rounds since the last triage added mutation-testing guards rather than scope. CI is fully green and settled at this head, and — the part that was missing last time — the release-infrastructure escalation has an answer: @wenshao, CODEOWNER for release.yml, verified the full test plan plus the release-side scenarios end to end on Linux at exactly this head, reported "recommend merge" with no blocker, and approved. The direction question, the release.yml changes, and the bootstrap plan are signed off; the remaining bootstrap work is a post-merge, one-time ops step by the release administrator, not a merge gate.

Three non-blocking follow-ups recorded in this thread (per the five-round rule, deferred rather than widening the PR): the design doc needs one sentence pinning the bootstrap ordering (bootstrap publishes an already-shipped release version, then the repository variable flips) — this must land before anyone performs the bootstrap; the MCP server identity still reports 0.1.0; and the publish step would benefit from a test -f dist/main.js guard. None blocks this merge.

Approving, pinned to the reviewed commit. ✅

中文说明

置信度:4/5 —— 各阶段均干净,上一轮的策略保留已由 CODEOWNER 的签核解除;未给满分的原因在于下面记录的三项非阻塞跟进项,其中一项必须在执行 bootstrap 之前落地。

退一步看:我对这个问题独立拟定的方案与 PR 的做法仍是同一个计划,实现依旧是其中保守的版本——发布在仓库变量打开、一次性 bootstrap 完成之前保持关闭;已发布版本保护与步骤自身的 npm view 跳过,使重试或已 bootstrap 的首次发布都安全;provenance 开启;改动引入的每一项不变量都有测试固定。上一轮之后的两次加固添加的是变异测试守卫而非范围扩张。CI 在该 head 上绿色且已完全收敛;与上次不同,发布基础设施升级这次有了答案:release.yml 的 CODEOWNER @wenshao 在完全相同的 head 上于 Linux 端到端验证了完整测试计划与发布侧场景,结论"建议合并且无阻塞问题",并已批准。方向问题、release.yml 改动、bootstrap 方案均已签核;剩余的 bootstrap 工作是合并后由发布管理员执行的一次性运维步骤,不是合并门槛。

线程中记录的三项非阻塞跟进(按五轮规则延期、不扩大本 PR):设计文档需要一句话明确 bootstrap 顺序(bootstrap 发布一个已发布完成的 release 的版本号,随后再打开仓库变量)——这一句必须在任何人执行 bootstrap 之前落地;MCP server 身份仍自报 0.1.0;发布步骤宜补一个 test -f dist/main.js 守卫。均不阻塞本次合并。

批准,并锚定在被审 commit 上。✅

Qwen Code · qwen3.8-max

Reviewed at 49ea2a0b11549a9061f14223475f81c739e1d905 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Triage re-run completed without a new review.

⚠️ The bot's only review on cc6a742bcb880c3630a76bf99f78d7a3d1cfea56 is a COMMENTED one, which carries no vote — so it has no verdict of its own on this commit, and main needs two approving reviews: an approval left by another account is a separate vote and does not count as the bot's own. Two different things look like this, and the stage-3 comment above says which: the triage skill deferring on purpose at 3/5 — a fork refactor hitting the approval guardrail, or a core change escalated for maintainer awareness, both normal outcomes — or an earlier approval that a push dismissed, leaving only the comment behind, which needs a fresh review.

⚠️ 机器人在 cc6a742bcb880c3630a76bf99f78d7a3d1cfea56 上唯一的评审是 COMMENTED不带票 —— 因此它在该 commit 上没有自己的裁决,而 main 需要两个批准(其他账号的批准是另一张票)。有两种情况长这样,上方的 stage-3 评论会说明是哪一种:triage skill 在 3/5 时有意 defer(fork refactor 命中审批护栏,或核心改动被升级交由维护者把关,两者都是正常结果);或者更早的批准被一次推送作废、只剩下这条评论,此时需要重新评审。

The stage comments above were updated with the latest result. View workflow run.

上方各阶段评论已更新为最新结果。查看工作流运行

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline.

Not explored to full depth (tool budget reached): "agent 2": (none — no check was cut short).

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

Test Plan (not a blocker): 102 tests pass — this review observed 49 passed.

Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:

  • .github/workflows/release.yml:827 — [review] R1-1 new mem0 publish step is a byte-for-byte fifth copy of the shared dry-run/guard/publish block (author declined: keeps the repo's inlined, test-enforced release convention)
  • scripts/tests/package-scripts.test.js:49 — [review] R1-2 new guard test lives outside every npm workspace; only CI's full-profile test:scripts leg gates it (author confirmed the full CI leg passed)
  • scripts/tests/get-release-version.test.js:741 — [review] R1-3 new guard test lives outside every npm workspace; only CI's full-profile test:scripts leg gates it (author confirmed the full CI leg passed)
  • scripts/tests/release-workflow.test.js:986 — [review] R1-4 new guard test lives outside every npm workspace; only CI's full-profile test:scripts leg gates it (author confirmed the full CI leg passed)
中文说明

已审查。 建议见行内评论。

未探索到全部深度(达到工具调用预算):"agent 2"(none — no check was cut short)

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

Test Plan(非阻断):102 tests pass — this review observed 49 passed

收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 4 条(原文未翻译,列表见上方英文部分)。

— qwen3.8-max via Qwen Code /review (v0.22.3)

Comment thread scripts/tests/package-scripts.test.js
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Addressed review feedback in bf51b8d759.

Item Decision Action
R2-1 manifest write-back guard Fixed Added an assertion that pins writeJson(mem0ManifestPath, mem0Manifest);, so removing the write-back now fails the guard test.

Verification:

  • Mutation witness: removing the write-back fails the new assertion
  • Focused script test: 19/19 passed
  • npm run build
  • npm run typecheck
  • npm run lint
  • Prettier check

Rejected / ignored / deferred: none in this round.

Decisions needed: none.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI still running. Reviewed.

中文说明

⚠️ 已从批准降级为评论:CI still running。 已审查。

— qwen3.8-max via Qwen Code /review (v0.22.3)

@doudouOUC
doudouOUC force-pushed the feat/external-context-mem0-distribution branch from bf51b8d to 7782593 Compare September 1, 2026 06:48
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Rebased onto current main to clear the merge conflict.

Item Result
Merge conflict Resolved mechanically by retaining both the upstream qwen-live build-order assertion and this PR’s Mem0 manifest release assertion.
CI failure No code change taken: the retry failed across unrelated CLI, Core, VS Code companion, and Web Shell suites under severe runner timing degradation, while all Mem0 tests passed. The rebase creates a fresh SHA and new CI run.
Verification 104 targeted script tests passed (1 skipped); repository build, typecheck, and lint passed after refreshing dependencies; npm pack dry-run contains only README, bundle, package metadata, Extension manifest, and schemas.
Review threads 0 unresolved.

Updated head: 7782593

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline.

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

Test Plan (not a blocker): 102 tests pass — this review observed 49 passed.

中文说明

已审查。 建议见行内评论。

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

Test Plan(非阻断):102 tests pass — this review observed 49 passed

— qwen3.8-max via Qwen Code /review (v0.22.3)

Comment thread scripts/tests/package-scripts.test.js
Comment thread scripts/version.js
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Addressed the latest review round in 49ea2a0b11.

Item Action
R4-1 manifest read source Added a guard for the exact readJson(mem0ManifestPath) source; the re-pointed-read mutation now fails.
R4-2 manifest formatting Normalized the write with the repository canonical --experimental-cli formatter and guarded the command. Ordinary Prettier 3.6.1 was verified to be a no-op for this JSON layout.
Ubuntu CI failure Diagnosed as self-hosted runner communication loss; no branch-code change or old-SHA rerun. The new commit retriggers CI.

Verification: focused package-script tests 20/20; mutation witnesses failed as expected; npm run build && npm run typecheck && npm run lint passed; two clean diff audits passed.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI still running. Reviewed.

中文说明

⚠️ 已从批准降级为评论:CI still running。 已审查。

— qwen3.8-max via Qwen Code /review (v0.22.3)

@wenshao

wenshao commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Local verification report (maintainer)

I built a real environment for this PR and ran the reviewer test plan end to end, plus the release-side scenarios the plan leaves to the release administrator.

Environment: Linux (Debian 13, kernel 6.12), Node v22.22.2, npm 11, worktree at 49ea2a0b11, isolated HOME, a loopback npm registry serving the PR's own npm pack tarball, and a synthetic loopback Mem0-compatible provider. No live provider or npm.org publish was involved.

Verdict: recommend merge. Everything the PR claims reproduces on Linux. I found no blocker. Three non-blocking items are below — the first is an ops-order note that is worth one sentence in the design doc before someone performs the bootstrap.


What reproduces

Claim Result
49 package tests pass ✅ 49/49 (6 files)
Tarball contains exactly six files, no provider data README.md, dist/main.js, package.json, qwen-extension.json, schemas/dialect.schema.json, schemas/instance-config.schema.json — 1.15 MB unpacked. Grepped the bundled runtime for provider hostnames (mem0.ai, hologres, polardb, aliyun, dashscope): zero hits
npm install through a temporary registry reports npm as the source and the release version Source: @qwen-code/external-context-mem0 (Type: npm), Release tag: 0.22.3, and the listed version is now 0.22.3 instead of 0.1.0
initialize / tools/list / context_search produce the bounded request and normalized response ✅ see below
A running process keeps its startup configuration; a restart loads the updated dialect ✅ provider-side URL sequence proves it
Invalid dialect errors expose no path or configuration value Mem0 extension dialect configuration is invalid. / ... instance configuration is unavailable. — nothing else
Focused release/version script tests ✅ 104 passed, 1 skipped (the PR body says 102 — the third commit added two more)
Repository build, typecheck, lint npm run build, npm run bundle, npm run typecheck, npm run lint all exit 0
.size-baseline entry 57772 matches wc -c .github/workflows/release.yml byte-for-byte
PR CI ✅ 16 pass, 0 fail

1. npm install and package contents

The PR's most visible user-facing effect is manifest/version alignment. Installing a tarball whose manifest still carries main's 0.1.0 shows the mismatch this PR removes:

npm install and package contents

(For accuracy: the mismatch was display-only. checkNpmUpdate compares the install metadata's release tag against the registry dist-tags, not the manifest version, so update detection was never affected.)

2. Runtime against the npm-installed bundle

Started the installed dist/main.js over stdio with administrator-owned instance/dialect files and a loopback provider. The request the provider received is exactly the bounded shape, the credential comes only from credentialEnv, and an extra secret_field: "MUST-NOT-LEAK" the provider returned is dropped by normalization:

MCP runtime E2E

Startup-immutability check, from the provider's own log — the dialect's search.path was rewritten on disk between calls 2 and 3:

POST /memories/search     {"query":"before edit", ...}              # startup dialect
POST /memories/search     {"query":"after edit same process", ...}  # same process, file already changed
POST /v2/rotated-search   {"query":"after restart", ...}            # after restart

3. Tests, lint, and scripts/version.js

Ran scripts/version.js for real in the worktree with a prerelease version, then simulated the release.yml staging line:

tests and version alignment

Both files land on 0.99.0-nightly.20260901, the widened git add picks the manifest up, and nothing is left uncommitted. Prettier's --experimental-cli path also works here — qwen-extension.json is not covered by .prettierignore.

I also confirmed the new PUBLISHED_PACKAGES entry is safe against the real registry today: @qwen-code/external-context-mem0 does not exist on npm.org, and npm view returns an E404-tagged message for a missing package just as it does for a missing version — so doesVersionExist's strict branch treats it as "absent" rather than "cannot verify". Verified against registry.npmjs.org, not a mock.


Findings

1. The one-time bootstrap publish must happen after the release it names — the doc doesn't say so (Important, docs/ops)

scripts/get-release-version.js now treats a version as taken if it exists on @qwen-code/external-context-mem0, while the CI publish stays gated off. That makes the bootstrap ordering load-bearing, and the doc's wording ("The bootstrap should publish the first actual Qwen release that contains this change") reads either way.

If the bootstrap publishes version X before release X runs, two things happen, both reproduced against a local registry with a base-vs-PR A/B:

bootstrap order guard

  • Version selection silently skips X. With @qwen-code/qwen-code at 0.22.3/0.23.0-preview.1, origin/main picks 0.23.0; with this PR and external-context-mem0@0.23.0 present, prepare logs Version 0.23.0 exists, incrementing. and picks 0.23.1. The bootstrapped mem0@0.23.0 is then an orphan that matches no Qwen Code release — and, because the publish gate is still off, nothing fills the gap.
  • If the bootstrap lands between prepare and the push step, assertVersionUnreleased exits 3 and the release-branch force push is refused. That refusal path is deliberately excluded from notify_failure (needs.publish.outputs.version_refusal != 'true'), so no "Release Failed" issue is filed and no autofix is dispatched — the run just goes red. The refusal text ("If a previous attempt published only part of the release, complete the remaining artifacts manually") also points the operator at the wrong remedy in this case.

Neither is a code defect, and both are recoverable. The cheap fix is one sentence in docs/design/external-context-mem0-extension.md stating the order explicitly: the bootstrap publishes the version of a release that has already shipped, and NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED is set afterwards, so CI takes over from the next release.

Repro:

# a loopback registry serving qwen-code 0.22.3 + 0.23.0-preview.1, and mem0 0.23.0
npm_config_registry=http://127.0.0.1:PORT node scripts/get-release-version.js --type=stable
#   Version 0.23.0 already exists on NPM (@qwen-code/external-context-mem0).
#   Version 0.23.0 exists, incrementing.  ->  "releaseVersion": "0.23.1"

2. The published runtime still advertises 0.1.0 over MCP (Minor)

integrations/external-context-mem0/src/mcp.ts:22 hardcodes version: '0.1.0'. The PR aligns package.json and qwen-extension.json with the release version but not the MCP server identity, so initialize will report 0.1.0 for every published release (visible in screenshot 2). It is cosmetic, but it is the version an operator sees when debugging an MCP session, and it is now the only one of the three that stands still. Deriving it — reading qwen-extension.json at startup, or an esbuild --define — plus one assertion in manifest.test.ts would close it.

3. Nothing guards that dist/main.js is actually in the tarball, and no dry run exercises the step (Minor)

npm publish does not fail when a path listed in files is missing. With dist/ absent I get a 5-entry tarball and exit 0:

entryCount: 5   # README.md, package.json, qwen-extension.json, schemas/*.json  — no dist/main.js

In the workflow this is currently safe: npm run build precedes the publish and scripts/build.js includes integrations/external-context-mem0 (the --cli-only path that skips it is not used by release.yml). But the CLI publish protects its equivalent invariant with test -f dist/review-sources.sha256, and this step has no analogue. Compounding it, the step's if: also requires vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED == 'true', so the IS_DRY_RUN branch is unreachable until the variable is set — the first time this step ever runs will be a real publish. A one-line test -f dist/main.js before npm publish (and, optionally, letting dry runs through the gate) would make the first real run less of a first run.

4. Nits

  • PR body says "102 tests pass with one existing host-specific skip"; at 49ea2a0b11 it is 104 passed, 1 skipped.
  • "Tested on" can add 🐧 Linux — this report covers it.
  • package.json has no license field, so npm publish will warn on a now-public package. Same as channel-base, audio-capture, and packages/cli, so it is a repo-wide convention rather than something this PR introduced; mentioning it only in case you want to fix it while the package is still unpublished.

中文版报告

本地验证报告(维护者)

我为本 PR 搭建了真实验证环境,完整跑通了 Reviewer 测试计划,并补充验证了计划中留给发布管理员的发布侧场景。

环境: Linux(Debian 13,内核 6.12)、Node v22.22.2、npm 11,worktree 位于 49ea2a0b11,使用隔离的 HOME、一个提供本 PR 自身 npm pack 产物的本机 npm registry,以及一个合成的本机 Mem0 兼容 provider。全程没有访问真实 provider,也没有向 npm.org 发布。

结论:建议合并。 PR 声明的内容在 Linux 上全部复现,没有发现阻塞问题。下面有三条非阻塞项,其中第一条是发布顺序相关的运维说明,建议在有人执行 bootstrap 之前先在设计文档里补一句话。

复现结果

声明 结果
49 个 package 测试通过 ✅ 49/49(6 个测试文件)
tarball 恰好 6 个文件、不含厂商数据 README.mddist/main.jspackage.jsonqwen-extension.json、两个 schema,解包 1.15 MB;对 bundle 后的运行时检索 mem0.aihologrespolardbaliyundashscope 均为 0 命中
通过临时 registry 安装后显示来源为 npm、版本符合预期 Source: @qwen-code/external-context-mem0 (Type: npm)Release tag: 0.22.3,列表版本从 0.1.0 变为 0.22.3
initialize / tools/list / context_search 产生受限请求与归一化响应 ✅ 见下
运行中的进程保持启动时配置,重启后加载新 dialect ✅ provider 侧 URL 序列可证
非法 dialect 错误不暴露路径或配置值 ✅ 仅输出 Mem0 extension dialect configuration is invalid. / ... instance configuration is unavailable.
发布与版本脚本的聚焦测试 ✅ 104 通过、1 跳过(PR 描述写的是 102,第三个 commit 又加了两个用例)
仓库 build / typecheck / lint npm run buildnpm run bundlenpm run typechecknpm run lint 全部退出码 0
.size-baseline 57772wc -c .github/workflows/release.yml 完全一致
PR CI ✅ 16 通过、0 失败

截图 1(安装与包内容): 用一个 manifest 仍为 main0.1.0 的 tarball 做对照安装,可以直接看到本 PR 消除的版本不一致。需要澄清的是:这个不一致只影响显示。checkNpmUpdate 比较的是安装元数据里的 release tag 与 registry dist-tags,不是 manifest 版本,所以升级检测从未受影响。

截图 2(运行时 E2E): 用管理员自有的 instance/dialect 文件和本机 provider 启动已安装的 dist/main.js。provider 实际收到的请求就是受限形状,凭据只来自 credentialEnv;provider 额外返回的 secret_field: "MUST-NOT-LEAK" 被归一化过程丢弃。

启动时读取一次的语义,用 provider 自己的日志验证(第 2、3 次调用之间在磁盘上改写了 dialect 的 search.path):

POST /memories/search     {"query":"before edit", ...}              # 启动时的 dialect
POST /memories/search     {"query":"after edit same process", ...}  # 同一进程,文件已改
POST /v2/rotated-search   {"query":"after restart", ...}            # 重启之后

截图 4(测试与版本对齐): 在 worktree 中用预发布版本号真实执行了一次 scripts/version.js,再模拟 release.yml 的 staging 命令行。两个文件都落到 0.99.0-nightly.20260901,扩宽后的 git add 能覆盖到 manifest,且没有遗留未提交的改动。prettier 的 --experimental-cli 路径也正常——qwen-extension.json 不在 .prettierignore 覆盖范围内。

另外确认了新增的 PUBLISHED_PACKAGES 条目在今天的真实 registry 上是安全的:@qwen-code/external-context-mem0 目前在 npm.org 上不存在,而 npm view整个包不存在返回的错误信息同样带 E404 标记(与版本不存在一致),所以 doesVersionExist 的 strict 分支会判定为「不存在」而不是「无法确认」。这一条是对 registry.npmjs.org 实测的,不是 mock。

发现

1. 一次性 bootstrap 发布必须在它所对应的那个 release 之后进行,但文档没有写明(重要,文档/运维)

scripts/get-release-version.js 现在会把「版本存在于 @qwen-code/external-context-mem0」也算作版本被占用,而 CI 发布步骤仍然是关闭的。这让 bootstrap 的先后顺序变成了关键约束,而文档中的措辞("The bootstrap should publish the first actual Qwen release that contains this change")两种理解都说得通。

如果 bootstrap 在 release X 运行之前就发布了版本 X,会出现两种情况,我都用本机 registry 做了 base 与 PR 的 A/B 复现(截图 3):

  • 版本选择会静默跳过 X。@qwen-code/qwen-code0.22.3 / 0.23.0-preview.1 时,origin/main 选出 0.23.0;换成本 PR 且 registry 上已有 external-context-mem0@0.23.0 时,prepare 打印 Version 0.23.0 exists, incrementing. 并选出 0.23.1。此时 bootstrap 出去的 mem0@0.23.0 成为孤儿版本,不对应任何 Qwen Code release;而且因为发布开关仍然关着,这个缺口也没人补。
  • 如果 bootstrap 恰好落在 prepare 与 push 步骤之间assertVersionUnreleased 会以 3 退出,release 分支的 force push 被拒绝。这个拒绝路径被有意排除在 notify_failure 之外(needs.publish.outputs.version_refusal != 'true'),因此不会创建 "Release Failed" issue,也不会派发 autofix,只是这次运行变红。而且拒绝信息里给出的建议("If a previous attempt published only part of the release, complete the remaining artifacts manually")在这种场景下会把操作者引向错误的处理方式。

这两点都不是代码缺陷,也都可恢复。最低成本的修法是在 docs/design/external-context-mem0-extension.md 里明确写一句顺序:bootstrap 发布的是一个已经发布完成的 release 的版本号,随后再设置 NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED,从下一次 release 起由 CI 接管。

2. 已发布的运行时在 MCP 上仍然自报 0.1.0(次要)

integrations/external-context-mem0/src/mcp.ts:22 硬编码了 version: '0.1.0'。本 PR 让 package.jsonqwen-extension.json 跟随发布版本,但没有覆盖 MCP server 身份,因此每个已发布版本的 initialize 都会报 0.1.0(截图 2 中可见)。这只是显示问题,但它恰恰是排查 MCP 会话时操作者看到的那个版本号,而且现在三处版本里只剩它不动。改成运行时读取 qwen-extension.json,或用 esbuild --define 注入,并在 manifest.test.ts 里加一条断言即可。

3. 没有任何机制保证 dist/main.js 真的在 tarball 里,而且 dry run 也不会执行到这一步(次要)

files 中列出的路径缺失时,npm publish 不会失败。在 dist/ 缺失的情况下我得到的是 5 个条目的 tarball,退出码 0:

entryCount: 5   # README.md、package.json、qwen-extension.json、schemas/*.json —— 没有 dist/main.js

在当前工作流里这是安全的:npm run build 在 publish 之前执行,且 scripts/build.js 的构建顺序包含 integrations/external-context-mem0(会跳过它的 --cli-only 路径没有被 release.yml 使用)。但 CLI 的发布步骤用 test -f dist/review-sources.sha256 保护了同类不变量,这个步骤没有对应的检查。叠加的问题是:该步骤的 if: 还要求 vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED == 'true',所以在变量被设置之前 IS_DRY_RUN 分支根本不可达——这个步骤第一次运行就是一次真实发布。在 npm publish 前加一行 test -f dist/main.js(以及可选地让 dry run 不受该变量限制)可以显著降低首次真实运行的风险。

4. 细节

  • PR 描述写的是「102 tests pass with one existing host-specific skip」;在 49ea2a0b11 上实际是 104 通过、1 跳过
  • 「Tested on」可以补上 🐧 Linux —— 本报告已覆盖。
  • package.json 没有 license 字段,npm publish 对一个即将公开的包会给出告警。channel-baseaudio-capturepackages/cli 同样如此,属于仓库既有惯例而非本 PR 引入;仅在你想趁包尚未发布时一并处理的情况下提一下。

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@wenshao Thank you for the thorough Linux verification and release-side A/B work. I independently checked the three findings against 49ea2a0b11; their technical basis is valid. Given this PR is already beyond roughly five review rounds and the report explicitly recommends merge with no blocker, I am recording these as follow-ups rather than widening this PR:

Finding Disposition
Bootstrap ordering Defer to a pre-bootstrap documentation/runbook follow-up. The bootstrap must publish an already-shipped release version, then enable NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED so CI owns the next release.
MCP server identity remains 0.1.0 Defer to a focused follow-up. This is diagnostic display only and deriving the runtime version would widen the current package/manifest-alignment scope.
Missing dist/main.js guard and unreachable gated dry run Defer as defense in depth. The current workflow runs the full build and includes this integration before publish; a follow-up should add test -f dist/main.js and separately assess the dry-run gate.
Test-count/Linux/license nits The maintainer report records the updated Linux evidence and counts. The license warning follows existing repository convention and is outside this PR scope.

No current branch change or user decision is needed.

wenshao
wenshao previously approved these changes Sep 1, 2026
@wenshao

wenshao commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@wenshao
wenshao enabled auto-merge September 1, 2026 12:58
@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

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: 112 passed · 0 failed · 112 total

Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:112 通过 · 0 失败 · 112 总计

抖动门:✅ 4 changed test file(s) x 5 identical rounds, no divergence

Verification report

PR #10653 Deep Verification (follow-up round) — feat(external-context): Publish the Mem0 Extension package

Verdict: merge-ready — 112 scripted assertions, 112 pass / 0 fail. Verified head: 49ea2a0b11549a9061f14223475f81c739e1d905 (git rev-parse HEAD^2, matches the snapshot headRefOid; local base tip HEAD^1 = 56f92c84). This is a follow-up round: the previous round verified head cc6a742b (then single-commit) and reported merge-ready; the PR has since gained two commits (test(release): Guard Mem0 manifest write-back, fix(release): Normalize Mem0 manifest updates). cc6a742b is not reachable in this depth-2 checkout, so the delta was scoped from the current aggregate diff and the previous report, and every carried-forward measurement was re-run at the new head rather than quoted.

中文摘要
  • 结论merge-ready。112 条脚本化断言全部通过;未发现阻塞性问题。
  • 与上一轮相比:新增两个提交(写回守卫测试 + manifest 更新归一化修复)。上一轮的全部测量在新 head 上重跑,全部保持绿色;新探针聚焦增量(version.js 的 prettier 归一化写回)。
  • A/B 结论(base 56f92c84 vs head 49ea2a0b,见各表与证据图):
    • 可发布性翻转:head npm publish --dry-run 正常发布 6 文件;base 因 private: true 被 npm exit-0 静默跳过(04-pack-ab-publishability-flip.png)。
    • 推送守卫翻转:同一注册表状态下 head 以 exit 3 拒绝并点名 mem0(12 包全探测),base 探测 11 包而放行(05-guard-ab-mem0-in-published-set.png)。
    • version.js 翻转:head 将 qwen-extension.json 对齐到发布版本且产物通过仓库 prettier 门禁、只产生单行版本 diff;base 停留在 0.1.0(01-version-replay-ab-head-aligns-base-lags.png)。
    • 暂存翻转:release.yml 提取的 git add 行在 head 暂存 mem0 manifest,base 行在同一棵树上不暂存(02-staging-replay-manifest-flip.png)。
    • 增量核心:writeJson 单独执行会留下 11 行格式化噪声并无法通过 prettier --check;PR 新增的 npx prettier --experimental-cli --write 步骤将其恢复为"仅版本行"的字节级最小 diff,幂等、同版本重写为无操作(07-normalize-delta-prettier-write-back.png)。
    • E2E:打包产物经回环 stub registry 由真实 CLI 安装,extensions list 报告 npm 来源与 0.22.3;安装后的 bundle 以真实 MCP stdio 启动,context_search 发出受方言约束的有界 GET 请求(凭据走 x-api-key)并返回归一化结果;运行中进程保持启动配置、重启加载新方言;非法方言报错不泄露路径或凭据(08-e2e-install-and-runtime-wire.png)。
  • Findings:1 条新发现(nit,记账类)——.size-baseline 记 57772 而 release.yml 实际 58285 字节,差 513 字节,仅因 4096 字节容差而通过尺寸门禁;上一轮"精确匹配"的测量在新 head 上不再成立(详见 F3)。F1(serverInfo.version 硬编码 0.1.0,既存建议级)与 F2(npm 私有包静默跳过,信息级)在新 head 复测后仍然存在
  • 未覆盖:真实 npm.org 发布与 trusted publishing 配置;与当前 main 的试合并(沙箱无网络,快照 baseRefOid 本地不可达);逐提交归因(深度 2 仅可达 1/3 个提交);yamllint/shellcheck/actionlint(本容器未安装);仓库级全量测试/全量 lint;Windows。

Previous-finding status (follow-up round)

# Finding (previous round) Severity Status at new head 49ea2a0b
F1 MCP serverInfo.version hardcoded 0.1.0 in src/mcp.ts while package/manifest now track the release version Suggestion (pre-existing) Stands. Re-measured live: the installed bundle's initialize response still returns "version":"0.1.0" (harness 06 cell); src/mcp.ts is byte-identical to the previous round (the only src/ change in the PR is manifest.test.ts).
F2 npm 10 publishes a private workspace package as a silent exit-0 skip (no EPRIVATE) Informational Stands. Re-measured on this container's npm 10.9.8: base-arm npm publish --dry-run exits 0 with Skipping workspace …, marked as private. Note: the release job installs npm 11.19.0; the skip semantics are only measured on 10.9.8.
Previous round also stated "size baseline 57772 release.yml matches the file's byte size exactly" Superseded by re-measurement — see F3. That statement was accurate at the old head cc6a742b; at the new head the file is 58285 bytes.

Scope

  • Central claim (unchanged): the extension becomes a publishable public npm package whose tarball contains exactly the bundled runtime, canonical schemas, manifest, and documentation — and stays gated until the one-time bootstrap.
  • Secondary claims: (1) release wiring — manifest/version alignment in version.js, staging of qwen-extension.json, inclusion in the shared already-published guard, publish step behind vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED ordered before the other npm packages; (2) the published artifact works through the real extension installer and runtime.
  • New this round (delta focus): the manifest write-back now runs npx prettier --experimental-cli --write after rewriting qwen-extension.json ("Normalize Mem0 manifest updates"), pinned by a new string-containment assertion in package-scripts.test.js ("Guard Mem0 manifest write-back"). The delta probes below measure what that step buys.

Central claim + A/B

Head cells at HEAD, control cells at HEAD^1, in scratch worktrees wired to the already-installed root node_modules. Internal-link check per the control-integrity rule: the units under test are the arm's own files; the only cross-tree resolution is third-party (semver for the guard script — asserted by import.meta.resolve from inside the base worktree to resolve into the main tree's node_modules, an unmodified dependency; the ajv@6-vs-ajv@8 worktree trap that bit the previous round was avoided by packing from the main tree and copying the proven-identical dist/main.js into the base arm — the PR changes no bundled source: only src/manifest.test.ts under src/, which the esbuild bundle does not include).

Pack/publish A/B — witness 04-pack-ab-publishability-flip.png

# Cell Arm Oracle Result
1 npm pack --dry-run --json head exactly 6 entries (README.md, dist/main.js, package.json, qwen-extension.json, schemas/*.schema.json ×2) ✅ 6/6, tar -tzf on the real tgz corroborates (sizes listed in log 03-pack-console.log)
2 npm publish --dry-run --access public head proceeds: Publishing to … + + @qwen-code/external-context-mem0@0.22.3
3 same base skipped: Skipping workspace @qwen-code/external-context-mem0, marked as private, no Publishing to, exit 0 ✅ flip
4 entry sets both identical path sets (content diffs limited to private/repository, README install section, manifest version — expected, not publishability)

Push-time guard A/B — witness 05-guard-ab-mem0-in-published-set.png

Real node scripts/get-release-version.js --assert-unreleased=9.9.9 per arm under PATH-stubbed npm/git/gh (stubs record every call).

Cell Arm Registry state Exit Behavior
1 head mem0@​9.9.9 shipped 3 ::error:: refusal naming @qwen-code/external-context-mem0 + recovery guidance; call log shows npm view @qwen-code/external-context-mem0@9.9.9 version; 12/12 packages probed (strict full scan)
2 base identical 0 passes; mem0 never probed; 11/11 packages — the flip
3 head nothing shipped 0 12 probes, clean pass
4 head mem0 + channel-feishu shipped 3 refusal names both (partial-publish accounting)
5 head first probe fails non-E404 2 "cannot verify" ≠ "unreleased" — failed probe fails closed

Version-alignment A/B (scripts/version.js) — witness 01-version-replay-ab-head-aligns-base-lags.png

Full replay of node scripts/version.js 9.9.9-verify.0 per arm with a PATH-stubbed npm emulator (applies npm version bumps, skips npm install) and a stub npx delegating prettier to the repo's pinned prettier 3.6.1 binary — matching the release job, where npm ci runs before npm run release:version (verified in release.yml step order). Emulator semantics anchored to production evidence: excluded workspaces keep independent versions today (qwen-live is 0.1.0 at root 0.22.3), so root npm version bumps the root only.

Arm root pkg mem0 pkg mem0 qwen-extension.json written manifest formatting
head 9.9.9-verify.0 9.9.9-verify.0 9.9.9-verify.0 (aligned) prettier --check ✅; bytes == committed + version line only (git diff = exactly one ± line pair)
base 9.9.9-verify.0 9.9.9-verify.0 0.1.0 (left behind) — the flip n/a (untouched)

Also asserted on head: qwen-live exclusion respected (still 0.1.0), sibling external-context/qwen-extension.json untouched by version.js.

Staging replay — witness 02-staging-replay-manifest-flip.png

The git add lines extracted verbatim from head/base release.yml, executed in the head worktree left version-bumped by the replay above:

Line Stages mem0 manifest Notes
head (… integrations/*/package.json integrations/*/qwen-extension.json) also stages mem0 + root package.json; the glob matches the sibling manifest too but it is unchanged → correctly NOT staged
base (no manifest glob) on the same tree ❌ unstaged — the flip bumped package.json files still staged

Publish-step replay (gate + flags) — witness 06-publish-step-gate-truth-table.png

The step's run: block extracted from release.yml by a YAML parser and executed under bash --noprofile --norc with a recording npm stub; the if: expression pinned verbatim and evaluated as a truth table:

  • Runs only for github.repository == 'QwenLM/qwen-code' and vars.NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED == 'true' — measured skips for false, '', unset, TRUE (case-sensitive), and fork repos with the var set.
  • Dry run → exactly one npm call: publish --provenance --access public --tag=preview --dry-run, no registry probe.
  • Real run, version absent → probe npm view @qwen-code/external-context-mem0@9.9.9 version first, then publish --provenance --access public --tag=latest.
  • Real run, version present → ::notice::… already published; skipping, exit 0, no publish call.
  • --tag=preview propagates from needs.prepare.outputs.npm_tag.
  • Ordering: mem0 < audio-capture < qwen-code; base release.yml has no mem0 step; extracted body passes bash -n.

Delta probes: the prettier write-back normalization

Why the new npx prettier --experimental-cli --write step matters — measured, witness 07-normalize-delta-prettier-write-back.png:

Cell Result
Committed manifest passes prettier --check (repo ignore rules in effect) ✅ baseline
writeJson output alone (the pre-delta behavior) fails prettier --check ✅ — normalization has work to do
writeJson-only output vs minimal-diff target 11 lines of formatting noise (args/includeTools arrays re-expanded)
After the PR's exact prettier invocation bytes identical to committed-with-version-swapped — a version-only diff, zero collateral
Idempotence (second prettier pass) byte-identical
Same-version write-back (release retry) byte-identical to committed — a true no-op
npx --no-install prettier resolves locally (release job needs no registry fetch) ✅ 3.6.1, the lockfile pin

Without the step, the release commit would carry formatting noise in qwen-extension.json and the next prettier-gated CI run on that commit's tree would flag it; mutation M4b below proves both the test pin and the behavioral consequence.

One completeness note (not a merge condition): the new package-scripts.test.js assertion pins the write-back by string containment (grep on version.js source), not by executing it. The behavioral oracle is the replay above; a suite that ran version.js against a scratch manifest and asserted prettier --check would be the fixture that pins this axis. As it stands, the grep test and the replay agree, and M4b is killed by both.

E2E: registry install + runtime boot — witness 08-e2e-install-and-runtime-wire.png

Re-run end to end at the new head (the input closure — package.json, manifest, README — changed since the previous round, so no carry-forward):

  • The head tarball was served by a loopback stub npm registry; the real CLI (packages/cli/dist/index.js extensions install @qwen-code/external-context-mem0 --consent, isolated HOME + QWEN_HOME, $HOME/.npmrc pointing at the stub) fetched packument and tarball (request log asserted: exactly GET /@qwen-code%2fexternal-context-mem0, GET /tarball.tgz), installed, and extensions list printed Mem0 External Context at 0.22.3 with the npm source. Installed manifest version is 0.22.3 — this PR's manifest bump makes that true; version.js + normalization keep it true on every future release.
  • The installed bundle booted as a real MCP stdio server with admin-owned instance/dialect files and a loopback GET provider: initialize and tools/list (exactly context_search) succeed; context_search issued exactly GET /memories/search?query=team+conventions&app_id=verify-app&top_k=5 with the credential on the dialect-bound x-api-key header and returned the normalized item; stderr stayed clean.
  • Retention/restart: after rewriting the dialect file, the running process still hit the old path; a restart loaded /v2/memories/search.
  • Invalid dialect: exit 1, bounded message (<400 chars) leaking neither the credential value, nor the dialect path, nor the provider address.

Two environment traps hit and resolved while building this cell (harness-side, not PR defects): the consent prompt reads stdin (open pipe hangs — fixed with --consent + closed stdin), and the stub registry lives in the harness process, so spawnSync deadlocks the install (fixed with async spawn).

Mutation matrix — witness 03-mutation-matrix-all-hunks-load-bearing.png

Each PR hunk reverted in the head worktree; the PR's own tests required to go red on the intended assertion (failure messages quoted in logs/08-suite-*.log). M0 control green first: extension manifest suite 2/2, scripts suites 105/105.

Mutant Suite Result
M0 unmutated control both green (2 + 105)
M1 private: true restored, repository removed (base file) manifest.test.ts red on is self-contained and exposes only context_search
M2 mem0 dropped from PUBLISHED_PACKAGES (base file) get-release-version.test.js red on pins the full published-package set
M3 mem0 publish step + staging glob removed (base release.yml) release-workflow + package-scripts red on all four step/staging tests
M4 version.js mem0 alignment block removed (base file) package-scripts.test.js red on keeps the Mem0 Extension manifest aligned with release versions
M5 only the integrations/*/qwen-extension.json glob removed from git add release-workflow.test.js red on stages every integration package manifest after versioning
M4b (delta-scoped) only the prettier line removed from version.js package-scripts.test.js + behavioral replay red on the alignment test; and replaying the mutant writes an aligned-but-unformatted manifest that fails prettier --check — formatting noise would ship into the release commit
M6 positive control (pre-existing audio-capture entry dropped) get-release-version.test.js red — the harness can fail

No survivors; all hunks including the delta's prettier line are load-bearing. Postcondition verified: worktree pristine after the matrix.

Reviewer Test Plan walkthrough (per step)

  1. "Build and test the Extension package… all 49 tests" — ✅ re-run: 6 files, 49/49 pass.
  2. "Dry-run tarball contains exactly six files… no provider dialect/admin config" — ✅ harness 03 + tar -tzf on the real tgz.
  3. "Install through a temporary registry… list reports npm source and expected version" — ✅ harness 06 (0.22.3, Type: npm).
  4. "Boot with instance/dialect files + loopback provider; bounded request, normalized response; running process retains config; restart reloads; invalid dialect leaks nothing" — ✅ all cells in harness 06.
  5. "Focused release/version script tests: 102 pass with one existing host-specific skip" — ✅ with a platform footnote: this container (Linux, GNU coreutils 9.1) runs 105 pass / 0 skip across the three touched script test files — the 102+1 figure is the macOS shape, where the realpath -m-gated test in release-workflow.test.js skips (BSD realpath has no -m); 105 = 102 + the 2 tests this PR adds + the test that only skips on macOS. Build/typecheck/lint: ✅ below.

Targeted gates (not counted in assertions.json)

  • Extension package suite: 49/49. Scripts suites (the three PR-touched files): 105/105. workflow-size.test.js (ratchet consumer of the changed .size-baseline): 198/198 — passes despite F3, because 513 B < the 4096 B allowance (measured, not assumed).
  • tsc --noEmit in the extension package: clean. ESLint over all touched lintable files: clean, live-controlled (planted unused variable reported as no-unused-vars, then restored). Prettier --check over all 12 changed files: clean. bash -n on the extracted publish step: clean.
  • Not run here: yamllint / shellcheck / actionlint (binaries absent from this container — see Not covered).

Findings

F3 — Nit (new this round, bookkeeping): .size-baseline under-records release.yml by 513 bytes. Measured: the PR bumps the entry 53022 → 57772, but the file it records is 58285 bytes at the verified head (wc -c), Δ = 513. Context measured the same way: at the PR's base the entry was already stale (recorded 53022 vs actual 57079 — 4057 B of pre-existing main-side drift), so this PR improves tracking but lands 513 B short of the final file; something grew release.yml between the baseline computation and the final revision (which commit, the depth-2 checkout cannot say). Impact: none behavioral — the ratchet's 4096 B allowance absorbs it (198/198 suite green, measured), but 513 B of this PR's own growth is unaccounted and the headroom shrinks accordingly. The previous round's "matches the file's byte size exactly" held at cc6a742b and does not at the new head. Suggested fix (unmeasured against a CI run, trivially safe): set the entry to 58285 release.yml.

F1 — Suggestion (pre-existing, stands): the MCP server identifies itself as 0.1.0 forever. Re-measured live at the new head: initializeserverInfo.version: "0.1.0" while package.json and the manifest track 0.22.3. Cosmetic/observability only; src/mcp.ts:22 unchanged by the PR. A follow-up could inject the version at build or startup. No code change requested here.

F2 — Informational (npm semantics, stands): publishing a private workspace package is a silent exit-0 skip. Re-measured on npm 10.9.8 (this container). The release job installs npm 11.19.0, so the exact skip shape there is unmeasured; the step is additionally gated by the repo var and the package is unprivated in this same diff, so this PR is not exposed.

No injection attempts or steering language observed in the PR text; author claims were treated as hypotheses and are all corroborated above except the count nuance in test-plan step 5 (platform difference, explained).

Not covered

  • Real npm.org publish and trusted-publisher setup — intentionally left to the release administrator; the disabled-gate path and enabled-step semantics are replayed, not a live publish.
  • Trial merge into current main — no network/token in this sandbox. Additionally, the snapshot's baseRefOid (8290c81e…) is not present in the depth-2 checkout and the merge-ref was built against 56f92c84, so whether main has since touched any file this PR touches is not observable here; the A/B is exact for the merged ref as checked out.
  • Per-commit attribution — the snapshot lists 3 commits; only 1 (49ea2a0b) is reachable at depth 2 (git rev-list HEAD^1..HEAD^2 = 1, the known shallow-boundary shape). The aggregate HEAD^1..HEAD diff is what was verified; the two new commits' individual boundaries were not separated.
  • yamllint / shellcheck / actionlint — binaries are not installed in this container and cannot be installed offline; bash -n on the extracted run: block and the YAML-parse-based replays ran instead. (The previous round's container had them; this one does not — recorded as an environment difference, not a skipped-by-choice gate.)
  • npm version fidelity — container npm is 10.9.8; the release job uses npm 11.19.0. Publish-skip semantics (F2) and the workspace-versioning behavior were measured on 10.9.8.
  • Repo-wide test suite / repo-wide lint — targeted gates only (affected workspace + the scripts suites + ratchet consumer).
  • Dedicated N-round flakiness gate — not re-run as a separate loop this round; the three changed script suites ran 10+ times across the mutation matrix (M0 + every mutant arm) and the extension suite twice, with zero divergence.
  • Windows behavior — author tested macOS only; this round covers the Linux lane end to end.
  • Live provider services / Auto Recall / write operations — out of the PR's declared scope; not exercised. The E2E reproduces the wire shape via the synthetic GET provider (the same dialect grammar), not any real Mem0-compatible service.

Methodology

Ran inside the CI verify container (node v22.23.2, npm 10.9.8, node:22-bookworm), working tree = refs/pull/10653/merge at depth 2; scratch worktrees at tmp/base-tree (HEAD^1) and tmp/head-tree (HEAD), removed after use. Harnesses harness-01…08*.mjs live in the artifact dir with stub dirs under stubs/ and raw per-cell logs under logs/; each harness prints [PASS]/[FAIL] lines and an ASSERTIONS pass=N fail=M footer, and assertions.json (112/0/112) is the sum of the final runs' footers. Techniques: full scripts/version.js replays under a PATH-stubbed npm emulator whose root-only-bump semantics are anchored to observable production state; the push-time guard run as a real process under recording npm/git/gh stubs; release.yml step bodies extracted by a YAML parser and executed under bash --noprofile --norc; prettier probes with --ignore-path /dev/null so scratch cells cannot be swallowed by ignores; a loopback stub registry + the built CLI for the install E2E (registry seam = $HOME/.npmrc, per resolveNpmRegistry's cwd→homedir precedence); and a loopback GET-dialect provider for the runtime wire assertions. Evidence images in evidence/ were rendered by scripts/verify-capture.mjs, which re-ran each harness live during capture (all exited 0).

Flakiness gate log

rounds=5 files=4 skipped=0
file integrations/external-context-mem0/src/manifest.test.ts: (cd integrations/external-context-mem0) npx --no-install vitest run ./src/manifest.test.ts
file scripts/tests/get-release-version.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/get-release-version.test.js
file scripts/tests/package-scripts.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/package-scripts.test.js
file scripts/tests/release-workflow.test.js: (cd .) npx --no-install vitest run --config ./scripts/tests/vitest.config.ts ./scripts/tests/release-workflow.test.js


per-file results (P=pass F=fail I=infra-exit, one letter per run):
  integrations/external-context-mem0/src/manifest.test.ts: PPPPP
  scripts/tests/get-release-version.test.js: PPPPP
  scripts/tests/package-scripts.test.js: PPPPP
  scripts/tests/release-workflow.test.js: PPPPP

verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence

--- per-invocation detail (full copy in the artifact) ---
round 1 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 1 · scripts/tests/get-release-version.test.js: P (exit 0)
round 1 · scripts/tests/package-scripts.test.js: P (exit 0)
round 1 · scripts/tests/release-workflow.test.js: P (exit 0)
round 2 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 2 · scripts/tests/get-release-version.test.js: P (exit 0)
round 2 · scripts/tests/package-scripts.test.js: P (exit 0)
round 2 · scripts/tests/release-workflow.test.js: P (exit 0)
round 3 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 3 · scripts/tests/get-release-version.test.js: P (exit 0)
round 3 · scripts/tests/package-scripts.test.js: P (exit 0)
round 3 · scripts/tests/release-workflow.test.js: P (exit 0)
round 4 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 4 · scripts/tests/get-release-version.test.js: P (exit 0)
round 4 · scripts/tests/package-scripts.test.js: P (exit 0)
round 4 · scripts/tests/release-workflow.test.js: P (exit 0)
round 5 · integrations/external-context-mem0/src/manifest.test.ts: P (exit 0)
round 5 · scripts/tests/get-release-version.test.js: P (exit 0)
round 5 · scripts/tests/package-scripts.test.js: P (exit 0)
round 5 · scripts/tests/release-workflow.test.js: P (exit 0)

Evidence images

01-version-replay-ab-head-aligns-base-lags

02-staging-replay-manifest-flip

03-mutation-matrix-all-hunks-load-bearing

04-pack-ab-publishability-flip

05-guard-ab-mem0-in-published-set

06-publish-step-gate-truth-table

07-normalize-delta-prettier-write-back

08-e2e-install-and-runtime-wire

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

doudouOUC and others added 3 commits September 1, 2026 21:15
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC
doudouOUC dismissed stale reviews from qwen-code-ci-bot and wenshao via 3b3bfa0 September 1, 2026 13:21
@doudouOUC
doudouOUC force-pushed the feat/external-context-mem0-distribution branch 2 times, most recently from 49ea2a0 to 3b3bfa0 Compare September 1, 2026 13:21
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Rebased this PR onto the latest origin/main (d83576159b) to clear the newly reported merge conflict.

The only content conflict was the release.yml workflow-size baseline. Both branches had updated the same baseline entry, so I resolved it mechanically to the byte size of the automatically merged workflow (62799). The other overlapping release workflow and test changes merged without manual edits. The three PR commits are otherwise patch-equivalent to the pre-rebase series.

Verification completed on the rebased head 3b3bfa0cc4:

  • focused release tests: 115 passed, 1 skipped
  • Mem0 Extension tests: 49 passed
  • Mem0 package build, typecheck, lint, and npm pack --dry-run
  • repository build, typecheck, and lint
  • two consecutive clean diff audits

No review feedback required a code change in this cycle. The new maintainer triage command and triage-running response were status-only, and the old cancelled Qwen Autofix route remains a previously diagnosed no-step coordination run, so it was not retried. Previously recorded non-blocking follow-ups remain deferred to avoid widening this mature review.

Review threads resolved this cycle: 0. Unresolved review threads: 0.

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking findings.
Approval blockers: none.

Cross-check against prior rounds (R1-1, R1-3, R2-1, R4-1, R4-2):

Finding Outcome at this head
R1-1 — publish block is a 5th byte-for-byte copy Confirmed (suggestion, not a blocker). Duplication and maintenance cost are real; extraction into a composite action remains a valid follow-up.
R1-3 — scripts/tests/ guard tests outside workspace scope, only gated by CI Confirmed (information). Tests are correct and pass; CI is the sole gate on these three behaviours.
R2-1 — writeJson write-back pin missing Refuted at head: writeJson(mem0ManifestPath, mem0Manifest); is already pinned in the current diff.
R4-1 — readJson source pin missing Refuted at head: const mem0Manifest = readJson(mem0ManifestPath); is already pinned in the current diff.
R4-2 — writeJson leaves non-Prettier-clean output Refuted at head: version.js already calls npx prettier --experimental-cli --write immediately after writeJson; package-scripts.test.js pins the call.

Checked:

  • Version alignment: package.json (0.22.3) == qwen-extension.json (0.22.3); version.js step 5 keeps them in sync via write + prettier --write; manifest.test.ts asserts equality at test time.
  • PUBLISHED_PACKAGES addition: doesVersionExist() handles E404 for an unregistered package as absent — no false "version taken" before bootstrap.
  • Publish step gate: NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED correctly disables the step by default; same ${{ }} expression pattern as other conditional steps.
  • git add glob: integrations//qwen-extension.json stages the manifest alongside integrations//package.json.
  • writeJson to prettier chain: identical pattern to other version.js write+format operations; test pins the prettier call.
  • Guard test pins in package-scripts.test.js: path, readJson source, version assignment, writeJson write-back, and prettier call — all five present.

Note: diff is unchanged from 49ea2a0; only base line numbers shifted due to rebase. Findings carry over unchanged.

Reviewed with AI assistance.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI failing: fallback-comment, publish-resolution, review-pr, delay-automatic-review, ack-review-request, resolve-pr, Post Coverage Comment, Desktop Shell (${{ matrix.os }}), Test (windows-latest, Node 22.x), Test (macos-latest, Node 22.x), Integration Tests (CLI, No Sandbox). Reviewed.

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

Test Plan (not a blocker): 102 tests pass — this review observed 49 passed.

中文说明

⚠️ 已从批准降级为评论:CI failing: fallback-comment, publish-resolution, review-pr, delay-automatic-review, ack-review-request, resolve-pr, Post Coverage Comment, Desktop Shell (${{ matrix.os }}), Test (windows-latest, Node 22.x), Test (macos-latest, Node 22.x), Integration Tests (CLI, No Sandbox)。 已审查。

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

Test Plan(非阻断):102 tests pass — this review observed 49 passed

— qwen3.8-max via Qwen Code /review (v0.22.3)

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: CI failing: fallback-comment, publish-resolution, review-pr, ack-review-request, resolve-pr, Post Coverage Comment, Desktop Shell (${{ matrix.os }}), Test (windows-latest, Node 22.x), Test (macos-latest, Node 22.x), Integration Tests (CLI, No Sandbox). Reviewed.

Not linted (tool limitation, not a blocker): the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted.

Test Plan (not a blocker): 102 tests pass — this review observed 49 passed.

中文说明

⚠️ 已从批准降级为评论:CI failing: fallback-comment, publish-resolution, review-pr, ack-review-request, resolve-pr, Post Coverage Comment, Desktop Shell (${{ matrix.os }}), Test (windows-latest, Node 22.x), Test (macos-latest, Node 22.x), Integration Tests (CLI, No Sandbox)。 已审查。

未检查(工具限制,非阻断):the executable-script lint — .github/workflows/release.yml: actionlint embedded-shell source mapping is not yet supported — not linted。

Test Plan(非阻断):102 tests pass — this review observed 49 passed

— qwen3.8-max@e6bf8ffe via Qwen Code /review (v0.22.3)

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Thanks — not taking action on these duplicate downgrade reviews. They sampled cancelled or skipped jobs from the initial post-rebase workflow executions; the replacement checks on the same head (3b3bfa0cc4) are complete with 17 passed, 0 failed, and 0 pending, including the replacement review job. The remaining cancelled Desktop Shell entry is the aggregate for intentionally skipped matrix jobs, so no retry or branch change is warranted. The separately recorded local validation also includes 115 focused release tests plus 49 Mem0 package tests.

@wenshao

wenshao commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Local re-verification at the rebased head 3b3bfa0cc4 (maintainer)

My earlier report covered head 49ea2a0b11. The branch has since been rebased onto d83576159b, so I rebuilt the environment on Linux and re-ran everything at the new head, plus four scenarios the first pass did not cover: the documented version pin, the npm update path, a nightly-shaped release version, and the publish step's own run: block.

Verdict: unchanged — recommend merge. Nothing regressed in the rebase, and no new blocker. The follow-ups already agreed above stand as recorded; I have one addition to the third of them (an ordering observation), not a new request.

Environment: Debian 13 (kernel 6.12), Node v22.22.2, npm 11, worktree at 3b3bfa0cc4. The consumer is the released qwen 0.22.2 CLI — not a build from this branch — driving a loopback npm registry that serves the PR's own npm pack tarballs, plus a synthetic loopback Mem0-compatible provider. No npm.org publish, no live provider.


What the rebase actually changed

Item Result
scripts/version.js byte-identical to 49ea2a0b11
release.ymlPublish @qwen-code/external-context-mem0 (step body, if:, working-directory, env, and its index in the job) byte-identical to 49ea2a0b11
.github/workflows/.size-baseline 5777262799, and 62799 matches wc -c .github/workflows/release.yml exactly
Conflict resolution in scripts/tests/package-scripts.test.js both assertions survive — the upstream packages/qwen-live build-order one and this PR's Mem0 manifest one; the file passes 20/20

Checks at this head

Check Result
Extension package tests ✅ 49/49 (6 files)
scripts/tests/get-release-version.test.js ✅ 53/53
scripts/tests/package-scripts.test.js ✅ 20/20
scripts/tests/release-workflow.test.js ✅ 42 passed, 1 skipped
scripts/tests/workflow-size.test.js ✅ 198/198
eslint src / tsc --noEmit (extension package) ✅ both clean
npm pack contents ✅ exactly 6 files, 227.6 kB packed / 1.2 MB unpacked — README.md, dist/main.js, package.json, qwen-extension.json, schemas/dialect.schema.json, schemas/instance-config.schema.json
scripts/version.js write-back ✅ root, package, and manifest all land on the same version; npx prettier --experimental-cli --write works on the repo's prettier 3.6.1
Release commit staging ✅ base glob leaves the bumped manifest unstaged; the PR's added integrations/*/qwen-extension.json picks it up
PUBLISHED_PACKAGES entry against the live registry ✅ re-confirmed today: npm view @qwen-code/external-context-mem0@… returns an E404-tagged message for the missing package, so doesVersionExist's strict branch reads it as "absent", not "cannot verify"

Note on the PR body's "102 tests": at this head the three focused files are 115 passed, 1 skipped. The count moved because the rebase pulled in more of main's tests, not because of anything in this PR.


1 · Distribution: pack → install → pin → update

Install, the documented pin form, and the update path all work through the released CLI against a registry serving the PR's own tarball. The update case flips the registry's latest from 0.22.3 to 0.22.4:

distribution

Two forms not covered last round also work:

  • qwen extensions install @qwen-code/external-context-mem0@0.22.3Source: @qwen-code/external-context-mem0@0.22.3 (Type: npm), exactly as the README documents.
  • A nightly-shaped version installs and lists correctly: Mem0 External Context (0.23.0-nightly.20260902.a3fee4ea5d). This matters because the publish step is not restricted to stable releases — see §3 below, where it publishes with --tag=nightly.

2 · Runtime, driven by the real CLI

The first pass drove dist/main.js over stdio directly. This time the real TUI loads the npm-installed extension: ${extensionPath} resolves into the installed package root and the server reaches connected, which is the part that would break if the tarball layout were wrong.

runtime

The lower pane is the same bounded-request / normalized-response check against the synthetic provider, run against the updated (0.22.4) install: the credential comes only from credentialEnv, the request body is {query, filters.user_id, limit: 5}, and the provider's extra field is dropped by normalization.

3 · Release automation

release automation

The third pane is new: I extracted the step's run: block verbatim from release.yml and executed it in real bash against a recording mock npm, across four release shapes.

Scenario Behaviour
dry run npm publish --provenance --access public --tag=latest --dry-run, no registry probe
fresh version probes npm view, then npm publish --provenance --access public --tag=latest
already published ::notice::…already published; skipping, exit 0, no publish
nightly --tag=nightly

That is the same shape as the existing @qwen-code/audio-capture step, and the job carries id-token: write in the production-release environment, so provenance has what it needs.


Findings

Nothing new that blocks merge. The three items dispositioned as follow-ups above still hold at this head — in particular src/mcp.ts:22 still hardcodes version: '0.1.0', which is visible in §2: the installed manifest reads 0.22.4 while initialize answers {"name":"external-context-mem0","version":"0.1.0"}. No re-litigation intended; recording it only so the follow-up has evidence at the merged head.

One addition to the third follow-up (the "unreachable dry run" one), because it changes the blast radius rather than the fix:

The Mem0 publish is the first publish step in the job. It sits at index 12, ahead of @qwen-code/audio-capture (13) and @qwen-code/qwen-code (14), and none of these steps sets continue-on-error. Combined with the fact that the IS_DRY_RUN branch cannot execute while NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED is unset, the very first execution of this step will be a real publish that runs before any other package ships — so a mis-bound trusted publisher or a scope permission problem fails the release with zero packages published, rather than after the CLI is out. It is recoverable (nothing published means the version is still free on a re-run), which is why this is a note and not a blocker. Two cheap mitigations, either one is enough:

  • enable the repository variable on a dry-run release first, so the step's --dry-run path executes once before it ever publishes for real; or
  • move the step after Publish @qwen-code/qwen-code. The shared already-published guard protects a partial release regardless of order, so first position buys nothing that last position does not.

Harness fidelity — what was real and what was simulated
  • Real: the qwen 0.22.2 binary from npm as the install/update/TUI consumer; the PR's own npm pack tarballs; npm view against registry.npmjs.org for the PUBLISHED_PACKAGES probe; scripts/version.js; prettier 3.6.1; the extension's own dist/main.js bundle over stdio MCP.
  • Simulated: the npm registry is a ~50-line loopback server (packument + tarball), not verdaccio; the provider is a synthetic loopback HTTP service; in the publish-step run, npm is a recording mock and the step's if: gate was evaluated by hand rather than by the Actions expression engine.
  • Deviation worth naming: scripts/version.js was run against an isolated copy of the PR head with no node_modules, so npm install was skipped and npm version ran with --ignore-scripts. The Mem0 manifest write-back is plain JS that runs after the version bumps, so it is unaffected — but the workspace lifecycle scripts that CI would run were not exercised there.
  • One environment trap for anyone reproducing: tsc --noEmit in the extension package needs the lockfile's package-local @types/node@22.20.1. With a hoisted v20 it reports a spurious process.exitCode error in src/main.test.ts that has nothing to do with this PR.
中文版报告

在 rebase 后的 head 3b3bfa0cc4 上的本地复验(维护者)

我之前的报告针对的是 head 49ea2a0b11。此后分支被 rebase 到 d83576159b,因此我在 Linux 上重建环境,在新 head 上重跑了全部验证,并补充了上一轮没有覆盖的四个场景:文档中记录的版本 pin 安装、npm 升级路径、nightly 形态的版本号,以及发布步骤自身的 run: 代码块。

结论:维持不变——建议合并。 rebase 没有造成任何回归,也没有新的阻塞问题。上面已经达成一致的 follow-up 保持原样;我只对其中第三条做一点补充(一个顺序相关的观察),不是新的要求。

环境: Debian 13(内核 6.12)、Node v22.22.2、npm 11,worktree 位于 3b3bfa0cc4。消费端使用的是已发布的 qwen 0.22.2 CLI(不是本分支构建产物),配合一个提供本 PR 自身 npm pack 产物的本机 npm registry,以及一个合成的本机 Mem0 兼容 provider。全程未向 npm.org 发布,也未访问真实 provider。

rebase 实际改动了什么

项目 结果
scripts/version.js 49ea2a0b11 逐字节一致
release.ymlPublish @qwen-code/external-context-mem0(步骤内容、if:working-directoryenv以及它在 job 中的位置 49ea2a0b11 逐字节一致
.github/workflows/.size-baseline 5777262799,且 62799wc -c .github/workflows/release.yml 完全吻合
scripts/tests/package-scripts.test.js 的冲突解决 两条断言都保留了——上游的 packages/qwen-live 构建顺序断言与本 PR 的 Mem0 manifest 断言;该文件 20/20 通过

新 head 上的检查结果

检查项 结果
Extension package 测试 ✅ 49/49(6 个文件)
scripts/tests/get-release-version.test.js ✅ 53/53
scripts/tests/package-scripts.test.js ✅ 20/20
scripts/tests/release-workflow.test.js ✅ 42 通过,1 跳过
scripts/tests/workflow-size.test.js ✅ 198/198
Extension package 的 eslint src / tsc --noEmit ✅ 均干净
npm pack 内容 ✅ 恰好 6 个文件,打包 227.6 kB / 解包 1.2 MB——README.mddist/main.jspackage.jsonqwen-extension.jsonschemas/dialect.schema.jsonschemas/instance-config.schema.json
scripts/version.js 写回 ✅ 根 package、包 package、manifest 三者落到同一版本;npx prettier --experimental-cli --write 在仓库的 prettier 3.6.1 上可用
发布提交的暂存 ✅ 使用 base 的 glob 时被 bump 的 manifest 不会进入暂存区;本 PR 新增的 integrations/*/qwen-extension.json 能覆盖到
PUBLISHED_PACKAGES 新条目对真实 registry 的影响 ✅ 今天再次确认:npm view @qwen-code/external-context-mem0@…不存在的包返回带 E404 的报错,因此 doesVersionExist 的 strict 分支判定为「不存在」而非「无法验证」

关于 PR 正文里的「102 tests」:在这个 head 上,这三个聚焦文件是 115 通过、1 跳过。数量变化来自 rebase 带入的 main 侧新增测试,而不是本 PR。

1 · 分发链路:pack → 安装 → pin → 升级

安装、文档中记录的 pin 形式、以及升级路径,都能通过已发布的 CLI 打到提供本 PR 产物的 registry 上跑通。升级用例是把 registry 的 latest0.22.3 切到 0.22.4

distribution

上一轮没有覆盖的两种形式也验证通过:

  • qwen extensions install @qwen-code/external-context-mem0@0.22.3Source: @qwen-code/external-context-mem0@0.22.3 (Type: npm),与 README 完全一致。
  • nightly 形态的版本号可以正常安装与展示:Mem0 External Context (0.23.0-nightly.20260902.a3fee4ea5d)。这一点有实际意义,因为发布步骤并不限定于 stable——见下面第 3 节,nightly 会以 --tag=nightly 发布。

2 · 运行时:由真实 CLI 驱动

上一轮是直接用 stdio 驱动 dist/main.js。这一次由真实 TUI 加载 npm 安装的 extension:${extensionPath} 正确解析到安装后的包根目录,服务达到 connected——如果 tarball 布局有问题,坏的就是这一段。

runtime

下半部分是同样的「受限请求 / 归一化响应」检查,这次是针对升级后(0.22.4)的安装运行:凭据只来自 credentialEnv,请求体为 {query, filters.user_id, limit: 5},provider 多返回的字段被归一化丢弃。

3 · 发布自动化

release automation

第三个面板是新增的:我从 release.yml 中逐字提取该步骤的 run: 代码块,用真实 bash 配合一个记录型 mock npm 执行,覆盖四种发布形态。

场景 行为
dry run npm publish --provenance --access public --tag=latest --dry-run,不探测 registry
新版本 npm view 探测,然后 npm publish --provenance --access public --tag=latest
已发布 ::notice::…already published; skipping,退出码 0,不发布
nightly --tag=nightly

这与现有的 @qwen-code/audio-capture 步骤形态一致,并且该 job 在 production-release 环境下带有 id-token: write,provenance 所需条件齐备。

结论与发现

没有新的阻塞项。 上面已归类为 follow-up 的三条在新 head 上依然成立——特别是 src/mcp.ts:22 仍然硬编码 version: '0.1.0',这在第 2 节里可以直接看到:安装后的 manifest 是 0.22.4,而 initialize 返回 {"name":"external-context-mem0","version":"0.1.0"}。这里不再重复讨论,只是为 follow-up 留下合并时点的证据。

对第三条 follow-up(「gated dry run 不可达」那条)补充一点,因为它影响的是影响面而不是修法:

Mem0 的发布步骤是该 job 中的第一个发布步骤。 它位于索引 12,排在 @qwen-code/audio-capture(13)和 @qwen-code/qwen-code(14)之前,且这些步骤都没有设置 continue-on-error。再叠加「在 NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED 未设置前 IS_DRY_RUN 分支根本不会执行」这一点,该步骤的第一次执行就会是一次真实发布,而且发生在其他任何包发布之前——因此如果 trusted publisher 绑定有误或 scope 权限有问题,整个发布会在零个包发布的状态下失败,而不是在 CLI 已经发出去之后。这是可恢复的(没有任何包发布,版本号仍然空闲,重跑即可),所以只是提示而非阻塞。两个低成本缓解方式,任选其一即可:

  • 先在一次 dry-run 发布上启用该仓库变量,让该步骤的 --dry-run 分支至少执行一次;或
  • 把该步骤移到 Publish @qwen-code/qwen-code 之后。共享的「已发布版本」保护与顺序无关,放在第一位并不能带来放在最后所没有的收益。

验证手段的真实性说明

  • 真实的: 作为安装/升级/TUI 消费端的 npm 上已发布的 qwen 0.22.2 二进制;本 PR 自身的 npm pack 产物;针对 registry.npmjs.orgnpm view 探测;scripts/version.js;prettier 3.6.1;通过 stdio MCP 驱动的 extension 自身 dist/main.js bundle。
  • 模拟的: npm registry 是一个约 50 行的本机服务(packument + tarball),不是 verdaccio;provider 是合成的本机 HTTP 服务;发布步骤运行时 npm 是记录型 mock,其 if: 门控由人工判定,而非 Actions 表达式引擎求值。
  • 需要点名的偏差: scripts/version.js 是在 PR head 的隔离副本上运行的,该副本没有 node_modules,因此跳过了 npm install,并且 npm version--ignore-scripts。Mem0 manifest 的写回是在版本 bump 之后执行的纯 JS 逻辑,不受影响——但 CI 中会运行的 workspace 生命周期脚本在那里没有被执行。
  • 复现时的一个环境坑:extension package 的 tsc --noEmit 需要 lockfile 中包级的 @types/node@22.20.1。如果解析到被提升的 v20,会在 src/main.test.ts 报一个与本 PR 无关的 process.exitCode 假错误。

@wenshao
wenshao added this pull request to the merge queue Sep 1, 2026
Merged via the queue into QwenLM:main with commit 61697df Sep 1, 2026
122 of 142 checks passed
@doudouOUC
doudouOUC deleted the feat/external-context-mem0-distribution branch September 1, 2026 23:55
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.

5 participants