@irythill/orq 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/stats.js CHANGED
@@ -6,7 +6,7 @@
6
6
  import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
7
7
  import { homedir } from "node:os";
8
8
  import { basename, join } from "node:path";
9
- const ROLE = /^(impl|fix\d*|review\d*|[a-z]+-review\d*)-?/;
9
+ const ROLE = /^(impl|fix\d*|review\d*|[a-z]+-review\d*|watchdog\d*)-?/;
10
10
  const read = (f) => { try {
11
11
  return readFileSync(f, "utf8");
12
12
  }
@@ -115,8 +115,9 @@ export function collectRuns(ws, days) {
115
115
  const name = basename(pre);
116
116
  const rm = ROLE.exec(name);
117
117
  const role = rm && rm[1].includes("review") ? "review"
118
- : name.startsWith("fix") || txt.includes("-R ") || txt.includes(" resume ") || txt.includes("--resume") ? "fix"
119
- : name.startsWith("impl") ? "impl" : "other";
118
+ : rm && rm[1].startsWith("watchdog") ? "watchdog"
119
+ : name.startsWith("fix") || txt.includes("-R ") || txt.includes(" resume ") || txt.includes("--resume") ? "fix"
120
+ : name.startsWith("impl") ? "impl" : "other";
120
121
  const out = read(`${pre}.out.md`);
121
122
  const [neu, tot] = tokens(agent, log, read(`${pre}.id`).trim());
122
123
  runs.push({ issue, name, agent, model, role, ok: read(done).includes("status=ok") && out.trim() !== "",
package/dist/testrun.js CHANGED
@@ -2,13 +2,14 @@
2
2
  // memory cap (src/process.ts) and read its JSON report into "<file relative to the repo> :: <full test name>" lines.
3
3
  // The runner's own bin (node_modules/<runner>/package.json "bin") runs with this node, so Windows needs no shell;
4
4
  // without it, npx as before. Also the background job both share (launch on a TerminalHost, queue, .pid/.done, notify).
5
- import { spawn, spawnSync } from "node:child_process";
5
+ import { spawnSync } from "node:child_process";
6
6
  import { createWriteStream, existsSync, lstatSync, readFileSync, realpathSync, rmdirSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
7
7
  import { dirname, isAbsolute, join, relative } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { writeAtomic } from "./fsutil.js";
10
10
  import { m } from "./i18n.js";
11
11
  import { acquire, release } from "./lock.js";
12
+ import { handoffFile, notify } from "./notice.js";
12
13
  import { killTree, parseSize, runCapped } from "./process.js";
13
14
  import { closeOwnTab, detectHost, launch } from "./terminal.js";
14
15
  export const JS_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|mts|cts)$/;
@@ -196,12 +197,5 @@ export async function inJob(j, label, slots, body, done) {
196
197
  }
197
198
  return st === "ok" ? 0 : 1;
198
199
  }
199
- /** The conductor is a herdr pane: type the line into it (herdr-notify waits for it to leave a dialog). In the
200
- * background when the caller must not wait for that (an agent's end-of-turn hook holds its screen meanwhile). */
201
- export function notify(pane, line, background = false) {
202
- const local = join(dirname(CLI), "..", "setup", "herdr-notify"), cmd = existsSync(local) ? local : "herdr-notify";
203
- if (background)
204
- spawn(cmd, [pane, line], { detached: true, stdio: "ignore" }).unref();
205
- else
206
- spawnSync(cmd, [pane, line], { stdio: "inherit" });
207
- }
200
+ /** Tell the conductor's herdr pane (src/notice.ts): returns at once, delivery runs detached with retries and a log. */
201
+ export { handoffFile, notify };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@irythill/orq",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Orchestrate coding agents (Claude Code, Codex, OpenCode, Antigravity, Copilot) from one conductor.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,7 +30,15 @@ Issue artifacts (prompts, notes) live in `$WS/.orquestra/<ID>/`.
30
30
  ## 1. Read the issue
31
31
  - **First, claim it:** `herdr-claim -w $WS <ID>`. Exit 1 = another conductor owns it: **stop** and tell the user
32
32
  (don't read it, don't dispatch anything). Taking it over (e.g. switching accounts) only with the user's OK:
33
- `herdr-claim -w $WS -f <ID>`. Several conductors may share a workspace; `herdr-claim -w $WS -l` lists who has what.
33
+ `herdr-claim -w $WS -f <ID>`. Several conductors may share a workspace; `herdr-claim -w $WS -l` lists who has what,
34
+ with each conductor's name (its herdr workspace label, e.g. "emp: orq-cms-dsgn"; outside herdr, `ORQ_CONDUCTOR_ID`).
35
+ - **The user names another conductor** ("take over orq-cms-dsgn's work"): never guess a pane id. Run
36
+ `orq claim -w $WS --from "<what they said>"` (any part of the name works). It lists that conductor's issues; exit 3 =
37
+ the name matches more than one conductor (show the list, ask which); exit 1 = none. With the user's OK,
38
+ `orq claim -w $WS --from "<name>" -f` takes them all. **Resuming** an issue someone else started: don't start over.
39
+ Read `$O/` first — `impl-<repo>.md`/`.out.md` (prompt, answer, `## Decisions`), `.pick` files, the last
40
+ `fix-<n>.md` and `review-<repo>.out.md`, `verify-<repo>.txt` — and the Plane comments, then go on from the step the
41
+ files show (a `.pane`/`.id` without `.done` = a child still running: `orq wait` for it instead of dispatching again).
34
42
  - `mcp__plane__workitem` `retrieve_by_identifier` with `workitem_identifier: "<ID>"`; then `workitem_comment list`.
35
43
  - Extract: goal, **acceptance criteria**, affected repos, links. No acceptance criteria → write a short proposal and
36
44
  **ask the user** before dispatching.
@@ -44,6 +52,14 @@ One task per repo. Level:
44
52
  - **medium** — business rule, API↔front integration, several files.
45
53
  - **heavy** — architecture, data migration, hard/concurrency bug, security.
46
54
 
55
+ **Scope check, before dispatching.** Estimate the size. Split into sub-issues or sequential batches (each its own PR)
56
+ when it touches several unrelated areas, changes roughly >400 lines, mixes refactor with behavior change, or has
57
+ independent acceptance criteria.
58
+ - Each batch is independently testable and reviewable; dispatch in order, one implementer per batch. Record the split in
59
+ Plane (sub-issues or a checklist comment) before starting.
60
+ - Scope too big mid-run (reviews hitting `policy.max_rounds`, repeated findings, the implementer asking to change
61
+ scope): stop, split the remainder, don't force more rounds. `orq rounds <ID>` shows the alert; on warn/stop `orq rounds <ID> --diagnose` says why.
62
+
47
63
  ## 3. Pick agent and model: always `orq pick`, never by hand
48
64
  `orq pick` decides agent, model, effort and account (user config + delivery and token history + current quota +
49
65
  rotation). **Do not choose by preference, by a rule you read elsewhere, or by what worked last time**: run it before
@@ -51,8 +67,10 @@ EVERY dispatch (implementation, review, second opinion) and use its output verba
51
67
  ```bash
52
68
  orq pick impl --level simple|medium|heavy # → e.g. -a codex -m gpt-6-sol -e low
53
69
  orq pick review --level <level> --impl-agent <agent that implemented> # → another vendor, e.g. -a opencode -m ...
54
- orq pick opinion # second opinion (e.g. Copilot), see step 5
70
+ orq pick opinion --impl-agent <agent that implemented> # second opinion (e.g. Copilot), see step 5
55
71
  ```
72
+ More than one agent eligible for `review`/`opinion` (declared, not excluded or blocked, with a model for the role; quota is not considered) and neither `--impl-agent` nor `--impl-vendor` given → exit 2:
73
+ always pass one of them (`--no-record`/`--explain` do not change this).
56
74
  The output goes straight into `herdr-agent` (`herdr-agent $(orq pick ...) -g ... -t ... -d ... -o ... -- prompt.md`).
57
75
  - Exit 3 = no candidate (quota exhausted, no agent with that role): **stop and ask the user**.
58
76
  - Implementer failed review twice (step 6) → pick again without it: `orq pick impl --level <level> --exclude-agent <agent>`.
@@ -108,7 +126,7 @@ tell me".
108
126
  orq pick impl --level <level> > $O/impl-<repo>.pick # the fix rounds reuse it (step 6)
109
127
  herdr-agent $(cat $O/impl-<repo>.pick) -g <group> -t "<ID> impl <repo>" -d $WT -o $O/impl-<repo> -- $O/impl-<repo>.md
110
128
  ```
111
- - The Claude account (`-c`) already comes from `orq pick` (the least used one under the limit).
129
+ - The Claude account (`-c`) already comes from `orq pick` (one under the limit whose pace stays within it, then the least used).
112
130
  - Full final answer in `$O/impl-<repo>.out.md`; log in `.log`; the agent's session id in `.id`.
113
131
  - Codex's sandbox has no network: `git fetch` is yours, beforehand (above). Push/PR: yours (step 7).
114
132
  - `herdr-agent` returns immediately (no need to background it); the child's pane is in `<prefix>.pane`
@@ -125,6 +143,9 @@ herdr-agent $(cat $O/impl-<repo>.pick) -g <group> -t "<ID> impl <repo>" -d $WT -
125
143
  origin/<BASE>...HEAD`); (2) `NODE_OPTIONS=--max-old-space-size=2048 tsc --noEmit` for the project. Don't run tests".
126
144
  Full suite only in CI (or a manual `herdr-baseline` if the user asks).
127
145
  - An issue that changes an API contract: **API first**; consumers afterwards, with the new contract in the prompt.
146
+ - **A red board line / `✗` pane title = a failure you may have missed.** If you see one (or the user says notices aren't
147
+ arriving), run `orq notice` (delivery log) and `<prefix>.done` for that issue, then handle the failure; running
148
+ `orq wait`, `orq verify` or `orq agent` for the issue acknowledges it.
128
149
  - **Never block waiting** (`herdr agent wait`, `pane wait-output`, `sleep`, `timeout`): completion arrives as a
129
150
  `[HERDR-AGENT DONE]` line in your pane. Dispatch, report in one line and end your turn. "Done yet?" = `<prefix>.done`
130
151
  exists (never `pgrep`).
@@ -160,7 +181,16 @@ are in the attached verify)". If the reviewer ends with `status=fail` and no ver
160
181
  `jetski: ... permission` line), that is not a rejection: run `orq pick review ... --exclude-agent <it>` and redo it.
