# Using SpecVerse App-Demo

`specverse-app-demo` is a full-stack application that **executes `.specly` files at runtime without code generation**. Load a YAML specification and instantly get a working application with REST API, WebSocket events, web UI, in-memory database, and AI-assisted spec editing — all dynamically generated from the spec.

**This is not code generation.** It's runtime interpretation: the specification *is* the application.

**See also:**
- [SPECVERSE-INTRO.md](SPECVERSE-INTRO.md) — philosophy and ecosystem overview
- [SPECVERSE-SPECIFYING.md](SPECVERSE-SPECIFYING.md) — write the specs that app-demo loads
- [SPECVERSE-TOOLING.md](SPECVERSE-TOOLING.md) — `spv realize` is the alternative (code generation, not runtime interpretation)
- [SPECVERSE-VIEW-RENDERING.md](SPECVERSE-VIEW-RENDERING.md) — the shared view-rendering architecture (app-demo is one of three consumers)
- [App-demo modernization proposal](../proposals/implemented/2026-05-16-APP-DEMO-MODERNIZATION.md) — the current plan for aligning app-demo with the engines 6.65 wave + emission-efficiency proposals (Phase 1 dep bump shipped 2026-05-16; Phase 2 AI-pane work pending)

**Latest dep landscape (as of 2026-05-16, post-Phase-1):**

| Package | App-demo version | Real-npm latest |
|---|---|---|
| `@specverse/engines` | `^6.65.0` | 6.65.0 ✓ |
| `@specverse/types` | `^5.3.1` | 5.3.1 ✓ |
| `@specverse/entities` | `^5.5.1` | 5.5.1 ✓ |
| `@specverse/runtime` | `^5.1.0` | 5.1.0 ✓ |
| `@specverse/assets` | `^1.21.0` | 1.21.0 ✓ |

---

## Contents

