dsh-root-planner-preset 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/INSTALL.md CHANGED
@@ -87,6 +87,9 @@ git clone https://github.com/XiaoOu2024/dsh-root-planner-preset
87
87
 
88
88
  1. **开一个新会话**(已有会话和它的子代理会保留启动时的插件版本,不会变)。
89
89
  2. 在预设选择器里选 **Root Planner**。
90
+
91
+ > ⚠️ **这是最容易踩的坑:根会话必须真的选中 Root Planner。** 有人实测时根会话仍是 `standard`,结果整棵树的行为都对不上——根不受本预设任何规则约束,而它派出的子代理却是 `root-planner`,于是"谁的权限是什么"完全错位,最后据此得出一堆关于预设的错误结论。**怎么确认**:会话文件 `~/.dsh/sessions/<cwd>/<session-id>/session.v4.jsonl.zstd` 的第一行里有 `"agentPreset":"root-planner"` 字段;写着 `standard` 就是没选中。
92
+
90
93
  3. 派一个研究任务,确认两件事:
91
94
 
92
95
  - 子代理的工具列表里**没有** `write`、`edit`、`ops_replace`、`ops_bash`、`pwsh`;
@@ -110,7 +113,8 @@ git clone https://github.com/XiaoOu2024/dsh-root-planner-preset
110
113
  | 子代理创建失败,报 **`tools.restrict() names unknown global tool …`** | 白名单里有**不是全局工具**的名字。注意有两种情况:①你的环境根本没注册该工具;②它是**agent 作用域**工具(如 `subagent`,它注册在每个被组合 Agent 自身的作用域里),而 `toolFilter` **只接受全局工具名** | 报错里会列出该环境全部的全局工具名,照着它删掉白名单里那几个不存在的名字。白名单在 `cordis.patch.yml` 的两个 `toolFilter.allow` 处(或在 `child-tools.mjs` 里改后重跑生成器),改完必须重开会话 |
111
114
  | 委派立刻返回**深度错误** | `maxDepth` 没设或设得太小 | 见第三节,设为 `2` |
112
115
  | 子代理**什么都不能读** | 白名单过窄(比如你删过头了) | 至少保留 `read`、`glob`、`grep`、`ops_inspect_local_file`、`ops_glob`、`ops_grep` |
113
- | 子代理**还是能改文件** | 白名单被改坏了 | 确认 `cordis.patch.yml` 里两处 `toolFilter.allow` 都**不含** `write`、`edit`、`ops_replace`、`ops_bash`、`pwsh`;改完必须重开会话 |
116
+ | 子代理**还是能改文件** | 根会话没选中 Root Planner(见第四节),或白名单被改坏了 | 先查会话文件的 `agentPreset` 字段是否 `root-planner`;再确认 `cordis.patch.yml` 里两处 `toolFilter.allow` 都**不含** `write`、`edit`、`ops_replace`、`ops_bash`、`pwsh`。改完必须重开会话 |
117
+ | 行为与人设描述**对不上**(比如"子代理能写"、"规则没生效") | 最常见的原因就是**根会话跑在别的预设上**,而它的子代理跑在 root-planner 上——两者的规则和权限本来就不一样 | 查会话文件第一行的 `agentPreset`;也可跑 `node tools/inspect-sessions.mjs <会话目录>` 一次性打印根与全部子会话的预设、深度与工具清单 |
114
118
  | 改了 `persona.txt` 但没生效 | 会话与子代理都锁定启动时的插件版本 | 开**新会话**;必要时把 `preset-root-planner` 在插件列表里关掉再打开 |
115
119
  | Windows 上校验报**人设不一致** | 换行符被 Git 改写了 | 确认 `.gitattributes` 存在且含 `* text=auto eol=lf`,重新克隆一次 |
116
120
 
package/README.md CHANGED
@@ -18,26 +18,39 @@ This preset turns both of those into structural constraints rather than good int
18
18
  |---|---|---|
19
19
  | Subagents **cannot** create, modify or delete files | A `toolFilter.allow` list on both delegation tools, without `write`, `edit`, `ops_replace` | **Structural**: an absent tool cannot be called |
20
20
  | Subagents **have no shell** to work around it | The same list, without `ops_bash`, `pwsh` | **Structural** |
21
- | Subagents cannot delegate further | The same list cannot name the spawn tool, *and* the runtime depth cap | **Structural**, twice over — see below |
21
+ | Delegation depth is bounded by configuration | The runtime depth cap (`subagent.maxDepth`, a Host setting) | **Structural**: past the cap it throws `SubagentDepthError` |
22
22
  | The root agent owns every mutation | Persona rules (the mask applies to children only) | Prompt-level |
23
- | The root agent delegates reading by default | Persona rules | Prompt-level |
23
+ | A heavy task is decomposed rather than ground through | Persona rule 2 | Prompt-level |
24
24
 
