omp-conductor 0.3.5 → 0.3.6
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 +112 -6
- package/package.json +1 -1
- package/src/briefs/orchestrator.md +18 -2
- package/src/orchestrator-tick.ts +433 -5
- package/src/plugin.ts +285 -98
- package/src/setup.ts +283 -2
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ The package ships three deployables, plus one skill:
|
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| omp plugin | `/conductor` slash command | Inspect and arm the conductor from inside an omp session: dry-run the queue, read status, pause, resume. |
|
|
40
40
|
| Standalone daemon | `omp-conductor` binary | The dispatch loop, managed as a background process (`start` / `stop` / `restart`) with a `/healthz` endpoint for a supervisor. |
|
|
41
|
-
| Orchestrator heartbeat | omp extension, activated by `.conductor-tick.json` | Prompts a 24/7 orchestrator session on a fixed interval so its standing loop actually runs, and marks the session stalled when its prompts stop being consumed. Inert in every other session. See [Orchestrator tick](#orchestrator-tick). |
|
|
41
|
+
| Orchestrator heartbeat | omp extension, activated by `.conductor-tick.json` | Prompts a 24/7 orchestrator session on a fixed interval so its standing loop actually runs, and marks the session stalled when its prompts stop being consumed. Inert in every other session — including a second session opened in the fleet's own directory. See [Orchestrator tick](#orchestrator-tick). |
|
|
42
42
|
| Onboarding skill | `skill://conductor-onboarding` | Directs an omp session to interview you, read your repos for real CI gates, and tailor `ORCHESTRATOR.md` — then finish through the wizard. Discovered automatically once the plugin is installed. See [Onboarding](#onboarding). |
|
|
43
43
|
|
|
44
44
|
The first two are thin wrappers over the same `daemon.ts`, so the plugin and the
|
|
@@ -170,7 +170,7 @@ Onboarding this package has two layers, and installing it gives you both.
|
|
|
170
170
|
|
|
171
171
|
| Layer | What it is | What it owns |
|
|
172
172
|
| --- | --- | --- |
|
|
173
|
-
| **`/conductor setup`** | The deterministic wizard. Closed questions, a label plan, a dry run, one confirm. | **Mechanical config.** It is the only thing that writes `config.json`, and it mutates nothing before you confirm. |
|
|
173
|
+
| **`/conductor setup`** | The deterministic wizard. Closed questions, a label plan, a dry run, one confirm. On a project it already knows, it offers to amend one area instead of re-asking everything — see [Changing one setting](#changing-one-setting). | **Mechanical config.** It is the only thing that writes `config.json`, and it mutates nothing before you confirm. |
|
|
174
174
|
| **`skill://conductor-onboarding`** | A skill bundled in this package (`skills/conductor-onboarding/SKILL.md`), discovered automatically by any omp session once the plugin is installed. | **Brief authoring.** The judgement the wizard cannot prompt for. |
|
|
175
175
|
|
|
176
176
|
The split exists because the two halves fail differently. A wrong config value is
|
|
@@ -215,6 +215,59 @@ turned on you can also invoke it directly:
|
|
|
215
215
|
Nothing about the wizard changes: `/conductor setup` on its own remains a
|
|
216
216
|
complete, supported path, and the brief it renders is safe unedited.
|
|
217
217
|
|
|
218
|
+
### Changing one setting
|
|
219
|
+
|
|
220
|
+
`config.json` is wizard-written, so changing a value means running the wizard —
|
|
221
|
+
and a wizard that re-asks twenty questions to add one key is a wizard people edit
|
|
222
|
+
the file behind instead. So a re-run against a project that is already configured
|
|
223
|
+
opens with one question:
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
"veltro" is already configured — what would you like to do?
|
|
227
|
+
> Change one area
|
|
228
|
+
asks one area's questions; every other answer is carried through from the saved config
|
|
229
|
+
Walk every question again
|
|
230
|
+
the full interview, every prompt pre-filled with what is configured now
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Amending is the default. Pick it and the eight areas are listed with what each one
|
|
234
|
+
says right now, so the row you want is the row you can see:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
Which area? Each row shows what it says now
|
|
238
|
+
tracker & repos — veltrosecurity/veltro, queue "ready-for-agent", "repo:" → veltro, chad, warden, vectorflow
|
|
239
|
+
gates — veltro: none; chad: ruff check . @ backend, pnpm lint @ frontend; warden: ruff check . @ backen…
|
|
240
|
+
caps & worker model — 2 workers, 120 turns, 90m, $25/day, 2 attempts (all defaults) — harness default model
|
|
241
|
+
code graph — not configured — workers grep
|
|
242
|
+
authority — merge=orchestrator, release=orchestrator
|
|
243
|
+
escalation & triage — tier 2 pages Telegram 8236653927, comments too, triage external
|
|
244
|
+
reporting scope — material — escalations, plus green PRs, second failures, and anything that stops the fleet
|
|
245
|
+
orchestrator brief — none at /root/.omp/conductor/worktrees/ORCHESTRATOR.md
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Only that area's questions are asked. Every other answer is read back out of
|
|
249
|
+
`config.json` and written again unchanged — the same answers, the same builder,
|
|
250
|
+
the same single confirm, so there is still exactly one thing in this package that
|
|
251
|
+
writes a config, and it still writes nothing before you agree. The consent screen
|
|
252
|
+
leads with the delta and then shows the whole project as it would be written:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
amending code graph — project veltro
|
|
256
|
+
was not configured — workers grep
|
|
257
|
+
now /root/.cache/conductor-graph/veltrosecurity — 4 clone(s): veltro, chad, warden, vectorflow
|
|
258
|
+
carried over tracker & repos, gates, caps & worker model, authority, escalation & triage, reporting scope, orchestrator brief
|
|
259
|
+
read back from /root/.omp/conductor/config.json and rewritten unchanged
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
A first run, or a project name this config has never seen, never sees either
|
|
263
|
+
question: there is nothing to amend, so it is the full interview exactly as
|
|
264
|
+
before. Choosing *Walk every question again* is also unchanged — every prompt
|
|
265
|
+
pre-filled with what is configured, Enter to keep it — with one wrinkle worth
|
|
266
|
+
knowing: the two authority confirms and the orchestrator-session confirm cannot
|
|
267
|
+
start on "yes", so Entering through the full interview **revokes** a delegation
|
|
268
|
+
rather than renewing it. Amending the `authority` area names the current grant in
|
|
269
|
+
the question, which is the safer way to leave one alone.
|
|
270
|
+
|
|
218
271
|
### Keeping a brief current
|
|
219
272
|
|
|
220
273
|
Upgrading the package does not upgrade a brief you are already running, and it is
|
|
@@ -552,7 +605,10 @@ Say yes and the wizard asks for one root, then derives one clone per routed repo
|
|
|
552
605
|
underneath it (default `~/.cache/conductor-graph/<org>/<repo>`) and writes it to
|
|
553
606
|
each repo's [`graphProject`](#configuration). Nothing else changes: this package
|
|
554
607
|
never runs an indexer, never imports one, and behaves identically with the graph
|
|
555
|
-
server absent — dispatch, caps and escalation do not know it exists.
|
|
608
|
+
server absent — dispatch, caps and escalation do not know it exists. On a fleet
|
|
609
|
+
that was configured before this key existed, `/conductor setup` and the `code
|
|
610
|
+
graph` area add it in two prompts — see
|
|
611
|
+
[Changing one setting](#changing-one-setting).
|
|
556
612
|
|
|
557
613
|
### Why the clone, and not your checkout or the worktree
|
|
558
614
|
|
|
@@ -686,6 +742,10 @@ The file is validated on every read. A malformed config produces one readable er
|
|
|
686
742
|
listing every fault, and the daemon refuses to start rather than running with half
|
|
687
743
|
a project.
|
|
688
744
|
|
|
745
|
+
`/conductor setup` is the only thing here that writes this file, and on a project
|
|
746
|
+
it already knows it can rewrite one area of it without re-asking the rest — see
|
|
747
|
+
[Changing one setting](#changing-one-setting).
|
|
748
|
+
|
|
689
749
|
`version` is `2`. A `version: 1` file still loads: caps it names that this build no
|
|
690
750
|
longer enforces are dropped rather than treated as typos, and the next save writes
|
|
691
751
|
it back as `2`. In a `version: 2` file an unrecognised cap key **is** an error,
|
|
@@ -811,7 +871,8 @@ in the orchestrator's working directory:
|
|
|
811
871
|
| `intervalSeconds` | yes | — | Whole seconds between ticks, minimum `60`. A tick costs a full turn of a frontier model, so a sub-minute period is refused rather than obeyed. |
|
|
812
872
|
| `armedFile` | no | none — the gate passes | Path to the arm marker. A tick does nothing while the file is missing. Relative paths resolve against the session cwd, so `state/armed` means `<cwd>/state/armed`. |
|
|
813
873
|
| `accessFile` | no | none — the gate passes | Path to the Telegram bridge's `access.json`. Every tick re-reads it and requires `enabled: true` with exactly one entry in `allowFrom`. Relative paths resolve against the session cwd. **Configure this on any fleet deploy** — see below. |
|
|
814
|
-
| `message` | no | `Tick <ISO timestamp>: re-read <workspaceRoot>/ORCHESTRATOR.md from disk, then run your standing loop from it.`, then the `reporting.scope` line, then the delivery rule | Sent verbatim when set — and then it owns the whole contract: neither the scope line nor the delivery rule is appended to a prompt you wrote yourself. Re-read from disk on **every** tick, so rewording it binds the next heartbeat instead of waiting for a session restart; a re-read that fails — caught mid-edit, removed, or invalid — keeps the value read at session start rather than stopping the heartbeat. `intervalSeconds` is *not* re-read: rescheduling a live timer still needs a restart. The default *orders* the session to re-read its brief, naming the path resolved from the
|
|
874
|
+
| `message` | no | `Tick <ISO timestamp>: re-read <workspaceRoot>/ORCHESTRATOR.md from disk, then run your standing loop from it.`, then the `reporting.scope` line, then the delivery rule | Sent verbatim when set — and then it owns the whole contract: neither the scope line nor the delivery rule is appended to a prompt you wrote yourself. Re-read from disk on **every** tick, so rewording it binds the next heartbeat instead of waiting for a session restart; a re-read that fails — caught mid-edit, removed, or invalid — keeps the value read at session start rather than stopping the heartbeat. `intervalSeconds` is *not* re-read: rescheduling a live timer still needs a restart. The default *orders* the session to re-read its brief, naming the path resolved from the project's `workspaceRoot`, because a standing prompt drifts out of a long-lived session's context while the file on disk does not. |
|
|
875
|
+
| `agentName` | no | `fleet` | The herdr agent name the orchestrator's pane is registered under. Under herdr this is the whole of the identity check below, and the default matches `AGENT_NAME=${AGENT_NAME:-fleet}` in the recovery plugin's `recover.sh`, so both halves key on one name. Rename the agent and set this to match. |
|
|
815
876
|
|
|
816
877
|
A tick sends one message (`customType` `omp-conductor.tick`, attributed to the
|
|
817
878
|
user): the standing-loop prompt, the one constraint line the project's
|
|
@@ -825,8 +886,6 @@ watched succeed. The tick starts a turn if the session is idle; while a turn is
|
|
|
825
886
|
streaming it is queued as a follow-up and consumed when that turn ends.
|
|
826
887
|
It sends **nothing** when:
|
|
827
888
|
|
|
828
|
-
- `/conductor pause` (or `omp-conductor pause`) holds the pause flag, the same
|
|
829
|
-
flag the dispatch loop reads, so pausing the fleet pauses its heartbeat;
|
|
830
889
|
- `armedFile` is configured and missing;
|
|
831
890
|
- `accessFile` is configured and the escalation channel is not verifiably up;
|
|
832
891
|
- an earlier tick is still queued. Ticks coalesce rather than stack, so a slow
|
|
@@ -834,6 +893,53 @@ It sends **nothing** when:
|
|
|
834
893
|
in a row are the signal that the session is not slow but wedged, which is
|
|
835
894
|
what the [stall marker](#a-wedged-session-and-the-marker-that-notices) is for.
|
|
836
895
|
|
|
896
|
+
### One session per directory ticks, and it says which
|
|
897
|
+
|
|
898
|
+
Activation is a property of the *directory*, so before it arms anything the
|
|
899
|
+
heartbeat asks whether this session is the orchestrator or merely a session
|
|
900
|
+
standing in its directory. It has to: opening a second omp session in the fleet's
|
|
901
|
+
cwd — a shell to read state, say — used to arm a second heartbeat that prompted
|
|
902
|
+
*that* session with the standing loop, and with
|
|
903
|
+
[`authority`](#configuration) delegated it would consider itself entitled to
|
|
904
|
+
merge PRs and cut releases. Two brains, one queue, and nothing in the log to tell
|
|
905
|
+
them apart.
|
|
906
|
+
|
|
907
|
+
**Under herdr** (`HERDR_ENV=1` with a `HERDR_PANE_ID`), the answer is the pane's
|
|
908
|
+
registered agent name: the heartbeat asks `herdr agent list` for the entry whose
|
|
909
|
+
`pane_id` is this pane's and ticks only when its `name` equals `agentName`. Fleetness
|
|
910
|
+
is the *session* — every pane in it shares `HERDR_SESSION` and the cwd — and
|
|
911
|
+
herdr's `agent` field is the *runtime*, `omp` for the orchestrator and for the
|
|
912
|
+
shell beside it, so neither can tell them apart. The registered name can, it is
|
|
913
|
+
what `herdr agent start fleet --kind omp --pane <id>` sets, and it is the same
|
|
914
|
+
identity the recovery plugin keys on. A pane with a different name, or no name at
|
|
915
|
+
all, stays inert.
|
|
916
|
+
|
|
917
|
+
**Without herdr**, the session claims the directory in a sibling
|
|
918
|
+
`.conductor-tick-owner.json` (pid, session file, claim time) and ticks only while
|
|
919
|
+
it is the live claimant. Liveness is a **pid check, never a timestamp**: a crashed
|
|
920
|
+
orchestrator's claim is reclaimed by the next session rather than wedging the
|
|
921
|
+
fleet until somebody deletes a file, and a slow-but-running orchestrator never
|
|
922
|
+
loses its claim to a lease that expired.
|
|
923
|
+
|
|
924
|
+
Declining is logged once, at session start, naming the holder — which is the whole
|
|
925
|
+
point, because the original failure was that the second ticker was
|
|
926
|
+
indistinguishable from the first:
|
|
927
|
+
|
|
928
|
+
```text
|
|
929
|
+
[omp-conductor] orchestrator tick inactive: pane w1:p1 (agent "fleet") owns the fleet tick here — this session will not tick
|
|
930
|
+
[omp-conductor] orchestrator tick inactive: this pane is agent "scratch", not the fleet agent "fleet" — this session will not tick
|
|
931
|
+
[omp-conductor] orchestrator tick inactive: pid 4147344 (claimed 2026-08-07T07:55:36.001Z, session …/fleet.jsonl) owns the fleet tick in /root/.omp/conductor — this session will not tick
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
A `herdr agent list` that does not answer also declines, for the same reason the
|
|
935
|
+
escalation channel fails closed: under herdr this session is one pane of several
|
|
936
|
+
in that directory, and an unproven identity is exactly the case the check exists
|
|
937
|
+
for. That includes `herdr` not being on the session's `PATH` — worth checking on a
|
|
938
|
+
fleet host, where the orchestrator's environment comes from a unit file rather
|
|
939
|
+
than a login shell — and `HERDR_BIN_PATH` names the binary when it is not, the
|
|
940
|
+
same escape hatch the recovery plugin's `recover.sh` has. On a host with no herdr
|
|
941
|
+
and no prior claimant — the ordinary single-session case — nothing changes.
|
|
942
|
+
|
|
837
943
|
### The escalation channel is a gate, and it fails closed
|
|
838
944
|
|
|
839
945
|
Unattended dispatch is only defensible while a tier-2 escalation can reach a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omp-conductor",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A 24/7 dispatcher that takes ready GitHub issues to green, mergeable PRs using omp coding sessions, with tiered escalation first to an orchestrator session and then to a human.",
|
|
@@ -176,12 +176,23 @@ Not yours to relax:
|
|
|
176
176
|
it, never you. A fleet that patches its own dispatcher is a fleet whose
|
|
177
177
|
behavior nobody can reproduce, and the next install silently reverts the
|
|
178
178
|
patch, which is worse than never having made it.
|
|
179
|
+
- **A worker's branch is theirs; the PR is yours to steer.** Never `git checkout`,
|
|
180
|
+
commit or push inside a worker's worktree, never cut a branch from one, never
|
|
181
|
+
force-push or rewrite history anywhere, and never author a commit under an
|
|
182
|
+
invented identity. But `gh pr update-branch` **is** yours to run and is the
|
|
183
|
+
sanctioned remedy for a green PR that has fallen behind: it is a server-side
|
|
184
|
+
merge of the base into the head, it destroys nothing, it rewrites nothing, and
|
|
185
|
+
under a ruleset that requires branches to be up to date it is the only way a
|
|
186
|
+
correct PR ever merges. Closing a green PR to make a fresh worker redo the
|
|
187
|
+
merge costs a whole attempt to buy what one command does in minutes — do not.
|
|
188
|
+
Bypassing branch protection with admin rights is still forbidden; updating the
|
|
189
|
+
branch is how you satisfy it, not how you dodge it.
|
|
179
190
|
|
|
180
191
|
**Your own** merge and release authority is not decided here. It is whatever your
|
|
181
192
|
operator granted at setup time, stated in the first paragraph of **Releases**
|
|
182
193
|
below; ungranted, it is none — you do not merge, tag, publish or deploy either.
|
|
183
194
|
That grant is a deliberate operator decision, changed by re-running setup rather
|
|
184
|
-
than by editing this file. The
|
|
195
|
+
than by editing this file. The five boundaries above are not.
|
|
185
196
|
|
|
186
197
|
## Learning loop
|
|
187
198
|
|
|
@@ -248,7 +259,12 @@ eventually publishes something at 03:00. Spell out all seven.
|
|
|
248
259
|
- **Whether you may merge**, and which PRs. Release work usually needs it, and a
|
|
249
260
|
procedure that has you landing a PR without saying so leaves you inferring
|
|
250
261
|
permission. Note that **one at a time, re-checked against the base branch** binds
|
|
251
|
-
you here exactly as it binds a human; that part is a hard boundary.
|
|
262
|
+
you here exactly as it binds a human; that part is a hard boundary. When that
|
|
263
|
+
re-check finds a green PR that is merely *behind*, the answer is
|
|
264
|
+
`gh pr update-branch` and a wait for the fresh run — never closing it, and
|
|
265
|
+
never an admin bypass. Merging promptly is itself the remedy that stops the
|
|
266
|
+
next PR falling behind: a queue of green PRs left unmerged makes each one
|
|
267
|
+
stale in turn.
|
|
252
268
|
- **The release authority**, named. Which workflow or command ships this repo, and
|
|
253
269
|
how it is invoked. If it is a protected or dispatchable workflow, your
|
|
254
270
|
instruction is to *dispatch it and verify the run*. You never reproduce what it
|