Skip to content

[Bug][DSH] Fork loses fold inheritance without adoption diagnostics; interrupted preflight leaves child in repeated local 413 #4215

Closed

Description

@abworks-dev

Summary

A compressed long-running DSH desktop session was forked through the host UI. The child replayed thousands of raw messages without inheriting the parent's billion-context fold state, entered multi-minute emergency preflight, disconnected after approximately five minutes, and subsequent retries consistently failed locally with 413.

Related: #3011 (DSH fork adoption failure), #3025 (retroactive adoption conflict), #3978 (long preflight + client disconnect), #4079 (overflow/recovery tracking), #4082 (request-scoped attribution).

Important distinction from #3011: we did NOT find a fork adoption ... degraded, snapshot-409, or successful-adoption log for this child. A 409 root cause is NOT established here. Please investigate silent skipping of the adoption entry path as well as recovery after interrupted preflight.

Environment / version caveat

  • Windows, DSH desktop, native billion-context plugin, OpenAI Codex Responses lane.
  • Model: gpt-6.1-sol; reported window: 744000.
  • Incident proxy logs: v0.1.202.
  • Installed bundle inspected later: 0.1.203; upstream master also inspected at c87185a05a44ecccc225f96f92089e8f309fb1d9.
  • We have not recovered the incident-time native-plugin bundle; current-source suspicions below are NOT presented as a proven 0.1.202 stack trace.
  • Parent/child IDs, usernames, business content, credentials and full transcripts are deliberately omitted.

Verified evidence

  1. The DSH child session header has isSeeded: true and the correct parentSession. The host copies the source event prefix into the new session (buildForkSeed -> agents.create). Host Session really exposes .header; requestHeader() is a different, folded model-request-header API and should not replace that branch metadata.
  2. The last successful inherited model call had totalTokens: 155439.
  3. Read-only inspection of bili's persisted SQLite state found:
    • Parent: 174 blocks / 151 active at investigation time (parent had later activity; these counts are NOT claimed to be the exact branch-time counts).
    • Child: exactly 1 block / 1 active, parent_session_id = NULL, fork_point_seq = NULL, no fork-receipt metadata.
    • The child's only block was b1, created during its own emergency preflight for m02870–m02924, not an inherited parent block.
  4. Child's first proxy request: processTurn: 8359 msgs; estimated context 2242367 tokens. This is a local estimate, not upstream measured billing.
  5. No fork adoption log was found in the inspected incident log. That is an observation, not proof that a particular skip predicate fired.

Sanitized incident timeline (UTC)

2026-10-21T17:00:58.421Z child: stamped type:"message" on 785 type-less input item(s)
2026-10-21T17:01:06.364Z child: processTurn: 8359 msgs
2026-10-21T17:01:06.674Z child: context 2242367 tokens reached preflight target 744000; preflight compressing before forward
2026-10-21T17:01:16.187Z preflight: round 1/32 ... 405 viable range(s)
2026-10-21T17:04:04.973Z compress requested m02870–m02924
2026-10-21T17:04:13.935Z compressed ... 1 block(s), ~35259 tokens saved; b1 summary 19955ch
2026-10-21T17:04:21.672Z preflight: round 2/32
2026-10-21T17:05:55.332Z preflight: summarization aborted: client disconnected
2026-10-21T17:06:05.118Z child: preflight aborted ... not forwarding
2026-10-21T17:06:08.209Z persist: cold-loaded child ... 8353 ref(s), 1 block(s) (1 active)
2026-10-21T17:06:18.808Z preflight fail-fast 413 (retryable=false): payload core text alone is ~5742630 bytes ... >= ~765684 tokens vs model window 744000 ... NOT forwarded

Further retries produced the same local rejection with 5742677 / 5742724 / 5742771 bytes. No model request succeeded in this child. The byte-density figures are lower bounds, NOT exact tokenizer counts.

Current-source suspicion: attribution and fork discovery use different identity paths

In src/agent/dsh-native.ts at the master commit above:

Thus a correctly identified proxy conversation does not imply that fork-parent discovery ran. A request with a valid scoped session ID but missing/cleared ambient initiator can take the normal compression lane while skipping adoption entirely. This fits the observed absence of adoption logs, but we have not reproduced that exact scope condition in the incident.

Other relevant current-state finding: parent metadata currently has publicSnapshotTextComparable: false (snapshot ~28.5 MB). Current snapshot code would refuse opaque/multimodal comparison. We do not claim that flag had the same value at branch time or that a snapshot request/409 actually occurred.

Expected behavior

Fork a previously compressed session without unfolding its entire raw history, or explicitly stop admission with a useful, recoverable explanation when inheritance cannot be established. A failed/aborted emergency preflight should not leave the child in a retry -> immediate-413 loop with no usable recovery path.

