harnex 0.9.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: 4158bd117bb14da526926a4f80e61c0d4809b5fb401ff718c2b64ddeb0075730
4
- data.tar.gz: 6c2b75a37fbe9dbc9236b02898e9672c30af2bf72716c1c83152006760ff67de
3
+ metadata.gz: d0a3ac349311bc2677614dabb091443bb21ec82bf63852d237fb38fd47ecaeda
4
+ data.tar.gz: f1b909c7ed43636ea57a297e25f2adc1d56283faa5f8af882ed22b63b4c55f2f
5
5
  SHA512:
6
- metadata.gz: 016be4a78e559a075d8477e99343290098025f0e0cf70538da97b647c281a94c8fbf3f704276dd5ba6d00d8d3f92321c16b4f875e6849e302037fb6bb2199cac
7
- data.tar.gz: 8cec64fc830b8a37238b5ea1d0068d4422190ffb76dc0f936a10de95b6928512fdf25cf032ec5990e8c3a6e32d2012d4d0fac41f5946f68ad4e74b2e25ebce3a
6
+ metadata.gz: fe2b745dd618e1967fa5bbe1f7f55f4c0c2beb104cd6d213592f8545528118d3b3a80053b5643fce5482d3eaca9c7e6d800b6f5dd6d41873c0c9e5cc73aff332
7
+ data.tar.gz: 0c13e6cb6fe6bbac64e14ae43b23061a2b5a96cbc7f4699d438b5e5eac48ef70435c017b638635c0ded52886536f94aac37df712b1d67a9c5485dc0dd1cfa4b7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,105 @@
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
+
3
103
  ## [0.9.0] - 2026-08-03 | 01:11 AM | IST
4
104
 
5
105
  Minor bump: the durable dispatch row family is now v2, `--summary-out` changes
data/README.md CHANGED
@@ -206,8 +206,8 @@ bare `--watch` means babysitter mode.
206
206
 
207
207
  For one-shot startup prompts, add `--auto-stop`. It requires `--context`.
208
208
  A Codex app-server turn launched from `--context` only counts as accepted
209
- autonomous work when structured command/tool activity, a Git delta, or a fresh
210
- 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
211
211
  `outcome.class=completed_no_activity`, makes `watch --until done` return
212
212
  non-zero, and auto-stops with a non-zero verdict instead of reporting success.
213
213
  PTY adapters still stop on prompt return because they do not expose equivalent
@@ -258,13 +258,12 @@ and one rich v2 `dispatch_end` row at teardown. Both use the canonical stream:
258
258
  `<git-root>/.harnex/dispatch.jsonl` inside a git repo, or
259
259
  `~/.local/state/harnex/dispatch.jsonl` otherwise. The end row combines the
260
260
  history envelope with usage, context, attribution, outcome, attempt,
261
- reliability, queue/orchestration, and optional artifact-report sections.
261
+ reliability, queue/orchestration, and a harness-authored observed-state receipt.
262
262
 
263
- `--summary-out PATH` has no default. It is an explicit compatibility mirror
264
- that copies the identical `dispatch_end` row to another JSONL file; do not pass
265
- it merely to preserve rich telemetry. `harnex history`,
266
- `harnex status --id ID --json`, and `harnex wait` all read the canonical stream
267
- when the live registry is gone. Mixed legacy v1 and v2 rows remain readable.
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.
268
267
 
269
268
  Use `harnex history` to inspect it:
270
269
 
@@ -279,36 +278,37 @@ Dispatch briefs can declare soft budget metadata through `--meta`:
279
278
  harnex run pi --meta '{"read_budget_lines":2000,"output_ceiling_lines":800}' ...
