浏览代码

docs(presets): name the new id in the ralph restore recipe

Tianyi Cui 1 月之前
父节点
当前提交
d49192b6ac

+ 2 - 2
.agents/notes/implemented/feature/2026-07-31-even-out-shipped-tool-rosters.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-31-even-out-shipped-tool-rosters.md
-2026-07-31-even-out-shipped-tool-rosters.md: caadb4601a56e9180ccb8d1eae571208941cbcba
-2026-07-31-even-out-shipped-tool-rosters.zh.md: 9503baf9f1f8f2567c3ccd81db685c74db189430
+2026-07-31-even-out-shipped-tool-rosters.md: 90efaeb4734c806bc1bdc63d883c4e3158792a13
+2026-07-31-even-out-shipped-tool-rosters.zh.md: 4379ffbcd23ea80acd6801ea260fb686cd955fa5

+ 1 - 1
.agents/notes/implemented/feature/2026-07-31-even-out-shipped-tool-rosters.md

@@ -12,7 +12,7 @@ The result was a user-visible difference nobody had decided: the same model, ask
 
 ## Decision
 
-The rows that are not surface-specific move into [`base.cordis.yml`](../../../../packages/bundle/base/cordis.patch.yml), and three more join them: `tool-session-query`, `tool-str-replace-editor`, and `repeat-tool-reminder`. Web search moves there too; its [deployment decision](2026-07-31-web-default-search.md) owns the security boundary while the shared base owns its surface-neutral mount. Both surfaces assemble the same roster, including fixed `glob` and `grep` members because `dsh-tool-fs-search` spawns the [packaged ripgrep binary](../../archived/architecture/2026-08-01-packaged-ripgrep-search.md). Later decisions narrow that roster: the [session-search decision](../../archived/feature/2026-08-02-session-search-not-shipped-default.md) keeps `tool-session-query` opt-in, the [single-editor decision](../../archived/simplification/2026-08-10-default-presets-single-editor.md) removes `tool-str-replace-editor` from general-purpose presets, and the [persistent-shell-only decision](../simplification/2026-09-03-minimal-profiles-persistent-shell-only.md) removes it from the minimal compositions.
+The rows that are not surface-specific move into [`base.cordis.yml`](../../../../packages/bundle/base/cordis.patch.yml), and three more join them: `tool-session-query`, `tool-str-replace-editor`, and `repeat-tool-reminder`. Web search moves there too; its [deployment decision](2026-07-31-web-default-search.md) owns the security boundary while the shared base owns its surface-neutral mount. Both surfaces assemble the same roster, including fixed `glob` and `grep` members because `dsh-tool-fs-search` spawns the [packaged ripgrep binary](../../archived/architecture/2026-08-01-packaged-ripgrep-search.md). Later decisions narrow that roster: the [session-search decision](../../archived/feature/2026-08-02-session-search-not-shipped-default.md) keeps `tool-session-query` opt-in, the [single-editor decision](../../archived/simplification/2026-08-10-default-presets-single-editor.md) removes `tool-str-replace-editor` from general-purpose presets, the [persistent-shell-only decision](../simplification/2026-09-03-minimal-profiles-persistent-shell-only.md) removes it from the minimal compositions, and the [ralph demotion](../simplification/2026-09-12-ralph-off-in-shipped-defaults.md) ships `ralph` disabled in this base roster and in the presets that mirror it.
 
 Two rows stay surface-specific. `tmux-context` is TUI-only because a browser surface has no terminal multiplexer to describe. `session-reference` is TUI-only because it drives the shared session-query index from the launcher's process-local path, and the browser sidebar reconciles that index on its own first search.
 

+ 1 - 1
.agents/notes/implemented/feature/2026-07-31-even-out-shipped-tool-rosters.zh.md

@@ -12,7 +12,7 @@ Status: implemented
 
 ## 决策
 
