This is a submission for the Sanity Challenge, Path Two: Vibe-Code Something Strange
Oracle Streams, born before 11.2, captured redo and continuously propagated database changes. It served as Oracle's replication engine with quiet persistence. Deprecated in 12.1 and desupported in 19c, Streams found Azure offered no direct reprieve… It is survived by Azure Database Migration Service, not at all; Azure Data Factory, only by workaround; Microsoft Fabric, partly; and Oracle Database@Azure, partly.
Read the paper: ora2az.vercel.app
What I Built
The Legacy Obituaries is a newspaper obituary column for Oracle Database features. The front page carries 28 notices, one for every feature that Oracle has deprecated or desupported, or that has no clean home in Azure; each of the 50 features in the graph has its own page:
- born in the release that introduced it,
- cause of departure: its deprecation and desupport releases, or the complication that keeps it out of Azure,
- survived by: the Azure services that carry on its work, and how faithfully (unchanged, partly, only by workaround, or not at all),
- contested accounts, when two sources disagree about how it died,
- notices of correction: every source, with the date I read it.
It's strange on purpose, but it's also useful. If you're scoping an Oracle-to-Azure move, the front page is a fast checklist of what won't come with you, and every line links to the page that says so.
Every notice is assembled live from a structured Sanity dataset in which each claim carries a source. The "In life" summaries are written by me; the obituary paragraph is machine-drafted from the graph and human-certified. The same dataset powers my Path One agent: Keyword search found the facts but lost the sources.
Demo
Live: https://ora2az.vercel.app
Behind the paper there's Mortician's Desk, a small Sanity App SDK app in the Dashboard. An editor sees every draft notice live, and Certify edits and publishes it in one transaction. Only certified obituary prose reaches the public site; right now two notices are waiting in the queue.
The paper also carries The Coroner's Reports: every answer my Path One agent gave in its evaluation, with the queries it ran.
Code
pyaroslav
/
ora2az
Oracle → Azure migration knowledge graph on Sanity: an agent over Sanity Context, and The Legacy Obituaries (DEV Sanity Challenge 2026)
ora2az — an Oracle → Azure migration knowledge graph on Sanity
A structured, sourced graph of how Oracle Database features map to Azure, and two things built on it for the DEV Sanity Challenge:
| What | Try it | |
|---|---|---|
| Path One | A migration-advisor agent that answers only from the graph, through two Sanity Context MCP endpoints, with a four-way eval | The Coroner's Reports: every eval answer, with the queries it ran. No install. |
| Path Two | The Legacy Obituaries: an obituary for every Oracle feature that didn't survive the move to Azure | ora2az.vercel.app |
Sanity project udyjvgsk · dataset production (public; query it anonymously, e.g. all disputes). Studio at https://ora2az.sanity.studio needs a Sanity login.
The result in one table
18 held-out questions, graded blind (method and caveats):
| Condition | Verdict correct | Cited every required publisher | Grounded |
|---|---|---|---|
| Model alone | 10/18 | 11/16 | 0/18 |
| Keyword search (BM25) over the same documents | 14/18 |
app/ is the Next.js site, desk/ the App SDK app, studio/ the schema, content/ the data, the drafting script and the tests. The session-by-session log is in docs/build-log.md.
My Build Process
Tool: Claude Code in the terminal. The model was Claude Fable 5.1 for most of the build and Claude Opus 5.5 at the end. One working session per step. The build log records each one: what I asked, what happened, what broke, how it was fixed.
My prompts were short; the agent did the planning and asked me the decisions. A few, verbatim:
"please analyze challenge at https://dev.to/devteam/join-the-sanity-challenge-2500-in-prizes-for-five-winners-514m and suggest next steps; make sure to do a thorough investigation and planning; ask questions if any"
That produced a plan and seven questions back to me: which path, which domain, account state, budget, which sources may be used, which brand, which Path Two concept. I picked Oracle → Azure because I've lived in that domain.
"suggest options that do not require payments - for example I have claude subscription and it sould be nice using it rather than buy a keys"
That decided the architecture: no API keys anywhere. The agent runs headless on the subscription, and the obituary prose is written by Sanity's Agent Actions on the free plan's AI credits.
"why do I need to certify? do I need to certify just one? can you do certification?"
Here's where the human gate got tested. More on that in step 4.
What went wrong, in order:
1. The schema came first, and it decided everything. I asked for a model where a keyword search could never fake the answer. That gave eight types: feature, Azure target, a mapping between them with a fidelity grade, caveats on each mapping scoped to Oracle versions, editions and Azure tiers, sources, and a dispute type that points at two caveats that disagree. The obituary is then just a GROQ join: desupportedIn gives the death, mapping gives the survivors, dispute gives the contested accounts.
azureTarget ◀── mapping ──▶ oracleFeature ──sources[]──▶ source
fidelity (dates, obituary, reviewStatus)
▲
│
caveat ──evidence──▶ source
appliesTo: versions · editions · tiers
▲ ▲
claimA │ │ claimB
dispute ──resolvedBy──▶ source
glossary, pattern ──sources[]──▶ source (the Knowledge Base)
Every arrow is a reference. A notice is one GROQ query that walks them backwards from the feature, and a claim with no path to a source fails the seed validator and a dataset test before it can reach the site.
2. My first seed was invisible. The agent wrote ids like feature.dbms-job. All 445 documents were created, and every public query returned 0. A dot in a Sanity _id is a path prefix, like drafts., so the documents were hidden from the published view. Fix: store feature_dbms-job, keep the readable form in the YAML, recreate the dataset. Most instructive bug of the weekend.
3. The epitaph was pessimistic. The first version picked the worst mapping, so Data Pump read "survived by a workaround" even though it lives on unchanged in Oracle Database@Azure. An obituary is about who carries on, so the epitaph now speaks for the best survivor. I only caught it from a screenshot.
4. Obituary prose, written by Sanity's Agent Actions. A script calls client.agent.action.generate for each feature. The only material the model gets is that feature's own graph neighbourhood, passed as a GROQ instruction parameter: its mappings, caveats and disputes. It writes to the draft document; the site reads published documents only. Three things went wrong:
The first prompt forgot the survivors, the heart of an obituary (paragraph three is now mandatory and starts "It is survived by"); regenerating appended a second obituary instead of replacing it; and my "does a draft exist?" check ran in the published perspective, so it never saw drafts. All three are fixed, and a re-run now skips all 50.
Then the honest part. The domain is mine, and much of the material comes from my own blog, so I asked to certify all 50 at once. Before publishing, a script checked every draft for Oracle releases or ORA- codes that don't appear in that feature's own data. It flagged none, and all 50 were certified in one pass, not read line by line. A reviewer later pointed out that this made the gate look like a rubber stamp and left the desk with an empty queue. So three notices went back to draft. Two of them, XMLType and VECTOR, have since gone all the way through the workflow in step 6, and a front-page notice, the original exp utility (desupported in 26ai), joined the queue, so the gate now protects something a visitor can see.
5. The App SDK desk was scaffolded by hand, because I wasn't logged in yet. The docs left three questions open (is an index.html needed, may the organization id come from .env, how does process.env reach the browser), and the installed CLI source answered all three; the answers are in the build log. Certify batches editDocument and publishDocument in one useApplyDocumentActions call, so a notice is never half-certified.
6. Workflows: a notice has a lifecycle, not just a flag. A Sanity Workflows definition, notice-lifecycle, now sits over each queued obituary (workflows/):
drafted ──fact-check pass──▶ fact-checked ──certify (human)──▶ certified
▲ ◀──fact-check fail──┘ │ │
└────────── send back (reason) ─┘◀────── retract (reason) ───────┘
-
Fact check. A script reads the draft obituary and fires
passorfail. It fails any Oracle release or ORA- code that isn't in that feature's own data. Right now two notices wait infact-checked, including theexputility from the front page, and XMLType and VECTOR have gone all the way tocertified. -
Certify. This is a human action. The desk's Certify button publishes the document and then fires the workflow's
certify. Un-certify firesretract. -
Freeze guard. While a notice sits in
certified, a guard (!delta::changedAny((obituary))) refuses edits to the obituary. That's whycertifieddeliberately keeps aretractexit: a stage with no way out ends the instance, and its guard with it.
What went wrong: the engine rejects drafts. ids as the subject (so the fact check reads the draft itself), an omitted optional action parameter crashed a field write (the bench caught it before deploy), and a leftover field would have re-routed a returning notice.
And the honest limit: this is early access (0.x), and per the docs guards are advisory. The Content Lake doesn't enforce them yet, and my App SDK desk doesn't read them. The workflow records the gate; it doesn't yet enforce it. The other 46 certified notices were certified before the workflow existed and have no instances.
Sanity Project Details
-
Project ID:
udyjvgsk· datasetproduction(public) - Schema, as code: studio/schemaTypes (Studio at https://ora2az.sanity.studio needs a Sanity login; the dataset itself is public)
- The roster query: every feature with a deprecation or desupport
Agent Session
Two episodes from the build, as recorded by Claude Code. Curated excerpts; local paths anonymized, credentials and unrelated local files removed.
The invisible seed and the certify-all decision (steps 2 and 4 above):
Building the notice-lifecycle workflow (step 6), by a helper agent working from a written brief:
Open the workflow-building session (linked rather than embedded: its package names read as @-mentions to DEV).
Sources are public only: Oracle documentation, Microsoft Learn, community documentation, and my own public blog and labs. — Yaro



Top comments (2)
The obituary format is such a good fit for deprecated features, it makes a dry migration list actually readable. I liked that each notice says which Azure service "carries on" its work instead of just declaring it dead.
The "invisible seed" bug made me laugh because I hit the exact same thing. A dot in my document IDs made them disappear from the public dataset, and it took me a while to realise that's how drafts. works.
Curious: did the Workflows guards ever stop you from doing something you actually needed to do, or did they feel helpful the whole time?
Thank you! "Survived by" was the whole idea: a migration checklist reads differently when it names who carries on, not just what died.
And the dot in
_idseems to be a rite of passage. 445 documents "created", zero visible. Glad I'm not the only one.Honest answer on guards: they never stopped me, because in the current early access they can't yet. Guards are advisory. The Content Lake doesn't enforce them, and my App SDK desk doesn't read them, so the freeze on a certified obituary is recorded rather than enforced. Where they did shape things was the design. A stage with no way out ends the instance, and its guard with it, so
certifiedkeeps aretractexit just to keep the freeze alive. What actually pushed back was the engine itself: it won't take adrafts.id as the subject, so the fact check reads the draft separately. If I take it further, the desk will check the guard before it enables editing.