> Canonical guide: https://developers.foxlight.ai/build/tutorials/video-studio/architecture/
> Contract snapshots: Skulk 2.0.0 (b0af39c79b6b7102b2478062904f1d7cc8619975); SDK 0.4.0 (31bb090b8f64689f87514e48385e22b6ad94a6c2). Check the installed runtime when versions differ.

# 1. Understand the application architecture

Start by separating the work your application owns from the work Skulk owns. Studio is a useful example because it is substantial enough to need persistence, a UI and several workflows, but it still calls ordinary public model APIs.

## Follow one user action

An operator writes a shot description and clicks Plan. Studio reads the model catalog and live placement, resolves the shot and returns a plan. The operator then starts a render. Studio records a durable operation, submits a Skulk video job and follows it until the finished clip is downloaded into Studio's own storage.

The UI, application process and inference model need not run on the same machine.

```text
Browser ── scoped HTTP ──┐
                         ├── Studio child ── HTTP ── Skulk API ── model runner
Fabric caller ── host ──┘         │
                                  ├── SDK operation journal
                                  └── application media library
```

The host supervises the child; the child tracks jobs; Skulk places and runs inference.

| Boundary                | Input                                                         | Output                                  | Owner                            |
| ----------------------- | ------------------------------------------------------------- | --------------------------------------- | -------------------------------- |
| Browser → Studio        | Application HTTP requests under the surface URL               | Plans, operation records and media      | Studio                           |
| Fabric caller → child   | Negotiated capability call through Skulk and the managed host | One typed result                        | Skulk routing + Studio behavior  |
| Child → Skulk API       | Catalog/state reads, video jobs, chat completions             | Model facts, job references and results | Skulk inference                  |
| Child → durable storage | Operation intent, logs, files and retention marks             | Restartable application state           | Studio + SDK persistence helpers |

The browser does not speak the managed child's socket protocol. The child does not import a model runner. These boundaries let you change the UI or switch the application workflow without rewriting Skulk's inference machinery.

## The source map

The Python application lives under `src/foxlight_video_studio/`.

| File                         | Read it for                                                                         |
| ---------------------------- | ----------------------------------------------------------------------------------- |
| `contracts.py`               | Typed requests, results, settings, descriptors and the signed manifest.             |
| `child.py`                   | Startup, SDK wiring, dispatch, health, background workers and shutdown.             |
| `planning.py`                | Resolving model cards, reference modes, geometry, engine settings and plan digests. |
| `skulk_api.py`               | Bounded HTTP requests and decoding Skulk replies.                                   |
| `render.py`                  | The SDK job adapter: submit, observe, collect and cancel.                           |
| `refine.py` / `guides.py`    | Prompt rewriting with a placed chat model and model-specific guidance.              |
| `assets.py` / `media.py`     | Imports, content identity, media validation and reference lookup.                   |
| `takes.py` / `history.py`    | Retention marks and migration of earlier operation history.                         |
| `server.py` / `webapp.py`    | Application HTTP routes and packed browser assets.                                  |
| `speed.py` / `thumbnails.py` | Local timing observations and thumbnail backfill.                                   |
| `packaging.py`               | Checking that the signed manifest matches the packaged code.                        |

The web application lives in `web/`. Its API client is separate from its state stores and components. `schemas/` is generated from the application contracts; `tools/` contains schema and packaging utilities; `tests/` and `integration/` exercise increasingly complete boundaries.

## What Studio uses from the SDK

| SDK module   | Studio's use                                                          | What you still implement                                 |
| ------------ | --------------------------------------------------------------------- | -------------------------------------------------------- |
| `contracts`  | Startup, Hello, Health, Invoke, Result, typed contracts and manifests | Your application schemas and dispatch behavior.          |
| `wire`       | Read/write bounded messages to the managed host                       | The child loop and owned cleanup.                        |
| `storage`    | Protected directories, startup reads and atomic writes                | Media validation, naming and retention.                  |
| `operations` | Journals, services, runners, logs and operation descriptors           | The backend adapter and meaningful plan.                 |
| `database`   | Used by SDK operation persistence                                     | Application-specific stores and migrations where needed. |
| `serving`    | Host-selected addresses and caller admission helpers                  | HTTP routes, UI authentication and surface readiness.    |
| `streams`    | Not used by Studio's six descriptors at this revision                 | Streaming handlers if your own workflow needs them.      |

Studio uses unary calls to start and inspect durable work. A render's duration is not a reason to declare a streaming capability. Choose streaming when you actually exchange incremental output or caller media during a call. See [choosing a contract](https://developers.foxlight.ai/build/guides/choose-a-contract/).

## Keep these identities separate

| Identity                | Example / source                     | Purpose                                                   |
| ----------------------- | ------------------------------------ | --------------------------------------------------------- |
| Bundle id               | `foxlight.video-studio`              | Which installed application package this is.              |
| Installed node id       | Host-provided `Startup.node_id`      | Which installation owns this child and its durable scope. |
| Capability id + version | `video.render@1.0.0`                 | Which operation a caller requests.                        |
| Descriptor revision     | Derived from the descriptor contract | Whether caller and provider agree on the exact contract.  |
| Operation id            | Caller-retained application id       | Deduplicating and observing application work.             |
| Backend reference       | Skulk video job id                   | Following the external inference job.                     |
| Plan digest             | Digest of resolved work              | Detecting changes between review and execution.           |

Do not substitute a job id for an operation id, or a plan digest for authorization. Each answers a different question.

## Check your understanding

If the Studio tab closes during a render, the application operation can continue. If the Studio child restarts, its journal remains and the stored video job can be reconciled. If the model host fails, Studio cannot manufacture a successful model job. You should be able to point to the owner of each failure before writing recovery code.

Next: [define the contracts](https://developers.foxlight.ai/build/tutorials/video-studio/contracts/).
