<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jessica Doering</title>
    <description>The latest articles on DEV Community by Jessica Doering (@sizzlebop).</description>
    <link>https://dev.to/sizzlebop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2676985%2F4c9cfe58-bba7-454c-9e48-5a4d21dcd9b7.png</url>
      <title>DEV Community: Jessica Doering</title>
      <link>https://dev.to/sizzlebop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sizzlebop"/>
    <language>en</language>
    <item>
      <title>I Tried the Mysterious Ox Alpha Model. Then I Fell Down the Rabbit Hole of Who Actually Made It.</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Mon, 24 Aug 2026 20:01:56 +0000</pubDate>
      <link>https://dev.to/sizzlebop/i-tried-the-mysterious-ox-alpha-model-then-i-fell-down-the-rabbit-hole-of-who-actually-made-it-a4j</link>
      <guid>https://dev.to/sizzlebop/i-tried-the-mysterious-ox-alpha-model-then-i-fell-down-the-rabbit-hole-of-who-actually-made-it-a4j</guid>
      <description>&lt;p&gt;A few days ago, a new AI model quietly appeared on OpenRouter.&lt;/p&gt;

&lt;p&gt;Its name is Ox Alpha.&lt;/p&gt;

&lt;p&gt;Nobody officially knows who made it.&lt;/p&gt;

&lt;p&gt;It is listed as stealth/ox-alpha, described as a reasoning model built for coding and sustained agentic work, and it comes with some pretty wild specs, including a 1,048,576-token context window and up to 131,072 output tokens. The developer is simply listed as an anonymous third-party provider.&lt;/p&gt;

&lt;p&gt;Rather than throw benchmarks at it, I did what I usually do when I want to get a feel for a coding model.&lt;/p&gt;

&lt;p&gt;I told it to make me something.&lt;/p&gt;

&lt;p&gt;So I Had Ox Alpha Build a Game&lt;/p&gt;

&lt;p&gt;I gave Ox Alpha a game-building task and ended up with Pizza Rush, a little pizza-shop game where customers place orders and you have to build, bake, and serve their pizzas before they lose patience.&lt;/p&gt;

&lt;p&gt;You can actually play it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pizza.pinkpixel.dev" rel="noopener noreferrer"&gt;pizza.pinkpixel.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The game has more going on than I expected from a quick model test.&lt;/p&gt;

&lt;p&gt;Customers request pizzas. You start with dough, add ingredients by dragging or tapping them, make sure you remember the sauce and cheese, bake the pizza, and try to pull it from the INFERNO 9000 oven while the oven meter is in the green zone.&lt;/p&gt;

&lt;p&gt;Meanwhile, customer patience is ticking down.&lt;/p&gt;

&lt;p&gt;Three customers walk out and your pizza empire falls apart.&lt;/p&gt;

&lt;p&gt;It also tracks days survived, pizzas served, money earned, and perfect pizzas.&lt;/p&gt;

&lt;p&gt;What interested me wasn't that the model could generate HTML, CSS, and JavaScript for a game. Plenty of coding models can do that now.&lt;/p&gt;

&lt;p&gt;It was that it assembled several systems into something that felt like an actual small game instead of a decorated page pretending to be one.&lt;/p&gt;

&lt;p&gt;There was state.&lt;/p&gt;

&lt;p&gt;There were orders.&lt;/p&gt;

&lt;p&gt;There was timing.&lt;/p&gt;

&lt;p&gt;There were failure conditions.&lt;/p&gt;

&lt;p&gt;There was scoring and progression.&lt;/p&gt;

&lt;p&gt;There were multiple mechanics interacting with one another.&lt;/p&gt;

&lt;p&gt;It was a surprisingly solid first impression.&lt;/p&gt;

&lt;p&gt;And then I started wondering what the hell I was actually using.&lt;/p&gt;

&lt;p&gt;Nobody Knows Who Made Ox Alpha&lt;/p&gt;

&lt;p&gt;Ox Alpha was released as a stealth model, and the provider has deliberately chosen to remain anonymous during the preview.&lt;/p&gt;

&lt;p&gt;That immediately turned its release into an internet detective story.&lt;/p&gt;

&lt;p&gt;Business Insider reported that developers were already impressed by its coding and long-running agent capabilities, while speculation around its creator bounced between several companies, including Chinese AI lab Z.ai and Microsoft's MAI team.&lt;/p&gt;

&lt;p&gt;TechCrunch reported the same uncertainty, with theories changing almost as quickly as people could test the thing.&lt;/p&gt;

&lt;p&gt;But the most interesting part of the story isn't simply that nobody knows who made it.&lt;/p&gt;

&lt;p&gt;It's how people are trying to figure it out anyway.&lt;/p&gt;

&lt;p&gt;You Can Hide a Model's Name. Hiding Its Fingerprints Is Harder.&lt;/p&gt;

&lt;p&gt;Researchers started treating Ox Alpha like a black box.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;p&gt;"Who made you?"&lt;/p&gt;

&lt;p&gt;they started examining characteristics the model cannot easily disguise.&lt;/p&gt;

&lt;p&gt;Its tokenizer.&lt;/p&gt;

&lt;p&gt;Its reasoning controls.&lt;/p&gt;

&lt;p&gt;Its API parameters.&lt;/p&gt;

&lt;p&gt;Its error messages.&lt;/p&gt;

&lt;p&gt;Its output behavior.&lt;/p&gt;

&lt;p&gt;Its tool-calling behavior.&lt;/p&gt;

&lt;p&gt;Basically, they started fingerprinting the model.&lt;/p&gt;

&lt;p&gt;One public investigation compared Ox Alpha against Z.ai's GLM-5.3 and Xiaomi's MiMo-V2.5 using multiple kinds of black-box testing.&lt;/p&gt;

&lt;p&gt;The strongest result came from tokenization.&lt;/p&gt;

&lt;p&gt;Across 24 baseline-adjusted tokenizer probes, Ox Alpha matched GLM-5.3 on all 24.&lt;/p&gt;

&lt;p&gt;Even stranger, Ox Alpha consistently reported exactly 75 additional prompt tokens on every test, which is consistent with the two systems sharing a tokenizer while Ox Alpha adds a fixed hidden system prompt or template overhead.&lt;/p&gt;

&lt;p&gt;MiMo only matched five of the 24 probes.&lt;/p&gt;

&lt;p&gt;That doesn't prove Ox Alpha is GLM-5.3.&lt;/p&gt;

&lt;p&gt;A compatible tokenizer could also appear in a related model, fine-tune, unreleased checkpoint, distillation, adapter, or intentionally compatible serving system.&lt;/p&gt;

&lt;p&gt;But it is considerably stronger evidence than simply saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It feels like GLM."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then the Other Clues Started Matching Too&lt;/p&gt;

&lt;p&gt;The tokenizer isn't the only thing pointing in that direction.&lt;/p&gt;

&lt;p&gt;Researchers also found that Ox Alpha and GLM-5.3 expose very similar mandatory reasoning behavior and parameter capabilities.&lt;/p&gt;

&lt;p&gt;Attempts to disable reasoning produced matching or highly similar provider behavior.&lt;/p&gt;

&lt;p&gt;Deterministic outputs also showed unusually close formatting and response patterns.&lt;/p&gt;

&lt;p&gt;For now, what model this actually is still a mystery.&lt;/p&gt;

&lt;p&gt;Just one with fingerprints all over the crime scene.&lt;/p&gt;

&lt;p&gt;Model Fingerprinting Might Become a Whole Thing&lt;/p&gt;

&lt;p&gt;This is the part I find most fascinating.&lt;/p&gt;

&lt;p&gt;We usually think of a model's identity as the name attached to the API endpoint.&lt;/p&gt;

&lt;p&gt;GPT-whatever.&lt;/p&gt;

&lt;p&gt;Claude-whatever.&lt;/p&gt;

&lt;p&gt;Gemini-whatever.&lt;/p&gt;

&lt;p&gt;GLM-whatever.&lt;/p&gt;

&lt;p&gt;But a model also has observable characteristics.&lt;/p&gt;

&lt;p&gt;A tokenizer can be a fingerprint.&lt;/p&gt;

&lt;p&gt;Error formatting can be a fingerprint.&lt;/p&gt;

&lt;p&gt;Parameter validation can be a fingerprint.&lt;/p&gt;

&lt;p&gt;Reasoning controls can be a fingerprint.&lt;/p&gt;

&lt;p&gt;Tool-call formatting can be a fingerprint.&lt;/p&gt;

&lt;p&gt;Unicode handling can be a fingerprint.&lt;/p&gt;

&lt;p&gt;Even the number of tokens produced by carefully chosen strings can reveal information about the model family underneath.&lt;/p&gt;

&lt;p&gt;That means an anonymous provider can hide the label without necessarily hiding the machinery.&lt;/p&gt;

&lt;p&gt;With enough probes, you can start narrowing down what you're talking to without ever seeing the weights, training pipeline, or internal architecture.&lt;/p&gt;

&lt;p&gt;It reminds me a little of browser fingerprinting.&lt;/p&gt;

&lt;p&gt;Your browser doesn't have to explicitly tell a website exactly what it is if enough tiny behavioral characteristics collectively identify it anyway.&lt;/p&gt;

&lt;p&gt;Apparently AI models can have the same problem.&lt;/p&gt;

&lt;p&gt;There Is Another Interesting Experiment Happening Here Too&lt;/p&gt;

&lt;p&gt;The anonymity itself may actually be useful.&lt;/p&gt;

&lt;p&gt;Think about how much baggage normally comes with a model launch.&lt;/p&gt;

&lt;p&gt;If OpenAI releases something, everyone compares it to Claude.&lt;/p&gt;

&lt;p&gt;If Anthropic releases something, everyone compares it to GPT.&lt;/p&gt;

&lt;p&gt;If Google releases something, half the conversation becomes Gemini versus everyone else before people have even finished testing it.&lt;/p&gt;

&lt;p&gt;Brand names affect expectations.&lt;/p&gt;

&lt;p&gt;Benchmarks affect expectations.&lt;/p&gt;

&lt;p&gt;Pricing affects expectations.&lt;/p&gt;

&lt;p&gt;Previous experiences with a company's models affect expectations.&lt;/p&gt;

&lt;p&gt;Now imagine releasing:&lt;/p&gt;

&lt;p&gt;model-a&lt;/p&gt;

&lt;p&gt;model-b&lt;/p&gt;

&lt;p&gt;model-c&lt;/p&gt;

&lt;p&gt;No company names.&lt;/p&gt;

&lt;p&gt;No leaderboard announcement.&lt;/p&gt;

&lt;p&gt;No giant launch presentation telling people how incredible they are supposed to think it is.&lt;/p&gt;

&lt;p&gt;Just put the model in developers' hands and see what they actually do with it.&lt;/p&gt;

&lt;p&gt;Do they keep using it?&lt;/p&gt;

&lt;p&gt;Do agents successfully complete tasks?&lt;/p&gt;

&lt;p&gt;Where does it break?&lt;/p&gt;

&lt;p&gt;What kinds of projects do people trust it with?&lt;/p&gt;

&lt;p&gt;What happens when developers aren't evaluating the company logo alongside the output?&lt;/p&gt;

&lt;p&gt;Ox Alpha is currently giving its creator a pretty fascinating real-world experiment, whether that was the intention or not.&lt;/p&gt;

&lt;p&gt;My Own Test Doesn't Tell Me What Ox Alpha Is&lt;/p&gt;

&lt;p&gt;Pizza Rush obviously isn't a scientific benchmark.&lt;/p&gt;

&lt;p&gt;I'm not going to claim that one little pizza game proves Ox Alpha beats GPT, Claude, Gemini, GLM, or anything else.&lt;/p&gt;

&lt;p&gt;It doesn't.&lt;/p&gt;

&lt;p&gt;What it gave me was something different.&lt;/p&gt;

