harnex 0.3.3 → 0.4.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.
@@ -6,7 +6,7 @@ module Harnex
6
6
  class Session
7
7
  OUTPUT_BUFFER_LIMIT = 64 * 1024
8
8
 
9
- attr_reader :repo_root, :host, :port, :session_id, :token, :command, :pid, :id, :adapter, :watch, :inbox, :description, :output_log_path
9
+ attr_reader :repo_root, :host, :port, :session_id, :token, :command, :pid, :id, :adapter, :watch, :inbox, :description, :output_log_path, :events_log_path
10
10
 
11
11
  def initialize(adapter:, command:, repo_root:, host:, port: nil, id: DEFAULT_ID, watch: nil, description: nil, inbox_ttl: Inbox::DEFAULT_TTL)
12
12
  @adapter = adapter
@@ -19,17 +19,21 @@ module Harnex
19
19
  @description = nil if @description.empty?
20
20
  @registry_path = Harnex.registry_path(repo_root, @id)
21
21
  @output_log_path = Harnex.output_log_path(repo_root, @id)
22
+ @events_log_path = Harnex.events_log_path(repo_root, @id)
22
23
  @session_id = SecureRandom.hex(8)
23
24
  @token = SecureRandom.hex(16)
24
25
  @port = Harnex.allocate_port(repo_root, @id, port, host: host)
25
26
  @mutex = Mutex.new
26
27
  @inject_mutex = Mutex.new
28
+ @events_mutex = Mutex.new
27
29
  @injected_count = 0
28
30
  @last_injected_at = nil
29
31
  @started_at = Time.now
30
32
  @server = nil
31
33
  @reader = nil
32
34
  @output_log = nil
35
+ @events_log = nil
36
+ @events_log_seq = 0
33
37
  @writer = nil
34
38
  @pid = nil
35
39
  @term_signal = nil
@@ -60,8 +64,10 @@ module Harnex
60
64
  def run(validate_binary: true)
61
65
  validate_binary! if validate_binary
62
66
  prepare_output_log
67
+ prepare_events_log
63
68
  @reader, @writer, @pid = PTY.spawn(child_env, *command)
64
69
  @writer.sync = true
70
+ emit_event("started", pid: @pid)
65
71
 
66
72
  install_signal_handlers
67
73
  sync_window_size
@@ -78,6 +84,7 @@ module Harnex
78
84
  _, status = Process.wait2(pid)
79
85
  @term_signal = status.signaled? ? status.termsig : nil
80
86
  @exit_code = status.exited? ? status.exitstatus : 128 + status.termsig
87
+ emit_exit_event
81
88
 
82
89
  output_thread.join(1)
83
90
  input_thread&.kill
@@ -91,6 +98,7 @@ module Harnex
91
98
  cleanup_registry
92
99
  @reader&.close unless @reader&.closed?
93
100
  @output_log&.close unless @output_log&.closed?
101
+ @events_log&.close unless @events_log&.closed?
94
102
  @writer&.close unless @writer&.closed?
95
103
  end
96
104
 
@@ -109,8 +117,10 @@ module Harnex
109
117
  started_at: @started_at.iso8601,
110
118
  last_injected_at: @last_injected_at&.iso8601,
111
119
  injected_count: @injected_count,
112
- output_log_path: output_log_path
120
+ output_log_path: output_log_path,
121
+ events_log_path: events_log_path
113
122
  }
123
+ payload.merge!(log_activity_snapshot)
114
124
  payload[:description] = description if description
115
125
 
116
126
  if watch
@@ -168,6 +178,7 @@ module Harnex
168
178
  input_state: payload[:input_state],
169
179
  force: payload[:force]
170
180
  )
181
+ .tap { emit_send_event(text, force: payload[:force]) }
171
182
  end
172
183
 
173
184
  def sync_window_size
@@ -327,6 +338,14 @@ module Harnex
327
338
  @output_log_failed = false
328
339
  end
329
340
 
