harnex 0.10.2 → 0.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 25ba2b12b69cc6351e2ca4ee1e72e5f378a3828f7486498a2e2a026cf9da771b
4
- data.tar.gz: 7e3cc95587c16dcb0a7b9a3e8339546a9ffbe085e4517407f1325e87fd2f45ce
3
+ metadata.gz: 3ea3eab047cc2e9f086fba4cfb98c6f585ffe888fdee0fb018c35515fe6257b4
4
+ data.tar.gz: ba40b96028fd01e6ade7517374e7d26d92a5d147a7be2ec08a581cd0203dae62
5
5
  SHA512:
6
- metadata.gz: fcaa6d99632c375c1315bfc553b99ccf5c583b58a52e627e0af951493481bf33af9b5de50ca4704a1e6e2d7d9a8aa6999b42d94347ff942b76ac5033eb870dae
7
- data.tar.gz: 929011b78aa399cc098b644f0bb2a0995222df71698e450e7ab4785e1be9249e07e4d40225fbf80e02ea9a11f6fefd7913c27682110944ccadacea8f879e7200
6
+ metadata.gz: e1e85c57df13dd411b10308659b72376f9491f1c6e4d7c4e5d906f9f78096dc0f82a0ac866c01e356d75cedb5f285b71970d0ea175e5935ca8ed0e3bcf40dd88
7
+ data.tar.gz: 1283d788ceaff2e608f7e13b7f4f640d65d9f683e35cbe649cc9dd7d0223e094740df146fbe10ed6fa15685bf27671e9059c6378bb2bc8acc5c850aef5f6c9ab
data/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.12.0] - 2026-09-03 | 10:09 AM | IST
4
+
5
+ ### Added
6
+
7
+ - Registered sessions now atomically publish a typed completion marker under
8
+ `$HARNEX_STATE_DIR/done/` at the first accepted, rejected, failed, or error
9
+ work result, without requiring a `wait` or `watch` client.
10
+ - `harnex run --on-done CMD` launches one non-blocking trusted local shell hook
11
+ with typed completion, receipt, commit, and elapsed-time environment values.
12
+ The option works in foreground, detached, and tmux launches.
13
+
14
+ ### Changed
15
+
16
+ - `harnex status` now renders settled live work (`done`, `rejected`, or
17
+ `failed`) ahead of an adapter's prompt/busy state. Existing JSON fields and
18
+ the v2 dispatch envelope remain unchanged.
19
+
20
+ ## [0.11.0] - 2026-08-14 | 01:03 PM | IST
21
+
22
+ ### Fixed
23
+
24
+ - Pi RPC completion now fences on Pi 0.80.4+'s `agent_settled` event instead of
25
+ the lower-level `agent_end`. Provider retries, compaction recovery, and queued
26
+ continuations can no longer publish premature `task_complete` or open the
27
+ inbox early. Final `error`, `aborted`, and `length` stop reasons fail closed;
28
+ a settled run without an authoritative final assistant reason also fails.
29
+ - Pi 0.84's breaking delta-only `message_update` shape is correlated from
30
+ `message_start` through authoritative `message_end`, preserving streamed
31
+ output without duplicating the final message.
32
+ - Harnex `--model` and `--effort` now become supported Pi `--model` /
33
+ `--thinking` startup controls and are verified through RPC `get_state`,
34
+ instead of adding ignored fields to `prompt` or persisting RPC setters into
35
+ the user's Pi defaults. Model mismatches and clamped effort requests fail
36
+ before prompting, telemetry distinguishes requested from observed effective
37
+ model values, and forced busy sends use Pi's `steer` streaming behavior.
38
+ - Pi RPC request waits are bounded, subprocess stderr is continuously drained
39
+ into a bounded diagnostic tail, and process status is read from Open3's wait
40
+ thread instead of racing it with a second `waitpid` and potentially losing a
41
+ nonzero exit status.
42
+
43
+ ### Changed
44
+
45
+ - **Compatibility:** structured Pi dispatch now requires Pi >= 0.80.4. Both
46
+ foreground and detached/tmux launches validate the installed version before
47
+ spawn. This is the first Pi release with the final `agent_settled` fence.
48
+ - Pi retry/compaction events retain settlement state and expose current
49
+ summarization-retry lifecycle events in Harnex's event stream.
50
+ - Codex schema fixtures are refreshed against CLI 0.147.0 after a bounded
51
+ semantic review. The drift is limited to a string path-alias replacement and
52
+ additive optional thread/item fields that Harnex does not consume; existing
53
+ minimal response fixtures still validate, so no Codex adapter behavior
54
+ changes.
55
+
56
+ ### Added
57
+
58
+ - `harnex doctor --adapter pi` statically verifies the Pi RPC version;
59
+ `--adapter all` checks both Pi and Codex while the no-argument default remains
60
+ Codex-compatible.
61
+ - `docs/pi-rpc.md` documents transport semantics, project trust, lifecycle,
62
+ model controls, telemetry, and known boundaries. An opt-in live contract test
63
+ (`PI_INTEGRATION=1`) verifies the installed Pi event shape without adding
64
+ provider cost to the normal suite.
65
+
3
66
  ## [0.10.2] - 2026-08-08 | 06:49 PM | IST
