harnex 0.8.0 → 0.10.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.
@@ -22,9 +22,11 @@ Inside a harnex-managed session, these environment variables are available:
22
22
  | `HARNEX_SESSION_REPO_ROOT` | Repo root for the session |
23
23
  | `HARNEX_SESSION_ID` | Internal harnex instance ID |
24
24
  | `HARNEX_SPAWNER_PANE` | Tmux pane ID of the invoker |
25
- | `HARNEX_ARTIFACT_REPORT_PATH` | Absolute configured proof-sidecar path |
26
- | `HARNEX_ARTIFACT_REPORT_SCHEMA` | Required sidecar schema identifier |
27
- | `HARNEX_ARTIFACT_REPORT_REQUIRED` | `1` when report proof is fail-closed |
25
+ | `HARNEX_ARTIFACT_REPORT_PATH` | Absolute harness-owned final receipt path |
26
+ | `HARNEX_ARTIFACT_CLAIMS_PATH` | Optional bounded worker-claims input path |
27
+ | `HARNEX_ARTIFACT_REPORT_SCHEMA` | Receipt schema identifier |
28
+ | `HARNEX_ARTIFACT_REPORT_MODE` | `observed_state` for harness-authored receipts |
29
+ | `HARNEX_ARTIFACT_REPORT_REQUIRED` | `1` when the compatibility strict flag was passed |
28
30
 
29
31
  Use `harnex send`, `harnex status`, `harnex wait`, `harnex pane`, and
30
32
  `harnex logs` to coordinate with peers. If you are not inside harnex,
@@ -69,8 +71,8 @@ harnex run pi --id pi-i-NN --tmux pi-i-NN \
69
71
  For one-shot context dispatches that should clean themselves up, add
70
72
  `--auto-stop`. It requires `--context` and does not keep the session alive for
71
73
  later reuse. On Codex app-server, a turn launched from `--context` is accepted
72
- only after structured command/tool activity, Git delta, or fresh
73
- accepted/no-change sidecar proof. A prose-only acknowledgment emits
74
+ only after structured command/tool activity or a Git delta. Optional claims and
75
+ final prose cannot satisfy that observed-activity gate. A prose-only acknowledgment emits
74
76
  `completed_no_activity`, is
75
77
  visible as `task_failed` before teardown, and exits non-zero. This keeps
76
78
  parallel orchestration compact without converting agent turn completion into
@@ -103,33 +105,29 @@ harnex run codex --cwd /tmp/leximaze_eval_run_001 \
103
105
  `--root DIR` only overrides harnex's root attribution; it does not change the
104
106
  child process cwd. Neither flag is a sandbox.
105
107
 
106
- For queue closeout, ask workers to write a compact sidecar in addition to their
107
- plain-text `koder/` artifact. For blind/unattended work, initialize and require
108
- the report so a normal final answer cannot bypass proof acceptance:
108
+ For queue closeout, do not ask workers to author proof JSON. Harnex writes a
109
+ canonical receipt for every dispatch from the Git delta, structured command
110
+ exits, turn outcome, and usage it observed. The default receipt lives outside
111
+ the checkout under the Harnex state directory; pass `--artifact-report PATH`
112
+ only when a queue needs a fixed destination. Consumers can run:
109
113
 
110
114
  ```bash
111
- harnex artifact-report init .harnex/reports/pi-i-NN.json
112
- harnex run pi --id pi-i-NN --tmux pi-i-NN \
113
- --artifact-report .harnex/reports/pi-i-NN.json \
114
- --require-artifact-report \
115
- --context 'Update the canonical koder file, finalize $HARNEX_ARTIFACT_REPORT_PATH, then run harnex artifact-report validate "$HARNEX_ARTIFACT_REPORT_PATH" --final' \
116
- --auto-stop
115
+ harnex artifact-report validate /path/from/the-dispatch-row.json --final
116
+ ```
117
+
118
+ A worker may add review context by writing only a small block to
119
+ `$HARNEX_ARTIFACT_CLAIMS_PATH` before it completes:
120
+
121
+ ```json
122
+ {"claims":{"summary":"Review complete","verdict":"changes_requested","findings":{"P1":0,"P2":1,"P3":0}}}
117
123
  ```
118
124
 
