Skip to main content
Q

dsh-grill-tree

qiz029/dsh-grill-tree

DeepSeek Harness plugin bundle: grill_tree, a fixed-policy design-space search tool. Expands one decision per node, fans out independent proposers, has a separate judge prune the branches, beam-limits each depth, and ends with complete designs or an infea

Install

dsh plugin --profile web add github:qiz029/dsh-grill-tree

README

dsh-grill-tree

grill_tree: a funnel beam search over a design space, packaged as a DeepSeek Harness plugin bundle.

Listed on DSH Plugin Hub npm version license: MIT DeepSeek Harness node

Matt Pocock's grill-me skill walks a design tree one question at a time and asks the human at every fork. grill_tree keeps the tree but replaces the human at the inner nodes with fan-out and an adversarial judge, so a run ends with a merged design document, or with the reason the thing cannot be built.

                       topic + constraints
                              │
                          ┌───▼───┐
                          │ recon │  workspace facts, constraints, unknowns
                          └───┬───┘
  depth 1   ┌───────── expand root ─────────┐   "what is the single most constraining decision?"
            │  5 proposers, one shape each  │   conventional · minimal · max-automation · human-in-loop · contrarian …
            │        ↓ judge ↓              │   merge duplicates · prune · score 0–10
            └── keep top 6 ─────────────────┘
  depth 2   3 proposers per node → judge → keep top 4
  depth 3   3 proposers per node → judge → keep top 3
  depth 4   … keep top 2
  depth 5   final depth: every branch must complete or declare itself infeasible
                              │
                        ┌─────▼─────┐
                        │ synthesis │  merged design document + open decisions + rejected alternatives
                        └───────────┘

Quick start

dsh plugin --profile grill add dsh-grill-tree      # installs the bundle and the "Grill Tree 模式" preset
cp .env.example .env                                # DEEPSEEK_API_KEY only
./start.sh                                          # web UI on http://127.0.0.1:3080

Then, in a session running the Grill Tree 模式 preset:

请用 grill_tree 工具探索这个设计空间,然后把工具返回的文本原样贴给我。topic:… constraints:…

A default run (funnel 6/4/3/2/2, 5 root proposers, depth 5) is about 60 to 75 child agents and 15 minutes on deepseek-v4-flash.

Contents

How a run works

Every tree node is a partial design: the ordered ledger of decisions taken on that branch.

  1. Recon (1 child): gathers workspace facts, inferred constraints, and unknowns. Every later child receives them.
  2. Search, once per depth up to maxDepth:
    • Expand (1 child per open node): returns complete with a design summary, infeasible with the conflict, or question naming the single most constraining open decision.
    • Propose (proposers children per question): each answers the question once, from a distinct stance (simplest, most robust, contrarian, most reversible, best fit with existing code, fastest to ship). Proposers never see each other.
    • Judge (1 child per question): merges duplicate proposals, prunes options that break a constraint, contradict the ledger, or are dominated, and scores the rest 0 to 10.
    • Beam: the top beamSchedule[depth] children across all nodes survive to the next depth; the rest are recorded as pruned. The default schedule is a funnel, [6, 4, 3, 2, 2]: wide where the root decision fixes the product's shape, narrow where branches converge anyway. The root question also gets rootProposers children (default 5) with shape-level stances (conventional, minimal, maximal automation, human in the loop, contrarian, ecosystem, long horizon, risk minimizing) instead of the engineering stances used deeper down.
  3. Synthesis (1 child): writes the deliverable, a markdown design document that merges the surviving branches (positioning, key decisions with the decisive fact each, architecture and data flow, open decisions with chosen defaults, rejected alternatives, risks and next steps). When nothing survived it writes the infeasibility report and names the root cause.

The search policy, child schemas, provider route, and caps live in the plugin. The model supplies only the topic, optional constraints, and optional lower caps.

Cost per depth is roughly survivors of the previous depth × (proposers + 2) children. With the default funnel, 5 root proposers, and 3 proposers elsewhere, a depth-5 run is at most about 75 children (1 + 6 + 30 + 20 + 15 + 10 + 1); maxTotalAgents is the hard stop and the script stops expanding, rather than failing, when the budget would be exceeded.

Install

Requires the dsh CLI at 0.1.0-rc.7 or later and pnpm on PATH.

# from a checkout
dsh plugin --profile grill add /path/to/dsh-grill-tree
# or, once published
dsh plugin --profile grill add dsh-grill-tree

The bundle contributes one host-plane row, grill-tree, over the dsh-base layer. At boot it:

  1. copies the packaged grill agent preset (the shipped standard composition plus grill_tree inside its delegation realm) into $DSH_HOME/.agent-presets/grill. Preset-based surfaces (the web app, dsh-tui) show it in the preset picker as "Grill Tree 模式". The copy carries a marker file; a directory the bundle did not write is never overwritten, and the tool module path inside the copy is absolute, so the preset resolves from every profile on the machine.
  2. mounts the grill_tree tool on the host plane as soon as a host-plane workflowEngine exists. That is the headless and base-only case. In preset compositions the engine lives inside the preset's isolate realm, so the host tool stays dormant and the preset row provides it instead.

Web profile

dsh plugin --profile grill add /path/to/dsh-grill-tree
# add the web bundle to the profile manifest's dsh.profile.bundles, e.g.
#   ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-grill-tree"]
cp .env.example .env      # fill DEEPSEEK_API_KEY
./start.sh                # http://127.0.0.1:3080

