Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/draft_test.go
T
jschoubben ba7b455719
mesh/merge-gate pass: builds new: modules/mesh-knowledge, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compo…
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 superseded: a newer head of the same pull request
Add mesh-knowledge, the knowledge base's first slice (hq issue 425, ADR 0290)
An agent learns what a decision says only by reading whole records. This module
holds every decision record and research effort whole in a database in the store,
with clauses that carry their own status and source, typed links read from both
ends, and every version. It answers ask, get and holds within a stated bound, and
takes a record through reserve, propose (refused on any of the 29 per-record
rules), review by someone other than the author, and acceptance, on the
operator's proven warrant when the record needs it. The 4 whole-set rules make
the module unhealthy when they fail. The import reads hq at a commit through the
records module, never overwrites, and changes nothing when run again. Prose
decisions get drafted clauses that a second judgement must confirm before they
are served.
2026-10-11 04:58:35 +02:00

244 lines
7.6 KiB
Go

package main
import (
"context"
"strings"
"testing"
)
// to-be 55 §10: "A drafted clause is never served as in force — the module's test: holds over a record with only
// drafted clauses answers none, and ask marks them." And §7: two separate judgements, the second seeing only the
// record and the drafts; the operator told once per topic; return drafts again.
// draftUntil takes drafting steps until the record's drafting reaches a state, at most a few.
func draftUntil(t *testing.T, w *world, k Key, state string) *Record {
t.Helper()
for i := 0; i < 40; i++ {
r := mustGet(t, w.k.Store, k).Current
if r.Drafting != nil && r.Drafting.State == state {
return r
}
if _, err := w.k.DraftStep(); err != nil {
t.Fatal(err)
}
}
t.Fatalf("%s's drafting never reached %s", k, state)
return nil
}
func TestADraftedClauseIsNeverServedAsInForce(t *testing.T) {
eachStore(t, func(t *testing.T, s Store) {
w := imported(t, s)
k := Key{KindDecision, 240}
r := draftUntil(t, w, k, DraftDrafted)
if len(r.Clauses) == 0 || r.Clauses[0].Source != SourceDrafted {
t.Fatalf("drafted: %+v", r.Clauses)
}
h, err := w.k.Holds("", &k, 0)
if err != nil {
t.Fatal(err)
}
if len(h.Clauses) != 0 || !strings.Contains(h.NotYet, "drafted clause(s) are not served") {
t.Fatalf("holds over a record with only drafted clauses: %+v", h)
}
// The topic's holds leaves them out too.
ht, _ := w.k.Holds(r.Topic, nil, 64<<10)
for _, c := range ht.Clauses {
if c.Record == k.String() {
t.Fatalf("the topic's holds served a drafted clause: %+v", c)
}
}
a, err := w.k.Ask("a module says how it is healthy and the node-engine judges it", 0)
if err != nil {
t.Fatal(err)
}
marked := false
for _, rec := range a.Records {
for _, c := range rec.Clauses {
if rec.Record == k.String() {
if !strings.HasPrefix(c.Holds, "drafted, not confirmed") {
t.Fatalf("ask served a drafted clause as %q", c.Holds)
}
marked = true
}
}
if rec.Record == k.String() && len(rec.Clauses) == 0 && strings.Contains(rec.Note, "drafted") {
marked = true
}
}
if !marked {
t.Fatalf("ask did not show 0240's drafted clauses as drafted: %+v", a)
}
// get marks each one.
g, _ := w.k.Get(GetAsk{Kind: KindDecision, Number: 240})
for _, c := range g.Clauses {
if !strings.HasPrefix(c.Holds, "drafted") {
t.Fatalf("get: %+v", c)
}
}
})
}
func TestClausesAreDraftedThenConfirmedByASeparateJudgementThatSeesOnlyTheRecordAndTheDrafts(t *testing.T) {
eachStore(t, func(t *testing.T, s Store) {
w := imported(t, s)
k := Key{KindDecision, 240}
r := draftUntil(t, w, k, DraftDone)
var draftQ, confirmQ string
n := 0
for _, q := range w.judge.questions {
if !strings.Contains(q, "ADR 0240") {
continue
}
n++
if strings.Contains(q, "drafted from it by someone else") {
confirmQ = q
} else {
draftQ = q
}
}
if n != 2 || draftQ == "" || confirmQ == "" {
t.Fatalf("0240 was not drafted and confirmed by two separate judgements: %d questions", n)
}
section, _ := decisionSection(r)
if !strings.Contains(confirmQ, section) || !strings.Contains(confirmQ, r.Clauses[0].Text) {
t.Fatal("the confirming judgement was not shown the record and the drafts")
}
// It sees only those: nothing of the mesh but the record, and none of the first judgement's own words.
for _, other := range []string{"Write what it decides as numbered", "An earlier draft"} {
if strings.Contains(confirmQ, other) {
t.Fatalf("the confirming judgement was shown %q", other)
}
}
if !strings.Contains(draftQ, section) || strings.Contains(draftQ, "The drafted clauses") {
t.Fatal("the drafting judgement's question is not the section alone")
}
for _, c := range r.Clauses {
if c.Source != SourceConfirmed {
t.Fatalf("not confirmed: %+v", c)
}
}
if len(w.said.of(EventDrafted)) == 0 || len(w.said.of(EventConfirmed)) == 0 {
t.Fatalf("events: %v", w.said.events)
}
// Now confirmed, holds serves them, marked.
h, _ := w.k.Holds("", &k, 0)
if len(h.Clauses) != len(r.Clauses) || !strings.Contains(h.Clauses[0].Holds, "confirmed") {
t.Fatalf("holds after confirming: %+v", h)
}
})
}
func TestAClauseTheSecondJudgementRejectsStaysDraftedWithTheReason(t *testing.T) {
w := imported(t, newMemStore())
k := Key{KindDecision, 240}
r := draftUntil(t, w, k, DraftDrafted)
ctx := context.Background()
e := mustGet(t, w.k.Store, k)
r = e.Current.Clone()
r.Clauses[0].Text += " NOT EXACT"
if err := w.k.Store.Save(ctx, 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.Clauses[0].Unconfirmed == "" {
t.Fatalf("a rejected clause: %+v", r.Clauses[0])
}
h, _ := w.k.Holds("", &k, 0)
for _, c := range h.Clauses {
if c.N == 1 {
t.Fatal("a clause the second judgement rejected is served")
}
}
}
func TestTheOperatorIsToldOncePerTopicAndReturnDraftsAgain(t *testing.T) {
w := imported(t, newMemStore())
for i := 0; i < 40; i++ {
said, err := w.k.DraftStep()
if err != nil {
t.Fatal(err)
}
if said == "" {
break
}
}
// Every prose record of the fixture's topics drafted and confirmed; each topic told once.
topics := map[string]int{}
for _, s := range w.told {
topics[s]++
}
if len(w.told) == 0 {
t.Fatal("the operator was told of no topic")
}
for s, n := range topics {
if n != 1 {
t.Fatalf("told %d times: %s", n, s)
}
}
told := len(w.told)
k := Key{KindDecision, 240}
if _, err := w.k.Return("operator@shanks", Key{KindDecision, 276}, "x"); err == nil {
t.Fatal("a record whose author wrote its clauses was returned")
}
if _, err := w.k.Return("operator@shanks", k, "clause 2 says more than the section"); err != nil {
t.Fatal(err)
}
r := mustGet(t, w.k.Store, k).Current
if len(r.Clauses) != 0 || r.Drafting.State != DraftWaiting || len(w.said.of(EventReturned)) != 1 {
t.Fatalf("returned: %+v", r)
}
r = draftUntil(t, w, k, DraftDone)
last := ""
for _, q := range w.judge.questions {
if strings.Contains(q, "An earlier draft was sent back") {
last = q
}
}
if !strings.Contains(last, "clause 2 says more than the section") {
t.Fatal("the drafting after a return did not read its reason")
}
if len(w.told) != told {
t.Fatal("the operator was told of the topic again")
}
}
// A supersedes-in-part hq wrote names the record; which clause it changes is judged, then confirmed, and only then
// is the older clause changed.
func TestWhichClauseAPartialSupersessionChangesIsJudgedThenConfirmed(t *testing.T) {
w := imported(t, newMemStore())
for i := 0; i < 40; i++ {
if said, err := w.k.DraftStep(); err != nil {
t.Fatal(err)
} else if said == "" {
break
}
}
r := mustGet(t, w.k.Store, Key{KindDecision, 282}).Current
var l *Link
for i := range r.Links {
if r.Links[i].Kind == LinkChanges && r.Links[i].To.Number == 276 {
l = &r.Links[i]
}
}
if l == nil || l.Source != SourceConfirmed || l.To.Clause != 1 {
t.Fatalf("0282's change of 0276: %+v", l)
}
c := mustGet(t, w.k.Store, Key{KindDecision, 276}).Current.Clause(1)
if c.Status != ClauseChanged || c.ChangedBy[0] != "ADR 0282" {
t.Fatalf("0276 §1: %+v", c)
}
}
func TestADraftingThatFailsIsSaidAndTriedAgainLater(t *testing.T) {
w := imported(t, newMemStore())
w.judge.fail = true
if _, err := w.k.DraftStep(); err == nil {
t.Fatal("a failed judgement was not said")
}
r := mustGet(t, w.k.Store, Key{KindDecision, 11}).Current
if r.Drafting.State != DraftFailed || r.Drafting.Tries != 1 || !strings.Contains(r.Drafting.Reason, "licence") {
t.Fatalf("%+v", r.Drafting)
}
}