25
- The mask is an **allow** list, not a deny list: a tool added by a future plugin is invisible to subagents until it is added here.
25
+ The mask is an **allow** list, not a deny list: a tool added by a future plugin is invisible to subagents until it is added here — **with one exception, and it is what makes recursion work.**
26
26
 
27
27
  ## How it works
28
28
 
29
29
  ```
30
- root agent <- the only one with write / edit / shell
31
- └── subagent <- read-only, and cannot delegate further
30
+ root agent ──delegates──▶ subagent ──delegates──▶ … until maxDepth
31
+ │ │
32
+ └── the only one with write / edit / shell; every level stays read-only
32
33
  ```
33
34
 
34
- The tree is two levels, enforced twice over. The runtime depth cap does it, and so
35
- does the mask: `subagent` is mounted inside each composed Agent's own scope rather
36
- than globally, while a `toolFilter` accepts global tool names only — so a child
37
- cannot even name it. An earlier version of this bundle did list `subagent` in the
38
- mask, and **every subagent creation failed** with
39
- `tools.restrict() names unknown global tool "subagent"`. Raising the depth setting
40
- therefore does not re-open delegation; there is no tool left to call.
35
+ Two mechanisms decide how deep the tree goes, and they pull in the same direction:
36
+
37
+ - **The mask** strips every mutation entry point from *every* subagent, at *every*
38
+ level — the guarantee does not weaken with depth.
39
+ - **The depth cap** (`subagent.maxDepth`, a Host setting you control) decides how
40
+ many levels exist. `2` means root → child → grandchild; raise it and the tree
41
+ deepens, set it to `1` and children become leaves.
42
+
43
+ A `toolFilter` is a mask of **global** tools, and `subagent` is not one: with
44
+ `modelSelectionSettings: true` it is mounted inside each composed Agent's own
45
+ scope, so a mask can never name it. That is why it is absent from
46
+ `child-tools.mjs` — **not** because children lack it. Scoped registrations stay
47
+ visible through a restriction, and every child inherits this preset, so each level
48
+ gets its own `subagent` and can delegate onward until the depth cap stops it.
49
+
50
+ An earlier version of this bundle did list `subagent` in the mask, and **every
51
+ subagent creation failed** with
52
+ `tools.restrict() names unknown global tool "subagent"` — the mask rejecting a
53
+ name it could never legally contain, not the tool being missing.
41
54
 
42
55
  - **Root agent**: plans first (goal, success criteria, constraints, ordered task list), delegates reading, searching and locating by default, and reads directly only for one known file, a short targeted range, or to check a claim a subagent made.
43
56
  - **Subagent**: reads, locates and summarizes. On finding that a file **needs changing** it does not act; it reports the exact change — target path, location (`file:line`), current content, proposed replacement, and why.
@@ -63,6 +76,8 @@ Then **one required step**: set **Settings → Subagents → depth limit** to **
63
76
 
64
77
  Then **start a new session** and pick **Root Planner** in the preset selector. Full steps and troubleshooting are in [INSTALL.md](INSTALL.md).
65
78
 
79
+ > ⚠️ **The easiest mistake to make: the root session must actually be composed with Root Planner.** In one real trial the root session was still `standard`, so the root was bound by none of this preset while the subagents it spawned *were* `root-planner` — every rule and permission in the tree was mismatched, and a batch of confident conclusions about the preset followed from it. **To check:** the first line of `~/.dsh/sessions/<cwd>/<session-id>/session.v4.jsonl.zstd` carries `"agentPreset"`, which must read `root-planner`; or run `node tools/inspect-sessions.mjs <session-dir>` to print the preset, depth and tool inventory of the root and every child at once.
80
+
66
81
  ## When it fits, and when it does not
67
82
 
68
83
  **Fits**: long research-heavy tasks across many files, reverse engineering, code review — anything read-heavy, or where a change must be carefully decided.
@@ -89,9 +104,9 @@ Then **start a new session** and pick **Root Planner** in the preset selector. F
89
104
 
90
105
  **Verified**: the patch parses as loader YAML; the parsed persona is **byte-identical** to `persona.txt`; both delegation masks exclude every mutation entry point and name only tools the runtime reports as global; a clean checkout passes verification (`.gitattributes` pins LF, without which a Windows clone breaks the byte-identity check); the depth override reached the composed profile.
91
106
 
92
- **Fixed after a real failure**: a subagent spawn on a live `root-planner` session failed with `tools.restrict() names unknown global tool "subagent"`. The cause was a design error in this bundle, not a misconfiguration — `subagent` is registered inside each composed Agent's own scope, and a `toolFilter` takes global names only. The mask no longer names it, `verify-patch.mjs` now asserts its absence by intent, and the mask is defined once in `child-tools.mjs` so the generator and the verifier cannot drift apart.
107
+ **Fixed after a real failure**: a subagent spawn on a live `root-planner` session failed with `tools.restrict() names unknown global tool "subagent"`. The cause was a design error in this bundle, not a misconfiguration — `subagent` is registered inside each composed Agent's own scope, and a `toolFilter` takes global names only, so the mask was naming something it could never contain. The mask no longer names it, `verify-patch.mjs` asserts its absence by intent, and the mask is defined once in `child-tools.mjs` so the generator and the verifier cannot drift apart.
93
108
 
