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 一律写「远程服务器」。
175 lines
16 KiB
Markdown
175 lines
16 KiB
Markdown
# 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")。
|
||
```
|