4a4ad6bc5f
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名, 不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体 run_boot_test.py 的真实调用。 - AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改 「编译(/keil-build)」「串口回归(/serial-boot-test)」 - keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree 方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围) - 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入 等 41 行历史证据保留旧名 - gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑 - 顺手补并行方案同行漏网计数 51→52 钩子 验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52 钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
88 lines
8.1 KiB
Markdown
88 lines
8.1 KiB
Markdown
### F. 与 DSH worktree 插件(`/worktree`、模型工具 `worktree_create`)共存
|
||
|
||
本仓现在有**两套**隔离空间。别混用,也别互相回收:
|
||
|
||
| | 本 skill(文档别名 `@task`,DSH 显式调用 `/task-worktree`) | DSH worktree 插件(`dsh-task-worktree`) |
|
||
|---|---|---|
|
||
| 树在哪 | 仓库**外** `../_wt/<ID>-<slug>/` | 仓库**内** `.dsh-worktrees/worktree/<name>/` |
|
||
| 谁建 | `new <ID> <slug>`(人来敲命令) | 会话选「Worktree模式」/ 模型调 `worktree_create`(零命令) |
|
||
| 台账 | `_wt/.state/<ID>.json`(本 skill) | `.dsh-worktrees/manifest.json`(插件私有格式,别去改) |
|
||
| 分支名 | `task/<ID>-<slug>` | `worktree/<name>` |
|
||
| 共享联接 | `new` 自动建 | **建树时没有** → 要 `plugin adopt` 补 |
|
||
| 回收 | `done`(预演 / 合并 / 门禁 / 清场)或 `abort` | 用户敲 `/worktree finish\|bring-back\|remove\|prune`(模型碰不到收尾命令) |
|
||
|
||
**为什么允许它把树建在仓内**(铁律 7 的唯一例外):路径写死在插件里(`WORKTREE_NAMESPACE = worktree`
|
||
+ `<repo>/.dsh-worktrees/`),而暴露给模型的工具只有 `worktree_create` / `worktree_list` /
|
||
`worktree_status` 三个,配置面改不动。兜法三件(均已落地):
|
||
|
||
1. `.gitignore` **预置** `.dsh-worktrees/` —— 插件首次 `create` 会往 `.gitignore` 追加一模一样的字面量,
|
||
`ensureIgnored()` 见到同名规则即跳过;不预置就等于让插件在会话中途改这份受控文件(diff 里冒出
|
||
一行"不是我加的")。
|
||
2. 全库扫描的排除面登记了它:`scripts/check_doc_links.py` 的 `SKIP_PARTS`、
|
||
`tools/refgraph/gen_refgraph.py` 的 `EXCLUDE_DIRNAMES`、`scripts/check_layer_deps.py` 的 `os.walk` 兜底。
|
||
一份仓库副本被全库扫描吞进去 = 文档链接假红 + 配图生成物逐字节漂移砸在某位同事头上。
|
||
3. 联接与锁归属由本节命令接上(下面三步)。
|
||
|
||
**这棵树是不是插件的**:`git worktree list` 里路径含 `.dsh-worktrees/` 就是。`$TW list` 现在会在台账段
|
||
下面另起一段列出它们,联接列**点名缺哪一样**(`已联` / `缺 .venv` / `缺 SDL2` / `齐(实体SDL2)`)。**插件树不占
|
||
`MAX_CONCURRENT = 8` 名额**——回收不归本 skill,
|
||
硬计入只会让别人那棵关不掉的树把 `new` 挡死(`new` 会打印一行「另有 N 个插件工作树不计入上限」提醒并发代价)。
|
||
|
||
三步接上(`<name>` 可带 `worktree/` 前缀,也可多段如 `refactor/logging`):
|
||
|
||
```bash
|
||
"$PY" "$TW" plugin list # 一览:分支 / 脏 / 缺哪个联接(缺项另起一行说后果)
|
||
"$PY" "$TW" plugin adopt N21-dhara-isfree # 只补缺失的 .venv 与 simulator/SDL2 联接
|
||
"$PY" "$TW" plugin unadopt N21-dhara-isfree # 只摘联接(os.rmdir),绝不动目标内容与树里文件
|
||
```
|
||
|
||
- **两条联接坏了的样子完全不同**:缺 `.venv` → `ci_local.py` 的四级解释器回退**静默**落到机器级
|
||
python(无 fontTools = `generated-fresh` 假红的老根因,AGENTS.md 记着);缺 `simulator/SDL2` →
|
||
`build_sim.bat` 当场报错(`SDL2_DIR` 是裸 `set`,没有 env 逃生口)。所以列表只说「已联」不够——
|
||
它按缺的那一样分别给后果,`adopt` 没补齐也只报「还不能算就绪」,不许说「现在可以跑」。
|
||
- **adopt 只碰联接**:已联的跳过;`simulator/SDL2` 若已是实体目录只告警不动;`.dsh-worktrees/` 之外的路径
|
||
一律 exit 2(实测 `plugin adopt ".."` 被拒)——这道闸防的是把主树或 `_wt` 目录当插件树遮蔽掉真实内容。
|
||
- **锁归属现在认得出来**:`/keil-build` 在插件树里自持自释时,持有者名从 cwd 反推成 `pw:<name>`(多段名压平成
|
||
`-`),与 `plugin adopt` 打印的那个名字逐字相同(`keil_build.py: plugin_tree_owner()` ↔
|
||
`task_worktree.py: plugin_lock_owner()`)→ `lock status` 不再一律落成 `build-cli`。
|
||
- 上台架仍须手工占锁、用同一个名:`lock acquire bench --task pw:<name> --note <说明>` →
|
||
`lock release bench --task pw:<name> --bench-pass`。插件树没有台账 → 按 B 节只 warn、锁照常放。
|
||
- **命名建议**:插件 `<name>` 沿用本 skill 的 `<ID>-<slug>` 形态(如 `N21-dhara-isfree`),两套视图同名好对账;
|
||
但分支前缀必然不同(`worktree/` vs `task/`),别把两者说成同一个分支。
|
||
- **收尾不在这**:`done` / `abort` 拿插件树名下手只会 exit 2(这是保护,别绕过去手工 `git worktree remove`;
|
||
要拆先 `plugin unadopt` 摘联接,再由**用户** `/worktree remove`)。`/worktree bring-back` 要求主工作树干净,
|
||
本仓常有在飞改动 → 提交或 stash 由用户定。反过来插件也不会碰 `_wt/`(路径根不同,谁也删不掉谁)。
|
||
|
||
### F-1. 两条共享联接**不是同级需求**(按仓库实配定级;别答成「两个都必须」)
|
||
|
||
- **`simulator/SDL2` = 硬需求**:`simulator/CMakeLists.txt:12` 是
|
||
`set(SDL2_DIR "\${CMAKE_CURRENT_SOURCE_DIR}/SDL2/SDL2-2.32.10")`——普通 `set()`、**非 `CACHE`**,所以既没有环境变量入口、
|
||
命令行 `-DSDL2_DIR=` 也会被这个普通变量盖掉;`SDL2_INCLUDE_DIR`(:34)、`SDL2_INCLUDE_PARENT`(:70)、`SDL2_LIB_X64`(:71)、
|
||
链接目录(:180)、`SDL2.dll` 拷贝(:207)**全部从它派生**。缺它 `build_sim.bat` 当场配不出来(脚本里的 `SDL2_DIR` 同样是
|
||
裸 `set`,也没有 env 逃生口)。
|
||
- **`.venv` = 软需求**(有显式逃生门,但绕开有代价):`scripts/ci_local.py` 的解释器解析顺序(:108-:138)——
|
||
① `DTU_PRECOMMIT_PYTHON`(显式配置,指错**立即报错**、不静默降级)→ ② `sys.executable` → ③ 仓库 `.venv` →
|
||
④ `shutil.which("python")` → ⑤ `_global_python_candidates()` 的常见机器级位置。树内没有 `.venv` 时会**静默**落到机器级
|
||
解释器 → 无 fontTools → `generated-fresh` 假红(AGENTS.md 记着的老根因);可以用 `DTU_PRECOMMIT_PYTHON` 顶掉,
|
||
但要每台机器自己配——这就是它「软」的意思:有路可走,走得不爽。
|
||
|
||
结论口径:**要补就两条都补(`plugin adopt` 一次干完,成本近乎零),但「都必须」是错的**——SDL2 没有任何替代路径,
|
||
`.venv` 有 `DTU_PRECOMMIT_PYTHON` 这条门。现役命令形式仍是 `plugin list` / `plugin adopt <name>`;**不改代码、
|
||
不改 `CMakeLists.txt`**(真要给 SDL2 加 CACHE 逃生门属于改模拟器构建口径,另案 → `lvgl-simulator` skill)。
|
||
|
||
### F-2. 「DSH 都有 worktree 插件了,为什么还自持 Python 生命周期」——取证姿势与答案边界
|
||
|
||
这题不许凭印象答,口径 = **实读插件源码**:在 DSH 安装目录定位插件包(`npm root -g` → `<global>/node_modules/@deepseek-ai/dsh/`,
|
||
或读 profile 的 `dsh.profile.bundles` 找包名),**记下版本号与文件路径**,再对三个概念名逐个 grep 并**报出命中数**:
|
||
|
||
1. 独占锁 / 资源串行(`lock`、`acquire`、`release`、TTL)——插件侧**零命中**:它的模型是「一棵树一个会话」,没有跨会话排队概念。
|
||
2. 共享目录联接(`.venv`、`simulator/SDL2`、`mklink /J`、reparse point)——零命中:插件建树就是纯 `git worktree add`,
|
||
所以插件树天生缺这两条联接(定级见 F-1)。
|
||
3. 联接状态显示(三态:已联 / 缺 .venv / 缺 SDL2 / 齐(实体SDL2))——零命中。
|
||
|
||
**重叠面要说清**:两侧底层是**同一条** `git worktree add`,本 skill 没有重造「建树」;重叠只在**开**与**收**两个动作,
|
||
而插件的收(`/worktree finish|bring-back|remove|prune`)是**人类命令**、模型碰不到,也没有「合并前预演 → 并集残留审计 →
|
||
补基线 → 门禁 → 先摘联接」这条编排。本仓为什么必须自持:硬件各只有一份(一块 ST-LINK、一套 Keil 授权、一个 COM6、
|
||
一个 `192.168.1.100`)→ 要 `build` / `bench` 锁串行;SDL2 是模拟器硬依赖且无逃生门 → 要共享联接;台架 PASS 要留给
|
||
`done` 记账 → 要台账。**给不出版本号与 grep 命中数,就不要下结论。**
|