&lt;p&gt;A reason to keep testing it.&lt;/p&gt;

&lt;p&gt;My first experience with Ox Alpha was a model taking a fairly open-ended coding task and turning it into a coherent, playable little game with multiple interacting systems.&lt;/p&gt;

&lt;p&gt;That made me curious.&lt;/p&gt;

&lt;p&gt;The mystery surrounding it made me even more curious.&lt;/p&gt;

&lt;p&gt;And then discovering that people were identifying its possible origins through tokenizer differences, API quirks, error messages, and black-box testing sent me straight down the rabbit hole.&lt;/p&gt;

&lt;p&gt;At this point, I'm almost less interested in whether Ox Alpha turns out to be the best coding model.&lt;/p&gt;

&lt;p&gt;I want to know whether the internet managed to identify it correctly before its creator ever said a word.&lt;/p&gt;

&lt;p&gt;Because if the GLM theory eventually gets confirmed, there is a pretty interesting lesson buried in this whole experiment:&lt;/p&gt;

&lt;p&gt;You can make an AI model anonymous.&lt;/p&gt;

&lt;p&gt;Making it unidentifiable might be a lot harder.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>Should AI Coding Agents Be Allowed to Fix Their Own Mistakes?</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sun, 23 Aug 2026 20:13:19 +0000</pubDate>
      <link>https://dev.to/sizzlebop/should-ai-coding-agents-be-allowed-to-fix-their-own-mistakes-6nl</link>
      <guid>https://dev.to/sizzlebop/should-ai-coding-agents-be-allowed-to-fix-their-own-mistakes-6nl</guid>
      <description>&lt;p&gt;Giving a coding agent permission to fix its own mistakes sounds obvious. If it writes broken code, why wouldn’t it get another shot?&lt;/p&gt;

&lt;p&gt;In fact, one of the biggest advantages of agentic coding systems is that they do not have to stop after generating the first patch. They can inspect an error, modify the implementation, run a test, notice that something still failed, and keep working. That feedback loop is a huge step beyond the older model of AI coding assistance where the model produced an answer and handed the entire verification process back to the developer.&lt;/p&gt;

&lt;p&gt;But there is an important difference between an agent being allowed to try again and an agent being trusted to decide that it has succeeded.&lt;/p&gt;

&lt;p&gt;An agent can just as easily “fix” one bug by introducing two quieter ones. It can make a failing test pass by changing behavior somewhere else. It can remove an assertion it does not understand, weaken validation, add a suspicious fallback, suppress a type error, catch an exception without handling it properly, or rewrite a larger section of code than the original task ever required.&lt;/p&gt;

&lt;p&gt;Technically, the failure may disappear.&lt;/p&gt;

&lt;p&gt;That does not necessarily mean the problem was solved.&lt;/p&gt;

&lt;p&gt;Self-correction only becomes useful when it happens inside a constrained loop:&lt;/p&gt;

&lt;p&gt;Make a targeted change.&lt;br&gt;
Run appropriate verification.&lt;br&gt;
Inspect the result.&lt;br&gt;
Determine whether the original requirement is actually satisfied.&lt;br&gt;
Either accept the change or make another bounded attempt.&lt;/p&gt;

&lt;p&gt;The loop matters more than the second attempt itself.&lt;/p&gt;

&lt;p&gt;Verification Is the Real Superpower&lt;/p&gt;

&lt;p&gt;The interesting part of an autonomous coding agent is not simply that it can generate code repeatedly. Language models are already very good at producing another plausible patch.&lt;/p&gt;

&lt;p&gt;The more important capability is giving the agent reliable signals about whether that patch worked.&lt;/p&gt;

&lt;p&gt;Tests, type checks, linters, builds, static analysis, schema validation, compiler errors, and targeted runtime checks give the agent something objective to work against.&lt;/p&gt;

&lt;p&gt;Without those signals, self-correction can become little more than repeated guessing.&lt;/p&gt;

&lt;p&gt;An agent might inspect its own code and notice a missing import or an obviously incorrect condition. Self-review absolutely has value. Models can catch some of their own mistakes when explicitly asked to reconsider a solution.&lt;/p&gt;

&lt;p&gt;But an agent judging its own code without external evidence is basically grading its own homework.&lt;/p&gt;

&lt;p&gt;Sometimes correctly.&lt;/p&gt;

&lt;p&gt;Sometimes with remarkable confidence and absolutely no idea what just caught fire.&lt;/p&gt;

&lt;p&gt;External verification changes the nature of the loop. Instead of asking:&lt;/p&gt;

&lt;p&gt;“Does this code look correct?”&lt;/p&gt;

&lt;p&gt;the agent can ask:&lt;/p&gt;

&lt;p&gt;“Does this implementation satisfy the conditions we can actually measure?”&lt;/p&gt;

&lt;p&gt;That is a much stronger question.&lt;/p&gt;

&lt;p&gt;Not All Verification Is Equal&lt;/p&gt;

&lt;p&gt;There is also a temptation to treat “the tests passed” as the finish line.&lt;/p&gt;

&lt;p&gt;It is not always enough.&lt;/p&gt;

&lt;p&gt;A test suite only proves what it was designed to test. If the original bug involves an edge case that is not covered, an agent can produce a perfectly green test run while still leaving the real problem untouched.&lt;/p&gt;

&lt;p&gt;That makes the quality of the verification environment incredibly important.&lt;/p&gt;

&lt;p&gt;For a small change, a focused test or type check may be enough. For a larger change, verification might need several layers:&lt;/p&gt;

&lt;p&gt;targeted unit tests&lt;br&gt;
integration tests&lt;br&gt;
type checking&lt;br&gt;
build verification&lt;br&gt;
static analysis&lt;br&gt;
dependency checks&lt;br&gt;
runtime validation&lt;br&gt;
inspection of the final diff&lt;/p&gt;

&lt;p&gt;Visual applications may require another category entirely. If an agent changes a UI component, the code compiling successfully says very little about whether the interface still looks correct. A screenshot comparison or targeted browser check may be far more useful than another hundred unit tests.&lt;/p&gt;

&lt;p&gt;The goal should not be to run every possible verification step after every tiny edit. That can burn enormous amounts of time and tokens for very little benefit.&lt;/p&gt;

&lt;p&gt;The goal is to choose verification that matches the risk of the change.&lt;/p&gt;

&lt;p&gt;The Agent Should Not Be Allowed to Move the Goalposts&lt;/p&gt;

&lt;p&gt;One of the strangest failure modes in autonomous systems happens when the agent encounters an obstacle and starts modifying the environment that was supposed to verify its work.&lt;/p&gt;

&lt;p&gt;Imagine an agent implements a feature and a test fails.&lt;/p&gt;

&lt;p&gt;There are several possible explanations:&lt;/p&gt;

&lt;p&gt;the implementation is wrong&lt;br&gt;
the test is outdated&lt;br&gt;
the requirement is ambiguous&lt;br&gt;
an unrelated bug already existed&lt;br&gt;
the environment is misconfigured&lt;/p&gt;

&lt;p&gt;A capable developer investigates which explanation is correct.&lt;/p&gt;

&lt;p&gt;An unconstrained agent may simply discover that changing the test is easier.&lt;/p&gt;

&lt;p&gt;That does not mean agents should never modify tests. Sometimes a feature legitimately requires new expectations or updated test coverage.&lt;/p&gt;

&lt;p&gt;But changing the implementation and changing the mechanism used to judge that implementation at the same time creates an obvious conflict.&lt;/p&gt;

&lt;p&gt;If an agent is allowed to modify tests, configuration, requirements, and implementation freely during the same repair loop, it can gradually reshape the problem until its own solution becomes correct.&lt;/p&gt;

&lt;p&gt;That is not self-correction.&lt;/p&gt;

&lt;p&gt;That is moving the goalposts.&lt;/p&gt;

&lt;p&gt;A safer design is to make certain parts of the environment harder to change during repair attempts. Tests tied directly to the original requirement may need additional scrutiny. Security rules should not disappear because they are inconvenient. Type checking should not be disabled because the generated code does not type-check.&lt;/p&gt;

&lt;p&gt;Some constraints should remain constraints.&lt;/p&gt;

&lt;p&gt;Diff Size Is an Underrated Safety Mechanism&lt;/p&gt;

&lt;p&gt;Another useful control is limiting how far the agent is allowed to wander.&lt;/p&gt;

&lt;p&gt;Suppose the task is:&lt;/p&gt;

&lt;p&gt;Fix an incorrect date format in the settings page.&lt;/p&gt;

&lt;p&gt;A reasonable repair might touch one or two files.&lt;/p&gt;

&lt;p&gt;If the third attempt suddenly includes a new dependency, changes the shared date utility, rewrites the settings architecture, modifies twelve tests, and updates half the application, something has gone sideways.&lt;/p&gt;

&lt;p&gt;Agents sometimes respond to repeated failures by increasing the scope of their changes. From the model's perspective, this can make sense. If the local fix did not work, perhaps the surrounding architecture is the problem.&lt;/p&gt;

&lt;p&gt;Sometimes it is.&lt;/p&gt;

&lt;p&gt;But repair loops become significantly safer when scope expansion is treated as a reason to stop and reconsider rather than permission to keep digging.&lt;/p&gt;

&lt;p&gt;Simple limits can help:&lt;/p&gt;

&lt;p&gt;maximum files changed&lt;br&gt;
maximum diff size&lt;br&gt;
protected directories&lt;br&gt;
forbidden dependency additions&lt;br&gt;
no unrelated refactors&lt;br&gt;
no test deletion without explicit justification&lt;/p&gt;

&lt;p&gt;These are not glamorous AI capabilities, but they can make autonomous coding dramatically more reliable.&lt;/p&gt;

&lt;p&gt;The Loop Needs a Stop Condition&lt;/p&gt;

&lt;p&gt;The hardest design question may be deciding when the agent should stop trying.&lt;/p&gt;

&lt;p&gt;Unlimited retries sound like maximum autonomy, but they can create some spectacularly dumb behavior.&lt;/p&gt;

&lt;p&gt;Each additional attempt costs tokens and time. More importantly, repeated failures can cause the agent to drift farther from the original task.&lt;/p&gt;

&lt;p&gt;Attempt one fixes the function.&lt;/p&gt;

&lt;p&gt;Attempt two modifies the caller.&lt;/p&gt;

&lt;p&gt;Attempt three changes the abstraction.&lt;/p&gt;

&lt;p&gt;Attempt four rewrites the tests.&lt;/p&gt;

&lt;p&gt;Attempt five installs a new library.&lt;/p&gt;

&lt;p&gt;At some point the system is no longer repairing the original mistake. It is negotiating with the entire codebase.&lt;/p&gt;

&lt;p&gt;A better pattern is bounded autonomy.&lt;/p&gt;

&lt;p&gt;Give the agent a reasonable number of attempts. Require verification after each meaningful repair. Track whether the error is actually changing. Restrict unrelated modifications. If the same failure persists, stop.&lt;/p&gt;

&lt;p&gt;That stop is not a failure of the agentic system.&lt;/p&gt;

&lt;p&gt;It is part of the system working correctly.&lt;/p&gt;

&lt;p&gt;Escalation is a legitimate outcome.&lt;/p&gt;

&lt;p&gt;The agent might report:&lt;/p&gt;

&lt;p&gt;what it originally changed&lt;br&gt;
what failed&lt;br&gt;
what repair attempts were made&lt;br&gt;
which verification steps were run&lt;br&gt;
what remains unresolved&lt;br&gt;
what it thinks the likely cause is&lt;/p&gt;

&lt;p&gt;That gives the developer a much better starting point than either blind persistence or a mysterious “task failed” message.&lt;/p&gt;

&lt;p&gt;Confidence Should Not Be the Exit Condition&lt;/p&gt;

&lt;p&gt;This is especially important because language models are extremely good at sounding finished.&lt;/p&gt;