280
279
  ```
281
280
 
282
- Workers can also write a small machine-readable proof sidecar while keeping the
283
- canonical explanation in plain-text `koder/` files. Initialize the real schema
284
- instead of asking a model to reproduce it from prose:
285
-
286
- ```bash
287
- harnex artifact-report init .harnex/reports/pi-i-61.json
288
- harnex run pi --id pi-i-61 \
289
- --artifact-report .harnex/reports/pi-i-61.json \
290
- --require-artifact-report \
291
- --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' \
292
- --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
+ }
293
301
  ```
294
302
 
295
- The sidecar schema is `harnex.artifact_report.v1`; harnex exposes the path as
296
- `HARNEX_ARTIFACT_REPORT_PATH` / `HARNEX_VALIDATION_REPORT_PATH`, the schema as
297
- `HARNEX_ARTIFACT_REPORT_SCHEMA`, and strict mode as
298
- `HARNEX_ARTIFACT_REPORT_REQUIRED=1`. `harnex artifact-report validate PATH`
299
- checks field shapes; add `--final` to require `status=pass`, an
300
- `accepted`/`no_change` outcome with a summary, valid command exit codes, and
301
- `validation.final_reported=true`. Diagnostics contain bounded field paths and
302
- shape errors, not report payloads or transcripts.
303
-
304
- Without `--require-artifact-report`, report defects remain fail-soft warning
305
- telemetry. With it, a missing, malformed, unsupported, oversized,
306
- contract-incomplete, rejected, or unchanged stale report makes the work verdict
307
- non-zero. A fresh explicit `no_change` report can prove an intentional no-delta
308
- task without fake edits. Harnex records compact `artifact_report`, `validation`,
309
- and `artifacts` blocks plus `outcome.class` / `outcome.report_status`; Git
310
- changes alone never imply semantic acceptance, and JSON printed in final prose
311
- 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.
312
312
 
313
313
  Queue runners can pass first-class attribution without hiding it in prose:
314
314
 
@@ -427,7 +427,7 @@ See [recipes/03_buddy.md](recipes/03_buddy.md) for the full pattern.
427
427
  | `harnex events --id <id>` | Stream structured session events (`--snapshot` for non-blocking dump) |
428
428
  | `harnex history` | List completed dispatches from `.harnex/dispatch.jsonl` |
429
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 |
430
- | `harnex artifact-report init\|validate PATH` | Create or validate bounded `harnex.artifact_report.v1` proof; use `validate --final` before strict completion |
430
+ | `harnex artifact-report init\|validate PATH` | Validate harness-authored `harnex.artifact_report.v1` receipts; `init` remains for legacy/manual documents |
431
431
  | `harnex doctor` | Run dependency and retention diagnostics; `--sweep` reports session drift, `--prune --dry-run` previews retention, and `--prune` applies it |
432
432
  | `harnex guide` | Getting started walkthrough |
433
433
  | `harnex agents-guide` | Agent-facing dispatch, chain, buddy, monitoring, and naming guides |
@@ -448,7 +448,7 @@ longer used. Remove stale `~/.claude/skills/harnex-*` or
448
448
  - [GUIDE.md](GUIDE.md) — getting started walkthrough with examples
449
449
  - [TECHNICAL.md](TECHNICAL.md) — full command reference, flags, HTTP API, architecture
450
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 retention
451
+ - [docs/configuration.md](docs/configuration.md) — phase allowlists and events/output/receipt retention
452
452
 
453
453
  ## License
454
454
 
data/TECHNICAL.md CHANGED
@@ -30,17 +30,16 @@ harnex run codex -- --cd ~/other/repo
30
30
  | `--context TXT` | Give the agent a task on startup |
31
31
  | `--auto-stop` | With `--context`, stop after accepted task completion |
32
32
  | `--meta JSON` | Attach bounded caller metadata |
33
- | `--summary-out PATH` | Mirror the canonical v2 dispatch end row to another JSONL file |
34
33
  | `--phase TEXT` | First-class phase attribution (optionally repo-allowlisted) |
35
- | `--artifact-report PATH` | Expose and ingest a bounded v1 proof sidecar |
36
- | `--require-artifact-report` | Fail closed unless the sidecar is accepted final proof |
34
+ | `--artifact-report PATH` | Override the harness-authored v1 receipt destination |
35
+ | `--require-artifact-report` | Compatibility strict flag; default receipt path is sufficient |
37
36
  | `--fast` | For Codex, use `service_tier="fast"` instead of default `flex` |
38
37
  | `--timeout SEC` | Wait budget for detached registration |
39
38
 
40
39
  Codex app-server auto-stop rejects completion with no structured command/tool
41
- activity, Git delta, or fresh accepted/no-change report as
42
- `completed_no_activity`. Strict artifact-report mode works across transports
43
- 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.
44
43
 
45
44
  ### `harnex doctor` — Preflight, drift, and retention
46
45
 
@@ -51,22 +50,23 @@ harnex doctor --prune --dry-run
51
50
  harnex doctor --prune
52
51
  ```
53
52
 