4
67
 
5
68
  Docs-only patch: no library or CLI behavior changes. Released so the
data/README.md CHANGED
@@ -12,8 +12,8 @@ gem install harnex
12
12
 
13
13
  Harnex itself requires **Ruby 3.x** and uses only the Ruby standard
14
14
  library. Install the CLIs you want to wrap separately; Codex JSON-RPC
15
- support requires Codex CLI **0.128.0 or newer**, and tmux-backed
16
- workflows require `tmux`.
15
+ support requires Codex CLI **0.128.0 or newer**, Pi structured RPC requires
16
+ Pi **0.80.4 or newer**, and tmux-backed workflows require `tmux`.
17
17
 
18
18
  Then ask the CLI what to do next:
19
19
 
@@ -23,8 +23,15 @@ harnex --help
23
23
  harnex agents-guide
24
24
  ```
25
25
 
26
- If you use Codex, run `harnex doctor` after installing or upgrading the
27
- Codex CLI. It verifies the local `codex app-server` prerequisite.
26
+ Run the matching static check after installing or upgrading an agent CLI:
27
+
28
+ ```bash
29
+ harnex doctor --adapter codex
30
+ harnex doctor --adapter pi
31
+ ```
32
+
33
+ These verify the local structured-transport version prerequisites without
34
+ starting a model turn.
28
35
 
29
36
  `harnex agents-guide` is the agent-facing reference for dispatch, chain,
30
37
  buddy, monitoring, and naming patterns. It is packaged in the gem; no skills
@@ -106,7 +113,7 @@ an explicit working-directory/root selector for automation.
106
113
  |-------|---------|
107
114
  | Claude Code | PTY adapter with prompt detection, stop sequence, workspace trust, and vim mode handling |
108
115
  | OpenAI Codex | JSON-RPC `codex app-server` adapter by default; PTY mode remains supported for TUI/interactive use via `--legacy-pty` |
109
- | Pi | JSONL RPC adapter (`pi --mode rpc`) with structured completion, tool events, extension-UI auto-cancel, and session stats telemetry |
116
+ | Pi | JSONL RPC adapter (`pi --mode rpc`, Pi >= 0.80.4) with settled completion, typed failures, tool/retry events, extension-UI auto-cancel, and session stats telemetry |
110
117
  | OpenCode | PTY adapter with native Ctrl+C stop handling and OpenCode-specific prompt/readiness heuristics |
111
118
  | Any terminal CLI | Generic PTY wrapping with local API, logs, status, and best-effort prompt detection |
112
119
 
@@ -121,9 +128,15 @@ terminal UI or PTY-only Codex flags. The flag name is historical; the PTY path
121
128
  is still supported.
122
129
 
123
130
  `harnex run pi` launches `pi --mode rpc` and sends `--context` as a structured
124
- `prompt` command (not a CLI positional argument). Pass Pi child flags after the
125
- separator, for example:
131
+ `prompt` command (not a CLI positional argument). Harnex `--model` / `--effort`
132
+ flags become Pi `--model` / `--thinking` startup controls and are verified over
133
+ RPC before the prompt; use a deterministic `provider/model` value. Pass other
134
+ Pi startup flags after the separator, for example:
126
135
  `harnex run pi --context "Implement X" -- --model anthropic/claude-sonnet-4-5 --thinking high`.
136
+ For unattended runs, explicitly choose Pi project trust with child
137
+ `--approve` or `--no-approve`; Harnex never auto-trusts repository code.
138
+ See [docs/pi-rpc.md](docs/pi-rpc.md) for lifecycle, compatibility, and failure
139
+ semantics.
127
140
 
128
141
  ## Multi-agent workflows
129
142
 
@@ -231,6 +244,8 @@ Choose the wait/watch predicate that matches how you launched the worker:
231
244
  `harnex wait --id ID --until task_complete --timeout SECS` when you need the
232
245
  exact successful-turn event instead of terminal-exit fallback. Use
233
246
  `--until task_failed` to wait specifically for a failed structured turn.
247
+ Pi completion is fenced on `agent_settled`, not the earlier `agent_end`, so
248
+ retries, compaction recovery, and queued continuations finish first.
234
249
  - `harnex send --wait-for-idle` is an atomic send fence for PTY-style
235
250
  interactions. It proves the turn returned to an idle/prompt state, not that
236
251
  your acceptance criteria passed.
data/TECHNICAL.md CHANGED
@@ -363,6 +363,8 @@ Each adapter in `lib/harnex/adapters/` implements:
363
363
  | `inject_exit(writer)` | Send adapter-specific stop text |
364
364
  | `infer_repo_path(argv)` | Extract repo path from CLI args |
365
365
  | `wait_for_sendable(...)` | Wait strategy before sending |
366
+ | `transport` | PTY, JSON-RPC, or JSONL RPC |
367
+ | `validate_runtime!` | Optional version/capability gate |
366
368
 
367
369
  ### Input States
368
370
 
@@ -404,6 +406,29 @@ The adapter reads the screen and returns a state hash:
404
406
  - See `docs/codex-appserver.md` for the full mapping table and
405
407
  troubleshooting.
406
408
 
409
+ ### Pi Adapter (JSONL RPC)
410
+
411
+ - `transport :stdio_jsonl_rpc` launches `pi --mode rpc` and speaks Pi's
412
+ strict LF-delimited JSONL protocol. It is not JSON-RPC 2.0.
413
+ - Requires Pi >= 0.80.4 and fails before spawn when the installed version
414
+ cannot provide the final `agent_settled` fence. Verify with
415
+ `harnex doctor --adapter pi`.
416
+ - `agent_end` remains busy because retry, compaction recovery, or a queued
417
+ continuation may follow. `agent_settled` inspects the authoritative final
418
+ assistant `stopReason`: `stop` completes; `error`, `aborted`, `length`, a
419
+ missing reason, or an unknown reason fails closed.
420
+ - Pi >= 0.84 emits delta-only `message_update` events. Harnex correlates those
421
+ deltas with `message_start` / `message_end` and does not depend on the removed
422
+ cumulative message snapshot.
423
+ - Harnex `--model` and `--effort` become Pi `--model` / `--thinking`
424
+ startup flags and are verified with `get_state`; a model mismatch or clamped
425
+ effort is rejected before prompting without persisting an RPC model switch
426
+ into Pi's user defaults. Forced busy sends use `streamingBehavior=steer`.
427
+ - RPC requests have a 30-second response bound. Stderr is continuously drained
428
+ into a bounded diagnostic tail, and subprocess status comes from Open3's wait
429
+ thread instead of racing it with another `waitpid`.
430
+ - See `docs/pi-rpc.md` for trust policy, telemetry, and compatibility details.
431
+
407
432
  #### Codex Adapter (legacy PTY — `--legacy-pty`, long-term fallback)
408
433
 
409
434
  - Launches with `--no-alt-screen` for inline screen output
data/docs/pi-rpc.md ADDED
@@ -0,0 +1,151 @@
1
+ # Pi RPC adapter
2
+
3
+ Harnex runs Pi workers over Pi's structured subprocess protocol rather than
4
+ scraping the TUI:
5
+
6
+ ```text
7
+ harnex run pi -> pi --mode rpc -> LF-delimited JSON objects on stdio
8
+ ```
9
+
10
+ Pi RPC is JSONL, not JSON-RPC 2.0. Codex uses `codex app-server` JSON-RPC;
11
+ Harnex presents the same run/send/wait/events control surface over both.
12
+
13
+ ## Compatibility
14
+
15
+ Harnex requires Pi **0.80.4 or newer** for structured dispatch. Pi 0.80.4 added
16
+ `agent_settled`, the first lifecycle event that guarantees no automatic retry,
17
+ compaction recovery, or queued continuation remains. `agent_end` is only a
18
+ low-level run boundary and is never accepted as Harnex work completion.
19
+
20
+ Check the installed transport before unattended work:
21
+
22
+ ```bash
23
+ harnex doctor --adapter pi
24
+ ```
25
+
26
+ `harnex run pi` also checks the version before spawn and fails closed when the
27
+ version cannot be parsed or predates 0.80.4. The current contract was live
28
+ verified against Pi 0.84.1.
29
+
30
+ Pi 0.84.0 removed cumulative message snapshots from `message_update`. Harnex
31
+ therefore assembles output from `assistantMessageEvent` deltas between
32
+ `message_start` and authoritative `message_end` events. This remains compatible
33
+ with Pi 0.80.4-0.83.x and avoids printing the final message twice on 0.84+.
34
+
35
+ ## Dispatch
36
+
37
+ ```bash
38
+ harnex run pi --id cx-i-42 --tmux cx-i-42 \
39
+ --context "Implement issue 42 and run tests" --auto-stop
40
+
41
+ harnex wait --id cx-i-42 --until task_complete --timeout 3600
42
+ harnex events --id cx-i-42
43
+ ```
44
+
45
+ `--context` is sent as an RPC `prompt` command; it is not passed as a Pi
46
+ positional argument.
47
+
48
+ Harnex model and effort flags are active controls, not telemetry-only labels:
49
+
50
+ ```bash
51
+ harnex run pi \
52
+ --model anthropic/claude-sonnet-4-5 --effort high \
53
+ --context "Review the change" --auto-stop
54
+ ```
55
+
56
+ Harnex maps those controls to Pi `--model` / `--thinking` startup flags, then
57
+ verifies the effective values with RPC `get_state` before prompting. This avoids
58
+ `set_model` / `set_thinking_level` persisting a dispatch override into the
59
+ user's Pi defaults. Use `provider/model` for deterministic dispatch. Harnex
60
+ fails before the prompt if Pi cannot apply the requested model or thinking level
61
+ exactly, so requested and effective policy cannot silently diverge. Direct
62
+ adapter callers can still use Pi's RPC setters for an intentional mid-session
63
+ switch.
64
+
65
+ Pass Pi startup flags after `--`:
66
+
67
+ ```bash
68
+ harnex run pi --context "Implement X" --auto-stop -- \
69
+ --no-session --no-extensions --model anthropic/claude-sonnet-4-5
70
+ ```
71
+
72
+ When both Harnex `--model` and a Pi child `--model` are supplied, the Harnex
73
+ startup override is appended last and is authoritative for that dispatch.
74
+
75
+ ## Project trust
76
+
77
+ Pi 0.79 added project trust. RPC mode cannot show the interactive trust prompt;
78
+ an unresolved project follows Pi's global `defaultProjectTrust` policy. Harnex
79
+ does not auto-trust repository code.
80
+
81
+ For deterministic unattended runs, choose explicitly after reviewing the
82
+ repository:
83
+
84
+ ```bash
85
+ # Load trusted project-local .pi settings, packages, and extensions
86
+ harnex run pi --context "..." -- --approve
87
+
88
+ # Ignore project-local executable resources
89
+ harnex run pi --context "..." -- --no-approve
90
+ ```
91
+
92
+ Context files continue to follow Pi's documented trust behavior. Third-party or
93
+ project extensions execute with the worker's OS privileges; `--approve` is a
94
+ security decision, not merely a prompt-suppression flag.
95
+
96
+ ## Lifecycle and failures
97
+
98
+ Harnex maps Pi events as follows:
99
+
100
+ | Pi event | Harnex behavior |
101
+ |---|---|
102
+ | `agent_start`, `turn_start` | busy |
103
+ | `agent_end` | remain busy; capture outcome/stats, but do not complete |
104
+ | retry/compaction events | remain busy and emit structured telemetry |
105
+ | `agent_settled` + final `stopReason=stop` | emit `task_complete` |
106
+ | `agent_settled` + `error`, `aborted`, or `length` | emit `task_failed` |
107
+ | `agent_settled` without an authoritative final stop reason | fail closed |
108
+ | EOF, malformed JSON, or request timeout | disconnect/failure |
109
+
110
+ A forced send while Pi is busy is translated to a `prompt` with
111
+ `streamingBehavior="steer"`; ordinary sends wait for the settled prompt state.
112
+ This avoids sending a plain prompt that current Pi correctly rejects while
113
+ streaming.
114
+
115
+ RPC requests are bounded, stderr is continuously drained into a bounded
116
+ diagnostic tail, and Harnex consumes Open3's wait thread so the real subprocess
117
+ exit status is not lost to a competing `waitpid`.
118
+
119
+ ## Telemetry
120
+
121
+ At settlement and teardown, Harnex reads `get_session_stats` and records:
122
+
123
+ - input/output/cache/total tokens and provider-reported cost;
124
+ - tool-call count;
125
+ - Pi session ID;
126
+ - effective provider and model;
127
+ - terminal and peak active-context usage.
128
+
129
+ Message/tool/retry/compaction events are available through `harnex events`.
130
+ Blocking extension dialogs (`select`, `confirm`, `input`, `editor`) are
131
+ currently auto-cancelled so autonomous workers cannot hang. Full extension UI
132
+ mediation is not implemented.
133
+
134
+ ## Boundaries
135
+
136
+ - The structured adapter does not embed Pi's TypeScript SDK.
137
+ - `--tmux` displays Harnex's synthesized RPC transcript, not Pi's native TUI.
138
+ - First-class native Pi TUI/PTTY markers remain tracked in Issue `#45`.
139
+ - Durable Pi session recovery is separate from lifecycle correctness and remains
140
+ part of the broader structured-recovery work.
141
+
142
+ ## Verification
143
+
144
+ Hermetic tests cover lifecycle settlement, retry boundaries, failure stop
145
+ reasons, delta-only message streaming, model/effort RPC controls, request
146
+ timeouts, and telemetry. The opt-in live contract test is:
147
+
148
+ ```bash
149
+ PI_INTEGRATION=1 ruby -Ilib -Itest \
150
+ test/harnex/adapters/pi_integration_test.rb
151
+ ```
@@ -87,6 +87,25 @@ for i in 1 2 3; do harnex watch --id w-$i --until done --max-wait 90m & done
87
87
  wait