341
+ def prepare_events_log
342
+ @events_log&.close unless @events_log&.closed?
343
+ @events_log = File.open(events_log_path, "ab")
344
+ @events_log.sync = true
345
+ @events_log_failed = false
346
+ @events_log_seq = 0
347
+ end
348
+
330
349
  def install_signal_handlers
331
350
  %w[INT TERM HUP QUIT].each do |signal_name|
332
351
  Signal.trap(signal_name) { forward_signal(signal_name) }
@@ -364,6 +383,60 @@ module Harnex
364
383
  warn("harnex: failed to write output log #{output_log_path}: #{e.message}")
365
384
  end
366
385
 
386
+ def emit_send_event(text, force:)
387
+ compact = text.to_s
388
+ truncated = compact.length > 200
389
+ preview = truncated ? "#{compact[0, 200]}…" : compact
390
+ emit_event("send", msg: preview, msg_truncated: truncated, forced: !!force)
391
+ end
392
+
393
+ def emit_exit_event
394
+ payload = { code: @exit_code }
395
+ payload[:signal] = @term_signal if @term_signal
396
+ emit_event("exited", **payload)
397
+ end
398
+
399
+ def emit_event(type, **payload)
400
+ @events_mutex.synchronize do
401
+ return unless @events_log
402
+
403
+ @events_log_seq += 1
404
+ event = {
405
+ schema_version: 1,
406
+ seq: @events_log_seq,
407
+ ts: Time.now.utc.iso8601,
408
+ id: id,
409
+ type: type
410
+ }.merge(payload)
411
+ @events_log.write(JSON.generate(event))
412
+ @events_log.write("\n")
413
+ @events_log.flush
414
+ end
415
+ rescue StandardError => e
416
+ return if defined?(@events_log_failed) && @events_log_failed
417
+
418
+ @events_log_failed = true
419
+ warn("harnex: failed to write events log #{events_log_path}: #{e.message}")
420
+ end
421
+
422
+ def log_activity_snapshot
423
+ return { log_mtime: nil, log_idle_s: nil } unless File.file?(output_log_path)
424
+ return { log_mtime: nil, log_idle_s: nil } if File.size?(output_log_path).nil?
425
+
426
+ mtime = File.mtime(output_log_path)
427
+ idle_seconds = (Time.now - mtime).to_i
428
+ idle_seconds = 0 if idle_seconds.negative?
429
+ {
430
+ log_mtime: mtime.iso8601,
431
+ log_idle_s: idle_seconds
432
+ }
433
+ rescue StandardError
434
+ {
435
+ log_mtime: nil,
436
+ log_idle_s: nil
437
+ }
438
+ end
439
+
367
440
  def screen_snapshot
368
441
  @mutex.synchronize { @output_buffer.dup }
369
442
  end
@@ -1,4 +1,4 @@
1
1
  module Harnex
2
- VERSION = "0.3.3"
3
- RELEASE_DATE = "2026-04-23"
2
+ VERSION = "0.4.0"
3
+ RELEASE_DATE = "2026-04-30"
4
4
  end
data/lib/harnex.rb CHANGED
@@ -12,12 +12,15 @@ require_relative "harnex/runtime/inbox"
12
12
  require_relative "harnex/runtime/file_change_hook"
13
13
  require_relative "harnex/runtime/api_server"
14
14
  require_relative "harnex/runtime/session"
15
+ require_relative "harnex/commands/watch"
16
+ require_relative "harnex/commands/watch_presets"
15
17
  require_relative "harnex/commands/run"
16
18
  require_relative "harnex/commands/send"
17
19
  require_relative "harnex/commands/wait"
18
20
  require_relative "harnex/commands/stop"
19
21
  require_relative "harnex/commands/status"
20
22
  require_relative "harnex/commands/logs"
23
+ require_relative "harnex/commands/events"
21
24
  require_relative "harnex/commands/pane"
22
25
  require_relative "harnex/commands/recipes"
23
26
  require_relative "harnex/commands/guide"
