{"slug": "paw-order-upload-your-dog-and-defend-them-against-evidence-generated-from-their", "title": "Paw & Order: upload your dog, and defend them against evidence generated from their own photo", "summary": "A developer built Paw & Order, a browser game where players upload a photo of their dog and defend it against AI-generated criminal charges. The game uses Gemini models to generate the entire case—crime, evidence, witnesses, and branching dialogue—before the trial begins, after which no models are called and the game runs deterministically. The project was created for the DEV Weekend Challenge: Dog Days Edition and is live at paw-order.pages.dev.", "body_md": "*This is a submission for the DEV Weekend Challenge: Dog Days Edition.*\n\nUpload a photo of your dog. An AI accuses them of a crime. You're their defense attorney.\n\n**Paw & Order** is a browser game where your own dog is the defendant.\n\nYou upload one photo and a few seconds later your dog has been arrested:\n\n```\nThe People vs. Biscuit\nDocket #PAW-042\nDEFENDANT: Biscuit\nCHARGE: Grand Theft Sausage\nCOUNSEL: You\nSTATUS: Extremely suspicious\n```\n\nThen the trial starts. The prosecutor puts a question to you, you pick a response, and the case branches from there. Three exhibits go into evidence: generated images of *your* dog, at the scene, with the frosting still on their muzzle. Two witnesses give statements, and at least one of them is usually lying.\n\nA trial runs a few minutes. At the end you get one of four verdicts:\n\nPlus a scoreline that isn't the same thing as winning:\n\n```\nVERDICT\n\nNOT GUILTY\n\nBiscuit is free to commit additional crimes.\n\nDefense Performance: 94/100\n```\n\nYou can lose the case and still score 96. You can win it badly.\n\nEvery case has a hidden truth, generated before the trial begins. Sometimes the dog really did it, sometimes they're innocent, sometimes the evidence just lies. The client never sees any of it, so you're not hunting for a correct answer. You're building the strongest defense the facts allow.\n\nChoices decide the outcome. Replay the same case, answer differently, and the verdict and the score change with you.\n\n**Live:** [https://paw-order.pages.dev](https://paw-order.pages.dev)\n\nBring a dog photo, or don't. The home page has a public docket of cases other players entered into the public record, and you can play any of them without uploading anything.\n\nJustice for every good boy.\n\nUpload a photo of your dog. AI generates a fictional criminal case around that dog. You defend them in court.\n\n**Live:** [https://paw-order.pages.dev](https://paw-order.pages.dev)\n\nBuilt for the [DEV Weekend Challenge: Dog Days\nEdition](https://dev.to/challenges/weekend-2026-08-13), start to finish inside\nthe challenge window. Everything up to the last commit before the deadline of\n2026-08-17 06:59 UTC is the submitted entry; anything after that timestamp is\npost-deadline work.\n\nSubmission post: `SUBMISSION.md`\n\n.\n\n```\nnvm use\nnpm ci\ncp packages/api/.env.example packages/api/.env   # GEMINI_API_KEY is the only one needed\nnpm run dev:api    # :4270\nnpm run dev:app    # :5173, proxies /api\n```\n\n`npm run all`\n\n(format, lint, typecheck, test) must be green before a commit.\n\nOne rule sits under everything else:\n\nThe AI creates the world. The engine runs the game.\n\nNothing calls a model during the trial. Generation runs once, up front, and produces the whole case: crime, hidden truth, evidence, witnesses, the branching question tree, and the verdict thresholds. After that the game is deterministic code reading structured data.\n\nSix model calls per case, all Gemini:\n\n`gemini-3.7-flash`\n\ncall against the photo before any of the rest runs.`gemini-3.1-flash-lite-image`\n\n, rendered concurrently, each with the player's photo passed inline as a reference so the same dog turns up in every exhibit. Without that likeness you're looking at a stock dog and the joke dies.When the prosecutor says:\n\n\"Then perhaps you can explain the frosting on your client's muzzle.\"\n\nthe exhibit had better show frosting on your dog's muzzle. Otherwise it's AI noise with a caption.\n\nSo each exhibit carries `visualFacts`\n\n, one to four things *visibly present in the rendered image*, and the tree model gets those and nothing else to write from. Three levels stay separate:\n\n| Level | Example |\n|---|---|\n| Narrative fact (what happened) | Biscuit ate the cake |\n| Evidence fact (what can be proven) | Biscuit has frosting on his muzzle |\n| Visual fact (what is actually in the picture) | White frosting is visible around Biscuit's mouth |\n\nThe trial can cite the third column, and can't invent a fourth.\n\nA response schema is a request, not a guarantee. Every generated payload goes through a hand-written validator before anything is stored. It checks what a JSON schema structurally can't:\n\nWhen validation fails, the reasons go back into the retry prompt. `tree node N4 points at unknown node N9`\n\nis something a model can act on, where a bare retry just rolls the dice again. One retry only.\n\nAsked to set its own thresholds, the model has to guess blind, before it knows what totals its own choice effects add up to. Measured over a batch of generated cases that came out at roughly 80% acquittals, with the tainted-acquittal ending unreachable in every case sampled.\n\nSo the engine works them out instead. It walks every run the finished tree can be played to and places the lines by quantile inside that real spread: the top 30% of endings acquit, the bottom 30% convict outright, the middle band convicts with reasonable doubt, and half the acquittals come out tainted. The same quantiles hold whatever numeric scale the model wrote its effects on.\n\nTwo edge cases needed handling. A plain quantile over endings like `0, 0, 10, 20, 30`\n\nlands *on* the minimum, so every run sits above the line and the verdict below it becomes unreachable. And two independent quantiles over one list can land on the same value, emptying the middle band while both lines still look correctly placed. Both are now drawn from the values strictly above the floor. A tree where every run ends on the same doubt total is rejected: if no choice decides anything, it's a cutscene rather than a trial.\n\nThe score is calculated the same way, weighted towards doubt and normalised against what *that particular tree* made possible. That's what lets a loss read as an excellent defense. The ceiling is usually below 100, because the run that maximises doubt and the run that argues best are rarely the same run.\n\nThe api serves the case minus `truth`\n\n, minus each exhibit's image prompt, minus whether a witness is reliable. Those stripping functions list every field by hand instead of spreading and deleting, so a field added to the type later can't ride out onto the wire without someone deciding it should. The verdict function never reads `truth`\n\nat all, only player state and the derived thresholds.\n\nAn anonymous upload endpoint in front of image generation needs bounds:\n\n`isDog`\n\nand closed on `safeForPublic`\n\n. A model outage shouldn't tell someone holding a real dog that it isn't a dog, but an unscreened photo must never reach the public docket.Vue 3 + Vite as a static SPA on Cloudflare Pages, Express + TypeORM on Railway with Postgres, images in Cloudflare R2. The shared types and the trial engine live in one workspace package, so the api is the authority on the verdict while the app renders a score from the exact same code. 167 tests, plus a CI job that dumps the production schema into a throwaway Postgres to prove the committed migrations still describe it.\n\n**Best use of Google AI.** Gemini does four separate jobs here:\n\n`gemini-3.7-flash`\n\nwrites the case as schema-constrained JSON, so what comes back is a structured world rather than prose`gemini-3.1-flash-lite-image`\n\nrenders the exhibits with the player's own photo as a reference, which keeps one specific dog recognisable across three separately generated imagesThen it stops. Gemini builds the world, and the verdict belongs to code that never saw the truth.\n\n*Justice for every good boy.*", "url": "https://wpnews.pro/news/paw-order-upload-your-dog-and-defend-them-against-evidence-generated-from-their", "canonical_source": "https://dev.to/arjen_/paw-order-upload-your-dog-and-defend-them-against-evidence-generated-from-their-own-photo-of7", "published_at": "2026-08-16 12:56:15+00:00", "updated_at": "2026-08-16 13:12:21.357932+00:00", "lang": "en", "topics": ["generative-ai", "ai-products", "developer-tools"], "entities": ["Paw & Order", "Gemini", "DEV Weekend Challenge"], "alternates": {"html": "https://wpnews.pro/news/paw-order-upload-your-dog-and-defend-them-against-evidence-generated-from-their", "markdown": "https://wpnews.pro/news/paw-order-upload-your-dog-and-defend-them-against-evidence-generated-from-their.md", "text": "https://wpnews.pro/news/paw-order-upload-your-dog-and-defend-them-against-evidence-generated-from-their.txt", "jsonld": "https://wpnews.pro/news/paw-order-upload-your-dog-and-defend-them-against-evidence-generated-from-their.jsonld"}}