DEV Community

Cover image for The AI clerk approved a 41 cm dragon. Your job is to stamp DENIED.
Malbolged
Malbolged Subscriber

Posted on

The AI clerk approved a 41 cm dragon. Your job is to stamp DENIED.

Sanity Challenge Path Two Submission

This is a submission for the Sanity Challenge, Path Two: Vibe-Code Something Strange

What I Built

The Bureau of Imaginary Permits: a pixel-art inspection-booth game about bureaucracy for things that don't exist, running on real Sanity Workflows.

You take the window. Applicants step up one at a time, wanting a Licence to Whistle on Tuesdays, a Permit to Keep a Small Dragon (Indoor), or the Deed to a Medium-Sized Cloud. They slide over their papers: an application, a Citizen Card with a photo, sometimes a licence they already hold.

Every file arrives already passed by Clerk Third Class, an AI. Its memo sits on your desk: "Named dragon, licensed puddle on file, fire plan mentions water. In order." The clerk is tired, though. The dragon is 41 cm, and the Rulebook says 35. You pull out the stamp tray, slam DENIED into the box, and hand the papers back.

  • Get it wrong and a citation prints out of the machine.
  • Day 2 brings a bulletin that changes the rules overnight. The clerk keeps applying yesterday's.

That is the Workflows bonus prompt played straight: "an agent can move a draft forward and a person can approve it." Here the agent always moves it forward, and the person is the only thing standing between a forged Citizen Card and a Licence to Look Up.

Behind the window there is a real citizen side. Anyone can file a permit:

  • A real AI clerk reads it and can send it back asking for Form 88-H, the Haunting Liability Waiver.
  • Sky permits also go to a second AI, the Ministry of Weather, which rules in the voice of a shipping forecast.
  • Human officials then stamp the file in an App SDK app inside the Sanity dashboard.

Demo

Play: https://bureau-of-imaginary-permits.vercel.app/shift (no account; seven applicants a day, two days)

Bulletin

dragon at window

stamp impact

stamped

citation

end of day

The citizen side: a real file that went through both AIs and two human signatures, with the GROQ rule behind every move.

citizen file

Code

Bureau of Imaginary Permits

A pixel-art inspection-booth game about bureaucracy for things that don't exist, built on Sanity Workflows.

Play: https://bureau-of-imaginary-permits.vercel.app/shift (no account needed)

You take the window. Applicants step up one by one, wanting a Licence to Whistle on Tuesdays, a Permit to Keep a Small Dragon (Indoor), or the Deed to a Medium-Sized Cloud. Each hands over a Citizen Card, an application, and sometimes a licence they already hold.

The AI clerk has already passed every file. The clerk is tired. Your job is to compare the papers against the Rulebook and the day's bulletin, stamp APPROVED or DENIED, and hand them back. Wrong stamps print a citation. Day 2 changes the rules.

There is also a real citizen side. Anyone can file a permit: a real AI clerk (Gemini) reads it, a second AI (the Ministry of Weather) rules on anything in the sky, and human…

My Build Process

IDE: Claude Code (Opus 5.5) in a Windows terminal. I typed very little, and that's the honest shape of this build. Claude wrote, ran and tested everything, and kept a BUILD-LOG.md as it went. This section comes from that log.

Every prompt that changed the direction

  1. "go path two". Claude came back with three strange ideas that could use both bonus areas. I picked the Bureau.
  2. "don't open the browser, tell us what you need and we'll provide it". Mid-build, Claude started driving my Chrome to test the dashboard app. I stopped it, and from then on it asked me for screenshots.
  3. After the first version shipped: "we should first redesign the application it looks too vibecoded (colors, fonts ...) we should gamify it, it should follow the "Papers, Please" game design it should have a stamp approval/denial stamping animation". Claude asked three questions:
    • Who does the visitor play? The inspector.
    • How is a stamp scored? Against planted discrepancies, with the truth stored in Sanity.
    • How far does the redesign go? A full pixel-art booth.

That's it. Everything else below is what an agent did with those sentences, including where it went wrong.

Version 1: a government office (and why I threw its look away)

