The round-four review's v6 pass found three factual gaps and the v5 pass two
nits. Correct them before merge:
- The in-process note claimed a koffi signature mistake is 'contained to
the worker thread' — worker_threads share the process, so a native access
violation takes down the whole Node process with no PowerShell fallback.
State the real blast radius and record the deferred pkg-VFS worker-spawn
arm in Consequences (both languages, pairing re-recorded).
- The 2026-07-27 picker note claimed unconditional 'per-monitor-v2 DPI';
PMv2-less hosts (Server 2016 / Win10 1607) cascade to per-monitor or
system-aware. Say 'the best thread DPI awareness the host accepts'
(both languages, pairing re-recorded).
- built-worker.e2e.ts was not in any keyless gate (vitest.e2e config is
not part of the default unit run and builtBinSmokeGate's explicit list
missed it), so lib/worker.cjs load regressions passed keyless CI. Add it
to builtBinSmokeGate alongside the workflow-workerthread sibling.
- Remove the dead trailing 'void bindings' in win32-dialog-logic.spec.ts
and give native-picker.spec.ts the sibling module header it lacked.
Master removed the TUI package, the `meta` and `upgrade` subcommands, and
`--config-replace`, and made raw `dsh` require a `--config` overlay. Resolved
onto that shape:
- Dropped this branch's TUI edits with the surface itself, including
`tui.cordis.yml`, `runTui`, and the TUI keyless PTY smoke.
- Dropped the `--config-replace` plumbing rather than reintroducing a flag
master deliberately removed. The gap this branch fixed remains: `dsh -p`
still could not name its composition, so it keeps `--config`.
- Kept this branch's deletion of the personal `$DSH_HOME/config.yaml` layer,
which master still carried, and provided the environment snapshot in the new
raw `runConfig` surface alongside web and headless.
- Ported the headless shutdown PTY test off the personal overlay onto a named
`--config` file, which is what proves that flag now exists on `-p`.
$DSH_HOME/.env had just become an ordinary environment layer, which left the
harness resolving user-facing values from a flattened process.env that could
no longer say where a value came from. A key stored through the web page
stayed shadowed by an older key in the user's own .env. An endpoint could be
redirected by the project: the invoking directory's .env is materialized like
every other layer, and a base URL decides where a resolved API key is sent, so
a DEEPSEEK_BASE_URL written into a model-editable workspace would send the
user's credential — and the prompts carrying their code — to whatever host
that file named.
Give every user-facing value one ordering, with four kinds of source:
explicit for this run per-operation override, CLI argument
> authored by deployment --config / --config-replace
> this launch's shell inherited process environment
> product-managed store settings.yaml, .credentials.yaml
> discovered file $DSH_HOME/.env
> defaults schema default, shipped base, public default
The domains differ only in which tiers exist. The earlier split — credentials
ranking the environment over the managed file while settings ranked over the
environment — was inconsistent: the distinguishing fact is who authored the
source, not the domain.
packages/util/environment owns an immutable snapshot with per-layer
provenance. getFrom(name, sources) searches only the layers a caller names,
and omitting one is a refusal rather than a demotion: the adapters ask for
['process', 'user-env'], so no reordering can let a project file back into a
decision it was excluded from.
isBootstrapOnly rejects, before anything is materialized, any .env setting a
variable that governs how a process launches (PATH, SHELL, NODE_OPTIONS,
LD_PRELOAD), where code or model-visible instructions load from (the whole
DSH_* namespace, HOME, XDG_*), or how the network is reached (proxy and CA
variables). The namespace is denied wholesale so a switch added later cannot
become settable by being forgotten, and there is no opt-out.
verify-config-source-ownership keeps both rules: no unregistered process.env
read under packages/*/*/src (26 allowlisted with reasons), and no apiKey,
baseURL, or headers inlined from the environment in shipped Cordis config —
removing those inlines is what makes the deployment tier meaningful.
- scrubbedParentEnv folds case before the DSH_ prefix check (Windows env
names are case-insensitive; a parent dsh_* entry read back as \*
in the child) and the service spec pins the lowercase probe.
- The acp.snapshot.ts pwsh probe follows resolvePwshPath() like the package
suites, so a Windows host with only an install-location pwsh still runs
the scenario.
- pwsh-tool-turn is re-recorded around [Console]::Out.Write('PWSH_OK'):
the fixture carries no platform newline, so one recording replays on
Windows and POSIX alike (record + refresh; replay-verified keyless).
- The pwsh-local Known Limitations bullet drops the self-defeating no-op
advice: & { } is scoped to param(...), using/#requires scripts run from a
file (both languages, pairing re-recorded).
- The capability-seams graph moves ctx.bashEnv ownership to bash-env and
lists pwsh-local/tool-pwsh on the ctx.bash seam (source updated,
docs regenerated).
- The tool-bash presenter fixture retires the stale 'command aborted'
literal for the shipped 'tool call aborted' message.
The hand-edit in 348ab41151 put the seq-17 abort error inside the tool-result
message and dropped a closing brace, breaking every JSONL consumer; tool-calls.ts
appends 'error' as a data-level sibling of 'message' (the seq-19 shape), so the
fixture now matches the emitter.
Coverage: pwsh-local's executor suites self-skip without a real pwsh, which left
per-file 100% unreachable on pwsh-less contributor hosts (mirror of the existing
windowsCoverageExclusions contract). A PATH-only probe exempts only
pwsh-local/src/index.ts; CI runners ship pwsh and still enforce the full bar.
docs/testing.md (+zh, pairing re-recorded) names the prerequisite; the testing.md
budget rises 1120->1150 because the coverage-gate contract genuinely grew.