@aron98/load-lock 1.4.0 → 1.5.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.
@@ -102,6 +102,7 @@ uninstall — see issue #19); not live-verified inside the real CLIs.
102
102
  load-lock service status # hello + pid + daemon version
103
103
  load-lock service start # ensure running (idempotent); refusals name the conflicting pid
104
104
  load-lock service stop # SIGTERM → bounded wait → SIGKILL; verifies the socket is free
105
+ load-lock service restart # stop then start; both phases reported; failures name the phase
105
106
  ```
106
107
 
107
108
  `service stop` is honest, not fire-and-forget: it signals the daemon
@@ -110,6 +111,11 @@ socket still answers, and only reports success once the endpoint is
110
111
  verifiably free (stale `daemon.json`/socket files are cleaned up either way).
111
112
  `service start` refuses loudly when something else owns the endpoint —
112
113
  naming the conflicting pid — instead of a bare "did not come up within 10s".
114
+ `service restart` (since #38) composes the same two hardened phases and
115
+ reports both lines; a failure names the phase and reason
116
+ (`restart failed in the STOP phase: ...` / `... in the START phase ...`).
117
+ Stopping when nothing runs is not an error: the start phase simply spawns a
118
+ fresh daemon.
113
119
 
114
120
  ## Update lifecycle
115
121
 
@@ -139,6 +145,48 @@ code. The **version handshake** closes that gap (v1.3.0+):
139
145
  pending self-restart; `doctor` actively waits a few seconds to confirm the
140
146
  daemon came back on the new version.
141
147
 
148
+ ### `load-lock update` (since #38)
149
+
150
+ The update command checks npm for a newer version, classifies where *this*
151
+ copy lives **before touching anything**, and applies the update via the
152
+ detected package manager — or refuses honestly rather than guessing:
153
+
154
+ | this copy is... | `load-lock update` does |
155
+ | ------------------------------------- | ------------------------------------------------------------------ |
156
+ | bun global (`~/.bun/install/global/…`) | `bun i -g @aron98/load-lock@latest` |
157
+ | npm global (under `npm root -g`) | `npm i -g @aron98/load-lock@latest` |
158
+ | opencode's npm-cache copy | refuses — opencode manages this copy and refreshes it on its own schedule |
159
+ | a git checkout (nearest `.git`) | refuses — update it via your git workflow (`git pull` etc.) |
160
+ | anything else | refuses — "cannot tell how this copy was installed" |
161
+
162
+ After a successful apply it runs `service restart` explicitly, so the
163
+ daemon moves onto the new code immediately (the version handshake remains
164
+ the safety net, not the primary means). The package manager's output is
165
+ shown live. `--check` is a dry run that touches nothing and uses
166
+ script-friendly exit codes:
167
+
168
+ - exit `0` — up to date (or this copy is ahead of the registry)
169
+ - exit `1` — an update is available (including the honest refusals above:
170
+ the update *exists*, this copy just cannot self-apply it)
171
+ - exit `70` — failure (registry unreachable, package manager failed, or a
172
+ refusal in apply mode)
173
+
174
+ Reports are prose for humans and `--json` structured objects for agents
175
+ (`state`: `up-to-date` | `update-available` | `refused` | `updated`).
176
+
177
+ ### Adapter-init drift notice
178
+
179
+ The mirror problem is a *newer adapter copy* with an *older CLI on PATH*
180
+ (typically: opencode refreshed its private cache copy, the global install
181
+ was never updated). At init, the OpenCode plugin and the MCP server compare
182
+ their own version with the PATH-resolved CLI copy's `package.json` and
183
+ surface **one clear notice** naming the exact command (`load-lock update`),
184
+ on stderr and in the injected context / MCP `instructions`. Notify only —
185
+ **nothing is ever auto-installed**. Honest limitation: a harness session
186
+ that was already running keeps the *old* adapter code in memory until the
187
+ harness itself restarts, so the notice fires from the new copy at its init
188
+ — sessions started before a refresh see it on their next restart.
189
+
142
190
  Limits, stated honestly: the respawn re-runs the **same argv** — it picks up
143
191
  an updated copy only if the daemon's launching copy was updated *in place*
144
192
  (the normal `npm i -g` case). A daemon spawned from a different copy (e.g.
@@ -146,10 +194,61 @@ opencode's private cache copy) keeps running that copy's code after a
146
194
  restart; see the multi-copy notes in
147
195
  [COMPATIBILITY.md](COMPATIBILITY.md#multiple-copies-of-load-lock-opencode).
148
196
  Pre-1.3.0 daemons do not answer the handshake and never self-restart —
149
- `service stop` followed by any load-lock command moves them onto current
150
- code. The restart behavior itself is live-verified on Linux by the test
151
- suite (`src/test/update.test.ts`); the in-place-update path beyond
152
- same-argv respawn is inspected, not live-tested.
197
+ `service restart` (or `service stop` followed by any load-lock command)
198
+ moves them onto current code. The restart behavior itself is live-verified
199
+ on Linux by the test suite (`src/test/update.test.ts`); the in-place-update
200
+ path beyond same-argv respawn is inspected, not live-tested. The `update`
201
+ command's stop→start compositing, its refusals, `--check` exit codes, and
202
+ the adapter-init drift notice are live-verified on Linux by
203
+ `src/test/update-command.test.ts` — with a **stubbed npm on PATH and a
204
+ planted copy**; no test ever contacts the real npm registry or installs
205
+ anything, so the genuine `npm i -g` round trip is contract-tested, not
206
+ live-tested.
207
+
208
+ ### Plugin in-memory staleness (issue #39)
209
+
210
+ The daemon heals itself via the handshake, but the **opencode plugin does
211
+ not**: opencode loads plugin code **once per process at startup** and never
212
+ hot-reloads it. After `npm i -g @aron98/load-lock@latest` (or opencode
213
+ refreshing its plugin cache for *new* sessions), an already-running opencode
214
+ session keeps executing the **old copy in memory** — even though the panel
215
+ and dashboards reconnect to the daemon just fine. A stale in-memory plugin
216
+ is not a reconnect failure; restarting opencode is the only way to pick up
217
+ the new plugin code.
218
+
219
+ To make that visible instead of mysterious (v1.4.1+):
220
+
221
+ - The dashboards (TUI panel and `load-lock dashboard`) periodically compare
222
+ the running copy's version with the PATH-resolved `load-lock` install.
223
+ When the PATH CLI is newer, the panel shows
224
+ `note: plugin updated — restart opencode to pick up vX (this session
225
+ still runs vY; opencode does not hot-reload plugins)`; the CLI surface
226
+ says the running copy is stale instead.
227
+ - When the daemon itself restarts onto a **different version** (update
228
+ self-restart, or a manual restart of a newer install), the reconnect note
229
+ names it: `daemon restarted onto v1.5.0 (was v1.4.0; pid N); event
230
+ history re-synced`.
231
+ - If a harness pruned the running copy's plugin-cache directory mid-session
232
+ (making the on-disk daemon entry vanish), client auto-spawn falls back to
233
+ the PATH-installed CLI's daemon instead of failing with ENOENT forever.
234
+ - Recovery story: the daemon side is automatic (handshake self-restart);
235
+ the plugin side needs exactly one action — **restart opencode**. Until
236
+ then everything keeps working; only the plugin's code (and its notes)
237
+ is one version behind.
238
+
239
+ The reverse direction — a *newer* adapter copy with an *older* CLI on PATH —
240
+ is #38's [adapter-init drift notice](#adapter-init-drift-notice), above.
241
+
242
+ Verification split (honest): live-verified on Linux by
243
+ `src/test/reconnect.test.ts` against real daemons — the daemon-kill
244
+ auto-respawn reconnect (the H1 repro), the failed-initial-connect recovery,
245
+ and the vanished daemon entry not crashing the host (real ENOENT spawn
246
+ through the compiled client). Contract-tested — mock client / planted
247
+ fixture PATH layouts, no version is ever really upgraded in a test — the
248
+ "restarted onto vX (was vY)" note wording, install-drift detection and its
249
+ per-surface hints, and the `resolveDaemonEntry` fallback table. The genuine
250
+ `npm i -g` + opencode-cache-refresh round trip is inspected, not
251
+ live-tested.
153
252
 
154
253
  ## Configuration
155
254
 
@@ -240,6 +339,14 @@ companion to the main plugin, which is unchanged). Inside the opencode TUI:
240
339
  palette — is open, so Esc keeps cancelling the dialog instead of
241
340
  closing the panel underneath it.
242
341
 
342
+ The panel surfaces two kinds of operational notes inline (issue #39): a
343
+ `daemon restarted (onto vX)` line after the daemon restarts underneath the
344
+ session (event history is re-synced automatically), and — because opencode
345
+ never hot-reloads plugins — a `plugin updated — restart opencode to pick
346
+ up vX` hint whenever the PATH `load-lock` install is newer than the copy
347
+ this session is running. See "Plugin in-memory staleness" under
348
+ [Update lifecycle](#update-lifecycle).
349
+
243
350
  The open command deliberately ships with **no global key binding** (issue
244
351
  #28). Never bind unmodified single letters or letter sequences (the old
245
352
  `gl`) at global scope: while the keymap engine waits for the next key of a
@@ -697,9 +804,10 @@ unless you want the grants in your real profile):
697
804
  # GOTCHA 2: the machine's real daemon was still v1.1.0 — a daemon
698
805
  # started before v1.2.0 silently strips the unknown labelPrefix field
699
806
  # (zod), so prefixed labels disappear without any error. Upgrade and
700
- # restart: load-lock service stop && load-lock service start. This
701
- # verification deliberately
702
- # used its own daemon on a temp state dir.
807
+ # restart: load-lock service stop && load-lock service start (at
808
+ # verification time there was no `service restart` subcommand; it exists
809
+ # since issue #38 and is the shorter spelling today). This verification
810
+ # deliberately used its own daemon on a temp state dir.
703
811
  LOAD_LOCK_STATE_DIR=/tmp/ll-32-test node dist/cli/main.js service start
704
812
 
705
813
  # 1. install the grants (merge-only; prints exactly what it merged)
@@ -763,7 +871,7 @@ The two gotchas that shaped the installer:
763
871
  2. **Label prefixes need a current daemon on the host.** A daemon
764
872
  started before v1.2.0 strips the unknown `labelPrefix` field silently
765
873
  (zod); the dashboard shows the bare label with no error anywhere.
766
- `load-lock service stop && load-lock service start` after upgrading.
874
+ `load-lock service restart` after upgrading.
767
875
 
768
876
  What was NOT verified: omac's macOS seatbelt path (same profile schema,
769
877
  per omac's docs, but the verification machine is Linux) and omac's agent
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aron98/load-lock",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Machine-wide counting semaphore coordinating resource-intensive work across parallel coding agents (OpenCode, Codex CLI, Claude Code, ...)",
5
5
  "type": "module",
6
6
  "license": "MIT",