The first build was a tasteful government-forms site: paper colours, a serif, rubber-stamp CSS. It worked end to end, but it looked like every other "nice" AI-built site. That was my second "too vibecoded" across two challenge entries, so the brief became: commit to a world.

Workflows: almost hand-rolled, then the real thing

Claude's first plan was to model the process itself, with a workflow type holding stages[] and transitions[]. Before writing it, it followed the "Workflows" link in the challenge post and found @sanity/workflow-engine (early access, on npm):

  • Definitions are deployed into a dataset as documents.
  • Every application is a sanity.workflow.instance with its stage, fields and full history.
  • Transitions are GROQ conditions.
  • AI work runs as queued effects.

The hand-rolled schema was dropped. There are now four chains, all data:

Chain Steps
express-permit AI clerk only
standard-permit AI clerk, then one human stamp
sky-permit AI clerk, Ministry of Weather AI, Under-Secretary, Minister
booth-inspection Straight to the window: the game

how it works

The game is real workflow traffic

Starting a shift turns the day's scripted case documents into real applications, each governed by a booth-inspection instance. Your stamp is a real fireAction. The server then scores it from the decision the engine recorded on the instance, checked against case.truth. The browser never reports its own score. Day sheets are shift documents, which is the leaderboard.

Honesty note: the clerk memos on scripted applicants are authored, not generated. Every file at the window arrives "passed" because that's the premise. If the real clerk saw those files, it would reject them, and they'd never reach you. The citizen side uses the real AI clerk.

Where it got stuck, and how it got unstuck

  • The engine's validator caught a real bug on the first deploy. One chain had a denied stage that no transition could reach, and sanity-workflows deploy --check refuses that.
  • The clerk misread "Yes". With consent answered "Yes", it twice demanded the consent waiver, then rejected the citizen's reply to that very form. Two causes:
    1. It wasn't forced to quote the failing answer.
    2. It never saw the question on the form it had requested.

After the fix, it quotes the answer that fails ("'Any time she fancies' fails the rule…") and accepts a correct reply.

  • Guardrails moved from the prompt into code:
    • Prerequisite permits are checked against the issued workflow instance.
    • Forms: the clerk may only request forms attached to that permit type.
    • The loop: after two returns, a file is "passed for stamping out of sheer fatigue". This one fired in testing before the "Yes" fix landed.
  • Scoring looked in the wrong place. windowDecision is declared on the stage, so it lives on the stage entry, not the instance's fields. The first scripted shift threw "The stamp was not recorded" until that was fixed.
  • Seven windows opened one after another took 24 seconds. In parallel it takes about 3.5 s, inside Next.js after() while you read the bulletin. A stamp takes about 3.5 s on the server, so the animation and sound fire at once and the citation prints when the verdict lands.
  • React StrictMode started two shifts per page load in dev. A started ref fixed it.
  • A privacy bug. The public permit certificate printed the stamping official's real Sanity name. It now shows the role. The identity stays on the private instance.
  • Windows papercuts. Git Bash rewrote /v9/projects/… into a Windows path, so vercel api silently created the project with default settings. A shell heredoc also choked on curly apostrophes in Day 2's dialogue.

Original art, no assets

  • Inspiration: inspection-booth games. Copied: nothing.
  • Faces: generated from a seed on a 20×24 grid, so a Citizen Card photo with a different seed is a forged photo (Day 2, applicant 4).
  • Fonts: Pixelify Sans, VT323 and Silkscreen from Google Fonts.
  • Sound: synthesised with WebAudio (stamp thunk, paper, counter bell, citation printer).
  • Motion: every animation uses CSS steps(), so it moves like sprites.

The App SDK Stamp Desk

Citizen files that reach a human desk land in the Stamp Desk, a React app deployed into the Sanity dashboard and built on @sanity/workflow-sdk:

  • useWorkflowInstances({filter: {stage: 'stamping'}}) is the live in-tray.
  • useWorkflowSession drives one file. Its evaluation decides whether the stamp buttons are enabled.

I checked a real stamp on the instance. The approve row is signed by my own Sanity user with executionContext: {kind: "sdk", runtime: "browser"}. The website's server token signs its rows as bureau-web. The move to issued was committed by the same browser session: the engine runs the cascade inside fireAction, so a human stamp needs no server.