54
- Plain output includes adapter prerequisites and events/output retention status.
53
+ Plain output includes adapter prerequisites and events/output/receipt retention status.
55
54
  `--sweep` adds read-only live-session/tmux drift diagnostics. `--prune --dry-run`
56
55
  previews age/size-cap deletions; `--prune` applies them while preserving current
57
56
  and live-session files. See [docs/configuration.md](docs/configuration.md).
58
57
 
59
- ### `harnex artifact-report` — Initialize and validate proof
58
+ ### `harnex artifact-report` — Validate receipts and legacy proof
60
59
 
61
60
  ```bash
62
61
  harnex artifact-report init .harnex/reports/worker.json
63
62
  harnex artifact-report validate .harnex/reports/worker.json --final
64
63
  ```
65
64
 
66
- `init` writes a schema-valid in-progress skeleton (`--force` replaces an
67
- existing file). `validate` emits bounded machine-readable field diagnostics;
68
- `--final` additionally requires accepted/no-change final proof and successful
69
- 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.
70
70
 
71
71
  ### `harnex send` — Talk to a running agent
72
72
 
@@ -115,6 +115,8 @@ Use `--json` for full payloads. JSON includes:
115
115
 
116
116
  - `log_mtime` (ISO8601 or `null`) — transcript file mtime
117
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
118
120
 
119
121
  Use `--all` for all repos.
120
122
 
@@ -134,8 +136,8 @@ harnex history --json | jq .
134
136
 
135
137
  Reads the canonical v2 stream at `<git-root>/.harnex/dispatch.jsonl`; use
136
138
  `--global` for `~/.local/state/harnex/dispatch.jsonl` outside a repo. Each run
137
- has one `dispatch_start` plus one rich `dispatch_end`. `--summary-out` is an
138
- explicit-only end-row mirror, not a second default stream.
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.
139
141
 
140
142
  ### `harnex logs` — Read session transcripts
141
143
 
@@ -320,7 +322,7 @@ When you run `harnex run codex --id worker`:
320
322
  hash(repo_root + id) % port_span + base_port
321
323
  walk forward until a free port is found
322
324
  6. Start HTTP server on 127.0.0.1:<port>
323
- 7. Opportunistically enforce configured age/size retention for events/output,
325
+ 7. Opportunistically enforce configured age/size retention for events/output/receipts,
324
326
  preserving current and live-session files; then write registry file:
325
327
  ~/.local/state/harnex/sessions/<repo_hash>--<id>.json
326
328
  and open transcript file:
@@ -42,7 +42,7 @@ After the handshake the client is ready to issue `thread/start` and
42
42
  |-----------------------------|--------------------|-------|
43
43
  | `thread/started` | (metadata) | Stashes `threadId` |
44
44
  | `turn/started` | `turn_started` | Carries `turnId` |
45
- | `turn/completed` | `task_complete` or `task_failed` | Failed/interrupted statuses emit `task_failed` with the Codex error. A completed `--context` turn emits `task_complete` only with structured command/tool activity, Git delta, or fresh accepted/no-change report proof; otherwise it emits typed `completed_no_activity`. |
45
+ | `turn/completed` | `task_complete` or `task_failed` | Failed/interrupted statuses emit `task_failed` with the Codex error. A completed `--context` turn emits `task_complete` only with structured command/tool activity or a Git delta; otherwise it emits typed `completed_no_activity`. Harnex writes the observed-state receipt before the successful event. |
46
46
  | `item/started` | (silent) | Streaming deltas opted out |
47
47
  | `item/completed` | `item_completed` + synthesized transcript | See "tmux/STDOUT" below |
48
48
  | `error` | `error` | Turn-level Codex error notification; preserves nested `error.message` and does not count as a transport disconnect. |
@@ -102,19 +102,21 @@ harnex wait --id cx-i-242 --until task_complete --timeout 300
102
102
 
103
103
  `--until done` returns non-zero when it sees `task_failed` or failed terminal
104
104
  telemetry. This includes acknowledgment-only autonomous `--context` turns
105
- (`outcome_class=completed_no_activity`) and strict sidecar failures
106
- (`report_missing`, `report_invalid`, or `report_rejected`). Harnex classifies
107
- these from app-server item counters, Git state, and the configured report path;
108
- it does not inspect final-answer prose. The task-complete/task-failed waiters
105
+ (`outcome_class=completed_no_activity`) and the rare receipt-write failure
106
+ (`report_invalid`). Harnex classifies completion from app-server item counters
107
+ and Git state; it does not inspect final-answer prose or trust worker claims.
108
+ The task-complete/task-failed waiters
109
109
  tail the events JSONL — not the API socket — so they keep working across
