From 788db0ff4d3396d3115e7c18307e9ee724186439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Bene=C5=A1?= Date: Tue, 10 Nov 2026 12:13:38 +0000 Subject: [PATCH] Harness spec 3.5.0: the watch command's hash puts the exit status after the bytes, so the output is hashed as it streams Co-Authored-By: Claude Opus 5.5 --- harness/README.md | 2 +- harness/acp.md | 13 +++++++------ harness/conformance/monitor.json | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/harness/README.md b/harness/README.md index f4079e5..dfe8600 100644 --- a/harness/README.md +++ b/harness/README.md @@ -93,7 +93,7 @@ round (`acp.md` *Scheduled work on a linked computer*, `loop.json` `scheduled`). goes on. `ask_user` and `plan_submit` have nobody to answer; `show` is not offered. - **Monitor mode, the device's half**: `_lembas/monitor` runs a schedule's watch command in its folder, with a timeout, under the mode's permission rules — **a command the mode would ask about - is refused** — and answers the output's hash (`sha256:` over the exit status and the bytes) and a + is refused** — and answers the output's hash (`sha256:` over the bytes and then the exit status) and a short excerpt; the server runs the model only when the hash changed, and records `unchanged` otherwise. - **Schedules over `/api/v2` with the device's token** (scope `link`): the CLI's `/schedule` lists, diff --git a/harness/acp.md b/harness/acp.md index 2cc07e1..1e3b9b8 100644 --- a/harness/acp.md +++ b/harness/acp.md @@ -435,10 +435,11 @@ default rules, the global rules and — in a trusted project — the project's), `scheduled.texts.monitor_denied`, both `-32602` with `data: { reason: "permission" }` (`conformance/monitor.json`). -**The hash** (`monitor.json` `hash` cases): `sha256:` and the lowercase hex SHA-256 of the exit -status as decimal digits, a newline, and then the output's bytes exactly as read — standard output -and standard error as one stream, as the `bash` tool reads them. Nothing is normalised: a watch -command that prints a time changes every time, and should not. +**The hash** (`monitor.json` `hash` cases): `sha256:` and the lowercase hex SHA-256 of the +output's bytes exactly as read — standard output and standard error as one stream, as the `bash` +tool reads them — followed by a newline and the exit status as decimal digits (after the bytes, so +the device hashes the output as it streams, however long). Nothing is normalised: a watch command +that prints a time changes every time, and should not. ## `_lembas/*` methods @@ -499,8 +500,8 @@ with no input; at the timeout the group is stopped. Result `{ hash, exit, timed_out, excerpt, truncated, bytes, duration_ms }`: `hash` as above (`null` when it timed out), `exit` the exit status (`null` on a signal or a timeout), `excerpt` the start of the output decoded as UTF-8 (invalid bytes replaced), at most `scheduled.monitor.excerpt_chars` -characters, `truncated` whether there was more, `bytes` the output's length. The device keeps at -most `scheduled.monitor.output_max_bytes` of output and hashes all of it as it streams. +characters, `truncated` whether there was more, `bytes` the output's length. The device hashes all +of the output as it streams and keeps at most `scheduled.monitor.output_max_bytes` of it. ### `_lembas/directories` — client → agent → result diff --git a/harness/conformance/monitor.json b/harness/conformance/monitor.json index dca9a5d..e4c1b1e 100644 --- a/harness/conformance/monitor.json +++ b/harness/conformance/monitor.json @@ -6,35 +6,35 @@ "kind": "hash", "exit": 0, "output": "ok\n", - "expect": "sha256:9dbd5131695af2d8636ea64178344ffcb5d682333c2ac8342153b5f8a999037a" + "expect": "sha256:9427ae13c91f98a3af2213b34fb59e4bb5aa9e6101a1f7407f526dde3571e6aa" }, { "about": "The same output with another exit status is a change.", "kind": "hash", "exit": 1, "output": "ok\n", - "expect": "sha256:262115bdbc989849fb45967adac594dde8d45deb291f3a26b38fb2bf4e10512b" + "expect": "sha256:21e0bbd37d04094ef29f22d0212bb0f4bedda1b1fab38d51f9b8670b5cf07df4" }, { "about": "No output.", "kind": "hash", "exit": 0, "output": "", - "expect": "sha256:9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa" + "expect": "sha256:c7757c0896cbfe6182d8ea2bda4a8bf94addc428980eedab8609c57ca7ff1763" }, { "about": "A trailing newline counts: nothing is normalised.", "kind": "hash", "exit": 0, "output": "ok", - "expect": "sha256:05df918976ca296956abf9b5fb424bb9bef7c32bb471b1005b417abc696e25a9" + "expect": "sha256:b9b8913ecae1ba8190ef1a22cc57afef08ad3933ab4b13a32f8cdccb07bb527b" }, { "about": "UTF-8 output, hashed as its bytes.", "kind": "hash", "exit": 0, "output": "lembas · ✓\n", - "expect": "sha256:cb00179ad9e36546b1b973ded0903592a6b975552b5596feac573426c67e24e4" + "expect": "sha256:3d1c38d3b96ee10d71faac35eb2933ba13b68d6325872fdda3e459c9fbd17d98" }, { "about": "A read-only command in Edit runs.",