Add mining self-surgery for on-host recovery when AI control detects stalls.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
When ai_control_enabled and mining interrupts or hashrate drops, the server composes same-agent fix plans (container restart, chain reorder, GPU swap, idle tune, RandomX restart) with Seer and oath ledger audit — no spread or lateral escalation.
This commit is contained in:
78
server/internal/miningsurgery/plan_test.go
Normal file
78
server/internal/miningsurgery/plan_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package miningsurgery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"crypto-miner-server/internal/epidemiology"
|
||||
)
|
||||
|
||||
func TestComposeFromInterruptContainerRestart(t *testing.T) {
|
||||
plan := ComposeFromInterrupt(epidemiology.MiningInterruptState{
|
||||
AgentID: "a1",
|
||||
ChainExhausted: true,
|
||||
FailedMethods: []epidemiology.MethodFailure{
|
||||
{Method: "container", Reason: "exited"},
|
||||
},
|
||||
ActiveMethod: "container",
|
||||
}, 20)
|
||||
if len(plan.Actions) == 0 {
|
||||
t.Fatal("expected actions")
|
||||
}
|
||||
found := false
|
||||
for _, a := range plan.Actions {
|
||||
if a == ActionContainerRestart {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("actions=%v want container_restart", plan.Actions)
|
||||
}
|
||||
if ContainsSpreadCommand([]string{"discover_and_join"}) {
|
||||
t.Fatal("spread guard misfire")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComposeFromLowHashrateIdleTune(t *testing.T) {
|
||||
plan := ComposeFromLowHashrate("a1", "inprocess", 0.5, 20)
|
||||
if plan.IdleThresholdPct != 30 {
|
||||
t.Fatalf("idle tune=%d want 30", plan.IdleThresholdPct)
|
||||
}
|
||||
hasTune := false
|
||||
for _, a := range plan.Actions {
|
||||
if a == ActionIdleThresholdTune {
|
||||
hasTune = true
|
||||
}
|
||||
}
|
||||
if !hasTune {
|
||||
t.Fatalf("actions=%v", plan.Actions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectLowHashrate(t *testing.T) {
|
||||
if DetectLowHashrate(epidemiology.StatsInput{MiningHashrate: 0}, 1) {
|
||||
t.Fatal("zero hashrate is interrupt not low")
|
||||
}
|
||||
if !DetectLowHashrate(epidemiology.StatsInput{
|
||||
MiningHashrate: 0.4,
|
||||
ActiveMethod: "inprocess",
|
||||
}, 1) {
|
||||
t.Fatal("expected low hashrate")
|
||||
}
|
||||
if DetectLowHashrate(epidemiology.StatsInput{FleetRole: "seeder", MiningHashrate: 0.1, ActiveMethod: "inprocess"}, 1) {
|
||||
t.Fatal("seeder excluded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReorderChainSkipsFailed(t *testing.T) {
|
||||
order := reorderChainFromState(epidemiology.MiningInterruptState{
|
||||
FailedMethods: []epidemiology.MethodFailure{{Method: "container"}},
|
||||
})
|
||||
if len(order) == 0 || order[0] != "inprocess" {
|
||||
t.Fatalf("order=%v", order)
|
||||
}
|
||||
for _, m := range order {
|
||||
if m == "container" {
|
||||
t.Fatalf("failed method still in order: %v", order)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user