dsh-git-badge 0.5.5 β†’ 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,141 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
+ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Versions before 0.6.0 predate this file; their history is in `git log` and on
9
+ npm. 0.6.0 is the first release recorded here.
10
+
11
+ ## [Unreleased]
12
+
13
+ ## [0.7.0] - 2026-09-12
14
+
15
+ ### Changed
16
+
17
+ - **Both surfaces draw the same status mark.** The input chip no longer leads with
18
+ a πŸ”΄/🟑/🟒 emoji dot: it renders the SAME 12px SVG mark as the sidebar row β€” a
19
+ filled **circle**, or a **tree** for a linked `git worktree` β€” so the shape says
20
+ *worktree or not* and the fill says *status*, identically wherever the badge
21
+ appears. The `β‘‚` worktree glyph is gone with the emoji: the tree shape already
22
+ says "worktree". The mark is drawn rather than typed because 🌳 is a **colour
23
+ emoji** β€” CSS cannot tint its leaves β€” and the fill comes from the app's own
24
+ `--dsw-alias-state-{success,warn,error}-primary` tokens, so the mark follows
25
+ light/dark and custom themes, which the hardcoded emoji could not. Neither colour
26
+ nor shape is the only channel: the mark carries an accessible name.
27
+ - **The sidebar row shows status, not the branch.** The row keeps the workspace
28
+ name on the left and floats the status mark to the right, with a worktree's
29
+ directory name beside it; the `|` separator is gone. The branch, sync counts and
30
+ operation token stay on the input chip, the surface scoped to the current
31
+ conversation.
32
+ - **The status endpoint no longer accepts a filesystem path.** The caller now says
33
+ *who it is* and the server resolves the workspace itself: `?session=<id>` for the
34
+ input chip, `?workspace=<id>` for a sidebar row. `?path=` is refused with
35
+ `target-required`; unresolvable ids return `session-not-found` or
36
+ `workspace-not-found`. The path was previously caller-chosen (allowlist-bounded
37
+ and read-only, but still chosen by the caller) on a route that answers without
38
+ GUI authentication.
39
+ - A session is resolved through the workspace registry first, so a conversation
40
+ that is no longer live still renders a badge. The live session's directory is
41
+ consulted only for the window before a new session is attached to its workspace,
42
+ and the registry still validates it.
43
+ - The client half no longer requires the `workspaces` service β€” declared services
44
+ are now just `["slots"]` β€” and change notifications carry the workspace id, so
45
+ subscribers match on identity rather than on a path.
46
+
47
+ ### Added
48
+
49
+ - **A client-half test suite, plus submodule coverage.** `test/client.test.mjs`
50
+ loads the real `lib/client.js` behind a stub module loader and renders both
51
+ registered surfaces, so the mark's shape/colour rules, the row's "never show the
52
+ branch" rule, the right-float and the worktree-name suppression are asserted in
53
+ CI rather than eyeballed. The node half gains a real-submodule fixture: a
54
+ submodule is asserted **not** to be a worktree (its gitfile alone must not decide
55
+ it β€” worktree-ness is the `commondir` marker) while its out-of-tree git dir is
56
+ still watched, like a linked worktree's.
57
+ - **The input chip now says which working tree a conversation is in.** A linked
58
+ `git worktree`'s directory name is appended after the branch β€”
59
+ `🌳 main hotfix-tree ↑0 ↓2 ✎3` β€” because several worktrees of one repository
60
+ otherwise render identical chips and the chip carries no other workspace
61
+ identity. The name is dropped when the branch already implies it (`repo-feat-x`
62
+ on `feat-x` shows just the tree), a main checkout appends nothing, and the
63
+ response reports `isWorktree` / `worktreeName` from one stat of the git dir the
64
+ existing `rev-parse` already returned β€” no extra git invocation. Naming the
65
+ worktree is chip-only, like the operation token. The value is a directory NAME,
66
+ never a path.
67
+ - **A server-side poll fallback for workspaces whose file watcher failed.** A
68
+ workspace whose recursive `fs.watch` could not be established, or which later
69
+ errored, is polled every 5s on a state key (status + refs fingerprint +
70
+ in-progress operation) instead of depending only on the client's 60s fallback.
71
+ - `seam/apply.sh status` β€” reports patched / pristine / upstream-landed / drifted
72
+ by hash, exit 1 on drift with the exact rebuild commands.
73
+
74
+ ### Fixed
75
+
76
+ - **The input chip now refreshes on change events.** Change events identify the
77
+ *workspace*, and the client matched them against its own target id. A sidebar row
78
+ targets by workspace id so rows kept working, but the chip targets by session id,
79
+ which can never equal a workspace id β€” so its handler never fired and the chip
80
+ only updated when it remounted (switching conversations) or on the 60s fallback
81
+ poll. The status response now echoes the resolved workspace id and the client
82
+ matches on that. This was a regression in the server-side resolution change.
83
+ - **A linked worktree's badge only refreshed on a file edit.** The watcher watched
84
+ the workspace root, but a linked worktree's git dir lives OUTSIDE it
85
+ (`<main>/.git/worktrees/<name>`), so `git add`, `git commit` and `git checkout`
86
+ there wrote nothing the root watch could see: the badge stayed stale until a
87
+ worktree file changed or the client's 60s poll fired. The per-worktree git dir
88
+ now gets its own watcher, resolved from the `.git` gitfile (which covers
89
+ submodules too), and either watcher erroring drops both and hands over to the
90
+ degraded poll. A regression test that stages a file written before the watch
91
+ existed fails without the second watcher.
92
+ - **The TTL fetch no longer delays the status response.** `ahead`/`behind` are
93
+ read from the local remote-tracking ref, and the fetch that refreshes it was
94
+ awaited before answering, so any request arriving after the 60s TTL had lapsed
95
+ paid a full network fetch first (~3.3s measured) β€” which is the first request
96
+ after every TTL window, i.e. constantly while editing. Badge updates triggered
97
+ by an edit took seconds for a repo with a remote. The response is now served
98
+ from the refs at hand and the fetch runs out of band; when it succeeds it
99
+ notifies subscribers, which reconverge in ~40ms. Same call, expired TTL: 27ms.
100
+ The trade is that `ahead`/`behind` can lag by up to one fetch β€” branch, dirty
101
+ state and file counts are never delayed.
102
+ - The plugin's `surfaces:` console line reported the boot-time race instead of the
103
+ outcome: it printed `sidebar rows = off (seam absent)` while row badges were
104
+ visibly rendering, because it sampled the seam declaration before the workspace
105
+ browser published it. It now reports from the registration callback, and the
106
+ boot-time line says it is awaiting the seam rather than declaring it missing.
107
+
108
+ ## [0.6.0] - 2026-09-12
109
+
110
+ ### Fixed
111
+
112
+ - **`✎n` under-counted new files.** Git collapses a new directory into a single
113
+ status entry, so adding a folder of 3 files displayed `✎1`. Untracked files are
114
+ now counted individually. If walking a very large untracked tree exceeds the time
115
+ budget, the badge falls back to the collapsed count and reports which mode
116
+ answered (`untrackedMode`) instead of silently showing a wrong number.
117
+
118
+ ### Added
119
+
120
+ - **An in-progress operation token.** A paused merge, rebase, cherry-pick, revert,
121
+ bisect or sequencer run now appears after the branch name β€”
122
+ `🟑 main βš”rebase ↑0 ↓2 ✎3`. A rebase whose conflicts are already staged has no
123
+ unmerged files, so the three-state dot could not express it. The dot contract
124
+ itself is unchanged, and the token is chip-only.
125
+ - **A 41-test node-half suite** (`npm test`) covering the status parser, git
126
+ plumbing against real repositories, request routing, SSE framing and the
127
+ filesystem watcher β€” runnable without booting DSH.
128
+ - **A CI test workflow** running the suite on Node 20/22/24.
129
+
130
+ ### Changed
131
+
132
+ - Publishing runs through **npm trusted publishing** (OIDC) on a published
133
+ release, gated on the suite, with SLSA provenance β€” no long-lived token.
134
+ - Event stream hardening: `x-accel-buffering: no`, named `change` frames, cleanup
135
+ on disconnect, and an unload disposer that also releases the filesystem watchers.
136
+ - `detail=1` is annotated as having no consumer yet (the hover card remains
137
+ pending). Its comment previously claimed `log -1` while the code ran `log -3`.
138
+
139
+ [Unreleased]: https://github.com/Kevin-McIsaac/dsh-workspace-git-badge/compare/v0.7.0...HEAD
140
+ [0.7.0]: https://github.com/Kevin-McIsaac/dsh-workspace-git-badge/releases/tag/v0.7.0
141
+ [0.6.0]: https://github.com/Kevin-McIsaac/dsh-workspace-git-badge/releases/tag/v0.6.0
package/README.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  Git status badges for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness):
4
4
 