119
- The worker should keep the full explanation in `koder/` and put only compact
120
- machine-readable proof in the sidecar: an `accepted` or `no_change` outcome,
121
- validation command/status/exit codes, typed artifact summaries (`finding`,
122
- `review`, `gate`, `blocker`, etc.), evidence, confidence, and canonical refs.
123
- `harnex artifact-report validate PATH` checks the schema without echoing report
124
- payloads; `--final` additionally requires accepted final proof and
125
- `validation.final_reported=true`.
126
-
127
- Without `--require-artifact-report`, missing/malformed reports remain warning
128
- telemetry. Strict mode fails closed for missing, malformed, unsupported,
129
- oversized, schema-incomplete, rejected, or unchanged stale reports. JSON in the
130
- agent's final prose does not count: only the configured sidecar path is read.
131
- A fresh valid `no_change` report is the explicit proof path for intentional
132
- no-delta work.
125
+ Claims are bounded and advisory. They never determine receipt validity, and
126
+ malformed/stale claims are ignored. `HARNEX_ARTIFACT_REPORT_PATH` is owned and
127
+ overwritten by Harnex; JSON printed in final prose is not scraped. The legacy
128
+ `artifact-report init` command and `--require-artifact-report` flag remain for
129
+ compatibility, but normal dispatches need neither worker JSON nor an explicit
130
+ receipt path.
133
131
 
134
132
  Queue runners should pass first-class attribution so dispatch rows can be grouped
135
133
  without path/id heuristics:
@@ -145,6 +143,11 @@ harnex run pi --id pi-i-NN --tmux pi-i-NN \
145
143
  `intent`, and at least one work id (`queue_id`, `entry_id`, `issue`, or `plan`)
146
144
  are present.
147
145
 
146
+ Every dispatch writes one `dispatch_start` and one rich v2 `dispatch_end` row
147
+ to the canonical repo/global dispatch stream, which is the only telemetry
148
+ destination. Repo `.harnex/config.json` can warn on or reject non-canonical
149
+ phase names before spawn.
150
+
148
151
  Pi runs use structured RPC (`pi --mode rpc`). Pass Pi child flags after `--`
149
152
  (e.g. `harnex run pi --context "..." -- --model anthropic/claude-sonnet-4-5 --thinking high`).
150
153
 
@@ -62,19 +62,19 @@ a coordination error (wrong id or wrong repo), and only `0` as success.
62
62
 
63
63
  ## Duplicate-Dispatch Guard
64
64
 
65
- `harnex run --attempt-kind retry` requires `--parent-dispatch-id`, and any
66
- retry/fix/superseding dispatch whose named parent is still running in the
67
- same repo is refused. Wait for the parent
65
+ `harnex run --attempt-kind retry|fallback` requires
66
+ `--parent-dispatch-id`, and any retry/fix/fallback/superseding dispatch whose
67
+ named parent is still running in the same repo is refused. Wait for the parent
68
68
  (`harnex wait --id <parent> --until done`) or stop it first. Pass
69
69
  `--allow-live-parent` only for intentional parallelism (e.g. isolated
70
70
  worktrees). `--attempt-kind review` is exempt: a completed parent may still
71
71
  sit at a live prompt while its work is reviewed. For structured sessions (Pi RPC and Codex app-server),
72
72
  `harnex wait --until task_complete` remains the exact accepted-turn fence.
73
73
  Codex acknowledgment-only auto-stop turns are typed
74
- `completed_no_activity` and fail this fence without transcript parsing.
75
- `--require-artifact-report` can additionally make sidecar shape/final-proof
76
- acceptance part of the verdict. Harnex still does not judge semantic quality;
77
- verify the expected artifact or tests afterward.
74
+ `completed_no_activity` and fail this fence without transcript parsing. Harnex
75
+ writes the observed-state receipt before publishing `task_complete`; optional
76
+ worker claims never make an inactive turn pass. Harnex still does not judge
77
+ semantic quality, so verify the expected artifact or tests afterward.
78
78
 
79
79
  ## Completion Test
80
80
 
@@ -91,8 +91,9 @@ harnex watch --id pi-i-NN --until done --max-wait 90m \
91
91
 
92
92
  `harnex watch --until done` wraps the `harnex wait --until done` work fence:
93
93
  it succeeds from `task_complete` or durable successful terminal telemetry
94
- (`--summary-out` / `.harnex/dispatch.jsonl` / exit status), returns non-zero for
95
- `task_failed` / failed terminal telemetry, returns `124` for `--max-wait`, and
94
+ (the v2 `dispatch_end` in `.harnex/dispatch.jsonl`, an explicit mirror when
95
+ configured, or exit status), returns non-zero for `task_failed` / failed
96
+ terminal telemetry, returns `124` for `--max-wait`, and
96
97
  only writes done/fail markers as compatibility outputs after harnex has seen a
97
98
  terminal work signal.
98
99
 
data/guides/05_naming.md CHANGED
@@ -66,17 +66,27 @@ If `--id` is missing, harnex generates a random session ID. The tmux window may
66
66
  look right, but `harnex status`, `harnex pane --id`, and logs need the random
67
67
  ID.
68
68
 
69
- ## Retry Suffixes
69
+ ## Retry Suffixes And Linkage
70
70
 
71
- If a session fails and you dispatch a fresh attempt, append a suffix:
71
+ If a session fails and you dispatch a fresh attempt, use a new ID and link it
72
+ to the completed parent so Harnex can derive chain counts and recovery:
72
73
 
