diff --git a/Dockerfile b/Dockerfile
index 23f91d71f7..dd77900317 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -62,11 +62,15 @@ RUN set -eux; \
# installs) lives inside the mounted state volume, never the rootfs.
# PUID/PGID: the ids dsh runs as; the entrypoint aligns the state volume to
# them at startup. Unraid's share default is 99:100.
+# DSH_WORKSPACE_DOCTOR: the entrypoint's workspace-grouping step. `repair` is
+# the default so an update heals the projects whose directories the update
+# erased; `warn` reports without repairing, `off` is silent.
ENV DSH_HOME=/home/node/.dsh \
PNPM_HOME=/home/node/.dsh/.pnpm \
XDG_CACHE_HOME=/home/node/.dsh/.cache \
XDG_STATE_HOME=/home/node/.dsh/.state \
XDG_CONFIG_HOME=/home/node/.dsh/.config \
+ DSH_WORKSPACE_DOCTOR=repair \
PUID=1000 \
PGID=1000
diff --git a/README.md b/README.md
index 598e8b1ff9..a35bf8ad2e 100644
--- a/README.md
+++ b/README.md
@@ -123,10 +123,11 @@ Three things went wrong with the alternatives, and this repo is shaped by each:
- **Workspace grouping is checked before every start.** A project directory outside the
mounted `/workspace` and state volume does not survive a container update, and dsh joins
a session to its project only at creation — so those chats collect under "Ungrouped" and
- stay there. The entrypoint reports every such project and every recoverable chat, and
- with `DSH_WORKSPACE_DOCTOR=repair` recreates the missing directories and re-accounts the
- orphaned sessions at startup. Session logs are never touched; the workspace registry is
- backed up first. See [Chats land in Ungrouped after an update](#chats-land-in-ungrouped-after-an-update).
+ stay there. Before every start the entrypoint recreates the missing project directories
+ and re-accounts the orphaned chats, so an update heals itself with no configuration.
+ Session logs are never touched; the workspace registry is backed up first.
+ `DSH_WORKSPACE_DOCTOR=warn` reports without repairing, `=off` is silent. See
+ [Chats land in Ungrouped after an update](#chats-land-in-ungrouped-after-an-update).
#### Operations
- **Upstream mirror.** The `master` branch and the `dsh-v*` tags track GitHub exactly,
@@ -229,16 +230,15 @@ Your DeepSeek API key can be set below (stored in the container env) or entered
1000
1000
0
- repair
+ repair
```
An **existing** container keeps the fields it was created with: Unraid does not add a
-variable a template gained later, so the Workspace Doctor row above does nothing until you
-add it by hand (Docker → Edit → *Add another Path, Port, Variable, Label or Device* →
-Variable, key `DSH_WORKSPACE_DOCTOR`, value `repair`) or re-apply the template. Without it
-the container stays on the image default (`warn`): it reports lost grouping at every start
-but never repairs it.
+variable a template gained later. That is fine here — the image default is already
+`repair`, so the Workspace Doctor row above only matters if you want to differ from it
+(`warn` reports without repairing, `off` is silent), set by hand under Docker → Edit →
+*Add another Path, Port, Variable, Label or Device* → Variable.
Behind a Cloudflare Tunnel, add the public hostname as a `--trusted-host` and have the
tunnel rewrite `Host` to that name (and strip `Origin`) so the fence sees a name it
@@ -257,7 +257,7 @@ trusts.
| `/home/node/.dsh` | `DSH_HOME` — settings, credentials, sessions, plugins, pnpm store/cache/config. **Mount this.** Taken over at startup. |
| `/workspace` | The agent's working directory (`WORKDIR`). Mount only what the agent may read and modify. Mount point made writable; contents untouched unless `DSH_CHOWN_WORKSPACE=1`. |
| `/opt/deepseek-harness/web.cordis.patch.yml` | The baked bind patch: `webserver.host: 0.0.0.0`, `port: 3080` |
-| Env | `PUID`, `PGID`, `DSH_CHOWN_WORKSPACE`, `DSH_WORKSPACE_DOCTOR` (`warn` default, `repair` fixes lost grouping, `off` is silent), plus `DSH_HOME` / `PNPM_HOME` / `XDG_*` pinned into the state volume |
+| Env | `PUID`, `PGID`, `DSH_CHOWN_WORKSPACE`, `DSH_WORKSPACE_DOCTOR` (`repair` default — reports and repairs lost grouping at every start; `warn` reports only, `off` is silent), plus `DSH_HOME` / `PNPM_HOME` / `XDG_*` pinned into the state volume |
| Labels | `dev.milesward.dsh-version`, `.pnpm-version`, `.base-digest`, `.recipe-hash` — what CI's freshness check reads |
Four rules that are easy to get wrong:
@@ -329,8 +329,8 @@ runs once per state volume, on the first project, and never again.) Two conseque
chat into it. The old chats stay under "Ungrouped".
Before every start the entrypoint reports this — each project with its storage, each
-missing directory, every chat it could recover — and with `DSH_WORKSPACE_DOCTOR=repair`
-it fixes it at startup:
+missing directory, every chat it could recover — and repairs it, with no configuration to
+set. `DSH_WORKSPACE_DOCTOR=warn` reports without repairing, `=off` is silent:
| | |
|---|---|
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 60e2b052e8..a603bc138c 100644
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -133,19 +133,29 @@ if [ "$(id -u)" = 0 ]; then
# session is created, against the project directory's canonical path — and a
# directory outside the mounted /workspace and state volume does not survive
# a container update. Those chats then collect under "Ungrouped" for good,
- # because the one-time history bootstrap never runs again. Report it always;
- # recreate the directories and re-account the orphaned sessions when asked
- # (backup first, session logs never touched). See README:
- # "Chats land in Ungrouped after an update".
+ # because the one-time history bootstrap never runs again. The default is to
+ # repair: recreate the directories and re-account the orphaned sessions
+ # (backup first, session logs never touched), so a container update heals
+ # itself with no configuration. DSH_WORKSPACE_DOCTOR=warn only reports,
+ # =off is silent. See README: "Chats land in Ungrouped after an update".
#
# Resolve it by name: the installed file is dsh-workspace-doctor.mjs, and a
# path spelled out here is exactly how a "not found" reaches production. The
# CI smoke test asserts this step's output appears in the boot log, so a
# rename or a wrong spelling fails the build instead of the container start.
DOCTOR="$(command -v dsh-workspace-doctor.mjs 2>/dev/null || true)"
- case "${DSH_WORKSPACE_DOCTOR:-warn}" in
+ case "${DSH_WORKSPACE_DOCTOR:-repair}" in
0|off|no) ;;
- repair|all|1|yes)
+ warn|check|report)
+ if [ -n "$DOCTOR" ]; then
+ "$DOCTOR" --check \
+ || log "workspace doctor: see the report above; DSH_WORKSPACE_DOCTOR defaults to repair, so this start was told not to fix it"
+ else
+ log "workspace doctor: dsh-workspace-doctor.mjs is not installed in this image;"
+ log " workspace grouping was NOT checked. Update the image."
+ fi
+ ;;
+ *)
if [ -n "$DOCTOR" ]; then
"$DOCTOR" --all \
|| log "workspace doctor: repair did not finish; starting dsh with the registry unchanged"
@@ -156,15 +166,6 @@ if [ "$(id -u)" = 0 ]; then
log " workspace grouping was NOT repaired. Update the image."
fi
;;
- *)
- if [ -n "$DOCTOR" ]; then
- "$DOCTOR" --check \
- || log "workspace doctor: see the report above - set DSH_WORKSPACE_DOCTOR=repair to fix it at startup"
- else
- log "workspace doctor: dsh-workspace-doctor.mjs is not installed in this image;"
- log " workspace grouping was NOT checked. Update the image."
- fi
- ;;
esac
export HOME="$HOME_DIR"