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
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.
114 lines
4.4 KiB
Go
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)
|
|
}
|
|
}
|