5
- - **Input-row chip** β€” `🟒 main` next to the access picker, showing the git state of the workspace the current conversation is attached to. Works on every install.
6
- - **Sidebar row badges** β€” full Claude-Code-statusline-style badges (`| 🟑 main ↑0 ↓2 ✎3`) on workspace rows. Requires the `sidebar.workspaces.row` seam; without it the plugin degrades to chip-only and logs what's active to the browser console.
5
+ - **Input-row chip** β€” `● main ↑0 ↓2 ✎3` next to the access picker, showing the git state of the workspace the current conversation is attached to. Works on every install.
6
+ - **Sidebar row badges** β€” the same status mark floated to the right of each workspace row (`Project 1 ●`), with a linked worktree's name beside it (`Project 2 hotfix-tree 🌳`). Requires the `sidebar.workspaces.row` seam; without it the plugin degrades to chip-only and logs what's active to the browser console.
7
7
 
8
- Event-driven freshness: working trees are watched and updates push over SSE β€” badges flip within ~1s of any commit, stage, or edit. One `git status --porcelain=v2` call per sample; the API only answers registered workspace paths.
8
+ The leading **mark** is identical on both surfaces: a circle filled with the status colour, or a **tree** when the workspace is a linked `git worktree`. (`●`/`🌳` stand in for it here β€” it is a drawn SVG so that it can take the theme's status colours.)
9
+
10
+ Event-driven freshness: working trees are watched and updates push over SSE β€” badges flip within ~1s of any commit, stage, or edit. One `git status --porcelain=v2` call per sample. The status API takes **no path**: the caller passes a session id (chip) or workspace id (row) and the server resolves the directory itself.
9
11
 
10
12
  ## Install
11
13
 
@@ -17,6 +19,10 @@ Restart the web process, refresh the browser. Requires Node β‰₯ 20 and `git` on
17
19
 
18
20
  Seam details: [github.com/Kevin-McIsaac/dsh-workspace-git-badge](https://github.com/Kevin-McIsaac/dsh-workspace-git-badge) (SEAM.md / PR.md).
19
21
 
22
+ ## Changelog
23
+
24
+ See [CHANGELOG.md](CHANGELOG.md).
25
+
20
26
  ## License
21
27
 
22
28
  MIT