mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
fix(ci): exempt non-TUI packages from the parity impact gate (#15937)
* fix(ci): exempt non-TUI packages from the parity impact gate * fix(ci): exempt the manifest-less SDK trees from the parity gate The exclusion list was audited from workspace manifests, so packages/sdk-java, packages/sdk-python and packages/zed-extension — none of which carries a package.json — stayed outside it and still selected the component lane for a PR touching only them. Add the three trees to the exclusion, pin each with a single-file case in the classifier contract suite, and record why they are hand-listed in the design doc pair. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvf8ed23g * fix(ci): name the policy exclusion and cover the CUA driver tree The exclusion branch skipped every matched path before recording a reason, so a PR excluded on policy reported the same "No TUI impact" as a PR whose paths the classifier never mapped. Record the reason once inside that branch (R1-2). packages/cua-driver is outside the --cli-only closure: node-repl declares @qwen-code/cua-sdk, but the build filter pulls node-repl without its dependencies and no TUI suite imports the SDK. Exempt it with a single-file row and a rename row (R2-1). The workspace-excluded trees were a third hand-maintained copy of the set pnpm-workspace.yaml states, with nothing reconciling them. A new case reads pnpm-workspace.yaml and asserts every excluded tree still classifies as no-impact (R2-2). The design docs claimed the excluded packages are never built by these gates. npm run bundle compiles integrations/external-context into dist/mem0 through hard-coded esbuild entries, so say what actually happens and name esbuild entries and copy_bundle_assets copies as inputs the manifest audit cannot see (R2-3). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvnt1g33t * test(ci): reconcile workspace negations by value, not literal spelling * test(ci): pin the policy reason for every excluded workspace tree R4-1: the reconciliation test only asserted outputs.component, the value the classifier starts with, so narrowing reasons.add(...) for any tree except cua-driver silently reverted the human-facing diagnostic to "No TUI impact" while all 163 tests stayed green. - assert outputs.reason inside the loop, not only for the CUA driver tree - extract excludedTrees(yamlText) and feed it a fixture with a trailing-comment negation and an !integrations/... negation, so the by-value derivation is exercised for the spellings its comment claims Witness (both mutants, unmutated suite 163/163 green): - reasons.add narrowed to packages/cua-driver/: reason assertion red - derivation reverted to the old literal regex: fixture assertion red Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvs3czk3z --------- Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: Qwen-Coder <[email protected]>
This commit is contained in:
4 files changed
+139
-3
No files matched your search
@@ -107,7 +107,16 @@ jobs:
|
||||
}
|
||||
const paths = new Set(files.flatMap((file) => [file.filename, file.previous_filename].filter(Boolean)));
|
||||
for (const path of paths) {
|
||||
if (/^(docs\/|packages\/managed-agent-server\/|packages\/vscode-ide-companion\/src\/)/.test(path)) continue;
|
||||
// Outside the TUI build/test closure, plus web-shell: its only CLI
|
||||
// path is the web-templates transcript bundle no TUI suite renders,
|
||||
// and ci.yml owns its build + E2E health. The sdk-java, sdk-python
|
||||
// and zed-extension trees carry no workspace manifest, so the
|
||||
// manifest audit below cannot see them; cua-driver is reached only
|
||||
// through node-repl's runtime import, which --cli-only never builds.
|
||||
if (/^(docs\/|packages\/(managed-agent-server|cua-driver|web-shell|chrome-extension|mobile-mcp|qwen-live|desktop|live-host|mobile-shell|sdk-java|sdk-python|zed-extension)\/|packages\/vscode-ide-companion\/src\/|integrations\/(external-context-mem0|external-context|test-failure-explorer)\/)/.test(path)) {
|
||||
reasons.add('Outside the TUI build/test closure');
|
||||
continue;
|
||||
}
|
||||
if (broad.some((pattern) => pattern.test(path))) {
|
||||
all();
|
||||
reasons.add('Shared renderer, runtime, dependency or test/build infrastructure');
|
||||
|
||||
@@ -32,7 +32,7 @@ Use the paginated GitHub PR-files API without checking out PR code in the classi
|
||||
|
||||
Core turn scheduling, cancellation, shell execution, session persistence and telemetry are inputs to these behaviors even when their paths are outside the UI tree. Known shared mechanisms can select multiple groups. A selected product group keeps all three existing `node:ink`, `bun:ink`, and `bun:opentui` legs, whole-file tests, zero retries and strict unhandled-error behavior. The viewport harness is an integration facility check, not a fast product unit test.
|
||||
|
||||
Java service, Companion source and documentation-only changes retain their unrelated-path exclusions. Companion manifests, bundler settings and scripts are not part of that exclusion. Existing workflow-level path filtering remains broad enough to see shared build inputs and all packages; a docs-only PR need not start this workflow at all.
|
||||
The unrelated-path exclusions cover documentation, the Java service, Companion source, and every package tree outside the TUI build/test closure: Web Shell, the Chrome extension, mobile-mcp, qwen-live, external-context, external-context-mem0, test-failure-explorer, the desktop, live-host and mobile-shell trees that the pnpm workspace itself excludes, the Java, Python and Zed SDK trees, which carry no workspace manifest at all and so are invisible to the manifest audit, and the CUA driver tree, whose only path into the CLI closure is `node-repl`'s runtime import, which the `--cli-only` filter never follows. The closure was audited from the workspace manifests against the `--cli-only` build filter plus the root dependencies; the manifest-less trees were added by hand for that reason, and the hard-coded `esbuild.config.js` entry points plus `scripts/copy_bundle_assets.js` copies are inputs that audit cannot see. Web Shell is the one deliberate exception to pure closure membership: the `web-templates` transcript bundle builds it, but no TUI suite renders that bundle, and ci.yml owns Web Shell build and E2E health. `external-context` is not exempt from building either: `npm run bundle`'s hard-coded esbuild entries compile it into `dist/mem0` on every lane that bundles, and no TUI suite executes that output. Companion manifests, bundler settings and scripts remain outside the Companion exclusion. Excluded manifests lose no unique coverage: a manifest-only change that breaks the shared frozen-lockfile install fails every workflow's install step, and an accompanying root lockfile change still selects full acceptance. Existing workflow-level path filtering remains broad enough to see shared build inputs and all packages; a docs-only PR need not start this workflow at all.
|
||||
|
||||
### Failure and evidence contracts
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
Core 的 turn 调度、取消、shell 执行、会话持久化和遥测即使不在 UI 目录下,也是这些行为的输入。已知共享机制可以同时选择多个组。每个选中的产品专项保留已有 `node:ink`、`bun:ink`、`bun:opentui` 三腿、整文件测试、零重试与严格未处理异常策略。Viewport 测试属于集成测试设施检查,不是快速产品单测。
|
||||
|
||||
Java 服务、Companion 源码和文档保留无关路径排除规则。Companion 清单、打包配置和 scripts 不在排除范围内。工作流级路径过滤继续覆盖共享构建输入和全部 package;纯文档 PR 可以完全不启动该工作流。
|
||||
无关路径排除覆盖文档、Java 服务、Companion 源码,以及 TUI 构建/测试闭包之外的全部包目录:Web Shell、Chrome 扩展、mobile-mcp、qwen-live、external-context、external-context-mem0、test-failure-explorer,pnpm workspace 本身就排除的 desktop、live-host 和 mobile-shell,完全不带 workspace 清单、因而不在清单审计视野里的 Java、Python 与 Zed SDK 目录,以及进入 CLI 闭包的唯一路径是 `node-repl` 运行时 import、`--cli-only` 过滤器不会跟随的 CUA driver 目录。闭包按 workspace 清单对照 `--cli-only` 构建过滤器及根目录依赖审计得出,不带清单的目录因此是手工补入的,而 `esbuild.config.js` 里硬编码的入口与 `scripts/copy_bundle_assets.js` 的拷贝是这份审计看不见的输入。Web Shell 是唯一刻意超出纯闭包成员资格的排除:`web-templates` 的 transcript 打包会构建它,但没有任何 TUI 套件渲染该打包产物,且 ci.yml 负责 Web Shell 的构建与 E2E 健康。`external-context` 同样仍会被构建:`npm run bundle` 的硬编码 esbuild 入口会把它编进 `dist/mem0`,凡走打包的 lane 都会执行,但没有任何 TUI 套件运行该产物。Companion 清单、打包配置和 scripts 仍不在 Companion 排除范围内。被排除的清单文件不损失独有覆盖:仅改清单导致共享 frozen-lockfile 安装失败时,每个工作流的安装步骤都会失败;伴随根 lockfile 变更仍会选择全量验收。工作流级路径过滤继续覆盖共享构建输入和全部 package;纯文档 PR 可以完全不启动该工作流。
|
||||
|
||||
### 失败与证据契约
|
||||
|
||||
|
||||
@@ -179,6 +179,20 @@ describe('TUI product evidence', () => {
|
||||
['integration-tests/test-helper.ts', lanes],
|
||||
['.github/workflows/tui-parity.yml', lanes],
|
||||
['docs/design/unrelated.md', []],
|
||||
['packages/web-shell/client/App.tsx', []],
|
||||
['packages/chrome-extension/package.json', []],
|
||||
['packages/mobile-mcp/package.json', []],
|
||||
['packages/qwen-live/build.mjs', []],
|
||||
['integrations/external-context/package.json', []],
|
||||
['integrations/external-context-mem0/package.json', []],
|
||||
['integrations/test-failure-explorer/package.json', []],
|
||||
['packages/desktop/package.json', []],
|
||||
['packages/live-host/electron-builder.yml', []],
|
||||
['packages/mobile-shell/build.gradle.kts', []],
|
||||
['packages/sdk-java/client/pom.xml', []],
|
||||
['packages/sdk-python/pyproject.toml', []],
|
||||
['packages/zed-extension/extension.toml', []],
|
||||
['packages/cua-driver/typescript/src/native-assets.ts', []],
|
||||
['scripts/unrelated-maintenance.js', ['component']],
|
||||
])(
|
||||
'selects mechanisms rather than blanket full acceptance for %s',
|
||||
@@ -593,6 +607,64 @@ describe('TUI product evidence', () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('skips Web Shell-only changes but runs when CLI source rides along', async () => {
|
||||
expect(
|
||||
await required([
|
||||
{ filename: 'packages/web-shell/client/App.tsx' },
|
||||
{
|
||||
filename: 'packages/web-shell/client/completions/slashCompletion.ts',
|
||||
},
|
||||
]),
|
||||
).toBe(false);
|
||||
expect(
|
||||
await required([
|
||||
{ filename: 'packages/web-shell/client/App.tsx' },
|
||||
{ filename: 'packages/cli/src/ui/App.tsx' },
|
||||
]),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('reports a policy exclusion instead of the no-impact default', async () => {
|
||||
const { outputs } = await impact([
|
||||
{ filename: 'packages/cua-driver/typescript/src/native-assets.ts' },
|
||||
]);
|
||||
expect(outputs.component).toBe('false');
|
||||
expect(outputs.reason).toBe('Outside the TUI build/test closure');
|
||||
});
|
||||
|
||||
// Parse by value, not by literal spelling: a trailing comment or an
|
||||
// `!integrations/...` negation is lint-legal and must still be reconciled.
|
||||
const excludedTrees = (yamlText) =>
|
||||
parse(yamlText)
|
||||
.packages.filter((entry) => entry.startsWith('!'))
|
||||
.map((entry) => entry.slice(1));
|
||||
|
||||
it('excludes every tree the pnpm workspace itself leaves out', async () => {
|
||||
const excluded = excludedTrees(readFileSync('pnpm-workspace.yaml', 'utf8'));
|
||||
expect(excluded.length).toBeGreaterThan(0);
|
||||
for (const tree of excluded) {
|
||||
const { outputs } = await impact([
|
||||
{ filename: `${tree}/placeholder.ts` },
|
||||
]);
|
||||
expect(outputs.component, tree).toBe('false');
|
||||
expect(outputs.reason, tree).toBe('Outside the TUI build/test closure');
|
||||
}
|
||||
});
|
||||
|
||||
it('reconciles negations spelled with a trailing comment or another scope', () => {
|
||||
expect(
|
||||
excludedTrees(
|
||||
[
|
||||
'packages:',
|
||||
" - 'packages/*'",
|
||||
" - '!packages/desktop' # promoted to its own release later",
|
||||
" - '!integrations/generated-ui'",
|
||||
'',
|
||||
].join('\n'),
|
||||
),
|
||||
).toEqual(['packages/desktop', 'integrations/generated-ui']);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'packages/cli/src/ui/App.tsx',
|
||||
'packages/vscode-ide-companion/package.json',
|
||||
@@ -658,6 +730,61 @@ describe('TUI product evidence', () => {
|
||||
'docs/design/runtime.md',
|
||||
'docs/design/old-runtime.md',
|
||||
],
|
||||
[
|
||||
'both ends in the Web Shell',
|
||||
'packages/web-shell/client/App.tsx',
|
||||
'packages/web-shell/client/OldApp.tsx',
|
||||
],
|
||||
[
|
||||
'both ends in the Chrome extension',
|
||||
'packages/chrome-extension/package.json',
|
||||
'packages/chrome-extension/old-package.json',
|
||||
],
|
||||
[
|
||||
'both ends in mobile-mcp',
|
||||
'packages/mobile-mcp/package.json',
|
||||
'packages/mobile-mcp/old-package.json',
|
||||
],
|
||||
[
|
||||
'both ends in qwen-live',
|
||||
'packages/qwen-live/build.mjs',
|
||||
'packages/qwen-live/old-build.mjs',
|
||||
],
|
||||
[
|
||||
'both ends in external-context',
|
||||
'integrations/external-context/package.json',
|
||||
'integrations/external-context/old-package.json',
|
||||
],
|
||||
[
|
||||
'both ends in external-context-mem0',
|
||||
'integrations/external-context-mem0/package.json',
|
||||
'integrations/external-context-mem0/old-package.json',
|
||||
],
|
||||
[
|
||||
'both ends in test-failure-explorer',
|
||||
'integrations/test-failure-explorer/package.json',
|
||||
'integrations/test-failure-explorer/old-package.json',
|
||||
],
|
||||
[
|
||||
'both ends in the workspace-excluded desktop tree',
|
||||
'packages/desktop/package.json',
|
||||
'packages/desktop/old-package.json',
|
||||
],
|
||||
[
|
||||
'both ends in the workspace-excluded live-host tree',
|
||||
'packages/live-host/electron-builder.yml',
|
||||
'packages/live-host/old-electron-builder.yml',
|
||||
],
|
||||
[
|
||||
'both ends in the workspace-excluded mobile-shell tree',
|
||||
'packages/mobile-shell/build.gradle.kts',
|
||||
'packages/mobile-shell/old-build.gradle.kts',
|
||||
],
|
||||
[
|
||||
'both ends in the CUA driver tree',
|
||||
'packages/cua-driver/typescript/src/native-assets.ts',
|
||||
'packages/cua-driver/typescript/src/old-native-assets.ts',
|
||||
],
|
||||
])(
|
||||
'still skips a rename with %s',
|
||||
async (_label, filename, previousFilename) => {
|
||||
|
||||
Reference in new issue
Block a user