Files
STM32F4-Base/.dsh/skills/task-worktree/reference/dsh-plugin-bridge.md
T
wandering1 4a4ad6bc5f chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/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 全过;字节卫生零漂移。
2026-10-30 13:03:12 +08:00

88 lines
8.1 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.
### 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 命中数,就不要下结论。**