This is a submission for the Sanity Challenge, Path Two: Vibe-Code Something Strange
What I Built
A recipe review queue in Sanity Studio where publishing is gated on somebody other than the author having actually cooked the thing.
Recipe sites publish recipes nobody made. "Approved" usually means one person clicked a button on their own work. I wanted a workflow where that is structurally impossible rather than discouraged by policy.
A recipe moves Drafting to Testing to Approval to Published. The author writes it and submits. Then a different person has to claim the test, cook it, and write up what happened before it can move on. A failed test sends it back to drafting. An approver sending it back wipes the previous test result, because that pass described a different version of the recipe.
The whole constraint is one filter in the workflow definition:
filter: '!defined($fields.tester) && $fields.author.id != $actor.id'
That line is the product. Everything else is scaffolding around it.
Demo
Two accounts, side by side. The author submits, tries to test their own recipe, and the action is refused. A second person picks it up, cooks it, reports back, and it publishes.
Code
Recipe Review Queue
A Sanity Studio where a recipe cannot be published until somebody other than its author has actually cooked it.
Built for the DEV x Sanity Challenge, Path Two.
The idea
Recipe sites publish recipes nobody made. "Approved" usually means one person clicked a button on their own work. This makes that structurally impossible rather than discouraged by policy.
A recipe moves through four stages:
Drafting -> Testing -> Approval -> Published
with a rejection at either Testing or Approval looping back to Drafting.
The constraint that matters is one filter in
workflows/recipeReview.ts:
filter: '!defined($fields.tester) && $fields.author.id != $actor.id'
The author is excluded from claiming the test. A recipe only reaches Approval once a second person has cooked it and written up what happened, so "approved" is a claim backed by evidence rather than a status someone set on their own work.
Two supporting decisions:
- A…
My Build Process
I used Claude Code in the terminal, not an IDE-embedded assistant. The whole thing is a Sanity Studio plus one workflow definition file, so there was very little UI to write and a lot of API surface to get right.
What worked: pointing it at the cookbook instead of describing the goal
The prompt that moved fastest was not a description of what I wanted. It was telling it to fetch Sanity's own Editorial review cookbook
and adapt it. Workflows is early access, so the model's training data does not contain defineWorkflow, defineStage, or the shape of an ops array. Asking it to invent that API from a description produced plausible nonsense. Asking it to read the real page first produced working code.
Same for the Studio plugin. Rather than guess at the config, it read the type definitions out of node_modules:
export declare interface WorkflowPluginConfig {
readonly tag: string;
readonly workflowDataset?: string;
readonly mappings?: readonly WorkflowMapping[];
...
tag is the only required field and bindings auto-discover from the deployed definition. That is two minutes of reading that replaced an afternoon of guessing.
What did not work: three separate environment failures
The documented CLI does not exist. The cookbook says to deploy with
sanity-workflows deploy. That package is not on npm. Neither is
@sanity/workflows-cli. The docs describe a tool that has not shipped. The programmatic path in the same doc does work, so deployment goes through a script instead:
await engine.deployDefinitions({
expectedMinReaderModel: 10,
definitions: [recipeReview],
})
Node 20 silently half-installs Sanity 6. npm install kept "succeeding" while leaving package.json untouched and node_modules/.bin empty. I went looking for a corrupted install, a bad npm config, a Windows symlink permissions problem. The actual cause was that sanity@6 requires Node 22.12+ and this machine had 20.19.5. Running the binary directly said so in one line:
ERROR: Node.js version >=22.12 required. You are running v20.19.5
The EBADENGINE warnings had been scrolling past the whole time. I had
dismissed them as the usual noise. Worth checking whether a package requires a newer runtime before assuming the install is broken.
sanity deploy refuses on a permission I appear to have. It fails with User is missing required grant sanity.project.deployStudio. I ruled out the obvious causes in order: the robot token in .env shadowing my login (re-ran with it unset, same error), my account role (administrator on the project), and the plan (the Manage UI shows an enabled "Add studio" button). Still refused. I stopped there and demoed from npm run dev instead. The brief asks for a video walkthrough or a link, not specifically a hosted Studio, and honestly the constraint demos better with two browsers side by side than it would on a static URL.
Where I nearly fooled myself
I wrote a script to drive an instance through the stages and prove the rule fires. It printed exactly what I wanted:
submit -> testing
claim test -> REFUSED: action filter returned false
Then I opened the Studio and looked at the instance. The author field said g-n9iPHYh2CdfV -- the API token's robot user. The script authenticates with a token, so the engine saw the robot as author, and the filter had correctly blocked the robot. I could have cheerfully tested my own recipe on camera and the demo would have proved nothing.
The engine takes its actor from the token and fireAction has no actor
override, which means one token is always one person. That is not a limitation to work around. It is the rule doing its job. Demonstrating a two-person constraint genuinely requires two people, so the video uses two real accounts and the script now says so in a comment rather than pretending otherwise.
What I would do differently
Check runtime requirements before debugging an install. Read the vendor's own docs into context before asking for code against a pre-release API. And verify who an automated test is acting as, not just what it printed.
Sanity Project Details
Project ID: qomqzwv7
Dataset: production (public)
Workflow definition: recipe-review v1
The dataset holds three recipes and the workflow instances. One instance is left at published, from the run recorded in the video, so the completed path is inspectable rather than just described.
Top comments (0)