From 431783426e62fa5897a503546ba39f705260e173 Mon Sep 17 00:00:00 2001 From: Huanqi Cao Date: Fri, 7 Aug 2026 20:50:36 +0800 Subject: [PATCH] test(cli): compose the real Windows shell roster through the shipped bundle layers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The resolver suite previously exercised only fixture patch lists, leaving the real shipped windows.cordis.patch.yml and the bundle→windows→user composition ordering untested on Linux CI. The new cases load a temp profile whose bundle layers resolve from the real dsh-base/dsh-web-app packages (app installation anchor), apply the platform layer through the boot's own composeEntries algorithm with the platform injected, and assert the win32 danger-full-access roster (eight disables, three inserts, no warnings on the web profile). A second case pins POSIX unchanged and the base-only-profile ui-permission no-match warning as warned-but-harmless, matching the patch header's documented contract. --- .../2026-08-01-windows-pwsh-default.i18n.yaml | 4 +- .../2026-08-01-windows-pwsh-default.md | 2 +- .../2026-08-01-windows-pwsh-default.zh.md | 2 +- apps/cli/tests/windows-shell.spec.ts | 60 +++++++++++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.i18n.yaml b/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.i18n.yaml index 48f7c38aa5..af9f64208f 100644 --- a/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.md -2026-08-01-windows-pwsh-default.md: 4cd17497c0185ba61fb4994a9167d114640b4866 -2026-08-01-windows-pwsh-default.zh.md: 201e5dd2c7c0b791bf6474261846bc7f4061cc5b +2026-08-01-windows-pwsh-default.md: ea45040a0eb6f1325ed8f3a02a477d5dfd614696 +2026-08-01-windows-pwsh-default.zh.md: 41324afccdd775cac1629ed69217cff4237be091 diff --git a/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.md b/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.md index 4cd17497c0..ea45040a0e 100644 --- a/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.md +++ b/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.md @@ -39,6 +39,6 @@ The pwsh GUI rendering stage (stage 2 of the original roadmap) shipped earlier w ## Verification -- Unit: `apps/cli/tests/windows-shell.spec.ts` pins the win32 default, the custom-profile skip, and the missing-patch failure, with the platform injected; `packages/bundle/base/tests/base.spec.ts` pins the shipped Windows roster (disables, inserts, and the absent approval service). +- Unit: `apps/cli/tests/windows-shell.spec.ts` pins the win32 default, the custom-profile skip, and the missing-patch failure with the platform injected, and composes the REAL shipped bundle layers (dsh-base + dsh-web-app resolved from the app installation) through the boot's patch algorithm to assert the win32 danger-full-access roster and the base-only-profile warning; `packages/bundle/base/tests/base.spec.ts` pins the shipped Windows patch file shape (disables, inserts, and the absent approval service). - Keyless: a win32 `dsh --profile --dump-config` shows the pwsh rows with `windows.cordis.patch.yml` provenance and the bash rows disabled; the POSIX dump (CI Linux) is unchanged. - The real-composition smoke boots the web profile on win32 with the pwsh stack mounted (the exact roster this note describes). diff --git a/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.zh.md b/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.zh.md index 201e5dd2c7..41324afccd 100644 --- a/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.zh.md +++ b/.agents/notes/implemented/feature/2026-08-01-windows-pwsh-default.zh.md @@ -39,6 +39,6 @@ harness 交付的执行画像在每个平台都是 bash 优先。Windows 主机 ## 验证 -- 单元:`apps/cli/tests/windows-shell.spec.ts` 固定 win32 默认、自定义 profile 跳过与缺文件失败,平台注入;`packages/bundle/base/tests/base.spec.ts` 固定交付的 Windows 清单(禁用、插入与缺席的 approval 服务)。 +- 单元:`apps/cli/tests/windows-shell.spec.ts` 以平台注入固定 win32 默认、自定义 profile 跳过与缺文件失败,并通过启动所用的 patch 算法组合真实交付的 bundle 层(从应用安装解析的 dsh-base + dsh-web-app)断言 win32 danger-full-access 清单与 base-only profile 警告;`packages/bundle/base/tests/base.spec.ts` 固定交付的 Windows patch 文件形状(禁用、插入与缺席的 approval 服务)。 - Keyless:win32 上的 `dsh --profile --dump-config` 显示带 `windows.cordis.patch.yml` 出处的 pwsh 行、被禁用的 bash 行;POSIX 转储(CI Linux)不变。 - 真实组合冒烟在 win32 上启动 web profile,pwsh 栈挂载成功(即本笔记描述的确切清单)。 diff --git a/apps/cli/tests/windows-shell.spec.ts b/apps/cli/tests/windows-shell.spec.ts index f0cfddbaae..80ba40cc34 100644 --- a/apps/cli/tests/windows-shell.spec.ts +++ b/apps/cli/tests/windows-shell.spec.ts @@ -2,7 +2,9 @@ import { afterEach, describe, expect, it } from 'vitest' import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' +import { fileURLToPath } from 'node:url' import type { ProfileLayer } from '@deepseek-ai/dsh-app-boot' +import { composeEntries, initProfile, loadProfile, PROFILES_DIR } from '@deepseek-ai/dsh-app-boot' import { BASE_BUNDLE, resolveWindowsShellLayer, @@ -62,3 +64,61 @@ describe('resolveWindowsShellLayer', () => { .toThrow(/@deepseek-ai\/dsh-base ships no windows\.cordis\.patch\.yml/) }) }) + +describe('the shipped Windows composition (real bundle layers)', () => { + let home: string + afterEach(() => { if (home !== undefined) rmSync(home, { recursive: true, force: true }) }) + // The app installation anchor, mirroring profile-boot.ts: the bundle layers + // resolve from the REAL dsh-base/dsh-web-app packages through it, so this + // suite composes the shipped patch files, not test fixtures. + const anchor = fileURLToPath(new URL('../package.json', import.meta.url)) + + it('composes the win32 danger-full-access roster through the real patch layers', () => { + home = mkdtempSync(join(tmpdir(), 'dsh-windows-home-')) + initProfile(join(home, PROFILES_DIR, 'web'), ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app']) + const profile = loadProfile('dsh', 'web', anchor, home) + const warnings: string[] = [] + const win32 = resolveWindowsShellLayer('win32', profile.layers, 'dsh') + expect(win32).toBeDefined() + const rows = composeEntries( + [...profile.layers.map(layer => layer.patches), win32!.patches], + message => warnings.push(message), + ) + const byId = new Map(rows.map(row => [row.id, row])) + for (const id of ['bash-sandbox', 'tool-bash', 'permission', 'ui-permission', + 'sandbox', 'sandbox-policy', 'fs-sandbox', 'approval']) { + expect(byId.get(id)?.disabled, `row ${id}`).toBe(true) + } + for (const id of ['pwsh-local', 'tool-pwsh', 'fs-local']) { + expect(byId.has(id), `inserted row ${id}`).toBe(true) + } + // The web-app layer provides ui-permission, so the full web profile + // composes without any no-match warning. + expect(warnings).toEqual([]) + }) + + it('leaves POSIX untouched and base-only profiles warned but harmless', () => { + home = mkdtempSync(join(tmpdir(), 'dsh-windows-home-')) + initProfile(join(home, PROFILES_DIR, 'web'), ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app']) + const profile = loadProfile('dsh', 'web', anchor, home) + // POSIX: no platform layer, the bash stack stays enabled. + const posixRows = composeEntries(profile.layers.map(layer => layer.patches)) + const posixById = new Map(posixRows.map(row => [row.id, row])) + expect(posixById.get('bash-sandbox')?.disabled).not.toBe(true) + expect(posixById.has('pwsh-local')).toBe(false) + + // A base-only custom profile (the DEFAULT_PROFILE_BUNDLES template): + // ui-permission has no row to patch, so the shipped layer warns once per + // composition — never fails — exactly as its header comment documents. + initProfile(join(home, PROFILES_DIR, 'base-only'), ['@deepseek-ai/dsh-base']) + const baseOnly = loadProfile('dsh', 'base-only', anchor, home) + const baseWarnings: string[] = [] + const win32 = resolveWindowsShellLayer('win32', baseOnly.layers, 'dsh') + expect(win32).toBeDefined() + composeEntries( + [...baseOnly.layers.map(layer => layer.patches), win32!.patches], + message => baseWarnings.push(message), + ) + expect(baseWarnings.some(message => message.includes('ui-permission'))).toBe(true) + }) +})