From a73b04843dd1d4ab3fec840b3582cc5383923691 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 10 Oct 2026 21:10:36 +0200 Subject: [PATCH] mesh-issues: a take-over note records who acted, not the worker named; tests for the remaining rules (hq issue 399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The note's by is who acted as the runtime proves it (ADR 0275 §5); the note's text names both workers. Tests for a resolved issue corrected, link and set keeping the worker current, and a take-over reason with a secret's shape. The ready-to-verify note says an issue resolves from fixing. --- .../mesh-issues/cmd/mesh-issues/delivery.go | 2 +- .../mesh-issues/cmd/mesh-issues/tracker.go | 2 +- .../cmd/mesh-issues/tracker_test.go | 1 + .../cmd/mesh-issues/worker_test.go | 55 ++++++++++++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/modules/mesh-issues/cmd/mesh-issues/delivery.go b/modules/mesh-issues/cmd/mesh-issues/delivery.go index 1e00298e..eaa84b82 100644 --- a/modules/mesh-issues/cmd/mesh-issues/delivery.go +++ b/modules/mesh-issues/cmd/mesh-issues/delivery.go @@ -187,7 +187,7 @@ func (t *Tracker) afterFix(n uint64) error { prefix := fmt.Sprintf("Ready to verify: every fix of this issue is delivered (%s).", strings.Join(prs, ", ")) if !noted(is, prefix) { if _, err := t.note(GroomBy, n, prefix+" Delivered is not resolved: prove the acceptance live, then "+ - "resolve it. (novox/hq ADR 0281)"); err != nil { + "resolve it, from fixing: move it to fixing with by first if it is not there. (novox/hq ADR 0281, ADR 0283)"); err != nil { return err } } diff --git a/modules/mesh-issues/cmd/mesh-issues/tracker.go b/modules/mesh-issues/cmd/mesh-issues/tracker.go index b45a5482..ea9b84cc 100644 --- a/modules/mesh-issues/cmd/mesh-issues/tracker.go +++ b/modules/mesh-issues/cmd/mesh-issues/tracker.go @@ -463,7 +463,7 @@ func (t *Tracker) move(by string, a MoveAsk) (*Moved, error) { changed = append(changed, "by") } if work.from != "" { - is.Notes = append(is.Notes, Note{By: worker, At: at, Text: takeOverNote(work, worker, takeOver, prev)}) + is.Notes = append(is.Notes, Note{By: by, At: at, Text: takeOverNote(work, worker, takeOver, prev)}) } // The same status again only to change what it holds: a fix named while the issue stays located, to be // resolved once it is delivered and proven (ADR 0281 §3). diff --git a/modules/mesh-issues/cmd/mesh-issues/tracker_test.go b/modules/mesh-issues/cmd/mesh-issues/tracker_test.go index f709b60c..1308826b 100644 --- a/modules/mesh-issues/cmd/mesh-issues/tracker_test.go +++ b/modules/mesh-issues/cmd/mesh-issues/tracker_test.go @@ -303,6 +303,7 @@ func TestTheCyclesRulesHoldAtTheWrite(t *testing.T) { {"take-over on another status", []string{"hq"}, "", MoveAsk{Status: StatusLocated, TakeOver: "x"}, "take-over is for"}, {"take-over with nobody to take from", nil, "", MoveAsk{Status: StatusFixing, By: "a@g14", TakeOver: "x"}, "nobody works on"}, {"an amended design that is not one", nil, "", MoveAsk{Status: StatusDiagnosing, By: "a@g14", AmendedDesign: []string{"../etc/passwd"}}, "not a design document"}, + {"a take-over reason that carries a secret", nil, StatusDiagnosing, MoveAsk{Status: StatusFixing, By: "b@ace", TakeOver: "the key is sk-ant-api03-abcdefghijklmnopqrstuvwxyz"}, "carries no secret"}, {"a fixed-by of two lines", []string{"hq"}, fx, MoveAsk{Status: StatusResolved, FixedBy: []string{"a\nb"}}, "one line"}, } for _, c := range cases { diff --git a/modules/mesh-issues/cmd/mesh-issues/worker_test.go b/modules/mesh-issues/cmd/mesh-issues/worker_test.go index e63448e7..be814aea 100644 --- a/modules/mesh-issues/cmd/mesh-issues/worker_test.go +++ b/modules/mesh-issues/cmd/mesh-issues/worker_test.go @@ -91,7 +91,7 @@ func TestATakeOverNeedsAReasonAndIsNoted(t *testing.T) { is, _ := h.Get(n) if is.Worker.By != "jochen@shanks" || m.TookOverFrom != "fix-a@g14" || len(is.Notes) != 1 || !strings.Contains(is.Notes[0].Text, "from fix-a@g14") || !strings.Contains(is.Notes[0].Text, "the operator finishes it by hand") || - is.Notes[0].By != "jochen@shanks" { + is.Notes[0].By != Unproven { t.Fatalf("worker %+v, notes %+v, answer %+v", is.Worker, is.Notes, m) } ev := h.said() @@ -244,3 +244,56 @@ func TestTheMoveVerbTakesByAndTakeOver(t *testing.T) { } } } + +// A resolved issue may still have what it holds corrected: resolved to resolved is not refused as "not from fixing". +func TestAResolvedIssueMayBeCorrected(t *testing.T) { + h := newHarness(t) + n := h.open(t, "x", "hq") + h.fixing(t, n) + if _, err := h.Move(MoveAsk{Number: n, Status: StatusResolved, FixedBy: []string{"hq#1"}}); err != nil { + t.Fatal(err) + } + if _, err := h.Move(MoveAsk{Number: n, Status: StatusResolved, FixedBy: []string{"hq#1", "hq#2"}}); err != nil { + t.Fatalf("a correction of a resolved issue: %v", err) + } + if is, _ := h.Get(n); len(is.FixedBy) != 2 { + t.Fatalf("%+v", is.FixedBy) + } +} + +// A caller's link and set keep the worker current, as a note does (ADR 0283 §4). +func TestALinkAndASetKeepTheWorkerCurrent(t *testing.T) { + for _, act := range []string{"link", "unlink", "set"} { + t.Run(act, func(t *testing.T) { + h := newHarness(t) + now := clocked(h) + n := h.open(t, "x", "hq") + other := h.open(t, "y") + if act == "unlink" { + if _, err := h.Link(LinkAsk{Number: n, Kind: LinkRelatesTo, Target: keyOf(other)}); err != nil { + t.Fatal(err) + } + } + if _, err := h.Move(MoveAsk{Number: n, Status: StatusFixing, By: "fix-a@g14"}); err != nil { + t.Fatal(err) + } + *now = now.Add(20 * time.Hour) + var err error + switch act { + case "link": + _, err = h.Link(LinkAsk{Number: n, Kind: LinkRelatesTo, Target: keyOf(other)}) + case "unlink": + _, err = h.Link(LinkAsk{Number: n, Kind: LinkRelatesTo, Target: keyOf(other), Remove: true}) + case "set": + _, err = h.Set(SetAsk{Number: n, Severity: "minor"}) + } + if err != nil { + t.Fatal(err) + } + *now = now.Add(10 * time.Hour) // 30 h after the start, 10 h after the act + if _, err := h.Move(MoveAsk{Number: n, Status: StatusFixing, By: "fix-b@ace"}); err == nil { + t.Fatalf("the %s did not keep the worker current", act) + } + }) + } +}