Files
dsh_shenxian/dsh-server-docs/ops/接续入口_覆盖网络线_20260916.md
T
admin 04776af4b1 docs: 工作区根项目文档批量入仓(04-调整方案 113–128 / 交接单 T09–T21 / ops / archive)
起因:用户 2026-09-17 明确「所有文档都要同步,都放在开发仓库 docs 对应文件夹下」。
判据:工作区根 *.md 中在仓库(git ls-files --quotepath=false)搜不到的那些。

入仓 33 份(一律复制,工作区根原件保留不动,避免引用断链):
- 04-调整方案/113–128(16 份 · 原子占号后落盘):覆盖网络 传输方案取舍 / 应用场景与待完善清单 /
  插件化vs改内核 / 问题逐条推演 / 参数表与观测口径;会合中继拆分取证与改造方案;
  集群化改造方案 Manager-Worker;跨节点迁移与节点自举;项目代码分层范式与迭代风险评估;
  搬运与共享重建方案 guest w47→w106;方案规划方法提炼;文档无效信息审计报告;
  会话接续机制复盘与修复;会话接续规范;dsh 客户端化部署方案;dsh 桌面客户端开发方案
- 交接单/archive/交接单-已完成/T09–T21(13 份 · 覆盖网络线已完成单归档)
- ops/(2 份运行态指针:接续入口 / 接续包 · 覆盖网络线)
- archive/(2 份临时与内部简报)

已排除(无需重复入仓):覆盖网络线 10 份方案正文已入档案 103–112(文件名不同)。

登记:INDEX.md §二 新增 04-113–128 共 16 行 + §四 追加 T09–T21 说明 + 机器摘要行刷新
(⛔ 未跑 docs-index-stats.py --write:该脚本会按 \r\n 归一化全文件行尾,故改为字节级单行替换);
README.md 追加 1 条入仓指针;docs-manifest.json 复跑 scripts/docs-manifest.py 刷新。

验收:工作区根 47 份 .md —— 同名已入仓 8 / 本次内容一致 29 / 已知改名映射 10 / 未入仓 0;
git status 待提交清单只含本次新增与登记 3 件(未涉 src/ 与 relay 代码面)。
2026-09-17 18:24:19 +08:00

