From 7310af36e6a04d187b24f889ee549f46087a9026 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 21:56:38 +0100 Subject: [PATCH] G findings4 watchman: a finding's id keeps the safe key parts its condition's key had MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 G §689, A-14 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ --- .../internal/watch/finding.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/mesh-delivery-watchman/internal/watch/finding.go b/modules/mesh-delivery-watchman/internal/watch/finding.go index b0707089..837e3db3 100644 --- a/modules/mesh-delivery-watchman/internal/watch/finding.go +++ b/modules/mesh-delivery-watchman/internal/watch/finding.go @@ -66,15 +66,29 @@ func (f Finding) Key() string { return FindingKey(f.Scope, f.ID, token) } +// KeyID is an id as the parts of a key: each dot-separated part made safe for a bus key, as the key of a condition is +// (mcc.Key), so the finding's key keeps the shape its condition had. +func KeyID(id string) string { + parts := strings.Split(id, ".") + for i, p := range parts { + p = unsafeKey.ReplaceAllString(strings.TrimSpace(p), "_") + if p == "" { + p = "_" + } + parts[i] = p + } + return strings.Join(parts, ".") +} + // Contract is f as the seat mesh-conditions' generated finding (design 47 G §686, §689): a ref, where the finding has a // token other than its kind, goes into the id as `.` (A-14), and is kept in `ref` so the finding reads back as // it was written. The resolved words are not a field of the seat (A-22), and its `at` is its `since`, so the same thing // observed again writes the same bytes. func (f Finding) Contract() condseat.Finding { - id, ref := f.ID, "" + id, ref := KeyID(f.ID), "" if f.Token != "" && f.Token != f.Kind { ref = f.Token - id = f.ID + "." + ref + id = id + "." + KeyID(ref) } v := condseat.Finding{Scope: f.Scope, ID: id, Kind: f.Kind, Ref: ref, Severity: f.Severity, Summary: f.Summary, Headline: f.Headline, Explanation: f.Explanation, Needs: f.Needs, Source: f.Source, Observer: Observer,