@@ -1,348 +1,20 @@
1
1
  ---
2
2
  name: harnex
3
- description: Collaborate with other AI agents (Codex, Claude) via harnex. Use when the user asks to send a message to another agent, check agent sessions, spawn workers, relay instructions, or coordinate multi-agent work. Also activates when incoming messages contain "[harnex relay" headers.
4
- allowed-tools: Bash(harnex *)
3
+ description: Deprecated alias for harnex-dispatch. Use harnex-dispatch for active harnex collaboration guidance.
5
4
  ---
6
5
 
7
- # Harnex Cross-Agent Collaboration
6
+ # Deprecated Skill Alias
8
7
 
9
- If this is your first time using harnex, run `harnex guide` for the full
10
- getting started walkthrough, and `harnex recipes` for tested workflow patterns.
8
+ `harnex` is deprecated.
11
9
 
12
- Harnex wraps interactive terminal agents (Claude Code, Codex) and opens a local
13
- control plane so they can discover and message each other. You use it to **send
14
- messages to a peer agent**, **check session status**, **spawn worker sessions**,
15
- and **wait for them to finish**.
10
+ Use `harnex-dispatch` as the canonical skill for harnex collaboration,
11
+ Fire & Watch dispatching, relay handling, and return-channel discipline.
16
12
 
