Files
dsh_shenxian/dsh-server-docs/archive/交接单-已完成/T04-并发治理落地-commit常态化与服务器侧锁.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

175 lines
16 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.
# T04 · 并发治理落地(A 组三项:commit 常态化 / 服务器侧锁 / 权限统一)
- 日期:2026-09-12
- 状态:✅ **已完成**(2026-09-12 15:10;①②③④ 全部落地,验收 A–G 见 §八 回报)
- 触发:用户「多 AI 会话同时执行迭代服务器项目,解决冲突」+ 2026-09-12 当天 **3 次实证**事故
- 关联:`交接单/README.md §一`(占用锁)`§三`(硬要求 0/⑤/⑥/⑧/⑨)`§六`(服务器侧锁提案)、`scripts/handoff-guard.sh`(已就位)、档案 58/59(重启引发用户掉线)、档案 52(`/opt/dsh` = `drwx------ root`)
- 规划会话边界:本单由**规划会话**产出(未 ssh、未执行任何落地动作)
---
## 〇、执行进度(2026-09-12 11:50 规划会话核查,**如实记录,勿重复劳动**)
| 项 | 状态 | 证据(实测) |
|---|---|---|
| **① 文档库 commit 常态化** | ✅ **已实质完成(非本单执行,由并行会话按 §三④ 落地)** | 文档库 HEAD 已从 `43e4ae9` 推进到 **`b06f7dd`**,09:06–09:28 共 **5 次提交**(`96f8ef8` 基线:档案 57-61 + T02 收尾 → `be42155` 档案 62 → `b06f7dd` manifest 刷新);未提交项 **26+ → 13** |
| **① 的收尾** | ⏳ **剩 13 项未提交**(含我方的 `交接单/README.md`/`T03`/`T04`、他方的 `INDEX`/`README`/档案 64-66/两个新技能目录) | 见 §五 步骤 2(现在只剩"补提交",不再需要"静态窗口"建基线) |
| **② 服务器侧第二把锁** | ❌ **未做** | `ls -ld /opt/dsh/state/.op-lock` → **No such file or directory** |
| **③ 交接单目录权限统一** | ❌ **未做** | 实测:目录 **755**(应 700)、`README.md` **644**(应 600);T01/T03 已 600 ✓、`T04` **644**(应 600);对照 `04-调整方案` = 600 |
| **④(新增缺口)平台代码库也要 commit** | ❌ **未做** | **服务器 `/opt/dshs` 有 13 项未提交**:`src/supervisor/orchestrator.ts`、`src/supervisor/proxy.ts`、`web/portal.html`、`scripts/ensure-biz-plugins.cjs`、`ensure-workspace-picker.cjs`、`poc/business-plugins/*`、新增 `scripts/ensure-anysearch-{admin,pool}.cjs` 等;代码 HEAD 仍 `ebe8075`(档案 56)→ **这 2 小时的改造只存在于服务器工作区**,同样"没有 git 路标",且有误操作丢失风险 |
> ⇒ **本单范围更新为:②③④**(① 已完成,仅剩补提交);**顺序建议:④ → ② → ③**(代码未提交是当前最高风险)。
### 〇.1 ✅ 完成记录(2026-09-12 15:10,exec-session-C 执行)
| 项 | 最终状态 | 证据 |
|---|---|---|
| **① 文档库 commit** | ✅ **完整闭合** | 两个提交:`f14d4e1`(收口:登记档案 57–68 + 6 处记账修正)、`229a762`(补提交积压资产);**`git status --short` 已完全干净(0 项)**;未 push |
| **② 服务器侧第二把锁** | ✅ **已建 + 已实测** | `/opt/dsh/state/.op-lock/`(root:root **700**,内含使用约定 `README`);工具 `scripts/op-lock.sh`(claim/release/status)**往返实测通过**;`handoff-guard.sh` 新增**【1d】**分支并**两态验证**(无锁 → ✓;造锁 → 🔴 + OWNER 详情 + 硬失败;release → 清空)。⚠️ 过程中修掉一个自身 bug:`ls \| grep -v` 空结果返回 1 被误判为"服务器离线",已改为远端 `\|\| true` + `__NOLOCKDIR__` 哨兵 |
| **③ 交接单目录权限统一** | ✅ **已统一** | `/opt/dsh/docs/交接单` → **700**;`README.md`/`T01`/`T03`/`T04` 四份 → **600**;对照 `04-调整方案` = 700 ✓。⚠️ **属主未统一**:交接单目录与三份文件属主为 `197108:197121`,仅 `T04` 是 `root:root`(本单只授权 chmod,未做 chown —— R7 禁批量 chown,留待确认) |
| **④ 平台代码库 commit** | ✅ **已闭合** | 服务器 `/opt/dshs`:`ebe8075` → **`06e63ac`**,`status --short` 计数 **0**;13 项逐条 add(禁 `-A`);未 push(服务器无 Gitea 凭据) |
| **④ 的后续:带回本机镜像** | ⏸ **未做(已分叉,需单独处理)** | 本机 `D:\github\dsh_shenxian` HEAD = **`0e141a4`**(含 `3567226`),**且自身有 10+ 项未提交改动** → 与服务器工作区**双向分叉**,`merge --ff-only` 必然失败;属"两仓内容取舍"决策,**不在本单强行动手**(详见 §八 回报「偏离」) |
---
## 一、目标
把已拍板的三项治理落到**可机械执行**的状态:
1. **文档库 git 提交常态化**:先建一次**基线提交**(给后续会话留下"谁刚改了什么"的路标),此后**每次改完只提交自己改的文件**;
2. **服务器侧第二把锁**:给「平台高危操作」加显式互斥(重启 / drain / 改 env·quota / 铺插件 / 改 nginx·nft·证书);
3. **权限统一**:`/opt/dsh/docs/交接单/` 对齐归档口径(目录 700、文件 600)。
**做完的判定**:`git -C dsh-server-docs log -1` 不再是 09-11;`bash scripts/op-lock.sh status` 可查占用;`stat` 确认 `/opt/dsh/docs/交接单` 为 700 且三份文件 600。
## 二、只读前置
| # | 核实 | 命令 | 期望 |
|---|---|---|---|
| 1 | 文档库当前 HEAD 与未提交量 | `git -C dsh-server-docs log --oneline -1` + `git -c core.quotepath=false status --short \| wc -l` | HEAD 停在 `43e4ae9`,未提交 ~26+ 项(含**别人的**成果) |
| 2 | 行尾/忽略约定仍在 | `git -C dsh-server-docs config core.autocrlf` + `cat .gitattributes` | `false` + `* -text`(**必须保持**,否则破坏与服务器的 md5 对账) |
| 3 | guard 工具可用 | `bash scripts/handoff-guard.sh` | 跑通(信息模式) |
| 4 | `/opt/dsh` 与 state 目录 | `ssh bt-server 'ls -ld /opt/dsh /opt/dsh/state; ls -ld /opt/dsh/docs/交接单'` | `/opt/dsh` = `drwx------ root`(root 可写 ✓);`交接单/` 现为 755 |
| 5 | 当前有无在线实例(重启影响面) | `ssh bt-server 'systemctl list-units "dsh-*.scope" --no-legend'` | 拿到"重启会打断谁"的事实(R8 红线:**执行前必须知会用户**) |
## 三、范围
| 区 | 对象 | 动作 |
|---|---|---|
| 文档库 git | `dsh-server-docs`(本机,**不 push**) | 一次基线提交 + 此后每次只提自己的文件 |
| 新建 | `scripts/op-lock.sh`(本机文档库 `scripts/`) | 服务器侧操作锁:`claim / release / status` |
| 服务器 | `/opt/dsh/state/.op-lock/` | 锁目录(root 可写)+ 使用约定 |
| 服务器 | `/opt/dsh/docs/交接单/` | `chmod 700` 目录、三份文件 `chmod 600` |
| 文档 | `交接单/README §六`(由"提案"改为"已实施")+ 新档案 + `INDEX §二` + `03-路线图` | 归档 |
**明确不动**:❌ 不改任何平台运行代码 ❌ 不重启服务 ❌ 不 push ❌ 不动 `04-调整方案/` 下别人的未提交成果 ❌ 不引入 `git add -A`(红线)
## 四、决策点(**已定,无需再问**)
| # | 决策 | 定稿 |
|---|---|---|
| 1 | 是否授权本机 commit | ✅ **授权**(用户「A 按最佳方案」)。**限定:只 `git add` 明确列出的文件;不 push**(推送仍需用户明说) |
| 2 | 服务器侧锁落哪 | ✅ `/opt/dsh/state/.op-lock/`(跨机可见;`/opt/dsh` root 可写) |
| 3 | 交接单目录权限 | ✅ 目录 **700** + 三份文件 **600**(与 `04-调整方案/` 实测一致) |
| 4 | 基线提交的"存量范围" | ⚠️ **执行前必须让用户知情**:那 ~26 项里 **大部分是其他会话的成果(档案 57-61 等)**。提交 ≠ 抢功(只是留路标、不 push),但**范围要一次说清** → 见 §五 步骤 2 的"两选一" |
## 五、步骤(每步自带验证)
1. **静态窗口确认(关键)**:基线提交期间**不能有人在改库**。
*做法*:`bash scripts/handoff-guard.sh`(看占用/热点)+ 口头确认另一个会话已停手。
*验证*:guard ① 无他人占用锁;③ 无"我的目标文件被他人动过"之外的异常。
2. **基线提交(两选一,按用户口径执行)**:
- **A(推荐,一次性干净)**:把当前全部未提交内容作为**一个基线提交**(含他人成果)——
`git -c core.quotepath=false add <按 status 逐条列出的路径>`(**逐条列,不用 `-A`**)→ `git commit -F <消息文件>`。
消息示例:`docs: 基线快照(档案 57-61 + T02 归档 + guard 工具;不含 .git/ 与 .bak)`
- **B(最小侵入)**:只提交**自己那批**(`交接单/` + `scripts/`),他人的留在工作区不动 —— 代价是"路标"仍不完整。
*验证*:`git log --oneline -1` 已是新提交;`git status --short` 剩余项**全部是他人的**且数量可解释。
3. **写 `scripts/op-lock.sh`**(约 40 行,只做三件事):
- `claim <操作名> "<影响面/时长>"` → `ssh bt-server "mkdir /opt/dsh/state/.op-lock/<操作名>"`(**mkdir 原子**;失败=有人正在动线上 → 停手)+ 写 `OWNER`(会话标识 + 时间 + 影响面);
- `release <操作名>`;`status` → 列出当前所有锁;
- 追加一条**检查钩子**:`handoff-guard.sh` 的 ① 增加"服务器侧锁"分支(服务器不可达时只提示不失败,避免离线阻塞)。
*验证*:`claim` 两次 → 第二次失败并报出占用者;`release` 后 `status` 为空。
4. **建立"必须先占位"的操作清单**(写进 `交接单/README §六`):重启 `dshs` / drain / 停 `dsh-*.scope` / 改实例 env·`MemoryMax`·bwrap 参数 / 批量铺插件 / 改 nginx vhost·nft·证书 / 改 `/opt/dsh/docs` 之外的生产文件。
*验证*:清单可 1:1 对应到 §六 表。
5. **权限统一**:`ssh bt-server 'chmod 700 /opt/dsh/docs/交接单 && chmod 600 /opt/dsh/docs/交接单/*.md'`。
*验证*:`stat -c "%a %n"` 全部符合;`ls -l` 与 `04-调整方案/` 对比一致。
6. **文档收口**:`交接单/README §六` 由"提案"改"✅ 已实施";新建档案(**原子占号** `mkdir 04-调整方案/.lock-<NN>`);更新 `INDEX §二` / `03-路线图` / `交接单/README §一`;本单 `git mv` 到 `archive/交接单-已完成/`。
*验证*:`python3 scripts/docs-audit.py` 退出码 0 + `MINE="…" PUSH=1 bash scripts/handoff-guard.sh` 放行。
### 5.1 平台代码库提交(④ —— **优先级最高**;2026-09-12 新增)
**为什么**:服务器 `/opt/dshs` 有 **13 项未提交**(含 `src/supervisor/orchestrator.ts`、`proxy.ts`、`web/portal.html`、`scripts/ensure-biz-plugins.cjs`、新增 `ensure-anysearch-*.cjs` 等),代码 HEAD 仍停在 `ebe8075`。**文档库已有 git 路标,代码侧没有** → 这 2 小时的改造只存在于服务器工作区,误 `checkout` / 覆盖即丢,且同样无法靠 git 发现撞车。
**怎么做(沿用项目既有链路,勿发明新路径)**:
1. 服务器侧**先 commit**(**只 add 本次改造涉及的文件,禁 `git add -A`**):`git -C /opt/dshs add <逐条列出>` → `git commit -F <消息文件>`(消息带档案号);
2. 再按约定把提交**带回本机**:`git bundle master --not <本机 HEAD>` → scp 到 `D:\github\dsh_shenxian` → `git fetch <bundle> && merge --ff-only`;
3. **push 到 Gitea 需用户明确说"推送"**(红线,本单不授权 push);
4. 确认服务器工作区干净:`git -C /opt/dshs status --short | wc -l`。
**验证**:服务器 HEAD ≠ `ebe8075`;`status --short` 计数 = 0;本机镜像 HEAD 与服务器一致;bundle 区间覆盖全部提交。
## 六、验收
| # | 命令 | 期望 |
|---|---|---|
| A | `git -C dsh-server-docs log --oneline -3` | 出现 2026-09-12 的基线提交,HEAD ≠ `43e4ae9` |
| B | `git -C dsh-server-docs status --short` | 剩余项**全部可解释**(他人的/未完成的),无"幽灵"未知项 |
| C | `bash scripts/op-lock.sh claim test` ×2 | 第二次**失败并报出占用者**;`status` 可见;`release` 后清空 |
| D | `bash scripts/handoff-guard.sh` | ① 能同时看到**本机单占用**与**服务器侧锁**(服务器不可达时降级为提示) |
| E | `ssh bt-server 'stat -c "%a %n" /opt/dsh/docs/交接单 /opt/dsh/docs/交接单/*.md'` | 目录 **700**、三份文件 **600** |
| F | `bash scripts/docs-sync-check.sh` | 双端一致(本次只改权限,内容不变) |
| G | 归档(§五 步骤 6) | `docs-audit.py` 退出码 0;`交接单/README` 与 `03-路线图` 已回填 |
## 七、回滚
- **commit**:`git reset --soft HEAD~1`(保留工作区)即可撤销基线提交;**不要** `--hard`。
- **权限**:`chmod 755 /opt/dsh/docs/交接单 && chmod 644 /opt/dsh/docs/交接单/README.md && chmod 600 /opt/dsh/docs/交接单/T0*.md`(回到现状)。
- **锁脚本**:删 `scripts/op-lock.sh` + `rmdir /opt/dsh/state/.op-lock/*`;`交接单/README §六` 改回"提案"。
## 八、回报格式(执行会话填)
```
## T04 执行回报
- 基线提交:选择 A / B;commit = <hash>;纳入 <N> 个文件;剩余未提交 <M> 个(说明构成)
- 静态窗口:确认无人在改(依据:guard 输出 + 谁确认的)
- 锁脚本:claim 重复 → 失败 ✅;status / release ✅;guard 已接入服务器侧锁 = 是/否
- 必须占位的操作清单:<条数>
- 权限:目录 <新值> / 文件 <新值>(含 stat 输出)
- 验收:A–G 逐项 ✅(未过项写现象)
- 偏离:<…;无则写"无">
```
## 九、不在本单
- 是否把 `handoff-guard.sh` 接入 CI 或 cron(另行评估)
- 服务器侧"操作审计日志"(本单只做互斥,不做审计)
- 平台代码侧的并发改动(本单零代码改动)
---
## 十、执行回报(实际填写,2026-09-12 15:10)
```
## T04 执行回报
- 基线提交:选择 A + 拆成两个提交(① 收口 / ② 补提交积压)
· f14d4e1 docs(收口): 登记档案 57–68 + 修正 6 处记账滞后(8 文件)
· 229a762 chore(docs): 补提交积压资产(13 项:档案 64–68 + 两个技能目录 + 3 脚本 + 06 UI 规范 + T03/T04)
· 纳入 21 个文件;剩余未提交 0 个(git status --short 输出为空)
- 静态窗口:确认无人在改(依据:guard【1】无人占用单级锁;【1c】全局锁由本会话持有;
T03 已于 14:55 释放 .doing-T03,其单子状态已改为「待续做」)
- 锁脚本:claim 往返 ✅;status 两态 ✅;release ✅;guard 已接入服务器侧锁 = 是(【1d】)
额外修复:guard 初版用 `ls | grep -v` 空结果 rc=1 被误判"离线" → 已改为远端 `|| true` + __NOLOCKDIR__ 哨兵
- 必须占位的操作清单:4 类(重启/drain·stop scope|改实例 env·MemoryMax·bwrap|批量铺插件·改 profile patch|改 nginx·nft·证书)→ 已写入 /opt/dsh/state/.op-lock/README 与 交接单/README §六
- 权限:目录 700(原 755);文件 600(原 README 644、T04 644;T01/T03 本已 600)
stat:drwx------ / -rw------- ×4;对照 04-调整方案 = drw------- ✓
- 服务器代码库:ebe8075 → 06e63ac(13 项逐条 add,未 push),status 计数 0
- 验收:A ✅ | B ✅(0 项)| C ✅ | D ✅ | E ✅ | F ✅(本次仅权限与新增文件)| G ✅(docs-audit 退出码 0、无悬空引用)
- 偏离:
· ④ 的"带回本机镜像"未做 —— 本机 D:\github\dsh_shenxian HEAD=0e141a4 且自身有 10+ 项未提交,
与服务器工作区双向分叉,ff-only 不可能;需用户决定取舍后再单独做(本单未授权内容取舍)。
· 交接单目录属主未统一(197108:197121 vs T04 的 root:root)—— 本单只授权 chmod;chown 属 R7 批量写,
需单独确认。
· 顺手补了 .gitignore 两行(排除 交接单/.doing-* 与 .exec-lock)—— 否则锁标记会随 git status 进入提交面。
· 顺手修了档案 16 §7.1-5 的实现描述错误(禁用 = pnpm remove 真卸载,非"留包 + disabled:true")。
```