From b9b9c5120c5cff8eb40dd0652b6a436922e7c324 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 23 Oct 2026 15:11:44 +0200 Subject: [PATCH] =?UTF-8?q?Name=20an=20ask's=20step=20key=20.=20and=20its=20watch,=20so=20the=20asker=20learns=20which=20?= =?UTF-8?q?node=20took=20it=20and=20refuses=20other=20keys=20(issue=201887?= =?UTF-8?q?=20A1b,=20ADR=200525=20=C2=A76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go/delivery/stepasks.go | 30 +++++++++++++++++++++++++ go/delivery/stepasks_key_test.go | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 go/delivery/stepasks_key_test.go diff --git a/go/delivery/stepasks.go b/go/delivery/stepasks.go index 6754074..e83c4a3 100644 --- a/go/delivery/stepasks.go +++ b/go/delivery/stepasks.go @@ -343,3 +343,33 @@ type KeptTaken struct { Digest string `json:"digest"` Tests int `json:"tests"` } + +// AskStepKey is the key, in the build module's StepsBucket, of the step that runs an ask on node: `.` +// (ADR 0525 ยง6). The node is the one that pulled the ask, which the asker learns from the key. +func AskStepKey(node, askID string) (string, error) { + if err := checkAskID(askID); err != nil { + return "", err + } + return StepKey(node, askID) +} + +// AskStepWatch is the pattern an asker watches its ask's step with, the node a wildcard: `*.`. +func AskStepWatch(askID string) (string, error) { + if err := checkAskID(askID); err != nil { + return "", err + } + return "*." + askID, nil +} + +// ParseAskStepKey reads the node and the ask id from a step key of an ask; it refuses a key that is not `.`, such as a have key `.` or a step key a direct tool call wrote under another id form. +func ParseAskStepKey(key string) (node, askID string, err error) { + node, askID, err = ParseStepKey(key) + if err != nil { + return "", "", err + } + if err := checkAskID(askID); err != nil { + return "", "", fmt.Errorf("delivery: %q is not an ask's step key: %w", key, err) + } + return node, askID, nil +} diff --git a/go/delivery/stepasks_key_test.go b/go/delivery/stepasks_key_test.go new file mode 100644 index 0000000..88e8625 --- /dev/null +++ b/go/delivery/stepasks_key_test.go @@ -0,0 +1,38 @@ +package delivery_test + +import ( + "testing" + + "git.novox.be/novox/mesh-sdk/go/buildstep" + "git.novox.be/novox/mesh-sdk/go/delivery" +) + +// 1887/A1b: the ask's step key is ., watched with *., and its parser refuses another bucket's key. +func TestAskStepKey(t *testing.T) { + id := buildstep.NewID() // an ask's id is a step id + key, err := delivery.AskStepKey("shanks", id) + if err != nil || key != "shanks."+id { + t.Fatalf("AskStepKey = %q, %v", key, err) + } + if w, err := delivery.AskStepWatch(id); err != nil || w != "*."+id { + t.Fatalf("AskStepWatch = %q, %v", w, err) + } + node, got, err := delivery.ParseAskStepKey(key) + if err != nil || node != "shanks" || got != id { + t.Fatalf("ParseAskStepKey = %q %q %v", node, got, err) + } + for _, other := range []string{ + "shanks.mesh-delivery-view", // a have key + "shanks.node", // a node's own have key + "shanks.postgres", // a have key whose module is one token + "mesh-delivery-builder_builds.x", // a pulse key + "shanks", + } { + if _, _, err := delivery.ParseAskStepKey(other); err == nil { + t.Errorf("parsed %q", other) + } + } + if _, err := delivery.AskStepKey("shanks", "postgres"); err == nil { + t.Error("AskStepKey took a module's name for an ask id") + } +}