Skip to main content

5. Follow a durable render operation

A model render can take much longer than a capability invocation. Studio returns an operation record quickly, then follows the backend job in its own supervised process. Polling status is a new unary call; it does not keep the original invocation open.

The SDK wiring​

Studio creates an OperationJournal, OperationLogs, OperationRunner and OperationService for rendering. The service handles the standard verbs; the runner advances queued work; the journal persists intent and status. Studio supplies the backend-specific SkulkRenderAdapter.

PieceResponsibility
OperationServiceValidate verb and authority, reserve the operation and answer plan/start/status/list/cancel/log.
OperationJournalPersist the id, input digest, submission state, backend reference and bounded result.
OperationRunnerSubmit queued work, observe accepted work, handle cancellation and reconcile after restart.
JobAdapterTranslate the application request into a backend request and report what the backend actually says.

The scope includes the installed node and capability identity. An operation id is meaningful within that scope. Retain your operation id before making a start request, so a lost reply does not make you invent a second operation.

Use the operation verbs​

These are payloads for the video.render capability, not complete Fabric transport envelopes. The input in the start example must come from your reviewed plan.

Operation payloads
{
"op": "plan",
"input": {
"prompt": "A fox walks through a snowy forest",
"seconds": 5,
"seed": 42
}
}

After reviewing the returned plan, send its effective input:

Start shape — substitute the returned input
{
"op": "start",
"operation_id": "forest-take-001",
"input": {
"prompt": "A fox walks through a snowy forest",
"seconds": 5,
"seed": 42,
"plan_digest": "<returned-plan-digest>"
}
}

The placeholder is intentionally not a valid digest. Copy the real one; do not send this example unchanged.

Inspect and cancel
{ "op": "status", "operation_id": "forest-take-001" }
{ "op": "cancel", "operation_id": "forest-take-001" }

list pages through summaries; status retrieves one operation's details. The UI hydrates individual records because list replies omit large input/results and backend references. log has its own offset. Offsets are not valid on every verb.

Read authority permits inspection. Start, cancel and plan require effect authority in the SDK service. A plan is read-only with respect to generation, but Studio exposes it through this governed operation interface.

Reserve before making an effect​

Starting the same id with the same input returns the existing operation. Starting that id with different input is refused. The journal reserves intent before the runner sends the backend request. A caller retry therefore does not normally submit a second render.

This does not create an exactly-once guarantee across an arbitrary HTTP service. The difficult boundary is a POST whose response is lost after the remote service accepts it.

Submission stateWhat is knownSafe next action
pendingIntent recorded; acceptance not yet established.Let the runner handle submission.
acceptedA backend job reference was recorded.Observe that same job.
uncertainSubmission may have taken effect, but no reliable reference was obtained.Investigate; do not automatically resend.

Keep submission state separate from operation state (queued, running, succeeded, failed, cancelled, interrupted). An interrupted accepted job may still be running remotely.

Implement the adapter boundary honestly​

This is Studio's actual submission method:

studio:render.py:SkulkRenderAdapter.submit
async def submit(self, operation_id: str, payload: dict[str, JsonValue]) -> str:
"""Plan once more against the live catalog, then post the job."""
try:
request = RenderRequest.model_validate_json(json.dumps(payload))
except ValidationError as error:
raise RenderRefused("render input is not a valid request") from error
fields, files = await self._before_send(self._prepare(request))
try:
job = await self.api.create_video(fields, files)
except SubmissionRejected as error:
raise RenderRefused(
"the API refused the render request; the plan no longer matches "
"what the card accepts"
) from error
return job.id

Before the POST, _prepare rechecks the plan, placement and references. Invalid input or a failed pre-send lookup can be reported as SubmissionRefused: the application knows it has not started a job. An explicit rejection is also a refusal. An ambiguous transport failure after sending must retain uncertainty.

SkulkAPI.create_video sends JSON for a text-only job and multipart data for references. Caller-provided server paths are not passed into the engine. References are verified application-owned byte snapshots.

The response is bounded and validated, including the returned job identity. A malformed success response is not evidence that nothing happened.

Observe, collect, then report success​

The adapter maps Skulk™'s queued/in-progress/completed/failed/cancelled states to SDK operation states. Progress is converted from the API's percentage into the SDK's 0–1 range. An unknown state is not guessed to be terminal.

A completed backend job is not yet a completed Studio operation. _collect must:

  1. Read declared output metadata.
  2. Download within byte bounds and check expected size and SHA-256.
  3. Collect a declared thumbnail where available.
  4. Publish the finished files into Studio storage.
  5. Apply retention and record measured timing.
  6. Return a validated RenderResult.

Collection failures have bounded retries. Reporting success while the file is corrupt or unavailable would leave a successful-looking take that the operator cannot use.

RenderResult contains geometry, frames, frame rate, actual seconds, audio metadata, digest, size and timing. Its filesystem paths belong to the application host. Remote browsers fetch media through the surface routes; they cannot open those paths as local files.

Cancellation and restart​

A cancel request asks the backend to stop. The operation becomes cancelled only when observation establishes that outcome. If work completed first, report completion; do not rewrite reality to satisfy the button click.

After a child restart, the journal preserves accepted job references so the runner can reconcile them. Pending rows interrupted before submission are failed rather than replayed. An uncertain submission without a reference is not replayed. A stale record reports stale evidence rather than pretending the backend is idle.

Studio uses bounded queues and retained history. Its manifest allows eight queued operations and a maximum operation runtime of 24 hours. These are application limits, not an estimate of model speed or a promise of inference capacity.

Beginner checkpoint​

Before integrating a real backend, prove these four cases with a fake adapter: one start, a repeated start, a cancellation race and a lost submit reply. If your implementation blindly retries the last case, it can duplicate expensive work.

Next: prompt refinement.