161
182
  `--exclude-agent` applies **only to redoing that review** (save the new pick to `review-<repo>.pick`); the next
162
183
  round reuses whoever gave the verdict. An agent that failed twice in a row with no verdict →
163
- `orq block <agent> --days 1` and tell the user.
184
+ `orq block <agent> --days 1` and tell the user. If it stopped on a quota limit, use `orq block <agent> --quota` (until
185
+ the window resets; a fixed period outlives it and keeps the agent out of `orq pick` for nothing). If `orq pick` finds no
186
+ candidate, its output is empty and `orq agent` refuses to start: stop and tell the user.
187
+ **Review tools** (an agent whose `run` has no `{prompt}`/`{prompt_text}`/stdin, e.g. a CodeRabbit-like CLI with
188
+ `ok_exit`/`filter` in the config): same command; it ignores the prompt and the attachments and reviews the branch
189
+ against `{base}` itself. Read its answer like any other: `status=ok` with `rc≠0` is a verdict with findings, not a
190
+ failure. Its high-severity items are the blockers, still under the step 6 rule (`file:line` + concrete failure).
191
+ If the tool's answer opens with (or, when short, consists of) a statement that nothing was reviewed (e.g. Revtool's "Nothing to review"), `agents.<name>.no_review` (a
192
+ regex) makes `orq agent` end that run `status=fail`: `orq pick`'s fallback and the conductor treat it as not run,
193
+ same as an empty answer.
164
194
  **Second opinion** (`orq pick opinion`, read only, same attachments): only on **heavy** issues or when reviewer and
