Compare commits

...
Author SHA1 Message Date
dsh agent 3849709cc2 Fix the doctor's invocation path; assert the boot log in CI
Sync upstream and build images / sync-and-build (push) Successful in 8m26s
The entrypoint called /usr/local/bin/dsh-workspace-doctor while the
Dockerfile installs /usr/local/bin/dsh-workspace-doctor.mjs, so every
start logged "...: not found" and the workspace doctor never ran: no
report, no repair. The CI smoke test checked the .mjs path with a direct
`docker exec`, which cannot see a mistake in the entrypoint's own
invocation, so the build stayed green while the container was broken.

Resolve the doctor by name (`command -v dsh-workspace-doctor.mjs`) in
both the root and the non-root path, and log an explicit "not installed
in this image" line when it cannot be found, instead of the shell's bare
"not found". The smoke test now reads the container's boot log: it
requires the doctor's own "dsh-workspace-doctor:" output to appear, and
fails if the entrypoint logs its "workspace doctor:" failure line — that
covers path, exec bit and invocation together, which is the class of
mistake that shipped.

README: manual repair through a one-off container with --entrypoint (dsh
stopped, so nothing races the registry write), and the note that an
existing Unraid container does not pick up the template's new Workspace
Doctor variable on its own.
2026-09-17 22:21:58 -04:00
3 changed files with 60 additions and 9 deletions

No files matched your search

+15
View File
@@ -215,6 +215,21 @@ jobs:
DOCTOR_RC=0
docker exec "$SMOKE" dsh-workspace-doctor.mjs --check > /dev/null || DOCTOR_RC=$?
[ "$DOCTOR_RC" -le 1 ]
# ...and the entrypoint must actually invoke it. The doctor prints a
# "dsh-workspace-doctor:" line when it runs; the entrypoint logs a
# "workspace doctor:" line only when it could not. A direct
# `docker exec` above sails past a wrong path or a lost exec bit, so
# the boot log, not the binary, is what proves the wiring.
if ! docker logs "$SMOKE" 2>&1 | grep -q "^dsh-workspace-doctor:"; then
echo "the workspace doctor did not run at boot"
docker logs "$SMOKE" | tail -30
exit 1
fi
if docker logs "$SMOKE" 2>&1 | grep -q "workspace doctor:"; then
echo "the entrypoint could not run the workspace doctor"
docker logs "$SMOKE" | tail -30
exit 1
fi
OK=""
for i in $(seq 1 30); do
if docker exec "$SMOKE" node -e '
+18 -1
View File
@@ -233,6 +233,13 @@ Your DeepSeek API key can be set below (stored in the container env) or entered
</Container>
```
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.
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
trusts.
@@ -330,7 +337,17 @@ it fixes it at startup:
| Repairs | recreates missing workspace directories, and re-accounts chats whose recorded directory matches a project but which no project lists |
| Never | touches a session log, deletes a session, invents a project, or moves a chat into a project it did not run in |
| Before writing | copies the workspace registry to `workspace.json.bak-<timestamp>`; the write itself is atomic and in dsh's own format |
| By hand | `docker exec -u 1000:1000 <container> dsh-workspace-doctor.mjs --check` (add `--all --dry-run` to preview a repair) |
| By hand | report with `docker exec <container> dsh-workspace-doctor.mjs --check`; repair with the one-off container below |
To repair without waiting for a container start, stop the container first so nothing races
the registry write, then run the doctor against the state volume directly (`--entrypoint`
bypasses dsh, and the image's `PUID`/`PGID` are applied to what it writes):
```sh
docker run --rm --entrypoint /usr/local/bin/dsh-workspace-doctor.mjs \
-v /mnt/user/appdata/deepseek-harness:/home/node/.dsh \
gitea.milesward.dev/mward4/deepseek-harness:latest --all
```
The recreated directory is empty: this restores the *grouping*, not the files — those lived
in the container and nothing here can bring them back. Keep project data under `/workspace`
+27 -8
View File
@@ -137,17 +137,33 @@ if [ "$(id -u)" = 0 ]; then
# 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".
#
# 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
0|off|no) ;;
repair|all|1|yes)
/usr/local/bin/dsh-workspace-doctor --all \
|| log "workspace doctor: repair did not finish; starting dsh with the registry unchanged"
# The doctor runs as root here; hand what it wrote back to the runtime user.
chown "$PUID:$PGID" "$STATE/storages/workspace.json" "$STATE/storages/"workspace.json.bak-* 2>/dev/null || true
if [ -n "$DOCTOR" ]; then
"$DOCTOR" --all \
|| log "workspace doctor: repair did not finish; starting dsh with the registry unchanged"
# The doctor runs as root here; hand what it wrote back to the runtime user.
chown "$PUID:$PGID" "$STATE/storages/workspace.json" "$STATE/storages/"workspace.json.bak-* 2>/dev/null || true
else
log "workspace doctor: dsh-workspace-doctor.mjs is not installed in this image;"
log " workspace grouping was NOT repaired. Update the image."
fi
;;
*)
/usr/local/bin/dsh-workspace-doctor --check \
|| log "workspace doctor: see the report above - set DSH_WORKSPACE_DOCTOR=repair to fix it at startup"
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
@@ -163,6 +179,9 @@ if [ ! -w "$STATE" ]; then
log " chown -R $(id -u):$(id -g) <host path mounted at $STATE>"
exit 1
fi
/usr/local/bin/dsh-workspace-doctor --check \
|| log "workspace doctor: see the report above (running unprivileged, so it only reports)"
DOCTOR="$(command -v dsh-workspace-doctor.mjs 2>/dev/null || true)"
if [ -n "$DOCTOR" ]; then
"$DOCTOR" --check \
|| log "workspace doctor: see the report above (running unprivileged, so it only reports)"
fi
exec dsh "$@"