Files
dsh_ai1net_server/.workbuddy/memory/2026-09-下14.md
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复:
- 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录)
- tmp/(32.4 M,按接续棒命名的过程临时区)
- .workbuddy/tmp/(39.5 M)
- 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物)
- tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留

入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与
接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、
.workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。

排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、
打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

47 KiB
Raw Blame History

工作日志 · 2026-09(第 15 片)

⚠️ 本目录日志已按【月】分片(2026-09-15 用户定,单文件 ≤50 KB):同月续片 2026-09.md / 2026-09-下.md / 2026-09-下2.md / 2026-09-下3.md / 2026-09-下4.md / 2026-09-下5.md / 2026-09-下6.md / 2026-09-下7.md / 2026-09-下8.md / 2026-09-下9.md / 2026-09-下10.md / 2026-09-下11.md / 2026-09-下12.md / 2026-09-下13.md / 2026-09-下14.md / 2026-09-下15.md / 2026-09-下16.md / 2026-09-下17.md / 2026-09-下18.md / 2026-09-下19.md / 2026-09-下20.md / 2026-09-下21.md / 2026-09-下22.md / 2026-09-下23.md 写入约定:一律 append 到当月最后一片;该片超 50 KB ⇒ 新建 2026-09-下N.md;⛔ 不要再按日新建 2026-09-DD.md。 覆盖来源:2026-09-13.md 上一片:2026-09-下13.md 下一片:2026-09-下15.md


11:21–11:3x · 把本轮教训升格为技能「重点规则」(用户:避免后续犯傻)

用户指令:「把这些纳入重点规则 放在 skill 中 避免后续犯傻」。

A. 技能 v1.2.0 三项改造(dsh-opensource-release)

  1. 新增 ## 0.5 🔴 事故清单 —— 放在「何时用」之后,开工前 30 秒读完。8 条真实事故:误放别人的锁 · 导出基线会漂 · 盲替脚本自身 · Dockerfile 漏脱敏 · package.json 被重建覆盖 · 废弃候选名静默残留 · 文档文案连改 7 轮 · 脚本用键名当标题。每条给「正确做法 + 详址」。
  2. 规则升格与重排 —— 原先埋在 R-O8 命名规则 下的三个「附则」升为顶层规则:R-O10 出处的位置与措辞 / R-O11 README 内容口径 / R-O12 README 结构顺序,并把 R-O9 排到 R-O8 之后;§1 标题改为「硬规则 R-O1–R-O12」并补全索引(原只有 R-O1–R-O5 + R-O6/7/8)。
  3. 修掉两条自相矛盾的旧规则(隐患最大):① 「措辞」行还在教写「此后在本仓库做了大量深度改造」(正是用户否掉的写法)→ 改为「起步时参考了 X(作者,许可证)—— 感谢作者开源」;② 「难在哪」行的示例引用了被禁的「多租户演示十分钟就能写出来」→ 改为「心里有数、但别写进 README」。

B. 又踩一个(已进事故清单第 8 条)

结构改写脚本里写成 marks[k][0] —— 拿到的是键名 "A" 而不是标题字符串 ⇒ replace 打歪:把 README 的 A/D 替换掉、把 PoC 的 C 删掉,两处标题没改名。已全部精确修回(改完读回原文复核)。教训:结构改写要用完整标题字符串做锚点,改完必须读回复核。

C. 锁纪律首次按新规矩执行(成功)

锁在 11:25 释放后:① 单独一条命令抢锁并当场看输出(✓ 已持全局执行锁(oss-sync-1125))→ ② 另一条命令里先断言 OWNER == 自己 才动手,末尾再验一次 OWNER 后才 --release-exec。同步技能归档(md5 一致)+ 四件套 rc=0;因 §10 记录更新需再同步一次,第二遍同法执行(oss-sync-1127,md5 dab0ce55…)。全程未触碰别人的锁。

现状:技能 v1.2.0,两副本 md5 一致(dab0ce55…),文档库无锁,四件套 rc=0。未 scp(等用户发话)。

⚠️ 11:28 我犯的流程事故(如实记录,已报告用户)

事实:本轮改档案 81 时,我把「抢全局执行锁」与「写文档」写在同一条命令里用 ; 分隔 —— 抢锁失败了(当时锁被并行会话 oss-sync-1127 持有,11:24 起),但后续写操作照样执行: ① 写了 04-调整方案/81(2 行替换 + 追加 §10.6)② 重新生成并推送了 docs-manifest.json; ③ 结尾执行 handoff-guard.sh --release-exec,把别人的锁放掉了(现已确认 .exec-lock 不存在)。

违反了什么:R9 的精神(锁的处置权只属于用户本人)+ 本库「抢不到锁 = 停手」的硬规矩。

根因:claim; write 是两条独立命令,抢锁失败不会让后面的 write 停下 —— 我的脚本没有做退出码检查。 (另外:--release-exec 未校验「我是不是占用者」,这也放大了后果。)

纠正(写进 MEMORY.md,必须遵守):

  1. 抢锁必须与写操作用 && 串联(claim-exec ... && <写操作>),抢锁失败即整条命令中止;
  2. 写库前先肉眼核一次 交接单/.exec-lock/OWNER,不是自己就停手;
  3. --release-exec 只在确认 OWNER 是自己时执行;
  4. 抢不到锁时的合规动作只有:停手 + 报告用户(不触碰锁状态,不代用户判断)。

11:25–11:3x · 核心卖点上移:「全部代码与文档由 AI 生成」

用户指令:「还有个重点要加载前面 —— 所有项目和文档全 AI 生成,使用模型 DeepSeek V4 / V4.1 和 WorkBuddy」。

README 三处落地

  1. Hero 区:新增 2 个徽章(code & docs-AI-generated、DeepSeek V4 / V4.1 × WorkBuddy,共 6 个,未超审计上限 8)+ 一行声明:「全部代码与文档由 AI 生成(DeepSeek V4 / V4.1 模型 × WorkBuddy)—— 一个可运行的、完整的多租户服务端。详见 AI 生成」。
  2. 新增 ## AI 生成 专节,置于第一个正文章节(目录之后、亮点之前):覆盖范围表(src/·web/·scripts/·deploy/·Dockerfile* ✅ / README·PLUGIN-PORTING·LICENSE·NOTICE·install.sh ✅ / test/ + 9 个 smoke:* ✅)+ 一句「人负责定方向、提需求与验收」+ 一句「可当作『AI 能不能独立写出可控的多租户服务端』的样本(含如实标注未验证能力)」。
  3. 目录与章节顺序同步 → 17 节,TOC 17/17 锚点正确;探针 0;495 行。

