@aron98/load-lock 1.4.0 → 1.6.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/README.md +61 -14
- package/dist/adapters/opencode/plugin.js +15 -1
- package/dist/adapters/opencode/tui.js +7 -2
- package/dist/cli/copies.d.ts +3 -10
- package/dist/cli/copies.js +32 -44
- package/dist/cli/hook.js +48 -4
- package/dist/cli/install.d.ts +2 -0
- package/dist/cli/install.js +68 -14
- package/dist/cli/main.js +108 -211
- package/dist/cli/service.d.ts +56 -0
- package/dist/cli/service.js +261 -0
- package/dist/cli/setup.d.ts +131 -0
- package/dist/cli/setup.js +942 -0
- package/dist/cli/update.d.ts +57 -0
- package/dist/cli/update.js +287 -0
- package/dist/core/daemon.d.ts +29 -3
- package/dist/core/daemon.js +111 -11
- package/dist/core/persist.d.ts +3 -0
- package/dist/core/persist.js +49 -10
- package/dist/core/state.d.ts +9 -0
- package/dist/core/state.js +41 -3
- package/dist/dashboard/feed.d.ts +23 -0
- package/dist/dashboard/feed.js +64 -2
- package/dist/dashboard/frame.js +2 -0
- package/dist/dashboard/view.d.ts +11 -0
- package/dist/dashboard/view.js +13 -0
- package/dist/mcp/server.d.ts +11 -3
- package/dist/mcp/server.js +38 -6
- package/dist/policy/text.d.ts +13 -3
- package/dist/policy/text.js +14 -3
- package/dist/shared/agent-guide.d.ts +3 -0
- package/dist/shared/agent-guide.js +16 -0
- package/dist/shared/client.js +16 -4
- package/dist/shared/drift.d.ts +23 -0
- package/dist/shared/drift.js +95 -0
- package/dist/shared/identity.d.ts +51 -0
- package/dist/shared/identity.js +116 -0
- package/dist/shared/installs.d.ts +39 -0
- package/dist/shared/installs.js +101 -0
- package/dist/shared/pkginfo.d.ts +25 -0
- package/dist/shared/pkginfo.js +70 -0
- package/dist/shared/protocol.d.ts +22 -0
- package/dist/shared/protocol.js +15 -0
- package/dist/shared/runtimes.d.ts +41 -0
- package/dist/shared/runtimes.js +81 -0
- package/dist/shared/version.d.ts +6 -0
- package/dist/shared/version.js +9 -1
- package/dist/test/ace-mcp.test.d.ts +1 -0
- package/dist/test/ace-mcp.test.js +157 -0
- package/dist/test/ace-opencode.test.d.ts +1 -0
- package/dist/test/ace-opencode.test.js +287 -0
- package/dist/test/check-pins.test.d.ts +1 -0
- package/dist/test/check-pins.test.js +108 -0
- package/dist/test/compat.test.d.ts +1 -0
- package/dist/test/compat.test.js +190 -0
- package/dist/test/hook.test.js +179 -1
- package/dist/test/identity.test.d.ts +1 -0
- package/dist/test/identity.test.js +155 -0
- package/dist/test/install.test.js +135 -1
- package/dist/test/launcher.test.d.ts +1 -0
- package/dist/test/launcher.test.js +222 -0
- package/dist/test/mcp.test.js +76 -7
- package/dist/test/plugin.test.d.ts +1 -0
- package/dist/test/plugin.test.js +144 -0
- package/dist/test/reconnect.test.d.ts +1 -0
- package/dist/test/reconnect.test.js +389 -0
- package/dist/test/runtimes.test.d.ts +1 -0
- package/dist/test/runtimes.test.js +282 -0
- package/dist/test/setup.test.d.ts +1 -0
- package/dist/test/setup.test.js +354 -0
- package/dist/test/update-command.test.d.ts +1 -0
- package/dist/test/update-command.test.js +679 -0
- package/dist/test/update.test.js +33 -1
- package/docs/ADAPTERS.md +3 -2
- package/docs/AGENT_GUIDE.md +48 -2
- package/docs/ARCHITECTURE.md +122 -7
- package/docs/COMPATIBILITY.md +156 -13
- package/docs/OPERATIONS.md +407 -21
- package/package.json +3 -2
- package/skills/load-lock/SKILL.md +62 -0
package/docs/OPERATIONS.md
CHANGED
|
@@ -7,6 +7,49 @@ Windows has a named-pipe transport adapter — implemented and unit-tested on
|
|
|
7
7
|
Linux via platform injection, not yet verified on a Windows machine; see the
|
|
8
8
|
Windows section at the bottom).
|
|
9
9
|
|
|
10
|
+
### The install ladder
|
|
11
|
+
|
|
12
|
+
load-lock can be installed at several levels ("rungs"). Each rung works on
|
|
13
|
+
its own, and a higher rung only adds capability. Installing more than one,
|
|
14
|
+
in any order, is safe: there is still one daemon and one budget, one
|
|
15
|
+
identity per session, and duplicate hooks are dropped by the daemon (see
|
|
16
|
+
[ARCHITECTURE.md](ARCHITECTURE.md#three-layers-daemon-runtime-copies-harness-wiring)).
|
|
17
|
+
|
|
18
|
+
| Rung | What you install | Runtime it runs | What the agent gets |
|
|
19
|
+
| --- | --- | --- | --- |
|
|
20
|
+
| 0 | **Skill only** (`skills/load-lock/SKILL.md`) | the load-lock MCP tools if listed, else `load-lock` on PATH, else `npx -y @aron98/load-lock@<version>` (pinned in the skill) | the full permit lifecycle through the CLI (`run`, `wait`, `acquire` / `claim` / `release`, `cancel`); the first call starts the daemon. No idle wakeup, no policy injection, nothing after compaction |
|
|
21
|
+
| 1 | **MCP server** (`npx -y @aron98/load-lock@<version> mcp`) | the pinned npx copy | the tools; your offers in every load-lock tool result; `wait_for_turn` / `wait_for_completion` bounded sleeps; the policy in the MCP `instructions` where the harness shows them; the `agent_guide` tool |
|
|
22
|
+
| 2 | **Harness plugin**: Claude Code `load-lock-plugin`, OpenCode npm plugin `@aron98/load-lock` | Claude Code: the plugin launcher (PATH CLI if new enough, else a known copy, else pinned npx); OpenCode: its own plugin cache copy | everything the harness supports: tools, policy at session start that survives compaction, offer wakeups at turn ends and while idle |
|
|
23
|
+
| — | **`npm i -g` + `load-lock install --harness <h>`** | the global CLI | the same wiring as loose config files. The only route for harnesses without a plugin, and the escape hatch where plugins aren't allowed |
|
|
24
|
+
|
|
25
|
+
Each rung points at the next one once: the skill, the MCP `instructions`
|
|
26
|
+
and the session-start brief end with a one-line pointer to
|
|
27
|
+
[`load-lock setup`](#load-lock-setup-what-is-installed-where), which lists
|
|
28
|
+
what is installed where and converges it. The rung each harness can reach
|
|
29
|
+
is in [COMPATIBILITY.md](COMPATIBILITY.md#rung-per-harness).
|
|
30
|
+
|
|
31
|
+
How to install each rung:
|
|
32
|
+
|
|
33
|
+
- **Skill (0).** Through ACE (the `load-lock` skill artifact, see
|
|
34
|
+
[Installing via ACE](#installing-via-ace)), or copy `skills/load-lock/`
|
|
35
|
+
(shipped in the npm package and the repo) into your harness's skills
|
|
36
|
+
directory. `load-lock install --harness claude-code` also copies it to
|
|
37
|
+
`~/.claude/skills/load-lock/`.
|
|
38
|
+
- **MCP server (1).** Through ACE (the `ace/mcp` artifact), or register a
|
|
39
|
+
stdio server named `load-lock` with command `npx` and args
|
|
40
|
+
`["-y", "@aron98/load-lock@<version>", "mcp"]`, exactly what
|
|
41
|
+
`ace/mcp/mcp.json` declares. `load-lock mcp` is the same server as the
|
|
42
|
+
`load-lock-mcp` binary. With a global install,
|
|
43
|
+
`load-lock install --harness generic` prints a snippet that runs that
|
|
44
|
+
copy instead of npx.
|
|
45
|
+
- **Plugin (2).** Claude Code: see [Claude Code plugin](#claude-code-plugin).
|
|
46
|
+
OpenCode: add `@aron98/load-lock` to the `plugins` array of
|
|
47
|
+
`~/.config/opencode/opencode.json` (OpenCode downloads and updates it),
|
|
48
|
+
or let `load-lock install --harness opencode` register it.
|
|
49
|
+
- **Loose wiring.** The installers below.
|
|
50
|
+
|
|
51
|
+
### `npm i -g` + `load-lock install`
|
|
52
|
+
|
|
10
53
|
```sh
|
|
11
54
|
npm install -g @aron98/load-lock # or: npm install -g ./aron98-load-lock-<ver>.tgz
|
|
12
55
|
load-lock install --harness opencode
|
|
@@ -32,13 +75,23 @@ round-trips in `src/test/install.test.ts` for all three (an earlier matcher
|
|
|
32
75
|
never matched its own entries — duplicate installs and hooks that survived
|
|
33
76
|
uninstall — see issue #19); not live-verified inside the real CLIs.
|
|
34
77
|
|
|
35
|
-
- **opencode** —
|
|
36
|
-
`~/.config/opencode/
|
|
37
|
-
|
|
78
|
+
- **opencode** — registers the published package `@aron98/load-lock` in
|
|
79
|
+
the `plugins` array of `~/.config/opencode/opencode.json` (OpenCode
|
|
80
|
+
installs and updates it itself) and removes the legacy plugin stub
|
|
81
|
+
`~/.config/opencode/plugins/load-lock/` if an older install left one.
|
|
38
82
|
- **claude-code** — adds `mcpServers.load-lock` to `~/.claude.json`; hooks
|
|
39
83
|
(`SessionStart`, `Stop`, async `await-offer`) to `~/.claude/settings.json`;
|
|
40
84
|
a marked section to `~/.claude/CLAUDE.md`; copies the skill to
|
|
41
|
-
`~/.claude/skills/load-lock/`.
|
|
85
|
+
`~/.claude/skills/load-lock/`. A skill dir it didn't write (an ACE
|
|
86
|
+
install with an `.ace.json` stamp, or a symlink) is left alone by both
|
|
87
|
+
`install` and `uninstall`, which say so (#89). With the
|
|
88
|
+
[Claude Code plugin](#claude-code-plugin) enabled in
|
|
89
|
+
`~/.claude/settings.json` (`enabledPlugins["load-lock-plugin@…"]: true`),
|
|
90
|
+
`install` instead removes the loose MCP server, hooks and `CLAUDE.md`
|
|
91
|
+
section, reporting each step, and only installs the skill (the plugin
|
|
92
|
+
ships none); `--legacy` installs the loose wiring anyway. `uninstall`
|
|
93
|
+
removes loose entries only and names the `/plugin uninstall …` command
|
|
94
|
+
for the plugin (#62).
|
|
42
95
|
- **codex** — writes `~/.codex/hooks.json` (`SessionStart`, `SubagentStart`,
|
|
43
96
|
`Stop`), appends `[mcp_servers.load-lock]` to `~/.codex/config.toml` if
|
|
44
97
|
absent, adds a marked section to `~/.codex/AGENTS.md`. **Then run `/hooks`
|
|
@@ -83,6 +136,135 @@ uninstall — see issue #19); not live-verified inside the real CLIs.
|
|
|
83
136
|
|
|
84
137
|
`load-lock uninstall --harness <harness>` reverses any of these.
|
|
85
138
|
|
|
139
|
+
### `load-lock setup`: what is installed where
|
|
140
|
+
|
|
141
|
+
`load-lock setup [--project <dir>] [--json]` lists every load-lock
|
|
142
|
+
integration it finds, for every harness `install` supports (the project
|
|
143
|
+
defaults to the cwd). It is **read-only**: it never writes a file.
|
|
144
|
+
|
|
145
|
+
For each harness it reports:
|
|
146
|
+
|
|
147
|
+
- **what** it found: plugins (Claude Code `enabledPlugins` entries named
|
|
148
|
+
`load-lock-plugin@…`, OpenCode `plugin` / `plugins` entries), MCP
|
|
149
|
+
registrations (user and project scope, including ACE's `.mcp.json`
|
|
150
|
+
`servers` patch), loose hooks, policy sections / rule files, skill
|
|
151
|
+
copies, and omac sandbox grants;
|
|
152
|
+
- **who** wrote each entry: `installer` (the exact shape `load-lock
|
|
153
|
+
install` writes), `harness` (the harness's own plugin state), `ace`
|
|
154
|
+
(an `.ace.json` stamp sits next to it), or `other` (manual, npx, …);
|
|
155
|
+
- **which copy** an entry runs, and whether that copy is gone (stale);
|
|
156
|
+
- the **rung**: `plugin`, `loose` (`load-lock install` wiring), `MCP only`,
|
|
157
|
+
`skill only`, or `not installed`;
|
|
158
|
+
- **findings**: loose hooks next to an active plugin, the MCP server
|
|
159
|
+
registered more than once (a Claude Code plugin's bundled server counts),
|
|
160
|
+
hooks from more than one copy, duplicate hook entries, several skill
|
|
161
|
+
copies, stale entries, disabled plugins.
|
|
162
|
+
|
|
163
|
+
It ends with the runtime copies: those on disk (as in `doctor`), the copy
|
|
164
|
+
the daemon runs (`daemon.json`), and the copies the daemon has seen.
|
|
165
|
+
|
|
166
|
+
An ACE-installed Claude plugin (unzipped into `.claude/skills/<name>/`) is
|
|
167
|
+
listed but not counted as an active plugin: whether Claude Code loads it
|
|
168
|
+
there is not verified yet (#63).
|
|
169
|
+
|
|
170
|
+
#### Converging: `load-lock setup --yes`
|
|
171
|
+
|
|
172
|
+
After the inventory, `setup` prints a **plan** that brings every harness
|
|
173
|
+
that already has some load-lock wiring to the recommended layout: the
|
|
174
|
+
highest rung available, exactly one of each. Without `--yes` it is a dry
|
|
175
|
+
run. `--yes` applies it (`--json` adds `plan` and `applied`). Harnesses with
|
|
176
|
+
nothing installed are left alone; the plan names them once.
|
|
177
|
+
|
|
178
|
+
The plan only uses moves the installers can reverse:
|
|
179
|
+
|
|
180
|
+
| Situation | Step |
|
|
181
|
+
|---|---|
|
|
182
|
+
| Claude Code plugin enabled **and** loose `install` wiring | `remove-loose`: drop the loose MCP server, hooks and `CLAUDE.md` section (the plugin provides them). The skill stays: the plugin ships none. Undo: `load-lock install --harness claude-code --legacy`. |
|
|
183
|
+
| Installer wiring incomplete, pointing at a missing copy, or hooks from several copies / duplicated | `install`: remove the installer-shaped hooks of every copy (and a stale codex `[mcp_servers.load-lock]` block), then `load-lock install --harness <h>` from the running copy |
|
|
184
|
+
| Only an MCP registration or only the skill | `install` (moves up to the installer's rung) |
|
|
185
|
+
| Anything not written by `load-lock install` (plugin state, ACE entries, manual / npx entries, extra skill copies) | `manual`: printed with what to remove or run (`/plugin uninstall …`), never applied |
|
|
186
|
+
|
|
187
|
+
Guards, each turning an `install` into a `manual` step:
|
|
188
|
+
|
|
189
|
+
- a foreign entry sits in the slot `install` would overwrite (e.g. a
|
|
190
|
+
hand-written `mcpServers.load-lock`);
|
|
191
|
+
- the Claude Code skill directory is managed by ACE (`.ace.json`);
|
|
192
|
+
- the running copy lives in the npx or opencode cache, which npm may prune
|
|
193
|
+
(install globally first);
|
|
194
|
+
- `cursor` / `roo-code` write into the project, so they are only refreshed
|
|
195
|
+
when the project already has their installer wiring.
|
|
196
|
+
|
|
197
|
+
`load-lock uninstall --harness <h>` still removes everything load-lock
|
|
198
|
+
added, after a converge as before it. The MCP instructions and the
|
|
199
|
+
session-start brief end with a one-line pointer to `load-lock setup`.
|
|
200
|
+
|
|
201
|
+
### Claude Code plugin
|
|
202
|
+
|
|
203
|
+
Instead of `install --harness claude-code`, Claude Code can load load-lock
|
|
204
|
+
as a plugin (issue #61). The repo root is a marketplace
|
|
205
|
+
(`.claude-plugin/marketplace.json`) listing one plugin, `load-lock-plugin`,
|
|
206
|
+
in `plugins/claude-code/`:
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
/plugin marketplace add aron98/load-lock
|
|
210
|
+
/plugin install load-lock-plugin@load-lock
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- **MCP server** `load-lock`, declared inline in `plugin.json` and started
|
|
214
|
+
through the launcher (`--bin load-lock-mcp`). Tools are named
|
|
215
|
+
`mcp__plugin_load-lock-plugin_load-lock__<tool>`.
|
|
216
|
+
- **Hooks** in `hooks/hooks.json`, the same three as the loose install:
|
|
217
|
+
`SessionStart` (policy + machine summary), `Stop` (offer continuation)
|
|
218
|
+
and the `async` + `asyncRewake` `await-offer` watcher. Each runs in shell
|
|
219
|
+
form, one command string `node "${CLAUDE_PLUGIN_ROOT}/scripts/load-lock.mjs"
|
|
220
|
+
hook claude-code <event>` with the root quoted (#63). Shell form is the
|
|
221
|
+
long-documented hook shape; exec form (`command` + `args`) has no
|
|
222
|
+
documented minimum Claude Code version. The string has no shell
|
|
223
|
+
metacharacters, so ACE's `plugin_hooks_command_safe` check passes.
|
|
224
|
+
- **No skill and no CLAUDE.md section.** The policy arrives through the
|
|
225
|
+
SessionStart hook (re-fired after compaction), and the full guide through
|
|
226
|
+
the `agent_guide` MCP tool. Install the `load-lock` skill separately if
|
|
227
|
+
wanted. The plugin is not named `load-lock`, so it doesn't clash with the
|
|
228
|
+
skill on ACE (#64).
|
|
229
|
+
- **Runtime**: the plugin ships no `dist/`. See [Plugin launcher](#plugin-launcher)
|
|
230
|
+
for how hooks and the MCP server find one.
|
|
231
|
+
- `plugin.json` `version` is pinned to `package.json` by `check-pins`. A
|
|
232
|
+
pinned version keeps users on it until the next release changes it.
|
|
233
|
+
|
|
234
|
+
Combining the plugin with a loose `install --harness claude-code` is safe
|
|
235
|
+
(one session identity, duplicate hooks dropped by the daemon, #54), but
|
|
236
|
+
registers two MCP servers; keep one. Once the plugin is enabled, re-running
|
|
237
|
+
`load-lock install --harness claude-code` (or `load-lock setup --yes`) moves
|
|
238
|
+
the loose MCP server, hooks and `CLAUDE.md` section out (`--legacy` keeps
|
|
239
|
+
them), and `load-lock doctor` reports the mode (`claudeCodeMode`: `plugin`,
|
|
240
|
+
`loose`, `both` or `none`) and warns on `both`.
|
|
241
|
+
|
|
242
|
+
### Installing via ACE
|
|
243
|
+
|
|
244
|
+
[ACE](https://github.com/TNG/ace) imports artifacts from this repository.
|
|
245
|
+
ACE does not edit harness config itself: its `install_artifact` tool hands
|
|
246
|
+
the agent a download plus instructions, and the agent unpacks and wires it
|
|
247
|
+
(findings in issue #64). This repo provides one artifact per folder:
|
|
248
|
+
|
|
249
|
+
| Folder | ACE kind | What installing it gives you |
|
|
250
|
+
| --- | --- | --- |
|
|
251
|
+
| `skills/load-lock/` | skill `load-lock` | rung 0; unpacked into the harness's skills directory (e.g. `.claude/skills/load-lock/`) |
|
|
252
|
+
| `ace/mcp/` | MCP server `load-lock` (local, stdio) | rung 1; ACE returns a patch with `npx -y @aron98/load-lock@<version> mcp` for the agent to merge into the MCP config (e.g. `.mcp.json`) |
|
|
253
|
+
| `plugins/claude-code/` | Claude Code plugin `load-lock-plugin` | rung 2; ACE unpacks Claude plugins as skills-directory plugins (`.claude/skills/load-lock-plugin/`), not through `/plugin install` |
|
|
254
|
+
| `ace/opencode/` | OpenCode plugin (pointer) | rung 2; install the published `@aron98/load-lock` into `opencode.json` as above, not the folder (see [COMPATIBILITY.md](COMPATIBILITY.md#ace-listing-opencode-issue-79)) |
|
|
255
|
+
|
|
256
|
+
Install the MCP server **or** the Claude Code plugin, not both: the plugin
|
|
257
|
+
already bundles the server (two registrations are harmless, just
|
|
258
|
+
redundant). The plugin is not named `load-lock`, so it doesn't unpack into
|
|
259
|
+
the skill's directory. ACE updates what it installed (it writes an
|
|
260
|
+
`.ace.json` stamp next to each bundle); `load-lock setup` reports those
|
|
261
|
+
entries as `ace` and never rewrites them.
|
|
262
|
+
|
|
263
|
+
Not yet verified (#63, #66): whether Claude Code loads a plugin from a
|
|
264
|
+
skills directory (and resolves `${CLAUDE_PLUGIN_ROOT}` there), and the ACE
|
|
265
|
+
install end to end on a clean machine. Until then `load-lock setup` lists an
|
|
266
|
+
ACE-installed Claude plugin but does not count it as an active plugin.
|
|
267
|
+
|
|
86
268
|
## The daemon
|
|
87
269
|
|
|
88
270
|
- State dir: `~/.local/state/load-lock` (Windows: `%LOCALAPPDATA%\load-lock`;
|
|
@@ -96,12 +278,23 @@ uninstall — see issue #19); not live-verified inside the real CLIs.
|
|
|
96
278
|
- Also honored by the shared client: `LOAD_LOCK_LABEL_PREFIX` (prefix for
|
|
97
279
|
holder labels; see Sandboxes).
|
|
98
280
|
- Files: `config.json` (settings + history), `journal.json` (held permits +
|
|
99
|
-
queued requests), `daemon.json` (pid
|
|
281
|
+
queued requests), `daemon.json` (pid, plus since #52 the daemon's
|
|
282
|
+
`packageRoot` and `version`), `runtimes.json` (known runtime copies, see
|
|
283
|
+
below), `spawn.lock` (bootstrap race guard).
|
|
284
|
+
- **Known runtime copies** (since #52): every client envelope carries its
|
|
285
|
+
package root (`clientRoot`); the daemon validates it (a package.json
|
|
286
|
+
naming `@aron98/load-lock`; the version is read from disk, not trusted
|
|
287
|
+
from the client) and records `{path, version, lastSeen}` in
|
|
288
|
+
`runtimes.json`, deduped by real path, pruning copies whose path is gone.
|
|
289
|
+
Small entry points (skill, plugin launchers) can read `daemon.json` /
|
|
290
|
+
`runtimes.json` to find an existing runtime without npx; `doctor` lists
|
|
291
|
+
them as `daemonRuntime` and `knownRuntimes`.
|
|
100
292
|
|
|
101
293
|
```sh
|
|
102
294
|
load-lock service status # hello + pid + daemon version
|
|
103
295
|
load-lock service start # ensure running (idempotent); refusals name the conflicting pid
|
|
104
296
|
load-lock service stop # SIGTERM → bounded wait → SIGKILL; verifies the socket is free
|
|
297
|
+
load-lock service restart # stop then start; both phases reported; failures name the phase
|
|
105
298
|
```
|
|
106
299
|
|
|
107
300
|
`service stop` is honest, not fire-and-forget: it signals the daemon
|
|
@@ -110,6 +303,64 @@ socket still answers, and only reports success once the endpoint is
|
|
|
110
303
|
verifiably free (stale `daemon.json`/socket files are cleaned up either way).
|
|
111
304
|
`service start` refuses loudly when something else owns the endpoint —
|
|
112
305
|
naming the conflicting pid — instead of a bare "did not come up within 10s".
|
|
306
|
+
`service restart` (since #38) composes the same two hardened phases and
|
|
307
|
+
reports both lines; a failure names the phase and reason
|
|
308
|
+
(`restart failed in the STOP phase: ...` / `... in the START phase ...`).
|
|
309
|
+
Stopping when nothing runs is not an error: the start phase simply spawns a
|
|
310
|
+
fresh daemon.
|
|
311
|
+
|
|
312
|
+
## Plugin launcher
|
|
313
|
+
|
|
314
|
+
Plugin and ACE bundles are git folders without `dist/`, so their hooks and
|
|
315
|
+
MCP server go through a committed, dependency-free launcher,
|
|
316
|
+
`plugins/claude-code/scripts/load-lock.mjs` (issue #60; plain ESM, Node
|
|
317
|
+
built-ins only, no build step):
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
node plugins/claude-code/scripts/load-lock.mjs hook claude-code stop # any `load-lock` CLI args
|
|
321
|
+
node plugins/claude-code/scripts/load-lock.mjs --bin load-lock-mcp # the MCP server
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
It runs the first runtime it finds:
|
|
325
|
+
|
|
326
|
+
1. `load-lock` on PATH, when its version is at least the launcher's pin;
|
|
327
|
+
2. else the newest known copy at least the pin from the state dir
|
|
328
|
+
(`runtimes.json`, `daemon.json`'s `packageRoot`; see #52 above);
|
|
329
|
+
3. else `npx --yes --package=@aron98/load-lock@<pin> -- <bin>`.
|
|
330
|
+
|
|
331
|
+
Branches 1 and 2 run the copy's compiled entry (`dist/cli/main.js` /
|
|
332
|
+
`dist/mcp/main.js`) with the launcher's own `node`, so no shell and no
|
|
333
|
+
`.cmd` shim is involved. For npx it prefers the `npx-cli.js` shipped next to
|
|
334
|
+
`node`, falling back to `npx` (POSIX) or `npx.cmd` through a shell
|
|
335
|
+
(Windows). The pin is kept equal to `package.json` by `check-pins`.
|
|
336
|
+
|
|
337
|
+
Hook mode (`hook ...`) never breaks or stalls a session:
|
|
338
|
+
|
|
339
|
+
- npx runs with `--offline` (npm cache only), so a cold cache fails in
|
|
340
|
+
well under a second instead of downloading. The MCP server's npx start
|
|
341
|
+
is online and warms the cache for later hooks.
|
|
342
|
+
- The run is bounded by `LOAD_LOCK_LAUNCHER_TIMEOUT_MS` (default 15000;
|
|
343
|
+
the process group is killed). Not applied to the long-lived
|
|
344
|
+
`await-offer` watcher.
|
|
345
|
+
- Any failure exits 0; exit 2 passes through (the `asyncRewake` wakeup).
|
|
346
|
+
A failed SessionStart that printed nothing prints one install hint
|
|
347
|
+
instead of the policy.
|
|
348
|
+
|
|
349
|
+
The MCP server mode passes the runtime's exit code through and reports a
|
|
350
|
+
missing runtime on stderr. `LOAD_LOCK_LAUNCHER_DEBUG=1` prints the chosen
|
|
351
|
+
branch to stderr.
|
|
352
|
+
|
|
353
|
+
**Measured latency (PATH branch).** On a Linux box (Node 22, 32 cores, warm
|
|
354
|
+
file cache, daemon running), median over 20 runs:
|
|
355
|
+
|
|
356
|
+
| Hook | Direct `node dist/cli/main.js` | Through the launcher |
|
|
357
|
+
|---|---|---|
|
|
358
|
+
| `hook claude-code stop` | 31 ms | 52 ms |
|
|
359
|
+
| `hook claude-code session-start` | 32 ms | 52 ms |
|
|
360
|
+
|
|
361
|
+
So the launcher adds about 20 ms (one extra Node start plus PATH/package.json
|
|
362
|
+
lookups). A warm npx (`--offline`) run adds about 0.2 s; a cold one fails in
|
|
363
|
+
about 0.2 s.
|
|
113
364
|
|
|
114
365
|
## Update lifecycle
|
|
115
366
|
|
|
@@ -121,9 +372,13 @@ code. The **version handshake** closes that gap (v1.3.0+):
|
|
|
121
372
|
carry `daemonVersion` (the package version of the code the daemon runs).
|
|
122
373
|
- When a **newer** client contacts an older daemon, the daemon finishes
|
|
123
374
|
answering, then **self-restarts**: it persists state + journal, drains all
|
|
124
|
-
connections, respawns
|
|
125
|
-
|
|
126
|
-
|
|
375
|
+
connections, respawns, and exits. Since #53 the respawn runs the **newer
|
|
376
|
+
client's copy** when the envelope's `clientRoot` checks out (a
|
|
377
|
+
`@aron98/load-lock` package.json whose on-disk version equals the
|
|
378
|
+
claimed `clientVersion`, plus a `dist/core/daemon-main.js`), so a daemon
|
|
379
|
+
spawned by an old global CLI moves onto e.g. a newer npx copy. Without a
|
|
380
|
+
valid hint it falls back to the same argv (`process.execPath` + original
|
|
381
|
+
arguments, so it runs whatever is now on disk at the same path). The respawn inherits a `LOAD_LOCK_RESTARTED_FOR` marker so
|
|
127
382
|
repeated contact with the same version cannot cause a restart loop — one
|
|
128
383
|
respawn attempt per version.
|
|
129
384
|
- Clients that were connected see a disconnect and simply reconnect to the
|
|
@@ -139,17 +394,117 @@ code. The **version handshake** closes that gap (v1.3.0+):
|
|
|
139
394
|
pending self-restart; `doctor` actively waits a few seconds to confirm the
|
|
140
395
|
daemon came back on the new version.
|
|
141
396
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
397
|
+
### `load-lock update` (since #38)
|
|
398
|
+
|
|
399
|
+
The update command checks npm for a newer version, classifies where *this*
|
|
400
|
+
copy lives **before touching anything**, and applies the update via the
|
|
401
|
+
detected package manager — or refuses honestly rather than guessing:
|
|
402
|
+
|
|
403
|
+
| this copy is... | `load-lock update` does |
|
|
404
|
+
| ------------------------------------- | ------------------------------------------------------------------ |
|
|
405
|
+
| bun global (`~/.bun/install/global/…`) | `bun i -g @aron98/load-lock@latest` |
|
|
406
|
+
| npm global (under `npm root -g`) | `npm i -g @aron98/load-lock@latest` |
|
|
407
|
+
| opencode's npm-cache copy | refuses — opencode manages this copy and refreshes it on its own schedule |
|
|
408
|
+
| npx cache copy (`~/.npm/_npx/<hash>/…`, since #57) | refuses — it is whatever version the launching `npx -y @aron98/load-lock@<pin>` pins; bump that pin (MCP/skill config or ACE artifact) |
|
|
409
|
+
| a git checkout (nearest `.git`) | refuses — update it via your git workflow (`git pull` etc.) |
|
|
410
|
+
| anything else | refuses — "cannot tell how this copy was installed" |
|
|
411
|
+
|
|
412
|
+
After a successful apply it runs `service restart` explicitly, so the
|
|
413
|
+
daemon moves onto the new code immediately (the version handshake remains
|
|
414
|
+
the safety net, not the primary means). The package manager's output is
|
|
415
|
+
shown live. `--check` is a dry run that touches nothing and uses
|
|
416
|
+
script-friendly exit codes:
|
|
417
|
+
|
|
418
|
+
- exit `0` — up to date (or this copy is ahead of the registry)
|
|
419
|
+
- exit `1` — an update is available (including the honest refusals above:
|
|
420
|
+
the update *exists*, this copy just cannot self-apply it)
|
|
421
|
+
- exit `70` — failure (registry unreachable, package manager failed, or a
|
|
422
|
+
refusal in apply mode)
|
|
423
|
+
|
|
424
|
+
Reports are prose for humans and `--json` structured objects for agents
|
|
425
|
+
(`state`: `up-to-date` | `update-available` | `refused` | `updated`).
|
|
426
|
+
|
|
427
|
+
### Adapter-init drift notice
|
|
428
|
+
|
|
429
|
+
The mirror problem is a *newer adapter copy* with an *older CLI on PATH*
|
|
430
|
+
(typically: opencode refreshed its private cache copy, the global install
|
|
431
|
+
was never updated). At init, the OpenCode plugin and the MCP server compare
|
|
432
|
+
their own version with the PATH-resolved CLI copy's `package.json` and
|
|
433
|
+
surface **one clear notice** naming the exact command (`load-lock update`),
|
|
434
|
+
on stderr and in the injected context / MCP `instructions`. Notify only —
|
|
435
|
+
**nothing is ever auto-installed**. Honest limitation: a harness session
|
|
436
|
+
that was already running keeps the *old* adapter code in memory until the
|
|
437
|
+
harness itself restarts, so the notice fires from the new copy at its init
|
|
438
|
+
— sessions started before a refresh see it on their next restart.
|
|
439
|
+
The same check covers **npx-vs-PATH skew** (#57): an MCP server launched
|
|
440
|
+
via a pinned `npx -y @aron98/load-lock@<pin>` runs from the npx cache, and
|
|
441
|
+
when that copy is newer than the PATH CLI the notice names it as the npx
|
|
442
|
+
copy. (The reverse — a pin older than the PATH CLI — is not flagged here;
|
|
443
|
+
the daemon's version handshake already keeps the daemon on the newer code.)
|
|
444
|
+
|
|
445
|
+
Limits, stated honestly: the respawn moves onto the newer client's copy
|
|
446
|
+
only when that client sends a valid `clientRoot` (#52/#53). Clients older
|
|
447
|
+
than #52 don't, so the respawn re-runs the **same argv** and picks up an
|
|
448
|
+
updated copy only if the daemon's launching copy was updated *in place*
|
|
449
|
+
(the normal `npm i -g` case); `load-lock service restart` moves it onto
|
|
450
|
+
the current CLI copy. See the multi-copy notes in
|
|
147
451
|
[COMPATIBILITY.md](COMPATIBILITY.md#multiple-copies-of-load-lock-opencode).
|
|
148
452
|
Pre-1.3.0 daemons do not answer the handshake and never self-restart —
|
|
149
|
-
`service stop` followed by any load-lock command
|
|
150
|
-
code. The restart behavior itself is live-verified
|
|
151
|
-
suite (`src/test/update.test.ts`); the in-place-update
|
|
152
|
-
same-argv respawn is inspected, not live-tested.
|
|
453
|
+
`service restart` (or `service stop` followed by any load-lock command)
|
|
454
|
+
moves them onto current code. The restart behavior itself is live-verified
|
|
455
|
+
on Linux by the test suite (`src/test/update.test.ts`); the in-place-update
|
|
456
|
+
path beyond same-argv respawn is inspected, not live-tested. The `update`
|
|
457
|
+
command's stop→start compositing, its refusals, `--check` exit codes, and
|
|
458
|
+
the adapter-init drift notice are live-verified on Linux by
|
|
459
|
+
`src/test/update-command.test.ts` — with a **stubbed npm on PATH and a
|
|
460
|
+
planted copy**; no test ever contacts the real npm registry or installs
|
|
461
|
+
anything, so the genuine `npm i -g` round trip is contract-tested, not
|
|
462
|
+
live-tested.
|
|
463
|
+
|
|
464
|
+
### Plugin in-memory staleness (issue #39)
|
|
465
|
+
|
|
466
|
+
The daemon heals itself via the handshake, but the **opencode plugin does
|
|
467
|
+
not**: opencode loads plugin code **once per process at startup** and never
|
|
468
|
+
hot-reloads it. After `npm i -g @aron98/load-lock@latest` (or opencode
|
|
469
|
+
refreshing its plugin cache for *new* sessions), an already-running opencode
|
|
470
|
+
session keeps executing the **old copy in memory** — even though the panel
|
|
471
|
+
and dashboards reconnect to the daemon just fine. A stale in-memory plugin
|
|
472
|
+
is not a reconnect failure; restarting opencode is the only way to pick up
|
|
473
|
+
the new plugin code.
|
|
474
|
+
|
|
475
|
+
To make that visible instead of mysterious (v1.4.1+):
|
|
476
|
+
|
|
477
|
+
- The dashboards (TUI panel and `load-lock dashboard`) periodically compare
|
|
478
|
+
the running copy's version with the PATH-resolved `load-lock` install.
|
|
479
|
+
When the PATH CLI is newer, the panel shows
|
|
480
|
+
`note: plugin updated — restart opencode to pick up vX (this session
|
|
481
|
+
still runs vY; opencode does not hot-reload plugins)`; the CLI surface
|
|
482
|
+
says the running copy is stale instead.
|
|
483
|
+
- When the daemon itself restarts onto a **different version** (update
|
|
484
|
+
self-restart, or a manual restart of a newer install), the reconnect note
|
|
485
|
+
names it: `daemon restarted onto v1.5.0 (was v1.4.0; pid N); event
|
|
486
|
+
history re-synced`.
|
|
487
|
+
- If a harness pruned the running copy's plugin-cache directory mid-session
|
|
488
|
+
(making the on-disk daemon entry vanish), client auto-spawn falls back to
|
|
489
|
+
the PATH-installed CLI's daemon instead of failing with ENOENT forever.
|
|
490
|
+
- Recovery story: the daemon side is automatic (handshake self-restart);
|
|
491
|
+
the plugin side needs exactly one action — **restart opencode**. Until
|
|
492
|
+
then everything keeps working; only the plugin's code (and its notes)
|
|
493
|
+
is one version behind.
|
|
494
|
+
|
|
495
|
+
The reverse direction — a *newer* adapter copy with an *older* CLI on PATH —
|
|
496
|
+
is #38's [adapter-init drift notice](#adapter-init-drift-notice), above.
|
|
497
|
+
|
|
498
|
+
Verification split (honest): live-verified on Linux by
|
|
499
|
+
`src/test/reconnect.test.ts` against real daemons — the daemon-kill
|
|
500
|
+
auto-respawn reconnect (the H1 repro), the failed-initial-connect recovery,
|
|
501
|
+
and the vanished daemon entry not crashing the host (real ENOENT spawn
|
|
502
|
+
through the compiled client). Contract-tested — mock client / planted
|
|
503
|
+
fixture PATH layouts, no version is ever really upgraded in a test — the
|
|
504
|
+
"restarted onto vX (was vY)" note wording, install-drift detection and its
|
|
505
|
+
per-surface hints, and the `resolveDaemonEntry` fallback table. The genuine
|
|
506
|
+
`npm i -g` + opencode-cache-refresh round trip is inspected, not
|
|
507
|
+
live-tested.
|
|
153
508
|
|
|
154
509
|
## Configuration
|
|
155
510
|
|
|
@@ -240,6 +595,14 @@ companion to the main plugin, which is unchanged). Inside the opencode TUI:
|
|
|
240
595
|
palette — is open, so Esc keeps cancelling the dialog instead of
|
|
241
596
|
closing the panel underneath it.
|
|
242
597
|
|
|
598
|
+
The panel surfaces two kinds of operational notes inline (issue #39): a
|
|
599
|
+
`daemon restarted (onto vX)` line after the daemon restarts underneath the
|
|
600
|
+
session (event history is re-synced automatically), and — because opencode
|
|
601
|
+
never hot-reloads plugins — a `plugin updated — restart opencode to pick
|
|
602
|
+
up vX` hint whenever the PATH `load-lock` install is newer than the copy
|
|
603
|
+
this session is running. See "Plugin in-memory staleness" under
|
|
604
|
+
[Update lifecycle](#update-lifecycle).
|
|
605
|
+
|
|
243
606
|
The open command deliberately ships with **no global key binding** (issue
|
|
244
607
|
#28). Never bind unmodified single letters or letter sequences (the old
|
|
245
608
|
`gl`) at global scope: while the keymap engine waits for the next key of a
|
|
@@ -697,9 +1060,10 @@ unless you want the grants in your real profile):
|
|
|
697
1060
|
# GOTCHA 2: the machine's real daemon was still v1.1.0 — a daemon
|
|
698
1061
|
# started before v1.2.0 silently strips the unknown labelPrefix field
|
|
699
1062
|
# (zod), so prefixed labels disappear without any error. Upgrade and
|
|
700
|
-
# restart: load-lock service stop && load-lock service start
|
|
701
|
-
# verification
|
|
702
|
-
#
|
|
1063
|
+
# restart: load-lock service stop && load-lock service start (at
|
|
1064
|
+
# verification time there was no `service restart` subcommand; it exists
|
|
1065
|
+
# since issue #38 and is the shorter spelling today). This verification
|
|
1066
|
+
# deliberately used its own daemon on a temp state dir.
|
|
703
1067
|
LOAD_LOCK_STATE_DIR=/tmp/ll-32-test node dist/cli/main.js service start
|
|
704
1068
|
|
|
705
1069
|
# 1. install the grants (merge-only; prints exactly what it merged)
|
|
@@ -763,7 +1127,7 @@ The two gotchas that shaped the installer:
|
|
|
763
1127
|
2. **Label prefixes need a current daemon on the host.** A daemon
|
|
764
1128
|
started before v1.2.0 strips the unknown `labelPrefix` field silently
|
|
765
1129
|
(zod); the dashboard shows the bare label with no error anywhere.
|
|
766
|
-
`load-lock service
|
|
1130
|
+
`load-lock service restart` after upgrading.
|
|
767
1131
|
|
|
768
1132
|
What was NOT verified: omac's macOS seatbelt path (same profile schema,
|
|
769
1133
|
per omac's docs, but the verification machine is Linux) and omac's agent
|
|
@@ -942,3 +1306,25 @@ Caveats to watch for (unverified expectations):
|
|
|
942
1306
|
`LOAD_LOCK_SOCKET` at a different pipe name if that is a problem.
|
|
943
1307
|
- `inspect_machine` cgroup detection reads `/sys/fs/cgroup` and will
|
|
944
1308
|
degrade gracefully (`cgroupVersion: "none"`), reporting raw host numbers.
|
|
1309
|
+
|
|
1310
|
+
## Releasing (maintainers)
|
|
1311
|
+
|
|
1312
|
+
Publishing is automated: publishing a GitHub Release runs
|
|
1313
|
+
`.github/workflows/publish.yml` (npm Trusted Publishing; read its comments
|
|
1314
|
+
before changing the flow). The release commit itself is manual:
|
|
1315
|
+
|
|
1316
|
+
1. Bump `version` in `package.json` (and `package-lock.json`), e.g.
|
|
1317
|
+
`npm version 1.6.0 --no-git-tag-version`.
|
|
1318
|
+
2. Sync every pinned `@aron98/load-lock@<version>` to it:
|
|
1319
|
+
`npm run check-pins -- --write` (alias for
|
|
1320
|
+
`node scripts/check-pins.mjs --write`), then `npm run check-pins` must
|
|
1321
|
+
print `pins ok`. Commit the bump and the pin updates together.
|
|
1322
|
+
3. Merge, then publish a GitHub Release tagged `v<version>`.
|
|
1323
|
+
|
|
1324
|
+
Pinned files (the skill's npx fallback, ACE `ace/mcp/mcp.json` and
|
|
1325
|
+
`ace/opencode/package.json`, the Claude Code plugin's `plugin.json` and
|
|
1326
|
+
launcher) are declared in the
|
|
1327
|
+
`PINNED` list at the top of `scripts/check-pins.mjs`; add an entry whenever
|
|
1328
|
+
a new artifact pins a version. CI (`pins` job in `ci.yml`) fails on any
|
|
1329
|
+
pin that differs from `package.json`, on a declared file that is missing,
|
|
1330
|
+
and on a declared file that no longer contains a pin.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aron98/load-lock",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|
|
@@ -88,7 +88,8 @@
|
|
|
88
88
|
"scripts": {
|
|
89
89
|
"build": "tsc -p tsconfig.json",
|
|
90
90
|
"test": "tsc -p tsconfig.json && node --test dist/test/*.test.js",
|
|
91
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
91
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
92
|
+
"check-pins": "node scripts/check-pins.mjs"
|
|
92
93
|
},
|
|
93
94
|
"engines": {
|
|
94
95
|
"node": ">=20"
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: load-lock
|
|
3
|
+
description: >-
|
|
4
|
+
Teach parallel AI coding agents sharing one machine to take turns on heavy
|
|
5
|
+
work such as full builds and test suites. Agents acquire a machine-wide
|
|
6
|
+
permit before resource-intensive local work, keep doing light work while
|
|
7
|
+
queued FIFO, sleep honestly with bounded waits instead of polling, and
|
|
8
|
+
release the permit when done. Use when several agents on one machine risk
|
|
9
|
+
overloading it by starting heavy work in the same minute. If load-lock is
|
|
10
|
+
not installed, this skill runs it via `npx -y @aron98/load-lock@1.6.0`
|
|
11
|
+
(downloads the pinned npm package), and load-lock keeps its shared state in
|
|
12
|
+
~/.local/state/load-lock (a small per-user daemon started on first use).
|
|
13
|
+
---
|
|
14
|
+
|
|
1
15
|
# load-lock: machine-wide heavy-work coordination
|
|
2
16
|
|
|
3
17
|
All coding agents on this machine (any harness, any repo/worktree) share one
|
|
@@ -5,6 +19,54 @@ small budget of heavy-work permits. Before resource-intensive local work, a
|
|
|
5
19
|
polite agent acquires a permit; everyone else keeps doing light work until an
|
|
6
20
|
offer arrives.
|
|
7
21
|
|
|
22
|
+
## How to call load-lock
|
|
23
|
+
|
|
24
|
+
Use the first of these that works; they all talk to the same daemon:
|
|
25
|
+
|
|
26
|
+
1. **load-lock MCP tools** (`acquire`, `wait_for_turn`, `run_with_permit`,
|
|
27
|
+
...), if your harness lists them. The rest of this skill names them.
|
|
28
|
+
2. **`load-lock` on PATH**: check with `load-lock version`.
|
|
29
|
+
3. **Pinned npx**: `npx -y @aron98/load-lock@1.6.0 <command>` (Node >= 20).
|
|
30
|
+
It downloads that exact npm package once into the npx cache. Below,
|
|
31
|
+
read `load-lock` as this whole prefix. `npm install -g @aron98/load-lock`
|
|
32
|
+
makes it permanent.
|
|
33
|
+
|
|
34
|
+
The first call starts the daemon on its own (shared state in
|
|
35
|
+
`~/.local/state/load-lock`, or `$XDG_STATE_HOME/load-lock`); you never start
|
|
36
|
+
it yourself.
|
|
37
|
+
|
|
38
|
+
### CLI-only use (no MCP tools)
|
|
39
|
+
|
|
40
|
+
`load-lock help` lists every command. The safest flow is `load-lock run`,
|
|
41
|
+
which acquires a permit, runs the command, and releases it when the command
|
|
42
|
+
exits (success or failure):
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
load-lock run --reason "full test suite" --op-key test/all -- npm test
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- Exit code **68** with `queued at position N (request <id>)` means you are
|
|
49
|
+
not allowed to start yet. Keep doing light work. Out of light work? Sleep
|
|
50
|
+
honestly with `load-lock wait --id <request-id> --timeout 600000`: it
|
|
51
|
+
wakes you on your turn, claims the offer, and prints a `permit_token`.
|
|
52
|
+
Then `load-lock run --permit <permit_token> -- npm test`. A `wait` that
|
|
53
|
+
exits 68 again (still queued) is a checkpoint: call it again.
|
|
54
|
+
- No longer need a queued request: `load-lock cancel --id <request-id>`.
|
|
55
|
+
Your own offers, queue entries and paused requests: `load-lock pending`.
|
|
56
|
+
|
|
57
|
+
**Identity.** The daemon routes offers and `pending` per agent. The CLI
|
|
58
|
+
derives your agent id from your harness session (Claude Code and OpenCode
|
|
59
|
+
session env vars; on Linux, otherwise your harness process), so every call
|
|
60
|
+
from one session counts as one agent, and other sessions are other agents.
|
|
61
|
+
If `pending` does not show a request you made, pin the id yourself: set
|
|
62
|
+
`LOAD_LOCK_AGENT=<unique-name>` (or pass `--agent <unique-name>`) on every
|
|
63
|
+
call.
|
|
64
|
+
|
|
65
|
+
Skill-only use has no idle wakeup (nothing tells you an offer arrived while
|
|
66
|
+
you sit idle; use `load-lock wait`) and no policy reminder after context
|
|
67
|
+
compaction. To get both, install the harness integration once:
|
|
68
|
+
`load-lock setup` (or `load-lock install --harness <name>`).
|
|
69
|
+
|
|
8
70
|
## When to use it
|
|
9
71
|
|
|
10
72
|
**Heavy** (acquire first): full builds, `cargo build`/`npm run build`, whole
|