Running scripts/install.sh from a checkout linked `dsh` straight at that
checkout, producing an install that `dsh-upgrade` cannot upgrade (there is
no `current` to repoint), that dangles if the checkout moves, and whose
launcher resolves to an arbitrary working branch.
In-repo mode still never clones and never touches the working tree, but it
now offers to adopt the checkout, and adoption is the default. The container
owns staging worktrees and `current`; the repository is discovered via
`git rev-parse --git-common-dir` rather than owned, so a clone anywhere on
disk converges on the same upgradable layout as a curl install and both
share one worktree/exclude/lock/link sequence.
Declining, or DSH_ADOPT=0, keeps the previous link-in-place behavior with a
warning naming what it costs, preserving the path that makes this script
testable against local source.
All path comparisons run on physical paths: macOS resolves /var through a
symlink to /private/var, and comparing a resolved path against an unresolved
one misclassified an existing managed install as a foreign clone.
Verified manually (no install.spec.ts, per request) with a harness driving
the real script under a stubbed pnpm across 33 assertions, plus both
interactive outcomes under tmux.
Review findings from ds-review-bot: closing the loading picker now
aborts the scan through the AbortSignal both query methods accept, a
signal-ignoring backend's late settlement is dropped by a staleness
check, one catch spans listing and projection so a projection failure
closes the overlay instead of stranding the loading placeholder,
setCandidates clears a stale still-loading error, and the batch
comment no longer overstates the win as scaling with session count.
Holds the session.history response at the browser's network boundary so
the auto-selected open is observable, then asserts the visible frame and
the recorded phase timeline. Registered host-plane like the other
scaffold-booting e2e files (host include + client project exclude).
The mount fixture always listed the session, so the case named "no list
summary" actually exercised a row proving non-blank. An omitSummaryRow
option drops the row, and the three cases now pin blank:false, an absent
row, and the summary-proven blank open.