@aron98/load-lock 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +115 -2
- package/demo/demo.sh +112 -0
- package/demo/sim-agent.mjs +149 -0
- package/dist/adapters/opencode/plugin.d.ts +18 -0
- package/dist/adapters/opencode/plugin.js +301 -0
- package/dist/cli/hook.d.ts +1 -0
- package/dist/cli/hook.js +141 -0
- package/dist/cli/install.d.ts +14 -0
- package/dist/cli/install.js +377 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +461 -0
- package/dist/core/daemon-main.d.ts +2 -0
- package/dist/core/daemon-main.js +27 -0
- package/dist/core/daemon.d.ts +79 -0
- package/dist/core/daemon.js +526 -0
- package/dist/core/persist.d.ts +11 -0
- package/dist/core/persist.js +66 -0
- package/dist/core/state.d.ts +203 -0
- package/dist/core/state.js +909 -0
- package/dist/core/sysinfo.d.ts +2 -0
- package/dist/core/sysinfo.js +123 -0
- package/dist/mcp/main.d.ts +2 -0
- package/dist/mcp/main.js +4 -0
- package/dist/mcp/server.d.ts +27 -0
- package/dist/mcp/server.js +352 -0
- package/dist/policy/text.d.ts +30 -0
- package/dist/policy/text.js +58 -0
- package/dist/shared/client.d.ts +30 -0
- package/dist/shared/client.js +227 -0
- package/dist/shared/protocol.d.ts +333 -0
- package/dist/shared/protocol.js +22 -0
- package/dist/test/core.test.d.ts +1 -0
- package/dist/test/core.test.js +281 -0
- package/dist/test/daemon.test.d.ts +1 -0
- package/dist/test/daemon.test.js +384 -0
- package/dist/test/hook.test.d.ts +1 -0
- package/dist/test/hook.test.js +126 -0
- package/dist/test/mcp.test.d.ts +1 -0
- package/dist/test/mcp.test.js +139 -0
- package/dist/test/run-helper.test.d.ts +1 -0
- package/dist/test/run-helper.test.js +84 -0
- package/dist/test/testutil.d.ts +27 -0
- package/dist/test/testutil.js +111 -0
- package/docs/ADAPTERS.md +118 -0
- package/docs/AGENT_GUIDE.md +142 -0
- package/docs/ARCHITECTURE.md +195 -0
- package/docs/COMPATIBILITY.md +116 -0
- package/docs/OPERATIONS.md +126 -0
- package/package.json +102 -4
- package/skills/load-lock/SKILL.md +73 -0
package/package.json
CHANGED
|
@@ -1,6 +1,104 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aron98/load-lock",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Machine-wide counting semaphore coordinating resource-intensive work across parallel coding agents (OpenCode, Codex CLI, Claude Code, ...)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/aron98/load-lock.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/aron98/load-lock/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/aron98/load-lock#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"coding-agent",
|
|
17
|
+
"agent-coordination",
|
|
18
|
+
"mcp",
|
|
19
|
+
"opencode",
|
|
20
|
+
"codex",
|
|
21
|
+
"claude-code",
|
|
22
|
+
"semaphore",
|
|
23
|
+
"resource-management",
|
|
24
|
+
"build"
|
|
25
|
+
],
|
|
26
|
+
"bin": {
|
|
27
|
+
"load-lock": "dist/cli/main.js",
|
|
28
|
+
"load-lock-mcp": "dist/mcp/main.js"
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/adapters/opencode/plugin.d.ts",
|
|
33
|
+
"default": "./dist/adapters/opencode/plugin.js"
|
|
34
|
+
},
|
|
35
|
+
"./shared/client": {
|
|
36
|
+
"types": "./dist/shared/client.d.ts",
|
|
37
|
+
"default": "./dist/shared/client.js"
|
|
38
|
+
},
|
|
39
|
+
"./shared/client.js": {
|
|
40
|
+
"types": "./dist/shared/client.d.ts",
|
|
41
|
+
"default": "./dist/shared/client.js"
|
|
42
|
+
},
|
|
43
|
+
"./shared/protocol": {
|
|
44
|
+
"types": "./dist/shared/protocol.d.ts",
|
|
45
|
+
"default": "./dist/shared/protocol.js"
|
|
46
|
+
},
|
|
47
|
+
"./shared/protocol.js": {
|
|
48
|
+
"types": "./dist/shared/protocol.d.ts",
|
|
49
|
+
"default": "./dist/shared/protocol.js"
|
|
50
|
+
},
|
|
51
|
+
"./policy/text": {
|
|
52
|
+
"types": "./dist/policy/text.d.ts",
|
|
53
|
+
"default": "./dist/policy/text.js"
|
|
54
|
+
},
|
|
55
|
+
"./policy/text.js": {
|
|
56
|
+
"types": "./dist/policy/text.d.ts",
|
|
57
|
+
"default": "./dist/policy/text.js"
|
|
58
|
+
},
|
|
59
|
+
"./mcp/server": {
|
|
60
|
+
"types": "./dist/mcp/server.d.ts",
|
|
61
|
+
"default": "./dist/mcp/server.js"
|
|
62
|
+
},
|
|
63
|
+
"./mcp/server.js": {
|
|
64
|
+
"types": "./dist/mcp/server.d.ts",
|
|
65
|
+
"default": "./dist/mcp/server.js"
|
|
66
|
+
},
|
|
67
|
+
"./core/daemon": {
|
|
68
|
+
"types": "./dist/core/daemon.d.ts",
|
|
69
|
+
"default": "./dist/core/daemon.js"
|
|
70
|
+
},
|
|
71
|
+
"./core/daemon.js": {
|
|
72
|
+
"types": "./dist/core/daemon.d.ts",
|
|
73
|
+
"default": "./dist/core/daemon.js"
|
|
74
|
+
},
|
|
75
|
+
"./dist/*": "./dist/*"
|
|
76
|
+
},
|
|
77
|
+
"files": [
|
|
78
|
+
"dist",
|
|
79
|
+
"docs",
|
|
80
|
+
"skills",
|
|
81
|
+
"demo",
|
|
82
|
+
"README.md"
|
|
83
|
+
],
|
|
84
|
+
"scripts": {
|
|
85
|
+
"build": "tsc -p tsconfig.json",
|
|
86
|
+
"test": "tsc -p tsconfig.json && node --test dist/test/*.test.js",
|
|
87
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
88
|
+
},
|
|
89
|
+
"engines": {
|
|
90
|
+
"node": ">=20"
|
|
91
|
+
},
|
|
92
|
+
"publishConfig": {
|
|
93
|
+
"access": "public"
|
|
94
|
+
},
|
|
95
|
+
"dependencies": {
|
|
96
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
97
|
+
"@opencode/plugin": "^2.0.29",
|
|
98
|
+
"zod": "^3.24.0"
|
|
99
|
+
},
|
|
100
|
+
"devDependencies": {
|
|
101
|
+
"@types/node": "^24.0.0",
|
|
102
|
+
"typescript": "^5.9.0"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# load-lock: machine-wide heavy-work coordination
|
|
2
|
+
|
|
3
|
+
All coding agents on this machine (any harness, any repo/worktree) share one
|
|
4
|
+
small budget of heavy-work permits. Before resource-intensive local work, a
|
|
5
|
+
polite agent acquires a permit; everyone else keeps doing light work until an
|
|
6
|
+
offer arrives.
|
|
7
|
+
|
|
8
|
+
## When to use it
|
|
9
|
+
|
|
10
|
+
**Heavy** (acquire first): full builds, `cargo build`/`npm run build`, whole
|
|
11
|
+
test suites, codegen over the whole repo, bulk compilation, anything that
|
|
12
|
+
pins several cores or gobs of RAM for minutes.
|
|
13
|
+
|
|
14
|
+
**Light** (no permit): file edits, greps, small targeted tests, single-file
|
|
15
|
+
checks, reading, planning, git operations, anything sub-second or trivial.
|
|
16
|
+
|
|
17
|
+
Judge by *expected* CPU/memory/IO impact, not by command name. When unsure,
|
|
18
|
+
`inspect_machine` shows real capacity (incl. container limits) and pressure.
|
|
19
|
+
|
|
20
|
+
## Core flow
|
|
21
|
+
|
|
22
|
+
1. `acquire(reason, op_key)` — idempotent per `op_key`. Result is either
|
|
23
|
+
`granted` (you hold a permit; start heavy work now) or `queued`
|
|
24
|
+
(keep doing light work; an offer will arrive).
|
|
25
|
+
2. While queued: **continue useful light work**. Do not idle-poll.
|
|
26
|
+
3. When an offer arrives (harness wakeup or `pending`): `claim(offer_token)`
|
|
27
|
+
inside the claim window (default 30s). Expired offers are rejected; your
|
|
28
|
+
request pauses until you `rejoin`.
|
|
29
|
+
4. Only after `granted`/`claim` succeeds, start the heavy work. Associate the
|
|
30
|
+
work with the permit:
|
|
31
|
+
- `run_with_permit(permit_token, command)` — best: spawns it, tracks the
|
|
32
|
+
process group, auto-releases on exit. Poll `job_status`; do NOT release
|
|
33
|
+
manually.
|
|
34
|
+
- `attach_pid(permit_token, pid)` — for a process you already started
|
|
35
|
+
(e.g. a background shell task). Auto-release when it exits.
|
|
36
|
+
- `mark_started(permit_token)` — last resort, no process association
|
|
37
|
+
(heartbeat lease applies instead).
|
|
38
|
+
5. `release(permit_token)` when done (idempotent), or `cancel(request_id)`
|
|
39
|
+
for queue entries you no longer need. Background jobs that outlive your
|
|
40
|
+
session keep their permit until the *process* exits — never release just
|
|
41
|
+
because a tool call returned a background-task id.
|
|
42
|
+
|
|
43
|
+
## Cleanup rules (why you still see permits released)
|
|
44
|
+
|
|
45
|
+
- Offers expire (short window) and never auto-recycle: a missed offer pauses
|
|
46
|
+
your request; `rejoin(request_id)` puts you back in the FIFO queue.
|
|
47
|
+
- Claimed-but-never-started permits are reclaimed at the start deadline
|
|
48
|
+
(default 120s) so a forgotten claim can't block the queue.
|
|
49
|
+
- Tracked jobs (run_with_permit/attach_pid) hold the permit exactly as long
|
|
50
|
+
as the process (group) lives; completion/failure releases automatically.
|
|
51
|
+
- Untracked (mark_started) permits are held under a heartbeat lease — if your
|
|
52
|
+
harness adapter disappears without a trace of the work, the lease reclaims
|
|
53
|
+
the permit. The tradeoff: a still-running but unobserved process could
|
|
54
|
+
overlap with new work. Prefer tracking.
|
|
55
|
+
- Never auto-released merely for being held a long time, if the work is
|
|
56
|
+
confirmed running.
|
|
57
|
+
|
|
58
|
+
## Machine awareness and config
|
|
59
|
+
|
|
60
|
+
- `inspect_machine` — cores, memory, cgroup/container limits, current pressure.
|
|
61
|
+
- `get_config` / `configure(expected_version, changes, reason)` — compare-and-swap
|
|
62
|
+
capacity and timing changes; read the version first, always explain why.
|
|
63
|
+
- `status` — machine-wide snapshot: holders, offers, queue.
|
|
64
|
+
- Anyone may adjust the budget; changes are persisted with rationale+author.
|
|
65
|
+
|
|
66
|
+
## Recovery
|
|
67
|
+
|
|
68
|
+
- `pending` — your own offers/queue/holds (check before giving up).
|
|
69
|
+
- `load-lock status` / `load-lock recover list` (human CLI) shows orphaned
|
|
70
|
+
permits with process liveness; `load-lock recover force-release --id` is the
|
|
71
|
+
explicit escape hatch (it prints holder/job info first).
|
|
72
|
+
- Daemon restart: running tracked permits survive (journaled); queued/offered
|
|
73
|
+
requests do not — re-`acquire`.
|