-那些并非 surface 专属的行移入 [`base.cordis.yml`](../../../../packages/bundle/base/cordis.patch.yml),另有三行加入:`tool-session-query`、`tool-str-replace-editor` 和 `repeat-tool-reminder`。Web 搜索也一并移入;其[部署决策](2026-07-31-web-default-search.zh.md)负责安全边界,共享 base 则负责与 surface 无关的挂载。两个 surface 组装同一份清单,其中 `glob` 和 `grep` 是固定成员,因为 `dsh-tool-fs-search` 直接 spawn [打包的 ripgrep 二进制](../../archived/architecture/2026-08-01-packaged-ripgrep-search.md)。后续决策收窄了这份清单:[session-search 决策](../../archived/feature/2026-08-02-session-search-not-shipped-default.md)让 `tool-session-query` 保持需显式启用,[单一 editor 决策](../../archived/simplification/2026-08-10-default-presets-single-editor.md)从通用 preset 移除 `tool-str-replace-editor`,[仅持久 shell 决策](../simplification/2026-09-03-minimal-profiles-persistent-shell-only.zh.md)则从极简组合移除它。
+那些并非 surface 专属的行移入 [`base.cordis.yml`](../../../../packages/bundle/base/cordis.patch.yml),另有三行加入:`tool-session-query`、`tool-str-replace-editor` 和 `repeat-tool-reminder`。Web 搜索也一并移入;其[部署决策](2026-07-31-web-default-search.zh.md)负责安全边界,共享 base 则负责与 surface 无关的挂载。两个 surface 组装同一份清单,其中 `glob` 和 `grep` 是固定成员,因为 `dsh-tool-fs-search` 直接 spawn [打包的 ripgrep 二进制](../../archived/architecture/2026-08-01-packaged-ripgrep-search.md)。后续决策收窄了这份清单:[session-search 决策](../../archived/feature/2026-08-02-session-search-not-shipped-default.md)让 `tool-session-query` 保持需显式启用,[单一 editor 决策](../../archived/simplification/2026-08-10-default-presets-single-editor.md)从通用 preset 移除 `tool-str-replace-editor`,[仅持久 shell 决策](../simplification/2026-09-03-minimal-profiles-persistent-shell-only.zh.md)则从极简组合移除它,[ralph 降级决策](../simplification/2026-09-12-ralph-off-in-shipped-defaults.zh.md)让 `ralph` 在这份 base 清单与镜像它的 preset 中默认关闭。
 
 有两行仍是 surface 专属。`tmux-context` 只在 TUI,因为浏览器 surface 没有终端复用器可描述。`session-reference` 只在 TUI,因为它以 launcher 的进程本地路径驱动共享的 session-query 索引,而浏览器侧边栏会在自己的首次搜索里重建该索引。
 

+ 2 - 2
.agents/notes/implemented/simplification/2026-09-12-ralph-off-in-shipped-defaults.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-09-12-ralph-off-in-shipped-defaults.md
-2026-09-12-ralph-off-in-shipped-defaults.md: 910b7ecdbe3763934e4325388cbd294e405c3e4f
-2026-09-12-ralph-off-in-shipped-defaults.zh.md: b6a8f24530847c0abc14a66e901cc6a335103473
+2026-09-12-ralph-off-in-shipped-defaults.md: fa6c2d665853d812579057fb40d9cadf9988f5fb
+2026-09-12-ralph-off-in-shipped-defaults.zh.md: ba43d3343b01e39daffc5ed1e48192e558a504a8

+ 4 - 4
.agents/notes/implemented/simplification/2026-09-12-ralph-off-in-shipped-defaults.md

@@ -8,7 +8,7 @@ English | [中文](2026-09-12-ralph-off-in-shipped-defaults.zh.md)
 
 The `ralph` tool runs a fixed foreground loop of fresh, unseeded child agents and returns when a worker reports completion or a concrete blocker. Its own model-facing description restricts it to runs the direct human explicitly asked for, and its README records that completion is a worker self-declaration with no independent evaluator and that the loop has no background collection, resume checkpoint, or scheduler.
 
-That tool shipped enabled in `packages/bundle/base/cordis.patch.yml` and in three of the four shipped agent presets. A default session therefore carried a tool whose own description told the model not to reach for it, and the default profile's catalog advertised a capability the Harness does not yet stand behind.
+That tool shipped enabled in `packages/bundle/base/cordis.patch.yml` and in three of the four shipped agent presets. A default session therefore carried a tool whose own description told the model not to reach for it, and the default profile's catalog advertised a capability the Harness does not yet stand behind. The [shipped tool roster](../feature/2026-07-31-even-out-shipped-tool-rosters.md) decision assembled that base roster and lists each later narrowing; this is one more entry in that list.
 
 ## Decision
 
@@ -16,7 +16,7 @@ That tool shipped enabled in `packages/bundle/base/cordis.patch.yml` and in thre
 
 The `ptc` preset also declares `workflow-worker-thread` disabled. That preset kept the engine for `ralph` alone after it dropped the general `workflow` tool, so disabling `ralph` left the engine with no consumer in that composition.
 
