From 741239a5068f20f0a4401887d96042ae9da2aaf2 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 12 Nov 2026 05:04:58 +0100 Subject: [PATCH] EE-K1: the Stop hook advises mesh_wait on a wait's event and a reread after every answer, or a watcher, with no condition on a grant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 E §20 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h --- modules/claude-code/cmd/claude-code/stop.go | 9 ++++----- modules/claude-code/cmd/claude-code/stop_test.go | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/claude-code/cmd/claude-code/stop.go b/modules/claude-code/cmd/claude-code/stop.go index d1328c778..2a7bab6b8 100644 --- a/modules/claude-code/cmd/claude-code/stop.go +++ b/modules/claude-code/cmd/claude-code/stop.go @@ -60,13 +60,12 @@ func waitBlocksStop(session string, w Wait, now time.Time) (bool, string) { "{\"op\":\"said\",\"session\":%q,\"by\":%q,\"id\":%q}.", w.ID, w.What, session, w.By, w.ID) } -// watcherAdvice is what a session is told to do to follow a wait (ADR 0489 §3, review round 1 item 3): a -// family is a candidate for mesh_wait only when the tool runner is granted it (ADR 0367 §2), which this hook -// cannot know, so it is offered conditionally; a watcher that rereads the read address always works. +// watcherAdvice is what a session is told to do to follow a wait (ADR 0489 §3, design 47 E §20): a wait with an event is +// followed by mesh_wait on it, rereading what it reads after every answer, or by a watcher that does; with no event, by a +// watcher that rereads the read address. The advice puts no condition on a grant. func watcherAdvice(w Wait) string { if w.Family != "" { - return fmt.Sprintf("mesh_wait on %s if this session's tool runner is granted it, otherwise start a watcher "+ - "that rereads %s", w.Family, w.Read.Address) + return fmt.Sprintf("mesh_wait on %s and reread %s after every answer, or start a watcher that does", w.Family, w.Read.Address) } return fmt.Sprintf("start a watcher that rereads %s", w.Read.Address) } diff --git a/modules/claude-code/cmd/claude-code/stop_test.go b/modules/claude-code/cmd/claude-code/stop_test.go index 71501a566..11ead0cf0 100644 --- a/modules/claude-code/cmd/claude-code/stop_test.go +++ b/modules/claude-code/cmd/claude-code/stop_test.go @@ -300,15 +300,15 @@ func TestStopAllowsWhenTheRefusalCounterCannotBeWritten(t *testing.T) { } } -// Review round 1, item 3 (1743/AC1, §3 with ADR 0367 §2): a family is offered only conditionally (the tool -// runner may not be granted it); without one, the advice is only to start a watcher. -func TestWaitBlocksStopAdvisesMeshWaitOnlyConditionallyOtherwiseAWatcher(t *testing.T) { +// Design 47 E §20: a wait with an event is followed by mesh_wait on it and a reread after every answer, or a watcher +// that does, with no condition on a grant; without an event, the advice is only to start a watcher. +func TestWaitBlocksStopAdvisesMeshWaitOnItsEventOtherwiseAWatcher(t *testing.T) { withFamily := openWait("w-f", 10) withFamily.Read = WaitRead{Address: "mesh-delivery.recheck"} withFamily.Family = "mesh-delivery.decided" _, reason := waitBlocksStop("sess-f", withFamily, stopT0.Add(time.Minute)) - if !strings.Contains(reason, "mesh_wait on mesh-delivery.decided if this session's tool runner is granted it") || - !strings.Contains(reason, "otherwise start a watcher that rereads mesh-delivery.recheck") { + if !strings.Contains(reason, "mesh_wait on mesh-delivery.decided and reread mesh-delivery.recheck after every answer, or start a watcher that does") || + strings.Contains(reason, "granted") { t.Fatalf("%s", reason) }