Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/judge_bound_test.go
T
jschoubben fcab25604e
mesh/merge-gate pass: builds claude-code, mesh-knowledge → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (…
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
Review a long record within a bound sized to it, and let a proposal waiting on its author leave the knowledge base healthy (hq issue 508)
A judgement over one bus call's 30 seconds is collected by its ticket from
claude-code, so an eleven-clause record can be reviewed; a proposal out of
review tries is told to the operator, not raised as the module's fault, so
it no longer holds the module's own rollouts.
2026-10-11 20:32:33 +02:00

114 lines
4.4 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"time"
)
// novox/hq issue 508: ADR 0297, eleven clauses and eleven links, could not be reviewed within the judgement's 25
// seconds five times running, while short records were reviewed in 14 to 25. A review's bound scales with what is
// judged, up to a cap, and a judgement past one bus call's time is collected by its ticket.
// aRecord is a decision of n clauses, each about size bytes.
func aRecord(n, size int) *Record {
r := &Record{Kind: KindDecision, Number: 297, Title: "Issues are kept as a log of operations", Status: StatusProposed}
for i := 1; i <= n; i++ {
r.Clauses = append(r.Clauses, Clause{N: i, Text: fmt.Sprintf("**Clause %d.** %s", i, strings.Repeat("A word of reasoning. ", size/20))})
}
r.Sections = []Section{{Name: "Context", Text: strings.Repeat("Why it was decided. ", n*size/40)}}
return r
}
// asked is the agent module, played over the calls the knowledge base makes to it.
type asked struct {
keys []string
bodies []map[string]any
pending int // how many times a ticketed judgement answers pending before its answer
refuse bool
}
func (a *asked) ask(key string, body any) (json.RawMessage, error) {
b, _ := body.(map[string]any)
a.keys, a.bodies = append(a.keys, key), append(a.bodies, b)
if strings.HasPrefix(key, "claude-code.claude_code_judge@") {
if _, long := b["within"]; long && a.refuse {
return nil, errors.New("claude_code_judge takes question, schema and model; not within")
}
if _, long := b["within"]; long && a.pending > 0 {
return json.RawMessage(`{"node":"novox","ticket":"t1","pending":true}`), nil
}
return json.RawMessage(`{"answer":{"verdict":"approve","findings":[]},"model":"sonnet","node":"novox"}`), nil
}
if key == "claude-code.claude_code_judged@novox" && b["ticket"] == "t1" {
if a.pending--; a.pending > 0 {
return json.RawMessage(`{"node":"novox","ticket":"t1","pending":true}`), nil
}
return json.RawMessage(`{"answer":{"verdict":"approve","findings":[]},"model":"sonnet","node":"novox","ticket":"t1"}`), nil
}
return nil, fmt.Errorf("nothing answers %s", key)
}
func withinOf(t *testing.T, b map[string]any) time.Duration {
t.Helper()
v, ok := b["within"]
if !ok {
return 0
}
s, ok := v.(int)
if !ok {
t.Fatalf("within is not whole seconds: %#v", v)
}
return time.Duration(s) * time.Second
}
func TestALongRecordsReviewGetsTheLargerBound(t *testing.T) {
short, long := ReviewQuestion(aRecord(2, 200), nil), ReviewQuestion(aRecord(12, 2000), nil)
a := &asked{}
j := judgementOver(a.ask, "novox")
for _, q := range []string{short, long} {
if _, _, err := j(q, reviewSchema, "sonnet"); err != nil {
t.Fatal(err)
}
}
s, l := withinOf(t, a.bodies[0]), withinOf(t, a.bodies[1])
if l <= s || l <= 25*time.Second || l > MostJudgeWithin {
t.Fatalf("a %d-byte review is bounded %s and a %d-byte one %s: the longer must get the larger bound, past 25s and at most %s",
len(short), s, len(long), l, MostJudgeWithin)
}
if got := JudgeWithin(strings.Repeat("x", MostReviewQuestion)); got != MostJudgeWithin {
t.Fatalf("the largest question is bounded %s, not the cap %s", got, MostJudgeWithin)
}
}
// A judgement answered pending is collected by its ticket from the node that holds it, and its answer is the
// judgement's.
func TestAPendingJudgementIsCollectedByItsTicket(t *testing.T) {
a := &asked{pending: 3}
answer, by, err := judgementOver(a.ask, "novox")(ReviewQuestion(aRecord(12, 2000), nil), reviewSchema, "sonnet")
if err != nil {
t.Fatal(err)
}
if string(answer) != `{"verdict":"approve","findings":[]}` || by != "the agent on novox (sonnet)" {
t.Fatalf("%s by %s", answer, by)
}
if strings.Join(a.keys, " ") != "claude-code.claude_code_judge@novox claude-code.claude_code_judged@novox claude-code.claude_code_judged@novox claude-code.claude_code_judged@novox" {
t.Fatalf("asked %q", a.keys)
}
}
// An agent module from before issue 508, which takes no within, is asked again without it: the review is had when
// it fits the old bound, and fails as before when it does not.
func TestAnAgentModuleThatTakesNoWithinIsAskedWithoutIt(t *testing.T) {
a := &asked{refuse: true}
if _, _, err := judgementOver(a.ask, "novox")(ReviewQuestion(aRecord(12, 2000), nil), reviewSchema, "sonnet"); err != nil {
t.Fatal(err)
}
if len(a.bodies) != 2 || withinOf(t, a.bodies[1]) != 0 {
t.Fatalf("asked %v", a.bodies)
}
}