mesh/delivery superseded: a newer head of the same pull request
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)
The operator decided on 2026-10-12 that a rule of the record set that fails is a fault of the records, which their authors repair, not the module failing: as issue 508 decided for a proposal never reviewed, health says only whether the module works, and a finding must not hold the module's own rollouts. The findings go through the notice the knowledge base already sends (operator-channel.notify, a warning), once per finding, kept as told across restarts, and cleared when the rule holds again.
193 lines
7.6 KiB
Go
193 lines
7.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A decision nothing would cite is not accepted: its review asks changes itself, without a judgement, naming the
|
|
// rule cited, and the module stays healthy (novox/hq issue 504). Before, the review approved it, the acceptance
|
|
// broke the rule, and the module turned unhealthy for a record its author could only repair after the fact.
|
|
func TestADecisionNothingWouldCiteIsNotAccepted(t *testing.T) {
|
|
eachStore(t, func(t *testing.T, s Store) {
|
|
w := imported(t, s)
|
|
n := w.reserved(t, "author@g14")
|
|
p := valid(n)
|
|
p.Homes = nil
|
|
if _, err := w.k.Propose("author@g14", p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
judged := len(w.judge.reviews)
|
|
w.reviewAll(t)
|
|
e := mustGet(t, s, Key{KindDecision, n})
|
|
if e.Current.Status != StatusProposed {
|
|
t.Fatalf("a decision nothing cites was %s by its review", e.Current.Status)
|
|
}
|
|
rv := e.Current.Reviews
|
|
if len(rv) == 0 || rv[len(rv)-1].Verdict != VerdictChanges || !strings.Contains(rv[len(rv)-1].Findings, "the rule cited") ||
|
|
rv[len(rv)-1].By != ReviewBy {
|
|
t.Fatalf("its review asked no changes for the rule cited: %+v", rv)
|
|
}
|
|
if len(w.judge.reviews) != judged {
|
|
t.Fatal("a judgement was spent on a record the rule cited refuses")
|
|
}
|
|
if h := w.k.Health(nil); !h.Healthy {
|
|
t.Fatalf("unhealthy for a record not accepted: %s", h.Why)
|
|
}
|
|
// Named with its home, it is accepted, and the module stays healthy.
|
|
p.Homes = []string{testHome}
|
|
w.accepted(t, "author@g14", p)
|
|
if h := w.k.Health(nil); !h.Healthy {
|
|
t.Fatalf("unhealthy after a decision with its home was accepted: %s", h.Why)
|
|
}
|
|
})
|
|
}
|
|
|
|
// A new decision is cited by a record proposed after it: no record can link a number only reserved. Once the
|
|
// citing record is accepted, the new one proposed again is accepted. A correction of the citing record that drops
|
|
// the link still leaves the decision cited by nothing, and that is loud: told to the operator once, as a warning
|
|
// naming the rule and the record, while the module stays healthy and its rollouts are not held (the operator's
|
|
// decision of 2026-10-12 on novox/hq issue 504; to-be 55 §10). A record that cites it clears the notice.
|
|
func TestARecordAcceptedCitingANewDecisionLetsItBeAcceptedAndDroppingTheLinkIsLoud(t *testing.T) {
|
|
eachStore(t, func(t *testing.T, s Store) {
|
|
w := imported(t, s)
|
|
n := w.reserved(t, "author@g14")
|
|
a := valid(n)
|
|
a.Homes = nil
|
|
if _, err := w.k.Propose("author@g14", a); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.reviewAll(t)
|
|
m := w.reserved(t, "author@g14")
|
|
b := valid(m)
|
|
b.Links = append(b.Links, LinkAsk{Kind: LinkCites, To: itoa4(n)})
|
|
w.accepted(t, "author@g14", b)
|
|
w.accepted(t, "author@g14", a) // its revision: an accepted record links it now
|
|
if h := w.k.Health(nil); !h.Healthy {
|
|
t.Fatalf("unhealthy with both accepted, one citing the other: %s", h.Why)
|
|
}
|
|
w.k.TellFindings()
|
|
if len(w.told) != 0 {
|
|
t.Fatalf("told with every rule holding: %v", w.told)
|
|
}
|
|
// A correction of the citing record drops its link: the rule, run after the acceptance, says so.
|
|
b.Links = b.Links[:len(b.Links)-1]
|
|
b.Insight = "it cited " + itoaKey(n)
|
|
w.accepted(t, "author@g14", b)
|
|
h := w.k.Health(nil)
|
|
if !h.Healthy || !strings.Contains(h.Why, "the rule cited") || !strings.Contains(h.Why, itoaKey(n)) {
|
|
t.Fatalf("a decision left cited by nothing: the health should stay healthy and name it, not %+v", h)
|
|
}
|
|
w.k.TellFindings()
|
|
w.k.TellFindings()
|
|
if len(w.told) != 1 || !strings.Contains(w.told[0], "rule cited") || !strings.Contains(w.told[0], itoaKey(n)) ||
|
|
!strings.HasPrefix(w.toldKeys[0], findingPrefix) {
|
|
t.Fatalf("the operator is not told once, naming the rule and the record: %v %v", w.told, w.toldKeys)
|
|
}
|
|
// A restart tells nothing again: what was told is kept in the store.
|
|
told := w.toldKeys[0]
|
|
restarted := newWorld(t, s, fixture())
|
|
restarted.clock = w.clock
|
|
restarted.k.TellFindings()
|
|
if len(restarted.told) != 0 {
|
|
t.Fatalf("told again after a restart: %v", restarted.told)
|
|
}
|
|
w.told = nil
|
|
if mustGet(t, s, Key{KindDecision, n}).Current.Status != StatusAccepted {
|
|
t.Fatal("the acceptance was undone")
|
|
}
|
|
// A record that cites it heals it.
|
|
k := w.reserved(t, "author@g14")
|
|
c := valid(k)
|
|
c.Links = append(c.Links, LinkAsk{Kind: LinkCites, To: itoa4(n)})
|
|
w.accepted(t, "author@g14", c)
|
|
if h := w.k.Health(nil); strings.Contains(h.Why, itoaKey(n)+" is accepted and nothing cites it") {
|
|
t.Fatalf("still said after a record cited it: %s", h.Why)
|
|
}
|
|
w.k.TellFindings()
|
|
if len(w.cleared) != 1 || w.cleared[0][0] != told {
|
|
t.Fatalf("the notice is not cleared once the rule holds again: %v", w.cleared)
|
|
}
|
|
w.k.TellFindings()
|
|
if len(w.cleared) != 1 || len(w.told) != 0 {
|
|
t.Fatalf("after the clear: cleared %v, told %v", w.cleared, w.told)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Each of the 4 whole-set rules, failing on a record set made to break it.
|
|
func TestEachWholeSetRuleFails(t *testing.T) {
|
|
acc := func(n int, links ...Link) *Record {
|
|
return &Record{Kind: KindDecision, Number: n, Status: StatusAccepted, Links: links,
|
|
Clauses: []Clause{{N: 1, Text: "**One.** x", Status: ClauseInForce, Source: SourceWritten}}}
|
|
}
|
|
to := func(kind string, n, c int) Link {
|
|
return Link{Kind: kind, To: Ref{Kind: KindDecision, Number: n, Clause: c}}
|
|
}
|
|
cases := []struct {
|
|
rule string
|
|
set []*Record
|
|
}{
|
|
{"cited", func() []*Record { r := acc(1); r.AcceptedAt = r.AcceptedAt.AddDate(2026, 0, 0); return []*Record{r} }()},
|
|
{"supersession", []*Record{func() *Record { r := acc(1); r.Status = StatusSuperseded; return r }()}},
|
|
{"supersession", []*Record{acc(1), acc(2, to(LinkSupersedes, 1, 0))}},
|
|
{"partial", []*Record{acc(1), acc(2, to(LinkChanges, 1, 1))}},
|
|
{"partial", []*Record{func() *Record { r := acc(1); r.Clauses[0].Status = ClauseChanged; return r }()}},
|
|
{"rests-on-live", []*Record{func() *Record { r := acc(1); r.Status = StatusSuperseded; return r }(),
|
|
acc(3, to(LinkSupersedes, 1, 0)), acc(2, to(LinkExtends, 1, 0))}},
|
|
}
|
|
seen := map[string]bool{}
|
|
for _, c := range cases {
|
|
found := false
|
|
for _, f := range JudgeSet(c.set) {
|
|
found = found || f.Rule == c.rule
|
|
}
|
|
if !found {
|
|
t.Errorf("the rule %s did not fail on its case: %+v", c.rule, JudgeSet(c.set))
|
|
}
|
|
seen[c.rule] = true
|
|
}
|
|
for _, r := range WholeSetRules {
|
|
if !seen[r.ID] {
|
|
t.Errorf("the whole-set rule %s has no case", r.ID)
|
|
}
|
|
}
|
|
if len(WholeSetRules) != 4 {
|
|
t.Errorf("%d whole-set rules; ADR 0290 counts 4", len(WholeSetRules))
|
|
}
|
|
// A consistent set: the superseder names it, the extender is the superseder, the change is applied.
|
|
ok := []*Record{func() *Record { r := acc(1); r.Status = StatusSuperseded; return r }(),
|
|
acc(2, to(LinkSupersedes, 1, 0), to(LinkExtends, 1, 0))}
|
|
if f := JudgeSet(ok); len(f) != 0 {
|
|
t.Errorf("a consistent set fails: %+v", f)
|
|
}
|
|
}
|
|
|
|
// A finding's notice the operator's channel refused is not kept as told: it is sent again at the next step.
|
|
func TestAFindingsNoticeThatFailsIsSentAgain(t *testing.T) {
|
|
eachStore(t, func(t *testing.T, s Store) {
|
|
w := newWorld(t, s, fixture())
|
|
f := Finding{Rule: "cited", Records: []string{"ADR 0300"}, Says: "ADR 0300 is accepted and nothing cites it"}
|
|
if err := s.SetFindings(context.Background(), []Finding{f}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tries := 0
|
|
w.k.Notify = func(key, summary, headline, explanation, resolved string) error {
|
|
tries++
|
|
if tries == 1 {
|
|
return errors.New("nothing answers operator-channel.notify")
|
|
}
|
|
w.told = append(w.told, summary)
|
|
return nil
|
|
}
|
|
w.k.TellFindings()
|
|
w.k.TellFindings()
|
|
w.k.TellFindings()
|
|
if tries != 2 || len(w.told) != 1 {
|
|
t.Fatalf("a refused notice: %d tries, told %v", tries, w.told)
|
|
}
|
|
})
|
|
}
|