73
74
  ```text
74
- pi-i-42 first attempt
75
- pi-i-42b second attempt
76
- pi-i-42c third attempt
75
+ pi-i-42 initial attempt
76
+ pi-i-42-r1 retry of pi-i-42
77
+ pi-i-42-r2 retry of pi-i-42-r1
77
78
  ```
78
79
 
79
- Keep the old session's logs. They are useful for diagnosis.
80
+ ```bash
81
+ harnex run pi --id pi-i-42-r1 --tmux pi-i-42-r1 \
82
+ --attempt-kind retry --parent-dispatch-id pi-i-42 \
83
+ --context "Retry the bounded task."
84
+ ```
85
+
86
+ Retry/fix/fallback/superseding work is refused while its named parent is still
87
+ running unless `--allow-live-parent` explicitly authorizes isolated parallelism.
88
+ Keep old logs for diagnosis; retention removes only expired/over-cap logs that
89
+ do not belong to a current or live session.
80
90
 
81
91
  ## Task Files
82
92
 
@@ -104,8 +114,8 @@ If a legacy workflow still expects a done marker, derive it from the session ID:
104
114
  ```
105
115
 
106
116
  Treat done markers as compatibility hints only. Canonical completion should come
107
- from harnex terminal telemetry (`harnex wait` / `harnex status --json` / summary
108
- rows in `.harnex/dispatch.jsonl`).
117
+ from harnex terminal telemetry (`harnex wait` / `harnex status --json` / v2
118
+ `dispatch_end` rows in `.harnex/dispatch.jsonl`).
109
119
 
110
120
  When a brief asks for a completion marker, make it one line and include the
111
121
  highest-signal result: tests passed, review clean, or the blocking issue.
@@ -37,6 +37,16 @@ module Harnex
37
37
  nil
38
38
  end
39
39
 
40
+ # Whether the usage this adapter captures reports input_tokens
41
+ # INCLUSIVE of cached_tokens. Depends on the capture path, not just
42
+ # the provider: codex app-server JSON reports cached as a subset of
43
+ # input, while the codex TUI line the PTY adapter scrapes shows
44
+ # non-cached input with cached as a separate additive count. Feeds
45
+ # Pricing.compute (plan 33 Phase 2).
46
+ def usage_input_includes_cached?
47
+ false
48
+ end
49
+
40
50
  # Probes `<base_command.first> --version` with a short timeout and
41
51
  # memoizes the result for the adapter's lifetime. Returns nil when
42
52
  # the binary is missing, exits non-zero, or stalls past the timeout.
@@ -59,6 +59,7 @@ module Harnex
59
59
  @initial_prompt = extract_initial_prompt(extra_args)
60
60
  @client = nil
61
61
  @thread_id = nil
62
+ @current_model = nil
62
63
  @current_turn_id = nil
63
64
  @state = :disconnected
64
65
  @last_completed_at = nil
@@ -78,6 +79,21 @@ module Harnex
78
79
  true
79
80
  end
80
81
 
82
+ # thread/tokenUsage/updated totals report cachedInputTokens as a
83
+ # subset of inputTokens (verified against a live captured row:
84
+ # input + output == total exactly, cached < input).
85
+ def usage_input_includes_cached?
86
+ true
87
+ end
88
+
89
+ # Effective model reported by the app-server's thread/start or
90
+ # thread/resume response (schema-required field). Feeds
91
+ # Session#summary_model so price-table cost can resolve without the
92
+ # caller passing `--meta '{"model": ...}'`.
93
+ def current_model
94
+ @current_model
95
+ end
96
+
81
97
  def context_telemetry_supported?
82
98
  true
83
99
  end
@@ -202,6 +218,7 @@ module Harnex
202
218
  ensure_open!
203
219
  result = @client.request("thread/resume", { threadId: thread_id })
204
220
  @thread_id = thread_id
221
+ @current_model = extract_model(result) || @current_model
205
222
  @state = :prompt
206
223
  result
207
224
  end
@@ -279,6 +296,7 @@ module Harnex
279
296
 
280
297
  result = @client.request("thread/start", {})
281
298
  @thread_id = extract_thread_id(result)
299
+ @current_model = extract_model(result) || @current_model
282
300
  end
283
301
 
284
302
  def extract_thread_id(payload)
@@ -287,6 +305,13 @@ module Harnex
287
305
  payload.dig("thread", "id")
288
306
  end
289
307
 
308
+ def extract_model(payload)
309
+ return nil unless payload.is_a?(Hash)
310
+
311
+ model = payload["model"]
312
+ model.is_a?(String) && !model.empty? ? model : nil
313
+ end
314
+
290
315
  def extract_initial_prompt(extra_args)
291
316
  return nil unless extra_args.is_a?(Array)
292
317