# Launcher environment compatibility

`dsh-claude-tui` is both a Harness bundle and a one-command launcher. The
launcher must make an existing DSH environment useful when it is safe, while
remaining able to start from a clean machine without adopting or overwriting
unowned state.

This document describes TUI `0.1.9`, targeting DSH `0.2.1-rc.2`. Publication and
registry identity are verified separately through the
[v0.1.9 Release](https://github.com/cogine-ai/dsh-claude-tui/releases/tag/v0.1.9)
and npm metadata.
On macOS arm64, the complete check on Node `24.16.0` and the fresh-cache ordinary
npm peer-resolution installed-bundle gate on Node `22.23.3` passed before review.
A subsequent roster-label ordering fix passed 64 application tests and typecheck.
Current review and [CI results](https://github.com/cogine-ai/dsh-claude-tui/pull/24/checks)
are tracked on [PR #24](https://github.com/cogine-ai/dsh-claude-tui/pull/24); local
results belong to the [0.2.1 adaptation record](harness-0.2.1-rc.2-adaptation.md).
The [0.2.0 adaptation record](harness-0.2.0-rc.2-adaptation.md) retains the old
Node-engine investigation and historical macOS/Ubuntu results; those results
do not qualify this version.

Version `0.1.9` retains Node **22.19+ in 22.x, or 24.2+**; its declared engines
and launcher gate are `^22.19.0 || >=24.2.0`. Unsupported versions, including
Node `24.0` and `24.1`, fail before external-runtime discovery or user DSH Home
writes. Eligible versions invoke the Harness executable directly and preserve
its arguments and foreground process behavior.

Node `24.0`/`24.1` remain excluded. The earlier Linux/Windows subprocess-runner
investigation and Ubuntu checks explain that floor in the historical record;
they are not a new platform result for DSH `0.2.1-rc.2`.

`DSH_TOOLS_MODE` is validated before discovery, probes, or user Home writes.
`native` and `ptc` are accepted, unset or blank values default to `ptc`, and the
legacy `code` input normalizes to `ptc`. `both` and unknown values fail with an
actionable error. In particular, setting `both` does not create, reconcile, or
migrate a DSH Home.

On macOS, Harness child processes default to `CHOKIDAR_USEPOLLING=true` and
`CHOKIDAR_INTERVAL=1000`. This retains settings, skills, and profile updates
while avoiding native FSEvents close deadlocks. The normal launcher preserves
explicit values from its caller; isolated probes use their own defaults. These
defaults apply to the child environment and may be inherited by its tools.

## Default decision order

With `DSH_CLAUDE_TUI_RUNTIME=auto` (the default), the launcher completes the
whole decision before changing the selected user DSH home:

1. Check the Node engine constraint and tools mode before discovery or DSH Home writes.
2. Inspect the requested/default DSH home and choose a launcher-owned profile.
3. Look for `@deepseek-ai/dsh` associated with that home under
   `profiles/node_modules`, then for a verifiable `dsh` executable on `PATH`.
4. Accept only package manifests named `@deepseek-ai/dsh` whose version is in
   `>=0.2.1-rc.2 <0.2.2` and whose declared bin exists inside the package.
5. Run each otherwise eligible external candidate through the current packed
   TUI's compatibility probe.
6. Use the first candidate that passes; otherwise use the launcher-pinned
   `@deepseek-ai/dsh@0.2.1-rc.2`.
7. Create or reconcile only the selected launcher-owned profile, then replace
   the launcher process with the selected Harness process.

The launcher does not scan arbitrary npm/pnpm caches, invoke a nested
`npx @deepseek-ai/dsh@latest`, or retry another runtime after a real Harness
launch has begun. The last rule prevents duplicate model requests and Session
writes.

## Welcome runtime provenance

After resolving the complete launch plan, the launcher replaces any inherited
internal snapshot with a bounded record of the selected Harness version,
`system`/`bundled` runtime kind, `shared`/`isolated` home kind, exact DSH home,
and effective `DSH_TOOLS_MODE`. The startup plugin validates this record before
the expanded welcome panel renders it; malformed or missing data is ignored
instead of crashing the TUI. The snapshot is process-local and is never written
to a Session, profile, credential store, prompt, or model request.

The product labels stay mapped to DSH's real tools configuration:

- `native` renders as `Standard`;
- `ptc` renders as `PTC`; the legacy `code` input is normalized to `ptc`.

DSH `0.2.1-rc.2` removes the mixed `both` mode. PTC executes through the shared base
bundle's `@deepseek-ai/dsh-ptc-runtime-node` provider, which starts fresh Node
processes under the Session's sandbox policy. The TUI patch does not mount the
removed worker-thread code-runtime package.

`Minimal` remains an Agent-composition choice, not a DSH tools mode. A
direct `dsh --profile ...` launch has no launcher decision to report, so the
panel says that provenance is unavailable rather than guessing it.

## DSH home and profile ownership

An unset or empty `DSH_HOME` selects `~/.dsh`. A non-empty `DSH_HOME` is an
explicit user choice and is authoritative.

The current profile name is `dsh-claude-tui`. The previous launcher-managed
name, `claude-tui`, remains supported:

| State | Result |
| --- | --- |
| Valid launcher-managed `claude-tui` | Reconcile and reuse it in place. |
| Unowned `claude-tui` with no launcher marker | Leave it untouched and use `dsh-claude-tui`. |
| Missing `dsh-claude-tui` | Create it atomically. |
| Valid launcher-managed `dsh-claude-tui` | Reconcile only launcher-owned registration and marker fields. |
| Corrupt/unsupported launcher marker or an unowned `dsh-claude-tui` | Treat the home as unsafe. |

If the implicit `~/.dsh` is unsafe, automatic mode uses the bundled runtime
with `~/.dsh-claude-tui` and renders a visible notice. Credentials and Sessions
are not copied, so they remain in the original home. If an explicit
`DSH_HOME` is unsafe, startup fails with the exact conflict instead of silently
switching homes. The isolated home is checked once; there is no recursive
fallback to more directories.

Sessions, credentials, settings, and the shared profile-module fallback are
DSH-home scoped rather than profile scoped. A shared-home launch sees existing
DSH state. An isolated-home launch intentionally does not.

DSH `0.2.1-rc.2` retains V4 Session generations. Supported older logs migrate
through upstream's adjacent converters and retain their predecessor files;
the unchanged V4 number does not establish compatibility with every earlier
V4 reader. New event types and experimental plugin records have their own
admission rules. Use a separate DSH Home for cross-version checks, and stop other
Harness processes before resuming existing Sessions with the new runtime.
Preserving predecessors does not establish downgrade support. The removed
SQLite Session backend requires export with its original runtime before
migration; SQLite query/index storage is separate.

## Credentials and current working directory

The interactive TUI keeps the credential store, authorization, and account
services from the shared base bundle. In DSH `0.2.1-rc.2`, a saved reference in
`$DSH_HOME/.credentials.yaml` precedes the same name in the launch environment;
project and Harness-home `.env` values remain fallbacks. `/provider` reads the
effective source and writability from DSH. Saving a key can therefore replace
an environment-backed value for this interactive profile.

The upstream `headless` profile disables those stored-authentication services
by default and uses launch-time authentication. This TUI does not inherit that
headless-only change. Review duplicate credential reference names before
choosing which account should authenticate a shared Home; do not print values.

DSH records the current working directory separately from the immutable
`Session.header.cwd`, which retains the original project identity. The TUI
removes the unsupported `{{cwd}}` prompt variable and indexes file mentions
using the directory service, refreshing after a committed directory change.
Session-picker project filtering continues to use the original project.

## Compatibility probe

The external-runtime probe uses a fresh temporary DSH home, OS home, and
working directory. It creates and disposes a temporary Agent and Session,
appends and reads back an event through `seq`, `eventAt()`, and
`snapshotEvents()`, verifies the V4 header and an embedded assistant stream, flushes that Session, and requires a token-bound machine-readable result from
the exact TUI package being launched. It does not send a model request.

The child receives a small platform/locale allowlist plus temporary paths,
`DSH_TELEMETRY_DISABLED=1`, and the probe token. Provider credentials, API
keys, proxy variables, `NODE_OPTIONS`, user `.env` files, user patches, and
bootstrap-hook variables are not inherited. Runtime and output are bounded;
timeout, excessive output, missing services, module identity errors, and an
unexpected result all reject that external candidate.

Temporary probe state is deleted after the child exits. A rejected external
candidate cannot modify the requested user DSH home.

## Deterministic override

`DSH_CLAUDE_TUI_RUNTIME` accepts:

- `auto` — qualified external DSH first, then pinned bundled DSH;
- `system` — require a qualified external DSH and fail if none passes;
- `bundled` — bypass external discovery and use the pinned bundled DSH.

Examples after building the TUI `0.1.9` checkout with DSH `0.2.1-rc.2`:

```sh
DSH_CLAUDE_TUI_RUNTIME=bundled node lib/cli.js
DSH_CLAUDE_TUI_RUNTIME=system node lib/cli.js
DSH_HOME=~/.dsh-team node lib/cli.js
```

## Remaining boundary

The probe qualifies one launch composition; it cannot make concurrent Harness
processes with different dependency trees safe against the same DSH home.
Harness maintains a home-level module fallback that either process may
reconcile. Use sequential launches, or give concurrent runtimes separate
`DSH_HOME` values.

Compatibility is intentionally limited to `>=0.2.1-rc.2 <0.2.2`; the older
`0.2.0` release, `0.2.1` alphas, and `0.2.1-rc.1` are outside this version's
target. A future package may satisfy the semantic range yet change an injected
service contract; the probe remains required in addition to the version check.
The probe sends no model request, so installed-artifact tool-turn and replay
tests remain separate qualification gates.