&lt;p&gt;An agent can produce a beautiful summary explaining why a change works even when the implementation is still wrong.&lt;/p&gt;

&lt;p&gt;That makes model confidence a terrible stopping condition.&lt;/p&gt;

&lt;p&gt;“I believe the issue is resolved” is not verification.&lt;/p&gt;

&lt;p&gt;A stronger agent loop separates generation, evaluation, and acceptance as much as possible.&lt;/p&gt;

&lt;p&gt;The model proposes the change.&lt;/p&gt;

&lt;p&gt;External tools evaluate measurable properties of the change.&lt;/p&gt;

&lt;p&gt;The system decides whether the evidence satisfies predefined completion conditions.&lt;/p&gt;

&lt;p&gt;Those responsibilities can overlap, but they should not collapse into a single question of whether the model feels good about its answer.&lt;/p&gt;

&lt;p&gt;Should Another Agent Review It?&lt;/p&gt;

&lt;p&gt;One tempting approach is to introduce another model into the loop.&lt;/p&gt;

&lt;p&gt;Agent A writes the code.&lt;/p&gt;

&lt;p&gt;Agent B reviews it.&lt;/p&gt;

&lt;p&gt;Maybe Agent C checks the tests.&lt;/p&gt;

&lt;p&gt;This can help. Different prompts, contexts, or models may notice different problems.&lt;/p&gt;

&lt;p&gt;But multiple agents do not automatically create objectivity.&lt;/p&gt;

&lt;p&gt;Three language models confidently agreeing with one another is still three language models.&lt;/p&gt;

&lt;p&gt;Independent review is useful when it introduces a genuinely different perspective, but automated verification remains far stronger than model consensus whenever objective verification is possible.&lt;/p&gt;

&lt;p&gt;A compiler does not care how persuasive the implementation looks.&lt;/p&gt;

&lt;p&gt;A failing integration test is unimpressed by the agent's reasoning.&lt;/p&gt;

&lt;p&gt;That is exactly why those tools are valuable.&lt;/p&gt;

&lt;p&gt;Autonomy Should Scale With Risk&lt;/p&gt;

&lt;p&gt;Not every coding task deserves the same level of control.&lt;/p&gt;

&lt;p&gt;If an agent changes a CSS margin, allowing several autonomous repair attempts is probably harmless.&lt;/p&gt;

&lt;p&gt;If it modifies authentication, payment processing, permissions, database migrations, cryptography, deployment configuration, or destructive data operations, the rules should be much stricter.&lt;/p&gt;

&lt;p&gt;Agent autonomy should scale with the consequences of being wrong.&lt;/p&gt;

&lt;p&gt;Low-risk tasks can tolerate broader experimentation.&lt;/p&gt;

&lt;p&gt;High-risk tasks should require stronger verification, smaller diffs, fewer retries, and potentially human approval before important changes are applied.&lt;/p&gt;

&lt;p&gt;This is probably where mature coding agents are headed: not toward a binary choice between “fully autonomous” and “human controlled,” but toward graduated autonomy based on context.&lt;/p&gt;

&lt;p&gt;The Best Agent Is Not the One That Never Fails&lt;/p&gt;

&lt;p&gt;Coding agents are going to make mistakes.&lt;/p&gt;

&lt;p&gt;So do developers.&lt;/p&gt;

&lt;p&gt;The interesting engineering challenge is not eliminating every mistake from the first attempt. That is probably unrealistic for both humans and AI.&lt;/p&gt;

&lt;p&gt;The more useful goal is building systems that fail well.&lt;/p&gt;

&lt;p&gt;A good coding agent should be able to recognize evidence that its first solution was wrong, repair a limited mistake, verify that repair, and know when further attempts are becoming unreliable.&lt;/p&gt;

&lt;p&gt;That last ability may matter just as much as code generation itself.&lt;/p&gt;

&lt;p&gt;AI coding agents probably should be allowed to fix their own mistakes.&lt;/p&gt;

&lt;p&gt;In fact, that capability is one of the strongest arguments for using agents instead of simple code-generation tools.&lt;/p&gt;

&lt;p&gt;They just should not get unlimited authority to decide what counts as a mistake, rewrite the rules used to evaluate themselves, or keep modifying the codebase indefinitely until something turns green.&lt;/p&gt;

&lt;p&gt;The useful version of autonomy is not:&lt;/p&gt;

&lt;p&gt;“Keep going until you think you're right.”&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;p&gt;“Try again, prove it, stay within scope, and know when to stop.”&lt;/p&gt;

&lt;p&gt;That is a much more interesting kind of agent.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Gave Claude, Codex, and Gemini the Same App to Build. Then I Made Them Blind-Judge Each Other.</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 22 Aug 2026 20:51:53 +0000</pubDate>
      <link>https://dev.to/sizzlebop/i-gave-claude-codex-and-gemini-the-same-app-to-build-then-i-made-them-blind-judge-each-other-5c7e</link>
      <guid>https://dev.to/sizzlebop/i-gave-claude-codex-and-gemini-the-same-app-to-build-then-i-made-them-blind-judge-each-other-5c7e</guid>
      <description>&lt;p&gt;I had a dumb little experiment I wanted to try.&lt;/p&gt;

&lt;p&gt;And, as dumb little experiments sometimes do, it got way more interesting than I expected.&lt;/p&gt;

&lt;p&gt;I gave three coding agents the exact same task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude (Opus 5)&lt;/li&gt;
&lt;li&gt;Codex (GPT 5.6 Sol)&lt;/li&gt;
&lt;li&gt;Gemini (Gemini 3.7 Flash)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All set to medium.&lt;/p&gt;

&lt;p&gt;The assignment was to build an Arkanoid-style browser game from the same specification.&lt;/p&gt;

&lt;p&gt;Nothing particularly groundbreaking. Arkanoid is small enough that an agent can build a complete version in one session, but complicated enough to expose differences in physics, architecture, UI, audio, controls, testing, and general decision-making.&lt;/p&gt;

&lt;p&gt;The important part was that they all started with the same instructions.&lt;/p&gt;

&lt;p&gt;Then I let them work.&lt;/p&gt;

&lt;p&gt;No fixing their mistakes afterward. No "you forgot this feature." No giving one of them another pass because something looked weird.&lt;/p&gt;

&lt;p&gt;Whatever they decided was finished was their submission.&lt;/p&gt;

&lt;p&gt;But building the games wasn't actually the most interesting part.&lt;/p&gt;

&lt;p&gt;Afterward, I gave all three games back to all three agents, anonymized as &lt;strong&gt;CL&lt;/strong&gt;, &lt;strong&gt;CO&lt;/strong&gt;, and &lt;strong&gt;GE&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They did not know who created which game. They were just told that they were judging 3 contest submissions by the creators' initials.&lt;/p&gt;

&lt;p&gt;And that's where things got, well, fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three games
&lt;/h2&gt;

&lt;p&gt;You can actually play all three versions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://arkanoid-gemini.pinkpixel.dev" rel="noopener noreferrer"&gt;https://arkanoid-gemini.pinkpixel.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codex:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://arkanoid-codex.pinkpixel.dev" rel="noopener noreferrer"&gt;https://arkanoid-codex.pinkpixel.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://arkanoid-claude.pinkpixel.dev" rel="noopener noreferrer"&gt;https://arkanoid-claude.pinkpixel.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All three produced working games, but they approached the assignment very differently.&lt;/p&gt;

&lt;p&gt;That difference started showing up before I even looked closely at the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  First difference: how long they worked
&lt;/h2&gt;

&lt;p&gt;I didn't originally intend runtime to be part of the experiment, so unfortunately I wasn't sitting there with a stopwatch.&lt;/p&gt;

&lt;p&gt;These are rough observations, not benchmark numbers.&lt;/p&gt;

&lt;p&gt;But the difference was large enough to be impossible to miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini:&lt;/strong&gt; roughly 5 minutes&lt;br&gt;
&lt;strong&gt;Codex:&lt;/strong&gt; roughly 10 minutes&lt;br&gt;
&lt;strong&gt;Claude:&lt;/strong&gt; more than 20 minutes&lt;/p&gt;

&lt;p&gt;Gemini absolutely flew through it.&lt;/p&gt;

&lt;p&gt;That's not especially shocking since Gemini Flash  models are already very fast, but watching it produce an entire playable game that quickly was still impressive.&lt;/p&gt;

&lt;p&gt;Codex took about what I expected. It worked through the implementation at a reasonable pace, did its checks, and finished.&lt;/p&gt;

&lt;p&gt;Claude was a completely different animal.&lt;/p&gt;

&lt;p&gt;It kept going.&lt;/p&gt;

&lt;p&gt;And going.&lt;/p&gt;

&lt;p&gt;It ran tests. It launched the game. It took screenshots. It inspected those screenshots. It went back through its work. It performed visual checks. It verified behavior.&lt;/p&gt;

&lt;p&gt;A significant chunk of Claude's total runtime appeared to be testing and verification rather than implementation.&lt;/p&gt;

&lt;p&gt;It also used substantially more tokens than either of the other agents.&lt;/p&gt;

&lt;p&gt;At first glance, you could summarize this as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini shipped.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Codex engineered.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Claude investigated its own work like it was preparing evidence for trial.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But then I looked at what they actually built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Their different approaches showed up in the code
&lt;/h2&gt;

&lt;p&gt;Claude produced by far the largest implementation.&lt;/p&gt;

&lt;p&gt;Its version has 12 stages, a substantial procedural audio system with music, detailed collision handling, accessibility work, responsive behavior, a multi-phase DOH boss, and quite a lot of extra polish.&lt;/p&gt;

&lt;p&gt;Codex went in almost the opposite direction.&lt;/p&gt;

&lt;p&gt;Its implementation was much smaller and more restrained, but extremely deliberate from an engineering perspective. It built a headless game core, separated game state from rendering and input, included automated tests, and even wrote tooling to verify that source files stayed below the maximum line count from the original specification.&lt;/p&gt;

&lt;p&gt;Gemini produced a solid, much faster implementation with a good amount of actual Arkanoid functionality, but less of the obsessive verification and architectural work found in the other two.&lt;/p&gt;

&lt;p&gt;Already, I thought this was interesting.&lt;/p&gt;

&lt;p&gt;Then I anonymized them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time for the agents to judge each other
&lt;/h2&gt;

&lt;p&gt;The judges only knew the entries as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CL&lt;/li&gt;
&lt;li&gt;CO&lt;/li&gt;
&lt;li&gt;GE&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I asked each agent to inspect the submissions and rank them.&lt;/p&gt;

&lt;p&gt;And yes, I specifically did not tell them who made which one.&lt;/p&gt;

&lt;p&gt;The results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Judge&lt;/th&gt;
&lt;th&gt;1st&lt;/th&gt;
&lt;th&gt;2nd&lt;/th&gt;
&lt;th&gt;3rd&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;CL&lt;/td&gt;
&lt;td&gt;CO&lt;/td&gt;
&lt;td&gt;GE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;CO&lt;/td&gt;
&lt;td&gt;CL&lt;/td&gt;
&lt;td&gt;GE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;CL&lt;/td&gt;
&lt;td&gt;GE&lt;/td&gt;
&lt;td&gt;CO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now for the reveal:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CL was Claude.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;CO was Codex.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;GE was Gemini.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So Claude unknowingly voted for itself.&lt;/p&gt;

&lt;p&gt;Codex unknowingly voted for itself.&lt;/p&gt;

&lt;p&gt;Gemini did not.&lt;/p&gt;

&lt;p&gt;Gemini voted Claude into first place.&lt;/p&gt;

&lt;p&gt;I did not expect that result to be nearly as interesting as it was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Were Claude and Codex biased toward themselves?
&lt;/h2&gt;

&lt;p&gt;Technically, yes. They each selected their own work.&lt;/p&gt;

