Inspiration

Most AI agent demos mutate your app silently. You ask for a change, it happens, you hope it was right. Approval dialogs are binary — yes or no — while real design work is never binary.

I wanted the opposite: what if every agent action arrived as a set of related, negotiable changes you can actually see together? A title, a background, a logo position — proposed at once, previewed as one coherent poster, edited one by one. That question became Atelier, powered by Redini.

## What it does Atelier is a WebMCP design editor where agent actions arrive as editable ChangeSets instead of silent mutations.


 ONE agent intent → ONE WebMCP call (design_update) → ONE ChangeSet → derived preview → amend / skip → atomic commit → receipt → Undo ↔ Redo

One call can propose 3 or 6 operations at once. Before anything touches the live canvas you can preview the full result, amend any parameter with typed controls, skip any operation, and commit only the accepted subset.

The receipt preserves the distance between what the agent intended and what you authorized:


 INTENDED / AMENDED BY YOU / SKIPPED BY YOU / APPLIED — STATE v0 → v2

Redini is the small, app-agnostic transaction layer underneath. Every commit, undo and redo advances the version — \( v_{n+1} > v_n \) — so a proposal that was staged on an old state becomes stale instead of being silently applied elsewhere.

Live: https://redini-atelier.netlify.app — Try ?clean=1 for the focused agent path.

## How we built it Stack: TypeScript + Vite, native DOM/CSS, Netlify static hosting. No framework, no snapshots — forward operations + compensating inverses only.

WebMCP: We use the native surface — document.modelContext.registerTool(), getTools(), executeTool(tool, JSON.stringify(args)). The public app exposes 5 focused tools: design_update, list_templates, get_current_design, filter_templates, get_vendor_content. design_update stays pending while you negotiate and resolves with a concise result like {"status":"committed","appliedCount":2}. Undo/Redo are intentionally human-side only.

Verified like a product: 104 tests, tsc --noEmit clean, production builds, and a real Chrome 149+ E2E that drives the true executeTool path — including PREVIEW — not applied yet, amend, skip, Commit N changes, receipt, Undo, Redo, keyboard ⌘Z / ⇧⌘Z, and stale proposals. Deployed with explicit Origin-Agent-Cluster: ?1 and Permissions-Policy: tools=(self) so the experimental browser gate is unambiguous.

## Challenges we ran into WebMCP is still experimental. executeTool expects the tool object + JSON string, Chrome needs --enable-features=WebMCP, and documents must be origin-keyed or registerTool must reject with SecurityError. We found all three with a real Puppeteer spike on Chrome 149.

Product challenges were harder:

  • Preview without double-render: simulate() already gave the right derived state, but we rendered it as a translucent overlay on top of the committed poster. Fix: hide the committed canvas while previewing.
  • Honest atomicity: if runtime.apply() mutates then throws without returning an inverse, the world may be partially changed. Redini never claims “nothing happened” in that case — it surfaces stateUncertain instead.
  • Derived bounds: move {x:640,y:400} passes a per-field check but puts the logo fully outside the 640×400 canvas. Bounds are now validated on the sequential derived logo state and re-validated at commit.

## Accomplishments that we're proud of A complete, demo-ready loop that holds together under verification: agent proposes 6 changes in one call → you amend one, skip one, commit 2 of 6 → receipt → Undo → Redo → provenance shows Agent proposed vs You staged Template "...", all with 0 console errors.

We are proud of the small things judges can falsify: the badge names the proposal that owns the preview, Commit is disabled at 0 ops, templates are real <button>s, and the 104-test suite includes the exact repros from our external audits.

And it works inside the real ChatGPT in-app browser — our final smoke was there, on the Netlify URL, with frame-ancestors allowlisted for it.

## What we learned A transaction layer does not need snapshots. Set-semantics + idempotent inverses are enough, and the hardest part is not the algorithm but making the UI tell the truth. We also learned to keep the agent surface small — 5 tools and one negotiable ChangeSet are more legible than a toolbox of approvals.

Built solo for my first hackathon, it was also a crash course in reading specs before building.

## What's next for Redini-Atelier Redini stays a small, reusable guard. Next we want to make it a tiny npm module with a cleaner validate(op, priorOps?) contract, add opt-in persistence for history, and try the same pattern on other domains — dashboards, forms, planning tools — where one agent intent naturally touches several related fields.

Code is MIT at https://github.com/cioffiAI/redini-atelier — v1.0.0-challenge is frozen, main stays open.

Built With

Share this project:

Updates

Submission history