技能 v1.3.0:§0 事实新增「内容来源」行(明示三处一个都不能少:Hero 一行 + 2 徽章 + 首个正文章节);R-O12 顺序图更新并把 6 个徽章写进去。台账 §九 C 同步。

⚠️ 遗留待用户确认(已当面提出):声明写的是「全部代码与文档由 AI 生成」。若起步阶段那份骨架(上游骨架仓库(已按要求不再具名),123 个提交)不是 AI 写的,「全部」就偏大 —— 备选措辞是「本仓库全部内容由 AI 生成」(把范围限定在本仓库)。用户一句话即可改。

归档同步:技能归档副本已同步(md5 f4bf7eb9…),四件套 rc=0,全程锁:先单独抢 + 断言 OWNER + 放前再验,未触碰别人的锁。


11:29–11:3x · 出处收敛为「一行致敬」(用户裁定)

用户指令:「如果起步阶段那份骨架(上游骨架仓库(已按要求不再具名),123 个提交)这个都全部改造了,致敬就可以了」—— 同时等于回答了我上一条的疑问:「全部代码与文档由 AI 生成」这个宣称成立(骨架已被整体重写),不必再加"范围限定"的补语。

改动(README 2 处 + NOTICE 2 处 + LICENSE 1 处)

位置 改前 改后
README 授权表 行1 「上游基础部分|起步时参考的骨架代码:认证 / 桌面 / 单 DSH 启停与反代 / 每文件夹插件 / 双部署框架」 「起步骨架部分|起步时参考的骨架(此后已在本仓库整体改造)」——去掉逐项枚举(枚举=把自己继承的份量说大)
README 授权表 行2 「本项目新增与修改部分|由本项目维护者新增/修改的代码」 「其余全部代码|新增与重写的源码、配置、脚本与文档」
README 授权摘要 「上游基础部分保持 MIT」 「起步骨架部分保持 MIT」
NOTICE §4「本项目」行 「以上游骨架为基线继续演进」 「起步骨架来自该项目,此后已在本仓库整体改造」
NOTICE §4「致谢」行 「认证与审核…双部署框架等骨架能力均源自上游。感谢 上游作者(已按要求不再具名) 的开源工作。」 「感谢 上游作者(已按要求不再具名) 的开源工作。」
LICENSE 第一层 「上游骨架:认证与审核、网页桌面、单 DSH 启停与反向代理、每文件夹插件、模式 A/B 部署框架」 「起步骨架;此后已在本仓库整体改造。⚠️ 尽管做了整体改造,其版权与许可声明按 MIT 要求依旧完整保留」

⚠️ 明确保留没动的部分(保守合规):LICENSE-UPSTREAM-MIT.txt 逐字节未动;授权分层结构保留;LICENSE 第一层的 MIT 声明保留 —— 改造过也不删(万一还有残存代码,这是保命条款)。

技能 v1.3.1:R-O10 新增该口径(骨架已整体改造 ⇒ 只留一行致敬、去掉枚举;但 MIT 声明与授权分层照旧保留)。归档副本已同步(md5 b73cd62e…),四件套 rc=0,锁按新规矩走。验证:探针 0、TOC 17/17 锚点正确、README 495 行。


11:30 · 更正我自己的错误结论:官方有 i18n(档案 81 §10.7)

起因:用户追问「有哪里是需要 替换官方包 / 改官方代码的」。核查时发现 §10.6 的结论建立在不完整抽查上(当时只看到 locale 包里有个 README.i18n.yaml,就当成"只是文档翻译")——错了。

实测(服务器只读):

  • @deepseek-ai/dsh-client-locale(v0.1.2-rc.1,MIT)真实存在:lib/index.js 1.3 KB(host)+ lib/client.js 55.5 KB(含内置词典)。
  • host 侧只注册 settings 命名空间 locale / 字段 preference;LOCALE_IDS = ["zh","en"](随包只发这两种)。
  • client 侧导出 LocaleRuntime / COMMON_NS / FALLBACK_LOCALE / SETTINGS_NS / apply / inject;内置 common 词典 = 通用词(确定/取消/关闭/复制成功…)。
  • 官方文档给出的扩展 API:ctx.locale.addLanguage({id,label,fallback}) + ctx.locale.register(ns, lang, {...}),消费走 ctx.locale.bind(ns) 或框架 t 座位;设置入口 Settings → General。官方原话:shipped zh/en,"external client plugins can add languages and their namespace dictionaries"。
  • 但官方没铺开:只有 2 个官方 UI 包引用了 locale(conversation、directory-picker-browse);其余大量硬编码中文 —— trajectory 167 行、conversation 138、chat 99、settings-models 98、workspace 64、settings-plugins 50、agent-preset 50 …

结论(对 R5 选型的影响):新增第 4 个选项 D · 走官方 locale 体系(自研插件 + 浮层文案用 addLanguage/register 跟实例内语言开关联动,不触 R2)= §10.4 方案 C 的加强版。原先 A/B/C 三项保留。

"要不要替换官方包 / 改官方代码"的净答复:平台页 / 注入层 / 自研插件 —— 零官方改动(都走官方扩展点);官方已迁 locale 的包 —— 切语言自动跟随(白拿);官方仍硬编码的 20+ 个包 —— 只能注入层改 DOM(脆弱、覆盖不全),替换官方同名包 = 改官方产物 ⇒ 触 R2 ⇒ 不做。

教训(已写进 §10.7 末尾):上一轮的错在**用一个文件名代替「读一读那个包」**就下了"官方没这个能力"的结论。凡结论会影响方案取舍(尤其"做不了"这类),必须落到实测证据,不能靠抽样直觉。

收尾:档案 81 就地改掉那两处错句 + 追加 §10.7 + §10.5 加 D 选项交叉指针;四件套 rc=0。未 scp、未 commit(按 §4 等用户发话)。同步对账报 5 处内容不一致 + 2 处仅本地(含既有的档案 42,非本次改动,未动)。锁:claim → 验 OWNER → 改 → 四件套 → 再验 OWNER → release,全程合规。


11:35–11:56 · 回答「改造完成了吗 / 部署了吗 / 能用吗」→ 真机验收

结论:R0+R1 已部署上线且真机可用;R2–R5 未做。

