From b6a75e1f5417f0b5c073cc664972f01316e43aa6 Mon Sep 17 00:00:00 2001 From: Dudu-0223 Date: Sat, 31 Oct 2026 00:09:40 +0800 Subject: [PATCH 1/2] fix(ci): stabilize Goal clocks and dependency restoration --- apps/web/tests/README.i18n.yaml | 4 ++-- apps/web/tests/README.md | 2 +- apps/web/tests/README.zh.md | 2 +- apps/web/tests/goal-multi-turn-actions.e2e.ts | 7 ++++++- python/sdk-runtime/README.i18n.yaml | 4 ++-- python/sdk-runtime/README.md | 2 +- python/sdk-runtime/README.zh.md | 2 +- scripts/build-exe-for-python-sdk.spec.ts | 1 + scripts/build-exe-for-python-sdk.ts | 2 +- 9 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/web/tests/README.i18n.yaml b/apps/web/tests/README.i18n.yaml index e314a707ba..b9ef470416 100644 --- a/apps/web/tests/README.i18n.yaml +++ b/apps/web/tests/README.i18n.yaml @@ -3,8 +3,8 @@ # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write apps/web/tests/README.md /apps-web-browser-e2e: - en: 2deef0df72a6465c - zh: b24989ffb2d8586a + en: fb3ce9b97f794a5d + zh: 1b042c0e6ac4bb6a /apps-web-browser-e2e/completion-observations: en: 8c139966983959cc zh: 54f09f9b7ac84cda diff --git a/apps/web/tests/README.md b/apps/web/tests/README.md index 969cf3a965..0b52f08aee 100644 --- a/apps/web/tests/README.md +++ b/apps/web/tests/README.md @@ -14,7 +14,7 @@ On Linux, `--with-deps` installs dependencies through the system package manager Ordinary scenarios begin with no registered Workspace or Session and a durable marker recording a removed default Workspace, so explicit folder-selection scenarios retain control of their cwd. `launchWebScaffold({ firstUse: true })` leaves initialization eligible for startup scenarios. -Same-day message goldens use the shared `WEB_FIXTURE_TIME`: seeded transcripts pass it as `createdAt` and pin the browser calendar with `pinBrowserClock`; scenarios that submit live messages also use `pinHostClock` and restore it after teardown. These clocks advance with real elapsed time while keeping native timers intact, so midnight does not change a scenario's date labels. +Same-day message goldens use the shared `WEB_FIXTURE_TIME`: seeded transcripts pass it as `createdAt` and pin the browser calendar with `pinBrowserClock`; scenarios that submit live messages, including autonomous Goal rounds, also use `pinHostClock` and restore it after teardown. These clocks advance with real elapsed time while keeping native timers intact, so midnight does not change a scenario's date labels. ## Completion observations diff --git a/apps/web/tests/README.zh.md b/apps/web/tests/README.zh.md index 29234f33c6..82b4e06232 100644 --- a/apps/web/tests/README.zh.md +++ b/apps/web/tests/README.zh.md @@ -14,7 +14,7 @@ pnpm --filter @deepseek-ai/dsh-web-frontend exec playwright install --with-deps 普通场景以没有已登记 Workspace 或 Session、但持久化标记记录默认 Workspace 已被删除的状态启动,使显式文件夹选择场景自行决定 cwd。`launchWebScaffold({ firstUse: true })` 保留初始化资格,供启动场景使用。 -当天消息的 golden 使用共享的 `WEB_FIXTURE_TIME`:预置会话将它传为 `createdAt`,并通过 `pinBrowserClock` 固定浏览器日期;提交实时消息的场景还使用 `pinHostClock`,在清理后恢复。固定后的时钟随真实耗时前进,保留原生定时器,因此跨午夜不会改变场景的日期标签。 +当天消息的 golden 使用共享的 `WEB_FIXTURE_TIME`:预置会话将它传为 `createdAt`,并通过 `pinBrowserClock` 固定浏览器日期;提交实时消息的场景(包括自主 Goal 回合)还使用 `pinHostClock`,在清理后恢复。固定后的时钟随真实耗时前进,保留原生定时器,因此跨午夜不会改变场景的日期标签。 ## 完成状态观察 diff --git a/apps/web/tests/goal-multi-turn-actions.e2e.ts b/apps/web/tests/goal-multi-turn-actions.e2e.ts index 4a1c6e8710..72f3e5400d 100644 --- a/apps/web/tests/goal-multi-turn-actions.e2e.ts +++ b/apps/web/tests/goal-multi-turn-actions.e2e.ts @@ -14,7 +14,7 @@ import { assertFixtureInventory, captureExpandedTurnProcessAria, captureStableAria, compareOrRefreshGolden, launchWebScaffold, recordFixture, watchConsole, webSnapshotMode, type WebScaffold, } from './scaffold.ts' -import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts' +import { connectFreshWorkspace, newEnglishPage, pinBrowserClock, pinHostClock, saveFailureShot } from './support.ts' const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/goal-multi-turn-actions', import.meta.url)) const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl') @@ -95,6 +95,7 @@ describe('web e2e: Goal keeps one assistant action row per completed turn', () = let page: Page let tripwire: ReturnType let sessionEvents: SessionEvent[] + let hostClock: ReturnType | undefined afterEach(async () => { const failures: unknown[] = [] @@ -103,6 +104,8 @@ describe('web e2e: Goal keeps one assistant action row per completed turn', () = const closing = scaffold scaffold = undefined await closing?.close().catch((error: unknown) => failures.push(error)) + hostClock?.mockRestore() + hostClock = undefined if (failures.length === 1) throw failures[0] if (failures.length > 1) throw new AggregateError(failures, 'goal-multi-turn-actions teardown failed') }) @@ -110,6 +113,7 @@ describe('web e2e: Goal keeps one assistant action row per completed turn', () = /** Boot the real Web composition and connect a fresh package fixture workspace. */ async function launch(): Promise { sessionEvents = [] + if (MODE !== 'record') hostClock = pinHostClock() scaffold = await launchWebScaffold( MODE === 'record' ? {} : { replayFixture: FIXTURE, replayOverride: OVERRIDE }, ) @@ -117,6 +121,7 @@ describe('web e2e: Goal keeps one assistant action row per completed turn', () = scaffold.ctx.on('session/event', (_session, event: SessionEvent) => { sessionEvents.push(event) }) browser = await chromium.launch() page = await newEnglishPage(browser) + if (MODE !== 'record') await pinBrowserClock(page) tripwire = watchConsole(page) await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' }) await page.waitForSelector('[class*="frame"]', { timeout: 30_000 }) diff --git a/python/sdk-runtime/README.i18n.yaml b/python/sdk-runtime/README.i18n.yaml index b7f6458756..86a351e7db 100644 --- a/python/sdk-runtime/README.i18n.yaml +++ b/python/sdk-runtime/README.i18n.yaml @@ -15,5 +15,5 @@ en: 0f476fb87a1a3349 zh: 359dbd9b24b18b46 /deepseek-harness-runtime-bin/build-and-distribution: - en: a41e8eb7e99b9532 - zh: 2789d8f43b1b54be + en: 80e861713ec5fb8b + zh: e2dd11e70d748dcf diff --git a/python/sdk-runtime/README.md b/python/sdk-runtime/README.md index 346813cb1c..72df976d7e 100644 --- a/python/sdk-runtime/README.md +++ b/python/sdk-runtime/README.md @@ -62,7 +62,7 @@ External profile management uses `dsh plugin --profile ...`. After explic ## Build and distribution -Production deployment permits unused workspace patches for packages outside the runtime closure; patches for included packages must still apply successfully. This exception is confined to the deploy command; repository installation still rejects unused patches. +Production deployment permits unused workspace patches for packages outside the runtime closure; patches for included packages must still apply successfully. This exception is confined to the deploy command; repository installation still rejects unused patches. Development dependency restoration keeps the lockfile frozen and prefers cached packages, but permits fetching missing registry metadata required by pnpm supply-chain checks. From the repository root, `pnpm exec tsx scripts/build-exe-for-python-sdk.ts` verifies the closure, builds packages, deploys a symlink-free tree, packages the selected target, and syncs the executable and sidecars into this module. The executable stores embedded files with Brotli compression. Only the target platform’s PTY prebuilds are packaged. Package README, CHANGELOG, HISTORY Markdown, source maps, and TypeScript declarations outside runtime assets are omitted. Deployment disables workspace hoisting, and packaging excludes checkout dependencies and workspace source trees outside the deployment so dependency scans cannot add development packages to the payload. Staging shares root workspace packages with nested consumers of the same pnpm dependency identity, preserving singleton services while retaining different peer resolutions. `scripts/build-python-release.py` stages release-shaped wheels at the root repository version and pins `deepseek-harness-sdk` to the exact runtime version. diff --git a/python/sdk-runtime/README.zh.md b/python/sdk-runtime/README.zh.md index 0339c1020b..724ce7aa56 100644 --- a/python/sdk-runtime/README.zh.md +++ b/python/sdk-runtime/README.zh.md @@ -62,7 +62,7 @@ Python 与打包的 bootstrap 共享资源布局:`//office/` ## 构建与分发 -生产部署允许工作区中不属于运行时闭包的补丁保持未使用;闭包内包的补丁仍必须成功应用。此例外仅用于部署命令,仓库安装仍拒绝未使用的补丁。 +生产部署允许工作区中不属于运行时闭包的补丁保持未使用;闭包内包的补丁仍必须成功应用。此例外仅用于部署命令,仓库安装仍拒绝未使用的补丁。恢复开发依赖时保持锁文件冻结并优先使用缓存包,但允许获取 pnpm 供应链检查所需的缺失 registry 元数据。 在仓库根目录运行 `pnpm exec tsx scripts/build-exe-for-python-sdk.ts`,会校验闭包、构建包、部署无符号链接的文件树、打包所选目标,并把可执行程序及伴随文件同步到本模块。可执行文件使用 Brotli 压缩存储内嵌文件。打包时只保留目标平台的 PTY 预构建文件,并排除运行时资源目录之外的包内 README、CHANGELOG、HISTORY Markdown 文档、source map 和 TypeScript 声明。部署会关闭工作区提升,打包时还会排除部署目录之外的检出依赖和工作区源码树,防止依赖扫描把开发包加入载荷。暂存步骤让使用相同 pnpm 依赖标识的嵌套消费者共享根目录的工作区包,保留服务单例,同时保留不同的对等依赖解析。`scripts/build-python-release.py` 按仓库根版本暂存发布形态的 wheel 包,并将 `deepseek-harness-sdk` 固定到完全相同的运行时版本。 diff --git a/scripts/build-exe-for-python-sdk.spec.ts b/scripts/build-exe-for-python-sdk.spec.ts index fdaf7202a7..9ffbc7352d 100644 --- a/scripts/build-exe-for-python-sdk.spec.ts +++ b/scripts/build-exe-for-python-sdk.spec.ts @@ -41,6 +41,7 @@ describe('Python runtime executable builder CLI', () => { expect(commands[0]).toMatch(/ run verify-runtime-closure$/) expect(commands[1]?.endsWith(' ' + command)).toBe(true) expect(commands[2]).toContain(' --filter dsh-python-runtime-closure deploy ') + expect(commands[3]).toMatch(/ install --prefer-offline --frozen-lockfile --prod=false --ignore-scripts$/) }) it('rejects simultaneous build modes before invoking pnpm', () => { diff --git a/scripts/build-exe-for-python-sdk.ts b/scripts/build-exe-for-python-sdk.ts index 447c98ff14..03c6c48b34 100644 --- a/scripts/build-exe-for-python-sdk.ts +++ b/scripts/build-exe-for-python-sdk.ts @@ -319,7 +319,7 @@ class SingleExeBuild { this.staging, ]) } finally { // Legacy deploy records production-only workspace state; restore the development installation before exec. - await this.runPnpm('restore development dependencies', ['install', '--offline', '--frozen-lockfile', '--prod=false', '--ignore-scripts']) + await this.runPnpm('restore development dependencies', ['install', '--prefer-offline', '--frozen-lockfile', '--prod=false', '--ignore-scripts']) } await this.restoreLegacyHoists() await this.materializeStagedLinks() From ff6960093a29da3a93ac11ce0fae9bb36d92f068 Mon Sep 17 00:00:00 2001 From: Dudu-0223 Date: Sat, 31 Oct 2026 00:14:05 +0800 Subject: [PATCH 2/2] fix(ci): isolate benchmark measurement workers --- .github/workflows/ci.yml | 4 ++-- vitest.bench.config.ts | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 916971cfa4..d3da0bda3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -274,8 +274,8 @@ jobs: || 'ubuntu-24.04' }} name: node 24 / benchmarks # Wall-clock budgets need an otherwise idle runner, so this job runs the - # benchmark lane alone instead of joining a concurrent gate aggregate, and - # keeps its own two-file parallelism inside that runner. The self-hosted + # benchmark lane alone instead of joining a concurrent gate aggregate, with + # one measured file at a time inside that runner. The self-hosted # failover pool is shared, so the benchmarks stay on hosted Linux there. timeout-minutes: 20 steps: diff --git a/vitest.bench.config.ts b/vitest.bench.config.ts index 053b7a2c73..8978ba2789 100644 --- a/vitest.bench.config.ts +++ b/vitest.bench.config.ts @@ -6,9 +6,8 @@ import { standardDecoratorPlugin, vitestExecArgv } from './vitest.shared.ts' * CI performance gate. Node CPU cases use compiled plain-Node workers under * `benchmarks/.dsh-build/`; browser cases drive built Client artifacts through * the shared shipped-composition Web scaffold. - * Two files run at a time so the four-vCPU CI runner keeps both cores busy. - * Budgets keep their reviewed CI scale and headroom; a pairing that inflates an - * endpoint surfaces as a failing budget and is never loosened to accept it. + * Files run one at a time so measured workers do not share CPU and memory + * bandwidth with another benchmark's workers or fixture preparation. */ export default defineConfig({ plugins: [tsconfigPaths({ projects: ['./tsconfig.base.json'] }), standardDecoratorPlugin()], @@ -21,8 +20,8 @@ export default defineConfig({ 'benchmarks/**/*.bench.ts', 'benchmarks/**/*.bench.client.ts', ], - fileParallelism: true, - maxWorkers: 2, + fileParallelism: false, + maxWorkers: 1, testTimeout: 600_000, hookTimeout: 120_000, disableConsoleIntercept: true,