@nledford/omp-coord 0.0.0-stage → 0.2.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/.omp-plugin/marketplace.json +19 -0
- package/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.md +67 -2
- package/docs/README.md +20 -0
- package/docs/agent-slots.md +38 -0
- package/docs/commands.md +46 -0
- package/docs/configuration.md +31 -0
- package/docs/coordination.md +41 -0
- package/docs/event-log.md +47 -0
- package/docs/images/slot-held.png +0 -0
- package/docs/images/spawn-refused.png +0 -0
- package/docs/install.md +53 -0
- package/docs/queues.md +52 -0
- package/docs/versioning.md +43 -0
- package/examples/omp-coord.json +44 -0
- package/package.json +49 -4
- package/src/adapters/cli/cli.ts +346 -0
- package/src/adapters/omp/api.ts +95 -0
- package/src/adapters/omp/extension.ts +621 -0
- package/src/adapters/os/load.ts +7 -0
- package/src/adapters/sqlite/store.ts +499 -0
- package/src/adapters/text/report.ts +188 -0
- package/src/adapters/unix/beacon.ts +68 -0
- package/src/app/coordinator.ts +211 -0
- package/src/app/doctor.ts +100 -0
- package/src/app/job-runner.ts +146 -0
- package/src/app/ports.ts +39 -0
- package/src/config.ts +237 -0
- package/src/domain/agents.ts +266 -0
- package/src/domain/claims.ts +185 -0
- package/src/domain/commands.ts +56 -0
- package/src/domain/control.ts +33 -0
- package/src/domain/history.ts +108 -0
- package/src/domain/limits.ts +30 -0
- package/src/domain/messages.ts +64 -0
- package/src/domain/model.ts +232 -0
- package/src/domain/notes.ts +26 -0
- package/src/domain/ports.ts +54 -0
- package/src/domain/queues.ts +226 -0
- package/src/domain/sessions.ts +64 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "omp-coord",
|
|
3
|
+
"owner": { "name": "Nathaniel Ledford" },
|
|
4
|
+
"metadata": { "description": "Coordination for several omp sessions on one machine" },
|
|
5
|
+
"plugins": [
|
|
6
|
+
{
|
|
7
|
+
"name": "omp-coord",
|
|
8
|
+
"source": "./",
|
|
9
|
+
"description": "Shared subagent caps, build and test queues, claims, pause, a decision log and messages across omp sessions working in one project on one machine.",
|
|
10
|
+
"version": "0.2.0",
|
|
11
|
+
"author": { "name": "Nathaniel Ledford" },
|
|
12
|
+
"homepage": "https://github.com/nledford/omp-coord",
|
|
13
|
+
"repository": "https://github.com/nledford/omp-coord",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"keywords": ["agents", "coordination", "queues", "subagents"],
|
|
16
|
+
"category": "productivity"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to omp-coord, newest first. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) as described in [docs/versioning.md](docs/versioning.md).
|
|
4
|
+
|
|
5
|
+
Add each user-visible change under Unreleased as it lands, in one of: Added, Changed, Deprecated, Removed, Fixed, Security. Mark a change that breaks the contract with **Breaking:** and say what to do about it.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-10-26
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Each release is published as the npm package `@nledford/omp-coord` to GitHub Packages; see [docs/install.md](docs/install.md).
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **Breaking:** the package is now named `@nledford/omp-coord`, so omp lists and upgrades a git or package install under that name (`omp plugin upgrade @nledford/omp-coord`). Marketplace installs keep `omp-coord@omp-coord`.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- In a container whose first process never reaps orphans, a job stopped by its time limit no longer holds its slot for the whole 10-second grace period: exited processes left in its process group no longer count as running.
|
|
22
|
+
|
|
23
|
+
## [0.1.0] - 2026-10-26
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Shared subagent caps across omp sessions in one project, with pools, exempt agent types, limits on agents started by subagents, and a fair line: a refused session gets the next free slot, held for it for a few minutes.
|
|
28
|
+
- Build and test queues: `omp-coord run <queue> -- <command>` waits for a slot. Queues can be ordered `fifo`, `fair-share` or `priority`, with lanes, aging, duplicate keys (refuse or supersede), and time limits. The extension rewrites agents' bash commands that match `queue` rules, and refuses those that match `refuse` rules.
|
|
29
|
+
- Messages between sessions and to another session's subagent (`coord_send`, `/coord send`, `B/<agent id>`).
|
|
30
|
+
- Claims on branches, worktrees and PRs (`coord_claim`, `/coord claim`): other sessions' agents are refused git and gh commands that would change a claimed one.
|
|
31
|
+
- Pause and resume for a whole scope (`/coord pause`, `omp-coord pause`).
|
|
32
|
+
- Lower agent caps and queue slots while the machine's load average is high (`loadCaps`, `loadSlots`).
|
|
33
|
+
- A block of ports and a scratch folder per session and per agent (`resources`, `coord_resources`, `OMP_COORD_PORTS`, `OMP_COORD_SCRATCH`).
|
|
34
|
+
- A shared decision log (`coord_note`, `/coord note`, `omp-coord note`).
|
|
35
|
+
- Reports from the event log: `omp-coord interrupted` (agents a crashed or closed session left mid-run) and `omp-coord stats`; `omp-coord doctor [--fix]`.
|
|
36
|
+
- An SQLite event log per scope, with retention.
|
|
37
|
+
- Packaging as an omp plugin, with a marketplace catalog; queued commands run on omp's own Bun.
|
|
38
|
+
|
|
39
|
+
[Unreleased]: https://github.com/nledford/omp-coord/compare/v0.2.0...HEAD
|
|
40
|
+
[0.2.0]: https://github.com/nledford/omp-coord/compare/v0.1.0...v0.2.0
|
|
41
|
+
[0.1.0]: https://github.com/nledford/omp-coord/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nathaniel Ledford
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
-
#
|
|
1
|
+
# omp-coord
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An [omp](https://github.com/can1357/oh-my-pi) plugin, plus a small command-line runner, that lets several omp sessions on one machine, working in the same project, share one set of agent caps and build/test queues, and talk to each other.
|
|
4
|
+
|
|
5
|
+
- **Shared agent caps.** Subagents count against pools (for example 8 `work` and 2 `short` slots) shared by every session in the project. A spawn over the cap is refused through omp's spawn check, so the cap does not depend on agents remembering to take and free slots.
|
|
6
|
+
- **A fair line.** A refused session joins the line; the next free slot is held for it, and it is told so.
|
|
7
|
+
- **Subagents start only reviewers.** "No nested worker agents" is enforced rather than asked for.
|
|
8
|
+
- **Build and test queues.** `omp-coord run <queue> -- <command>` waits for one of the queue's slots, then runs the command, within an optional time limit. Agents' matching bash commands are rewritten to go through it.
|
|
9
|
+
- **Messages, claims, pause.** Sessions message each other and each other's subagents, claim branches, worktrees and PRs against other sessions' git commands, and pause the whole project at once.
|
|
10
|
+
- **Load-aware limits, ports and scratch folders, a decision log, reports** of waits, refusals and agents a crash left mid-run, and an SQLite event log.
|
|
11
|
+
|
|
12
|
+
Each session gets a letter and shows `coord B · work 5/8 · short 1/2` in omp's footer. Here session B's agents hold both `work` slots, so session A's spawn is refused, A goes first in line, and `/coord` shows the board:
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
omp plugin install "git+ssh://git@github.com/nledford/omp-coord.git"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then put a `.omp-coord.json` in the project's top folder:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"scope": "my-project",
|
|
27
|
+
"agents": { "pools": { "work": { "cap": 6 } }, "exempt": ["reviewer"] },
|
|
28
|
+
"queues": { "build": { "slots": 2, "order": "fair-share" } },
|
|
29
|
+
"commands": [{ "pattern": "(^|\\s)npm run build\\b", "action": "queue", "queue": "build" }]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Every omp session started in that folder or below now shares 6 agent slots, and agents' `npm run build` waits for one of 2 build slots. `/coord` shows the board; `omp-coord doctor` checks the setup. A fuller example: [`examples/omp-coord.json`](examples/omp-coord.json).
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
| | |
|
|
38
|
+
|---|---|
|
|
39
|
+
| [Install](docs/install.md) | Plugin install over SSH or the marketplace, updating, the `omp-coord` command. |
|
|
40
|
+
| [Configuration](docs/configuration.md) | Every `.omp-coord.json` field. |
|
|
41
|
+
| [Agent slots](docs/agent-slots.md) | How caps, the line, holds and nested agents work. |
|
|
42
|
+
| [Queues](docs/queues.md) | `omp-coord run`, order, lanes, time limits, agents' commands. |
|
|
43
|
+
| [Working across sessions](docs/coordination.md) | Messages, claims, pause, the decision log, ports, crash recovery. |
|
|
44
|
+
| [Commands](docs/commands.md) | `/coord`, agent tools, CLI, environment variables. |
|
|
45
|
+
| [Event log](docs/event-log.md) | The SQLite tables and events, with example queries. |
|
|
46
|
+
| [Versioning](docs/versioning.md) | What the version promises; cutting a release. |
|
|
47
|
+
|
|
48
|
+
[CHANGELOG.md](CHANGELOG.md) lists what each release changed.
|
|
49
|
+
|
|
50
|
+
## Limits
|
|
51
|
+
|
|
52
|
+
- The cap is an agreement between sessions, not a security boundary: if the shared board cannot be read, spawns are let through with an error notice.
|
|
53
|
+
- Sessions of one scope should read the same `.omp-coord.json`. Two different files under one scope (for example one per worktree) make sessions disagree.
|
|
54
|
+
- Claims match command text: a guard against mistakes, not a lock.
|
|
55
|
+
- A message is put into the other session's conversation as written. Text an agent copies into a message from an untrusted source reaches the other session too.
|
|
56
|
+
- `/tan` clones are subagents to omp, so they take slots from the default pool and fall under the rules for agents started by subagents.
|
|
57
|
+
- On one machine only: sessions find each other through a shared folder, not the network.
|
|
58
|
+
|
|
59
|
+
## Develop
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
just install
|
|
63
|
+
just fmt # format with Biome
|
|
64
|
+
just check # typecheck, lint, tests, version files agreeing
|
|
65
|
+
just # every workflow
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Code layout and conventions: [AGENTS.md](AGENTS.md). License: [MIT](LICENSE).
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# omp-coord documentation
|
|
2
|
+
|
|
3
|
+
Setting up:
|
|
4
|
+
|
|
5
|
+
- [Install](install.md): plugin install, updating, the `omp-coord` command.
|
|
6
|
+
- [Configuration](configuration.md): every `.omp-coord.json` field.
|
|
7
|
+
|
|
8
|
+
How it works:
|
|
9
|
+
|
|
10
|
+
- [Agent slots and the line](agent-slots.md): caps, holds, revivals, agents started by subagents.
|
|
11
|
+
- [Build and test queues](queues.md): `omp-coord run`, order, lanes, time limits, agents' commands.
|
|
12
|
+
- [Working across sessions](coordination.md): messages, claims, pause, the decision log, ports, crash recovery.
|
|
13
|
+
|
|
14
|
+
Reference:
|
|
15
|
+
|
|
16
|
+
- [Commands](commands.md): `/coord`, the agent tools, `omp-coord`, environment variables.
|
|
17
|
+
- [State and event log](event-log.md): the SQLite tables, event types, example queries.
|
|
18
|
+
- [Versioning and releases](versioning.md): what the version promises, and how releases are cut.
|
|
19
|
+
|
|
20
|
+
Contributing: [AGENTS.md](../AGENTS.md) covers the code layout and conventions.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Agent slots and the line
|
|
2
|
+
|
|
3
|
+
Subagents count against pools of slots (for example 8 `work` and 2 `short`) shared by every session in the scope. Pools, caps and agent types are set under `agents` in the [configuration](configuration.md).
|
|
4
|
+
|
|
5
|
+
## How slots are counted
|
|
6
|
+
|
|
7
|
+
| Event | Effect |
|
|
8
|
+
|---|---|
|
|
9
|
+
| omp's spawn check approves a subagent | It takes a slot at once (`starting`). |
|
|
10
|
+
| The subagent's run starts (`agent_start`) | The slot becomes `running`. |
|
|
11
|
+
| The subagent's run ends (`agent_end`: finished, failed or killed) | The slot frees. An idle or parked agent holds no slot. |
|
|
12
|
+
| An idle agent is revived (`write agent://…`) | It takes a slot again, even over the cap. omp does not run the spawn check for revivals, and running agents are never stopped; new spawns are refused until the total is under the cap. |
|
|
13
|
+
| An approved spawn never starts | Its slot frees after `agents.startTimeoutSeconds`. |
|
|
14
|
+
| A session exits or its process dies | All its slots and its place in line go, within about 10 seconds. |
|
|
15
|
+
|
|
16
|
+
## The line
|
|
17
|
+
|
|
18
|
+
A spawn over the cap is refused, and the session joins the line for that pool. When a slot frees, it is held for the session refused first (default 5 minutes, `agents.holdMinutes`) and that session is told so; other sessions' spawns are refused meanwhile. A session that does not use its held slot in time loses it to the next in line.
|
|
19
|
+
|
|
20
|
+
The refusal, as the refused session sees it (here both `work` slots are in use by session B):
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
When one of B's agents ends, the slot is held for A, which is woken with a notice and starts the refused agent:
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
omp gives the spawn check 30 seconds and then lets the spawn through, so the extension never waits inside it: it refuses at once and holds the next free slot instead. If the shared board cannot be read, spawns are let through with an error notice: the cap is an agreement between sessions, not a security boundary.
|
|
29
|
+
|
|
30
|
+
While the machine's 1-minute load average is above a pool's `loadCaps` threshold, the lower cap applies. Running agents are never stopped; new ones wait until the total is under the lower cap.
|
|
31
|
+
|
|
32
|
+
## Agents started by subagents
|
|
33
|
+
|
|
34
|
+
A subagent may start only the agent types listed under `agents.nested.allow` (by default the exempt types, such as reviewers), at most `agents.nested.maxRunningPerParent` at a time, outside the caps. Everything else is refused, so "no nested worker agents" is enforced rather than asked for.
|
|
35
|
+
|
|
36
|
+
## Exempt agents
|
|
37
|
+
|
|
38
|
+
Agent types under `agents.exempt` never take a slot. They still start while the scope is paused.
|
package/docs/commands.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Command reference
|
|
2
|
+
|
|
3
|
+
## In omp
|
|
4
|
+
|
|
5
|
+
| Command | Does |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `/coord` | Sessions, slots, the line, claims, queues, the latest decisions; marks a paused scope and sessions reading a different config file. |
|
|
8
|
+
| `/coord send <letter\|B/agent\|all> <text>` | Message other sessions or one of their subagents. |
|
|
9
|
+
| `/coord pause [reason]`, `/coord resume` | Stop and restart new agents and queued jobs in every session. |
|
|
10
|
+
| `/coord claim <branch\|worktree\|pr> <target> [note]`, `/coord release <kind> <target>` | Claim or release. |
|
|
11
|
+
| `/coord note <text>`, `/coord notes [n]` | Write to and read the decision log. |
|
|
12
|
+
| `/coord stats [days]`, `/coord interrupted [hours]` | Reports from the event log. |
|
|
13
|
+
|
|
14
|
+
## Agent tools
|
|
15
|
+
|
|
16
|
+
| Tool | Does |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `coord_status` | The same overview as `/coord`. |
|
|
19
|
+
| `coord_send` | Message another session or one of its subagents (main session only). |
|
|
20
|
+
| `coord_claim`, `coord_release` | Claim or release a branch, worktree or PR. |
|
|
21
|
+
| `coord_note` | Add to the decision log. |
|
|
22
|
+
| `coord_resources` | The calling agent's own ports and scratch folder. |
|
|
23
|
+
| `coord_job_priority` | Reorder this session's waiting jobs. |
|
|
24
|
+
|
|
25
|
+
## `omp-coord`
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
omp-coord run <queue> [--lane L] [--priority N] [--key K] [--owner SESSION] [--timeout 90m] -- <command> [args…]
|
|
29
|
+
omp-coord status | stats [days] | interrupted [hours] | notes [count] | note <text…>
|
|
30
|
+
omp-coord pause [reason…] | resume | doctor [--fix]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`run` is described under [queues](queues.md). The others act on the scope whose `.omp-coord.json` is found from the current folder.
|
|
34
|
+
|
|
35
|
+
Exit codes: `0` success, the command's own for `run`, `75` a job not run, `124` a job stopped by its time limit, `2` a usage or configuration error.
|
|
36
|
+
|
|
37
|
+
## Environment
|
|
38
|
+
|
|
39
|
+
| Variable | Set by | Meaning |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `OMP_COORD_CONFIG` | you | The config file to use instead of searching from the current folder. |
|
|
42
|
+
| `OMP_COORD_STATE_DIR` | you | The state folder instead of `~/.omp/run/omp-coord` (use a scratch folder for tests). |
|
|
43
|
+
| `OMP_COORD_SESSION` | the extension | The session that owns jobs started from its agents' commands. |
|
|
44
|
+
| `OMP_COORD_PORTS` | the extension | The session's port block, e.g. `18700-18799`, when `resources.ports` is set. |
|
|
45
|
+
| `OMP_COORD_SCRATCH` | the extension | The session's scratch folder, when `resources.scratch` is set. |
|
|
46
|
+
| `OMP_COORD_JOB`, `OMP_COORD_QUEUE` | `omp-coord run` | The running job's id and queue, for nested runs. |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
`.omp-coord.json`, in the project's top folder (see [`examples/omp-coord.json`](../examples/omp-coord.json)). Sessions find it in their folder or a parent folder, or through `OMP_COORD_CONFIG`. Every session of a scope should read the same file: each applies the caps and timeouts of the file it found, and `/coord` marks sessions that read a different one.
|
|
4
|
+
|
|
5
|
+
| Field | Default | Meaning |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `scope` | required | Sessions coordinate only with sessions of the same scope. Letters, digits, `.`, `_`, `-`. |
|
|
8
|
+
| `agents.pools` | `{ "work": { "cap": 8 } }` | Pools of slots: `cap`, `agentTypes` (omp agent definition names) that use the pool, and `loadCaps`: `[{ "above": 150, "cap": 4 }]` lowers the cap while the 1-minute load average is above 150. |
|
|
9
|
+
| `agents.defaultPool` | first pool | Pool for agent types no pool lists. |
|
|
10
|
+
| `agents.exempt` | `[]` | Agent types that never take a slot (read-only reviewers). |
|
|
11
|
+
| `agents.nested.allow` | `agents.exempt` | Agent types a subagent may start. |
|
|
12
|
+
| `agents.nested.maxRunningPerParent` | `1` | How many of those one subagent may run at once. |
|
|
13
|
+
| `agents.holdMinutes` | `5` | How long a freed slot stays held for the session first in line. |
|
|
14
|
+
| `agents.startTimeoutSeconds` | `120` | How long an approved spawn may take to start before its slot frees. |
|
|
15
|
+
| `messages.perMinute` | `10` | Messages one session may send per minute. It slows two sessions that keep answering each other; it does not stop them. Each delivered message starts a turn in an idle session. |
|
|
16
|
+
| `commands` | `[]` | Rules for agents' bash commands, first match wins: `{ "pattern": "<regex>", "action": "refuse", "reason": "…" }`, or `{ "pattern", "action": "queue", "queue": "<name>", "lane"? }`. The user's own `!` commands are not affected. |
|
|
17
|
+
| `queues.<name>.slots` | required | How many jobs of this queue run at once, across all processes in the scope. Queue names use letters, digits, `.`, `_`, `-`. |
|
|
18
|
+
| `queues.<name>.order` | `"fifo"` | `"fifo"`, `"fair-share"` or `"priority"` (see [queues](queues.md#order)). |
|
|
19
|
+
| `queues.<name>.lanes` | `["normal"]` | Lane names, first served first; the last is the default. |
|
|
20
|
+
| `queues.<name>.agingMinutes` | none | Waiting this long adds 1 to a job's priority. |
|
|
21
|
+
| `queues.<name>.onDuplicate` | `"refuse"` | `"refuse"` or `"supersede"` a waiting job with the same key. |
|
|
22
|
+
| `queues.<name>.timeoutMinutes` | none | Stop a job's command after this long: SIGTERM to its process group, then SIGKILL to whatever of it is left 10 s later; it exits 124. `--timeout` overrides it per job. |
|
|
23
|
+
| `queues.<name>.loadSlots` | `[]` | `[{ "above": 150, "slots": 1 }]`: fewer slots under load; `0` holds the queue. |
|
|
24
|
+
| `resources.ports` | none | `{ "from", "to", "perSession", "perAgent" (10) }`: each session gets a block of `perSession` ports, each agent a block of `perAgent` from its session's. |
|
|
25
|
+
| `resources.scratch` | none | Root of the scratch folders: `<scratch>/<letter>` per session, `<scratch>/<letter>/<agent id>` per agent. Relative to the config file. |
|
|
26
|
+
| `history.retentionDays` | `30` | Events and decision-log notes older than this are deleted. |
|
|
27
|
+
| `history.maxEvents` | `100000` | The event log keeps at most this many of the newest events. |
|
|
28
|
+
|
|
29
|
+
Agent type names are matched lowercased against omp's agent definition names (`task`, `scout`, `reviewer`, `security-reviewer`, `sonic`, or your own).
|
|
30
|
+
|
|
31
|
+
A file with a mistake turns omp-coord off in that session, with an error that names the field, e.g. `omp-coord is off: /repo/.omp-coord.json: queues.build.slots must be a whole number of at least 1`. `omp-coord doctor` reports the same.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Working across sessions
|
|
2
|
+
|
|
3
|
+
Each session gets a letter (A, B, …) and shows `coord B · work 5/8 · short 1/2` in the footer. A session that exits or crashes leaves the board within about 10 seconds. Commands and tools are listed in the [command reference](commands.md).
|
|
4
|
+
|
|
5
|
+
## Messages
|
|
6
|
+
|
|
7
|
+
`coord_send` (or `/coord send B …`) delivers a message into the other session's conversation at its next step, waking it when idle; `all` reaches every other session, and `B/<agent id>` one of session B's subagents. Messages arrive as `[omp-coord] Message from omp session A …`, marked as coming from another session rather than from the user. A message for a subagent that is not running goes to its main session, which can pass it on with `write agent://<id>`.
|
|
8
|
+
|
|
9
|
+
Messages are rate-limited (`messages.perMinute`), and each delivered message starts a turn in an idle session. Text an agent copies into a message from an untrusted source reaches the other session as written.
|
|
10
|
+
|
|
11
|
+
## Claims
|
|
12
|
+
|
|
13
|
+
A session claims a branch, worktree or PR (`coord_claim`, `/coord claim`); other sessions' agents are refused git and gh commands that would change it until it is released or the session ends.
|
|
14
|
+
|
|
15
|
+
**Claims are a guard against mistakes, not a lock.** They match command text:
|
|
16
|
+
|
|
17
|
+
- a git command that changes a claimed worktree: run in it, after `cd` into it, or with `git -C`. A worktree nested inside a claimed checkout is its own.
|
|
18
|
+
- a git command that changes a branch named in it: push (including force-push with `+`), checkout, rebase, reset, …. Commit messages do not count.
|
|
19
|
+
- a gh command that changes a claimed PR: `gh pr edit`, `comment`, `merge`, … with the number or URL anywhere outside `--body` and `--title`, and `gh api` writes to `pulls/N` or `issues/N`.
|
|
20
|
+
|
|
21
|
+
Reading commands always pass, and so does anything written to dodge the patterns.
|
|
22
|
+
|
|
23
|
+
## Pause and drain
|
|
24
|
+
|
|
25
|
+
`/coord pause [reason]` or `omp-coord pause` stops every new agent and queued job in the scope while running work finishes; `resume` lifts it. Exempt agents still start.
|
|
26
|
+
|
|
27
|
+
**Pausing keeps everyone's place.** A slot held for a session when the pause starts stays held, and on resume each held slot gets its full hold time again.
|
|
28
|
+
|
|
29
|
+
## Decision log
|
|
30
|
+
|
|
31
|
+
`coord_note`, `/coord note` and `omp-coord note` record a decision every session sees in `/coord` and `coord_status`, numbered and with its author. `/coord notes` and `omp-coord notes` list them. Notes are kept like events (`history.retentionDays`).
|
|
32
|
+
|
|
33
|
+
## Ports and scratch folders
|
|
34
|
+
|
|
35
|
+
With `resources.ports`, each session gets its own block of ports, in `OMP_COORD_PORTS` (e.g. `18700-18799`) for every command its agents run; with `resources.scratch`, its own folder in `OMP_COORD_SCRATCH`. An agent that needs ports or a folder of its own (a server, a control port) calls `coord_resources` and gets a share of its session's, the same one each time. A session started when every block was taken gets no ports and is told so.
|
|
36
|
+
|
|
37
|
+
## After a crash
|
|
38
|
+
|
|
39
|
+
`/coord interrupted` (or `omp-coord interrupted`, which first drops sessions whose process is gone) lists, per session that left, the agents that were mid-run and the folder the session ran in. omp keeps their transcripts; revive each from a session in that folder with `write agent://<id>`.
|
|
40
|
+
|
|
41
|
+
`/coord stats` sums waits, refusals, peaks and job outcomes per day; `omp-coord doctor` checks the scope's health, and `doctor --fix` drops sessions and jobs whose process is gone.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# State and event log
|
|
2
|
+
|
|
3
|
+
Kept in `~/.omp/run/omp-coord/` (owner-only; `OMP_COORD_STATE_DIR` overrides): one SQLite database per scope, `<scope>.db`, and one Unix socket per live session and per queued job. A session or job is gone when nothing listens on its socket, which a reused PID cannot fake.
|
|
4
|
+
|
|
5
|
+
The database is meant to be read by other tools. The schema version is in `PRAGMA user_version`; a database with a different schema is refused rather than misread.
|
|
6
|
+
|
|
7
|
+
## Current state
|
|
8
|
+
|
|
9
|
+
These tables are rebuilt as sessions come and go; their layout is not part of the [versioned contract](versioning.md).
|
|
10
|
+
|
|
11
|
+
| Table | Rows |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `sessions` | Live sessions: `id`, `label` (A, B…), `pid`, `cwd`, `socket`, `started_at`, `config_hash` (of the `.omp-coord.json` it read), `ports_from`/`ports_to`. |
|
|
14
|
+
| `slots` | Agent slots held: `session_id`, `agent_id`, `parent_id`, `agent_type`, `pool` (null outside the caps), `state` (`starting` or `running`), `since`. |
|
|
15
|
+
| `tickets` | The line: `session_id`, `pool`, `since`, `hold_until` (set while a slot is held for it), `notified`. |
|
|
16
|
+
| `messages` | Messages not yet delivered. |
|
|
17
|
+
| `jobs` | Queued and running build/test jobs: `id`, `queue`, `owner` (session id, or null outside omp), `lane`, `priority`, `key`, `command`, `pid`, `state` (`waiting`, `running`, `superseded`), `since`, `started_at`. |
|
|
18
|
+
| `claims` | `kind` (`branch`, `worktree`, `pr`), `target`, `session_id`, `agent_id`, `note`, `since`. |
|
|
19
|
+
| `pause` | One row while the scope is paused: `by_session` (null from a terminal), `reason`, `since`. |
|
|
20
|
+
| `agent_ports` | Port blocks agents took: `session_id`, `agent_id`, `from_port`, `to_port`. |
|
|
21
|
+
| `machine_load` | The latest load average any session or runner measured, and when. |
|
|
22
|
+
|
|
23
|
+
## Event log and decision log
|
|
24
|
+
|
|
25
|
+
`events` is an append-only log: `at` (ms since epoch), `type`, `session_id` (null for jobs from outside omp), `label`, and `data` (JSON). Types:
|
|
26
|
+
|
|
27
|
+
- Sessions and agents: `session_joined`, `session_left`, `spawn_approved`, `spawn_refused` (with `position`, `used`, `cap`, or `nested` and `reason`), `run_started` (with `revived`), `run_ended` (with `ranMs`), `start_expired`, `hold_granted`, `hold_used` and `hold_expired` (with `waitedMs` since the first refusal), `message_sent` (recipients and length, never the text).
|
|
28
|
+
- Jobs: `job_queued` (with `queue`, `lane`, `priority`, `key`), `job_refused` (with `reason`), `job_started` (with `waitedMs`), `job_ended` (with `exitCode`, `ranMs`, and `timedOut` when the time limit stopped it), `job_withdrawn` (left the line before starting), `job_superseded`, `job_lost` (its runner died), `job_reprioritized`.
|
|
29
|
+
- Control: `paused` (with `reason`), `resumed` (with `pausedMs`), `claimed`, `released`, `claim_refused` (with `holder`), `note_added` (note number and length).
|
|
30
|
+
|
|
31
|
+
`notes` is the decision log: `id`, `at`, `label`, `author`, `text`.
|
|
32
|
+
|
|
33
|
+
Retention: events and notes older than `history.retentionDays`, and all but the newest `history.maxEvents` events, are deleted when a session starts and then at most once an hour. All times are milliseconds since the epoch.
|
|
34
|
+
|
|
35
|
+
## Queries
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
db=~/.omp/run/omp-coord/my-project.db # <scope>.db
|
|
39
|
+
# Agent slots held right now, per session
|
|
40
|
+
sqlite3 -column -header "$db" "select s.label, sl.pool, count(*) n from slots sl join sessions s on s.id = sl.session_id group by 1, 2"
|
|
41
|
+
# How long sessions waited for a slot (minutes), per day
|
|
42
|
+
sqlite3 -column -header "$db" "select date(at/1000,'unixepoch','localtime') day, label, count(*) holds, round(max(json_extract(data,'$.waitedMs'))/60000.0,1) max_wait_min from events where type='hold_used' group by 1, 2"
|
|
43
|
+
# Refusals per hour
|
|
44
|
+
sqlite3 -column -header "$db" "select strftime('%Y-%m-%d %H:00',at/1000,'unixepoch','localtime') hour, count(*) refused from events where type='spawn_refused' group by 1"
|
|
45
|
+
# Build waits per queue and day: jobs, average and longest wait (minutes)
|
|
46
|
+
sqlite3 -column -header "$db" "select date(at/1000,'unixepoch','localtime') day, json_extract(data,'$.queue') queue, count(*) jobs, round(avg(json_extract(data,'$.waitedMs'))/60000.0,1) avg_wait, round(max(json_extract(data,'$.waitedMs'))/60000.0,1) max_wait from events where type='job_started' group by 1, 2"
|
|
47
|
+
```
|
|
Binary file
|
|
Binary file
|
package/docs/install.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Install
|
|
2
|
+
|
|
3
|
+
Requires omp 18.12 or later. No dependencies at runtime: the extension and the runner use the Bun built into omp.
|
|
4
|
+
|
|
5
|
+
## From GitHub Packages
|
|
6
|
+
|
|
7
|
+
Each release is published as the npm package `@nledford/omp-coord` to GitHub Packages. It is private like the repository, and GitHub Packages asks for a token even for public packages, so each machine needs one-time setup: a token that can read packages, and the `@nledford` scope pointed at GitHub's registry in `~/.npmrc`.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
gh auth refresh --scopes read:packages # once: let the GitHub CLI's token read packages
|
|
11
|
+
cat >> ~/.npmrc <<EOF
|
|
12
|
+
@nledford:registry=https://npm.pkg.github.com
|
|
13
|
+
//npm.pkg.github.com/:_authToken=$(gh auth token)
|
|
14
|
+
EOF
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then install a release, or the newest one:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
omp plugin install @nledford/omp-coord@0.2.0
|
|
21
|
+
omp plugin install @nledford/omp-coord
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## From GitHub
|
|
25
|
+
|
|
26
|
+
Over SSH. The repository is private for now, and the `github:` shorthand and HTTPS URLs fail without credentials; `github:nledford/omp-coord` works once it is public:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
omp plugin install "git+ssh://git@github.com/nledford/omp-coord.git"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or through omp's marketplace, which this repository also serves:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
omp plugin marketplace add git@github.com:nledford/omp-coord.git
|
|
36
|
+
omp plugin install omp-coord@omp-coord
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Updating
|
|
40
|
+
|
|
41
|
+
`omp plugin upgrade @nledford/omp-coord` moves a package or git URL install to the newest release or commit; `omp plugin upgrade omp-coord@omp-coord` moves a marketplace install once a release has raised the catalog's version. Bun remembers the commit a git URL resolved to, so installing a git URL again can bring back an older commit; to pin a release over git, add its tag: `…/omp-coord.git#v0.2.0`. [CHANGELOG.md](../CHANGELOG.md) lists what each release changed.
|
|
42
|
+
|
|
43
|
+
## From a checkout
|
|
44
|
+
|
|
45
|
+
Link it instead: `omp plugin install /path/to/omp-coord`, or load it for one run with `omp -e /path/to/omp-coord/src/index.ts`.
|
|
46
|
+
|
|
47
|
+
## The `omp-coord` command
|
|
48
|
+
|
|
49
|
+
For scripts and terminals: `bun link` in the checkout puts it on `PATH` (`~/.bun/bin`). Without Bun, call it through omp: `BUN_BE_BUN=1 omp /path/to/omp-coord/src/adapters/cli/cli.ts <subcommand>`.
|
|
50
|
+
|
|
51
|
+
## Turning it on in a project
|
|
52
|
+
|
|
53
|
+
The extension stays inactive in a session unless it finds a `.omp-coord.json` in the session's folder or a parent folder (so every worktree below a checkout uses the checkout's file), or the file named by `OMP_COORD_CONFIG`. See [configuration](configuration.md). Run `omp-coord doctor` in the project to check the setup.
|
package/docs/queues.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Build and test queues
|
|
2
|
+
|
|
3
|
+
`omp-coord run <queue> -- <command>` waits for one of the queue's slots, shared by everything on this machine in the same scope, then runs the command. `just` recipes, scripts and terminals call it; agents' matching bash commands are rewritten to go through it. Queues are set under `queues` in the [configuration](configuration.md).
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
omp-coord run <queue> [--lane L] [--priority N] [--key K] [--owner SESSION] [--timeout 90m] -- <command> [args…]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Running a job
|
|
10
|
+
|
|
11
|
+
`run` waits for a slot of `<queue>`, runs the command with the terminal's input and output, and exits with the command's exit code. Ctrl-C while waiting leaves the line; while running, it is passed to the command.
|
|
12
|
+
|
|
13
|
+
The command runs in its own process group and session, so a time limit stops everything it started: SIGTERM to the group, then SIGKILL to whatever is left 10 seconds later. The command has no controlling terminal: it reads and writes the terminal through its input and output, but prompts that open `/dev/tty` (sudo, ssh or gpg passphrases, git credential prompts) fail, and Ctrl-Z stops only the runner.
|
|
14
|
+
|
|
15
|
+
- **Exit codes**: the command's own, `75` when the job was not run (a refused or superseded duplicate), `124` when its time limit stopped it, `2` for a usage or configuration error.
|
|
16
|
+
- **Owner**: jobs belong to the omp session in `OMP_COORD_SESSION` (set by the extension, so commands an agent runs inherit it) or `--owner`; otherwise to no session ("outside omp").
|
|
17
|
+
- **Duplicates**: jobs with the same `--key` (for example `"<checkout> ci"`) are duplicates. One that duplicates a running job is refused; one that duplicates a waiting job is refused or, with `onDuplicate: "supersede"`, replaces it in its place in line.
|
|
18
|
+
|
|
19
|
+
## Order
|
|
20
|
+
|
|
21
|
+
Per queue:
|
|
22
|
+
|
|
23
|
+
- `fifo`: oldest first.
|
|
24
|
+
- `fair-share`: owners take turns (the one served longest ago goes next), and within one owner higher `priority` goes first, so priority never lets a session jump another.
|
|
25
|
+
- `priority`: higher priority first across everyone.
|
|
26
|
+
|
|
27
|
+
With `agingMinutes`, each that many minutes of waiting adds 1 to a job's priority, so no job waits forever behind others *in its lane*.
|
|
28
|
+
|
|
29
|
+
**Lanes**: `--lane` picks one of the queue's `lanes`; earlier lanes go first. Without it, the job goes in the last lane. Lanes are strict: steady front-lane traffic keeps the normal lane waiting.
|
|
30
|
+
|
|
31
|
+
Agents reorder their own session's waiting jobs with `coord_job_priority`. Priorities run from -1,000,000 to 1,000,000. While the load average is above a queue's `loadSlots` threshold, fewer jobs start.
|
|
32
|
+
|
|
33
|
+
## Nested runs
|
|
34
|
+
|
|
35
|
+
The running command gets `OMP_COORD_JOB` (its job id) and `OMP_COORD_QUEUE`. An `omp-coord run` on the same queue from inside that command runs at once, since it already holds that queue's slot (otherwise a one-slot queue would wait for itself). It stays in the outer job's process group, so the outer time limit stops it too. So a script can queue itself:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
# At the top of a script that needs a heavy slot: re-run this script inside one.
|
|
39
|
+
[ "${OMP_COORD_QUEUE:-}" = heavy ] || exec omp-coord run heavy --key "$PWD ci" -- "$0" "$@"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## When runners die
|
|
43
|
+
|
|
44
|
+
A runner that dies (crash, `kill -9`) loses its slot within about 10 seconds: every runner listens on a Unix socket, and the others drop jobs whose socket stops answering. The command it started is not stopped: after `kill -9` of the runner it keeps running outside the queue. Stop runners with Ctrl-C or `kill` (SIGTERM), which pass the signal on.
|
|
45
|
+
|
|
46
|
+
A command that puts its work in the background (`cargo test &`) holds the slot only until the shell exits, so the background work runs unqueued.
|
|
47
|
+
|
|
48
|
+
## Agents' commands
|
|
49
|
+
|
|
50
|
+
When an agent's bash command matches a `queue` rule under `commands`, the extension rewrites it to run `omp-coord run <queue> --owner <session> -- sh -c '<command>'` with omp's own binary as Bun (`BUN_BE_BUN=1`), so it needs no separate Bun install. The waiting happens inside the bash call, which counts against the bash tool's timeout: agents should run long jobs as background (`async`) bash calls. The rewritten command runs in `sh`, so shell variables the agent set earlier without exporting them are not visible to it.
|
|
51
|
+
|
|
52
|
+
A command matching a `refuse` rule is refused with the rule's reason.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Versioning and releases
|
|
2
|
+
|
|
3
|
+
Releases follow [Semantic Versioning](https://semver.org/) and are tagged `vX.Y.Z`; [CHANGELOG.md](../CHANGELOG.md) lists what each one changed.
|
|
4
|
+
|
|
5
|
+
## The contract
|
|
6
|
+
|
|
7
|
+
The version covers:
|
|
8
|
+
|
|
9
|
+
- the `.omp-coord.json` fields, their defaults and what they accept ([configuration](configuration.md));
|
|
10
|
+
- the `omp-coord` subcommands, flags and exit codes (75, 124, 2) ([commands](commands.md));
|
|
11
|
+
- the agent tools (`coord_*`) and their parameters, and the `/coord` commands;
|
|
12
|
+
- the environment variables it sets or reads (`OMP_COORD_*`);
|
|
13
|
+
- the event types and fields in the event log, and the decision log ([event log](event-log.md));
|
|
14
|
+
- the minimum omp version;
|
|
15
|
+
- the package name (`@nledford/omp-coord`) and the marketplace plugin name (`omp-coord@omp-coord`) that install and upgrade it.
|
|
16
|
+
|
|
17
|
+
Not covered: the wording of messages and reports, the other state tables (rebuilt as sessions come and go), and anything under `src/` that the above does not expose.
|
|
18
|
+
|
|
19
|
+
## Which number moves
|
|
20
|
+
|
|
21
|
+
| Change | Before 1.0.0 | From 1.0.0 |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Breaks the contract: a removed or renamed field, flag, tool or event; a narrower accepted value; a changed default or exit code; a higher minimum omp version | minor (0.3.4 → 0.4.0) | major |
|
|
24
|
+
| Adds to it without breaking: a new field, flag, tool, event or event field | patch (0.3.4 → 0.3.5) | minor |
|
|
25
|
+
| Fixes behavior to match the documentation | patch | patch |
|
|
26
|
+
|
|
27
|
+
1.0.0 comes when the contract is declared stable, after real use in a project. Until then, read the changelog before upgrading across a minor version.
|
|
28
|
+
|
|
29
|
+
There are no prereleases (`-rc.1` and the like): the marketplace catalog follows `main`, and omp would move every marketplace install onto one.
|
|
30
|
+
|
|
31
|
+
## Cutting a release
|
|
32
|
+
|
|
33
|
+
From `main`, with a clean work tree, not behind `origin/main`:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
just release-plan # commits and changelog since the last release; the next version for each kind of change
|
|
37
|
+
just release fix # or feature, breaking, or an exact version such as 1.0.0
|
|
38
|
+
just release-publish # asks, then pushes main and the tag; CI checks it and creates the GitHub release
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`just release` refuses an empty Unreleased section in the changelog, sets the version in `package.json` and the marketplace catalog, dates the changelog, runs the checks, then commits `Release vX.Y.Z` and tags it, all locally. If anything fails before the commit, the files are put back. The version changes only in release commits, because omp upgrades marketplace installs only when the catalog's version goes up.
|
|
42
|
+
|
|
43
|
+
The release workflow checks that the tag is on `main` and matches the version files and the changelog, runs the checks on macOS, and creates the GitHub release with the changelog entry as its notes.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scope": "my-project",
|
|
3
|
+
"agents": {
|
|
4
|
+
"pools": {
|
|
5
|
+
"work": { "cap": 6, "loadCaps": [{ "above": 12, "cap": 3 }] },
|
|
6
|
+
"short": { "cap": 2, "agentTypes": ["scout"] }
|
|
7
|
+
},
|
|
8
|
+
"exempt": ["reviewer", "security-reviewer"],
|
|
9
|
+
"nested": { "allow": ["reviewer"], "maxRunningPerParent": 1 },
|
|
10
|
+
"holdMinutes": 5
|
|
11
|
+
},
|
|
12
|
+
"queues": {
|
|
13
|
+
"build": {
|
|
14
|
+
"slots": 2,
|
|
15
|
+
"order": "fair-share",
|
|
16
|
+
"lanes": ["urgent", "normal"],
|
|
17
|
+
"agingMinutes": 15,
|
|
18
|
+
"timeoutMinutes": 60,
|
|
19
|
+
"onDuplicate": "supersede",
|
|
20
|
+
"loadSlots": [{ "above": 12, "slots": 1 }]
|
|
21
|
+
},
|
|
22
|
+
"test": { "slots": 3, "order": "fair-share", "agingMinutes": 10, "timeoutMinutes": 30 }
|
|
23
|
+
},
|
|
24
|
+
"commands": [
|
|
25
|
+
{ "pattern": "(^|[;&|(\\s])(npm|pnpm|yarn|bun)\\s+(run\\s+)?test\\b", "action": "queue", "queue": "test" },
|
|
26
|
+
{ "pattern": "(^|[;&|(\\s])(npm|pnpm|yarn|bun)\\s+run\\s+build\\b", "action": "queue", "queue": "build" },
|
|
27
|
+
{
|
|
28
|
+
"pattern": "(^|[;&|(\\s])pkill\\b",
|
|
29
|
+
"action": "refuse",
|
|
30
|
+
"reason": "pkill can stop other agents' processes; stop only the process you started, by its PID"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"pattern": "(^|[;&|(\\s])git\\s+(-C\\s+\\S+\\s+)?stash\\b",
|
|
34
|
+
"action": "refuse",
|
|
35
|
+
"reason": "stashes are shared by all worktrees: commit to a throwaway branch instead"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"resources": {
|
|
39
|
+
"ports": { "from": 20000, "to": 20999, "perSession": 200, "perAgent": 10 },
|
|
40
|
+
"scratch": ".omp-coord-scratch"
|
|
41
|
+
},
|
|
42
|
+
"messages": { "perMinute": 10 },
|
|
43
|
+
"history": { "retentionDays": 30, "maxEvents": 100000 }
|
|
44
|
+
}
|