package api import ( "encoding/json" "net/http" "strings" "crypto-miner-server/internal/clearance" "crypto-miner-server/internal/db" "crypto-miner-server/internal/strategy" ) type fleetGraftRequest struct { SourceAgentID string `json:"source_agent_id"` TargetAgentID string `json:"target_agent_id"` } // PostFleetGraft splices a winning strain from source onto target without re-spreading target. func (f *FleetHandler) PostFleetGraft(w http.ResponseWriter, r *http.Request) { if f.db == nil || f.ws == nil { http.Error(w, "fleet services unavailable", http.StatusServiceUnavailable) return } policy := f.ws.ServerPolicy() if !strategy.GraftEnabled(policy.AIControlEnabled, policy.FleetRolesEnabled) { http.Error(w, "graft requires ai_control_enabled and fleet_roles_enabled", http.StatusBadRequest) return } var req fleetGraftRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid body", http.StatusBadRequest) return } sourceID := strings.TrimSpace(req.SourceAgentID) targetID := strings.TrimSpace(req.TargetAgentID) if sourceID == "" || targetID == "" { http.Error(w, "source_agent_id and target_agent_id are required", http.StatusBadRequest) return } if sourceID == targetID { http.Error(w, "source and target must differ", http.StatusBadRequest) return } level := clearance.L0 if mgr := f.ws.ClearanceManager(); mgr != nil { level = mgr.Level(targetID) } if level < clearance.L4 { writeJSON(w, map[string]interface{}{ "success": false, "error": "target clearance insufficient for spread_graft (requires L4)", }) return } graftPolicy, err := f.ws.ApproveFleetGraft(sourceID, targetID) if err != nil { _ = (&OathLedgerBridge{DB: f.db, Hub: f.ws}).Record( AuthUsername(r), db.OathGraft, targetID, "", db.OathOutcomeFail, map[string]string{"source_agent_id": sourceID, "target_agent_id": targetID}, map[string]interface{}{"error": err.Error()}, ) writeJSON(w, map[string]interface{}{ "success": false, "error": err.Error(), }) return } writeJSON(w, map[string]interface{}{ "success": true, "source_id": sourceID, "target_id": targetID, "graft_policy": graftPolicy, "pushed": f.ws.IsAgentReachable(targetID), }) _ = f.db.InsertAudit(AuthUsername(r), "spread_graft", targetID, map[string]interface{}{ "source_agent_id": sourceID, "graft_tier": graftPolicy.GraftTier, "strain": graftPolicy.GraftSourceStrain, }) _ = (&OathLedgerBridge{DB: f.db, Hub: f.ws}).Record( AuthUsername(r), db.OathGraft, targetID, graftPolicy.GraftSourceStrain, db.OathOutcomeSuccess, map[string]interface{}{ "source_agent_id": sourceID, "graft_tier": graftPolicy.GraftTier, "graft_policy": graftPolicy, }, map[string]interface{}{ "source_agent_id": sourceID, "graft_tier": graftPolicy.GraftTier, "pushed": f.ws.IsAgentReachable(targetID), }, ) }