整合作业规矩:agent-operating-rules 整包并入本技能 references/作业规矩/
This commit is contained in:
1 parent
119f0084e2
commit
9fd120f40b
12 files changed
+1725
-7
No files matched your search
@@ -0,0 +1,268 @@
|
||||
# 参考 02 — 工作区纪律(全文)
|
||||
|
||||
> 本文件是 `agent-operating-rules` 的**细节层**。入口已给硬规则;本文件给**完整依据、出处与全表**。**按需读**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 🔴 工作区归属三律(**最优先,违反即事故**)
|
||||
|
||||
**背景实测**:某工作区会话「参考接续会话的规则」时,**照抄了另一个工作区的绝对路径** ⇒ 把**入口文件**和**接续任务**都落到了别人的工作区,而它那条线自己的家是第三处。同一份入口出现两处、md5 相同 = **第二真相源**;平台工作区的状态脚本因此把**别线**报成了自己的线(实测报"2 条工作线",其中一条不是它的)。
|
||||
|
||||
| 律 | 内容 | 反例(都真发生过) |
|
||||
|---|---|---|
|
||||
| ① **入口只允许一份** | 位置 = **那条线自己的工作区根**;头部必须写 `> 🔴 **工作区**:<该工作区绝对路径>` | 两处同改(两个工作区各一份,md5 相同) |
|
||||
| ② **自动化 `cwds` = 本工作区** | 定时 / 接续任务的 `cwds` 只认**那条线自己的工作区**;⛔ 不因"脚本在别的工作区"就把 `cwds` 设过去 | 照抄"第 0 步跑 `state.py`" ⇒ `cwds` 写成**脚本所在**的工作区 |
|
||||
| ③ **参考规则 = 加载技能,不是读别的工作区的文档** | 要副本就复制**规则文档**到本工作区 `docs/会话与接续/`(或等价目录);⛔ **入口文件不复制** | 直接读别的工作区的 `接续入口_*.md`,照抄其中路径 |
|
||||
|
||||
**跨工作区取脚本的正确姿势**(不违反律②)—— 本工作区没有脚本时,用**绝对路径**调、并指定目标工作区:
|
||||
|
||||
```bash
|
||||
python "<脚本绝对路径>" --ws "<自己的工作区绝对路径>"
|
||||
```
|
||||
|
||||
⛔ **把脚本复制到每个工作区** = 多一份要维护的代码;`--ws` 是正解。
|
||||
⚠️ 若脚本**不支持 `--ws`**,仍然用绝对路径调用它,**并额外用本项目自己的方式取状态** —— ⛔ 不要为了"对齐输出格式"而把脚本抄一份过来。
|
||||
|
||||
### 1.1 收尾自检一句
|
||||
|
||||
> **本棒的 `cwds` 是否 = 我这条线自己的工作区?入口文件是否只在我这个工作区存在一份?**
|
||||
|
||||
### 1.2 发现自己正在越界时怎么办
|
||||
|
||||
- **只读**别的工作区:允许,但要意识到**它的结论不是本工作区的结论**。
|
||||
- **写入**别的工作区:⛔ **停手**,先报告 —— 除非用户明确要求。
|
||||
- 已经在别处留了副本:**先报告清单**(哪份文件、在哪两处、md5 是否相同),**取得确认后再删副本**(删除不可逆)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 并发纪律:一把锁,顺序固定,反序释放
|
||||
|
||||
> 多会话并行是常态。**「无锁」的正确读法 =「你快去抢」,不是「可以开工」**(实测:两个会话把「✓ 无全局锁」读成"环境干净" ⇒ 同时改了同一批文件)。
|
||||
|
||||
### 2.1 取锁(**改任何文件之前的第一步,不是"检查"是"抢"**)
|
||||
|
||||
```bash
|
||||
bash "<锁脚本绝对路径>" --claim-exec "<你的会话名>"
|
||||
```
|
||||
|
||||
- **抢到之前不要动任何文件**;**抢不到 = 有会话在跑 ⇒ 停手 + 报告**。
|
||||
- 细粒度锁(如果项目有):先占单 → 再动单内的文件。
|
||||
- 生产侧操作锁(如果项目有):重启 / 停任务 / 改配置 / 铺包 / 改网关 → 先声明**影响面**(谁会被断、断多久)。
|
||||
- 完工:先放细粒度锁 / 操作锁,**最后** `--release-exec "<会话名>"`(⛔ 不带会话名 ⇒ **拒绝释放**;且护栏额度耗尽时会被 `safe-delete` 拦住 ⇒ 见 §2.3)。
|
||||
|
||||
### 2.2 🔍 抢锁必须"校验结果",不能"看输出"(**实测事故**)
|
||||
|
||||
把抢锁命令的输出**用管道截尾**(`| grep` / `| tail`)时,**失败提示里也含关键词**(如"…必须 `--release-exec` 才算完成")⇒ `grep -q` 会**假命中** ⇒ 于是"以为抢到了"而**在无锁状态下改文件**。
|
||||
|
||||
✅ **正确判据(二选一,缺一不可)**:
|
||||
|
||||
① **检查退出码**(不要接管道):
|
||||
|
||||
```bash
|
||||
if bash "<锁脚本>" --claim-exec "X"; then ... ; fi
|
||||
```
|
||||
|
||||
② **复读锁文件里的 `OWNER` 并断言等于自己的会话名**。
|
||||
|
||||
⛔ 事故版形态:`OUT=$(… --claim-exec … | grep 已持…)` —— **grep 吃掉了退出码,也吃掉了"抢不到"这个事实**。
|
||||
|
||||
### 2.3 释放时机 = **整个交付闭环走完**,不是"改完文件就放"
|
||||
|
||||
`回填台账 → 校验 → commit → 推送 + 对账 → 归档` 全部结束后才释放(**反序**)。
|
||||
理由:中间放锁 = 别的会话可能在你 commit 前挤进来,让**你的半成品被它的提交带走**。
|
||||
|
||||
- 🔴 **护栏额度耗尽时的等效释放(2026-09-25 实测)**:`safe-delete` 批量护栏用尽后,任何 `rm -rf` 都会被拦住并**挂起至超时** ⇒ 连**自己的锁**也放不掉(`--release-exec` 返回 **rc=124**)。处置 = **`mv` 把锁目录改名移走**(走 ENOENT 快路径、零删除),并在回报里写明理由。⚠️ 护栏阈值变量真名 = `CODEBUDDY_SAFE_DELETE_BULK_GUARD`(⛔ 不是 `…_BULK_THRESHOLD`);护栏**按会话计数** ⇒ 新会话额度是满的。
|
||||
|
||||
### 2.4 三条硬配套
|
||||
|
||||
- ⏸️ **持锁期间若要等用户拍板 ⇒ 先释放锁,再等** —— 锁是"**正在动手**"的凭证,不是"先占着"。
|
||||
- 🔒 **锁的生命周期 = 任务的生命周期**:⛔ 禁止"抢到锁、做一半、不解锁就结束回合 / 结束会话" —— 带锁结束 = 把其他所有会话挡在门外。
|
||||
- 🗒️ **结束语必须对锁状态负责**:要么写明"**已释放**",要么**显式点名"锁仍在 `<OWNER>`、未释放、原因、下一步"**(仅限释放通道不可用的极端情形)。⛔ "忘了 / 做不完就走"一律不允许。
|
||||
|
||||
### 2.5 ⛔ 绝对禁止「人工删锁 / 接管」
|
||||
|
||||
- AI 一律不得:删锁目录、删占用标记、或以「持有者疑似已死 / 卡住 / 太久没动 / 只在只读分析没产出」为由**单方面接管**。
|
||||
- 锁**只能由持有者自己释放**;脚本输出里的「或确认接管后人工删锁」**不构成授权**。
|
||||
- 抢不到锁时**唯一**合规动作 = **停手 + 报告用户** —— **锁的处置权只属于用户本人**。
|
||||
- **理由**:删锁 = 在**无法验证**对方死活的前提下单方面撤销互斥(**无心跳机制**)⇒ 一旦对方仍在跑,就退回「两个会话同时改同一批文件」,而这正是这把锁存在的理由。
|
||||
|
||||
### 2.6 ✅ 锁只约束「写」,不约束「读」
|
||||
|
||||
读文档 / 读代码 / 只读命令随时可做。
|
||||
⚠️ 但**会改本地状态的命令不算"读"**:`git fetch` / `checkout` / `stash` / `reset` / `switch` 一律要持锁(它们会写 `.git/refs` 或工作树)。
|
||||
|
||||
### 2.7 ⚠️ 细粒度锁管不住跨单撞车
|
||||
|
||||
多个会话各做各的单时,单级锁互不冲突,但**都会改共享文件**(清单 / 台账 / 索引)⇒ **只有全局锁能串行化**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 目录落位与命名
|
||||
|
||||
> 判据:**新增任何文件前,先问"它属于哪个工作区、哪一类"**,答不出来就先去问规则文件,⛔ 不要随手写在根目录。
|
||||
|
||||
### 3.1 通用落位形态(具体目录名以本工作区规范为准)
|
||||
|
||||
| 手上是什么 | 放哪 |
|
||||
|---|---|
|
||||
| 正式文档(方案 / 报告 / 规范 / 复盘) | `docs/<主题>/` |
|
||||
| 交给下一棒的交接单 | `交接单/`(或项目约定的台账目录) |
|
||||
| **工作线入口**(每个会话第一个读的) | **工作区根**,命名 `接续入口_<线名>_<日期>.md` |
|
||||
| 一次性脚本、探针输出、中间证据 | `tmp/<任务名>-<日期>/` |
|
||||
| 过程目录(一个任务一整个目录) | `tmp/历史过程目录/` |
|
||||
| 不再引用但需留痕 | `归档/` |
|
||||
| 疑似可删 | `待清理/`,**列清单等确认** |
|
||||
|
||||
### 3.2 ⚠️ 入口文件**必须在根**(会出事故)
|
||||
|
||||
状态脚本通常用 `listdir(工作区根)` 扫描 `接续入口_*.md`。**移走 ⇒ 新会话读到的第一个信号就是错的**(事故级)。
|
||||
⇒ 入口文件**不允许**被"整理"进子目录。
|
||||
|
||||
### 3.3 命名
|
||||
|
||||
| 类型 | 规则 | 例 |
|
||||
|---|---|---|
|
||||
| 正式文档 | `<主题>_<YYYYMMDD>.md` | `集群化改造方案_20260914.md` |
|
||||
| 交接单 | `交接单_<主题>_<日期>.md` | `交接单_组密钥加密_20260918.md` |
|
||||
| 线入口 | `接续入口_<线名>_<日期>.md` | 放根 |
|
||||
| 临时脚本 / 输出 | `_<用途>.<ext>` | `_probe_instance_mem.sh` |
|
||||
| 过程目录 | `_tmp_<序号或主题>/` | `_tmp_seq41/` |
|
||||
|
||||
日期一律 **8 位 `YYYYMMDD`**,不加分隔符。
|
||||
|
||||
### 3.4 ⚠️ 改写文档内的引用路径时
|
||||
|
||||
**映射键必须收敛到「带日期戳」的文件名** —— 通用名(`README.md` / `INDEX.md` / `architecture.md`)在任何文档里都可能指别处,映射它**必然误伤**。
|
||||
|
||||
### 3.5 路径书写约定
|
||||
|
||||
- ⛔ **禁止"跨目录只写文件名"** —— 从别处引用子目录文件时必须写**相对该引用点可定位**的路径。
|
||||
- 占位符用尖括号 `` `<NN>-<主题>.md` ``,⛔ 不用 `NN-*`(易被误认为真实路径)。
|
||||
- 路径一律反引号包裹,便于机器扫描与跳转。
|
||||
|
||||
### 3.6 删除一律不可逆
|
||||
|
||||
⇒ 先移入 `待清理/`,**出清单 + 取得确认**后才真删。`待清理/` 非空时,收尾报告需提一句它还剩什么。
|
||||
|
||||
### 3.7 分层判据(防止把规则做成指针)
|
||||
|
||||
> **"不看会违规 / 会出事"的 → 写成实体内容;"看了更准但不看也不违规"的 → 才给指针。**
|
||||
|
||||
⇒ 红线、判据、环境陷阱**必须常驻**;平台背景知识、UI 细节、档案模板、历史方案**可以只给指针**。
|
||||
|
||||
---
|
||||
|
||||
## 4. 提交边界
|
||||
|
||||
> **未明确要求 → 不 commit / 不 push / 不同步仓库。** 用户说"提交 / 推送 / 同步"时才做,且**只 add 自己改的文件**。
|
||||
|
||||
- ⛔ **三类内容通常禁入库**:**临时目录**(`tmp/`)、**中间产物**(`_tmp*/`、`_中间产物_*/`)、**会话交接单**(若项目约定它们只作本地台账)。用 `.gitignore` 兜住。
|
||||
- 🔴 **不要用 `git status` 判"交接单要不要提交"** —— 被 ignore 之后它们**根本不会出现**在 status 里;这是本条禁令的**预期行为**,不是"没生成"。确需入库只能显式 `git add -f`,且**先说明理由**。
|
||||
- ✅ **落点与入库解耦**:交接单**照旧写到约定目录**(路径不变,同一处找得到),只是**不进 Git**、以本地未跟踪文件形态保留。
|
||||
- 🔴 **提交前自查**:`git diff --cached --name-only` 里出现禁入类 ⇒ **立即 `git reset` 撤出**。
|
||||
- ⚠️ **推送前必须对账**:「仅本地」里若有**不在你清单里的文件 → 立刻停手**(幽灵文件);**只推自己本次改的文件**。
|
||||
- ⚠️ **核验"推送是否到位"用 `git ls-remote origin refs/heads/<branch>`**(与本地 `git rev-parse --short HEAD` 对比)。⚠️ 本机可能**没有 remote-tracking ref** ⇒ `git log origin/main..HEAD` 直接报 `unknown revision`,**别把它的空输出当成"已全部推送"**。
|
||||
- ⚠️ **同一条纪律适用于共享配置文件**(工具配置的 `hooks` 段、用户级记忆文件等):**只能 Edit 增删条目,禁止整段覆盖** —— 顶层键被覆盖会**静默**抹掉别人的配置。
|
||||
- ⚠️ **脚本路径失配 = fail-closed**:钩子打不开脚本 ⇒ 该机**所有会话**的写操作全被拒 ⇒ **迁移 / 改名后第一件事 = 核对钩子里的绝对路径**。
|
||||
|
||||
---
|
||||
|
||||
## 5. 批量操作红线
|
||||
|
||||
- ⛔ **禁止未经确认的批量 / 全仓写入**:全库遍历改写(`os.walk` / `find -exec` / `grep -rl | xargs`)、通配符重写、批量 `chmod` / `chown`、**批量换行符转换(CRLF↔LF)**、`cp -r` 整目录覆盖、`git add -A`。
|
||||
- **任何可能影响 >10 个文件的操作,用前必须先出受影响清单并取得确认**;先用 1 个对象**单点验证**,确认后果符合预期再推广。
|
||||
- **只做被明确要求的事**:执行过程中发现的额外问题——哪怕看起来"很小、很好修"——一律**先报告、后动手**。用户说"按建议处理"只授权**那条建议本身**,不等于授权一切顺带优化。
|
||||
- 🔑 **判据看"归属",不看"是不是平台组件"**:
|
||||
**本项目自己的**资源(自己的服务器单元、自己的数据目录、自己的网关配置、自己的端口)⇒ **按本项目规则直接做**,动手前一句话说明即可。
|
||||
**别人的 / 归属不明**的对象 ⇒ **一律只报告、不动手**,**哪怕改它能让自己流程跑通**。
|
||||
- ⚠️ **"我 lane 内的执行细节"不算批量越界**:部署 / 上线(换包、传产物、改静态页、投放)、重启自己的服务、改自己的配置、跑自己的脚本 ⇒ **别拿"批量红线"当挡箭牌去问,直接做**,事后一句「我选了什么(可推翻)」。
|
||||
- 🔑 **本机副本不是沙箱**:本机的批量改动即便不带任何"部署"动作,也会在下一次同步时**传导到生产**。传播前必须用 `git status` 确认**待传清单只含本次真实改动**。
|
||||
|
||||
---
|
||||
|
||||
## 6. 环境陷阱清单(通用形态,**具体路径以本工作区为准**)
|
||||
|
||||
| 陷阱 | 表现 | 正确做法 |
|
||||
|---|---|---|
|
||||
| **PATH 被削** | `ls` / `grep` / `dirname` / `head` 全部 `command not found`,报错里出现 `cd: null directory` | 每条命令**显式前置** PATH(把工具链的 `usr/bin` 与 `mingw64/bin` 都加回去),⛔ 不要指望 shell 继承 |
|
||||
| ⛔ **别把系统目录前置进 PATH** | 某些环境里那里的 `bash` 是**另一个子系统的启动器** ⇒ 只剩乱码报错 | 用工具链自带的 POSIX 目录,**不要加系统盘目录** |
|
||||
| **沙箱可能拦某个程序** | 报 "PROGRAM BLOCKED BY SECURITY POLICY" | ⛔ **不要重试、不要绕道调用**(换 shell / 写脚本都不行);改用手上可用的等价手段,并向用户说明 |
|
||||
| **行尾(CRLF / LF)** | 本机工作副本是 CRLF、目标环境是 LF ⇒ 直接传会污染生产 | 只转**本次要传的那一个**文件(`tr -d '\r' < 源 > /tmp/x` 再传);判据用 **字节级**(数 `\r` / `od -c`),⚠️ **别用 `grep -c $'\r'`**(在 git bash 里会误报) |
|
||||
| **运行时版本错配** | 原生模块报 `ERR_DLOPEN_FAILED` / `NODE_MODULE_VERSION` 不符 ⇒ **看起来像"我改坏了",其实是环境** | 跑单测 / 验收用**项目要求的那个版本**(显式绝对路径调用) |
|
||||
| **依赖隔离** | 全局装包 ⇒ 污染用户环境 | 用项目约定的隔离方式(venv / 本地 `node_modules`);用绝对路径调用解释器 |
|
||||
| ⚠️ **`python -c` 内含引号** | 转义地狱 | 先落成 `.py` 文件再跑 |
|
||||
| ⚠️ **语法检查落盘污染对账** | 语法检查命令**必然**在脚本旁落 `__pycache__/*.pyc` ⇒ 被对账脚本算成"待推送" | 用**不落盘**写法(`ast.parse`);落了就清掉并**复跑对账清零** |
|
||||
| ⚠️ **含反引号 / 特殊字符的命令** | 命令行转义不可控 | 用写文件的方式(Write / Edit)落命令,再执行 |
|
||||
| ⚠️ **改表格行:别按整行文本匹配** | 想给某行追加说明,用「整行字符串相等」定位会**静默失配**(实际引号字符 / 全半角与脚本内不同)⇒ 改了但没写进去 | **按行号或行首前缀锚定**(`ln.startswith("| `sk-xxx/`")`),改完**打印命中行号**自证 |
|
||||
| ⚠️ **对账脚本的默认远程可能失效** | 脚本写死某 ssh 别名(或调用 ssh 时**没带非默认端口**),而该别名端口已改 ⇒ 直接跑必然报「无法读取服务器目录」,**看起来像服务器挂了** | 先用 `ssh -p <真实端口> <别名> 'echo OK'` **单独探连通**;再用脚本的 **环境变量 override**(如 `DOCS_REMOTE=user@ip`)跑;⛔ 脚本没改就别顺手改,记入待办 |
|
||||
|
||||
### 6.1 🔴 本机铁律:绝不以 root(或非该资源所属 uid)运行 / 触碰别人的东西
|
||||
|
||||
**实测事故**:为量内存**用 root 手动起某个实例的 profile** ⇒ 它以 root 写入状态文件 ⇒ **属主变 root** ⇒ 实例进程 `EACCES` ⇒ **崩溃循环 ⇒ 页面 404**。
|
||||
|
||||
**规则**:
|
||||
① 对用户实例的**一切验证 / 冒烟 / 探针必须以该 uid 运行**(`setpriv --reuid <uid> --regid <uid> --clear-groups`)或按平台姿势进沙箱;⛔ **禁止 root 直跑**。
|
||||
② 确需临时以 root 跑 ⇒ **收尾必须** `find <目录> -user root` 列出 + `-exec chown <uid>:<uid> {} +` 修正。
|
||||
③ 「起不来」排查**先看属主 / `EACCES`**,**不要先怀疑内存**(实测先后误判为内存问题,绕了 20 分钟)。
|
||||
|
||||
### 6.2 四条取数坑
|
||||
|
||||
① `pkill -f` 匹配**实际 argv**(不是你以为的模式)⇒ 定位进程用 `ss -lntpH 'sport = :PORT'`;
|
||||
② 某些取数工具的 `fetch` **静默丢 `Host` 头** ⇒ 假 404 / 假 200 ⇒ 用 `curl -H "Host: …"`;
|
||||
③ 本机与远端 `md5sum` 输出格式不同(`hash *path` vs `hash path`)⇒ 先 `cut -d' ' -f1`;
|
||||
④ `journalctl --since` **不吃 ISO 偏移格式** ⇒ 用 `--since @<epoch>`;且"查询失败"与"确无该行"**必须可分**(⚠️ `| grep … || true` 会把"上游失败"伪装成"无命中")。
|
||||
|
||||
### 6.3 🔴 多处副本同步:**先判方向,再推**(实测踩过)
|
||||
|
||||
**起因(真实事故)**:某个"本机 ↔ 中继仓 ↔ 服务器镜像"三处链路,我**默认"本机最新"就批量从中间那处推远端** ⇒ 结果把**滞后的中间副本**推了上去。事后全量 md5 比对才发现:**6 个文件本机 ≠ 中间仓 = 远端**,且**本机版本号普遍更高**(例:`2.10.8` vs 中间仓的 `1.7.8`)。⇒ 中间仓与远端**长期滞后**,我的"同步"其实是**倒退**。
|
||||
|
||||
**判据(推任何多处副本之前,按序做三步)**:
|
||||
|
||||
1. **先全量比对**(一次跑完,别一个个看):逐文件算 md5,列出三方矩阵 ⇒ 看清是「三方一致」还是「某一方偏离」。
|
||||
2. **判方向 = 比 `version:` 字段或 mtime**,⛔ **不是**比"谁在我手边"。`version` 更可靠(mtime 会被 copy 保留 `-p` 而失真)。
|
||||
3. **只推确实滞后的那些**(先出清单再动手);⛔ 不要"整目录覆盖" —— 那会把方向搞反的代价放大到全部文件。
|
||||
|
||||
**⚠️ 关键认知**:多处副本场景里,**"本机"不等于"最新"**(别人可能已经改过远端);**"中间仓"也不等于"权威"**(它可能只是个滞后的归档)。**权威由 `version` / mtime / 内容共同决定,不由路径决定。**
|
||||
|
||||
**批量同步的硬闸**:任何"整目录 / 全量覆盖式"同步都属**批量操作**(见 §5)⇒ **先出"谁滞后"清单**,确认后才推。
|
||||
|
||||
### 6.4 🔴 批量 `scp` **同名文件**到同一目录 ⇒ 互相覆盖,只剩最后一个(2026-09-22 实测)
|
||||
|
||||
**症状**:把 12 个技能各自的 `SKILL.md` 一次 `scp` 到同一个暂存目录:
|
||||
|
||||
```bash
|
||||
scp a/SKILL.md b/SKILL.md c/SKILL.md host:/tmp/push/ # ❌ 12 个文件同名
|
||||
ls /tmp/push/ # 只有 1 个 SKILL.md
|
||||
```
|
||||
|
||||
⇒ **同名 ⇒ 后传的覆盖先传的**,最终只剩**最后一个**的内容。
|
||||
⚠️ 若下一步是"拿暂存目录里的文件去就位覆盖生产",就会**用 A 技能的内容覆盖 B 技能** —— 一次操作毁 11 个技能。
|
||||
|
||||
**为什么容易中招**:命令**返回码是 0**、不报错、不警告;只有去**数目标目录文件数**才发现。
|
||||
|
||||
**正解(二选一)**:
|
||||
1. **逐文件推到各自路径**(推荐,最直白):`for f in ...; do scp "$f/SKILL.md" host:/dest/$f/SKILL.md; done`
|
||||
2. **本地先按名组织目录树**再整树传:本地建 `push/<name>/SKILL.md` 后 `scp -r push/ host:/dest/`。
|
||||
|
||||
**通用判据(可推广到任何批量传输)**:
|
||||
- 传前先问「**目标目录里会不会有重名**」—— 会 ⇒ 必须让**路径携带区分信息**(子目录),⛔ 不能只靠文件名。
|
||||
- **传完必数文件数**(`ls | wc -l` 与预期比)。返回码 0 **不代表**数量对。
|
||||
|
||||
### 6.5 🔴 MSYS 路径(`/e/…`)⛔ **不许交给 Windows 原生程序**(2026-09-22 实测,正在增长)
|
||||
|
||||
**症状**:盘根长出**影子目录** —— 在 E 盘根出现 `E:\e\ProgramData\…`,与真 `E:\ProgramData\…` 并存。
|
||||
|
||||
**根因**:Git-Bash 风格路径 `/e/ProgramData/x` 交给 **Windows 原生程序**(`python.exe` / Chromium / node)后,
|
||||
它按「**当前盘根 + 相对路径**」解释 ⇒ `e\ProgramData\x` ⇒ 当前盘是 E 就落到 **`E:\e\ProgramData\x`**。
|
||||
|
||||
**实测规模**:29 MB / 355 文件,含**本工作区自己的 hook 日志**(`stop-dialog-guard.log`,写入时间 = 当天 ⇒ **仍在增长**)
|
||||
与浏览器 `_devlogs/<profile>/Cache`(Chromium user-data-dir)。
|
||||
⚠️ **两个受害者同因**:hook 与浏览器自动化 —— 只要交出去的是 `/e/…` 形式就会中招。
|
||||
|
||||
**判据与修法**:
|
||||
- **写盘前规范化**:`^/([A-Za-z])(/.*)?$` → `<大写盘符>:` + 余部(`/e/foo` → `E:/foo`);**幂等**(已是 Windows 形式则原样返回)。
|
||||
- **修在入口**(取路径处包一层),不要在每个使用点打补丁。参考实现:本工作区 hook 的 `_norm_path()`。
|
||||
- **自检**:`ls /<盘>/` 看有没有**单字母目录**(`e/` / `c/`);有 ⇒ 立即查是谁在写。
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user