This is a submission for the Sanity Challenge, Path Two: Vibe-Code Something Strange
What I Built
The Department of Small Grievances. A fake government office for petty disputes nobody would ever take to a real court: a roommate who leaves dishes in the sink, a coworker who microwaves fish in the shared kitchen, a friend who leaves you on read for three days.
You file a formal complaint. An AI Commissioner reads the case and writes a real ruling: guilty, not guilty, or both parties at fault, with written reasoning and a petty-but-fitting penalty. Every case moves through an actual workflow in Sanity, from Filed to Under Review to Ruling Issued, and a human can still intervene, appeal a ruling, or close a case from the Studio.
Demo
Live app: https://small-grievances.vercel.app
Case permalink: https://small-grievances.vercel.app/docket/DSG-0001
Code
https://github.com/respect-20/small-grievances
My Build Process
I built this with Claude Code: Next.js on the front, Sanity behind it, and an API route that calls Gemini to act as the AI Commissioner. The honest part is how many small, real bugs showed up along the way. None of them were exotic. All of them were the kind of thing you only find by actually running the thing.
The model name was already dead. The first model I called, gemini-2.5-flash, had been retired. Google's own error message told me exactly which model replaced it, so that part was a quick fix once I read the error instead of guessing.
Rulings came back as broken JSON. Once the model name was right, I started getting "Unterminated string in JSON" errors. Gemini's newer models spend part of their output budget on internal reasoning before writing the real answer, so my ruling was getting cut off mid-sentence. Turning off thinking tokens and raising the output limit fixed it.
Vercel quietly blocked everyone but me. My first deploy looked successful, but nobody else could open the link. Vercel's Deployment Protection was on by default. I only found it by reading the deploy response closely and noticing "deploymentProtection": ["vercel_authentication"], then turned it off through the API.
The Studio worked locally and broke in production. Visiting /studio showed "Tool not found: studio," but only on the live site. I had the page statically exported, which doesn't play well with Sanity's embedded Studio and its own routing. Removing the static export and setting basePath explicitly in the Sanity config fixed it.
The Workflow board looked empty even though the app worked fine. This one took the longest to track down. sanity-plugin-workflow does not read whatever status field you give it. It tracks state on a separate workflow.metadata document, created through its own "Begin Workflow" action, and my app had no idea that document existed. I read the plugin's source, found the exact document shape it expects, and wrote a small function that keeps a real workflow.metadata document in sync every time a grievance changes status. The board now genuinely reflects what is happening, instead of just rendering empty columns that look right.
How Sanity Fits
Four document types, linked by reference: grievance, ruling, commissioner, and appeal. Nothing is a flat blob. A grievance holds a reference to its ruling once one exists. A ruling holds a reference back to the grievance and to the commissioner who wrote it. An appeal references the grievance it is appealing and can later hold a reference to a new ruling.
The AI Commissioner is not a person, it is a route handler. It reads a grievance from Sanity, calls Gemini in character using the active Commissioner's personality field, writes a new ruling document back, and moves the grievance through real Workflow states: Filed, Under Review, Ruling Issued, with Appealed and Closed left for a human to use from inside the Studio. That is close to word for word what the challenge calls out as a bonus: a process modeled as data next to the content, where an agent can move a draft forward and a person can still approve or intervene through the same states.
Sanity Project Details
Project ID: ufko3d3l
The dataset is public, so the schema and content are visible through the API without logging in. The Studio itself does need a Sanity login to browse directly, since the project has no outside collaborators added yet.

Top comments (0)