- [When to use app-demo vs `spv realize`](#when-to-use-app-demo-vs-spv-realize)
- [Installation](#installation)
- [Running](#running)
- [The Nine Tabs](#the-nine-tabs)
- [The Server Manager](#the-server-manager)
- [Hot Reload](#hot-reload)
- [The AI Pane](#the-ai-pane)
- [The 3D Graph](#the-3d-graph)
- [Runtime Behavior Execution](#runtime-behavior-execution)
- [Development Mode](#development-mode)
- [Deployment](#deployment)
- [Architecture Notes](#architecture-notes)
- [Troubleshooting](#troubleshooting)

---

## When to use app-demo vs `spv realize`

Both execute a `.specly` spec. They differ in **when** execution happens and **what shape** the output takes.

| | app-demo (runtime interpreter) | `spv realize` (code generator) |
|---|---|---|
| **What runs** | A single Node.js process interprets the spec at runtime | Generated project (Fastify + Prisma + React, or similar) runs as a normal app |
| **Startup** | Load spec → running API + UI in seconds | Realize → `npm install` → `npm run dev` (longer) |
| **Backend** | In-memory database, spec-driven controllers, event bus | Persistent database (Prisma + PostgreSQL/SQLite), generated routes |
| **Frontend** | Dev GUI rendered at browser-runtime from the loaded spec | Generated React app (slim shell or full starter kit) |
| **Hot reload** | Edit spec → save → re-render UI in < 2s | Edit spec → re-run `spv realize` → restart app |
| **Cost** | Zero persistence, state lost on restart | Full persistence, production-ready |
| **Use case** | Rapid prototyping, spec authoring, AI-assisted iteration | Production systems, deployable artifacts |

**Use app-demo when:**
- You're writing a new spec and want fast feedback
- You want to see a spec "working" without running a build pipeline
- You're using the AI pane to iterate on spec content
- You're demoing SpecVerse to someone who doesn't want to install a full stack
- You need the 3D architecture visualization or live event stream

**Use `spv realize` when:**
- You need a persistent database
- You're shipping to production
- You want to edit generated code (ReactAppStarter path)
- You need CI/CD deployment

You can switch between them at any time — the same `.specly` file works in both.

---

## Installation

```bash
# Clone and install
git clone https://github.com/SpecVerse/specverse-app-demo.git
cd specverse-app-demo
npm install                                    # installs @specverse/engines, @specverse/runtime, etc.
cd frontend-react && npm install && cd ..
cd manager-ui-react && npm install && cd ..

# Build
npm run build                                  # backend + runtime UI + manager UI
```

The repo ships with example specs in `examples/` (notably `examples/blog.specly`).

---

## Running

### Option 1: Server Manager (multiple specs, recommended)

The **Server Manager** orchestrates multiple spec servers simultaneously. Open in a browser to upload specs, start/stop servers, and open per-spec GUIs.

```bash
npm run manager                                # boots manager on port 9000
# or: node dist/cli/manager-command.js --port 9000
```

Then open [http://localhost:9000](http://localhost:9000).

From the manager UI you can:
- Upload `.specly` files via the web UI
- Browse the filesystem for existing specs
- **Create a new spec** from engine templates (with a Category+Item default fallback)
- Start / stop / restart servers (each spec runs on its own port: 3050, 3051, …)
- Launch the runtime GUI for each running server

### Option 2: Single demo server

For a single spec with frontend dev server:

```bash
# Terminal 1: demo server
node dist/cli/demo-command.js examples/blog.specly --port 3000

# Terminal 2: frontend dev server
cd frontend-react && npm run dev
# → http://localhost:5173
```

The frontend proxies API requests to the demo server on port 3000.

### Option 3: Production build

```bash
npm run build                                  # build everything
npm run manager                                # serves built UI + all runtime UIs
# → http://localhost:9000
```

---

## The Nine Tabs

Once a spec is running, the runtime UI exposes nine tabs:

| Tab | Purpose | What you can do |
|---|---|---|
| **Views** | Spec-declared UI | Custom dashboards, lists, details, forms. Pattern-based rendering with navigation between views. Multi-model dashboards. |
| **Models** | CRUD interface | Create / edit / delete entities. Date pickers, relationship dropdowns, lifecycle state transitions. Auto-generated controllers for models without explicit ones. |
| **Services** | Service operation monitoring | Real-time view of service calls. Operation parameters, return values, subscription chains. |
| **Events** | Live event stream | Every domain event in the system — entity created, state transitioned, spec reloaded. Full payload visible. 1000-event history. |
| **Diagrams** | Auto-generated Mermaid | ER, lifecycle, architecture, event flow, deployment topology. 12+ diagram types. |
| **3D Graph** | Interactive visualization | Three.js 5-layer architecture diagram. Rotate, zoom, pan with arrow keys. Click nodes for details. |
| **Specly** | Raw `.specly` editor | View + live-edit the source. Syntax highlighting, folding, validation, save (writes to disk and reloads runtime). |
| **AI** | Claude Code integration | Describe changes → Claude generates/modifies the `.specly` → Apply to hot-reload. Shows system prompt for transparency. Chat history persists. |
| **Help** | Built-in documentation | This guide + the language reference ([SPECVERSE-SPECIFYING.md](SPECVERSE-SPECIFYING.md)). |

### Views tab (spec-declared UI)

The Views tab renders UI declared in your spec's `views:` section. The walker in `@specverse/runtime/views/core` drives rendering; the React adapter in `@specverse/runtime/views/react` turns walker output into JSX.

Supported view types: `detail`, `dashboard`, `board`, `timeline`, `calendar`, `workflow`, plus any custom type declared in the spec. Navigation between views is automatic — click Edit on a detail view to open the form pre-populated; click a related entity to cross-navigate.

**belongsTo relationships render as resolved display names** (not UUIDs). **hasMany relationships render as tabs** in detail views. **lifecycle states render as `<select>` dropdowns** in forms. **FK dropdowns** show entity names via `getEntityDisplayName()`.

**`list` and `form` views are hidden from this tab** (since runtime 5.2.0) — they're available via the Models tab, which provides per-model CRUD that includes both list and form. To suppress this filter (e.g. for a different consumer of `@specverse/runtime/views/react`), omit the `hideTypes={['list', 'form']}` prop when constructing `<ViewRouter>`. Models that have ONLY `list`+`form` in the spec (no explicit detail view) still surface their auto-generated `{Model}DetailView` in this tab — the runtime applies hideTypes AFTER its auto-detail-generation.

### Models tab (CRUD interface)

The Models tab gives you a CRUD interface for any model in the spec, even if no explicit controller was defined. Behind the scenes:

- `DynamicControllerEngine` generates CURVED route handlers at runtime
- `DynamicModelStore` maintains in-memory state (UUIDs, auto-generated fields, version counters)
- Auto-generated fields (`auto=now`, `auto=uuid4`) are hidden from forms and generated on create
- Lifecycle transitions go through the `evolve` endpoint with state validation

Each model gets a list / create / edit / delete / evolve UI automatically.

### Events tab

Every event in the system streams through here via WebSocket. The event bus keeps a 1000-event ring buffer so you can scroll back through recent activity. Use this to:

- Debug event chains (`OrderPlaced` → `PaymentProcessed` → `OrderConfirmed` → …)
- Verify `publishes:` in behaviors actually fires
- Watch cascade effects from a single CRUD operation

### Specly tab (live edit + hot reload)

You can edit the raw `.specly` file in the browser, save (Cmd/Ctrl+S or the Save button), and the runtime reloads within ~2 seconds:

1. Save writes the file to disk
2. File watcher picks up the change
3. Runtime parses + validates the new spec
4. `DynamicSchemaRegistry` clears and rebuilds
5. All panes re-render (models, views, events, diagrams, 3D graph)

Validation errors surface inline in the editor. Invalid specs are rejected; the previous spec stays active until the edit validates.

---

## The Server Manager

The Server Manager runs multiple specs side-by-side on different ports. Useful for comparing specs, running tests, or juggling projects.

- **Port allocation:** each spec gets a unique port starting at 3050 (3050, 3051, …). The ecosystem port plan: 3000-3049 for generated project backends, 3050-3099 for app-demo per-spec backends, 5173-5199 for vite frontends, 9000+ for the manager UI
- **Spec actions:** Start / Stop / Restart / Open GUI / View Logs per-spec
- **File upload:** drag-and-drop `.specly` files into the browser
- **Filesystem browse:** navigate to find existing specs
- **New Spec:** creates a fresh spec from engine templates (Category+Item canonical default)

The manager UI communicates with the manager backend over REST + WebSocket for real-time status updates.

---

## Hot Reload

app-demo's killer feature — the fastest feedback loop for spec authors:

```
Edit spec → Save → Runtime reloads → UI updates
     ↑                                      │
     └──────── (< 2 seconds) ───────────────┘
```

Works regardless of where you edit:
- In-browser via the Specly tab
- In your IDE (the file watcher picks it up)
- Via the AI pane (generated spec auto-applies)
- Via CLI tools writing to the file

No build step. No code generation. The spec is the application.

---

## The AI Pane

AI-assisted spec generation inside app-demo. Routes through any provider supported by the engines AI abstraction:

1. **Type a natural-language prompt** — "add a Comment model with hasMany on Post and lifecycle draft → published → archived"
2. **The active provider generates a modified `.specly`** — streamed token-by-token so you see the diff emerging
3. **Click "Apply to Spec"** — the new spec is saved, the runtime reloads, the UI reflects the change

### Multi-user behavior — single-editor lock

Since 2026-05-16 (app-demo commit `9abd417`), each spec instance enforces a **writer-control lock**: at most one user at a time holds the "writer pen" — the right to use the AI pane or Apply spec changes. Everyone else is a **read-only spectator** who can still use the running app fully (CRUD via the Models tab, lifecycle transitions in detail views, etc.).

**User flow:**

1. First user opens a spec instance → Views/Models/etc. work; AI tab shows "Claim writer pen" button (no one holds it yet)
2. User clicks **Claim** → becomes the writer → AI pane unlocks with `✎ You are writing` badge
3. User submits prompts, applies specs as normal
4. Second user opens the same spec instance → all other tabs work; AI tab shows "<First-user-name> currently holds the writer pen — inactive 0s. Available to claim in ~10m if the current writer stays inactive."
5. First user clicks **Release** → writer becomes null → second user's "Take over" button enables → they click → roles swap

**Inactivity-based takeover**: if the writer goes silent (no AI/Apply activity) for 10 minutes (default), the lock becomes claimable by anyone — closes the "closed-laptop-with-pen" failure mode. Threshold is env-tunable via `WRITER_INACTIVITY_MS` (milliseconds).

**What's gated**: only `POST /api/ai/*` endpoints (start, generate, cancel, apply, validate, guards). The AI pane is hidden entirely from readers.

**What's NOT gated**: model CRUD, lifecycle transitions, service operations, all reads. Multiple users browsing + modifying entity data simultaneously is supported and intentional — that's how the demo shows real-time event streaming.

**Identity** comes from the manager's GitHub OAuth session (when the gate is engaged — see [Auth gate](#auth-gate--restrict-access-to-a-github-org) above). When OAuth is disabled (local dev, no `GITHUB_CLIENT_ID`), everyone is `local-user` and effectively the only writer — single-user mode just works.

### Transparency features

- **System prompt visible** — the full system prompt sent to the provider is shown at the top
- **Full user prompt visible** — exactly what was sent for this turn, including any spec context injected
- **AI backend badge** in the header — shows which provider is active (`claude-cli` / `anthropic` / `openai-compatible` / `stub`)
- **Chat history persists** — across page reloads, via the session-management backend
- **Diff view** — before/after of the `.specly` with syntax highlighting

### Under the hood

Since Phase 2 of the app-demo modernization (engines 6.65.0+, 2026-05-16), the AI pane uses the **`@specverse/engines/ai` provider abstraction** rather than a direct `spawn(claude-cli)`. This means:

- One code path supports four providers — `claude-cli` (local binary), `anthropic` (API), `openai-compatible` (Marrbox / SGLang / Ollama / any OpenAI-compatible endpoint), `stub` (fixtures only)
- Provider is selected via env vars (see [Railway env-var matrix](#ai-pane-on-railway--env-var-configuration) above)
- Streaming flows through the Vercel AI SDK's `streamText` → WebSocket to the browser
- Anthropic prompt caching is enabled by default via `providerOptions.anthropic.cacheControl: 'ephemeral'` when the provider is Anthropic — cuts repeat-turn input costs significantly
- Per-turn telemetry sidecar at `<spec-dir>/.specverse/app-demo-ai.json` — captures providerId, tokens, duration, success/error for each turn

**System prompt is sourced from `@specverse/assets`** via `loadPrompt('app-demo')` + `assembleSystem(prompt)` from `@specverse/engines/ai` (since engines 6.66.0). This handles partial-rendering — `{{> attribute-conventions}}`, `{{> lifecycle-rules}}`, etc. are expanded inline. Final system prompt is ~15.2K chars; prior to the partial-rendering fix it was 6.7K (the partials were emitted as literal strings to the model).

### Session management

Sessions are backed by `spv session` (see [SPECVERSE-TOOLING.md](SPECVERSE-TOOLING.md#spv-session)). Each session is keyed by name; chat history is persisted to disk. You can:

- Create named sessions for different features you're iterating on
- List recent sessions
- Delete sessions
- Process queued jobs asynchronously

---

## The 3D Graph

Interactive 5-layer architecture visualization, powered by Three.js via the [layered-3d-graph](https://github.com/SpecVerse/layered-3d-graph) library.

### Layer structure

| Plane | Layer | What it contains |
|---|---|---|
| 0 | Data | Models and their relationships |
| 1 | Control | Controllers managing CURVED operations |
| 2 | Service | Service components and logic |
| 3 | Event | Event publishers and subscribers |
| 4 | View | Views and UI components |

### Interactive controls

- **Rotate:** click and drag to rotate
- **Zoom:** scroll to zoom in/out
- **Pan:** arrow keys (↑ ↓ ← →) for pan — perfect for laptop users without a mouse
- **Click a node** — see details about the component
- **Follow edges** — color-coded: 🔵 Sync, 🟠 Async, 🟢 Data/Relation, 🟣 Event, 🔘 Dependency

### Visual features

- **Auto-layout:** automatically positions nodes in a 2D grid per layer, adapting to any number of models
- **Directional arrows:** relational edges show arrow heads pointing to targets
- **Straight lines:** clean, direct connections between related components
- **Resizable sidebar** with layer toggles

### Editing the 3D library

> **⚠️ IMPORTANT:** The 3D graph code in `frontend-react/public/graph3d/` and `frontend-react/lib/@layered-graph/core/` is **compiled JavaScript** synced from [layered-3d-graph](https://github.com/SpecVerse/layered-3d-graph). **NEVER edit these files directly!**

To modify the 3D graph:

```bash
# 1. Edit TypeScript source in the other repo
cd ../layered-3d-graph
# Make changes in src/

# 2. Build
npm run build

# 3. Sync to app-demo
cd ../specverse-app-demo
npm run sync:graph3d

# 4. Commit in BOTH repositories
```

See `specverse-app-demo/docs/features/GRAPH3D-INTEGRATION.md` for the full sync workflow.

---

## Runtime Behavior Execution

app-demo interprets **L3 behaviors** at runtime (as opposed to `spv realize` which transpiles them to TypeScript). This means:

- **15 convention patterns** — CRUD, validation, lifecycle transitions, relationship management are executed directly by the `BehaviorInterpreter`
- **Model constraint guards** — when models declare `constraints:`, the interpreter JIT-compiles them (same Quint→TS pipeline as realize's `<Model>.guards.ts`) and evaluates them as preconditions on every mutation (fail-open). (The entity-module Quint invariants are validate-time spec checks, not part of this runtime path.)
- **Controller engine pipeline** — preconditions → steps → postconditions → events, executed per request
- **Schema registry** — clears and rebuilds on spec reload for consistent state across all panes

The runtime produces the same observable behavior as `spv realize`-generated code: same CURVED operations, same event publications, same lifecycle enforcement. The difference is execution model — in-memory interpretation vs compiled code. Parity is enforced by the P3 test suite in specverse-engines.

---

## Development Mode

### Manager UI development

```bash
# Terminal 1: manager backend
npm run manager

# Terminal 2: manager UI dev mode (HMR)
npm run dev:manager
# → http://localhost:5174 with hot reload
```

### Runtime UI development

```bash
# Terminal 1: demo server with a spec
node dist/cli/demo-command.js examples/blog.specly --port 3000

# Terminal 2: runtime UI dev mode (HMR)
npm run dev:runtime
# → http://localhost:5173 with hot reload
```

### Full-stack development

```bash
# Terminal 1: backend watch mode
npm run build:backend -- --watch

# Terminal 2: demo server
node dist/cli/demo-command.js examples/blog.specly --port 3000

# Terminal 3: runtime UI dev server
npm run dev:runtime
```

---

## Deployment

app-demo runs on traditional hosting platforms (not Vercel — it uses WebSockets and persistent processes).

### Railway (recommended)

Two-minute deploy with auto sleep/wake:

1. Go to [railway.app](https://railway.app) and sign up
2. New Project → Deploy from GitHub repo → select `specverse-app-demo`
3. Settings → Networking → Generate Domain
4. Done! App lives at `https://your-app.railway.app`

**Railway offers:**
- $5/month free credit — covers ~150-200 hours of active use
- Auto sleep/wake — sleeps after 5 min, wakes in ~30 seconds
- GitHub auto-deploy on every push
- WebSocket support
- File uploads via the web UI
- Reverse proxy — all servers accessible through a single URL

#### AI pane on Railway — env-var configuration

Since Phase 2 of the modernization (engines 6.65.0+), app-demo's AI pane uses the `@specverse/engines/ai` provider abstraction. **`claude-cli` does NOT work in Railway containers** (no Anthropic auth, no binary). Configure ONE of the following via Railway env vars instead:

```bash
# Option A — Anthropic API directly (simplest, default for Claude users)
SPECVERSE_AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
# Optional: SPECVERSE_AI_MODEL=claude-sonnet-4-7

# Option B — OpenAI-compatible endpoint (Marrbox, local SGLang via tunnel, etc.)
SPECVERSE_AI_PROVIDER=openai-compatible
SPECVERSE_AI_BASE_URL=https://api.marrbox.net/v1
SPECVERSE_AI_API_KEY=<provider key>
SPECVERSE_AI_MODEL=marrbox-local

# Option C — leave unset → AI pane is gracefully disabled with a setup
# message; rest of app-demo (Views/Models/Services/Events/Diagrams/3D/Specly)
# works fully without it.
```

The active provider shows in the AI pane header (e.g. `AI backend: anthropic`). When unconfigured, the pane shows a setup message instead of accepting prompts — the rest of app-demo is unaffected.

#### Railway + cloud-rented SGLang (low-cost AI backend)

For users who want SGLang's RadixCache (85-95% prefix hit rate, much cheaper per inference) without owning a DGX Spark, the **Railway + rented cloud GPU** pattern is a first-class deployment recipe:

```
Railway: hosts app-demo (UI + runtime interpreter)
    ↓ HTTPS
RunPod serverless OR Lambda Labs H100: SGLang + Qwen3-Coder-30B-A3B-FP8
```

Setup:
1. Stand up SGLang on rented cloud GPU (see [Option B-cloud](../proposals/in-progress/2026-05-16-emission-efficiency/2026-05-16-OPTION-B-CLOUD-RENTAL-SGLANG.md) for the full recipe)
2. Configure Railway env vars to point at the cloud endpoint:
   ```bash
   SPECVERSE_AI_PROVIDER=openai-compatible
   SPECVERSE_AI_BASE_URL=https://your-runpod-endpoint.runpod.net/v1
   SPECVERSE_AI_API_KEY=<your provider key>
   SPECVERSE_AI_MODEL=Qwen3-Coder-30B-A3B-Instruct-FP8
   ```
3. The AI pane lights up; per-turn cost ≈ $0.05-0.20 (serverless) or $0 within a Lambda Labs always-on window

Cost ballpark: ~$5/month Railway tier + per-call SGLang charges. Compared to using Anthropic's API for the AI pane: significantly cheaper at moderate volume, comparable speed, no Anthropic dependency.

See [Option B-cloud](../proposals/in-progress/2026-05-16-emission-efficiency/2026-05-16-OPTION-B-CLOUD-RENTAL-SGLANG.md) for the full SGLang serving recipe + provider comparison.

#### Auth gate — restrict access to a GitHub org

**Without an auth gate, your Railway URL is fully public.** Anyone who has (or guesses) the URL can use the manager, spawn spec instances, and drive AI calls billed to your AI provider key. Always engage the gate before sharing a Railway URL.

App-demo ships with built-in GitHub OAuth that gates every request behind an org-membership check. Active members of the configured GitHub org can sign in; everyone else gets 403.

**Setup:**

1. Create a GitHub OAuth App under your org's settings (`https://github.com/organizations/<your-org>/settings/applications` → New OAuth App):
   - **Homepage URL**: `https://your-app.railway.app/`
   - **Authorization callback URL**: `https://your-app.railway.app/auth/github/callback` (must match exactly)
2. Note the **Client ID** + click **Generate a new client secret** for the **Client Secret**.
3. Approve the OAuth App for your org in *Third-party Access → OAuth App access* (or set the org policy to auto-approve internal apps).
4. Set five env vars in Railway:

   | Var | Value |
   |---|---|
   | `GITHUB_CLIENT_ID` | from the OAuth App |
   | `GITHUB_CLIENT_SECRET` | from the OAuth App (treat as password) |
   | `GITHUB_CALLBACK_URL` | `https://your-app.railway.app/auth/github/callback` |
   | `SESSION_SECRET` | a long random string (`openssl rand -hex 32`) |
   | `ALLOWED_GITHUB_ORG` | your GitHub org slug (e.g. `SpecVerse`) |

5. Redeploy. The startup log should show `[auth] GitHub OAuth gate ENABLED — org "<your-org>"`.

**Behavior:**

- `/api/health` is always exempt (Railway healthchecks)
- `/auth/*` routes are exempt (the OAuth flow itself)
- Every other HTTP request → if no valid session, 302 to `/auth/github` (HTML) or 401 JSON (API)
- After successful login, user is redirected back to the URL they originally requested
- Active org members are allowed; pending invitations / suspended members are rejected
- Sessions last 7 days; `POST /auth/logout` clears them

**Disable mode (local dev):** if `GITHUB_CLIENT_ID` is unset, the gate is **disabled** and the server runs ungated. The startup log shows `[auth] GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET not set — auth gate DISABLED`. This is the expected state for `sh run-manager.sh` on a laptop.

**Known gap (follow-up work):** WebSocket upgrades go through Node's `'upgrade'` event and bypass Express middleware. The HTTP gate keeps the manager UI + spec-instance HTML out of reach (you need a logged-in HTML page to even know the WS URL), but a determined attacker who knows the URL structure could open a WS directly. Adding WS auth means attaching the session store to the `upgrade` handler — tracked as a TODO. For an org-only deploy this is low-risk; for public-facing deploys it would be a blocker.

**Standalone help-only site:** a static, GitHub-OAuth-gated mirror of *just* this app's Help tab (the SpecVerse guides viewer) lives at `specverse-app-demo/help-site/` — a separate Vite build + `server.mjs` that ports this same gate. Deploy it as a **second Railway service** (Root Directory `help-site/`) reusing `ALLOWED_GITHUB_ORG` + `SESSION_SECRET`, but with its **own** OAuth App + `GITHUB_CALLBACK_URL`: a classic OAuth App registers one callback host, so each domain needs its own app (the org-membership check is independent of which app). Two gotchas that bite: **Railway shared variables are not auto-injected into a new service** — add/reference them on the help-site service explicitly or it boots *ungated*; and *deleting* `GITHUB_CLIENT_ID` makes it public, not "logged in." See `help-site/README.md`.

Using your deployed app:

```
Manager UI (control panel):
https://your-app.railway.app

Client UI (runtime demo) for a specific spec:
https://your-app.railway.app/client?api=https://your-app.railway.app/servers/server-0
```

### Other platforms

See `specverse-app-demo/docs/deployment/DEPLOYMENT.md` for:
- **Render:** free tier with 750 hours/month
- **Fly.io:** global CDN with 3 free VMs
- **Docker:** self-host with `docker-compose up`

**Not Vercel compatible** — app-demo requires WebSockets and long-running processes.

---

## Architecture Notes

### Backend (`src/`)

- **`runtime/`** — core runtime engine
  - `runtime-engine.ts` — main coordinator
  - `schema-registry.ts` — dynamic schema management
  - `entity-store.ts` — in-memory data store
  - `controller-executor.ts` — CURVED operation execution
  - `lifecycle-manager.ts` — state transitions
  - `event-bus.ts` — pub/sub with history
- **`api/`** — HTTP + WebSocket servers
- **`cli/`** — command-line entry points (`demo-command.ts`, `manager-command.ts`)
- **`manager/`** — multi-server orchestration

### Frontend — Phase 4B walker integration

As of Phase 4B (shipped 2026-04-20), app-demo's frontend is a **thin consumer of `@specverse/runtime`**. The entire view-rendering stack lives in the runtime package:

| Layer | Source |
|---|---|
| Pattern library + walker | `@specverse/runtime/views/core` |
| React adapter | `@specverse/runtime/views/react` (DevShell, ViewRouter, RuntimeView, FormView, ModelManager, hooks) |
| Tailwind renderer | `@specverse/runtime/views/tailwind` |
| app-demo consumer | `frontend-react/src/components/views/ViewRenderer.tsx` — a ~10-line thin wrapper around `ViewRouter` |

app-demo previously forked ~1700 lines of `react-pattern-adapter.tsx`. That fork is gone; the canonical adapter lives in `@specverse/runtime/views/react`. See [SPECVERSE-VIEW-RENDERING.md](SPECVERSE-VIEW-RENDERING.md) for the full "one pattern library, three consumers" architecture (app-demo is Consumer 1).

### Real-time events

WebSocket integration is global:

- `ApiInitializer.tsx` opens the global WebSocket connection
- Automatic subscription to CURVED events (`{Model}Created`, `{Model}Updated`, `{Model}Deleted`, `{Model}Evolved`)
- Query invalidation triggers React Query refetch
- All views react to entity changes in real time

### Entry point REST API

```
GET  /health                              # Health check
GET  /api/runtime/info                    # Runtime metadata
GET  /api/specly                          # Raw spec file content
GET  /api/models/:model/schema            # Model schema
GET  /api/behaviors/:model                # Model behaviors
GET  /api/views                           # All views
GET  /api/services                        # All services
GET  /api/runtime/events                  # Event history
GET  /api/diagrams/types                  # Available diagram types
GET  /api/diagrams/:type                  # Generate diagram
POST /api/controllers/:controller/:op     # Execute CURVED operation
WS   /ws                                  # WebSocket for real-time events
```

The manager UI uses a different REST surface for multi-spec orchestration.

---

## Troubleshooting

### "Cannot find module '@specverse/engines'" (or runtime / entities / types)

Usually a stale install:

```bash
rm -rf node_modules package-lock.json
npm install
npm run build:backend
```

### Frontend build missing

If the GUI doesn't load, ensure both React UIs are built:

```bash
npm run build                                  # builds both
npm run manager
```

### Port already in use

```bash
npm run cleanup                                # kills processes on 3000/3001/3100/3101/5173/9000
# or
lsof -ti:3000 | xargs kill -9
# or
node dist/cli/demo-command.js examples/blog.specly --port 3001
```

### Hot reload not working

1. Check `watchForChanges` isn't disabled in the demo server config
2. Verify the file watcher is working: edit the spec and watch the terminal for reload messages
3. Restart the demo server

### 3D graph library not yet available

```bash
npm run sync:graph3d
```

---

## Related

- [SPECVERSE-INTRO.md](SPECVERSE-INTRO.md) — the documentation hub
- [SPECVERSE-SPECIFYING.md](SPECVERSE-SPECIFYING.md) — write specs that app-demo runs
- [SPECVERSE-TOOLING.md](SPECVERSE-TOOLING.md) — the CLI alternative (`spv realize`)
- [SPECVERSE-VIEW-RENDERING.md](SPECVERSE-VIEW-RENDERING.md) — the shared walker + three-consumer architecture
- [specverse-app-demo CLAUDE.md](https://github.com/SpecVerse/specverse-app-demo/blob/main/CLAUDE.md) — contributor development guide