部署实证(只读 ssh):

  • 服务 active,11:14:45 重启(lib/supervisor/proxy.js 产物 11:14:44 → 产物早于启动 = 新代码在跑)。
  • assets/inject/{recovery,assist}.js 于 11:09 上传;编译产物含 loadInject(6 处)、不再有内联模板字面量(0 处)⇒ R1-① 已落地。
  • 服务器 /opt/dshs HEAD 仍 b23e386,但工作树有 ~19 M + ?? assets/ + ?? scripts/verify-inject.cjs,且已 build+重启 ⇒ 服务器工作树不再是干净基线(MEMORY.md 已记,下次同步前必须比对)。
  • R3 双名期三处 symlink 确认(11:17):/opt/dshs、/var/lib/dshs、/etc/systemd/system/dshs.service。
  • assets/ 下只有 inject/ ⇒ R5 i18n 词条目录尚不存在(未做)。

真机验收(无头 Chrome channel:'chrome' + R4 允许的临时会话 mksess-guest.cjs,用后即删):

项 结果
公网首页 https://alotbuy.com/ 200,title=工作台
去痕迹 6 个平台页 0 处 dshs
登录页新版 is-busy 命中 3;两段式文案「登录中」→「正在进入工作台」
过渡页 title=正在启动工作区;wk-orb 命中
注入是否真执行 2 个 <script>;哨兵 window.__dshRecover===1;window.fetch 已非 native(被包装);__dshAssistBar 已建;零页面错误
助手面板(档案 56) ✅ 真机验收通过 —— 点「📁 我的文件」弹出面板,列出工作区目录(.cache/.config/MCN短视频创作/syslibs…)+ 说明文案
恢复浮层 ✅ 真机验收通过 —— 文案「工作区已休眠,正在唤醒…(已等待 N 秒)」倒计时逐秒递增、DSH · AUTO RECOVERY、AI 核心视觉 + 进度条

踩到的验证坑(值得记):

  1. page.route 拦不住 WebSocket ⇒ 用 setOffline / 拦 /api/* 都逼不出浮层。
  2. probe() 有 15 s 节流,心跳(25 s)会把它吃掉 ⇒ 手动 dispatchEvent(new Event('focus')) 常被静默丢弃。
  3. recover() 成功后 0.7 s 内 location.reload() ⇒ 5 秒轮询全踩空,误判"浮层没出现"。
  4. ✅ 正解:把 /api/dsh/enter 挂住不回(page.route(url, () => {})),浮层就停住可观测;且 status 仿真 {"running":false} 是纯客户端、不碰生产。
  5. 助手条 __dshAssistBar 本身是容器,里面是两个 button(📁 我的文件 / 🧭 能力)—— 必须点按钮,点容器无效。

清理:临时浏览脚本已删;临时会话全部删除(残留 6 个是别人的真实登录);截图保留 3 张在 _中间产物_待清理/。


13:32–13:5x · 用户 5 项反馈的处置(A1–A4 + B1/B2)

⚠️ 最重要的两条更正 —— 我上一轮的「待办清单」里有过期判断:

  1. B1/B2 都不需要做了(我原报"待窗口"):

    • B1(档案 79)已修并部署:产物 lib/web/routes/business-plugins.js 实测 —— :575 = ['remove', id, '-w', '--reporter', 'silent'](D2 已修)、:587/:617 = await uninstall(sel.id)(D1 已修)、:613+:718 路由外层 void (async …)().catch(…)(加固 a 已做)。加固 b(半应用态主动自愈)未做,可选。
    • B2(内存预算)已被 R1 消解:R1-④ 把堆限与 MemoryMax 改成按插件集合动态计算(比"写死 256/512"更优)。实测运行中:guest NODE_OPTIONS=--max-old-space-size=256 / scope MemoryMax=544 MiB(=160+univer 384);admin 也是 256。平台 env 里那个写死的 160 已被代码侧 withHeap() 覆盖、不是生效值。装上 mcn-suite 后会自动变 672 MiB。
    • ⇒ "需 R8 窗口"的前提已不成立。
  2. D3("服务器工作树未回灌")说法要更正:用 git hash-object 比对报"不一致"是行尾差异造成的假阳性(本机 CRLF / 服务器 LF)。去掉 CRLF 后逐字节比对:src/**、web/**、design.css 两侧完全一致。真实双向漂移只有 2 处:① scripts/ensure-anysearch-admin.cjs 服务器领先(多了已退役硬拦 + exit 2)⇒ 不可用本机覆盖;② test/crash-policy.test.mjs 本机领先(多了档案 78 的 3 条熔断单测)。

用户 4 项决定的落档(持锁 → 改 → 四件套 rc=0 → 验 OWNER → release):

  • A1 · R2 形态改定 = 原生弹窗(用户:「为什么要 iframe,原生弹窗体验更好」)⇒ 档案 81 新增 §9.2b:如实记录 iframe 当初是为省工程量(把 portal.html 整页嵌进来 ⇒ 零重写)而牺牲体验;改定为插件内原生渲染只读子集(调平台 /api/*),并写明「不得两头都要」——全量就得把门户重写进插件(双源 + 与档案 39 相冲)。
  • A2 · R4 待用户选(用户「没看懂 R4 要做什么」)⇒ 档案 81 §9.3 重写:先补动机(同一个改动要分两次走 / 两库版本对不上 / scp 镜像靠仪式维持),再列 (a)(b)。
  • A3 · R5 定为 D(走官方方案)(用户:「R5 肯定是匹配 dsh 官方方案,什么会有疑问呢」)⇒ ctx.locale.addLanguage() + register(ns);零官方改动。⚠️ 唯一边界(是事实不是疑问):官方自己只迁了 2 个 UI 包,仍有 20+ 个包硬编码中文 ⇒ 切英文后那些包不变。A/B/C 三项降级/被 D 覆盖。
  • A4 · 悬空引用查清(用户「R7 在哪里 什么东西悬空了」):R7 = 项目根 CODEBUDDY.md §3 红线表第 7 条(禁未经确认的批量/全仓写入,>10 文件先出清单)。悬空的 = 开源导出仓库里 29 个文件的 42 处注释指向 4 个根本不存在的文档(docs/k8s.md×37、docs/k8s-deploy.md×3、docs/domain-config.md×1、docs/blueprint.md×1,因 docs/ 按 R-O4 整体移除)。台账 §八 C 已有该数据与建议改法(纯注释替换)。
  • 顺带修正:MEMORY.md 里档案 79 的描述原是「探针不再带崩平台」写错了,已改为正确标题。
  • 顺带发现:开源导出目录已从项目内移到 E:\ProgramData\AI技能\dsh-laijing-github\(另一会话搬的);项目根另有 26 个 _* 临时文件(多为 09-12 遗留)待清。

新增规则(用户明令,已写进 CODEBUDDY.md §1 + R8 与 MEMORY.md §六):🔴 服务器 47.77.182.89 是「开发环境服务器」,不用担心中断用户 ⇒ 重启 / 停实例 / drain / 改配额或 env / 改 nginx·nft 直接做,只需动手前一句话说明;未放开不可逆破坏性操作(删数据 / 迁 DB / 清目录)。

技能维护(同轮,锁第二轮)

  1. dsh-instance-diagnose 新增「注入层/浮层真机验证配方」节 —— 沉淀本轮 4 个坑:① page.route 拦不住 WebSocket(setOffline 也不掐已建 WS)⇒ 逼不出浮层;② probe() 15 s 节流被心跳(25 s)吃掉 ⇒ 手动 focus 常被静默丢弃;③ recover() 成功后 0.7 s 就 location.replace() ⇒ ≥2 s 轮询必然踩空、会误判"浮层没出现";④ #__dshAssistBar 是容器,里面才是两个 button。正解:仿真 status={running:false} + 把 /api/dsh/enter 挂住不回 ⇒ 浮层停住可观测。附哨兵表(window.__dshRecover===1 是最强判据,比找 DOM 可靠)。
  2. 同技能修正两处事实漂移:① 「实例配额」原写死 MemoryMax 384 MiB + 堆 160 → 改为按插件集合动态算(R1-④);② 隔离复现配方里的 -p MemoryMax=402653184 → -p MemoryMax=384M(顺带消掉 docs-consistency.py 把它读成 4026 的取值冲突);③ 「绝不在生产实例压测」按新 R8 口径改为「压测走隔离环境」。
  3. 补齐缺口:dsh-instance-diagnose 原先没有归档副本(两处位置规则漏了它)→ 已建;顺带发现 dsh-decision-method(工作 v1.4.0 vs 归档 v1.3.0)与 dsh-feature-first(v1.3.0 vs v1.2.0)归档落后 → 按「单向:本机 → 归档」补齐。现 6 个技能两副本 md5 全一致。
  4. ⚠️ docs-consistency.py 的一个局限(未改脚本):它把 MemoryMax=402653184(字节)读成 4026 与 384(MiB)冲突 —— 只看数字不看单位。以后再遇到,优先把文档写成同一单位(384M)而不是改脚本。

14:22–14:3x · 「文档的处理直接执行就好」→ A4 执行 + 文档库全量收口

用户批准后直接执行,两件事都做完。

① A4 · 开源导出:去「档案 NN」+ 修悬空 docs 引用(写成脚本规则,重建不丢)

E:\ProgramData\AI技能\dsh-laijing-github\_build_export.py 新增 REGEX_RULES(含 ARCHIVE_REF / DOC_SECTION / DOC_REF)+ import re,在 sanitize() 里于 GLOBAL 之后应用。

项 重建前 重建后
「档案 NN」(含 档案 19 §C8 / 档案 81 · R1 / 档案 56:…) 197 处 / 41 文件 0
悬空 docs/*.md(k8s.md×37 / k8s-deploy.md×3 / blueprint.md×1 / domain-config.md×1) 42 处 / 29 文件 0(映射到 README 真实小节:部署形态 / 架构 / 配置)
交接单 2 0
blocking leak probe — 0
info hits 2 0
tsc — exit 0 · no diagnostics
文件数 141 141(不变)

另加 GLOBAL 两条字面量补语义缺口((档案 74 的下调)→(此前下调过);档案 16 已宣布→平台已宣布)+ 命中新阻断探针的 mcn-workstation→my-skill。

🔴 这一轮最大的教训(已同时写进 dsh-opensource-release 技能 §0.5 事故 #10 + 台账 §八 D)

我的"顺手清理"正则把 ASCII 标点也吃进去了:清理规则写成 [((]\s*[))] / [;;,,]\s*[))],字符类里混了 ASCII ( ) , ; ⇒ 源码里所有 foo() 被删成 foo ⇒ whitelist.ts / security-scan.ts 当场语法错(tsc 报 Invalid character / Unterminated string literal)。 ❗ 当时 leak 探针全过 —— 因为探针查的是"有没有敏感标识",完全不看代码是否还能编译。 ✅ 两条铁律(已写进脚本注释 + 技能):

  1. 清理类正则只准碰全角标点(()·、,:;),绝不可把 ASCII 语法符号写进字符类;
  2. 改完必跑 _verify_tsc.mjs,exit 0 + no diagnostics 才是"没改坏代码"的唯一证据 —— 探针全过 ≠ 代码还活着。 (另:(\s*/\s* 这类会吃掉路径前导斜杠的规则一律不要写。)