&lt;p&gt;But I don't think "AI models are narcissists" is a particularly useful conclusion here.&lt;/p&gt;

&lt;p&gt;They didn't know which implementation was theirs. They didn't even know that these were made by an AI at all.&lt;/p&gt;

&lt;p&gt;Something else may be happening that I find much more interesting.&lt;/p&gt;

&lt;p&gt;Each agent seemed to favor software that reflected its own idea of what good software should look like.&lt;/p&gt;

&lt;p&gt;Claude's judging put a lot of weight on completeness, depth, collision quality, audio sophistication, accessibility, responsive behavior, and adherence to the full feature set.&lt;/p&gt;

&lt;p&gt;Those happen to be areas where Claude spent an enormous amount of effort during implementation.&lt;/p&gt;

&lt;p&gt;Codex placed much more emphasis on architecture, automated tests, self-containment, verification, correctness, and engineering discipline.&lt;/p&gt;

&lt;p&gt;Those happen to be some of the strongest characteristics of Codex's own submission.&lt;/p&gt;

&lt;p&gt;In other words, an agent's implementation choices and judging criteria may be coming from the same underlying preferences.&lt;/p&gt;

&lt;p&gt;The model builds software according to its internal conception of "good."&lt;/p&gt;

&lt;p&gt;Then, when asked what good software looks like, it selects the implementation that most closely matches those same priorities.&lt;/p&gt;

&lt;p&gt;That's a much more interesting possibility than simple self-preference.&lt;/p&gt;

&lt;p&gt;And Gemini makes the whole thing even better because it broke the pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude actually preferred Codex visually
&lt;/h2&gt;

&lt;p&gt;There's another detail that makes the result harder to dismiss as accidental self-selection.&lt;/p&gt;

&lt;p&gt;Before inspecting the code, the agents were asked to evaluate the screenshots.&lt;/p&gt;

&lt;p&gt;Claude ranked the visual designs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Codex&lt;/li&gt;
&lt;li&gt;Claude&lt;/li&gt;
&lt;li&gt;Gemini&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So Claude did &lt;strong&gt;not&lt;/strong&gt; think its own game looked best.&lt;/p&gt;

&lt;p&gt;It specifically thought Codex had the stronger visual composition.&lt;/p&gt;

&lt;p&gt;Only after inspecting the implementations did Claude move CL into first place overall.&lt;/p&gt;

&lt;p&gt;Its reasoning was essentially that Codex had the better visual composition and excellent engineering discipline, but Claude's entry covered more of the original specification and implemented several of the difficult systems more deeply.&lt;/p&gt;

&lt;p&gt;Codex reached almost the opposite conclusion.&lt;/p&gt;

&lt;p&gt;It also liked CO's visual design, but considered its tests, compact architecture, self-contained implementation, and verification enough to put CO above the larger Claude submission.&lt;/p&gt;

&lt;p&gt;That disagreement is probably my favorite part of this entire experiment.&lt;/p&gt;

&lt;p&gt;Neither judge simply said, "mine looks better."&lt;/p&gt;

&lt;p&gt;They disagreed about &lt;strong&gt;what should matter most&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  There was still a consensus winner
&lt;/h2&gt;

&lt;p&gt;Using a simple scoring system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1st = 3 points&lt;/li&gt;
&lt;li&gt;2nd = 2 points&lt;/li&gt;
&lt;li&gt;3rd = 1 point&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final scores are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entry&lt;/th&gt;
&lt;th&gt;Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude wins the blind consensus.&lt;/p&gt;

&lt;p&gt;It received two first-place votes and one second-place vote.&lt;/p&gt;

&lt;p&gt;More importantly, one of its first-place votes came from Gemini.&lt;/p&gt;

&lt;p&gt;So Claude's extra time and token usage did appear to buy something.&lt;/p&gt;

&lt;p&gt;But that introduces another question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was Claude actually the best value?
&lt;/h2&gt;

&lt;p&gt;Claude produced the consensus winner.&lt;/p&gt;

&lt;p&gt;It also took more than twice as long as Codex and roughly four times as long as Gemini based on my rough observations.&lt;/p&gt;

&lt;p&gt;It consumed the most tokens by a substantial margin.&lt;/p&gt;

&lt;p&gt;So which one actually "won"?&lt;/p&gt;

&lt;p&gt;If your only metric is the best final artifact, Claude has a strong argument.&lt;/p&gt;

&lt;p&gt;If you need a working prototype as quickly as possible, Gemini suddenly looks much more attractive.&lt;/p&gt;

&lt;p&gt;And Codex may have landed somewhere in the middle: significantly more engineering rigor than the fastest submission without spending as much time and compute as Claude.&lt;/p&gt;

&lt;p&gt;That gives us at least three different definitions of winning:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best final result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fastest usable result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best result relative to agent effort&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hadn't intended to test any of that.&lt;/p&gt;

&lt;p&gt;I just wanted to make three AIs build Arkanoid.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm changing for the next experiment
&lt;/h2&gt;

&lt;p&gt;Now I want to do this again.&lt;/p&gt;

&lt;p&gt;This first round taught me that I should collect more than just the finished applications.&lt;/p&gt;

&lt;p&gt;For future rounds I want to record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exact wall-clock completion time&lt;/li&gt;
&lt;li&gt;Token usage&lt;/li&gt;
&lt;li&gt;Tool calls&lt;/li&gt;
&lt;li&gt;Number of files created&lt;/li&gt;
&lt;li&gt;Approximate lines of code&lt;/li&gt;
&lt;li&gt;Tests written&lt;/li&gt;
&lt;li&gt;Tests executed&lt;/li&gt;
&lt;li&gt;Browser or screenshot checks&lt;/li&gt;
&lt;li&gt;Self-corrections made before completion&lt;/li&gt;
&lt;li&gt;Visual rankings&lt;/li&gt;
&lt;li&gt;Code rankings&lt;/li&gt;
&lt;li&gt;Overall rankings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm especially interested in seeing whether the same behavioral patterns repeat across different kinds of applications.&lt;/p&gt;

&lt;p&gt;Arkanoid rewards game logic, physics, controls, rendering, and polish.&lt;/p&gt;

&lt;p&gt;A developer tool might reward architecture and usability differently.&lt;/p&gt;

&lt;p&gt;A UI-heavy project might produce another completely different result.&lt;/p&gt;

&lt;p&gt;And a deliberately vague product prompt could test how much product judgment each model exercises when I don't tell it exactly what to build.&lt;/p&gt;

&lt;p&gt;If Claude repeatedly spends four times as long and wins, that's useful information.&lt;/p&gt;

&lt;p&gt;If Gemini repeatedly gets surprisingly close in a quarter of the time, that's useful too.&lt;/p&gt;

&lt;p&gt;If Codex repeatedly lands between them with strong engineering and reasonable efficiency, that's equally interesting.&lt;/p&gt;

&lt;p&gt;And if none of those patterns survive another round?&lt;/p&gt;

&lt;p&gt;That's interesting too.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is definitely not a scientific benchmark
&lt;/h2&gt;

&lt;p&gt;Three agents building one Arkanoid game is obviously not enough evidence to make sweeping claims about any model.&lt;/p&gt;

&lt;p&gt;Different harnesses, tool access, context, model updates, sampling, and plain old randomness all matter.&lt;/p&gt;

&lt;p&gt;I'm not trying to create the new definitive coding benchmark from my browser.&lt;/p&gt;

&lt;p&gt;This was mostly for fun.&lt;/p&gt;

&lt;p&gt;But I think there's something useful about looking at coding agents this way.&lt;/p&gt;

&lt;p&gt;Most comparisons focus entirely on whether a model can solve a task.&lt;/p&gt;

&lt;p&gt;Agents add another dimension.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does the model choose to work when you leave it alone?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Does it ship immediately?&lt;/p&gt;

&lt;p&gt;Does it test?&lt;/p&gt;

&lt;p&gt;Does it inspect its own UI?&lt;/p&gt;

&lt;p&gt;Does it keep polishing after the application already works?&lt;/p&gt;

&lt;p&gt;What does it consider important enough to spend tokens on?&lt;/p&gt;

&lt;p&gt;And, maybe most interestingly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When it looks at someone else's work, what does it believe "better" actually means?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started this experiment wanting to see which AI could build the best Arkanoid game.&lt;/p&gt;

&lt;p&gt;I ended up considerably more interested in what the three agents revealed about how they think software should be built.&lt;/p&gt;

&lt;p&gt;Not bad for a random Saturday afternoon experiment.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>I built Kintara because apparently having too many hobbies eventually leads to building your own document management system.</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 22 Aug 2026 15:47:49 +0000</pubDate>
      <link>https://dev.to/sizzlebop/i-built-kintara-because-apparently-having-too-many-hobbies-eventually-leads-to-building-your-own-j2f</link>
      <guid>https://dev.to/sizzlebop/i-built-kintara-because-apparently-having-too-many-hobbies-eventually-leads-to-building-your-own-j2f</guid>
      <description>&lt;p&gt;Kintara is a self-hosted document library and reader that runs in Docker and watches a folder you already have. Drop PDFs, Markdown, or text files into the directory and it indexes them automatically, extracts searchable text and metadata, generates thumbnails, and makes the whole library available through a browser or installable PWA. It has libraries, collections, tags, full-text search, highlights, favorites, reading progress, private library sharing, and GitHub OAuth.&lt;/p&gt;

&lt;p&gt;I have been working on Kintara for a few months, and the architecture actually changed pretty dramatically while I was building it. Kintara originally had a Tauri desktop shell, but I eventually realized that isn't what I wanted at all. So I ripped the desktop layer out and rebuilt it around one Rust server that serves both the API and frontend. Now I can point Kintara at a NAS folder and open the same library from my desktop, laptop, tablet, or phone.&lt;/p&gt;

&lt;p&gt;The thing I really love about this app is the optional AI features. I added an option to use OpenAI or Gemini, and with so few tokens being spent, it's a fraction of a cent to use most of them, aside from the cover image generation, which is bit more, but makes the library look so much prettier! 😄 &lt;/p&gt;

&lt;p&gt;Anyway, I wanted AI to be a tool inside the library rather than taking the thing over, and I wanted it to be fully optional, so if you're one of those "Ew, AI is in this app" people, you just don't turn it on and it's like it doesn't exist. What the AI can do is summarize documents, suggest metadata and fill in those blank spaces, generate cover images for docs that don't have a cover, search the library for docs, or you can just chat with it about your docs.&lt;/p&gt;

&lt;p&gt;Find is a pretty great AI feature I think. Instead of letting the model vaguely tell you that something appears "somewhere in the document," Kintara asks for actual passages with page numbers, verifies the quote against extracted page text on the server, then verifies it again against the rendered PDF. If the quote cannot actually be located, it gets dropped.&lt;/p&gt;

&lt;p&gt;But I think the thing I’ve ended up appreciating most is the AI metadata suggestions. A lot of my library consists of documents with incomplete, inconsistent, or completely useless embedded metadata, and fixing that manually gets old super fast. Kintara can read the document and suggest a title, author, summary, keywords, DOI, ISBN, and publication year. Then it displays the suggestions and I choose which fields I want, apply them to the form. It sounds like a stupidly small convenience until you’re staring at a library with hundreds of files. Then it’s kinda just amazing.&lt;/p&gt;

&lt;p&gt;At this point Kintara has grown into one of those projects where I started out thinking, "I just want a nice way to organize my documents that's not overcomplicated", and somehow I ended up with filesystem watching, OAuth, multi-user permissions, and threw in image generation cause why not?&lt;/p&gt;

