Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/issue494_test.go
T
jochen 9f893b6305
mesh/merge-gate pass: builds mesh-knowledge → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
mesh-knowledge: let a record change a drafted clause, and never redraft a record whose clauses others link by number
A changes link onto a drafted clause was refused, so records that really
change such a rule could only cite it (ADR 0294, 0301, 0320); and return
renumbered ADR 0281's clauses, pointing other records' links at other
clauses. The changed drafted clause keeps its number and stays unserved;
return refuses a record whose clauses another record changed or links by
number. Issue 494, ADR 0321.
2026-10-12 14:05:37 +02:00

109 lines
4.4 KiB
Go

package main
import (
"context"
"strings"
"testing"
)
// novox/hq issue 494: a record may change a clause the knowledge base holds only as drafted, and a record whose
// clauses another record links by number is never drafted again, so no link ever points at another clause.
// draftedNotConfirmed drafts 0240 and has the second judgement reject its first clause: it stays drafted.
func draftedNotConfirmed(t *testing.T, w *world) Key {
t.Helper()
k := Key{KindDecision, 240}
draftUntil(t, w, k, DraftDrafted)
e := mustGet(t, w.k.Store, k)
r := e.Current.Clone()
r.Clauses[0].Text += " NOT EXACT"
if err := w.k.Store.Save(context.Background(), Version{By: "test", Why: "a draft the checker will reject", Record: r}, true, false, e.Revision); err != nil {
t.Fatal(err)
}
r = draftUntil(t, w, k, DraftDone)
if r.Clauses[0].Source != SourceDrafted || !r.Accepted() {
t.Fatalf("0240 §1 is not a drafted clause of an accepted record: %s %+v", r.Status, r.Clauses[0])
}
return k
}
func TestARecordMayChangeAClauseHeldOnlyAsDrafted(t *testing.T) {
eachStore(t, func(t *testing.T, s Store) {
w := imported(t, s)
k := draftedNotConfirmed(t, w)
n := w.reserved(t, "author@test")
p := valid(n)
p.Links = append(p.Links, LinkAsk{Kind: LinkChanges, To: "0240 §1", From: 2})
if _, err := w.k.Propose("author@test", p); err != nil {
t.Fatalf("a change of a drafted clause was refused: %v", err)
}
w.reviewAll(t)
if e := mustGet(t, s, Key{KindDecision, n}); !e.Current.Accepted() {
t.Fatalf("not accepted: %s", e.Current.Status)
}
// The drafted clause is changed by it, keeps its number and stays drafted: still never served as in force.
r := mustGet(t, s, k).Current
c := r.Clause(1)
want := itoa4(n) + " §2"
if c == nil || c.Status != ClauseChanged || c.Source != SourceDrafted || len(c.ChangedBy) != 1 || !strings.HasSuffix(c.ChangedBy[0], want) {
t.Fatalf("0240 §1 after the change: %+v", c)
}
if Served(r, *c) {
t.Fatal("a drafted clause that was changed is served as holding")
}
g, err := w.k.Get(GetAsk{Kind: KindDecision, Number: 240})
if err != nil {
t.Fatal(err)
}
if h := g.Clauses[0].Holds; !strings.HasPrefix(h, "drafted, not confirmed") || !strings.Contains(h, "changed by ADR "+want) {
t.Fatalf("get says of 0240 §1: %q", h)
}
// The whole-set rules find nothing wrong with the change.
f, _ := s.Findings(context.Background())
for _, x := range f {
if x.Rule == "partial" {
t.Fatalf("a whole-set finding: %+v", x)
}
}
})
}
func TestARecordWhoseClausesAreLinkedByNumberIsNeverReturned(t *testing.T) {
eachStore(t, func(t *testing.T, s Store) {
w := imported(t, s)
k := draftedNotConfirmed(t, w)
// A proposed record cites 0240 §2: returning 0240 would point it at whatever is drafted second next time.
n := w.reserved(t, "author@test")
p := valid(n)
p.Links = append(p.Links, LinkAsk{Kind: LinkCites, To: "0240 §2", From: 1})
w.judge.review = `{"verdict":"changes","findings":["not yet"]}`
if _, err := w.k.Propose("author@test", p); err != nil {
t.Fatal(err)
}
w.reviewAll(t)
before := mustGet(t, s, k)
_, err := w.k.Return("operator@shanks", k, "clause 1 says more than the section")
if err == nil {
t.Fatal("a record a proposed record links by clause was returned")
}
if !strings.Contains(err.Error(), "ADR "+itoa4(n)+" (proposed) cites §2") || !strings.Contains(err.Error(), "changes 0240 §N") {
t.Fatalf("the refusal does not say what holds the clauses or the way forward: %v", err)
}
after := mustGet(t, s, k)
if after.Versions != before.Versions || len(after.Current.Clauses) != len(before.Current.Clauses) || len(w.said.of(EventReturned)) != 0 {
t.Fatalf("a refused return changed 0240: %d versions (was %d), %d clauses", after.Versions, before.Versions, len(after.Current.Clauses))
}
// A clause another record changed holds the record to its numbers too, with no link left to scan.
w2 := imported(t, newMemStore())
k2 := draftedNotConfirmed(t, w2)
n2 := w2.reserved(t, "author@test")
p2 := valid(n2)
p2.Links = append(p2.Links, LinkAsk{Kind: LinkChanges, To: "0240 §1", From: 2})
w2.accepted(t, "author@test", p2)
if _, err := w2.k.Return("operator@shanks", k2, "again"); err == nil || !strings.Contains(err.Error(), "§1 is changed by ADR "+itoa4(n2)+" §2") {
t.Fatalf("a record with a changed clause was returned, or the refusal does not name the change: %v", err)
}
})
}