diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27bb978..13ba829 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,9 @@ jobs: git remote add origin https://github.com/deepseek-ai/deepseek-harness.git git fetch --depth 1 -q origin \ refs/tags/dsh-v0.1.1-rc.2:refs/tags/dsh-v0.1.1-rc.2 \ - refs/tags/dsh-v0.1.2-alpha.2:refs/tags/dsh-v0.1.2-alpha.2 + refs/tags/dsh-v0.1.2-alpha.2:refs/tags/dsh-v0.1.2-alpha.2 \ + refs/tags/dsh-v0.1.2-alpha.5:refs/tags/dsh-v0.1.2-alpha.5 \ + refs/tags/dsh-v0.1.2-rc.1:refs/tags/dsh-v0.1.2-rc.1 - name: Test run: DSH_REPO="$PWD/.tmp/dsh-repo" pnpm test diff --git a/README.md b/README.md index f61fc94..d1c5136 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ In **Settings → Plugins → Plugin configuration**, the **Context** card holds ## Good to know - **Estimates vs actuals** — category figures use dsh's own fixed-density heuristic (the same one as its built-in token meter); the pinned trend details and Token/Timing rings show provider-reported actuals next to them. -- **Compatibility** — works on `@deepseek-ai/dsh` **0.1.1-rc2+** and **0.1.2-alpha2+**. +- **Compatibility** — works on `@deepseek-ai/dsh` **0.1.1-rc2+** and **0.1.2-alpha2+**. The per-release matrix and how it is verified: [docs/compatibility.md](docs/compatibility.md). - **I18n** — UI in English and 简体中文. ## Like it? diff --git a/docs/compatibility.md b/docs/compatibility.md new file mode 100644 index 0000000..7759769 --- /dev/null +++ b/docs/compatibility.md @@ -0,0 +1,28 @@ +# Compatibility + +dsh-context declares per-release compatibility with `@deepseek-ai/dsh` in its package manifest (`dsh.compatibility.dshReleases`). This page records what is actually verified for each declared release, and how. + +Last verified: **2026-09-03** (plugin `dsh-context@0.41.2` source tree). + +## Supported dsh releases + +| dsh release | npm channel | Declared | Automated seam matrix | Disposable-profile install / uninstall | +| --- | --- | --- | --- | --- | +| `0.1.1-rc.2` | `latest` (stable) | compatible | ✅ baseline `v0.1.1-rc.2` | ✅ install OK → 1 composed row → uninstall OK → 0 rows | +| `0.1.2-alpha.2` | — | compatible | ✅ baseline `v0.1.2-alpha.2` | not run (covered by the seam matrix at its tag) | +| `0.1.2-alpha.3` | — | compatible | same generation, bracketed by the alpha.2 / alpha.5 baselines | ✅ install OK → 1 composed row → uninstall OK → 0 rows | +| `0.1.2-alpha.4` | — | compatible | same generation, bracketed by the alpha.2 / alpha.5 baselines | ✅ install OK → 1 composed row → uninstall OK → 0 rows | +| `0.1.2-alpha.5` | `alpha` (preview) | compatible | ✅ baseline `v0.1.2-alpha.5` | ✅ install OK → 1 composed row → uninstall OK → 0 rows | +| `0.1.2-rc.1` | `next` | compatible | ✅ baseline `v0.1.2-rc.1` | ✅ install OK → 1 composed row → uninstall OK → 0 rows | + +## What each check means + +- **Automated seam matrix** — part of this repository's `pnpm test` (the `compat` vitest project). For every baseline tag it stages the harness's REAL sources at that tag, boots the plugin's built host entry into that tag's actual `SessionProjectionRegistry` on the cordis release the line vendors, and probes the tag's client seams (slots, finalized-nodes seat, image loader, history face/envelope, markdown chrome, platform module table, durable-event vocabulary, settings namespace). Definitions live in `tests/baselines.ts`; the release workflow fetches the pinned baseline tags before testing. +- **Disposable-profile install / uninstall** — for each release, that exact `dsh` CLI version was installed from npm into a temporary `DSH_HOME` (the real `~/.dsh` is never touched), then: + 1. `dsh plugin --profile add dsh-context` — install OK; + 2. `dsh --profile --dump-config` — the bundle's `- id: dsh-context` row composes into the effective configuration (exactly 1 row); + 3. `dsh plugin --profile remove dsh-context` — uninstall OK, dump-config back to 0 rows. + +## Scope + +These checks prove source-level seam compatibility and disposable-profile install/start-composition/uninstall per release. They are not a claim of full web-app runtime acceptance on a real Profile — visible UI behavior depends on the harness generation and the browser half, which the seam matrix approximates from the tag's sources. diff --git a/tests/baselines.ts b/tests/baselines.ts index 809b827..e513ea7 100644 --- a/tests/baselines.ts +++ b/tests/baselines.ts @@ -16,7 +16,7 @@ */ /** Every dsh line this plugin must install and work on (see AGENTS.md "Compatibility"). */ -export const SUPPORTED_BASELINES = ['v0.1.1-rc.2', 'v0.1.2-alpha.2'] as const +export const SUPPORTED_BASELINES = ['v0.1.1-rc.2', 'v0.1.2-alpha.2', 'v0.1.2-alpha.5', 'v0.1.2-rc.1'] as const export type BaselineId = (typeof SUPPORTED_BASELINES)[number] @@ -73,15 +73,39 @@ export interface Baseline { tag: string /** The vendored @deepseek-ai/cordis release that harness line ships. */ cordis: string + /** + * The @deepseek-ai/dsh-session release that line vendors. The staged + * session-projection sources import runtime values from it (SessionLogOffset / + * SessionSeq, non-type imports since 0.1.2-alpha.3), so the compat driver must + * resolve the specifier to the tag's own generation. + */ + session: string host: HostSeam client: ClientSeam } +/** The client faces shared by the whole 0.1.2 generation (verified identical alpha.2 through rc.1). */ +const CLIENT_0_1_2: ClientSeam = { + chatNodesSeat: 'useChat', + imageFace: { service: 'uiConversation', method: 'imageUrl' }, + historyFace: 'remote.session.page', + historyEnvelope: 'clientResult', + historyRowsField: 'records', + markdownChrome: 'labels', + platformModules: [ + 'react', 'react/jsx-runtime', 'react-dom', 'react-dom/client', '@deepseek-ai/cordis', + '@deepseek-ai/dsh-client-store', + '@deepseek-ai/dsh-client-ui-slots', + '@deepseek-ai/dsh-client-ui-primitives', + ], +} + export const BASELINES: readonly Baseline[] = [ { id: 'v0.1.1-rc.2', tag: 'dsh-v0.1.1-rc.2', cordis: '4.0.1', + session: '0.1.1-rc.2', host: { initHeader: false, seedsOnCreate: false }, client: { chatNodesSeat: 'useSession', @@ -101,20 +125,28 @@ export const BASELINES: readonly Baseline[] = [ id: 'v0.1.2-alpha.2', tag: 'dsh-v0.1.2-alpha.2', cordis: '4.0.2', + session: '0.1.2-alpha.2', host: { initHeader: true, seedsOnCreate: true }, - client: { - chatNodesSeat: 'useChat', - imageFace: { service: 'uiConversation', method: 'imageUrl' }, - historyFace: 'remote.session.page', - historyEnvelope: 'clientResult', - historyRowsField: 'records', - markdownChrome: 'labels', - platformModules: [ - 'react', 'react/jsx-runtime', 'react-dom', 'react-dom/client', '@deepseek-ai/cordis', - '@deepseek-ai/dsh-client-store', - '@deepseek-ai/dsh-client-ui-slots', - '@deepseek-ai/dsh-client-ui-primitives', - ], - }, + client: CLIENT_0_1_2, + }, + { + // The preview window's head — the DSH STORE latest-three compatibility + // window currently ends here (alpha.3/alpha.4 sit between the probed + // alpha.2 and alpha.5 tags with no seam drift). + id: 'v0.1.2-alpha.5', + tag: 'dsh-v0.1.2-alpha.5', + cordis: '4.0.2', + session: '0.1.2-alpha.5', + host: { initHeader: true, seedsOnCreate: true }, + client: CLIENT_0_1_2, + }, + { + // The `next`-channel release candidate — the newest declared dsh release. + id: 'v0.1.2-rc.1', + tag: 'dsh-v0.1.2-rc.1', + cordis: '4.0.2', + session: '0.1.2-rc.1', + host: { initHeader: true, seedsOnCreate: true }, + client: CLIENT_0_1_2, }, ] diff --git a/tests/compat/staging.ts b/tests/compat/staging.ts index d05b66f..62807f5 100644 --- a/tests/compat/staging.ts +++ b/tests/compat/staging.ts @@ -62,17 +62,27 @@ export function stageFile(baseline: Baseline, tagPath: string, localName: string return file } -/** The tag's vendored cordis, installed once per baseline (npm-cached afterwards). */ -export function ensureCordis(baseline: Baseline): void { +/** The host packages the tag's staged sources import at runtime, at this line's own versions. */ +function hostDepsOf(baseline: Baseline): Record { + return { + '@deepseek-ai/cordis': baseline.cordis, + '@deepseek-ai/dsh-session': baseline.session, + } +} + +/** The baseline's host deps, installed once per baseline (npm-cached afterwards). */ +export function ensureHostDeps(baseline: Baseline): void { const dir = join(STAGE, baseline.id) - if (existsSync(join(dir, 'node_modules', '@deepseek-ai', 'cordis'))) return + const deps = hostDepsOf(baseline) mkdirSync(dir, { recursive: true }) writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: `dsh-context-compat-${baseline.id}`, private: true, type: 'module', - dependencies: { '@deepseek-ai/cordis': baseline.cordis }, + dependencies: deps, }, null, 2)) + const missing = Object.keys(deps).filter(name => !existsSync(join(dir, 'node_modules', ...name.split('/')))) + if (missing.length === 0) return const run = spawnSync('npm', ['install', '--no-audit', '--no-fund', '--loglevel=error'], { cwd: dir, encoding: 'utf8' }) - if (run.status !== 0) throw new Error(`npm install cordis ${baseline.cordis} failed: ${(run.stderr ?? '').trim().slice(0, 300)}`) + if (run.status !== 0) throw new Error(`npm install ${JSON.stringify(deps)} failed: ${(run.stderr ?? '').trim().slice(0, 300)}`) } /** The namespace pattern the tag's settings module enforces (module-private on every baseline). */ @@ -140,7 +150,7 @@ process.stdout.write('DRIVER-JSON ' + JSON.stringify({ /** Stage the registry sources + the lossless-JSON probe and run the driver for one baseline. */ export function runDriver(baseline: Baseline): DriverReport { - ensureCordis(baseline) + ensureHostDeps(baseline) stageFile(baseline, 'packages/session/session-projection/src/index.ts', join('registry', 'index.ts')) stageFile(baseline, 'packages/session/session-projection/src/types.ts', join('registry', 'types.ts')) // The lossless-JSON probe the projection cache's write path uses (moved