-Each disabled row carries its restore recipe in a local comment. An overlay row re-enables the capability for a base-backed profile from `$DSH_HOME/cordis.patch.yml` or a `--patch` file. Preset files take no patches (`packages/preset/agent-presets/README.md`), so a Web session that wants `ralph` duplicates the preset into `$DSH_HOME/.agent-presets` and drops `disabled`; in `ptc` that means the tool row and the engine row together, because `tool-ralph` injects `ctx.workflowEngine`.
+Each disabled row carries its restore recipe in a local comment. An overlay row re-enables the capability for a base-backed profile from `$DSH_HOME/cordis.patch.yml` or a `--patch` file. Preset files take no patches (`packages/preset/agent-presets/README.md`), so a Web session that wants `ralph` duplicates the preset under a **new id** into `$DSH_HOME/.agent-presets` and drops `disabled`; a copy reusing the shipped id is shadowed by the shipped root, which wins duplicate ids (`packages/preset/agent-presets/src/index.ts`), and `copy()` refuses an id any root already supplies. In `ptc` the duplicate drops `disabled` from the tool row and the engine row together, because `tool-ralph` injects `ctx.workflowEngine`.
 
 `packages/bundle/web-app/cordis.patch.yml` keeps its own `tool-ralph` disable even though `base` now ships the row off. `validatePresetPlaneSeparation` in `scripts/verify-cordis-config.ts` collects declared row ids without regard to `disabled`, so deleting that row would return `tool-ralph` to the Web host plane and collide with the same id in every preset.
 
@@ -44,6 +44,6 @@ A default Web, headless, sdk, acp, or custom base-backed session no longer offer
 
 ## Verification
 
-`packages/preset/agent-presets/tests/shipped-root.spec.ts` pins that every preset carrying `tool-ralph` disables it, that `ptc` disables `workflow-worker-thread`, and that `standard` and `cordis` keep the engine enabled for their `workflow` tool. `apps/cli/tests/web-agent-presets.e2e.ts` and `apps/web/tests/shipped-composition.e2e.ts` pin the exact default and PTC tool catalogs, so a row that stops contributing is a test failure rather than a silently shorter list. `scripts/verify-cordis-config.ts` continues to pass its plane-separation check against the disabled rows.
+`packages/preset/agent-presets/tests/shipped-root.spec.ts` pins that every preset carrying `tool-ralph` disables it, that the `minimal` roster carries no such row, that `ptc` disables `workflow-worker-thread`, and that `standard` and `cordis` keep the engine enabled for their `workflow` tool. `apps/cli/tests/web-agent-presets.e2e.ts` and `apps/web/tests/shipped-composition.e2e.ts` pin the exact default and PTC tool catalogs, so a row that stops contributing is a test failure rather than a silently shorter list. `scripts/verify-cordis-config.ts` continues to pass its plane-separation check against the disabled rows.
 
-Every affected recorded-session sidecar was refreshed with `pnpm run test:snapshot:refresh`, and `pnpm run test:snapshot` replays the corpus. `snapshots/session/ralph-loop` passes under its own composition patch, which is the evidence that the demotion removed the row from the defaults without removing coverage of the tool.
+Most affected recorded-session sidecars were regenerated with `DSH_SNAPSHOT=refresh pnpm run test:snapshot`, and `pnpm run test:snapshot` replays the corpus. Six sidecars were curated by hand instead, because no local run produces them. Four belong to `pwsh-tool-turn` and `persistent-pwsh-tool-turn`, which this host skips for a missing `pwsh`; their removed text is byte-identical to what the refresh removed elsewhere. The remaining two are `snapshots/web/schedule-catalog`, which no executing test writes or reads: `schedule-after.e2e.ts` reads only that directory's `catalog.expected.md` and `session.v3.jsonl`, and `assertFixtureInventory` checks only that the four files exist, so the `header.pin: true` in its `snapshot.yml` is not enforced. `snapshots/session/ralph-loop` passes under its own composition patch, which is the evidence that the demotion removed the row from the defaults without removing coverage of the tool.

+ 5 - 5
.agents/notes/implemented/simplification/2026-09-12-ralph-off-in-shipped-defaults.zh.md

@@ -1,4 +1,4 @@
-# Agent Note: Ship ralph off in the default compositions
+# Agent Note: 在随附默认组合中关闭 ralph
 
 Status: implemented
 
@@ -8,7 +8,7 @@ Status: implemented
 
 `ralph` 工具运行一个固定的前台循环:每一轮开启一个全新的、不带对话种子的子代理,直到某个 worker 报告完成或给出具体阻塞原因才返回。它面向模型的描述把用途限制为直接人类明确要求的场合,其 README 也记录:完成与否是 worker 的自我声明,没有独立评估;该循环没有后台收集、没有可恢复的检查点,也没有调度器。
 