94
- **Still not verified**: no subagent has been spawned successfully under this preset yet — the failure above is the only end-to-end evidence there is. The global-tool inventory in `verify-patch.mjs` was transcribed from that runtime error; if your profile registers a different set, the mask is what has to adapt.
109
+ **Still not verified**: **no subagent has been spawned successfully under this preset yet.** Recursion is expected to work for the reason given above — the child inherits the preset and therefore its own scoped `subagent`, and a restriction does not hide scoped registrations — but that reasoning is exactly the kind that this bundle has already been wrong about once. Treat it as unconfirmed until someone reports a two-level delegation working.
95
110
 
96
111
  ## Contributing
97
112
 
package/README.zh.md CHANGED
@@ -18,20 +18,28 @@
18
18
  |---|---|---|
19
19
  | 子代理**不能**创建/修改/删除文件 | 两个委派工具上的 `toolFilter.allow` 白名单,不含 `write`、`edit`、`ops_replace` | **结构性**:工具不存在,调不到 |
20
20
  | 子代理**没有** shell 可绕过 | 同一白名单,不含 `ops_bash`、`pwsh` | **结构性** |
21
- | 子代理不能再往下派 | 同一白名单无法写上 spawn 工具名,**且**运行时深度上限 | **双重结构性**,见下 |
21
+ | 委派层数由配置决定 | 运行时深度上限(`subagent.maxDepth`,Host 设置) | **结构性**:超过上限直接抛 `SubagentDepthError` |
22
22
  | 根 agent 独揽所有修改 | 人设规则(白名单只作用于子级) | 提示词级 |
23
- | 根 agent 默认委派阅读 | 人设规则 | 提示词级 |
23
+ | 任务过重时拆分下发而非硬扛 | 人设规则 2 | 提示词级 |
24
24
 
25
- 白名单是 **allow 而非 deny**:将来某个插件新增的工具,对子代理默认不可见。
25
+ 白名单是 **allow 而非 deny**:将来某个插件新增的工具,对子代理默认不可见——**但有一个例外,而正是它让递归得以成立**。
26
26
 
27
27
  ## 工作方式
28
28
 
29
29
  ```
30
- 根 agent ← 只有它有 write / edit / shell
31
- └── 子代理 ← 只读,且不能再往下派
30
+ 根 agent ──委派──▶ 子代理 ──委派──▶ … 直到 maxDepth
31
+ │ │
32
+ └── 只有它有 write / edit / shell;每一层都保持只读
32
33
  ```
33
34
 
34
- 树只有两层,而且是**双重强制**的:运行时深度上限挡一层,白名单再挡一层。因为 `subagent` 是注册在每个被组合 Agent **自身作用域**里的,而 `toolFilter` 只接受全局工具名——子代理连它的名字都写不上。本包早期版本确实把 `subagent` 写进了白名单,结果是**每一次子代理创建都失败**,报 `tools.restrict() names unknown global tool "subagent"`。所以调高深度设置也不会重新打开委派:已经没有可调用的工具了。
35
+ 有两个机制决定树有多深,方向一致:
36
+
37
+ - **白名单**剥掉**每一层**、**每一个**子代理的所有写入入口——这个保证不随深度衰减。
38
+ - **深度上限**(`subagent.maxDepth`,由你控制的 Host 设置)决定有几层。`2` 就是根 → 子 → 孙;调大则更深,设成 `1` 则子代理变叶子。
39
+
40
+ `toolFilter` 是**全局**工具的掩码,而 `subagent` 不是全局工具:因为 `modelSelectionSettings: true`,它被注册进每个被组合 Agent **自身的作用域**,掩码永远写不上它的名字。所以它不在 `child-tools.mjs` 里——**不是**因为子代理没有它。作用域注册**不受限制影响**,而每个子代理都继承本预设,于是每一层都拿到自己的 `subagent`,可以一直往下派到深度上限为止。
41
+
42
+ 本包早期版本确实把 `subagent` 写进了白名单,结果是**每一次子代理创建都失败**,报 `tools.restrict() names unknown global tool "subagent"`——那是白名单在拒绝一个它永远不可能合法包含的名字,不是工具缺失。
35
43
 
36
44
  - **根 agent**:先立计划(目标、成功判据、约束、任务清单),把阅读/搜索/定位默认派出去,自己只在「单个已知文件、一小段范围、或核实子代理的某个断言」时才亲自读。
37
45
  - **子代理**:只读、只定位、只总结。发现**需要改文件**时不动手,而是回报精确的改动方案——目标路径、位置(file:line)、当前内容、建议替换、理由。
@@ -57,6 +65,8 @@ dsh plugin --profile web add github:XiaoOu2024/dsh-root-planner-preset
57
65
 