stamp desk

Sanity Project Details

  • Project ID: e23lrcxv
  • Public datasets:
    • production: departments, forms, permit types, applications, scripted applicants, bulletins, shifts.
    • workflows: the engine's definitions and instances. Their dotted IDs keep them out of anonymous reads, so the site renders them on How it works.
  • Try a query: Day 2's applicants and the clerk's memos. The truth field is on those documents too. Play first.
  • Schema highlights:
    • permitType: requirements[], each with a clerkCheck rule (the Rulebook); prerequisites[] pointing to other permit types; the forms[] the clerk may request; and workflow naming its chain.
    • case: the papers handed over (cards and licences with photo seeds), the authored clerk memo, truth and reason.
    • bulletin: the day's rules and in-game date.
    • shift: results written only by the server.
    • application: holds answers and agent memos. Its progress lives in the workflow instance.

Agent Session

Building the Bureau of Imaginary Permits: a Papers, Please–style game on Sanity Workflows claude-opus-5-5
Agent
2 messages not shown
Agent
Agent
Agent
Agent
Agent
Agent
Agent

Status: Sanity Workflows is a real npm engine (@sanity/workflow-engine 0.36, early access). I'm reading its API so the permit approval chains run on the actual engine and aren't hand-rolled.

Agent
1 message not shown
Agent
Agent
Agent
Agent
Agent
11 messages not shown
Agent
Agent
3 messages not shown
Agent
21 messages not shown
Agent
Agent
6 messages not shown
Agent
2 messages not shown
Agent
Agent
67 messages not shown
Agent
1 message not shown
Agent
6 messages not shown
Agent
Agent
Agent
Agent
Agent
1 message not shown
Agent
73 messages not shown
Agent

Everything is deployed. One check is still blocked on you: IP-ZQ8YWL is still waiting at the Under-Secretary's desk, so I haven't seen a stamp from the Stamp Desk yet.

Your next steps:
1. In the Stamp Desk, click Stamp APPROVED on IP-ZQ8YWL and send me a screenshot.
- I'll then check that the engine recorded the stamp under your Sanity identity, not the server's token. The post's App SDK claim depends on that, so it stays unconfirmed until then.
- The desk shows your real name ("Signing as…"). Blur it in any screenshot that goes into the post.
2. Create the repo and push it. Run these yourself:

cd [REDACTED]\Desktop\bureau-of-imaginary-permits
git init
git add .
git commit -m "initial commit"
gh repo create malbolged/bureau-of-imaginary-permits --public --source . --push

submission/ and .env are in .gitignore, so neither gets pushed.

Live and verified:

What Where
Public site https://bureau-of-imaginary-permits.vercel.app (an express permit was issued in about 9 s in production)
Studio https://imaginary-permits.sanity.studio
Stamp Desk https://www.sanity.io/@o9gofjii1/application/fitcgnallbe1vgmbhcicvis9
Sanity project e23lrcxv, 3 workflow definitions deployed

Ready on disk (in bureau-of-imaginary-permits\):
- README.md, and BUILD-LOG.md kept up to date as the build went.
- submission\post.md: the full DEV draft in the Path Two template, with the required first line and an explicit answer on which AI-native IDE was used. It still has three placeholders: screenshots, the desk screenshot and the session embed.
- submission\images\: 5 production screenshots (home, application form, file returned to the citizen, issued cloud certificate, How it works).
- submission\claude-code-session-path2.jsonl: 426 messages, 383 KB. It's sanitized: no name, email, company, username or Sanity user ID. I'll re-cut it at the very end so it includes everything up to then.

After your stamp, I'll:
- Verify the actor and update the post.
- Re-cut the session file.
- Give you the paste-ready post body and the upload steps.

13 messages not shown
You

we should first redesign the application it looks too vibecoded (colors, fonts ...)
we should gamify it, it should follow the "Papers, Please" game design
it should have a stamp approval/denial stamping animation

2 messages not shown
Agent
Agent
1 message not shown
Agent
16 messages not shown
Agent
123 messages not shown
Agent

Top comments (0)