88
88
  ```
89
89
 
90
+ For unattended work, add a session-owned push signal rather than depending on
91
+ one long blocking watcher call:
92
+
93
+ ```bash
94
+ harnex run pi --id pi-i-NN --tmux pi-i-NN \
95
+ --context "Read and execute /tmp/task-NN.md" --auto-stop \
96
+ --on-done 'printf "%s %s\n" "$HARNEX_ID" "$HARNEX_OUTCOME" >> koder/scratch/HARNEX_WAKE.txt'
97
+ ```
98
+
99
+ Every registered session writes
100
+ `$HARNEX_STATE_DIR/done/<repo-key>--<normalized-id>.<outcome>`, even without a
101
+ hook. `--on-done CMD` receives `HARNEX_ID`, `HARNEX_OUTCOME`,
102
+ `HARNEX_WORK_STATE`, `HARNEX_RECEIPT_PATH`, `HARNEX_END_SHA`, and
103
+ `HARNEX_ELAPSED_S`. Use a gitignored wake file. `CMD` is trusted `/bin/sh -c`
104
+ input; never embed secrets in the command line.
105
+
106
+ A push only wakes the consumer. Even `completed` requires report, artifact,
107
+ test, and Git verification plus bounded `harnex watch --until done` calls.
108
+
90
109
  Rule: when you use `--tmux`, pass the same name as `--id`. If you pass only
91
110
  `--tmux NAME`, harnex creates a random session ID and the pane name no longer
92
111
  matches `harnex status` or `harnex pane --id`.
@@ -148,8 +167,13 @@ to the canonical repo/global dispatch stream, which is the only telemetry
148
167
  destination. Repo `.harnex/config.json` can warn on or reject non-canonical
149
168
  phase names before spawn.
150
169
 
151
- Pi runs use structured RPC (`pi --mode rpc`). Pass Pi child flags after `--`
152
- (e.g. `harnex run pi --context "..." -- --model anthropic/claude-sonnet-4-5 --thinking high`).
170
+ Pi runs use structured RPC (`pi --mode rpc`) and require Pi >= 0.80.4; gate
171
+ unattended work with `harnex doctor --adapter pi`. Harnex `--model` / `--effort`
172
+ apply verified Pi startup controls before the prompt. Pass other Pi startup
173
+ flags after `--` (e.g. `harnex run pi --context "..." -- --model
174
+ anthropic/claude-sonnet-4-5 --thinking high`).
175
+ Since RPC cannot display Pi's trust prompt, explicitly pass child `--approve`
176
+ for reviewed project-local resources or `--no-approve` to ignore them.
153
177
 
154
178
  Codex flag forms differ between transports. The default JSON-RPC adapter
155
179
  (`codex app-server`) does not accept `-m`/`--model`; pass the model as
@@ -17,12 +17,30 @@ Prefer signals in this order:
17
17
  | `harnex pane` | Live UI interpretation and prompt/error diagnosis |
18
18
  | `harnex status` | Session liveness and coarse state |
19
19
 
20
- For unattended monitors on existing visible/detached sessions, prefer
21
- `harnex watch --until done`: it returns on the work-level `task_complete` or
20
+ For unattended sessions, combine bounded watcher calls with the runner-owned
21
+ push path. Every session writes
22
+ `$HARNEX_STATE_DIR/done/<repo-key>--<id>.<outcome>` at its first work-terminal
23
+ result. `harnex run --on-done CMD` can also launch one non-blocking local hook,
24
+ even without a watcher and while the agent remains alive at a prompt:
25
+
26
+ ```bash
27
+ harnex run pi --id pi-i-NN --tmux pi-i-NN --context "Read the task brief" \
28
+ --auto-stop \
29
+ --on-done 'printf "%s %s\n" "$HARNEX_ID" "$HARNEX_OUTCOME" >> koder/scratch/HARNEX_WAKE.txt'
30
+ harnex watch --id pi-i-NN --until done --max-wait 30m
31
+ ```
32
+
33
+ Use a gitignored wake destination. `CMD` is trusted shell input; never embed
34
+ secrets. It receives `HARNEX_ID`, `HARNEX_OUTCOME`, `HARNEX_WORK_STATE`,
35
+ `HARNEX_RECEIPT_PATH`, `HARNEX_END_SHA`, and `HARNEX_ELAPSED_S`. `completed`
36
+ wakes the consumer but does not certify the prose; verify the receipt and
37
+ artifact before acting.
38
+
39
+ `harnex watch --until done` returns on the work-level `task_complete` or
22
40
  `task_failed` signal, or terminal exit, whichever comes first. Successful work
23
41
  exits `0`, failed work exits non-zero, and wall-clock caps exit `124`. For
24
42
  callers that need the lower-level primitive, `harnex wait --until done` exposes
25
- the same work fence.
43
+ the same work fence. Do not park an orchestrator in one unbounded watcher call.
26
44
 
27
45
  ## Live-Run Visibility
28
46
 
@@ -31,8 +49,10 @@ Every dispatch appends a `dispatch_start` row to the repo's dispatch stream
31
49
  completes it. Between those two rows the run is visible to every documented
32
50
  signal, from any cwd in the same repo:
33
51
 
34
- - `harnex status --id X` reports `state=running` for a live session. When the
35
- live HTTP status API is unreachable it still reports running from the
52
+ - `harnex status --id X` reports settled work as `done`, `rejected`, or
53
+ `failed` even while the adapter is back at `prompt`. When work is unsettled,
54
+ it reports the adapter input state. If the live HTTP status API is
55
+ unreachable it still reports running from the
36
56
  registry (or, failing that, from the uncompleted start row) and labels the
37
57
  row `degraded: true` with `source` set to `registry` or `dispatch_start`.
38
58
  - `harnex history` shows uncompleted dispatches as `running` (pid alive) or
@@ -68,8 +88,11 @@ named parent is still running in the same repo is refused. Wait for the parent
68
88
  (`harnex wait --id <parent> --until done`) or stop it first. Pass
69
89
  `--allow-live-parent` only for intentional parallelism (e.g. isolated
70
90
  worktrees). `--attempt-kind review` is exempt: a completed parent may still
71
- sit at a live prompt while its work is reviewed. For structured sessions (Pi RPC and Codex app-server),
72
- `harnex wait --until task_complete` remains the exact accepted-turn fence.
91
+ sit at a live prompt while its work is reviewed. For structured sessions (Pi
92
+ RPC and Codex app-server), `harnex wait --until task_complete` remains the exact
93
+ accepted-turn fence. Pi reaches that fence only at `agent_settled`; an earlier
94
+ `agent_end` can still be followed by retry, compaction recovery, or queued work.
95
+ Final Pi `error`, `aborted`, and `length` stop reasons emit `task_failed`.
73
96
  Codex acknowledgment-only auto-stop turns are typed
74
97
  `completed_no_activity` and fail this fence without transcript parsing. Harnex
75
98
  writes the observed-state receipt before publishing `task_complete`; optional
@@ -205,7 +228,8 @@ interpretation.
205
228
  - Polling `state=completed` alone and missing live sessions with `task_complete=true`.
206
229
  - Polling `state=prompt` alone and calling it done.
207
230
  - Wrapping `harnex wait` in loops that swallow non-zero `task_failed` results.
208
- - Blocking orchestrators on `/tmp/*-done.txt` as the only completion signal.
231
+ - Blocking orchestrators on caller-owned `/tmp/*-done.txt` as the only completion signal.
232
+ - Using one long watcher call without the runner-owned marker or `--on-done` push path.
209
233
  - Letting an unattended loop run with no wall-clock cap.
210
234
  - Reading raw tmux panes instead of `harnex pane`.
211
235
  - Using `--wait-for-idle` as acceptance proof.