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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9142cc69cec0a0cf0dc3961ccc27dc3728baea445c7d83b4c2d69a34b7e6a499
4
- data.tar.gz: b89c616cb2484d48e74ef88985a438f7ebb1c22b1af17dd44cbaab3ee0c020cd
3
+ metadata.gz: d0a3ac349311bc2677614dabb091443bb21ec82bf63852d237fb38fd47ecaeda
4
+ data.tar.gz: f1b909c7ed43636ea57a297e25f2adc1d56283faa5f8af882ed22b63b4c55f2f
5
5
  SHA512:
6
- metadata.gz: 220566d1c646ca49b7f41f8ed053b2909dda8468ab3dc877bfcfe5f47b52988492d54b9d2fcc42ff0e42154921fde97af0824688b27df69e94bd9fc49b77c2f8
7
- data.tar.gz: e52eca128c1f078171ab991acd25bafa810e8341a7f6c8f199535cd03b7a881ee03f2c4193afb01ece92cc00d0a8025a6bdb564739aeb13cbe6a02a4f4d87d0a
6
+ metadata.gz: fe2b745dd618e1967fa5bbe1f7f55f4c0c2beb104cd6d213592f8545528118d3b3a80053b5643fce5482d3eaca9c7e6d800b6f5dd6d41873c0c9e5cc73aff332
7
+ data.tar.gz: 0c13e6cb6fe6bbac64e14ae43b23061a2b5a96cbc7f4699d438b5e5eac48ef70435c017b638635c0ded52886536f94aac37df712b1d67a9c5485dc0dd1cfa4b7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,193 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.0] - 2026-08-03 | 01:44 PM | IST
4
+
5
+ Minor bump, **breaking**: `--summary-out` is removed outright, so the canonical
6
+ `.harnex/dispatch.jsonl` is the only telemetry destination. Also fixes four
7
+ runner reliability defects found while verifying that removal — concurrent
8
+ registry corruption, a delivered send reported as failed, a corrupt registry
9
+ entry crashing every session scan, and a closed stdout wedging the wrapped
10
+ agent.
11
+
12
+ ### Fixed
13
+
14
+ - **Concurrent registry writes no longer corrupt each other** (#66).
15
+ `write_registry` derived its temp path from the pid alone, but several
16
+ threads in one session write the same registry — the startup persist, the
17
+ inbox delivery thread, and one thread per API client. One thread renamed the
18
+ file another was still writing, and the loser raised `Errno::ENOENT`
19
+ (2080 failures in a 2400-write reproduction; now zero). Writes are also no
20
+ longer defeated by a state directory that was reaped mid-session.
21
+ - **A failed registry write no longer fails an already delivered send** (#66).
22
+ Registry persistence runs *after* the prompt has reached the agent, so a
23
+ bookkeeping failure was reporting a dispatched turn as failed — enough to
24
+ make an orchestrator retry work already in flight. Post-injection refresh now
25
+ warns; startup persistence stays strict so an undiscoverable session still
26
+ fails loudly instead of running unreachable.
27
+ - **One corrupt registry file no longer crashes every session scan** (#66).
28
+ A non-numeric pid raised `ArgumentError` out of `active_sessions`, taking
29
+ down `harnex status`, `harnex send`, and `harnex pane`. Such an entry is now
30
+ treated as dead and pruned, matching the existing self-healing for
31
+ unparseable JSON.
32
+ - **A closed stdout no longer wedges the wrapped agent** (#66).
33
+ `Errno::EPIPE`/`EBADF` are not `IOError` subclasses, so they escaped the
34
+ output reader's rescue and killed the thread; the PTY then stopped draining
35
+ and the agent blocked forever on write, presenting as an agent hang with no
36
+ harness error. Draining is now unconditional and stdout echo is best-effort.
37
+ Both reader loops report an unexpected exit instead of vanishing silently.
38
+ - `Retention` metadata writes route through the shared atomic writer instead of
39
+ repeating the same pid-only temp-name pattern.
40
+
41
+ ### Removed
42
+
43
+ - **BREAKING: `--summary-out` is gone** (#65). `.harnex/dispatch.jsonl` is now
44
+ the only destination a dispatch writes telemetry to. Passing `--summary-out
45
+ PATH` or `--summary-out=PATH` is rejected as an unknown flag and exits
46
+ non-zero; it is deliberately not a silent ignore, so a stale caller fails
47
+ immediately rather than believing it still has a second copy. 0.9.0 demoted
48
+ the flag to an explicit-only mirror but left it in place; that mirror was the
49
+ source of a three-times-hand-reconciled stranded-telemetry class.
50
+ - `summary_out_path` no longer appears on `dispatch_start` or `dispatch_end`
51
+ rows, and `summary_out` no longer appears in `harnex status --json`,
52
+ `harnex wait`, or `harnex watch` payloads. The `summary` event no longer
53
+ carries `mirror_path`.
54
+
55
+ ### Added
56
+
57
+ - **Harness-authored observed-state receipts** (#64): every dispatch now writes
58
+ a canonical `harnex.artifact_report.v1` receipt without worker-authored JSON.
59
+ Receipts carry explicit `receipt.author=harnex` provenance, start/end Git
60
+ state (committed plus staged/unstaged/untracked paths and LOC), bounded Codex
61
+ command exits, turn acceptance, and usage. The receipt is written before a
62
+ structured `task_complete` event and refreshed with final teardown telemetry.
63
+ - Every session receives a default repo-keyed receipt under
64
+ `~/.local/state/harnex/receipts/`; `--artifact-report` now overrides that
65
+ destination. Live status, detached/tmux launch payloads, dispatch-start rows,
66
+ and dispatch-end metadata expose the receipt and claims paths.
67
+ - Review workers may write a bounded optional block to
68
+ `HARNEX_ARTIFACT_CLAIMS_PATH` (`summary`, `verdict`, and P1/P2/P3 counts).
69
+ Claims are sanitized and copied into the receipt but never participate in
70
+ completion acceptance or final-receipt validity.
71
+ - Receipt files and claims inputs join the existing 45-day / 1-GiB retention
72
+ policy as a third independently configurable directory, including
73
+ `HARNEX_RECEIPTS_MAX_AGE_DAYS` / `HARNEX_RECEIPTS_MAX_BYTES` and live/current
74
+ path protection.
75
+
76
+ ### Changed
77
+
78
+ - `TerminalStatus` resolves exclusively from the canonical stream. It
79
+ previously preferred the mirror file named by a record's `summary_out_path`,
80
+ which — with the writer removed — would have let a leftover mirror from an
81
+ older release resolve status from stale data. A pre-existing mirror file on
82
+ disk now has no effect on any id.
83
+ - `status["source"]` reports `dispatch_end` where it previously reported
84
+ `summary_out` for rich end rows. `dispatch_history`, `dispatch_start`,
85
+ `live`, `registry`, and `none` are unchanged.
86
+ - `artifact-report validate --final` preserves the legacy manual-v1 contract
87
+ while recognizing the additive harness-receipt contract. Harness receipts
88
+ validate observed acceptance and zero-delta evidence; failed exploratory
89
+ command exits remain factual telemetry for queue policy rather than allowing
90
+ worker claims to decide receipt validity.
91
+ - Artifact fingerprints are now internal claims-freshness bookkeeping only.
92
+ Pre-existing, stale, malformed, or missing worker reports are overwritten by
93
+ fresh harness proof instead of becoming work-acceptance failures. The legacy
94
+ `init` command and `--require-artifact-report` flag remain compatible, but
95
+ neither an explicit path nor model-authored proof is required.
96
+ - Codex autonomous completion still rejects acknowledgment-only turns as
97
+ `completed_no_activity`; optional claims cannot satisfy the observed-activity
98
+ gate. Receipt write/validation failure is fail-closed as `report_invalid`.
99
+ - Git observation now baselines the starting worktree so uncommitted product
100
+ edits are included while unchanged pre-existing dirt and harness-owned
101
+ dispatch/receipt files are excluded.
102
+
103
+ ## [0.9.0] - 2026-08-03 | 01:11 AM | IST
104
+
105
+ Minor bump: the durable dispatch row family is now v2, `--summary-out` changes
106
+ from a default destination to an explicit mirror, and retention/phase policy add
107
+ operator-visible behavior.
108
+
109
+ ### Added
110
+
111
+ - **Price-table cost for token-reporting adapters** (#63, plan 33
112
+ Phase 2; subsumes #58's cost gap): new `lib/harnex/pricing.rb` holds a
113
+ static per-1M-token USD rate table keyed by provider + model, each entry
114
+ `as_of`-dated and hand-copied from the provider pricing pages
115
+ (OpenAI gpt-5.x/codex and Anthropic Claude families, rates as of
116
+ 2026-08-02; update procedure documented in the file header).
117
+ `build_summary_usage` applies it only when `cost_usd` is null, usage
118
+ status is `observed`/`zero`, and the effective model matches the table —
119
+ then `cost_source: "price_table"` and a new always-present
120
+ `usage.cost_price_as_of` field record provenance. Unknown models stay
121
+ null; provider-reported cost (Pi) is never overwritten; costs are never
122
+ backfilled.
123
+ - The codex app-server adapter now captures the effective model from the
124
+ schema-required `model` field on `thread/start` / `thread/resume`
125
+ responses, so `agent.model_effective` (and price-table lookup) resolves
126
+ without the caller passing `--meta '{"model": ...}'`.
127
+ - Token-semantics are capture-path-aware: new adapter hook
128
+ `usage_input_includes_cached?` distinguishes codex app-server JSON
129
+ (cached ⊆ input → billable input = input − cached) from the codex PTY
130
+ transcript line (input excludes cached → cached prices additively).
131
+ Verified against the captured schema fixtures and a live app-server row.
132
+ - Price-table lookup now covers Holm's effective `gpt-5.5` model and keys its
133
+ tier-sensitive rates by the recorded service tier (`standard`, `flex`, or
134
+ `fast`; `priority` aliases fast). Because published `gpt-5.5` rates split at
135
+ 272K active context, Harnex also requires an observed peak below that boundary;
136
+ missing/long-context evidence remains unpriced rather than applying the wrong
137
+ short-context rate.
138
+ - Optional repo phase policy in `.harnex/config.json`: an allowlist can `warn`
139
+ or `reject` non-canonical effective `meta.phase` values before spawn. Explicit
140
+ malformed policy files fail closed; no config preserves existing behavior.
141
+ - Bounded events/output retention: 45-day and 1-GiB defaults per directory,
142
+ repo config plus environment overrides, current/live-session protection,
143
+ oldest-first age/size pruning, one-hour automatic throttle, and bounded
144
+ last-prune metadata. `harnex doctor` reports size/limits; `--prune --dry-run`
145
+ previews bounded candidate paths and `--prune` applies the policy.
146
+
147
+ ### Changed
148
+
149
+ - **Single tracked telemetry stream** (#63, plan 33 Phase 1): a dispatch
150
+ now writes exactly two rows to the repo-tracked `.harnex/dispatch.jsonl`
151
+ — the `dispatch_start` row and one unified v2 `dispatch_end` row that
152
+ merges the thin envelope (top-level `schema_version: 2`, `record_type`,
153
+ `id`, `status`, `tier`, timing, `tmux_state`, …) with the rich summary
154
+ sections (`meta`, `predicted`, `actual`, `agent`, `usage`, `context`,
155
+ `attribution`, `outcome`, `attempt`, `reliability`, `queue?`,
156
+ `orchestration?`, artifact-report keys). Start rows stamp
157
+ `schema_version: 2` as well. Readers accept v1 and v2 rows mixed in one
158
+ file; `harnex history` keeps skipping pre-0.7.3 envelope-less rows.
159
+ - `--summary-out` is now an explicit-only mirror: no default path. When
160
+ set, the identical v2 end record is appended there in addition to the
161
+ tracked stream. Consumers that redirected `--summary-out` to keep rich
162
+ rows should drop the flag — the tracked stream now carries everything.
163
+ `Harnex.default_summary_out_path` is removed; every writer and reader
164
+ resolves the stream through `DispatchHistory.path_for` (git-root walk,
165
+ global fallback), so non-git roots stream to the global file instead of
166
+ a repo-local `.harnex/` directory.
167
+ - The `summary` event now points `path` at the tracked stream and carries
168
+ `mirror_path` when a mirror is configured.
169
+ - `TerminalStatus` resolves a v2 end row as both summary and history in
170
+ one shot (branching on `record_type` first), so `wait --until done` and
171
+ `status --id` fall back to the unified stream; legacy duck-types remain
172
+ for pre-v2 files.
173
+ - Cross-dispatch attempt fields are harness-derived from the canonical stream:
174
+ `attempts_total`/succeeded/failed, `fallback_triggered`, and
175
+ `reliability.recovered` follow bounded parent links and degrade safely on
176
+ missing, duplicate, malformed, or cyclic history. `fallback` is now a public,
177
+ live-parent-guarded attempt kind; in-run `retry_count` remains separate.
178
+ - Public telemetry/configuration references under `docs/*.md` are now packaged
179
+ in the gem. README, GUIDE, TECHNICAL, agent guides, and event/telemetry docs
180
+ use the same canonical-stream, mirror, native-watch, pricing, phase-policy,
181
+ and retention terminology.
182
+
183
+ ### Fixed
184
+
185
+ - `harnex history` no longer renders blank rows for pre-0.7.3
186
+ `{meta, predicted, actual}`-schema telemetry rows in
187
+ `.harnex/dispatch.jsonl`: rows recognized as neither start nor end
188
+ records are skipped in both table and `--json` output. The raw file is
189
+ untouched — legacy rows remain available for forensics.
190
+
3
191
  ## [0.8.0] - 2026-08-02 | 08:46 PM | IST
4
192
 
5
193
  Minor bump: two behavior changes below (`wait --until done` exit codes,
data/GUIDE.md CHANGED
@@ -8,11 +8,11 @@ Treat harnex as a local supervisor harness, not as a conversation
8
8
  bus between agents.
9
9
 
10
10
  - Start a fresh worker for each step, usually with `--tmux`
11
- - Send one clear task, often by pointing the worker at a file
12
- - Use `--wait-for-idle` as a fence, then inspect with `harnex pane`
13
- - Ask the worker to write its output to a file when the next step
14
- needs structured input
15
- - Stop the worker when that step is done
11
+ - Give one clear startup task with `--context`; add `--auto-stop` for one-shot work
12
+ - Monitor existing visible work with `harnex watch --until done`
13
+ - Use `--wait-for-idle` only as a follow-up-send fence, then verify the artifact
14
+ - Ask the worker to write its output to a file when the next step needs it
15
+ - Stop completed interactive workers promptly
16
16
 
17
17
  For multi-step flows, chain fresh workers with file handoffs:
18
18
  Codex writes a plan, another Codex implements it, Claude reviews it,
@@ -49,14 +49,18 @@ automatically when the agent is ready. You don't have to wait
49
49
  or retry. Queueing exists, but the default workflow should still be
50
50
  one task per fresh worker.
51
51
 
52
- For unattended dispatch, prefer built-in monitoring over external poll loops:
52
+ For unattended visible dispatch, prefer the native work-level watcher over
53
+ external poll loops:
53
54
 
54
55
  ```bash
55
- harnex run codex --id impl --tmux impl --watch --preset impl
56
+ harnex run codex --id impl --tmux impl \
57
+ --context "Read and execute /tmp/task-impl.md" --auto-stop
58
+ harnex watch --id impl --until done --max-wait 90m
56
59
  ```
57
60
 
58
- This adds a foreground watcher that checks idle activity and performs bounded
59
- force-resume nudges. For full flag behavior and event-stream consumers, see
61
+ `harnex run --watch --preset impl` is a separate foreground launch-and-stall
62
+ babysitter; do not combine it with `--tmux` or `--detach`. For full flag
63
+ behavior and event-stream consumers, see
60
64
  [TECHNICAL.md](TECHNICAL.md) and the built-in monitoring section in
61
65
  [README.md](README.md).
62
66
 
@@ -262,4 +266,7 @@ harnex agents-guide # deeper agent-facing guidance
262
266
  ## What's next
263
267
 
264
268
  For the full command reference, flags, HTTP API, and internals,
265
- see [TECHNICAL.md](TECHNICAL.md).
269
+ see [TECHNICAL.md](TECHNICAL.md). The packaged
270
+ [dispatch telemetry](docs/dispatch-telemetry.md) and
271
+ [configuration](docs/configuration.md) references cover the canonical v2 stream,
272
+ phase allowlists, and log retention.
data/README.md CHANGED
@@ -60,9 +60,10 @@ job, watch it work, stop it when done.
60
60
  ### Run from a temporary/public bundle
61
61
 
62
62
  Use `--cwd DIR` when the worker should see a specific directory rather than
63
- the orchestrator's current repo. Harnex starts the wrapped agent in `DIR`, sets
64
- that directory as the session root, and resolves default telemetry such as
65
- `.harnex/dispatch.jsonl` there:
63
+ the orchestrator's current repo. Harnex starts the wrapped agent in `DIR` and
64
+ uses it for session attribution. Canonical dispatch telemetry is written to the
65
+ enclosing git root's `.harnex/dispatch.jsonl`; a non-git directory uses the
66
+ global `~/.local/state/harnex/dispatch.jsonl` fallback:
66
67
 
67
68
  ```bash
68
69
  harnex run codex --cwd /tmp/leximaze_eval_run_001 \
@@ -205,8 +206,8 @@ bare `--watch` means babysitter mode.
205
206
 
206
207
  For one-shot startup prompts, add `--auto-stop`. It requires `--context`.
207
208
  A Codex app-server turn launched from `--context` only counts as accepted
208
- autonomous work when structured command/tool activity, a Git delta, or a fresh
209
- accepted/no-change artifact report proves activity. An acknowledgment-only turn emits
209
+ autonomous work when structured command/tool activity or a Git delta proves
210
+ activity. Optional receipt claims cannot satisfy this gate. An acknowledgment-only turn emits
210
211
  `outcome.class=completed_no_activity`, makes `watch --until done` return
211
212
  non-zero, and auto-stops with a non-zero verdict instead of reporting success.
212
213
  PTY adapters still stop on prompt return because they do not expose equivalent
@@ -252,14 +253,17 @@ Schema details and compatibility policy are documented in
252
253
 
253
254
  ## Dispatch history
254
255
 
255
- Every finished `harnex run` writes dispatch records. By default, the terminal
256
- summary JSONL path is `<session-root>/.harnex/dispatch.jsonl`; `--cwd DIR` makes
257
- `DIR` the session root, including for non-git temporary bundles. The compact
258
- history record is repo-local in a git tree and falls back to
259
- `~/.local/state/harnex/dispatch.jsonl` outside git.
260
- `harnex history` reads the compact records from that location, and
261
- `harnex status --id ID --json` / `harnex wait` can use the same durable
262
- terminal summaries when the live session registry is already gone.
256
+ Every `harnex run` writes exactly one v2 `dispatch_start` row at registration
257
+ and one rich v2 `dispatch_end` row at teardown. Both use the canonical stream:
258
+ `<git-root>/.harnex/dispatch.jsonl` inside a git repo, or
259
+ `~/.local/state/harnex/dispatch.jsonl` otherwise. The end row combines the
260
+ history envelope with usage, context, attribution, outcome, attempt,
261
+ reliability, queue/orchestration, and a harness-authored observed-state receipt.
262
+
263
+ The canonical stream is the only destination; there is no flag to mirror a
264
+ second copy elsewhere. `harnex history`, `harnex status --id ID --json`, and
265
+ `harnex wait` all read the canonical stream when the live registry is gone.
266
+ Mixed legacy v1 and v2 rows remain readable.
263
267
 
264
268
  Use `harnex history` to inspect it:
265
269
 
@@ -274,36 +278,37 @@ Dispatch briefs can declare soft budget metadata through `--meta`:
274
278
  harnex run pi --meta '{"read_budget_lines":2000,"output_ceiling_lines":800}' ...
275
279
  ```
276
280
 
277
- Workers can also write a small machine-readable proof sidecar while keeping the
278
- canonical explanation in plain-text `koder/` files. Initialize the real schema
279
- instead of asking a model to reproduce it from prose:
280
-
281
- ```bash
282
- harnex artifact-report init .harnex/reports/pi-i-61.json
283
- harnex run pi --id pi-i-61 \
284
- --artifact-report .harnex/reports/pi-i-61.json \
285
- --require-artifact-report \
286
- --context 'Run validation, update the canonical koder artifact, finalize $HARNEX_ARTIFACT_REPORT_PATH, and validate it with harnex artifact-report validate "$HARNEX_ARTIFACT_REPORT_PATH" --final' \
287
- --auto-stop
281
+ Every dispatch gets a canonical `harnex.artifact_report.v1` receipt generated
282
+ by Harnex from observed state: start/end Git SHA and delta, changed paths and
283
+ LOC, structured command exits when the transport exposes them, turn outcome,
284
+ and usage. No model-authored proof JSON or `--artifact-report` flag is needed.
285
+ The default file lives under `~/.local/state/harnex/receipts/`; the live status
286
+ payload and final dispatch row expose its absolute path.
287
+
288
+ Use `--artifact-report PATH` only to override that destination. During the run,
289
+ `HARNEX_ARTIFACT_REPORT_PATH` names the harness-owned final file and
290
+ `HARNEX_ARTIFACT_CLAIMS_PATH` names a separate optional worker input. A review
291
+ worker may attach bounded context without controlling acceptance:
292
+
293
+ ```json
294
+ {
295
+ "claims": {
296
+ "summary": "Review complete; one P2 remains.",
297
+ "verdict": "changes_requested",
298
+ "findings": { "P1": 0, "P2": 1, "P3": 0 }
299
+ }
300
+ }
288
301
  ```
289
302
 
290
- The sidecar schema is `harnex.artifact_report.v1`; harnex exposes the path as
291
- `HARNEX_ARTIFACT_REPORT_PATH` / `HARNEX_VALIDATION_REPORT_PATH`, the schema as
292
- `HARNEX_ARTIFACT_REPORT_SCHEMA`, and strict mode as
293
- `HARNEX_ARTIFACT_REPORT_REQUIRED=1`. `harnex artifact-report validate PATH`
294
- checks field shapes; add `--final` to require `status=pass`, an
295
- `accepted`/`no_change` outcome with a summary, valid command exit codes, and
296
- `validation.final_reported=true`. Diagnostics contain bounded field paths and
297
- shape errors, not report payloads or transcripts.
298
-
299
- Without `--require-artifact-report`, report defects remain fail-soft warning
300
- telemetry. With it, a missing, malformed, unsupported, oversized,
301
- contract-incomplete, rejected, or unchanged stale report makes the work verdict
302
- non-zero. A fresh explicit `no_change` report can prove an intentional no-delta
303
- task without fake edits. Harnex records compact `artifact_report`, `validation`,
304
- and `artifacts` blocks plus `outcome.class` / `outcome.report_status`; Git
305
- changes alone never imply semantic acceptance, and JSON printed in final prose
306
- is never scraped as a sidecar.
303
+ Harnex sanitizes that block and writes it into the receipt; malformed, stale,
304
+ or missing claims are ignored. Claims and final prose can never turn an
305
+ otherwise rejected completion into accepted proof. `harnex artifact-report
306
+ validate PATH --final` validates the harness receipt for consumers, while the
307
+ older `init` command and legacy worker-authored v1 validation remain available
308
+ for compatibility. `--require-artifact-report` is also retained for existing
309
+ scripts but no longer requires an explicit path. A receipt write failure fails
310
+ closed as `report_invalid`; ordinary workers no longer fail because they forgot
311
+ or malformed proof JSON.
307
312
 
308
313
  Queue runners can pass first-class attribution without hiding it in prose:
309
314
 
@@ -312,17 +317,17 @@ harnex run pi --project-id harnex --queue-id queue-005 --entry-id SP-4 \
312
317
  --phase implement --intent queue-work --require-attribution ...
313
318
  ```
314
319
 
315
- Soft budget metadata is copied into summary `meta`; queue/agent/reliability
316
- metadata is copied into top-level `queue`, `agent`, and `reliability` summary
317
- blocks. Every terminal row also has `usage` (so null is distinguishable from
318
- explicit zero or an estimate), `attribution`, `outcome`, and a joinable
319
- per-session `attempt` block. Terminal summary `actual` records timing, exit
320
- classification, token usage when the adapter can capture it, adapter-reported
321
- `cost_usd` when reliably available, git deltas, task-completion state,
322
- operational counters (`stalls`, `force_resumes`, `disconnections`,
323
- `tool_calls`, `commands_executed`), output/event log paths, and rough volume
324
- measurements such as `lines_changed`, `output_lines`, `output_bytes`, and
325
- `event_records`.
320
+ Soft budget metadata is copied into end-row `meta`; queue/agent/reliability
321
+ metadata is copied into top-level `queue`, `agent`, and `reliability` blocks.
322
+ Every end row also has `usage` (so null is distinguishable from explicit zero or
323
+ an estimate), `attribution`, `outcome`, and a joinable `attempt` block. When an
324
+ adapter reports tokens but no cost, Harnex may compute provider list-price cost
325
+ only for an exact maintained provider/model/service-tier/context-rate match; such rows use
326
+ `usage.cost_source: "price_table"` and carry `usage.cost_price_as_of`.
327
+ Provider-reported cost remains authoritative, estimates remain labelled, and
328
+ unknown rates stay null. `actual` also records timing, exit classification, git
329
+ deltas, task-completion state, harness-derived cross-dispatch attempt counts,
330
+ operational counters, output/event paths, and bounded volume measurements.
326
331
 
327
332
  Long queue runners can also opt into logical primary-orchestrator rollups:
328
333
 
@@ -422,8 +427,8 @@ See [recipes/03_buddy.md](recipes/03_buddy.md) for the full pattern.
422
427
  | `harnex events --id <id>` | Stream structured session events (`--snapshot` for non-blocking dump) |
423
428
  | `harnex history` | List completed dispatches from `.harnex/dispatch.jsonl` |
424
429
  | `harnex wait --id <id>` | Block until process exit by default; use `--until done` for unattended work completion or `--until task_complete` for exact structured turn completion |
425
- | `harnex artifact-report init\|validate PATH` | Create or validate bounded `harnex.artifact_report.v1` proof; use `validate --final` before strict completion |
426
- | `harnex doctor` | Run adapter dependency preflight checks; add `--sweep` for read-only session drift diagnostics |
430
+ | `harnex artifact-report init\|validate PATH` | Validate harness-authored `harnex.artifact_report.v1` receipts; `init` remains for legacy/manual documents |
431
+ | `harnex doctor` | Run dependency and retention diagnostics; `--sweep` reports session drift, `--prune --dry-run` previews retention, and `--prune` applies it |
427
432
  | `harnex guide` | Getting started walkthrough |
428
433
  | `harnex agents-guide` | Agent-facing dispatch, chain, buddy, monitoring, and naming guides |
429
434
  | `harnex recipes` | List and read tested workflow patterns (`show 01`, `show buddy`) |
@@ -442,6 +447,8 @@ longer used. Remove stale `~/.claude/skills/harnex-*` or
442
447
 
443
448
  - [GUIDE.md](GUIDE.md) — getting started walkthrough with examples
444
449
  - [TECHNICAL.md](TECHNICAL.md) — full command reference, flags, HTTP API, architecture
450
+ - [docs/dispatch-telemetry.md](docs/dispatch-telemetry.md) — v2 dispatch stream and field contract
451
+ - [docs/configuration.md](docs/configuration.md) — phase allowlists and events/output/receipt retention
445
452
 
446
453
  ## License
447
454
 
data/TECHNICAL.md CHANGED
@@ -29,27 +29,44 @@ harnex run codex -- --cd ~/other/repo
29
29
  | `--watch-file PATH` | Auto-send a file-change hook (`--watch PATH`/`--watch=PATH` legacy) |
30
30
  | `--context TXT` | Give the agent a task on startup |
31
31
  | `--auto-stop` | With `--context`, stop after accepted task completion |
32
- | `--artifact-report PATH` | Expose and ingest a bounded v1 proof sidecar |
33
- | `--require-artifact-report` | Fail closed unless the sidecar is accepted final proof |
32
+ | `--meta JSON` | Attach bounded caller metadata |
33
+ | `--phase TEXT` | First-class phase attribution (optionally repo-allowlisted) |
34
+ | `--artifact-report PATH` | Override the harness-authored v1 receipt destination |
35
+ | `--require-artifact-report` | Compatibility strict flag; default receipt path is sufficient |
34
36
  | `--fast` | For Codex, use `service_tier="fast"` instead of default `flex` |
35
37
  | `--timeout SEC` | Wait budget for detached registration |
36
38
 
37
39
  Codex app-server auto-stop rejects completion with no structured command/tool
38
- activity, Git delta, or fresh accepted/no-change report as
39
- `completed_no_activity`. Strict artifact-report mode works across transports
40
- and returns non-zero for missing, invalid, rejected, or stale proof.
40
+ activity or Git delta as `completed_no_activity`. Every transport gets a
41
+ harness-authored observed-state receipt; optional worker claims never determine
42
+ acceptance, and receipt-write failure is fail-closed.
41
43
 
42
- ### `harnex artifact-report` — Initialize and validate proof
44
+ ### `harnex doctor` — Preflight, drift, and retention
45
+
46
+ ```bash
47
+ harnex doctor
48
+ harnex doctor --sweep
49
+ harnex doctor --prune --dry-run
50
+ harnex doctor --prune
51
+ ```
52
+
53
+ Plain output includes adapter prerequisites and events/output/receipt retention status.
54
+ `--sweep` adds read-only live-session/tmux drift diagnostics. `--prune --dry-run`
55
+ previews age/size-cap deletions; `--prune` applies them while preserving current
56
+ and live-session files. See [docs/configuration.md](docs/configuration.md).
57
+
58
+ ### `harnex artifact-report` — Validate receipts and legacy proof
43
59
 
44
60
  ```bash
45
61
  harnex artifact-report init .harnex/reports/worker.json
46
62
  harnex artifact-report validate .harnex/reports/worker.json --final
47
63
  ```
48
64
 
49
- `init` writes a schema-valid in-progress skeleton (`--force` replaces an
50
- existing file). `validate` emits bounded machine-readable field diagnostics;
51
- `--final` additionally requires accepted/no-change final proof and successful
52
- validation records.
65
+ Normal runs write their own final receipt, so workers do not call `init`.
66
+ `validate` emits bounded machine-readable field diagnostics; `--final` accepts
67
+ a complete harness receipt whose observed terminal state was accepted. `init`
68
+ remains for legacy/manual v1 documents, whose older successful-validation
69
+ contract is still supported.
53
70
 
54
71
  ### `harnex send` — Talk to a running agent
55
72
 
@@ -98,6 +115,8 @@ Use `--json` for full payloads. JSON includes:
98
115
 
99
116
  - `log_mtime` (ISO8601 or `null`) — transcript file mtime
100
117
  - `log_idle_s` (Integer or `null`) — seconds since last transcript write
118
+ - `artifact_report_path` — harness-owned final observed-state receipt
119
+ - `artifact_claims_path` — optional bounded worker-claims input
101
120
 
102
121
  Use `--all` for all repos.
103
122
 
@@ -115,8 +134,10 @@ harnex history
115
134
  harnex history --json | jq .
116
135
  ```
117
136
 
118
- Reads `<repo>/.harnex/dispatch.jsonl`, where `<repo>` is found by walking up
119
- until `.git/` is present. Use `--global` for the no-repo fallback file.
137
+ Reads the canonical v2 stream at `<git-root>/.harnex/dispatch.jsonl`; use
138
+ `--global` for `~/.local/state/harnex/dispatch.jsonl` outside a repo. Each run
139
+ has one `dispatch_start` plus one rich `dispatch_end`. That stream is the only
140
+ destination; no flag mirrors the end row into a second file.
120
141
 
121
142
  ### `harnex logs` — Read session transcripts
122
143
 
@@ -246,8 +267,9 @@ Transport file (append-only JSONL):
246
267
  ```
247
268
 
248
269
  Each row uses schema v1 with envelope fields `schema_version`, `seq`, `ts`,
249
- `id`, and `type`. Emitted today: `started`, `send`, `exited`. `send.msg` is a
250
- 200-character preview with `msg_truncated` when shortened.
270
+ `id`, and `type`. Lifecycle, send, usage, git, summary, attempt, completion,
271
+ and adapter-specific events are additive; consumers must ignore unknown event
272
+ types. `send.msg` is a 200-character preview with `msg_truncated` when shortened.
251
273
 
252
274
  Schema details and compatibility guarantees are in [docs/events.md](docs/events.md).
253
275
 
@@ -300,7 +322,8 @@ When you run `harnex run codex --id worker`:
300
322
  hash(repo_root + id) % port_span + base_port
301
323
  walk forward until a free port is found
302
324
  6. Start HTTP server on 127.0.0.1:<port>
303
- 7. Write registry file:
325
+ 7. Opportunistically enforce configured age/size retention for events/output/receipts,
326
+ preserving current and live-session files; then write registry file:
304
327
  ~/.local/state/harnex/sessions/<repo_hash>--<id>.json
305
328
  and open transcript file:
306
329
  ~/.local/state/harnex/output/<repo_hash>--<id>.log