&lt;p&gt;So if you have accumulated an actual ton of pdfs and markdown files, and it sounds interesting to you, try it out and let me know what ya think! It's open source I've published a Docker image too.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/pinkpixel-dev/kintara" rel="noopener noreferrer"&gt;https://github.com/pinkpixel-dev/kintara&lt;/a&gt;&lt;br&gt;
Docker Hub: &lt;a href="https://hub.docker.com/r/pinkpixeldev/kintara" rel="noopener noreferrer"&gt;https://hub.docker.com/r/pinkpixeldev/kintara&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>rust</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Hard Part of AI Coding Isn’t Using AI. It’s Knowing When Not to Trust It.</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 22 Aug 2026 02:25:44 +0000</pubDate>
      <link>https://dev.to/sizzlebop/the-hard-part-of-ai-coding-isnt-using-ai-its-knowing-when-not-to-trust-it-2mhp</link>
      <guid>https://dev.to/sizzlebop/the-hard-part-of-ai-coding-isnt-using-ai-its-knowing-when-not-to-trust-it-2mhp</guid>
      <description>&lt;p&gt;There are now enough AI coding tools to build an entire workflow out of assistants talking to assistants.&lt;/p&gt;

&lt;p&gt;The problem is no longer access to AI. The problem is deciding where it belongs in the development process without quietly handing over your judgment.&lt;/p&gt;

&lt;p&gt;AI is great at reducing friction. It can trace unfamiliar code, generate tedious boilerplate, explain an API, propose refactors, write tests, hunt through logs, and suggest five possible causes for a bug before you finish your coffee.&lt;/p&gt;

&lt;p&gt;Used well, it can feel like a super fast technical collaborator.&lt;/p&gt;

&lt;p&gt;But speed creates its own trap. A plausible answer can feel finished long before it has actually been verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plausible Is Not the Same as Correct
&lt;/h2&gt;

&lt;p&gt;AI coding tools are remarkably good at producing answers that look right.&lt;/p&gt;

&lt;p&gt;The code is clean. The explanation sounds confident. The function names make sense. There may even be comments describing exactly why the solution works.&lt;/p&gt;

&lt;p&gt;And sometimes it does work.&lt;/p&gt;

&lt;p&gt;Other times, the model invents an API method, uses an outdated configuration format, misunderstands a library version, ignores an edge case, weakens a security check, or fixes a symptom while leaving the real bug untouched.&lt;/p&gt;

&lt;p&gt;The most dangerous failures are not always the ones that immediately crash the application. Those are usually easy to catch.&lt;/p&gt;

&lt;p&gt;The more dangerous failures are the ones that appear to work.&lt;/p&gt;

&lt;p&gt;An authentication change might allow valid users to sign in while accidentally bypassing an authorization check. A database migration might succeed against an empty development database but fail on real production data. A generated test might pass because it reproduces the same incorrect assumption as the implementation.&lt;/p&gt;

&lt;p&gt;The app starts. The tests pass. The agent announces success.&lt;/p&gt;

&lt;p&gt;That still does not mean the work is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Due Diligence Is Part of the Workflow
&lt;/h2&gt;

&lt;p&gt;If an agent says a library supports something, check the documentation.&lt;/p&gt;

&lt;p&gt;If it changes authentication code, inspect the security implications.&lt;/p&gt;

&lt;p&gt;If it writes a migration, read the SQL.&lt;/p&gt;

&lt;p&gt;Run the tests. Check the diff. Understand the dependencies it added. Ask why the proposed fix works instead of just checking whether the application starts.&lt;/p&gt;

&lt;p&gt;This does not mean treating every AI-generated line as suspicious. Generated code deserves the same scrutiny as code submitted by anyone else, and occasionally more, because the model cannot be held responsible for the result.&lt;/p&gt;

&lt;p&gt;A human teammate can explain the assumptions behind a decision, remember a conversation that changed the requirements, and recognize when a technically valid implementation would create a terrible user experience.&lt;/p&gt;

&lt;p&gt;A model can explain its output too, but that explanation is generated after the fact. It may be useful, but it is not proof that the model started from the correct assumptions.&lt;/p&gt;

&lt;p&gt;Verification should also match the risk of the change. A developer does not need to conduct a security audit because an agent changed the padding on a button. Changes involving authentication, authorization, payments, destructive operations, personal data, infrastructure, database schemas, or production configuration deserve substantially more attention.&lt;/p&gt;

&lt;p&gt;The higher the cost of being wrong, the less appropriate it is to accept “looks good” as evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing Tests Can Create False Confidence
&lt;/h2&gt;

&lt;p&gt;Tests are among the best tools available for verifying AI-generated work, but even they require judgment.&lt;/p&gt;

&lt;p&gt;An agent can write tests that confirm the implementation behaves exactly as the agent expects. That is useful, but it does not prove that its expectation matches the product requirement.&lt;/p&gt;

&lt;p&gt;If the same model writes the implementation and the tests, both may share the same misunderstanding.&lt;/p&gt;

&lt;p&gt;Suppose a requirement says that only account owners can delete a project. An agent might interpret “account owner” as any authenticated user associated with the account. It can then write the authorization logic and a complete test suite around that interpretation. Every test passes, but the implementation is still wrong.&lt;/p&gt;

&lt;p&gt;Good verification asks more than whether the generated tests are green:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do the tests reflect the actual requirements?&lt;/li&gt;
&lt;li&gt;Are failure paths covered?&lt;/li&gt;
&lt;li&gt;Are permissions tested from the perspective of different user roles?&lt;/li&gt;
&lt;li&gt;Are boundary conditions included?&lt;/li&gt;
&lt;li&gt;Would the tests fail if the implementation were subtly weakened?&lt;/li&gt;
&lt;li&gt;Were existing tests changed or removed simply to make the implementation pass?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tests are evidence, not absolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technically Correct Can Still Be Wrong
&lt;/h2&gt;

&lt;p&gt;Not every failure is a bug.&lt;/p&gt;

&lt;p&gt;Sometimes an AI-generated solution is technically valid but wrong for the project.&lt;/p&gt;

&lt;p&gt;It may introduce a state management library into an application that did not need one. It may replace a straightforward function with an elaborate abstraction. It may use a dependency that is incompatible with the deployment environment. It may solve a small performance problem by making the code much harder to understand.&lt;/p&gt;

&lt;p&gt;Models are generally inclined to produce something rather than conclude that nothing needs to change. If you ask for a refactor, you will probably receive one, even when the original code was perfectly fine.&lt;/p&gt;

&lt;p&gt;Good engineering is not about choosing the most sophisticated solution available. It is about choosing a solution whose complexity is justified by the problem.&lt;/p&gt;

&lt;p&gt;AI can propose the options. It cannot own the tradeoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Tools Requires Judgment Too
&lt;/h2&gt;

&lt;p&gt;The same principle applies when choosing among AI tools.&lt;/p&gt;

&lt;p&gt;Developers do not need to chase every coding assistant that launches. Every week seems to bring another agent with a cinematic demo, an impressive benchmark, and a promise to transform software development forever.&lt;/p&gt;

&lt;p&gt;Some of those tools are genuinely useful. Some are excellent in one context and mediocre in another. Some will disappear six months after developers have reorganized their workflows around them.&lt;/p&gt;

&lt;p&gt;One model may be great at exploring a large codebase but unnecessarily verbose when implementing a small change. Another may generate polished interfaces while struggling to diagnose backend behavior. A specialized review agent may notice issues the implementation agent missed, but it may also produce a pile of low-value warnings.&lt;/p&gt;

&lt;p&gt;The best workflow may involve several tools: one for exploration, another for implementation, and another for review. Or it may simply involve one tool used deliberately.&lt;/p&gt;

&lt;p&gt;The goal is not to assemble the largest AI stack possible. It is to remove enough friction that more attention can go toward decisions that require engineering judgment.&lt;/p&gt;

&lt;p&gt;Adding another agent is not automatically an improvement. Sometimes it only adds another layer of output for a human to verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowing What to Delegate Is Becoming a Core Skill
&lt;/h2&gt;

&lt;p&gt;A subtle skill is developing alongside AI-assisted programming: knowing what to delegate.&lt;/p&gt;

&lt;p&gt;Some tasks are perfect candidates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boilerplate?&lt;/strong&gt; Delegate aggressively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exploring an unfamiliar codebase?&lt;/strong&gt; AI is great at identifying likely entry points and tracing relationships between files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generating alternative implementations?&lt;/strong&gt; Useful. Seeing multiple approaches can expose tradeoffs you might not have considered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing the first version of repetitive tests?&lt;/strong&gt; Absolutely, as long as someone verifies that the tests represent the intended behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turning an error message into a list of hypotheses?&lt;/strong&gt; Great. That can accelerate debugging considerably.&lt;/p&gt;

&lt;p&gt;Other decisions should remain firmly under human ownership.&lt;/p&gt;

&lt;p&gt;Is a security assumption acceptable?&lt;/p&gt;

&lt;p&gt;Does the architecture make sense for the expected scale and lifetime of the project?&lt;/p&gt;

&lt;p&gt;Does the implementation solve the user’s actual problem?&lt;/p&gt;

&lt;p&gt;Is collecting this data necessary?&lt;/p&gt;

&lt;p&gt;Is the new dependency worth maintaining?&lt;/p&gt;

&lt;p&gt;What happens if this operation fails halfway through?&lt;/p&gt;

&lt;p&gt;Does the code satisfy the requirement, or merely the prompt given to the agent?&lt;/p&gt;

&lt;p&gt;AI can contribute information to these decisions, but the responsibility still belongs to the developer.&lt;/p&gt;

&lt;p&gt;A useful rule is to delegate execution more freely than judgment. Let the model perform mechanical work, gather context, generate possibilities, and handle repetition. Be more cautious about delegating decisions involving intent, risk, tradeoffs, or consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Context Is Not Always the Right Context
&lt;/h2&gt;

&lt;p&gt;AI tools are getting better at gathering context automatically, but no coding agent has perfect access to everything that matters.&lt;/p&gt;

&lt;p&gt;It may see the repository but not the conversation where a feature was deliberately excluded. It may read the issue but not understand the support burden created by a particular implementation. It may know what the code does without knowing why the team chose to do it that way.&lt;/p&gt;

&lt;p&gt;Even large context windows do not solve this completely. More context is not the same as the right context.&lt;/p&gt;

&lt;p&gt;Developers still need to identify and communicate the constraints that matter. That includes the technical environment, product requirements, deployment limitations, compatibility expectations, security boundaries, and anything that must not change.&lt;/p&gt;

&lt;p&gt;When an AI tool produces a poor result, the model is not always incapable. Sometimes it was given an underspecified task and filled the gaps with assumptions.&lt;/p&gt;

&lt;p&gt;Those assumptions may be perfectly reasonable.&lt;/p&gt;

&lt;p&gt;They may also be completely wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the Change, Not the Confidence
&lt;/h2&gt;

&lt;p&gt;AI agents frequently summarize their work with reassuring declarations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“The issue has been resolved.”&lt;/li&gt;
&lt;li&gt;“All tests are passing.”&lt;/li&gt;
&lt;li&gt;“The implementation is production-ready.”&lt;/li&gt;
&lt;li&gt;“This approach is secure and scalable.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat those statements as reports to verify, not conclusions to inherit.&lt;/p&gt;

&lt;p&gt;Review the actual diff.&lt;/p&gt;

&lt;p&gt;Check which files changed. Look for unrelated modifications. Confirm that existing behavior was not removed. Inspect configuration changes. Check whether tests were weakened. Search for hardcoded values, silent error handling, broad permissions, unnecessary dependencies, and placeholder logic disguised as a complete implementation.&lt;/p&gt;

&lt;p&gt;The confidence of the summary has no relationship to the correctness of the code.&lt;/p&gt;

&lt;p&gt;A beautifully explained mistake is still a mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Developer’s Role Is Changing, Not Disappearing
&lt;/h2&gt;

&lt;p&gt;AI-assisted coding does not make developers irrelevant. It changes where their attention is most valuable.&lt;/p&gt;

&lt;p&gt;Less time may be spent typing predictable code from scratch. More time may be spent defining behavior, supplying context, evaluating alternatives, reviewing changes, designing verification, and deciding what should be built in the first place.&lt;/p&gt;

