This is a submission for the Sanity Challenge, Path Two: Vibe-Code Something Strange
TLDR: Entrepreneur artist vibe codes her way to workflow optimization by consolidating all business social media posts through a single sanity content lake.
Why: Social media management is a huge task. It needs to be simplified. If you are a party of one like myself, you need all the optimization you can get.
I always thought it would be cool to have a single place to draft and send posts to all my social media platforms at once for my business. But I wanted to hold onto my final approval, to make sure a bot doesn't do something insane. We want to keep some sanity here. ๐
A customized Sanity Studio A Claude agent that drafts seasonal social posts by reading the shop and a Knowledge Base over Sanity Context MCP A Node publisher that serves a human review queue An App SDK app that reviews them live
All of it is built around my existing Rails storefront at everfluorescent.com. Nothing posts to a platform yet, and that is deliberate. The first half is the story of getting here, including the parts that cost me days; the second half is how the system works.
New Additions: {% embed https://github.com/AnnaVi11arrea1/everfluorescent-desk %}
I built this project from a terminal using Claude CLI. When Claude started to irritate me, Github Copilot CLI came to the rescue once. Ok, maybe I opened Vim, like 10 times. I prefer to not pass my api keys around if possible. Rather do that myself.
Here is a brief timeline:### Up to 9/23: the store side. The Phase 0 Studio was in place: schema, per-platform previews, preflight checks, a publish gate, Obsidian sync and catalog sync. On the neoncart side, four PRs merged: a product id and gallery endpoint, the Retro post search archive, the admin approve queue, and follow-up fixes. I also built a publisher tool into my admin dashboard, so I can preview business posts and publish them from inside my own store; it pulls from Sanity and saves published posts for reuse or modification later. I ran the migration, restarted the Jetson services, and both new admin tabs loaded. 9/24: reconnected the shop to Sanity through Vercel. 9/26: the agent. With the shop connected and posts populating in the approval area, it was time to hand the heavy lifting to an agent. generate.mjs already drafted campaign posts at needsreview from a template (captionFor). The agent replaces that template and the rest of the pipeline stays as it was. Its drafts now reach the admin queue, and the review card shows where sources disagreed and what each caption was based on. 9/27: refinement. I collected upcoming events near my customers, plus holidays and seasons, into one file so posts could be scoped to the time of year. I wrote a longer prompt that also records timings, which is where the caching numbers below come from. That was also the day I found stale data coming out of Sanity and finally traced it to the sync (details in the next section). The re-run wrote five clean Halloween drafts. Last: the review desk. An App SDK app (everfluorescent-desk) lists variants across every post with the gate's verdict, and Approve / Back-to-draft write variant.status directly. It type-checks and builds, but I have not yet rendered it against my live data. I expect two things when I do: every card will say no knowledge notes were cited, and if no platformAccount document exists everything will show Blocked. Both would be real findings, not app bugs.
Time-Costing Traps ๐ฅ Turn the MCP feature on before creating the endpoint. You cannot see the dashboard page that creates it until the feature is enabled. If you give your project a token before that, you have to delete it and create a new one with Context Viewer access. Build the Knowledge Base entries after adding them. I built mine from the website and uploaded files once an Anthropic API key was in place. Entries that are added but not built are invisible to the agent, and this step is easy to miss. The sync has to run from my local computer. Changes to the knowledge base only reached my server after I ran the sync locally. What gave it away was a last-sync date five days old. Files also had to be added to the sync list or they were never pulled in, so the agent kept getting old or incomplete data. After fixing that I had to rebuild the knowledge base again and redeploy Sanity. Files that are missing can also be archived. The agent cannot see posts. The Context endpoint does not expose them, so my first --write run proposed two products that already had template drafts from the Fall campaign. Both were correctly skipped, but that run's $0.49 was wasted. The agent is now told up front which products already have posts. Claude caught its own bug in the review desk. The projection selects only some fields of each variant, so writing that array back would have silently destroyed the assets. It found the fix (useEditDocument with no path and a functional update) in the SDK docs rather than guessing.
My sources were disagreeing with themselves. Sanity can be used for quality control.
Challenge How it was overcome Where does content live: Sanity, Obsidian or the store? Obsidian stays my writing surface, with one-way sync into Sanity. Paintings mirror from the store instead of moving into Sanity. Where does publishing actually run? The publish worker runs on Vercel, not inside the neoncart store app. Where does post approval happen? Moved out of the Studio into my own store admin, with published posts archived in neoncart's database (the Retro post search tab). Products with missing data (no price, no image) Saved but flagged and blocked from posting, not silently skipped. Title, price and image are hard blocks; description and category only warn. Lost catalog schema design doc One salvaged copy survives (docs/catalog-schema-design.SALVAGE.md). Still open whether to rewrite it properly. Per-platform post length and media limits (platformSpec.ts) Found to be partly wrong against the real docs, so I am correcting them against the Instagram, Facebook, TikTok and LinkedIn developer documentation. Facebook's rate-limit documentation in particular is not good. neoncart has no CI or tests, and deploy is manual on my Jetson Every merged PR needs a git pull, rails db:migrate and a service restart by hand. Nothing auto-deploys. New admin tabs not appearing after a merge A stale running server: the migration alone does not restart Puma or the worker. Fixed with systemctl restart.
I stumbled into prompt caching along the way, and it could arguably be a post of its own.
Two cache points: one on the instructions and tool definitions, which do not change during a run. One that moves to the end of the conversation each turn so every turn reuses everything before it. The run summary line prints cache reads and writes with a dollar estimate, so I can see what each run costs.
Cost The same three-draft run went from about $1.50 to about $0.39. Across six runs I spent $4.34, against $5.89 without caching. Priced again with identical tokens and no caching, each cached run saved between 47% and 62%, and the saving grew with the number of turns.
Request Time Cache write Cache read Uncached input Output 1 3.1s 10,149 0 2 72 2 6.9s 4,223 10,149 2 608 3 6.8s 2,904 14,372 2 620 4 5.9s 8,034 17,276 2 473 5 20.0s 13,687 25,310 2 1,638 6 95.5s 11,125 38,997 2 8,863 Total 50,122 106,104 12 12,274
Input and output are in tokens. After the first request, everything before it was read from the cache, and only 12 input tokens in the whole run were billed at the full rate. Without caching that run would have cost about $1.09, so caching saved about $0.41, or 38%.
Drafts that admit where sources disagree ๐ญ Disclaimer: I can not correct all my data sources in a timely manner, so I've chosen to highlight this as a feature. Definitely not user error at all. ๐