279 lines
93 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 接续入口 · 覆盖网络线(2026-09-16)
> **这是本工作线的唯一入口。** 新会话先读这一份(约 3 KB),再按需打开下面的文档。
> 详细过程记录在 `.workbuddy/memory/2026-09-16.md` 末尾的「会话接续包」——**不必先读那个 60 KB 日志**。
---
## 0. 当前状态(一句话)
> 🆕 **2026-09-17 18:1x 刷新(序 ⑲ 执行棒收官 · presence 节点在线态已上线)**:**presence 改造已落地生产并真机验收** —— 产物 = `交接单_presence在线态_20260917.md` **§8★ 回报(8.0–8.9 已回填)**。**改法**:节点/端点在线态从「Manager 每 5 s 拉 `/status` 快照」改为「**连接生命周期驱动 + 订阅式推送 + 1 s 批合并 + TTL 安全网**」;`wire.ts` **末尾追加** `SUB 0x10 / UNSUB 0x11 / PRESENCE 0x12 / SNAP 0x13`;`client.ts` 订阅侧 + `rendezvous.ts` 主路径(订阅 `undefined` 才回落 `/status`)+ `web/server.ts` 装配(订阅新鲜时 `/status` 轮询**挂起**)。**真机数据(47 relay)**:`presenceTiming={graceMs 10000, offlineDebounceMs 30000, batchMs 1000, ttlMs 45000, subMax 0}`、`counters={subs 1, pushed 3, snaps 2, rejected 0}`、Manager 日志 `[relay-client] presence SNAP 2 条(scope=all)`。**验收**:`npm test` **169/0/1**(基线 161 + 新用例 **T25–T32 共 8 个**)|单测**先红后绿**(0/7 → 8/8)|**S6 本机多实例真机 8/8**(稳态 60 s **Δ帧=0**(期间 24 个心跳帧在流)|一次上下线 **各 1 帧**、断连→改口 **40985 ms**|首帧 `snapFrames=1`|跨网 **显式拒绝 rejected 0→1**|降幅 **63×**(字面口径)/10.5×(同消费者)/∞(稳态))|`--scene all` **12 PASS / 0 SKIP / 0 FAIL**|探针 **15 PASS / 1 FAIL**(唯一红项 `OBS-09`)|池口 **64**|`dshs/dshs-relay/dshs-worker/dshs-pg` 全 active。🔴 **两个必须记住的实测**:① **假死已修** —— `presenceTouch` 原早于 `ensureEndpoint`、且落点口号是 `listen(0)` **异步**才落地 ⇒ 首帧推 **`localPort=0`** 且**永不重推** ⇒ 订阅方 `addressOf` 查不到 ⇒ **页面打不开但不报错**;修法 = touch 后移 + 落点落地/端口变更时**强制重推**(`publishPresence(force)`),**新用例 `T32` 先红后绿覆盖**,真机复验 `localPorts=[{port:19000, localPort:35289}]`(非 0)。② **`OBS-09` 红 = S7 步骤与 E9 判据冲突**(`systemctl restart dshs` 会收掉 `dsh-*.scope`,平台**按需拉起**、⛔ 无 reconciler ⇒ 无人访问时实例不自回)⇒ 已按 **§9-8 停下报告**(详见该单 §8.8-1/§8.8-4)。**边界自证**:⛔ 未改任何生产值(六值逐项未变)· ⛔ 未新增公网口 · ⛔ 未改 nft/nginx · 🔴 `COOLDOWN_MS=0` 计数 **0** · ⛔ 未 commit / 未 push。**参数表指纹 `8f08e74b026e6e5b5e1b3db813f031ae` → `13de5f9b77c486d71e5b83ec909b17b2`**。
>
> 🆕 **2026-09-17 17:2x 刷新(序 ⑱ 规划棒收官 · presence 已出可执行单)**:**presence(在线状态)改造的规划已完成** ⇒ 产物 = 工作区根 **`交接单_presence在线态_20260917.md`**(287 行 + §11 回报;**§8 前缀指纹 `f612858344077420cff5f1f9ef1c942f`**;全文件 md5 `619a37818765152d74cef499e31bc41d`,落单时;⚠️ 加 §11 后**总字节变了、前缀指纹不变**)。**核心判断**:presence 的第一刀落在**节点 / 端点在线态** —— 因为**全仓零应用层**(`grep -rilE "\b(room|chat)\b" src | wc -l` = **0**)、`presence` 字样**只在 `src/net/relay/placement.ts:21` 的注释里**,而 Manager 现在的在线态来源是**拉 relay `/status` 快照 + 15 s 陈旧回退**(`src/net/relay/server.ts` 第 **413** / **1234** 行原文自证)= 正是要替换的"轮询"路径。**做法** = 照抄 `覆盖网络_瓶颈落地方案_20260916.md §1` 七条并逐条映射到本仓(**连接生命周期** + **1 s 批合并** + **订阅式扇出** + grace 10 s / debounce 30 s + **同 hostId 设备聚合** + **最终一致 5–15 s** + **TTL 安全网**)。**用户拍板** = 「**a 要做**」;⚠️ **B(内容分发 / 块级内容寻址)仍待拍板** ⇒ **只登记、⛔ 不规划**(该单 §0.4-1)。**边界**:§4.2 真取舍 = **空**、§4.3 待拍板 = **空**、**§4.4 R5 = 未命中**(新增监听口 / 凭据 / 入站均 **0**,可见面只收窄);**§9 有 9 条回头条件**(第 9 条 = 🔴 `COOLDOWN_MS=0` 硬禁令)。➡️ **下一棒 = 序 ⑲(执行棒)**,automation **`107b8e38-ccbd-4dcf-8b49-25dad7c89be9`**(一次性 · **2026-09-17 17:28** = 收口 +4 min)。
>
> 🆕 **(已被上行取代)2026-09-17 17:1x 刷新(用户拍板 ⇒ 下一棒已登记)**:用户答「**a 要做**」⇒ **presence(在线状态)改造正式启动**。**下一棒 = 序 ⑱(规划棒)**,automation **`50882a38-07cd-494c-ae73-cbbf27f3604d`**(一次性 · **2026-09-17 17:22** = 收口 +~5 min)⇒ 产出 = presence 的**可执行交接单**(依据 = `覆盖网络_应用场景与待完善清单_20260916.md` §五 第 7 步 + `覆盖网络_千台全场景推演_20260916.md` + `覆盖网络_瓶颈落地方案_20260916.md`);⚠️ **只出规划**(⛔ 不改码 / ⛔ 不动 47/106 / ⛔ 不 commit·push)。⚠️ 另一项 **B = 内容分发(块级内容寻址)仍待拍板** —— 复杂度已评估为「**中**」(方案 §3:需新建 ① 块级切分与哈希标识 ② 内容寻址存储 + 客户端校验 ③ 同网段 peer 发现 + 内容源优先级链;可照抄 BranchCache / Delivery Optimization / SCCM;验收判据单值可测「**回源字节数 ≈ 1 份 × 组数**」;⛔ 不做"包级"=已被证过的全量重拉风暴成因)⇒ **⛔ 本棒未登记、未规划**。
>
> 🆕 **2026-09-17 17:3x 刷新(序 ⑰ 执行棒收官 · 本线在册项清干)**:**四条在册项 —— S1 ✅ / S2 ✅ / S3 ✅ / S4 ⚠️(临界档 · 记录入册、⛔ 不判 FAIL)**。**S1** mksess 口径「DB 直插」→「**PG 直插**」(5 处操作性载体:`02-运维手册.md` + `skills/dsh-change-workflow/SKILL.md`×3 行 + `skills/dsh-env-bootstrap/references/常驻规则-快照.md`×1 行 + 本机技能同内容;⛔ `04-调整方案/**` 档案正文**未动**;判据 E1 = **0 行**)|**S2** 技能 `dsh-auto-handoff-chain`(v1.3.2)**三处同步首次建立** —— 文档库新建副本 + `README.md`/`INDEX.md` 登记 + 47 镜像 `/opt/dsh/docs/skills/` ⇒ `diff -r` = **0 行**、md5 **三处同值**(`0c5c4103…` / `636c4f33…`)|**S3** 代码仓 `.bak-seq7-*` **5 个 → 0**(全 untracked、seq7 11:21 过期快照 ⇒ **非有效回滚点**;`npm run build` RC=0 + lib 产物 md5 **零差异**)|**S4** 幕 4-A 五样本 **18772 / 20121 / 20246 / 21223 / 24477 ms** ⇒ p95(保守 nearest-rank 口径)= **24477 ms** ⇒ **临界档(24000 < p95 ≤ 27000)⇒ 记录在册、⛔ 不判 FAIL、⛔ 未调任何生产值**,⚠️ 回头条件(> 27000 ms)**未触发**。**零回归全绿**:`npm test` **162/161/0/1**、`--scene all` **12 PASS / 0 SKIP / 0 FAIL**(幕 4-A 20419 / 18333 ms)、`overlay-probe` **12/12**;收口三服务 active、池口 **64**。
> 🗃 **Q4 已由用户授权解除并办结**(原话「执行完毕后 同步到仓库」)⇒ `146c3d2`(代码/测试面 **53** 文件)+ `bc0dd2c`(文档库面 **10** 文件)已 push;远端 `master` = **`bc0dd2c`**,`git ls-files src/net/relay | wc -l` **0 → 15**(序⑭/⑮ 的修复**首次进版本库**)。
> 🛑 **本线已无自动接续**:四条在册项清干后,**剩余项全部落在「需拍板」区**(presence / 房间层 / 内容分发**定序** = 业务优先级;骨干节点服务范围 = §4 的 A/B 既有待拍板项)⇒ 按登记门禁**不登记下一棒**,等你拍板后再建。证据 = `交接单_在册收尾_20260917.md` **§9**;参数表指纹 **`8f08e74b026e6e5b5e1b3db813f031ae` 未变**。
>
> 🆕 **2026-09-17 16:4x 刷新(序 ⑯ 规划棒收官)**:**① 复核判定 = 会合 / 中继从 Manager 拆分「不做」** —— 原方案 `会合中继拆分_取证与改造方案_20260916.md` 的四个耦合点 C1–C4 与两步改造 S3/S4 **逐条已被覆盖、未覆盖部分 = 无**:C1 → 序② P0-2(`DSHS_RENDEZVOUS_URL` 优先 / `DSHS_TUNNEL_TARGET` 降兜底,`src/config.ts:485-490`)|C2 → **R5**(Manager 只拨出、落点搬到本机回环池 `127.0.0.1:25000..26000`,`src/config.ts:498-500`;`dialer.ts:11`)|C3 → S2/P2(`dsh_hosts.via` + `RendezvousRegistry`)|C4 → 方案 §8.1 实测**前提不成立**|S3 → 方案 §9.1 自我证伪后改「实例端口区间隔离」|S4 → R2(`dshs-relay` 独立单元)+ 序⑥ S8(106 升格第二中继)+ 序⑦(多实例切流实测)。该方案文档**头部已加状态块、正文未改**(存档)。**② 剩余在册四条已定序并出执行单** ⇒ 产物 = 工作区根 **`交接单_在册收尾_20260917.md`**(243 行;**§8 前缀指纹 `bab83b7219b2669d5a6e9f1acf782e1f`**、全文 md5 `ca13b9ded3d71268f8b0e56755d7aea4`)= **序 ⑰ 执行棒**,顺序 = **S1 mksess 文档口径(「DB 直插」→「PG 直插」)→ S2 技能 `dsh-auto-handoff-chain` 三处同步建立 → S3 清理代码仓 `.bak-seq7-*`(🔴 **范围修正:5 个,不是 3 个**)→ S4 幕 4-A 余量判据化 + 5 样本采样**。🔴 本棒两条新事实:**① Q5 真实范围 = 5 个**(多出 `src/web/server.ts.bak-seq7-*`、`src/worker/tunnel.ts.bak-seq7-*`,**5 个全 untracked**、均为过期快照 ⇒ 无回滚价值)② **`04-调整方案/**` 档案的 mksess 旧口径不改**(档案 = 当时事实,只改操作性载体 + 写勘误指针)。⚠️ 登记:`src/worker/tunnel.ts`(SSH 隧道)现已是**生产死路径**,是否删 = 独立决策,⛔ 本单未处置。➡️ **下一棒 = 序 ⑰(执行棒)**,automation **`067b0892-964e-4deb-bf84-4f1c37bebea8`**(16:44 起)。证据 = `交接单_观测口径与在册缺陷_20260917.md` **§12**。
>
> 🆕 **2026-09-17 16:3x 刷新(序 ⑮ 执行棒收官 → 缺陷 B 已修)**:**拨号池"未分配槽位被一条连接永久杀死"已修并端到端验收** —— 病根 = `src/net/relay/dialer.ts#onConn` 见到 `slot.key === undefined` 时 `slot.server.close()`:关掉的只是**服务器**,槽位**仍留在 `slots` 里**、`key` 仍是 `undefined` ⇒ `localPortFor()` 的 `slots.find(s => s.key === undefined)` **下次还会选中它** ⇒ 把死口号当落点发出去(47 上实测:`16:00:27 落点 127.0.0.1:25000 -> ops/w-106:19000` 紧接 `16:00:31/35/40` 三条 `POST /api/dsh/enter` → **500 `fetch failed: connect ECONNREFUSED 127.0.0.1:25000`**)。**改法 = 只丢弃那条无法路由的连接(槽位原样保留)+ `stray` 计数 + 点名日志**,另加纵深防御(`localPortFor`/`lruIdle` 只认 `server.listening`;`status().pool` 改报"实际在听")。⛔ **不取"把槽位从 `slots` 摘掉"那条** —— 那等于让**任意本地连接**都能**永久**蚕食池容量(R11)。**验收**:**先故意对未分配池口 25000 发一条连接**,随后 guest `enter` 仍 **200/200**、该口同连接连发 3 次 **200/200/200**、打后池口仍 **64**;`npm test` **162/161/0/1**(+T24)、`--scene all` **12 PASS / 0 SKIP / 0 FAIL**、`overlay-probe` **12/12**。部署 = `dialer.js`(`6446fe9b…`)**五处同值**、只重启 47 的 `dshs`。⚠️ 顺带确认一条**环境口径**:对 47/106 的 ssh **必须显式 `-p 22`** —— 别名 `bt-server` 里的 `32022` 是**失效残留口**(47 上 sshd 只监听 22,自 09-16 22:12:48 起)。➡️ **下一棒 = 序 ⑯(规划棒:会合/中继拆分复核 + 在册收尾定序)**,automation `91c53ef8-c3f0-4bb3-9f0a-943790178176`(16:36 起)。证据 = `交接单_观测口径与在册缺陷_20260917.md` **§11**。
>
> 🆕 **2026-09-17 16:1x 刷新(序 ⑭ 执行棒收官)**:**relay 流 keep-alive 复用缺陷已修并端到端验收** —— 真因 = `src/net/relay/client.ts#openStream` 里多挂了一行 `duplex.on('data', …)`,把**读侧接回了出向**:agent 的响应经 relay 回到拨号方后又被 `pumpDial()` 打回去,worker 把它写进 agent socket ⇒ agent 拿 `HTTP/1.1 200 OK` 当**请求行**解析 ⇒ Fastify `clientError` 回 `400`(**用户可见:任何 `via='relay'` 的 host(今天 = w-106)用户 `POST /api/dsh/enter` 必 500,「登录直达工作区」整体不可用**;curl 看到的"第 2 条 400"其实是第 1 条响应的回声)。**改法 = 删掉那一行**(出向唯一入口是 `_write`→`onOut`;`duplex.write()` 与 `tcp.pipe(duplex)` 本就走同一条路,该监听纯属多余且方向错)。**验收**:guest `enter` **200**、同连接连发两次 **200/200**(三次 200/200/200)、`npm test` **160 pass / 0 fail / 1 skip**(基线 159 + 新回归用例 **T23**)、`--scene all` **12 PASS / 0 SKIP / 0 FAIL**、`overlay-probe` **12/12**。部署 = `client.js`(`b8b29afb…`)铺 5 处、**只重启 47 的 `dshs`**(改动是拨号方专属)。🔴 **新发现在册缺陷 B(R7 只报告、未动手)**:拨号池**未分配**槽位被任意一条连接碰到 ⇒ 该槽位**永久死亡**(`dialer.ts#onConn` 只 `slot.server.close()` 却不摘除槽位),而 `localPortFor()` 之后照样把它发出去 ⇒ `ECONNREFUSED` ⇒ `enter` 500 `fetch failed`。证据 + 三个候选修法 + 判据 = `交接单_观测口径与在册缺陷_20260917.md` **§10.8**;参数表指纹仍 **`8f08e74b026e6e5b5e1b3db813f031ae`**(**未变** ⇒ D1 自证)。➡️ **下一棒 = 序 ⑮(执行棒:缺陷 B 修复)**。
> 🆕 **2026-09-17 15:5x 刷新(序 ⑬ 执行棒收官)**:**在册缺陷清算 Q1→Q3 —— `Q1 ✅` / `Q2 🔴 阻塞(新真因)` / `Q3 ✅`**。**Q1**:`/opt/dshs/mksess{,-guest}.cjs` 由**写 SQLite 旧库**改为**写 PG**(连接串从 env / `dshs.env` / `dshs.service.d` 读,⛔ 不固化凭据)⇒ R4 的实例面验收手段恢复(实例面 `curl` 回 **401**,非 `000`);指纹 `mksess.cjs` **`a97c0f214650fbc552c07d804fd153be`**、`mksess-guest.cjs` **`af6eab38c4ff5c365ee17e29d7b98a31`**。**Q3** ✅:**探针 12/12 PASS · rc=0** —— `OBS-09 本机:20000=401 w-106:42461=401` 绿,且"有实例态"下 `OBS-11` 仍绿(实际 77 / 多出 0 / 缺失 0、`20000` 在允许区间)。🔴 **Q2 未达成,且真因不是单内记的 `landModels` 本地路径** —— 是**新发现的数据面缺陷**:**relay 流上的 HTTP keep-alive 复用**(同一条连接第 2 条 agent 请求必回 `400 clientError`,机制 = **worker 侧把上一份响应回灌给 agent**)⇒ **任何 `via='relay'` 的 host(今天 = w-106)用户 `POST /api/dsh/enter` 必 500 = 登录直达整体不可用(用户可见)**。证据链与 1 条命令的最小复现 = `交接单_观测口径与在册缺陷_20260917.md` **§9.3**;`src/` 与参数表**零改动**(参数表指纹仍 `8f08e74b…`)。➡️ **下一棒 = 序 ⑭(执行棒:relay 流 keep-alive 缺陷修复)**。
> 🆕 **2026-09-17 15:3x 刷新(序 ⑫ 执行棒收官)**:**`OBS-11` 已由「计数相等」改为「三集包含式白名单集合」(`nft` 同族改「入站 accept 集合 ⊆ 白名单」)** —— 参数表 §6 **新增 4 键**(`LISTEN_REQUIRED` / `LISTEN_ALLOWED` / `LISTEN_ALLOWED_RANGES` / `NFT_ALLOW_INBOUND`)、`LISTEN_COUNT` / `NFT_RULES` **退役为仅对账**;唯一代码改动 = 代码仓 `scripts/overlay-probe.cjs`(⛔ `src/**` 零改动 ⇒ `git status src/` **20 → 20**);**新增夹具模式**(`--listen/nft/status-fixture`,离线、零生产副作用、输出带 `FIXTURE` 标记)。**验收**:`overlay-probe` **10/12 → 11/12**(剩 `OBS-09` = 环境态);ⓐ **假红已消除**(真态 77 行 ≠ 旧阈值 79 ⇒ 旧判据必红,新判据 PASS)ⓑ **假绿已能抓**(`fx-swap` 行数恒 79 ⇒ 旧判据 PASS,新判据同时点名 `多出 127.0.0.1:9999` + `缺失 0.0.0.0:888`)ⓒ 真机受控临时口「多出必红」实证(`127.0.0.1:27000` 起口 ⇒ FAIL 点名,关口 ⇒ 回 PASS、`ss` 回 77)ⓓ `nft` 走 `-j` 正路且退化文本路径**链感知**(不误抓 `FORWARD` 的 accept);零回归 = `npm test` **160/159/0/1**、`--scene all` 全绿、`ss` 77 / `nft` 72。**参数表指纹 `99e9e17b…` → `8f08e74b026e6e5b5e1b3db813f031ae`**。⚠️ 两条与单不符处已按**实质判据**处理(P1-注 的「零 `*`」⇒「**Local 列**零 `*`」;P4/E7 的「`src/` = 0」⇒「**不新增** `src/` 改动」,20 是既存未提交基线)。➡️ **下一棒 = 序 ⑬(执行棒:在册小缺陷清算 Q1→Q2→Q3)**。证据 = 本单 §8 + `_中间产物_待清理/seq11/`。
>
> 🆕 **2026-09-17 15:0x 刷新(序 ⑪ 规划棒收官)**:**观测口径重构已定序并出单** —— 工作区根 `交接单_观测口径与在册缺陷_20260917.md`(**§8 前缀指纹 `3ece0f870cba67d0113a4c5f9de9d812`**,244 行)⇒ **① OBS-11 判据由「计数相等」改为「白名单集合」**(三集包含式 + `nft` 同族改「入站 accept 集合」)|**② 6 条在册小缺陷清算排序**(`mksess*.cjs` 失效 ⇒ **阻塞实例面验收 ⇒ 先修**|guest(w-106) 实例页 502|…;排序见该单 §5.9)。范围 = **只改观测面**(`scripts/overlay-probe.cjs` + 参数表 §6),⛔ `src/**` 零改动。➕ 顺手办:`.workbuddy/memory/MEMORY.md` **真瘦身 8 121 → 7 712 字符**(回到注入上限内)。➡️ **下一棒 = 序 ⑫(执行棒)**,口径见 §2「🎯 本轮动作」。证据 = 该单 §5–§7 与 §8.9。
>
> 🆕 **2026-09-17 14:5x 刷新(序 ⑩ 收官 · 三件技术债全清)**:**① 幕 2 状态依赖 ✅ 已修**(`--scene all` **12 PASS / 0 SKIP / 0 FAIL**,原 11/1;`--scene 2` 单跑 **3 PASS / 0 FAIL**)|**② `COOLDOWN_MS=0` ✅ 0 命中**(`--scene 4c` / `ctrl` 整体移除 + 显式拒绝 = 退出码 2)|**③ OBS-11 `78 → 77` ✅ 有名字 = `20000`**(47 实例档;当前 47 无活跃实例 ⇒ 未监听。76 条 socket **逐条点名、零无名**,池 64 口全在;另订正 `39463` 实为 relay **动态**落点,现 `46147`)。➡️ **下一棒 = 序 ⑪(规划棒:OBS-11 计数口径 → 白名单集合 + 在册小缺陷清算排序)**,口径见 §2「🎯 本轮动作」。证据 = `交接单_检测时延与deadline_20260917.md` **§8.8-4**。
>
> 🆕 **2026-09-17 13:5x 刷新(序 ⑤–⑨ 全部收官)**:**① 参数表 · 观测 · 权限评估(⑤)② 3–5 台最小形态(⑥)③ 中继失败切流(⑦)④ 切流冷却语义(⑧)⑤ 检测时延与 deadline(⑨)—— 五序全绿**。现行形态:**两台真中继(47 + 106,均只绑回环)**|`RELAY_MAX_HOSTS` = **7515**|**失败自动切流 + 冷却语义(含一跳豁免)+ 换址终态失败早退**已落三处装配点|`npm test` **160/159/0/1**。🔴 **本线唯一的"换址慢"已闭环**:白等 **12.03 s → 0.09 s**、端到端 **34.1 s(中位)→ 25.3 s(中位)**。⇒ **下一棒 = 序 ⑩(覆盖网络线技术债清算 · 执行棒)**,口径见 §2「🎯 本轮动作」。
> 🆕 **2026-09-17 11:0x 刷新(序 ⑥ 执行部分收官)**:**覆盖网络已是"两台真中继"** —— 47 + **106**(106 复用既有 443、零新增公网口、只绑回环 `20080`);参数表 **五项待测全部换成实测**、**待测 4 → 0**;`RELAY_MAX_HOSTS` 按实测重算 **225 → 7515** 并已下发两台;**收口 `overlay-probe` 12/12 PASS**、47 监听口 **79 = S0 基线**、`npm test` 137 pass ⇒ **无退化**。⚠️ **唯一未过项 = 「杀掉任一台中继不自动切另一台」(无失败切流)** ⇒ 下一棒 = **序 ⑦ 规划棒**。证据 = `交接单_最小形态真机批次_20260917.md` §8(§8.9 可复现指纹)。
> 🟢 **2026-09-16 23:0x 刷新(机制层 R0–R5 全部完工)**:**R2 / R3 / R4 / R5 全部落地并端到端验收** —— relay 在 47 常驻(`dshs-relay`,只绑 `127.0.0.1:20080`)|经 `https://alotbuy.com/dshs-relay` 可达(origin 与 CF 双路 `101`)|**w-106 已 `via='relay'`**(agent 面与**实例面**都经 relay)|**sshd 反向隧道已下线且不可能重建**(47 收回隧道公钥、`32022` 回收;106 摘掉 `DSHS_TUNNEL_*`、私钥移走)⇒ 47 公网暴露面**净减 1 口**。
> 🆕 **R5 = 会合可换机(本轮新增)**:R1–R4 隐含的「**Manager 必须与 relay 同机**」这条前提**已不存在** —— Manager 改为**只拨出**一条 wss,落点搬到**自己本机**的回环池(`127.0.0.1:25000+`,64 口)。**relay 放哪台机器都不再影响 Manager。**
> **完整证据 = 交接单 `交接单_relay落地R2-R4_20260916.md` §9(R2)/ §10.4(R3 Step 0)/ §10.5(R3 本体 + R4 全链)/ §11(R5)**(均含命令原文级输出);单测 `relay + remote-spawner + reachability + instance-port` = **38/38**(R5 新增 T18/T19);本机 `lib/` 与 47 `/opt/dshs/lib/` 对账 **0 差异**(R5 产物 29/29 hash 全同)。
> 🔴 **本线留下的一条硬教训(别重犯)**:`RemoteSpawner` 构造函数漏赋值 `this.translateEndpoint` ⇒ 端点翻译**静默失效** ⇒ Manager 拿 Worker 侧口号往**自己本机**拨 ⇒ **浏览器只见「空响应」、平台零日志**。定位靠**判别器**(临时监听 `21000` 把请求接走),不靠读代码猜 —— 判据已钉进 `test/remote-spawner.test.mjs`(先红后绿已实证)。
> ✅ **R5 又抓到同族第二条(已于 2026-09-16 23:4x 修完并端到端验收 ⇒ R2-R4 单子 §12)**:`src/fs/remote-user-fs.ts:70-73` 在 `agentFor(hostId)` 解析不出时**静默回退到 w-47 本地 agent** ⇒ w-106 用户的 launch/文件面在**每次重启后的一小段窗口**里回 **假 `404 {"error":"not_found"}`**(与"文件夹不存在"完全同形)。**判别器 = 看 relay 有没有 `DIAL`**:没有 = 请求根本没出去。
> 🔴 **下一个动作(⛔ 已勘误,勿再按旧指示走)**:⛔ **不是** "presence 改造"。机制层 R0–R5 已齐,但 `覆盖网络_应用场景与待完善清单 §五` 把**房间层/presence 排在第 7 步**,其前置是 **② 网抽象 + 地址规划 + 引导(P0-1/2/3) ③ 一机一钥 + 信任根 ④ 443/TCP 兜底 ⑤ 参数表 ⑥ 3–5 台最小形态**(且仓库里目前**没有应用层代码**,"presence 改造"无从下手)。⛔ 不要再做 R2/R3/R4/R5、不要再开 32023、不要再做 SSH 版中继。
> 🟢 **2026-09-17 07:4x 刷新(② 收官)**:**序 ② 已完成**(P0-1 网抽象 / P0-2 引导三级链 / P0-3 名字解析与授权,三步全部落地 + 端到端验收;执行进度:`交接单_网抽象与地址规划R6_20260916.md` 的「执行进度」段,指纹 `0d3f1f305e9e6219afa97cdfecff05ba`)。
> ⇒ **下一棒已定序(2026-09-17 07:5x 定序 · 09:1x 刷新)**:~~序 ③(一机一钥 + 信任根)~~ ✅ **已完成**(证据 = `交接单_一机一钥与信任根_20260917.md` §8)→ ~~序 ④(443/TCP 兜底)~~ ✅ **已完成**(证据 = `交接单_443兜底_20260917.md` §8)→ ~~**下一棒 = 序 ⑤(参数表·观测·权限评估)**~~(⛔ **已过期**:⑤–⑨ 均已收官 ⇒ 现行下一棒 = **序 ⑩**,见上方最新刷新行与 §2「🎯 本轮动作」)。**定序理由**:③ 是身份基线、④ 是失败域分离,⑤ 建立在前两者之上(参数表要把 ④ 带回的 45% 口径填成数值,权限评估要覆盖 L3 跨机真容灾)。⛔ 不要重做 ② / ③ / ④ 的任何一步。
> 另:本轮把 §11.6 **记下没动手**的两条缺陷修完并端到端验收 —— 见 R2-R4 单子 **§12**(A1 假 404 真因定死为"文件面 `hostDirectory` 无刷新机制"+ 治本/治安全双修 + 冷启动直接证据;A2 `state.py` 恒报锁空闲)。
覆盖网络/客户端化这条线已产出 **10 份规划文档 + 1 份执行交接单**(全在工作区根目录),**代码已有 S0+S1 落地(本机 commit `640813e`),且 R2–R5 的产物已铺到 47(Manager)与 106(Worker)两端**(R5 产物对账 29/29 hash 全同);⚠️ **方案 S3 的机制已于 2026-09-16 15:2x 实测证伪并改写** ⇒ 详见下方「15:2x 复核」与 §2。
| 项 | 值 |
|---|---|
| 文档库执行锁 | ✅ **空闲**(2026-09-17 15:0x 由「覆盖网络线-序11规划棒」释放;该棒只出规划 + 改 `.workbuddy/memory/MEMORY.md`,⛔ 未碰 `src/`、未动参数表) |
| **S0 / S1 落地真相** | ✅ **两端都已铺**(2026-09-16 23:0x 复核):本机 `lib/` vs 47 `/opt/dshs/lib/` ⇒ **R5 产物逐文件对账 29/29 hash 全同**;relay 运行时 `/opt/dsh-relay/lib/net/relay/*` 同步 |
| **15:2x 复核(实测)** | 🔴 **S3 原机制证伪**:47 `sshd gatewayports no` ⇒ `-R 127.0.0.2:…` 被**静默改写为 `127.0.0.1`**(ssh 侧零报错)⇒ 回环别名方案作废,改「**实例端口区间隔离**」|🔴 **发现真 bug**:`findFreePort()` 各 worker 各自随机 ⇒ 同号在 47 的 `127.0.0.1` 撞号、而 `tunnel.forward()` 返回值被忽略 ⇒ **静默打到别人实例**|⚠️ `verify-cluster-cross.mjs` 默认端口 13080 ≠ 现役 3080,且**有生产副作用**(改容量/建用户/拉实例)⇒ 跑完要清 |
| 代码 / 服务器 | 🆕 **序⑩ 改动(2026-09-17)**:仅 `scripts/overlay-failover-drill.cjs`(幕 2 状态无关化 + 删掉 `4c`/`ctrl` 两个 `COOLDOWN_MS=0` 场景),⛔ **未碰 `src/`**、未改任何生产阈值、relay/Manager 均已复原并复验(`systemctl restart dshs` 归零回 47)|⚠️ **本轮有改动**:代码仓 `src/net/relay/*`(R5:`wire/duplex(新)/server/client/dialer(新)/main/index`)+ `src/config.ts` + `src/web/server.ts` + `test/relay.test.mjs`;47 上 relay/manager 两个 drop-in + `relay-keys.json`(均有 `.bak-r5*` 备份) |
| 文档库 | ✅ **10 份已转正式档案 103–112**(`4e3a1a4`)+ 已推镜像,**双端对账 192/192 全绿**(2026-09-16 11:5x) |
| 当前基线 | 代码仓 HEAD = `640813e` —— ✅ **已 push**(2026-09-16)|⚠️ 本机工作区**有未提交改动**(S2 会合中继拆分 + R3/R4/R5 产物 + 新增 `test/remote-spawner.test.mjs`)⇒ ⛔ **未获授权不 commit / 不 push** |
| 根副本 vs 档案 | 本线 10 份根文档的正文**已入档案 103–112**(行覆盖 **98.5–99.0%**,差异仅一级标题行)⇒ **根副本保留为工作副本,⛔ 不搬**(被 11 处引用,搬走会断链) |
| **自动接续** | ❌ **已于 11:00 删除,且⛔ 不要再建** —— 两版都试过(7 积分/轮 → 优化版 **9 积分/轮**),**证明"定时任务省积分"是伪命题**。原因见下方「成本真相」。 |
> 💰 **成本真相(终版 · 2026-09-16 11:2x,**实测那个会话本身**)**
> ⛔ 前两版都错:v1 把固定注入当"一次性入场费"(错);v2 我**按最新 mtime 猜会话**,分析到了另一个"会话复盘"会话(`e2e090be`,错)。
>
> **定位方法(可复跑,别再用 mtime 猜)**:拿 prompt 里的独有字串去搜转录 ——
> `grep "能脚本化的全部脚本化" <projects>/*.jsonl` ⇒ 唯一命中 **`e265f0cd-bd24-48c7-953e-4bbfa615bc7d.jsonl`**(761 KB,10:50:09 结束)。
>
> | 指标 | 实测 |
> |---|---|
> | **工具调用次数** | **32**(Bash 16 / Edit 6 / Write 5 / Read 4 / present_files 1) |
> | 模型请求(记录数) | 54(转录对每次请求记**两条**:一条无 `cache_read`、一条有 ⇒ **实际约 27**) |
> | **Σ input** | 5,205,114(同口径折算 ≈ 2.6 M) |
> | Σ cache_read | 2.55 M(≈49%) |
> | 单次 input:首 → 峰/末 | 52,582 → **141,401** |
> | **固定注入 5 万 × 请求数** | **占 51.9%**(对折不改变比例) |
> | **本次新灌入的内容** | 工具输出合计 **84,870 字符 ≈ 2.4 万 token ⇒ 仅占 0.5%** |
>
> **它实际干了什么**:32 次调用集中在 `D:/github/dsh_shenxian` 的 **git / 文件读写**,并读过一次 `接续入口`(4,443 字符)。**并没有去读源码做取证。**
>
> **三条结论**
> 1. **成本 ≈ 请求次数 × (固定注入 + 累积历史)**。本次 27–54 次请求里,**固定注入一项就占 51.9%** —— 即**花掉的一半积分,只是"把指令与常驻规则重复发了二三十遍"**。
> 2. **本次真正"读进来"的东西极少(≈2.4 万 token,占 0.5%)** ⇒ 这笔钱**不是"上下文太大"造成的,是"请求太多次"造成的**。
> 3. ⇒ **唯一的大杠杆 = 压请求次数(= 压工具调用次数)**:32 次调用若能压到 1–3 次,Σinput 掉一个数量级(9 积分 → ~1 积分量级)。**这才是真能省下来的钱。**
>
> **⛔ 由此两条硬规矩**
> ① **批量活不要"要求 agent 写脚本",而要"只准跑这个脚本"** —— 脚本**由上一会话预先写好**,任务里命令式指定;agent 没有探索余地,请求次数自然压到个位数。
> ② **绝不把"读代码 / 大范围取证"派给无人值守会话**(要么脚本先 grep 计数,要么有人盯着)。
> ⚠️ **成本教训(2026-09-16 实测,勿重蹈)**
> 1. **"新会话"不等于"不花钱"**:本工作区**每轮固定开销实测 ≈ 51,830 token**(系统提示 + 工具定义 + 常驻规则 + 注入的记忆)⇒ 任何新会话第一轮都要付这笔钱。准确的收益口径是「**把每轮水位从 39 万降到 ~5 万(约 1/8)**」,**不是"零成本"**(我此前把它说成"降低 token 消耗",属**不准确承诺**)。
> 2. **最大的可压缩项 = 每轮注入的记忆文件超限**:✅ **2026-09-16 修复轮已办** —— 实测真被截断的是**用户级** `MEMORY.md`(注入上限 **≈4,000 字符**),已按**分层重排、零删除**修好;🔑 **重排是杠杆,删减不是**(方法已沉淀为 `dsh-knowledge-upkeep §8.6「注入预算」`)。
> 3. ⛔ **不要把"10 份文档档案化"交给 agent 逐份做** —— 那是 20+ 轮 × 7 积分 ≈ **140+ 积分**,违反项目省积分第一招「**批量活写脚本**」。**正确做法 = 写一个转换脚本一次跑完**(加 8 段头部 + 原子占号 + 登记 + audit 全在脚本里),agent 只读摘要 ⇒ **1–2 轮完成**。
---
## 1. 十份文档(按阅读顺序;前 3 份是决策层,其余是专项)
| # | 文件 | 一句话 |
|---|---|---|
| 1 | `可行性评估_客户端安装与覆盖网络互联_20260916.md` | 判定可做;列出**现有架构已给出的 80% 形状**(soft 隔离 = 裸子进程 / Worker 四条协议纪律 / 拨出式反向隧道 / 全仓仅一处平台分支);§六 = 与另两份客户端文档的对账 |
| 2 | `覆盖网络_全球架构复盘_20260916.md` | 五层架构 + 12 条内建特性 + 选型对照 + **10 类风暴类型学** + 流量组织五原则 + 7 步落地顺序 |
| 3 | `覆盖网络_瓶颈落地方案_20260916.md` | **九大瓶颈逐个给可执行做法**(含 Slack / SCCM 官方做法照抄点 + 验收判据);**"只做三件事"= presence + 游戏服放 L1 + 块级内容寻址** |
| 4 | `覆盖网络_骨干层方案_20260916.md` | 多中心骨干(≤10–20 成员)+ 选择性加入;三条硬约束(接入/成员/可见三分离、资格只能控制面签发、骨干不得被默认征用) |
| 5 | `覆盖网络_百台规模推演_20260916.md`(v2) | 100 台异构画像;L1 10 / L2 30 / **L3 需中继 40–55**;全互联 4,950 vs 星型 100 |
| 6 | `覆盖网络_千台全场景推演_20260916.md` | 1000 台 × 11 场景;**瓶颈排序**(presence 第一)+ 流量预算总表 |
| 7 | `覆盖网络_游戏专项_MMORPG与MUD_20260916.md` | MMORPG/MUD/传奇 = **最匹配**;核心简化:**玩家之间不需要互联,中继按"服数"算** |
| 8 | `覆盖网络_调研_游戏网络特征与群聊上限_20260916.md` | 游戏网络参数(每玩家 2–20 KB/s、jitter<20 ms)+ 群聊上限**按扇出预算定义**的分档 |
| 9 | `覆盖网络_答疑_群聊备份迁移保密_20260916.md` | 四问:群聊+agent / 备份(主备份用对象存储)/ 迁移提速 / 三层加密与元数据保护 |
| 10 | `覆盖网络_补遗与参考方案_20260916.md` | 补遗 24 条 + 参考方案对照 + **反模式 12 条** |
| 11 | `参数表_覆盖网络_20260917.md` | **序⑤ 产物 · 唯一一张参数表**(155 行):输入参数 + 代码固化常量 + 45% 口径填值(`--max-hosts = 225`)+ 观测阈值 `OBS-01..12` + 权限影响评估 + 待测汇总。**观测探针** = 代码仓 `scripts/overlay-probe.cjs`(一条命令 / 12 行 / 退出码 0-1-2;cwd = 本工作区根即可跑) |
**另有两份他人产出(已按最佳方案收口,勿重复改)**:`dsh客户端化部署方案_20260916.md`(v3,平台侧单一来源)|`dsh桌面客户端_开发方案_20260916.md`(v1,交付载体层)。
> 🧭 **方法论(2026-09-16 16:4x 新增)**:`方案规划方法_覆盖网络线提炼_20260916.md` —— 把本线「**信息收集 → 方案调研 → 场景梳理 → 逻辑验证 → 应用推演**」五段作业法反推成可复用方法(含每段骨架/出口判据、**出口闸门三件事**、8 次真实纠错自证、可抄模板)。**下次做新方案规划时先读它。**
---
## 2. 下一步
> ✅ **上一轮已完成(2026-09-17 08:32,执行棒)**:**序 ③(一机一钥 + 信任根)已收官** —— 四层密钥模型(离线根 → 47 在线签名者 → 每机节点密钥 → 会话)落地;relay `keys` 表改**逻辑名 `<net>/<hostId>`** 索引 + **成员资格校验**;新增密钥仪式 CLI `scripts/overlay-keyring.cjs` 与 `test/overlay-identity.test.mjs`(25 例);**吊销演练 + 节点密钥轮换 + 根密钥恢复演练(判据①②③全绿,794 ms)全部通过**;**relay 已开强制身份**(`identityRequired=true` / `identityOk=3`);`npm test` 130/129/0;对账 148/148 + 26/26 全同。**全部证据 = `交接单_一机一钥与信任根_20260917.md` §8**(回填后指纹 `md5 204c0f69630e5117c227828f5fc01f2d`,427 行)。⚠️ 一条**与序 ③ 无关**的未过项:guest(w-106) 实例页 **502**(真因 = 平台 `landModels → writeHomeFile` 用**本地路径**写远端实例 home;另发现 `/opt/dshs/mksess*.cjs` 仍写 SQLite 旧库、**已失效**)⇒ 两条并入序 ④ 的相邻缺陷清单。⛔ 不要重做序 ③。
>
> ✅ **上一轮已完成(2026-09-17 08:4x,规划棒)**:**序 ④ 的 443/TCP 兜底交接单已出** —— `交接单_443兜底_20260917.md`(工作区根,225 行,**md5 `b9aa6bbc0a458481627f7aeb1f17ab54`**)。要点:端口仍 443(零新增公网口)+ 47 上**独立 server 块 / 独立 server_name**(去 CF、去门户 conf 两个失败域)+ 入口列表走 **`DSHS_OVERLAY_BOOTSTRAP_SEEDS`**(⛔ 不用会**压制引导链**的 `DSHS_RELAY_URL`)+ 地址覆盖走**独立配置项**(⛔ 不塞 URL / 不塞签名目录)+ 签名目录 `relays[]`/`bootstrap[]` 自动广播(改一次 seeds 全网刷新)+ 降级必须可 grep。**L3(跨机真容灾)已明确后置**(命中 R5,属序 ⑤ 权限评估)。⛔ 不要重做规划。
>
> ✅ **上一轮已完成(2026-09-17 09:1x,执行棒)**:**序 ④(443/TCP 兜底)已收官** —— 47 上新增**独立 443 server 块** `relay-direct.conf`(`server_name relay-direct.alotbuy.com`,**只**放行 `/dshs-relay` 与 `/dshs-overlay/bootstrap`、其余一律 404)+ **seeds 广播**(`relays[]`/`bootstrap[]` 均含兜底项、**主入口首位**)+ 客户端**地址覆盖**(定向 DNS 改写 ⇒ 直连指定 IP、**SNI 仍为域名**、证书照验)+ **「同源优先」**(补掉"兜底项在取址路径上**永远选不中**"这个真缺陷 —— 它使 §1 目标与 D6 同时落空)。**D1–D8 逐条验收**(D2 **字面**判据因 `*.alotbuy.com` 是 **CF 泛解析**而不可满足 ⇒ 按**实质**判据「不经 CF」的两条独立证据判绿;**§4.3 的 A/B 两个候选都不需要 ⇒ 本单零依赖关闭**)。**全部证据 = `交接单_443兜底_20260917.md` §8**(该单 **§8.9** 有可复现指纹口径)。
> ⚠️ **序 ④ 带回的四条遗留**(下一位读者必须看):① D2 解析层 —— 运行期"去 CF"由 `DSHS_OVERLAY_ADDR_OVERRIDES` 保证,"解析层也不经 CF"需灰云记录(未做);② 🔴 **106 的 agent 面不吃引导链** —— `DSHS_RENDEZVOUS_URL` 被 `worker/agent.ts` **直接当 relay URL 用**,⚠️ **S2-附 的字面执行会造成生产回归(`tunnel===undefined`)⇒ 已判定不执行**;③ 引导链缓存两支没有"答出者"信息 ⇒ CF 打挂后切换有 **≤300 s 收敛期**;④ **45% 容量口径未填数值**(留给序 ⑤ 参数表)。
> ✅ **上一轮已完成(2026-09-17 09:1x,规划棒)**:**序 ⑤ 的交接单已出** —— `交接单_参数表与观测_20260917.md`(工作区根,**250 行**,md5 `40f08167990eac54ec8a026b9c57f807`)。要点:**三件一体**(参数表 / 观测最小集 / 权限评估,= 清单 §五 第 5 项原文);**唯一代码改动 = `src/net/relay/server.ts` 补 `dial/dialDenied/dialFailed` 判别器计数**(今天 `DIAL` **只有日志行**、脚本无法断言 —— 判别器教训的固化);45% 口径**按"单台中继 `--max-hosts`"填值**(⛔ 不填"全网 45%",实例参数填全网数字不可执行),设值走 47 **新建 drop-in** `capacity.conf` + 硬约束 **`max > used×4`**(防自锁,不满足就不设值);观测落成**一条命令出 PASS/FAIL** 的 `scripts/overlay-probe.cjs`(阈值⛔不许是脚本魔数);**L3 跨机真容灾只评估、不实施,归序 ⑥**(序⑥本就要起 3–5 台 ⇒ 合批明显更省,现在做要多花一台机器/一个公网 IP);**§4.3 待拍板 = 空**。⛔ 不要重做规划。
>
> ⛔ 不要重做序 ② / ③ / ④。
>
> ✅ **上一轮已完成(2026-09-17 09:5x,规划棒)**:**序 ⑥ 的交接单已出** —— `交接单_最小形态真机批次_20260917.md`(工作区根,**321 行**;核对口径 `sed '/^## §10 指纹/,$d' 交接单_最小形态真机批次_20260917.md | md5sum` = `8bea0ac79697b80790a273b54f1db37c`)。要点:**S0–S9 + E1–E12 + 回滚 + §8 回报格式**;五项待测/待校准(打洞率 / 每玩家带宽 / 跨云稳态吞吐 / 中继 jitter / `MEM_PER_HOST_MB`)**逐项写了"怎么测 / 样本多大 / 判据 / 写回参数表哪一行"**;**合批落地 L3 第二中继机(106 升格)**;**回头条件写死**(§5 S7 七件必做)。🔴 **本单新增一条关键事实**:`src/` 全仓**零 UDP / NAT 穿透代码**(`grep -iE "dgram|createSocket|stun|punch|udp" src/` 命中**全是 `signature`/`native` 假阳性**)⇒ ⇒ **打洞项的口径定为"测该网络能不能打洞",⛔ 本单不做打洞实现**(`§4.1-1`);另一条:`RELAY_RTT_W106 = 336 ms` 是**心跳往返**、**不一定是网络 RTT** ⇒ 本单要求做 **ICMP / TCP / relay 三方对比**(`S3(b)`;若它其实是口径问题,所有"跨云不可玩"的结论都要重判)。**§4.3 唯一待你拍板项 = 第 4/5 台真机的来源**(A 先用现有 3 台 / B 你自备设备跑一次性探测 / C 新开云主机;**倾向 A+B,⛔ 执行棒不必等**)。⛔ 不要重做规划。
>
> ✅ **上一轮已完成(2026-09-17 11:0x,执行棒)**:**序 ⑥(3–5 台最小形态)执行部分已收官** —— 五项待测/待校准**全部换成实测**(`HOLE_PUNCH_RATE_LOCAL` 分层实测 2/2 可打洞、`PER_PLAYER_BW_LOCAL` 9.8/3.9 KB/s、`JITTER_LINK_MEASURED` `p95(|ΔRTT|)`=**3 ms 达标**、`WAN_STEADY_THROUGHPUT` 352/12213 KB/s、`MEM_PER_HOST_MB` **2 → 0.06**,R²=0.9424)+ **参数表 §7 待测 4 → 0**;🔴 **S7 回头条件已触发并执行**:`RELAY_MAX_HOSTS` **225 → 7515** 并**重下发** 47/106 两台的 `capacity.conf`(drop-in 重写 ExecStart,绕开"CLI 优先于 env"静默忽略坑);**S8 L3 第二中继机已落地**(106 `dshs-relay` 只绑 `127.0.0.1:20080`,复用既有 443 ⇒ **零新增公网口**,WS 握手 **101**,seeds 末位广播);**收口 12/12 PASS**、监听口 **79 = 基线**、`npm test` 137 pass。**全部证据 = `交接单_最小形态真机批次_20260917.md` §8**(1–8.9 全节;§8.9 可复现指纹:参数表 `db1317c2f7aaef7b47785c1f4fc9de03`、本单前缀 `1edde731eba5034c5f6f3a43864e5a8e`)。⚠️ **唯一未过项 = E9 后半「无失败切流」**(客户端把 relay url 首次解析后**钉死**;worker 不吃引导链;Manager 只在"目录地址变了"时换址)⇒ **见 §8.8-1,已判为"新功能",本轮只报告不动手**。
>
> ✅ **上一轮已完成(2026-09-17 11:1x,规划棒)**:**序 ⑦ 的交接单已出** —— `交接单_中继失败切流_20260917.md`(工作区根,**280 行**;**§8 前缀指纹 `419abf308c00b7668e8898aaa91ba9e8`**,口径 = `sed '/^## §8 回报格式/,$d' … | md5sum`)。要点:**一份切换实现(新增 `src/net/relay/switcher.ts`)+ 三个装配点复用**(C1 Manager 拨号通道 `web/server.ts#refreshOverlay` / C2 worker 实例面 / C3 `relay --client`);🔴 **规划棒新查出的证据级细化**:根因不止"换址条件是目录地址变了",而是 `directory.ts:348 pickFromDoc` **取到第一个候选就 return** ⇒ **候选集退化成单点**(重解析恒返回同一台;S1 就改这里);**阈值已定** = `attempts ≥ 3` **∨** `backoff ≥ 15 s`(D3)+ **排除 + 冷却 300 s**(D5)+ **无候选时不切、绝不静默回退默认机**(D6);**S7 三幕真机演练**(杀 106 / 杀 47 / 两台全杀 —— 幕 3 就是 **R11** 的现场判据);**§4.5 已出 R5 评估:未命中,暴露面零变化(新增监听口 0 / 凭据 0 / 入站 0)**;**§4.3 待拍板 = 空**(第 4/5 台真机来源属序⑥ 遗留同题,⛔ 执行棒不必等)。⛔ 不要重做规划。
>
> ✅ **拍板已闭环(2026-09-17 11:00,用户原话)**:「**1 本机内存大 可以模拟多台**」⇒ **第 4/5 台 = 本机多实例**(本机 47.6 GB / 空闲 27.4 GB / 32 核;relay 单实例 ≈ 48 MB),**放弃**"用户自备设备"与"新开云主机"。⚠️ 局限:多实例共用同一出口 IP ⇒ 对**切流逻辑**够用、对**家宽 / 运营商 NAT 差异**无增量。**已落三处**:`参数表_覆盖网络_20260917.md` §11 / `交接单_最小形态真机批次_20260917.md` §11 / 本单 `交接单_中继失败切流_20260917.md` §11(三处补记**均在各自指纹口径之外** ⇒ 三个指纹均不变)。⛔ **后续任何一棒都不许再上抛这道题**。
>
> ✅ **上一轮已完成(2026-09-17 11:2x–12:2x,执行棒)**:**序 ⑦(中继失败切流)已收官** —— S1–S5 全落(`directory.ts` 拆出 `listCandidatesFromDoc`;**新增 `src/net/relay/switcher.ts` = 唯一实现**,三装配点 C1 `web/server.ts` / C2 `worker/relay-tunnel.ts`+`worker/agent.ts` / C3 `net/relay/main.ts` 复用;`test/relay-failover.test.mjs` F1–F11;`scripts/overlay-failover-drill.cjs` 三幕真机演练);参数表新增 `RELAY_FAILOVER_*` 6 键 + 7 个演练坐标,**§9 第 5 行已改「已闭环」**(指纹 → `24cf2efdbcdcbe61267126ed65dba006`)。**全部证据 = `交接单_中继失败切流_20260917.md` §8**(§8 前缀指纹 `419abf308c00b7668e8898aaa91ba9e8`,收口后**未变**)。真机:`--scene all` = **8 PASS / 0 SKIP / 0 FAIL**;杀 47 ⇒ 切到 106,**三个时延样本 30563 / 27878 / 29176 ms(全部贴 30 s deadline)**;两台全挂 ⇒ **0 行切换 + D6 原生判别器**;`npm test` **149/148/0/1**(+F1–F11)。⚠️ 遗留四条见该单 **§8.8**(**§8.8-4 = 下一棒主题**)。⛔ 不要重做序 ⑦。
>
> ✅ **上一轮已完成(2026-09-17 12:3x,规划棒)**:**序 ⑧ 的交接单已出** —— `交接单_切流冷却语义_20260917.md`(工作区根,**345 行**;**§8 前缀指纹 `aa3a6ec0d66dd81f465cea2a3a08ad27`**,口径 = `sed '/^## §8 回报格式/,$d' … | md5sum`)。**三问已给判定**:① **应当拆开,但拆的是「准入方向」而不是「冷却时长」** —— health 路径允许**一跳豁免**、directory 路径 **⛔ 完全不豁免**(依据 = 11:43:26 真机实测:directory 靠"地址变了"把刚冷却的 47 换回来 ⇒ 给它豁免权 = 复活抖动风暴)② **键仍按 `url`**,新增 `kind ∈ {switched-away, open-failed}` **只决定豁免优先级**(把键换成"原因"会让同一 url 存在多条冷却 ⇒ 抖动抑制失效 = **净退化 R11**;且 `reason` 是自由文本 ⇒ 字符串耦合)③ 🔴 **E5 改判为「幕 4」** —— 本单**已证明**(命题 P-③)在 D5 生产值下"杀 106 时 47 未被冷却"这个窗口**不会被自然产生**(当前在 106 ⟹ 47 必曾进冷却 ⟹ 若未满 300 s 则仍在冷却)⇒ 上单 §8.8-3 的回头条件**指向一条走不通的路**;正解 = `停 47 → 切 106 → 恢复 47 → 停 106(预期 D6,记 SKIP)→ 等冷却过期 → 断言 ≤ deadline 切回 47`(副产品 = §8.8-4 的正面复现)。**另加一条判据级事实**:**构 C(`--url` / `DSHS_RELAY_URL` 钉 106)已判不可用** —— `directory.ts:642-651` 明证 env 显式会让**候选链退化成单点**,杀 106 后仍无候选。**S0–S9 + E1–E13 + 回滚 + §8 回报格式均已就位**;**§4.3 待拍板 = 空**;**§4.5 R5 评估 = 未命中,暴露面零变化**。⛔ 不要重做规划。
>
> ✅ **上一轮已完成(2026-09-17 12:40–13:2x,执行棒)**:**序 ⑧(切流冷却语义)已收官** —— S1–S5 全落:`src/net/relay/switcher.ts` 冷却表结构化(键仍按 `url` + 新增 `kind ∈ {switched-away, open-failed}`)+ `replace(targetUrl, reason, origin)` 闸门**只对 directory 收口**(health 可**一跳豁免**)+ `tick()` 的 **D6 现场**加豁免(新增单点判据 `pickExemptTarget`)+ 豁免有界(每 url 每冷却周期一次,`exemptedAtMs`;豁免也失败 ⇒ 重置冷却 + 本周期不再豁免);`src/web/server.ts` **仅一处**(传 `'directory'`);`scripts/overlay-failover-drill.cjs` 新增 `--scene 4|4b|4c|ctrl`;`test/relay-failover.test.mjs` +**F12–F17**。**全部证据 = `交接单_切流冷却语义_20260917.md` §8**(1–8.9 全节;**§8 前缀指纹 `aa3a6ec0d66dd81f465cea2a3a08ad27`,回填后未变**)。**真机核心成果**:**幕 4(构 A)= 4 PASS / 0 FAIL** —— 豁免在 **19218 ms** 内切回(原文 `|豁免 kind=switched-away 剩 269991ms` = "47 当时确在冷却"的**直接证据**,原因 = 当前通道不健康/health 路径)⇒ **对序⑦ §8.8-4 缺陷正面闭环**(序⑦ 下同场景最长 ~300 s 不切流);**幕 4b = 4 PASS** —— D6 判别器原文(`候选 3 条,排除 3 条`)+ 冷却未过期 **0 行**切换 + 冷却过期后**自然**切回(25125 ms,**无豁免标记**)⇒ 归因收敛到冷却语义;幕 1/2/3 未退化(幕1-A 29586 ms PASS);`npm test` **155/154/0/1**(+F12–F17);部署 47/106 四处 lib,`grep -c RELAY_FAILOVER_EXEMPT <lib>/net/relay/switcher.js` = **2** 自证。⚠️ **遗留六条见该单 §8.8**(🔴 **§8.8-2 = 下一棒主题**:S0 基线样本 **32550 ms > deadline 30000**,回头条件已命中)。⛔ 不要重做序 ⑧。
>
> 🔴 **本单一条判据级新发现(§8.8-4,留档)**:**`RELAY_FAILOVER_COOLDOWN_MS=0` 是"看起来合法、实际会自锁"的配置** —— `num()` 的 `/^\d+$/` 放行 `'0'`,但归零会让**序⑦ F10 的"失败候选必须被排除"**一起失效 ⇒ 候选链卡在第一个失败候选上(实测 **121–123 s 无切换**)。构 B(原定归因构造)因此判**不可用**,已由 **幕 4b** 替代并拿到更强证据。⛔ **任何一棒都不许把 `COOLDOWN_MS=0` 写进回滚路径**。
>
> ✅ **上一轮已完成(2026-09-17 13:40–13:5x,执行棒)**:**序 ⑨(检测时延与 deadline)已收官** —— **P10 分解逐项复现成功**(两样本结构完全一致:检测 **15.085 / 15.119 s** + 首试 0.258 / 1.416 s + 白等 **12.033 / 12.029 s** + 建连 2.821 / 2.724 s)⇒ D1 解锁;**S2 修前 N=5 = 5/5 全超 deadline**(33914 / 31646 / 34078 / 35321 / 41252 ms,中位 34078);**S4 落点 = 抽公共 `waitUpOnStatus`(`src/net/relay/client.ts`)+ 三处装配点全部委托**(C1 `web/server.ts` / C2 `worker/relay-tunnel.ts` / C3 `net/relay/main.ts`,D7 收口),落点自证 47/106 四处 `lib` 各 3 命中;**S5 先红后绿**(改完先对旧 lib 跑 ⇒ `SyntaxError: … does not provide an export named 'openedChannelFailedTerminally'`;build 后 22/22 全绿);**S7 修后 N=5 = 5/5 进 deadline 内侧**(25465 / 25342 / 21985 / 27389 / 23318 ms,中位 25342);**白等 12.03 s → 0.09 s**、总 19.81–23.93 s,早退日志原文 `[relay-failover] ⛔ 新通道终态失败(state=backoff attempts=1 burst=false lastError="transport error")⇒ 提前放弃,不等满 12000ms`;**S8 不退化**:`npm test` **160/159/0/1**(基线 155 +新增 F18–F22)、`--scene all` 11 PASS / 1 FAIL(幕 2-B 已归因)、幕 4-A/B/C 全绿、`overlay-probe` 10/12 与 S0 逐项一致、`ss -lntp|wc -l` = 77、`nft` = 72、门户 200、演练 env 残留 0;**收口归零**(`restart dshs` 把 Manager 归位回 47,probe 回到 10/12)。**全部证据 = `交接单_检测时延与deadline_20260917.md` §8**(1–8.9 全节;**§8 前缀指纹 `d903b4eeabf25ef381379cdbaac77e8a`,回填后未变**;全文件 md5 `ccc28dafd10004d11740834004b02154`,529 行)。参数表指纹 `e6b669c257d8e8964273b3b400238351` → **`99e9e17b0c1ce0550e4bc7626a5a0494`**(新增 `DRILL_SAMPLE_N`=5 / `RELAY_GRACEFUL_BURST_MS`=15000,`DRILL_POLL_MS` 2000→500,§9 新增第 9 行「换址墙钟的四段分解」)。⚠️ **遗留三条见该单 §8.8**(= 下一棒主题)。⛔ 不要重做序 ⑨。
>
> ✅ **序 ⑩ 已收官(2026-09-17 14:23–14:5x)** |🆕 **下一棒已登记自动接续** = automation `3d4dffc0-356e-4bc6-9628-b7d664da7db9`「覆盖网络线-序11规划棒(观测口径重构+在册小缺陷清算)」(**一次性** · `scheduledAt` = **2026-09-17 14:50** = 收口 +5 min · `nextRunAt` = 1789627800000)⇒ 三件技术债全清 —— 幕 2 状态依赖**已修**(幕 2 起手现场重读权威通道归属后**两台都停** ⇒ 与上一幕解耦;`--scene all` **12 PASS / 0 FAIL**)|`COOLDOWN_MS=0` **0 命中**(`4c`/`ctrl` 整体移除并**显式拒绝**,⛔ 不静默空跑)|OBS-11 差额**有名字 = `20000`**(47 实例档、无活跃实例)。证据 = `交接单_检测时延与deadline_20260917.md` **§8.8-4**。
>
> ✅ **序 ⑪ 已收官(2026-09-17 15:0x,规划棒)** —— 出单 = `交接单_观测口径与在册缺陷_20260917.md`(**§8 前缀指纹 `3ece0f870cba67d0113a4c5f9de9d812`**)⇒ **OBS-11 判据 计数 → 白名单集合**(三集包含式 + `nft` 入站 accept 集合)+ 6 条在册小缺陷排序(§5.9);只改观测面,⛔ `src/**` 零改动。⛔ 不要重做规划。
>
> ⏹️ **(存档)序 ⑭ 本轮动作口径(已收官 · 结果见 `交接单_观测口径与在册缺陷_20260917.md` **§10**)**:**序 ⑭ · 执行棒 —— 修掉「relay 流上的 HTTP keep-alive 复用」这条数据面缺陷**(口径 = `交接单_观测口径与在册缺陷_20260917.md` **§9.3**:现象 + **1 条命令的最小复现** + 完整取证链 + 回头条件;⚠️ 不必再出规划单)。**背景(上一棒结论)**:Q1 ✅(`mksess` 改 PG)、Q3 ✅(探针 **12/12**)、**Q2 🔴 未达成** —— 单内记的 `landModels → writeHomeFile` 本地路径问题**不是** `enter` 500 的真因;真因是**同一条连接上的第 2 条 agent 请求必回 `400`(Fastify `clientError`)**,抓包实证 = **worker 侧把 agent 上一份响应回灌给了 agent**。**开工顺序**:① 先按 §9.3 复现;② **补帧级取证**(relay `workerStream`/`dialStream` 路由表、`net/relay/server.ts` 的 stream 表、`dialer.ts` 的 `tcp.pipe(duplex).pipe(tcp)` 收尾、`client.ts#onOpenRequest/onRemoteData`)**定位到代码行再改**;③ 先红后绿;④ 端到端:guest `enter` **200**、同连接连发两次都 **200**、`npm test` 不退化(160/159/0/1)、`--scene all` 不退化、探针保持 **12/12**。**三条硬门**:**D1**(复现类前提未复现前 ⛔ 不许改任何生产值;⛔ 不动 `RELAY_FAILOVER_*`/`HB_SEC`/burst)|**R7**(Q4 `src/net/relay/**` untracked 缺口卡在**提交授权** ⇒ ⛔ 只报告、⛔ 不 commit/push;Q5 `.bak-seq7-*` 残留未动)|🔴 ⛔ **不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练 / 夹具路径**。⛔ **若改动必须超出 `src/net/relay/**` + 其测试 ⇒ 停下报告、别扩大**。⛔ 不 commit / 不 push。**收口必须**:释放锁 + 陈述句告知 + 登记下一棒 + 推进本入口 + 写工作区日志。
>
> ✅ **序 ⑮ 已收官(2026-09-17 16:14–16:3x,执行棒)**:**缺陷 B(拨号池未分配槽位自毁)已修并端到端验收** —— 判据 a/b/c 全绿;**先故意对未分配池口 25000 发一条连接**,随后 guest `enter` 仍 **200/200**、该口同连接连发 3 次 **200/200/200**、打后池口仍 **64**;`npm test` **162/161/0/1**、`--scene all` **12 PASS / 0 SKIP / 0 FAIL**、`overlay-probe` **12/12**。改法 = `onConn` ⛔ 不再 `close()` 服务器(只丢连接 + `stray` 计数 + 点名日志)+ `localPortFor`/`lruIdle` 只认 `server.listening` + `status().pool` 改报"实际在听"并新增 `stray`;⛔ 不取"把槽位从 `slots` 摘掉"那条(= 让任意本地连接**永久**蚕食池容量,R11)。**全部证据 = `交接单_观测口径与在册缺陷_20260917.md` §11**(含 47 上 16:00:31/35/40 三条 500 的时间线归因)。⚠️ 顺带确认一条**环境口径**:对 47/106 的 ssh **必须显式 `-p 22`**(别名 `bt-server` 里的 `32022` 是失效残留口,见该单 §11.10)。
>
> ⏹️ **(存档)序 ⑰ 本轮动作口径(已收官 · 结果见 `交接单_在册收尾_20260917.md` §9)**:**序 ⑰ · 执行棒 —— 在册收尾(S1 → S2 → S3 → S4)**(⚠️ **执行棒:按单开工,无须再出规划单**)。**开工依据 = 工作区根 `交接单_在册收尾_20260917.md`**(**§8 前缀指纹 `bab83b7219b2669d5a6e9f1acf782e1f`**,全文 md5 `ca13b9ded3d71268f8b0e56755d7aea4`)⇒ 按该单 §5 顺序:**S1** mksess 文档口径校正(「DB 直插」→「PG 直插」;只改操作性载体 = `02-运维手册.md` + `skills/**` + 本机技能,⛔ **不改 `04-调整方案/**` 档案正文**)|**S2** 技能 `dsh-auto-handoff-chain` **三处同步建立**(本机 ← 文档库新建副本 ← 47 镜像 `/opt/dsh/docs/skills/`;**与 S1 共用一次 scp** ⇒ 故 S1 必先)|**S3** 清理代码仓 `.bak-seq7-*`(🔴 **范围修正 = 5 个**,全 untracked、均为过期快照 ⇒ 非有效回滚点)|**S4** 幕 4-A 余量判据化(三档:`p95 ≤ 24 s` 健康 / `24–27 s` 临界记录不判 FAIL / `> 27 s` 停下报告)+ `--scene 4` × `DRILL_SAMPLE_N`=5 样本。**验收** = 单内 §6 的 E1–E9(其中 E6 三件套 = `npm test` 162/161/0/1、`--scene all` 12 PASS、探针 12/12)。**两条硬门**:**D1**(不改任何生产值)|**R7**(Q4 ⛔ 只报告、⛔ 不 commit/push)。🔴 ⛔ **不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练 / 夹具路径**。⛔ 不改 `04-调整方案/**` 档案正文、⛔ 不重做已收官序 ①–⑮、⛔ 不做 presence / 房间层 / 内容分发。**收口必须**:释放锁 + 陈述句告知 + 登记下一棒 + 推进本入口 + 写工作区日志 + 回填该单 §8。
>
> ⏹️ **(存档)序 ⑯ 本轮动作口径(已收官 · 结果见 `交接单_观测口径与在册缺陷_20260917.md` **§12**)**:**序 ⑯ · 规划棒 —— 复核「会合 / 中继从 Manager 拆分」是否仍需要 + 给剩余在册项定序、出执行单**(⚠️ **只出规划、⛔ 不改码、⛔ 不动服务器**)。依据 = 工作区根 `会合中继拆分_取证与改造方案_20260916.md`(S0–S4)+ 事实「**R5 已解掉『Manager 必须与 relay 同机』**(Manager 改为只拨出一条 wss、落点搬到本机回环池 `127.0.0.1:25000+`)」。**只读取证 ≤3 条命令**,结论落成二选一:① 已被 R5 覆盖 ⇒ 判「不做」+ 写清证据;② 仍有未覆盖部分 ⇒ 写成含**逐步验收 / 回滚**的 S 步骤。**同一张单**里给剩余在册项定序:`Q5`(`src/net/relay/*.bak-*` 3 个残留)|`seq13 文档口径`(文档库 / 技能对 `mksess.cjs` 的「DB 直插」描述已过期)|`长期在册`(技能 `dsh-auto-handoff-chain` 在文档库无副本 ⇒ 三处同步从未建立)|`§8.8-2`(幕 4-A 19.5–20.8 s 的临界项)。⛔ **Q4(`src/net/relay/**` untracked 的提交 / 推送授权)不写进执行范围**(待你拍板,只登记一行);⛔ 不得自行为 presence / 房间层 / 内容分发定序(需拍板)。**收口必须**:释锁 + 陈述句告知 + 登记下一棒 + 推进入口 + 写工作区日志 + 追加回报。
>
> ⏹️ **(存档)序 ⑮ 本轮动作口径(已收官 · 结果见 `交接单_观测口径与在册缺陷_20260917.md` §11)**:**序 ⑮ · 执行棒 —— 修掉「拨号池未分配槽位被任意一条连接永久杀死」(缺陷 B)**(口径 = `交接单_观测口径与在册缺陷_20260917.md` **§10.8**:现象 + 出处代码行 `src/net/relay/dialer.ts#onConn` + 三个候选修法 + 判据;⚠️ 不必再出规划单)。**背景(上一棒结论)**:序 ⑭ 已修好 relay keep-alive 复用缺陷(guest `enter` 200、探针 12/12),但**从旁路发现了缺陷 B** —— `onConn` 撞到 `key === undefined`(未分配槽位)时只 `slot.server.close()` + `tcp.destroy()`,**不把槽位从 `slots` 里摘掉** ⇒ 该槽位永久死亡,而 `localPortFor()` 的 `slots.find(s => s.key === undefined)` **下次还会选中它** ⇒ `ECONNREFUSED`(实测:池报"64 个口"而 `ss` 只见 63,缺口正是死掉的 `25000`)。**开工顺序**:① 先**不改代码**复现 + 帧级取证(谁/何时连了未分配槽位;`status().pool` 与实际在听口号数何时起不相等);② **定位到代码行**再改;③ 先红后绿(往 `test/relay.test.mjs` 追加用例,⛔ 不许改 `package.json`);④ 端到端:guest `enter` **200**、同连接连发两次 **200**、`npm test` 不退化(现基线 **160/0/1 skip**)、`--scene all` 不退化(现 **12 PASS**)、`overlay-probe` **12/12**(须带 `--table`)。🔴 **本棒专属陷阱**:⛔ **绝不要对「未分配」的池口发连接**(那正是缺陷 B 的触发器)—— 要打池口,**先用一次真实 `enter` 把落点分配出来**,再从日志 `[relay-dialer] 落点 127.0.0.1:<口> -> <逻辑名>:<端口>` 取**已分配**的那个口号。**三条硬门**:**D1**|**R7**(Q4 untracked 缺口卡在**提交授权** ⇒ ⛔ 只报告、⛔ 不 commit/push)|🔴 ⛔ **不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练 / 夹具路径**。⛔ **若改动必须超出 `src/net/relay/**` + 其测试 ⇒ 停下报告、别扩大**。⛔ 不 commit / 不 push。**收口必须**:释放锁 + 陈述句告知 + 登记下一棒 + 推进本入口 + 写工作区日志 + 追加本单 §11。
> ✅ **序 ⑮ 已完成**(automation `1933b18a-0c2a-46a2-829c-440a900fed08` 已执行完毕、入口 §0/§2 已推进)。
> 🏁 **序 ⑰ 已收官(2026-09-17 16:44–17:3x,执行棒)—— 本线「在册收尾四条」全部清干**:**S1 ✅** mksess 文档口径校正(「DB 直插」→「PG 直插」,5 处操作性载体;E1 = 0 行)|**S2 ✅** 技能 `dsh-auto-handoff-chain` **三处同步首次建立**(本机 ↔ 文档库 ↔ 47 镜像,`diff -r` = 0 行、md5 三处同值;S1 的两个技能文件同批上镜像)|**S3 ✅** 代码仓 `.bak-seq7-*` 清理(**5 个 → 0**;untracked、非有效回滚点;`npm run build` RC=0 + lib 产物 md5 零差异)|**S4 ⚠️ 临界档**(`--scene 4` × 5 样本 18772/20121/20246/21223/**24477** ms;p95 保守口径 = **24477 ms** ⇒ 记录入册、⛔ 不判 FAIL、⛔ 未调任何生产值;⛔ 回头条件未触发)。**零回归三件套全绿**:`npm test` **162/161/0/1**、`--scene all` **12 PASS / 0 SKIP / 0 FAIL**、`overlay-probe` **12/12**;收口归零 = 三服务 active + 池口 **64**。**全部证据 = `交接单_在册收尾_20260917.md` §9**(§8 前缀指纹 `bab83b72…` 回填后**未变**;参数表 `8f08e74b…` **未变** ⇒ D1 自证)。
> 🗃 **Q4 已解除**(用户本轮明确授权「执行完毕后 同步到仓库」)⇒ 两个 commit 已推送:**`146c3d2`**(代码/测试面 53 文件)+ **`bc0dd2c`**(文档库面 10 文件);远端 `master` = **`bc0dd2c`**,`git ls-files src/net/relay | wc -l` **0 → 15**(untracked 缺口闭合)。
> 🛑 **本线已无自动接续**:四条在册项全部清干后,**剩余项全部落在「需拍板」区**(presence / 房间层 / 内容分发**定序** = 业务优先级;骨干节点服务范围 = §4 的 A/B 既有待拍板项)⇒ 按登记门禁(`dsh-auto-handoff-chain §3.3`)**不登记下一棒**,等你拍板后再建。
> ⏹️ **(存档)序 ⑱ 本轮动作口径(已收官 · 产物 = 工作区根 `交接单_presence在线态_20260917.md`,§8 前缀指纹 `f612858344077420cff5f1f9ef1c942f`)**:**序 ⑱ · 规划棒 —— presence(在线状态)改造的方案规划**(⚠️ **只出规划**:⛔ 不改任何代码、⛔ 不动 47/106 配置、⛔ 不 commit / 不 push、⛔ 不改任何生产值)。**依据** = `覆盖网络_应用场景与待完善清单_20260916.md` **§五 第 7 步** + `覆盖网络_千台全场景推演_20260916.md` + `覆盖网络_瓶颈落地方案_20260916.md`。**用户拍板** = 「**a 要做**」(2026-09-17 17:1x)⇒ presence 先做。**产出** = 按项目 8 段模板的**执行交接单**。⚠️ **B(内容分发 / 块级内容寻址)仍待拍板 ⇒ ⛔ 本棒不规划、只登记**。✅ **已收官(17:2x)** —— 落点判为**节点 / 端点在线态**(零应用层自证),S0–S7 + E1–E11 + §9 九条回头条件 + 三层回滚已就位;**§4.2 真取舍 = 空、§4.3 待拍板 = 空、§4.4 R5 = 未命中**。⛔ **不要重做规划**。
>
> 🎯 **本轮动作(2026-09-17 17:28 起,唯一一件)**:**序 ⑲ · 执行棒 —— presence(节点在线态)改造落地**(⚠️ **按单开工,⛔ 无须再出规划单**)。**开工依据 = 工作区根 `交接单_presence在线态_20260917.md`**(**§8 前缀指纹 `f612858344077420cff5f1f9ef1c942f`**)⇒ 按该单 §5 顺序:**S0** 只读前置 + 基线采样(§2 的 P1–P5,**零改动**)|**S1** relay 侧 presence 权威表(连接生命周期驱动 + **TTL 安全网** + 同 hostId 设备聚合;**判别器必须落计数**,⛔ 不许只写日志)|**S2+S3** 线协议帧(`SUB/UNSUB/PRESENCE/SNAP`,帧号**末尾追加**)+ **1 s 窗口批合并**|**S4** 消费侧接线(C1 `web/server.ts` / C2 worker,**`/status` 降级为兜底、⛔ 不删**)|**S5** 观测(参数表 `PRESENCE_*` + `OBS-13/14/15` + 探针新项,⛔ 无 `待测` 残留)|**S6** 本机多实例真机验收(N ≥ 4,实测 **稳态 0 帧 / 一次变化 ≤1 帧 / 降幅 ≥ 10×**)|**S7** 收口(部署属 lane 内,**直接做**)。**验收** = 单内 **E1–E11**(零回归三件套 = `npm test` **162/161/0/1**、`--scene all` **12 PASS**、`overlay-probe` **12/12 + 新项**,均**必须带 `--table`**)。**三条硬门**:**D1**(⛔ 不改任何生产值 —— `RELAY_FAILOVER_*` / `HB_SEC` / burst)|**R5**(⛔ 不新增公网监听口 / 不改 nft·nginx;新增监听口·凭据·入站必须仍为 **0**)|**R7**(⛔ 不改 `04-调整方案/**` 档案正文、⛔ 不 commit / 不 push)。🔴 ⛔ **不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练 / 夹具路径**。⚠️ **命中该单 §9 的任一条回头条件**(要动生产值 / 要扩大暴露面 / 要超出该单 §3.1 文件集 / 批合并失效 / 观测跑不出单值 / 出现静默放行 / 要求"先有房间层" / 零回归退化)⇒ **立刻停下报告,别扩大**。⛔ 不做内容分发、⛔ 不做房间层、⛔ 不做打洞实现、⛔ 不重做已收官序 ①–⑰。**收口必须**:释放锁 + 陈述句告知 + 登记下一棒 + 推进本入口 + 写工作区日志 + 回填该单 §8。
>
> 📌 **自动接续登记(2026-09-17 17:2x)**:**下一棒 = automation id `107b8e38-ccbd-4dcf-8b49-25dad7c89be9`**(名称「覆盖网络线-序19执行棒-presence节点在线态」,**一次性**,`scheduledAt` = **2026-09-17 17:28** = 收口 +4 min,`nextRunAt` = 1789637280000)⇒ **已登记自动接续,约 4 分钟后自动开新会话;接续点 = 本文件 §2「🎯 本轮动作」**。(已完成的一次性棒:序 ⑱ 规划棒 = `50882a38-07cd-494c-ae73-cbbf27f3604d`;序 ⑰ 执行棒 = `067b0892-964e-4deb-bf84-4f1c37bebea8`。)
>
> 📌 **(历史)自动接续登记(2026-09-17 16:4x)**:**下一棒 = automation id `067b0892-964e-4deb-bf84-4f1c37bebea8`**(名称「覆盖网络线-序17执行棒-在册收尾(mksess口径/技能三处同步/Q5清理/幕4A临界项)」,**一次性**,`scheduledAt` = **2026-09-17 16:44**,`nextRunAt` = 1789634640000)⇒ **已登记自动接续,约 5 分钟后自动开新会话;接续点 = 本文件 §2「🎯 本轮动作」**。(已完成的一次性棒:序 ⑯ 规划棒 = `91c53ef8-c3f0-4bb3-9f0a-943790178176`。)
>
> 📌 **(历史)自动接续登记(2026-09-17 16:3x)**:**下一棒 = automation id `91c53ef8-c3f0-4bb3-9f0a-943790178176`**(名称「覆盖网络线-序16规划棒-会合中继拆分复核与在册收尾定序」,**一次性**,`scheduledAt` = **2026-09-17 16:36**,`nextRunAt` = 1789634160000)⇒ **已登记,约 5 分钟后自动开新会话接续,接续点 = 本文件 §2「🎯 本轮动作」**。
>
> 📌 **(历史)自动接续登记(2026-09-17 16:1x)**:**下一棒 = automation id `1933b18a-0c2a-46a2-829c-440a900fed08`**(名称「覆盖网络线-序15执行棒-拨号池槽位自毁缺陷B修复」,**一次性**,`scheduledAt` = **2026-09-17 16:14** = 收口 +4 min,`nextRunAt` = 1789632840000)⇒ 已登记,会自动开新会话接续。(已完成的一次性棒:序 ⑭ 执行棒 = `3e0a7b01-9257-4b76-b82f-4998c9f7eab0`;序 ⑬ 执行棒 = `a513b25c-9399-4fd5-9a00-a19a04c36d74`。)
>
> ⏹️ **(存档)序 ⑬ 本轮动作口径(已收官 · 结果见 `交接单_观测口径与在册缺陷_20260917.md` §9)**:**序 ⑬ · 执行棒 —— 在册小缺陷清算 `Q1 → Q2 → Q3`**(口径 = `交接单_观测口径与在册缺陷_20260917.md` **§5.9 表** + 本棒 §8.8 转办;**该单已给每条的最小验收与回滚 ⇒ 直接执行、无须再出规划单**)。按序三条:**Q1** `/opt/dshs/mksess*.cjs` 仍写 **SQLite 旧库** ⇒ **已失效**(判据 = 用它开一次临时 session ⇒ 实例面 `curl -w '%{http_code}'` 回 **200/401**,⛔ 非 `000`;用完即删。**它是 R4 规定的唯一实例面验收手段 ⇒ 不修则 Q2/Q3 连"做完了没有"都判不了**)|**Q2** guest(**w-106**) 实例页 **502**(真因 = 平台 `landModels → writeHomeFile` 用**本地路径**写**远端**实例 home;判据 = 同 Q1 的临时 session ⇒ w-106 实例页 **200**)|**Q3** `OBS-09` 实例面 `000`(**环境态非缺陷**:47 无活跃实例 ⇒ 起一个 ⇒ `OBS-09` 绿 **且** 新 `OBS-11` 在"有实例态"下仍绿 = `20000` 落在允许区间)。⚠️ **Q3 与 Q2 同批做最省**(同一临时 session 一次验两件)。**三条硬门**:**D1**(分解类前提未复现前 ⛔ 不许改任何生产值)|**R7**(范围外先报告、不动手 ⇒ **Q4 `src/net/relay/**` untracked 缺口卡在"用户授权"、⛔ 本棒只报告;Q5 `client.ts.bak-*` 残留 = 清理类,待 Q1–Q3 后办**)|🔴 ⛔ **不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练 / 夹具路径**(该值看似合法、实际自锁)。⛔ **不 commit / 不 push**(Q4 要授权)。**收口必须**:释放锁 + 陈述句告知 + 登记下一棒 + 推进本入口 + 写工作区日志。
>
> 📌 **自动接续登记(2026-09-17 15:2x)**:**下一棒 = automation id `a513b25c-9399-4fd5-9a00-a19a04c36d74`**(名称「覆盖网络线-序13执行棒-在册小缺陷清算Q1-Q3」,**一次性**,`scheduledAt` = **2026-09-17 15:24** = 收口 +3 min,`nextRunAt` = 1789629840000)⇒ 已登记,会自动开新会话接续。(已完成的一次性棒:序 ⑫ 执行棒 = `52645861-6895-459c-a07e-a5d686787d9c`。)
>
> ~~(存档)序 ⑫ 执行棒口径:**序 ⑫ · 执行棒 —— 观测口径重构(`OBS-11` 计数 → 白名单集合)**。✅ **已收官(2026-09-17 15:3x)**:见上 §0 最新刷新行;产物 = 代码仓 `scripts/overlay-probe.cjs`(新增夹具模式)+ 参数表 §6 四键;`overlay-probe` **11/12**;参数表指纹 `8f08e74b026e6e5b5e1b3db813f031ae`。⛔ 不要重做。~~
>
> ~~(存档)序 ⑪ 规划棒口径:**序 ⑪ · 规划棒 —— 观测口径重构 + 在册小缺陷清算排序**。⚠️ **只出规划、不改码**(规划与执行分离):产出交接单(8 段模板)+ 为下一棒定序。范围**仅限边界内技术项**:① **OBS-11 判据从「计数相等」改为「白名单集合」**(现口径两处结构缺陷:对实例/端点在线态**敏感 ⇒ 假红**;对"一进一出"**替换式变化不敏感 ⇒ 假绿**),连带处理 `LISTEN_COUNT` 的"有/无实例态"两值与 `scripts/overlay-probe.cjs` 改造;② **在册小缺陷清算排序**(`OBS-09` 实例面 000|guest(w-106) 实例页 502|`mksess*.cjs` 失效|`src/net/relay/**` untracked 留档缺口|`client.ts.bak-seq7-*` 残留|参数表 `LISTEN_COUNT`=79 与现态差的 1 条无名口)。⛔ **不得自行为"大方向 / 业务优先级"定序**(presence / 房间层 / 内容分发 = **需用户拍板**,只列候选与优缺点、不决)。➕ **顺手办(同一棒内)**:`.workbuddy/memory/MEMORY.md` 的**真瘦身** —— 现 **8 121 字符 > 注入上限(实测 ≈7 78x)** ⇒ 每轮尾部被截断;把 §二 里"已有 PLAYBOOK 指针"的规则**下沉为一行指针**(体积只减不增)。**✅ 已办(15:0x)= `MEMORY.md` 8 121 → 7 712 字符**。⛔ 不 commit / 不 push。~~
>
> ~~**序 ⑩ · 执行棒(已收官 · 内容留档)**~~:**序 ⑩ · 执行棒 —— 覆盖网络线技术债清算(3 件)**。**直接出执行棒、无须规划棒**:三件均已定位、均属 §1 边界内技术项(实现路径 / 排查方法 / 脚本自洽),**无可上抛项**。三步:**① 演练脚本自洽 —— 幕 2 的状态依赖**(`scripts/overlay-failover-drill.cjs`:注释写"两台全杀"、实现只停 106,而幕 1 已把 Manager 通道切到 47 ⇒ 停的是当前**不用**的那台 ⇒ 120 s 窗口 0 行 `[relay-skip]`;修法 = 幕 2 先判定当前活跃通道再停它,并复验正确前置下 3 PASS);**② 清掉 `COOLDOWN_MS=0` 的 5 处命中**(全部序⑧ 遗留 ⇒ 该值已判「看似合法、实际自锁」⇒ 从演练 / 回滚路径彻底移除,改用合法构造);**③ OBS-11 的 `78 → 77` 那 1 口定位**(两次连采同值、差 1 < 2 未命中回头条件 ⇒ 需把 78 与 77 逐口对账,定位是哪一条消失)。**验收**:`--scene all` 一键全绿(含幕 2)+ `grep -c 'COOLDOWN_MS=0' scripts/` = **0** + OBS-11 差值**有名字**(否则写明"已排查 N 条、卡在哪、回头条件")。**三条硬门**:**D1**(分解类前提未复现前 ⛔ 不许改任何生产值)|**R7**(范围外缺陷先报告、不动手)|🔴 ⛔ **不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练路径**。⛔ 不 commit / 不 push;⛔ 不做 presence / 房间层 / 内容分发。**收口必须**:释放锁 + 登记下一棒 + 推进本入口 + 写工作区日志。
>
> 📌 **自动接续登记(2026-09-17 14:1x)**:**下一棒 = automation id `be7242dd-ac3e-43d0-b52c-a465683cf972`**(名称「覆盖网络线-序10执行棒-技术债清算」,一次性,`scheduledAt` = **2026-09-17 14:23**)⇒ 已登记,会自动开新会话接续。(已完成的一次性棒:序 ⑨ 执行棒 = `2ca4048b-ab74-4845-8964-47a88910c679`。)
>
> ~~(存档)序 ⑨ 规划棒口径:~~ **序 ⑨ · 执行棒 —— 按 `交接单_检测时延与deadline_20260917.md` 开工**(工作区根;8 段模板;**出单前缀指纹 = `d903b4eeabf25ef381379cdbaac77e8a`**)。**核心结论(规划棒已用现有日志分解完成,执行棒须先复现)**:30 s ≈ **检测 15.0–15.1 s**(= `gracefulBurstMs` 的 15 s 地板,⛔ **不是** 2.5×`HB_SEC`=37.5 s,上单 §8.8-2 的口径提示已在单内勘误)+ **白等 12.0 s**(`waitUpOn` 只轮询 `state==='up'`、**不对终态失败早退** ⇒ 对一个必然失败的同机候选吃满 `upTimeoutMs`)+ **建连 2.7–2.8 s**;两个逐行复核样本(`unhealthyForMs=30563` / `29376`)结构完全一致。**首选修法 = `waitUpOn` 死候选早退**(三处装配点 `server.ts` / `relay-tunnel.ts` / `relay/main.ts` 逐处验收),⛔ **不调 `RELAY_FAILOVER_DEADLINE_MS`、⛔ 不动 `HB_SEC`、⛔ 不动 burst 语义**。**开工第一件事 = 单内 §2-P10 分解复现**(复现不出,或 S2 采到任一 < 27 s 的样本 ⇒ 停下报告)。**OBS-11 已复取 = `77`**(47 无活跃实例档,口径 78;差 1 < 2 ⇒ 未命中 §8.8-3 回头条件;两次连采同值)。**三条硬门**:**D1**(⛔ 分解复现成功前不许改任何 `RELAY_FAILOVER_*` 生产值)|**R7**(范围外缺陷先报告、不动手)|**§8.8-4**(🔴 ⛔ 不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练路径)。⛔ 不 commit / 不 push;⛔ 不做 presence / 房间层 / 内容分发(清单第 7 步)。**收口必须**:释放锁 + 登记下一棒 + 推进本入口 + 写工作区日志。
>
> ~~(存档)序 ⑥ 规划棒口径:**序 ⑤ · 执行棒已收官** —— 证据 = `交接单_参数表与观测_20260917.md` **§8 执行回报**(可复现指纹见该单 **§8.9**;E1–E9 = **8 绿 + 1 部分绿**;唯一部分绿 = E5 的"在 47 上受控制造一次真实拨号",卡点 / 已做到哪一步 / 回头条件见该单 **8.7-①**)⇒ **下一棒 = 序 ⑥ · 3–5 台最小形态**:按 `覆盖网络_应用场景与待完善清单_20260916.md` **§五 第 6 行**开工 —— **先把 `参数表_覆盖网络_20260917.md` §7 的 4 个 `待测` 项 + 1 个待校准推导项换成实机实测**(打洞率 / 每玩家带宽 / 跨云稳态吞吐 / 中继 jitter / `MEM_PER_HOST_MB`),并**合批**落地 **L3 第二中继机**(参数表 §5.2 校验③ 已算出:千台 L3 需求 450 > 单台 `--max-hosts` 225 ⇒ **需 ≥2 台中继**)。⚠️ **回头条件已写死**:`MEM_PER_HOST_MB` 或 `WAN_STEADY_THROUGHPUT` 一换成实测 ⇒ **必须重算 `RELAY_MAX_HOSTS` 并重下发 47 的 `capacity.conf`**。⚠️ 两条待办随本轮带过来:① guest(w-106) 实例页 **502**(`landModels → writeHomeFile` 用**本地路径**写远端实例 home;且 `/opt/dshs/mksess*.cjs` 仍写 SQLite 旧库、**已失效**);② 🔴 **106 agent 面不吃引导链**(`worker/agent.ts` 把 `DSHS_RENDEZVOUS_URL` 直接当 relay URL 用;按字面撤掉 = `tunnel===undefined` 生产回归,**已判不执行**)。⛔ 不重做序 ②/③/④/⑤;⛔ 不做 presence(清单第 7 步);⛔ 不 commit / 不 push。
>
> ~~(存档)原规划棒口径:**序 ⑤ · 规划棒 —— 给「参数表 · 观测 · 权限评估」出一份可执行交接单**(落盘工作区根,命名 `交接单_参数表与观测_20260917.md`,按 8 段模板:目标 / 只读前置 / 范围 / 决策点 / 步骤 S0–Sn / 逐条验收判据 / 回滚 / §8 回报格式)。**必带输入** = `交接单_443兜底_20260917.md` **§8.7 的四条遗留** + 该单 **§8.8 的 45% 口径待填参数位**(relay `--max-hosts` / `/status` 的 `capacity{max,used}` / `retryAfterMs`)+ **L3 跨机真容灾**(命中 **R5**,须先出权限影响评估)。⛔ 本轮**只出单、不改服务器**(规划与执行分离);**不 commit / 不 push / 不做 presence**;收口必须释放锁 + **登记下一棒**。~~
>
> ~~(存档)原规划棒口径:**序 ④ · 规划棒 —— 给「443/TCP 兜底」出一份可执行交接单**(落盘到工作区根,命名 `交接单_443兜底_20260917.md`,按 8 段模板:目标 / 只读前置 / 范围 / 决策点 / 步骤 S0–Sn / 逐条验收判据 / 回滚 / §8 回报格式)。**背景**:现役只有 `wss://alotbuy.com/dshs-relay` 一条路且经 nginx + CF(单点);序 ④ 要补的是**CF / nginx 不可用时的 443/TCP 直连兜底**(异构设计纪律:relay 不按同构 15%,而是 **45% 设计、55% 留余量**)。**取证入口** = `覆盖网络_应用场景与待完善清单 §五`(主线第 ④ 项)+ `覆盖网络_补遗与参考方案_20260916.md` + `覆盖网络_全球架构复盘_20260916.md`。⛔ 本轮**只出单、不改服务器**(规划与执行分离);**不 commit / 不 push / 不做序 ⑤ / 不碰 presence**;收口必须释放锁 + **登记下一棒**。
> 🟢 **最新口径覆盖本节历史内容(23:0x)**:**R2 / R3 / R4 / R5 全部已完成并端到端验收**(→ 交接单 **§9 / §10.4 / §10.5 / §11**)⇒ **机制层 R0–R5 已齐**(**R5 已解掉"Manager 必须与 relay 同机"这条前提**)。
> 🔴 **下一动作已勘误**:⛔ **不是** "presence 改造"。按 `覆盖网络_应用场景与待完善清单 §五` 的主线,presence/房间层是**第 7 步**,前置为 **② 网抽象 + 地址规划 + 引导(P0-1/2/3) → ③ 一机一钥 + 信任根 → ④ 443/TCP 兜底 → ⑤ 参数表 → ⑥ 3–5 台最小形态**。(另:仓库里目前**没有任何应用层代码** —— 全仓唯一含 "presence" 字样的文件是 `src/net/relay/placement.ts` ⇒ "presence 改造"根本无从下手,这条旧指示是上一轮的笔误。)
> ✅ **2026-09-17 07:4x:序 ② 已收官(①②③ 全绿,其执行进度见 `交接单_网抽象与地址规划R6_20260916.md`)** ⇒ 🟢 **2026-09-17 07:5x 已定序:下一棒 = 序 ③(一机一钥 + 信任根)**,之后 ④ / ⑤(理由见 §0)。
> ⛔ **不要再做 R2 / R3 / R4 / R5、不要再开 `32023`、不要再做 SSH 版中继**(隧道凭据与 `32022` 都已收回,那条路已不可能重建)。
> (按顺序 · **2026-09-16 11:5x 刷新**)
1. **先抢锁**:`bash dsh-server-docs/scripts/handoff-guard.sh --claim-exec "<会话名>"` —— 锁已释放,抢到即可开工。
2. **【第一优先 · 真活】会合 / 中继从 Manager 拆分的落地**(方案:工作区根 `会合中继拆分_取证与改造方案_20260916.md`,S0–S4 分步 + 每步验收 / 回滚)
- ✅ **P1 / P2 / P3 已完成并验收(2026-09-16 15:5x–16:2x)** —— 全部实测证据在 **交接单 §8**(含命令原文级输出)。摘要:
· **P1** 47 对齐到 `640813e`(Manager 侧真补上 S0+S1)|多用户验收:`admin`(w-47) 与 `guest`(w-106) 实例页均 200、跨机文件面落盘已核实
· **P2** `dsh_hosts.via` 列(迁移 v8)|回填 `w-47=local` / `w-106=manager-ssh`|`hostsProvider` 走 `RendezvousRegistry`|`GET /api/admin/hosts` 已可见 `via`
· **P3** **实例端口区间隔离**(`w-47` 20000+ / `w-106` 21000+,避开 OS 临时段)⇒ 修掉「跨 worker 同号 ⇒ 47 的 `127.0.0.1` 撞号 ⇒ `tunnel.forward()` 返回值被忽略 ⇒ **静默拨到别人实例**」这条真 bug;两实例实测落 20000 / 21000 且互不重叠
- ✅ **P4(SSH 版中继 / 新开 32023)已判「不做」**(17:2x)—— SSH 反向隧道**不是"不安全"**(专用非特权账号 + `PermitOpen` + 限速是业界标准做法),但**不该长期做数据面**;它想要的两样(与宝塔 sshd 解耦、甩掉 root 凭据)在 relay 方案里**同样拿到** ⇒ 两步合一。⛔ **不要新开 32023、不要再做 SSH 版中继**。
- ✅ **R0/R1/R1.5/R2 已完成**(R0 判据表+链路画像、R1 relay 最小闭环、R1.5 韧性+准入选点、**R2 常驻+nginx 443 暴露**)⇒ 全部证据在方案 **§9–§14**。
- ✅ **R3 + R4 已完成并端到端验收(2026-09-16 21:37–22:2x)** —— R3=`RendezvousRegistry` 已接线 `RelayRendezvous`(含 relay 实时快照 + 15 s 陈旧回退)+ 106 常驻 + `w-106 via='relay'`;R4=实例面经 relay(`PORT_ADD/DEL/ACK` + `RelayTunnel` + 端点翻译)+ **sshd 反向隧道下线且不可能重建**(47 收回隧道公钥、`32022` 回收;106 摘 `DSHS_TUNNEL_*`、私钥移走)⇒ 47 公网暴露面**净减 1 口**。证据 = 交接单 **§10.5**(含一份"静默失效"缺陷的定位与修复 + 先红后绿回归测试)。
- ✅ **R5 = 会合可换机 已完成并端到端验收(2026-09-16 22:3x–23:0x)** —— Manager 改为 `dialer: true` **只拨出**一条 wss,落点在**自己本机**预绑池(`127.0.0.1:25000..26099`,64 口);新增 `DIAL(0x0e)/DIAL_ACK(0x0f)` 帧、`StreamPeer` 抽象(数据面同形)、relay 侧 `DSHS_RELAY_DIALERS=manager` 白名单 + 每机独立密钥(权限只收窄)。证据 = 交接单 **§11**:① 单测 **38/38**;② E2E `endpoints: [(19000, 41233, 0)]`(**relay 回环口 streams=0**)+ `DIAL manager -> w-106:21000 ok`(实例面)+ 门户 200/62451 B;③ 🔴 **换机等价实验**(清掉 `DSHS_RELAY_STATUS_URL` ⇒ Manager 对 relay 回环口一无所知)**仍全通**,两个回环端点 `streams` **全为 0**;④ 新增 64 口**全部仅回环**,公网零新增。
- ⏳ **下一步 = 主线第 ② 项「网抽象 + 地址规划 + 引导」**(`覆盖网络_应用场景与待完善清单 §五`)—— ⛔ **不是 presence**(那是第 7 步)。⛔ 不要再做 R0/R2/R3/R4/R5。
🟢 **阶段 ①(S0–S4 + R5)已完工;`清单 §11.6` 记下的两条缺陷(A1 假 404 / A2 锁信号)也已修完并端到端验收**(→ R2-R4 单子 **§12**)。
🟢 **2026-09-17 07:4x:② 的 Step 3(P0-3 名字解析与授权)也已完成并端到端验收 ⇒ ② 全单关闭(①②③ 全绿)**(证据 = ② 单的「执行进度」段)。⇒ **下一棒不再看 ② 的单子**,按 `清单 §五` 主线往下走(序 ③ 一机一钥 + 信任根 / 序 ④ 443/TCP 兜底 / 序 ⑤ 参数表·观测·权限评估 —— **开工顺序已于 07:5x 定为 ③ → ④ → ⑤**,理由见 §0)。⛔ 不要再重做 Step 1 / Step 2 / Step 3。
- ✅ **A1 已修并端到端验收(2026-09-16 23:2x–23:4x)** —— `RemoteUserFs.target()` 原在 `agentFor(hostId)` 查不到时**静默回退** `DSHS_CLUSTER_AGENT_URL`(= w-47 本地 agent)⇒ 每次 `restart dshs` 的窗口期内 w-106 用户回**假 `404 not_found`**(与“文件夹不存在”同形,误导排查)。**真因** = 文件面 `server.ts:262` 的 `hostDirectory` 是惰性 Map、此前**只被 spawner** 刷新 ⇒ 启动时表里只有本机。**修法** = `ensureHost` **按需补齐**(治本)+新增码 `host_unresolved` → **503 且零请求发默认机**(治安全)。**证据** = R2-R4 单子 **§12**(先红后绿 + 冷启动 `launch 200` + relay 有 `DIAL` + `not_found=0`)。判别器仍是:**relay 有没有 `DIAL`**。
- ~~原 R0 描述~~(已过期,仅存档):只看 `接续包_覆盖网络线_20260916.md`(它 17:29 已同步):**R0-a** 把六条判据(认证模型 / 默认姿态 / 零入站 / 爆炸半径 / 可观测 / 生态)补成**可打分表**;**R0-b** 测链路画像(本机↔47↔106 的 RTT / jitter / 带宽)。**R1 起才选 relay 实现**(候选 OpenZiti / Nebula 身份型优先 > rathole > frp)。⛔ **本轮不引入任何第三方 relay**。
- 🔴 **本 §2 的口径截至 2026-09-16 17:29**(17:2x 起 P4 改判、17:3x 撤销"首选 frp")。⚠️ **接续包每次更新后,必须回来同步本入口** —— 16:43–17:29 期间本入口曾滞后 41 分钟,导致 17:04 那位自动接续会话按旧口径得出「P4 **并入自研 relay**」的结论,与最新定案(**实现未定、先做 R0**)**表述不一致**(方向一致、粒度不同)。⛔ 别再让"下一棒的第一信息源"比接续包旧。
- ⏳ **未做**:git commit(未授权)、档案占号建档(建议与 P4 合并做,届时才有 commit hash 可写)。
3. **平台那批待办** ✅ **已清**:`BRIEF.md` 刷新至 09-16|T08 单子已归档 `交接单/archive/交接单-已完成/`|`.doing-T08` 已随 T08 归档(内容保留为 `T08-执行标记-已释放.md`)|`04-调整方案/.lock-*` 16 个空占号残留已清|未提交改动已按 5 个定向 commit 提交(HEAD `59f1a89`,**已 push**,远端 `master` 一致)。
⏳ **仍未动**:`INDEX.md §二` 的既有失真(`04-90~04-102` 共 13 行物理落在表格外 ⇒ `docs-index-stats.py` 统计不到)—— 搬别人的行有风险,留待专项。
4. **10 份文档转正式档案** ✅ **已完成**(档案 103–112,`4e3a1a4`;正文行覆盖率 98.5–99.0%)。
5. **`MEMORY.md` 精简** ✅ **已完成**(工作区 -26%;**真被截断的是用户级那份**,已按「分层重排、零删除」修好)。
6. **技术侧开工**:按 `覆盖网络_瓶颈落地方案_20260916.md` 的「只做三件事」(presence 改造 + 游戏服放 L1 + 块级内容寻址)开工;**前置仍是第 2 条**。
7. **代码分层 / 依赖方向**(与覆盖网络并行的一条线)—— ✅ **P0 已完成**(2026-09-16 13:2x,本机):新增 `docs/architecture.md`(四层 `①入口 → ②领域 → ③能力 → ④基础` + **R1–R4 判据** + 三条工程纪律)+ `README.md` 文档表登记。
- 📄 评估文档 = 工作区根 `项目代码_分层范式与迭代风险评估_20260916.md`(**§8 已勘误**)。
- 🔴 **勘误(勿再复用旧结论)**:所谓「**3 处基础层反向依赖**」**不成立** —— 那几处是**入口层 `cli.ts`** 的合法依赖(`①→③`);实测 `src/*.ts` 里从子目录 import 的**只有 `cli.ts`**,基础层四文件**零 import** ⇒ **真实违规 0 处**。
- ✅ **可执行判据已补(同日)**:`scripts/check-layering.mjs`(`npm run check:layering`,已挂进 `npm run verify`)—— 静态扫描四层 + **ratchet 基线**(`scripts/layering-baseline.json`,新增违规 ⇒ 退出码 1;只许减)。**首跑实测 60 个 .ts**:`①24 / ②0 / ③32 / ④4`,**现存违规 5 条**(全是 `③能力→①入口`:`supervisor/proxy.ts`→`web/auth.ts`+`web/middleware/authn.ts`;`fs/{local,remote}-user-fs.ts`+`fs/workspace.ts`→`web/middleware/fs-guard.ts`)。
- ⏳ **剩三件(均命中 R7,动工前先出受影响清单)**:① **整改上面那 5 条**(修法已写在 `docs/architecture.md §5.1`)② 补 `src/domain/*`(把 `web/routes` 的业务规则抽出来 —— **行为敏感重构**,非纯类型)③ 给模块入口补「职责 / 依赖谁 / 被谁依赖」。
- **顺序建议**:**P0 ✅ → 覆盖网络 S1–S4 → P1**(P1 不阻塞覆盖网络)。
8. ✅ **两条「已定位未修」缺陷 —— 均已于 2026-09-16 23:4x 修完并端到端验收**(证据 = R2-R4 单子 **§12**):
- **① `state.py` 锁误报(安全信号)**:`state.py:39` 原把 `交接单/.exec-lock` 当**文件**读,而 `--claim-exec` 建的是**目录**(内含 `OWNER`)⇒ 永远报「[锁] 空闲 ✅ 可以动手」⇒ **每个新会话读到的第一个信号是错的**。**已修**:是目录就读 `LOCK/OWNER`(兼容遗留的普通文件形态);复验 = 持锁时正确报「🔴 被占用」。
- **② 假 404 / 静默回退到错 agent**:见上方第 2 条末段(`src/fs/remote-user-fs.ts` 的 `target()`) —— **已修**。
> ✅ **上一轮已完成(2026-09-17 17:28–18:16,执行棒)**:**序 ⑲(presence 节点在线态)已收官并上线** —— 详见 §0 最新那条刷新的全部数据;**产物 = `交接单_presence在线态_20260917.md` §8★ 回报(8.0–8.9 已回填)**。一句话:**在线态从"每 5 s 拉 `/status`"改为"连接生命周期 + 订阅推送 + 1 s 批合并 + TTL 安全网"**,真机 `counters={subs 1, pushed 3, snaps 2}`、单测 **169/0/1**、`--scene all` **12 PASS / 0 FAIL**、S6 本机多实例 **8/8**。⛔ 不要重做序 ⑲。
>
> 🎯 **序 ⑳ 的动作已定(本棒登记)**:① **修 `OBS-09` 口径** —— 「实例不在册 ⇒ **SKIP**(⛔ 不是 PASS、也不是假红);在册 ⇒ 必须可达 `PROBE_CODE_SET`」+ 两侧夹具实证;② **量化 presence 真机收益**(`counters.statusHits` 增速 ⇒ `/status` 轮询降幅,给算式与实测);③ presence 线**收口复验**(`npm test` / `--scene all --table` / `overlay-probe --table`);④ 处置 `web/server.ts` `translateEndpoint` 的**键口径既存缺陷**(`hostVia` 表键 = 逻辑名、传入裸 `hostId` ⇒ 恒 `undefined`)。
> ➡️ **下一棒 = 序 ⑳(执行棒)**,automation **`51cfd12f-918d-4069-81d8-c148fc174282`**(一次性 · **2026-09-17 18:20** = 收口 +4 min)。⛔ 未登记更后一棒:**`OBS-09` 的判据口径修正属技术实现 ⇒ 本棒自决**,但「**平台是否要在 Manager 重启后自动拉起既有实例**」是**业务取向**(可用性 vs 资源/复杂度)⇒ **待拍板,⛔ 不作为任何一棒的前置**(详见本棒回报的最后一节)。
---
## 3. 关键决定(**已定,勿再上抛**)
1. **方案只做技术实现;跨境数据合规由使用者自行考虑**(用户原话)⇒ 选型不为合规让路、跨境不设限、⛔ 不再把合规当前置条件。
2. **单机自用 ≠ 不需要互联**(用户原话:「单机用也要互联,这正是建立覆盖网络的目的」)⇒ **租户维度收窄 ≠ 网络维度收窄**。
3. **覆盖网络按异构设计**:中继按 **45% 设计 / 55% 留余量**(不是同构假设的 15%);**必须补 443/TCP 兜底**;有公网 IP 的节点升格为中继候选。
4. **权威状态单点**:归属/租约/骨干资格只能控制面写;会合/中继可多实例。
5. **形态已拍板**:单机自用为默认;"当服务器给他人用"的多人维度已放弃。
6. **游戏重点是 MMORPG(2D/2.5D)/ MUD / 传奇类** ⇒ 判定上修为"最匹配",非 3D 强实时竞技。
## 4. 待拍板(唯一一项,真取舍)
**骨干节点的服务范围**:A 只服务自己名下设备(优点:无资源/计费纠纷、权限面不变;缺点:骨干少、冗余低)| B 服务全网(优点:骨干多、可用性高、中继容量易满足;缺点:他人流量跑在你的机器上、元数据暴露、被攻破影响面大)。
**倾向 A→B 渐进**。详见 `覆盖网络_骨干层方案_20260916.md` §7。
---
## 5. 回滚点
- **R5(本轮)三层回滚,均秒级**(详见交接单 §11.7):
① **Manager 拨号通道** = 删 manager drop-in 里 `DSHS_RELAY_DIAL_*` 5 行 → `daemon-reload` → `restart dshs`(`addressOf` 自动回退到 R3 的 `/status` 快照路径,"relay 同机"假设回来);
② **relay 侧白名单** = 删 `dshs-relay.service.d/dialers.conf`(不删也行:无拨号方接入 ⇒ 行为同 R3);
③ **代码级** = revert `src/net/relay/*` + `src/config.ts` + `src/web/server.ts` → `npm run build` → scp `lib/` → `restart dshs`。
⚠️ 备份:`/etc/dshs/relay-keys.json.bak-r5-20260916-224838`、`/etc/systemd/system/dshs.service.d/cluster.conf.bak-r5-20260916-224838`(及 `.bak-r5swap-20260916-225546`)。
- **缺陷 A1/A2(本轮)回滚**(详见交接单 §12.5):A1 = `cp -a /opt/dshs/lib-bak-a1-20260916-232914/lib/. /opt/dshs/lib/` → `systemctl restart dshs`(回到"假 404"复现);A2 = 还原 `state.py` 第 1 节为"按文件读"(**不建议**)。
⚠️ **A1 的备份目录只有这一份**(推送过程中滚动过两次,同名文件以后者为准,两次都在同一个目录里)。
- **有意保留、⛔ 别当"没做完"**:R5 新增的 `127.0.0.1:25000..25063` **仅回环**(公网零新增);relay 仍为端口绑动态回环口(`exposeLoopback` **有意未关**,理由见交接单 §11.5)。
- 文档侧:10 份为**新增**;被修改的 3 份(客户端化部署方案 v3 / 桌面客户端方案 v1 / 可行性评估)改动均为**追加或标注**,可反向还原。
- 服务器侧待复核事实:控制面机规格(1.8 GB / 2 核 是 09-08 旧记录);47↔106 跨云实测 **~22 KB/s**。
> 📌 交接单指纹(2026-09-16 23:4x):`tail -n +4 交接单_relay落地R2-R4_20260916.md | md5sum` = `b110b5c4e3eb8afcac08af537d333c99`(**本单已执行完毕**:R2→§9、R3 Step 0→§10.4、R3 本体+R4→§10.5、**R5→§11**、**缺陷 A1/A2→§12**)
> 📌 **② 的交接单**(**已执行完毕**):`交接单_网抽象与地址规划R6_20260916.md`,指纹 `0d3f1f305e9e6219afa97cdfecff05ba`。
> 🟢 **2026-09-17 07:4x 更新(② 收官)**:**Step 1(P0-1 网抽象)/ Step 2(P0-2 引导三级链)/ Step 3(P0-3 名字解析与授权)全部落地并端到端验收** ——
> 证据见该单的「**执行进度**」段(含判据命令与实测输出)。**② 单关闭 ⇒ 它不再是下一棒的开工依据**(指纹留着只用于"文档有没有被改过")。
> ⛔ **不要重做 Step 1 / Step 2 / Step 3**。下一棒按 `清单 §五` 往下:序 ③ 一机一钥 + 信任根(P0-4)/ 序 ④ 443/TCP 兜底 / 序 ⑤ 参数表·观测·权限评估
> —— **开工顺序已于 2026-09-17 07:5x 定为 序③ → 序④ → 序⑤**(理由见 §0)。