An entry for the Sanity Challenge — Path Two.
What I Built
Alibi Atlas is a continuity workbench for fiction. It asks a narrow question: can a character be where the scene puts them?
The starter case, The missing lantern, has three characters, four places and six events. Mira appears at the archive and the dock at overlapping times. Theo has four minutes to make a ten-minute journey. Jules says they visited the dock, but that statement is not treated as an established scene.
The app puts physical conflicts beside a timeline and a diagram of authored routes. Select an event, try different times or a different place, and see which conflicts remain. These preview changes stay in the browser; they do not rewrite the author's Sanity content.
It is a tool for checking a fictional draft, not for deciding whether a real person's account is truthful.
Demo
Open Alibi Atlas — no login needed.
Try this short walkthrough:
- Select Checks the departing boat. Change its time to 18:34–18:44, then choose Apply to preview. Mira's overlap is resolved, with enough time for the eight-minute journey.
- Select Delivers to the keeper and try 18:30–18:38. The second physical conflict disappears.
- Select Jules's account needs a decision. Change it to This happens in the story. A new overlap appears: the keeper is now established at the tower and the dock at the same time.
- Reset preview restores the loaded story. Inspect report JSON shows the case, changes, findings and source metadata for inspection or manual copying.
Example preview report · 390px layout
Code
GitHub repository — MIT licensed, with local setup, Studio instructions and tests.
The public viewer is a Next.js static export on GitHub Pages. It reads the real public Sanity dataset through @sanity/client. Studio runs separately for authenticated authoring. No write token is bundled into the viewer, and there is no runtime AI call.
My Build Process
Codex generated and revised the implementation, fictional case, documentation and this article. This is a record of an autonomous AI build, not a claim that I wrote the code by hand or a verbatim prompt transcript.
The brief and the schema
The working brief, paraphrased, was to build a fiction-continuity editor backed by real Sanity content. Characters, places, journeys and scenes needed their own documents. A character's statement had to remain separate from an event established by the author. Readers needed to test corrections without changing the published story.
That distinction became an explicit certainty field rather than something inferred from the wording of a scene. Each event also keeps the author's scene or dialogue note visible in the inspector.
| Document | Count | What it records |
|---|---|---|
| Character | 3 | Name and role |
| Place | 4 | Name and diagram coordinates |
| Journey | 4 | Two place references and a minimum time |
| Story event | 6 | Character, place, interval, certainty and source note |
| Case | 1 | Time window and references to the other documents |
The case references those documents instead of storing the story as one block of prose. A GROQ query dereferences them into the graph used by the workbench. Journey times are authored constraints, not estimates from map coordinates. The analysis uses the shortest connected, undirected route; a missing route stays unknown and appears as a review item.
Where the early attempts broke
An initial-value resolver tried to use context.documentId. TypeScript rejected that API. The replacement uses fixed starter IDs and explicit initial-value templates. The starter tool creates the 18 linked documents through the authenticated Studio client and reads them back without overwriting existing documents.
The first architecture put Studio inside the Next.js app. It was separated so the public reader could be a static export while authoring retained its authenticated session. This build uses the Sanity client and Studio, not the App SDK or Sanity Workflows.
The first all-clear wording was too strong when a route could be missing. It now says only that no physical conflicts were detected under the authored constraints. Unknown journeys still need review.
Browser checks also caught a stale validation message after reset. Reset now starts a fresh inspector without remounting it on every input change. The time-input automation changed displayed values without updating React until native key input was used; the final correction checks used keyboard input.
What was checked
All 12 domain tests passed, including nested overlaps, route choice, reported accounts, missing routes and invalid references or intervals. The public Pages workflow passed tests, type checking, the static build and deployment. At 390px, the page had no horizontal overflow; the timeline scrolls inside its own container.
A published description change produced the live-update notice and kept an in-progress preview. An actual Studio event edit reduced the public viewer from two physical conflicts to one after refresh, without preview edits. That test edit was then restored. A complete case readback matched the pre-test snapshot, and the dataset again contained the original 18 documents.
The visible report was read back with two preview edits, zero physical conflicts and one reported-account review. Browser automation did not confirm a completed file download or clipboard contents, so neither is claimed from the buttons alone. The inspectable JSON and linked example make the payload available directly. The controller also stalled on a dirty-preview refresh confirmation; a separate reader verified the new published content, and clean-preview refresh worked.
The prototype handles one case and one time window. It does not infer travel times, prove a statement is true or review every kind of plot hole. The aim is to expose contradictions the author can inspect, with the underlying event and source note still visible.
Sanity Project Details
-
Project ID:
0k0a3q37 -
Dataset:
production— public, original fictional demonstration content only. - Published documents: 18 across the five schema types above.
The viewer queries published content without a token. Sanity remains the authoring source: publishing a change raises a notice, and the reader chooses when to reload it. A connected query failure shows an error instead of silently substituting the local fixture.


Top comments (0)