-该工具在 `packages/bundle/base/cordis.patch.yml` 以及四个随附 agent preset 中的三个里默认启用。于是默认会话带着一个自身描述就叫模型不要主动使用的工具,默认档位的工具目录也在宣传 Harness 尚未背书的能力。
+该工具在 `packages/bundle/base/cordis.patch.yml` 以及四个随附 agent preset 中的三个里默认启用。于是默认会话带着一个自身描述就叫模型不要主动使用的工具,默认档位的工具目录也在宣传 Harness 尚未背书的能力。[随附工具清单](../feature/2026-07-31-even-out-shipped-tool-rosters.zh.md)决策组装了这份 base 清单,并逐条列出此后的每次收窄;本次是那份清单上新增的一条。
 
 ## 决策
 
@@ -16,7 +16,7 @@ Status: implemented
 
 `ptc` preset 还额外把 `workflow-worker-thread` 声明为禁用。该 preset 在去掉通用 `workflow` 工具之后只为 `ralph` 保留了这个引擎,因此禁用 `ralph` 使该组合中的引擎不再有消费方。
 
-每个被禁用的行都在本地注释里带上恢复方法。对基于 base 的档位,用一行 overlay 即可从 `$DSH_HOME/cordis.patch.yml` 或 `--patch` 文件重新启用。preset 文件不接受补丁(`packages/preset/agent-presets/README.md`),因此想要 `ralph` 的 Web 会话要把 preset 复制到 `$DSH_HOME/.agent-presets` 并删掉 `disabled`;在 `ptc` 中这意味着工具行和引擎行都要恢复,因为 `tool-ralph` 注入 `ctx.workflowEngine`。
+每个被禁用的行都在本地注释里带上恢复方法。对基于 base 的档位,用一行 overlay 即可从 `$DSH_HOME/cordis.patch.yml` 或 `--patch` 文件重新启用。preset 文件不接受补丁(`packages/preset/agent-presets/README.md`),因此想要 `ralph` 的 Web 会话要以**新 id** 把 preset 复制到 `$DSH_HOME/.agent-presets` 并删掉 `disabled`;沿用随附 id 的副本会被随附 root 遮蔽,因为重复 id 由更靠前的 root 胜出(`packages/preset/agent-presets/src/index.ts`),而 `copy()` 也拒绝任何 root 已提供的 id。在 `ptc` 中,副本要同时删掉工具行和引擎行的 `disabled`,因为 `tool-ralph` 注入 `ctx.workflowEngine`。
 
 `packages/bundle/web-app/cordis.patch.yml` 保留了自己那条 `tool-ralph` 禁用声明,尽管 `base` 现在已把该行默认关闭。`scripts/verify-cordis-config.ts` 中的 `validatePresetPlaneSeparation` 在收集已声明的行 id 时不看 `disabled`,所以删掉这一行会让 `tool-ralph` 回到 Web 宿主平面,并与每个 preset 中的同名行冲突。
 
@@ -44,6 +44,6 @@ Status: implemented
 
 ## 验证
 
-`packages/preset/agent-presets/tests/shipped-root.spec.ts` 固定了三点:每个带 `tool-ralph` 的 preset 都禁用它;`ptc` 禁用 `workflow-worker-thread`;`standard` 与 `cordis` 为各自的 `workflow` 工具保留引擎启用。`apps/cli/tests/web-agent-presets.e2e.ts` 与 `apps/web/tests/shipped-composition.e2e.ts` 固定默认与 PTC 的确切工具目录,因此某一行不再贡献会直接导致测试失败,而不是让列表悄悄变短。`scripts/verify-cordis-config.ts` 的平面隔离检查在禁用行存在的情况下继续通过。
+`packages/preset/agent-presets/tests/shipped-root.spec.ts` 固定了四点:每个带 `tool-ralph` 的 preset 都禁用它;`minimal` 清单不含该行;`ptc` 禁用 `workflow-worker-thread`;`standard` 与 `cordis` 为各自的 `workflow` 工具保留引擎启用。`apps/cli/tests/web-agent-presets.e2e.ts` 与 `apps/web/tests/shipped-composition.e2e.ts` 固定默认与 PTC 的确切工具目录,因此某一行不再贡献会直接导致测试失败,而不是让列表悄悄变短。`scripts/verify-cordis-config.ts` 的平面隔离检查在禁用行存在的情况下继续通过。
 