Requested investigation / acceptance tests

  1. Carry explicit branch identity with the request scope, or resolve the scoped session ID to validated host branch metadata; do not rely solely on ambient currentInitiator() for fork discovery.
  2. Add structured diagnostics for EVERY skipped fork-adoption gate, distinguishing not-a-fork from missing attribution/session/header and protocol/snapshot rejection.
  3. Do not silently degrade a large compressed parent into an uncompressed child when that history cannot fit. Preserve the safe overflow guard, but provide an actionable recovery/adoption path.
  4. Verify retry semantics for temporarily unavailable snapshots and CHILD_CONFLICT after a first degraded request (related issues above).
  5. Test interrupted preflight: persist progress and allow a subsequent request to resume shrinking rather than repeatedly fail-fast without further progress.
  6. Regression matrix: normal fork; parent cold-load/eviction; valid request-scoped ID + absent ambient initiator; opaque/multimodal history; fork through a fold boundary; client disconnect mid-preflight and retry.

No live sessions, compression DB, host services, or business configuration were modified during this investigation. No full transcript/database is attached for privacy reasons. Please treat the missing inheritance + preflight/retry chain as confirmed and the exact adoption-entry skip as still requiring instrumentation/reproduction.

Activity

  1. ranxianglei commented on Oct 21, 2026

    @ranxianglei
    Owner

    🤖 Powered by ework · qwen3.8-27b

    (ai: vllm-qwen/qwen3.8-27b)

    查重:重复处理——本 issue 的两个侧面都归入既有家族,按先来后到并入后关闭本帖,补充信息已写入先到 issue。

    标签:bug / dsh / scope/wiring / p1(缺陷;DSH 宿主车道;主面是客户端接入层的 fork 判定接线,preflight 侧已在 #3978/scope 域内跟踪;后果 = 会话彻底不可用,按 2026-10-19 修订属 p1 不降档)。

    代码级复核(v0.1.202 逐行核对,master 对照)

    侧面一:你的怀疑方向成立,且比「currentInitiator 缺失」更结构化。 master 上 DSH native 车道有两条身份解析路径,且不同源:请求归属判定(attributionOf,src/agent/dsh-native.ts:1162)优先用 #4082 的请求作用域 sid(AsyncLocalStorage,由 dsh llm/stream 瀑布写入),ambient currentInitiator() 只是回落;而 fork 发现(forkChildOf,:1208-1220)只读 ambient currentInitiator().session,拿不到 header 元数据就静默返回 undefined。事故版本 v0.1.202 还没有 #4082(sidScope 是其后才进的),两条路径当时同源——所以当时要么是整条请求 ambient initiator 缺失(那归属也会失败、走匿名车道),要么命中了后面的某个静默门。无论哪条,maybeForkAdoptBeforeSend(v0.1.202 :1090 / master :1242)的五道前置门——toolsReady、child 发现、body 解析、side-shaped body、base URL——全部零日志零标记,这就是「事故日志里连一行 degraded 都没有」的直接原因:不是某条 409 被吞,而是根本没走到 adoption。这正对应你的验收测试 2。另外你提到的父会话 publicSnapshotTextComparable:false(~28.5MB 快照)说明即便 adoption 走到快照端点也会命中 opaque 门(#3025-C6,方向待 owner 拍板)——这条也已在 #3011 登记为 C6 的又一现场佐证。

    侧面二:数字与代码完全对得上。 413 守卫的阈值是 max(2MB, 窗口×7.5):744000×7.5=5,580,000 < 5,742,630,且 ceil(5742630/7.5)=765,684 ≥ 744000——你的日志逐字节吻合。关键顺序细节(v0.1.202 src/server.ts ~:3383-3424):preflight 返回 compressedRanges>0 后,代码先把 lastInputTokens 写成 estimate 源(:3393-3394),然后才检查 client abort 并「not forwarding」早退——所以一次被中断的 preflight 会把 estimate 计量器持久化下来;之后每次重试都满足 probeForEvidence(estimate 源 ⇒ baselineFloor=0)⇒ 原始 payload doomed 检查 ⇒ 直接终态 413,一次折叠都不尝试。首条请求能逃过守卫只是因为全新子会话 lastInputTokens=0。注意:#3978 的持久化修复(PR #4046,每轮 fold 落盘)治不了这个重试侧终态判决——那是另一个机制点(有可压 range 时不应在折叠前判死),与 floor 16 的记录一致,等 owner 定落点后一并修。

    处置

    1. 补充评论已发 [Bug] Fork of DSH branch clone: inheritance permanently abandoned by 409 SNAPSHOT_UNAVAILABLE — zero-fold clone triggers context_length_exceeded 400 on first turn | [Bug] DSH 分支克隆的 fork 继承被 409 SNAPSHOT_UNAVAILABLE 永久放弃:克隆零折叠,首轮即 context_length_exceeded … #3011(新机制子缺陷:入口判定分裂 + 五道静默门,含验收测试 1/2 转移)和 [Bug] Excessive preflight compression time on ultra-large contexts; client mid-disconnect causes repeated request forwar | [Bug] 超大上下文 preflight 压缩耗时过长,客户端中途断开导致请求反复无法转发 #3978(本事故的 413 侧独立复核证据)。
    2. 无决策依赖的部分我现在就修:给 DSH native adoption 入口的每一道跳过门加结构化诊断日志(区分「不是 fork / 无归属 / 无会话 / 无 header / 未 seeded / parent 为空或等于自身 / toolsReady / body 不可解析 / side-shaped / base 缺失」),纯日志、不改任何判定行为——对应你的验收测试 2 和 [Bug] Fork of DSH branch clone: inheritance permanently abandoned by 409 SNAPSHOT_UNAVAILABLE — zero-fold clone triggers context_length_exceeded 400 on first turn | [Bug] DSH 分支克隆的 fork 继承被 409 SNAPSHOT_UNAVAILABLE 永久放弃:克隆零折叠,首轮即 context_length_exceeded … #3011 原帖建议 3。PR 稍后贴在这里和 [Bug] Fork of DSH branch clone: inheritance permanently abandoned by 409 SNAPSHOT_UNAVAILABLE — zero-fold clone triggers context_length_exceeded 400 on first turn | [Bug] DSH 分支克隆的 fork 继承被 409 SNAPSHOT_UNAVAILABLE 永久放弃:克隆零折叠,首轮即 context_length_exceeded … #3011。
    3. 需要 owner 决策的部分(不在本 PR 范围):验收测试 1 的两条路线取舍(显式携带分支身份 vs 从作用域 sid 反查宿主分支元数据——后者需要宿主侧按 id 暴露 header 元数据,当前类型化 surface 只给 id);413 重试侧修复的落点([Bug] Fork adoption server-side completion window: After the child session's first degraded request is committed, POST fork is permanently latched by 409 CHILD_CONFLICT, preventing retroactive adoption | [Bug] fork adoption 服务端完成窗口:子会话首条降级请求落地后,POST fork… #3025 还是 [Bug] Excessive preflight compression time on ultra-large contexts; client mid-disconnect causes repeated request forwar | [Bug] 超大上下文 preflight 压缩耗时过长,客户端中途断开导致请求反复无法转发 #3978);C6 方向。
    4. 上述完成后按规则关闭本帖(duplicate of [Bug] Fork of DSH branch clone: inheritance permanently abandoned by 409 SNAPSHOT_UNAVAILABLE — zero-fold clone triggers context_length_exceeded 400 on first turn | [Bug] DSH 分支克隆的 fork 继承被 409 SNAPSHOT_UNAVAILABLE 永久放弃:克隆零折叠,首轮即 context_length_exceeded … #3011,preflight 侧关联 [Bug] Excessive preflight compression time on ultra-large contexts; client mid-disconnect causes repeated request forwar | [Bug] 超大上下文 preflight 压缩耗时过长,客户端中途断开导致请求反复无法转发 #3978)。
  2. ranxianglei commented on Oct 21, 2026

    @ranxianglei
    Owner

    🤖 Powered by ework · qwen3.8-27b

    (ai: vllm-qwen/qwen3.8-27b)

    关闭结论:duplicate of #3011。 本票 part 1(DSH native fork adoption 入口被静默跳过、零痕迹)与 #3011 同症状(fork 丢失折叠继承 → 零折叠子会话),属新机制实例,已登记为 #3025 清单 C10;part 2(preflight 中断后子会话陷入重复本地 413)已在 #3011 floor 16 记录、并归 #3978 失败域,处置位置待 owner 拍板。按「同症状 = 一个 issue、先来后到」规则,本票以 duplicate 关闭,补充信息均已写入先到的条目。

    交付:PR #4223(无决策依赖部分,已开) — #4223 :给 DSH native adoption 入口的每一道跳过门加结构化诊断日志(log-only,零行为变更)。C10b 身份路由扩展需 owner 拍板,未含在内(选项在 #3011 线程)。

    结构化八问

    1. 是什么问题:DSH fork 出的子会话没有继承父会话的压缩状态;事发时全日志找不到任何 adoption 痕迹,事后无法回答「它不是 fork,还是我们没看见的 fork」;叠加 preflight 被客户端断连中断后,子会话每次重试都收到本地 413,彻底发不出请求。
    2. 什么原因导致的:两个独立机制——① adoption 入口有 8 个静默早退分支,且 fork 检测用的身份(ambient currentInitiator())与请求归属用的身份(Why did it spike to several M tokens: after dsh attribution was lost, requests bypassed bili and were sent directly, ren | 为什么会涨到几 M tokens:dsh 归因丢失后请求绕过 bili 直发,压缩与护栏全部失效 #4082 scoped sid)不在同一条路由上,initiator 不可用时 seeded 子会话被无声跳过、不留痕迹;② 中断的 preflight 在 abort 返回前已把 estimate-source meter 写回会话状态,后续请求因此进入 probeForEvidence 分支,raw-payload doomed 检查(核心字节数 > max(2MB, 窗口×7.5))在任何 fold 尝试之前直接判终端 413,形成 livelock。本票事故字节数与该公式精确吻合(~5742630B ≥ ~765684 tok vs 窗口 744000)。
    3. 影响范围与严重程度:p1。触发条件:DSH 桌面 fork 会话 + 父会话压缩状态大到需要多轮 preflight + 用户在 preflight 期间断连。后果:子会话完全不可用(重复 413,无任何模型请求成功)。影响面限于该特定场景组合。
    4. 怎么修复的:本 PR 只做 ① 的诊断面(C10a):8 种跳过形状全部命名、每道入口门按会话节流记录、畸形谱系告警、盲点形状每进程一条 info 并注明 Why did it spike to several M tokens: after dsh attribution was lost, requests bypassed bili and were sent directly, ren | 为什么会涨到几 M tokens:dsh 归因丢失后请求绕过 bili 直发,压缩与护栏全部失效 #4082 scoped 归属当时是否活跃(即 C10b 的分叉形态)。② 的重试侧修复(尚有可压 range 时不判终端 / 恢复收缩)在 [Bug] Excessive preflight compression time on ultra-large contexts; client mid-disconnect causes repeated request forwar | [Bug] 超大上下文 preflight 压缩耗时过长,客户端中途断开导致请求反复无法转发 #3978/[Bug] Fork of DSH branch clone: inheritance permanently abandoned by 409 SNAPSHOT_UNAVAILABLE — zero-fold clone triggers context_length_exceeded 400 on first turn | [Bug] DSH 分支克隆的 fork 继承被 409 SNAPSHOT_UNAVAILABLE 永久放弃:克隆零折叠,首轮即 context_length_exceeded … #3011-floor16 下等 owner 处置决定;C10b 身份路由扩展等 owner 拍板。
    5. 是否有回归问题:无。log-only:判定逻辑、执行顺序、wire、配置一概未动(verdict 与原判断逐形状等价,矩阵测试钉住);测试双向断言(该触发时触发 / 不该触发时完全静默,普通非 fork 流量零日志行);相邻套件 dsh-native 65/65、dsh-fork-adopt 20/20 绿,typecheck 干净,全量回归交 CI。
    6. 是否破坏缓存:不涉及(未触碰任何缓存/重试/增量游标逻辑)。
    7. 是否改了设计方向:无。C10b(宿主携带显式分支身份 vs scoped-sid 查询 header 元数据)列为 owner 决策项,未擅自定方向。
    8. 是否引入新配置:无。新增输出仅为 host 日志中若干条 [dsh-client] 记录,不进任何客户端或模型可见路径。

    本票验收测试的去向

    #1 → C10b(待拍板);#2 → C10a(本 PR 已实现);#3/#5 → #3978 域(floor 4/5 已有持久化修复 #4046,重试侧判定仍在讨论);#4 → #3011 C6/C7 域;#6 → 既有回归矩阵 + 本 PR 测试覆盖。

    标签:bug、dsh、scope/wiring、p1(已打)+ duplicate(随后补上)。现在执行关闭。若你认为某个机制没被上述映射覆盖,请附细节重开。

  3. abworks-dev commented on Oct 23, 2026

    @abworks-dev
    ContributorAuthor

    (ai: openai/gpt-6.1-sol)
    Scoped-discovery fix submitted as #4424: when the ambient session is absent, resolve the existing request-scoped session ID through agents.get and validate the returned ID before applying the unchanged seeded-fork lineage gates. The regression runs through the real llm/stream waterfall; the original helper fails it, and the fix passes 25 targeted attribution/fork tests. Typecheck/build passed. The broader native suite has an explicitly disclosed Windows rmSync EPERM cleanup failure. This addresses C10b alongside the diagnostic-only #4223; parent snapshot availability/opaque-content and interrupted-preflight recovery are not claimed fixed by this PR. No private session data was uploaded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdshduplicateThis issue or pull request already existsp1scope/wiring

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions