回收 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)、记忆修复前备份。
437 lines
47 KiB
Markdown
437 lines
47 KiB
Markdown
# 工作日志 · 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-④ 的产物):
|
||
```ts
|
||
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 档。
|
||
|
||
|