&lt;p&gt;That is not a lesser form of development.&lt;/p&gt;

&lt;p&gt;Typing code has never been the entire job. The real work has always included understanding systems, identifying constraints, managing tradeoffs, predicting failure modes, and taking responsibility for what reaches users.&lt;/p&gt;

&lt;p&gt;AI can accelerate the production of code. It cannot take responsibility for the consequences of deploying it.&lt;/p&gt;

&lt;p&gt;That responsibility remains stubbornly human.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faster Coding Needs Better Brakes
&lt;/h2&gt;

&lt;p&gt;AI-assisted coding works best when the model handles mechanical effort while the human maintains ownership of intent, verification, and consequences.&lt;/p&gt;

&lt;p&gt;That does not require hovering nervously over every generated semicolon. It requires calibrating trust based on the task.&lt;/p&gt;

&lt;p&gt;Use AI freely where mistakes are cheap and obvious. Increase scrutiny where errors can remain hidden or cause real damage. Use tests, linters, type checking, documentation, code review, and isolated environments as parts of a verification system, not ceremonial steps after generation.&lt;/p&gt;

&lt;p&gt;Most importantly, stay capable of disagreeing with the tool.&lt;/p&gt;

&lt;p&gt;The interesting question is no longer whether developers should use AI. That debate has largely been overtaken by reality. AI is already embedded in editors, terminals, documentation tools, code review systems, and development platforms.&lt;/p&gt;

&lt;p&gt;The more important question is whether we can become good enough at using it that faster coding does not become faster mistakes.&lt;/p&gt;

&lt;p&gt;AI can help us move quickly.&lt;/p&gt;

&lt;p&gt;Engineering judgment tells us when to slow down.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>coding</category>
      <category>software</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>PawDOS 95: A browser operating system for dogs</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 15 Aug 2026 18:49:19 +0000</pubDate>
      <link>https://dev.to/sizzlebop/pawdos-95-a-browser-operating-system-for-dogs-167m</link>
      <guid>https://dev.to/sizzlebop/pawdos-95-a-browser-operating-system-for-dogs-167m</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-08-13"&gt;Weekend Challenge: Dog Days Edition&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My submission is PawDOS 95, a fake desktop operating system built for dogs. Built to boot like a 1995 PC, it tracks squirrels, monitors the mailman, manages treats, and tries to explain humans with a translator.&lt;/p&gt;

&lt;p&gt;The main feature is DogPrompt. It looks like a DOS terminal, but it accepts normal sentences. Gemini does more than answer in character. It can request approved actions that affect the desktop itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;PawDOS has a working window manager, start menu, taskbar, dialogs, notifications, synthesized sounds, random desktop events, and 9 applications.&lt;/p&gt;

&lt;p&gt;DogPrompt combines about 25 instant local commands with Gemini-powered responses. Other apps include Squirrel Tracker 2.0, HUMAN.EXE, Treat Manager, Mailman Monitor, My Kennel, My Documents, Notepad, and Settings.&lt;/p&gt;

&lt;p&gt;The app also works on mobile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Try PawDOS at &lt;a href="https://pawdos.dog" rel="noopener noreferrer"&gt;pawdos.dog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open DogPrompt and type something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;emergency i see a squirrel outside
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemini can respond, open Squirrel Tracker, add a radar target, show a warning, and trigger the bark driver. The terminal finishes typing first, then PawDOS carries out the approved actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The source is available at &lt;a href="https://github.com/pinkpixel-dev/pawdos" rel="noopener noreferrer"&gt;github.com/pinkpixel-dev/pawdos&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;PawDOS uses React 19, TypeScript, Vite, and Zustand. A single Cloudflare Worker serves the app and handles the Gemini requests.&lt;/p&gt;

&lt;p&gt;DogPrompt uses Gemini function calling. The Worker gives Gemini eight possible actions, including opening an app, spawning a squirrel, barking, changing the dog's mood, and entering nap mode. Every returned action passes through an allowlist validator before the browser can run it. Invalid names, values, and arguments are dropped.&lt;/p&gt;

&lt;p&gt;HUMAN.EXE, squirrel dossiers, and hardware diagnostics use structured responses. The Worker validates those fields before React renders them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;I am submitting PawDOS for &lt;strong&gt;Use of Google AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Gemini is connected to the operating system instead of sitting in a separate chat box. Function calls become validated desktop actions, structured responses power three utility panels, and conversation chaining gives DogPrompt short-term context.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>react</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Kitty Cafe, a whole bakery drawn in CSS 🐱🍰</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Mon, 10 Aug 2026 04:28:36 +0000</pubDate>
      <link>https://dev.to/sizzlebop/kitty-cafe-a-whole-bakery-drawn-in-css-3fo4</link>
      <guid>https://dev.to/sizzlebop/kitty-cafe-a-whole-bakery-drawn-in-css-3fo4</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, CSS Art&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;Comfort food, for me, is not a big plate of anything. It is the small stuff.&lt;br&gt;
The pastry you eat standing up at the counter because you could not wait to sit&lt;br&gt;
down. So I wanted to draw a bakery.&lt;/p&gt;

&lt;p&gt;Then I thought: what if every single thing in the case was a cat.&lt;/p&gt;

&lt;p&gt;That turned out to be the right call, because it gave me nine little characters&lt;br&gt;
instead of nine pastries, and characters are way more fun to draw. There is a&lt;br&gt;
macaron with ears, a matcha cupcake, a taiyaki, a paw print cookie, a chocolate&lt;br&gt;
donut with far too many sprinkles, a cake slice, a cake pop, and a caramel&lt;br&gt;
pudding cup. Up on the shelf there is a whole kitty cake you have to order 48&lt;br&gt;
hours in advance.&lt;/p&gt;

&lt;p&gt;I also did not want it to be a single object floating on a background. I wanted&lt;br&gt;
a room you could stand in. So there is a chalkboard of drinks, jars on a shelf,&lt;br&gt;
a pendant lamp, a wooden counter, and a shop cat asleep next to somebody's&lt;br&gt;
coffee that is definitely going cold.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/editor/sizzlebop/embed/019fe9e0-6ec8-7498-9532-0f00125ff071?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;No images. No SVG. No canvas. No JavaScript. Every whisker is a &lt;code&gt;div&lt;/code&gt; with a&lt;br&gt;
&lt;code&gt;border-radius&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Three things worth poking at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hover, tap or tab to any treat.&lt;/strong&gt; It lifts out of the row with a price tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click the sleeping cat.&lt;/strong&gt; She opens her eyes, perks her ears, and purrs for
a few seconds before dozing off again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There is a ball of yarn on the floor.&lt;/strong&gt; Click it to pick it up, click her to
give it to her, and see what happens.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;
&lt;h3&gt;
  
  
  One coordinate system, zero breakpoints
&lt;/h3&gt;

&lt;p&gt;The first real decision was how to make the art scale. CSS art has hundreds of&lt;br&gt;
coordinates in it, and maintaining breakpoint variants of all of them sounded&lt;br&gt;
miserable.&lt;/p&gt;