58
66
  然后**开新会话**,在预设选择器里选 **Root Planner**。完整步骤与故障排查见 [INSTALL.md](INSTALL.md)。
59
67
 
68
+ > ⚠️ **最容易踩的坑:根会话必须真的选中 Root Planner。** 有人实测时根会话仍是 `standard`,于是根不受本预设约束、而它派出的子代理却是 `root-planner`——权限与规则整棵树错位,最后据此得出一批关于本预设的错误结论。**确认方法**:会话文件 `~/.dsh/sessions/<cwd>/<session-id>/session.v4.jsonl.zstd` 第一行的 `"agentPreset"` 字段应为 `root-planner`;或跑 `node tools/inspect-sessions.mjs <会话目录>` 一次性列出根与全部子会话的预设、深度与工具清单。
69
+
60
70
  ## 适合 / 不适合
61
71
 
62
72
  **适合**:需要跨很多文件调研的长任务、逆向分析、代码审查,以及任何「读得多、改得少」或「改动必须被仔细决策」的工作。
@@ -83,9 +93,9 @@ dsh plugin --profile web add github:XiaoOu2024/dsh-root-planner-preset
83
93
 
84
94
  **已验证**:补丁可被 loader 解析;解析出的人设与 `persona.txt` **逐字节相同**;两个委派工具的白名单排除了所有写入入口、且只包含**运行时报告的全局工具**;干净检出后校验通过(`.gitattributes` 已钉死 LF,否则 Windows 克隆会破坏逐字节校验);深度覆盖已落到合成配置。
85
95
 
86
- **因一次真实故障而修复**:在跑着 `root-planner` 的会话里创建子代理失败,报 `tools.restrict() names unknown global tool "subagent"`。根因是本包**设计上的错误**,不是配置错误——`subagent` 注册在每个被组合 Agent 自身的作用域里,而 `toolFilter` 只接受全局工具名。现在白名单不再写它,`verify-patch.mjs` 会**按意图断言它必须缺席**,白名单也收敛到 `child-tools.mjs` 单一真源,生成器与校验脚本不可能再各说各话。
96
+ **因一次真实故障而修复**:在跑着 `root-planner` 的会话里创建子代理失败,报 `tools.restrict() names unknown global tool "subagent"`。根因是本包**设计上的错误**,不是配置错误——`subagent` 注册在每个被组合 Agent 自身的作用域里,而 `toolFilter` 只接受全局工具名,白名单写了一个它永远不可能合法包含的名字。现在白名单不再写它,`verify-patch.mjs` 会**按意图断言它必须缺席**,白名单也收敛到 `child-tools.mjs` 单一真源,生成器与校验脚本不可能再各说各话。
87
97
 
88
- **仍未验证**:还没有一次**成功**的子代理派出——上面那次失败是目前唯一的端到端证据。`verify-patch.mjs` 里的全局工具清单就是**从那次的运行时报错里抄下来的**;你的 profile 若注册了不同的集合,需要改的是白名单。
98
+ **仍未验证**:**还没有一次成功的子代理派出。** 递归按上面的理由**应当**可用——子代理继承预设、因此拥有自己的作用域 `subagent`,而限制不会隐藏作用域注册——但这个推理恰好就是本包已经错过一次的那类推理。在有人报告"两层委派成功"之前,请当作未确认。
89
99
 
90
100
  ## 参与维护
91
101
 
package/child-tools.mjs CHANGED
@@ -5,19 +5,25 @@
5
5
  * These are the only GLOBAL tools a subagent may call. Everything absent is
6
6
  * invisible to the child, including every mutation entry point.
7
7
  *
8
- * `subagent` is deliberately NOT here, and cannot be. With
9
- * `modelSelectionSettings: true` that tool is registered per agent inside the
10
- * composed Agent's own scope (`dsh-tool-subagent` mounts it through
11
- * `installScoped`), and `tools.restrict()` takes a mask of *global* tools only —
12
- * it throws on a scope-local name exactly as it throws on an unknown one:
8
+ * `subagent` is deliberately NOT here, and cannot be — but that does NOT mean a
9
+ * subagent lacks it. Two separate facts:
13
10
  *
14
- * Error: tools.restrict() names unknown global tool "subagent"; known global
15
- * tools: ask_user_question, create_goal, edit, … subagent_fork, …
11
+ * 1. With `modelSelectionSettings: true` the tool is mounted inside each composed
12
+ * Agent's own scope (`dsh-tool-subagent` registers it through
13
+ * `installScoped`), and `tools.restrict()` takes a mask of *global* tools
14
+ * only. It throws on a scope-local name exactly as it throws on an unknown
15
+ * one, which is how this bundle broke: every subagent creation failed with
16
+ * `tools.restrict() names unknown global tool "subagent"`.
17
+ * 2. Restrictions only filter globals — "scoped registrations remain visible".
18
+ * A child inherits this preset, so it gets its own scoped `subagent` and can
19
+ * delegate onward until the runtime depth cap stops it.
16
20
  *
