Repair the workspace grouping a container update erases (Ungrouped chats) #1

Merged
mward4 merged 1 commits from fix/workspace-grouping-doctor into main 2026-09-17 22:03:49 -04:00
Owner

Symptom

After every release is installed and the container is recreated, older chats sit under Ungrouped instead of their project.

Cause (verified against this host's own state volume, dsh 0.1.5-rc.2)

dsh joins a session to a project when the session is created, against the project directory's realpath-canonicalized path. There is no later re-scan: upstream's history bootstrap runs once per state volume (initialized: true here since 2026-09-02) and never again. Re-adding a folder creates a fresh, empty project; existing chats are never moved into it.

So a project directory that the container update erases keeps its chats ungrouped for good. On this host, only /workspace and /home/node/.dsh are persisted, while the workspace picker opens in /home/node:

Project Directory Chats
/workspace/Validation ok (persisted) 35 accounted, all grouped
/home/node/App Development MISSING (container filesystem) 1 accounted + 3 recoverable
/home/node/Game Servers MISSING 1 accounted
/home/node/Health MISSING 1 accounted
/home/node/Sondra ok, but ephemeral 1 accounted + 5 recoverable

The 8 recoverable chats are exactly the 8 rows the sidebar shows under Ungrouped.

Fix

dsh-workspace-doctor.mjs runs from the entrypoint before dsh starts:

  • reports every project directory that no longer resolves, with its storage, and every chat recoverable from one;
  • with DSH_WORKSPACE_DOCTOR=repair (the template default), recreates the missing directories and re-accounts the recoverable sessions, so the chats return to their project at the next start.

Safety: session logs are never modified, no project is invented, no chat is moved into a project it did not run in, the registry is backed up to workspace.json.bak-<stamp> first, the write is the same atomic 0600 fsynced replacement dsh's JSON backend performs, an unknown registry version is refused (exit 2), and the run is idempotent. Image default is report-only (warn); off is silent.

Verification

  • --check against the live state volume: reports 3 missing directories and the 8 recoverable chats, exit 1, writes nothing (registry md5 unchanged).
  • --all against a copy of it: exit 0, Validation 35 → 35, App Development 1 → 4, Sondra 1 → 6, one backup, second run a no-op.
  • The rewritten registry parses with dsh's own workspaceDomainState / workspaceRecord zod schemas and satisfies the startup invariants (registry order ↔ table, one owner per session).
  • Unknown-format and fresh-volume paths tested; entrypoint warn / repair / off branches tested with sh.

Note

This repairs grouping, not files: a recreated directory is empty, because the files lived in the container. Keeping project data under /workspace prevents the situation entirely, which the README now states as an image-contract rule. The same PR adds the doctor to the CI recipe hash and smoke test so the next built image carries it.

Authored by the dsh agent on the maintainer's behalf; review as you would any agent change.

## Symptom After every release is installed and the container is recreated, older chats sit under **Ungrouped** instead of their project. ## Cause (verified against this host's own state volume, dsh 0.1.5-rc.2) dsh joins a session to a project **when the session is created**, against the project directory's `realpath`-canonicalized path. There is no later re-scan: upstream's history bootstrap runs once per state volume (`initialized: true` here since 2026-09-02) and never again. Re-adding a folder creates a fresh, empty project; existing chats are never moved into it. So a project directory that the container update erases keeps its chats ungrouped for good. On this host, only `/workspace` and `/home/node/.dsh` are persisted, while the workspace picker opens in `/home/node`: | Project | Directory | Chats | |---|---|---| | `/workspace/Validation` | ok (persisted) | 35 accounted, all grouped | | `/home/node/App Development` | MISSING (container filesystem) | 1 accounted + 3 recoverable | | `/home/node/Game Servers` | MISSING | 1 accounted | | `/home/node/Health` | MISSING | 1 accounted | | `/home/node/Sondra` | ok, but ephemeral | 1 accounted + 5 recoverable | The 8 recoverable chats are exactly the 8 rows the sidebar shows under Ungrouped. ## Fix `dsh-workspace-doctor.mjs` runs from the entrypoint before dsh starts: - reports every project directory that no longer resolves, with its storage, and every chat recoverable from one; - with `DSH_WORKSPACE_DOCTOR=repair` (the template default), recreates the missing directories and re-accounts the recoverable sessions, so the chats return to their project at the next start. Safety: session logs are never modified, no project is invented, no chat is moved into a project it did not run in, the registry is backed up to `workspace.json.bak-<stamp>` first, the write is the same atomic 0600 fsynced replacement dsh's JSON backend performs, an unknown registry version is refused (exit 2), and the run is idempotent. Image default is report-only (`warn`); `off` is silent. ## Verification - `--check` against the live state volume: reports 3 missing directories and the 8 recoverable chats, exit 1, writes nothing (registry md5 unchanged). - `--all` against a copy of it: exit 0, Validation 35 → 35, App Development 1 → 4, Sondra 1 → 6, one backup, second run a no-op. - The rewritten registry parses with dsh's own `workspaceDomainState` / `workspaceRecord` zod schemas and satisfies the startup invariants (registry order ↔ table, one owner per session). - Unknown-format and fresh-volume paths tested; entrypoint `warn` / `repair` / `off` branches tested with `sh`. ## Note This repairs *grouping*, not files: a recreated directory is empty, because the files lived in the container. Keeping project data under `/workspace` prevents the situation entirely, which the README now states as an image-contract rule. The same PR adds the doctor to the CI recipe hash and smoke test so the next built image carries it. Authored by the dsh agent on the maintainer's behalf; review as you would any agent change.
mward4 added 1 commit 2026-09-17 22:03:45 -04:00
Repair workspace grouping that a container update erases
Sync upstream and build images / sync-and-build (push) Successful in 9m9s
c41d7fb403
Updating the container recreates it, so anything outside the mounted
/workspace and state volume is gone. dsh joins a session to its project
when the session is created (upstream's history bootstrap runs once per
state volume and never again), and the workspace registry stores an
absolute canonical directory path — so a project directory under
/home/node, where the workspace picker opens by default, loses its chats
to "Ungrouped" permanently on the next update.

Ship dsh-workspace-doctor.mjs and run it from the entrypoint before dsh
starts: it reports every project directory that no longer resolves and
every chat recoverable from one, and with DSH_WORKSPACE_DOCTOR=repair it
recreates the missing directories and re-accounts those sessions in the
workspace registry. Session logs are never read for writing and never
modified, no project is invented, no chat is moved into a project it did
not run in, and the registry is copied to workspace.json.bak-<stamp>
before the atomic, 0600, fsynced write dsh's own JSON backend performs.
The doctor refuses to write a registry version it does not know, is
idempotent, and defaults to report-only in the image (warn) so nothing
changes unasked; the Unraid template defaults to repair.

CI: the doctor joins the recipe hash and the push path list, and the
image smoke test asserts it is installed and runs against a fresh volume
(exit 2, the tool itself failing, is the only outcome that fails a build).
mward4 force-pushed fix/workspace-grouping-doctor from c4085bbb59 to c41d7fb403 2026-09-17 22:03:45 -04:00 Compare
mward4 merged commit c41d7fb403 into main 2026-09-17 22:03:49 -04:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mward4/deepseek-harness#1