Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,17 +428,24 @@ These exist in the schema but render no control — each is a literal, a marker,

**Verified on DeepSeek Harness `0.2.1` — both the `alpha` and `rc` channels (Node 24+)**

The harness publishes two **channels**, `alpha` and `rc`, and they are parallel tracks rather than a version ordering: semver sorts `0.2.1-rc.2` _above_ `0.2.1-alpha.5`. The plugin targets two lines on both channels: **`0.2.1`**, the `rc` channel's latest, and the **`0.2.2`** prereleases the `alpha` channel now carries (the test install runs `0.2.2-alpha.2`). The `@deepseek-ai/dsh-typert-protocol` peer range says so exactly:
The harness publishes two **channels**, `alpha` and `rc`, and they are parallel tracks rather than a version ordering: semver sorts `0.2.1-rc.2` _above_ `0.2.1-alpha.5`. The plugin targets two lines on both channels: **`0.2.1`**, whose `rc` builds `dsh@latest` still carries, and the **`0.2.2`** prereleases on both channels: `alpha` is at `0.2.2-alpha.3`, and `rc` opened the line with `0.2.2-rc.1` (published under the `next` dist-tag; the test install runs `0.2.2-alpha.2`). The `@deepseek-ai/dsh-typert-protocol` peer range says so exactly:

```
>=0.2.1-alpha.1 <0.3.0 || >=0.2.2-0 <0.3.0
```

The first clause covers the `0.2.1` line on both channels: its lower bound (`alpha.1`) sorts below every `rc` of the same line, so rc needs no separate entry. The second is **not** redundant — semver excludes prereleases unless a comparator in the same `major.minor.patch` tuple carries one, so `<0.3.0` alone would silently drop every `0.2.2-*` build, alpha and rc alike. Both clauses stop at `0.3.0`, which leaves the next line a separate decision rather than a silent widening.

The optional `@deepseek-ai/cordis` peer follows the typert range. Each line moved the host core: the `0.2.1` builds peer `~4.0.5` (or a `4.0.5` prerelease), the `0.2.2` alphas a `4.0.6` prerelease, and `0.2.2-rc.1` `~4.1.0`. A fresh install takes the cordis the **newest** admitted typert build peers on, so a cordis peer that misses it fails with `ERESOLVE`; one that misses an older line's cordis fails beside a host already installed on that line. The peer therefore admits every one of them and stops at the next tuple:

```
~4.0.4 || >=4.0.5-0 <4.1.0 || >=4.0.6-0 <4.1.0 || ~4.1.0
```