165
195
  implementer disagree about a blocker. It never becomes a round by itself: you weigh both answers and decide.
166
196
  **Before dispatching:** `git -C $R fetch origin $BASE; git -C $WT merge-base --is-ancestor origin/$BASE HEAD` — if it
@@ -180,7 +210,7 @@ repo checklist.
180
210
  **Only `[blocker]` items open a round.** An item tagged blocker without `file:line` and a concrete failure, or that
181
211
  only restates a `## Decisions` choice, counts as a suggestion; when you downgrade one, say why in the Plane comment.
182
212
  No blockers left → APPROVED, even with suggestions.
183
- Blocking problems → write the list (blockers only) to `$O/fix-<n>.md` and continue the implementer's session (any agent):
213
+ Blocking problems → write the list (blockers only) to `$O/fix-<n>.md` (always this name; reviews `$O/review-<repo>-<n>`, never `fix<n>-<repo>` or other variants — a later conductor counts rounds by these files) and continue the implementer's session (any agent):
184
214
  `herdr-agent $(orq pick fix --same $O/impl-<repo>.pick) -g <group> -t "<ID> fix<n> <repo>" -d $WT -o $O/impl-<repo> -R $(cat $O/impl-<repo>.id) -- $O/fix-<n>.md`.
185
215
  A fix is a continuation: same agent, model, account and session, effort one level up (`--same` reads the implementer's
186
216
  pick; never a new `orq pick`, never `-a` alone — without `-m` the session resumes on the CLI's default model, which
@@ -196,10 +226,18 @@ Then a new `herdr-verify` (4b) and the **round review** (step 5 with these chang
196
226
  (the full `-b $BASE` diff stays, as context).
197
227
  - The prompt asks a closed question: "for each item in fix-<n>.md: RESOLVED or NOT RESOLVED (why); then the fix diff:
198
228
  any regression it introduced, as `[blocker]`/`[suggestion]`. Outside the fix diff, report only a blocker you missed
199
- before that would break production, and say why it wasn't in the first review". Anything else is a suggestion. **Maximum: `orq config get policy.max_rounds` rounds**; over it → `orq pick impl
229
+ before that would break production, and say why it wasn't in the first review". Anything else is a suggestion. A review tool can't answer that: it reviews the whole
230
+ branch again, so you do the matching — a finding is a blocker this round only if it is inside `fix-<n>.diff` or is a
231
+ NOT RESOLVED item of `fix-<n>.md`; one you already decided on (fixed, rejected or downgraded) stays decided. **Maximum: `orq config get policy.max_rounds` rounds** — `orq agent -R` refuses the next one (exit 4) and counts per session, so a renamed prefix doesn't reset it; on exit 4 stop and tell the user. With their OK run `orq rounds <ID> --diagnose` (a cheap independent agent, advisory: read its `RECOMMENDATION` — continue / split / change-implementer / ask-user — and tell the user), then `-F` (refused without a diagnosis newer than the last fix/review; `-F -F` skips that, only if the user says so); over it → `orq pick impl
200
232
  --exclude-agent <it> > $O/impl-<repo>.pick` only with the user's OK, or stop and report. Non-blocking suggestions go into the comment; they
201
233
  don't become a round.
202
234
 
235
+ ## Screenshots / visual validation
236
+ Only when the issue asks for it. **One** attempt (a job with `--effort low`, a short prompt that says which screens and
237
+ where to save the images), at most ~6 images, and read them only as files you need to judge — never re-read the same
238
+ image. If it fails, tell the user instead of dispatching a second `shots` job. Playwright runs inside that child's
239
+ worktree, never as an MCP in the conductor (images in the conductor's context are re-billed every turn).
240
+
203
241
  ## 7. Close
204
242
  **Commit/PR conventions belong to the repo, not to you.** The **implementer, in the worktree** (where the repo's
205
243
  rules live) commits and writes the PR. Find and cite in the prompt, by path, whatever the repo has:
@@ -212,6 +250,10 @@ rules live) commits and writes the PR. Find and cite in the prompt, by path, wha
212
250
  issue) and the answer in `$O/impl-<repo>.out.md`. Check the opened PR against the template.
213
251
  - Plane comment (`workitem_comment create`): summary, repo(s), branch, agents and models used (`orq pick` output),
214
252
  review result, checks that ran.
253
+ - **Review bots on the PR** (a GitHub App reviewer): one more input, not a new review cycle. Only a finding with
254
+ high severity, `file:line` and a concrete failure, not already decided in the rounds, goes into a fix round (it
255
+ counts towards `policy.max_rounds`); the rest goes into the Plane comment or a follow-up issue. Don't re-request the
256
+ bot's review to "get it clean".
215
257
  - **CI after every push:** `gh pr checks <n>` (no `--watch`; if pending, end your turn and check on the next notice).
216
258
  Red → `gh run view <id> --log-failed | tail -40`: error from the diff → fix round; error already on the base → create
217
259
  a Plane issue (workspace standard) and tell the user. "Ready to merge" only with green checks or an explained failure
@@ -61,7 +61,7 @@ Offer `$ORQ/examples/irythill/config.toml` as an example, saying clearly **it is
61
61
 
62
62
  ## 4. Policy
63
63
  Explain simply, then suggest the example's values and ask if they want to change any:
64
- - `quota_limit` (90): an agent at or above this % of its 5h/weekly quota is left out.
64
+ - `quota_week` (95) and `quota_5h` (simple 90, medium 85, heavy 80): an agent at or above these % is left out.
65
65
  - weights — already suggested by `orq setup` from the parallel tasks: `quality` (did it deliver?), `tokens` (spent
66
66
  little?), `speed` (finished fast?), `rotation` (penalty for being picked recently, spreads the work).
67
67
  - `max_rounds` (2) review rounds before handing back; `machine.agent_mem_max` / `test_mem_max` come from `orq setup`
@@ -11,6 +11,14 @@ rules — read the repo's own before touching it. This file only holds the map a
11
11
  outside herdr; one at a time on the machine); the child does lint + `tsc` (`NODE_OPTIONS=--max-old-space-size=2048`).
12
12
  - **Agents:** chosen by `orq-pick` from each developer's `~/.config/orq/config.toml` — nothing about models here.
13
13
 
14
+ ## Scope
15
+ - Before dispatching, split big issues into sub-issues or sequential batches (one PR, one implementer each) when they touch
16
+ several unrelated areas, change roughly >400 lines, mix refactor with behavior change, or have independent acceptance
17
+ criteria. Each batch must be testable and reviewable on its own; record the split in Plane before starting.
18
+ - Reviews hitting `policy.max_rounds`, repeated findings or the implementer asking to change scope mean it was too big:
19
+ stop and split the remainder instead of forcing more rounds. `orq rounds <ID> --diagnose` asks an independent agent why
20
+ (advisory); `orq agent -R -F` past `max_rounds` needs that diagnosis (`-F -F` skips it, only on the user's word).
21
+
14
22
  ## Repositories
15
23
 
16
24
  | Folder | What it is | Stack / PM | Group | Base | Verification | Checklist |