.env may hold credentials only: dsh refuses a .env that sets any DSH_* variable, DEEPSEEK_BASE_URL, DEEPSEEK_SEARCH_BASE_URL, or proxy variables. Those are exported by start.sh, which also turns on web_fetch via DSH_WEB_FETCH_PROVIDER=http (the HTTP fetch provider has no private-network protection; comment the line out to keep fetch off). web_search needs only the API key.

The profile's own patch layer ($DSH_HOME/profiles/grill/cordis.patch.yml) pins the defaults: preset grill, model deepseek-v4-flash, thinking on at high effort.

Create a session with the Grill Tree 模式 preset, or set it as the default in $DSH_HOME/settings.yaml:

agent-presets:
  default: grill

Headless profile

dsh plugin --profile headless add /path/to/dsh-grill-tree
dsh --profile headless "explore the design space for X and tell me which designs survive"

Configuration

Override any key from the profile's cordis.patch.yml (host row) or by editing $DSH_HOME/.agent-presets/grill/agent.cordis.yml (preset row):

- id: grill-tree
  config:
    installPresets: true
    subagentProvider: spawn
    beamWidth: 8              # ceiling for any schedule entry and for the per-call cap
    beamSchedule: [6, 4, 3, 2, 2]
    rootProposers: 5
    maxDepth: 5
    proposers: 3
    maxTotalAgents: 80
    minDecisions: 3
    maxFieldChars: 400
    maxDesignChars: 4000
    maxDocumentChars: 12000
    maxLedgerChars: 12000
    maxResultChars: 40000

maxDepth and proposers are both defaults and per-call ceilings: a call may lower them but not raise them. beamSchedule gives the survivors kept at each depth (the last entry repeats for deeper levels); a call's beamWidth caps every entry, and the deployment beamWidth is the ceiling for both. rootProposers applies at depth 1 only. minDecisions stops a branch from being declared complete before it carries that many decisions. maxFieldChars, maxDesignChars, and maxDocumentChars are hard caps the script applies to every child's strings (question, answer, reason; design summaries; the synthesis document); longer text is clipped with an ellipsis rather than failing the run, and children are told the limits up front. At the final depth an expand child may only return complete or infeasible, so a run always ends in designs, an infeasibility report, or a budget stop. subagentProvider must be a fresh, structured-output provider (spawn in the shipped base); a provider that inherits parent context is refused.

Tool contract

grill_tree({ topic, constraints?, beamWidth?, maxDepth?, proposers? })

Canonical result: { runId, agentsStarted, result } where result is

FieldMeaning
statusdesigns, infeasible, depth-limited, budget-limited, or failed (root children returned nothing even after a retry; a worker failure, not a verdict)
designscomplete branches: id, depth, judge score, decision ledger, design summary
openbranches still open when depth or budget ran out
prunedevery cut branch with stage (expand, judge, beam) and the reason
failednodes whose child returned no structured report; expand and judge children get one automatic retry first
synthesisdocument (markdown design document), openDecisions (question, proposedDefault, why), rootCause (non-empty only when no design survived)
treeone compact row per node: parent, depth, question, answer, score, fate

The rendered text the model sees puts the design document first, then a search appendix: complete branches with their ledgers, open branches, pruned branches with reasons. All verdicts are worker reports, not independent verification.

What a result looks like

The rendered result the model relays is a design document first and a search appendix second:

grill_tree: search finished with complete designs.
Agents used: 66. Complete designs: 2. Pruned branches: 22. Open branches: 0. Failed children: 0.

# Design document (synthesis worker report)
## 一句话定位
## 关键决策            one line each: decision → the decisive fact, "已核实" where a worker checked it
## 架构与数据流
## 未决项与默认值      decisions the search never reached, with the default the synthesis chose
## 已否决的方案        from the pruned list, with reasons
## 风险与下一步

# Search appendix
## Complete branch 1 (n31, depth 4, judge score 8)   the ledger of decisions + the branch's design summary
## Complete branch 2 …
## Pruned branches      every cut branch: stage (expand / judge / beam), the rejected answer, the reason

On the content-creator-center topic used during development, a default run produced two complete branches with different root shapes (a daily human approval gate versus unattended publishing behind a policy surface); the synthesis merged them into a gate-then-trust-mode design and attached verified platform facts such as per-post pricing and token refresh windows.

Package layout

PathRole
src/index.ts (dsh-grill-tree)bundle host plugin: preset install + nested engine-gated tool mount
src/tool.ts (dsh-grill-tree/tool)the grill_tree tool plugin and its fixed workflow script
src/presets.ts (dsh-grill-tree/presets)packaged-preset installer
presets/grill/the shipped preset (composition, display metadata)
cordis.patch.ymlthe bundle's patch layer

Development

pnpm install
pnpm test        # stub-engine unit tests, preset installer, host-plugin gating, and one end-to-end run over the real spawn provider and worker-thread engine with a scripted model
pnpm build       # emits lib/

Keep run outputs out of the workspace

Children inspect the workspace the session runs in. A saved output from an earlier run that sits inside that workspace will be found by recon and expand children and will steer the next search toward it. Save run outputs somewhere the session does not scan, for example $DSH_HOME/grill-tree-runs/.

Limitations

  • Children of one depth share a barrier: the next depth starts only after every judge at the current depth has settled.
  • The proposer stances are fixed text; there is no per-child persona or tool filter because the workflow agent() hook does not expose them.
  • The run blocks the calling turn until it settles, like ralph; there is no background start.
  • Ledgers are carried as prompt text, not inherited context, so a deep branch re-sends its whole ledger to every child.
  • The packaged preset is a snapshot of the shipped standard composition at dsh 0.1.0-rc.7; upstream changes to standard do not flow into it automatically.

Related plugins