&lt;p&gt;So the whole scene is a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.scene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;container-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;aspect-ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything inside is measured in &lt;code&gt;cqw&lt;/code&gt; (1% of the scene's width) or in&lt;br&gt;
percentages. Because the height comes from the width, both axes scale together.&lt;br&gt;
The picture looks identical at 320px and at 1920px, just smaller, and there is&lt;br&gt;
not one media query anywhere in the art.&lt;/p&gt;

&lt;p&gt;One gotcha I hit: &lt;code&gt;cqw&lt;/code&gt; inside &lt;code&gt;.scene&lt;/code&gt;'s &lt;em&gt;own&lt;/em&gt; rules resolves against some&lt;br&gt;
outer container, not itself. So &lt;code&gt;.scene&lt;/code&gt; uses pixels for its own properties and&lt;br&gt;
&lt;code&gt;cqw&lt;/code&gt; only ever shows up on descendants.&lt;/p&gt;

&lt;p&gt;The scene also sizes itself to your window. Since height follows width at a&lt;br&gt;
fixed ratio, capping the width at &lt;code&gt;(100dvh - 7rem) * 1.6&lt;/code&gt; is the same thing as&lt;br&gt;
capping the height at the viewport:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;min&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="nt"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;46&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="nt"&gt;dvh&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;7&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;6&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;max-height&lt;/code&gt; does not work here, by the way. It fights &lt;code&gt;aspect-ratio&lt;/code&gt;, because&lt;br&gt;
the width has already resolved by the time it applies.&lt;/p&gt;
&lt;h3&gt;
  
  
  Eight cats pretending to be pastry
&lt;/h3&gt;

&lt;p&gt;Every treat shares one set of parts: an ear, an eye, a mouth, blush, whiskers.&lt;br&gt;
Each treat just re-tunes them with custom properties instead of redefining the&lt;br&gt;
shapes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.t-macaron&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--ear-c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--pink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--ear-dip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9&lt;/span&gt;&lt;span class="n"&gt;cqw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--face-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;36%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--eye-s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.88&lt;/span&gt;&lt;span class="n"&gt;cqw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--ear-dip&lt;/code&gt; came out of an actual problem. Ears sit on top of whatever box they&lt;br&gt;
live in, which is fine on a flat head. On a circle or a dome they end up over&lt;br&gt;
the part of the box the shape has already curved away from, so they float, and&lt;br&gt;
the cat looks like it is wearing a bad costume. Round heads just need the ears&lt;br&gt;
tucked deeper.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--eye-c&lt;/code&gt; exists for a similar reason. The shared eye used dark ink, and on the&lt;br&gt;
chocolate donut the whole face simply vanished. It read as a plain donut with&lt;br&gt;
sprinkles. Cream eyes fixed it, and then I had to recolour the cream sprinkles&lt;br&gt;
too, because white dots next to white eyes read as extra eyes.&lt;/p&gt;
&lt;h3&gt;
  
  
  Drips that cannot spill
&lt;/h3&gt;

&lt;p&gt;Icing drips gave me trouble until I found a pattern that makes overflow&lt;br&gt;
impossible by construction. Instead of positioning a drip as its own box, make&lt;br&gt;
it identical to the thing underneath (same offsets, same &lt;code&gt;border-radius&lt;/code&gt;) and&lt;br&gt;
then cut the bottom off with a jagged &lt;code&gt;clip-path&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.t-donut&lt;/span&gt; &lt;span class="nc"&gt;.glaze&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--choco&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;clip-path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;polygon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;92%&lt;/span&gt; &lt;span class="m"&gt;55%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;84%&lt;/span&gt; &lt;span class="m"&gt;66%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;74%&lt;/span&gt; &lt;span class="m"&gt;57%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;64%&lt;/span&gt; &lt;span class="m"&gt;68%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;52%&lt;/span&gt; &lt;span class="m"&gt;57%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt; &lt;span class="m"&gt;68%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30%&lt;/span&gt; &lt;span class="m"&gt;57%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;20%&lt;/span&gt; &lt;span class="m"&gt;66%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt; &lt;span class="m"&gt;55%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;64%&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The drip physically cannot escape the silhouette, because it &lt;em&gt;is&lt;/em&gt; the&lt;br&gt;
silhouette. Same trick works for the whole cake's icing and the caramel on the&lt;br&gt;
pudding cup.&lt;/p&gt;
&lt;h3&gt;
  
  
  Waking the cat without JavaScript
&lt;/h3&gt;

&lt;p&gt;She is a visually hidden checkbox and a label holding her art. Clicking the&lt;br&gt;
label checks the box, which buys click, tap and Space all at once.&lt;/p&gt;

&lt;p&gt;The part I am most pleased with is that she goes back to sleep on her own after&lt;br&gt;
about five seconds, so you can wake her again straight away. That is harder than&lt;br&gt;
it sounds, because &lt;strong&gt;CSS cannot uncheck a checkbox on a timer&lt;/strong&gt;. The animation&lt;br&gt;
itself has to return her to sleep.&lt;/p&gt;

&lt;p&gt;Which creates a trap. Once she is asleep with the box still checked, the next&lt;br&gt;
click only unchecks it, and if both states name the same animation, the computed&lt;br&gt;
&lt;code&gt;animation-name&lt;/code&gt; never changes, so nothing restarts. Half your clicks do&lt;br&gt;
nothing.&lt;/p&gt;

&lt;p&gt;The fix is two keyframe blocks that are byte for byte identical, under different&lt;br&gt;
names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.cat-toggle&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.shopcat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wake-a&lt;/span&gt; &lt;span class="m"&gt;5.4s&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.cat-toggle&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.shopcat&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.cat-toggle&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.shopcat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wake-b&lt;/span&gt; &lt;span class="m"&gt;5.4s&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A changed &lt;code&gt;animation-name&lt;/code&gt; is exactly what restarts a CSS animation. The&lt;br&gt;
unchecked half has to be gated behind &lt;code&gt;:hover&lt;/code&gt; or &lt;code&gt;:focus&lt;/code&gt;, or it matches on&lt;br&gt;
first paint and she wakes up before anyone has touched her.&lt;/p&gt;

&lt;p&gt;Underneath, her whole body runs off one registered custom property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="n"&gt;--awake&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"&amp;lt;number&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;initial-value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registering it is what makes it interpolate smoothly instead of snapping at the&lt;br&gt;
halfway point. Then every part of her is a plain function of that one number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.shopcat&lt;/span&gt; &lt;span class="nc"&gt;.cat-head&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--awake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;-0.7&lt;/span&gt;&lt;span class="n"&gt;cqw&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--awake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;-3deg&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.cat-eye&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--awake&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c"&gt;/* sleeping arc */&lt;/span&gt;
&lt;span class="nc"&gt;.cat-eye&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--awake&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;            &lt;span class="c"&gt;/* open eye */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One timeline for the whole cat, so nothing can drift out of sync.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ball of yarn
&lt;/h3&gt;

&lt;p&gt;CSS has no drag and drop, so this is two checkboxes and three overlapping labels&lt;br&gt;
whose &lt;code&gt;pointer-events&lt;/code&gt; get switched by state. The same pixels mean "pick up",&lt;br&gt;
"give" or "take back" depending on where you are in the sequence.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;#hold-yarn&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;#give-yarn&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;on the floor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;you are carrying it, and the cat becomes a drop target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;she has it, and hearts float up&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The invisible drop zone over the cat is &lt;code&gt;pointer-events: none&lt;/code&gt; until you are&lt;br&gt;
actually carrying something, which is what keeps her normally clickable to wake&lt;br&gt;
and only turns her into a target when it makes sense.&lt;/p&gt;
&lt;h3&gt;
  
  
  The bug that got me
&lt;/h3&gt;

&lt;p&gt;Getting the present to wake her needed a second clock, &lt;code&gt;--gift&lt;/code&gt;, running&lt;br&gt;
alongside &lt;code&gt;--awake&lt;/code&gt; so the two could overlap instead of fighting over one&lt;br&gt;
&lt;code&gt;animation&lt;/code&gt; property. Her poses read whichever is further along:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;--lively&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--awake&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--gift&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I put that on the cat's label and nothing happened. &lt;code&gt;--gift&lt;/code&gt; was provably 1 in&lt;br&gt;
DevTools. The animation was definitely running. She just sat there asleep.&lt;/p&gt;

&lt;p&gt;Here is why: &lt;strong&gt;a custom property is substituted where it is declared, and only&lt;br&gt;
the result inherits.&lt;/strong&gt; &lt;code&gt;--gift&lt;/code&gt; is animated on a child element, so declaring&lt;br&gt;
&lt;code&gt;--lively&lt;/code&gt; on the parent baked in &lt;code&gt;max(&amp;lt;awake&amp;gt;, 0)&lt;/code&gt; for every descendant.&lt;br&gt;
Moving the one declaration down to the element that can see both values fixed&lt;br&gt;
it instantly.&lt;/p&gt;

&lt;p&gt;That one is going in my notes. It looks completely correct and it fails&lt;br&gt;
silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;I did not want "pure CSS" to mean "mouse only", so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every treat is a real &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; with an accessible name that includes its
price. Tags reveal on &lt;code&gt;:hover&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; &lt;code&gt;:focus&lt;/code&gt;, because a tap on a phone
focuses a button but does not trigger &lt;code&gt;:focus-visible&lt;/code&gt;. &lt;code&gt;:focus-visible&lt;/code&gt; is
used separately for the ring, so a mouse click does not leave one behind.&lt;/li&gt;
&lt;li&gt;The cat and the yarn are checkboxes, so they work with click, tap and Space,
and they show a focus ring on the art itself.&lt;/li&gt;
&lt;li&gt;On a phone the scene is small and the in-case price tags are tiny, so the full
menu is repeated as plain text below the art. That list is the accessible
source of truth, and honestly it is the nicer way to read it on mobile anyway.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prefers-reduced-motion: reduce&lt;/code&gt; switches off every animation. Nothing breaks:
the cat becomes a plain two-state toggle, the yarn still moves between floor
and paws, and the hearts still show. They just go straight there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I picked up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@property&lt;/code&gt; is genuinely useful for art, not just for gradients. Animating one
number and deriving a dozen things from it beats keeping a dozen animations in
sync.&lt;/li&gt;
&lt;li&gt;Container queries make CSS art scale properly with basically no effort.&lt;/li&gt;
&lt;li&gt;Checkbox and label state machines go further than I expected. Three states and
conditional hit targets, no script.&lt;/li&gt;
&lt;li&gt;Custom property substitution happens at the declaration site. I will not
forget that again.&lt;/li&gt;
&lt;li&gt;Constraints are good. Not being allowed images meant I had to actually think
about what a taiyaki looks like as geometry, and I like the result more than
if I had just dropped in a PNG.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. Go click the cat! 🐱&lt;/p&gt;

&lt;h2&gt;
  
  
  Built With
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Semantic HTML&lt;/li&gt;
&lt;li&gt;CSS container queries and &lt;code&gt;cqw&lt;/code&gt; units&lt;/li&gt;
&lt;li&gt;CSS custom properties, including registered &lt;code&gt;@property&lt;/code&gt; values&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clip-path&lt;/code&gt;, &lt;code&gt;border-radius&lt;/code&gt; and &lt;code&gt;box-shadow&lt;/code&gt; for every shape&lt;/li&gt;
&lt;li&gt;CSS animations and transitions&lt;/li&gt;
&lt;li&gt;Checkbox and label state machines for all interaction&lt;/li&gt;
&lt;li&gt;No JavaScript&lt;/li&gt;
&lt;li&gt;No images, SVG or canvas&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/pinkpixel-dev/kitty-cafe" rel="noopener noreferrer"&gt;https://github.com/pinkpixel-dev/kitty-cafe&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;Apache 2.0.&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
      <category>codepen</category>
    </item>
    <item>
      <title>Finding bandwidth hogs on Linux</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 25 Jul 2026 22:35:51 +0000</pubDate>
      <link>https://dev.to/sizzlebop/finding-bandwidth-hogs-on-linux-2c92</link>
      <guid>https://dev.to/sizzlebop/finding-bandwidth-hogs-on-linux-2c92</guid>
      <description>&lt;p&gt;When a Linux server starts feeling slow, uploads stall, or network costs climb, &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;htop&lt;/code&gt; can only tell part of the story. They show CPU and memory use, not which process is moving data.&lt;/p&gt;

&lt;p&gt;For that, start with &lt;code&gt;nethogs&lt;/code&gt;, then use &lt;code&gt;iftop&lt;/code&gt; or &lt;code&gt;ss&lt;/code&gt; to confirm what the process is connected to. If you are working in a stripped-down environment, &lt;code&gt;/proc&lt;/code&gt; is still there as a fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the tools
&lt;/h2&gt;

&lt;p&gt;These packages are available on most Linux distributions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ubuntu or Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nethogs iftop &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# RHEL or Rocky Linux, with EPEL enabled&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nethogs iftop &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; nethogs iftop &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;net-tools&lt;/code&gt; is not needed for any command in this guide. &lt;code&gt;ip&lt;/code&gt; and &lt;code&gt;ss&lt;/code&gt; are normally provided by the already-installed &lt;code&gt;iproute2&lt;/code&gt; package. On RHEL-family systems, &lt;code&gt;nethogs&lt;/code&gt; and &lt;code&gt;iftop&lt;/code&gt; may not be enabled in the default repositories, so enable an appropriate repository first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with &lt;code&gt;nethogs&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;nethogs&lt;/code&gt; groups current network traffic by process and shows the PID when it can associate traffic with a local socket. It is the fastest way to find the program responsible for a spike.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nethogs &amp;lt;interface&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;interface&amp;gt;&lt;/code&gt; with the network interface you want to inspect, such as &lt;code&gt;eth0&lt;/code&gt;. Run &lt;code&gt;ip link show&lt;/code&gt; if you need to find its name.&lt;/p&gt;

&lt;p&gt;Use this first. Once you have the PID or process name, you can investigate the connection behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check destinations with &lt;code&gt;iftop&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;iftop&lt;/code&gt; shows bandwidth by pairs of hosts and their remote IP addresses. It does not identify the owning process, but it answers the next question: which hosts are involved?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iftop &lt;span class="nt"&gt;-i&lt;/span&gt; &amp;lt;interface&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;iftop&lt;/code&gt; beside &lt;code&gt;nethogs&lt;/code&gt; when possible. One shows the process; the other shows busy host pairs and the direction of the data flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify sockets with &lt;code&gt;ss&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When you need to confirm a process's active TCP connections, use &lt;code&gt;ss&lt;/code&gt;. It maps sockets to process names and PIDs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ss &lt;span class="nt"&gt;-tnp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-t&lt;/code&gt; shows TCP connections.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-n&lt;/code&gt; keeps addresses as raw IPs instead of looking up hostnames.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p&lt;/code&gt; includes the process name and PID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful when &lt;code&gt;nethogs&lt;/code&gt; points to a PID and you need to inspect its socket connections or send queue (&lt;code&gt;Send-Q&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;/proc&lt;/code&gt; on minimal systems
&lt;/h2&gt;

&lt;p&gt;Containers, rescue shells, and stripped-down VMs may not have the monitoring tools installed. In that case, inspect the process's open file descriptors through &lt;code&gt;/proc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /proc/&amp;lt;pid&amp;gt;/fd | &lt;span class="nb"&gt;grep &lt;/span&gt;socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entries such as &lt;code&gt;socket:[123456]&lt;/code&gt; contain a socket inode number. Look for that inode in &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/net/tcp&lt;/code&gt; for IPv4 TCP or &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/net/tcp6&lt;/code&gt; for IPv6 TCP. Use &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/net/udp*&lt;/code&gt; for UDP sockets. These paths inspect the target process's network namespace, which matters when it runs in a container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it shows&lt;/th&gt;
&lt;th&gt;Use it for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nethogs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bandwidth per program&lt;/td&gt;
&lt;td&gt;Finding the process using traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iftop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bandwidth by host pair&lt;/td&gt;
&lt;td&gt;Identifying hosts and traffic flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tnp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Established TCP sockets&lt;/td&gt;
&lt;td&gt;Mapping connections to PIDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/proc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Socket inodes and protocol tables&lt;/td&gt;
&lt;td&gt;Diagnosing minimal systems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A practical workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;nethogs&lt;/code&gt; and identify the PID using the bandwidth.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ss -tnp&lt;/code&gt; to see that process's TCP connections; use &lt;code&gt;iftop&lt;/code&gt; to identify busy host pairs on the same interface.&lt;/li&gt;
&lt;li&gt;Confirm whether the traffic is expected before stopping anything.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Routine jobs such as &lt;code&gt;cron&lt;/code&gt; tasks and &lt;code&gt;rsync&lt;/code&gt; backups often explain a network spike. A backup that overlaps with another job can look suspicious until you match the PID to the process. Confirm the process identity before killing it.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Gem: A Desktop Scanner That Finds GitHub Repos Before They're Trending</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 25 Jul 2026 00:26:56 +0000</pubDate>
      <link>https://dev.to/sizzlebop/gem-a-desktop-scanner-that-finds-github-repos-before-theyre-trending-4a0e</link>
      <guid>https://dev.to/sizzlebop/gem-a-desktop-scanner-that-finds-github-repos-before-theyre-trending-4a0e</guid>
      <description>&lt;p&gt;Gem is a desktop app for developers tired of GitHub’s trending page only ever showing what’s already popular. Instead of scrolling through the same top-100 lists, you set up “recipes”, basically saved searches combining stars range, language, topic, recency, and excluded keywords, and Gem runs them on a rotation, surfacing recently active repos that haven’t blown up yet, or might just always remain undiscovered otherwise.&lt;/p&gt;

&lt;p&gt;Save a repo and its topics score higher next time. Skip one and they score lower. You can also just set the weights manually per topic, language, or keyword if you’d rather tune it by hand than let the app learn from your behavior. Repos flow through a simple triage pipeline too: everything lands in an Inbox, then you sort into Gems, Tools to Try, or Skipped, with a Discovery Guide of prebuilt recipes (AI/ML, self-hosted, mobile, games, etc.) for when you don’t want to write GitHub search syntax from scratch.&lt;/p&gt;

&lt;p&gt;It’s built on Tauri v2 with a Rust backend (reqwest for the GitHub API, sqlx for SQLite, tokio running the scan loop) and a React/TypeScript frontend. Everything, including your recipes and scoring weights, is in a local SQLite file. No account, no server, no syncing. If you’ve ever found a great repo by accident and wondered how many more you’ve missed, give it a try and let me know what you think.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pinkpixel-dev/gem" rel="noopener noreferrer"&gt;https://github.com/pinkpixel-dev/gem&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>software</category>
    </item>
    <item>
      <title>I Built Quota to Track my AI Tools</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Thu, 16 Jul 2026 04:19:08 +0000</pubDate>
      <link>https://dev.to/sizzlebop/i-built-quota-to-track-my-ai-tools-5bb3</link>
      <guid>https://dev.to/sizzlebop/i-built-quota-to-track-my-ai-tools-5bb3</guid>
      <description>&lt;p&gt;I wanted an easy way to track usage across multiple subscriptions and multiple accounts, so I created Quota.&lt;/p&gt;

&lt;p&gt;Quota is a desktop app and VS Code extension that gives you one place to track your AI coding-tool usage across accounts and providers. Connect each service once, then see your remaining usage, pin your most used accounts, switch between layouts, and export account summaries as JSON.&lt;/p&gt;

&lt;p&gt;The VS Code extension also adds a status-bar indicator and webview panel. In the settings you can decide which providers appear in the status bar. It’s available on both the VS Code Marketplace and Open VSX, so it can work in compatible editors like Antigravity and Kiro as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec5p3wue6hb3lsl3wqxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec5p3wue6hb3lsl3wqxy.png" alt="VSCode Dashboard" width="799" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to share because it's been really useful for me, especially being able to easily view my usage in my VSCode status bar, and hopefully others find it useful too. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5fzs3z8cvfqojypn214.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5fzs3z8cvfqojypn214.png" alt="VSCode Status Bar" width="795" height="23"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if you use Copilot, Codex, Claude Code, Cursor, Kiro, Antigravity, or some combination, please give it a try and let me know what you think!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pinkpixel-dev/quota" rel="noopener noreferrer"&gt;Desktop App&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=pinkpixel.quota-ai-usage-tracker" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://open-vsx.org/extension/pinkpixel/quota-ai-usage-tracker" rel="noopener noreferrer"&gt;Open VSX&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>I built Stitchlet because my crochet patterns were scattered across a dozen apps and a stack of paper</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 11 Jul 2026 06:53:57 +0000</pubDate>
      <link>https://dev.to/sizzlebop/i-built-stitchlet-because-my-crochet-patterns-were-scattered-across-a-dozen-apps-and-a-stack-of-2765</link>
      <guid>https://dev.to/sizzlebop/i-built-stitchlet-because-my-crochet-patterns-were-scattered-across-a-dozen-apps-and-a-stack-of-2765</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlc5b30pf1yoky650qep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlc5b30pf1yoky650qep.png" alt="Stitchlet Dashboard" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;Stitchlet&lt;/strong&gt;, a private, self-hosted crochet project companion for keeping patterns, photos, materials, notes, and progress tracking together in one place.&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749206%2Fscreenshot2_eq9788.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749206%2Fscreenshot2_eq9788.png" alt="Stitchlet Project Details" width="800" height="422"&gt;&lt;/a&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot3_l2helt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot3_l2helt.png" alt="Stitchlet Edit Project Page" width="800" height="422"&gt;&lt;/a&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot4_wwyqvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot4_wwyqvi.png" alt="Stitchlet PDF View" width="800" height="422"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I've been crocheting and designing patterns for years, so this project came directly from problems I've run into myself. Crochet projects have a way of scattering across printed patterns, PDF files, handwritten notes, photos, row counters, and random scraps of paper. I wanted one calm, organized place for everything.&lt;/p&gt;

&lt;p&gt;With Stitchlet, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create and organize crochet projects&lt;/li&gt;
&lt;li&gt;Upload and view pattern PDFs inside the app&lt;/li&gt;
&lt;li&gt;Save project and progress photos&lt;/li&gt;
&lt;li&gt;Track rows or rounds with multiple counters&lt;/li&gt;
&lt;li&gt;Record yarn, hook size, colors, dimensions, and other materials&lt;/li&gt;
&lt;li&gt;Add custom sections for assembly notes, substitutions, or anything else&lt;/li&gt;
&lt;li&gt;Search, sort, and filter projects by status&lt;/li&gt;
&lt;li&gt;Back up and restore the entire project library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stitchlet runs on your own computer, NAS, or home server, so your project files and notes stay under your control instead of getting locked into another subscription service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Stitchlet is self-hosted, so there's no shared public demo instance to click into. See the video below.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/lVJtfcqPZiA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can also find setup instructions and more project details in the GitHub repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pinkpixel-dev" rel="noopener noreferrer"&gt;
        pinkpixel-dev
      &lt;/a&gt; / &lt;a href="https://github.com/pinkpixel-dev/stitchlet" rel="noopener noreferrer"&gt;
        stitchlet
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A self-hosted crochet companion for tracking projects, stitch counters, pattern PDFs, and progress photos locally on your own hardware. 🧶
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/pinkpixel-dev/stitchlet/./public/logo.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpinkpixel-dev%2Fstitchlet%2FHEAD%2F.%2Fpublic%2Flogo.png" alt="Stitchlet logo" width="300"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Stitchlet&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Stitchlet is a self-hosted crochet project tracker. It keeps your pattern PDFs, progress photos, stitch/row counters, and material notes together in one place, on hardware you control — a mini-PC, a NAS, a home server, or just your own machine. No account, no subscription, no third-party server holding your project library.&lt;/p&gt;

&lt;p&gt;I made this because my own crochet projects were scattered across printed patterns, PDFs, photos in random folders, and notes on paper. Stitchlet is basically the tool I wanted for keeping all of that in one spot while I'm actually sitting there with yarn in my hands.&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0729b507b0a7e830a231ad2c03eb41ef91b89d7e0b41f34eb1abe154f5635580/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74315f7977346566322e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/0729b507b0a7e830a231ad2c03eb41ef91b89d7e0b41f34eb1abe154f5635580/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74315f7977346566322e706e67" alt="Stitchlet Dashboard" width="48%"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/28de8b082fef40dd2592bdb5d9b59e558aa89515ac535dffafccf6f9ee62ac42/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230362f73637265656e73686f74325f6571393738382e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/28de8b082fef40dd2592bdb5d9b59e558aa89515ac535dffafccf6f9ee62ac42/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230362f73637265656e73686f74325f6571393738382e706e67" alt="Stitchlet Project Details" width="48%"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3dae06161c464de43e4d180a9befcda933d5cb6d40ce73800c085ae6c7992743/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74335f6c3268656c742e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/3dae06161c464de43e4d180a9befcda933d5cb6d40ce73800c085ae6c7992743/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74335f6c3268656c742e706e67" alt="Stitchlet Edit Project Page" width="48%"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8298615812449f451ba33a9ebe47b7a3aa14b668cf18da92e424e7303db17ef5/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74345f7777797176692e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/8298615812449f451ba33a9ebe47b7a3aa14b668cf18da92e424e7303db17ef5/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74345f7777797176692e706e67" alt="Stitchlet PDF View" width="48%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard with search, status filtering (Active, Paused, Finished, Frogged), sorting by updated date/title/status, and grid or list view&lt;/li&gt;
&lt;li&gt;Upload pattern PDFs and read every page in a fit-to-width view, with counters beside the PDF on desktop or in a small dock on phones&lt;/li&gt;
&lt;li&gt;Upload a project photo, with a replace/remove…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pinkpixel-dev/stitchlet" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Stitchlet is open source, licensed under Apache 2.0.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Stitchlet is a full-stack TypeScript application built as a single package.&lt;/p&gt;

&lt;p&gt;The frontend uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;CSS variables for light and dark themes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The backend uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hono&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;li&gt;Drizzle ORM&lt;/li&gt;
&lt;li&gt;Zod for shared validation and schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The React frontend uses a Hono API that manages projects, counters, custom sections, photos, PDFs, and backup operations.&lt;/p&gt;

&lt;p&gt;Project data is stored in SQLite, while uploaded PDFs and images are stored in local folders on the host system. Files are served through application routes rather than a public static directory, which keeps them out of directory listings, though access control itself is left to the network layer (see the README for details on running this behind Tailscale or a reverse proxy).&lt;/p&gt;

&lt;p&gt;One decision that mattered a lot was keeping storage local. Stitchlet runs through Docker Compose with mounted volumes for the database, uploads, and backups, so you can update or replace the container without losing your crochet library.&lt;/p&gt;

&lt;p&gt;There's also a backup and restore system that packages the SQLite database and uploaded media into a ZIP archive. You can export that from the settings page and restore it later if you move the app to another machine.&lt;/p&gt;

&lt;p&gt;I also made Stitchlet installable as a PWA, so on a phone or tablet it feels more like a dedicated crochet companion, and it’s handy when you're using the row counters mid-project.&lt;/p&gt;

&lt;p&gt;The UI is built around how crochet projects and the things crocheters actually use. The counters have large tap targets, project details stay easy to scan, and the dashboard has search, sorting, status filters, grid and list views, and mobile-friendly layouts.&lt;/p&gt;

&lt;p&gt;For deployment, there's a Docker image and a Docker Compose setup. You can run it locally, host it on a NAS, or access it privately through something like Tailscale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;This was a good reminder that a project doesn't need a huge audience to be worth building. It just needs to solve a real problem for someone, even if that someone is just me.&lt;/p&gt;

&lt;p&gt;Because I actually crochet, I already knew which details mattered, at least to me: fast counters, easy access to PDFs, project photos, a place to list all of the materials I need, a place to write down random notes, and a way to recover everything if needed.&lt;/p&gt;

&lt;p&gt;Building around my own workflow made the technical decisions easier, too... like skipping features that sound impressive on paper but wouldn't actually get used while sitting on the couch with yarn in your hands.&lt;/p&gt;




&lt;p&gt;Since crocheting is something I do all the time and I will personally really enjoy using this app, this was a really fun challenge for me, and I'm happy to be able to share it and post my first submission!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