17
- ## Detect your context
18
-
19
- Check environment variables to understand your role:
20
-
21
- | Variable | Meaning |
22
- |----------|---------|
23
- | `HARNEX_SESSION_CLI` | Which CLI you are (`claude` or `codex`) |
24
- | `HARNEX_ID` | Your session ID |
25
- | `HARNEX_SESSION_REPO_ROOT` | Repo root this session is scoped to |
26
- | `HARNEX_SESSION_ID` | Internal instance identifier |
27
- | `HARNEX_SPAWNER_PANE` | Tmux pane ID (`%N`) of whoever spawned this session |
28
-
29
- If these are set, you are **inside a harnex session** and can send messages to
30
- peer sessions or spawn new worker sessions.
31
-
32
- `HARNEX_SPAWNER_PANE` is the stable tmux pane ID of the invoker — use it to
33
- reach back to the session that launched you, even if that session is not
34
- harnex-managed:
35
-
36
- ```bash
37
- # Read the invoker's screen
38
- tmux capture-pane -t "$HARNEX_SPAWNER_PANE" -p
39
-
40
- # Type into the invoker
41
- tmux send-keys -t "$HARNEX_SPAWNER_PANE" "done — results in /tmp/result.md" Enter
42
- ```
43
-
44
- ## Mode preference
45
-
46
- When starting another agent session for the user, default to a visible tmux
47
- session via `harnex run <cli> --tmux`. That is the preferred interactive mode
48
- because the user can watch the peer's work live.
49
-
50
- Use other modes only when the user asks for them or when visibility is not
51
- wanted:
52
-
53
- - prefer `--tmux` over a hidden foreground PTY for peer-agent work
54
- - use plain foreground `harnex run` only when the current terminal is meant to
55
- become that peer's UI
56
- - use `--detach` only for explicitly headless/background workflows
57
-
58
- ## Return channel first
59
-
60
- Before you start a peer session or send it work, decide how the result will get
61
- back to you.
62
-
63
- Preferred pattern when you are inside harnex:
64
- - Use your own `HARNEX_ID` as the return address
65
- - Tell the peer to send its final result back with `harnex send --id <YOUR_ID>`
66
- - Wait for the peer's reply; do not rely on scraping logs or tmux panes as the
67
- primary way to collect the answer
68
-
69
- Fallback when you are not inside harnex:
70
- - Define another explicit return channel before delegating, such as a known file
71
- path in the repo
72
-
73
- Do not launch a worker/reviewer without an explicit completion contract.
74
-
75
- ## Two rules for every send
76
-
77
- **1. Keep messages short — use file references for long prompts.**
78
- Long inline prompts can stall delivery (PTY buffer limits) and break shell
79
- quoting. Instead, write the full task to a file and tell the peer to read it:
80
-
81
- ```bash
82
- # Write the task to a file
83
- cat > /tmp/task-impl1.md <<'EOF'
84
- Implement phase 2 from koder/plans/03_output_streaming.md.
85
- ... detailed instructions ...
86
- EOF
87
-
88
- # Send a short message pointing to it
89
- harnex send --id impl-1 --message "Read and execute /tmp/task-impl1.md. When done, send results back: harnex send --id $HARNEX_ID --message '<summary>'"
90
- ```
91
-
92
- If the task is already written down (a plan file, issue, koder doc), just
93
- reference it directly — no need for a temp file.
94
-
95
- **2. Always tell the peer how to reply.**
96
- Every delegated task must include a return path. Without it, the peer finishes
97
- silently and you have no way to collect the result:
98
-
99
- ```bash
100
- # Always end with the reply instruction
101
- harnex send --id impl-1 --message "Review src/auth.rb. When done: harnex send --id $HARNEX_ID --message '<your findings>'"
102
- ```
103
-
104
- ## Core commands
105
-
106
- ### Send a message to a peer agent
107
-
108
- ```bash
109
- harnex send --id <ID> --message "<text>"
110
- ```
111
-
112
- - `--id` targets a specific session by its unique ID
113
- - `--message` is the prompt text injected into the peer's terminal
114
- - Message is auto-submitted (peer receives it as a prompt)
115
- - `--no-submit` types without pressing Enter
116
- - `--force` sends even if peer UI is not at a prompt (bypasses queue)
117
- - `--submit-only` sends only Enter (submit what's already in the input box)
118
- - `--wait-for-idle` blocks until the agent finishes processing (prompt→busy→prompt)
119
- - `--no-wait` returns immediately with a message_id (don't wait for delivery)
120
- - `--cli` filters by CLI type when multiple sessions share resolution scope
121
-
122
- When the target agent is busy, the message is **queued** (HTTP 202) and
123
- delivered automatically when the agent returns to a prompt. The sender polls
124
- until delivery completes using one overall `--timeout` budget (default 120s).
125
-
126
- ### Atomic send+wait (recommended for orchestration)
127
-
128
- Use `--wait-for-idle` instead of separate `send` + `sleep` + `wait` commands:
13
+ If you are installing skills, use:
129
14
 
130
15
  ```bash
131
- # Instead of:
132
- harnex send --id cx-1 --message "implement the plan"
133
- sleep 5
134
- harnex wait --id cx-1 --until prompt --timeout 600
135
-
136
- # Use:
137
- harnex send --id cx-1 --message "implement the plan" --wait-for-idle --timeout 600
138
- ```
139
-
140
- This eliminates the race condition where `wait --until prompt` sees the stale
141
- prompt state before the agent starts working. The `--timeout` budget covers
142
- the entire lifecycle (lookup + send + idle wait).
143
-
144
- **Multi-line messages** (when a file reference isn't practical): use a heredoc:
145
-
146
- ```bash
147
- harnex send --id worker-1 --message "$(cat <<'EOF'
148
- Line one of the message.
149
- Line two of the message.
150
- EOF
151
- )"
152
- ```
153
-
154
- ### Check session status
155
-
156
- ```bash
157
- harnex status # sessions for current repo
158
- harnex status --all # sessions across all repos
159
- ```
160
-
161
- Shows live sessions with their ID, CLI, port, PID, age, and input state.
162
-
163
- ### Inspect a specific session
164
-
165
- ```bash
166
- harnex status --id <ID> --json
167
- ```
168
-
169
- Returns JSON with input state, agent state, inbox stats, description,
170
- watch config, and timestamps.
171
-
172
- ### Only when explicitly requested: spawn a detached worker session
173
-
174
- ```bash
175
- # Headless (no terminal)
176
- harnex run codex --id impl-1 --detach -- --cd /path/to/worktree
177
-
178
- # In a tmux window (observable)
179
- harnex run codex --id impl-1 --tmux cx-p1 -- --cd /path/to/worktree
16
+ harnex skills install harnex-dispatch
180
17
  ```
181
18
 
182
- - `--detach` starts the session in the background, returns JSON with pid/port
183
- - `--tmux` creates a tmux window (implies `--detach`)
184
- - `--tmux NAME` sets a custom window title (keep names terse: `cx-p3`, `cl-r3`)
185
- - `--context TEXT` sets an initial prompt with session ID auto-included
186
- - `--description TEXT` stores a short session description in the registry/API
187
- - Returns immediately; use `harnex send` to inject work, `harnex wait` to block
188
-
189
- Do this only if the user explicitly asks for detached/background execution.
190
-
191
- #### Using `--context` to orient spawned agents
192
-
193
- `--context` prepends a context string as the agent's initial prompt, with the
194
- session ID automatically included as `[harnex session id=<ID>]`. The spawner
195
- decides what context to provide — harnex only adds the session ID.
196
-
197
- ```bash
198
- # Fire-and-forget: give the task upfront
199
- harnex run codex --id impl-1 --tmux cx-p1 \
200
- --context "Implement the feature in koder/plans/03_auth.md. Commit when done." \
201
- -- --cd /path/to/worktree
202
-
203
- # Fire-and-wait: give context, then send work separately
204
- harnex run codex --id reviewer --tmux cx-rv \
205
- --context "You are a code reviewer. Wait for instructions via harnex relay messages." \
206
- -- --cd /path/to/repo
207
- harnex send --id reviewer --message "Review the changes in src/auth.rb"
208
- harnex wait --id reviewer
209
- ```
210
-
211
- The context string is the spawner's responsibility — tailor it to the use case.
212
-
213
- ### Wait for a session to exit
214
-
215
- ```bash
216
- harnex wait --id impl-1
217
- harnex wait --id impl-1 --timeout 300
218
- ```
219
-
220
- Blocks until the session process exits. Returns JSON with exit code and timing.
221
- Exit code 124 on timeout.
222
-
223
- ## Relay headers
224
-
225
- When you send from inside a harnex session to a **different** session, harnex
226
- automatically prepends a relay header:
227
-
228
- ```
229
- [harnex relay from=claude id=supervisor at=2026-03-14T12:00:00+04:00]
230
- <your message>
231
- ```
232
-
233
- The peer sees this header and knows the message came from another agent. When
234
- you **receive** a message with a `[harnex relay ...]` header, treat it as a
235
- prompt from the peer agent — read the body and respond to it.
236
-
237
- Control relay behavior:
238
- - `--relay` forces the header even outside a session
239
- - `--no-relay` suppresses the header
240
-
241
- ## Collaboration patterns
242
-
243
- ### Reply to a peer
244
-
245
- When the user (or a relay message) asks you to reply to the other agent:
246
-
247
- ```bash
248
- harnex send --id <TARGET_ID> --message "Your response here"
249
- ```
250
-
251
- ### Delegate work and get the answer back
252
-
253
- When you ask a peer agent to do work, include the return path in the task
254
- itself.
255
-
256
- Preferred when you are inside harnex:
257
-
258
- ```bash
259
- harnex send --id reviewer --message "$(cat <<EOF
260
- Review the current working tree.
261
-
262
- Return your final findings to me with:
263
- harnex send --id $HARNEX_ID --message '<findings>'
264
-
265
- Use findings-first format with file paths and line numbers where possible.
266
- EOF
267
- )"
268
- ```
269
-
270
- Then wait for the peer to answer you. Do not assume you can reconstruct the
271
- result later from detached logs or tmux capture.
272
-
273
- ### Supervisor pattern
274
-
275
- Use this only when the user explicitly wants detached/background workers.
276
-
277
- A supervisor session spawns workers, sends them tasks, and waits for completion:
278
-
279
- ```bash
280
- # Spawn workers
281
- harnex run codex --id impl-1 --tmux cx-p1 -- --cd ~/repo/wt-feature-a
282
- harnex run codex --id impl-2 --tmux cx-p2 -- --cd ~/repo/wt-feature-b
283
-
284
- # Send work and wait for completion (atomic)
285
- harnex send --id impl-1 --message "implement plan 150" --wait-for-idle --timeout 600
286
- harnex send --id impl-2 --message "implement plan 151" --wait-for-idle --timeout 600
287
-
288
- # Review phase
289
- harnex run claude --id review-1 --tmux cl-r1
290
- harnex send --id review-1 --message "review changes in wt-feature-a"
291
- harnex wait --id review-1
292
- ```
293
-
294
- ### File watch hook
295
-
296
- Sessions can watch a shared file (e.g. `--watch ./tmp/tick.jsonl`). When the
297
- file changes, harnex injects a `file-change-hook: read <path>` message. If you
298
- receive this hook, read the file and act on its contents.
299
-
300
- ## Buddy pattern — accountability for long-running work
301
-
302
- For any work that will take a long time (overnight pipelines, multi-hour
303
- implementations, unattended batch jobs), spawn a **buddy** — a second harnex
304
- session that watches the worker and nudges it if it stalls.
305
-
306
- ```bash
307
- # Spawn the worker
308
- harnex run codex --id worker-42 --tmux worker-42
309
- harnex send --id worker-42 --message "Read and execute /tmp/task-42.md"
310
-
311
- # Spawn a buddy to watch it
312
- harnex run claude --id buddy-42 --tmux buddy-42
313
- harnex send --id buddy-42 --message "Read and execute /tmp/buddy-42.md"
314
- ```
315
-
316
- The buddy's prompt tells it: poll `harnex pane` and `harnex status` every N
317
- minutes, nudge with `harnex send` if the worker stalls, report back to
318
- `$HARNEX_SPAWNER_PANE` when done.
319
-
320
- See `recipes/03_buddy.md` for the full pattern.
321
-
322
- **When to spawn a buddy:**
323
- - The user says "do this overnight" or "run this while I'm away"
324
- - The task is expected to take more than 30 minutes unattended
325
- - The user explicitly asks for a buddy or accountability partner
326
-
327
- **When NOT to spawn a buddy:**
328
- - Short tasks you're actively watching
329
- - The user hasn't asked for long-running autonomy
330
-
331
- ## Important rules
332
-
333
- 1. **Always confirm with the user before sending** unless they explicitly asked
334
- you to send a specific message. Sending injects a prompt into the peer's
335
- terminal — it's an action visible to others.
336
- 2. **Never auto-loop** relay conversations. One send per user request unless
337
- told otherwise.
338
- 3. **Check status first** if unsure whether a peer is running: `harnex status`
339
- 4. **Use `--force` sparingly** — it bypasses the inbox queue and adapter
340
- readiness checks. Can corrupt peer input if it's mid-response.
341
- 5. **Relay headers are automatic** when sending from inside a session. Don't
342
- manually prepend them.
343
- 6. When composing a message to send, be concise and actionable — the peer agent
344
- receives it as a prompt and will act on it.
345
- 7. Do not spawn detached or tmux-backed sessions unless the user explicitly
346
- asked for detached/background execution.
347
- 8. Before delegating work, define the result return path. Prefer a reply back to
348
- your own `HARNEX_ID` over logs, pane capture, or other indirect collection.
19
+ Compatibility note: `harnex skills install harnex` remains supported and
20
+ installs `harnex-dispatch`.
@@ -8,9 +8,17 @@ description: Spawn an accountability partner for long-running harnex sessions. U
8
8
  For any long-running or unattended work, spawn a **buddy** — a second harnex
9
9
  agent that watches the worker and nudges it if it stalls.
10
10
 
11
+ For plain stall recovery (force-resume on inactivity), prefer
12
+ `harnex run --watch --preset impl`. Use a buddy when you need reasoning that
13
+ policy checks cannot provide (doc drift, semantic checks, multi-session
14
+ correlation).
15
+
11
16
  The buddy is an LLM, so it has intelligence for free. It reads the worker's
12
17
  screen, reasons about whether it's stuck, and composes a meaningful nudge.
13
18
 
19
+ Dispatch workers via `harnex-dispatch` first. This skill owns only buddy
20
+ behavior after the worker is already running.
21
+
14
22
  ## When to activate
15
23
 
16
24
  - User says "do this overnight" or "run this while I'm away"
@@ -20,12 +28,11 @@ screen, reasons about whether it's stuck, and composes a meaningful nudge.
20
28
 
21
29
  ## Spawn the buddy
22
30
 
23
- After dispatching the worker, spawn a buddy alongside it:
31
+ After dispatching the worker with `harnex-dispatch`, spawn a buddy alongside
32
+ it. Keep ID/tmux naming consistent with `harnex-dispatch` (`--tmux` matches
33
+ `--id`):
24
34
 
25
35
  ```bash
26
- # Worker already running
27
- harnex run codex --id worker-42 --tmux worker-42
28
-
29
36
  # Spawn its buddy
30
37
  harnex run claude --id buddy-42 --tmux buddy-42
31
38
  ```
@@ -36,17 +43,17 @@ Write a task file with the watching instructions, then send it:
36
43
 
37
44
  ```bash
38
45
  cat > /tmp/buddy-42.md <<'EOF'
39
- You are an accountability partner for harnex session `worker-42`.
46
+ You are an accountability partner for harnex session `cx-impl-42`.
40
47
 
41
48
  Your job:
42
49
  1. Every 5 minutes, check on the worker:
43
- - `harnex pane --id worker-42 --lines 30`
44
- - `harnex status --id worker-42 --json`
50
+ - `harnex pane --id cx-impl-42 --lines 20`
51
+ - `harnex status --id cx-impl-42 --json`
45
52
  2. If the worker appears stuck at a prompt for more than 10 minutes
46
53
  with no progress, nudge it:
47
- - `harnex send --id worker-42 --message "You appear to have stalled. Continue with your current task."`
54
+ - `harnex send --id cx-impl-42 --message "You appear to have stalled. Continue with your current task."`
48
55
  3. If the worker has exited, report back to the invoker:
49
- - `tmux send-keys -t "$HARNEX_SPAWNER_PANE" "worker-42 has exited. Check results." Enter`
56
+ - `tmux send-keys -t "$HARNEX_SPAWNER_PANE" "cx-impl-42 has exited. Check results." Enter`
50
57
  4. Keep watching until the worker finishes or is stopped.
51
58
 
52
59
  Do not interfere with work in progress. Only nudge when clearly stalled.
@@ -76,12 +83,8 @@ The invoker does NOT need to be a harnex session. It just needs to be in tmux.
76
83
 
77
84
  ## Naming convention
78
85
 
79
- | Role | ID pattern | Example |
80
- |------|-----------|---------|
81
- | Worker | `worker-NN` | `worker-42` |
82
- | Buddy | `buddy-NN` | `buddy-42` |
83
-
84
- Match the buddy ID to the worker it watches.
86
+ Use naming from `harnex-dispatch`: set `--tmux <same-as-id>` for every
87
+ session and keep the buddy ID paired with the worker step ID.
85
88
 
86
89
  ## Cleanup
87
90
 
@@ -93,6 +96,8 @@ harnex stop --id buddy-42
93
96
 
94
97
  ## Notes
95
98
 
99
+ - For chain orchestration, phase gates, and the 5-concurrent parallel planning
100
+ cap, see `harnex-chain`.
96
101
  - One buddy per worker, or one buddy watching multiple sessions
97
102
  - The buddy is a regular harnex session — stop, inspect, log it like any other
98
103
  - Tune polling and thresholds in the buddy's prompt, not in harnex config