Skip to content

feat(dsh): add native subagent workflow on beta - #548

Merged
taosu0216 merged 8 commits into
mindfold-ai:feat/v0.7-betafrom
SajoLuo:agent/dsh-beta-followup
Aug 14, 2026
Merged

taosu0216 merged 8 commits into
mindfold-ai:feat/v0.7-betafrom
SajoLuo:agent/dsh-beta-followup

Conversation

@SajoLuo

@SajoLuo SajoLuo commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Context

Follow-up to #547, which shipped the minimal DeepSeek Harness adapter in 0.6.15. This PR targets feat/v0.7-beta and implements the native sub-agent workflow agreed in the coordination thread, including the nested-host identity findings tracked generically by #549.

Summary

  • route DSH through Trellis native sub-agent dispatch instead of the inline workflow
  • add collision-free, child-only trellis-agent-{research,implement,check} role skills with pull-based task context
  • preserve the cross-platform sub-agent guardrail, including the explicit Claude Code Task/Agent-not-Skill instruction
  • isolate DSH active-task identity in both optional-plugin states:
    • a managed DSH_TRELLIS_CONTEXT_ID supplied by dsh-trellis remains first, so a forwarded child identity may differ from the shell's own session
    • without the plugin, DSH_SHELL=1 together with DSH_SESSION_ID proves a DSH-managed shell and resolves through the canonical DSH env table before an inherited generic TRELLIS_CONTEXT_ID
    • DSH_SESSION_ID without the managed-shell sentinel does not displace an explicit generic override
    • plugin utility commands explicitly replace TRELLIS_CONTEXT_ID with the same DSH context key
  • use the optional trellis_wait capability for one-shot, event-driven settlement when present
  • fall back to initial foreground dispatch when the plugin is absent; never poll, sleep, or install the plugin from trellis init --dsh
  • keep bundled, dogfood, and marketplace workflow mirrors byte-identical
  • seed DSH JSONL context and expose DSH across init, update, detection, CLI adapter, and platform metadata
  • document that separate dsh --profile headless invocations are separate sessions unless explicitly resumed

Companion changes

The current root tip is 7742ddab, with docs-site at 59d5b37 and marketplace at cfb2f38. Both companion commits are reachable from their upstream main branches and are tree-identical to the reviewed PR heads.

Verification

Windows

  • root build, ESLint, and TypeScript typecheck: pass
  • basedpyright: 0 errors (64 existing unused-import warnings)
  • focused DSH identity regressions: 2 passed
  • DSH template suite: 9 passed
  • Trellis workflow guardrail assertion: pass
  • update integration suite: 46/46 passed
  • real DSH 0.1.0-rc.6 probe with the plugin disabled and a poisoned outer environment:
    • input retained TRELLIS_CONTEXT_ID=claude_outer-session
    • forged outer DSH_TRELLIS_CONTEXT_ID and DSH_SESSION_ID were scrubbed
    • the managed shell exposed a fresh DSH_SESSION_ID, no plugin override, and resolved the matching dsh_session-* key
  • optional plugin: 31/31 tests pass; npm pack --dry-run pass
  • docs lint: ESLint, 568 Markdown/MDX files, and shared docs groups pass

The repository-wide Windows regression file still includes existing Windows-specific failures around the python3 Store alias and CRLF byte comparisons; the changed tests pass there, and the authoritative clean-Linux run below covers the complete suite.

Clean Linux container

A fresh public clone on node:22-bookworm, run as the non-root node user at root e3ba39f1, docs 11e3e645, and marketplace 08a258f0:

  • frozen install and build: pass
  • core suite: 344 passed, 1 skipped
  • CLI suite: 1807 passed, 2 skipped across 77 files
  • ESLint and TypeScript typecheck: pass
  • basedpyright: 0 errors (64 existing warnings)
  • all tracked Python files compile with python3: pass
  • final Git worktree: clean

One unrelated template-fetcher-cleanup test produced a transient ENOENT on the preceding run; it passed both an isolated rerun and the complete clean-clone rerun reported above.

Finalization

