config_switches_test.go
Rama main · réplica de lectura del repositorio original.
package graymatter
import "testing"
// The retrieval switches had no surface outside StoreConfig, so the only way
// to reach them was to import the library and build a Config in Go — which the
// CLI, the daemon, the MCP server and the REST server all refuse to do (they
// build from DefaultConfig, deliberately, so there is one place that decides
// what a store is). A knob nobody can turn is a knob that does not exist; this
// pins that DefaultConfig is that place.
//
// UsageAliasLearning is the one switch that is still opt-in: measured against
// real agent reformulations it is worth +2 families out of 40 and promotes
// about one junk pair in three, so only an explicit yes may turn it on.
func TestRetrievalSwitchesReadTheEnvironment(t *testing.T) {
const key = "GRAYMATTER_USAGE_ALIAS"
if DefaultConfig().UsageAliasLearning {
t.Fatal("UsageAliasLearning defaults to true with the variable unset, want false")
}
// The switch changes what the store promotes from observed queries, so
// the default has to survive anything that is not a deliberate yes.
for _, off := range []string{"", "0", "false", "no", "off", "maybe", " "} {
t.Setenv(key, off)
if DefaultConfig().UsageAliasLearning {
t.Errorf("%s=%q turned UsageAliasLearning on; only an explicit yes may", key, off)
}
}
for _, on := range []string{"1", "true", "TRUE", "yes", "on", " 1 "} {
t.Setenv(key, on)
if !DefaultConfig().UsageAliasLearning {
t.Errorf("%s=%q did not turn UsageAliasLearning on", key, on)
}
}
}
// The switches are independent, and they do not share a default. Usage-alias
// learning is off; stemming and candidate retrieval are on; and each has to
// stay where it is when the others move: measured on a second evaluation corpus, running
// the learner with the stemmer scored BELOW running the learner alone (9/10
// against 10/10), so a switch that silently dragged another along would ship a
// configuration nobody chose and nobody measured.
func TestRetrievalSwitchesAreIndependent(t *testing.T) {
t.Setenv("GRAYMATTER_USAGE_ALIAS", "1")
cfg := DefaultConfig()
if !cfg.UsageAliasLearning {
t.Fatal("GRAYMATTER_USAGE_ALIAS=1 did not enable usage-alias learning")
}
if !cfg.StemKeywords {
t.Error("enabling usage-alias learning turned the stemmer off")
}
if !cfg.CandidateRetrieval {
t.Error("enabling usage-alias learning turned candidate retrieval off")
}
t.Setenv("GRAYMATTER_USAGE_ALIAS", "")
t.Setenv("GRAYMATTER_STEM_KEYWORDS", "0")
cfg = DefaultConfig()
if cfg.StemKeywords {
t.Fatal("GRAYMATTER_STEM_KEYWORDS=0 did not opt out")
}
if cfg.UsageAliasLearning {
t.Error("opting out of the stemmer turned usage-alias learning on")
}
if !cfg.CandidateRetrieval {
t.Error("opting out of the stemmer turned candidate retrieval off")
}
t.Setenv("GRAYMATTER_STEM_KEYWORDS", "")
t.Setenv("GRAYMATTER_CANDIDATE_RETRIEVAL", "0")
cfg = DefaultConfig()
if cfg.CandidateRetrieval {
t.Fatal("GRAYMATTER_CANDIDATE_RETRIEVAL=0 did not opt out")
}
if !cfg.StemKeywords {
t.Error("opting out of candidate retrieval turned the stemmer off")
}
if cfg.UsageAliasLearning {
t.Error("opting out of candidate retrieval turned usage-alias learning on")
}
}
// The affinity gate is the one retrieval knob that is not a boolean, and the
// owner's decision was explicit: the mechanism stays reachable, the default
// stays conservative. Both halves need pinning — a knob nobody can set is not
// "documented and available", and a default that drifts open would ship the
// pollution the measurement rejected.
func TestUsageAliasAffinityIsReachableAndDefaultsClosed(t *testing.T) {
if got := DefaultConfig().UsageAliasAffinityMin; got != 0 {
t.Fatalf("affinity defaults to %d; 0 is the value memory.Open normalises to the conservative gate", got)
}
t.Setenv("GRAYMATTER_USAGE_ALIAS_AFFINITY", "-1")
if got := DefaultConfig().UsageAliasAffinityMin; got != -1 {
t.Errorf("the synonym-class mode is unreachable from the environment: got %d", got)
}
// A typo must land on the documented default, not on a number nobody chose.
for _, junk := range []string{"", "off", "yes", "-", "1.5"} {
t.Setenv("GRAYMATTER_USAGE_ALIAS_AFFINITY", junk)
if got := DefaultConfig().UsageAliasAffinityMin; got != 0 {
t.Errorf("%q parsed to %d, want the 0 that means \"use the default\"", junk, got)
}
}
}
// StemKeywords is a retrieval switch that defaults ON, so it needs the
// mirror image of the opt-in test above: the default has to survive anything
// that is not a deliberate NO, and the deliberate no has to work.
//
// The second half is the part that would have shipped broken. envBool maps
// everything that is not an explicit yes to false, so a default-on flag built
// on it can be turned on and never off — "unset" and "0" give the same answer.
// That is the shape of every reachability bug this project has paid for.
func TestStemKeywordsDefaultsOnAndCanBeTurnedOff(t *testing.T) {
if !DefaultConfig().StemKeywords {
t.Fatal("stemming must default ON: measured +4/35 on a corpus it was not designed against, losing nothing")
}
for _, on := range []string{"", "1", "true", "yes", "on", "maybe", " "} {
t.Setenv("GRAYMATTER_STEM_KEYWORDS", on)
if !DefaultConfig().StemKeywords {
t.Errorf("%q turned stemming off; only an explicit no may", on)
}
}
for _, off := range []string{"0", "false", "no", "off", "OFF", " 0 "} {
t.Setenv("GRAYMATTER_STEM_KEYWORDS", off)
if DefaultConfig().StemKeywords {
t.Errorf("GRAYMATTER_STEM_KEYWORDS=%q did not opt out; the flag is unreachable in the off direction", off)
}
}
}
// CandidateRetrieval is the other retrieval switch that defaults ON, so it
// gets the same mirror: the default has to survive anything that is not a
// deliberate NO, and the deliberate no has to work.
//
// The default was earned the way a default has to be — the 30k latency gate
// green on two machines (p99 20.4 ms and 21.2 ms against the 40 ms bar, with
// a harness that verifies every measured recall returned facts) and the
// write-cost bar ratified at <= 3 ms by the owner. It ships to everyone, so
// everyone must be able to turn it off: the reachability bug in the off
// direction is the one this project has paid for three times.
func TestCandidateRetrievalDefaultsOnAndCanBeTurnedOff(t *testing.T) {
if !DefaultConfig().CandidateRetrieval {
t.Fatal("candidate retrieval must default ON: latency gate green on two machines, write bar ratified")
}
for _, on := range []string{"", "1", "true", "yes", "on", "maybe", " "} {
t.Setenv("GRAYMATTER_CANDIDATE_RETRIEVAL", on)
if !DefaultConfig().CandidateRetrieval {
t.Errorf("%q turned candidate retrieval off; only an explicit no may", on)
}
}
for _, off := range []string{"0", "false", "no", "off", "OFF", " 0 "} {
t.Setenv("GRAYMATTER_CANDIDATE_RETRIEVAL", off)
if DefaultConfig().CandidateRetrieval {
t.Errorf("GRAYMATTER_CANDIDATE_RETRIEVAL=%q did not opt out; the flag is unreachable in the off direction", off)
}
}
}