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
4 changes: 4 additions & 0 deletions docs/reference/dsh-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ Git Client 从 primitives 的公开入口复用 `DiffBlock`,不导入官方 `u

候选验证于 2026-10-17 在开发机完成。`DSH_COMPAT_MODE=warn` 下分别以 `0.2.1-alpha.5`、`0.2.1-rc.1`、`0.2.1-rc.2` 三条车道安装,`pnpm compat:check`、`pnpm test`(299)、`pnpm docs:check`、`pnpm build` 与 `pnpm test:artifact` 全部通过;晋级清单写入后在 `0.2.1-rc.2` 安装树上以正式门禁重跑 `pnpm compat:check` 通过,开发 pin 与锁文件恢复 `0.2.0-rc.2`。

## Session 日志的回读边界(降级不可读)

宿主持久化读路径拒绝解释包含未知必需事件的日志:`dsh-session-persistence` 对不在 `KNOWN_SESSION_EVENT_TYPES` 且未标 `ignorable` 的事件直接拒绝。`0.2.1-alpha.3` 起写入方新增 `job/registered` / `job/settled` 两个必需事件(后台作业登记,agent 的 bash 调用即产生);因此凡在 ≥ `0.2.1-alpha.3` 宿主上运行过这类作业的会话,回到 `0.2.1-alpha.2` 及更早版本(含 `0.2.0-rc.2`)打开时,历史加载以 `SESSION_QUERY_PERSISTENCE_FAILED` 失败。这是上游刻意的前向兼容防护,与本插件的支持声明无关——插件不读 Session 日志,宿主该行为不受插件影响。症状与解法见 [troubleshooting 条目](../troubleshooting/session-log-newer-harness.md)。

## 静态一致性门禁

上游 DSH 包把同级依赖写成 caret,例如 `^0.1.5-rc.2`。这个范围接受同一 core 上更后的 prerelease,pnpm 在 `autoInstallPeers` 下会装成 registry 里当前最高的匹配版本。只钉住根清单时,锁文件仍会混入 `0.1.5-rc.3`、`0.1.7-alpha.1` 或更旧的 `0.1.1-rc.2`。`.pnpmfile.cjs` 在每次安装时把传递依赖、可选依赖和 peer 里的全部 `@deepseek-ai/dsh-*` 改成当前精确版本。目标版本优先取 `DSH_COMPAT_VERSION`,否则取根 devDependency 的唯一 pin。根清单自己的 devDependency pin 和多版本 peer OR 范围保持不变。
Expand Down
1 change: 1 addition & 0 deletions docs/troubleshooting/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ Holds **recurring problems, or problems whose diagnosis was expensive**: symptom
- [`minimum-release-age-dev-pin.en.md`](minimum-release-age-dev-pin.en.md) — Main CI frozen install rejects a development pin younger than pnpm's 24-hour release-age window; replace `minimumReleaseAgeExclude` with that pin only (pnpm honors the first version per package name).
- [`windows-desktop-console-flood.md`](windows-desktop-console-flood.md) — Windows desktop terminal-window flood with a blank Git UI: the host's `CreateProcessW` omits `CREATE_NO_WINDOW`, which multiplies focus events, while the client refresh neither coalesces nor lets overlapping rounds land. (Chinese canonical.)
- [`worktree-endpoints-linux-ci-timeout.md`](worktree-endpoints-linux-ci-timeout.md) — Intermittent `worktree-endpoints` timeouts on the compatibility matrix's Linux lanes: `dsh-subprocess-local` blocks on a user-systemd probe per spawn with a success-only cache, giving an unbounded tail; the spec now injects a direct `child_process` fake subprocess service. (Chinese canonical.)
- [`session-log-newer-harness.en.md`](session-log-newer-harness.en.md) — Older hosts fail to open sessions written by newer harnesses with "history load failed": `0.2.1-alpha.3` began writing required `job/registered` / `job/settled` events, and earlier persistence readers refuse the whole log on an unknown non-ignorable event — an upstream forward-compat guard, not a plugin defect.
1 change: 1 addition & 0 deletions docs/troubleshooting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
- [`minimum-release-age-dev-pin.md`](minimum-release-age-dev-pin.md) —— 主 CI 冻结安装因 pnpm 24 小时发布冷却拒绝未满一天的开发 pin;用当前 pin 整表替换 `minimumReleaseAgeExclude`(同名包只认第一条版本)。
- [`windows-desktop-console-flood.md`](windows-desktop-console-flood.md) —— Windows 桌面端终端窗口风暴与 Git 界面空白:宿主 `CreateProcessW` 缺 `CREATE_NO_WINDOW` 放大焦点事件,客户端刷新无合并且轮次互相判旧。
- [`worktree-endpoints-linux-ci-timeout.md`](worktree-endpoints-linux-ci-timeout.md) —— 兼容矩阵 Linux 车道 `worktree-endpoints` 用例间歇性超时:`dsh-subprocess-local` 每次 spawn 阻塞探测用户 systemd 且只锁正向缓存,尾部无界;该 spec 改为注入直起 `child_process` 的 fake subprocess 服务。
- [`session-log-newer-harness.md`](session-log-newer-harness.md) —— 旧宿主打开新版写入的会话报「历史加载失败」:`0.2.1-alpha.3` 起写入 `job/registered` / `job/settled` 必需事件,更早 harness 的持久化读路径按未知非 ignorable 事件整段拒读,属上游前向兼容防护而非插件缺陷。
27 changes: 27 additions & 0 deletions docs/troubleshooting/session-log-newer-harness.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Older hosts refuse session logs written by newer harnesses (`job/registered`)

## Symptom

- On DSH `0.2.0-rc.2` (or any host older than `0.2.1-alpha.3`), opening an existing session shows "history load failed" with `failed to observe session "...": session "..." contains event type "job/registered" (seq N) unknown to this harness and not marked ignorable; refusing to interpret the log — it was likely written by a newer harness`, tagged `(gateway/internal)`.
- Sessions created on the same host, and older logs without `job/*` events, are unaffected; plugin features (Changes / Graph / Worktrees / commit message generation) are unaffected.

## Root cause

Starting with `0.2.1-alpha.3`, the upstream session event vocabulary gained `job/registered` and `job/settled` (written by `dsh-commands`, `dsh-subagent`, `dsh-client-file-upload`, etc. when registering background jobs; an agent bash tool call produces a `kind:"bash"` job event). They are written as **required** events: the envelope carries no `ignorable: true`, because job state participates in session reconstruction and silently dropping it would rebuild a wrong session.

On the read path, `dsh-session-persistence`'s `validateStoredEvents` checks every event against `KNOWN_SESSION_EVENT_TYPES`; an unknown type without `ignorable` throws `SessionFormatUnsupportedError`. `dsh-session-query.observeSession` wraps it as `SESSION_QUERY_PERSISTENCE_FAILED` and returns it to the client history stream through the gateway. Neither `0.2.0-rc.2` nor `0.2.1-alpha.1` / `alpha.2` knows `job/*`, so the whole log is refused.

The log format version is not the blocker: both sides use `SESSION_FORMAT_VERSION` 4; the version check passes and the failure happens during event validation.

Unrelated to this plugin: the plugin never calls `observeSession` / `sessionQuery` and never parses session logs; its `ctx.subprocess` git calls emit no `job/*` events; the `working-directory/change` event it can trigger is a known type on both version lines. The error fires inside the client's history stream when a session is opened — host behavior independent of whether this plugin is installed. The plugin's declared `0.2.0-rc.2` support remains valid.

## Fix (verified)

- Open those sessions on a host ≥ `0.2.1-alpha.3` (the version that wrote them); staying on `0.2.0-rc.2` leaves logs containing `job/*` events permanently unreadable — an intentional upstream forward-compatibility guard, not a plugin defect.
- Do not patch logs or intercept this error in the plugin: the read path lives in the host persistence layer, unreachable from the plugin, and treating a required event as `ignorable` would corrupt job-state reconstruction.
- If an upstream fix is wanted, the options are backporting the `job/*` event types onto the `0.2.0` line or having the writer mark them `ignorable` (a semantic-safety call for upstream); both are out of this repository's scope.

## Related

- Evidence: `validateStoredEvents` in `dsh-session-persistence@0.2.0-rc.2`; `KNOWN_SESSION_EVENT_TYPES` diffs across `dsh-session` versions showing `job/*` entering at `0.2.1-alpha.3`; a real `session.v4.jsonl.zstd` whose seq 24 is a `job/registered` (`kind:"bash"`) without `ignorable`.
- Compatibility contract: [`../reference/dsh-compatibility.md`](../reference/dsh-compatibility.md).
27 changes: 27 additions & 0 deletions docs/troubleshooting/session-log-newer-harness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 旧宿主拒绝读取新版写入的 Session 日志(`job/registered`)

## 症状

- 在 DSH `0.2.0-rc.2`(或任何早于 `0.2.1-alpha.3` 的宿主)上打开既有会话,历史区域报「历史加载失败」,详情为 `failed to observe session "...": session "..." contains event type "job/registered" (seq N) unknown to this harness and not marked ignorable; refusing to interpret the log — it was likely written by a newer harness`,错误标记 `(gateway/internal)`。
- 同一宿主上新建的会话、以及不含 `job/*` 事件的旧会话不受影响;插件自身功能(Changes / Graph / Worktrees / 提交信息生成)不受影响。

## 根因

`0.2.1-alpha.3` 起,上游 Session 事件词表新增 `job/registered` 与 `job/settled`(`dsh-commands`、`dsh-subagent`、`dsh-client-file-upload` 等在登记后台作业时写入;agent 的 bash 工具调用即产生 `kind:"bash"` 的 job 事件)。这些事件按**必需事件**写入:envelope 上没有 `ignorable: true`——作业状态参与会话重建,静默丢弃会重建出错误的会话。

读路径上,`dsh-session-persistence` 的 `validateStoredEvents` 逐条检查 `KNOWN_SESSION_EVENT_TYPES`:遇到未知类型且未标 `ignorable` 的事件即抛 `SessionFormatUnsupportedError`;`dsh-session-query.observeSession` 把它包装成 `SESSION_QUERY_PERSISTENCE_FAILED`,经 gateway 返回客户端历史流。`0.2.0-rc.2` 与 `0.2.1-alpha.1` / `alpha.2` 的词表都不含 `job/*`,因此整段日志被拒绝解释。

日志格式版本不是原因:两侧 `SESSION_FORMAT_VERSION` 均为 4,版本检查通过后才在事件校验处失败。

与本插件无关:插件不调用 `observeSession` / `sessionQuery`,不解析 Session 日志;`ctx.subprocess` 的 git 调用不产生 `job/*` 事件;插件触发的 `working-directory/change` 是两个版本线共有的已知类型。报错发生在客户端打开会话时的历史流加载阶段,属于宿主行为,与是否安装本插件无关;插件对 `0.2.0-rc.2` 的支持声明保持有效。

## 解法(已验证)

- 用写入这些日志的宿主版本(≥ `0.2.1-alpha.3`)打开对应会话;若留在 `0.2.0-rc.2`,含 `job/*` 事件的会话将持续不可读——这是上游刻意的前向兼容防护,不是本插件缺陷。
- 不要在插件侧修补日志或拦截该错误:读路径在宿主持久化层,插件不可达;把必需事件当作 `ignorable` 跳过会破坏作业状态重建。
- 若需要上游修复,方向是向 `0.2.0` 线回补 `job/*` 事件类型,或由上游裁决是否将这类事件标记为 `ignorable`;两者都超出本仓库范围。

## 关联

- 验证依据:`dsh-session-persistence@0.2.0-rc.2` 的 `validateStoredEvents`;`dsh-session` 各版本 `KNOWN_SESSION_EVENT_TYPES` 差分显示 `job/*` 自 `0.2.1-alpha.3` 进入词表;实机日志 `session.v4.jsonl.zstd` 中 seq 24 为未标 `ignorable` 的 `job/registered`(`kind:"bash"`)。
- 兼容契约:[`../reference/dsh-compatibility.md`](../reference/dsh-compatibility.md)。
Loading