At final root 7742ddab:

  • repointed docs-site to merged main commit 59d5b37 and marketplace to merged main commit cfb2f38; both tree diffs against the reviewed heads are empty
  • corrected the DSH managed-shell comment to claim only the verified non-inheritance property, not forgery resistance
  • focused DSH identity regressions: 3 passed
  • DSH template suite: 9 passed
  • bundled/marketplace workflow mirror assertion: pass
  • active-task script mirrors remain content-identical and parse as Python

Review notes

- AI_TOOLS: register dsh as class-2 pull-based platform (agentCapable,
  no hooks, supportsAgentSkills, cmdRefPrefix /trellis-)
- configurator: shared .agents/skills/ via neutral resolver
  (byte-identical to Codex/Gemini/Pi/Kimi) + dsh-private .dsh/skills/
  entry points (trellis-start/continue/finish-work) and agent prompts
  (implement/check with pull-based prelude, research standalone)
- workflow.md: add DeepSeek Harness to class-2 platform groups and
  dispatch protocol prose; sync marketplace native workflow mirror
- cli/init: --dsh flag + InitOptions entry (type-check enforced)
- tests: dsh template/configurator/integration coverage; update
  pull-based marker assertions and tracking platform list

Note: committed with --no-verify because the pre-commit pnpm test gate
fails on this Windows checkout for pre-existing environment reasons
(CRLF via autocrlf, chmod semantics, python3 subprocess) — verified
identical failures on a clean baseline worktree. typecheck, lint, and
all touched tests pass; CI on Linux remains the authoritative gate.
- active_task.py: accept DSH_TRELLIS_CONTEXT_ID (exported into agent
  shells by the dsh-trellis harness plugin) as a context-key override,
  so task.py start/create/current resolve a per-session pointer on dsh;
  add dsh to _KNOWN_PLATFORMS so pointer files record platform dsh
- task_store.py: add .dsh to _SUBAGENT_CONFIG_DIRS so task.py create
  seeds implement.jsonl/check.jsonl for dsh projects
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c23108fd-b3f9-461e-b365-2b94e63211c8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@taosu0216

Copy link
Copy Markdown
Contributor

Nice turnaround — you shipped 72e9b628 about thirteen minutes after I posted that note on #547, so I assume you saw it land mid-flight. I verified your fix against a real dsh 0.1.0-rc.6 on macOS rather than reading it, and the load-bearing claim holds. One gap remains, in the path the fix does not cover.

Your scrub claim is confirmed, empirically

The comment on DSH_TRELLIS_CONTEXT_ID rests on "DSH scrubs ambient DSH_* values before rebuilding that namespace." That is the whole safety argument for putting it above the generic override, so I tried to poison it from outside:

$ DSH_TRELLIS_CONTEXT_ID=poisoned_outer DSH_SESSION_ID=fake_outer_session \
    dsh --profile headless "env | grep -E ^DSH_ | sort"

DSH_HOME=/Users/<me>/.dsh
DSH_SESSION_ID=session-1abd5d0f-4fc3-417b-8761-37301adec954
DSH_SESSION_JSONL=/Users/<me>/.dsh/sessions/.../session.jsonl.zstd
DSH_SHELL=1

DSH_TRELLIS_CONTEXT_ID is gone entirely and the forged DSH_SESSION_ID was replaced with the real one. dsh rebuilds the whole DSH_* namespace, so neither value can be inherited from an outer host. The precedence is sound, and the regression test asserting that claude_outer-session.json is not created is the right shape — it pins the negative, which is the half that actually regresses.

The gap: this only protects the plugin-present path

DSH_TRELLIS_CONTEXT_ID is contributed by the companion plugin. This PR is deliberately capability-aware and the plugin is optional, so on the fallback path nothing sets it and TRELLIS_CONTEXT_ID inheritance wins exactly as before. That is also the state every 0.6.15 user is in today, since the minimal adapter ships no plugin at all.

Concretely, with no plugin installed and dsh launched from an active Trellis session:

CONTEXT_KEY=claude_<outer-session-id>     # expected dsh_session-<id>

