Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Saving Theme Data to a File

    Saving Theme Data to a File

    Your life isn’t all WordPress.

    I know, I know, I said a dirty thing, but let’s be honest, everything isn’t always all WordPress. And when that happens, you have to do some weird things to make your data shared.

    One of the things I needed one day was a way for non-WordPress files to get access to a theme setting. See, the theme let me set a top-bar and customize it. Sometimes I did that to share news, sometimes to link to latest posts. Regardless, I updated it via Customizer, and it worked great for WordPress.

    Not so much for my static HTML site, or my non-WordPress PHP site.

    I dwelled on it for a while and then thought “Wait, if the theme knows how to echo a specific setting, then there has to be a function for that. And if there’s a function, then can’t I hook into Customizer saving to trigger the creation of an HTML file with the data and call that from my other sites?”

    And guess what? You can!

    The WordPress Code

    Toss this in an MU Plugin and it’ll save a file to wp-content/top-bar.html when you save customizer settings.

    <?php
    class HELF_Top_Bar {
    
    
    	public function __construct() {
    		add_action( 'customize_save_after', array( $this, 'save_top_file' ) );
    	}
    
    
    	/**
    	 * Safe the top file
    	 * When customizer is saved, copy the get_theme_mod() data and parse it
    	 * into a file: wp-content/top-bar.html
    	 * @return n/a
    	 */
    	public function save_top_file() {
    		$default     = 'Something Default Here';
    		$banner_text = get_theme_mod( 'top-banner-text', $default );
    
    		$fp = fopen( WP_CONTENT_DIR . '/top-bar.html', 'w' );
    		fwrite( $fp, $banner_text );
    		fclose( $fp );
    
    	}
    
    }
    
    new HELF_Top_Bar();
    
    

    The Javascript

    The what?

    For my other PHP file, it’s a simple file_get_contents() call to the HTML. But for static HTML I had to resort to trickery with Javascript.

    <script>
    	var elem = document.getElementById("wpcontent");
    
    fetch('https://example.com/wp-content/top-bar.html', {
            credentials: 'include'
        })
            .then(function (response) {
                return response.text();
            })
            .then(function (text) {
                console.log('Request successful', text.length);
    			console.log('Request successful2', text);
    			$('.wpcontent').html(text);
            })
            .catch(function (error) {
                console.log('Request failed', error)
            });
    </script>
    
    <div class="wpcontent"></div>
    
    

    And magic happens.

  • Rolling Your Own Related Posts

    Rolling Your Own Related Posts

    To start out at the top, I did not write a whole ‘related posts’ plugin. As with all things, I started by asking myself “What’s the problem I’m trying to solve?”

    The answer is “I have a custom post type that needs to relate to other posts in the type, but based on my specific criteria which is currently organized into custom taxonomies and post meta.” And from the outset, that certainly sounds like a massive custom job. it was one I was dreading until I remembered that a developer I respected and trusted had once complained to me about the problems with all those other auto-related-posts plugins.

    1. They’re heavy and use a lot of ram
    2. They don’t let you customize ‘weight’ of relations
    3. They’re not extendable

    So I did the next logical thing and I looked up their plugins.

    The Plugin

    The crux of why I chose this plugin was simply that it’s extendable, but also that it started out with what I had:

    Posts with the most terms in common will display at the top!

    Perfect!

    Design The Basics

    Before you jump into coding, you need to know what you’re doing. I chose to isolate what I needed first. I made a list of everything I thought was relative:

    • Taxonomies: Tropes, Genres, Intersectionality, Tags, Stars
    • Post Meta: Worth It, Loved, Calculated Score

    Yes, it’s that site again.

    I read the plugin documentation and verified that for most of that I just needed to list the taxonomies in the shortcode like this:

    [related_posts_by_tax fields="ids" order="RAND" title="" format="thumbnails" image_size="postloop-img" link_caption="true" posts_per_page="6" columns="0" post_class="similar-shows" taxonomies="lez_tropes,lez_genres,lez_stars,lez_intersections,lez_showtagged"]
    

    Initially I didn’t list the stars because the way the code works, it would say “If you have a Gold Star, show other Gold Stars.” And that wasn’t what I wanted to see. I wanted “If you have ANY star, show other shows with a star.” That said, once we got over 12 shows in each ‘star’ category, this became much easier to match and I could add it in.

    The rest of the code, those checks for meta, needed actual code written.

    Meta Checks

    There’s a helpful filter, related_posts_by_taxonomy_posts_meta_query, that lets you filter the meta queries used by the post. Leveraging that, we can make our checks:

    1. Match the ‘worth it’ value of a show
    2. If the show is loved, list other loved show
    3. If the show isn’t loved, use the score to find show with the same relative value

    Both Worth It and Loved are post meta values. Mine happen to be added by CMB2, but the logic remains the same regardless how you add it. Worth It has four possible values (Yes, No, Maybe, TBD), and the check is either the value or false. Loved is a checkbox, a boolean exists or not, which means it’s a true/falsy. The score is a number that’s generated every time the show is saved, and it’s crazy complicated and another story.

    The code I use looks like this:

    add_filter( 'related_posts_by_taxonomy_posts_meta_query', 'MYSITE_RPBT_meta_query', 10, 4 );
    function MYSITE_RPBT_meta_query( $meta_query, $post_id, $taxonomies, $args ) {
    	$worthit = ( get_post_meta( $post_id, 'lezshows_worthit_rating', true ) ) ? get_post_meta( $post_id, 'lezshows_worthit_rating', true ) : false;
    	$loved   = ( get_post_meta( $post_id, 'lezshows_worthit_show_we_love', true ) ) ? true : false;
    	$score   = ( get_post_meta( $post_id, 'lezshows_the_score', true ) ) ? get_post_meta( $post_id, 'lezshows_the_score', true ) : 10;
    
    	// We should match up the worth-it value as well as the score.
    	// After all, some low scores have a thumbs up.
    	if ( false !== $worthit ) {
    		$meta_query[] = array(
    			'key'     => 'lezshows_worthit_rating',
    			'compare' => $worthit,
    		);
    	}
    
    	// If the show is loved, we want to include it here.
    	if ( $loved ) {
    		$meta_query[] = array(
    			'key'     => 'lezshows_worthit_show_we_love',
    			'compare' => 'EXISTS',
    		);
    	}
    
    	// If they're NOT loved, we use the scores for a value.
    	if ( ! $loved ) {
    		// Score: If the score is similar +/- 10
    		if ( $score >= 90 ) {
    			$score_range = array( 80, 100 );
    		} elseif ( $score <= 10 ) {
    			$score_range = array( 10, 30 );
    		} else {
    			$score_range = array( ( $score - 10 ), ( $score + 10 ) );
    		}
    		$meta_query[] = array(
    			'key'     => 'lezshows_the_score',
    			'value'   => $score_range,
    			'type'    => 'numeric',
    			'compare' => 'BETWEEN',
    		);
    	}
    
    	return $meta_query;
    }
    

    More Similar

    But there’s one more thing we wanted to include. When I built this out, Tracy said “There should be a way for us to pick the shows we think are similar!”

    She’s right! I built in a CMB2 repeatable field where you can pick shows from a dropdown and that saves the show post IDs as an array. That was the easy part, since we were already doing that in another place.

    Once that list exists, we grab the handpicked list, break it out into a simple array, check if the post is published and not already on the list, and combine it all:

    add_filter( 'related_posts_by_taxonomy', array( $this, 'alter_results' ), 10, 4 );
    function alter_results( $results, $post_id, $taxonomies, $args ) {
    	$add_results = array();
    
    	if ( ! empty( $results ) && empty( $args['fields'] ) ) {
    		$results = wp_list_pluck( $results, 'ID' );
    	}
    
    	$handpicked  = ( get_post_meta( $post_id, 'lezshows_similar_shows', true ) ) ? wp_parse_id_list( get_post_meta( $post_id, 'lezshows_similar_shows', true ) ) : array();
    	$reciprocity = self::reciprocity( $post_id );
    	$combo_list  = array_merge( $handpicked, $reciprocity );
    
    	if ( ! empty( $combo_list ) ) {
    		foreach ( $combo_list as $a_show ) {
    			//phpcs:ignore WordPress.PHP.StrictInArray
    			if ( 'published' == get_post_status( $a_show ) && ! in_array( $a_show, $results ) && ! in_array( $a_show, $add_results ) ) {
    				$add_results[] = $a_show;
    			}
    		}
    	}
    
    	$results = $add_results + $results;
    
    	return $results;
    }
    

    But … you may notice $reciprocity and wonder what that is.

    Well, in a perfect world if you added The Good Fight as a show similar to The Good Wife, you’d also go back and add The Good Wife to The Good Fight. The reality is humans are lazy. There were two ways to solve this reciprocity of likes issues.

    1. When a show is added as similar to a show, the code auto-adds it to the other show
    2. When the results are generated, the code checks if any other show likes the current show and adds it

    Since we’re already having saving speed issues (there’s a lot of back processing going on with the scores) and I’ve integrated caching, it was easier to pick option 2.

    function reciprocity( $post_id ) {
    	if ( ! isset( $post_id ) || 'post_type_shows' !== get_post_type( $post_id ) ) {
    		return;
    	}
    
    	$reciprocity      = array();
    	$reciprocity_loop = new WP_Query(
    		array(
    			'post_type'              => 'post_type_shows',
    			'post_status'            => array( 'publish' ),
    			'orderby'                => 'title',
    			'order'                  => 'ASC',
    			'posts_per_page'         => '100',
    			'no_found_rows'          => true,
    			'update_post_term_cache' => true,
    			'meta_query'             => array(
    				array(
    					'key'     => 'lezshows_similar_shows',
    					'value'   => $post_id,
    					'compare' => 'LIKE',
    				),
    			),
    		)
    	);
    
    	if ( $reciprocity_loop->have_posts() ) {
    		while ( $reciprocity_loop->have_posts() ) {
    			$reciprocity_loop->the_post();
    			$this_show_id = get_the_ID();
    			$shows_array  = get_post_meta( $this_show_id, 'lezshows_similar_shows', true );
    
    			if ( 'publish' === get_post_status( $this_show_id ) && isset( $shows_array ) && ! empty( $shows_array ) ) {
    				foreach ( $shows_array as $related_show ) {
    					if ( $related_show == $post_id ) {
    						$reciprocity[] = $this_show_id;
    					}
    				}
    			}
    		}
    		wp_reset_query();
    		$reciprocity = wp_parse_id_list( $reciprocity );
    	}
    
    	return $reciprocity;
    }
    

    There’s a little looseness with the checks, and because there are some cases were shows show up wrong because of the ids (ex: show 311 and 3112 would both be positive for a check on 311), we have to double up on the checks to make sure that the show is really the same.

    What’s Next?

    There are still some places I could adjust this. Like if I use more filters I can make the show stars worth ‘more’ than the genres and so on. And right now, due to the way most Anime are based on Manga (and thus get flagged as “Literary Inspired”), anything based on Sherlock Holmes ends up with a lot of recommended Anime.

    Still, this gives me a way more flexible way to list what’s similar.

  • On Behavior and Respect

    On Behavior and Respect

    I’ve had an interesting week with WordPress. It’s been bad enough that I have to preface this post with a note.

    I have no plans to quit WordPress at this time.

    Good Faith and History

    This morning, I woke up thinking about a statement I picked up from Wikipedia. Assume good faith. I like that. I try to do it. The concept is simple and direct. Don’t assume everyone’s evil, instead assume they do mean well, but sometimes they may have trouble expressing it properly.

    And while I do believe that most people don’t mean to be evil (there are exceptions…), I think that more people remain concerned about themselves over anything else. And this self-involved nature causes problems like happened recently, with choices certain companies made to self-promote in ways that other people found offensive and harmful.

    So when I think about ‘good faith’ I do it with a look back to the previous actions someone (an individual or a company) has taken. How have they behaved before? Have they constantly shown poor choices? Is this a first? What happened the last time I tried to talk to them about it? Did we have a discussion? Did I get 15 emails in a row, alternately being called names or being begged to give them another chance?

    That means I find it strange to watch people use the concept of ‘Good Faith’ to argue that they don’t look at people’s past actions to judge their current ones.

    I’d like to think that my consistency would be something people would use to judge my actions, but I’ve learned people whom I’d trusted don’t. And yes, that’s sad. It’s depressing to find out people would rather jump to outrage and pointing fingers and blaming me than taking into consideration 10 years of work.

    Respect and Doubt

    Respect is both given and earned. You give people respect for a position, under the assumption they deserve it, and people either live up to that respect and thus earn more, or they don’t. But when you have an unknown person, you start from assuming good faith based on the hope that they have legitimately earned the position.

    Obviously when you know someone, hung out and had dinner, your assumptions are based on more than that. And if someone has a public history you can turn to, you can use that to base your assumptions.

    That’s not what happened to me this week. Instead, I found out people actually assume bad faith, because perhaps my opinions are different than theirs, or because I saw something in a different way. It felt like “Assume good faith, but only if you’re on my side.” And that? That is sad.

    I imagine how different things would have been to say “Hey, y’all. Mika’s been really careful about using her power here for five years. Give her the benefit of the doubt.”

    Instead, people said I was seeing things that weren’t there. I was playing a victim. It was all in my head. There’s a word for that: gaslighting. God help you if you call them out on it.

    Damage and Care

    It’s in a week like this where I totally understand why so many people have been quitting WordPress. People have worked hard to do good for a community, without any expectations of compensation, but they find out their opinions are dismissed and their word discarded or minimized. They feel disrespected, and it’s worse when they feel made fun of by the community they’re trying to help.

    Some of them have chosen to walk away from WordPress, and I fully support that choice. To do anything less would be like telling someone that the beatings will stop once morale improves. It would be cruel and unkind to dismiss their feelings, and it would mean I’m not listening to them and have no empathy for them.

    Also I’d have to be blind not to see it, because it happens to me all the time. This week? People I thought I knew assumed the worst in me. They didn’t give me the benefit of the doubt and, when I asked what I’d done to deserve that, they said I’d done nothing. They said they were just being fair and hearing all sides.

    In other words all the work I’d done, being consistent and fair, acting carefully and listening to everyone was pointless. In the end, they were just going to dismiss all of that and jump on the bandwagon with everyone else.

    And it was more than one person I’ve know for about a decade who did it.

    For a while I wondered “Did I really mess up here?” And then I asked myself if I’m told I did nothing to be not-trusted, but I was going to be anyway, was there a possible positive outcome here?

    Empathy and Power

    It really boils down to empathy. If someone says “Hey, this hurt me.” and your reply is “Yeah, I don’t see that” then you’re dismissing what they said. And it’s not just because I get treated badly that I have empathy, it’s because my parents, my family taught me to have empathy and care about the strangers as much as I cared about myself. We don’t live in isolation, we live in a community.

    You can see why I call myself a Socialist, right? I care not just about the people I know, but the people I don’t know. I think about the impact my choices have beyond me, and given the amount of power I wield, that has a lot more weight than you might think.

    Oh yes, I have an insane amount of power, and it scares the hell out of me. I could destroy a company with a click. I could insta-ban people for wrongs. I could close plugins for every single security and guideline mistake. I could publicize exactly what specific people did to get permanently banned. Worse, I could spread fear and doubt in the entirety of WordPress, just by closing a plugin.

    I don’t. I handle the majority of that quietly, on the books but privately, because I assume good faith in everyone, even people who make massive mistakes. And because I consider the negative impact to the community in general before I take an action.

    How much trust do I erode in WordPress as a whole with what I do or say? How much damage do I cause? How many people do I hurt? How many people will this person hurt if I permit them to carry on as they have been? Will their uncensured actions damage the reputation of WordPress? Will the community forgive a mistake?

    That’s what I think about, every single day, before I approve, reject, close, or open any plugin.

    Alone and Together

    If you look at some of the people who’ve left the WordPress Community recently, you’ll see a trend. They feel alone. They feel like they’ve been tasked with ever increasing, insurmountable, chores, and they have no support or backup.

    I feel that way too. It took months to be taken seriously about a problem, to the point that serious action was taken. Months, in which I questioned myself. Was I seeing something that wasn’t there? Was my value so little that I’m not worth taking the time to address this problem?

    To put it in perspective for you, someone told me that my father’s death was my fault for banning them for abusive behavior.

    When you look at it, you’d wonder how I could ever doubt myself. Well, that’s what happens when people don’t step up and ask how they can help. And certainly I could have been more vocal about it, but at the same time, it illustrates the invisibility problem in our community. People are hurt all the time, and no one is looking out for them.

    Should I have to scream that someone is hurting me for it to be seen? There’s no oversight in all things, but there’s also no clear way to ask for help. How much worse would this have been if I didn’t have support from people in the community, people in places who could (and did) help me?

    What about everyone else?

    Unending and Critical

    Now look back at Slacks and Blogs and Twitter. You know which ones I mean. Read what people are saying and assuming, and ask yourself “Is this making a welcoming environment?”

    Far too many of us have used our critiques as excuses, without caring for the damage they cause. Dismissing people’s pain. Not offering honest and sincere apologies. We hide behind the veneer of “I’m just passionate” or “I’m being critical.” And instead of discussing the idea, we sling ‘understandings’ like accusations, and we cut at people for disagreeing. We assume the worst and treat people shamefully.

    And worst of all? Our comrades allow this to happen in their backyards. They won’t remove a homophobic ‘joke’ comment because clearly it’s not meant sincerely. They will allow someone to be called a powerless puppet. They give space for hateful comments that barely even have a veneer of merit.

    We’ve stopped encouraging meaningful discourse and regressed into screaming across the aisle that the other person is wrong. We believe our way is the only valid way, and we will tear people down, all the while claiming we’re doing it for the greater good.

    And yet people can look at all that and not see the pitchforks and tiki torches.

    Comments are Disabled

    There’s a reason I disabled comments and mute and block people on twitter with ruthless abandon. It’s not that I don’t want to hear different voices, it’s that it’s stressful to be attacked all the time. It makes a person physically ill. Certainly it’s made me that on more than one occasion.

    I don’t leave comments open, I don’t engage with certain community news sources, I left many Slack groups and I don’t offer comments when asked very often. You see, I can either do good work for the community, do my best and keep things safe, secure, and as fair as a human can, or I can wade through toxicity.

    I decided to do good work.

    I would like to think that a decade of it would allow any perceived missteps of mine to be taken with a grain of salt and a sip of trust. I will still believe in the inherent goodness of people, and their ability to make colossal mistakes. I will still accept an appology when sincerely given.

    But I will not forget and I may not forgive.

    Then again, forgiveness should never be the point of your apology.

  • Spam Your Blacklist

    Spam Your Blacklist

    As mentioned when I began my hiatus, there would be the occasional code post. Here’s one that is born from how annoying someone is.

    The Situation

    I have a serial harasser. He’s a troll and a semi-stalker who doesn’t understand the meaning of “No.” I’ve blocked him on social media, his emails are blackholes, and as I don’t have contact forms on my sites, nor do I have open comment forms at the moment, it’s a non-issue here.

    However, I do have another site which he found and decided to use my contact form to spam me and my co-admin with 10+ emails. When I found out, I blocked his IP address. He was on mobile, though, so I knew this would only last as long as he was on his phone. I needed a better solution.

    This is not a rare problem. Especially not for women online. One of the many ways in which men drive women offline is by upping the emotional labor needed to be online. That is, they attack us with message after message, generally in the guise of being ‘a nice guy,’ or ‘just trying to have an open conversation.’ But the reality is that they want to wear you down and get you to do what they want.

    It’s exhausting. If you’ve ever gone car shopping and had the dealer call you over and over with the hard sell, it’s like that.

    The Paradox

    Contact Forms are meant to be a way for people to contact you, outside of the comments on your site. That being so, they really do need to exist outside the confines of the comments, which means your comment moderation list is a bit inappropriate. You want people who are having comment problems to get a hold of you.

    At the same time, if you’ve blackholed someone, you don’t. You don’t want them to bother you at all, as reading their messages, even though you’re deleting them, is draining. So you want to be able to block them.

    Here’s the problem: most contact forms don’t let you do this out of the box.

    Yeah, think on that for a moment.

    Here are the top four contact form plugins:

    I use Jetpack, and while I may be annoyed I’m also a developer. So I did made an answer.

    The Caution

    This will not block everyone. If your harasser changes emails a lot, you’re out of luck. And this is the ‘excuse’ I see a lot of the time. Why bother if they’re going to change emails? The answer is obvious. If I can inconvenience them enough, and make it clear I don’t care, they’ll go away.

    Also if you do this right, they never know they’ve been blacklisted, so they think they’re getting to you and you’re sipping a damn mai tai.

    The Solution

    In March 2014, I opened a ticket asking for a way to blacklist people. They have made zero forward momentum on this in the 4.5 years since. So this little red hen is doing it herself.

    By using the built in filter for spam (which Akismet uses), this code checks if someone’s on the comment blacklist by IP or email, and if so, flags the message as spam. You don’t get an email. You do still get the message in your spam, which is not a great fix. I’d rather it just get dumped into trash, but there’s no filter I can find for that.

    Still. This works, and it shut the guy up.

    add_filter( 'jetpack_contact_form_is_spam', 'jetpack_spammers', 11, 2 );
    
    function jetpack_spammers( $is_spam, $form ) {
    	if ( $is_spam ) {
    		return $is_spam;
    	}
    
    	if ( wp_blacklist_check( $form['comment_author'], $form['comment_author_email'], $form['comment_author_url'], $form['comment_content'], $form['user_ip'], $form['user_agent'] ) ) {
    		return true;
    	}
    
    	return false;
    }
    

    But. That only helps what’s on the blacklist. And the blacklist has a couple drawbacks. First of all, while it absolutely does handle multiple words (so I can block ‘milady mika’ if I want), it’s a little more complex if you wanted to block someone using gmail and a plus sign in the email address. So if you want to block example+spammer@gmail.comthen you either have to add that in literally or you get creative. I went creative.

    add_filter( 'jetpack_contact_form_is_spam', 'jetpack_harassment', 11, 2 );
    
    function jetpack_harassment( $is_spam, $form ) {
    	// Bail early if already spam
    	if ( $is_spam ) {
    		return $is_spam;
    	}
    	$badlist   = array();
    	$blacklist = explode( "\n", get_option( 'blacklist_keys' ) );
    
    	// Check the list for valid emails. Add the email _USERNAME_ to the list
    	foreach ( $blacklist as $spammer ) {
    		if ( is_email( $spammer ) ) {
    			$emailparts = explode( '@', $spammer );
    			$username   = $emailparts[0];
    			$badlist[]  = $username;
    		}
    	}
    
    	// Check if the comment author name matches an email we've banned
    	// You'd think we didn't have to do this but ...
    	if ( in_array( $form['comment_author'], $badlist ) ) {
    		return true;
    	}
    
    	// Check if the email username is one of the bad ones
    	// This will allow spammer@example.com AND spammer+foobar@example.com to get caught
    	foreach ( $badlist as $bad_person ) {
    		if ( preg_match( '/' . $bad_person . '/', $form['comment_author_email'] ) ) {
    			return true;
    		}
    	}
    
    	return false;
    }
    

    My original take was hardcoded in, but this way is more elegant and covers the majority of the ways ‘nice’ people try to get around blocks. Now, if you’ve blocked spammer@example.com and someone submits a form with spammer+avoid@example.com this will catch them. It has a higher chance of catching ‘innocents’ (like innocent@spammer.com) however considering I’m looking for something like rosbeitam@example.com I’m reasonably confident in this for my personal application.

    The Take Away

    If you make a contact form, you damn well better make a way for users to block people from the back end, without having to code it.

    Merry Christmas, ya filthy animals.

  • Hiatus: Irregular Updates

    Hiatus: Irregular Updates

    This will not be the final post on this site, however after six years of regular, two to three times a week posting, I am putting a pin in the promise of two posts (minimum) a week.

    The reality is that I’m just too busy now with writing to write.

    You can pause for laughter there.

    I’m busy with another site, and writing and the various work that it entails. Shockingly to me, I’ve found I’d rather work on it than this. For a long time, I was super happy to do both, but the reality now is that I’m not.

    Of course if something comes up in my regular work that I can share, I will, or if I find a wild bug or a cool workaround. But I don’t actually have the time to dedicate to that amount of blogging here.

    And so, at 1035 posts, I thank you all for reading. This blog absolutely changed my life.

  • Monitored Automation

    Monitored Automation

    One of the things I touched on in my talk at WordCamp NYC in September was the fact that automation has it’s flaws. The problems we face with automated systems flagging LGBTQ videos as ‘restricted’ or trending horrific topics or promulgating fake news is all because of automation. We did this to ourselves.

    Computers Share Our Biases

    Humans have biases, which color the ways in which we develop code. If we feel no one should be able to use a lower case P in WordPress (because that’s it’s name) then we can use our biases to programatically force that. Right or wrong, we are biased in many other ways.

    The bias of robots is limited to what we’ve taught the robot. There’s no such thing as a true AI yet. Yes, I know European Parliament has declared robots to be ‘electronic persons’ and therefore responsible for their actions. In the case of automated reviews, the robot lacks the ability to detect nuance. It lacks ethics and morals. It cannot make a judgment of what is worth more than something else.

    The flip side to this is that humans do what we’re told to do too. Sort of. If I tell you to download a file, review the code for security and sanity, but not make a moral judgement on it’s use, you will and you won’t. Oh sure, you understand it’s not your job, or responsibility, so you’ll try not to. We don’t ask the TSA to make a value judgement on what’s in our luggage, we ask them to determine if it’s allowed or not.

    Biases Drive Design

    Computers, even the most advanced, make decisions based on how we program them. If we tell them “The word Nazi is bad” then anything with that term that is submitted will be rejected. Even a tool by, say, the Holocaust Museum, talking about how their skill provides trivia about the Nazis’ rise to power.

    The reason a computer has bias is because we, the people who program the computers, have bias. We are capable of discussing the paradox of tolerance and, in many cases, of coming to an agreement as to what should and should not be permitted. It’s easy to say that guns are illegal on airplanes. It’s not easy to say that being mean will get you kicked off a plane, because that’s subjective.

    Back to the TSA. If you brought a pair of fuzzy handcuffs in your carry on, which is legal as of the time I wrote this, you would be permitted to do so, but someone would comment. You may even end up having a very public conversation about your private life. And yes, that TSA agent is totally making a value judgement about you.

    Who Watches the WatchMon?

    The answer, the solution to these problems, is difficult. On the one hand, a computer won’t judge you for the fuzzy handcuffs. On the other, it may also decide you’re a criminal for having them in your luggage. A human would understand the purpose of a skill that talks about Nazis not being hate speech, but they also may judge you for having some Nazi swag in your bag for your demonstration class.

    Curiously the solution comes with a blend of automation and humanity. There are some things that should be auto-rejected. If you have a rule based on a clear technical limitation, then a computer should be relied on to process those. Except in practice, this is not the case. Instead, we need a human to check, approve or not, and move on to the next possibility. That’s right, the solution to the automation problem is human monitoring.

    Human Bias Isn’t Solveable

    We can’t stop people from being biased.

    We can meet regularly to discuss the situation, but something needs to go over all the approvals and rejections to see what people are actually doing. So then we have automation monitor the human in reverse. A computer monitors and makes its calls “It looks like X is trending.” A human checks the trends and, if they notice something abnormal (like Betty White trending on a Tuesday afternoon), then can check if she’s done something.

    If a human manually removes Betty White from trending every time is shows up, a computer can flag that for their supervisor to ask why the hate for The Golden Girls. But this means someone has to sit down and talk about removing one’s personal biases from work, and I promise you, it’s harder than it looks.

    Automate But Verify

    The ultimate answer? If you’re not monitoring and verifying your automation, you’re doing it wrong. Much like we say ‘test your backups,’ you have to test everything you task a computer to automagically do.