54 lines
14 KiB
Plaintext
54 lines
14 KiB
Plaintext
# DSH 平台 — 记忆(**状态层**)
|
||||
|
|
|
|||
|
|
> **判据**:不看它会不会导致「违规」或「事故」?会 ⇒ 写实体;只"更慢更绕" ⇒ 只留指针。
|
|||
|
|
> **落点**:动作前必须生效的规则 → 根 `CODEBUDDY.md`(**不重复**)|会变的状态 → 本文件|方法论 → 技能|一次性事实·命令原文 → `PLAYBOOK` / `04/<NN>`|当天流水 → `memory/YYYY-MM-DD.md`|跨项目偏好 → `~/.workbuddy/MEMORY.md`。
|
|||
|
|
> 📄 **详版 = `.workbuddy/memory/PLAYBOOK-实例与插件坑.md`**(**报障第一件事就开它**,§1–12)。
|
|||
|
|
> ⚠️ **注入上限(超了会被截断)** ⇒ 新增**按序插入对应小节,⛔ 别追加到末尾**;**体积只减不增**;**历史叙事一律不进本文件**(进交接单 §8 / 当天日志)。
|
|||
|
|
|
|||
|
|
## 一、状态速查(会变的都在这)
|
|||
|
|
|
|||
|
|
| 项 | 状态 |
|
|||
|
|
|---|---|
|
|||
|
|
| **在途单** | **下一棒 = 序 ⑦ · 规划棒**(主题 = **中继失败切流**,产物 = `交接单_中继失败切流_20260917.md`);automation = **`f9acaf5a`**(09-17 10:58)。序 ①–⑥ 全收官,⛔ 不要重做。⚠️ **唯一未过项** = 序⑥ **E9 后半「杀掉任一台中继 ⇒ 客户端不自动切另一台」**(根因与判据见参数表 §9 第 5 行;判为**新功能**)。⚠️ 两条老待办随轮带过来(细节见入口 §2):① guest(w-106) 实例页 **502**;② 🔴 **106 agent 面不吃引导链**(`worker/agent.ts` 把 `DSHS_RENDEZVOUS_URL` **当 relay URL 用**;撤掉 ⇒ `tunnel===undefined` ⇒ **已判不执行**)。⚠️ `/opt/dshs/mksess*.cjs` 已失效 ⇒ 开临时 session 用 **PG 直插**。⛔ 非 presence(第 7 步) |
|
|||
|
|
| **生产形态** | 47 = **Manager**(cluster;控制面库 = 47 的 PG13 `127.0.0.1:15432`,单元 `dshs-pg`)+本地 Worker `w-47`(19100);**106 = Worker `w-106`**(19000)。存量锚 `w-47`、新用户按容量落 `w-106`(**粘性优先**)。⚠️ **权威库 = 47 的 PG**(`/var/lib/dshs/dshs.db` 仅回滚用)。⛔ 47 兼作 Worker 是**有意设计**。**回滚** = 删 drop-in `dshs.service.d/cluster.conf` → `daemon-reload` → `restart dshs`(cluster 配置在**这个 drop-in 里**,⛔ 不在 `/etc/dshs.env`) |
|
|||
|
|
| **106 节点** | 实例能力已补齐(`users/` 权限**必须 711**,700 ⇒ 实例必崩)。⏳ **provisioner 未铺**;⚠️ 旧控制面 `dsh-users-platform.service`(3080) 待清。🔴 **106 的 nginx 归宝塔托管**(`bt.service`,master = `/www/server/nginx/sbin/nginx`)⇒ `systemctl is-active nginx` = `inactive` **是正常态**,⛔ 任何"用 systemctl 判 106 nginx 死活"的脚本都会误判 |
|
|||
|
|
| **覆盖网络线 · 形态** | 入口 = `接续入口_覆盖网络线_20260916.md`(**§2「🎯 本轮动作」= 唯一执行依据**)。序 ①–⑥ **全收官**(证据在各单 **§8**:R2-R4/R6/一机一钥/443/参数表 §8.9/最小形态),⛔ 不要重做。**已落**:自研 relay **两台**(47+106,只绑回环 `20080`;106 复用既有 443);w-106 `via='relay'`;sshd 反向隧道**已下线且不可能重建**(`32022` 回收);R5 ⇒「Manager 必须与 relay 同机」**已不成立**。⛔ **P4(SSH 版中继/32023)判「不做」**;**worker 永远只拨出**(106 入站 = 0);relay = **自研**,⛔ 别走选型;⛔ **不做打洞实现**(`src/` 零 UDP/穿透代码,只测"网络能不能打洞") |
|
|||
|
|
| **覆盖网络线 · 判据 / 路径(🔴 假绿假红都在这)** | ① `http2 on` ⇒ curl **必 `--http1.1`**(否则假 404)② 端点翻译**失败关闭**,判据取 `dsh_hosts.via` 原文(⛔ 非 `reachability.via`)③ 静默失效靠**判别器**(relay `DIAL`)④ 路由未命中 ⇒ **先补齐再失败关闭**(⛔ 绝不静默回退默认机)⑤ 47 上无 session 直连实例页**一律 401** ⇒ 验收须开临时 session。🔴 `--max-hosts`:主单元 ExecStart 已有显式 `--max-hosts 0` 且 **CLI 优先于 env** ⇒ 只设 `Environment=` 被**静默忽略** ⇒ 必须 **drop-in 重写 ExecStart**(现 47/106 = 7515)。🔴 **探针值格必须纯数字**(`KEY_RE` 取整格 ⇒ 夹注 ⇒ `NaN` ⇒ **`OBS-02` 假红**)。🔴 新开 `server` 块**不生效**(`server_name` 同名 ⇒ 老块优先 = 404)。🔴 47 云安全组**拦 UDP 入站**(nft 是 `accept`,别误判)。🔴 relay 内存端点表**留离线条目且各占 1 个本地监听** ⇒ 探针残留使监听口 79→81(**重启即复原,非泄漏**)。路径:47=`/opt/dshs/lib/`、106=`/opt/dshs-cluster/lib/`、**relay 真身=`/opt/dsh-relay/lib/`**(两处都要铺)|**部署=本机 build 后 scp**|47 的 PG **只能 TCP**|`npm test` 必须 Node 22(`npm.cmd test`)|残留安全组 `32022`。观测 = `scripts/overlay-probe.cjs`(`OBS-01..12`,**阈值全从工作区根 `参数表_覆盖网络_20260917.md` 读**,脚本内零数字字面量)。**参数表 = 单一来源**(§10 指纹口径 = `sed '/^## §10 指纹/,$d' … \| md5sum`) |
|
|||
|
|
| **代码分层** | ✅ P0 已立 = 代码仓 `docs/architecture.md`(①入口→②领域→③能力→④基础 + R1–R4);🔴 旧结论「3 处反向依赖」**已勘误为 0 处** ⇒ 剩 P1 `src/domain/*` / P2 模块头注释 |
|
|||
|
|
| 集群化 | 单一来源 = 项目根 `集群化改造方案_Manager-Worker_20260914.md`;D1–D5 已定,⏳ 仅 D6(不阻塞) |
|
|||
|
|
| 实例内存(档案 96) | 与插件开关**解耦**:`MemoryHigh=MIN=448`+`MemoryMax=MAX=1024`;`/api/dsh/status` 带 `quota{...}`。⚠️ `MEM_TABLE` **仅预估、不驱动配额** |
|
|||
|
|
| 归档编号 / 其他线 | 归档编号至 **112**(103–112 = 覆盖网络线),**63 空号勿补占**|熔断 78:冷却 6h+双通道告警已上线,**L1 实测未做**|MCN 0.3.10 已铺发,`mcn-task` 重复建组未解|univer = **内存探针,有意保留 ⇒ 勿下架** |
|
|||
|
|
| **提交基线** | HEAD **`640813e`**(已 push);⚠️ 工作区**有未提交改动**(覆盖网络线各序产物,⛔ 未获授权不 commit/push)。🔑 **本机 `refs/remotes/**` 写不进** ⇒ `git fetch` **静默失败** ⇒ 分叉判定**必须** `git ls-remote origin refs/heads/master` 取**裸 sha** + `git merge-base --is-ancestor`;推前 `--dry-run`、推后重验。🗣 **「本机」= 跑 WorkBuddy 的开发机** |
|
|||
|
|
|
|||
|
|
## 二、本机铁律(⛔ 违反即事故)
|
|||
|
|
|
|||
|
|
- 🔴 **R10 实例起不来先查属主/EACCES,别先怀疑 OOM** —— root 跑 `dsh --profile` ⇒ 属主变 root ⇒ 崩溃循环/404。修 `find <home> -user root -exec chown <uid>:<uid> {} +` → 重启。⚠️ 用户 home 是 watch 域 ⇒ 读不了的文件同样致命 ⇒ 平台侧备份一律 `/opt/dsh/backups/`。
|
|||
|
|
- 🔴 **改 bwrap 参数前先看目标机 bubblewrap 版本**(**47=0.4.0 / 106=0.11.0**):`--perms` 属 0.5+ ⇒ 47 上 `Unknown option` ⇒ **沙箱起不来 = 所有实例全挂**;要权限**用 `--tmpfs`**。⚠️ `smoke-isolation` **不能**作 account 模式验收。
|
|||
|
|
- 🔴 **集群化数据分层判据**:① 归属/租约 ⇒ **只有 Manager 能写**(双写 = 脑裂)② 跟着用户走 ⇒ 放**实例 home** ③ 本机运维用 ⇒ **Worker 本地库**。
|
|||
|
|
- 🔴 **hook 脚本必须走 `sys.stdout.buffer.write(bytes)`** —— 文本层 `sys.stdout.write` + 文案含 `⛔` ⇒ 宿主 stdout 非 UTF-8 ⇒ `UnicodeEncodeError` ⇒ **stdout 空 ⇒ 静默放行**。判据 = **"写日志 + emit"双动作且 emit 走 buffer**。⛔ 别再写"hook 拦不住"。📂 自证日志 = `D:\github\dsh_shenxian\.workbuddy\lock-hook.log`。⇒ PB §10。
|
|||
|
|
- 🔴 **两条命令坑**:① `pkill -f` 要匹配"实际 argv" ⇒ ✅ `ss -lntpH 'sport = :PORT' | grep -oP 'pid=\K[0-9]+'`。② **`fetch` 静默丢 `Host` 头**(undici)⇒ 多租户验证落"无租户"路由、**假 404/200** ⇒ ✅ `curl -H "Host: …"`。
|
|||
|
|
- 🔴 **两条取数坑**:① **本机 `md5sum` 输出 `hash *path`(二进制)、远端是 `hash path`(文本)** ⇒ 直接 `diff` 得"**所有文件都不同**"的**假象**(59/59 全红其实只差 7 个)⇒ 先归一化路径字段。② **`ssh '…'` 单引号里不能出现单引号**(SQL 字面量)⇒ `'local'` 被本地 shell 吃掉 ⇒ ✅ 用 `cat > f <<"EOF"` 落文件,或本地 `echo` 管道给远端 `psql -f -`,或 `ssh host 'bash -s' < 本地脚本`。
|
|||
|
|
- 🔴 **「单机自用」≠「不需要互联」**(用户原话:「**单机用也要互联,这正是建立覆盖网络的目的**」):**租户维度收窄与网络维度收窄正交** ⇒ ⛔ 别再用"单机自用所以没流量要互联"当前提。
|
|||
|
|
- 🔴 **覆盖网络按异构设计**:① 中继按 **45% 设计、55% 留余量**(⛔ 别用同构的 15%)② **必须补 443/TCP 兜底** ③ 有公网 IP 的节点自动升格为中继候选,但**权威状态单点 Manager** ④ VPN/代理类**检测即降级中继**、不重试 ⑤ 节点自报网络画像。
|
|||
|
|
- 🔴 **方案只做技术实现,合规不进方案**(用户原话:「**方案只考虑技术实现,跨境数据合规是谁用谁自己考虑**」)⇒ ⛔ 不再把合规/数据主权/备案当前置条件或上抛项。
|
|||
|
|
- 🔴 **接手前人结论,先做一次最小取证**(同日踩两次):① `proxy.ts:109` 实为**上游 Host 头**;② "3 处反向依赖" 实为 **0 处**。⛔ 别拿上一份文档的结论当既成事实。
|
|||
|
|
- 🔔 **技能加载闸门**:`scripts/skill-load-guard.py` —— 命中「决策方法/按你的规划/别问我/自行决策」⇒ 注入"必须先加载 `dsh-decision-method`"(急停 `DSH_SKILL_GUARD_OFF=1`)。⚠️ **需完全重启**。
|
|||
|
|
- ⚠️ **本机执行环境**:**Python stdin/stdout 必走 `buffer` 显式 UTF-8**|改文件**必用 `newline=""`(读也要)**|**ESM**|bash **PATH 被 shim 重置** ⇒ 先 `export PATH=<PortableGit>/usr/bin:…:/c/Windows/System32:/c/Windows:$PATH`|**Python exe 不认 `/e/…`** ⇒ 传参 `E:/…`|bash 内联 `\` 被 MSYS 改写 ⇒ 含 Windows 路径的 Python **落 .py 再跑**|`tar czf E:/…` 加 `--force-local`|`ls` 被 MSYS 搞坏 ⇒ 先 `cd` 再相对路径|🔴 **`cat X >> Y` 实测写到 Y 的「开头」** ⇒ 追加 .md 后**必核「在末尾」+「整块 `decode('utf-8')` 合法」**(几个非法字节 ⇒ 检测走偏 ⇒ **整份读出来全乱码**)⇒ PB §10.1。
|
|||
|
|
- ⚠️ **行尾只信字节级**(`grep -c $'\r$'` 会误报)⇒ Python `b.count(b'\r')`。BRIEF/README/`skills/**`/`memory/**` = 纯 LF;**`INDEX.md` = 混合** ⇒ 字节级单行插入;⛔ 别对它跑 `git checkout --`。
|
|||
|
|
- ⚠️ **引用用户口径只写原话**|**含反引号的内容一律用 Write/Edit 写** —— 含 **`python -c "…"` 内联**这条最隐蔽的变体:反引号会被**外层 shell 先当命令替换**吃掉(09-17 实测:`s.replace('`x`','…')` 被替换成空串 ⇒ 变成"每字符之间插一段"⇒ 一次写入 25,761 段垃圾串;当场发现并完整回滚)⇒ **凡要传给 Python 的字符串里有反引号,先 `Write` 落成 `.py`/`.md` 再跑,或用 `chr(96)`**|**进程替换 `diff <(a) <(b)` 不可用**|**`Edit` 是替换不是追加**。
|
|||
|
|
- ⚠️ **打包与改名**:`npm pack` 前核断言锚点、先删 `lib/*.bak` 与 `*.tgz`、每包必须有 `.npmignore`;改名三件套 = SQLite `.db`+`-wal`+`-shm` 一起改 / 压缩包内 grep-sed 够不到 ⇒ 解压→改→重压 / 改完真环境跑一次。
|
|||
|
|
- 浏览器只准 `agent-browser` / `browser-harness`(CDP 9223);⛔ **Playwright 全面禁止**。
|
|||
|
|
|
|||
|
|
## 三、成本与自动化(09-16 实测)
|
|||
|
|
|
|||
|
|
- 🔴 **成本 ≈ 单价 × 一轮内工具调用次数**(单价随水位涨:<10 万 ≈0.10、15 万 ≈0.41 分/次,**4 倍**)⇒ **真杠杆 = 一轮工具次数**;⛔「切会话能归零」**错**(自动化全新会话首轮就烧 8.9–13.8 分)。**三条硬纪律**:❶ 大输出先落盘只读关键行 ❷ 命令层限流(`\| head -30` / `--max-hosts`→`grep -c`)❸ 让脚本内部聚合只 print 摘要 —— ⛔ 禁 `cat` 大文件、无 `head` 的 `grep -r`、`ls -laR`。
|
|||
|
|
- 🔴 **自动化会话为什么贵**:① **一条 prompt 塞 N 件事**(prompt 必写「**本轮只做一件事,做完即停**」+「**取证最多 3 命令,要动代码先停下报告**」)② **无人能打断** ⇒ 规则本身不许制造调用。⇒ `会话接续规范_20260916.md §3.2.1/§6`。
|
|||
|
|
- ⚠️ **自动化 = 开新会话的唯一通道**;**钩子无创建/切换会话能力** ⇒ 接续**只能半自动**:钩子注入收口指令 → **模型调 `automation_update`** 登记 +2min 一次性自动化。⛔ 钩子不得绕过它写库。🔴 **四条**:① 开机**第 0 步 = 跑 `state.py`**;② prompt **⛔ 不许抄任务细节**;③ 多线并行时口令**必带线名**、登记前先看 `automation_runtime_state.running`、抢不到锁 = 正常信号只报告;④ 🔴 **登记 automation 后必须在回复里用陈述句告知**("已登记自动接续,约 N 分钟后自动开新会话,不用你操作;接续点 = X")—— **只登记不告知 = 缺陷**(实证:会话 `408636f2` 只登记未提及,用户两小时后自己发现)。
|
|||
|
|
|
|||
|
|
## 四、仓库 / 推送 / 授权
|
|||
|
|
|
|||
|
|
- 📦 **09-15 文档库已并入代码仓** ⇒ **`D:\github\dsh_shenxian\dsh-server-docs\`**(git = `D:/github/dsh_shenxian/.git`)。锁脚本 = `…\dsh-server-docs\scripts\handoff-guard.sh`。镜像 `/opt/dsh/docs` 不变。⚠️ hooks 路径改动要**完全重启**才生效。
|
|||
|
|
- 代码仓 `D:\github\dsh_shenxian` → `…dsh_shenxian.git`(master)|**推送只能从本机**;服务器改码走 `git bundle`。文档库 `autocrlf=false` + `.gitattributes(* -text)` **必须保持**。
|
|||
|
|
- ⚠️ **技能是「三处」**:本机 `.workbuddy/skills/<name>/` ← 文档库 `skills/<name>/` ← 镜像 `/opt/dsh/docs/skills/`。**验收 = 两副本 md5 一致 + 镜像一致 + `README.md`/`INDEX.md` 登记跟着改**;同步前先抢锁。📄 其余"去查"索引 = PLAYBOOK §11。
|
|||
|
|
- ⚠️ **改任何文件前先抢全局执行锁**(`--claim-exec`);**释放了再想继续写就得重抢**。
|
|||
|
|
- 🔒 **授权类**:文件已全部移除(不得再列为待办或上抛)。上游 MIT、**不得对上游代码附加限制**;**对外发布前先放回那三份**(`_授权归档_发布时再放回\`)。
|