Category: News about this site

Posts about how this site is built, and the free Claude skills published alongside them.

  • The window that typed my words into the wrong place

    The window that typed my words into the wrong place

    This article comes with a free Claude skill

    The rule this story ends with is packaged as windows-silent-subprocess – one small file you drop into Claude Code so your own assistant works this way too. It is public domain: copy it, change it, no attribution and no permission needed. How to install it is at the end of this article.

    I was halfway through a sentence when the window appeared.

    It was a console window, black, maybe two inches tall, and it belonged to a script that had just started doing something useful in the background. It took the keyboard with it. The rest of my sentence went into it instead of into the form I was filling in, and one of those characters was Enter, and the console did what consoles do with Enter.

    Nothing crashed. There was no error. The form I had been filling in was simply gone, along with about ten minutes of typing, and the only evidence that anything had happened was a window that closed a second later.

    I lost that ten minutes to a program I wrote, doing a job I asked for, working correctly.

    Focus theft is data loss

    It is easy to file this under annoyance. A window pops up, you swear at it, you carry on. But a window that steals the keyboard while a person is typing does not just interrupt them: it takes whatever they type next and gives it to something else. If that something else is a shell, the keystrokes are commands. If it is a dialog, one of them is an answer.

    The distance between “mildly irritating” and “silently destroyed ten minutes of work” is one keystroke, and which side you land on is luck.

    So the rule here is not “try to avoid popping up windows.” It is that a background job must be incapable of taking the foreground, and that has to be true by construction, because a rule you have to remember is a rule you will forget on the afternoon you are in a hurry.

    The part that surprised me

    The obvious mental model is wrong, and being wrong about it is what causes the bug.

    You would assume a program launched quietly stays quiet, and that its children inherit that. The opposite is true on Windows. A console program attaches to its parent’s console if one exists. If the parent has no console, the child cannot attach to anything, so it allocates a brand new visible one.

    Read that again, because it inverts the intuition completely: making the parent quieter is what makes the child loud. The scheduled task with no window, the always-on agent running headless, the service started at boot – those are precisely the parents whose children flash a console into the middle of your screen. The more carefully you have hidden the parent, the more reliably the child appears.

    That is why this bug survives so long in a codebase. It does not show up while you are developing, because you are running things from a terminal that already has a console for the child to borrow. It shows up in production, on someone else’s machine, at a moment nobody is watching.

    Launching a background process on Windows without a console window

    Give the child a console it can have, and make it invisible. On Windows that is a single creation flag, and the important property is that grandchildren inherit it – so the whole process tree stays silent, including the parts you did not write and cannot change.

    The fix is small. Making it stick is the real work, because the failure mode is a single ordinary spawn call written months later by someone who never lost ten minutes. So it goes in one shared helper that every spawn goes through, and a check fails the build if a raw spawn appears anywhere else. Not because the helper is clever, but because “everyone remembers to use the helper” is not a mechanism, and this is a bug you only notice by being its victim.

    A few related traps, all the same shape – something that seems hideable and is not:

    • The “Hidden” checkbox in a task scheduler hides the task from the list. It does not hide the window. Two different things wearing one word.
    • A .bat wrapper flashes a console before it runs anything at all, so wrapping a silent program in one makes it loud.
    • Opening a file through its default application hands control to a GUI you cannot suppress.
    • An interactive prompt inside a hidden console does not fail. It hangs, invisibly, forever, waiting for an answer from a window nobody can see.

    That last one is the worst of them, and it is the one that taught me the general lesson: hiding a thing does not remove its need to be interacted with. It just removes your ability to notice.

    The rule

    A background job must be unable to take the foreground, enforced in one place that every spawn goes through.

    Not “avoid popping windows.” Not “remember the flag.” One helper, one check that fails when someone bypasses it, and a default that is silent even when the caller forgets to ask for silence.

    The person this protects is the one typing, who has no idea any of this exists and should not have to.

    Get the skill

    Everything above is generic. None of it is about rats, and none of it is specific to this site. So the rule is also published on its own as a Claude skill: a single Markdown file that an AI coding assistant reads and applies when the situation comes up.

    The file: windows-silent-subprocess/SKILL.md

    Or take the whole set:

    git clone https://github.com/blonderoofrat/agent-skills

    Installing it in Claude Code. Copy the skill’s folder into one of these, so the file ends up at .../skills/windows-silent-subprocess/SKILL.md:

    • ~/.claude/skills/ (available in every project on your machine)
    • your-project/.claude/skills/ (that one project only)

    Claude reads it at the start of the next session and applies it when what you are doing matches the description at the top of the file. You can also ask for it by name.

    Using a different assistant? The file is plain Markdown with a two-line header. Paste the body into whatever system prompt, rules file or instructions file your tool uses. Nothing in the rule itself is Claude-specific.

    Licence: CC0, public domain. Copy it, adapt it, ship it in commercial work, no attribution required. These are deliberately frozen snapshots rather than a maintained project, so if one is wrong for your situation, change it. That is easier than asking us to.


    Part of Notes from building this site: articles about working practices that exist because something here went wrong first. The rule above is also published on its own, as a free public-domain instruction file for AI coding assistants — the windows-silent-subprocess skill, in blonderoofrat/agent-skills on GitHub.

  • The page looked perfect. One button just did nothing.

    The page looked perfect. One button just did nothing.


    This article comes with a free Claude skill

    The rule this story ends with is packaged as wp-inline-js-safety – one small file you drop into Claude Code so your own assistant works this way too. It is public domain: copy it, change it, no attribution and no permission needed. How to install it is at the end of this article.

    The page looked perfect. One button just did nothing.

    We have a page on this site that helps you work out whether a food is safe for a rat. It is mostly JavaScript: you pick a food, it does some arithmetic, it tells you the answer.

    One day it stopped answering. Not with an error. Not with a blank page. The page rendered exactly as it always had, every word in place, and the button simply did not respond. Nothing in the server logs. Nothing in the PHP error log. The code had not changed.

    The cause turned out to be WordPress, and it is worth writing down because it is the kind of thing you cannot reason your way to. You have to already know.

    Why WordPress turns && into an HTML entity inside a script tag

    WordPress runs page content through a tidying filter that makes typography look nicer: straight quotes become curly ones, three dots become an ellipsis. Useful for prose.

    It also encodes an ampersand that it finds sitting between a less-than sign and a greater-than sign, because in that position it looks like it belongs to an HTML tag.

    Now consider ordinary JavaScript:

    if (weight < limit && age > minimum) { ... }

    What arrives in the browser is this, and if you have landed here from a search for those characters, this is the thing you pasted:

    if (weight < limit &#038;&#038; age > minimum) { ... }

    Read that as the filter reads it, with no idea it is code. There is a <. Later there is a >. Between them sits &&. So the ampersands get converted into HTML entities, and what reaches the browser is not JavaScript any more. It is a syntax error.

    The entire script block dies. Not the one line. The browser hits the broken statement, abandons the whole block, and every function defined in it is silently absent. Your page looks completely normal and nothing in it works.

    Nothing on the server ever knew. From PHP’s point of view the page was generated correctly, because it was: the damage happens after your code has finished, on the way out.

    How to avoid it

    Three habits, in order of how much they help:

    1. Do not let && sit between a < and a >. Flip the comparison so the angle brackets point the same way: if (limit > weight && age > minimum). Same logic, nothing for the filter to catch.
    2. Put page behaviour in a properly enqueued script file, not inline in page content. Files are not passed through the content filter at all. This is the real fix; the first habit is for when you genuinely must emit inline code.
    3. If you must emit inline JavaScript, emit it somewhere the filter does not reach. In our case that meant hooking the footer rather than putting it in the page body.

    The part that cost us the most

    We did not find this by reading the code. We had read the code several times, and the code was correct.

    We found it by looking at what the browser actually received, which was different from what we had written. That gap is the whole lesson, and it is why we now have an automated check that compares emitted JavaScript against what WordPress’s own filter does to it, using the real filter rather than our idea of it.

    If a page of yours is behaving impossibly, view the source the browser got. Not your template. Not your file. The bytes that arrived.

    We wrote this up as a skill

    Everything above is generic: it is true of any WordPress site, and none of it is about rats.

    So it exists separately as a short instruction file for AI coding assistants, along with ten others we extracted from the same project. Each one is a habit that exists because something here went wrong first. They are public domain, free to copy, and require nothing from us:

    https://github.com/blonderoofrat/agent-skills

    They are deliberately unmaintained snapshots rather than a project. If one is wrong for your situation, change it. That is easier than asking us to.

    Get the skill

    Everything above is generic. None of it is about rats, and none of it is specific to this site. So the rule is also published on its own as a Claude skill: a single Markdown file that an AI coding assistant reads and applies when the situation comes up.

    The file: wp-inline-js-safety/SKILL.md

    Or take the whole set:

    git clone https://github.com/blonderoofrat/agent-skills

    Installing it in Claude Code. Copy the skill’s folder into one of these, so the file ends up at .../skills/wp-inline-js-safety/SKILL.md:

    • ~/.claude/skills/ (available in every project on your machine)
    • your-project/.claude/skills/ (that one project only)

    Claude reads it at the start of the next session and applies it when what you are doing matches the description at the top of the file. You can also ask for it by name.

    Using a different assistant? The file is plain Markdown with a two-line header. Paste the body into whatever system prompt, rules file or instructions file your tool uses. Nothing in the rule itself is Claude-specific.

    Licence: CC0, public domain. Copy it, adapt it, ship it in commercial work, no attribution required. These are deliberately frozen snapshots rather than a maintained project, so if one is wrong for your situation, change it. That is easier than asking us to.


    Part of Notes from building this site: articles about working practices that exist because something here went wrong first. The rule above is also published on its own, as a free public-domain instruction file for AI coding assistants — the wp-inline-js-safety skill, in blonderoofrat/agent-skills on GitHub.

  • Twenty-two little text adventures about roof rat life

    Twenty-two little text adventures about roof rat life

    If you are old enough to remember typing GO NORTH at a blinking cursor, you know what these are. If you are not, they are worth a look anyway: you read a bit of a scene, type what you want to do next, and feel your way through.

    Ours are about roof rats. Their care, how they actually behave, and some of the science, all worked into the story instead of lectured at you. You do pick up real things by playing, but mostly it is just fun to poke around and see what happens.

    There are twenty-two of them now, in five tracks: bringing a rat home, health and care, breeding responsibly, a set that shrinks you down and sends you through a rat’s own body, and two where you command the immune system. Have a wander.

    Play the text adventures

  • Memory Match, now with cats getting in the way

    Memory Match, now with cats getting in the way

    You know this one already. Flip two cards, try to remember where everything is, match the pairs. Ours are made from photos of our roof rats.

    The wrinkle is the cats. Some of the cards are cartoon cats that do not match anything, so flipping one just costs you a turn and a little dignity. Cats, getting in the way of rats, as usual. There is also a countdown clock, so you cannot dawdle forever.

    It is a quick one. Good for a coffee break, or for anyone who thinks they have a good memory and needs to be humbled.

    Play Memory Match

  • Rat 2048: merge your way up to the cutest rat

    Rat 2048: merge your way up to the cutest rat

    If you have ever lost an hour to 2048, consider this your warning. Same slide-and-merge game, but the tiles are photos of our roof rats.

    The rats are ranked from ordinary-looking all the way up to the cutest one at the top. Slide two matching rats together and they merge into the next rat up. Keep merging and you climb the ladder toward that top rat.

    There is one wildcard. Once per game you get a gold “Blondie!” tile you can drop wherever you like, and she merges with almost any rat to bump it up a level. There is a catch: dropping her resets your score to zero. So pick your moment.

    Play Rat 2048

  • Slide the tiles, rebuild the rat

    Slide the tiles, rebuild the rat

    This is the one for when the kettle is boiling and your hands want something to do. Nothing to install, nothing to sign up for.

    It is the old sliding tile puzzle, the kind with one empty square you shuffle the pieces around in. The picture you are putting back together is a photo of one of our actual roof rats. Slide tiles into the gap until the whole picture lines up.

    If you get stuck, and you will at the bigger sizes, there is a Hint button that nudges one tile into place, and a Solve button that finishes the whole thing for you. No shame in either one.

    Play the Sliding Puzzle

  • A new roof rat joke, every day

    A new roof rat joke, every day

    Sometimes you have thirty seconds and a phone in your hand and nothing much to do with either. Here is a small, silly thing for exactly that.

    The Daily Squeak is one roof-rat joke that changes every day. Mock news, wordplay, the odd comic or song parody. Most of them have a little “The science…” note underneath, because a surprising number of these are built on real rat facts. You can read it or ignore it, your call.

    Every squeak we have ever run is kept in the archive, and it is searchable. Try “almond” or “hammock” and see what comes up. You can also link straight to any single joke if you want to send one to a friend.

    Read today’s squeak