DEV Community

Pratik
Pratik

Posted on

Research Dossier — Vibe-Coding a Case File, Then Teaching It to Hold Its Own Workflow

Sanity Challenge Path Two Submission

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

A note up front: this extends the same codebase as my Path One submission — one project, two angles. Path One is about the agent and the structured content it reasons over. This post is about the other half: how the app itself got built almost entirely through prompting, and what it took to push past a read-only frontend into something with a real Sanity-backed workflow.

What I Built

Research Dossier is a Next.js app, Sanity behind it, built through iterative conversational prompting rather than hand-writing the UI from scratch. The interface is deliberately not a blog-template look: a two-pane "case file" layout — a terminal-style live log on the left showing agent progress, a paper-dossier report on the right with a hand-stamped "CONTESTED" mark when sources disagree, custom ink/paper/gold color tokens, serif body type paired with monospace for the log — none of it from a component library's defaults.

For this path specifically, I pushed further than the Path One version: the system now models its own approval workflow as real Sanity content, not just UI state.

Demo

Live app: https://multi-agent-research-analyst.vercel.app/

Ask a question, watch the agents work in the case log, review the draft, click approve — then visit /cases to see the same investigation listed with a status badge that flips from draft to approved. Open Sanity Studio and you'll find the same document, same transition, visible there too.

Code

https://github.com/pratikdevelop/multi-agent-research-analyst

My Build Process

This was built in long back-and-forth passes rather than one clean generation, and most of the real work was debugging what got generated, not writing it from scratch. A rough timeline of what actually broke and got fixed along the way:

  • State/node name collision — LangGraph refused to compile because a node was named the same as a state channel (research, analysis). Not obvious from the error message's framing at first; fixed by renaming nodes to research_agent, analysis_agent, etc.
  • Model availability churn — the first two Groq model names I tried (llama-3.3-70b-versatile, then llama-3.1-8b-instant) weren't actually available on my account's model list, discovered only by hitting /v1/models directly and reading what my key could actually see. Landed on openai/gpt-oss-20b.
  • A real empty-research bug — the agent's tool-call loop wasn't feeding tool results back to the model, so research came back blank and every downstream agent had nothing to work with. Had to rewrite the loop to actually execute tool calls and continue the conversation.
  • A Sanity auth error that turned out to be a project-ID/org-ID mixup — "Session does not match project host" traced back to the request URL hitting the org ID instead of the project ID, both of which were sitting right next to each other on the same dashboard screen.
  • A null-crash in production — contradicts[]->{...} returns null in GROQ when the field was never set, not []. Crashed the Knowledge Base browser page until guarded.
  • Vercel deploy failures, three different ones in a row — a stray output: 'export' in next.config.js that forced static export and broke every API route; a peer-dependency conflict needing --legacy-peer-deps set via .npmrc AND the Vercel install-command override, since one alone didn't stick; then a stale production deployment pointing at an old build while the working one sat under "Preview."
  • The MCP swap — once Sanity Context was enabled on my org, swapping the research agent from a direct GROQ query to the real Context MCP endpoint meant discovering the actual tool name it exposes (knowledge_base_read) and that it serves a /initial-context grounding endpoint worth injecting into the system prompt — neither of which is obvious without just connecting and inspecting what comes back. Kept the GROQ version as an automatic fallback rather than a hard cutover.
  • The Workflow feature, for this path specifically — added a caseReport document type with a status field (draft → approved), a write-scoped Sanity client kept deliberately separate from the read-only one used for research, and two API routes: one that persists the agent's draft the moment it's ready, one that patches the same document to approved when a human closes the case. The /cases page and Sanity Studio both show the same transition, because it's the same document.

How I Used Sanity — Workflow

Three document types: topic, source, claim (the Path One side), plus caseReport for this path — question, draftText, finalText, status, createdAt, approvedAt.

The workflow itself is two writes against one document: the research/writing/review pipeline creates it with status: "draft", and a human approving (or editing, then approving) in the UI patches the same document to status: "approved" with the final text and a timestamp. No separate "approval" document, no external state machine — the content is the workflow state, which is the same philosophy as the contradicts field in the Path One schema: make the thing you care about tracking an explicit field, not something inferred.

I didn't build an App SDK component for this submission — Workflows was the deeper of the two bonuses I had time to do properly, and the challenge notes doing one well beats doing both shallowly.

Sanity Project Details

Project ID: 4kagnnrl

Top comments (2)

Collapse
 
respect17 profile image
Kudzai Murimi •

"Teaching it to hold its own workflow" is a good way to put it, that's the part people usually skip when they vibe-code something and it stays a toy. Curious how you set up the state tracking on Sanity's side.

Collapse
 
pratik_12b3f8bf3b50e48bae profile image
Pratik •

Thanks! That was the part that made it feel like more than a demo once it clicked.

It's one Sanity document type, caseReport, with a status field that goes draft → approved. The agent creates it the moment the report's ready; approving in the UI patches that same document to approved with the final text. No separate state machine, no audit log — the document's own history in Sanity is the workflow.

Also split the Sanity clients: read-only (Viewer token) for the research agent, write-scoped (Editor token) only for the case endpoints, in separate files on purpose — so a bug in one can't accidentally touch the other's permissions.