| Surface | Target |
| :-- | :-- |
| **Harness line** | **`0.2.1`** and the **`0.2.2`** prereleases, `alpha` and `rc` channels alike (peer: the range above) |
| **Host core** | `@deepseek-ai/cordis` `4.0.4` up to `4.1.x`, including the `4.0.5` and `4.0.6` prereleases (optional peer: the range above) |
| **Plugin package** | [`dsh-opencode-patch`](https://www.npmjs.com/package/dsh-opencode-patch) |
| **Host profile** | DSH Web profile (`patchReload: live`) |
| **Routes claimed** | `opencode`, `opencode-go`, `opencode-responses`, `opencode-anthropic` |
Expand Down
9 changes: 8 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,17 +422,24 @@ Zen 路线上不出现任何 Go 的数字:根本不画圆环(没有可量的

**已在 DeepSeek Harness `0.2.1` 上验证 —— `alpha` 与 `rc` 两个通道(Node 24+)**

Harness 按 `alpha` 与 `rc` 两个**通道**发布,它们是平行的发布线而非版本先后:semver 中 `0.2.1-rc.2` 排序在 `0.2.1-alpha.5` **之上**。本插件在两个通道上都针对两条线:**`0.2.1`**(`rc` 通道的最新版本),以及 `alpha` 通道目前发布的 **`0.2.2`** 预发布版本(测试安装运行的是 `0.2.2-alpha.2`)。`@deepseek-ai/dsh-typert-protocol` 的 peer 范围正是这么写的:
Harness 按 `alpha` 与 `rc` 两个**通道**发布,它们是平行的发布线而非版本先后:semver 中 `0.2.1-rc.2` 排序在 `0.2.1-alpha.5` **之上**。本插件在两个通道上都针对两条线:**`0.2.1`**(`dsh@latest` 仍停留在它的 `rc` 构建上),以及两个通道上的 **`0.2.2`** 预发布版本:`alpha` 已到 `0.2.2-alpha.3`,`rc` 以 `0.2.2-rc.1` 开启了这条线(发布在 `next` dist-tag 下;测试安装运行的是 `0.2.2-alpha.2`)。`@deepseek-ai/dsh-typert-protocol` 的 peer 范围正是这么写的:

```
>=0.2.1-alpha.1 <0.3.0 || >=0.2.2-0 <0.3.0
```

第一个子句覆盖两个通道上的 `0.2.1` 整条线:下界 `alpha.1` 排序低于同线的所有 `rc`,因此无需单独枚举 rc。第二个子句**并不多余**——除非同一 `major.minor.patch` 的某个比较符带有预发布号,semver 会把预发布版本排除在外,所以只写 `<0.3.0` 会悄悄丢掉所有 `0.2.2-*` 构建,alpha 与 rc 都一样。两个子句都止于 `0.3.0`,下一条线因此是一个单独的决定,而不是悄悄放宽。

可选的 `@deepseek-ai/cordis` peer 跟随 typert 的范围。每条线都推进了宿主内核:`0.2.1` 的构建依赖 `~4.0.5`(或 `4.0.5` 的预发布版本),`0.2.2` 的 alpha 依赖 `4.0.6` 的预发布版本,`0.2.2-rc.1` 依赖 `~4.1.0`。全新安装会采用**最新**被接纳的 typert 构建所依赖的那个 cordis,所以 cordis peer 漏掉它就会以 `ERESOLVE` 失败;漏掉较早某条线的 cordis,则会在已安装该线宿主的环境旁安装失败。因此这个 peer 接纳其中每一个,并止于下一个版本元组:

```
~4.0.4 || >=4.0.5-0 <4.1.0 || >=4.0.6-0 <4.1.0 || ~4.1.0
```

| 层面 | 目标 |
| :-- | :-- |
| **Harness 版本线** | **`0.2.1`** 与 **`0.2.2`** 预发布版本,`alpha` 与 `rc` 通道均可(peer:见上方范围) |
| **宿主内核** | `@deepseek-ai/cordis` 从 `4.0.4` 到 `4.1.x`,包括 `4.0.5` 与 `4.0.6` 的预发布版本(可选 peer:见上方范围) |
| **插件包** | [`dsh-opencode-patch`](https://www.npmjs.com/package/dsh-opencode-patch) |
| **宿主 profile** | DSH Web profile(`patchReload: live`) |
| **声明的路由** | `opencode`、`opencode-go`、`opencode-responses`、`opencode-anthropic` |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"yaml": "2.9.1"
},
"peerDependencies": {
"@deepseek-ai/cordis": "~4.0.4 || >=4.0.5-0 <4.1.0 || >=4.0.6-0 <4.1.0",
"@deepseek-ai/cordis": "~4.0.4 || >=4.0.5-0 <4.1.0 || >=4.0.6-0 <4.1.0 || ~4.1.0",
"@deepseek-ai/dsh-typert-protocol": ">=0.2.1-alpha.1 <0.3.0 || >=0.2.2-0 <0.3.0"
},
"peerDependenciesMeta": {
Expand Down
108 changes: 108 additions & 0 deletions test/harness-channel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ describe("harness channel alignment", () => {
"0.2.1",
// 0.2.2 line, both channels, and its stable release.
"0.2.2-alpha.2",
"0.2.2-alpha.3",
"0.2.2-rc.1",
"0.2.2",
];
Expand All @@ -125,4 +126,111 @@ describe("harness channel alignment", () => {
).toBe(false);
}
});

it("the cordis peer admits every cordis an admitted typert build can install", () => {
// npm resolves the typert peer to the NEWEST build the range admits (the
// `latest` dist-tag, 0.1.0-rc.6, is outside it), then installs the newest
// PUBLISHED cordis that build peers on. If the plugin's own cordis peer
// excludes it, a fresh install fails with ERESOLVE — which is what
// happened the morning 0.2.2-rc.1 was published with `~4.1.0` while this
// peer stopped below 4.1.0. Beside a host already installed on an older
// line, the cordis is whichever published one that line's build admits.
// So every published cordis an admitted build admits must satisfy the
// cordis peer.
//
// Checked against concrete PUBLISHED versions, never by range overlap:
// `semver.intersects` would call `~4.0.4 || ~4.1.0` compatible with
// `~4.0.6-alpha.2` although no 4.0.6 prerelease satisfies it, because the
// prerelease-tuple rule npm resolves under is not part of an overlap.
//
// Both tables are recorded from the registry on 2026-11-04 (`npm view
// @deepseek-ai/dsh-typert-protocol@<v> peerDependencies` for every build
// on the 0.2.1 and 0.2.2 lines; `npm view @deepseek-ai/cordis versions`).
// A new typert build joins the first with its cordis range, and a new
// cordis release joins the second, when they are published.
const CORDIS_PUBLISHED = [
"4.0.4",
"4.0.5-alpha.1",
"4.0.5-alpha.2",
"4.0.5",
"4.0.6-alpha.1",
"4.0.6-alpha.2",
"4.1.0",
];
const CORDIS_PEER_OF_TYPERT: Record<string, string> = {
"0.2.1-alpha.1": "~4.0.5-alpha.1",
"0.2.1-alpha.2": "~4.0.5-alpha.1",
"0.2.1-alpha.3": "~4.0.5-alpha.2",
"0.2.1-alpha.4": "~4.0.5-alpha.2",
"0.2.1-alpha.5": "~4.0.5-alpha.2",
"0.2.1-rc.1": "~4.0.5",
"0.2.1-rc.2": "~4.0.5",
"0.2.1-rc.3": "~4.0.5",
"0.2.1-rc.4": "~4.0.5",
"0.2.1-rc.5": "~4.0.5",
"0.2.2-alpha.2": "~4.0.6-alpha.1",
"0.2.2-alpha.3": "~4.0.6-alpha.2",
"0.2.2-rc.1": "~4.1.0",
};
const peers = manifest().peerDependencies ?? {};
const typertPeer = peers["@deepseek-ai/dsh-typert-protocol"] ?? "";
const cordisPeer = peers["@deepseek-ai/cordis"] ?? "";
expect(cordisPeer).not.toBe("");

// Each `typert@build -> cordis@version` pair npm could install that the
// given cordis peer would refuse.
const refused = (peer: string): string[] =>
Object.entries(CORDIS_PEER_OF_TYPERT).flatMap(([build, range]) =>
CORDIS_PUBLISHED.filter(
(version) =>
semver.satisfies(version, range) && !semver.satisfies(version, peer)
).map((version) => `typert@${build} -> cordis@${version}`)
);

for (const [build, range] of Object.entries(CORDIS_PEER_OF_TYPERT)) {
// The table is about builds the typert peer admits, each of which has a
// published cordis to install; otherwise a row would check nothing.
expect(
semver.satisfies(build, typertPeer),
`typert ${build} is admitted by ${typertPeer}`
).toBe(true);
expect(
semver.maxSatisfying(CORDIS_PUBLISHED, range),
`typert ${build} peers cordis ${range}, which has a published release`
).not.toBeNull();
}
expect(refused(cordisPeer)).toEqual([]);

// Known answers, so the check above is shown to bite: the peer this
// repository shipped before 0.2.2-rc.1 refuses that build's cordis, and a
// peer without the 4.0.6 clause refuses the alpha line's prereleases
// (semver admits a prerelease only through a comparator in its own tuple).
expect(refused("~4.0.4 || >=4.0.5-0 <4.1.0 || >=4.0.6-0 <4.1.0")).toEqual([
"typert@0.2.2-rc.1 -> cordis@4.1.0",
]);
expect(refused("~4.0.4 || >=4.0.5-0 <4.1.0 || ~4.1.0")).toEqual(
expect.arrayContaining(["typert@0.2.2-alpha.3 -> cordis@4.0.6-alpha.2"])
);

// The concrete cordis each channel ships is admitted, and the range still
// stops at the next tuple: a 4.1.x prerelease or 4.2 is a reviewed
// widening, not a silent one.
for (const version of [
"4.0.5",
"4.0.6-alpha.1",
"4.0.6-alpha.2",
"4.1.0",
]) {
expect(
semver.satisfies(version, cordisPeer),
`cordis ${version} is admitted`
).toBe(true);
}
for (const version of ["4.1.1-alpha.1", "4.2.0", "5.0.0"]) {
expect(
semver.satisfies(version, cordisPeer),
`cordis ${version} is outside the peer`
).toBe(false);
}
});
});
Loading