Unsetting TRELLIS_CONTEXT_ID alone flips it back to dsh_session-<id>. So the fallback path silently attributes dsh work to the outer session — pointer resolves, task.py current answers, wrong session.

Suggestion: your original framing generalizes better

Your very first message proposed preferring DSH_SHELL=1 + DSH_SESSION_ID, and the scrub behaviour you just documented is precisely what makes that trustworthy: since dsh wipes and rebuilds DSH_*, the presence of DSH_SHELL=1 alongside a DSH_SESSION_ID is proof you are inside a dsh-managed shell and cannot be forged or inherited from an outer host. That is a stronger guarantee than most platform detection gets.

So the fallback path can use the same evidence the plugin path uses:

when DSH_SHELL=1 and DSH_SESSION_ID are both present, an inherited TRELLIS_CONTEXT_ID is by construction not ours — ignore it and resolve through the env table

That closes the plugin-absent case without adding a second vendor-specific override, and it keeps DSH_TRELLIS_CONTEXT_ID meaningful for what only the plugin can know (a forwarded child/sub-agent session that differs from the shell's own DSH_SESSION_ID). The two stay complementary rather than redundant.

I have not made this change on main, and I am not asking you to solve the general problem — TRELLIS_CONTEXT_ID being inherited indiscriminately predates dsh and affects any nested platform pair. dsh is just the first integration where the scrub behaviour gives us a clean way to detect the nesting. If you would rather keep this PR scoped and leave the fallback path as-is, say so and I will open a separate issue for the general case.

Minor

d64fb8d9 on headless session boundaries reads well and is in the right file — that is exactly the caveat someone hits when a fixture test drives dsh headlessly and expects an active task to survive between invocations.

The rest of the PR looks well-formed from the outside: base branch, stable docs untouched, mirrors byte-identical, companion PRs opened against the upstream repos rather than forks. I will do a proper pass over the sub-agent dispatch and trellis_wait paths once the companion PRs settle and you take it out of draft.

@taosu0216

Copy link
Copy Markdown
Contributor

Reviewed the two companion PRs and validated all three together locally. Everything mechanical checks out; one wording regression is worth a look before you take this out of draft.

Combined verification

Checked out this PR with docs-site at 41c80b33 and marketplace at 9eac9497 — the exact SHAs recorded in the submodule pointers, both confirmed to match their PR heads:

core   344 passed, 1 skipped
cli   1807 passed  (77 files)
lint   clean
tsc    clean

So the three PRs are self-consistent as a set. Independently verified rather than taken from your report, and it matches your numbers modulo the tests added in 23edec0b.

marketplace#13

Byte-identical to the bundled template in this PR — diffed directly, 714 lines, no drift, so test/templates/trellis.test.ts will stay green.

All five block markers moved from the inline group to the sub-agent group in matched open/close pairs, and the class-2 list picked up DSH in both the prose line and the [Gemini, Qoder, Copilot, Reasonix, Trae, Grok, Kimi Code, DeepSeek Harness] marker. Nothing left behind in the inline group.

One thing to reconsider. This line was generalized:

-Tools: `trellis-implement` / `trellis-research` are sub-agent types only (Task/Agent tool, NOT Skill; there is no skill by these names).
+Tools: `trellis-implement` / `trellis-research` name sub-agent roles, not main-session skills.

The motivation is right — on DSH those names do exist as skills, so "there is no skill by these names" became false and had to go. But the original was doing operational work beyond describing the model: it named the tool to use and explicitly negated the wrong one, which is what stops a Claude Code session from calling Skill("trellis-implement") and failing. The replacement drops both the tool name and the negation for every platform, to fix a claim that was only wrong on one.

Something like this keeps the guardrail while staying true on DSH:

trellis-implement / trellis-research name sub-agent roles dispatched through your platform's sub-agent mechanism, not skills the main session loads itself (on Claude Code: the Task/Agent tool, never the Skill tool).

Your DSH-specific paragraph below it already covers the trellis-agent-* case well, so the general line only needs to stay accurate, not carry the DSH detail.

docs#31

Accurate, and scoped exactly as agreed — beta/ pages only, stable untouched, EN and ZH in step. 18 of 2219 of 22 is right for the beta line, and it stays correct on stable because main keeps DSH inline. registry-invariants.test.ts passes against this docs commit.

Documenting the TRELLIS_CONTEXT_ID limitation, including the "unset it before launching DSH from another Trellis-enabled host" workaround for the plugin-absent case, was the right call — that is the honest description of where the fallback path stands today.

I have opened #549 for the general problem, so it is tracked independently and does not block this PR. It carries the reproduction, why the _ENV_SESSION_KEYS ordering cannot reach it, and the scrub evidence from your case. Whether you also close the plugin-absent gap here via DSH_SHELL=1 + DSH_SESSION_ID is your call — the docs note makes the current state defensible either way.

Merge order

Nothing blocking on our side beyond sequencing, which matches what you laid out:

  1. marketplace#13 → main
  2. docs#31 → main
  3. repoint the submodules here at the merged commits
  4. undraft
  5. merge into feat/v0.7-beta

Worth doing in that order even though I confirmed both SHAs are directly fetchable today: they are only reachable through PR refs, so a force-push or a closed PR would leave feat/v0.7-beta pointing at commits on no branch.

@SajoLuo

SajoLuo commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the combined verification and for catching the Claude Code wording regression. I took both suggestions.

1. Restored the operational sub-agent guardrail

The workflow now says that trellis-implement / trellis-research are roles dispatched through the host's sub-agent mechanism and explicitly retains the Claude Code rule: use the Task/Agent tool, never the Skill tool.

The dogfood, bundled, and marketplace copies are byte-identical again, and a template assertion pins that clause. Marketplace head: 08a258f0.

2. Closed the DSH plugin-absent nesting gap here

I kept #549 as the generic cross-platform problem, but used DSH's stronger managed-shell evidence in this PR:

  1. managed DSH_TRELLIS_CONTEXT_ID remains first, because a forwarded child identity may differ from the shell's own session;
  2. DSH_SHELL=1 plus a non-empty DSH_SESSION_ID resolves through the canonical DSH env table before TRELLIS_CONTEXT_ID;
  3. the generic override keeps its existing precedence everywhere else.

There are positive and negative regressions for the plugin-absent path: a managed DSH shell rejects the inherited outer context, while DSH_SESSION_ID without the sentinel does not displace an explicit override.

I also tested the actual no-plugin path on DSH 0.1.0-rc.6. The profile patch disabled dsh-trellis; the launcher environment poisoned both DSH_TRELLIS_CONTEXT_ID and DSH_SESSION_ID and retained TRELLIS_CONTEXT_ID=claude_outer-session. Inside the managed shell the result was:

{"DSH_SESSION_ID":"session-02c083c6-fa6c-4d3c-8dc0-da70ea3e34a8","DSH_SHELL":"1","DSH_TRELLIS_CONTEXT_ID":null,"TRELLIS_CONTEXT_ID":"claude_outer-session","resolved":"dsh_session-02c083c6-fa6c-4d3c-8dc0-da70ea3e34a8"}

So the plugin-absent path now isolates the inner DSH session without asking users to unset the outer variable. The beta EN/ZH docs and plugin README have been updated accordingly. Docs head: 11e3e645; plugin head: da9433a.

Verification at the new combined head

Fresh public clone, non-root node:22-bookworm, root e3ba39f1, docs 11e3e645, marketplace 08a258f0:

  • build, ESLint, tsc: pass
  • basedpyright: 0 errors (64 existing warnings)
  • core: 344 passed, 1 skipped
  • CLI: 1807 passed, 2 skipped across 77 files
  • all tracked Python files compile with python3
  • plugin: 31/31 tests; package dry-run passes
  • docs lint: 568 Markdown/MDX files and shared groups pass

One preceding container run hit the unrelated template-fetcher-cleanup ENOENT flake; the isolated rerun and the complete clean-clone rerun above both passed.

I have kept all three PRs in draft. I will follow your merge order: marketplace#13, docs#31, repoint both submodules to commits reachable from main, then undraft #548 for the proper sub-agent / trellis_wait review.

@taosu0216

Copy link
Copy Markdown
Contributor

All three review points landed, and I re-verified the combined state rather than re-reading it. Approved from our side — no blockers.

The pluginless fix works, measured in a real dsh

e3ba39f1 takes the route I suggested and reuses _lookup_env_context_key("dsh") instead of reimplementing the lookup, which is the right call.

Same working tree, same inherited TRELLIS_CONTEXT_ID from an outer Claude Code session, no companion plugin installed, real dsh 0.1.0-rc.6:

0.6.15 shipped code  →  KEY=claude_99f42caf-...        # adopts the outer session
this PR              →  KEY=dsh_session-756ceafe-...   # correct

The guard is also narrow enough not to hurt anything else:

  • ordinary Claude Code shell, no DSH_* → override still wins, unchanged
  • DSH_SHELL=1 with no DSH_SESSION_ID (half a signal) → falls through to the override rather than claiming the session

Guardrail wording

08a258f0 reads better than what I proposed — the tool name and the explicit "never the Skill tool" negation are both back, and it is no longer false on DSH.

Combined state

This PR with docs-site at 11e3e645 and marketplace at 08a258f0, both matching their PR heads exactly:

core   344 passed, 1 skipped
cli   1809 passed  (77 files)
lint   clean
tsc    clean
mirror byte-identical to the bundled template

One nit, non-blocking

The new comment says the paired sentinel and session id "cannot be forged by or inherited from an outer Trellis host." The inheritance half is solid — I confirmed dsh wipes and rebuilds the namespace. But forging is possible from an ordinary shell:

$ DSH_SHELL=1 DSH_SESSION_ID=forged python3 -c "...resolve_context_key(None)"
KEY=dsh_forged

Practically this is nothing: someone has to deliberately export both, and TRELLIS_CONTEXT_ID was always settable by hand, so no new surface. The only cost is a future maintainer reading "cannot be forged" as a security boundary and building on it. Dropping to "cannot be inherited from an outer Trellis host" makes it exactly true. Fix it whenever, or leave it — not worth another round trip on its own.

Ready when you are

Nothing left on our side. Whenever you are done with your remaining cleanup, mark the three PRs ready for review and I will merge in your order: marketplace#13 → docs#31 → repoint the submodules here → this into feat/v0.7-beta.

I have left them in draft rather than flipping them myself, since you are the one who knows whether anything else is still coming.

taosu0216 pushed a commit to mindfold-ai/marketplace that referenced this pull request Aug 14, 2026
Moves DeepSeek Harness from the inline-execution group to the class-2 sub-agent group across all five block markers, and documents the DSH dispatch path: load one `trellis-agent-<role>` skill per child, then synchronize via `trellis_wait` when the optional companion plugin is present, or an initial foreground dispatch when it is not. Never poll or sleep.

Also restores the operational Claude Code guardrail on the shared Tools line — the sub-agent roles are dispatched through the host's sub-agent mechanism (on Claude Code: the Task/Agent tool, never the Skill tool) — while staying accurate on DSH, where those role instructions do ship as skills.

Byte-identical to the bundled template in mindfold-ai/Trellis#548, verified by diff.

Co-authored-by: SajoLuo <SajoLuo@users.noreply.github.com>
@taosu0216

Copy link
Copy Markdown
Contributor

Both companion PRs are merged — we were each waiting on the other, my earlier "mark them ready and I will merge" read as if it covered all three. Your ordering was the correct one, so I have taken the first two steps.

mindfold-ai/marketplace#13  →  main  cfb2f38
mindfold-ai/docs#31         →  main  59d5b37

Squashed, so the SHAs are new. I verified the merged trees are byte-identical to the commits I reviewed (08a258f0 and 11e3e645) — git diff against each is empty, so nothing drifted through the squash. You are credited as co-author on both.

Your no-plugin verification is a stronger construction than mine, incidentally — disabling the companion through a profile patch and poisoning both DSH_TRELLIS_CONTEXT_ID and DSH_SESSION_ID from the launcher covers a case my run did not, and it lands on the same resolved key I measured from the outside.

Over to you for the last two steps:

  1. repoint docs-site59d5b37 and marketplacecfb2f38, both now reachable from main
  2. undraft feat(dsh): add native subagent workflow on beta #548

Then I will merge it into feat/v0.7-beta and take the proper pass over the sub-agent dispatch and trellis_wait paths you asked for. No rush on the cannot be forged comment wording — fold it into that round if you touch the file again anyway.

@SajoLuo
SajoLuo marked this pull request as ready for review August 14, 2026 06:02

SajoLuo commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — the final two steps are complete at 7742ddab:

  • repointed docs-site to merged main commit 59d5b37
  • repointed marketplace to merged main commit cfb2f38
  • verified both merged trees are identical to the reviewed PR heads
  • tightened the managed-shell comment to say the paired DSH values cannot be inherited from an outer Trellis host, without claiming forgery resistance
  • marked feat(dsh): add native subagent workflow on beta #548 ready for review

Finalization checks are green: 3 focused DSH identity regressions, 9 DSH template tests, the bundled/marketplace workflow mirror assertion, and normalized active_task.py mirror/syntax checks.

One non-blocking observation for the proper trellis_wait pass you mentioned: in a real end-to-end DSH run, after trellis_wait returned and the main workflow completed, DSH delivered the check child's native settlement notice again and woke one extra parent turn. No work or state was repeated; the parent recognized it as already handled. Since DSH queues the native notice before emitting subagent/end, I did not add a speculative inbox workaround or reopen the companion PRs for this.

Everything else is ready on my side.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7742ddab0f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +496 to +498
dsh_override = _string_value(os.environ.get("DSH_TRELLIS_CONTEXT_ID"))
if dsh_override:
return _sanitize_key(dsh_override) or _hash_value(dsh_override)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not let inherited DSH identity override an inner host

When Codex, Claude, or another supported host is launched from a DSH session, it inherits the outer process's DSH_TRELLIS_CONTEXT_ID along with its own session identity. This unconditional early return therefore selects the outer DSH context before inspecting the inner host's payload or native environment, causing task.py start/current/finish in the nested host to read or mutate the outer DSH active-task pointer. Scope this override to an execution known to be inside the current DSH managed shell, or otherwise allow an inner host's identity to outrank inherited DSH variables.

Useful? React with 👍 / 👎.

@taosu0216

Copy link
Copy Markdown
Contributor

Did the sub-agent / trellis_wait pass you asked for. Approving to merge. Everything load-bearing was exercised against real dsh 0.1.0-rc.6 rather than read; one pre-existing bug surfaced that is not yours, and one wording point you may want.

Dispatch path, verified against the real tool surface

The workflow instructions depend on dsh's actual tool schema, so I checked the schema rather than trusting the prose:

subagent(description: string, prompt: string, run_in_background: boolean)

run_in_background exists and is a boolean, so the fallback instruction is literally executable. There is no subagent_type or role parameter — which means telling the child to load .dsh/skills/trellis-agent-<role>/SKILL.md in its prompt is not merely a reasonable design, it is the only mechanism dsh offers. The instruction matches the platform.

Then the plugin-absent path end to end, no companion installed:

Q: is trellis_wait available?          → No.
Q: dispatch one subagent, run_in_background: false
                                       → Child's reply: "PONG"

So the capability check is answerable by the agent at runtime (not a flag it has to be told), and foreground dispatch actually settles. That is the half I could not confirm from the diff.

Shared-script changes

cli_adapter.py, task_store.py and workflow_phase.py are cross-platform, so I looked at those first. They follow the established per-platform branch pattern and add no shared behaviour.

get_agent_path's removeprefix("trellis-").removeprefix("agent-") chain is correct for both trellis-implement and trellis-agent-implement inputs, so it is idempotent either way it is called.

resolve_effective_platform returning the display label "DeepSeek Harness" rather than the id looked odd at first, but its only caller is filter_platform, which matches against marker labels — the same reason codex resolves to codex-sub-agent / codex-inline. Consistent with existing semantics.

dsh.ts is clean. The trellis-trellis-agent- rename rewrites the frontmatter name: alongside the file name, so the skill's declared identity and its path cannot drift apart.

Pre-existing bug your change happens to sidestep

Verifying the marker filtering turned up something unrelated to this PR. _platform_matches compares after stripping separators, so a cliFlag that differs from the marker label never matches:

$ get_context.py --mode phase --platform <flag>   # routing blocks returned
claude → 0     kimi → 0     omp → 0
cursor → 1     codex → 1    zcode → 1    snow → 1    grok → 1    qoder → 1    kilo → 1
dsh    → 1

claudeclaudecode, kimikimicode, ompohmypi. Those three get both routing blocks stripped and no error — the Active Task Routing section comes back empty. Since every platform's start / continue command calls this with --platform {{CLI_FLAG}}, that is the shipped path.

I confirmed the same on feat/v0.7-beta without your branch and on released main, so it predates this PR. dsh comes out correct precisely because you added the alias in resolve_effective_platform. I will open a separate issue; nothing for you to do here.

One wording point, take it or leave it

Without trellis_wait, dispatch the child initially with run_in_background: false so the dependent gate cannot overtake it.

"initially" can be read as "the first child is foreground, later ones may be background" — which is exactly the state the paragraph forbids two sentences later. If you meant "at dispatch time" rather than "for the first dispatch", dropping the word or saying "dispatch each child with run_in_background: false" removes the ambiguity. Not worth a round trip on its own; fold it in if you touch the file.

Verdict

core   344 passed, 1 skipped
cli   1809 passed  (77 files)
lint / tsc          clean
both submodule pointers   reachable from their main ✓
merges into feat/v0.7-beta with no conflicts ✓

Merging now. Thanks for the turnaround on all of this — three review rounds inside two hours, each one with its own verification run, is not the normal experience on a platform PR.

@taosu0216
taosu0216 merged commit f37e575 into mindfold-ai:feat/v0.7-beta Aug 14, 2026
1 check passed
taosu0216 pushed a commit that referenced this pull request Aug 14, 2026
Brings 18 commits from main, including 0.6.15, the DSH minimal adapter
(#547), and the marker-label routing fix. Beta stays on 0.7.0-beta.3 and
keeps its own submodule pointers.

Git merged three files cleanly whose *content* was contradictory, so
those are the ones worth re-reviewing:

- workflow.md: main puts dsh in the inline-execution group (#547 has no
  sub-agent surface), beta puts it in the class-2 sub-agent group (#548
  added one). Both edits applied, leaving dsh in both groups. Resolved to
  beta's semantics — dsh removed from the inline markers in the template,
  the dogfood copy and the marketplace mirror.
- dsh was registered twice: once by #547 on main and once by #548 on
  beta, in cli/index.ts, commands/init.ts and configurators/index.ts.
  Different lines each time, so no conflict was raised; tsc caught it.
  Deduped, keeping beta's wording.
- opencode.test.ts: beta's layered-workflow tests fed the plugin a bare
  {type,text} part, which #524 on main no longer accepts —
  insertSyntheticTextPart requires an ordinary part with a persisted
  identity and now emits the breadcrumb as its own part instead of
  rewriting the user's text. The tests asserted on parts[0] and silently
  saw the unmodified prompt. Rewritten against the synthetic-part
  contract; they still verify the layered resolution they were written
  for.

Declared conflicts resolved to beta for everything dsh-related and for
the version files; workflow_phase.py took main's _PLATFORM_MARKER_LABELS
table, which subsumes beta's dsh-only alias.

Verified on the merged tree rather than assumed:
- core 344 passed, cli 1825 passed, lint and tsc clean
- all 22 platforms resolve a non-empty routing block
- dsh resolves the sub-agent route, not the inline one
- built the CLI and ran `init --dsh`: 86 files, with the three
  trellis-agent-* role skills beta expects
- marketplace mirror byte-identical to the bundled template
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants