Files
deepseek-harness/skills/dsh-upgrade/SKILL.md
T

9.4 KiB

name, description
name description
dsh-upgrade Upgrades a source-installed, personally customized DSH checkout to upstream master while preserving local changes and an unchanged rollback worktree. Use when the user asks to update or upgrade DSH.

DSH Upgrade

Prepare and validate the upgrade in a fresh staging worktree of the master clone, leave the worktree the installed launcher currently uses unchanged, then atomically repoint the stable current symlink once. Read and follow dsh-customize before starting; it owns checkout discovery and lock handling.

Layout

A source-installed DSH keeps every checkout under one container directory <source> (default ~/.dsh/source): the master clone at <source>/master (remote tracking master, the fetch/upgrade base, never a launcher target) and each staging checkout as a git worktree <source>/staging-<timestamp> on branch dsh-staging/<timestamp>. The stable symlink <source>/current points at the active staging worktree, and the PATH launcher links to <source>/current/bin/dsh, so the launcher resolves PATH -> current -> staging worktree. Cutover repoints current alone; the PATH launcher is written once at install and never moves. All worktrees share the master clone's single .git object store; the master clone's .git/info/exclude is inherited by every linked worktree, so one .agents/merge.lock entry there excludes the lock in all of them. An older install may link PATH straight at a worktree (no current) or use scattered sibling clones; if so, follow the recorded launcher checkout rather than assuming this layout, treat that sibling clone as its own master, and create current and repoint PATH to current/bin/dsh as a one-time migration at cutover.

Names

One upgrade attempt uses one UTC basic timestamp YYYYMMDDTHHMMSSZ for all names:

  • new staging worktree: <source>/staging-<timestamp>;
  • preparation branch: dsh-upgrade/prepare-<timestamp>;
  • installed staging branch: dsh-staging/<timestamp>;
  • fetched upstream ref: refs/dsh-upgrade/upstream-<timestamp>;
  • recovery ref: refs/dsh-upgrade/recovery-<timestamp>;
  • recorded current target before cutover: the old staging worktree path, kept for symlink rollback.

The worktree name is always staging-<timestamp> under <source>, never derived from the current staging directory name, so successive upgrades stay in one place and do not accumulate timestamps. The preparation branch and private refs are local-only and must never be pushed. Before starting, reject a current staging branch named exactly dsh-staging, because Git cannot also create dsh-staging/<timestamp>; require the user to choose a non-conflicting staging namespace rather than silently renaming it. If the new staging worktree path exists, resume only when it is a clean worktree of this master clone whose recorded old tip, upstream ref, recovery ref, and named branches exactly match this attempt; otherwise stop. Never add an ad hoc suffix or delete an unknown directory.

Upgrade

  1. Resolve the installed launcher, its staging worktree and branch, the master clone, the current DSH process source, and authoritative upstream. Record exact tips, paths, clean status, remotes, dependencies, worktrees, and in-progress Git operations. Require the installed staging worktree to be clean and its .agents/merge.lock to exist and be Git-excluded. Never stash automatically.
  2. Treat the staging worktree behind the installed launcher as immutable for the whole attempt: do not touch its branch, HEAD, index, tracked or untracked files, dependencies, worktree registration, or lock file. Fetching into the shared master clone and creating new branches, worktrees, and private refs there are allowed because they are append-only and never alter the old worktree's checkout; opening and holding the existing lock is the only operation on the old worktree.
  3. Allocate the timestamp and new staging worktree path. Acquire the installed worktree's existing .agents/merge.lock, repeat every precondition, and keep it through preparation, validation, and the current cutover. If staging moves while waiting, unlock and restart with a new timestamp; remove only attempt artifacts that this run created and verified as disposable.
  4. In the master clone, create refs/dsh-upgrade/recovery-<timestamp> at the recorded old staging tip and dsh-upgrade/prepare-<timestamp> from that tip. Fetch exact authoritative upstream master into refs/dsh-upgrade/upstream-<timestamp> and record its object ID. Add a fresh worktree <source>/staging-<timestamp> checked out on the preparation branch. Confirm the master clone's .git/info/exclude excludes .agents/merge.lock, which the new worktree inherits.
  5. Inspect the Git log and commit ranges between the staging base, old staging tip, and fetched upstream tip. Identify incoming upstream changes, personal commits to preserve, likely duplicates, and conflict-prone areas before rebasing.
  6. In the new worktree, rebase the preparation branch onto the fetched upstream commit. Preserve intentional customizations and drop behavior already upstream. If upstream contains the customization and its remaining local diff only documents that customization, prefer upstream and drop the documentary diff rather than retaining a stale local account. Preserve documentation only when it adds a current, independently useful contract absent upstream. Abort without changing the installed launcher when resolution is uncertain.
  7. Install dependencies in the new worktree, review the resulting diff, and run the repository-required checks. Fix failures and rerun affected checks. Test the new worktree's bin/dsh directly.
  8. Point dsh-staging/<timestamp> at the validated prepared tip and check it out in the new worktree. Ensure its .agents/merge.lock exists (Git-excluded through the shared master exclude). Verify its branch, exact commit, clean status, remotes, dependencies, and absence of in-progress Git operations, then smoke its bin/dsh from a clean temporary workspace. The preparation branch remains temporary; the timestamped staging branch owns the installed commit.
  9. Recheck the old worktree, existing lock, launcher, current, master clone, new worktree, refs, and exact tips. Record current's pre-cutover target, then repoint current at the new staging worktree in one atomic swap with ln -sfn (the -n stops ln from dereferencing the existing directory symlink and writing the link inside the old worktree; mv behaves the same way and is unusable). Leave the PATH launcher alone once it already resolves through current; if a legacy install still links PATH straight at a worktree, create current and repoint PATH to current/bin/dsh as a one-time migration here. The current target must be a clean staging worktree on a staging branch and must never be the master clone or a preparation, feature, review, publication, or detached checkout. Smoke the installed dsh command from a clean temporary workspace.
  10. On failure before the current cutover, leave current, the launcher, and the old worktree unchanged and remove only verified attempt artifacts created by this run (including the new worktree registration if empty). On failure during or after cutover, inspect current's observed target before acting; if cutover did not verify, atomically repoint current back to its recorded pre-cutover target with ln -sfn and verify that dsh starts from the unchanged old staging worktree. This rollback is the sole exception allowing current to return to the old staging worktree. Never retry a side-effecting operation blindly.
  11. Release the old worktree's lock and tell the user to restart DSH through the installed launcher. The current process may continue from the old worktree, but no operation may mutate or remove it until the restarted process proves that it runs from dsh-staging/<timestamp> and the user confirms stability. Avoid customization integration during this confirmation window; if rollback is required after new work lands, reconcile that work explicitly rather than silently stranding it.
  12. After confirmation, remove the preparation branch if no process uses it. Keep the old staging worktree and branch, the recovery ref, and the recorded pre-cutover current target as rollback until the user explicitly approves their removal; leave the actual git worktree remove and directory deletion to the user. Report old, upstream, prepared, and new staging commits; both staging worktree paths and branches; the master clone path; process-source evidence; the current pre-cutover target and cutover; commands and checks; final status; recovery ref; and retained rollback artifacts.

The installed launcher always resolves through current to a staging worktree, never the master clone. Upgrade preparation adds a new worktree that shares the master object store while leaving the old worktree's checkout untouched; cutover is one atomic current repoint to the separately validated timestamped staging worktree, and the PATH launcher never moves.

Recommend upstream candidates

After a successful upgrade, load dsh-upstream-customization and classify each remaining personal customization by its rules. For each candidate, explain its classification and upstream value and recommend whether to propose it, then ask which named candidate, if any, the user wants to upstream. The answer selects a candidate to start that skill's publication workflow; it is not publishing approval, which that workflow still requires.