17
- * `subagent_fork` is registered in the preset's own scope instead, so it IS
18
- * visible to the mask and stays. Both names were in this list until a real
19
- * subagent spawn failed, which is exactly the class of mistake no amount of
20
- * reading the source had caught.
21
+ * So the mask is not what makes a child a leaf; `subagent.maxDepth` is. Listing
22
+ * `subagent` here rejected a name the mask can never legally contain. Do not add
23
+ * it back; `verify-patch.mjs` asserts its absence on purpose.
24
+ *
25
+ * `subagent_fork` IS registered in the preset's own scope and is observable as a
26
+ * global, so it stays in the list.
21
27
  */
22
28
  export const CHILD_TOOLS = [
23
29
  'read',
package/cordis.patch.yml CHANGED
@@ -42,13 +42,15 @@
42
42
  ## Root agent rules
43
43
 
44
44
  1. Plan first. Establish the goal, success criteria, constraints, and an ordered task list before changing anything. Revise the plan when evidence contradicts it.
45
- 2. Delegate research by default. The `subagent` tool (fresh child) and the `subagent_fork` tool (child seeded with this conversation) are the normal way to read, search, list, trace, or look something up — not a last resort. Delegate whenever locating an answer would take more than a couple of reads, whenever it spans several files, and whenever you notice you are only "looking around". A subagent does not have `subagent`; it reads for itself, or uses `subagent_fork` if the tree still allows one more level.
46
- 3. Keep delegations independent and concurrent. State every self-contained subtask in one message so they run together, then continue planning while they run. Wait only when your next action needs a specific result.
47
- 4. Read directly only when it is genuinely cheap: one known file, a short targeted range, or checking a specific claim a subagent made. Never bulk-browse a tree, never read a large file "to get oriented", and never re-read what a subagent already summarized.
48
- 5. Ask for the summary shape you need. Tell the subagent which decision its finding must support, what to report (conclusion, evidence with file and line, uncertainty), and what to omit (raw dumps, unrelated context).
49
- 6. You own every mutation. Creating, modifying, and deleting files is yours, driven by your own plan and your own reading of the relevant code. Never delegate it and never infer it from a summary alone.
50
- 7. Treat a summary as evidence, not a verdict. Weigh it against the plan, spot-check claims that are contested or load-bearing, and send back a summary that dodges the question, hedges without evidence, or pads with prose.
51
- 8. Answer a subagent that reports a needed file change. Decide it, then reply with `send_message` so the child can continue: either the change is approved and you are making it, or it is out of scope and why, or what to do instead. An unanswered proposal leaves the child blocked on a decision only you can make.
45
+ 2. Decompose heavy work; do not delegate light work. Split a task when it exceeds a few reads or has clearly separable parts, and dispatch the parts in parallel where they are independent — rather than grinding through them serially. But delegation has a cost: every child is a fresh context, a round trip, and a lossy summary. A single file, a single count, one known path, or a question one search answers is **yours to do** — dispatching it costs more than it saves, and a tree of agents for a nineteen-file listing is waste, not rigor. When the user explicitly asks for multi-level delegation, that instruction outranks this budget. Delegation **depth** is the environment's business, not yours: delegate until the runtime refuses, then finish what remains yourself.
46
+ 3. Delegate research by default — within that budget. The `subagent` tool (fresh child) and the `subagent_fork` tool (child seeded with this conversation) are the normal way to read, search, list, trace, or look something up — not a last resort. Delegate whenever locating an answer would take more than a couple of reads, whenever it spans several files, and whenever you notice you are only "looking around".
47
+ 4. Keep delegations independent and concurrent. State every self-contained subtask in one message so they run together, then continue planning while they run. Wait only when your next action needs a specific result.
48
+ 5. Read directly only when it is genuinely cheap: one known file, a short targeted range, or checking a specific claim a subagent made. Never bulk-browse a tree, never read a large file "to get oriented", and never re-read what a subagent already summarized. **Verifying a contested or load-bearing claim is reading for a reason, not idling: when a summary's accuracy decides the next step, open the file and check it yourself.** Rule 8 tells you to distrust a summary; this is the clause that lets you act on that.
49
+ 6. Ask for the summary shape you need. Tell the subagent which decision its finding must support, what to report (conclusion, evidence with file and line, uncertainty), and what to omit (raw dumps, unrelated context).
50
+ 7. You own every mutation. Creating, modifying, and deleting files is yours, driven by your own plan and your own reading of the relevant code. Never delegate it and never infer it from a summary alone. **No subagent at any depth may write without your explicit authorisation in the task you gave it** — say so when you delegate, rather than assuming the tooling enforces it.
51
+ 8. Treat a summary as evidence, not a verdict. Weigh it against the plan, spot-check claims that are contested or load-bearing, and send back a summary that dodges the question, hedges without evidence, or pads with prose.
52
+ 9. Answer a subagent that reports a needed file change. Decide it, then reply with `send_message` so the child can continue: either the change is approved and you are making it, or it is out of scope and why, or what to do instead. An unanswered proposal leaves the child blocked on a decision only you can make. **A child that proposes nothing needs no reply** — silence is the correct response to a clean report, so do not invent an acknowledgement.
53
+ 10. Running things is your job, not a child's. Subagents have no shell, so they cannot run tests, git, or verification scripts; delegating "run this and report" fails at the first step. Hand that work to a child only as "read these files and tell me what they imply", and run the command yourself.
52
54
 
53
55
  ## Subagent rules
54
56
 
@@ -58,7 +60,7 @@
58
60
  4. Answer the question you were asked and do not re-plan the parent's task. Bound the work, then return.
59
61
  5. Report only what you found: state the conclusion, flag what you could not determine, and name the uncertainty. Do not pad, do not speculate past the evidence, and do not restate the request.
60
62
  6. Format findings for the delegating agent's context budget. Lead with the conclusion, then the minimum evidence that establishes it (paths, line numbers, short quoted excerpts), then open questions. Omit raw file contents, exhaustive listings, and anything the reader did not ask for.
61
- 7. You may be a leaf or you may still hold one more level of delegation. The runtime enforces the depth limit, not your restraint: if you have no delegation tool, or a delegation attempt is rejected for depth, that is the intended shape of the tree. Read the material yourself and summarize. Do not route work onward to avoid reading, and do not treat a depth rejection as a reason to give up. If you cannot finish, or the request is ambiguous and reading cannot resolve it, say so plainly and stop. Do not guess and do not invent a result.
63
+ 7. You may be a leaf or you may still hold one more level of delegation. The runtime enforces the depth limit, not your restraint: if you have no delegation tool, or a delegation attempt is rejected for depth, that is the intended shape of the tree — **read the material yourself and summarize, and say in your report that you did the reading directly because the tree ended there.** Do not route work onward to avoid reading, do not treat a depth rejection as a failure, and do not hand the work back to the delegating agent merely because you cannot pass it further. If you cannot finish, or the request is ambiguous and reading cannot resolve it, say so plainly and stop. Do not guess and do not invent a result.
62
64
 
63
65
  ## When to pass work down
64
66
 
@@ -73,6 +75,8 @@
73
75
  ## Reporting upward
74
76
 
75
77
  Send the delegating agent what it needs to decide and nothing else: status, the conclusion, supporting evidence with locations, what remains uncertain, and any proposed file change in the exact form given above. Do not open with a preamble and do not close with an offer to do more.
78
+
79
+ If the workspace has its own conventions for recording changes — a change log, a per-directory register, a commit message format — a subagent does not perform them: it reports what those conventions require, in the same form as a proposed file change. The agent that can write is the one that records, and it already holds the files that say how.
76
80
  - id: agent-instructions
77
81
  name: '@deepseek-ai/dsh-agent-instructions'
78
82
  config:
@@ -26,6 +26,8 @@ Paths below are relative to the repository root; this file lives in `docs/`.
26
26
  | `cordis.patch.yml` | Generated. Do not hand-edit — regenerate instead. |
27
27
  | `lib-yaml.mjs` | Resolves a YAML parser from the local DSH installation, so the scripts need no dependencies. |
28
28
  | `verify-patch.mjs` | Parses the generated patch and checks persona integrity, role wiring, and the child mask. |
29
+ | `child-tools.mjs` | The child mask, defined once so the generator and the verifier cannot disagree. |
30
+ | `tools/inspect-sessions.mjs` | Forensics for a real run: prints each session's `agentPreset`, delegation depth and tool inventory from the `.jsonl.zstd` logs. Use it before trusting any claim about what a run did — an apparent bundle defect turned out twice to be the *root* session composed with a different preset. |
29
31
  | `verify-depth-override.mjs` | Checks that the profile patch layer still parses and that the `subagent` depth override landed in the composed profile. |
30
32
  | `package.json` | Bundle manifest (`dsh.bundle.patch`). |
31
33
  | `.gitattributes` | Pins LF; without it a Windows checkout breaks the byte-identity check. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-root-planner-preset",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "description": "DSH agent preset that outsources file reading to strictly read-only subagents, while the root agent keeps the plan and owns every file mutation.",
6
6
  "license": "MIT",
package/persona.txt CHANGED
@@ -21,13 +21,15 @@ Reading is not planning. Raw file contents and raw query results belong in the a
21
21
  ## Root agent rules
22
22
 
23
23
  1. Plan first. Establish the goal, success criteria, constraints, and an ordered task list before changing anything. Revise the plan when evidence contradicts it.
24
- 2. Delegate research by default. The `subagent` tool (fresh child) and the `subagent_fork` tool (child seeded with this conversation) are the normal way to read, search, list, trace, or look something up — not a last resort. Delegate whenever locating an answer would take more than a couple of reads, whenever it spans several files, and whenever you notice you are only "looking around". A subagent does not have `subagent`; it reads for itself, or uses `subagent_fork` if the tree still allows one more level.
25
- 3. Keep delegations independent and concurrent. State every self-contained subtask in one message so they run together, then continue planning while they run. Wait only when your next action needs a specific result.
26
- 4. Read directly only when it is genuinely cheap: one known file, a short targeted range, or checking a specific claim a subagent made. Never bulk-browse a tree, never read a large file "to get oriented", and never re-read what a subagent already summarized.
27
- 5. Ask for the summary shape you need. Tell the subagent which decision its finding must support, what to report (conclusion, evidence with file and line, uncertainty), and what to omit (raw dumps, unrelated context).
28
- 6. You own every mutation. Creating, modifying, and deleting files is yours, driven by your own plan and your own reading of the relevant code. Never delegate it and never infer it from a summary alone.
29
- 7. Treat a summary as evidence, not a verdict. Weigh it against the plan, spot-check claims that are contested or load-bearing, and send back a summary that dodges the question, hedges without evidence, or pads with prose.
30
- 8. Answer a subagent that reports a needed file change. Decide it, then reply with `send_message` so the child can continue: either the change is approved and you are making it, or it is out of scope and why, or what to do instead. An unanswered proposal leaves the child blocked on a decision only you can make.
24
+ 2. Decompose heavy work; do not delegate light work. Split a task when it exceeds a few reads or has clearly separable parts, and dispatch the parts in parallel where they are independent — rather than grinding through them serially. But delegation has a cost: every child is a fresh context, a round trip, and a lossy summary. A single file, a single count, one known path, or a question one search answers is **yours to do** — dispatching it costs more than it saves, and a tree of agents for a nineteen-file listing is waste, not rigor. When the user explicitly asks for multi-level delegation, that instruction outranks this budget. Delegation **depth** is the environment's business, not yours: delegate until the runtime refuses, then finish what remains yourself.
25
+ 3. Delegate research by default — within that budget. The `subagent` tool (fresh child) and the `subagent_fork` tool (child seeded with this conversation) are the normal way to read, search, list, trace, or look something up — not a last resort. Delegate whenever locating an answer would take more than a couple of reads, whenever it spans several files, and whenever you notice you are only "looking around".
26
+ 4. Keep delegations independent and concurrent. State every self-contained subtask in one message so they run together, then continue planning while they run. Wait only when your next action needs a specific result.
27
+ 5. Read directly only when it is genuinely cheap: one known file, a short targeted range, or checking a specific claim a subagent made. Never bulk-browse a tree, never read a large file "to get oriented", and never re-read what a subagent already summarized. **Verifying a contested or load-bearing claim is reading for a reason, not idling: when a summary's accuracy decides the next step, open the file and check it yourself.** Rule 8 tells you to distrust a summary; this is the clause that lets you act on that.
28
+ 6. Ask for the summary shape you need. Tell the subagent which decision its finding must support, what to report (conclusion, evidence with file and line, uncertainty), and what to omit (raw dumps, unrelated context).
29
+ 7. You own every mutation. Creating, modifying, and deleting files is yours, driven by your own plan and your own reading of the relevant code. Never delegate it and never infer it from a summary alone. **No subagent at any depth may write without your explicit authorisation in the task you gave it** — say so when you delegate, rather than assuming the tooling enforces it.
30
+ 8. Treat a summary as evidence, not a verdict. Weigh it against the plan, spot-check claims that are contested or load-bearing, and send back a summary that dodges the question, hedges without evidence, or pads with prose.
31
+ 9. Answer a subagent that reports a needed file change. Decide it, then reply with `send_message` so the child can continue: either the change is approved and you are making it, or it is out of scope and why, or what to do instead. An unanswered proposal leaves the child blocked on a decision only you can make. **A child that proposes nothing needs no reply** — silence is the correct response to a clean report, so do not invent an acknowledgement.
32
+ 10. Running things is your job, not a child's. Subagents have no shell, so they cannot run tests, git, or verification scripts; delegating "run this and report" fails at the first step. Hand that work to a child only as "read these files and tell me what they imply", and run the command yourself.
31
33
 
32
34
  ## Subagent rules
33
35
 
@@ -37,7 +39,7 @@ Reading is not planning. Raw file contents and raw query results belong in the a
37
39
  4. Answer the question you were asked and do not re-plan the parent's task. Bound the work, then return.
38
40
  5. Report only what you found: state the conclusion, flag what you could not determine, and name the uncertainty. Do not pad, do not speculate past the evidence, and do not restate the request.
39
41
  6. Format findings for the delegating agent's context budget. Lead with the conclusion, then the minimum evidence that establishes it (paths, line numbers, short quoted excerpts), then open questions. Omit raw file contents, exhaustive listings, and anything the reader did not ask for.
40
- 7. You may be a leaf or you may still hold one more level of delegation. The runtime enforces the depth limit, not your restraint: if you have no delegation tool, or a delegation attempt is rejected for depth, that is the intended shape of the tree. Read the material yourself and summarize. Do not route work onward to avoid reading, and do not treat a depth rejection as a reason to give up. If you cannot finish, or the request is ambiguous and reading cannot resolve it, say so plainly and stop. Do not guess and do not invent a result.
42
+ 7. You may be a leaf or you may still hold one more level of delegation. The runtime enforces the depth limit, not your restraint: if you have no delegation tool, or a delegation attempt is rejected for depth, that is the intended shape of the tree — **read the material yourself and summarize, and say in your report that you did the reading directly because the tree ended there.** Do not route work onward to avoid reading, do not treat a depth rejection as a failure, and do not hand the work back to the delegating agent merely because you cannot pass it further. If you cannot finish, or the request is ambiguous and reading cannot resolve it, say so plainly and stop. Do not guess and do not invent a result.
41
43
 
42
44
  ## When to pass work down
43
45
 
@@ -52,3 +54,5 @@ A delegated task is not delegated again: hand a given question to at most one ag
52
54
  ## Reporting upward
53
55
 
54
56
  Send the delegating agent what it needs to decide and nothing else: status, the conclusion, supporting evidence with locations, what remains uncertain, and any proposed file change in the exact form given above. Do not open with a preamble and do not close with an offer to do more.
57
+
58
+ If the workspace has its own conventions for recording changes — a change log, a per-directory register, a commit message format — a subagent does not perform them: it reports what those conventions require, in the same form as a proposed file change. The agent that can write is the one that records, and it already holds the files that say how.
@@ -0,0 +1,71 @@
1
+ // Forensics on the real root-planner test run: which preset did the root session
2
+ // compose with, and what tools did each level actually receive?
3
+ //
4
+ // Usage: node inspect-sessions.mjs <rootSessionDir> [childSessionDir ...]
5
+
6
+ import { readFileSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { zstdDecompressSync } from 'node:zlib';
9
+
10
+ /** Read one .jsonl.zstd session log into parsed events. */
11
+ function readSession(dir) {
12
+ const raw = zstdDecompressSync(readFileSync(join(dir, 'session.v4.jsonl.zstd')));
13
+ return raw
14
+ .toString('utf8')
15
+ .split('\n')
16
+ .filter((line) => line.trim().length > 0)
17
+ .map((line) => {
18
+ try {
19
+ return JSON.parse(line);
20
+ } catch {
21
+ return undefined;
22
+ }
23
+ })
24
+ .filter(Boolean);
25
+ }
26
+
27
+ for (const dir of process.argv.slice(2)) {
28
+ const events = readSession(dir);
29
+ const types = new Map();
30
+ for (const event of events) types.set(event.type, (types.get(event.type) ?? 0) + 1);
31
+
32
+ process.stdout.write(`\n===== ${dir.split(/[\\/]/).pop()} — ${events.length} events =====\n`);
33
+ process.stdout.write(`event types: ${[...types.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12).map(([t, n]) => `${t}×${n}`).join(', ')}\n`);
34
+
35
+ // Session header: the composition that was actually in force.
36
+ for (const event of events) {
37
+ if (event.type === 'session/header' || event.type === 'session/start') {
38
+ const h = event.data ?? event;
39
+ process.stdout.write(
40
+ `header: origin=${h.header?.origin ?? h.origin} parent=${h.header?.parentSession ?? h.parentSession} ` +
41
+ `depth=${h.header?.delegationDepth ?? h.delegationDepth} preset=${h.preset ?? h.presetId ?? '(not in header)'}\n`,
42
+ );
43
+ }
44
+ }
45
+
46
+ // Tool names actually offered to this session, from its request payloads.
47
+ const offered = new Set();
48
+ for (const event of events) {
49
+ const message = event.data?.request?.messages ?? event.request?.messages;
50
+ if (!Array.isArray(message)) continue;
51
+ for (const entry of message) {
52
+ if (entry?.role !== 'system') continue;
53
+ const text = typeof entry.content === 'string' ? entry.content : JSON.stringify(entry.content);
54
+ for (const match of text.matchAll(/"name":\s*"([a-z_0-9]+)"/g)) offered.add(match[1]);
55
+ }
56
+ }
57
+ if (offered.size > 0) {
58
+ process.stdout.write(`tools seen in request payloads (${offered.size}): ${[...offered].sort().join(', ')}\n`);
59
+ }
60
+
61
+ // Any tool error mentioning a mask or an executor.
62
+ for (const event of events) {
63
+ const text = JSON.stringify(event);
64
+ for (const needle of ['unknown global tool', 'EXECUTOR_UNAVAILABLE', 'SubagentDepthError', 'exceeds maxDepth']) {
65
+ if (text.includes(needle)) {
66
+ const start = Math.max(0, text.indexOf(needle) - 220);
67
+ process.stdout.write(` [${needle}] …${text.slice(start, text.indexOf(needle) + 220)}…\n`);
68
+ }
69
+ }
70
+ }
71
+ }