② 文档库收口:推送镜像 + 提交 + push(均已闭环)

  • 推送镜像:39 项待处理文件。⚠️ 逐个 scp 会超时(39 条 SSH 连接)⇒ 改用单个 tar 管道(tar -cf - -T list | ssh 'cd /opt/dsh/docs && tar -xf -')一次连接传完,随后 find 修正远端权限(目录 700 / 文件 600)。这是本次新摸索出的高效传法,值得复用。
  • 对账:141/141 双端一致 ✅(此前是 129 一致 / 9 不一致 / 3 仅本地)
  • 提交:6e09edb(39 项,显式 git add 逐个,未用 git add -A);工作树清零
  • push:43e4ae9..6e09edb HEAD -> main ✅
  • 技能追加:dsh-opensource-release v1.4.0→v1.4.1(§0.5 新增事故 #10)→ 归档副本 md5 一致 → 提交 87505d5 → push ✅(本地=远端=87505d5,0 领先)
  • 四件套全程 rc=0;锁两轮均按纪律走(claim 单独一条 → 验 OWNER → 改 → 四件套 → 再验 → release)

14:38–14:5x · 「都可以处理」→ 代码仓收口 + 开源导出剩余项

① 代码仓(D:\github\dsh_shenxian)已提交并推送

  • 修双向漂移:① scripts/ensure-anysearch-admin.cjs 从服务器取回(310 行,含 AnySearch 退役硬拦 + exit 2 —— 本机原先缺);② test/crash-policy.test.mjs 送到服务器(档案 78 的 3 条熔断单测,breakerCooldownMs 命中 6 处);③ mksess.cjs 两侧本就一致 ✓
  • .gitignore 补两条:*.tgz(构建产物,「历史上从未提交过 tgz」已核实)、.workbuddy/(工作数据非仓库内容)⇒ 30 项待处理收敛为 24 项
  • 提交前跑 npm run verify:注入脚本 2/2 语法通过 + proxy.ts 走 loadInject 2 处 + 9 页静态不变量全合格
  • 提交 c0abec8(25 项),工作树清零;push da3e0f9..c0abec8 → master ✅
  • ⚠️ 只推 origin(Gitea),upstream(github.com/上游作者(已按要求不再具名))绝不推
  • 残留说明:mksess-guest.cjs 只在服务器(有意保留,服务器专用临时工具,不入库);poc/business-plugins/* 本机领先、服务器未用(PoC)

② 开源导出剩余 3 项

项 结果
#3 占位符 7 处 ✅ 已填:<YOUR_GITHUB_ACCOUNT>→maogeigei、<CONTACT_EMAIL>→[email protected]、<COPYRIGHT_HOLDER>→maogeigei(按本机 git config 推断)。取值集中在 _build_export.py 的 PUBLISH_*;README/LICENSE 属 OVERLAY ⇒ 那 6 处写死,改身份要两处都改
#4 install.sh 真机预演 ✅ 在服务器隔离目录 /tmp/inst-dry/ 跑通 --dry-run(已清理该目录)。顺带修 4 个真实缺陷
#5 法律意见 ⏳ AI 做不了,留给用户/律师

🔴 本轮两个新的结构性坑(都已进技能 dsh-opensource-release §0.5 事故 #11/#12)

  1. 同一个名字不能既做「脱敏目标」又做「公开值」:maogeigei 同时在 GLOBAL(→占位符)与 LEAK_PROBES(判泄露)里 ⇒ ① 刚填好的公开值被规则改回占位符("占位符残留 0"是假象),② 探针报 blocking hits: 4。修法 = 两处同时移除(只删一处 = 另一种错);私有仓库地址仍由 work.alotbuy 探针兜底。通用判据:任何进 PUBLISH_* 的值,先 grep 确认它不在 GLOBAL/探针里。
  2. --dry-run 报"假成功":run() 只给命令加 [dry-run] 前缀跳过执行,但结果提示语是硬编码的 ⇒ 预演满屏 ✓ 构建完成 / ✓ 服务已启动 / ✓ 部署完成,还打印了一个无效的初始管理员密码(bootstrap-admin 没跑);未设域名时文案还出现空缺「把 与 *. 的 DNS A 记录」。判据:dry-run 输出里不允许出现任何 ✓,只允许 [dry-run]。 4 处已修并复跑验证。

③ 顺带观察:有另一个会话在并发改开源导出(非我)

证据:INCLUDE_DIRS 被补了 assets/(⇒ 导出物 141→143)、台账"终检"行的文件数被改成 143、技能版本被从 1.4.1 直接跳到 1.5.0。我的 §0.5 #10/#11/#12 三条已自然合并、无冲突。⚠️ 我持锁期间它仍在改同一批文件 —— 这是本库「多会话并行」的既有风险(细锁管不住跨单撞车)。本轮未撞车,但值得留意。

交付闭环

  • 文档库:双端 141/141 一致;提交 fc010f5;push 已确认落到远端(用 git ls-remote origin refs/heads/main 直连核对 = fc010f5)
  • 开源导出:blocking 0 / info 0 / 档案 NN 0 / docs 0 / 占位符 0 / tsc exit 0 / 手工层 6-6 / 必需文件 26-26
  • ⚠️ 导出仓仍未 git init(未发布)—— 发布应是刻意动作,用户没说要发

④ 收尾时发现:代码仓有对端会话正在改(未提交)

src/supervisor/orchestrator.ts 在我提交(c0abec8,14:47)之后被人改过 —— PLUGIN_MEM_MB['dsh-univer-office'] 384 → 512,注释写着「2026-09-13 14:44 …544 仍欠 ~50-120 MiB,实测 gateway 起不来;512 ⇒ 配额 672」。

⇒ 两点结论:

  1. 确实有对端会话在并发改码仓,且未持全局锁(我持锁期间它仍在写)。本轮未撞车(改的是不同文件/不同位置),但这是本库既有风险的又一处实证。
  2. 我要更正此前记录的容量口径:我曾按 R1 的公式算出 guest = 544 MiB 并记为"已消解 B2"。对端实测发现 544 仍不够(univer gateway 起不来),故把 univer 的预估提到 512 ⇒ 配额 672。该项工作属对端在途,我未碰(R7:只做被明确要求的事;R9:不接管他人工作)。若他提交,我记忆里"544 已足够"的说法需同步改。

未动:mksess-guest.cjs(服务器专用,有意保留)、上述对端在途改动、导出仓的 git init。


15:10–15:2x · 「授权条款」不属我的范围 → 全项目扫描并移除授权类文件

用户定性:「「授权条款」具体指什么 做这个事情干什么呢,需要你考虑吗,你的任务是改造和优化,这些内容全部删除」 ⇒ 授权/法务不属本项目范围,不列为待办、不再上抛。

全项目扫描结果(文件名 + 内容双维度,覆盖本机三处 + 服务器)

位置 授权类文件 性质
D:\github\dsh_shenxian\LICENSE 1 份 ⚠️ 上游自带(08-18 上游提交 a2b095b,Copyright (c) 2026 dshs contributors)⇒ 不是我们造的,未动
/opt/dshs/LICENSE 1 份 同上(服务器副本)
dsh-laijing-github\dsh-multitenant\ 3 份 LICENSE(自拟分层条款)/ LICENSE-UPSTREAM-MIT.txt(上游 MIT 副本)/ THIRD-PARTY-NOTICES.md —— 我们造的 ⇒ 本次移除对象
dsh-laijing-github\_overlay\ 3 份同名快照 重建用;只删导出不删它 = 下次重建复活 ⇒ 一并删
代码内容里的「授权」表述 README.md(授权章节+徽章+目录项) · PLUGIN-PORTING.md(链接) · package.json(license 字段) 连带清理

执行(先备份、再删、并改脚本,否则重建会复活)

  • 备份 → dsh-laijing-github\_授权归档_发布时再放回\:那 3 份原文 + README.md.orig-带授权章节 + package.json.orig-带SEE-LICENSE
  • 删:导出仓 3 份 + _overlay 3 份;同步改 _build_export.py —— 从 OVERLAY 摘除 3 项、从 REQUIRED_EXPORT 摘除 3 项(原 26 项 → 23 项)、删掉把 license 改成 SEE LICENSE IN LICENSE 的 GLOBAL 规则(package.json 回到上游原值 MIT)
  • 连带清理死链:README 删 License 徽章 / 目录条目 / 文件清单两处 / 整个「授权与商业使用」章节;PLUGIN-PORTING.md 删 THIRD-PARTY-NOTICES 链接与「授权」字样
  • 技能同步更正(dsh-opensource-release v1.5.1→v1.5.2):§5 改为「授权类文件已全部移除」(但保留「上游 MIT 不得被附加限制」这条事实 —— 删了会误导后人写违规声明);§0 手工撰写层改「现为 3 份」;R-O5 改为「已移除、不再处理」;§3 映射表 <CONTACT_EMAIL> 行标注消失

终检:导出仓 0 个授权类文件 / 0 处授权类引用;REQUIRED_EXPORT 23/23;探针 0;tsc exit 0;README 目录锚点 0 悬空;文件数 143→140。

⚠️ 已记入台账 §F 的硬提醒(不是待办,是"若要发布必须先做")

要对外发布,必须先把那三份放回(并恢复 OVERLAY/REQUIRED_EXPORT)—— 否则:① 无 LICENSE ⇒ 默认「保留所有权利」⇒ 任何人都不能合法使用;② 缺上游 MIT 声明 ⇒ 侵犯 上游作者(已按要求不再具名) 的著作权;③ 缺第三方 NOTICES ⇒ 多款依赖许可证同样要求保留。当前仓库未发布(无 .git),所以现状无风险。

收尾:文档库提交 2e12b07 并 push(本地=远端,0 未提交);技能两副本 md5 一致;双端 141/141;锁按纪律走完并已释放。


15:24–15:3x · 「同步到仓库,把仓库中的这类授权信息也都删除」

用户追加要求:把代码仓里的授权类信息也删掉,并同步到仓库。

扫描(两仓,文件名 + 内容双维度)

位置 授权类信息
代码仓 D:\github\dsh_shenxian LICENSE(上游 MIT 原文,1107B)· package.json 的 "license": "MIT" · README.md 的 License 徽章(第 4 行)+ ## 许可证 章节(末 3 行)
服务器 /opt/dshs 同一份工作树的三个文件(另有细微差异:LICENSE 1086B / README 11547B / package.json 2668B)
文档库 仅 skills/dsh-opensource-release/SKILL.md(知识记录,非授权文件,不删)

执行

  1. 备份 11 份 → dsh-laijing-github\_授权归档_发布时再放回\(导出仓 3 份 + 2 个改动前版本;代码仓\ 3 份;服务器工作树\ 3 份)
  2. 本机代码仓:删 LICENSE;README.md 去徽章与许可证章节;package.json 删 license 字段(JSON 合法性已验证,version=0.1.0,无 license 字段)
  3. 重建开源导出(源 package.json 变了)→ 导出 package.json 也已无 license 字段;required 23/23、探针 0、文件 140、授权类文件/引用 0
  4. 提交 + 推送:ca5b62c(3 文件,LICENSE -21 行 / README.md -5 / package.json -3+1)→ c0abec8..ca5b62c → master,本地 = 远端 = ca5b62c ⚠️ 只提交我改的 3 个文件;对端会话在途的 M src/supervisor/orchestrator.ts(univer 内存预估 384→512)原样留着没碰
  5. 同步服务器工作树(否则又多一处漂移):删 /opt/dshs/LICENSE、送 README/package.json(转 LF)→ 复核 LICENSE=无、license 字段=0、许可证章节=0,两文件 md5 与本机一致
  6. 健康检查:平台 active、首页 HTTP 200(改的是 README/package.json,不影响运行时,未重启)

⚠️ 两点如实说明(已写进 commit message 与台账)

  1. git 历史里仍在:LICENSE 是上游 08-18 提交 a2b095b 引入的,删掉的是工作树;历史提交仍含它。要彻底抹掉得改写历史(未做,通常也不该做)。
  2. 合规前提:本仓是上游 MIT 项目的衍生仓。私有内部使用不受影响(MIT 的"保留声明"义务针对分发);但若日后要对外分发/发布,必须恢复 MIT 版权与许可声明,否则构成侵权 —— 备份就在 _授权归档_发布时再放回\代码仓\。

11:32–11:4x · 双语文档口径(用户:「文档别忘了中英文双语」)

用户指令:「文档别忘了中英文双语,后续优先写中文,需要同步到 GitHub 时再更新英语。」

结论:定口径 + 写进技能,但英文版此刻刻意不生成(按用户"同步时再更新")。

口径(技能 R-O13 · 双语文档,v1.3.2)

项 值
母本 中文(README.md / PLUGIN-PORTING.md),日常只维护中文
英文文件 README.en.md / PLUGIN-PORTING.en.md
生成时机 发布 / 推送 GitHub 之前一步(SOP 新增 5.5 步)—— 即 §7 验证后、§6 交付前
语言切换行 两份顶部都要有;⚠️ 英文文件存在之前不加(否则死链,扣 P1)
逐字保留 代码块 / 命令 / 路径 / env 名 / 包名 / URL / ASCII 图 / 表格结构 / 「版本与迭代」表;占位符同步替换
不翻译 LICENSE-UPSTREAM-MIT.txt(MIT 英文原文逐字节)· LICENSE(已是中文正文 + 英文摘要,现状即符合)· THIRD-PARTY-NOTICES.md(中文即可)
校验 英文版同样跑 TOC 锚点(按英文标题)+ 相对链接存在性;两版版本表行数与版本号一致

技能改动:新增 R-O13(含 SOP 5.5 步 + §0 事实「文档语言」行 + §10 ⏳ 待办「英文版待生成·发布前必做」);§1 标题改 R-O1–R-O13。台账新增 §九 F 双语文档。 顺手修:frontmatter 的 last_change 因替换锚点只取前缀导致新旧两段拼接(1.3.2 文 + 1.3.1 残留),已重写为一行干净历史。⚠️ 教训:改 frontmatter 这类单行长文本时,锚点要覆盖整行(或整行替换),别用前缀。 归档同步:md5 a4b04f99… 两副本一致,四件套 rc=0,锁按新规矩走。当前英文版未生成(符合用户口径)。


13:34–13:5x · 开源资料独立成文件夹:E:\ProgramData\AI技能\dsh-laijing-github\

用户指令:「E:\ProgramData\AI技能\dsh-laijing-github 把相关资料整理到 这个文件夹下,这个文件夹作为项目独立的 github 开源项目文件夹」。

结果(新工作根 = E:\ProgramData\AI技能\dsh-laijing-github\)

dsh-laijing-github\
├── dsh-multitenant\          ← 仓库根(141 文件,可直接 git init / push)
├── _build_export.py          ← OUT 已改指新工作根
├── _verify_tsc.mjs           ← EX 已改指新工作根
├── _overlay\                 ← 手工撰写层快照(6 个文件)
├── _rename_selfref.py        ← 一次性改名脚本(保留备查)
└── _导出说明与脱敏台账.md

原位置 aliyun-dsh-server\_开源导出_20260913\ 已整体迁走并删除(无残留)。

⚠️ 过程中闯了一次祸(已修,已进技能事故清单 #9 / 坑 17):我先 mv 目录、后才改脚本常量 —— 中间为了「验证」跑了一次重建,脚本仍指向旧路径 ⇒ 在旧位置把整个仓库重新建出来(135 文件),且因旧位置没有 _overlay,6 个手工层文件(README / LICENSE / LICENSE-UPSTREAM-MIT / NOTICE / PLUGIN-PORTING / install.sh)全部缺席。所幸:① 真身(新位置的仓库 + _overlay)完好无损(README 37375 字节未变);② 已删掉复活目录;③ 改正常量顺序后重建 = 141 文件、手工层 6/6 恢复、探针 0、tsc exit 0。 教训(已固化):迁工作根的顺序必须是 先改 OUT/EX → 再搬目录 → 再重建 → 核对文件数与手工层 6/6 → ls 确认旧位置没复活;OUT 指错时脚本不报错,只在别处默默新建一套。

同步更新:技能 §0 事实(导出根 → 工作根)、§9 命令、§10 说明、相关链接全部改新路径;项目 MEMORY.md 的「开源导出」条已改为新工作根。技能升 v1.4.0,归档副本已同步(md5 d00d8fb7…),四件套 rc=0,锁按新规矩走(claim 单独 → 验 OWNER → 操作 → 再验 → release)。 未做(等指示):git init / commit / push(R-O6)。

14:25–14:50 会话 a2665dd3:★我的失误(0.2.19 误带堆限)+ 深挖出「384 MiB 结构上装不下 gateway」的定论

一、失误与修复(责任在我)

  • 0.2.18 的 V8 堆限补丁我只"回滚了部署、没回滚源码" ⇒ 随后从同一工作树打出的 0.2.19 把 --max-old-space-size=160 一起发出去了 ✗✗。
  • 实例内 agent(turn 9)实测到后果:Mark-Compact (reduce) 158.4 (160.8) -> 158.0 (160.8) MB + FATAL ERROR: Reached heap limit,且 OOM 发生在编译 51 MB 包的懒编译函数时(Runtime_CompileLazy → ObjectLiteralBoilerplateBuilder)⇒ gateway 根本起不来(比我原先判断的"无效"更糟:有害)。
  • 已发 0.2.20 修复:删掉堆限块(max-old-space-size 归零 ✓),保留 watchdog / idle 透传 / 20s 就绪超时 / 陈旧 socket 清理。池内与 guest 均已 0.2.20 ✓(池内 42,046,113 B @ 14:30)。
  • 教训:回滚必须回滚源码(部署回滚只在制品层,下次重建会把补丁带回来)。⇒ 已把"改完必须核对源码里没有残留"作为动作。

二、★更深的定论:不设堆限,gateway 仍然 OOM

  • 0.2.20 下去后再触发:gateway/start → ok:false,"… v8::internal::V8::FatalProcessOutOfMemory …" ✗(无任何显式堆限)。
  • 机理:V8 会感知 cgroup 上限(实例 384 MiB)并据此设堆顶;而 gateway 需要的堆(无上限口径实测 ~290 MB)+ 代码/外部(~100 MB)结构性超过 384 ⇒ 在 384 MiB 的实例里,univer 的 gateway 无论怎么调参都跑不起来(三种口径互证:无上限实测 RSS 335–400 / 堆需求 290 MB / cgroup 感知下 abort)。
  • 现场旁证:带 gateway 时 cgroup 读数 412–499 MiB / 384(超顶),实例已被迫重启过一次(scope 从 0ee7ec3c → 0db05698 → cba60bd0)。
  • ⇒ 结论:要 univer 真能用,只有 (a) 提配额(≥640) 或 (D) 把 gateway 移出实例 cgroup;E(空闲自停)只解决"平时占用"(已实测回收后 cgroup 落到 98 MiB ✓),救不了"用的时候"。

三、"能不能按需加载/只加载文档+表格"(实测判定)

  • gateway 产物里没有 slides(@univerjs/slides=0 / slides-ui=0 / docs-ui=0),只有 @univerjs/sheets(42) / @univerjs/docs(14) / boards(32) 引用 ⇒ 它本来就≈"表格+文档(+board)",没有幻灯片可省。
  • @univerjs-pro/collaboration-service 是 1.5 KB 转发壳(真实现别处);产物是全静态单文件 bundling,不存在运行时按需加载;要按需只能改打包(code splitting),而静态 import 的引擎切分省不了"运行时真要用"的部分。
  • 真正的"按需"机会在用法:现在连"列 unit / 读内容 / 导出"都要先起 gateway(resolveTarget() 用 GatewayClient.listUnits)⇒ 若改 fork 的数据访问路径(直接从文件/sqlite 取 unit 列表),看文件/导出可只走 worker(短命进程、不常驻)。工作量中等,属"轻量路线"。

14:26–14:5x · 上传完整性核对 ⇒ 查出 assets/ 漏收录(严重)

用户指令:「确认所有需要上传的内容 都同步到新文件了吗」。

方法:不是"看一眼目录",而是 12 项机检(含源仓库顶层条目逐项对照、隐藏文件、违规目录、垃圾文件、README 目录结构自洽、package.json 入口、用仓库自带校验脚本验收)。

🔴 问题 1:assets/ 没进 INCLUDE_DIRS(严重)

  • 源仓库有 assets/inject/{recovery.js,assist.js}(R1-① 外置的注入脚本,共 ~30 KB),导出物里没有。
  • 危害:src/supervisor/proxy.ts 的 loadInject() 按包根解析(<pkgRoot>/assets/inject/<file>)且fail-fast 抛错(「assets/inject 必须随包部署」)⇒ 导出的仓库根本起不来;仓库自带 scripts/verify-inject.cjs(npm test 的一环)也会判失败 —— 实测修前该脚本会报「assets/inject 下没有 .js」。
  • 另发现:package.json 的 files(lib, web, cordis.patch.yml, README.md)也缺 assets ⇒ npm 打包 / dsh plugin add github: 装法同样会缺。
  • 处置:① INCLUDE_DIRS 补 assets;② package.json 的 files 补 assets(写成 LINE_REWRITE 规则);③ 新增 REQUIRED_EXPORT 清单(26 项):构建时逐个 os.path.exists,缺一即 return 1(白名单收录漏项是静默的,必须有断言兜底)。

🔴 问题 2:档案号剥除留下空括号残渣(轻微但显眼)

  • REGEX_RULES 删掉「档案 NN」后,ASCII 括号侧没有清理规则 ⇒ 导出物里出现 backoff (). / simulate... auto-restarts (). / section (). Same-name...(3 处)。全角侧早有清理规则,ASCII 侧因为曾误伤 foo() 导致 tsc 报错而被"铁律"挡住。
  • 处置:加两条受限 ASCII 规则 —— ① (档案 NN)(模式里必须出现档案号 ⇒ 不可能命中 foo());② () 紧跟句末 .+空白/行尾((): void 后是 : ⇒ 不命中)。
  • 验证:重建后残渣 0、正常 (): type 保留 8 处、tsc --noEmit exit 0 ✓(tsc 就是这类正则的安全网)。

✅ 其余 10 项通过

143 文件 · 目录分布(assets 2 / src 51 / web 13 / scripts 28 / deploy 11 / poc 16 / test 5 / .github 1)· 隐藏文件齐(.gitignore/.dockerignore/.github)· 关键文件 17 项齐 · 手工层 6/6 md5 与 _overlay 一致 · 源→导出只剩 3 项有意排除(docs/ STANDARD.md .workbuddy/)· 违规目录 0 · 垃圾文件 0 · 旧位置没复活 · README 目录结构 6/6 存在 · bin→lib/cli.js 有 prepare 构建 ✓ · node scripts/verify-inject.cjs → 全部合格 ✅。

技能 v1.5.0:§2 补 assets/(含危害说明)与目录计数 · §3 补「档案号 / 悬空引用清理」两行(原先完全没记录 REGEX_RULES)· §0.5 加事故 #10 · §8 加坑 18(白名单静默失败 → 断言 + 自带校验脚本双防线)。台账加 §八 G 上传完整性核对(12 项 + 2 个问题 + 处置)。归档副本已同步(md5 见下),四件套 rc=0。

14:36 会话 a2665dd3:配额/堆的推导机制(拍板 a 还是 D 的事实依据)

读 src/supervisor/orchestrator.ts(档案 81 R1-④ 的产物):

const MIN_MEM_MB = 384, MAX_MEM_MB = 1024, HEAP_HEADROOM_MB = 96
instanceMemMb(profileDir) = clamp(BASE_MEM_MB + Σ PLUGIN_MEM_MB[bundle], 384, 1024)   // 读 profile 的 bundles 推导
heapMbFor(memMb)          = clamp(memMb - 96, 128, 256)                              // 堆 = 配额 − 96,夹 128–256
withHeap()                → NODE_OPTIONS=--max-old-space-size=<heapMbFor>(env 只承载其它选项;强指用 DSHS_HEAP_MB_OVERRIDE)
systemd-run … -p MemoryMax=<memMb>M

⇒ 两个关键结论

  1. (a) 提配额 = 改 PLUGIN_MEM_MB['dsh-univer-office'] 一个数字(+ build + 重启服务):配额自动升到 BASE+Σ(上限 1024),隔离完全不变。
  2. gateway 的 V8 堆不受 NODE_OPTIONS 影响(host spawn 时把 env 白名单化,只传 HOME/LANG/LC_ALL/PATH/TMPDIR + 显式变量)⇒ gateway 的堆顶由 V8 感知 cgroup 上限决定 ⇒ 只要 MemoryMax 够大,gateway 的堆就够大。这解释了为什么"给 gateway 塞 flag"无效(0.2.18/0.2.19 反而 abort ✗),而"抬配额"是有效路径 ✓。
  3. heapMbFor 夹到 256 上限 ⇒ 若将来实例自己也吃紧,需一并调整该 clamp(属同一次改码)。

14:36–14:55 会话 a2665dd3:★更正:实例真实配额是 544 MiB(不是 384)—— 预算表机制已上线

更正:此前我反复引用的"384"是旧写死值 / MIN_MEM_MB 下限。实测:

  • guest 实例 MemoryMax = 544 MiB(=基座 BASE_MEM_MB=160 + PLUGIN_MEM_MB['dsh-univer-office']=384)✓
  • admin 实例 = 384 MiB(其 bundles 不含 univer ⇒ 160+0 → 被 MIN_MEM_MB 夹到 384)✓ 两条实测互证了推导机制
  • 预算表已在服务器上线(src/lib mtime 均 11:14;实例 11:40 起)✓ 隔离零改动。
  • 实例 heap = heapMbFor(544) = clamp(544−96,128,256) = 256 ✓

⇒ 这直接改变了 a/D 的判断

  1. (a) 不是"加内存"这么笼统,机制已在跑;剩余动作 = 把 PLUGIN_MEM_MB['dsh-univer-office'] 从 384 → 512(配额 → 672)+ build + 重启服务。1 行改动、隔离不变。
  2. (D) 的独有好处只有一条:实例配额保持不变(探针继续只测本体);而它不省宿主内存(gateway 仍占 ~390,只是换 cgroup 记账),却要付隔离降级 + 生命周期自理 + 审计的代价。
  3. 宿主容量(重要):total 1870 / used 676 / available 1194,但 swap 已用 965 / 1024(94%) ⚠️ ⇒ 抬配额(+128 MB)够让 univer 勉强跑起来,但要真正宽裕必须扩宿主内存(花钱)。
  4. 当前实测:guest usage 363 MiB(无 gateway 时 rss 198–305);gateway 需求 ~390 ⇒ 544 仍欠 ~50–120 MB,故需 672 档。