ADRs
ADR 0028 — Deploy JSON consumer
  • Date: 2026-05-27
  • Status: Resolved — decision is to defer. Not pending work. The ADR 0027 open question ("who reads pages/{id}.layout.json?") is answered: no consumer is built until a user demands a public render URL. Today the artifact is audit-and-restore only. This is a deliberate, final decision per YAGNI — re-open only when a trigger below fires.
  • Affects:
    • apps/api/src/deploy.ts — author of pages/{id}.layout.json
    • Future: a separate "render template" repo OR an in-ARNO renderer route
  • References: ADR 0023 (Deploy pipeline), ADR 0027 (Zones unification), packages/render-adapter/_index.md

Context

ADR 0027 left an explicit open question: the published pages/{id}.layout.json artifact has no reader.

Current ARNO has two render paths:

  1. Editor preview iframepackages/render-adapter postMessage protocol. The ARNO parent sends the live composition tree to a sandboxed iframe that mounts real React components from the connected DS bundle. State flows in-memory, not via git.
  2. Deploy button (ADR 0023) — writes pages/{id}.layout.json to the connected repo. Atomic commit, idempotent. Nothing consumes the JSON.

After the user clicks Deploy, the JSON exists on main/staging, but no URL serves the rendered page. Until that gap closes, "Deploy" is a snapshot-without-publication: useful as audit trail and recovery point, but the user does not see a deployed product.

Decision

Defer the consumer choice. Today the JSON is an audit-and-restore artifact. The first user request for "show me my published site" will pick the path from the options below.

Why defer rather than build

  • ARNO target audience (frontend builder, non-developers) does not currently ask for a public-facing rendered page — they edit and preview inside ARNO. The Deploy button satisfies a different need (commit-to-git for version control + handoff to engineers).
  • Each option below carries non-trivial scope (new repo template, build pipeline, hosting decisions, render adapter coupling). Building speculatively risks lock-in to the wrong choice.
  • ADR 0027 already ensures the JSON is correct (full zones, versioned, schema-stable). Whichever consumer we pick later reads the same artifact — no rework on the producer side.

Options on the table

#PathEffortTrade-offs
AStatic site generator in connected repo. New ARNO render template repo (Next.js / Astro) reads pages/*.json at build time, renders to HTML via the DS bundle, deploys to CF Pages / Vercel. ARNO Deploy = git commit; CI in connected repo = build + publish.Medium. New repo template, framework choice, CI workflow, hosting per user.Closest to "user clicks Deploy → sees site". Aligned with Vercel/Netlify pattern. Couples ARNO to a specific SSG choice. Slower to first preview (CI run).
BRuntime fetch via CDN. Published JSON served as static asset (CF Pages serves the connected repo). ARNO itself adds a /render/{projectId}/{screenId} route that fetches the JSON + DS bundle and mounts the render adapter.Small-medium. One new route in apps/web, no new repo.Fastest path to "public URL renders the screen". No SSG needed. But ARNO becomes the runtime for user-facing pages — increases our cost surface, ties uptime of user sites to ours.
CAudit-and-restore only. Acknowledge JSON has no runtime consumer. Document recovery procedure (restore composition from git into Postgres). Defer rendering until real demand.Zero. Already where we are.Honest about current scope. JSON still has value: version control, code review on changes, rollback via git. Forces explicit decision later (not silently broken).
DGitHub Action codegen → TSX. Render JSON → TSX files in the same repo. Rejected by ADR 0023 spirit ("no codegen"). User-decided settled: ARNO does not produce code.n/aOut of scope per product positioning.

Recommendation when the question becomes live

Option B first, then Option A as the user base grows:

  • B is the cheapest path to a working arno.app/render/{project}/{screen} URL. One Next.js route in apps/web, reuses existing render adapter, reuses existing JSON fetched from raw.githubusercontent.com (public repos) or via the GitHub App proxy (private repos). Users get shareable "see my prototype" links immediately.
  • A becomes worthwhile when users want custom domains, SEO, or to "own" their deployment outside ARNO infrastructure. That request is post-MVP. By then the JSON shape will have battle-tested under runtime fetch usage.

Do not implement B preemptively. Wait for the request. The first signal will be a user asking "how do I share my prototype with a stakeholder without giving them an ARNO login" — currently /share?token= (Phase 10) covers this for in-ARNO viewing. When that becomes insufficient, B is one route away.

Consequences

Positive:

  • Deploy artifact is correct and stable (ADR 0027) — future consumer reads a known shape
  • Scope creep blocked: no premature SSG, no premature CDN runtime
  • Decision criteria explicit: revisit when user asks for public render URLs

Negative / debt:

  • Deploy button semantics remain partly opaque to users ("you clicked deploy — what happened?"). Mitigation: button tooltip and post-deploy modal explain "committed to git, see [PR link]"
  • No public-facing rendered URL until B or A lands. Acceptable for dogfood and B2B handoff workflows where the engineer pulls the JSON

Triggers to revisit:

  • First user request for "public URL of my screen without ARNO login"
  • First user request for "deploy my project to my own domain"
  • Connected repo CI feature request (e.g. build-on-push)
  • Any of these → re-open this ADR, pick B or A, supersede with ADR 0028a/b