-所有受影响的录制会话旁挂文件都用 `pnpm run test:snapshot:refresh` 刷新,并由 `pnpm run test:snapshot` 回放整个语料。`snapshots/session/ralph-loop` 在它自己的组合补丁下通过,这证明这次降级把该行移出了默认组合,却没有移除对该工具的覆盖。
+多数受影响的录制会话旁挂文件由 `DSH_SNAPSHOT=refresh pnpm run test:snapshot` 重新生成,并由 `pnpm run test:snapshot` 回放整个语料。另有六个旁挂文件是人工整理,因为本机没有任何一次运行会产出它们。其中四个属于 `pwsh-tool-turn` 与 `persistent-pwsh-tool-turn`:本机缺少 `pwsh`,这两个场景在本地被跳过;它们删掉的文本与刷新在别处删掉的文本逐字节相同。剩下两个是 `snapshots/web/schedule-catalog`,没有任何在执行中的测试写入或读取它们:`schedule-after.e2e.ts` 只读该目录的 `catalog.expected.md` 与 `session.v3.jsonl`,`assertFixtureInventory` 也只检查那四个文件存在,因此它 `snapshot.yml` 里的 `header.pin: true` 并未被强制执行。`snapshots/session/ralph-loop` 在它自己的组合补丁下通过,这证明这次降级把该行移出了默认组合,却没有移除对该工具的覆盖。

+ 3 - 3
packages/bundle/web-app/cordis.patch.yml

@@ -458,9 +458,9 @@
 - id: tool-workflow
   disabled: true
 
-# Kept as an explicit disable even though `base` now ships this row off: the
-# preset-plane gate reads declared row ids without regard to `disabled`, so
-# deleting this row would collide every preset's `tool-ralph` with this plane.
+# `base` ships this row disabled; the preset-plane gate reads declared row ids
+# without regard to `disabled`, so this row must stay to keep `tool-ralph` off
+# the host plane and out of collision with every preset's row.
 - id: tool-ralph
   disabled: true
 

+ 3 - 2
packages/preset/agent-presets/presets/cordis/agent.cordis.yml

@@ -218,8 +218,9 @@
     # Off by default with the base row it mirrors: the tool description
     # restricts `ralph` to runs the human explicitly asked for, and completion
     # is a worker self-report, not an independent evaluation. Preset files take
-    # no patches, so a session that wants it duplicates this preset and drops
-    # `disabled`.
+    # no patches, so a session that wants it duplicates this preset under a new
+    # id and drops `disabled`; the shipped root shadows a copy that reuses this
+    # id.
     - id: tool-ralph
       name: '@deepseek-ai/dsh-tool-ralph'
       disabled: true

+ 3 - 2
packages/preset/agent-presets/presets/ptc/agent.cordis.yml

@@ -241,8 +241,9 @@
       disabled: true
 
     # Off by default with the base row it mirrors. Preset files take no patches,
-    # so a session that wants it duplicates this preset and drops `disabled`
-    # from this row and from the engine above.
+    # so a session that wants it duplicates this preset under a new id and drops
+    # `disabled` from this row and from the engine above; the shipped root
+    # shadows a copy that reuses this id.
     - id: tool-ralph
       name: '@deepseek-ai/dsh-tool-ralph'
       disabled: true

+ 3 - 2
packages/preset/agent-presets/presets/standard/agent.cordis.yml

@@ -230,8 +230,9 @@
     # Off by default with the base row it mirrors: the tool description
     # restricts `ralph` to runs the human explicitly asked for, and completion
     # is a worker self-report, not an independent evaluation. Preset files take
-    # no patches, so a session that wants it duplicates this preset and drops
-    # `disabled`.
+    # no patches, so a session that wants it duplicates this preset under a new
+    # id and drops `disabled`; the shipped root shadows a copy that reuses this
+    # id.
     - id: tool-ralph
       name: '@deepseek-ai/dsh-tool-ralph'
       disabled: true

+ 1 - 0
packages/preset/agent-presets/tests/shipped-root.spec.ts

@@ -157,5 +157,6 @@ describe('the shipped preset root', () => {
     for (const id of ['cordis', 'ptc', 'standard']) {
       expect(findEntry(await shippedEntries(id), 'tool-ralph')?.disabled, id).toBe(true)
     }
+    expect(findEntry(await shippedEntries('minimal'), 'tool-ralph')).toBeUndefined()
   })
 })

+ 2 - 2
snapshots/session/ralph-loop/cordis.yml

@@ -1,6 +1,6 @@
 # This scenario owns the `ralph` composition. The harness layers the shared
 # headless patch (`text-turn/cordis.yml`) underneath, so this file carries only
-# what makes the composition different: the shipped base layer now ships
-# `tool-ralph` off, and this is the one scenario that exercises the tool.
+# what makes the composition different: the shipped base layer carries
+# `tool-ralph` disabled, and this is the one scenario that exercises the tool.
 - id: tool-ralph
   disabled: false