Repository navigation
Releases: ds-hu/dsh-better-workspaces
Release list
v0.3.0 — the snapshot read path never waits
Fixed
Startup no longer saturates the browser connection pool. On a host with 20 workspaces, opening the GUI could freeze the page for up to a minute: the sidebar posted one POST /snapshots per workspace, each of those requests awaited 10+ Git subprocesses per cwd, and a browser allows six HTTP/1.1 connections per origin. POST /snapshots now answers from what is known and names the rest in pending; snapshots arrive over SSE. The response no longer waits for a compute.
- One git lane, two classes (
lib/lane.js): foreground = a request is waiting, background = a recompute or a periodic fetch. Foreground starts first; a background task that waits too long is promoted, so a steady request stream cannot starve a fetch. The class follows the caller — the never-wait batch and its authorizations are background,/actions,/snapshotand{ fresh: true }are foreground. - Git discovery cache (
lib/discovery.js, ADR 0013 Amendment 2): 30 s TTL keyed by canonical path and dev/ino. Only Git's printed strings are cached; every identity and ownership proof is stillstated at its own commit point, and every mutating route re-discovers.detectReponow answers the whole geometry with onerev-parseinstead of three spawns. - One status probe:
git status --porcelain=v2 --branch -zreplaces the separate branch and status probes, normalized to the v1 code spelling the rest of the plugin reads. A corrupted HEAD falls back to the narrow probes instead of being reported as unborn. - Per-repository facts: default branch and origin URL 5 min, comparison ref 60 s, dropped by a completed fetch and by
invalidate(). - Fetches no longer start at boot: the first fetch is deferred 10 s, at most 2 start per 5 s tick with a 30 s timeout, and they are background-class. This is a rate limit, not a concurrency cap: a cold 20-repository host can have ~12 in flight, all inside the one 8-slot lane, so the
git.exepeak stays ≤ 8 and an interactive read never queues behind them. - Steady state recomputes one repository family:
tools/resultandapi-session/statusresolve Session → cwd → family. A bounded round-robin sweep (≤ 4 targets/minute) covers watchers that silently stop delivering; degraded polling backs off 5 → 15 → 30 s. GET /worktrees/deferredis a read and no longer queues behind a repository's write gate;finalize-deletestays inside the gate and revalidates its token there.- Client: tracking a cwd marks it dirty and one debounced batch asks for all of them (one batch in flight, one queued rerun, nothing dropped). Deferred-Workspace recovery takes main checkouts from the feed instead of one
/detectper workspace.
Contract changes
POST /snapshotsaccepts an optional{ fresh: true }(the previous await-everything shape) and returns a top-levelpending: string[]in its default shape. A cwd missing frombyCwdmeans "not known yet", not "not git". Snapshot objects and SSE frames are unchanged.GET /worktrees/deferredno longer takes the mutation gate.
Both are additive for existing clients; the bundled client uses them.
What the numbers do and do not say
The spawn counts in ADR 0018 are derived from reading the call paths, not counted. Only two properties are guaranteed by construction: one rev-parse per discovery, and concurrent git.exe ≤ 8 (one lane, capacity 8). A warm POST /snapshots answers from cache; a cold process (plugin just loaded, or the page closed longer than the 30 s TTL) still pays one discovery — one spawn, after the merge — per unseen workspace before it can answer. Its absolute time on a 20-workspace host is unmeasured and not promised.
Verification
npm run check passes locally on the merged tree (lint, host suite, security, mutation, client smoke, client React, mount, package), and all three CI jobs pass on the release commit: Node 20, Node 22, Windows path model.
Install / upgrade
dsh plugin --profile web add @ds-hu/dsh-better-workspaces@0.3.0This release changes host code, so restart dsh --profile web; a page refresh only rebuilds the client bundle. From 0.2.10 the bump is a normal upgrade — no migration, no new config.
中文摘要
打开装有 20 个工作区的界面时,整个页面曾会卡住最长约一分钟:侧栏为每个工作区各发一条 POST /snapshots,每条都在等待每个 cwd 的十几个 Git 子进程,而浏览器对同一源只有 6 条 HTTP/1.1 连接。现在 /snapshots 只回答已知事实,把尚未算过的 cwd 放进顶层 pending,快照由 SSE 补齐,响应不再等待任何计算。
lib/lane.js把 git 车道分成前台(有请求在等)与后台(重算、周期 fetch);前台先开工,等待过久的后台任务会被提升,避免被持续的请求饿死。分类看调用方:不等待的批量及其授权算后台,/actions、/snapshot、{ fresh: true }算前台。lib/discovery.js增加 30 s 发现缓存,键含规范路径与 dev/ino;只缓存 Git 打印的字符串,所有身份与归属证明仍在各自提交点stat;写入路由全部绕开缓存。detectRepo由 3 个 spawn 合并为 1 个rev-parse。- 一次
git status --porcelain=v2 --branch -z取代分支与状态两次探测;HEAD 损坏时回退到旧探测,不会被误报为 unborn。 - 仓库级事实(默认分支、origin URL 5 分钟;比较 ref 60 秒)单独缓存,fetch 完成或
invalidate()时清空。 - 首次 fetch 延迟 10 s,每 5 s tick 最多起 2 条、30 s 超时,全部属后台类。这是速率限制而不是并发硬限:冷启动最多约 12 条在途,但共用同一条 8 槽车道,
git.exe峰值仍 ≤ 8,且不会排在交互式读取前面。 - 稳态按仓库族重算(Session → cwd → 族),另有每分钟最多 4 个 target 的轮转扫描兜住静默失效的 watcher;degraded 轮询退避 5 → 15 → 30 s。
GET /worktrees/deferred是只读,不再排在写事务门后。- 客户端把轮询合并为一次去抖批量(同时只有一批在途、一批排队、不丢 cwd);deferred 恢复从 feed 取主检出,不再逐工作区
/detect。
契约变化:/snapshots 新增可选 { fresh: true } 与响应顶层 pending: string[];byCwd 中缺失的 cwd 表示“尚未知道”,不是“非 git”。快照对象与 SSE 帧未变。宿主代码有改动,升级后需重启 dsh --profile web。spawn 数字是按调用链推导、非计数实测;按构造可保证的只有“每次发现 1 个 rev-parse”和“并发 git.exe ≤ 8”;冷进程仍要为每个未见过的工作区付一次发现,其绝对耗时不作承诺。
v0.2.10 — configurable worktree base directory (worktreesDir)
Added
worktreesDir— a plugin-row config key that moves the base directory of the managed worktree namespace (default$DSH_HOME/worktrees). Config only, no UI; the layout keeps the per-repository hash grouping:<base>/<8-char base36 sha256(main checkout)>/<repo>-<branch>. See ADR 0017 and TECHNICAL.md § Configuration.
# $DSH_HOME/profiles/web/cordis.patch.yml
- id: better-workspaces
config:
worktreesDir: 'D:\worktrees'- Structural mistakes fail the mount loudly with the offending value (relative path, non-string, unknown key such as a misspelled
worktreeDir); disk problems (missing, unwritable, offline volume, symlink/junction/short-name, a base inside the checkout being branched) fail on first use, so a disconnected network drive cannot take badges, diffs and PRs down with it. - Changing the value later strands existing worktrees by design: they keep diffs, commits, pushes, PRs and merge actions, but archive and abandoned cleanup no longer reach them (
/detectreportsmanaged: false). The mount logs one warning naming both directories and the count.
Fixed
- The test suite can now run on Windows: POSIX-only fixtures report
SKIP <label>: <reason>instead of failing, the fakeghfixture asserts PATH resolves it (five forge cases had been silently calling the realghagainst github.com), and awindows-latestCI job verifies the Windows path model continuously.
Install
dsh plugin --profile web add @ds-hu/dsh-better-workspaces@0.2.10中文摘要
新增插件行配置键 worktreesDir,可把托管 worktree 的基目录改到任意磁盘(默认 $DSH_HOME/worktrees);布局仍是 <基目录>/<仓库 hash>/<repo>-<branch>。仅配置、无界面。相对路径、非字符串、未知键会让该行挂载失败并点名错值;目录缺失/不可写/离线卷、符号链接、以及位于被创建仓库内的基目录在首次使用时拒绝。改动该值会让旧目录下的既有 worktree 不再受管(diff/提交/推送照旧,归档与 abandoned 清理不再触及),挂载日志会点名两个目录与数量。测试套件同时修好了在 Windows 上无法运行的问题,并新增 windows-latest CI 作业。