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 一律写「远程服务器」。
16 KiB
16 KiB
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 必然失败;属"两仓内容取舍"决策,不在本单强行动手(详见 §八 回报「偏离」) |
一、目标
把已拍板的三项治理落到可机械执行的状态:
- 文档库 git 提交常态化:先建一次基线提交(给后续会话留下"谁刚改了什么"的路标),此后每次改完只提交自己改的文件;
- 服务器侧第二把锁:给「平台高危操作」加显式互斥(重启 / drain / 改 env·quota / 铺插件 / 改 nginx·nft·证书);
- 权限统一:
/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 的"两选一" |
五、步骤(每步自带验证)
- 静态窗口确认(关键):基线提交期间不能有人在改库。
做法:
bash scripts/handoff-guard.sh(看占用/热点)+ 口头确认另一个会话已停手。 验证:guard ① 无他人占用锁;③ 无"我的目标文件被他人动过"之外的异常。 - 基线提交(两选一,按用户口径执行):
- 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剩余项全部是他人的且数量可解释。
- A(推荐,一次性干净):把当前全部未提交内容作为一个基线提交(含他人成果)——
- 写
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为空。
- 建立"必须先占位"的操作清单(写进
交接单/README §六):重启dshs/ drain / 停dsh-*.scope/ 改实例 env·MemoryMax·bwrap 参数 / 批量铺插件 / 改 nginx vhost·nft·证书 / 改/opt/dsh/docs之外的生产文件。 验证:清单可 1:1 对应到 §六 表。 - 权限统一:
ssh bt-server 'chmod 700 /opt/dsh/docs/交接单 && chmod 600 /opt/dsh/docs/交接单/*.md'。 验证:stat -c "%a %n"全部符合;ls -l与04-调整方案/对比一致。 - 文档收口:
交接单/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 发现撞车。
怎么做(沿用项目既有链路,勿发明新路径):
- 服务器侧先 commit(只 add 本次改造涉及的文件,禁
git add -A):git -C /opt/dshs add <逐条列出>→git commit -F <消息文件>(消息带档案号); - 再按约定把提交带回本机:
git bundle master --not <本机 HEAD>→ scp 到D:\github\dsh_shenxian→git fetch <bundle> && merge --ff-only; - push 到 Gitea 需用户明确说"推送"(红线,本单不授权 push);
- 确认服务器工作区干净:
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")。