110
110
  restarts and are adapter-agnostic.
111
111
 
112
- For blind dispatches that require report proof, combine
113
- `--artifact-report PATH --require-artifact-report`. Only a fresh valid sidecar
114
- at `PATH` can satisfy the contract; report-shaped JSON printed in an
115
- `agentMessage` is ordinary transcript text and is ignored. Use
116
- `harnex artifact-report init PATH` and `harnex artifact-report validate PATH
117
- --final` to avoid hand-authoring the schema.
112
+ Every blind dispatch receives a harness-authored receipt. No worker report is
113
+ required: Harnex captures command exits, Git state, completion, and usage, then
114
+ writes `HARNEX_ARTIFACT_REPORT_PATH` before emitting `task_complete`. Use
115
+ `--artifact-report PATH` only to choose a fixed destination; otherwise the
116
+ status/end row points to the default state-directory path. Review workers may
117
+ write advisory summary/verdict/P1-P3 counts to `HARNEX_ARTIFACT_CLAIMS_PATH`.
118
+ Claims and report-shaped `agentMessage` text cannot satisfy the activity gate.
119
+ Consumers can run `harnex artifact-report validate PATH --final` afterward.
118
120
 
119
121
  ## `harnex doctor`
120
122
 
@@ -152,8 +154,9 @@ for autonomous worker dispatches; legacy-pty is for interactive/TUI use.
152
154
  - **`task_failed` immediately after dispatch.** Check
153
155
  `harnex events --id <session>`. Provider/model failures retain their Codex
154
156
  error message. `completed_no_activity` means the turn ended with no
155
- command/tool or Git/report proof; `report_missing` / `report_invalid` /
156
- `report_rejected` identify strict sidecar defects. Common provider failures
157
+ command/tool or Git activity. `report_invalid` now primarily identifies a
158
+ harness receipt-write failure; old rows may still contain the legacy
159
+ `report_missing` / `report_rejected` classes. Common provider failures
157
160
  include auth environment variables (for example `OPENAI_API_KEY` /
158
161
  `AZURE_OPENAI_API_KEY`) and model unavailability.
159
162
 
@@ -46,14 +46,15 @@ The effective value is the first-class `harnex run --phase TEXT` value, or
46
46
  The allowlist must be an array of non-empty strings. Policy must be `warn` or
47
47
  `reject`.
48
48
 
49
- ## Events and output retention
49
+ ## Events, output, and receipt retention
50
50
 
51
- Per-session event JSONL and output transcripts live under Harnex's local state
52
- directory and are not the durable dispatch stream:
51
+ Per-session event JSONL, output transcripts, and generated proof receipts live
52
+ under Harnex's local state directory and are not the durable dispatch stream:
53
53
 
54
54
  ```text
55
55
  ~/.local/state/harnex/events/
56
56
  ~/.local/state/harnex/output/
57
+ ~/.local/state/harnex/receipts/
57
58
  ```
58
59
 
59
60
  Defaults apply independently to each directory:
@@ -73,6 +74,10 @@ Override them in repo configuration:
73
74
  "output": {
74
75
  "max_age_days": 45,
75
76
  "max_bytes": 1073741824
77
+ },
78
+ "receipts": {
79
+ "max_age_days": 45,
80
+ "max_bytes": 1073741824
76
81
  }
77
82
  }
78
83
  }
@@ -85,10 +90,12 @@ HARNEX_EVENTS_MAX_AGE_DAYS
85
90
  HARNEX_EVENTS_MAX_BYTES
86
91
  HARNEX_OUTPUT_MAX_AGE_DAYS
87
92
  HARNEX_OUTPUT_MAX_BYTES
93
+ HARNEX_RECEIPTS_MAX_AGE_DAYS
94
+ HARNEX_RECEIPTS_MAX_BYTES
88
95
  ```
89
96
 
90
97
  Limits must be positive integers. Harnex deletes only regular files directly
91
- owned by the `events` and `output` directories: expired files first, then the
98
+ owned by the `events`, `output`, and `receipts` directories: expired files first, then the
92
99
  oldest unprotected files until the size cap is met. It never follows paths
93
100
  outside those directories. Files for the current session, live registry PIDs,
94
101
  and alive uncompleted dispatch-start rows are protected. If protected files